~ruther/qmk_firmware

30b46fad5764b54ab4d47e9c4024f8030e1bf1a7 — Ryan 4 years ago be897cb
UART driver refactor (#11637)

M docs/_summary.md => docs/_summary.md +1 -0
@@ 138,6 138,7 @@
      * [WS2812 Driver](ws2812_driver.md)
      * [EEPROM Driver](eeprom_driver.md)
      * ['serial' Driver](serial_driver.md)
      * [UART Driver](uart_driver.md)
    * [GPIO Controls](internals_gpio_control.md)
    * [Keyboard Guidelines](hardware_keyboard_guidelines.md)


A docs/uart_driver.md => docs/uart_driver.md +90 -0
@@ 0,0 1,90 @@
# UART Driver

The UART drivers used in QMK have a set of common functions to allow portability between MCUs.

Currently, this driver does not support enabling hardware flow control (the `RTS` and `CTS` pins) if available, but may do so in future.

## AVR Configuration

No special setup is required - just connect the `RX` and `TX` pins of your UART device to the opposite pins on the MCU:

|MCU          |`TX`|`RX`|`CTS`|`RTS`|
|-------------|----|----|-----|-----|
|ATmega16/32U2|`D3`|`D2`|`D7` |`D6` |
|ATmega16/32U4|`D3`|`D2`|`D5` |`B7` |
|AT90USB64/128|`D3`|`D2`|*n/a*|*n/a*|
|ATmega32A    |`D1`|`D0`|*n/a*|*n/a*|
|ATmega328/P  |`D1`|`D0`|*n/a*|*n/a*|

## ChibiOS/ARM Configuration

You'll need to determine which pins can be used for UART -- as an example, STM32 parts generally have multiple UART peripherals, labeled USART1, USART2, USART3 etc.

To enable UART, modify your board's `halconf.h` to enable the serial driver:

```c
#define HAL_USE_SERIAL TRUE
```

Then, modify your board's `mcuconf.h` to enable the peripheral you've chosen, for example:

```c
#undef STM32_SERIAL_USE_USART2
#define STM32_SERIAL_USE_USART2 TRUE
```

Configuration-wise, you'll need to set up the peripheral as per your MCU's datasheet -- the defaults match the pins for a Proton-C, i.e. STM32F303.

|`config.h` override       |Description                                                    |Default Value|
|--------------------------|---------------------------------------------------------------|-------------|
|`#define SERIAL_DRIVER`   |USART peripheral to use - USART1 -> `SD1`, USART2 -> `SD2` etc.|`SD1`        |
|`#define SD1_TX_PIN`      |The pin to use for TX                                          |`A9`         |
|`#define SD1_TX_PAL_MODE` |The alternate function mode for TX                             |`7`          |
|`#define SD1_RX_PIN`      |The pin to use for RX                                          |`A10`        |
|`#define SD1_RX_PAL_MODE` |The alternate function mode for RX                             |`7`          |
|`#define SD1_CTS_PIN`     |The pin to use for CTS                                         |`A11`        |
|`#define SD1_CTS_PAL_MODE`|The alternate function mode for CTS                            |`7`          |
|`#define SD1_RTS_PIN`     |The pin to use for RTS                                         |`A12`        |
|`#define SD1_RTS_PAL_MODE`|The alternate function mode for RTS                            |`7`          |

## Functions

### `void uart_init(uint32_t baud)`

Initialize the UART driver. This function must be called only once, before any of the below functions can be called.

#### Arguments

 - `uint32_t baud`  
   The baud rate to transmit and receive at. This may depend on the device you are communicating with. Common values are 1200, 2400, 4800, 9600, 19200, 38400, 57600, and 115200.

---

### `void uart_putchar(uint8_t c)`

Transmit a single byte.

#### Arguments

 - `uint8_t c`  
   The byte (character) to send, from 0 to 255.

---

### `uint8_t uart_getchar(void)`

Receive a single byte.

#### Return Value

The byte read from the receive buffer.

---

### `bool uart_available(void)`

Return whether the receive buffer contains data. Call this function to determine if `uart_getchar()` will return meaningful data.

#### Return Value

`true` if the receive buffer length is non-zero.

R tmk_core/common/uart.c => drivers/avr/uart.c +20 -22
@@ 1,5 1,3 @@
// TODO: Teensy support(ATMega32u4/AT90USB128)
// Fixed for Arduino Duemilanove ATmega168p by Jun Wako
/* UART Example for Teensy USB Development Board
 * http://www.pjrc.com/teensy/
 * Copyright (c) 2009 PJRC.COM, LLC


@@ 31,22 29,7 @@

#include "uart.h"

#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
#    define UDRn UDR0
#    define UBRRnL UBRR0L
#    define UCSRnA UCSR0A
#    define UCSRnB UCSR0B
#    define UCSRnC UCSR0C
#    define U2Xn U2X0
#    define RXENn RXEN0
#    define TXENn TXEN0
#    define RXCIEn RXCIE0
#    define UCSZn1 UCSZ01
#    define UCSZn0 UCSZ00
#    define UDRIEn UDRIE0
#    define USARTn_UDRE_vect USART_UDRE_vect
#    define USARTn_RX_vect USART_RX_vect
#elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega32U2__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
#if defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
#    define UDRn UDR1
#    define UBRRnL UBRR1L
#    define UCSRnA UCSR1A


@@ 76,6 59,21 @@
#    define UDRIEn UDRIE
#    define USARTn_UDRE_vect USART_UDRE_vect
#    define USARTn_RX_vect USART_RX_vect
#elif defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__)
#    define UDRn UDR0
#    define UBRRnL UBRR0L
#    define UCSRnA UCSR0A
#    define UCSRnB UCSR0B
#    define UCSRnC UCSR0C
#    define U2Xn U2X0
#    define RXENn RXEN0
#    define TXENn TXEN0
#    define RXCIEn RXCIE0
#    define UCSZn1 UCSZ01
#    define UCSZn0 UCSZ00
#    define UDRIEn UDRIE0
#    define USARTn_UDRE_vect USART_UDRE_vect
#    define USARTn_RX_vect USART_RX_vect
#endif

// These buffers may be any size from 2 to 256 bytes.


@@ 131,16 129,16 @@ uint8_t uart_getchar(void) {
    return c;
}

// Return the number of bytes waiting in the receive buffer.
// Return whether the number of bytes waiting in the receive buffer is nonzero.
// Call this before uart_getchar() to check if it will need
// to wait for a byte to arrive.
uint8_t uart_available(void) {
bool uart_available(void) {
    uint8_t head, tail;

    head = rx_buffer_head;
    tail = rx_buffer_tail;
    if (head >= tail) return head - tail;
    return RX_BUFFER_SIZE + head - tail;
    if (head >= tail) return (head - tail) > 0;
    return (RX_BUFFER_SIZE + head - tail) > 0;
}

// Transmit Interrupt

A drivers/avr/uart.h => drivers/avr/uart.h +34 -0
@@ 0,0 1,34 @@
/* UART Example for Teensy USB Development Board
 * http://www.pjrc.com/teensy/
 * Copyright (c) 2009 PJRC.COM, LLC
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#pragma once

#include <stdint.h>

void uart_init(uint32_t baud);

void uart_putchar(uint8_t c);

uint8_t uart_getchar(void);

bool uart_available(void);

A drivers/chibios/uart.c => drivers/chibios/uart.c +59 -0
@@ 0,0 1,59 @@
/* Copyright 2021
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include "uart.h"

#include "quantum.h"

static SerialConfig serialConfig = {
    SERIAL_DEFAULT_BITRATE,
    SD1_CR1,
    SD1_CR2,
    SD1_CR3
};

void uart_init(uint32_t baud) {
    static bool is_initialised = false;

    if (!is_initialised) {
        is_initialised = true;

        serialConfig.speed = baud;

#if defined(USE_GPIOV1)
        palSetLineMode(SD1_TX_PIN, PAL_MODE_STM32_ALTERNATE_OPENDRAIN);
        palSetLineMode(SD1_RX_PIN, PAL_MODE_STM32_ALTERNATE_OPENDRAIN);
#else
        palSetLineMode(SD1_TX_PIN, PAL_MODE_ALTERNATE(SD1_TX_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN);
        palSetLineMode(SD1_RX_PIN, PAL_MODE_ALTERNATE(SD1_RX_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN);
#endif
        sdStart(&SERIAL_DRIVER, &serialConfig);
    }
}

void uart_putchar(uint8_t c) {
    sdPut(&SERIAL_DRIVER, c);
}

uint8_t uart_getchar(void) {
    msg_t res = sdGet(&SERIAL_DRIVER);

    return (uint8_t)res;
}

bool uart_available(void) {
    return !sdGetWouldBlock(&SERIAL_DRIVER);
}

A drivers/chibios/uart.h => drivers/chibios/uart.h +77 -0
@@ 0,0 1,77 @@
/* Copyright 2021
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#pragma once

#include <stdint.h>

#include <hal.h>

#ifndef SERIAL_DRIVER
#    define SERIAL_DRIVER SD1
#endif

#ifndef SD1_TX_PIN
#    define SD1_TX_PIN A9
#endif

#ifndef SD1_TX_PAL_MODE
#    define SD1_TX_PAL_MODE 7
#endif

#ifndef SD1_RX_PIN
#    define SD1_RX_PIN A10
#endif

#ifndef SD1_RX_PAL_MODE
#    define SD1_RX_PAL_MODE 7
#endif

#ifndef SD1_CTS_PIN
#    define SD1_CTS_PIN A11
#endif

#ifndef SD1_CTS_PAL_MODE
#    define SD1_CTS_PAL_MODE 7
#endif

#ifndef SD1_RTS_PIN
#    define SD1_RTS_PIN A12
#endif

#ifndef SD1_RTS_PAL_MODE
#    define SD1_RTS_PAL_MODE 7
#endif

#ifndef SD1_CR1
#    define SD1_CR1 0
#endif

#ifndef SD1_CR2
#    define SD1_CR2 0
#endif

#ifndef SD1_CR3
#    define SD1_CR3 0
#endif

void uart_init(uint32_t baud);

void uart_putchar(uint8_t c);

uint8_t uart_getchar(void);

bool uart_available(void);

M keyboards/mschwingen/modelm/rules.mk => keyboards/mschwingen/modelm/rules.mk +1 -1
@@ 29,7 29,7 @@ DYNAMIC_MACRO_ENABLE = yes
UART_DEBUG = no

SRC += matrix.c
QUANTUM_LIB_SRC += $(COMMON_DIR)/uart.c \
QUANTUM_LIB_SRC += uart.c \
                   spi_master.c

OPT_DEFS += -DSLEEP_LED_ENABLE # we need our own sleep callbacks to turn of WS2812 LEDs

M keyboards/nullbitsco/nibble/remote_kb.c => keyboards/nullbitsco/nibble/remote_kb.c +1 -0
@@ 27,6 27,7 @@ This will require a new communication protocol, as the current one is limited.
*/

#include "remote_kb.h"
#include "uart.h"

uint8_t
 msg[UART_MSG_LEN],

M keyboards/nullbitsco/nibble/remote_kb.h => keyboards/nullbitsco/nibble/remote_kb.h +0 -1
@@ 16,7 16,6 @@
#pragma once

#include "quantum.h"
#include "tmk_core/common/uart.h"

#define SERIAL_UART_BAUD 153600 //low error rate for 32u4 @ 16MHz


M keyboards/nullbitsco/nibble/rules.mk => keyboards/nullbitsco/nibble/rules.mk +2 -5
@@ 1,9 1,6 @@
# MCU name
MCU = atmega32u4

# Interrupt driven control endpoint task(+60)
OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT

# Bootloader selection
BOOTLOADER = atmel-dfu



@@ 31,5 28,5 @@ CUSTOM_MATRIX = lite        # Lite custom matrix
SRC += matrix.c \
       bitc_led.c \
       big_led.c \
       remote_kb.c \
       tmk_core/common/uart.c
       remote_kb.c
QUANTUM_LIB_SRC += uart.c

M platforms/chibios/QMK_PROTON_C/configs/mcuconf.h => platforms/chibios/QMK_PROTON_C/configs/mcuconf.h +2 -2
@@ 212,8 212,8 @@
/*
 * SERIAL driver system settings.
 */
#define STM32_SERIAL_USE_USART1             FALSE
#define STM32_SERIAL_USE_USART2             TRUE
#define STM32_SERIAL_USE_USART1             TRUE
#define STM32_SERIAL_USE_USART2             FALSE
#define STM32_SERIAL_USE_USART3             FALSE
#define STM32_SERIAL_USE_UART4              FALSE
#define STM32_SERIAL_USE_UART5              FALSE

D tmk_core/common/uart.h => tmk_core/common/uart.h +0 -8
@@ 1,8 0,0 @@
#pragma once

#include <stdint.h>

void    uart_init(uint32_t baud);
void    uart_putchar(uint8_t c);
uint8_t uart_getchar(void);
uint8_t uart_available(void);