📜 ⬆️ ⬇️

The system of automatic counting circles and time for RC-car. Part 2 Protocols AMB20 and AMBRc

In the previous article I talked about transponders, how information is transmitted from the transponder to the decoder through the air. Today I will tell you how to transfer information about the number and time of the transponder to the computer. Some of the information I picked here and on rctech.net

Let's collect the scheme

And we will collect it on a breadboard without soldering: What we need: 1. atmega16 (in my device is atmega32, but the essence does not change) 2. Quartz for USART 7.3728 MHz. (In my device - 16.59 MHz) 3. Quartz watch on 32kgts.4. USB-RS232 adapter5. The LED, the button and the resistor. In order not to bathe with the MK firmware - we flood it with the bootloader. Excellent article is on easyelectronics.ru . That's it, I use it. The LED and the button are needed both for the bootloader and for the main program. Our decoder has its own clock for a more accurate calculation of lap times, and how can we make it even more accurate? Yes, no matter how, just transmit the transponder number and the time when it was passed through the loop. The LED in the bootloader will indicate that the bootloader is loaded. And in the main program, it will just blink, saying that everything is fine with the clock. With a button, we will imitate the model's passage over the loop. In principle, any USB-RS232 adapter will do. A circuit in the proteus to see what the terminal gives out:

AMB20

This protocol can be divided into two parts: Initialization of the controller and transmission of information about the transponder and time.
Counting time
A timer with 32kHz quartz is interrupted once a second.
interrupt [TIM2_OVF] void timer2_ovf_isr(void) { char i; if (++GlobalS==60) { if (++GlobalM==60) { GlobalH++; GlobalM=0; } GlobalS=0; } LED=!LED; } 
I think everything is clear and simple.
Initialization
During initialization, the program opens the COM port and puts the RTS on the ground. The output of this signal will hang on INT0 controller. By interrupt, we send to the computer program @ 00000001 and reset the time in the controller:
 delay_ms(100); printf("@00000001\r\n"); GlobalH=0; GlobalM=0; GlobalS=0; GlobalMs=0; 
Passing tag through loops
In order for the program to understand that a label has passed through the loop, it needs to send a record of the form: @ AABBCCDDEE where AAA is the transponder number from 1 to 99BB - hours CC - minutes DD - seconds EE - hundredths of a second
 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); 
For example: @ 0102030405 means that the transponder with the number 01 passed a loop for 2 hours 3 minutes 4 seconds and 50ms Everything is very simple.
Program code
 /***************************************************** This program was produced by the CodeWizardAVR V2.05.0 Professional Automatic Program Generator © Copyright 1998-2010 Pavel Haiduc, HP InfoTech srl 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) { } } 
The disadvantages of this protocol are obvious - the number of the transponder is not more than 99. Consider another protocol.

AMBRC

This protocol requires much more memory from the MK. Further it will be clear why. This protocol can be divided into three parts: the initialization of the decoder, the passage through the label and the status of the decoder.
Initialization
The program sends the following text to the decoder "?; 0; 0; 0;" and return the corset (clcf) bytes - (0x0D, 0x0A). Nothing more interesting in the decoder program does not send. Therefore, when at the input we have a baytik 0x0A and set the initialization flag (why we need the flags will be described below).
 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; } } } 
But in the main loop we initialize:
  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; } 
I did not find a complete description of the protocol, I found only a partial one. An entry to the COM port will be sent as follows:
01 24 09 31 30 30 09 37 09 30 09 31 09 31 09 F8 F9 0D 0A
Here the decoder number is 100. The recording always starts with a byte (0x01). Next comes the $ sign. Between each value is a Tab (0x09) sign. All entries end with a carriage return (0x0D 0x0A). Well, do not forget that you need to reset the time and sequence_number. sequence_number is the number of the parcel, incremented with each parcel.
Decoder status
Every 4 seconds we need to send the status. Package type:
# 100 980 0 x06BA
The bytes are like this:
01 23 09 31 30 30 09 31 30 32 38 09 30 09 78 39 42 39 46 0D 0A
Here you can see the decoder number, sequence_number, which is zero (I don’t know why) and the checksum. The checksum is considered in the putchar procedure:
 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") } 
The crcTable table is calculated in advance; it is here:
Hidden text
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,369,65439 , 61374,57309,53244,48923,44858,40793,36728,37256,33193,45514,41451,53516,49453,61774,57711,42424,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,54 572,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};
The code for the package itself looks like this:
  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; } 
Here you can see how the sequece_number is incremented. Initially, 0x01 is sent, then crcwork = 0xFFFF is reset. Helmet part of the parcel. At the same time, a checksum is considered. The next action helmet is our checksum and carriage return. And so every four seconds:
 interrupt [TIM2_OVF] void timer2_ovf_isr(void) { static char times; if (++times==4) { TimeToSendAmbStatus=1; times=0; } GlobalS++; LED=!LED; } 
Transponder number and time sending
This is where the difficulties begin. Transforming the time and the number of the transponder and sending it to the buffer for USART takes a lot of time (as it was about 300ms for me) and that is why I do not send anything directly in the interrupts, but leave this case to the main loop. In order for everything to work, you need to use an intermediate buffer and some variables:
 #define TranspondersCountMax 20 unsigned long int Transponders[TranspondersCountMax]; unsigned long int TranspondersTimeS[TranspondersCountMax]; unsigned int TranspondersTimeMS[TranspondersCountMax]; char TransponderNeedToSend, TranspondersCountInt, TranspondersCountMain; 
The buffer is cyclical by the way. And this is how we will fill the buffer:
 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; } } 
And in the main loop:
 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--; } 
The comments indicate the same thing, only easier to understand, because the line "@ \ t% i \ t% i \ t% 07lu \ t% u.% 03u \ t130 \ t119 \ t2 \ t" will break the leg. Sending one, counting checksum is the same as sending decoder status.
@ 100 10 1234567 37.589 130 119 2 xEB94
As you can see, everything is very simple.

A few words about the counting programs

There are many AMB serif-compatible programs, but I prefer to use RCM Begginers. It is free, in Russian and very simple. You can download it here . In Russia, RCM Ultimate (which is cooler and more expensive) is extremely popular among modellers. Both AMB20 and AMBRc are configured as: I didn’t really like the rest of the programs.

Instead of conclusion

Source codes for CodeVision Avr and a scheme for the proteus can be found here Video work:

')

Source: https://habr.com/ru/post/196514/


All Articles