diff --git a/include/freertos/tc_ioctl.h b/include/freertos/tc_ioctl.h index 3cb714241..b7ab168ef 100644 --- a/include/freertos/tc_ioctl.h +++ b/include/freertos/tc_ioctl.h @@ -46,11 +46,17 @@ #define TCPAREVEN IO(TERMIOS_IOC_MAGIC, 0xF2) #define TCPARONE IO(TERMIOS_IOC_MAGIC, 0xF3) #define TCPARZERO IO(TERMIOS_IOC_MAGIC, 0xF4) +/// One stop bit +#define TCSTOPONE IO(TERMIOS_IOC_MAGIC, 0xF8) +/// Two stop bits +#define TCSTOPTWO IO(TERMIOS_IOC_MAGIC, 0xF9) /// Argument is a Notifiable* pointer. This notifiable will be invoked when all /// bytes have completed transferring and the transmit engine is idle. #define TCDRAINNOTIFY IOW(TERMIOS_IOC_MAGIC, 0xE0, 4) +/// Argument is the desired baud rate for the port. +#define TCBAUDRATE IOW(TERMIOS_IOC_MAGIC, 0xE1, 4) #endif // _FREERTOS_TC_IOCTL_H_ diff --git a/src/freertos_drivers/ti/CC32xxUart.cxx b/src/freertos_drivers/ti/CC32xxUart.cxx index b89b4e900..1c72cd2ea 100644 --- a/src/freertos_drivers/ti/CC32xxUart.cxx +++ b/src/freertos_drivers/ti/CC32xxUart.cxx @@ -67,13 +67,19 @@ CC32xxUart::CC32xxUart(const char *name, unsigned long base, uint32_t interrupt, TxEnableMethod tx_enable_assert, TxEnableMethod tx_enable_deassert) : Serial(name) - , txEnableAssert(tx_enable_assert) - , txEnableDeassert(tx_enable_deassert) - , base(base) - , interrupt(interrupt) - , txPending(false) - , hwFIFO(hw_fifo) + , txEnableAssert_(tx_enable_assert) + , txEnableDeassert_(tx_enable_deassert) + , base_(base) + , interrupt_(interrupt) + , baud_(baud) + , txPending_(false) + , hwFIFO_(hw_fifo) { + static_assert( + UART_CONFIG_PAR_NONE == 0, "driverlib changed against our assumptions"); + static_assert( + UART_CONFIG_STOP_ONE == 0, "driverlib changed against our assumptions"); + HASSERT(mode <= 0xFFu); switch (base) { @@ -89,30 +95,30 @@ CC32xxUart::CC32xxUart(const char *name, unsigned long base, uint32_t interrupt, break; } - MAP_UARTConfigSetExpClk(base, cm3_cpu_clock_hz, baud, + MAP_UARTConfigSetExpClk(base_, cm3_cpu_clock_hz, baud, mode | UART_CONFIG_PAR_NONE); - MAP_IntDisable(interrupt); + MAP_IntDisable(interrupt_); /* We set the priority so that it is slightly lower than the highest needed * for FreeRTOS compatibility. This will ensure that CAN interrupts take * precedence over UART. */ - MAP_IntPrioritySet(interrupt, + MAP_IntPrioritySet(interrupt_, std::min(0xff, configKERNEL_INTERRUPT_PRIORITY + 0x20)); - MAP_UARTIntEnable(base, UART_INT_RX | UART_INT_RT); + MAP_UARTIntEnable(base_, UART_INT_RX | UART_INT_RT); } /** Enable use of the device. */ void CC32xxUart::enable() { - MAP_IntEnable(interrupt); - MAP_UARTEnable(base); - if (hwFIFO) + MAP_IntEnable(interrupt_); + MAP_UARTEnable(base_); + if (hwFIFO_) { - MAP_UARTFIFOEnable(base); + MAP_UARTFIFOEnable(base_); } else { - MAP_UARTFIFODisable(base); + MAP_UARTFIFODisable(base_); } } @@ -120,8 +126,8 @@ void CC32xxUart::enable() */ void CC32xxUart::disable() { - MAP_IntDisable(interrupt); - MAP_UARTDisable(base); + MAP_IntDisable(interrupt_); + MAP_UARTDisable(base_); } /** Request an ioctl transaction @@ -137,39 +143,63 @@ int CC32xxUart::ioctl(File *file, unsigned long int key, unsigned long data) default: return -EINVAL; case TCSBRK: - MAP_UARTBreakCtl(base, true); + MAP_UARTBreakCtl(base_, true); // need to wait at least two frames here MAP_UtilsDelay(100 * 26); - MAP_UARTBreakCtl(base, false); + MAP_UARTBreakCtl(base_, false); MAP_UtilsDelay(12 * 26); break; case TCPARNONE: - MAP_UARTParityModeSet(base, UART_CONFIG_PAR_NONE); + mode_ &= ~UART_CONFIG_PAR_MASK; + mode_ |= UART_CONFIG_PAR_NONE; + MAP_UARTParityModeSet(base_, UART_CONFIG_PAR_NONE); break; case TCPARODD: - MAP_UARTParityModeSet(base, UART_CONFIG_PAR_ODD); + mode_ &= ~UART_CONFIG_PAR_MASK; + mode_ |= UART_CONFIG_PAR_ODD; + MAP_UARTParityModeSet(base_, UART_CONFIG_PAR_ODD); break; case TCPAREVEN: - MAP_UARTParityModeSet(base, UART_CONFIG_PAR_EVEN); + mode_ &= ~UART_CONFIG_PAR_MASK; + mode_ |= UART_CONFIG_PAR_EVEN; + MAP_UARTParityModeSet(base_, UART_CONFIG_PAR_EVEN); break; case TCPARONE: - MAP_UARTParityModeSet(base, UART_CONFIG_PAR_ONE); + mode_ &= ~UART_CONFIG_PAR_MASK; + mode_ |= UART_CONFIG_PAR_ONE; + MAP_UARTParityModeSet(base_, UART_CONFIG_PAR_ONE); break; case TCPARZERO: - MAP_UARTParityModeSet(base, UART_CONFIG_PAR_ZERO); + mode_ &= ~UART_CONFIG_PAR_MASK; + mode_ |= UART_CONFIG_PAR_ZERO; + MAP_UARTParityModeSet(base_, UART_CONFIG_PAR_ZERO); + break; + case TCSTOPONE: + mode_ &= ~UART_CONFIG_STOP_MASK; + mode_ |= UART_CONFIG_STOP_ONE; + set_mode(); + break; + case TCSTOPTWO: + mode_ &= ~UART_CONFIG_STOP_MASK; + mode_ |= UART_CONFIG_STOP_TWO; + set_mode(); + break; + case TCBAUDRATE: + baud_ = data; + set_mode(); break; case TCDRAINNOTIFY: { Notifiable* arg = (Notifiable*)data; { AtomicHolder h(&isr_lock); - if (txComplete != nullptr) + if (txComplete_ != nullptr) { return -EBUSY; } - if (txPending) + if (txPending_) { - txComplete = arg; + txComplete_ = arg; arg = nullptr; } } @@ -184,6 +214,12 @@ int CC32xxUart::ioctl(File *file, unsigned long int key, unsigned long data) return 0; } +/** Sets the port baud rate and mode from the class variables. */ +void CC32xxUart::set_mode() +{ + MAP_UARTConfigSetExpClk(base_, cm3_cpu_clock_hz, baud_, mode_); +} + /** Send data until there is no more space left. */ void CC32xxUart::send() @@ -193,7 +229,7 @@ void CC32xxUart::send() uint8_t data = 0; if (txBuf->get(&data, 1)) { - MAP_UARTCharPutNonBlocking(base, data); + MAP_UARTCharPutNonBlocking(base_, data); } else @@ -201,18 +237,18 @@ void CC32xxUart::send() break; } } - while (MAP_UARTSpaceAvail(base)); + while (MAP_UARTSpaceAvail(base_)); if (txBuf->pending()) { /* more data to send later */ - MAP_UARTTxIntModeSet(base, UART_TXINT_MODE_FIFO); + MAP_UARTTxIntModeSet(base_, UART_TXINT_MODE_FIFO); } else { /* no more data left to send */ - MAP_UARTTxIntModeSet(base, UART_TXINT_MODE_EOT); - MAP_UARTIntClear(base, UART_INT_TX); + MAP_UARTTxIntModeSet(base_, UART_TXINT_MODE_EOT); + MAP_UARTIntClear(base_, UART_INT_TX); } } @@ -220,17 +256,17 @@ void CC32xxUart::send() */ void CC32xxUart::tx_char() { - if (txPending == false) + if (txPending_ == false) { - if (txEnableAssert) + if (txEnableAssert_) { - txEnableAssert(); + txEnableAssert_(); } send(); - txPending = true; + txPending_ = true; - MAP_UARTIntEnable(base, UART_INT_TX); + MAP_UARTIntEnable(base_, UART_INT_TX); txBuf->signal_condition(); } } @@ -241,16 +277,16 @@ void CC32xxUart::interrupt_handler() { int woken = false; /* get and clear the interrupt status */ - unsigned long status = MAP_UARTIntStatus(base, true); - MAP_UARTIntClear(base, status); + unsigned long status = MAP_UARTIntStatus(base_, true); + MAP_UARTIntClear(base_, status); /** @todo (Stuart Baker) optimization opportunity by getting a write * pointer to fill the fifo and then advance the buffer when finished */ /* receive charaters as long as we can */ - while (MAP_UARTCharsAvail(base)) + while (MAP_UARTCharsAvail(base_)) { - long data = MAP_UARTCharGetNonBlocking(base); + long data = MAP_UARTCharGetNonBlocking(base_); if (data >= 0 && data <= 0xff) { unsigned char c = data; @@ -262,7 +298,7 @@ void CC32xxUart::interrupt_handler() } } /* transmit a character if we have pending tx data */ - if (txPending && (status & UART_INT_TX)) + if (txPending_ && (status & UART_INT_TX)) { if (txBuf->pending()) { @@ -272,19 +308,19 @@ void CC32xxUart::interrupt_handler() else { /* no more data left to send */ - HASSERT(MAP_UARTTxIntModeGet(base) == UART_TXINT_MODE_EOT); - if (txEnableDeassert) + HASSERT(MAP_UARTTxIntModeGet(base_) == UART_TXINT_MODE_EOT); + if (txEnableDeassert_) { - txEnableDeassert(); + txEnableDeassert_(); } - txPending = false; - if (txComplete) + txPending_ = false; + if (txComplete_) { - Notifiable *t = txComplete; - txComplete = nullptr; + Notifiable *t = txComplete_; + txComplete_ = nullptr; t->notify_from_isr(); } - MAP_UARTIntDisable(base, UART_INT_TX); + MAP_UARTIntDisable(base_, UART_INT_TX); } } os_isr_exit_yield_test(woken); diff --git a/src/freertos_drivers/ti/CC32xxUart.hxx b/src/freertos_drivers/ti/CC32xxUart.hxx index d9eb26300..663d36be9 100644 --- a/src/freertos_drivers/ti/CC32xxUart.hxx +++ b/src/freertos_drivers/ti/CC32xxUart.hxx @@ -89,8 +89,8 @@ public: */ void interrupt_handler(); - /** Request an ioctl transaction. Currently the only supported ioctl is - * TCSBRK. */ + /** Request an ioctl transaction. Supported ioctl is TCSBRK, TCDRAINNOTIFY, + * TCSTOP*, TCBAUDRATE and TCPAR* from include/freertos/tc_ioctl.h */ int ioctl(File *file, unsigned long int key, unsigned long data) override; private: @@ -106,19 +106,23 @@ private: */ void send(); + /** Sets the port baud rate and mode from the class variables. */ + void set_mode(); + /** function pointer to a method that asserts the transmit enable. */ - TxEnableMethod txEnableAssert; + TxEnableMethod txEnableAssert_; /** function pointer to a method that deasserts the transmit enable. */ - TxEnableMethod txEnableDeassert; + TxEnableMethod txEnableDeassert_; /** Notifiable to invoke when the transmit engine has finished operation. */ - Notifiable* txComplete{nullptr}; + Notifiable* txComplete_{nullptr}; - unsigned long base; /**< base address of this device */ - unsigned long interrupt; /**< interrupt of this device */ - bool txPending; /**< transmission currently pending */ - bool hwFIFO; /**< true if hardware fifo is to be enabled, else false */ + unsigned long base_; /**< base address of this device */ + uint32_t interrupt_ : 8; /**< interrupt of this device */ + uint32_t baud_ : 24; /**< desired baud rate */ + uint8_t txPending_; /**< transmission currently pending */ + uint8_t hwFIFO_; /**< true if hardware fifo is to be enabled, else false */ /** Default constructor. */ diff --git a/src/freertos_drivers/ti/TivaDev.hxx b/src/freertos_drivers/ti/TivaDev.hxx index f42130ee8..87b396eb2 100644 --- a/src/freertos_drivers/ti/TivaDev.hxx +++ b/src/freertos_drivers/ti/TivaDev.hxx @@ -44,6 +44,7 @@ #include "driverlib/rom_map.h" #include "driverlib/interrupt.h" #include "driverlib/timer.h" +#include "driverlib/uart.h" #include "usblib/usblib.h" #include "usblib/usbcdc.h" #include "usblib/usb-ids.h" @@ -154,12 +155,27 @@ private: class TivaUart : public Serial { public: + /** These mode bits need to be OR-ed together for the mode argument and + * ioctl. */ + enum Mode + { + CS5 = UART_CONFIG_WLEN_5, /**< 5-bits word length */ + CS6 = UART_CONFIG_WLEN_6, /**< 6-bits word length */ + CS7 = UART_CONFIG_WLEN_7, /**< 7-bits word length */ + CS8 = UART_CONFIG_WLEN_8, /**< 8-bits word length */ + CSTOPB = UART_CONFIG_STOP_TWO, /**< send two stop bits instead of 1 */ + }; + /** Constructor. * @param name name of this device instance in the file system * @param base base address of this device * @param interrupt interrupt number of this device + * @param baud desired baud rate + * @param mode to configure the UART for + * @param hw_fifo true if hardware fifo is to be enabled, else false. */ - TivaUart(const char *name, unsigned long base, uint32_t interrupt); + TivaUart(const char *name, unsigned long base, uint32_t interrupt, + uint32_t baud = 115200, uint32_t mode = CS8, bool hw_fifo = true); /** Destructor. */ @@ -172,6 +188,10 @@ public: */ void interrupt_handler(); + /** Request an ioctl transaction. Supported ioctl is TCSBRK, TCDRAINNOTIFY, + * TCSTOP*, TCBAUDRATE and TCPAR* from include/freertos/tc_ioctl.h */ + int ioctl(File *file, unsigned long int key, unsigned long data) override; + private: void enable() override; /**< function to enable device */ void disable() override; /**< function to disable device */ @@ -180,9 +200,18 @@ private: */ void tx_char() override; - unsigned long base; /**< base address of this device */ - unsigned long interrupt; /**< interrupt of this device */ - bool txPending; /**< transmission currently pending */ + /** Sets the port baud rate and mode from the class variables. */ + void set_mode(); + + /** Notifiable to invoke when the transmit engine has finished operation. */ + Notifiable* txComplete_{nullptr}; + + uint32_t base_; /**< base address of this device */ + uint32_t interrupt_ : 8; /**< interrupt of this device */ + uint32_t baud_ : 24; /**< desired baud rate */ + uint8_t hwFIFO_; /**< enable HW FIFO */ + uint8_t mode_; /**< uart config (mode) flags */ + uint8_t txPending_; /**< transmission currently pending */ /** Default constructor. */ diff --git a/src/freertos_drivers/ti/TivaUart.cxx b/src/freertos_drivers/ti/TivaUart.cxx index a93208557..5e41cb7b4 100644 --- a/src/freertos_drivers/ti/TivaUart.cxx +++ b/src/freertos_drivers/ti/TivaUart.cxx @@ -34,6 +34,7 @@ #include #include +#include #include "inc/hw_types.h" #include "inc/hw_memmap.h" @@ -50,19 +51,31 @@ /** Instance pointers help us get context from the interrupt handler(s) */ static TivaUart *instances[8] = {NULL}; +/** Critical section lock between ISR and ioctl */ +static Atomic isr_lock; + /** Constructor. * @param name name of this device instance in the file system * @param base base address of this device * @param interrupt interrupt number of this device */ -TivaUart::TivaUart(const char *name, unsigned long base, uint32_t interrupt) - : Serial(name), - base(base), - interrupt(interrupt), - txPending(false) +TivaUart::TivaUart(const char *name, unsigned long base, uint32_t interrupt, + uint32_t baud, uint32_t mode, bool hw_fifo) + : Serial(name) + , base_(base) + , interrupt_(interrupt) + , baud_(baud) + , hwFIFO_(hw_fifo) + , mode_(mode) + , txPending_(false) { + static_assert( + UART_CONFIG_PAR_NONE == 0, "driverlib changed against our assumptions"); + static_assert( + UART_CONFIG_STOP_ONE == 0, "driverlib changed against our assumptions"); + HASSERT(mode <= 0xFFu); - switch (base) + switch (base_) { default: HASSERT(0); @@ -99,58 +112,58 @@ TivaUart::TivaUart(const char *name, unsigned long base, uint32_t interrupt) instances[7] = this; break; } - - /* We set the preliminary clock here, but it will be re-set when the device - * gets enabled. The reason for re-setting is that the system clock is - * switched in HwInit but that has not run yet at this point. */ - MAP_UARTConfigSetExpClk(base, cm3_cpu_clock_hz, 115200, - UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE); - MAP_UARTFIFOEnable(base); - MAP_UARTTxIntModeSet(base, UART_TXINT_MODE_EOT); - MAP_IntDisable(interrupt); + + MAP_UARTConfigSetExpClk(base_, cm3_cpu_clock_hz, baud_, mode_); + MAP_UARTTxIntModeSet(base_, UART_TXINT_MODE_EOT); + MAP_IntDisable(interrupt_); /* We set the priority so that it is slightly lower than the highest needed * for FreeRTOS compatibility. This will ensure that CAN interrupts take * precedence over UART. */ - MAP_IntPrioritySet(interrupt, + MAP_IntPrioritySet(interrupt_, std::min(0xff, configKERNEL_INTERRUPT_PRIORITY + 0x20)); - MAP_UARTIntEnable(base, UART_INT_RX | UART_INT_RT); + MAP_UARTIntEnable(base_, UART_INT_RX | UART_INT_RT); } /** Enable use of the device. */ void TivaUart::enable() { - MAP_UARTConfigSetExpClk(base, cm3_cpu_clock_hz, 115200, - UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE); - MAP_IntEnable(interrupt); - MAP_UARTEnable(base); - MAP_UARTFIFOEnable(base); + MAP_IntEnable(interrupt_); + MAP_UARTEnable(base_); + if (hwFIFO_) + { + MAP_UARTFIFOEnable(base_); + } + else + { + MAP_UARTFIFODisable(base_); + } } /** Disable use of the device. */ void TivaUart::disable() { - MAP_IntDisable(interrupt); - MAP_UARTDisable(base); + MAP_IntDisable(interrupt_); + MAP_UARTDisable(base_); } /** Try and transmit a message. */ void TivaUart::tx_char() { - if (txPending == false) + if (txPending_ == false) { uint8_t data = 0; if (txBuf->get(&data, 1)) { - MAP_UARTCharPutNonBlocking(base, data); + MAP_UARTCharPutNonBlocking(base_, data); - MAP_IntDisable(interrupt); - txPending = true; - MAP_UARTIntEnable(base, UART_INT_TX); - MAP_IntEnable(interrupt); + MAP_IntDisable(interrupt_); + txPending_ = true; + MAP_UARTIntEnable(base_, UART_INT_TX); + MAP_IntEnable(interrupt_); txBuf->signal_condition(); } } @@ -162,16 +175,16 @@ void TivaUart::interrupt_handler() { int woken = false; /* get and clear the interrupt status */ - unsigned long status = MAP_UARTIntStatus(base, true); - MAP_UARTIntClear(base, status); + unsigned long status = MAP_UARTIntStatus(base_, true); + MAP_UARTIntClear(base_, status); /** @todo (Stuart Baker) optimization opportunity by getting a write * pointer to fill the fifo and then advance the buffer when finished */ /* receive charaters as long as we can */ - while (MAP_UARTCharsAvail(base)) + while (MAP_UARTCharsAvail(base_)) { - long data = MAP_UARTCharGetNonBlocking(base); + long data = MAP_UARTCharGetNonBlocking(base_); if (data >= 0 && data <= 0xff) { unsigned char c = data; @@ -183,24 +196,30 @@ void TivaUart::interrupt_handler() } } /* transmit a character if we have pending tx data */ - if (txPending) + if (txPending_) { /** @todo (Stuart Baker) optimization opportunity by getting a read * pointer to fill the fifo and then consume the buffer when finished. */ - while (MAP_UARTSpaceAvail(base)) + while (MAP_UARTSpaceAvail(base_)) { unsigned char data; if (txBuf->get(&data, 1) != 0) { - MAP_UARTCharPutNonBlocking(base, data); + MAP_UARTCharPutNonBlocking(base_, data); txBuf->signal_condition_from_isr(); } else { /* no more data pending */ - txPending = false; - MAP_UARTIntDisable(base, UART_INT_TX); + txPending_ = false; + if (txComplete_) + { + Notifiable *t = txComplete_; + txComplete_ = nullptr; + t->notify_from_isr(); + } + MAP_UARTIntDisable(base_, UART_INT_TX); break; } } @@ -208,6 +227,96 @@ void TivaUart::interrupt_handler() os_isr_exit_yield_test(woken); } +/** Sets the port baud rate and mode from the class variables. */ +void TivaUart::set_mode() +{ + MAP_UARTConfigSetExpClk(base_, cm3_cpu_clock_hz, baud_, mode_); +} + +/** Request an ioctl transaction + * @param file file reference for this device + * @param key ioctl key + * @param data key data + * @return 0 upon success, -errno upon failure + */ +int TivaUart::ioctl(File *file, unsigned long int key, unsigned long data) +{ + switch (key) + { + default: + return -EINVAL; + case TCSBRK: + MAP_UARTBreakCtl(base_, true); + // need to wait at least two frames here + MAP_SysCtlDelay(100 * 26); + MAP_UARTBreakCtl(base_, false); + MAP_SysCtlDelay(12 * 26); + break; + case TCPARNONE: + mode_ &= ~UART_CONFIG_PAR_MASK; + mode_ |= UART_CONFIG_PAR_NONE; + MAP_UARTParityModeSet(base_, UART_CONFIG_PAR_NONE); + break; + case TCPARODD: + mode_ &= ~UART_CONFIG_PAR_MASK; + mode_ |= UART_CONFIG_PAR_ODD; + MAP_UARTParityModeSet(base_, UART_CONFIG_PAR_ODD); + break; + case TCPAREVEN: + mode_ &= ~UART_CONFIG_PAR_MASK; + mode_ |= UART_CONFIG_PAR_EVEN; + MAP_UARTParityModeSet(base_, UART_CONFIG_PAR_EVEN); + break; + case TCPARONE: + mode_ &= ~UART_CONFIG_PAR_MASK; + mode_ |= UART_CONFIG_PAR_ONE; + MAP_UARTParityModeSet(base_, UART_CONFIG_PAR_ONE); + break; + case TCPARZERO: + mode_ &= ~UART_CONFIG_PAR_MASK; + mode_ |= UART_CONFIG_PAR_ZERO; + MAP_UARTParityModeSet(base_, UART_CONFIG_PAR_ZERO); + break; + case TCSTOPONE: + mode_ &= ~UART_CONFIG_STOP_MASK; + mode_ |= UART_CONFIG_STOP_ONE; + set_mode(); + break; + case TCSTOPTWO: + mode_ &= ~UART_CONFIG_STOP_MASK; + mode_ |= UART_CONFIG_STOP_TWO; + set_mode(); + break; + case TCBAUDRATE: + baud_ = data; + set_mode(); + break; + case TCDRAINNOTIFY: + { + Notifiable* arg = (Notifiable*)data; + { + AtomicHolder h(&isr_lock); + if (txComplete_ != nullptr) + { + return -EBUSY; + } + if (txPending_) + { + txComplete_ = arg; + arg = nullptr; + } + } + if (arg) + { + arg->notify(); + } + break; + } + } + + return 0; +} + extern "C" { /** UART0 interrupt handler. */