Обработчик прерывания и SLEEP
- Войдите на сайт для отправки комментариев
Вс, 10/09/2017 - 19:43
Делаю брелок сигнализатор. В проэкте использую ATtiny13.
На стороне АРДУИНО использую библиотеку VirtualWire.
Из-за того что готовая библиотека занимает много места и не вмещаеться в памяти тини13 написал кусок который отправляет пакет.
#include <avr/io.h> #include <avr/interrupt.h> #include <util/crc16.h> #include <util/delay.h> #include <string.h> #include <avr/sleep.h> #define _code "R01" //#define _code "R02" //#define _code "R03" //#define _code "R04" #define Input 1 #define Led 2 #define RF_vcc 3 #define TXPORT PORTB // Имя порта для передачи #define TXDDR DDRB // Регистр направления порта на передачу #define TXD 0 // Номер бита порта для использования на передачу #define VW_HEADER_LEN 8 //Длина хидера #define VW_MAX_MESSAGE_LEN 6 //Максимальная длина сообщения /* Ниже задаются константы, определяющие скорость передачи данных (бодрейт) расчет BAUD_DIV осуществляется следующим образом: BAUD_DIV = (CPU_CLOCK / DIV) / BAUD_RATE где CPU_CLOCK - тактовая частота контроллера, BAUD_RATE - желаемая скорость UART, а DIV - значение делителя частоты таймера, задающееся регистром TCCR0B. Например, тактовая частота 9.6 МГц, делитель на 8, скорость порта 9600 бод: BAUD_DIV = (9 600 000 / 8) / 9600 = 125 (0x7D). */ //#define T_DIV 0x01 // DIV = 1 //#define T_DIV 0x02 // DIV = 8 #define T_DIV 0x03 // DIV = 64 #define BAUD_DIV 0x4B //Скорость 2000 бод //#define BAUD_DIV 0x7D // Скорость = 9600 бод uint8_t symbols[16] = { 0xd, 0xe, 0x13, 0x15, 0x16, 0x19, 0x1a, 0x1c, 0x23, 0x25, 0x26, 0x29, 0x2a, 0x2c, 0x32, 0x34 }; uint8_t vw_tx_buf[(VW_MAX_MESSAGE_LEN * 2) + VW_HEADER_LEN] = {0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x38, 0x2c}; volatile uint16_t txbyte; volatile uint8_t txbitcount; volatile byte alarm_flag = false; const char *msg = _code; void tx_send(); void RF_send(uint8_t* buf, uint8_t len); ISR(TIM0_COMPA_vect) { TXPORT = (TXPORT & ~(1 << TXD)) | ((txbyte & 0x01) << TXD); txbyte >>= 1; if (txbitcount > 0) // Если идет передача (счетик бит больше нуля), { txbitcount--; // то уменьшаем его на единицу. } } ISR(INT0_vect) { alarm_flag = true; } int main(void) { pinMode(Input, INPUT_PULLUP); pinMode(Led, OUTPUT); pinMode(RF_vcc, OUTPUT); digitalWrite(Led, LOW); digitalWrite(RF_vcc, LOW); GIMSK |= (1 << INT0); MCUCR |= (1 << ISC01) | (0 << ISC00); txbyte = 0xFFFF; // Значение буфера на передачу - все единицы txbitcount = 0x00; // Значение счетчика преедаваемых бит - ноль (ничего пока не передаем) TXDDR |= (1 << TXD); // Задаем направление порта на передачу как выход OCR0A = BAUD_DIV; // Задаем значение регистра OCR0A в соответствии с бодрейтом TIMSK0 |= (1 << OCIE0A); // Разрешаем прерывание TIM0_COMPA TCCR0A |= (1 << WGM01); // Режим таймера CTC (очистка TCNT0 по достижению OCR0A) TCCR0B |= T_DIV; // Задаем скорость счета таймера в соответствии с делителем sei(); // Разрешаем прерывания глобально while (1) { if (alarm_flag == true) { digitalWrite(Led, HIGH); digitalWrite(RF_vcc, HIGH); _delay_ms(1); RF_send((uint8_t *)msg, strlen(msg)); _delay_ms(1); digitalWrite(RF_vcc, LOW); while (digitalRead(Input) == LOW); digitalWrite(Led, LOW); alarm_flag = false; } } return (0); } void tx_send() { uint8_t i; for (i = 0; i < strlen((char *)vw_tx_buf); i++) { while (txbitcount); // Ждем пока закончится передача предыдущего байта txbyte = vw_tx_buf[i]; txbitcount = 0x06; // Задаем счетчик байт равным 10 } } void RF_send(uint8_t* buf, uint8_t len) { uint8_t i; uint16_t crc = 0xffff; uint8_t *p = vw_tx_buf + VW_HEADER_LEN; // start of the message area uint8_t count = len + 3; // Added byte count and FCS to get total number of bytes // Encode the message length crc = _crc_ccitt_update(crc, count); *p++ = symbols[count >> 4]; *p++ = symbols[count & 0xf]; // Encode the message into 6 bit symbols. Each byte is converted into // 2 6-bit symbols, high nybble first, low nybble second for (i = 0; i < len; i++) { crc = _crc_ccitt_update(crc, buf[i]); *p++ = symbols[buf[i] >> 4]; *p++ = symbols[buf[i] & 0xf]; } // Append the fcs, 16 bits before encoding (4 6-bit symbols after encoding) // Caution: VW expects the _ones_complement_ of the CCITT CRC-16 as the FCS // VW sends FCS as low byte then hi byte crc = ~crc; *p++ = symbols[(crc >> 4) & 0xf]; *p++ = symbols[crc & 0xf]; *p++ = symbols[(crc >> 12) & 0xf]; *p++ = symbols[(crc >> 8) & 0xf]; tx_send(); }
В данный момент программа работает, но есть несколько вопросов.
1. Почему в обработчике прерывания выполнить такой код? Приходиться выставлять флаг и через него в основном цикле через проверку выполнять этот код.
ISR(INT0_vect) { digitalWrite(Led, HIGH); digitalWrite(RF_vcc, HIGH); _delay_ms(1); RF_send((uint8_t *)msg, strlen(msg)); _delay_ms(1); digitalWrite(RF_vcc, LOW); while (digitalRead(Input) == LOW); digitalWrite(Led, LOW); }
2.Каким оброзом заставить процессор уходить в сон для снижения энергопотреления? Питаться устройство будет от батарейки.
3.Почему используюя attachInterrupt в обработчике не устанавливаеться флаг или теряеться после завершения?
pinMode(0,INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(0), blink, CHANGE); void blink(){ flag = true; }
anarch, не знаю, что Вы делаете и зачем, но настоятельно рекомендую вынести из прерывания:
1. Цикл while (насколько я понимаю, ожидание готовности устройства),
2. Вызовы delay().
3. Вызов RF_send (насколько я понимаю, команда на передачу медленному устройству).
4. Заменить вызовы digitalWrite на прямую работу с портами (впрочем, если Вы выполните предыдущие 3 пункта, их просто можно убрать).
По 3 вопросу: вероятно, Вы забыли описать flag как volatile.
Описан как volatile.
Заменить digitalWrite можно, но не уверен что пренисет результата.
Вот на этой функци и происходит зависание. Зажигаеться светодиод, подаеться питание на пин и все дальше выполнение программы завершаеться. До digitalWrite(Led, LOW); программа не доходит.
Если развернуть основной рабочий код то можно увидеть, что вынеся все из обработчика программа начала работать.
В обработчике устанавливаю флаг.
вынеся все из обработчика программа начала работать.
Я за нее рад.
Понял свою ошибку по поводу прерывания. Но осталься вопрос по ардуиновскому attachInterrupt упорно не хочет работать.
Ни как не мого заставить уснуть камень. Попробовал напрямую через регистры, ток не падает. Что я делаю не так.
Вот таким образом уходит в сон но просыпаться ни в какую :(