uart.c 36.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
 * This file is part of the Micro Python project, http://micropython.org/
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2013, 2014 Damien P. George
 *
 * 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.
 */

27
28
#include <stdio.h>
#include <string.h>
29
#include <stdarg.h>
30

31
32
33
#include "py/nlr.h"
#include "py/runtime.h"
#include "py/stream.h"
34
#include "py/mperrno.h"
35
#include "py/mphal.h"
Damien George's avatar
Damien George committed
36
#include "uart.h"
37
#include "irq.h"
38
#include "genhdr/pins.h"
39

40
41
42
43
/// \moduleref pyb
/// \class UART - duplex serial communication bus
///
/// UART implements the standard UART/USART duplex serial communications protocol.  At
44
45
46
/// the physical level it consists of 2 lines: RX and TX.  The unit of communication
/// is a character (not to be confused with a string character) which can be 8 or 9
/// bits wide.
47
///
48
/// UART objects can be created and initialised using:
49
50
51
52
///
///     from pyb import UART
///
///     uart = UART(1, 9600)                         # init with given baudrate
53
///     uart.init(9600, bits=8, parity=None, stop=1) # init with given parameters
54
///
55
/// Bits can be 8 or 9.  Parity can be None, 0 (even) or 1 (odd).  Stop can be 1 or 2.
56
///
57
58
59
60
/// A UART object acts like a stream object and reading and writing is done
/// using the standard stream methods:
///
///     uart.read(10)       # read 10 characters, returns a bytes object
61
///     uart.read()         # read all available characters
62
63
64
65
66
67
68
69
70
71
///     uart.readline()     # read a line
///     uart.readinto(buf)  # read and store into the given buffer
///     uart.write('abc')   # write the 3 characters
///
/// Individual characters can be read/written using:
///
///     uart.readchar()     # read 1 character and returns it as an integer
///     uart.writechar(42)  # write 1 character
///
/// To check if there is anything to be read, use:
72
73
///
///     uart.any()               # returns True if any characters waiting
74

75
76
77
#define CHAR_WIDTH_8BIT (0)
#define CHAR_WIDTH_9BIT (1)

Damien George's avatar
Damien George committed
78
struct _pyb_uart_obj_t {
Dave Hylands's avatar
Dave Hylands committed
79
    mp_obj_base_t base;
80
    UART_HandleTypeDef uart;            // this is 17 words big
81
    IRQn_Type irqn;
82
83
84
85
    pyb_uart_t uart_id : 8;
    bool is_enabled : 1;
    byte char_width;                    // 0 for 7,8 bit chars, 1 for 9 bit chars
    uint16_t char_mask;                 // 0x7f for 7 bit, 0xff for 8 bit, 0x1ff for 9 bit
86
87
88
89
90
91
    uint16_t timeout;                   // timeout waiting for first char
    uint16_t timeout_char;              // timeout waiting between chars
    uint16_t read_buf_len;              // len in chars; buf can hold len-1 chars
    volatile uint16_t read_buf_head;    // indexes first empty slot
    uint16_t read_buf_tail;             // indexes first full slot (not full if equals head)
    byte *read_buf;                     // byte or uint16_t, depending on char size
Dave Hylands's avatar
Dave Hylands committed
92
};
93

94
95
96
STATIC mp_obj_t pyb_uart_deinit(mp_obj_t self_in);

void uart_init0(void) {
97
98
    for (int i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(pyb_uart_obj_all)); i++) {
        MP_STATE_PORT(pyb_uart_obj_all)[i] = NULL;
99
100
101
102
103
    }
}

// unregister all interrupt sources
void uart_deinit(void) {
104
105
    for (int i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(pyb_uart_obj_all)); i++) {
        pyb_uart_obj_t *uart_obj = MP_STATE_PORT(pyb_uart_obj_all)[i];
106
107
108
109
110
111
        if (uart_obj != NULL) {
            pyb_uart_deinit(uart_obj);
        }
    }
}

112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
STATIC bool uart_exists(int uart_id) {
    if (uart_id > MP_ARRAY_SIZE(MP_STATE_PORT(pyb_uart_obj_all))) {
        // safeguard against pyb_uart_obj_all array being configured too small
        return false;
    }
    switch (uart_id) {
        #if defined(MICROPY_HW_UART1_TX) && defined(MICROPY_HW_UART1_RX)
        case PYB_UART_1: return true;
        #endif

        #if defined(MICROPY_HW_UART2_TX) && defined(MICROPY_HW_UART2_RX)
        case PYB_UART_2: return true;
        #endif

        #if defined(MICROPY_HW_UART3_TX) && defined(MICROPY_HW_UART3_RX)
        case PYB_UART_3: return true;
        #endif

        #if defined(MICROPY_HW_UART4_TX) && defined(MICROPY_HW_UART4_RX)
        case PYB_UART_4: return true;
        #endif

        #if defined(MICROPY_HW_UART5_TX) && defined(MICROPY_HW_UART5_RX)
        case PYB_UART_5: return true;
        #endif

        #if defined(MICROPY_HW_UART6_TX) && defined(MICROPY_HW_UART6_RX)
        case PYB_UART_6: return true;
        #endif

        #if defined(MICROPY_HW_UART7_TX) && defined(MICROPY_HW_UART7_RX)
        case PYB_UART_7: return true;
        #endif

        #if defined(MICROPY_HW_UART8_TX) && defined(MICROPY_HW_UART8_RX)
        case PYB_UART_8: return true;
        #endif

        default: return false;
    }
}

