Pull to refresh

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

Reading time 9 min
Views 18K
В предыдущей статье я рассказывал о транспондерах, как передается информация от транспондера к декодеру по воздуху. Сегодня я расскажу как передать информацию о номере и время транспондера в компьютер.Часть информации я подчерпнул здесь и на 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 и схему для протеуса можно взять здесьВидео работы:
Tags:
Hubs:
+12
Comments 1
Comments Comments 1

Articles