в общем удалось обуздать протокол NEC, но есть некие приколы. При огромном расстоянии команда приходит с потерями и происходит адовая каша, такое программа не обрабатывает (на конечном этапе), но потом при подаче команды с нормального расстояния ваще ничего не работает, пока не перезагрузишь МК. вот код Код (C++): /* * main.c * * Created: 24.12.2018 19:24:51 * Author : Redfern.89 */ // Частота проца в герцах #ifndef F_CPU #define F_CPU 16000000UL #endif // Системные инклюцы #include <avr/io.h> #include <avr/interrupt.h> // Запуск и стоп таймера 0 #define T0_START { TCCR0B |= (1 << CS01) | (1 << CS00); } #define T0_STOP { TCCR0B = 0x00; } // Макросы для USART'а #define FOSC 16000000L #define BAUD 9600L #define MYUBRR FOSC / 16 / BAUD -1 // Кое-какие глобальные переменные const int NEC_MAX_RESET_OVF = 1200; // Столько тиков должно пройти, что-бы система перешла вновь в режим ожидания (1200 * 64 = 76.8ms) volatile int NEC_SCLK = 0; // Тактовые синхроимпульсы 64us volatile int NEC_START_FLAG = 0; // Говорит о том, что пришло стартовое сообщение volatile int NEC_REPEAT_FLAG = 0; // Говорит о том, что идет повтор сообщения (не используется) volatile int NEC_RECV_CNT = 0; // Сколько бит принято при обнаружении volatile unsigned char addr1 = 0x00; // Адрес volatile unsigned char addr2 = 0x00; // Инверсия адреса volatile unsigned char cmd1 = 0x00; // Команда volatile unsigned char cmd2 = 0x00; // Инверсия команды const int NEC_MIN_CLK = 5; // Минимальное значение, при котором следует начинать захват const int NEC_PACKET_LENGTH = 32; // Длина пакета /* Минимальные м максимальные интервалы. Значения были вычесленны при 100 нажатиях на кнопки пульта, полученные значения были запёханы в массив и найдены минимумы и максимумы */ const int NEC_MIN_HEADER_MESSAGE_CLK = 200; const int NEC_MAX_HEADER_MESSAGE_CLK = 235; const int NEC_MIN_ONE_BIT_CLK = 33; const int NEC_MAX_ONE_BIT_CLK = 38; const int NEC_MIN_NUL_BIT_CLK = 17; const int NEC_MAX_NUL_BIT_CLK = 20; // Это смещения в битах. Нужно для определения позиции следующего байта const int offset1_addr1 = 0; const int offset2_addr1 = 9; const int offset1_addr2 = 9; const int offset2_addr2 = 17; const int offset1_cmd1 = 17; const int offset2_cmd1 = 25; const int offset1_cmd2 = 25; const int offset2_cmd2 = 33; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ USART functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ void USORT_Init(unsigned char ubrr){ UBRR0H = (unsigned char)(ubrr >> 8); UBRR0L = (unsigned char)ubrr; UCSR0B = (1 << RXEN0) | (1 << TXEN0); UCSR0C = (1 << USBS0) | (3 << UCSZ00); } void USORT_Transmit( unsigned char data ) { while ( !( UCSR0A & (1 << UDRE0)) ); UDR0 = data; } // Вход в прерывание по переполнению таймера ISR (TIMER0_OVF_vect) { TCNT0 = 0xF0; // Вот тут мы переходим в ожидание следующей команды if (++NEC_SCLK >= NEC_MAX_RESET_OVF) { T0_STOP; NEC_SCLK = 0; NEC_START_FLAG = 0; TCNT0 = 0xF0; } } // Вход во внешнее прерывание SIGNAL (INT0_vect) { T0_START; // Начинаем отсчет if (NEC_SCLK > NEC_MIN_CLK) { // Первое значение приходит нулевым. нах@й его, пропускаем и идем дальше // Тут определяем стартовое сообщение (преамбулу) if (NEC_SCLK >= NEC_MIN_HEADER_MESSAGE_CLK && NEC_SCLK < NEC_MAX_HEADER_MESSAGE_CLK) { NEC_START_FLAG = 1; NEC_REPEAT_FLAG = 0; } /* Знаю, по-идиотски, но умнее лень было придумывать */ // Тут определяем биты нулевого значения if ((NEC_SCLK >= NEC_MIN_NUL_BIT_CLK && NEC_SCLK < NEC_MAX_NUL_BIT_CLK) && NEC_START_FLAG) { NEC_RECV_CNT++; // Инкрементируем колличество принятых нулей // ну а тут мутнаые процедуры записи значений в переменные if (NEC_RECV_CNT >= offset1_addr1 && NEC_RECV_CNT < offset2_addr1) { addr1 &= ~(1 << (NEC_RECV_CNT - offset1_addr1)); } if (NEC_RECV_CNT >= offset1_addr2 && NEC_RECV_CNT < offset2_addr2) { addr2 &= ~(1 << (NEC_RECV_CNT - offset1_addr2)); } if (NEC_RECV_CNT >= offset1_cmd1 && NEC_RECV_CNT < offset2_cmd1) { cmd1 &= ~(1 << (NEC_RECV_CNT - offset1_cmd1)); } if (NEC_RECV_CNT >= offset1_cmd2 && NEC_RECV_CNT < offset2_cmd2) { cmd2 &= ~(1 << (NEC_RECV_CNT - offset1_cmd2)); } } // Тут определяем биты положительного значения if ((NEC_SCLK >= NEC_MIN_ONE_BIT_CLK && NEC_SCLK < NEC_MAX_ONE_BIT_CLK) && NEC_START_FLAG) { NEC_RECV_CNT++; // Инкрементируем колличество принятых едениц if (NEC_RECV_CNT >= offset1_addr1 && NEC_RECV_CNT < offset2_addr1) { addr1 |= (1 << (NEC_RECV_CNT - offset1_addr1)); } if (NEC_RECV_CNT >= offset1_addr2 && NEC_RECV_CNT < offset2_addr2) { addr2 |= (1 << (NEC_RECV_CNT - offset1_addr2)); } if (NEC_RECV_CNT >= offset1_cmd1 && NEC_RECV_CNT < offset2_cmd1) { cmd1 |= (1 << (NEC_RECV_CNT - offset1_cmd1)); } if (NEC_RECV_CNT >= offset1_cmd2 && NEC_RECV_CNT < offset2_cmd2) { cmd2 |= (1 << (NEC_RECV_CNT - offset1_cmd2)); } } //USORT_Transmit(NEC_RECV_CNT); // Тут обнуляем счетчик синхроимульсов, что-бы отсчитывать время следующего прерывания с нуля NEC_SCLK = 0; // Колличество нулей и едениц в конечном счете должно быть 32, на этом и остановимся if (NEC_RECV_CNT == NEC_PACKET_LENGTH) { // Выставляем в стартовое положение все счетчики и останавлиеваем подсчет T0_STOP; NEC_RECV_CNT = 0; TCNT0 = 0xF0; // Проверка на целостность сообщения if ((addr1 + addr2 == 0xFF) && (cmd1 + cmd2) == 0xFF) { // Тестовый вывод в UART USORT_Transmit(addr1); USORT_Transmit(addr2); USORT_Transmit(cmd1); USORT_Transmit(cmd2); } } else if (NEC_RECV_CNT < NEC_PACKET_LENGTH && NEC_SCLK >= NEC_MAX_RESET_OVF) { //NEC_RECV_CNT = 0; } } } int main(void) { asm("CLI"); // Инициализация внешнего прерывания (на спад) EICRA |= (1 << ISC01) | (0 << ISC00); EIMSK |= (1 << INT0); EIFR |= (1 << INTF0); PCICR = 0x00; // Инициализация таймера/счестчика 0 для подсчета временного интервала между импульсами TCCR0B = 0x00;//(1 << CS00) | (1 << CS01); TIMSK0 |= (1 << TOIE0); TCNT0 = 0xF0; USORT_Init(MYUBRR); asm("SEI"); while (1) { } return 0; }
Посмотрел по диагонали. Вот тут нет сброса предделителя, поэтому каждый раз интервал будет разным. Вот здесь нет никакой гарантии, что таймер не поднял флаг прерывания. Надо посмотреть по даташиту - какой адрес перрывания у INT0 и у переполнения таймера - у кого адрес меньше, тот и уйдет раньше на прерывание.
и зачем он здесь? Вообще, у меня такое ощущение, что там что-то оптимизатор мудрит. Ну и я бы ввел в код диагностические сообщения - хотя бы понимать, где что зависает.
Почти вся работа построена на прерываниях. Если из прерываний выводить что-то через uart, то это уже даёт ложные результаты, ибо работают они ооочень уж не предсказуемо или как-то, я не знаю как. Хотя переменная NEC_RECV_CNT уже не может обнулиться после приёма ошибочных данных
во внутрь бесконечного цикла уводи МК в IDLE. Сразу после него выводи глобальную переменную, которую меняй в обработчиках прерываний и в прочих местах. Я так USI n nRF отлаживал, когда ЛА не было. Только смотри - сразу после выхода из сна и перед входом в обработчик МК выполняет толи одну инструкцию, толи несколько (в дашике есть инфа), поэтому имеет смысл nop поставить.
Да попробуй все))) И погоняй в симуляторе - посмотри дизасм - как компилятор всё по регистрам раскладывает.