ATmega328 NEC-протокол

Тема в разделе "Микроконтроллеры AVR", создана пользователем Redfern89q, 13 янв 2019.

?

Вышвырнуть МК в окно и заняться вышиванием крестиком?

  1. Да

    0 голосов
    0,0%
  2. Стопудофф да

    2 голосов
    100,0%
Можно выбрать сразу несколько вариантов.
  1. Redfern89q

    Redfern89q Нуб

    в общем удалось обуздать протокол 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;
    }

     
     
    Последнее редактирование: 13 янв 2019
  2. parovoZZ

    parovoZZ Гуру

    Посмотрел по диагонали.
    Вот тут
    нет сброса предделителя, поэтому каждый раз интервал будет разным.

    Вот здесь
    нет никакой гарантии, что таймер не поднял флаг прерывания. Надо посмотреть по даташиту - какой адрес перрывания у INT0 и у переполнения таймера - у кого адрес меньше, тот и уйдет раньше на прерывание.
     
    KindMan нравится это.
  3. Redfern89q

    Redfern89q Нуб

    у INT0 выше приоритет... а как решить данную проблему?)
     
  4. parovoZZ

    parovoZZ Гуру

    int - 'это что за размерность? Два байта или один?
     
  5. Redfern89q

    Redfern89q Нуб

  6. parovoZZ

    parovoZZ Гуру

    и зачем он здесь? Вообще, у меня такое ощущение, что там что-то оптимизатор мудрит. Ну и я бы ввел в код диагностические сообщения - хотя бы понимать, где что зависает.
     
  7. Redfern89q

    Redfern89q Нуб

    Почти вся работа построена на прерываниях. Если из прерываний выводить что-то через uart, то это уже даёт ложные результаты, ибо работают они ооочень уж не предсказуемо или как-то, я не знаю как. Хотя переменная NEC_RECV_CNT уже не может обнулиться после приёма ошибочных данных
     
  8. parovoZZ

    parovoZZ Гуру

    во внутрь бесконечного цикла уводи МК в IDLE. Сразу после него выводи глобальную переменную, которую меняй в обработчиках прерываний и в прочих местах. Я так USI n nRF отлаживал, когда ЛА не было. Только смотри - сразу после выхода из сна и перед входом в обработчик МК выполняет толи одну инструкцию, толи несколько (в дашике есть инфа), поэтому имеет смысл nop поставить.
     
  9. Redfern89q

    Redfern89q Нуб

    А это не приведёт к замедление и без того медленной системы?) ))))
     
  10. parovoZZ

    parovoZZ Гуру

    После отладки удалишь всё ненужное.
     
  11. Redfern89q

    Redfern89q Нуб

    а как понять, когда выводить?

    Как его настроить? это ведь про avr-gcc речь?
     
  12. parovoZZ

    parovoZZ Гуру

    В тулчейне, далее опции компилятора. Выбрать оптимизацию Os или O1, ну или Выключить ее.
     
  13. Redfern89q

    Redfern89q Нуб

    вот настройки оптимизации. что лучше выбрать?
     

    Вложения:

  14. parovoZZ

    parovoZZ Гуру

    Да попробуй все))) И погоняй в симуляторе - посмотри дизасм - как компилятор всё по регистрам раскладывает.
     
  15. Redfern89q

    Redfern89q Нуб

    вот уж кому точно не стоит верить - так это симулятором. говно редкостное.