Code: Select all
MODULE Port0;
(*
A. V. Shiryaev, 2009.12, 2011.04, 2012.06, 2014.06
LPC2378
UART0
ASSERTs: 100..105
Default (lowest) priority (LPC2378)
FIFOs enabled (RX trigger level = 0)
RX errors not detected
TODO:
change FIFO RX trigger level?
detect RX errors?
if send something immediatelly after Init, it will be lost (on bauds >= 115200 only?)
*)
IMPORT SYSTEM, Traps, LPC, LPC2378;
CONST
parityNone* = 0;
parityOdd* = 8;
parityEven* = 16 + 8;
(* buffer sizes *)
outBufLen = 256; (* 2^n *)
inBufLen = 256; (* 2^n *)
(* Interrupt Enable Register (IER) bits: *)
RBRIE = 0;
THREIE = 1;
RLSIE = 2;
ABEOIE = 8;
ABTOIE = 9;
(* LSR bits: *)
RDR = 0;
OE = 1;
PE = 2;
FE = 3;
BI = 4;
THRE = 5;
TEMT = 6;
RXFE = 7;
VAR
(* out *)
outBuf: ARRAY outBufLen OF CHAR;
outFree: INTEGER;
outW: INTEGER; (* writer *)
outR: INTEGER; (* reader *)
outBusy: BOOLEAN;
(* in *)
inBuf: ARRAY inBufLen OF CHAR;
inFree: INTEGER;
inW: INTEGER; (* writer *)
inR: INTEGER; (* reader *)
(* statistics/debug *)
inBytesReceived*, inBytesLost*: INTEGER;
PROCEDURE SendNext;
VAR x: CHAR;
PROCEDURE* Get (): CHAR;
VAR x: CHAR;
BEGIN
x := outBuf[outR];
outR := (outR + 1) MOD outBufLen;
INC(outFree)
RETURN x
END Get;
BEGIN
IF outFree < outBufLen THEN
outBusy := TRUE;
x := Get();
SYSTEM.PUT(LPC2378.U0THR, ORD(x))
ELSE
outBusy := FALSE
END
END SendNext;
(* This procedure may be called from RX interrupts handler only *)
PROCEDURE* ByteReceived (x: CHAR);
BEGIN
INC(inBytesReceived);
IF inFree > 0 THEN
DEC(inFree);
inBuf[inW] := x;
inW := (inW + 1) MOD inBufLen
ELSE
INC(inBytesLost)
END
END ByteReceived;
PROCEDURE InterruptsHandler [Traps.IRQ];
VAR x: SET; t, n, m: INTEGER;
BEGIN
(*
SYSTEM.GET(LPC.VICAddress, t);
ASSERT(t = SYSTEM.ADR(InterruptsHandler), 100);
*)
SYSTEM.GET(LPC.VICIRQStatus, x);
(* ASSERT(6 IN x, 101); *)
IF 6 IN x THEN
m := 1000;
SYSTEM.GET(LPC2378.U0IIR, t);
WHILE ~ODD(t) & (m > 0) DO
n := 1000;
SYSTEM.GET(LPC2378.U0LSR, x);
WHILE (x * {RDR,OE,PE,FE,BI,RXFE} # {}) & (n > 0) DO
IF RDR IN x THEN
SYSTEM.GET(LPC2378.U0RBR, t);
ByteReceived(CHR(t))
END;
DEC(n);
SYSTEM.GET(LPC2378.U0LSR, x)
END;
IF THRE IN x THEN SendNext END;
DEC(m);
SYSTEM.GET(LPC2378.U0IIR, t)
END
(* ELSE ASSERT(x = {}, 102) *) (* spurious interrupt? *) (* FIXME *)
END;
(* Update the VIC priority hardware *)
SYSTEM.PUT(LPC2378.VICAddress, 0)
END InterruptsHandler;
PROCEDURE Init* (baud, parity: INTEGER);
VAR x: SET; divisor: INTEGER;
PROCEDURE* Match (desiredBaud, matchPCLK, matchBaud, DL, FDR: INTEGER);
BEGIN
IF (LPC.PCLK = matchPCLK) & (desiredBaud = matchBaud) THEN
SYSTEM.PUT(LPC2378.U0DLM, DL DIV 256); SYSTEM.PUT(LPC2378.U0DLL, DL MOD 256);
SYSTEM.PUT(LPC2378.U0FDR, FDR)
END
END Match;
BEGIN
ASSERT(baud > 0, 103);
ASSERT((parity = parityNone) OR (parity = parityOdd) OR (parity = parityEven), 104);
(* disable interrupts *)
SYSTEM.PUT(LPC.VICIntEnClr, {6});
SYSTEM.PUT(LPC2378.U0IER, {});
(* out *)
outW := 0;
outR := 0;
outFree := outBufLen;
outBusy := FALSE;
(* in *)
inW := 0;
inFree := inBufLen;
inR := 0;
(* statistics *)
inBytesReceived := 0; inBytesLost := 0;
SYSTEM.GET(LPC2378.PCONP, x);
SYSTEM.PUT(LPC2378.PCONP, x - {3}); (* disable UART0 power *)
SYSTEM.GET(LPC2378.PCONP, x);
SYSTEM.PUT(LPC2378.PCONP, x + {3}); (* enable UART0 power *)
(* Enable RX and TX pins *)
SYSTEM.GET(LPC2378.PINSEL0, x);
SYSTEM.PUT(LPC2378.PINSEL0, x + {4,6} - {5,7}); (* P0.3: RX0, P0.2: TX0 *) (* LPC2378 *)
(*
SYSTEM.GET(LPC2378.PINMODE0, x);
SYSTEM.PUT(LPC2378.PINMODE0, x - {4,5,6,7}); (* P0.2, P0.3: (on-chip) pull-up resistors enabled *)
*)
(* Set DLAB *)
SYSTEM.PUT(LPC2378.U0LCR, {0,1,7}); (* 8-bit, 1 stop bit, parity none, disable break transmission, enable access to Divisor Latches *)
(* Actual baud rate = VPB clock / (16 * divisor) *)
divisor := (LPC.PCLK + baud * 8) DIV (baud * 16);
SYSTEM.PUT(LPC2378.U0DLM, divisor DIV 256); SYSTEM.PUT(LPC2378.U0DLL, divisor MOD 256);
SYSTEM.PUT(LPC2378.U0FDR, 16); (* divAddVal := 0; mulVal := 1 *)
(* Try to reduce error. This table generated automatically *)
Match(baud, 15000000, 1200, 625, 65);
Match(baud, 15000000, 2400, 217, 84);
Match(baud, 15000000, 4800, 179, 177);
Match(baud, 15000000, 9600, 71, 131);
Match(baud, 15000000, 19200, 38, 114);
Match(baud, 15000000, 38400, 19, 114);
Match(baud, 15000000, 57600, 12, 229);
Match(baud, 15000000, 115200, 6, 229);
Match(baud, 30000000, 1200, 875, 235);
Match(baud, 30000000, 2400, 625, 65);
Match(baud, 30000000, 4800, 217, 84);
Match(baud, 30000000, 9600, 179, 177);
Match(baud, 30000000, 19200, 71, 131);
Match(baud, 30000000, 38400, 38, 114);
Match(baud, 30000000, 57600, 24, 229);
Match(baud, 30000000, 115200, 12, 229);
Match(baud, 60000000, 2400, 875, 235);
Match(baud, 60000000, 4800, 625, 65);
Match(baud, 60000000, 9600, 217, 84);
Match(baud, 60000000, 19200, 179, 177);
Match(baud, 60000000, 38400, 71, 131);
Match(baud, 60000000, 57600, 47, 213);
Match(baud, 60000000, 115200, 24, 229);
Match(baud, 17971200, 19200, 36, 133);
Match(baud, 17971200, 38400, 18, 133);
Match(baud, 17971200, 57600, 12, 133);
Match(baud, 17971200, 115200, 6, 133);
Match(baud, 18000000, 1200, 500, 135);
Match(baud, 18000000, 2400, 250, 135);
Match(baud, 18000000, 4800, 125, 135);
Match(baud, 18000000, 9600, 74, 199);
Match(baud, 18000000, 19200, 37, 199);
Match(baud, 18000000, 38400, 23, 179);
Match(baud, 18000000, 57600, 11, 151);
Match(baud, 18000000, 115200, 8, 146);
Match(baud, 36000000, 2400, 500, 135);
Match(baud, 36000000, 4800, 250, 135);
Match(baud, 36000000, 9600, 125, 135);
Match(baud, 36000000, 19200, 74, 199);
Match(baud, 36000000, 38400, 37, 199);
Match(baud, 36000000, 57600, 23, 167);
Match(baud, 36000000, 115200, 11, 151);
Match(baud, 72000000, 4800, 500, 135);
Match(baud, 72000000, 9600, 250, 135);
Match(baud, 72000000, 19200, 125, 135);
Match(baud, 72000000, 38400, 74, 199);
Match(baud, 72000000, 57600, 71, 161);
Match(baud, 72000000, 115200, 23, 167);
(* Clear DLAB; 8-bit data; 1 stop bit; select parity *)
SYSTEM.PUT(LPC2378.U0LCR, 03H + parity); (* 8-bit, 1 stop bit, select parity, disable break transmission, disable access to Divisor Latches *)
SYSTEM.PUT(LPC2378.U0FCR, {0,1,2}); (* enable FIFOs, clear them, RX trigger level := 0 *)
SYSTEM.PUT(LPC2378.U0TER, {7}); (* TXEN *)
(* Configure the Vectored Interrupt Controller *)
(* Use IRQ not FIQ for UART0 *)
SYSTEM.GET(LPC.VICIntSelect, x);
SYSTEM.PUT(LPC.VICIntSelect, x - {6});
(* Install handler *)
SYSTEM.PUT(LPC2378.VICVectAddr6, SYSTEM.ADR(InterruptsHandler)); (* LPC2378: IRQ 6 only for UART0 *)
(* Leave default IRQ priority (LPC2378) *)
(* enable interrupts *)
SYSTEM.PUT(LPC2378.U0IER, {RBRIE,THREIE,RLSIE});
SYSTEM.PUT(LPC.VICIntEnable, {6})
END Init;
PROCEDURE Write* (adr: INTEGER; n: INTEGER; VAR done: INTEGER);
PROCEDURE* Get (VAR adr: INTEGER): CHAR;
VAR x: CHAR;
BEGIN
SYSTEM.GET(adr, x);
INC(adr)
RETURN x
END Get;
PROCEDURE* Put (x: CHAR);
BEGIN
outBuf[outW] := x;
outW := (outW + 1) MOD outBufLen
END Put;
BEGIN
ASSERT(n >= 0, 105);
done := outFree; (* outFree access is safe here, because it may be incremented only in interrupts handler *)
IF n < done THEN
done := n
END;
IF done > 0 THEN
n := done;
REPEAT
Put(Get(adr));
DEC(n)
UNTIL n = 0;
(* CRITICAL *)
SYSTEM.PUT(LPC.VICIntEnClr, {6});
outFree := outFree - done;
IF ~outBusy THEN
SendNext
END;
SYSTEM.PUT(LPC.VICIntEnable, {6})
END
END Write;
PROCEDURE Read* (adr: INTEGER; n: INTEGER; VAR done: INTEGER);
(* CRITICAL *)
PROCEDURE* Available (): INTEGER;
RETURN inBufLen - inFree
END Available;
(* CRITICAL *)
PROCEDURE* Free (toFree: INTEGER);
BEGIN
SYSTEM.PUT(LPC.VICIntEnClr, {6});
inFree := inFree + toFree;
SYSTEM.PUT(LPC.VICIntEnable, {6})
END Free;
PROCEDURE* Get (): CHAR;
VAR x: CHAR;
BEGIN
x := inBuf[inR];
inR := (inR + 1) MOD inBufLen
RETURN x
END Get;
PROCEDURE* Put (VAR adr: INTEGER; x: CHAR);
BEGIN
SYSTEM.PUT(adr, x);
INC(adr)
END Put;
BEGIN
done := Available();
IF n < done THEN
done := n
END;
IF done > 0 THEN
n := done;
REPEAT
Put(adr, Get());
DEC(n)
UNTIL n = 0;
Free(done)
END
END Read;
END Port0.