154
// assumes Init parameters have been set up correctly
155
STATIC bool uart_init2(pyb_uart_obj_t *uart_obj) {
156
157
    USART_TypeDef *UARTx;
    IRQn_Type irqn;
158
    int uart_unit;
159

160
    const pin_obj_t *pins[4] = {0};
161

162
163
    switch (uart_obj->uart_id) {
        #if defined(MICROPY_HW_UART1_TX) && defined(MICROPY_HW_UART1_RX)
164
        case PYB_UART_1:
165
            uart_unit = 1;
166
167
            UARTx = USART1;
            irqn = USART1_IRQn;
168
169
            pins[0] = &MICROPY_HW_UART1_TX;
            pins[1] = &MICROPY_HW_UART1_RX;
170
171
172
173
            __USART1_CLK_ENABLE();
            break;
        #endif

174
        #if defined(MICROPY_HW_UART2_TX) && defined(MICROPY_HW_UART2_RX)
Damien George's avatar
Damien George committed
175
        case PYB_UART_2:
176
            uart_unit = 2;
Damien George's avatar
Damien George committed
177
            UARTx = USART2;
178
            irqn = USART2_IRQn;
179
180
            pins[0] = &MICROPY_HW_UART2_TX;
            pins[1] = &MICROPY_HW_UART2_RX;
181
            #if defined(MICROPY_HW_UART2_RTS)
182
            if (uart_obj->uart.Init.HwFlowCtl & UART_HWCONTROL_RTS) {
183
                pins[2] = &MICROPY_HW_UART2_RTS;
184
            }
185
186
            #endif
            #if defined(MICROPY_HW_UART2_CTS)
187
            if (uart_obj->uart.Init.HwFlowCtl & UART_HWCONTROL_CTS) {
188
                pins[3] = &MICROPY_HW_UART2_CTS;
189
            }
190
            #endif
191
192
            __USART2_CLK_ENABLE();
            break;
193
        #endif
194

195
        #if defined(MICROPY_HW_UART3_TX) && defined(MICROPY_HW_UART3_RX)
Damien George's avatar
Damien George committed
196
        case PYB_UART_3:
197
            uart_unit = 3;
Damien George's avatar
Damien George committed
198
            UARTx = USART3;
199
            irqn = USART3_IRQn;
200
201
            pins[0] = &MICROPY_HW_UART3_TX;
            pins[1] = &MICROPY_HW_UART3_RX;
202
            #if defined(MICROPY_HW_UART3_RTS)
203
            if (uart_obj->uart.Init.HwFlowCtl & UART_HWCONTROL_RTS) {
204
                pins[2] = &MICROPY_HW_UART3_RTS;
205
            }
206
207
            #endif
            #if defined(MICROPY_HW_UART3_CTS)
208
            if (uart_obj->uart.Init.HwFlowCtl & UART_HWCONTROL_CTS) {
209
                pins[3] = &MICROPY_HW_UART3_CTS;
210
            }
211
            #endif
212
213
            __USART3_CLK_ENABLE();
            break;
214
        #endif
215

216
        #if defined(MICROPY_HW_UART4_TX) && defined(MICROPY_HW_UART4_RX)
Damien George's avatar
Damien George committed
217
        case PYB_UART_4:
218
            uart_unit = 4;
Damien George's avatar
Damien George committed
219
            UARTx = UART4;
220
            irqn = UART4_IRQn;
221
222
            pins[0] = &MICROPY_HW_UART4_TX;
            pins[1] = &MICROPY_HW_UART4_RX;
223
224
            __UART4_CLK_ENABLE();
            break;
225
        #endif
226

227
        #if defined(MICROPY_HW_UART5_TX) && defined(MICROPY_HW_UART5_RX)
Dave Hylands's avatar
Dave Hylands committed
228
        case PYB_UART_5:
229
            uart_unit = 5;
Dave Hylands's avatar
Dave Hylands committed
230
231
            UARTx = UART5;
            irqn = UART5_IRQn;
232
233
            pins[0] = &MICROPY_HW_UART5_TX;
            pins[1] = &MICROPY_HW_UART5_RX;
Dave Hylands's avatar
Dave Hylands committed
234
235
236
237
            __UART5_CLK_ENABLE();
            break;
        #endif

238
        #if defined(MICROPY_HW_UART6_TX) && defined(MICROPY_HW_UART6_RX)
Damien George's avatar
Damien George committed
239
        case PYB_UART_6:
240
            uart_unit = 6;
Damien George's avatar
Damien George committed
241
            UARTx = USART6;
242
            irqn = USART6_IRQn;
243
244
            pins[0] = &MICROPY_HW_UART6_TX;
            pins[1] = &MICROPY_HW_UART6_RX;
245
246
            __USART6_CLK_ENABLE();
            break;
247
        #endif
248

249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
        #if defined(MICROPY_HW_UART7_TX) && defined(MICROPY_HW_UART7_RX)
        case PYB_UART_7:
            uart_unit = 7;
            UARTx = UART7;
            irqn = UART7_IRQn;
            pins[0] = &MICROPY_HW_UART7_TX;
            pins[1] = &MICROPY_HW_UART7_RX;
            __UART7_CLK_ENABLE();
            break;
        #endif

        #if defined(MICROPY_HW_UART8_TX) && defined(MICROPY_HW_UART8_RX)
        case PYB_UART_8:
            uart_unit = 8;
            UARTx = UART8;
            irqn = UART8_IRQn;
            pins[0] = &MICROPY_HW_UART8_TX;
            pins[1] = &MICROPY_HW_UART8_RX;
            __UART8_CLK_ENABLE();
            break;
        #endif

271
        default:
272
            // UART does not exist or is not configured for this board
273
            return false;
274
275
    }

276
277
    uint32_t mode = MP_HAL_PIN_MODE_ALT;
    uint32_t pull = MP_HAL_PIN_PULL_UP;
278

279
280
281
282
283
284
285
    for (uint i = 0; i < 4; i++) {
        if (pins[i] != NULL) {
            bool ret = mp_hal_pin_config_alt(pins[i], mode, pull, AF_FN_UART, uart_unit);
            if (!ret) {
                return false;
            }
        }
286
287
    }

288
289
290
    uart_obj->irqn = irqn;
    uart_obj->uart.Instance = UARTx;

Damien George's avatar
Damien George committed
291
292
    // init UARTx
    HAL_UART_Init(&uart_obj->uart);
293

Damien George's avatar
Damien George committed
294
    uart_obj->is_enabled = true;
295
296
297
298

    return true;
}

