Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Full-duplex serial driver for ARM boards #9842

Merged
merged 12 commits into from
May 27, 2021
154 changes: 147 additions & 7 deletions docs/serial_driver.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ This driver powers the [Split Keyboard](feature_split_keyboard.md) feature.

?> Serial in this context should be read as **sending information one bit at a time**, rather than implementing UART/USART/RS485/RS232 standards.

All drivers in this category have the following characteristics:
* Provides data and signaling over a single conductor
* Limited to single master, single slave
Drivers in this category have the following characteristics:
* bit bang and USART Half-duplex provide data and signaling over a single conductor
* USART Full-duplex provide data and signaling over two conductors
* They are all limited to single master and single slave communication scheme

## Supported Driver Types

| | AVR | ARM |
|-------------------|--------------------|--------------------|
| ----------------- | ------------------ | ------------------ |
| bit bang | :heavy_check_mark: | :heavy_check_mark: |
| USART Half-duplex | | :heavy_check_mark: |
| USART Full-duplex | | :heavy_check_mark: |

## Driver configuration

Expand Down Expand Up @@ -42,23 +44,24 @@ Configure the driver via your config.h:
Along with the generic options above, you must also turn on the `PAL_USE_CALLBACKS` feature in your halconf.h.

### USART Half-duplex
Targeting STM32 boards where communication is offloaded to a USART hardware device. The advantage is that this provides fast and accurate timings. `SOFT_SERIAL_PIN` for this driver is the configured USART TX pin. **The TX pin must have appropriate pull-up resistors**. To configure it, add this to your rules.mk:
Targeting STM32 boards where communication is offloaded to a USART hardware device. The advantage over bitbang is that this provides fast and accurate timings. `SERIAL_PIN_TX` for this driver is the configured USART TX pin. As this Pin is configured in open-drain mode an **external pull-up resistor is needed to keep the line high** (resistor values of 1.5k to 8.2k are known to work). To configure it, add this to your rules.mk:

```make
SERIAL_DRIVER = usart
```

Configure the hardware via your config.h:
```c
#define SOFT_SERIAL_PIN B6 // USART TX pin
#define SOFT_SERIAL_PIN B6 // USART TX pin
//#define USART1_REMAP // Remap USART TX and RX pins on STM32F103 MCUs, see table below.
#define SELECT_SOFT_SERIAL_SPEED 1 // or 0, 2, 3, 4, 5
// 0: about 460800 baud
// 1: about 230400 baud (default)
// 2: about 115200 baud
// 3: about 57600 baud
// 4: about 38400 baud
// 5: about 19200 baud
#define SERIAL_USART_DRIVER SD1 // USART driver of TX pin. default: SD1
#define SERIAL_USART_DRIVER SD1 // USART driver of TX pin. default: SD1
#define SERIAL_USART_TX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7
#define SERIAL_USART_TIMEOUT 100 // USART driver timeout. default 100
```
Expand All @@ -68,3 +71,140 @@ You must also enable the ChibiOS `SERIAL` feature:
* In your board's mcuconf.h: `#define STM32_SERIAL_USE_USARTn TRUE` (where 'n' matches the peripheral number of your selected USART on the MCU)

Do note that the configuration required is for the `SERIAL` peripheral, not the `UART` peripheral.

### USART Full-duplex
Targeting STM32 boards where communication is offloaded to a USART hardware device. The advantage over bitbang is that this provides fast and accurate timings. USART Full-Duplex requires two conductors instead of one conductor unlike the Half-duplex driver, but it is more efficent as it uses DMA transfers, which can result in even faster transmission speeds.
drashna marked this conversation as resolved.
Show resolved Hide resolved

#### Pin configuration

