В предыдущей статье я рассказывал о транспондерах, как передается информация от транспондера к декодеру по воздуху. Сегодня я расскажу как передать информацию о номере и время транспондера в компьютер.Часть информации я подчерпнул здесь и на 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("@00000001rn");
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%02urn",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("@00000001rn");
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%02urn",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%it7t0t1t1t",decoderid);
putchar(0xF8);
putchar(0xF9);
printf("rn");
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%it%it0t",decoderid,sequence_number);
printf("x%02X%02Xrn",((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%it%it%07lut%u.%03ut130t119t2t",
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%02Xrn",((crcwork>>8)&0xff),(crcwork&0xff));
if (++TranspondersCountMain==TranspondersCountMax) TranspondersCountMain=0;
TransponderNeedToSend--;
}
В коментариях указано тоже самое, только проще для понимания, ведь в строчке "@t%it%it%07lut%u.%03ut130t119t2t" черт ногу сломит. Отправка единицы, подсчет контрольной суммый — все тоже самое что и в отправке статуса декодера.
@ 100 10 1234567 37.589 130 119 2 xEB94
Как видите, все очень даже просто.
Пару слов о программах подсчета кругов
Есть много программ, совместимых с засечками AMB, но я предпочитаю пользоваться RCM Begginers. Она бесплатная, на русском языке и очень простая. Скачать ее можно тут. В России RCM Ultimate (которая покруче и дорогая) пользуется исключительной популярностью среди моделистов. И AMB20 и AMBRc настраиваются так: Остальные программы мне не особо понравились.
Вместо заключения
Исходники на CodeVision Avr и схему для протеуса можно взять здесьВидео работы:
Автор: payalneg