Система автоматического подсчета кругов и времени для RC-автомоделей. Часть 2 Протоколы AMB20 и AMBRc

    В предыдущей статье я рассказывал о транспондерах, как передается информация от транспондера к декодеру по воздуху. Сегодня я расскажу как передать информацию о номере и время транспондера в компьютер.Часть информации я подчерпнул здесь и на rctech.net

    Соберем схему

    А соберем ее на макетке без пайки:Что нам нужно:1. atmega16 (в моем девайсе стоит atmega32, но суть не меняется)2. Кварц для USART 7.3728Мгц. (В моем девайсе — 16.59Мгц)3. Кварц часовой на 32кгц.4. USB-Rs232 переходник5. Светодиод, кнопочка и резистор.Что бы не париться с прошивкой МК — зальем ему бутлоадер. Отличная статья есть на easyelectronics.ru. Вот именно им я и пользуюсь. Светодиод и кнопочка нужна и для бутлоадера и для основной программы.В нашем декодере есть собственные часы для более точного подсчета времени прохождения круга, а как еще точнее можно сделать? Да ни как, просто передавать номер транспондера и время, когда он был пройден через петлю.Светодиод в бутлоадере будет говорить о том, что бутлоадер загружен. А в основной программе он будет просто моргать, говоря о том, что с часами все в порядке.Кнопочкой мы будем имитировать проезд модели над петлей.USB-Rs232 я взял всеми любимый FT232RL, который требует минимум обвязки. В принципе, подойдет любой USB-RS232 переходник.Схема в протеусе что бы видеть что выдает терминал:

    AMB20

    Данный протокол можно разделить на две части: Инициализация контроллера и передачи информации о транспондере и времени.
    Подсчет времени
    Таймер с 32кгц кварцем прерывается раз в секунду.
    interrupt [TIM2_OVF] void timer2_ovf_isr(void)
    {
    char i;
    if (++GlobalS==60)
        {
        if (++GlobalM==60) 
            {
            GlobalH++;
            GlobalM=0;
            }
        GlobalS=0;
        }        
    LED=!LED;
    }
    
    Я думаю тут все ясно и просто.
    Инициализация
    Во время инициализации программа открывает СОМ порт и сажает RTS на землю. Выход этого сигнала повесим на INT0 контроллера. По прерыванию отсылаем в компьютер программе @00000001 и сбрасываем время в контроллере:
    delay_ms(100);
    printf("@00000001\r\n");
    GlobalH=0;
    GlobalM=0;
    GlobalS=0;
    GlobalMs=0;
    
    Прохождение метки через петли
    Что бы программа понял, что через петлю прошла метка, ей нужно отправить запись вида:@AABBCCDDEEгдеAA — номер транспондера от 1 до 99BB — часыCC — минутыDD — секундыEE — сотые секунды
    Trans=1;
    MS=GlobalMs_Timer/2.56;
    H=GlobalH;
    M=GlobalM;
    S=GlobalS;
    printf("@%02u%02u%02u%02u%02u\r\n",Trans,H,M,S,MS);
    
    К примеру:@0102030405значит, что транспондер с номером 01 прошел петлю на 2 час 3 минуты 4 секунды и 50мсВсе очень даже просто.
    Код программы
    /*****************************************************
    This program was produced by the
    CodeWizardAVR V2.05.0 Professional
    Automatic Program Generator
    © Copyright 1998-2010 Pavel Haiduc, HP InfoTech s.r.l.
    http://www.hpinfotech.com
    
    Project : 
    Version : 
    Date    : 01.04.2013
    Author  : 
    Company : 
    Comments: 
    
    
    Chip type               : ATmega16
    Program type            : Application
    AVR Core Clock frequency: 7,372800 MHz
    Memory model            : Small
    External RAM size       : 0
    Data Stack size         : 256
    *****************************************************/
    
    #include <mega16.h>
    
    
    #ifndef RXB8
    #define RXB8 1
    #endif
    
    #ifndef TXB8
    #define TXB8 0
    #endif
    
    #ifndef UPE
    #define UPE 2
    #endif
    
    #ifndef DOR
    #define DOR 3
    #endif
    
    #ifndef FE
    #define FE 4
    #endif
    
    #ifndef UDRE
    #define UDRE 5
    #endif
    
    #ifndef RXC
    #define RXC 7
    #endif
    
    #define FRAMING_ERROR (1<<FE)
    #define PARITY_ERROR (1<<UPE)
    #define DATA_OVERRUN (1<<DOR)
    #define DATA_REGISTER_EMPTY (1<<UDRE)
    #define RX_COMPLETE (1<<RXC)
    
    // USART Receiver buffer
    #define RX_BUFFER_SIZE 32
    char rx_buffer[RX_BUFFER_SIZE];
    
    #if RX_BUFFER_SIZE <= 256
    unsigned char rx_wr_index,rx_rd_index,rx_counter;
    #else
    unsigned int rx_wr_index,rx_rd_index,rx_counter;
    #endif
    
    // This flag is set on USART Receiver buffer overflow
    bit rx_buffer_overflow;
    
    // USART Receiver interrupt service routine
    interrupt [USART_RXC] void usart_rx_isr(void)
    {
    char status,data;
    status=UCSRA;
    data=UDR;
    if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
       {
       rx_buffer[rx_wr_index++]=data;
    #if RX_BUFFER_SIZE == 256
       // special case for receiver buffer size=256
       if (++rx_counter == 0)
          {
    #else
       if (rx_wr_index == RX_BUFFER_SIZE) rx_wr_index=0;
       if (++rx_counter == RX_BUFFER_SIZE)
          {
          rx_counter=0;
    #endif
          rx_buffer_overflow=1;
          }
       }
    }
    
    #ifndef _DEBUG_TERMINAL_IO_
    // Get a character from the USART Receiver buffer
    #define _ALTERNATE_GETCHAR_
    #pragma used+
    char getchar(void)
    {
    char data;
    while (rx_counter==0);
    data=rx_buffer[rx_rd_index++];
    #if RX_BUFFER_SIZE != 256
    if (rx_rd_index == RX_BUFFER_SIZE) rx_rd_index=0;
    #endif
    #asm("cli")
    --rx_counter;
    #asm("sei")
    return data;
    }
    #pragma used-
    #endif
    
    // USART Transmitter buffer
    #define TX_BUFFER_SIZE 256
    char tx_buffer[TX_BUFFER_SIZE];
    
    #if TX_BUFFER_SIZE <= 256
    unsigned char tx_wr_index,tx_rd_index,tx_counter;
    #else
    unsigned int tx_wr_index,tx_rd_index,tx_counter;
    #endif
    
    // USART Transmitter interrupt service routine
    interrupt [USART_TXC] void usart_tx_isr(void)
    {
    if (tx_counter)
       {
       --tx_counter;
       UDR=tx_buffer[tx_rd_index++];
       }
    }
    
    #ifndef _DEBUG_TERMINAL_IO_
    // Write a character to the USART Transmitter buffer
    #define _ALTERNATE_PUTCHAR_
    #pragma used+
    void putchar(char c)
    {
    if (tx_counter || ((UCSRA & DATA_REGISTER_EMPTY)==0))
       {
       tx_buffer[tx_wr_index++]=c;
       ++tx_counter;
       }
    else
       UDR=c;
    }
    #pragma used-
    #endif
    
    // Standard Input/Output functions
    #include <stdio.h>
    #include <delay.h>
    
    char GlobalH,GlobalM,GlobalS,GlobalMs;
    float TempFloat;
    #define GlobalMs_Timer TCNT2
    #define LED PORTD.7 
    unsigned int Trans;
    
    // External Interrupt 0 service routine
    interrupt [EXT_INT0] void ext_int0_isr(void)
    {
    delay_ms(100);
    printf("@00000001\r\n");
    GlobalH=0;
    GlobalM=0;
    GlobalS=0;
    GlobalMs=0;
    }
    
    
    interrupt [EXT_INT1] void ext_int1_isr(void)
    {
    char H,M,S,MS,Trans;
    Trans=1;
    MS=GlobalMs_Timer/2.56;
    H=GlobalH;
    M=GlobalM;
    S=GlobalS;
    printf("@%02u%02u%02u%02u%02u\r\n",Trans,H,M,S,MS);
    }
     
    interrupt [EXT_INT2] void ext_int2_isr(void)
    {
    
    }
    
    interrupt [TIM0_OVF] void timer0_ovf_isr(void)
    {
    
    }
    
    interrupt [TIM2_OVF] void timer2_ovf_isr(void)
    {
    char i;
    if (++GlobalS==60)
        {
        if (++GlobalM==60) 
            {
            GlobalH++;
            GlobalM=0;
            }
        GlobalS=0;
        }        
    LED=!LED;
    }
    
    void main(void)
    {
    PORTA=0x00;
    DDRA=0x00;
    PORTB=0x00;
    DDRB=0x00;
    PORTC=0x00;
    DDRC=0x00;
    PORTD=0b1001100;
    DDRD=0x80;
    
    TCCR0=0x05;
    TCNT0=0x00;
    OCR0=0x00;
    
    ASSR=0x08;
    TCCR2=0x05;
    TCNT2=0x00;
    OCR2=0x00;
    
    GICR|=0xE0;
    MCUCR=0x0A;
    MCUCSR=0x00;
    GIFR=0xE0;
    
    TIMSK=0x41;
    
    // USART initialization
    // Communication Parameters: 8 Data, 1 Stop, No Parity
    // USART Receiver: On
    // USART Transmitter: On
    // USART Mode: Asynchronous
    // USART Baud Rate: 9600
    UCSRA=0x00;
    UCSRB=0xD8;
    UCSRC=0x86;
    UBRRH=0x00;
    UBRRL=0x2F;
    
    #asm("sei")
    
    while (1)
          {                                               
          }
    }
    
    Недостатки этого протокола очевидны — номер транспондера не больше 99. Рассмотри другой протокол.

    AMBRC

    Этот протокол требует гораздо больше памяти от МК. Дальше будет ясно почему. Разделить этот протокол можно на три части: инициализация декодера, проход через метку и статус декодера.
    Инициализация
    Программа шлет декодеру следующий текст "?;0;0;0;" и возврат коретки (clcf) байты — (0x0D,0x0A). Больше ничего интересного в декодер программа не шлет. Поэтому когда на входе имеем байтик 0x0A и ставим флаг инициализации (зачем нужны флаги будет рассказано ниже).
    interrupt [USART_RXC] void usart_rx_isr(void)
    {
    char status,data;
    status=UCSRA;
    data=UDR;
    if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
       {
       rx_buffer[rx_wr_index++]=data;   
       if (data==0x0A) {TimeToInit=1;}   //Здесь смотрим на байты и ставим флаг
    #if RX_BUFFER_SIZE == 256
       // special case for receiver buffer size=256
       if (++rx_counter == 0)
          {
    #else
       if (rx_wr_index == RX_BUFFER_SIZE) rx_wr_index=0;
       if (++rx_counter == RX_BUFFER_SIZE)
          {
          rx_counter=0;
    #endif
          rx_buffer_overflow=1;
          }
       }
    }
    
    А вот в главном цикле мы инициализируемся:
          if (TimeToInit)
            {
            putchar(1);   
            printf("$\t%i\t7\t0\t1\t1\t",decoderid);
            putchar(0xF8);
            putchar(0xF9);
            printf("\r\n");
            GlobalS=0;
            GlobalMs_Timer=0;    
            sequence_number=0;     
            TimeToInit=0;
            }  
    
    Полного описания протокола я не нашел, нашел только частичный. Запись в СОМ порт будет отправлена вида:
    01 24 09 31 30 30 09 37 09 30 09 31 09 31 09 F8 F9 0D 0A
    Здесь номер декодера 100. Запись всегда начинается с байта (0x01). Дальше идет знак $. Между каждым значением идет знак Tab (0x09). Все записи заканчиваются возвратом каретки (0x0D 0x0A). Ну и не забываем, что нужно сбросить время и sequence_number. sequence_number — это номер посылки, с каждой посылкой инкрементируется.
    Статус декодера
    Каждые 4 секунды нам необходимо слать статус. Посылка вида:
    # 100 980 0 x06BA
    Байты примерно такие:
    01 23 09 31 30 30 09 31 30 32 38 09 30 09 78 39 42 39 46 0D 0A
    Здесь видно номер декодера, sequence_number, какой то ноль (не знаю зачем он) и контрольная сумма. Контрольная сумма считается в процедуре putchar:
    void putchar(char c)
    {
    while (tx_counter == TX_BUFFER_SIZE);
    #asm("cli")
    crcwork = (crcTable[(crcwork >> 8) & 0xff] ^ (crcwork << 8) ^ c); //вот здесь мы считаем контрольную сумму
    if (tx_counter || ((UCSRA & DATA_REGISTER_EMPTY)==0))
       {
       tx_buffer[tx_wr_index++]=c;
       ++tx_counter;
       }
    else
       UDR=c;    
    #asm("sei")
    }
    
    таблица crcTable подсчитана заранее, она здесь:
    Скрытый текст
    flash unsigned int crcTable[256]={0,4129,8258,12387,16516,20645,24774,28903,33032,37161,41290,45419,49548,53677,57806,61935,4657,528,12915,8786,21173,17044,29431,25302,37689,33560,45947,41818,54205,50076,62463,58334,9314,13379,1056,5121,25830,29895,17572,21637,42346,46411,34088,38153,58862,62927,50604,54669,13907,9842,5649,1584,30423,26358,22165,18100,46939,42874,38681,34616,63455,59390,55197,51132,18628,22757,26758,30887,2112,6241,10242,14371,51660,55789,59790,63919,35144,39273,43274,47403,23285,19156,31415,27286,6769,2640,14899,10770,56317,52188,64447,60318,39801,35672,47931,43802,27814,31879,19684,23749,11298,15363,3168,7233,60846,64911,52716,56781,44330,48395,36200,40265,32407,28342,24277,20212,15891,11826,7761,3696,65439,61374,57309,53244,48923,44858,40793,36728,37256,33193,45514,41451,53516,49453,61774,57711,4224,161,12482,8419,20484,16421,28742,24679,33721,37784,41979,46042,49981,54044,58239,62302,689,4752,8947,13010,16949,21012,25207,29270,46570,42443,38312,34185,62830,58703,54572,50445,13538,9411,5280,1153,29798,25671,21540,17413,42971,47098,34713,38840,59231,63358,50973,55100,9939,14066,1681,5808,26199,30326,17941,22068,55628,51565,63758,59695,39368,35305,47498,43435,22596,18533,30726,26663,6336,2273,14466,10403,52093,56156,60223,64286,35833,39896,43963,48026,19061,23124,27191,31254,2801,6864,10931,14994,64814,60687,56684,52557,48554,44427,40424,36297,31782,27655,23652,19525,15522,11395,7392,3265,61215,65342,53085,57212,44955,49082,36825,40952,28183,32310,20053,24180,11923,16050,3793,7920};
    Сам код для посылки выглядит так:
          if (TimeToSendAmbStatus)
            {
            sequence_number++;                           
            putchar(1);                              
            crcwork=0xFFFF; 
            printf("#\t%i\t%i\t0\t",decoderid,sequence_number);  
            printf("x%02X%02X\r\n",((crcwork>>8)&0xff),(crcwork&0xff));                                                                             
            TimeToSendAmbStatus=0;
            }   
    
    Тут видно как инкрементируется sequece_number. Вначале шлется 0x01, дальше сбрасывается crcwork = 0xFFFF. Шлем часть посылки. В это же время считается контрольная сумма. Следующим действием шлем нашу контрольную сумму и возврат каретки. И так каждые четыре секунды:
    interrupt [TIM2_OVF] void timer2_ovf_isr(void)
    {
    static char times;
    
    if (++times==4) 
        {       
        TimeToSendAmbStatus=1;  
        times=0;
        }
    GlobalS++;     
    LED=!LED;
    }
    
    Отсылка номера транспондера и времени
    Вот здесь начинаются сложности. Преобразовать время и номер транспондера и отправить его в буфер для USART занимает очень много времени (у меня как то было порядка 300мс) и вот поэтому я ничего не отправляю прямо в прерываниях, а оставляю это дело основному циклу. Для того что бы все работало, нужно использовать промежуточный буфер и немного переменных:
    #define TranspondersCountMax 20
    unsigned long int Transponders[TranspondersCountMax];
    unsigned long int TranspondersTimeS[TranspondersCountMax];
    unsigned int TranspondersTimeMS[TranspondersCountMax];
    char TransponderNeedToSend, TranspondersCountInt, TranspondersCountMain;
    
    Буфер у нас циклический кстати.А вот так мы будем заполнять буфер:
    interrupt [EXT_INT1] void ext_int1_isr(void)
    {
    unsigned long int S;
    unsigned int MS;
    MS=GlobalMs_Timer*3.90625;
    S=GlobalS;
    if (TransponderNeedToSend<TranspondersCountMax) 
        {
        TransponderNeedToSend++;    
        Transponders[TranspondersCountInt]=1234567;
        TranspondersTimeS[TranspondersCountInt]=S;
        TranspondersTimeMS[TranspondersCountInt]=MS;                                       
        if (++TranspondersCountInt==TranspondersCountMax) TranspondersCountInt=0;
        }
    }
    
    А в основном цикле:
    if (TransponderNeedToSend) 
            {                                       
            sequence_number++;                
            putchar(1);
            crcwork=0xFFFF;           
            printf("@\t%i\t%i\t%07lu\t%u.%03u\t130\t119\t2\t",
            decoderid,
            sequence_number,
            Transponders[TranspondersCountMain],
            TranspondersTimeS[TranspondersCountMain],
            TranspondersTimeMS[TranspondersCountMain]);
            //putchar('@');             
            //putchar(9);
            //printf("%i",decoderid);
            //putchar(9);
            //printf("%i",sequence_number);
            //putchar(9);         
            //printf("%07lu",Trans);
            //putchar(9);
            //printf("%u.%03u",S,MS);
            //putchar(9);
            //printf("130");
            //putchar(9);
            //printf("119");
            //putchar(9);
            //printf("2");
            //putchar(9);         
            printf("x%02X%02X\r\n",((crcwork>>8)&0xff),(crcwork&0xff));                                       
            if (++TranspondersCountMain==TranspondersCountMax) TranspondersCountMain=0; 
            TransponderNeedToSend--;
            }
    
    В коментариях указано тоже самое, только проще для понимания, ведь в строчке "@\t%i\t%i\t%07lu\t%u.%03u\t130\t119\t2\t" черт ногу сломит. Отправка единицы, подсчет контрольной суммый — все тоже самое что и в отправке статуса декодера.
    @ 100 10 1234567 37.589 130 119 2 xEB94
    Как видите, все очень даже просто.

    Пару слов о программах подсчета кругов

    Есть много программ, совместимых с засечками AMB, но я предпочитаю пользоваться RCM Begginers. Она бесплатная, на русском языке и очень простая. Скачать ее можно тут. В России RCM Ultimate (которая покруче и дорогая) пользуется исключительной популярностью среди моделистов. И AMB20 и AMBRc настраиваются так: Остальные программы мне не особо понравились.

    Вместо заключения

    Исходники на CodeVision Avr и схему для протеуса можно взять здесьВидео работы:
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 1

      0
      Очень интересно, что у вас получилось в итоге?

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

      Самое читаемое