unit uart; {UART-Routinen für ATmega16A}
{$mode objfpc}
{$O-} {erstmal nur zur Sicherheit: Optimizer aus}
{$H-} {keine Ansistrings}
{$R-} {RangeChecks = KZP -Cr}
{$Q-} {OverflowChecks = KZP -Co}
{$S-} {StackOverflow = KZP -Ct = macht ca. 1800d Bytes Unterschied}
{********************************} interface {*********************************}
type bool = boolean;
s255 = string[255];
{============================ Allgemeiner V24-Kram: ===========================}
{$IFDEF FPC_MCU_ATMEGA16} {wenn CPU = ATMega16: }
const UART_RXerrBits: byte = 0; {merkt evtl. Error-Bits aus UART_checkChar():
Bit 2: Frame Error;
Bit 1: Data Overrun;
Bit 0: Parity Error;
Wird in UART_checkChar() gesetzt bzw. geleert}
procedure UART_init(baudDiv: word); {init: }
function UART_checkChar(out c: char): bool; {get: }
function UART_readChar: char;
function UART_tryTX(c: char): bool; {put: }
procedure UART_sendChar(c: char);
procedure UART_sendByte(b: byte);
procedure UART_sendString(s: s255);
procedure UART_terminal1; {Terminal}
{$ENDIF}
{*****************************} implementation {*******************************}
{============================ Allgemeiner V24-Kram: ===========================}
{$IFDEF FPC_MCU_ATMEGA16} {wenn CPU = ATMega16: }
{Port D, Bit 1 = TxD (USART Output)
Port D, Bit 0 = RxD (USART Input)
Quelle: https://wiki.freepascal.org/AVR_Embedded_Tutorial_-_Entry_Lazarus_and_Arduino#Serial_input.2Foutput
Doku zum USART: "16_atmel-avr-atmega16a_datasheet.pdf"
- S.158..162 = USART Register Description}
{alle verwendeten Ports: }
var UART_BaudH: byte absolute UBRRH; {Baudraten-Register-High (4 Bits)}
UART_BaudL: byte absolute UBRRL; {Baudraten-Register-Low}
UART_A: byte absolute UCSRA; {USART Control and Status Register A}
UART_B: byte absolute UCSRB; {USART Control and Status Register B}
UART_C: byte absolute UCSRC; {USART Control and Status Register C}
UART_Data: byte absolute UDR; {USART I/O Data Register}
{für diverse Bits werden folgende const benutzt:
- RXEN = 4; // Receiver Enable
- TXEN = 3; // Transmitter Enable
- URSEL = 7; // USART Register Select
- RXC = 7; // USART Receive Complete
- UDRE = 5; // USART Data Register Empty
- UPE = 2; // Parity Error (maskiert 3 Error-Bits)}
procedure UART_init(baudDiv: word);
{initialisiert die V24 mit 8 Bits, Even parity, 1 Stop Bits;
I: baudDiv: Formel = CPU_Clock div(16 * BaudRate) - 1;
ex O: UART_RXerrBits;
Wurde für den ATMega16 entwickelt und bisher nur dort getestet!}
begin
UART_BaudH:=hi(baudDiv); {zuerst: set Baudrate-High und set Bit 'URSEL=0'}
UART_BaudL:=lo(baudDiv); {danach: set Baudrate-Low = start Prescaler}
UART_A:=0; {Bit7=0: read only bit;
6=0: nothing to do (USART Transmit Complete);
5=0: read only bit;
4=0: read only bit;
3=0: read only bit;
2=0: read only bit;
1=0: synchronous mode => use 0 /
asynchronous mode: 1=verdoppelt die Baudrate;
0=0: kein Multi-processor Communication Mode}
UART_B:=(1 shl RXEN) or (1 shl TXEN);
{Bit7=0: disable RX Complete Interrupt;
6=0: disable TX Complete Interrupt;
5=0: disable USART Data Register Empty Interrupt;
4=1: RXEN=enables the USART Receiver;
3=1: TXEN=enables the USART Transmitter;
2=0: wählt 5..8 Daten-Bits / 1=wählt 9 Datenbits;
1=0: read only bit;
0=0: setzt das 9. Bit im 9-Bit Datenbetrieb}
UART_C:=(1 shl URSEL) or $20 or $06;
{Bit7=1: URSEL=enables Register 'UART_C' statt 'UART_BaudH';
6=0: wählt Asynchronous Mode (erlaubt Pausen zw. den Bytes);
5+4=10: wählt Even Parity/ 00=None / 11=Odd;
3=0: wählt 1 Stop-Bit / 1=wählt 2 Stop-Bits;
2+1=11: wählt 8 Daten-Bits / 10=7 Daten-Bits;
0=0: clock polarity in synchronous mode}
UART_RXerrBits:=0; {reset Error-Bits für UART_checkChar()}
end; {UART_init}
function UART_checkChar(out c: char): bool;
{liefert nur dann ein empfangenes char, falls eins da ist; Falls ja, werden
evtl. RX-Errors in 'UART_RXerrBits' gemerkt}
begin
c:=#0;
if UART_A and (1 shl RXC) = 0 then exit(false); {wenn nix da}
UART_RXerrBits:=(UART_A shr UPE) and $07; {lese evtl. RX-Errors}
c:=char(UART_Data); {das Lesen hier löscht die 3 Error-Bits}
exit(true);
end;
function UART_readChar: char;
{wartet bis 1 char empfangen wurde; Evtl. RX-Errors werden in
'UART_RXerrBits' gemerkt}
var c: char;
begin
while not UART_checkChar(c) do begin end; {warte bis was da ist}
exit(c);
end;
function UART_tryTX(c: char): bool;
{versucht char 'c' zu senden, falls der TX-Buffer frei ist}
begin
if UART_A and (1 shl UDRE) = 0 then exit(false); {wenn TX-Buffer belegt ist}
UART_Data:=byte(c);
exit(true);
end;
procedure UART_sendChar(c: char);
{sendet char 'c' und wartet vorher, bis der TX-Buffer frei ist}
begin
while not UART_tryTX(c) do begin end; {warte bis TX-Buffer auch frei war}
end;
procedure UART_sendByte(b: byte);
{sendet Byte 'b' und wartet vorher, bis der TX-Buffer frei ist}
begin
UART_sendChar(char(b));
end;
procedure UART_sendString(s: s255);
var i: byte;
begin
for i:=1 to length(s) do UART_sendChar(s[i]);
end;
procedure UART_terminal1;
{wartet auf empfangene Bytes und sendet diese zurück}
const BaudR = 2400; {wanted Baudrate}
CPU_Clock = 1000000; {Default CPU clock frequency = 1 MHz}
BaudDiv = CPU_Clock div(16 * BaudR) - 1; {Teiler für die Baudrate}
var c: char;
ok: bool;
begin
UART_init(BaudDiv); {initialisiert die V24}
repeat ok:=UART_checkChar(c);
if ok then UART_sendChar(c);
until false;
end; {UART_terminal1}
{$ENDIF FPC_MCU_ATMEGA16}
{****************************** Initialisierung *******************************}
end.