Skip to content

Commit 460dda0

Browse files
authored
Merge pull request adafruit#7616 from dhalbert/8.0.x-fix-atmel-uart
Fix pad assignments on atmel-samd UART
2 parents 9b6abea + 2684aeb commit 460dda0

File tree

7 files changed

+120
-67
lines changed

7 files changed

+120
-67
lines changed

locale/circuitpython.pot

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1987,10 +1987,6 @@ msgstr ""
19871987
msgid "Stopping AP is not supported."
19881988
msgstr ""
19891989

1990-
#: ports/mimxrt10xx/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c
1991-
msgid "Supply at least one UART pin"
1992-
msgstr ""
1993-
19941990
#: shared-bindings/alarm/time/TimeAlarm.c
19951991
msgid "Supply one of monotonic_time or epoch_time"
19961992
msgstr ""
@@ -4116,8 +4112,6 @@ msgstr ""
41164112
msgid "twai_start returned esp-idf error #%d"
41174113
msgstr ""
41184114

4119-
#: ports/atmel-samd/common-hal/busio/UART.c
4120-
#: ports/espressif/common-hal/busio/UART.c ports/nrf/common-hal/busio/UART.c
41214115
#: shared-bindings/busio/UART.c shared-bindings/canio/CAN.c
41224116
msgid "tx and rx cannot both be None"
41234117
msgstr ""

ports/atmel-samd/common-hal/busio/UART.c

Lines changed: 92 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ static void usart_async_rxc_callback(const struct usart_async_descriptor *const
5858
// Nothing needs to be done by us.
5959
}
6060

