Description
The Propeller 2 can manage multiple UARTs simultaneously and be an aggreator of many serial inputs. This driver is useful for interfacing with multiple serial devices (GPS, sensors, etc) functioning in parallel. This application uses smart pins and only one of eight cogs in the Propeller 2.
This example shows how 8 full-duplex UARTs may be configured on the Propeller 2 using 16 I/Os, on any pins 0 to 63. These UARTs may be configured in any combination of transmitters and receivers at any baud. The code provided in these examples runs on PNut, Propeller Tool for Windows, and FlexProp.
The terminal displays above are showing characters received from serial pin 63 and sending-receiving it via pins 3-2-5-4-7-6-9-8-11-10-13-12-15-14 and finally back to pin 63, with Propeller Serial Terminal and FlexProp Terminal, respectively.
Three demonstrations are provided in the zip code below:
- One (1) full-duplex serial port (UART) using (2) I/O (mpx_demo2.spin2)
- Two (2) full-duplex serial ports (UARTs) using (4) I/O (mpx_demo4.spin2)
- Eight (8) full-duplex serial ports (UARTs) using (16) I/O (mpx_demo16.spin2)
It is possible to modify the driver to support more than 16 I/Os as the P2 could configure all 64 pins to be used as serial UARTs.
Starting the Driver
The following instruction starts a new cog with the PASM driver code as follows:
cog := mpxcog.start(@port_control) ' start multiport serial pasm driver in new cog
@port_control is the pointer to the port control and parameter buffers. To stop the driver cog, execute the following instruction:
cogstop(cog - 1) ' stop the driver
The driver continuously scans the port_control buffer to determine which of the 16 possible pins/ports are running UARTs. For those which are, the driver checks to see if it is a transmitter pin/port and if so checks the relevant buffer for a character. If a character is in the buffer it will transmit the character. If the pin/port is a receiver, the driver will check to see if a character is available from the smart pin, and if so it will read it and put it in the relevant buffer.
The smart-pins are started/stopped using the start/openport/closeport methods.
Configuring the Pins/Ports
There are two methods (calls) that can be used to configure the pins/ports. The first configures a single full-duplex port:
pub start(p_portctl, rxxport, rxpin, rxmode, rxbaud, p_rxstart, p_rxend, txxport, txpin, txmode, txbaud, p_txstart, p_txend)
'' Start simple serial coms on rxpin and txpin (can be individually configured using openport)
'' -- p_portctl. hub addr of port_control[0-15] followed by port_params[16]
'' -- rxport.... receive port[n] (0-15) (-1 if not used)
'' -- rxpin..... receive pin (-1 if not used)
'' -- rxmode.... %0xx1 = invert rx (same for txmode)
'' %0x1x = invert tx
'' %01xx = open-drain/open-source tx
'' -- rxbaud.... receive baud
'' -- p_rxstart. hub address of start of the receive buffer
'' -- p_rxend... hub address of end+1 of the receive buffer
'' -- txport.... transmit port[n] (0-15) (-1 if not used)
'' -- txpin..... transmit pin (-1 if not used)
'' -- txmode.... %0xx1 = invert rx (same for txmode)
'' %0x1x = invert tx
'' %01xx = open-drain/open-source tx
'' -- txbaud.... transmit baud
'' -- p_txstart. hub address of start of the transmit buffer
'' -- p_txend... hub address of end+1 of the transmit buffer
The second method (call) configures just a single port at a time. A transmit pin/port and a receive pin/port is possible per object:
pub openport(p_portctl, xport, xpin, xdirn, xmode, xbaud, p_xstart, p_xend) : status | spmode, baudcfg
The closeport method (call) closes the pin/port:
pub closeport(p_portctl, xport, xpin)
Methods (calls) Provided
There are additional configuration methods (calls):
pub setStripLF(n) ' 0=OFF, 1=ON optionally strip <lf> when transmitting characters
pub setEcho(n) ' 0=OFF, 1=ON optionally echo received characters to the transmitter
The following are the basic methods (calls) for the transmitter pin/port:
pub txChr(chr) | n, p, head, new '* xmit a character
pub txStr(p_str) '* xmit a formatted string
pub txStrCR(p_str) '* xmit a formatted string followed by <cr><lf>
pub txCR() '* xmit <cr><lf>
pub txASCII(chr) '* xmit a character (converts $00-$1F & $7F-$FF to ".")
pub txDump(addr1, addr2) | addr, addrend, i, j, mask '* dumps hub memory as lines of 16 bytes + ASCII
pub txn(chr, n) '* xmit a character 'n' times
pub txFlush() | p ' wait until the xmit buffer and uart are empty
'* = above methods optionally strip <lf>
The following are the basic methods (calls) for the receiver pin/port:
pub rxChr() : chr | p, head, tail, new '* recv a character
pub rxStr(p_str) | addr, chr '* recv a 0 terminated string (<cr> terminates entry)
pub rxCheck() : chr | p ' checks rx buffer and returns -1 (True) if no chars available, else returns a char
PUB rxPeek() : stat | p ' checks rx buffer and returns -1 (True) if no chars available, else 0 (False)
pub rxAvail() : count | p ' checks rx buffer and returns the number of characters in the buffer, 0 = none
pub rxTime(ms) : chr | mstix, t '* waist up to ms for a character, returns -1 (True) if no chars available, else returns a char
pub rxTix(tix) : chr | t '* waits up to clock-ticks for a char, returns -1 (True) if no chars available, else returns a char
pub rxFlush() ' flushes (clears) the rx buffer
'* = above methods optionally echo the received char to the transmitter (if enabled)
In addition to the above transmit methods, the following unformatted string methods (calls) are provided:
pub str(p_str) '* xmit an un-formatted string
pub substr(p_str, len) | chr '* xmit an un-formatted string with a maximum length
pub padstr(p_str, width, pad) '* xmit an un-formatted string of 'width' characters padded using the 'pad' character
' -- width: positive is right alignment, negative is left alignment
'* = above methods optionally strip <lf>
In addition to the above transmit methods, the following formatted string methods (calls) are provided:
pub fstr0(p_str) '* xmit a formatted string
pub fstr1(p_str, arg1) '* xmit a formatted string with 1 variable
pub fstr2(p_str, arg1, arg2) '* xmit a formatted string with 2 variables
pub fstr3(p_str, arg1, arg2, arg3) '* xmit a formatted string with 3 variables
pub fstr4(p_str, arg1, arg2, arg3, arg4) '* xmit a formatted string with 4 variables
pub fstr5(p_str, arg1, arg2, arg3, arg4, arg5) '* xmit a formatted string with 5 variables
pub fstr6(p_str, arg1, arg2, arg3, arg4, arg5, arg6) '* xmit a formatted string with 6 variables
pub format(p_str, p_args) | idx, c, asc, field, digits '* xmit a formatted string with n variables
'* = above methods optionally strip <lf>
In addition to the above transmit methods, the following methods (calls) to convert values are provided:
' xmit 'value' converted to a number using...
pub fmt_number(value, base, digits, width, pad) ' -- decimal/hex/octal/quarternary/binary
pub dec(value) ' -- decimal
pub fdec(value, digits) ' -- decimal and...
pub jdec(value, digits, width, pad) ' -- decimal and...
pub dpdec(value, dp) ' -- decimal and...
pub jdpdec(value, dp, width, pad) ' -- decimal and...
pub hex(value) ' -- hex
pub fhex(value, digits) ' -- hex and...
pub jhex(value, digits, width, pad) ' -- hex and...
pub oct(value) ' -- octal
pub foct(value, digits) ' -- octal and...
pub joct(value, digits, width, pad) ' -- octal and...
pub qrt(value) ' -- quarternary
pub fqrt(value, digits) ' -- quarternary and...
pub jqrt(value, digits, width, pad) ' -- quarternary and...
pub bin(value) ' -- binary
pub fbin(value, digits) ' -- binary and...
pub jbin(value, digits, width, pad) ' -- binary and...
' where... digits: number of digits
' dp: number of decimals
' width: positive is right alignment, negative is left alignment
' pad: 'pad' character
Buffers (in hub memory)
The calling program must provide the following contiguous buffers in hub memory:
port_control byte 0[16] '\ control (always 16 ports) %atpppppp where...
'| -- a: 1 = port active, t: 1 = tx, 0 = rx, pppppp: = port pin
port_params long 0[4*MAX_PORTS] '| max (16 ports) of 4 longs... (note: (no need to fill/reserve unused ports))
'/ -- port[n]: p_head, p_tail, p_start, p_end
In addition to the above buffers, the following buffer(s) must be provided in hub memory for each pin/port:
byte ??BUF?[??BUF?_SIZE] ' buffer of desired length, one for each pin/port (maximum of 16)
Tools and Operating System
- PNut_v35c.exe for Windows
- Propeller Tool for Windows
- FlexProp for Windows, Mac, Linux
Document Author
- Ray Rodrick (Cluso99)
Source Code Author
- Ray Rodrick (Cluso99)
- Jon McPhalen