299
/* obsolete and unused
Damien George's avatar
Damien George committed
300
301
bool uart_init(pyb_uart_obj_t *uart_obj, uint32_t baudrate) {
    UART_HandleTypeDef *uh = &uart_obj->uart;
Dave Hylands's avatar
Dave Hylands committed
302
303
    memset(uh, 0, sizeof(*uh));
    uh->Init.BaudRate = baudrate;
304
305
306
307
    uh->Init.WordLength = UART_WORDLENGTH_8B;
    uh->Init.StopBits = UART_STOPBITS_1;
    uh->Init.Parity = UART_PARITY_NONE;
    uh->Init.Mode = UART_MODE_TX_RX;
Dave Hylands's avatar
Dave Hylands committed
308
309
    uh->Init.HwFlowCtl = UART_HWCONTROL_NONE;
    uh->Init.OverSampling = UART_OVERSAMPLING_16;
Damien George's avatar
Damien George committed
310
    return uart_init2(uart_obj);
311
}
312
*/
313

314
315
316
317
318
319
320
321
322
mp_uint_t uart_rx_any(pyb_uart_obj_t *self) {
    int buffer_bytes = self->read_buf_head - self->read_buf_tail;
    if (buffer_bytes < 0) {
        return buffer_bytes + self->read_buf_len;
    } else if (buffer_bytes > 0) {
        return buffer_bytes;
    } else {
        return __HAL_UART_GET_FLAG(&self->uart, UART_FLAG_RXNE) != RESET;
    }
323
324
}

325
326
327
328
329
330
331
332
333
334
335
336
// Waits at most timeout milliseconds for at least 1 char to become ready for
// reading (from buf or for direct reading).
// Returns true if something available, false if not.
STATIC bool uart_rx_wait(pyb_uart_obj_t *self, uint32_t timeout) {
    uint32_t start = HAL_GetTick();
    for (;;) {
        if (self->read_buf_tail != self->read_buf_head || __HAL_UART_GET_FLAG(&self->uart, UART_FLAG_RXNE) != RESET) {
            return true; // have at least 1 char ready for reading
        }
        if (HAL_GetTick() - start >= timeout) {
            return false; // timeout
        }
337
        MICROPY_EVENT_POLL_HOOK
338
    }
339
340
}

341
342
343
344
345
346
347
348
349
350
351
// assumes there is a character available
int uart_rx_char(pyb_uart_obj_t *self) {
    if (self->read_buf_tail != self->read_buf_head) {
        // buffering via IRQ
        int data;
        if (self->char_width == CHAR_WIDTH_9BIT) {
            data = ((uint16_t*)self->read_buf)[self->read_buf_tail];
        } else {
            data = self->read_buf[self->read_buf_tail];
        }
        self->read_buf_tail = (self->read_buf_tail + 1) % self->read_buf_len;
352
353
354
355
        if (__HAL_UART_GET_FLAG(&self->uart, UART_FLAG_RXNE) != RESET) {
            // UART was stalled by flow ctrl: re-enable IRQ now we have room in buffer
            __HAL_UART_ENABLE_IT(&self->uart, UART_IT_RXNE);
        }
356
357
358
        return data;
    } else {
        // no buffering
359
        #if defined(MCU_SERIES_F7) || defined(MCU_SERIES_L4)
360
361
        return self->uart.Instance->RDR & self->char_mask;
        #else
362
        return self->uart.Instance->DR & self->char_mask;
363
        #endif
364
365
366
    }
}

367
368
369
370
371
372
373
374
375
376
377
// Waits at most timeout milliseconds for TX register to become empty.
// Returns true if can write, false if can't.
STATIC bool uart_tx_wait(pyb_uart_obj_t *self, uint32_t timeout) {
    uint32_t start = HAL_GetTick();
    for (;;) {
        if (__HAL_UART_GET_FLAG(&self->uart, UART_FLAG_TXE)) {
            return true; // tx register is empty
        }
        if (HAL_GetTick() - start >= timeout) {
            return false; // timeout
        }
378
        MICROPY_EVENT_POLL_HOOK
379
380
381
    }
}

382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
// Waits at most timeout milliseconds for UART flag to be set.
// Returns true if flag is/was set, false on timeout.
STATIC bool uart_wait_flag_set(pyb_uart_obj_t *self, uint32_t flag, uint32_t timeout) {
    // Note: we don't use WFI to idle in this loop because UART tx doesn't generate
    // an interrupt and the flag can be set quickly if the baudrate is large.
    uint32_t start = HAL_GetTick();
    for (;;) {
        if (__HAL_UART_GET_FLAG(&self->uart, flag)) {
            return true;
        }
        if (timeout == 0 || HAL_GetTick() - start >= timeout) {
            return false; // timeout
        }
    }
}