`SERIAL_USART_TX_PIN` is the USART `TX` pin, `SERIAL_USART_RX_PIN` is the USART `RX` pin. No external pull-up resistors are needed as the `TX` pin operates in push-pull mode. To use this driver the usart peripherals `TX` and `RX` pins must be configured with the correct Alternate-functions. If you are using a Proton-C everything is already setup, same is true for STM32F103 MCUs. For MCUs which are using a modern flexible GPIO configuration you have to specify these by setting `SERIAL_USART_TX_PAL_MODE` and `SERIAL_USART_RX_PAL_MODE`. Refeer to the corresponding datasheets of your MCU or find those settings in the table below.

#### Connecting the halves and Pin Swap
Please note that `TX` of the master half has to be connected with the `RX` pin of the slave half and `RX` of the master half has to be connected with the `TX` pin of the slave half! Usually this pin swap has to be done outside of the MCU e.g. with cables or on the pcb. Some MCUs like the STM32F303 used on the Proton-C allow this pin swap directly inside the MCU, this feature can be enabled using `#define SERIAL_USART_PIN_SWAP` in your config.h.

#### Setup
To use the driver, add this to your rules.mk:

```make
SERIAL_DRIVER = usart_duplex
```

Next configure the hardware via your config.h:

```c
#define SERIAL_USART_TX_PIN B6 // USART TX pin
#define SERIAL_USART_RX_PIN B7 // USART RX pin
//#define USART1_REMAP // Remap USART TX and RX pins on STM32F103 MCUs, see table below.
//#define SERIAL_USART_PIN_SWAP // Swap TX and RX pins if keyboard is master halve.
// Check if this feature is necessary with your keyboard design and available on the mcu.
#define SELECT_SOFT_SERIAL_SPEED 1 // or 0, 2, 3, 4, 5
// 0: 460800 baud
// 1: 230400 baud (default)
// 2: 115200 baud
// 3: 57600 baud
// 4: 38400 baud
// 5: 19200 baud
#define SERIAL_USART_DRIVER UARTD1 // USART driver of TX and RX pin. default: UARTD1
#define SERIAL_USART_TX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7
#define SERIAL_USART_RX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7
#define SERIAL_USART_TIMEOUT 100 // USART driver timeout. default 100
```

You must also enable the ChibiOS `UART` with blocking api feature:
* In your board's halconf.h: `#define HAL_USE_UART TRUE` and `#define UART_USE_WAIT TRUE`
* In your board's mcuconf.h: `#define STM32_UART_USE_USARTn TRUE` (where 'n' matches the peripheral number of your selected USART on the MCU)

Do note that the configuration required is for the `UART` peripheral, not the `SERIAL` peripheral.

#### Pins for USART Peripherals with Alternate Functions for selected STM32 MCUs
KarlK90 marked this conversation as resolved.
Show resolved Hide resolved

##### STM32F303 / Proton-C [Datasheet](https://www.st.com/resource/en/datasheet/stm32f303cc.pdf)

Pin Swap available: :heavy_check_mark:

| Pin | Function | Mode |
| ---------- | -------- | ---- |
| **USART1** | | |
| PA9 | TX | AF7 |
| PA10 | RX | AF7 |
| PB6 | TX | AF7 |
| PB7 | RX | AF7 |
| PC4 | TX | AF7 |
| PC5 | RX | AF7 |
| PE0 | TX | AF7 |
| PE1 | RX | AF7 |
| **USART2** | | |
| PA2 | TX | AF7 |
| PA3 | RX | AF7 |
| PA14 | TX | AF7 |
| PA15 | RX | AF7 |
| PB3 | TX | AF7 |
| PB4 | RX | AF7 |
| PD5 | TX | AF7 |
| PD6 | RX | AF7 |
| **USART3** | | |
| PB10 | TX | AF7 |
| PB11 | RX | AF7 |
| PC10 | TX | AF7 |
| PC11 | RX | AF7 |
| PD8 | TX | AF7 |
| PD9 | RX | AF7 |

##### STM32F072 [Datasheet](https://www.st.com/resource/en/datasheet/stm32f072c8.pdf)

Pin Swap available: :heavy_check_mark:

| Pin | Function | Mode |
| ------ | -------- | ---- |
| USART1 | | |
| PA9 | TX | AF1 |
| PA10 | RX | AF1 |
| PB6 | TX | AF0 |
| PB7 | RX | AF0 |
| USART2 | | |
| PA2 | TX | AF1 |
| PA3 | RX | AF1 |
| PA14 | TX | AF1 |
| PA15 | RX | AF1 |
| USART3 | | |
| PB10 | TX | AF4 |
| PB11 | RX | AF4 |
| PC4 | TX | AF1 |
| PC5 | RX | AF1 |
| PC10 | TX | AF1 |
| PC11 | RX | AF1 |
| PD8 | TX | AF0 |
| PD9 | RX | AF0 |
| USART4 | | |
| PA0 | TX | AF4 |
| PA1 | RX | AF4 |

##### STM32F103 Medium Density (C8-CB) [Datasheet](https://www.st.com/resource/en/datasheet/stm32f103c8.pdf)

Pin Swap available: N/A

TX Pin is always Alternate Function Push-Pull, RX Pin is always regular input pin for any USART peripheral. **For STM32F103 no additional Alternate Function configuration is necessary. QMK is already configured.**

Pin remapping:

The pins of USART Peripherals use default Pins that can be remapped to use other pins using the AFIO registers. Default pins are marked **bold**. Add the appropriate defines to your config.h file.

| Pin | Function | Mode | USART_REMAP |
| ---------- | -------- | ---- | ------------------- |
| **USART1** | | | |
| **PA9** | TX | AFPP | |
| **PA10** | RX | IN | |
| PB6 | TX | AFPP | USART1_REMAP |
| PB7 | RX | IN | USART1_REMAP |
| **USART2** | | | |
| **PA2** | TX | AFPP | |
| **PA3** | RX | IN | |
| PD5 | TX | AFPP | USART2_REMAP |
| PD6 | RX | IN | USART2_REMAP |
| **USART3** | | | |
| **PB10** | TX | AFPP | |
| **PB11** | RX | IN | |
| PC10 | TX | AFPP | USART3_PARTIALREMAP |
| PC11 | RX | IN | USART3_PARTIALREMAP |
| PD8 | TX | AFPP | USART3_FULLREMAP |
| PD9 | RX | IN | USART3_FULLREMAP |
69 changes: 20 additions & 49 deletions drivers/chibios/serial_usart.c
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
#include "quantum.h"
#include "serial.h"
#include "print.h"

#include <ch.h>
#include <hal.h>
/* Copyright 2021 QMK
*
* 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 <http://www.gnu.org/licenses/>.
*/

#ifndef USART_CR1_M0
# define USART_CR1_M0 USART_CR1_M // some platforms (f1xx) dont have this so
#endif
#include "serial_usart.h"

#ifndef USE_GPIOV1
// The default PAL alternate modes are used to signal that the pins are used for USART
Expand All @@ -20,50 +27,10 @@
# define SERIAL_USART_DRIVER SD1
#endif

#ifndef SERIAL_USART_CR1
# define SERIAL_USART_CR1 (USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0) // parity enable, odd parity, 9 bit length
#endif

#ifndef SERIAL_USART_CR2
# define SERIAL_USART_CR2 (USART_CR2_STOP_1) // 2 stop bits
#endif

#ifndef SERIAL_USART_CR3
# define SERIAL_USART_CR3 0
#endif

#ifdef SOFT_SERIAL_PIN
# define SERIAL_USART_TX_PIN SOFT_SERIAL_PIN
#endif

#ifndef SELECT_SOFT_SERIAL_SPEED
# define SELECT_SOFT_SERIAL_SPEED 1
#endif

