Buffered UART Routines

Download pre-release library modules and new examples to use with Astrobe for LPC2000. Forum members can also upload their own source code examples.
Post Reply
pompey
Posts: 3
Joined: Tue Jan 04, 2011 12:44 pm

Buffered UART Routines

Post by pompey » Thu Mar 24, 2011 10:06 pm

I need to access UART0 and UART1 with buffered IO on both Input and Output.

To save reinventing the wheel I figured I'd ask here first if anyone has such a set of routines in Oberon07, or a link to a source? Google came up empty.

Failing that would anyone like to collaborate in mapping the standard C routines into Oberon? I'm a hardware guy, and as a newbie to Oberon I'm finding the task more difficult than anticipated :shock:

Alexander Shiryaev
Posts: 12
Joined: Mon Apr 04, 2011 7:20 pm
Location: Russia
Contact:

Re: Buffered UART Routines

Post by Alexander Shiryaev » Tue Apr 05, 2011 7:09 am

What is purpose to use UARTs?

I can offer implementation of messages exchange via UARTs, with buffering, on interrupts.

pompey
Posts: 3
Joined: Tue Jan 04, 2011 12:44 pm

Re: Buffered UART Routines

Post by pompey » Tue Apr 05, 2011 10:38 pm

Thanks for the offer. Do you have a link to the files? I'm not sure if they can be uploaded to this site...

I use UART0 to communicate with the user terminal, and UART1 talks to a RF telemetry transponder. Both need to be buffered and interrupt driven. My controller is LPC2103 based. This is not a commercial project.

Alexander Shiryaev
Posts: 12
Joined: Mon Apr 04, 2011 7:20 pm
Location: Russia
Contact:

Re: Buffered UART Routines

Post by Alexander Shiryaev » Wed Apr 06, 2011 8:31 pm

Code: Select all

DEFINITION MODULE Port0;

	PROCEDURE Init* (baud, parity: INTEGER);

	PROCEDURE Write* (adr: INTEGER; n: INTEGER; VAR done: INTEGER);

	PROCEDURE Read* (adr: INTEGER; n: INTEGER; VAR done: INTEGER);

END Port0.

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.

Very simple test, one direction only:

Code: Select all

MODULE Port0Test1;

	IMPORT Main, SYSTEM, Port0;

	PROCEDURE Do;
		VAR done: INTEGER;
			a: ARRAY 32 OF CHAR;
			i: INTEGER;
	BEGIN
		i := 0;
		WHILE i < LEN(a) - 1 DO
			a[i] := CHR(ORD('0') + i MOD 10);
			INC(i)
		END;
		a[i] := CHR(10);

		Port0.Init(38400, Port0.parityNone);
		i := 0;
		REPEAT
			Port0.Write(SYSTEM.ADR(a) + i, LEN(a) - i, done);
			i := (i + done) MOD LEN(a)
		UNTIL FALSE
	END Do;

BEGIN
	Do
END Port0Test1.
Echo:

Code: Select all

MODULE Port0Test2;

	IMPORT Main, SYSTEM, Port0;

	VAR
		a: ARRAY 256 OF CHAR;
		av, done, wr: INTEGER;

BEGIN
	Port0.Init(38400, Port0.parityNone);
	REPEAT
		Port0.Read(SYSTEM.ADR(a), LEN(a), av);
		done := 0;
		WHILE av > 0 DO
			Port0.Write(SYSTEM.ADR(a) + done, av, wr);
			done := done + wr;
			av := av - wr
		END
	UNTIL FALSE
END Port0Test2.
Last edited by Alexander Shiryaev on Fri Jul 25, 2014 11:05 am, edited 6 times in total.

Oberoid
Posts: 18
Joined: Fri Jan 28, 2011 8:57 pm
Location: Amsterdam, Netherlands

Re: Buffered UART Routines

Post by Oberoid » Sat Jun 25, 2011 10:35 pm

Hi Alexander,

i have a question about the following section of your code:

Code: Select all

(* CRITICAL *)
          PROCEDURE* Available (): INTEGER;
             VAR res: INTEGER;
          BEGIN
             SYSTEM.PUT(LPC.U0IER, 02H); (* disable RX interrupts (TX enabled) *)
                res := inBufLen - inFree;
             SYSTEM.PUT(LPC.U0IER, 03H); (* enable RX interrupts (TX enabled) *)
             RETURN res
          END Available;
Your procedure resembles the one Niklaus Wirth uses in V24.Mod ["Project oberon" - section 9.2. ]
However, Wirth does not disable the RX interrupts before reading the buffer.
I wonder if it is necessary to stop RX interrupts. Even when an interrupt occurs while accessing the buffer, just another item is added . Not loading incoming data in the buffer can lead to data loss. However I do not have a complete insight how the LPC handles interrupts. I can imagine that you have genuine reasons for writing your code as it is .

Greetings,

Frans-Pieter Vonck

Alexander Shiryaev
Posts: 12
Joined: Mon Apr 04, 2011 7:20 pm
Location: Russia
Contact:

Re: Buffered UART Routines

Post by Alexander Shiryaev » Sun Jul 13, 2014 5:01 pm

For three years, I found some errors and corrected them. I updated my previous post.

Post Reply