// src - a pointer to the data to send (16-bit aligned for 9-bit chars)
// num_chars - number of characters to send (9-bit chars count for 2 bytes from src)
// *errcode - returns 0 for success, MP_Exxx on error
// returns the number of characters sent (valid even if there was an error)
STATIC size_t uart_tx_data(pyb_uart_obj_t *self, const void *src_in, size_t num_chars, int *errcode) {
    if (num_chars == 0) {
        *errcode = 0;
        return 0;
    }

    uint32_t timeout;
409
410
411
    if (self->uart.Init.HwFlowCtl & UART_HWCONTROL_CTS) {
        // CTS can hold off transmission for an arbitrarily long time. Apply
        // the overall timeout rather than the character timeout.
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
        timeout = self->timeout;
    } else {
        // The timeout specified here is for waiting for the TX data register to
        // become empty (ie between chars), as well as for the final char to be
        // completely transferred.  The default value for timeout_char is long
        // enough for 1 char, but we need to double it to wait for the last char
        // to be transferred to the data register, and then to be transmitted.
        timeout = 2 * self->timeout_char;
    }

    const uint8_t *src = (const uint8_t*)src_in;
    size_t num_tx = 0;
    USART_TypeDef *uart = self->uart.Instance;

    while (num_tx < num_chars) {
        if (!uart_wait_flag_set(self, UART_FLAG_TXE, timeout)) {
            *errcode = MP_ETIMEDOUT;
            return num_tx;
        }
        uint32_t data;
        if (self->char_width == CHAR_WIDTH_9BIT) {
            data = *((uint16_t*)src) & 0x1ff;
            src += 2;
        } else {
            data = *src++;
        }
        #if defined(MCU_SERIES_F4)
        uart->DR = data;
        #else
        uart->TDR = data;
        #endif
        ++num_tx;
    }

    // wait for the UART frame to complete
    if (!uart_wait_flag_set(self, UART_FLAG_TC, timeout)) {
        *errcode = MP_ETIMEDOUT;
        return num_tx;
    }

    *errcode = 0;
    return num_tx;
454
455
}

456
STATIC void uart_tx_char(pyb_uart_obj_t *uart_obj, int c) {
457
458
459
    uint16_t ch = c;
    int errcode;
    uart_tx_data(uart_obj, &ch, 1, &errcode);
460
461
}

Damien George's avatar
Damien George committed
462
void uart_tx_strn(pyb_uart_obj_t *uart_obj, const char *str, uint len) {
463
464
    int errcode;
    uart_tx_data(uart_obj, str, len, &errcode);
465
466
}

Damien George's avatar
Damien George committed
467
void uart_tx_strn_cooked(pyb_uart_obj_t *uart_obj, const char *str, uint len) {
468
469
    for (const char *top = str + len; str < top; str++) {
        if (*str == '\n') {
Damien George's avatar
Damien George committed
470
            uart_tx_char(uart_obj, '\r');
471
        }
Damien George's avatar
Damien George committed
472
        uart_tx_char(uart_obj, *str);
473
474
475
    }
}

476
477
478
// this IRQ handler is set up to handle RXNE interrupts only
void uart_irq_handler(mp_uint_t uart_id) {
    // get the uart object
479
    pyb_uart_obj_t *self = MP_STATE_PORT(pyb_uart_obj_all)[uart_id - 1];
480
481
482
483
484
485
486
487
488
489
490

    if (self == NULL) {
        // UART object has not been set, so we can't do anything, not
        // even disable the IRQ.  This should never happen.
        return;
    }

    if (__HAL_UART_GET_FLAG(&self->uart, UART_FLAG_RXNE) != RESET) {
        if (self->read_buf_len != 0) {
            uint16_t next_head = (self->read_buf_head + 1) % self->read_buf_len;
            if (next_head != self->read_buf_tail) {
491
                // only read data if room in buf
492
                #if defined(MCU_SERIES_F7) || defined(MCU_SERIES_L4)
493
494
495
496
497
                int data = self->uart.Instance->RDR; // clears UART_FLAG_RXNE
                #else
                int data = self->uart.Instance->DR; // clears UART_FLAG_RXNE
                #endif
                data &= self->char_mask;
498
499
500
501
502
503
                if (self->char_width == CHAR_WIDTH_9BIT) {
                    ((uint16_t*)self->read_buf)[self->read_buf_head] = data;
                } else {
                    self->read_buf[self->read_buf_head] = data;
                }
                self->read_buf_head = next_head;
504
505
            } else { // No room: leave char in buf, disable interrupt
                __HAL_UART_DISABLE_IT(&self->uart, UART_IT_RXNE);
506
507
508
509
510
            }
        }
    }
}

511
512
513
/******************************************************************************/
/* Micro Python bindings                                                      */

514
STATIC void pyb_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
Damien George's avatar
Damien George committed
515
    pyb_uart_obj_t *self = self_in;
516
    if (!self->is_enabled) {
517
        mp_printf(print, "UART(%u)", self->uart_id);
518
    } else {
519
520
521
522
        mp_int_t bits = (self->uart.Init.WordLength == UART_WORDLENGTH_8B ? 8 : 9);
        if (self->uart.Init.Parity != UART_PARITY_NONE) {
            bits -= 1;
        }
523
        mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=",
524
            self->uart_id, self->uart.Init.BaudRate, bits);
525
        if (self->uart.Init.Parity == UART_PARITY_NONE) {
526
            mp_print_str(print, "None");
527
        } else {
528
            mp_printf(print, "%u", self->uart.Init.Parity == UART_PARITY_EVEN ? 0 : 1);
529
        }
530
531
532
533
534
535
536
537
538
        if (self->uart.Init.HwFlowCtl) {
            mp_printf(print, ", flow=");
            if (self->uart.Init.HwFlowCtl & UART_HWCONTROL_RTS) {
                mp_printf(print, "RTS%s", self->uart.Init.HwFlowCtl & UART_HWCONTROL_CTS ? "|" : "");
            }
            if (self->uart.Init.HwFlowCtl & UART_HWCONTROL_CTS) {
                mp_printf(print, "CTS");
            }
        }
539
        mp_printf(print, ", stop=%u, timeout=%u, timeout_char=%u, read_buf_len=%u)",
540
            self->uart.Init.StopBits == UART_STOPBITS_1 ? 1 : 2,
541
542
            self->timeout, self->timeout_char,
            self->read_buf_len == 0 ? 0 : self->read_buf_len - 1); // -1 to adjust for usable length of buffer
543
    }
544
545
}

546
/// \method init(baudrate, bits=8, parity=None, stop=1, *, timeout=1000, timeout_char=0, flow=0, read_buf_len=64)
547
///
548
/// Initialise the UART bus with the given parameters:
549
550
///
///   - `baudrate` is the clock rate.
551
///   - `bits` is the number of bits per byte, 7, 8 or 9.
552
///   - `parity` is the parity, `None`, 0 (even) or 1 (odd).
553
554
555
///   - `stop` is the number of stop bits, 1 or 2.
///   - `timeout` is the timeout in milliseconds to wait for the first character.
///   - `timeout_char` is the timeout in milliseconds to wait between characters.
556
///   - `flow` is RTS | CTS where RTS == 256, CTS == 512
557
558
559
560
561
562
563
///   - `read_buf_len` is the character length of the read buffer (0 to disable).
STATIC mp_obj_t pyb_uart_init_helper(pyb_uart_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
    static const mp_arg_t allowed_args[] = {
        { MP_QSTR_baudrate, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 9600} },
        { MP_QSTR_bits, MP_ARG_INT, {.u_int = 8} },
        { MP_QSTR_parity, MP_ARG_OBJ, {.u_obj = mp_const_none} },
        { MP_QSTR_stop, MP_ARG_INT, {.u_int = 1} },
564
        { MP_QSTR_flow, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UART_HWCONTROL_NONE} },
565
566
567
568
        { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1000} },
        { MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
        { MP_QSTR_read_buf_len, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} },
    };
569
570

    // parse args
571
572
573
574
575
    struct {
        mp_arg_val_t baudrate, bits, parity, stop, flow, timeout, timeout_char, read_buf_len;
    } args;
    mp_arg_parse_all(n_args, pos_args, kw_args,
        MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t*)&args);
576

Damien George's avatar
Damien George committed
577
    // set the UART configuration values
578
579
    memset(&self->uart, 0, sizeof(self->uart));
    UART_InitTypeDef *init = &self->uart.Init;
580
581

    // baudrate
582
    init->BaudRate = args.baudrate.u_int;
583
584

    // parity
585
586
    mp_int_t bits = args.bits.u_int;
    if (args.parity.u_obj == mp_const_none) {
587
588
        init->Parity = UART_PARITY_NONE;
    } else {
589
        mp_int_t parity = mp_obj_get_int(args.parity.u_obj);
590
        init->Parity = (parity & 1) ? UART_PARITY_ODD : UART_PARITY_EVEN;
591
        bits += 1; // STs convention has bits including parity
592
    }
593
594
595
596
597
598
599

    // number of bits
    if (bits == 8) {
        init->WordLength = UART_WORDLENGTH_8B;
    } else if (bits == 9) {
        init->WordLength = UART_WORDLENGTH_9B;
    } else {
600
        mp_raise_ValueError("unsupported combination of bits and parity");
601
602
603
    }

    // stop bits
604
    switch (args.stop.u_int) {
605
606
607
        case 1: init->StopBits = UART_STOPBITS_1; break;
        default: init->StopBits = UART_STOPBITS_2; break;
    }
608
609

    // flow control
610
    init->HwFlowCtl = args.flow.u_int;
611
612
613

    // extra config (not yet configurable)
    init->Mode = UART_MODE_TX_RX;
614
615
    init->OverSampling = UART_OVERSAMPLING_16;

Damien George's avatar
Damien George committed
616
617
    // init UART (if it fails, it's because the port doesn't exist)
    if (!uart_init2(self)) {
618
        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "UART(%d) doesn't exist", self->uart_id));
619
620
    }

621
    // set timeout
622
    self->timeout = args.timeout.u_int;
623
624
625

    // set timeout_char
    // make sure it is at least as long as a whole character (13 bits to be safe)
626
    // minimum value is 2ms because sys-tick has a resolution of only 1ms
627
    self->timeout_char = args.timeout_char.u_int;
628
    uint32_t min_timeout_char = 13000 / init->BaudRate + 2;
629
630
631
    if (self->timeout_char < min_timeout_char) {
        self->timeout_char = min_timeout_char;
    }
632
633
634
635

    // setup the read buffer
    m_del(byte, self->read_buf, self->read_buf_len << self->char_width);
    if (init->WordLength == UART_WORDLENGTH_9B && init->Parity == UART_PARITY_NONE) {
636
        self->char_mask = 0x1ff;
637
638
        self->char_width = CHAR_WIDTH_9BIT;
    } else {
639
640
641
642
643
        if (init->WordLength == UART_WORDLENGTH_9B || init->Parity == UART_PARITY_NONE) {
            self->char_mask = 0xff;
        } else {
            self->char_mask = 0x7f;
        }
644
645
646
647
        self->char_width = CHAR_WIDTH_8BIT;
    }
    self->read_buf_head = 0;
    self->read_buf_tail = 0;
648
    if (args.read_buf_len.u_int <= 0) {
649
650
651
652
653
654
655
        // no read buffer
        self->read_buf_len = 0;
        self->read_buf = NULL;
        HAL_NVIC_DisableIRQ(self->irqn);
        __HAL_UART_DISABLE_IT(&self->uart, UART_IT_RXNE);
    } else {
        // read buffer using interrupts
656
        self->read_buf_len = args.read_buf_len.u_int + 1; // +1 to adjust for usable length of buffer
657
        self->read_buf = m_new(byte, self->read_buf_len << self->char_width);
658
        __HAL_UART_ENABLE_IT(&self->uart, UART_IT_RXNE);
659
        HAL_NVIC_SetPriority(self->irqn, IRQ_PRI_UART, IRQ_SUBPRI_UART); 
660
        HAL_NVIC_EnableIRQ(self->irqn);
661
662
    }

