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