@@ -58,6 +58,8 @@ static void usart_async_rxc_callback(const struct usart_async_descriptor *const
58
58
// Nothing needs to be done by us.
59
59
}
60
60
61
+ // shared-bindings validates that the tx and rx are not both missing,
62
+ // and that the pins are distinct.
61
63
void common_hal_busio_uart_construct (busio_uart_obj_t * self ,
62
64
const mcu_pin_obj_t * tx , const mcu_pin_obj_t * rx ,
63
65
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,
92
94
bool have_rts = rts != NULL ;
93
95
bool have_cts = cts != NULL ;
94
96
95
- if (!have_tx && !have_rx ) {
96
- mp_raise_ValueError (translate ("tx and rx cannot both be None" ));
97
- }
98
-
99
97
if (have_rx && receiver_buffer_size > 0 && (receiver_buffer_size & (receiver_buffer_size - 1 )) != 0 ) {
100
98
mp_raise_ValueError_varg (translate ("%q must be power of 2" ), MP_QSTR_receiver_buffer_size );
101
99
}
@@ -107,6 +105,20 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self,
107
105
// This assignment is only here because the usart_async routines take a *const argument.
108
106
struct usart_async_descriptor * const usart_desc_p = (struct usart_async_descriptor * const )& self -> usart_desc ;
109
107
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
+
110
122
for (int i = 0 ; i < NUM_SERCOMS_PER_PIN ; i ++ ) {
111
123
Sercom * potential_sercom = NULL ;
112
124
if (have_tx ) {
@@ -115,29 +127,71 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self,
115
127
continue ;
116
128
}
117
129
potential_sercom = sercom_insts [sercom_index ];
130
+
131
+ // SAMD21 and SAMx5x have different requirements.
132
+
118
133
#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.
122
136
continue ;
123
137
}
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
+ }
124
157
#endif
158
+
125
159
#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
128
171
continue ;
129
172
}
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
+ }
130
180
#endif
181
+
131
182
tx_pinmux = PINMUX (tx -> number , (i == 0 ) ? MUX_C : MUX_D );
132
183
tx_pad = tx -> sercom [i ].pad ;
133
184
if (have_rts ) {
134
185
rts_pinmux = PINMUX (rts -> number , (i == 0 ) ? MUX_C : MUX_D );
135
186
}
136
- if (rx == NULL ) {
187
+ if (!have_rx ) {
188
+ // TX only, so don't need to look further.
137
189
sercom = potential_sercom ;
138
190
break ;
139
191
}
140
192
}
193
+
194
+ // Have TX, now look for RX match. We know have_rx is true at this point.
141
195
for (int j = 0 ; j < NUM_SERCOMS_PER_PIN ; j ++ ) {
142
196
if (((!have_tx && rx -> sercom [j ].index < SERCOM_INST_NUM &&
143
197
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,
160
214
if (sercom == NULL ) {
161
215
raise_ValueError_invalid_pins ();
162
216
}
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
-
173
217
// Set up clocks on SERCOM.
174
218
samd_peripherals_sercom_clock_init (sercom , sercom_index );
175
219
176
- if (rx && receiver_buffer_size > 0 ) {
220
+ if (have_rx && receiver_buffer_size > 0 ) {
177
221
self -> buffer_length = receiver_buffer_size ;
178
222
if (NULL != receiver_buffer ) {
179
223
self -> buffer = receiver_buffer ;
@@ -204,36 +248,41 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self,
204
248
// which don't necessarily match what we need. After calling it, set the values
205
249
// specific to this instantiation of UART.
206
250
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.
218
252
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 ;
228
266
}
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
229
277
230
278
// Doing a group mask and set of the registers saves 60 bytes over setting the bitfields individually.
231
279
232
280
sercom -> USART .CTRLA .reg &= ~(SERCOM_USART_CTRLA_TXPO_Msk |
233
281
SERCOM_USART_CTRLA_RXPO_Msk |
234
282
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 ) |
237
286
(parity == BUSIO_UART_PARITY_NONE ? 0 : SERCOM_USART_CTRLA_FORM (1 ));
238
287
239
288
// Enable tx and/or rx based on whether the pins were specified.
0 commit comments