663
664
    // compute actual baudrate that was configured
    // (this formula assumes UART_OVERSAMPLING_16)
665
666
667
668
669
670
671
672
673
674
675
676
677
    uint32_t actual_baudrate = 0;
    #if defined(MCU_SERIES_F7)
    UART_ClockSourceTypeDef clocksource = UART_CLOCKSOURCE_UNDEFINED;
    UART_GETCLOCKSOURCE(&self->uart, clocksource);
    switch (clocksource) {
        case UART_CLOCKSOURCE_PCLK1:  actual_baudrate = HAL_RCC_GetPCLK1Freq(); break;
        case UART_CLOCKSOURCE_PCLK2:  actual_baudrate = HAL_RCC_GetPCLK2Freq(); break;
        case UART_CLOCKSOURCE_HSI:    actual_baudrate = HSI_VALUE; break;
        case UART_CLOCKSOURCE_SYSCLK: actual_baudrate = HAL_RCC_GetSysClockFreq(); break;
        case UART_CLOCKSOURCE_LSE:    actual_baudrate = LSE_VALUE; break;
        case UART_CLOCKSOURCE_UNDEFINED: break;
    }
    #else
678
679
680
681
682
    if (self->uart.Instance == USART1
        #if defined(USART6)
        || self->uart.Instance == USART6
        #endif
        ) {
683
684
685
686
        actual_baudrate = HAL_RCC_GetPCLK2Freq();
    } else {
        actual_baudrate = HAL_RCC_GetPCLK1Freq();
    }
687
    #endif
688
689
690
691
692
693
694
695
696
697
698
699
700
701
    actual_baudrate /= self->uart.Instance->BRR;

    // check we could set the baudrate within 5%
    uint32_t baudrate_diff;
    if (actual_baudrate > init->BaudRate) {
        baudrate_diff = actual_baudrate - init->BaudRate;
    } else {
        baudrate_diff = init->BaudRate - actual_baudrate;
    }
    init->BaudRate = actual_baudrate; // remember actual baudrate for printing
    if (20 * baudrate_diff > init->BaudRate) {
        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "set baudrate %d is not within 5%% of desired value", actual_baudrate));
    }

702
703
704
    return mp_const_none;
}

705
706
707
708
709
710
711
/// \classmethod \constructor(bus, ...)
///
/// Construct a UART object on the given bus.  `bus` can be 1-6, or 'XA', 'XB', 'YA', or 'YB'.
/// With no additional parameters, the UART object is created but not
/// initialised (it has the settings from the last initialisation of
/// the bus, if any).  If extra arguments are given, the bus is initialised.
/// See `init` for parameters of initialisation.
712
713
714
715
716
717
718
719
///
/// The physical pins of the UART busses are:
///
///   - `UART(4)` is on `XA`: `(TX, RX) = (X1, X2) = (PA0, PA1)`
///   - `UART(1)` is on `XB`: `(TX, RX) = (X9, X10) = (PB6, PB7)`
///   - `UART(6)` is on `YA`: `(TX, RX) = (Y1, Y2) = (PC6, PC7)`
///   - `UART(3)` is on `YB`: `(TX, RX) = (Y9, Y10) = (PB10, PB11)`
///   - `UART(2)` is on: `(TX, RX) = (X3, X4) = (PA2, PA3)`
720
STATIC mp_obj_t pyb_uart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
721
722
723
    // check arguments
    mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);

724
    // work out port
725
    int uart_id = 0;
726
727
    if (MP_OBJ_IS_STR(args[0])) {
        const char *port = mp_obj_str_get_str(args[0]);
728
        if (0) {
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
        #ifdef MICROPY_HW_UART1_NAME
        } else if (strcmp(port, MICROPY_HW_UART1_NAME) == 0) {
            uart_id = PYB_UART_1;
        #endif
        #ifdef MICROPY_HW_UART2_NAME
        } else if (strcmp(port, MICROPY_HW_UART2_NAME) == 0) {
            uart_id = PYB_UART_2;
        #endif
        #ifdef MICROPY_HW_UART3_NAME
        } else if (strcmp(port, MICROPY_HW_UART3_NAME) == 0) {
            uart_id = PYB_UART_3;
        #endif
        #ifdef MICROPY_HW_UART4_NAME
        } else if (strcmp(port, MICROPY_HW_UART4_NAME) == 0) {
            uart_id = PYB_UART_4;
        #endif
        #ifdef MICROPY_HW_UART5_NAME
        } else if (strcmp(port, MICROPY_HW_UART5_NAME) == 0) {
            uart_id = PYB_UART_5;
        #endif
        #ifdef MICROPY_HW_UART6_NAME
        } else if (strcmp(port, MICROPY_HW_UART6_NAME) == 0) {
            uart_id = PYB_UART_6;
        #endif
753
        } else {
754
            nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "UART(%s) doesn't exist", port));
755
        }
756
    } else {
757
        uart_id = mp_obj_get_int(args[0]);
758
        if (!uart_exists(uart_id)) {
759
            nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "UART(%d) doesn't exist", uart_id));
760
761
762
763
        }
    }

    pyb_uart_obj_t *self;
764
    if (MP_STATE_PORT(pyb_uart_obj_all)[uart_id - 1] == NULL) {
765
766
767
768
        // create new UART object
        self = m_new0(pyb_uart_obj_t, 1);
        self->base.type = &pyb_uart_type;
        self->uart_id = uart_id;
769
        MP_STATE_PORT(pyb_uart_obj_all)[uart_id - 1] = self;
770
771
    } else {
        // reference existing UART object
772
        self = MP_STATE_PORT(pyb_uart_obj_all)[uart_id - 1];
773
774
    }

