HWSUART
Overview
Hardware-assisted software uart (hwsuart)
This module is an interrupt-driven 'software' uart that uses hardware peripheral functions to assist in the generation of the serial data timing and detection of the RX start bit. In addition, it provides an input RX buffer to hold incoming data allowing the module to be used for asyncronous reception (which is something most software uarts cannot do).
There are three implementations provided:
- hwsuart_T0CKI - Simplest, and supports most devices.
Uses TMR0 T0CKI pin for RX input. Half-duplex (cannot transmit and receive simultaneuosly)
- hwsuart_HD - Uses INT, TMR, and CCP peripherals.
RX input can be INT0, INT1, INT2, or IOCB4. Half-duplex (cannot transmit and receive simultaneuosly)
- hwsuart_FD - Uses INT, and two TMR/CCP peripherals for independant RX/TX timing.
RX input can be INT0, INT1, INT2, or IOCB4. Full-duplex (simultaneous transmit/receive supported)
The RX input must be connected to a pin that can generate an interrupt on a high- to-low transition in order to detect the beginning of the incoming START bit. The TX output can be assigned to any pin.
For all implementations the max allowable baudrate depends on the cpu clock frequency, but in general the half-duplex methods support up to 115K baud while full-duplex is limited to 57600.
Example 1 - simple echo test
program hwsuart_test // define device and clock device = 18F25K22 clock = 64 include "intosc.bas" include "setdigitalio.bas" // select library to use (T0CKI tmr0, HD half-duplex, or FD full-duplex) #option LIB_SEL = T0CKI #option HWSUART_DEBUG_PIN = PORTC.3 // debug output pin used for scope timing #if (LIB_SEL = T0CKI) #warning "using hwsuart_T0CKI tmr0 library" // RXD must be the T0CKI pin (on K22 T0CKI=RA4) #option HWSUART_TXD_PIN = PORTC.6 // TXD pin... use RC6_TX #option HWSUART_RXD_PIN = PORTA.4 // RXD pin (MUST be T0CKI) #option HWSUART_RX_BUFFER_SIZE = 4 #option HWSUART_TX_PACING = 10 // in usecs include "hwsuart_T0CKI.bas" #elseif (LIB_SEL = HD) #warning "using hwsuart_HD half-duplex library" #option HWSUART_TXD_PIN = PORTC.6 // TXD pin... use RC6_TX #option HWSUART_INT_SELECT = 0 // RXD pin 0=INT0, 1=INT1, 2=INT2, 4=IOCB4 #option HWSUART_TMR_SELECT = 1 // TMR #option HWSUART_CCP_SELECT = 1 // CCP #option HWSUART_RX_BUFFER_SIZE = 4 include "hwsuart_HD.bas" #elseif (LIB_SEL = FD) #warning "using hwsuart_FD full-duplex library" #option HWSUART_TXD_PIN = PORTC.6 // TXD pin... use RC6_TX #option HWSUART_INT_SELECT = 0 // RXD pin 0=INT0, 1=INT1, 2=INT2, 4=IOCB4 #option HWSUART_TMR_SELECT = 1 // TMR #option HWSUART_CCP_SELECT = 1 // CCP #option HWSUART_TX_TMR_SELECT = 3 // TX TMR #option HWSUART_TX_CCP_SELECT = 2 // TX CCP #option HWSUART_RX_BUFFER_SIZE = 64 include "hwsuart_FD.bas" #endif const CR = 13, LF = 10 public dim rxc as byte // last received char public dim baud as longword // baud rate main: setalldigital() delayms(10) // init the hwsuart baud = 115200 hwsuart.initialize(baud) // test tx timing (infinite loop sending 'U' $55) #if (false) hwsuart.check_bit_timing() #endif // show we're alive hwsuart.write_byte(">") // echo test while (true) // check to see if there's a byte available to read if (hwsuart.data_available()) then rxc = hwsuart.read_byte() // get byte from the rx buffer hwsuart.write_byte(rxc) // and echo it if (rxc = CR) then // if CR then send a LF to go along with it hwsuart.write_byte(LF) endif endif end while end program
Example 2 - RX input buffer test
program hwsuart_test // define device and clock device = 18F25K22 clock = 64 include "intosc.bas" include "setdigitalio.bas" // select library to use (T0CKI tmr0, HD half-duplex, or FD full-duplex) #option LIB_SEL = T0CKI #option HWSUART_DEBUG_PIN = PORTC.3 // debug output pin used for scope timing #if (LIB_SEL = T0CKI) #warning "using hwsuart_T0CKI tmr0 library" // RXD must be the T0CKI pin (on K22 T0CKI=RA4) #option HWSUART_TXD_PIN = PORTC.6 // TXD pin... use RC6_TX #option HWSUART_RXD_PIN = PORTA.4 // RXD pin (MUST be T0CKI) #option HWSUART_RX_BUFFER_SIZE = 4 #option HWSUART_TX_PACING = 10 // in usecs include "hwsuart_T0CKI.bas" #elseif (LIB_SEL = HD) #warning "using hwsuart_HD half-duplex library" #option HWSUART_TXD_PIN = PORTC.6 // TXD pin... use RC6_TX #option HWSUART_INT_SELECT = 0 // RXD pin 0=INT0, 1=INT1, 2=INT2, 4=IOCB4 #option HWSUART_TMR_SELECT = 1 // TMR #option HWSUART_CCP_SELECT = 1 // CCP #option HWSUART_RX_BUFFER_SIZE = 4 include "hwsuart_HD.bas" #elseif (LIB_SEL = FD) #warning "using hwsuart_FD full-duplex library" #option HWSUART_TXD_PIN = PORTC.6 // TXD pin... use RC6_TX #option HWSUART_INT_SELECT = 0 // RXD pin 0=INT0, 1=INT1, 2=INT2, 4=IOCB4 #option HWSUART_TMR_SELECT = 1 // TMR #option HWSUART_CCP_SELECT = 1 // CCP #option HWSUART_TX_TMR_SELECT = 3 // TX TMR #option HWSUART_TX_CCP_SELECT = 2 // TX CCP #option HWSUART_RX_BUFFER_SIZE = 64 include "hwsuart_FD.bas" #endif const CR = 13, LF = 10 public dim rxc as byte // last received char public dim baud as longword // baud rate public dim numchars as byte // number of chars in the input buffer public dim maxchars as byte // max number of chars seen in the buffer public dim txchars as word // number of chars we've transmitted main: setalldigital() delayms(10) // init statistics maxchars = 0 txchars = 0 // init the hwsuart baud = 115200 hwsuart.initialize(baud) // set addtl pacing delay of one bit time (ie two stop bits) hwsuart.tx_pacing = hwsuart.bit_time() // high-level functions hwsuart.write("hwsuart echo test...", CR, LF) // low-level functions (non-blocking) while (true) // check to see if there's a byte available to read if (hwsuart.data_available()) then rxc = hwsuart.read_byte() // get byte from the rx buffer hwsuart.write_byte(rxc) // and echo it if (rxc = CR) then // if CR then send a LF to go along with it hwsuart.write_byte(LF) endif txchars = txchars + 1 // count of chars we've transmitted endif // test rx buffering // baudrate and RX_BUFFER_SIZE determine how long we can wait here delayms(1) // delay while chars come in // see if we can keep up with the transmitter sending chars... // record max number of chars in rx buffer numchars = hwsuart.buffer_count() if (numchars > maxchars) then maxchars = numchars endif if (hwsuart.is_full()) then // buffer is full... while (hwsuart.data_available()) // empty (and echo) it rxc = hwsuart.read_byte() hwsuart.write_byte(rxc) end while hwsuart.write_byte(CR) hwsuart.write_byte(LF) endif if (hwsuart.overrun()) then hwsuart.flush_input() endif end while end program
Example 3 - high-level function examples
program hwsuart_test // define device and clock device = 18F25K22 clock = 64 include "intosc.bas" include "setdigitalio.bas" // select library to use (T0CKI tmr0, HD half-duplex, or FD full-duplex) #option LIB_SEL = FD #option HWSUART_DEBUG_PIN = PORTC.3 // debug output pin used for scope timing #if (LIB_SEL = T0CKI) #warning "using hwsuart_T0CKI tmr0 library" // RXD must be the T0CKI pin (on K22 T0CKI=RA4) #option HWSUART_TXD_PIN = PORTC.6 // TXD pin... use RC6_TX #option HWSUART_RXD_PIN = PORTA.4 // RXD pin (MUST be T0CKI) #option HWSUART_RX_BUFFER_SIZE = 4 #option HWSUART_TX_CHAR_PACING = 10 // in usecs, 0-255 include "hwsuart_T0CKI.bas" #elseif (LIB_SEL = HD) #warning "using hwsuart_HD half-duplex library" #option HWSUART_TXD_PIN = PORTC.6 // TXD pin... use RC6_TX #option HWSUART_INT_SELECT = 0 // RXD pin 0=INT0, 1=INT1, 2=INT2, 4=IOCB4 #option HWSUART_TMR_SELECT = 1 // TMR #option HWSUART_CCP_SELECT = 1 // CCP #option HWSUART_RX_BUFFER_SIZE = 4 include "hwsuart_HD.bas" #elseif (LIB_SEL = FD) #warning "using hwsuart_FD full-duplex library" #option HWSUART_TXD_PIN = PORTC.6 // TXD pin... use RC6_TX #option HWSUART_INT_SELECT = 0 // RXD pin 0=INT0, 1=INT1, 2=INT2, 4=IOCB4 #option HWSUART_TMR_SELECT = 1 // TMR #option HWSUART_CCP_SELECT = 1 // CCP #option HWSUART_TX_TMR_SELECT = 3 // TX TMR #option HWSUART_TX_CCP_SELECT = 2 // TX CCP #option HWSUART_RX_BUFFER_SIZE = 64 include "hwsuart_FD.bas" #endif const CR = 13, LF = 10 public dim baud as longword // baud rate public dim rxc as byte // last received char public dim s as string // define a string/buffer union structure SBUF_T buf(24) as byte union s as string union end structure public dim sb as SBUF_T main: setalldigital() delayms(10) // init the hwsuart #if (LIB_SEL = FD) baud = 57600 #else baud = 115200 #endif hwsuart.initialize(baud) // high-level functions hwsuart.write(CR, LF) hwsuart.write("hwsuart function test...", CR, LF) // read and echo string // default length of a string is 23 chars + null hwsuart.ReadTerminator = CR hwsuart.write("read/write string (23 chars max + CR): ") hwsuart.read(s) hwsuart.write(s, CR, LF) // change read terminator to ':' char hwsuart.ReadTerminator = ":" hwsuart.write("read string ':' term: ") hwsuart.read(s) hwsuart.write(s, CR, LF) // check data available with timeout hwsuart.flush_input() hwsuart.write("data available timeout (5 sec): ") if (DataAvailableTimeout(5000)) then hwsuart.write("ok") else hwsuart.write("**timeout**") endif hwsuart.write(CR, LF) // wait up to 5 secs for "1" // discards all chars hwsuart.flush_input() hwsuart.write("waitfor '1' timeout (5 sec): ") if (WaitForTimeout(byte("1"), 5000)) then hwsuart.write("ok") else hwsuart.write("**timeout**") endif hwsuart.write(CR, LF) // waitfor 10 bytes hwsuart.flush_input() hwsuart.write("waitfor 10 chars: ") WaitForCount(sb.buf, 10) hwsuart.write("ok ") sb.buf(10) = 0 hwsuart.write(sb.s, CR, LF) hwsuart.write("echo test: ") while(true) // check to see if there's a byte available to read if (hwsuart.data_available()) then rxc = hwsuart.read_byte() // get byte from the rx buffer hwsuart.write_byte(rxc) // and echo it if (rxc = CR) then // if CR then send a LF to go along with it hwsuart.write_byte(LF) endif endif end while end program
Example 4 - Using PPS to assign RX input (18FxxK40)
program hwsuart_test // define device and clock device = 18F27K40 clock = 64 include "intosc.bas" include "setdigitalio.bas" // for the K40 we're going to use PPS to assign T0CKI RXD pin to PORTB.0 include "pps.bas" #option HWSUART_TXD_PIN = PORTC.6 // TXD pin... use RC6_TX #option HWSUART_RXD_PIN = PORTB.0 // RXD pin (MUST be T0CKI, default is PORTA.4) #option HWSUART_RX_BUFFER_SIZE = 4 #option HWSUART_TX_CHAR_PACING = 10 // in usecs, 0-255 include "hwsuart_T0CKI.bas" const CR = 13, LF = 10 public dim rxc as byte // last received char public dim baud as longword // baud rate main: setalldigital() // assign T0CKI input to RB0 pps.assign_input(T0CKIPPS, PPS_PORTB, 0) delayms(10) // init the hwsuart baud = 115200 hwsuart.initialize(baud) // high-level functions hwsuart.write("hwsuart echo test...", CR, LF) // low-level functions (non-blocking) while (true) // check to see if there's a byte available to read if (hwsuart.data_available()) then rxc = hwsuart.read_byte() // get byte from the rx buffer hwsuart.write_byte(rxc) // and echo it if (rxc = CR) then // if CR then send a LF to go along with it hwsuart.write_byte(LF) endif endif end while end program