61+
// shared-bindings validates that the tx and rx are not both missing,
62+
// and that the pins are distinct.
6163
void common_hal_busio_uart_construct(busio_uart_obj_t *self,
6264
const mcu_pin_obj_t *tx, const mcu_pin_obj_t *rx,
6365
const mcu_pin_obj_t *rts, const mcu_pin_obj_t *cts,
@@ -92,10 +94,6 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self,
9294
bool have_rts = rts != NULL;
9395
bool have_cts = cts != NULL;
9496

95-
if (!have_tx && !have_rx) {
96-
mp_raise_ValueError(translate("tx and rx cannot both be None"));
97-
}
98-
9997
if (have_rx && receiver_buffer_size > 0 && (receiver_buffer_size & (receiver_buffer_size - 1)) != 0) {
10098
mp_raise_ValueError_varg(translate("%q must be power of 2"), MP_QSTR_receiver_buffer_size);
10199
}
@@ -107,6 +105,20 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self,
107105
// This assignment is only here because the usart_async routines take a *const argument.
108106
struct usart_async_descriptor *const usart_desc_p = (struct usart_async_descriptor *const)&self->usart_desc;
109107

108+
// Allowed pads for USART. See the SAMD21 and SAMx5x datasheets.
109+
// TXPO:
110+
// (both) 0x0: TX pad 0; no RTS/CTS
111+
// (SAMD21) 0x1: TX pad 2; no RTS/CTS
112+
// (SAMx5x) 0x1: reserved
113+
// (both) 0x2: TX pad 0; RTS: pad 2, CTS: pad 3
114+
// (SAMD21) 0x3: reserved
115+
// (SAMx5x) 0x3: TX pad 0; RTS: pad 2; no CTS
116+
// RXPO:
117+
// 0x0: RX pad 0
118+
// 0x1: RX pad 1
119+
// 0x2: RX pad 2
120+
// 0x3: RX pad 3
121+
110122
for (int i = 0; i < NUM_SERCOMS_PER_PIN; i++) {
111123
Sercom *potential_sercom = NULL;
112124
if (have_tx) {
@@ -115,29 +127,71 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self,
115127
continue;
116128
}
117129
potential_sercom = sercom_insts[sercom_index];
130+
131+
// SAMD21 and SAMx5x have different requirements.
132+
118133
#ifdef SAMD21
119-
if (potential_sercom->USART.CTRLA.bit.ENABLE != 0 ||
120-
!(tx->sercom[i].pad == 0 ||
121-
tx->sercom[i].pad == 2)) {
134+
if (potential_sercom->USART.CTRLA.bit.ENABLE != 0) {
135+
// In use.
122136
continue;
123137
}
138+
if (tx->sercom[i].pad != 0 &&
139+
tx->sercom[i].pad != 2) {
140+
// TX must be on pad 0 or 2.
141+
continue;
142+
}
143+
if (have_rts) {
144+
if (rts->sercom[i].pad != 2 ||
145+
tx->sercom[i].pad == 2) {
146+
// RTS pin must be on pad 2, so if TX is also on pad 2, not possible
147+
continue;
148+
}
149+
}
150+
if (have_cts) {
151+
if (cts->sercom[i].pad != 3 ||
152+
(have_rx && rx->sercom[i].pad == 3)) {
153+
// CTS pin must be on pad 3, so if RX is also on pad 3, not possible
154+
continue;
155+
}
156+
}
124157
#endif
158+
125159
#ifdef SAM_D5X_E5X
126-
if (potential_sercom->USART.CTRLA.bit.ENABLE != 0 ||
127-
!(tx->sercom[i].pad == 0)) {
160+
if (potential_sercom->USART.CTRLA.bit.ENABLE != 0) {
161+
// In use.
162+
continue;
163+
}
164+
if (tx->sercom[i].pad != 0) {
165+
// TX must be on pad 0
166+
continue;
167+
}
168+
169+
if (have_rts && rts->sercom[i].pad != 2) {
170+
// RTS pin must be on pad 2
128171
continue;
129172
}
173+
if (have_cts) {
174+
if (cts->sercom[i].pad != 3 ||
175+
(have_rx && rx->sercom[i].pad == 3)) {
176+
// CTS pin must be on pad 3, so if RX is also on pad 3, not possible
177+
continue;
178+
}
179+
}
130180
#endif
181+
131182
tx_pinmux = PINMUX(tx->number, (i == 0) ? MUX_C : MUX_D);
132183
tx_pad = tx->sercom[i].pad;
133184
if (have_rts) {
134185
rts_pinmux = PINMUX(rts->number, (i == 0) ? MUX_C : MUX_D);
135186
}
136-
if (rx == NULL) {
187+
if (!have_rx) {
188+
// TX only, so don't need to look further.
137189
sercom = potential_sercom;
138190
break;
139191
}
140192
}
193+
194+
// Have TX, now look for RX match. We know have_rx is true at this point.
141195
for (int j = 0; j < NUM_SERCOMS_PER_PIN; j++) {
142196
if (((!have_tx && rx->sercom[j].index < SERCOM_INST_NUM &&
143197
sercom_insts[rx->sercom[j].index]->USART.CTRLA.bit.ENABLE == 0) ||
@@ -160,20 +214,10 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self,
160214
if (sercom == NULL) {
161215
raise_ValueError_invalid_pins();
162216
}
163-
if (!have_tx) {
164-
tx_pad = 0;
165-
if (rx_pad == 0) {
166-
tx_pad = 2;
167-
}
168-
}
169-
if (!have_rx) {
170-
rx_pad = (tx_pad + 1) % 4;
171-
}
172-
173217
// Set up clocks on SERCOM.
174218
samd_peripherals_sercom_clock_init(sercom, sercom_index);
175219

176-
if (rx && receiver_buffer_size > 0) {
220+
if (have_rx && receiver_buffer_size > 0) {
177221
self->buffer_length = receiver_buffer_size;
178222
if (NULL != receiver_buffer) {
179223
self->buffer = receiver_buffer;
@@ -204,36 +248,41 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self,
204248
// which don't necessarily match what we need. After calling it, set the values
205249
// specific to this instantiation of UART.
206250

207-
// Set pads computed for this SERCOM. Refer to the datasheet for details on pads.
208-
// TXPO:
209-
// 0x0: TX pad 0; no RTS/CTS
210-
// 0x1: reserved
211-
// 0x2: TX pad 0; RTS: pad 2, CTS: pad 3
212-
// 0x3: TX pad 0; RTS: pad 2; no CTS
213-
// RXPO:
214-
// 0x0: RX pad 0
215-
// 0x1: RX pad 1
216-
// 0x2: RX pad 2
217-
// 0x3: RX pad 3
251+
// See the TXPO/RXPO table above for how RXPO and TXPO are chosen below.
218252

219-
// Default to TXPO with no RTS/CTS
220-
uint8_t computed_txpo = 0;
221-
// If we have both CTS (with or without RTS), use second pinout
222-
if (have_cts) {
223-
computed_txpo = 2;
224-
}
225-
// If we have RTS only, use the third pinout
226-
if (have_rts && !have_cts) {
227-
computed_txpo = 3;
253+
// rxpo maps directly to rx_pad.
254+
// Set to 0x0 if no RX, but it doesn't matter because RX will not be enabled.
255+
const uint8_t rxpo = have_rx ? rx_pad : 0x0;
256+
257+
#ifdef SAMD21
258+
// SAMD21 has only one txpo value when using either CTS or RTS or both.
259+
// TX is on pad 0 or 2, or there is no TX.
260+
// 0x0 for pad 0, 0x1 for pad 2.
261+
uint8_t txpo;
262+
if (tx_pad == 2) {
263+
txpo = 0x1;
264+
} else {
265+
txpo = (have_cts || have_rts) ? 0x2 : 0x0;
228266
}
267+
#endif
268+
269+
#ifdef SAM_D5X_E5X
270+
// SAMx5x has two different possibilities, per the chart above.
271+
// We already know TX is on pad 0, or there is no TX.
272+
273+
// Without RTS or CTS, txpo can be 0x0.
274+
// It's not clear if 0x2 would cover all our cases, but this is known to be safe.
275+
uint8_t txpo = (have_rts || have_cts) ? 0x2: 0x0;
276+
#endif
229277

230278
// Doing a group mask and set of the registers saves 60 bytes over setting the bitfields individually.
231279

232280
sercom->USART.CTRLA.reg &= ~(SERCOM_USART_CTRLA_TXPO_Msk |
233281
SERCOM_USART_CTRLA_RXPO_Msk |
234282
SERCOM_USART_CTRLA_FORM_Msk);
235-
sercom->USART.CTRLA.reg |= SERCOM_USART_CTRLA_TXPO(computed_txpo) |
236-
SERCOM_USART_CTRLA_RXPO(rx_pad) |
283+
// See chart above for TXPO values and RXPO values.
284+
sercom->USART.CTRLA.reg |= SERCOM_USART_CTRLA_TXPO(txpo) |
285+
SERCOM_USART_CTRLA_RXPO(rxpo) |
237286
(parity == BUSIO_UART_PARITY_NONE ? 0 : SERCOM_USART_CTRLA_FORM(1));
238287

239288
// Enable tx and/or rx based on whether the pins were specified.

ports/espressif/common-hal/busio/UART.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,8 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self,
112112

113113
uart_config_t uart_config = {0};
114114
bool have_rs485_dir = rs485_dir != NULL;
115-
if (!have_tx && !have_rx) {
116-
mp_raise_ValueError(translate("tx and rx cannot both be None"));
117-
}
115+
116+
// shared-bindings checks that TX and RX are not both None, so we don't need to check here.
118117

119118
// Filter for sane settings for RS485
120119
if (have_rs485_dir) {

ports/mimxrt10xx/common-hal/busio/UART.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self,
179179
break;
180180
}
181181
} else {
182-
mp_raise_ValueError(translate("Supply at least one UART pin"));
182+
// TX and RX are both None. But this is already handled in shared-bindings, so
183+
// we won't get here.
183184
}
184185

185186
if (rx && !rx_config) {

ports/nrf/common-hal/busio/UART.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,9 +183,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self,
183183
mp_raise_ValueError(translate("All UART peripherals are in use"));
184184
}
185185

186-
if ((tx == NULL) && (rx == NULL)) {
187-
mp_raise_ValueError(translate("tx and rx cannot both be None"));
188-
}
186+
// shared-bindings checks that TX and RX are not both None, so we don't need to check here.
189187

190188
mp_arg_validate_int_min(receiver_buffer_size, 1, MP_QSTR_receiver_buffer_size);
191189

ports/stm/common-hal/busio/UART.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self,
8585
bool sigint_enabled) {
8686

8787
// match pins to UART objects
88-
USART_TypeDef *USARTx;
88+
USART_TypeDef *USARTx = NULL;
8989

9090
uint8_t tx_len = MP_ARRAY_SIZE(mcu_uart_tx_list);
9191
uint8_t rx_len = MP_ARRAY_SIZE(mcu_uart_rx_list);
@@ -159,8 +159,8 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self,
159159
USARTx = assign_uart_or_throw(self, (self->tx != NULL),
160160
periph_index, uart_taken);
161161
} else {
162-
// both pins cannot be empty
163-
mp_raise_ValueError(translate("Supply at least one UART pin"));
162+
// TX and RX are both None. But this is already handled in shared-bindings, so
163+
// we won't get here.
164164
}
165165

166166
// Other errors

shared-bindings/busio/UART.c

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,13 @@
4848
//|
4949
//| def __init__(
5050
//| self,
51-
//| tx: microcontroller.Pin,
52-
//| rx: microcontroller.Pin,
51+
//| tx: Optional[microcontroller.Pin] = None,
52+
//| rx: Optional[microcontroller.Pin] = None,
5353
//| *,
54+
//| rts: Optional[microcontroller.Pin] = None,
55+
//| cts: Optional[microcontroller.Pin] = None,
56+
//| rs485_dir: Optional[microcontroller.Pin] = None,
57+
//| rs485_invert: bool = False,
5458
//| baudrate: int = 9600,
5559
//| bits: int = 8,
5660
//| parity: Optional[Parity] = None,
@@ -74,11 +78,13 @@
7478
//| :param float timeout: the timeout in seconds to wait for the first character and between subsequent characters when reading. Raises ``ValueError`` if timeout >100 seconds.
7579
//| :param int receiver_buffer_size: the character length of the read buffer (0 to disable). (When a character is 9 bits the buffer will be 2 * receiver_buffer_size bytes.)
7680
//|
81+
//| ``tx`` and ``rx`` cannot both be ``None``.
82+
//|
7783
//| *New in CircuitPython 4.0:* ``timeout`` has incompatibly changed units from milliseconds to seconds.
7884
//| The new upper limit on ``timeout`` is meant to catch mistaken use of milliseconds.
7985
//|
8086
//| **Limitations:** RS485 is not supported on SAMD, nRF, Broadcom, Spresense, or STM.
81-
//| On i.MX and Raspberry Pi RP2040 support is implemented in software:
87+
//| On i.MX and Raspberry Pi RP2040, RS485 support is implemented in software:
8288
//| The timing for the ``rs485_dir`` pin signal is done on a best-effort basis, and may not meet
8389
//| RS485 specifications intermittently.
8490
//| """
@@ -118,11 +124,21 @@ STATIC mp_obj_t busio_uart_make_new(const mp_obj_type_t *type, size_t n_args, si
118124

119125
const mcu_pin_obj_t *rx = validate_obj_is_free_pin_or_none(args[ARG_rx].u_obj, MP_QSTR_rx);
120126
const mcu_pin_obj_t *tx = validate_obj_is_free_pin_or_none(args[ARG_tx].u_obj, MP_QSTR_tx);
121-
127+
const mcu_pin_obj_t *rts = validate_obj_is_free_pin_or_none(args[ARG_rts].u_obj, MP_QSTR_rts);
128+
const mcu_pin_obj_t *cts = validate_obj_is_free_pin_or_none(args[ARG_cts].u_obj, MP_QSTR_cts);
129+
const mcu_pin_obj_t *rs485_dir = validate_obj_is_free_pin_or_none(args[ARG_rs485_dir].u_obj, MP_QSTR_rs485_dir);
122130
if ((tx == NULL) && (rx == NULL)) {
123131
mp_raise_ValueError(translate("tx and rx cannot both be None"));
124132
}
125133

134+
// Pins must be distinct.
135+
if ((tx != NULL && (tx == rx || tx == rts || tx == cts || tx == rs485_dir)) ||
136+
(rx != NULL && (rx == rts || rx == cts || rx == rs485_dir)) ||
137+
(rts != NULL && (rts == cts || rts == rs485_dir)) ||
138+
(cts != NULL && (cts == rs485_dir))) {
139+
raise_ValueError_invalid_pins();
140+
}
141+
126142
uint8_t bits = (uint8_t)mp_arg_validate_int_range(args[ARG_bits].u_int, 5, 9, MP_QSTR_bits);
127143

128144
busio_uart_parity_t parity = BUSIO_UART_PARITY_NONE;
@@ -137,10 +153,6 @@ STATIC mp_obj_t busio_uart_make_new(const mp_obj_type_t *type, size_t n_args, si
137153
mp_float_t timeout = mp_obj_get_float(args[ARG_timeout].u_obj);
138154
validate_timeout(timeout);
139155

140-
const mcu_pin_obj_t *rts = validate_obj_is_free_pin_or_none(args[ARG_rts].u_obj, MP_QSTR_rts);
141-
const mcu_pin_obj_t *cts = validate_obj_is_free_pin_or_none(args[ARG_cts].u_obj, MP_QSTR_cts);
142-
const mcu_pin_obj_t *rs485_dir = validate_obj_is_free_pin_or_none(args[ARG_rs485_dir].u_obj, MP_QSTR_rs485_dir);
143-
144156
const bool rs485_invert = args[ARG_rs485_invert].u_bool;
145157

146158
// Always initially allocate the UART object within the long-lived heap.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy