qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

serial_usart.c (8280B)


      1 // Copyright 2021 QMK
      2 // Copyright 2022 Stefan Kerkmann
      3 // SPDX-License-Identifier: GPL-2.0-or-later
      4 
      5 #include "serial_usart.h"
      6 #include "serial_protocol.h"
      7 #include "synchronization_util.h"
      8 #include "chibios_config.h"
      9 
     10 #if defined(SERIAL_USART_CONFIG)
     11 static QMKSerialConfig serial_config = SERIAL_USART_CONFIG;
     12 #elif defined(MCU_AT32) /* AT32 MCUs */
     13 static QMKSerialConfig serial_config = {
     14     .speed = (SERIAL_USART_SPEED),
     15     .ctrl1 = (SERIAL_USART_CTRL1),
     16     .ctrl2 = (SERIAL_USART_CTRL2),
     17 #    if !defined(SERIAL_USART_FULL_DUPLEX)
     18     .ctrl3 = ((SERIAL_USART_CTRL3) | USART_CTRL3_SLBEN) /* activate half-duplex mode */
     19 #    else
     20     .ctrl3 = (SERIAL_USART_CTRL3)
     21 #    endif
     22 };
     23 #elif defined(MCU_STM32) /* STM32 MCUs */
     24 static QMKSerialConfig serial_config = {
     25 #    if HAL_USE_SERIAL
     26     .speed = (SERIAL_USART_SPEED),
     27 #    else
     28     .baud = (SERIAL_USART_SPEED),
     29 #    endif
     30     .cr1 = (SERIAL_USART_CR1),
     31     .cr2 = (SERIAL_USART_CR2),
     32 #    if !defined(SERIAL_USART_FULL_DUPLEX)
     33     .cr3 = ((SERIAL_USART_CR3) | USART_CR3_HDSEL) /* activate half-duplex mode */
     34 #    else
     35     .cr3 = (SERIAL_USART_CR3)
     36 #    endif
     37 };
     38 #elif defined(MCU_RP) /* Raspberry Pi MCUs */
     39 /* USART in 8E2 config with RX and TX FIFOs enabled. */
     40 // clang-format off
     41 static QMKSerialConfig serial_config = {
     42     .baud = (SERIAL_USART_SPEED),
     43     .UARTLCR_H = UART_UARTLCR_H_WLEN_8BITS | UART_UARTLCR_H_PEN | UART_UARTLCR_H_STP2 | UART_UARTLCR_H_FEN,
     44     .UARTCR = 0U,
     45     .UARTIFLS = UART_UARTIFLS_RXIFLSEL_1_8F | UART_UARTIFLS_TXIFLSEL_1_8E,
     46     .UARTDMACR = 0U
     47 };
     48 // clang-format on
     49 #else
     50 #    error MCU Familiy not supported by default, supply your own serial_config by defining SERIAL_USART_CONFIG in your keyboard files.
     51 #endif
     52 
     53 static QMKSerialDriver* serial_driver = (QMKSerialDriver*)&SERIAL_USART_DRIVER;
     54 
     55 #if HAL_USE_SERIAL
     56 
     57 /**
     58  * @brief SERIAL Driver startup routine.
     59  */
     60 static inline void usart_driver_start(void) {
     61     sdStart(serial_driver, &serial_config);
     62 }
     63 
     64 inline void serial_transport_driver_clear(void) {
     65     osalSysLock();
     66     bool volatile queue_not_empty = !iqIsEmptyI(&serial_driver->iqueue);
     67     osalSysUnlock();
     68 
     69     while (queue_not_empty) {
     70         osalSysLock();
     71         /* Hard reset the input queue. */
     72         iqResetI(&serial_driver->iqueue);
     73         osalSysUnlock();
     74         /* Allow pending interrupts to preempt.
     75          * Do not merge the lock/unlock blocks into one
     76          * or the code will not work properly.
     77          * The empty read adds a tiny amount of delay. */
     78         (void)queue_not_empty;
     79         osalSysLock();
     80         queue_not_empty = !iqIsEmptyI(&serial_driver->iqueue);
     81         osalSysUnlock();
     82     }
     83 }
     84 
     85 #elif HAL_USE_SIO
     86 
     87 /**
     88  * @brief SIO Driver startup routine.
     89  */
     90 static inline void usart_driver_start(void) {
     91     sioStart(serial_driver, &serial_config);
     92 }
     93 
     94 inline void serial_transport_driver_clear(void) {
     95     if (sioHasRXErrorsX(serial_driver)) {
     96         sioGetAndClearErrors(serial_driver);
     97     }
     98     osalSysLock();
     99     while (!sioIsRXEmptyX(serial_driver)) {
    100         (void)sioGetX(serial_driver);
    101     }
    102     osalSysUnlock();
    103 }
    104 
    105 #else
    106 
    107 #    error Either the SERIAL or SIO driver has to be activated to use the usart driver for split keyboards.
    108 
    109 #endif
    110 
    111 inline bool serial_transport_send(const uint8_t* source, const size_t size) {
    112     bool success = (size_t)chnWriteTimeout(serial_driver, source, size, TIME_MS2I(SERIAL_USART_TIMEOUT)) == size;
    113 
    114 #if !defined(SERIAL_USART_FULL_DUPLEX)
    115     /* Half duplex fills the input queue with the data we wrote - just throw it away. */
    116     if (likely(success)) {
    117         size_t bytes_left = size;
    118 #    if HAL_USE_SERIAL
    119         /* The SERIAL driver uses large soft FIFOs that are filled from an IRQ
    120          * context, so there is a delay between receiving the data and it
    121          * becoming actually available, therefore we have to apply a timeout
    122          * mechanism. Under the right circumstances (e.g. bad cables paired with
    123          * high baud rates) less bytes can be present in the input queue as
    124          * well. */
    125         uint8_t dump[64];
    126 
    127         while (unlikely(bytes_left >= 64)) {
    128             if (unlikely(!serial_transport_receive(dump, 64))) {
    129                 return false;
    130             }
    131             bytes_left -= 64;
    132         }
    133 
    134         return serial_transport_receive(dump, bytes_left);
    135 #    else
    136         /* The SIO driver directly accesses the hardware FIFOs of the USART
    137          * peripheral. As these are limited in depth, the RX FIFO might have
    138          * been overflowed by a large transaction that we just send. Therefore
    139          * we attempt to read back all the data we send or until the FIFO runs
    140          * empty in case it overflowed and data was truncated. */
    141         if (unlikely(sioSynchronizeTXEnd(serial_driver, TIME_MS2I(SERIAL_USART_TIMEOUT)) < MSG_OK)) {
    142             return false;
    143         }
    144 
    145         osalSysLock();
    146         while (bytes_left > 0 && !sioIsRXEmptyX(serial_driver)) {
    147             (void)sioGetX(serial_driver);
    148             bytes_left--;
    149         }
    150         osalSysUnlock();
    151 #    endif
    152     }
    153 #endif
    154 
    155     return success;
    156 }
    157 
    158 inline bool serial_transport_receive(uint8_t* destination, const size_t size) {
    159     bool success = (size_t)chnReadTimeout(serial_driver, destination, size, TIME_MS2I(SERIAL_USART_TIMEOUT)) == size;
    160     return success;
    161 }
    162 
    163 inline bool serial_transport_receive_blocking(uint8_t* destination, const size_t size) {
    164     bool success = (size_t)chnRead(serial_driver, destination, size) == size;
    165     return success;
    166 }
    167 
    168 #if !defined(SERIAL_USART_FULL_DUPLEX)
    169 
    170 /**
    171  * @brief Initiate pins for USART peripheral. Half-duplex configuration.
    172  */
    173 __attribute__((weak)) void usart_init(void) {
    174 #    if defined(MCU_STM32) || defined(MCU_AT32) /* STM32 and AT32 MCUs */
    175 #        if defined(USE_GPIOV1)
    176     palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_OPENDRAIN);
    177 #        else
    178     palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_OUTPUT_TYPE_OPENDRAIN);
    179 #        endif
    180 
    181 #        if defined(USART_REMAP)
    182     USART_REMAP;
    183 #        endif
    184 #    elif defined(MCU_RP) /* Raspberry Pi MCUs */
    185 #        error Half-duplex with the SIO driver is not supported due to hardware limitations on the RP2040, switch to the PIO driver which has half-duplex support.
    186 #    else
    187 #        pragma message "usart_init: MCU Familiy not supported by default, please supply your own init code by implementing usart_init() in your keyboard files."
    188 #    endif
    189 }
    190 
    191 #else
    192 
    193 /**
    194  * @brief Initiate pins for USART peripheral. Full-duplex configuration.
    195  */
    196 __attribute__((weak)) void usart_init(void) {
    197 #    if defined(MCU_STM32) || defined(MCU_AT32) /* STM32 and AT32 MCUs */
    198 #        if defined(USE_GPIOV1)
    199     palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_PUSHPULL);
    200     palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_INPUT);
    201 #        else
    202     palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST);
    203     palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_RX_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST);
    204 #        endif
    205 
    206 #        if defined(USART_REMAP)
    207     USART_REMAP;
    208 #        endif
    209 #    elif defined(MCU_RP) /* Raspberry Pi MCUs */
    210     palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_UART);
    211     palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE_UART);
    212 #    else
    213 #        pragma message "usart_init: MCU Familiy not supported by default, please supply your own init code by implementing usart_init() in your keyboard files."
    214 #    endif
    215 }
    216 
    217 #endif
    218 
    219 /**
    220  * @brief Overridable master specific initializations.
    221  */
    222 __attribute__((weak, nonnull)) void usart_master_init(QMKSerialDriver** driver) {
    223     (void)driver;
    224     usart_init();
    225 }
    226 
    227 /**
    228  * @brief Overridable slave specific initializations.
    229  */
    230 __attribute__((weak, nonnull)) void usart_slave_init(QMKSerialDriver** driver) {
    231     (void)driver;
    232     usart_init();
    233 }
    234 
    235 void serial_transport_driver_slave_init(void) {
    236     usart_slave_init(&serial_driver);
    237     usart_driver_start();
    238 }
    239 
    240 void serial_transport_driver_master_init(void) {
    241     usart_master_init(&serial_driver);
    242 
    243 #if defined(MCU_STM32) && defined(SERIAL_USART_PIN_SWAP)
    244     serial_config.cr2 |= USART_CR2_SWAP; // master has swapped TX/RX pins
    245 #endif
    246 
    247     usart_driver_start();
    248 }