diff options
| author | robert-hh <robert@hammelrath.com> | 2023-09-18 09:08:24 +0200 |
|---|---|---|
| committer | Damien George <damien@micropython.org> | 2023-11-06 11:10:11 +1100 |
| commit | 59afeb056ac3657eebfffc16136d7eedbbc378e2 (patch) | |
| tree | 09cd6d2e4a8ba4498f07d3c63943828a5f17af92 | |
| parent | 2c1f23820572e36a1d12597dff3261242fd6573c (diff) | |
samd/machine_uart: Add machine_uart_set_baudrate() function.
Changing the baudrate requires a complete re-configuration of the Sercom
device, which is put into a separate rather large function. This new
machine_uart_set_baudrate() function will be useful for future drivers such
as Bluetooth.
Signed-off-by: robert-hh <robert@hammelrath.com>
| -rw-r--r-- | ports/samd/machine_uart.c | 159 |
1 files changed, 86 insertions, 73 deletions
diff --git a/ports/samd/machine_uart.c b/ports/samd/machine_uart.c index ada193020..de6c92c5f 100644 --- a/ports/samd/machine_uart.c +++ b/ports/samd/machine_uart.c @@ -49,6 +49,7 @@ typedef struct _machine_uart_obj_t { uint8_t bits; uint8_t parity; uint8_t stop; + uint8_t flow_control; uint8_t tx; uint8_t rx; sercom_pad_config_t tx_pad_config; @@ -112,6 +113,85 @@ void common_uart_irq_handler(int uart_id) { } } +// Configure the Sercom device +STATIC void machine_sercom_configure(machine_uart_obj_t *self) { + Sercom *uart = sercom_instance[self->id]; + + // Reset (clear) the peripheral registers. + while (uart->USART.SYNCBUSY.bit.SWRST) { + } + uart->USART.CTRLA.bit.SWRST = 1; // Reset all Registers, disable peripheral + while (uart->USART.SYNCBUSY.bit.SWRST) { + } + + uint8_t txpo = self->tx_pad_config.pad_nr; + #if defined(MCU_SAMD21) + if (self->tx_pad_config.pad_nr == 2) { // Map pad 2 to TXPO = 1 + txpo = 1; + } else + #endif + if (self->tx_pad_config.pad_nr != 0) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid UART pin")); + } + #if MICROPY_HW_UART_RTSCTS + if ((self->flow_control & FLOW_CONTROL_RTS) && self->rts_pad_config.pad_nr == 2) { + txpo = 2; + mp_hal_set_pin_mux(self->rts, self->rts_pad_config.alt_fct); + } + if ((self->flow_control & FLOW_CONTROL_CTS) && self->cts_pad_config.pad_nr == 3) { + txpo = 2; + mp_hal_set_pin_mux(self->cts, self->cts_pad_config.alt_fct); + } + #endif + + uart->USART.CTRLA.reg = + SERCOM_USART_CTRLA_DORD // Data order + | SERCOM_USART_CTRLA_FORM(self->parity != 0 ? 1 : 0) // Enable parity or not + | SERCOM_USART_CTRLA_RXPO(self->rx_pad_config.pad_nr) // Set Pad# + | SERCOM_USART_CTRLA_TXPO(txpo) // Set Pad# + | SERCOM_USART_CTRLA_MODE(1) // USART with internal clock + ; + uart->USART.CTRLB.reg = + SERCOM_USART_CTRLB_RXEN // Enable Rx & Tx + | SERCOM_USART_CTRLB_TXEN + | ((self->parity & 1) << SERCOM_USART_CTRLB_PMODE_Pos) + | (self->stop << SERCOM_USART_CTRLB_SBMODE_Pos) + | SERCOM_USART_CTRLB_CHSIZE((self->bits & 7) | (self->bits & 1)) + ; + while (uart->USART.SYNCBUSY.bit.CTRLB) { + } + + // USART is driven by the clock of GCLK Generator 2, freq by get_peripheral_freq() + // baud rate; 65536 * (1 - 16 * 115200/bus_freq) + uint32_t baud = 65536 - ((uint64_t)(65536 * 16) * self->baudrate + get_peripheral_freq() / 2) / get_peripheral_freq(); + uart->USART.BAUD.bit.BAUD = baud; // Set Baud + + sercom_register_irq(self->id, &common_uart_irq_handler); + + // Enable RXC interrupt + uart->USART.INTENSET.reg = SERCOM_USART_INTENSET_RXC; + #if defined(MCU_SAMD21) + NVIC_EnableIRQ(SERCOM0_IRQn + self->id); + #elif defined(MCU_SAMD51) + NVIC_EnableIRQ(SERCOM0_0_IRQn + 4 * self->id + 2); + #endif + #if MICROPY_HW_UART_TXBUF + // Enable DRE interrupt + // SAMD21 has just 1 IRQ for all USART events, so no need for an additional NVIC enable + #if defined(MCU_SAMD51) + NVIC_EnableIRQ(SERCOM0_0_IRQn + 4 * self->id + 0); + #endif + #endif + + sercom_enable(uart, 1); +} + +void machine_uart_set_baudrate(mp_obj_t self_in, uint32_t baudrate) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + self->baudrate = baudrate; + machine_sercom_configure(self); +} + STATIC void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, " @@ -194,22 +274,22 @@ STATIC void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, if (args[ARG_rx].u_obj != mp_const_none) { self->rx = mp_hal_get_pin_obj(args[ARG_rx].u_obj); } + self->flow_control = 0; #if MICROPY_HW_UART_RTSCTS - uint8_t flow_control = 0; // Set RTS/CTS pins if configured. if (args[ARG_rts].u_obj != mp_const_none) { self->rts = mp_hal_get_pin_obj(args[ARG_rts].u_obj); self->rts_pad_config = get_sercom_config(self->rts, self->id); - flow_control = FLOW_CONTROL_RTS; + self->flow_control = FLOW_CONTROL_RTS; } if (args[ARG_cts].u_obj != mp_const_none) { self->cts = mp_hal_get_pin_obj(args[ARG_cts].u_obj); self->cts_pad_config = get_sercom_config(self->cts, self->id); - flow_control |= FLOW_CONTROL_CTS; + self->flow_control |= FLOW_CONTROL_CTS; } // rts only flow control is not allowed. Otherwise the state of the // cts pin is undefined. - if (flow_control == FLOW_CONTROL_RTS) { + if (self->flow_control == FLOW_CONTROL_RTS) { mp_raise_ValueError(MP_ERROR_TEXT("cts missing for flow control")); } #endif @@ -278,75 +358,8 @@ STATIC void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, // Next: Set up the clocks enable_sercom_clock(self->id); - // Next: Configure the USART - Sercom *uart = sercom_instance[self->id]; - // Reset (clear) the peripheral registers. - while (uart->USART.SYNCBUSY.bit.SWRST) { - } - uart->USART.CTRLA.bit.SWRST = 1; // Reset all Registers, disable peripheral - while (uart->USART.SYNCBUSY.bit.SWRST) { - } - - uint8_t txpo = self->tx_pad_config.pad_nr; - #if defined(MCU_SAMD21) - if (self->tx_pad_config.pad_nr == 2) { // Map pad 2 to TXPO = 1 - txpo = 1; - } else - #endif - if (self->tx_pad_config.pad_nr != 0) { - mp_raise_ValueError(MP_ERROR_TEXT("invalid UART pin")); - } - #if MICROPY_HW_UART_RTSCTS - if ((flow_control & FLOW_CONTROL_RTS) && self->rts_pad_config.pad_nr == 2) { - txpo = 2; - mp_hal_set_pin_mux(self->rts, self->rts_pad_config.alt_fct); - } - if ((flow_control & FLOW_CONTROL_CTS) && self->cts_pad_config.pad_nr == 3) { - txpo = 2; - mp_hal_set_pin_mux(self->cts, self->cts_pad_config.alt_fct); - } - #endif - - uart->USART.CTRLA.reg = - SERCOM_USART_CTRLA_DORD // Data order - | SERCOM_USART_CTRLA_FORM(self->parity != 0 ? 1 : 0) // Enable parity or not - | SERCOM_USART_CTRLA_RXPO(self->rx_pad_config.pad_nr) // Set Pad# - | SERCOM_USART_CTRLA_TXPO(txpo) // Set Pad# - | SERCOM_USART_CTRLA_MODE(1) // USART with internal clock - ; - uart->USART.CTRLB.reg = - SERCOM_USART_CTRLB_RXEN // Enable Rx & Tx - | SERCOM_USART_CTRLB_TXEN - | ((self->parity & 1) << SERCOM_USART_CTRLB_PMODE_Pos) - | (self->stop << SERCOM_USART_CTRLB_SBMODE_Pos) - | SERCOM_USART_CTRLB_CHSIZE((self->bits & 7) | (self->bits & 1)) - ; - while (uart->USART.SYNCBUSY.bit.CTRLB) { - } - - // USART is driven by the clock of GCLK Generator 2, freq by get_peripheral_freq() - // baud rate; 65536 * (1 - 16 * 115200/bus_freq) - uint32_t baud = 65536 - ((uint64_t)(65536 * 16) * self->baudrate + get_peripheral_freq() / 2) / get_peripheral_freq(); - uart->USART.BAUD.bit.BAUD = baud; // Set Baud - - sercom_register_irq(self->id, &common_uart_irq_handler); - - // Enable RXC interrupt - uart->USART.INTENSET.reg = SERCOM_USART_INTENSET_RXC; - #if defined(MCU_SAMD21) - NVIC_EnableIRQ(SERCOM0_IRQn + self->id); - #elif defined(MCU_SAMD51) - NVIC_EnableIRQ(SERCOM0_0_IRQn + 4 * self->id + 2); - #endif - #if MICROPY_HW_UART_TXBUF - // Enable DRE interrupt - // SAMD21 has just 1 IRQ for all USART events, so no need for an additional NVIC enable - #if defined(MCU_SAMD51) - NVIC_EnableIRQ(SERCOM0_0_IRQn + 4 * self->id + 0); - #endif - #endif - - sercom_enable(uart, 1); + // Configure the sercom module + machine_sercom_configure(self); } } |
