where : ibrtses embedded
AVR Pascal (AVRCo) UART Interrupt
Despite AVRCo offering the UART as library function, without the source
it sooner or later leads to problems, eg when longer buffers are considered,
and is therefore to be avoided.
Here, a replacement with source is provided. Contrary to the provided library
UART, some more decision have to be made. Here, I found it convenient to have the whole protocol in the UART_Rx_Interrupt. The protocol is an RS422 master-slave protocol, that
has to shut the sender off after the message being sent, and therefor allows for multiple devices on one bus.
; protocol :
;
; index 0 1 2 3 4 5
; [SYN] STX LEN SRC DST MSG [DATA] CRChi CRClo
; 0x16 0x02 .. .. .. ..
;
; LEN = lenght including STX ... CRCHi
;
; CRC:=crc(STX..data);
; CRC poly:=2^16+2^15+2^2+1
the main
In the main, the ADC is handled in the procedure COMLoop.
{ Type Declarations }
type
ComBuffer = Array[0..50] of byte; //or whatever
VAR
ComState:byte;
rxptr:byte; // the pointers and presets
txptr:byte; // the pointers and presets
rxlen:byte; // the pointers and presets
txlen:byte; // the pointers and presets
rxbyte:byte; // temporary interrupt storage
DeviceID:byte; // from EEPROM
tx_en[@portd,2]:bit; // enable the transceiver
// buffers
RxBuf:ComBuffer;
TxBuf:ComBuffer;
begin // the main
..
SetupVariables;
..
EnableInts;
..
loop
COMLoop;
..
..
endloop;
end; // main
The statemachine being conscious about the state is the Var Comstate:byte,
above.
; rx : COMState
; state 0 := wait for STX
; state 1 := get len
; state 2 := count down
; state F := got message
;
.EQU STX =0x02
.EQU SYN =0x16
The commandset and its excution
The concept of the protocol is that each command has a reply with
the same command code. The content of the reply may be empty, in
this case the command is just sent back with SRC and DST swapped.
// 0 1 2 3 4 5
// STX LEN SRC DST MSG [Data] CRCHi CRCLo
//Len includes STX & CRC
procedure COMLoop; // does something when comstate = $0F
begin
if (ComState=$0F) then
RxCRCMessage(rxptr-2); // CRC is updated
if (CRChigh=RxBuf[rxptr-2])and
(CRCLow=RxBuf[rxptr-1]) then // crc ok.
if (RxBuf[3]=DeviceID)or(RxBuf[3]=$FF) then
src:=RxBuf[2];
dst:=RxBuf[3];
cmd:=RxBuf[4];
case cmd of
$00:// Echo
CopyRxTx;
AddCrc;
SetupTx;|
$01:// read measurements
txlen:=measurebytes+9;
SetupTxFrame;
CopyBlock(@Pot,@TxBuf+6,word(MeasureBytes));
AddCrc;
SetupTx;|
$02://read config
txlen:=CurrentConfigSize+9;
SetupTxFrame;
CopyBlock(@ConfigSize,@TxBuf+6,word(currentconfigsize));
AddCrc;
SetupTx;|
$03://read settings
ErrorCodeb:=ErrorCode;
txlen:=SettingBytes+8;
SetupTxFrame;
CopyBlock(@SetCurrentb,@TxBuf+6,word(SettingBytes));
AddCrc;
SetupTx;|
$04://ramp current right
rampright;
CopyRxTx;
AddCrc;
SetupTx;|
$05://ramp current left
rampleft;
CopyRxTx;
AddCrc;
SetupTx;|
$06://read math
txlen:=MathBytes+9;
SetupTxFrame;
CopyBlock(@DeltaValue,@TxBuf+6,word(MathBytes)); // no
AddCrc;
SetupTx;|
$08://read calibration
txlen:=CalibrationSize+8;
SetupTxFrame;
CopyBlock(@Calib1,@TxBuf+6,word(CalibrationSize));
AddCrc;
SetupTx;|
$10://current zero
setcurrentzero2612;
CopyRxTx;
AddCrc;
SetupTx;|
$11://write current left
tb1:=RxBuf[5];
tb2:=RxBuf[6];
SetCurrent:=til;
if SetCurrent<0 then SetCurrent:=-SetCurrent; endif;
SetCurrentLeft;
CopyRxTx;
AddCrc;
SetupTx;|
..............
else
Endcase;
endif; // ID checked
endif; // crc checked
ComState:=0; // SetupRx
RxPtr:=0;
endif;
end;
Messages and CRC
CRCs are used to verify the content of the messages and are handled here.
VAR
CRC:Word;
CRCLow[@CRC]:byte;
CRCHigh[@CRC+1]:byte;
procedure CRCByte(b:byte); // crc for one byte
var i:byte;
begin
for i:= 0 to 7 do
if (((b and $01)XOR(CRClow and $01))<>0) then
CRC:=CRC shr 1;
CRC:=CRC XOR $A001;
else
CRC:=CRC shr 1;
endif;
b:=b shr 1;
endfor;
end;
procedure RxCRCMessage(len:byte); // updates global CRC variable
var u:byte;
begin
CRC:=0;
for u:=0 to len-1 do
CRCByte(RxBuf[u]);
endfor;
end;
procedure TxCRCMessage(len:byte); // updates global CRC variable
var u:byte;
begin
CRC:=0;
for u:=1 to len do
CRCByte(TxBuf[u]);
endfor;
end;
procedure AddCrc; // CRC to the complete TxMessage, incl TxLen=#bytes
var i:byte;
begin
i:=txlen-3; // not over SYN, CRC
TxCRCMessage(i); //
TxBuf[txlen-2]:=CrcHigh;
TxBuf[TxLen-1]:=CRCLow;
end;
procedure SetupTx;
begin
txptr:=0;
tx_en:=true;
ASM;
sbi UCSRB,5 ;UDRIE
ENDASM;
end;
procedure CopyRxTx;
var i,j:byte;
begin
j:=Rxbuf[1]+1;
TxLen:=j;
TxBuf[0]:=SYN;
TxBuf[1]:=STX;
TxBuf[2]:=j-1; //len
TxBuf[3]:=DST;
TxBuf[4]:=SRC;
TxBuf[5]:=CMD;
for i:=6 to j-1 do
TxBuf[i]:=RxBuf[i-1];
endfor;
end;
procedure SetupTxFrame; // txlen is set = #bytes to tx, incl SYN.
begin
TxBuf[0]:=SYN;
TxBuf[1]:=STX;
TxBuf[2]:=txlen-1;
Txbuf[3]:=DST;
TxBuf[4]:=SRC;
txBuf[5]:=CMD;
end;
The UART interrupt
The UART_Rx is handled with a statemachine incoporating the protocol,
described above. The UART_Tx uses the UDRE interrupt to fetch the next
byte until the is no more. Then the TxRdy interrupt is enable to await the
last bit of the last byte being shifted out. Note that this approach tends
to disable the transceiver before the stopbit of this last byte was sent.
Since I never experienced problems, I was never forced to change it.
A timer could be used await the stopbit being shifted.
Interrupt RxRdy; // RxRdy of Serial 1
begin
rxbyte:=UDR1;
if (rxptr=buflen)then Comstate:=$0F;
else
case ComState of
0: if (rxbyte=STX) then // wait for STX
Comstate:=1;
RxBuf[0]:=rxbyte;
rxptr:=1;
endif;
if (rxbyte=SYN) then
rxptr:=0;
endif;|
1: rxlen:=rxbyte; // take len
ComState:=2;
RxBuf[1]:=rxbyte;
inc(rxptr); |
2: RxBuf[rxptr]:=rxbyte;
inc(rxptr);
if (rxptr=rxlen) then
Comstate:=$0F;
endif; |
else Comstate:=$0F;
endcase;
endif; // buffer full
end;
interrupt UDRE; // tx data register empty
begin
if (txptr=txlen) then
ASM;
cbi UCSRB,5 ;UDRIE1
sbi UCSRB,6 ;TXCIE1
ENDASM;
else
UDR1:=TxBuf[txptr];
inc(txptr);
endif;
end;
interrupt TxRdy; // tx complete
begin
tx_en:=false; //RS485 line deactivate
ASM;
cbi UCSRB,6 ;txcie1
sbi UCSRA,6 ;txc1
ENDASM;
end;
Setup the UART
Setting up the UART is straight forward. First the variables, then the registers.
Enabling the interrupts happens later in the main.
procedure InitUART;
begin
CRC:=0;
Comstate:=0;
rxptr:=0;
rxlen:=0;
txptr:=0;
txlen:=0;
// uart
UBRRL:=51; // 4800 baud @ 4MHz
UBRRH:=0;
UCSRC:=%10000110; // async, no parity, 1 stop, 8bit,
UCSRB:=%10011000; // rx enable, rxie, tx enable, 8bits only
UCSRA:=%00000000; // single speed, single processor
end;
Questions ?
Suggestions?
Feedback ?
sponsored links
AVR
embedded
home
last updated : 30.aug.04, or perhaps later
Copyright (99,2004) Ing.Büro R.Tschaggelar