diff options
Diffstat (limited to 'stmhal/uart.c')
-rw-r--r-- | stmhal/uart.c | 354 |
1 files changed, 230 insertions, 124 deletions
diff --git a/stmhal/uart.c b/stmhal/uart.c index cbcb7c059..81e6f69a5 100644 --- a/stmhal/uart.c +++ b/stmhal/uart.c @@ -34,10 +34,8 @@ #include "py/mperrno.h" #include "py/mphal.h" #include "uart.h" -#include "pybioctl.h" #include "irq.h" - -//TODO: Add UART7/8 support for MCU_SERIES_F7 +#include "genhdr/pins.h" /// \moduleref pyb /// \class UART - duplex serial communication bus @@ -60,7 +58,7 @@ /// using the standard stream methods: /// /// uart.read(10) # read 10 characters, returns a bytes object -/// uart.readall() # read all available characters +/// uart.read() # read all available characters /// uart.readline() # read a line /// uart.readinto(buf) # read and store into the given buffer /// uart.write('abc') # write the 3 characters @@ -111,153 +109,185 @@ void uart_deinit(void) { } } +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; + } +} + // assumes Init parameters have been set up correctly STATIC bool uart_init2(pyb_uart_obj_t *uart_obj) { USART_TypeDef *UARTx; IRQn_Type irqn; - uint32_t GPIO_Pin, GPIO_Pin2 = 0; - uint8_t GPIO_AF_UARTx = 0; - GPIO_TypeDef* GPIO_Port = NULL; - GPIO_TypeDef* GPIO_Port2 = NULL; + int uart_unit; - switch (uart_obj->uart_id) { - #if defined(MICROPY_HW_UART1_PORT) && defined(MICROPY_HW_UART1_PINS) - // USART1 is on PA9/PA10 (CK on PA8), PB6/PB7 - case PYB_UART_1: - UARTx = USART1; - irqn = USART1_IRQn; - GPIO_AF_UARTx = GPIO_AF7_USART1; - GPIO_Port = MICROPY_HW_UART1_PORT; - GPIO_Pin = MICROPY_HW_UART1_PINS; - __USART1_CLK_ENABLE(); - break; - #endif + const pin_obj_t *pins[4] = {0}; - #if defined(MICROPY_HW_UART1_TX_PORT) && \ - defined(MICROPY_HW_UART1_TX_PIN) && \ - defined(MICROPY_HW_UART1_RX_PORT) && \ - defined(MICROPY_HW_UART1_RX_PIN) + switch (uart_obj->uart_id) { + #if defined(MICROPY_HW_UART1_TX) && defined(MICROPY_HW_UART1_RX) case PYB_UART_1: + uart_unit = 1; UARTx = USART1; irqn = USART1_IRQn; - GPIO_AF_UARTx = GPIO_AF7_USART1; - GPIO_Port = MICROPY_HW_UART1_TX_PORT; - GPIO_Pin = MICROPY_HW_UART1_TX_PIN; - GPIO_Port2 = MICROPY_HW_UART1_RX_PORT; - GPIO_Pin2 = MICROPY_HW_UART1_RX_PIN; + pins[0] = &MICROPY_HW_UART1_TX; + pins[1] = &MICROPY_HW_UART1_RX; __USART1_CLK_ENABLE(); break; #endif - #if defined(MICROPY_HW_UART2_PORT) && defined(MICROPY_HW_UART2_PINS) + #if defined(MICROPY_HW_UART2_TX) && defined(MICROPY_HW_UART2_RX) case PYB_UART_2: + uart_unit = 2; UARTx = USART2; irqn = USART2_IRQn; - GPIO_AF_UARTx = GPIO_AF7_USART2; - GPIO_Port = MICROPY_HW_UART2_PORT; - GPIO_Pin = MICROPY_HW_UART2_PINS; + pins[0] = &MICROPY_HW_UART2_TX; + pins[1] = &MICROPY_HW_UART2_RX; #if defined(MICROPY_HW_UART2_RTS) if (uart_obj->uart.Init.HwFlowCtl & UART_HWCONTROL_RTS) { - GPIO_Pin |= MICROPY_HW_UART2_RTS; + pins[2] = &MICROPY_HW_UART2_RTS; } #endif #if defined(MICROPY_HW_UART2_CTS) if (uart_obj->uart.Init.HwFlowCtl & UART_HWCONTROL_CTS) { - GPIO_Pin |= MICROPY_HW_UART2_CTS; + pins[3] = &MICROPY_HW_UART2_CTS; } #endif __USART2_CLK_ENABLE(); break; #endif - #if defined(USART3) && defined(MICROPY_HW_UART3_PORT) && defined(MICROPY_HW_UART3_PINS) - // USART3 is on PB10/PB11 (CK,CTS,RTS on PB12,PB13,PB14), PC10/PC11 (CK on PC12), PD8/PD9 (CK on PD10) + #if defined(MICROPY_HW_UART3_TX) && defined(MICROPY_HW_UART3_RX) case PYB_UART_3: + uart_unit = 3; UARTx = USART3; irqn = USART3_IRQn; - GPIO_AF_UARTx = GPIO_AF7_USART3; - GPIO_Port = MICROPY_HW_UART3_PORT; - GPIO_Pin = MICROPY_HW_UART3_PINS; + pins[0] = &MICROPY_HW_UART3_TX; + pins[1] = &MICROPY_HW_UART3_RX; #if defined(MICROPY_HW_UART3_RTS) if (uart_obj->uart.Init.HwFlowCtl & UART_HWCONTROL_RTS) { - GPIO_Pin |= MICROPY_HW_UART3_RTS; + pins[2] = &MICROPY_HW_UART3_RTS; } #endif #if defined(MICROPY_HW_UART3_CTS) if (uart_obj->uart.Init.HwFlowCtl & UART_HWCONTROL_CTS) { - GPIO_Pin |= MICROPY_HW_UART3_CTS; + pins[3] = &MICROPY_HW_UART3_CTS; } #endif __USART3_CLK_ENABLE(); break; #endif - #if defined(UART4) && defined(MICROPY_HW_UART4_PORT) && defined(MICROPY_HW_UART4_PINS) - // UART4 is on PA0/PA1, PC10/PC11 + #if defined(MICROPY_HW_UART4_TX) && defined(MICROPY_HW_UART4_RX) case PYB_UART_4: + uart_unit = 4; UARTx = UART4; irqn = UART4_IRQn; - GPIO_AF_UARTx = GPIO_AF8_UART4; - GPIO_Port = MICROPY_HW_UART4_PORT; - GPIO_Pin = MICROPY_HW_UART4_PINS; + pins[0] = &MICROPY_HW_UART4_TX; + pins[1] = &MICROPY_HW_UART4_RX; __UART4_CLK_ENABLE(); break; #endif - #if defined(UART5) && \ - defined(MICROPY_HW_UART5_TX_PORT) && \ - defined(MICROPY_HW_UART5_TX_PIN) && \ - defined(MICROPY_HW_UART5_RX_PORT) && \ - defined(MICROPY_HW_UART5_RX_PIN) + #if defined(MICROPY_HW_UART5_TX) && defined(MICROPY_HW_UART5_RX) case PYB_UART_5: + uart_unit = 5; UARTx = UART5; irqn = UART5_IRQn; - GPIO_AF_UARTx = GPIO_AF8_UART5; - GPIO_Port = MICROPY_HW_UART5_TX_PORT; - GPIO_Port2 = MICROPY_HW_UART5_RX_PORT; - GPIO_Pin = MICROPY_HW_UART5_TX_PIN; - GPIO_Pin2 = MICROPY_HW_UART5_RX_PIN; + pins[0] = &MICROPY_HW_UART5_TX; + pins[1] = &MICROPY_HW_UART5_RX; __UART5_CLK_ENABLE(); break; #endif - #if defined(MICROPY_HW_UART6_PORT) && defined(MICROPY_HW_UART6_PINS) - // USART6 is on PC6/PC7 (CK on PC8) + #if defined(MICROPY_HW_UART6_TX) && defined(MICROPY_HW_UART6_RX) case PYB_UART_6: + uart_unit = 6; UARTx = USART6; irqn = USART6_IRQn; - GPIO_AF_UARTx = GPIO_AF8_USART6; - GPIO_Port = MICROPY_HW_UART6_PORT; - GPIO_Pin = MICROPY_HW_UART6_PINS; + pins[0] = &MICROPY_HW_UART6_TX; + pins[1] = &MICROPY_HW_UART6_RX; __USART6_CLK_ENABLE(); break; #endif + #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 + default: // UART does not exist or is not configured for this board return false; } - uart_obj->irqn = irqn; - uart_obj->uart.Instance = UARTx; + uint32_t mode = MP_HAL_PIN_MODE_ALT; + uint32_t pull = MP_HAL_PIN_PULL_UP; - // init GPIO - mp_hal_gpio_clock_enable(GPIO_Port); - GPIO_InitTypeDef GPIO_InitStructure; - GPIO_InitStructure.Pin = GPIO_Pin; - GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; - GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; - GPIO_InitStructure.Pull = GPIO_PULLUP; - GPIO_InitStructure.Alternate = GPIO_AF_UARTx; - HAL_GPIO_Init(GPIO_Port, &GPIO_InitStructure); - - // init GPIO for second pin if needed - if (GPIO_Port2 != NULL) { - mp_hal_gpio_clock_enable(GPIO_Port2); - GPIO_InitStructure.Pin = GPIO_Pin2; - HAL_GPIO_Init(GPIO_Port2, &GPIO_InitStructure); + 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; + } + } } + uart_obj->irqn = irqn; + uart_obj->uart.Instance = UARTx; + // init UARTx HAL_UART_Init(&uart_obj->uart); @@ -349,27 +379,89 @@ STATIC bool uart_tx_wait(pyb_uart_obj_t *self, uint32_t timeout) { } } -STATIC HAL_StatusTypeDef uart_tx_data(pyb_uart_obj_t *self, uint8_t *data, uint16_t len) { +// 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; 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. - return HAL_UART_Transmit(&self->uart, data, len, self->timeout); - } - // 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. - return HAL_UART_Transmit(&self->uart, data, len, 2 * self->timeout_char); + 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; } STATIC void uart_tx_char(pyb_uart_obj_t *uart_obj, int c) { - uint8_t ch = c; - uart_tx_data(uart_obj, &ch, 1); + uint16_t ch = c; + int errcode; + uart_tx_data(uart_obj, &ch, 1, &errcode); } void uart_tx_strn(pyb_uart_obj_t *uart_obj, const char *str, uint len) { - uart_tx_data(uart_obj, (uint8_t*)str, len); + int errcode; + uart_tx_data(uart_obj, str, len, &errcode); } void uart_tx_strn_cooked(pyb_uart_obj_t *uart_obj, const char *str, uint len) { @@ -531,8 +623,9 @@ STATIC mp_obj_t pyb_uart_init_helper(pyb_uart_obj_t *self, mp_uint_t n_args, con // set timeout_char // make sure it is at least as long as a whole character (13 bits to be safe) + // minimum value is 2ms because sys-tick has a resolution of only 1ms self->timeout_char = args.timeout_char.u_int; - uint32_t min_timeout_char = 13000 / init->BaudRate + 1; + uint32_t min_timeout_char = 13000 / init->BaudRate + 2; if (self->timeout_char < min_timeout_char) { self->timeout_char = min_timeout_char; } @@ -569,7 +662,19 @@ STATIC mp_obj_t pyb_uart_init_helper(pyb_uart_obj_t *self, mp_uint_t n_args, con // compute actual baudrate that was configured // (this formula assumes UART_OVERSAMPLING_16) - uint32_t actual_baudrate; + 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 if (self->uart.Instance == USART1 #if defined(USART6) || self->uart.Instance == USART6 @@ -579,6 +684,7 @@ STATIC mp_obj_t pyb_uart_init_helper(pyb_uart_obj_t *self, mp_uint_t n_args, con } else { actual_baudrate = HAL_RCC_GetPCLK1Freq(); } + #endif actual_baudrate /= self->uart.Instance->BRR; // check we could set the baudrate within 5% @@ -611,7 +717,7 @@ STATIC mp_obj_t pyb_uart_init_helper(pyb_uart_obj_t *self, mp_uint_t n_args, con /// - `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)` -STATIC mp_obj_t pyb_uart_make_new(const mp_obj_type_t *type, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { +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) { // check arguments mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); @@ -649,7 +755,7 @@ STATIC mp_obj_t pyb_uart_make_new(const mp_obj_type_t *type, mp_uint_t n_args, m } } else { uart_id = mp_obj_get_int(args[0]); - if (uart_id < 1 || uart_id > MP_ARRAY_SIZE(MP_STATE_PORT(pyb_uart_obj_all))) { + if (!uart_exists(uart_id)) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "UART(%d) does not exist", uart_id)); } } @@ -726,6 +832,20 @@ STATIC mp_obj_t pyb_uart_deinit(mp_obj_t self_in) { __USART6_RELEASE_RESET(); __USART6_CLK_DISABLE(); #endif + #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 } return mp_const_none; } @@ -749,15 +869,15 @@ STATIC mp_obj_t pyb_uart_writechar(mp_obj_t self_in, mp_obj_t char_in) { uint16_t data = mp_obj_get_int(char_in); // write the character - HAL_StatusTypeDef status; + int errcode; if (uart_tx_wait(self, self->timeout)) { - status = uart_tx_data(self, (uint8_t*)&data, 1); + uart_tx_data(self, &data, 1, &errcode); } else { - status = HAL_TIMEOUT; + errcode = MP_ETIMEDOUT; } - if (status != HAL_OK) { - mp_hal_raise(status); + if (errcode != 0) { + mp_raise_OSError(errcode); } return mp_const_none; @@ -799,8 +919,6 @@ STATIC const mp_map_elem_t pyb_uart_locals_dict_table[] = { /// \method read([nbytes]) { MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read_obj }, - /// \method readall() - { MP_OBJ_NEW_QSTR(MP_QSTR_readall), (mp_obj_t)&mp_stream_readall_obj }, /// \method readline() { MP_OBJ_NEW_QSTR(MP_QSTR_readline), (mp_obj_t)&mp_stream_unbuffered_readline_obj}, /// \method readinto(buf[, nbytes]) @@ -878,24 +996,12 @@ STATIC mp_uint_t pyb_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t } // write the data - HAL_StatusTypeDef status = uart_tx_data(self, (uint8_t*)buf, size >> self->char_width); - - if (status == HAL_OK) { - // return number of bytes written - return size; - } else if (status == HAL_TIMEOUT) { // UART_WaitOnFlagUntilTimeout() disables RXNE interrupt on timeout - if (self->read_buf_len > 0) { - __HAL_UART_ENABLE_IT(&self->uart, UART_IT_RXNE); // re-enable RXNE - } - // return number of bytes written - if (self->char_width == CHAR_WIDTH_8BIT) { - return size - self->uart.TxXferCount - 1; - } else { - int written = self->uart.TxXferCount * 2; - return size - written - 2; - } + 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; } else { - *errcode = mp_hal_status_to_errno_table[status]; return MP_STREAM_ERROR; } } @@ -903,14 +1009,14 @@ STATIC mp_uint_t pyb_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t STATIC mp_uint_t pyb_uart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { pyb_uart_obj_t *self = self_in; mp_uint_t ret; - if (request == MP_IOCTL_POLL) { + if (request == MP_STREAM_POLL) { mp_uint_t flags = arg; ret = 0; - if ((flags & MP_IOCTL_POLL_RD) && uart_rx_any(self)) { - ret |= MP_IOCTL_POLL_RD; + if ((flags & MP_STREAM_POLL_RD) && uart_rx_any(self)) { + ret |= MP_STREAM_POLL_RD; } - if ((flags & MP_IOCTL_POLL_WR) && __HAL_UART_GET_FLAG(&self->uart, UART_FLAG_TXE)) { - ret |= MP_IOCTL_POLL_WR; + if ((flags & MP_STREAM_POLL_WR) && __HAL_UART_GET_FLAG(&self->uart, UART_FLAG_TXE)) { + ret |= MP_STREAM_POLL_WR; } } else { *errcode = MP_EINVAL; |