775
776
777
778
    if (n_args > 1 || n_kw > 0) {
        // start the peripheral
        mp_map_t kw_args;
        mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
779
        pyb_uart_init_helper(self, n_args - 1, args + 1, &kw_args);
780
781
    }

782
    return self;
783
784
}

785
STATIC mp_obj_t pyb_uart_init(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
Damien George's avatar
Damien George committed
786
    return pyb_uart_init_helper(args[0], n_args - 1, args + 1, kw_args);
787
}
Damien George's avatar
Damien George committed
788
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_uart_init_obj, 1, pyb_uart_init);
789

790
791
/// \method deinit()
/// Turn off the UART bus.
Damien George's avatar
Damien George committed
792
793
STATIC mp_obj_t pyb_uart_deinit(mp_obj_t self_in) {
    pyb_uart_obj_t *self = self_in;
794
795
796
797
798
799
800
801
802
803
804
805
806
    self->is_enabled = false;
    UART_HandleTypeDef *uart = &self->uart;
    HAL_UART_DeInit(uart);
    if (uart->Instance == USART1) {
        HAL_NVIC_DisableIRQ(USART1_IRQn);
        __USART1_FORCE_RESET();
        __USART1_RELEASE_RESET();
        __USART1_CLK_DISABLE();
    } else if (uart->Instance == USART2) {
        HAL_NVIC_DisableIRQ(USART2_IRQn);
        __USART2_FORCE_RESET();
        __USART2_RELEASE_RESET();
        __USART2_CLK_DISABLE();
807
    #if defined(USART3)
808
809
810
811
812
    } else if (uart->Instance == USART3) {
        HAL_NVIC_DisableIRQ(USART3_IRQn);
        __USART3_FORCE_RESET();
        __USART3_RELEASE_RESET();
        __USART3_CLK_DISABLE();
813
814
    #endif
    #if defined(UART4)
815
816
817
818
819
    } else if (uart->Instance == UART4) {
        HAL_NVIC_DisableIRQ(UART4_IRQn);
        __UART4_FORCE_RESET();
        __UART4_RELEASE_RESET();
        __UART4_CLK_DISABLE();
820
    #endif
Dave Hylands's avatar
Dave Hylands committed
821
822
823
824
825
826
827
    #if defined(UART5)
    } else if (uart->Instance == UART5) {
        HAL_NVIC_DisableIRQ(UART5_IRQn);
        __UART5_FORCE_RESET();
        __UART5_RELEASE_RESET();
        __UART5_CLK_DISABLE();
    #endif
828
    #if defined(UART6)
829
830
831
832
833
    } else if (uart->Instance == USART6) {
        HAL_NVIC_DisableIRQ(USART6_IRQn);
        __USART6_FORCE_RESET();
        __USART6_RELEASE_RESET();
        __USART6_CLK_DISABLE();
834
    #endif
835
836
837
838
839
840
841
842
843
844
845
846
847
848
    #if defined(UART7)
    } else if (uart->Instance == UART7) {
        HAL_NVIC_DisableIRQ(UART7_IRQn);
        __UART7_FORCE_RESET();
        __UART7_RELEASE_RESET();
        __UART7_CLK_DISABLE();
    #endif
    #if defined(UART8)
    } else if (uart->Instance == UART8) {
        HAL_NVIC_DisableIRQ(UART8_IRQn);
        __UART8_FORCE_RESET();
        __UART8_RELEASE_RESET();
        __UART8_CLK_DISABLE();
    #endif
849
    }
850
851
    return mp_const_none;
}
Damien George's avatar
Damien George committed
852
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_uart_deinit_obj, pyb_uart_deinit);
853

854
855
/// \method any()
/// Return `True` if any characters waiting, else `False`.
Damien George's avatar
Damien George committed
856
857
STATIC mp_obj_t pyb_uart_any(mp_obj_t self_in) {
    pyb_uart_obj_t *self = self_in;
858
    return MP_OBJ_NEW_SMALL_INT(uart_rx_any(self));
859
}
Damien George's avatar
Damien George committed
860
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_uart_any_obj, pyb_uart_any);
861

862
863
/// \method writechar(char)
/// Write a single character on the bus.  `char` is an integer to write.
864
/// Return value: `None`.
865
866
STATIC mp_obj_t pyb_uart_writechar(mp_obj_t self_in, mp_obj_t char_in) {
    pyb_uart_obj_t *self = self_in;
867

868
869
    // get the character to write (might be 9 bits)
    uint16_t data = mp_obj_get_int(char_in);
870

871
    // write the character
872
    int errcode;
873
    if (uart_tx_wait(self, self->timeout)) {
874
        uart_tx_data(self, &data, 1, &errcode);
875
    } else {
876
        errcode = MP_ETIMEDOUT;
877
    }
878

879
880
    if (errcode != 0) {
        mp_raise_OSError(errcode);
881
    }
882

883
884
    return mp_const_none;
}
885
STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_uart_writechar_obj, pyb_uart_writechar);
886

887
888
889
890
891
892
893
894
895
896
897
898
899
900
/// \method readchar()
/// Receive a single character on the bus.
/// Return value: The character read, as an integer.  Returns -1 on timeout.
STATIC mp_obj_t pyb_uart_readchar(mp_obj_t self_in) {
    pyb_uart_obj_t *self = self_in;
    if (uart_rx_wait(self, self->timeout)) {
        return MP_OBJ_NEW_SMALL_INT(uart_rx_char(self));
    } else {
        // return -1 on timeout
        return MP_OBJ_NEW_SMALL_INT(-1);
    }
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_uart_readchar_obj, pyb_uart_readchar);

901
902
903
// uart.sendbreak()
STATIC mp_obj_t pyb_uart_sendbreak(mp_obj_t self_in) {
    pyb_uart_obj_t *self = self_in;
904
    #if defined(MCU_SERIES_F7) || defined(MCU_SERIES_L4)
905
906
    self->uart.Instance->RQR = USART_RQR_SBKRQ; // write-only register
    #else
907
    self->uart.Instance->CR1 |= USART_CR1_SBK;
908
    #endif
909
910
911
912
    return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_uart_sendbreak_obj, pyb_uart_sendbreak);

913
STATIC const mp_rom_map_elem_t pyb_uart_locals_dict_table[] = {
914
    // instance methods
915

916
917
918
    { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&pyb_uart_init_obj) },
    { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&pyb_uart_deinit_obj) },
    { MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&pyb_uart_any_obj) },
919
920

    /// \method read([nbytes])
921
    { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
922
    /// \method readline()
923
    { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj)},
Damien George's avatar
Damien George committed
924
    /// \method readinto(buf[, nbytes])
925
    { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
926
    /// \method write(buf)
927
    { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
928

929
930
931
    { MP_ROM_QSTR(MP_QSTR_writechar), MP_ROM_PTR(&pyb_uart_writechar_obj) },
    { MP_ROM_QSTR(MP_QSTR_readchar), MP_ROM_PTR(&pyb_uart_readchar_obj) },
    { MP_ROM_QSTR(MP_QSTR_sendbreak), MP_ROM_PTR(&pyb_uart_sendbreak_obj) },
932
933

    // class constants
934
935
    { MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(UART_HWCONTROL_RTS) },
    { MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(UART_HWCONTROL_CTS) },
936
937
};

Damien George's avatar
Damien George committed
938
STATIC MP_DEFINE_CONST_DICT(pyb_uart_locals_dict, pyb_uart_locals_dict_table);
939

940
941
942
943
944
945
STATIC mp_uint_t pyb_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) {
    pyb_uart_obj_t *self = self_in;
    byte *buf = buf_in;

    // check that size is a multiple of character width
    if (size & self->char_width) {
946
        *errcode = MP_EIO;
947
948
949
950
951
952
953
954
955
956
957
958
959
        return MP_STREAM_ERROR;
    }

    // convert byte size to char size
    size >>= self->char_width;

    // make sure we want at least 1 char
    if (size == 0) {
        return 0;
    }

    // wait for first char to become available
    if (!uart_rx_wait(self, self->timeout)) {
960
        // return EAGAIN error to indicate non-blocking (then read() method returns None)
961
        *errcode = MP_EAGAIN;
962
        return MP_STREAM_ERROR;
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
    }

    // read the data
    byte *orig_buf = buf;
    for (;;) {
        int data = uart_rx_char(self);
        if (self->char_width == CHAR_WIDTH_9BIT) {
            *(uint16_t*)buf = data;
            buf += 2;
        } else {
            *buf++ = data;
        }
        if (--size == 0 || !uart_rx_wait(self, self->timeout_char)) {
            // return number of bytes read
            return buf - orig_buf;
        }
    }
}

STATIC mp_uint_t pyb_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) {
    pyb_uart_obj_t *self = self_in;
    const byte *buf = buf_in;

    // check that size is a multiple of character width
    if (size & self->char_width) {
988
        *errcode = MP_EIO;
989
990
991
        return MP_STREAM_ERROR;
    }

992
    // wait to be able to write the first character. EAGAIN causes write to return None
993
    if (!uart_tx_wait(self, self->timeout)) {
994
        *errcode = MP_EAGAIN;
995
996
997
        return MP_STREAM_ERROR;
    }

998
    // write the data
999
1000
1001
1002
1003
    size_t num_tx = uart_tx_data(self, buf, size >> self->char_width, errcode);

    if (*errcode == 0 || *errcode == MP_ETIMEDOUT) {
        // return number of bytes written, even if there was a timeout
        return num_tx << self->char_width;
1004
1005
1006
1007
1008
    } else {
        return MP_STREAM_ERROR;
    }
}

1009
STATIC mp_uint_t pyb_uart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) {
1010
1011
    pyb_uart_obj_t *self = self_in;
    mp_uint_t ret;
1012
    if (request == MP_STREAM_POLL) {
1013
        mp_uint_t flags = arg;
1014
        ret = 0;
1015
1016
        if ((flags & MP_STREAM_POLL_RD) && uart_rx_any(self)) {
            ret |= MP_STREAM_POLL_RD;
1017
        }
1018
1019
        if ((flags & MP_STREAM_POLL_WR) && __HAL_UART_GET_FLAG(&self->uart, UART_FLAG_TXE)) {
            ret |= MP_STREAM_POLL_WR;
1020
1021
        }
    } else {
1022
        *errcode = MP_EINVAL;
1023
        ret = MP_STREAM_ERROR;
1024
1025
1026
1027
1028
    }
    return ret;
}

STATIC const mp_stream_p_t uart_stream_p = {
1029
1030
1031
    .read = pyb_uart_read,
    .write = pyb_uart_write,
    .ioctl = pyb_uart_ioctl,
1032
1033
1034
    .is_text = false,
};

Damien George's avatar
Damien George committed
1035
const mp_obj_type_t pyb_uart_type = {
1036
    { &mp_type_type },
Damien George's avatar
Damien George committed
1037
1038
1039
    .name = MP_QSTR_UART,
    .print = pyb_uart_print,
    .make_new = pyb_uart_make_new,
1040
    .getiter = mp_identity_getiter,
1041
    .iternext = mp_stream_unbuffered_iter,
1042
    .protocol = &uart_stream_p,
1043
    .locals_dict = (mp_obj_dict_t*)&pyb_uart_locals_dict,
1044
};