From 8aebe25c07185d51258dd306aee1123de4656baa Mon Sep 17 00:00:00 2001 From: jaakkoiot Date: Sat, 15 Oct 2022 15:17:53 +0300 Subject: [PATCH] Add serial & UART classes for Modbus use --- Modbus/inc/SerialPort.h | 20 ++++ Modbus/inc/Uart.h | 62 +++++++++++ Modbus/src/SerialPort.cpp | 50 +++++++++ Modbus/src/Uart.cpp | 227 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 359 insertions(+) create mode 100644 Modbus/inc/SerialPort.h create mode 100644 Modbus/inc/Uart.h create mode 100644 Modbus/src/SerialPort.cpp create mode 100644 Modbus/src/Uart.cpp diff --git a/Modbus/inc/SerialPort.h b/Modbus/inc/SerialPort.h new file mode 100644 index 0000000..6e89b6e --- /dev/null +++ b/Modbus/inc/SerialPort.h @@ -0,0 +1,20 @@ +#ifndef SERIALPORT_H_ +#define SERIALPORT_H_ + +#include "Uart.h" + +class SerialPort { +public: + SerialPort(); + virtual ~SerialPort(); + int available(); + void begin(int speed = 9600); + int read(); + int write(const char* buf, int len); + int print(int val, int format); + void flush(); +private: + static LpcUart *u; +}; + +#endif /* SERIALPORT_H_ */ diff --git a/Modbus/inc/Uart.h b/Modbus/inc/Uart.h new file mode 100644 index 0000000..ce3c007 --- /dev/null +++ b/Modbus/inc/Uart.h @@ -0,0 +1,62 @@ +/* + * LpcUart.h + * + * Created on: 4.2.2019 + * Author: keijo + */ + +#ifndef LPCUART_H_ +#define LPCUART_H_ + +#include +#include "chip.h" + +struct LpcPinMap { + int port; /* set to -1 to indicate unused pin */ + int pin; /* set to -1 to indicate unused pin */ +}; + +struct LpcUartConfig { + LPC_USART_T *pUART; + uint32_t speed; + uint32_t data; + bool rs485; + LpcPinMap tx; + LpcPinMap rx; + LpcPinMap rts; /* used as output enable if RS-485 mode is enabled */ + LpcPinMap cts; +}; + + +class LpcUart { +public: + LpcUart(const LpcUartConfig &cfg); + LpcUart(const LpcUart &) = delete; + virtual ~LpcUart(); + int free(); /* get amount of free space in transmit buffer */ + int peek(); /* get number of received characters in receive buffer */ + int write(char c); + int write(const char *s); + int write(const char *buffer, int len); + int read(char &c); /* get a single character. Returns number of characters read --> returns 0 if no character is available */ + int read(char *buffer, int len); + void txbreak(bool brk); /* set break signal on */ + bool rxbreak(); /* check if break is received */ + void speed(int bps); /* change transmission speed */ + bool txempty(); + + void isr(); /* ISR handler. This will be called by the HW ISR handler. Do not call from application */ +private: + LPC_USART_T *uart; + IRQn_Type irqn; + /* currently we support only fixed size ring buffers */ + static const int UART_RB_SIZE = 256; + /* Transmit and receive ring buffers */ + RINGBUFF_T txring; + RINGBUFF_T rxring; + uint8_t rxbuff[UART_RB_SIZE]; + uint8_t txbuff[UART_RB_SIZE]; + static bool init; /* set when first UART is initialized. We have a global clock setting for all UARTSs */ +}; + +#endif /* LPCUART_H_ */ diff --git a/Modbus/src/SerialPort.cpp b/Modbus/src/SerialPort.cpp new file mode 100644 index 0000000..953d99a --- /dev/null +++ b/Modbus/src/SerialPort.cpp @@ -0,0 +1,50 @@ +#include "SerialPort.h" + + + +SerialPort::SerialPort() { + if(!u) { + LpcPinMap none = {-1, -1}; // unused pin has negative values in it + LpcPinMap txpin = { 0, 28 }; // transmit pin that goes to rs485 driver chip + LpcPinMap rxpin = { 0, 24 }; // receive pin that goes to rs485 driver chip + LpcPinMap rtspin = { 1, 0 }; // handshake pin that is used to set tranmitter direction + LpcUartConfig cfg = { LPC_USART1, 9600, UART_CFG_DATALEN_8 | UART_CFG_PARITY_NONE | UART_CFG_STOPLEN_2, true, txpin, rxpin, rtspin, none }; + u = new LpcUart(cfg); + } +} + +LpcUart *SerialPort::u = nullptr; + +SerialPort::~SerialPort() { + /* DeInitialize UART peripheral */ + delete u; +} + +int SerialPort::available() { + return u->peek(); +} + +void SerialPort::begin(int speed) { + u->speed(speed); + +} + +int SerialPort::read() { + char byte; + if(u->read(byte)> 0) return (byte); + return -1; +} +int SerialPort::write(const char* buf, int len) { + return u->write(buf, len); +} + +int SerialPort::print(int val, int format) { + // here only to maintain compatibility with Arduino interface + (void) val; + (void) format; + return (0); +} + +void SerialPort::flush() { + while(!u->txempty()) __WFI(); +} diff --git a/Modbus/src/Uart.cpp b/Modbus/src/Uart.cpp new file mode 100644 index 0000000..6a45cae --- /dev/null +++ b/Modbus/src/Uart.cpp @@ -0,0 +1,227 @@ +#include +#include "Uart.h" + + +static LpcUart *u0; +static LpcUart *u1; +static LpcUart *u2; + +extern "C" { +/** + * @brief UART interrupt handler using ring buffers + * @return Nothing + */ +void UART0_IRQHandler(void) +{ + /* Want to handle any errors? Do it here. */ + + /* Use default ring buffer handler. Override this with your own + code if you need more capability. */ + if(u0) u0->isr(); +} + +void UART1_IRQHandler(void) +{ + /* Want to handle any errors? Do it here. */ + + /* Use default ring buffer handler. Override this with your own + code if you need more capability. */ + if(u1) u1->isr(); +} + +void UART2_IRQHandler(void) +{ + /* Want to handle any errors? Do it here. */ + + /* Use default ring buffer handler. Override this with your own + code if you need more capability. */ + if(u2) u2->isr(); +} + +} + + +void LpcUart::isr() { + Chip_UART_IRQRBHandler(uart, &rxring, &txring); +} + +bool LpcUart::init = false; + +LpcUart::LpcUart(const LpcUartConfig &cfg) { + CHIP_SWM_PIN_MOVABLE_T tx; + CHIP_SWM_PIN_MOVABLE_T rx; + CHIP_SWM_PIN_MOVABLE_T cts; + CHIP_SWM_PIN_MOVABLE_T rts; + bool use_rts = (cfg.rts.port >= 0); + bool use_cts = (cfg.cts.port >= 0); + + if(!init) { + init = true; + /* Before setting up the UART, the global UART clock for USARTS 1-4 + * must first be setup. This requires setting the UART divider and + * the UART base clock rate to 16x the maximum UART rate for all + * UARTs. + * */ + /* Use main clock rate as base for UART baud rate divider */ + Chip_Clock_SetUARTBaseClockRate(Chip_Clock_GetMainClockRate(), false); + } + + uart = nullptr; // set default value before checking which UART to configure + + if(cfg.pUART == LPC_USART0) { + if(u0) return; // already exists + else u0 = this; + tx = SWM_UART0_TXD_O; + rx = SWM_UART0_RXD_I; + rts = SWM_UART0_RTS_O; + cts = SWM_UART0_CTS_I; + irqn = UART0_IRQn; + } + else if(cfg.pUART == LPC_USART1) { + if(u1) return; // already exists + else u1 = this; + tx = SWM_UART1_TXD_O; + rx = SWM_UART1_RXD_I; + rts = SWM_UART1_RTS_O; + cts = SWM_UART1_CTS_I; + irqn = UART1_IRQn; + } + else if(cfg.pUART == LPC_USART2) { + if(u2) return; // already exists + else u2 = this; + tx = SWM_UART2_TXD_O; + rx = SWM_UART2_RXD_I; + use_rts = false; // UART2 does not support handshakes + use_cts = false; + irqn = UART2_IRQn; + } + else { + return; + } + + uart = cfg.pUART; // set the actual value after validity checking + + + if(cfg.tx.port >= 0) { + Chip_IOCON_PinMuxSet(LPC_IOCON, cfg.tx.port, cfg.tx.pin, (IOCON_MODE_INACT | IOCON_DIGMODE_EN)); + Chip_SWM_MovablePortPinAssign(tx, cfg.tx.port, cfg.tx.pin); + } + + if(cfg.rx.port >= 0) { + Chip_IOCON_PinMuxSet(LPC_IOCON, cfg.rx.port, cfg.rx.pin, (IOCON_MODE_INACT | IOCON_DIGMODE_EN)); + Chip_SWM_MovablePortPinAssign(rx, cfg.rx.port, cfg.rx.pin); + } + + if(use_cts) { + Chip_IOCON_PinMuxSet(LPC_IOCON, cfg.cts.port, cfg.cts.pin, (IOCON_MODE_INACT | IOCON_DIGMODE_EN)); + Chip_SWM_MovablePortPinAssign(cts, cfg.cts.port, cfg.cts.pin); + } + + if(use_rts) { + Chip_IOCON_PinMuxSet(LPC_IOCON, cfg.rts.port, cfg.rts.pin, (IOCON_MODE_INACT | IOCON_DIGMODE_EN)); + Chip_SWM_MovablePortPinAssign(rts, cfg.rts.port, cfg.rts.pin); + } + + + /* Setup UART */ + Chip_UART_Init(uart); + Chip_UART_ConfigData(uart, cfg.data); + Chip_UART_SetBaud(uart, cfg.speed); + + if(use_rts && cfg.rs485) { + uart->CFG |= (1 << 20); // enable rs485 mode + //uart->CFG |= (1 << 18); // OE turnaraound time + uart->CFG |= (1 << 21);// driver enable polarity (active high) + } + + Chip_UART_Enable(uart); + Chip_UART_TXEnable(uart); + + /* Before using the ring buffers, initialize them using the ring + buffer init function */ + RingBuffer_Init(&rxring, rxbuff, 1, UART_RB_SIZE); + RingBuffer_Init(&txring, txbuff, 1, UART_RB_SIZE); + + + /* Enable receive data and line status interrupt */ + Chip_UART_IntEnable(uart, UART_INTEN_RXRDY); + Chip_UART_IntDisable(uart, UART_INTEN_TXRDY); /* May not be needed */ + + /* Enable UART interrupt */ + NVIC_EnableIRQ(irqn); +} + +LpcUart::~LpcUart() { + if(uart != nullptr) { + NVIC_DisableIRQ(irqn); + Chip_UART_IntDisable(uart, UART_INTEN_RXRDY); + Chip_UART_IntDisable(uart, UART_INTEN_TXRDY); + + if(uart == LPC_USART0) { + u0 = nullptr; + } + else if(uart == LPC_USART1) { + u1 = nullptr; + } + else if(uart == LPC_USART2) { + u2 = nullptr; + } + } +} + + +int LpcUart::free() +{ + return RingBuffer_GetCount(&txring);; +} + +int LpcUart::peek() +{ + return RingBuffer_GetCount(&rxring); +} + +int LpcUart::read(char &c) +{ + return Chip_UART_ReadRB(uart, &rxring, &c, 1); +} + +int LpcUart::read(char *buffer, int len) +{ + return Chip_UART_ReadRB(uart, &rxring, buffer, len); +} + +int LpcUart::write(char c) +{ + return Chip_UART_SendRB(uart, &txring, &c, 1); +} + +int LpcUart::write(const char *s) +{ + return Chip_UART_SendRB(uart, &txring, s, strlen(s)); +} + +int LpcUart::write(const char *buffer, int len) +{ + return Chip_UART_SendRB(uart, &txring, buffer, len); +} + +void LpcUart::txbreak(bool brk) +{ + // break handling not implemented yeet +} + +bool LpcUart::rxbreak() +{ + // break handling not implemented yeet + return false; +} + +void LpcUart::speed(int bps) +{ + Chip_UART_SetBaud(uart, bps); +} + +bool LpcUart::txempty() +{ + return (RingBuffer_GetCount(&txring) == 0); +}