#ifdef SERIAL_USART_SPEED
// Allow advanced users to directly set SERIAL_USART_SPEED
#elif SELECT_SOFT_SERIAL_SPEED == 0
# define SERIAL_USART_SPEED 460800
#elif SELECT_SOFT_SERIAL_SPEED == 1
# define SERIAL_USART_SPEED 230400
#elif SELECT_SOFT_SERIAL_SPEED == 2
# define SERIAL_USART_SPEED 115200
#elif SELECT_SOFT_SERIAL_SPEED == 3
# define SERIAL_USART_SPEED 57600
#elif SELECT_SOFT_SERIAL_SPEED == 4
# define SERIAL_USART_SPEED 38400
#elif SELECT_SOFT_SERIAL_SPEED == 5
# define SERIAL_USART_SPEED 19200
#else
# error invalid SELECT_SOFT_SERIAL_SPEED value
#endif

#ifndef SERIAL_USART_TIMEOUT
# define SERIAL_USART_TIMEOUT 100
#endif

#define HANDSHAKE_MAGIC 7

static inline msg_t sdWriteHalfDuplex(SerialDriver* driver, uint8_t* data, uint8_t size) {
msg_t ret = sdWrite(driver, data, size);

Expand Down Expand Up @@ -123,6 +90,10 @@ __attribute__((weak)) void usart_init(void) {
#else
palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN);
#endif

#if defined(USART_REMAP)
USART_REMAP;
#endif
}

void usart_master_init(void) {
Expand Down
78 changes: 78 additions & 0 deletions drivers/chibios/serial_usart.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/* Copyright 2021 QMK
*
* 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 <http://www.gnu.org/licenses/>.
*/

#pragma once

#include "quantum.h"
#include "serial.h"
#include "printf.h"

#include <ch.h>
#include <hal.h>

#ifndef USART_CR1_M0
# define USART_CR1_M0 USART_CR1_M // some platforms (f1xx) dont have this so
#endif

#ifndef SERIAL_USART_CR1
# define SERIAL_USART_CR1 (USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0) // parity enable, odd parity, 9 bit length
#endif

#ifndef SERIAL_USART_CR2
# define SERIAL_USART_CR2 (USART_CR2_STOP_1) // 2 stop bits
#endif

#ifndef SERIAL_USART_CR3
# define SERIAL_USART_CR3 0
#endif

#if defined(USART1_REMAP)
# define USART_REMAP do { (AFIO->MAPR |= AFIO_MAPR_USART1_REMAP); } while(0)
#elif defined(USART2_REMAP)
# define USART_REMAP do { (AFIO->MAPR |= AFIO_MAPR_USART2_REMAP); } while(0)
#elif defined(USART3_PARTIALREMAP)
# define USART_REMAP do { (AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_PARTIALREMAP); } while(0)
#elif defined(USART3_FULLREMAP)
# define USART_REMAP do { (AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_FULLREMAP); } while(0)
#endif

#ifndef SELECT_SOFT_SERIAL_SPEED
# define SELECT_SOFT_SERIAL_SPEED 1
#endif

#ifdef SERIAL_USART_SPEED
// Allow advanced users to directly set SERIAL_USART_SPEED
#elif SELECT_SOFT_SERIAL_SPEED == 0
# define SERIAL_USART_SPEED 460800
#elif SELECT_SOFT_SERIAL_SPEED == 1
# define SERIAL_USART_SPEED 230400
#elif SELECT_SOFT_SERIAL_SPEED == 2
# define SERIAL_USART_SPEED 115200
#elif SELECT_SOFT_SERIAL_SPEED == 3
# define SERIAL_USART_SPEED 57600
#elif SELECT_SOFT_SERIAL_SPEED == 4
# define SERIAL_USART_SPEED 38400
#elif SELECT_SOFT_SERIAL_SPEED == 5
# define SERIAL_USART_SPEED 19200
#else
# error invalid SELECT_SOFT_SERIAL_SPEED value
#endif

#ifndef SERIAL_USART_TIMEOUT
# define SERIAL_USART_TIMEOUT 100
#endif

#define HANDSHAKE_MAGIC 7
Loading