Система автоматического подсчета кругов и времени для 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 и схему для протеуса можно взять здесьВидео работы:

ссылка на оригинал статьи http://habrahabr.ru/post/196514/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *