aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Kerkmann <karlk90@pm.me>2021-05-27 05:37:54 +0200
committerGitHub <noreply@github.com>2021-05-26 20:37:54 -0700
commitd9610120de0452bc6a548bd36b1d4fdfba56d071 (patch)
treea4fb5379522d2c8c174dde6e80990af643343596
parenta78964c91839e5bc682806b88635c3a1a3d01da5 (diff)
downloadqmk_firmware-d9610120de0452bc6a548bd36b1d4fdfba56d071.tar.gz
qmk_firmware-d9610120de0452bc6a548bd36b1d4fdfba56d071.zip
Add Full-duplex serial driver for ARM boards (#9842)
-rw-r--r--docs/serial_driver.md154
-rw-r--r--drivers/chibios/serial_usart.c69
-rw-r--r--drivers/chibios/serial_usart.h78
-rw-r--r--drivers/chibios/serial_usart_duplex.c261
4 files changed, 506 insertions, 56 deletions
diff --git a/docs/serial_driver.md b/docs/serial_driver.md
index c98f4c117..359fc5955 100644
--- a/docs/serial_driver.md
+++ b/docs/serial_driver.md
@@ -3,16 +3,18 @@ This driver powers the [Split Keyboard](feature_split_keyboard.md) feature.
3 3
4?> Serial in this context should be read as **sending information one bit at a time**, rather than implementing UART/USART/RS485/RS232 standards. 4?> Serial in this context should be read as **sending information one bit at a time**, rather than implementing UART/USART/RS485/RS232 standards.
5 5
6All drivers in this category have the following characteristics: 6Drivers in this category have the following characteristics:
7* Provides data and signaling over a single conductor 7* bit bang and USART Half-duplex provide data and signaling over a single conductor
8* Limited to single master, single slave 8* USART Full-duplex provide data and signaling over two conductors
9* They are all limited to single master and single slave communication scheme
9 10
10## Supported Driver Types 11## Supported Driver Types
11 12
12| | AVR | ARM | 13| | AVR | ARM |
13|-------------------|--------------------|--------------------| 14| ----------------- | ------------------ | ------------------ |
14| bit bang | :heavy_check_mark: | :heavy_check_mark: | 15| bit bang | :heavy_check_mark: | :heavy_check_mark: |
15| USART Half-duplex | | :heavy_check_mark: | 16| USART Half-duplex | | :heavy_check_mark: |
17| USART Full-duplex | | :heavy_check_mark: |
16 18
17## Driver configuration 19## Driver configuration
18 20
@@ -42,7 +44,7 @@ Configure the driver via your config.h:
42Along with the generic options above, you must also turn on the `PAL_USE_CALLBACKS` feature in your halconf.h. 44Along with the generic options above, you must also turn on the `PAL_USE_CALLBACKS` feature in your halconf.h.
43 45
44### USART Half-duplex 46### USART Half-duplex
45Targeting 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: 47Targeting 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:
46 48
47```make 49```make
48SERIAL_DRIVER = usart 50SERIAL_DRIVER = usart
@@ -50,7 +52,8 @@ SERIAL_DRIVER = usart
50 52
51Configure the hardware via your config.h: 53Configure the hardware via your config.h:
52```c 54```c
53#define SOFT_SERIAL_PIN B6 // USART TX pin 55#define SOFT_SERIAL_PIN B6 // USART TX pin
56//#define USART1_REMAP // Remap USART TX and RX pins on STM32F103 MCUs, see table below.
54#define SELECT_SOFT_SERIAL_SPEED 1 // or 0, 2, 3, 4, 5 57#define SELECT_SOFT_SERIAL_SPEED 1 // or 0, 2, 3, 4, 5
55 // 0: about 460800 baud 58 // 0: about 460800 baud
56 // 1: about 230400 baud (default) 59 // 1: about 230400 baud (default)
@@ -58,7 +61,7 @@ Configure the hardware via your config.h:
58 // 3: about 57600 baud 61 // 3: about 57600 baud
59 // 4: about 38400 baud 62 // 4: about 38400 baud
60 // 5: about 19200 baud 63 // 5: about 19200 baud
61#define SERIAL_USART_DRIVER SD1 // USART driver of TX pin. default: SD1 64#define SERIAL_USART_DRIVER SD1 // USART driver of TX pin. default: SD1
62#define SERIAL_USART_TX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7 65#define SERIAL_USART_TX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7
63#define SERIAL_USART_TIMEOUT 100 // USART driver timeout. default 100 66#define SERIAL_USART_TIMEOUT 100 // USART driver timeout. default 100
64``` 67```
@@ -68,3 +71,140 @@ You must also enable the ChibiOS `SERIAL` feature:
68* 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) 71* 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)
69 72
70Do note that the configuration required is for the `SERIAL` peripheral, not the `UART` peripheral. 73Do note that the configuration required is for the `SERIAL` peripheral, not the `UART` peripheral.
74
75### USART Full-duplex
76Targeting 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 **without** pull-up resistors instead of one conductor with a pull-up resistor unlike the Half-duplex driver, but it is more efficent as it uses DMA transfers, which can result in even faster transmission speeds.
77
78#### Pin configuration
79
80`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.
81
82#### Connecting the halves and Pin Swap
83Please 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.
84
85#### Setup
86To use the driver, add this to your rules.mk:
87
88```make
89SERIAL_DRIVER = usart_duplex
90```
91
92Next configure the hardware via your config.h:
93
94```c
95#define SERIAL_USART_TX_PIN B6 // USART TX pin
96#define SERIAL_USART_RX_PIN B7 // USART RX pin
97//#define USART1_REMAP // Remap USART TX and RX pins on STM32F103 MCUs, see table below.
98//#define SERIAL_USART_PIN_SWAP // Swap TX and RX pins if keyboard is master halve.
99 // Check if this feature is necessary with your keyboard design and available on the mcu.
100#define SELECT_SOFT_SERIAL_SPEED 1 // or 0, 2, 3, 4, 5
101 // 0: 460800 baud
102 // 1: 230400 baud (default)
103 // 2: 115200 baud
104 // 3: 57600 baud
105 // 4: 38400 baud
106 // 5: 19200 baud
107#define SERIAL_USART_DRIVER UARTD1 // USART driver of TX and RX pin. default: UARTD1
108#define SERIAL_USART_TX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7
109#define SERIAL_USART_RX_PAL_MODE 7 // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7
110#define SERIAL_USART_TIMEOUT 100 // USART driver timeout. default 100
111```
112
113You must also enable the ChibiOS `UART` with blocking api feature:
114* In your board's halconf.h: `#define HAL_USE_UART TRUE` and `#define UART_USE_WAIT TRUE`
115* 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)
116
117Do note that the configuration required is for the `UART` peripheral, not the `SERIAL` peripheral.
118
119#### Pins for USART Peripherals with Alternate Functions for selected STM32 MCUs
120
121##### STM32F303 / Proton-C [Datasheet](https://www.st.com/resource/en/datasheet/stm32f303cc.pdf)
122
123Pin Swap available: :heavy_check_mark:
124
125| Pin | Function | Mode |
126| ---------- | -------- | ---- |
127| **USART1** | | |
128| PA9 | TX | AF7 |
129| PA10 | RX | AF7 |
130| PB6 | TX | AF7 |
131| PB7 | RX | AF7 |
132| PC4 | TX | AF7 |
133| PC5 | RX | AF7 |
134| PE0 | TX | AF7 |
135| PE1 | RX | AF7 |
136| **USART2** | | |
137| PA2 | TX | AF7 |
138| PA3 | RX | AF7 |
139| PA14 | TX | AF7 |
140| PA15 | RX | AF7 |
141| PB3 | TX | AF7 |
142| PB4 | RX | AF7 |
143| PD5 | TX | AF7 |
144| PD6 | RX | AF7 |
145| **USART3** | | |
146| PB10 | TX | AF7 |
147| PB11 | RX | AF7 |
148| PC10 | TX | AF7 |
149| PC11 | RX | AF7 |
150| PD8 | TX | AF7 |
151| PD9 | RX | AF7 |
152
153##### STM32F072 [Datasheet](https://www.st.com/resource/en/datasheet/stm32f072c8.pdf)
154
155Pin Swap available: :heavy_check_mark:
156
157| Pin | Function | Mode |
158| ------ | -------- | ---- |
159| USART1 | | |
160| PA9 | TX | AF1 |
161| PA10 | RX | AF1 |
162| PB6 | TX | AF0 |
163| PB7 | RX | AF0 |
164| USART2 | | |
165| PA2 | TX | AF1 |
166| PA3 | RX | AF1 |
167| PA14 | TX | AF1 |
168| PA15 | RX | AF1 |
169| USART3 | | |
170| PB10 | TX | AF4 |
171| PB11 | RX | AF4 |
172| PC4 | TX | AF1 |
173| PC5 | RX | AF1 |
174| PC10 | TX | AF1 |
175| PC11 | RX | AF1 |
176| PD8 | TX | AF0 |
177| PD9 | RX | AF0 |
178| USART4 | | |
179| PA0 | TX | AF4 |
180| PA1 | RX | AF4 |
181
182##### STM32F103 Medium Density (C8-CB) [Datasheet](https://www.st.com/resource/en/datasheet/stm32f103c8.pdf)
183
184Pin Swap available: N/A
185
186TX 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.**
187
188Pin remapping:
189
190The 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.
191
192| Pin | Function | Mode | USART_REMAP |
193| ---------- | -------- | ---- | ------------------- |
194| **USART1** | | | |
195| **PA9** | TX | AFPP | |
196| **PA10** | RX | IN | |
197| PB6 | TX | AFPP | USART1_REMAP |
198| PB7 | RX | IN | USART1_REMAP |
199| **USART2** | | | |
200| **PA2** | TX | AFPP | |
201| **PA3** | RX | IN | |
202| PD5 | TX | AFPP | USART2_REMAP |
203| PD6 | RX | IN | USART2_REMAP |
204| **USART3** | | | |
205| **PB10** | TX | AFPP | |
206| **PB11** | RX | IN | |
207| PC10 | TX | AFPP | USART3_PARTIALREMAP |
208| PC11 | RX | IN | USART3_PARTIALREMAP |
209| PD8 | TX | AFPP | USART3_FULLREMAP |
210| PD9 | RX | IN | USART3_FULLREMAP |
diff --git a/drivers/chibios/serial_usart.c b/drivers/chibios/serial_usart.c
index 7c81b1646..cae29388c 100644
--- a/drivers/chibios/serial_usart.c
+++ b/drivers/chibios/serial_usart.c
@@ -1,13 +1,20 @@
1#include "quantum.h" 1/* Copyright 2021 QMK
2#include "serial.h" 2 *
3#include "print.h" 3 * This program is free software: you can redistribute it and/or modify
4 4 * it under the terms of the GNU General Public License as published by
5#include <ch.h> 5 * the Free Software Foundation, either version 3 of the License, or
6#include <hal.h> 6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
7 16
8#ifndef USART_CR1_M0 17#include "serial_usart.h"
9# define USART_CR1_M0 USART_CR1_M // some platforms (f1xx) dont have this so
10#endif
11 18
12#ifndef USE_GPIOV1 19#ifndef USE_GPIOV1
13// The default PAL alternate modes are used to signal that the pins are used for USART 20// The default PAL alternate modes are used to signal that the pins are used for USART
@@ -20,50 +27,10 @@
20# define SERIAL_USART_DRIVER SD1 27# define SERIAL_USART_DRIVER SD1
21#endif 28#endif
22 29
23#ifndef SERIAL_USART_CR1
24# define SERIAL_USART_CR1 (USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0) // parity enable, odd parity, 9 bit length
25#endif
26
27#ifndef SERIAL_USART_CR2
28# define SERIAL_USART_CR2 (USART_CR2_STOP_1) // 2 stop bits
29#endif
30
31#ifndef SERIAL_USART_CR3
32# define SERIAL_USART_CR3 0
33#endif
34
35#ifdef SOFT_SERIAL_PIN 30#ifdef SOFT_SERIAL_PIN
36# define SERIAL_USART_TX_PIN SOFT_SERIAL_PIN 31# define SERIAL_USART_TX_PIN SOFT_SERIAL_PIN
37#endif 32#endif
38 33
39#ifndef SELECT_SOFT_SERIAL_SPEED
40# define SELECT_SOFT_SERIAL_SPEED 1
41#endif
42
43#ifdef SERIAL_USART_SPEED
44// Allow advanced users to directly set SERIAL_USART_SPEED
45#elif SELECT_SOFT_SERIAL_SPEED == 0
46# define SERIAL_USART_SPEED 460800
47#elif SELECT_SOFT_SERIAL_SPEED == 1
48# define SERIAL_USART_SPEED 230400
49#elif SELECT_SOFT_SERIAL_SPEED == 2
50# define SERIAL_USART_SPEED 115200
51#elif SELECT_SOFT_SERIAL_SPEED == 3
52# define SERIAL_USART_SPEED 57600
53#elif SELECT_SOFT_SERIAL_SPEED == 4
54# define SERIAL_USART_SPEED 38400
55#elif SELECT_SOFT_SERIAL_SPEED == 5
56# define SERIAL_USART_SPEED 19200
57#else
58# error invalid SELECT_SOFT_SERIAL_SPEED value
59#endif
60
61#ifndef SERIAL_USART_TIMEOUT
62# define SERIAL_USART_TIMEOUT 100
63#endif
64
65#define HANDSHAKE_MAGIC 7
66
67static inline msg_t sdWriteHalfDuplex(SerialDriver* driver, uint8_t* data, uint8_t size) { 34static inline msg_t sdWriteHalfDuplex(SerialDriver* driver, uint8_t* data, uint8_t size) {
68 msg_t ret = sdWrite(driver, data, size); 35 msg_t ret = sdWrite(driver, data, size);
69 36
@@ -123,6 +90,10 @@ __attribute__((weak)) void usart_init(void) {
123#else 90#else
124 palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); 91 palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN);
125#endif 92#endif
93
94#if defined(USART_REMAP)
95 USART_REMAP;
96#endif
126} 97}
127 98
128void usart_master_init(void) { 99void usart_master_init(void) {
diff --git a/drivers/chibios/serial_usart.h b/drivers/chibios/serial_usart.h
new file mode 100644
index 000000000..d35b5d12c
--- /dev/null
+++ b/drivers/chibios/serial_usart.h
@@ -0,0 +1,78 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include "quantum.h"
20#include "serial.h"
21#include "printf.h"
22
23#include <ch.h>
24#include <hal.h>
25
26#ifndef USART_CR1_M0
27# define USART_CR1_M0 USART_CR1_M // some platforms (f1xx) dont have this so
28#endif
29
30#ifndef SERIAL_USART_CR1
31# define SERIAL_USART_CR1 (USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0) // parity enable, odd parity, 9 bit length
32#endif
33
34#ifndef SERIAL_USART_CR2
35# define SERIAL_USART_CR2 (USART_CR2_STOP_1) // 2 stop bits
36#endif
37
38#ifndef SERIAL_USART_CR3
39# define SERIAL_USART_CR3 0
40#endif
41
42#if defined(USART1_REMAP)
43# define USART_REMAP do { (AFIO->MAPR |= AFIO_MAPR_USART1_REMAP); } while(0)
44#elif defined(USART2_REMAP)
45# define USART_REMAP do { (AFIO->MAPR |= AFIO_MAPR_USART2_REMAP); } while(0)
46#elif defined(USART3_PARTIALREMAP)
47# define USART_REMAP do { (AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_PARTIALREMAP); } while(0)
48#elif defined(USART3_FULLREMAP)
49# define USART_REMAP do { (AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_FULLREMAP); } while(0)
50#endif
51
52#ifndef SELECT_SOFT_SERIAL_SPEED
53# define SELECT_SOFT_SERIAL_SPEED 1
54#endif
55
56#ifdef SERIAL_USART_SPEED
57// Allow advanced users to directly set SERIAL_USART_SPEED
58#elif SELECT_SOFT_SERIAL_SPEED == 0
59# define SERIAL_USART_SPEED 460800
60#elif SELECT_SOFT_SERIAL_SPEED == 1
61# define SERIAL_USART_SPEED 230400
62#elif SELECT_SOFT_SERIAL_SPEED == 2
63# define SERIAL_USART_SPEED 115200
64#elif SELECT_SOFT_SERIAL_SPEED == 3
65# define SERIAL_USART_SPEED 57600
66#elif SELECT_SOFT_SERIAL_SPEED == 4
67# define SERIAL_USART_SPEED 38400
68#elif SELECT_SOFT_SERIAL_SPEED == 5
69# define SERIAL_USART_SPEED 19200
70#else
71# error invalid SELECT_SOFT_SERIAL_SPEED value
72#endif
73
74#ifndef SERIAL_USART_TIMEOUT
75# define SERIAL_USART_TIMEOUT 100
76#endif
77
78#define HANDSHAKE_MAGIC 7
diff --git a/drivers/chibios/serial_usart_duplex.c b/drivers/chibios/serial_usart_duplex.c
new file mode 100644
index 000000000..cc9b889ac
--- /dev/null
+++ b/drivers/chibios/serial_usart_duplex.c
@@ -0,0 +1,261 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "serial_usart.h"
18
19#include <stdatomic.h>
20
21#if !defined(USE_GPIOV1)
22// The default PAL alternate modes are used to signal that the pins are used for USART
23# if !defined(SERIAL_USART_TX_PAL_MODE)
24# define SERIAL_USART_TX_PAL_MODE 7
25# endif
26# if !defined(SERIAL_USART_RX_PAL_MODE)
27# define SERIAL_USART_RX_PAL_MODE 7
28# endif
29#endif
30
31#if !defined(SERIAL_USART_DRIVER)
32# define SERIAL_USART_DRIVER UARTD1
33#endif
34
35#if !defined(SERIAL_USART_TX_PIN)
36# define SERIAL_USART_TX_PIN A9
37#endif
38
39#if !defined(SERIAL_USART_RX_PIN)
40# define SERIAL_USART_RX_PIN A10
41#endif
42
43#define SIGNAL_HANDSHAKE_RECEIVED 0x1
44
45void handle_transactions_slave(uint8_t sstd_index);
46static void receive_transaction_handshake(UARTDriver* uartp, uint16_t received_handshake);
47
48/*
49 * UART driver configuration structure. We use the blocking DMA enabled API and
50 * the rxchar callback to receive handshake tokens but only on the slave halve.
51 */
52// clang-format off
53static UARTConfig uart_config = {
54 .txend1_cb = NULL,
55 .txend2_cb = NULL,
56 .rxend_cb = NULL,
57 .rxchar_cb = NULL,
58 .rxerr_cb = NULL,
59 .timeout_cb = NULL,
60 .speed = (SERIAL_USART_SPEED),
61 .cr1 = (SERIAL_USART_CR1),
62 .cr2 = (SERIAL_USART_CR2),
63 .cr3 = (SERIAL_USART_CR3)
64};
65// clang-format on
66
67static SSTD_t* Transaction_table = NULL;
68static uint8_t Transaction_table_size = 0;
69static atomic_uint_least8_t handshake = 0xFF;
70static thread_reference_t tp_target = NULL;
71
72/*
73 * This callback is invoked when a character is received but the application
74 * was not ready to receive it, the character is passed as parameter.
75 * Receive transaction table index from initiator, which doubles as basic handshake token. */
76static void receive_transaction_handshake(UARTDriver* uartp, uint16_t received_handshake) {
77 /* Check if received handshake is not a valid transaction id.
78 * Please note that we can still catch a seemingly valid handshake
79 * i.e. a byte from a ongoing transfer which is in the allowed range.
80 * So this check mainly prevents any obviously wrong handshakes and
81 * subsequent wakeups of the receiving thread, which is a costly operation. */
82 if (received_handshake > Transaction_table_size) {
83 return;
84 }
85
86 handshake = (uint8_t)received_handshake;
87 chSysLockFromISR();
88 /* Wakeup receiving thread to start a transaction. */
89 chEvtSignalI(tp_target, (eventmask_t)SIGNAL_HANDSHAKE_RECEIVED);
90 chSysUnlockFromISR();
91}
92
93__attribute__((weak)) void usart_init(void) {
94#if defined(USE_GPIOV1)
95 palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_STM32_ALTERNATE_PUSHPULL);
96 palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_INPUT);
97#else
98 palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST);
99 palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_RX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST);
100#endif
101}
102
103/*
104 * This thread runs on the slave half and reacts to transactions initiated from the master.
105 */
106static THD_WORKING_AREA(waSlaveThread, 1024);
107static THD_FUNCTION(SlaveThread, arg) {
108 (void)arg;
109 chRegSetThreadName("slave_usart_tx_rx");
110
111 while (true) {
112 /* We sleep as long as there is no handshake waiting for us. */
113 chEvtWaitAny((eventmask_t)SIGNAL_HANDSHAKE_RECEIVED);
114 handle_transactions_slave(handshake);
115 }
116}
117
118void soft_serial_target_init(SSTD_t* const sstd_table, int sstd_table_size) {
119 Transaction_table = sstd_table;
120 Transaction_table_size = (uint8_t)sstd_table_size;
121 usart_init();
122
123#if defined(USART_REMAP)
124 USART_REMAP;
125#endif
126
127 tp_target = chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL);
128
129 // Start receiving handshake tokens on slave halve
130 uart_config.rxchar_cb = receive_transaction_handshake;
131 uartStart(&SERIAL_USART_DRIVER, &uart_config);
132}
133
134/**
135 * @brief React to transactions started by the master.
136 * This version uses duplex send and receive usart pheriphals and DMA backed transfers.
137 */
138void inline handle_transactions_slave(uint8_t sstd_index) {
139 size_t buffer_size = 0;
140 msg_t msg = 0;
141 SSTD_t* trans = &Transaction_table[sstd_index];
142
143 /* Send back the handshake which is XORed as a simple checksum,
144 to signal that the slave is ready to receive possible transaction buffers */
145 sstd_index ^= HANDSHAKE_MAGIC;
146 buffer_size = (size_t)sizeof(sstd_index);
147 msg = uartSendTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index, TIME_MS2I(SERIAL_USART_TIMEOUT));
148
149 if (msg != MSG_OK) {
150 if (trans->status) {
151 *trans->status = TRANSACTION_NO_RESPONSE;
152 }
153 return;
154 }
155
156 /* Receive transaction buffer from the master. If this transaction requires it.*/
157 buffer_size = (size_t)trans->initiator2target_buffer_size;
158 if (buffer_size) {
159 msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->initiator2target_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT));
160 if (msg != MSG_OK) {
161 if (trans->status) {
162 *trans->status = TRANSACTION_NO_RESPONSE;
163 }
164 return;
165 }
166 }
167
168 /* Send transaction buffer to the master. If this transaction requires it. */
169 buffer_size = (size_t)trans->target2initiator_buffer_size;
170 if (buffer_size) {
171 msg = uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->target2initiator_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT));
172 if (msg != MSG_OK) {
173 if (trans->status) {
174 *trans->status = TRANSACTION_NO_RESPONSE;
175 }
176 return;
177 }
178 }
179
180 if (trans->status) {
181 *trans->status = TRANSACTION_ACCEPTED;
182 }
183}
184
185void soft_serial_initiator_init(SSTD_t* const sstd_table, int sstd_table_size) {
186 Transaction_table = sstd_table;
187 Transaction_table_size = (uint8_t)sstd_table_size;
188 usart_init();
189
190#if defined(SERIAL_USART_PIN_SWAP)
191 uart_config.cr2 |= USART_CR2_SWAP; // master has swapped TX/RX pins
192#endif
193
194#if defined(USART_REMAP)
195 USART_REMAP;
196#endif
197
198 uartStart(&SERIAL_USART_DRIVER, &uart_config);
199}
200
201/**
202 * @brief Start transaction from the master to the slave.
203 * This version uses duplex send and receive usart pheriphals and DMA backed transfers.
204 *
205 * @param index Transaction Table index of the transaction to start.
206 * @return int TRANSACTION_NO_RESPONSE in case of Timeout.
207 * TRANSACTION_TYPE_ERROR in case of invalid transaction index.
208 * TRANSACTION_END in case of success.
209 */
210#if !defined(SERIAL_USE_MULTI_TRANSACTION)
211int soft_serial_transaction(void) {
212 uint8_t sstd_index = 0;
213#else
214int soft_serial_transaction(int index) {
215 uint8_t sstd_index = index;
216#endif
217
218 if (sstd_index > Transaction_table_size) {
219 return TRANSACTION_TYPE_ERROR;
220 }
221
222 SSTD_t* const trans = &Transaction_table[sstd_index];
223 msg_t msg = 0;
224 size_t buffer_size = (size_t)sizeof(sstd_index);
225
226 /* Send transaction table index to the slave, which doubles as basic handshake token. */
227 uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index, TIME_MS2I(SERIAL_USART_TIMEOUT));
228
229 uint8_t sstd_index_shake = 0xFF;
230 buffer_size = (size_t)sizeof(sstd_index_shake);
231
232 /* Receive the handshake token from the slave. The token was XORed by the slave as a simple checksum.
233 If the tokens match, the master will start to send and receive possible transaction buffers. */
234 msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index_shake, TIME_MS2I(SERIAL_USART_TIMEOUT));
235 if (msg != MSG_OK || (sstd_index_shake != (sstd_index ^ HANDSHAKE_MAGIC))) {
236 dprintln("USART: Handshake Failed");
237 return TRANSACTION_NO_RESPONSE;
238 }
239
240 /* Send transaction buffer to the slave. If this transaction requires it. */
241 buffer_size = (size_t)trans->initiator2target_buffer_size;
242 if (buffer_size) {
243 msg = uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->initiator2target_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT));
244 if (msg != MSG_OK) {
245 dprintln("USART: Send Failed");
246 return TRANSACTION_NO_RESPONSE;
247 }
248 }
249
250 /* Receive transaction buffer from the slave. If this transaction requires it. */
251 buffer_size = (size_t)trans->target2initiator_buffer_size;
252 if (buffer_size) {
253 msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->target2initiator_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT));
254 if (msg != MSG_OK) {
255 dprintln("USART: Receive Failed");
256 return TRANSACTION_NO_RESPONSE;
257 }
258 }
259
260 return TRANSACTION_END;
261}