diff --git a/.github/workflows/action.yml b/.github/workflows/action.yml index a15feed1987..1ad6634774f 100644 --- a/.github/workflows/action.yml +++ b/.github/workflows/action.yml @@ -8,7 +8,7 @@ on: - cron: '0 16 1 * *' push: branches: - - master + - serialX-dev paths-ignore: - documentation/** - '**/README.md' diff --git a/bsp/bluetrum/libraries/hal_drivers/SConscript b/bsp/bluetrum/libraries/hal_drivers/SConscript index b35a6e89ccb..50a895e1d95 100644 --- a/bsp/bluetrum/libraries/hal_drivers/SConscript +++ b/bsp/bluetrum/libraries/hal_drivers/SConscript @@ -12,7 +12,10 @@ if GetDepend('RT_USING_PIN'): src += ['drv_gpio.c'] if GetDepend('RT_USING_SERIAL'): - src += ['drv_usart.c'] + if GetDepend(['RT_USING_SERIAL_X']): + src += ['drv_usartX.c'] + else: + src += ['drv_usart.c'] if GetDepend('RT_USING_SDIO'): src += ['drv_sdio.c'] diff --git a/bsp/bluetrum/libraries/hal_drivers/drv_usartX.c b/bsp/bluetrum/libraries/hal_drivers/drv_usartX.c new file mode 100644 index 00000000000..c3b0e9299b6 --- /dev/null +++ b/bsp/bluetrum/libraries/hal_drivers/drv_usartX.c @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2020-2022, Bluetrum Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2020-11-20 greedyhao first version + * 2022-06-08 THEWON first version for serialX + */ + +#include "board.h" +#include + +#ifdef RT_USING_SERIAL + +//#define DRV_DEBUG +#define LOG_TAG "drv.usart" +#include + +enum +{ +#ifdef BSP_USING_UART0 + UART0_INDEX, +#endif +#ifdef BSP_USING_UART1 + UART1_INDEX, +#endif +#ifdef BSP_USING_UART2 + UART2_INDEX, +#endif +}; + +static struct ab32_uart_config uart_config[] = +{ +#ifdef BSP_USING_UART0 + { + .name = "uart0", + .instance = UART0_BASE, + .mode = UART_MODE_TX_RX | UART_MODE_1LINE, + }, +#endif +#ifdef BSP_USING_UART1 + { + .name = "uart1", + .instance = UART1_BASE, + .mode = UART_MODE_TX_RX, + }, +#endif +#ifdef BSP_USING_UART2 + { + .name = "uart2", + .instance = UART2_BASE, + .mode = UART_MODE_TX_RX, + } +#endif +}; + +static struct ab32_uart uart_obj[sizeof(uart_config) / sizeof(uart_config[0])] = {0}; + +static rt_err_t ab32_init(struct rt_serial_device *serial) +{ + struct ab32_uart *uart; + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct ab32_uart, serial); + uart->handle.instance = uart->uart_config->instance; + uart->handle.init.baud = serial->config->baud_rate; + uart->handle.init.mode = uart->uart_config->mode; + + switch (serial->config->data_bits) + { + case DATA_BITS_8: + uart->handle.init.word_len = UART_WORDLENGTH_8B; + break; + case DATA_BITS_9: + uart->handle.init.word_len = UART_WORDLENGTH_9B; + break; + default: + uart->handle.init.word_len = UART_WORDLENGTH_8B; + break; + } + + switch (serial->config->stop_bits) + { + case STOP_BITS_1: + uart->handle.init.stop_bits = UART_STOPBITS_1; + break; + case STOP_BITS_2: + uart->handle.init.stop_bits = UART_STOPBITS_2; + break; + default: + uart->handle.init.stop_bits = UART_STOPBITS_1; + break; + } + + hal_uart_init(&uart->handle); + + return RT_EOK; +} + +static rt_err_t ab32_configure(struct rt_serial_device *serial, struct serial_configure *cfg) +{ + struct ab32_uart *uart; + RT_ASSERT(serial != RT_NULL); + RT_ASSERT(cfg != RT_NULL); + + uart = rt_container_of(serial, struct ab32_uart, serial); + uart->handle.instance = uart->uart_config->instance; + uart->handle.init.baud = cfg->baud_rate; + uart->handle.init.mode = uart->uart_config->mode; + + switch (cfg->data_bits) + { + case DATA_BITS_8: + uart->handle.init.word_len = UART_WORDLENGTH_8B; + break; + case DATA_BITS_9: + uart->handle.init.word_len = UART_WORDLENGTH_9B; + break; + default: + uart->handle.init.word_len = UART_WORDLENGTH_8B; + break; + } + + switch (cfg->stop_bits) + { + case STOP_BITS_1: + uart->handle.init.stop_bits = UART_STOPBITS_1; + break; + case STOP_BITS_2: + uart->handle.init.stop_bits = UART_STOPBITS_2; + break; + default: + uart->handle.init.stop_bits = UART_STOPBITS_1; + break; + } + + hal_uart_init(&uart->handle); + + return RT_EOK; +} + +static rt_err_t ab32_control(struct rt_serial_device *serial, int cmd, void *arg) +{ + struct ab32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + uart = rt_container_of(serial, struct ab32_uart, serial); + + switch (cmd) + { + case RT_DEVICE_CTRL_OPEN: + uart->intTxing = RT_FALSE; + break; + /* disable interrupt */ + case RT_DEVICE_CTRL_CLR_INT: + hal_uart_control(uart->handle.instance, UART_RXIT_ENABLE, HAL_DISABLE); + break; + /* enable interrupt */ + case RT_DEVICE_CTRL_SET_INT: + hal_uart_clrflag(uart->handle.instance, UART_FLAG_RXPND); + hal_uart_control(uart->handle.instance, UART_RXIT_ENABLE, HAL_ENABLE); + break; + case RT_DEVICE_CTRL_CLOSE: + hal_uart_deinit(uart->handle.instance); + break; + } + + return RT_EOK; +} + +static int ab32_putc(struct rt_serial_device *serial, char ch) +{ + struct ab32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct ab32_uart, serial); + + hal_uart_write(uart->handle.instance, ch); + + return 1; +} + +static int ab32_getc(struct rt_serial_device *serial) +{ + int ch; + struct ab32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct ab32_uart, serial); + + ch = -1; + if (hal_uart_getflag(UART0_BASE, UART_FLAG_RXPND)) { + ch = hal_uart_read(uart->handle.instance); + hal_uart_clrflag(UART0_BASE, UART_FLAG_RXPND); + } + + return ch; +} + +static int ab32_flush(struct rt_serial_device *serial) +{ + struct ab32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct ab32_uart, serial); + while(hal_uart_getflag(uart->handle.instance, UART_FLAG_TXPND) == 0); +} + +rt_bool_t ab32_int_txing(struct rt_serial_device *serial) +{ + struct ab32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct ab32_uart, serial); + + return uart->intTxing; +} + +static void ab32_start_tx(struct rt_serial_device *serial, rt_uint8_t ch) +{ + struct ab32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct ab32_uart, serial); + uart->intTxing = RT_TRUE; + hal_uart_control(uart->handle.instance, UART_TXIT_ENABLE, HAL_ENABLE); + hal_uart_write(uart->handle.instance, ch); +} + +static void ab32_stop_tx(struct rt_serial_device *serial) +{ + struct ab32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct ab32_uart, serial); + hal_uart_control(uart->handle.instance, UART_TXIT_ENABLE, HAL_DISABLE); + uart->intTxing = RT_FALSE; +} + +RT_SECTION(".irq.usart") +static void uart_isr(int vector, void *param) +{ + rt_interrupt_enter(); + +#ifdef BSP_USING_UART0 + if(hal_uart_getflag(UART0_BASE, UART_FLAG_RXPND)) //RX one byte finish + { + rt_hw_serial_isr(&(uart_obj[UART0_INDEX].serial), RT_SERIAL_EVENT_RX_IND); + hal_uart_clrflag(UART0_BASE, UART_FLAG_RXPND); + } + if(hal_uart_getflag(UART0_BASE, UART_FLAG_TXPND)) //TX one byte finish + { + rt_hw_serial_isr(&(uart_obj[UART0INDEX].serial), RT_SERIAL_EVENT_TX_DONE); + hal_uart_clrflag(UART0_BASE, UART_FLAG_TXPND); + } +#endif +#ifdef BSP_USING_UART1 + if(hal_uart_getflag(UART1_BASE, UART_FLAG_RXPND)) //RX one byte finish + { + rt_hw_serial_isr(&(uart_obj[UART1_INDEX].serial), RT_SERIAL_EVENT_RX_IND); + hal_uart_clrflag(UART1_BASE, UART_FLAG_RXPND); + } + if(hal_uart_getflag(UART1_BASE, UART_FLAG_TXPND)) //TX one byte finish + { + rt_hw_serial_isr(&(uart_obj[UART1_INDEX].serial), RT_SERIAL_EVENT_TX_DONE); + hal_uart_clrflag(UART1_BASE, UART_FLAG_TXPND); + } +#endif +#ifdef BSP_USING_UART2 + if(hal_uart_getflag(UART2_BASE, UART_FLAG_RXPND)) //RX one byte finish + { + rt_hw_serial_isr(&(uart_obj[UART2_INDEX].serial), RT_SERIAL_EVENT_RX_IND); + hal_uart_clrflag(UART2_BASE, UART_FLAG_RXPND); + } + if(hal_uart_getflag(UART2_BASE, UART_FLAG_TXPND)) //TX one byte finish + { + rt_hw_serial_isr(&(uart_obj[UART2_INDEX].serial), RT_SERIAL_EVENT_TX_DONE); + hal_uart_clrflag(UART2_BASE, UART_FLAG_TXPND); + } +#endif + + rt_interrupt_leave(); +} + +#ifdef HUART_ENABLE +RT_SECTION(".irq.huart") +void huart_timer_isr(void) +{ + huart_if_rx_ovflow(); + + if (0 == huart_get_rxcnt()) { + return; + } +} +#else +RT_SECTION(".irq.huart") +void huart_timer_isr(void) +{ +} +#endif + +static const struct rt_uart_ops ab32_uart_ops = +{ + .init = ab32_init, + .configure = ab32_configure, + .control = ab32_control, + .putc = ab32_putc, + .getc = ab32_getc, + .flush = ab32_flush, + .is_int_txing = ab32_int_txing, + .start_tx = ab32_start_tx, + .stop_tx = ab32_stop_tx, +}; + +int rt_hw_usart_init(void) +{ + rt_size_t obj_num = sizeof(uart_obj) / sizeof(struct ab32_uart); + rt_err_t result = 0; + + rt_hw_interrupt_install(IRQ_UART0_2_VECTOR, uart_isr, RT_NULL, "ut_isr"); + + for (int i = 0; i < obj_num; i++) + { + /* init UART object */ + uart_obj[i].uart_config = &uart_config[i]; + uart_obj[i].serial.ops = &ab32_uart_ops; + + /* register UART device */ + result = rt_hw_serial_register(&uart_obj[i].serial, uart_obj[i].uart_config->name, + RT_DEVICE_FLAG_RDWR + | RT_DEVICE_FLAG_INT_RX + | RT_DEVICE_FLAG_INT_TX + , RT_NULL); + RT_ASSERT(result == RT_EOK); + } + + return result; +} + +#endif diff --git a/bsp/bluetrum/libraries/hal_drivers/drv_usartX.h b/bsp/bluetrum/libraries/hal_drivers/drv_usartX.h new file mode 100644 index 00000000000..acd4b63a840 --- /dev/null +++ b/bsp/bluetrum/libraries/hal_drivers/drv_usartX.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020-2022, Bluetrum Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2020-11-20 greedyhao first version + * 2022-06-08 THEWON first version for serialX + */ + +#ifndef DRV_USART_H__ +#define DRV_USART_H__ + +#include "drv_common.h" + +#ifdef RT_USING_SERIAL + +/* an32 config class */ +struct ab32_uart_config +{ + const char *name; + hal_sfr_t instance; + uint8_t mode; +}; + +/* ab32 uart driver class */ +struct ab32_uart +{ + struct uart_handle handle; + struct rt_serial_device serial; + struct ab32_uart_config *uart_config; + rt_bool_t intTxing; +}; + +#endif + +int rt_hw_usart_init(void); + +#endif diff --git a/bsp/gd32/arm/libraries/gd32_drivers/SConscript b/bsp/gd32/arm/libraries/gd32_drivers/SConscript index f2ed0f2d71e..65ba16b0a6f 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/SConscript +++ b/bsp/gd32/arm/libraries/gd32_drivers/SConscript @@ -14,7 +14,10 @@ if GetDepend('RT_USING_PIN'): # add usart drivers. if GetDepend(['RT_USING_SERIAL']): - src += ['drv_usart.c'] + if GetDepend(['RT_USING_SERIAL_X']): + src += ['drv_usartX.c'] + else: + src += ['drv_usart.c'] # add i2c drivers. if GetDepend(['RT_USING_I2C', 'RT_USING_I2C_BITOPS']): diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_usartX.c b/bsp/gd32/arm/libraries/gd32_drivers/drv_usartX.c new file mode 100644 index 00000000000..93272250669 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_usartX.c @@ -0,0 +1,848 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2009-01-05 Bernard the first version + * 2010-03-29 Bernard remove interrupt Tx and DMA Rx mode + * 2012-02-08 aozima update for F4. + * 2012-07-28 aozima update for ART board. + * 2016-05-28 armink add DMA Rx mode + * 2022-06-09 THEWON first version for serialX + */ + +#include +#include +#include + +#ifdef RT_USING_SERIAL + +#if !defined(BSP_USING_UART0) && !defined(BSP_USING_UART1) && \ + !defined(BSP_USING_UART2) && !defined(BSP_USING_UART3) && \ + !defined(BSP_USING_UART4) && !defined(BSP_USING_UART5) && \ + !defined(BSP_USING_UART6) && !defined(BSP_USING_UART7) +#error "Please define at least one UARTx" + +#endif + +enum +{ +#ifdef BSP_USING_UART0 + UART0_INDEX, +#endif +#ifdef BSP_USING_UART1 + UART1_INDEX, +#endif +#ifdef BSP_USING_UART2 + UART2_INDEX, +#endif +#ifdef BSP_USING_UART3 + UART3_INDEX, +#endif +#ifdef BSP_USING_UART4 + UART4_INDEX, +#endif +#ifdef BSP_USING_UART5 + UART5_INDEX, +#endif +#ifdef BSP_USING_UART6 + UART6_INDEX, +#endif +#ifdef BSP_USING_UART7 + UART7_INDEX, +#endif +}; + +/* GD32 uart driver */ + +static void uart_isr(struct rt_serial_device *serial); + +#ifdef RT_SERIAL_USING_DMA +static void DMA_RX_Configuration(struct rt_serial_device *serial); +static void DMA_TX_Configuration(struct rt_serial_device *serial); +#endif + +static struct gd32_uart uarts[] = { + #ifdef BSP_USING_UART0 + { + .uart_periph = USART0, // uart peripheral index + .uart_config = { + .name = "uart0", + .irqn = USART0_IRQn, // uart iqrn + RCU_USART0, RCU_GPIOA, RCU_GPIOA, // periph clock, tx gpio clock, rt gpio clock + GPIOA, GPIO_AF_7, GPIO_PIN_9, // tx port, tx alternate, tx pin + GPIOA, GPIO_AF_7, GPIO_PIN_10, // rx port, rx alternate, rx pin + }, +#ifdef RT_SERIAL_USING_DMA + .dmaTxing = RT_FALSE, + .dma_rx = { + .dma_periph = DMA1, + .dma_channel = DMA_CH2, + .dma_subperi = DMA_SUBPERI4, + .dma_irq = DMA1_Channel2_IRQn, + }, + .dma_tx = { + .dma_periph = DMA1, + .dma_channel = DMA_CH7, + .dma_subperi = DMA_SUBPERI4, + .dma_irq = DMA1_Channel7_IRQn, + }, +#endif + }, + #endif + + #ifdef BSP_USING_UART1 + { + .uart_periph = USART1, // uart peripheral index + .uart_config = { + .name = "uart1", + .irqn = USART1_IRQn, // uart iqrn + RCU_USART1, RCU_GPIOA, RCU_GPIOA, // periph clock, tx gpio clock, rt gpio clock + GPIOA, GPIO_AF_7, GPIO_PIN_2, // tx port, tx alternate, tx pin + GPIOA, GPIO_AF_7, GPIO_PIN_3, // rx port, rx alternate, rx pin + } + }, + #endif + + #ifdef BSP_USING_UART2 + { + .uart_periph = USART2, // uart peripheral index + .uart_config = { + .name = "uart2", + .irqn = USART2_IRQn, // uart iqrn + RCU_USART2, RCU_GPIOB, RCU_GPIOB, // periph clock, tx gpio clock, rt gpio clock + GPIOB, GPIO_AF_7, GPIO_PIN_10, // tx port, tx alternate, tx pin + GPIOB, GPIO_AF_7, GPIO_PIN_11, // rx port, rx alternate, rx pin + } + }, + #endif + + #ifdef BSP_USING_UART3 + { + .uart_periph = UART3, // uart peripheral index + .uart_config = { + .name = "uart3", + .irqn = UART3_IRQn, // uart iqrn + RCU_UART3, RCU_GPIOC, RCU_GPIOC, // periph clock, tx gpio clock, rt gpio clock + GPIOC, GPIO_AF_8, GPIO_PIN_10, // tx port, tx alternate, tx pin + GPIOC, GPIO_AF_8, GPIO_PIN_11, // rx port, rx alternate, rx pin + } + }, + #endif + + #ifdef BSP_USING_UART4 + { + .uart_periph = UART4, // uart peripheral index + .uart_config = { + .name = "uart4", + .irqn = UART4_IRQn, // uart iqrn + RCU_UART4, RCU_GPIOC, RCU_GPIOD, // periph clock, tx gpio clock, rt gpio clock + GPIOC, GPIO_AF_8, GPIO_PIN_12, // tx port, tx alternate, tx pin + GPIOD, GPIO_AF_8, GPIO_PIN_2, // rx port, rx alternate, rx pin + } + }, + #endif + + #ifdef BSP_USING_UART5 + { + .uart_periph = USART5, // uart peripheral index + .uart_config = { + .name = "uart5", + .irqn = USART5_IRQn, // uart iqrn + RCU_USART5, RCU_GPIOC, RCU_GPIOC, // periph clock, tx gpio clock, rt gpio clock + GPIOC, GPIO_AF_8, GPIO_PIN_6, // tx port, tx alternate, tx pin + GPIOC, GPIO_AF_8, GPIO_PIN_7, // rx port, rx alternate, rx pin + } + }, + #endif + + #ifdef BSP_USING_UART6 + { + .uart_periph = UART6, // uart peripheral index + .uart_config = { + .name = "uart6", + .irqn = UART6_IRQn, // uart iqrn + RCU_UART6, RCU_GPIOE, RCU_GPIOE, // periph clock, tx gpio clock, rt gpio clock + GPIOE, GPIO_AF_8, GPIO_PIN_7, // tx port, tx alternate, tx pin + GPIOE, GPIO_AF_8, GPIO_PIN_8, // rx port, rx alternate, rx pin + } + }, + #endif + + #ifdef BSP_USING_UART7 + { + .uart_periph = UART7, // uart peripheral index + .uart_config = { + .name = "uart7", + .irqn = UART7_IRQn, // uart iqrn + RCU_UART7, RCU_GPIOE, RCU_GPIOE, // periph clock, tx gpio clock, rt gpio clock + GPIOE, GPIO_AF_8, GPIO_PIN_0, // tx port, tx alternate, tx pin + GPIOE, GPIO_AF_8, GPIO_PIN_1, // rx port, rx alternate, rx pin + } + }, + #endif +}; + +/** +* @brief UART MSP Initialization +* This function configures the hardware resources used in this example: +* - Peripheral's clock enable +* - Peripheral's GPIO Configuration +* - NVIC configuration for UART interrupt request enable +* @param huart: UART handle pointer +* @retval None +*/ +void gd32_uart_gpio_init(struct gd32_uart *uart) +{ + /* enable USART clock */ + rcu_periph_clock_enable(uart->uart_config.tx_gpio_clk); + rcu_periph_clock_enable(uart->uart_config.rx_gpio_clk); + rcu_periph_clock_enable(uart->uart_config.per_clk); + + /* connect port to USARTx_Tx */ + gpio_af_set(uart->uart_config.tx_port, uart->uart_config.tx_af, uart->uart_config.tx_pin); + + /* connect port to USARTx_Rx */ + gpio_af_set(uart->uart_config.rx_port, uart->uart_config.rx_af, uart->uart_config.rx_pin); + + /* configure USART Tx as alternate function push-pull */ + gpio_mode_set(uart->uart_config.tx_port, GPIO_MODE_AF, GPIO_PUPD_PULLUP, uart->uart_config.tx_pin); + gpio_output_options_set(uart->uart_config.tx_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, uart->uart_config.tx_pin); + + /* configure USART Rx as alternate function push-pull */ + gpio_mode_set(uart->uart_config.rx_port, GPIO_MODE_AF, GPIO_PUPD_NONE, uart->uart_config.rx_pin); + gpio_output_options_set(uart->uart_config.rx_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, uart->uart_config.rx_pin); +} + +static rt_err_t gd32_configure(struct rt_serial_device *serial, struct serial_configure *cfg) +{ + struct gd32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + RT_ASSERT(cfg != RT_NULL); + + uart = rt_container_of(serial, struct gd32_uart, serial); + + usart_baudrate_set(uart->uart_periph, cfg->baud_rate); + + switch (cfg->data_bits) + { + case DATA_BITS_9: + usart_word_length_set(uart->uart_periph, USART_WL_9BIT); + break; + + default: + usart_word_length_set(uart->uart_periph, USART_WL_8BIT); + break; + } + + switch (cfg->stop_bits) + { + case STOP_BITS_2: + usart_stop_bit_set(uart->uart_periph, USART_STB_2BIT); + break; + default: + usart_stop_bit_set(uart->uart_periph, USART_STB_1BIT); + break; + } + + switch (cfg->parity) + { + case PARITY_ODD: + usart_parity_config(uart->uart_periph, USART_PM_ODD); + break; + case PARITY_EVEN: + usart_parity_config(uart->uart_periph, USART_PM_EVEN); + break; + default: + usart_parity_config(uart->uart_periph, USART_PM_NONE); + break; + } + + return RT_EOK; +} + +static rt_err_t gd32_init(struct rt_serial_device *serial) +{ + struct gd32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct gd32_uart, serial); + + gd32_uart_gpio_init(uart); + + if (gd32_configure(serial, &serial->config) != RT_EOK) + { + return -RT_ERROR; + } + + usart_receive_config(uart->uart_periph, USART_RECEIVE_ENABLE); + usart_transmit_config(uart->uart_periph, USART_TRANSMIT_ENABLE); + + return RT_EOK; +} + +static rt_err_t gd32_control(struct rt_serial_device *serial, int cmd, void *arg) +{ + struct gd32_uart *uart; + rt_ubase_t ctrl_arg = (rt_ubase_t)arg; + + RT_ASSERT(serial != RT_NULL); + uart = rt_container_of(serial, struct gd32_uart, serial); + + switch (cmd) { + case RT_DEVICE_CTRL_OPEN: + usart_interrupt_disable(uart->uart_periph, USART_INT_TBE); + usart_interrupt_disable(uart->uart_periph, USART_INT_TC); + usart_flag_clear(uart->uart_periph, USART_FLAG_RBNE); + usart_flag_clear(uart->uart_periph, USART_FLAG_TBE); + usart_flag_clear(uart->uart_periph, USART_FLAG_TC); + /* enable rx irq */ + NVIC_SetPriority(uart->uart_config.irqn, 0); + NVIC_EnableIRQ(uart->uart_config.irqn); + usart_enable(uart->uart_periph); +#ifdef RT_SERIAL_USING_DMA + uart->dmaTxing = RT_FALSE; +#endif + break; + case RT_DEVICE_CTRL_CLOSE: + NVIC_DisableIRQ(uart->uart_config.irqn); + usart_interrupt_disable(uart->uart_periph, USART_INT_RBNE); + usart_interrupt_disable(uart->uart_periph, USART_INT_TBE); + usart_interrupt_disable(uart->uart_periph, USART_INT_IDLE); + usart_interrupt_disable(uart->uart_periph, USART_INT_TC); + usart_disable(uart->uart_periph); + usart_deinit(uart->uart_periph); +#ifdef RT_SERIAL_USING_DMA + NVIC_DisableIRQ(uart->dma_rx.dma_irq); + dma_interrupt_disable(uart->dma_rx.dma_periph, uart->dma_rx.dma_channel, DMA_CHXCTL_HTFIE); + dma_interrupt_disable(uart->dma_rx.dma_periph, uart->dma_rx.dma_channel, DMA_CHXCTL_FTFIE); + dma_channel_disable(uart->dma_rx.dma_periph, uart->dma_rx.dma_channel); + dma_deinit(uart->dma_rx.dma_periph, uart->dma_rx.dma_channel); + + NVIC_DisableIRQ(uart->dma_tx.dma_irq); + dma_interrupt_disable(uart->dma_tx.dma_periph, uart->dma_tx.dma_channel, DMA_CHXCTL_FTFIE); + dma_channel_disable(uart->dma_tx.dma_periph, uart->dma_tx.dma_channel); + dma_deinit(uart->dma_tx.dma_periph, uart->dma_tx.dma_channel); +#endif + break; + case RT_DEVICE_CTRL_CLR_INT: + /* disable interrupt */ + if (ctrl_arg & RT_DEVICE_FLAG_INT_RX) { + usart_interrupt_disable(uart->uart_periph, USART_INT_RBNE); + } +#ifdef RT_SERIAL_USING_DMA + /* disable DMA */ + if (ctrl_arg & RT_DEVICE_FLAG_DMA_RX) { + NVIC_DisableIRQ(uart->dma_rx.dma_irq); + dma_interrupt_disable(uart->dma_rx.dma_periph, uart->dma_rx.dma_channel, DMA_CHXCTL_HTFIE); + dma_interrupt_disable(uart->dma_rx.dma_periph, uart->dma_rx.dma_channel, DMA_CHXCTL_FTFIE); + } + if(ctrl_arg & RT_DEVICE_FLAG_DMA_TX) { + NVIC_DisableIRQ(uart->dma_tx.dma_irq); + dma_interrupt_disable(uart->dma_tx.dma_periph, uart->dma_tx.dma_channel, DMA_CHXCTL_FTFIE); + } +#endif + break; + case RT_DEVICE_CTRL_SET_INT: + /* enable interrupt */ + if (ctrl_arg & RT_DEVICE_FLAG_INT_RX) { + usart_interrupt_enable(uart->uart_periph, USART_INT_RBNE); + } + break; + /* USART config */ + case RT_DEVICE_CTRL_CONFIG : +#ifdef RT_SERIAL_USING_DMA + if (ctrl_arg & RT_DEVICE_FLAG_DMA_RX) { + DMA_RX_Configuration(serial); + } else if (ctrl_arg & RT_DEVICE_FLAG_DMA_TX) { + DMA_TX_Configuration(serial); + } +#endif + break; + default : + break; + } + + return RT_EOK; +} + +static int gd32_putc(struct rt_serial_device *serial, char ch) +{ + struct gd32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + uart = rt_container_of(serial, struct gd32_uart, serial); + + while((usart_flag_get(uart->uart_periph, USART_FLAG_TBE) == RESET)); + usart_data_transmit(uart->uart_periph, ch); + + return 1; +} + +static int gd32_getc(struct rt_serial_device *serial) +{ + int ch; + struct gd32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + uart = rt_container_of(serial, struct gd32_uart, serial); + + ch = -1; + if (usart_flag_get(uart->uart_periph, USART_FLAG_RBNE) != RESET) { + ch = usart_data_receive(uart->uart_periph); + } + return ch; +} + +static int gd32_flush(struct rt_serial_device *serial) +{ + struct gd32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct gd32_uart, serial); + while (!(usart_flag_get(uart->uart_periph, USART_FLAG_TBE) == SET && usart_flag_get(uart->uart_periph, USART_FLAG_TC) == SET)); + + return 1; +} + +static void gd32_start_tx(struct rt_serial_device *serial) +{ + struct gd32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct gd32_uart, serial); + usart_interrupt_enable(uart->uart_periph, USART_INT_TBE); +} + +static void gd32_stop_tx(struct rt_serial_device *serial) +{ + struct gd32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct gd32_uart, serial); + usart_interrupt_disable(uart->uart_periph, USART_INT_TBE); +} + +#ifdef RT_SERIAL_USING_DMA +static rt_bool_t gd32_is_dma_txing(struct rt_serial_device *serial) +{ + struct gd32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct gd32_uart, serial); + + return uart->dmaTxing; //RT_FALSE; +} + +static void gd32_start_dma_tx(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size) +{ + struct gd32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct gd32_uart, serial); + + // TODO: 启用 DMA 发送 + DMA_CHM0ADDR(uart->dma_tx.dma_periph, uart->dma_tx.dma_channel) = (uint32_t)(buf); + DMA_CHCNT(uart->dma_tx.dma_periph, uart->dma_tx.dma_channel) = (uint32_t)(size); + dma_channel_enable(uart->dma_tx.dma_periph, uart->dma_tx.dma_channel); + + uart->dmaTxing = RT_TRUE; +} + +static void gd32_stop_dma_tx(struct rt_serial_device *serial) +{ + struct gd32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct gd32_uart, serial); + + // TODO: 禁用 DMA 发送 + dma_channel_disable(uart->dma_tx.dma_periph, uart->dma_tx.dma_channel); + uart->dmaTxing = RT_FALSE; +} +#endif + +static void gd32_enable_interrupt(struct rt_serial_device *serial) +{ + struct gd32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct gd32_uart, serial); + + NVIC_EnableIRQ(uart->uart_config.irqn); +} + +static void gd32_disable_interrupt(struct rt_serial_device *serial) +{ + struct gd32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct gd32_uart, serial); + + NVIC_DisableIRQ(uart->uart_config.irqn); +} + +#ifdef RT_SERIAL_USING_DMA +/** + * DMA receive done process. This need add to DMA receive done ISR. + * + * @param serial serial device + */ +static void dma_rx_done_isr(struct rt_serial_device *serial) +{ + struct gd32_uart *uart; + rt_size_t dma_cnt; + + uart = rt_container_of(serial, struct gd32_uart, serial); + + dma_cnt = RT_SERIAL_DMA_BUFSZ - DMA_CHCNT(uart->dma_rx.dma_periph, uart->dma_rx.dma_channel); + + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (dma_cnt << 8)); + + dma_interrupt_flag_clear(uart->dma_rx.dma_periph, uart->dma_rx.dma_channel, DMA_INT_FLAG_HTF); + dma_interrupt_flag_clear(uart->dma_rx.dma_periph, uart->dma_rx.dma_channel, DMA_INT_FLAG_FTF); +} +/** + * DMA transmit done process. This need add to DMA transmit done ISR. + * + * @param serial serial device + */ +static void dma_tx_done_isr(struct rt_serial_device *serial) +{ + struct gd32_uart *uart; + rt_size_t dma_cnt; + + uart = rt_container_of(serial, struct gd32_uart, serial); + + dma_cnt = DMA_CHCNT(uart->dma_tx.dma_periph, uart->dma_tx.dma_channel); + + if (dma_cnt == 0) + { + rt_hw_serial_isr(&uart->serial, RT_SERIAL_EVENT_TX_DMADONE); + } + dma_interrupt_flag_clear(uart->dma_tx.dma_periph, uart->dma_tx.dma_channel, DMA_INT_FLAG_FTF); +} +#endif + +/** + * Uart common interrupt process. This need add to uart ISR. + * + * @param serial serial device + */ +static void uart_isr(struct rt_serial_device *serial) +{ + struct gd32_uart *uart; +#ifdef RT_SERIAL_USING_DMA + rt_size_t dma_cnt; +#endif + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct gd32_uart, serial); + + /* UART in mode Receiver -------------------------------------------------*/ + if ((usart_interrupt_flag_get(uart->uart_periph, USART_INT_FLAG_RBNE) != RESET)) + { + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND); + /* Clear RXNE interrupt flag */ + usart_interrupt_flag_clear(uart->uart_periph, USART_INT_FLAG_RBNE); + } + if ((usart_interrupt_flag_get(uart->uart_periph, USART_INT_FLAG_RBNE_ORERR) != RESET)) + { + usart_data_receive(uart->uart_periph); + } + if ((usart_interrupt_flag_get(uart->uart_periph, USART_INT_FLAG_TBE) != RESET)) + { + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_TX_DONE); + usart_interrupt_flag_clear(uart->uart_periph, USART_INT_FLAG_TBE); + } +#ifdef RT_SERIAL_USING_DMA + if ((usart_interrupt_flag_get(uart->uart_periph, USART_INT_FLAG_IDLE) != RESET)) + { + usart_data_receive(uart->uart_periph); + dma_cnt = RT_SERIAL_DMA_BUFSZ - DMA_CHCNT(uart->dma_rx.dma_periph, uart->dma_rx.dma_channel); + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (dma_cnt << 8)); + } + if ((usart_interrupt_flag_get(uart->uart_periph, USART_INT_FLAG_TC) != RESET)) + { + usart_interrupt_flag_clear(uart->uart_periph, USART_INT_FLAG_TC); + } +#endif + if ((usart_interrupt_flag_get(uart->uart_periph, USART_INT_FLAG_PERR) != RESET)) + { + usart_data_receive(uart->uart_periph); + } + if ((usart_interrupt_flag_get(uart->uart_periph, USART_INT_FLAG_EB) != RESET)) + { + usart_interrupt_flag_clear(uart->uart_periph, USART_INT_FLAG_EB); + } + if ((usart_interrupt_flag_get(uart->uart_periph, USART_INT_FLAG_RT) != RESET)) + { + usart_interrupt_flag_clear(uart->uart_periph, USART_INT_FLAG_RT); + } +} + +#if defined(BSP_USING_UART0) + +void USART0_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + uart_isr(&uarts[UART0_INDEX].serial); + + /* leave interrupt */ + rt_interrupt_leave(); +} + +#ifdef RT_SERIAL_USING_DMA +void DMA1_Channel2_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + dma_rx_done_isr(&uarts[UART0_INDEX].serial); + + /* leave interrupt */ + rt_interrupt_leave(); +} +void DMA1_Channel7_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + dma_tx_done_isr(&uarts[UART0_INDEX].serial); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#endif + +#endif /* BSP_USING_UART0 */ + +#if defined(BSP_USING_UART1) + +void USART1_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + uart_isr(&uarts[UART1_INDEX].serial); + + /* leave interrupt */ + rt_interrupt_leave(); +} + +#endif /* BSP_USING_UART1 */ + +#if defined(BSP_USING_UART2) + +void USART2_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + uart_isr(&uarts[UART2_INDEX].serial); + + /* leave interrupt */ + rt_interrupt_leave(); +} + +#endif /* BSP_USING_UART2 */ + +#if defined(BSP_USING_UART3) + +void UART3_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + uart_isr(&uarts[UART3_INDEX].serial); + + /* leave interrupt */ + rt_interrupt_leave(); +} + +#endif /* BSP_USING_UART3 */ + +#if defined(BSP_USING_UART4) + +void UART4_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + uart_isr(&uarts[UART4_INDEX].serial); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#endif /* BSP_USING_UART4 */ + +#if defined(BSP_USING_UART5) + +void USART5_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + uart_isr(&uarts[UART5_INDEX].serial); + + /* leave interrupt */ + rt_interrupt_leave(); +} + +#endif /* BSP_USING_UART5 */ + +#if defined(BSP_USING_UART6) + +void UART6_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + uart_isr(&uarts[UART6_INDEX].serial); + + /* leave interrupt */ + rt_interrupt_leave(); +} + +#endif /* BSP_USING_UART6 */ + +#if defined(BSP_USING_UART7) + +void UART7_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + uart_isr(&uarts[UART7_INDEX].serial); + + /* leave interrupt */ + rt_interrupt_leave(); +} + +#endif /* BSP_USING_UART7 */ + +#ifdef RT_SERIAL_USING_DMA +// TODO: 添加发送 DMA 配置,添加接收 DMA 配置 +static void DMA_RX_Configuration(struct rt_serial_device *serial) +{ + dma_single_data_parameter_struct dma_init_struct; + struct gd32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + uart = rt_container_of(serial, struct gd32_uart, serial); + + /* enable DMA1 */ + rcu_periph_clock_enable(RCU_DMA1); + + /* deinitialize DMA channel3(USART0 tx) */ + dma_deinit(uart->dma_rx.dma_periph, uart->dma_rx.dma_channel); + dma_init_struct.direction = DMA_PERIPH_TO_MEMORY; + dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; + dma_init_struct.memory0_addr = (uint32_t)(serial->serial_dma_rx); + dma_init_struct.number = RT_SERIAL_DMA_BUFSZ; + dma_init_struct.periph_addr = (uint32_t)(uart->uart_periph + 0x04); + dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE; + dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT; + dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH; + dma_init_struct.circular_mode = DMA_CIRCULAR_MODE_ENABLE; + + dma_single_data_mode_init(uart->dma_rx.dma_periph, uart->dma_rx.dma_channel, &dma_init_struct); + dma_channel_subperipheral_select(uart->dma_rx.dma_periph, uart->dma_rx.dma_channel, uart->dma_rx.dma_subperi); + + dma_interrupt_enable(uart->dma_rx.dma_periph, uart->dma_rx.dma_channel, DMA_CHXCTL_HTFIE); + dma_interrupt_enable(uart->dma_rx.dma_periph, uart->dma_rx.dma_channel, DMA_CHXCTL_FTFIE); + NVIC_SetPriority(uart->dma_rx.dma_irq, 0); + NVIC_EnableIRQ(uart->dma_rx.dma_irq); + dma_channel_enable(uart->dma_rx.dma_periph, uart->dma_rx.dma_channel); + usart_dma_receive_config(uart->uart_periph, USART_DENR_ENABLE); +} +static void DMA_TX_Configuration(struct rt_serial_device *serial) +{ + dma_single_data_parameter_struct dma_init_struct; + struct gd32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + uart = rt_container_of(serial, struct gd32_uart, serial); + + /* enable DMA1 */ + rcu_periph_clock_enable(RCU_DMA1); + /* deinitialize DMA channel3(USART0 tx) */ + dma_deinit(uart->dma_tx.dma_periph, uart->dma_tx.dma_channel); + dma_init_struct.direction = DMA_MEMORY_TO_PERIPH; + dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; +// dma_init_struct.memory0_addr = (uint32_t)tx_buffer; +// dma_init_struct.number = ARRAYNUM(tx_buffer); + dma_init_struct.periph_addr = (uint32_t)(uart->uart_periph + 0x04); + dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE; + dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT; + dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH; + dma_init_struct.circular_mode = DMA_CIRCULAR_MODE_DISABLE; + + dma_single_data_mode_init(uart->dma_tx.dma_periph, uart->dma_tx.dma_channel, &dma_init_struct); + dma_channel_subperipheral_select(uart->dma_tx.dma_periph, uart->dma_tx.dma_channel, uart->dma_tx.dma_subperi); + + dma_interrupt_enable(uart->dma_tx.dma_periph, uart->dma_tx.dma_channel, DMA_CHXCTL_FTFIE); + NVIC_SetPriority(uart->dma_tx.dma_irq, 0); + NVIC_EnableIRQ(uart->dma_tx.dma_irq); + + usart_interrupt_enable(uart->uart_periph, USART_INT_IDLE); + usart_interrupt_enable(uart->uart_periph, USART_INT_TC); + usart_dma_transmit_config(uart->uart_periph, USART_DENT_ENABLE); +} +#endif +static const struct rt_uart_ops gd32_uart_ops = +{ + .init = gd32_init, + .configure = gd32_configure, + .control = gd32_control, + .putc = gd32_putc, + .getc = gd32_getc, + .flush = gd32_flush, + .start_tx = gd32_start_tx, + .stop_tx = gd32_stop_tx, +#ifdef RT_SERIAL_USING_DMA + .is_dma_txing = gd32_is_dma_txing, + .start_dma_tx = gd32_start_dma_tx, + .stop_dma_tx = gd32_stop_dma_tx, +#endif + .enable_interrupt = gd32_enable_interrupt, + .disable_interrupt = gd32_disable_interrupt, +}; + +int rt_hw_usart_init(void) +{ + int i; + + for (i = 0; i < sizeof(uarts) / sizeof(uarts[0]); i++) + { + uarts[i].serial.ops = &gd32_uart_ops; + + /* register UART1 device */ + rt_hw_serial_register(&uarts[i].serial, + uarts[i].uart_config.name, + RT_DEVICE_FLAG_RDWR | + RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX +#ifdef RT_SERIAL_USING_DMA + | RT_DEVICE_FLAG_DMA_RX | RT_DEVICE_FLAG_DMA_TX +#endif + , RT_NULL); + } + + return 0; +} +#endif // RT_USING_SERIAL diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_usartX.h b/bsp/gd32/arm/libraries/gd32_drivers/drv_usartX.h new file mode 100644 index 00000000000..1a5a44a12d1 --- /dev/null +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_usartX.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2009-01-05 Bernard the first version + * 2022-06-09 THEWON first version for serialX + */ + +#ifndef __DRV_USARTX_H__ +#define __DRV_USARTX_H__ + +#include +#include +#include + +#define UART_ENABLE_IRQ(n) NVIC_EnableIRQ((n)) +#define UART_DISABLE_IRQ(n) NVIC_DisableIRQ((n)) + +/* stm32 config class */ +struct gd32_uart_config +{ + const char *name; + IRQn_Type irqn; + + rcu_periph_enum per_clk; + rcu_periph_enum tx_gpio_clk; + rcu_periph_enum rx_gpio_clk; + uint32_t tx_port; + uint16_t tx_af; + uint16_t tx_pin; + uint32_t rx_port; + uint16_t rx_af; + uint16_t rx_pin; +}; + +struct gd32_uart_dma +{ + /* dma channel */ + uint32_t dma_periph; + dma_channel_enum dma_channel; + dma_subperipheral_enum dma_subperi; + /* dma irq channel */ + IRQn_Type dma_irq; +}; + +/* gd32 uart driver class */ +struct gd32_uart +{ + uint32_t uart_periph; + struct rt_serial_device serial; + struct gd32_uart_config uart_config; +#ifdef RT_SERIAL_USING_DMA + rt_bool_t dmaTxing; + struct gd32_uart_dma dma_rx; + struct gd32_uart_dma dma_tx; +#endif +}; + +int rt_hw_usart_init(void); + +#endif // __DRV_USARTX_H__ diff --git a/bsp/n32/libraries/n32_drivers/SConscript b/bsp/n32/libraries/n32_drivers/SConscript index 107e4d1b333..8b2f3a26b77 100644 --- a/bsp/n32/libraries/n32_drivers/SConscript +++ b/bsp/n32/libraries/n32_drivers/SConscript @@ -14,6 +14,8 @@ if GetDepend(['RT_USING_PIN']): if GetDepend(['RT_USING_SERIAL']): if GetDepend(['RT_USING_SERIAL_V2']): src += ['drv_usart_v2.c'] + elif GetDepend(['RT_USING_SERIAL_X']): + src += ['drv_usartX.c'] else: src += ['drv_usart.c'] diff --git a/bsp/n32/libraries/n32_drivers/drv_usartX.c b/bsp/n32/libraries/n32_drivers/drv_usartX.c new file mode 100644 index 00000000000..cc025080fcb --- /dev/null +++ b/bsp/n32/libraries/n32_drivers/drv_usartX.c @@ -0,0 +1,839 @@ +/* + * File : usart.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006-2022, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rt-thread.org/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2009-01-05 Bernard the first version + * 2010-03-29 Bernard remove interrupt Tx and DMA Rx mode + * 2013-05-13 aozima update for kehong-lingtai. + * 2015-01-31 armink make sure the serial transmit complete in putc() + * 2016-05-13 armink add DMA Rx mode + * 2017-01-19 aubr.cool add interrupt Tx mode + * 2017-04-13 aubr.cool correct Rx parity err + * 2021-08-20 breo.com first version + * 2022-06-01 THEWON first version for serialX + */ + +#include +#include +#include +#include "drv_usartX.h" + +#define UART_ENABLE_IRQ(n) NVIC_EnableIRQ((n)) +#define UART_DISABLE_IRQ(n) NVIC_DisableIRQ((n)) + +#ifdef RT_SERIAL_USING_DMA +static void DMA_RX_Configuration(struct rt_serial_device *serial); +static void DMA_TX_Configuration(struct rt_serial_device *serial); +#endif + +static rt_err_t n32_uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg) +{ + struct n32_uart *uart; + USART_InitType USART_InitStructure; + + RT_ASSERT(serial != RT_NULL); + RT_ASSERT(cfg != RT_NULL); + + uart = rt_container_of(serial, struct n32_uart, serial); + + USART_InitStructure.BaudRate = cfg->baud_rate; + + switch (cfg->data_bits) + { + case DATA_BITS_8 : + USART_InitStructure.WordLength = USART_WL_8B; + break; + case DATA_BITS_9 : + USART_InitStructure.WordLength = USART_WL_9B; + break; + default: + USART_InitStructure.WordLength = USART_WL_8B; + break; + } + + switch (cfg->stop_bits) + { + case STOP_BITS_1 : + USART_InitStructure.StopBits = USART_STPB_1; + break; + case STOP_BITS_2 : + USART_InitStructure.StopBits = USART_STPB_2; + break; + default: + USART_InitStructure.StopBits = USART_STPB_1; + break; + } + + switch (cfg->parity) + case PARITY_NONE : + USART_InitStructure.Parity = USART_PE_NO; + break; + case PARITY_ODD : + USART_InitStructure.Parity = USART_PE_ODD; + break; + case PARITY_EVEN : + USART_InitStructure.Parity = USART_PE_EVEN; + break; + default: + USART_InitStructure.Parity = USART_PE_NO; + break; + } + + USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE; + USART_InitStructure.Mode = USART_MODE_RX | USART_MODE_TX; + USART_Init(uart->uart_device, &USART_InitStructure); + + return RT_EOK; +} + +static rt_err_t n32_uart_init(struct rt_serial_device *serial) +{ + struct n32_uart *uart; + USART_InitType USART_InitStructure; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct n32_uart, serial); + + n32_msp_usart_init(uart->uart_device); + + if (n32_uart_configure(serial, &serial->config) != RT_EOK) + { + return -RT_ERROR; + } + + /* Enable USART */ + USART_Enable(uart->uart_device, ENABLE); + + USART_ClrFlag(uart->uart_device, USART_FLAG_TXDE|USART_FLAG_TXC); + + return RT_EOK; +} + +static rt_err_t n32_uart_control(struct rt_serial_device *serial, int cmd, void *arg) +{ + struct n32_uart *uart; + rt_uint32_t ctrl_arg = (rt_uint32_t)(arg); + NVIC_InitType NVIC_InitStructure; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct n32_uart, serial); + + switch (cmd) { + case RT_DEVICE_CTRL_OPEN: + USART_ConfigInt(uart->uart_device, USART_INT_TXDE, DISABLE); + USART_ClrIntPendingBit(uart->uart_device, USART_INT_RXDNE); + USART_ClrIntPendingBit(uart->uart_device, USART_INT_TXDE); + USART_ClrIntPendingBit(uart->uart_device, USART_INT_TXC); + /* enable rx irq */ + UART_ENABLE_IRQ(uart->irq); +#ifdef RT_SERIAL_USING_DMA + uart->dmaTxing = RT_FALSE; +#endif + break; + case RT_DEVICE_CTRL_CLOSE: + /* disable rx irq */ + UART_DISABLE_IRQ(uart->irq); + USART_ConfigInt(uart->uart_device, USART_INT_RXDNE, DISABLE); + USART_ConfigInt(uart->uart_device, USART_INT_TXDE, DISABLE); + USART_ConfigInt(uart->uart_device, USART_INT_TXC, DISABLE); + USART_ClrIntPendingBit(uart->uart_device, USART_INT_RXDNE); + USART_ClrIntPendingBit(uart->uart_device, USART_INT_TXDE); + USART_ClrIntPendingBit(uart->uart_device, USART_INT_TXC); + +#ifdef RT_SERIAL_USING_DMA + NVIC_InitStructure.NVIC_IRQChannel = uart->dma_rx.dma_irq; + NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE; + NVIC_Init(&NVIC_InitStructure); + DMA_EnableChannel(uart->dma_rx.dma_ch, DISABLE); + DMA_ConfigInt(uart->dma_rx.dma_ch, DMA_INT_HTX, DISABLE); + DMA_ConfigInt(uart->dma_rx.dma_ch, DMA_INT_TXC, DISABLE); + DMA_DeInit(uart->dma_rx.dma_ch); + NVIC_InitStructure.NVIC_IRQChannel = uart->dma_tx.dma_irq; + NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE; + NVIC_Init(&NVIC_InitStructure); + DMA_EnableChannel(uart->dma_tx.dma_ch, DISABLE); + DMA_ConfigInt(uart->dma_tx.dma_ch, DMA_INT_TXC, DISABLE); + DMA_DeInit(uart->dma_tx.dma_ch); +#endif + break; + case RT_DEVICE_CTRL_CLR_INT: + /* disable interrupt */ + if (ctrl_arg & RT_DEVICE_FLAG_INT_RX) { + USART_ConfigInt(uart->uart_device, USART_INT_RXDNE, DISABLE); + } + +#ifdef RT_SERIAL_USING_DMA + /* disable DMA */ + if (ctrl_arg & RT_DEVICE_FLAG_DMA_RX) { + NVIC_InitStructure.NVIC_IRQChannel = uart->dma_rx.dma_irq; + NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE; + NVIC_Init(&NVIC_InitStructure); + DMA_ConfigInt(uart->dma_rx.dma_ch, DMA_INT_HTX, DISABLE); + DMA_ConfigInt(uart->dma_rx.dma_ch, DMA_INT_TXC, DISABLE); + } + if(ctrl_arg & RT_DEVICE_FLAG_DMA_TX) { + NVIC_InitStructure.NVIC_IRQChannel = uart->dma_tx.dma_irq; + NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE; + NVIC_Init(&NVIC_InitStructure); + DMA_ConfigInt(uart->dma_tx.dma_ch, DMA_INT_TXC, DISABLE); + } +#endif + break; + case RT_DEVICE_CTRL_SET_INT: + /* enable interrupt */ + if (ctrl_arg & RT_DEVICE_FLAG_INT_RX) { + USART_ConfigInt(uart->uart_device, USART_INT_RXDNE, ENABLE); + } + break; + /* USART config */ + case RT_DEVICE_CTRL_CONFIG : +#ifdef RT_SERIAL_USING_DMA + if (ctrl_arg & RT_DEVICE_FLAG_DMA_RX) { + DMA_RX_Configuration(serial); + } else if (ctrl_arg & RT_DEVICE_FLAG_DMA_TX) { + DMA_TX_Configuration(serial); + } +#endif + break; + default : + break; + } + return RT_EOK; +} + +static int n32_uart_putc(struct rt_serial_device *serial, char c) +{ + struct n32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct n32_uart, serial); + + while (!(uart->uart_device->STS & USART_FLAG_TXDE)); + uart->uart_device->DAT = c; + + return 1; +} + +static int n32_uart_getc(struct rt_serial_device *serial) +{ + int ch; + struct n32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct n32_uart, serial); + + ch = -1; + if (uart->uart_device->STS & USART_FLAG_RXDNE) + { + ch = uart->uart_device->DAT & 0xff; + } + + return ch; +} + +static int n32_uart_flush(struct rt_serial_device *serial) +{ + struct n32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct n32_uart, serial); + while(!((uart->uart_device->STS & USART_FLAG_TXDE) && (uart->uart_device->STS & USART_FLAG_TXC))); + + return 0; +} + +static void n32_start_tx(struct rt_serial_device *serial) +{ + struct n32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct n32_uart, serial); + + USART_ConfigInt(uart->uart_device, USART_INT_TXDE, ENABLE); +} + +static void n32_stop_tx(struct rt_serial_device *serial) +{ + struct n32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct n32_uart, serial); + + USART_ConfigInt(uart->uart_device, USART_INT_TXDE, DISABLE); +} + +#ifdef RT_SERIAL_USING_DMA +static rt_bool_t n32_is_dma_txing(struct rt_serial_device *serial) +{ + struct n32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct n32_uart, serial); + +// return uart->dmaTxing; //RT_FALSE; + return (DMA_GetFlagStatus(uart->dma_tx.dma_flag, uart->dma_tx.dma_module) == SET); +} + +static void n32_start_dma_tx(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size) +{ + struct n32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct n32_uart, serial); + + // TODO: 启用 DMA 发送 + uart->dma_tx.dma_ch->MADDR = (uint32_t)(buf); + uart->dma_tx.dma_ch->TXNUM = (uint32_t)(size); + DMA_EnableChannel(uart->dma_tx.dma_ch, ENABLE); + + uart->dmaTxing = RT_TRUE; +} + +static void n32_stop_dma_tx(struct rt_serial_device *serial) +{ + struct n32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct n32_uart, serial); + + // TODO: 禁用 DMA 发送 + DMA_EnableChannel(uart->dma_tx.dma_ch, DISABLE); + uart->dmaTxing = RT_FALSE; +} +#endif + +static void n32_enable_interrupt(struct rt_serial_device *serial) +{ + struct n32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct n32_uart, serial); + + UART_ENABLE_IRQ(uart->irq); +} + +static void n32_disable_interrupt(struct rt_serial_device *serial) +{ + struct n32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct n32_uart, serial); + + UART_DISABLE_IRQ(uart->irq); +} + +#ifdef RT_SERIAL_USING_DMA +/** + * DMA receive done process. This need add to DMA receive done ISR. + * + * @param serial serial device + */ +static void dma_rx_done_isr(struct rt_serial_device *serial) +{ + struct n32_uart *uart; + rt_size_t dma_cnt; + + uart = rt_container_of(serial, struct n32_uart, serial); + + dma_cnt = RT_SERIAL_DMA_BUFSZ - DMA_GetCurrDataCounter(uart->dma_rx.dma_ch); + + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (dma_cnt << 8)); + + DMA_ClearFlag(uart->dma_rx.dma_flag, uart->dma_rx.dma_module); + DMA_ClrIntPendingBit(uart->dma_rx.dma_flag, uart->dma_rx.dma_module); +} +/** + * DMA transmit done process. This need add to DMA transmit done ISR. + * + * @param serial serial device + */ +static void dma_tx_done_isr(struct rt_serial_device *serial) +{ + struct n32_uart *uart; + rt_size_t dma_cnt; + + uart = rt_container_of(serial, struct n32_uart, serial); + + dma_cnt = DMA_GetCurrDataCounter(uart->dma_tx.dma_ch); + + if (dma_cnt == 0) + { + rt_hw_serial_isr(&uart->serial, RT_SERIAL_EVENT_TX_DMADONE); + } + DMA_ClrIntPendingBit(uart->dma_tx.dma_flag, uart->dma_rx.dma_module); +} +#endif + +/** + * Uart common interrupt process. This need add to uart ISR. + * + * @param serial serial device + */ +static void uart_isr(struct rt_serial_device *serial) +{ + struct n32_uart *uart; +#ifdef RT_SERIAL_USING_DMA + rt_size_t dma_cnt; +#endif + + RT_ASSERT(uart != RT_NULL); + + uart = rt_container_of(serial, struct n32_uart, serial); + + if(USART_GetIntStatus(uart->uart_device, USART_INT_RXDNE) != RESET) + { + if(USART_GetFlagStatus(uart->uart_device, USART_FLAG_PEF) == RESET) + { + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND); + } + /* clear interrupt */ + USART_ClrIntPendingBit(uart->uart_device, USART_INT_RXDNE); + } + if(USART_GetIntStatus(uart->uart_device, USART_INT_TXDE) != RESET) + { + if(USART_GetFlagStatus(uart->uart_device, USART_FLAG_PEF) == RESET) + { + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_TX_DONE); + } + /* clear interrupt */ + USART_ClrIntPendingBit(uart->uart_device, USART_INT_TXDE); + } + +#ifdef RT_SERIAL_USING_DMA + if(USART_GetIntStatus(uart->uart_device, USART_INT_IDLEF) != RESET) + { + /* read a data for clear receive idle interrupt flag */ + USART_ReceiveData(uart->uart_device); + + dma_cnt = RT_SERIAL_DMA_BUFSZ - DMA_GetCurrDataCounter(uart->dma_rx.dma_ch); + + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (dma_cnt << 8)); + } +#endif + if (USART_GetFlagStatus(uart->uart_device, USART_FLAG_OREF) == SET) + { + n32_uart_getc(serial); + } +} + +static const struct rt_uart_ops n32_uart_ops = +{ + .configure = n32_uart_configure, + .control = n32_uart_control, + .putc = n32_uart_putc, + .getc = n32_uart_getc, + .flush = n32_uart_flush, + .start_tx = n32_start_tx, + .stop_tx = n32_stop_tx, +#ifdef RT_SERIAL_USING_DMA + .is_dma_txing = n32_is_dma_txing, + .start_dma_tx = n32_start_dma_tx, + .stop_dma_tx = n32_stop_dma_tx, +#endif + .enable_interrupt = n32_enable_interrupt, + .disable_interrupt = n32_disable_interrupt, +}; + +#if defined(BSP_USING_UART1) +/* UART1 device driver structure */ +struct n32_uart uart1 = +{ + .uart_device = USART1, + .irq = USART1_IRQn, +#ifdef RT_SERIAL_USING_DMA + .dmaTxing = RT_FALSE, + .dma_rx = { + DMA1_CH5, + DMA1, + DMA1_FLAG_TC5 | DMA1_FLAG_HT5, + DMA1_Channel5_IRQn, + }, + .dma_tx = { + DMA1_CH4, + DMA1, + DMA1_FLAG_TC4, + DMA1_Channel4_IRQn, + }, +#endif +}; + +void USART1_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + uart_isr(&uart1.serial); + + /* leave interrupt */ + rt_interrupt_leave(); +} + +#ifdef RT_SERIAL_USING_DMA +void DMA1_Channel5_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + dma_rx_done_isr(&uart1.serial); + + /* leave interrupt */ + rt_interrupt_leave(); +} +void DMA1_Channel4_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + dma_tx_done_isr(&uart1.serial); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#endif +#endif /* BSP_USING_UART1 */ + +#if defined(BSP_USING_UART2) +/* UART2 device driver structure */ +struct n32_uart uart2 = +{ + .uart_device = USART2, + .irq = USART2_IRQn, +#ifdef RT_SERIAL_USING_DMA + .dmaTxing = RT_FALSE, + .dma_rx = { + DMA1_CH6, + DMA1, + DMA1_FLAG_TC6 | DMA1_FLAG_HT6, + DMA1_Channel6_IRQn, + }, + .dma_tx = { + DMA1_CH7, + DMA1, + DMA1_FLAG_TC7, + DMA1_Channel7_IRQn, + }, +#endif +}; + +void USART2_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + uart_isr(&uart2.serial); + + /* leave interrupt */ + rt_interrupt_leave(); +} + +#ifdef RT_SERIAL_USING_DMA +void DMA1_Channel6_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + dma_rx_done_isr(&uart2.serial); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#endif +#endif /* BSP_USING_UART2 */ + +#if defined(BSP_USING_UART3) +/* UART3 device driver structure */ +struct n32_uart uart3 = +{ + .uart_device = USART3, + .irq = USART3_IRQn, +#ifdef RT_SERIAL_USING_DMA + .dmaTxing = RT_FALSE, + .dma_rx = { + DMA1_CH3, + DMA1, + DMA1_FLAG_TC3 | DMA1_FLAG_HT3, + DMA1_Channel3_IRQn, + }, + .dma_tx = { + DMA1_CH2, + DMA1, + DMA1_FLAG_TC2, + DMA1_Channel2_IRQn, + }, +#endif +}; + +void USART3_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + uart_isr(&uart3.serial); + + /* leave interrupt */ + rt_interrupt_leave(); +} + +#ifdef RT_SERIAL_USING_DMA +void DMA1_Channel3_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + dma_rx_done_isr(&uart3.serial); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#endif +#endif /* BSP_USING_UART3 */ + +#if defined(BSP_USING_UART4) +/* UART4 device driver structure */ +struct n32_uart uart4 = +{ + .uart_device = UART4, + .irq = UART4_IRQn, +#ifdef RT_SERIAL_USING_DMA + .dmaTxing = RT_FALSE, + .dma_rx = { + DMA2_CH3, + DMA2, + DMA2_FLAG_TC3 | DMA2_FLAG_HT3, + DMA2_Channel3_IRQn, + }, + .dma_tx = { + DMA2_CH5, + DMA2, + DMA2_FLAG_TC5, + DMA2_Channel5_IRQn, + }, +#endif +}; + +void UART4_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + uart_isr(&uart4.serial); + + /* leave interrupt */ + rt_interrupt_leave(); +} + +#ifdef RT_SERIAL_USING_DMA +void DMA2_Channel3_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + dma_rx_done_isr(&uart4.serial); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#endif +#endif /* BSP_USING_UART4 */ + +static void NVIC_Configuration(struct n32_uart *uart) +{ + NVIC_InitType NVIC_InitStructure; + + /* Enable the USART1 Interrupt */ + NVIC_InitStructure.NVIC_IRQChannel = uart->irq; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; + NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE; + NVIC_Init(&NVIC_InitStructure); +} + +#ifdef RT_SERIAL_USING_DMA +// TODO: 添加发送 DMA 配置,添加接收 DMA 配置 +static void DMA_RX_Configuration(struct rt_serial_device *serial) +{ + DMA_InitType DMA_InitStructure; + NVIC_InitType NVIC_InitStructure; + struct n32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + uart = rt_container_of(serial, struct n32_uart, serial); + + /* enable transmit idle interrupt */ + USART_ConfigInt(uart->uart_device, USART_INT_IDLEF, ENABLE); + + /* DMA clock enable */ + RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_DMA1, ENABLE); + RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_DMA2, ENABLE); + + /* rx dma config */ + DMA_InitStructure.PeriphAddr = (uint32_t)&(uart->uart_device->DAT); + DMA_InitStructure.MemAddr = (uint32_t)(serial->serial_dma_rx); + DMA_InitStructure.Direction = DMA_DIR_PERIPH_SRC; + DMA_InitStructure.BufSize = RT_SERIAL_DMA_BUFSZ; + DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE; + DMA_InitStructure.DMA_MemoryInc = DMA_MEM_INC_ENABLE; + DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_BYTE; + DMA_InitStructure.MemDataSize = DMA_MemoryDataSize_Byte; + DMA_InitStructure.CircularMode = DMA_MODE_CIRCULAR; + DMA_InitStructure.Priority = DMA_PRIORITY_HIGH; + DMA_InitStructure.Mem2Mem = DMA_M2M_DISABLE; + + DMA_DeInit(uart->dma_rx.dma_ch); + DMA_Init(uart->dma_rx.dma_ch, &DMA_InitStructure); + + DMA_ClearFlag(uart->dma_rx.dma_flag, uart->dma_rx.dma_module); + DMA_ConfigInt(uart->dma_rx.dma_ch, DMA_INT_HTX, ENABLE); + DMA_ConfigInt(uart->dma_rx.dma_ch, DMA_INT_TXC, ENABLE); + + USART_EnableDMA(uart->uart_device, USART_DMAREQ_RX, ENABLE); + DMA_EnableChannel(uart->dma_rx.dma_ch, ENABLE); + + /* rx dma interrupt config */ + NVIC_InitStructure.NVIC_IRQChannel = uart->dma_rx.dma_irq; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); +} +static void DMA_TX_Configuration(struct rt_serial_device *serial) +{ + DMA_InitType DMA_InitStructure; + NVIC_InitType NVIC_InitStructure; + struct n32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + uart = rt_container_of(serial, struct n32_uart, serial); + + /* enable transmit idle interrupt */ +// USART_ConfigInt(uart->uart_device, USART_INT_IDLEF, ENABLE); + + /* DMA clock enable */ + RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_DMA1, ENABLE); + RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_DMA2, ENABLE); + + /* tx dma config */ + DMA_InitStructure.PeriphAddr = (uint32_t)&(uart->uart_device->DAT); +// DMA_InitStructure.MemAddr = (uint32_t)(serial->serial_dma_tx); + DMA_InitStructure.Direction = DMA_DIR_PERIPH_DST; +// DMA_InitStructure.BufSize = serial->config.bufsz; + DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE; + DMA_InitStructure.DMA_MemoryInc = DMA_MEM_INC_ENABLE; + DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_BYTE; + DMA_InitStructure.MemDataSize = DMA_MemoryDataSize_Byte; + DMA_InitStructure.CircularMode = DMA_MODE_NORMAL; + DMA_InitStructure.Priority = DMA_PRIORITY_HIGH; + DMA_InitStructure.Mem2Mem = DMA_M2M_DISABLE; + + DMA_DeInit(uart->dma_tx.dma_ch); + DMA_Init(uart->dma_tx.dma_ch, &DMA_InitStructure); + + DMA_ClearFlag(uart->dma_tx.dma_flag, uart->dma_tx.dma_module); + DMA_ConfigInt(uart->dma_tx.dma_ch, DMA_INT_TXC, ENABLE); + USART_EnableDMA(uart->uart_device, USART_DMAREQ_TX, ENABLE); +// DMA_EnableChannel(uart->dma_tx.dma_ch, ENABLE); + + /* rx dma interrupt config */ + NVIC_InitStructure.NVIC_IRQChannel = uart->dma_tx.dma_irq; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); +} +#endif + +int rt_hw_usart_init(void) +{ + struct n32_uart *uart; + +#if defined(BSP_USING_UART1) + uart = &uart1; + + uart->serial.ops = &n32_uart_ops; + + NVIC_Configuration(uart); + + /* register UART1 device */ + rt_hw_serial_register(&uart->serial, "uart1", + RT_DEVICE_FLAG_RDWR | + RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX +#ifdef RT_SERIAL_USING_DMA + | RT_DEVICE_FLAG_DMA_RX | RT_DEVICE_FLAG_DMA_TX +#endif + , uart); +#endif /* BSP_USING_UART1 */ + +#if defined(BSP_USING_UART2) + uart = &uart2; + + uart->serial.ops = &n32_uart_ops; + + NVIC_Configuration(uart); + + /* register UART2 device */ + rt_hw_serial_register(&uart->serial, "uart2", + RT_DEVICE_FLAG_RDWR | + RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX +#ifdef RT_SERIAL_USING_DMA + | RT_DEVICE_FLAG_DMA_RX | RT_DEVICE_FLAG_DMA_TX +#endif + , uart); +#endif /* BSP_USING_UART2 */ + +#if defined(BSP_USING_UART3) + uart = &uart3; + + uart->serial.ops = &n32_uart_ops; + + NVIC_Configuration(uart); + + /* register UART3 device */ + rt_hw_serial_register(&uart->serial, "uart3", + RT_DEVICE_FLAG_RDWR | + RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX +#ifdef RT_SERIAL_USING_DMA + | RT_DEVICE_FLAG_DMA_RX | RT_DEVICE_FLAG_DMA_TX +#endif + , uart); +#endif /* BSP_USING_UART3 */ + +#if defined(BSP_USING_UART4) + uart = &uart4; + + uart->serial.ops = &n32_uart_ops; + + NVIC_Configuration(uart); + + /* register UART4 device */ + rt_hw_serial_register(&uart->serial, "uart4", + RT_DEVICE_FLAG_RDWR | + RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX +#ifdef RT_SERIAL_USING_DMA + | RT_DEVICE_FLAG_DMA_RX | RT_DEVICE_FLAG_DMA_TX +#endif + , uart); +#endif /* BSP_USING_UART4 */ + + return RT_EOK; +} +INIT_BOARD_EXPORT(rt_hw_usart_init); + diff --git a/bsp/n32/libraries/n32_drivers/drv_usartX.h b/bsp/n32/libraries/n32_drivers/drv_usartX.h new file mode 100644 index 00000000000..c383b6d55a9 --- /dev/null +++ b/bsp/n32/libraries/n32_drivers/drv_usartX.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-06-06 THEWON first version for serialX + */ + +#ifndef __DRV_USARTX_H__ +#define __DRV_USARTX_H__ + +#include "rtdevice.h" + +struct n32_uart_dma +{ + /* dma channel */ + DMA_ChannelType *dma_ch; + DMA_Module *dma_module; + /* dma global flag */ + uint32_t dma_flag; + /* dma irq channel */ + uint8_t dma_irq; +}; + +struct n32_uart +{ + USART_Module *uart_device; + struct rt_serial_device serial; + IRQn_Type irq; +#ifdef RT_SERIAL_USING_DMA + rt_bool_t dmaTxing; + struct n32_uart_dma dma_rx; + struct n32_uart_dma dma_tx; +#endif +}; + +#endif /* __DRV_USART_H__ */ diff --git a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_common.c b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_common.c index 11790226e54..415ff6ca35f 100644 --- a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_common.c +++ b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_common.c @@ -13,7 +13,11 @@ #include #include #include "board.h" +#ifdef RT_USING_SERIAL_X +#include "drv_uartX.h" +#else #include "drv_uart.h" +#endif #include "drv_sys.h" #if defined(BSP_USING_MMU) diff --git a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_uartX.c b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_uartX.c new file mode 100644 index 00000000000..1f17d89fbb9 --- /dev/null +++ b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_uartX.c @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-01 THEWON first version for serialX + */ + +#include + +#ifdef RT_USING_SERIAL + +#include +#include "drv_uartX.h" +#include "drv_sys.h" + +//#define DRV_DEBUG +#define LOG_TAG "drv.uart" +// #include + +#if !defined(BSP_USING_UART1) && !defined(BSP_USING_UART2) && !defined(BSP_USING_UART3) && \ + !defined(BSP_USING_UART4) && !defined(BSP_USING_UART5) && !defined(BSP_USING_UART6) && \ + !defined(BSP_USING_UART7) && !defined(BSP_USING_UART8) && !defined(BSP_USING_UART9) && \ + !defined(BSP_USING_UARTA) +#error "Please define at least one BSP_USING_UARTx" +/* this driver can be disabled at menuconfig -> RT-Thread Components -> Device Drivers */ +#endif + +static int nu_uart_flush(struct rt_serial_device *serial); + +static void nu_uart_isr(int vector, void *param); + +static struct nu_uart nu_uart_arr[] = +{ +#ifdef BSP_USING_UART0 + UART0_CONFIG, +#endif +#ifdef BSP_USING_UART1 + UART1_CONFIG, +#endif +#ifdef BSP_USING_UART2 + UART2_CONFIG, +#endif +#ifdef BSP_USING_UART3 + UART3_CONFIG, +#endif +#ifdef BSP_USING_UART4 + UART4_CONFIG, +#endif +#ifdef BSP_USING_UART5 + UART5_CONFIG, +#endif +#ifdef BSP_USING_UART6 + UART6_CONFIG, +#endif +#ifdef BSP_USING_UART7 + UART7_CONFIG, +#endif +#ifdef BSP_USING_UART8 + UART8_CONFIG, +#endif +#ifdef BSP_USING_UART9 + UART9_CONFIG, +#endif +#ifdef BSP_USING_UARTA + UARTA_CONFIG, +#endif +}; + +// for ALL uarts +static void nu_uart_isr(int vector, void *param) +{ + /* Get base address of uart register */ + nu_uart_t serial = (nu_uart_t)param; + UART_T *uart_base = ((nu_uart_t)serial)->uart_base; + + /* Get interrupt event */ + uint32_t u32IntSts = uart_base->INTSTS; + uint32_t u32FIFOSts = uart_base->FIFOSTS; + + if (u32IntSts & (UART_INTSTS_RDAINT_Msk | UART_INTSTS_RXTOINT_Msk)) { // Received Data Available interrupt + rt_hw_serial_isr(&serial->dev, RT_SERIAL_EVENT_RX_IND); + } + + if (u32IntSts & UART_INTSTS_THREINT_Msk) { // Transmit Holding Register Empty interrupt + rt_hw_serial_isr(&serial->dev, RT_SERIAL_EVENT_TX_DONE | (16<<8)); + } + + // if (uRegISR & UART_INTSTS_MODEMINT_Msk) { + // uRegMSR = huart->Instance->MSR; + // uRegMSR |= UART_MSR_DCTSF_Msk; + // huart->Instance->MSR = uRegMSR; + // } + + // if (uRegISR & UART_ISR_BUF_ERR_INT_Msk) { + // if (uRegFSR & (UART_FSR_TX_OVER_IF_Msk)) { + // huart->Instance->FSR = UART_FSR_TX_OVER_IF_Msk; + // } + // if (uRegFSR & (UART_FSR_RX_OVER_IF_Msk)) { + // huart->Instance->FSR = UART_FSR_RX_OVER_IF_Msk; + // } + // } + + // if (uRegFSR & (UART_FSR_BIF_Msk | UART_FSR_FEF_Msk | UART_FSR_PEF_Msk | UART_FSR_RX_OVER_IF_Msk | UART_FSR_TX_OVER_IF_Msk)) { + // huart->Instance->FSR = (UART_FSR_BIF_Msk | UART_FSR_FEF_Msk | UART_FSR_PEF_Msk | UART_FSR_RX_OVER_IF_Msk | UART_FSR_TX_OVER_IF_Msk); + // } + uart_base->INTSTS = u32IntSts; + uart_base->FIFOSTS = u32FIFOSts; +} + +/** + * Configure uart port + */ +static rt_err_t nu_uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg) +{ + rt_err_t ret = RT_EOK; + uint32_t uart_word_len = 0; + uint32_t uart_stop_bit = 0; + uint32_t uart_parity = 0; + + /* Get base address of uart register */ + UART_T *uart_base = ((nu_uart_t)serial)->uart_base; + + /* Check baudrate */ + RT_ASSERT(cfg->baud_rate != 0); + + /* Check word len */ + switch (cfg->data_bits) { + case DATA_BITS_5: + uart_word_len = UART_WORD_LEN_5; + break; + case DATA_BITS_6: + uart_word_len = UART_WORD_LEN_6; + break; + case DATA_BITS_7: + uart_word_len = UART_WORD_LEN_7; + break; + case DATA_BITS_8: + uart_word_len = UART_WORD_LEN_8; + break; + default: + uart_word_len = UART_WORD_LEN_8; + break; + } + + /* Check stop bit */ + switch (cfg->stop_bits) { + case STOP_BITS_1: + uart_stop_bit = UART_STOP_BIT_1; + break; + case STOP_BITS_2: + uart_stop_bit = UART_STOP_BIT_2; + break; + default: + uart_stop_bit = UART_STOP_BIT_1; + break; + } + + /* Check parity */ + switch (cfg->parity) + { + case PARITY_NONE: + uart_parity = UART_PARITY_NONE; + break; + case PARITY_ODD: + uart_parity = UART_PARITY_ODD; + break; + case PARITY_EVEN: + uart_parity = UART_PARITY_EVEN; + break; + default: + uart_parity = UART_PARITY_NONE; + break; + } + + /* Set line configuration. */ + UART_SetLineConfig(uart_base, cfg->baud_rate, uart_word_len, uart_parity, uart_stop_bit); + + return RT_EOK; +} + +/** + * Initialize uart port + */ +static rt_err_t nu_uart_init(struct rt_serial_device *serial) +{ + /* Get base address of uart register */ + UART_T *uart_base = ((nu_uart_t)serial)->uart_base; + +// nu_sys_ip_reset(((nu_uart_t)serial)->rstidx); + + /* Open Uart and set UART Baudrate */ + UART_Open(uart_base, serial->config->baud_rate); + + if (nu_uart_configure(serial, &serial->config) != RT_EOK) + { + return -RT_ERROR; + } + + return RT_EOK; +} + +static rt_err_t nu_uart_control(struct rt_serial_device *serial, int cmd, void *arg) +{ + nu_uart_t psNuUart = (nu_uart_t)serial; + rt_err_t result = RT_EOK; + rt_uint32_t flag; + rt_ubase_t ctrl_arg = (rt_ubase_t)arg; + + RT_ASSERT(serial != RT_NULL); + + /* Get base address of uart register */ + UART_T *uart_base = psNuUart->uart_base; + + switch (cmd) { + case RT_DEVICE_CTRL_OPEN: + /* Enable interrupt. */ + rt_hw_interrupt_umask(((nu_uart_t)serial)->irqn); + break; + case RT_DEVICE_CTRL_CLR_INT: + /* disable interrupt */ + if (ctrl_arg & RT_DEVICE_FLAG_INT_TX) { + UART_DISABLE_INT(uart_base, UART_INTEN_THREIEN_Msk); + } + if (ctrl_arg & RT_DEVICE_FLAG_INT_RX) { + flag = UART_INTEN_RDAIEN_Msk | UART_INTEN_RXTOIEN_Msk | UART_INTEN_TOCNTEN_Msk; + UART_DISABLE_INT(uart_base, flag); + } + +#ifdef RT_SERIAL_USING_DMA + /* disable DMA */ +#endif + break; + case RT_DEVICE_CTRL_SET_INT: + /* enable interrupt */ + if (ctrl_arg & RT_DEVICE_FLAG_INT_TX) { + UART_ENABLE_INT(uart_base, UART_INTEN_THREIEN_Msk); + } + if (ctrl_arg & RT_DEVICE_FLAG_INT_RX) { + flag = UART_INTEN_RDAIEN_Msk | UART_INTEN_RXTOIEN_Msk | UART_INTEN_TOCNTEN_Msk; + UART_ENABLE_INT(uart_base, flag); + } + break; + case RT_DEVICE_CTRL_CONFIG: +#ifdef RT_SERIAL_USING_DMA +#endif + break; + case RT_DEVICE_CTRL_CLOSE: + /* disable interrupt */ + rt_hw_interrupt_mask(psNuUart->irqn); + + /* Close UART port */ + UART_Close(uart_base); + break; + default : + break; + } + return RT_EOK; +} + +static int nu_uart_putc(struct rt_serial_device *serial, char c) +{ + UART_T *uart_base; + + RT_ASSERT(serial != RT_NULL); + + /* Get base address of uart register */ + uart_base = ((nu_uart_t)serial)->uart_base; + /* Waiting if TX-FIFO is full. */ + while (UART_IS_TX_FULL(uart_base)); + /* Put char into TX-FIFO */ + UART_WRITE(uart_base, c); + return 1; +} + +static int nu_uart_getc(struct rt_serial_device *serial) +{ + int ch; + UART_T *uart_base; + + RT_ASSERT(serial != RT_NULL); + + /* Get base address of uart register */ + uart_base = ((nu_uart_t)serial)->uart_base; + + /* Return failure if RX-FIFO is empty. */ + if (UART_GET_RX_EMPTY(uart_base)) { + return -1; + } + /* Get char from RX-FIFO */ + ch = UART_READ(uart_base); + + return ch; +} + +static int nu_uart_flush(struct rt_serial_device *serial) +{ + UART_T *uart_base; + + RT_ASSERT(serial != RT_NULL); + + /* Get base address of uart register */ + uart_base = ((nu_uart_t)serial)->uart_base; + while(!UART_IS_TX_EMPTY(uart_base)){;} + + return 1; +} + +static void nu_start_tx(struct rt_serial_device *serial) +{ + UART_T *uart_base; + + RT_ASSERT(serial != RT_NULL); + + uart_base = ((nu_uart_t)serial)->uart_base; + + /* enable interrupt */ + UART_ENABLE_INT(uart_base, UART_INTEN_THREIEN_Msk); +} + +static void nu_stop_tx(struct rt_serial_device *serial) +{ + UART_T *uart_base; + + RT_ASSERT(serial != RT_NULL); + + uart_base = ((nu_uart_t)serial)->uart_base; + + /* disable interrupt */ + UART_DISABLE_INT(uart_base, UART_INTEN_THREIEN_Msk); +} + +static void nu_enable_interrupt(struct rt_serial_device *serial) +{ + RT_ASSERT(serial != RT_NULL); + + rt_hw_interrupt_umask(((nu_uart_t)serial)->irqn); +} + +static void nu_disable_interrupt(struct rt_serial_device *serial) +{ + RT_ASSERT(serial != RT_NULL); + + rt_hw_interrupt_mask(((nu_uart_t)serial)->irqn); +} + +static const struct rt_uart_ops nu_uart_ops = +{ + .init = nu_uart_init, + .configure = nu_uart_configure, + .control = nu_uart_control, + .putc = nu_uart_putc, + .getc = nu_uart_getc, + .flush = nu_uart_flush, + .start_tx = nu_start_tx, + .stop_tx = nu_stop_tx, + .enable_interrupt = nu_enable_interrupt, + .disable_interrupt = nu_disable_interrupt, +}; + +int rt_hw_uart_init(void) +{ + rt_size_t obj_num = sizeof(nu_uart_arr) / sizeof(struct nu_uart); + rt_err_t result = 0; + int i; + + for (i = 0; i < obj_num; i++) + { + /* init UART object */ + nu_uart_arr[i].dev.ops = &nu_uart_ops; + + rt_hw_interrupt_install(nu_uart_arr[i].irqn, nu_uart_isr, &nu_uart_arr[i], nu_uart_arr[i].name); + + nu_sys_ipclk_enable(nu_uart_arr[i].clkidx); + + /* register UART device */ + result = rt_hw_serial_register(&nu_uart_arr[i].dev, nu_uart_arr[i].name, + RT_DEVICE_FLAG_RDWR + | RT_DEVICE_FLAG_INT_RX + | RT_DEVICE_FLAG_INT_TX + , NULL); + RT_ASSERT(result == RT_EOK); + } + + return result; +} + +#endif /* RT_USING_SERIAL */ diff --git a/bsp/nuvoton/libraries/nuc980/rtt_port/drv_uartX.h b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_uartX.h new file mode 100644 index 00000000000..98cea487fac --- /dev/null +++ b/bsp/nuvoton/libraries/nuc980/rtt_port/drv_uartX.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-01 THEWON first version for serialX + */ + +#ifndef __DRV_UART_H__ +#define __DRV_UART_H__ + +#include +#include "rtdevice.h" +#include +#include +#include "NuMicro.h" +#include + +typedef void(*uart_isr_cb)(int, void*); + +/* Private typedef --------------------------------------------------------------*/ +struct nu_uart +{ + struct rt_serial_device dev; + char *name; + UART_T *uart_base; + IRQn_Type irqn; + E_SYS_IPRST rstidx; + E_SYS_IPCLK clkidx; + +#if defined(RT_SERIAL_USING_DMA) + uint32_t dma_flag; + int16_t pdma_perp_tx; + int8_t pdma_chanid_tx; + + int16_t pdma_perp_rx; + int8_t pdma_chanid_rx; + int32_t rx_write_offset; + int32_t rxdma_trigger_len; + + nu_pdma_desc_t pdma_rx_desc; +#endif + +}; +typedef struct nu_uart *nu_uart_t; + +#if defined(BSP_USING_UART0) +#ifndef UART0_CONFIG +#define UART0_CONFIG \ + { \ + .name = "uart0", \ + .uart_base = UART0, \ + .irqn = IRQ_UART0, \ + .rstidx = UART0RST, \ + .clkidx = UART0CKEN, \ + } +#endif /* UART0_CONFIG */ + +#endif /* BSP_USING_UART0 */ + +#if defined(BSP_USING_UART1) +#ifndef UART1_CONFIG +#define UART1_CONFIG \ + { \ + .name = "uart1", \ + .uart_base = UART1, \ + .irqn = IRQ_UART1, \ + .rstidx = UART1RST, \ + .clkidx = UART1CKEN, \ + } +#endif /* UART1_CONFIG */ + +#endif /* BSP_USING_UART1 */ + +int rt_hw_uart_init(void); + +#endif /* __DRV_UART_H__ */ diff --git a/bsp/renesas/libraries/HAL_Drivers/SConscript b/bsp/renesas/libraries/HAL_Drivers/SConscript index 37e7359d950..feb5d010712 100644 --- a/bsp/renesas/libraries/HAL_Drivers/SConscript +++ b/bsp/renesas/libraries/HAL_Drivers/SConscript @@ -12,6 +12,8 @@ src = Split(""" if GetDepend(['BSP_USING_UART']): if GetDepend(['RT_USING_SERIAL_V2']): src += ['drv_usart_v2.c'] + elif GetDepend(['RT_USING_SERIAL_X']): + src += ['drv_usartX.c'] else: print("\nThe current project does not support serial-v1\n") Return('group') diff --git a/bsp/renesas/libraries/HAL_Drivers/drv_usartX.c b/bsp/renesas/libraries/HAL_Drivers/drv_usartX.c new file mode 100644 index 00000000000..f620dde27e9 --- /dev/null +++ b/bsp/renesas/libraries/HAL_Drivers/drv_usartX.c @@ -0,0 +1,625 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-07-29 KyleChan first version + * 2022-06-08 THEWON first version for serialX + */ + +#include + +#ifdef RT_USING_SERIAL_X + +//#define DRV_DEBUG +#define DBG_TAG "drv.usart" +#ifdef DRV_DEBUG + #define DBG_LVL DBG_LOG +#else + #define DBG_LVL DBG_INFO +#endif /* DRV_DEBUG */ +#include + +/* SCI SCR register bit masks */ +#define SCI_SCR_TEIE_MASK (0x04U) ///< Transmit End Interrupt Enable +#define SCI_SCR_RE_MASK (0x10U) ///< Receive Enable +#define SCI_SCR_TE_MASK (0x20U) ///< Transmit Enable +#define SCI_SCR_RIE_MASK (0x40U) ///< Receive Interrupt Enable +#define SCI_SCR_TIE_MASK (0x80U) ///< Transmit Interrupt Enable + +static struct ra_uart_config uart_config[] = +{ +#ifdef BSP_USING_UART0 + UART0_CONFIG, +#endif + +#ifdef BSP_USING_UART1 + UART1_CONFIG, +#endif + +#ifdef BSP_USING_UART2 + UART2_CONFIG, +#endif + +#ifdef BSP_USING_UART3 + UART3_CONFIG, +#endif + +#ifdef BSP_USING_UART4 + UART4_CONFIG, +#endif + +#ifdef BSP_USING_UART5 + UART5_CONFIG, +#endif + +#ifdef BSP_USING_UART6 + UART6_CONFIG, +#endif + +#ifdef BSP_USING_UART7 + UART7_CONFIG, +#endif + +#ifdef BSP_USING_UART8 + UART8_CONFIG, +#endif + +#ifdef BSP_USING_UART9 + UART9_CONFIG, +#endif +}; + +enum +{ +#ifdef BSP_USING_UART0 + UART0_INDEX, +#endif + +#ifdef BSP_USING_UART1 + UART1_INDEX, +#endif + +#ifdef BSP_USING_UART2 + UART2_INDEX, +#endif + +#ifdef BSP_USING_UART3 + UART3_INDEX, +#endif + +#ifdef BSP_USING_UART4 + UART4_INDEX, +#endif + +#ifdef BSP_USING_UART5 + UART5_INDEX, +#endif + +#ifdef BSP_USING_UART6 + UART6_INDEX, +#endif + +#ifdef BSP_USING_UART7 + UART7_INDEX, +#endif + +#ifdef BSP_USING_UART8 + UART8_INDEX, +#endif + +#ifdef BSP_USING_UART9 + UART9_INDEX, +#endif +}; + +static struct ra_uart uart_obj[sizeof(uart_config) / sizeof(uart_config[0])] = {0}; + +static void ra_uart_get_config(void) +{ + struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; + +#ifdef BSP_USING_UART0 + uart_obj[UART0_INDEX].serial.config = config; +#endif + +#ifdef BSP_USING_UART1 + uart_obj[UART1_INDEX].serial.config = config; +#endif + +#ifdef BSP_USING_UART2 + uart_obj[UART2_INDEX].serial.config = config; +#endif + +#ifdef BSP_USING_UART3 + uart_obj[UART3_INDEX].serial.config = config; +#endif + +#ifdef BSP_USING_UART4 + uart_obj[UART4_INDEX].serial.config = config; +#endif + +#ifdef BSP_USING_UART6 + uart_obj[UART6_INDEX].serial.config = config; +#endif + +#ifdef BSP_USING_UART7 + uart_obj[UART7_INDEX].serial.config = config; + uart_config[UART7_INDEX].uart_cfg = g_uart7_cfg; +#endif + +#ifdef BSP_USING_UART8 + uart_obj[UART8_INDEX].serial.config = config; +#endif + +#ifdef BSP_USING_UART9 + uart_obj[UART9_INDEX].serial.config = config; +#endif +} + +/* + * UART interface + */ +static rt_err_t ra_uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg) +{ + struct ra_uart *uart; + RT_ASSERT(serial != RT_NULL); + RT_ASSERT(cfg != RT_NULL); + + fsp_err_t err = FSP_SUCCESS; + + uart = rt_container_of(serial, struct ra_uart, serial); + RT_ASSERT(uart != RT_NULL); + + if (cfg->data_bits == DATA_BITS_7) + { + uart->uart_config->uart_cfg.data_bits = UART_DATA_BITS_7; + } + else if (cfg->data_bits == DATA_BITS_8) + { + uart->uart_config->uart_cfg.data_bits = UART_DATA_BITS_8; + } + else if (cfg->data_bits == DATA_BITS_9) + { + uart->uart_config->uart_cfg.data_bits = UART_DATA_BITS_9; + } + + if (cfg->stop_bits == STOP_BITS_1) + { + uart->uart_config->uart_cfg.stop_bits = UART_STOP_BITS_1; + } + else if (cfg->stop_bits == STOP_BITS_2) + { + uart->uart_config->uart_cfg.stop_bits = UART_STOP_BITS_2; + } + + if (cfg->parity == PARITY_NONE) + { + uart->uart_config->uart_cfg.parity = UART_PARITY_OFF; + } + else if (cfg->parity == PARITY_ODD) + { + uart->uart_config->uart_cfg.parity = UART_PARITY_ODD; + } + else if (cfg->parity == PARITY_EVEN) + { + uart->uart_config->uart_cfg.parity = UART_PARITY_EVEN; + } + + err = R_SCI_UART_Open(uart->uart_config->p_api_ctrl, &uart->uart_config->uart_cfg); + if (FSP_SUCCESS != err) + { + return -RT_ERROR; + } + + return RT_EOK; +} + +/* + * Initialize UART interface + */ +static rt_err_t ra_uart_init(struct rt_serial_device *serial) +{ + if (ra_uart_configure(serial, &serial->config) != RT_EOK) + { + return -RT_ERROR; + } + + return RT_EOK; +} + +static rt_err_t ra_uart_control(struct rt_serial_device *serial, int cmd, void *arg) +{ + struct ra_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct ra_uart, serial); + + switch (cmd) { + case RT_DEVICE_CTRL_OPEN: + uart->intTxing = RT_FALSE; + break; + case RT_DEVICE_CTRL_CLOSE: + R_SCI_UART_Close(uart->uart_config->p_api_ctrl); + break; + case RT_DEVICE_CTRL_CLR_INT: + /* disable interrupt */ + break; + case RT_DEVICE_CTRL_SET_INT: + /* enable interrupt */ + break; + /* USART config */ + case RT_DEVICE_CTRL_CONFIG : + break; + default : + break; + } + return RT_EOK; +} + +static int ra_uart_putc(struct rt_serial_device *serial, char c, rt_bool_t useint) +{ + struct ra_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct ra_uart, serial); + + sci_uart_instance_ctrl_t *p_ctrl = (sci_uart_instance_ctrl_t *)uart->uart_config->p_api_ctrl; + + while ((p_ctrl->p_reg->SSR_b.TDRE) == 0); + p_ctrl->p_reg->TDR = c; + if (useint) { + p_ctrl->p_reg->SCR |= SCI_SCR_TE_MASK; + p_ctrl->p_reg->SCR |= SCI_SCR_TIE_MASK; + } + + return RT_EOK; +} + +static int ra_uart_getc(struct rt_serial_device *serial) +{ + int ch; + struct ra_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct ra_uart, serial); + + sci_uart_instance_ctrl_t *p_ctrl = (sci_uart_instance_ctrl_t *)uart->uart_config->p_api_ctrl; + + ch = -1; + if ((p_ctrl->p_reg->SSR_b.RDRF) == 1) { + ch = p_ctrl->p_reg->RDR & 0xFF; + p_ctrl->p_reg->SSR_b.RDRF = 0; + } + + return ch; +} + +static int ra_uart_flush(struct rt_serial_device *serial) +{ + struct ra_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct ra_uart, serial); + + sci_uart_instance_ctrl_t *p_ctrl = (sci_uart_instance_ctrl_t *)uart->uart_config->p_api_ctrl; + + while (!((p_ctrl->p_reg->SSR_b.TEND) == 1 && (p_ctrl->p_reg->SSR_b.TDRE) == 1)); + + return 0; +} + +rt_bool_t ra_int_txing(struct rt_serial_device *serial) +{ + struct ra_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct ra_uart, serial); + + return uart->intTxing; +} + +static void ra_start_tx(struct rt_serial_device *serial, rt_uint8_t ch) +{ + struct ra_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct ra_uart, serial); + + sci_uart_instance_ctrl_t *p_ctrl = (sci_uart_instance_ctrl_t *)uart->uart_config->p_api_ctrl; + + uart->intTxing = RT_TRUE; + p_ctrl->p_reg->SCR &= ~(SCI_SCR_TIE_MASK | SCI_SCR_TEIE_MASK); + p_ctrl->p_reg->TDR = ch; + p_ctrl->p_reg->SCR |= SCI_SCR_TE_MASK; + /* Trigger a TXI interrupt. This triggers the transfer instance or a TXI interrupt if the transfer instance is + * not used. */ + p_ctrl->p_reg->SCR |= SCI_SCR_TIE_MASK; +} + +static void ra_stop_tx(struct rt_serial_device *serial) +{ + struct ra_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct ra_uart, serial); + + sci_uart_instance_ctrl_t *p_ctrl = (sci_uart_instance_ctrl_t *)uart->uart_config->p_api_ctrl; + + p_ctrl->p_reg->SCR &= ~(SCI_SCR_TIE_MASK | SCI_SCR_TEIE_MASK); + uart->intTxing = RT_FALSE; +} + +#ifdef BSP_USING_UART0 +void user_uart0_callback(uart_callback_args_t *p_args) +{ + rt_interrupt_enter(); + + struct rt_serial_device *serial = &uart_obj[UART0_INDEX].serial; + RT_ASSERT(serial != RT_NULL); + + if (UART_EVENT_RX_CHAR == p_args->event) + { + struct rt_serial_rx_fifo *rx_fifo; + rx_fifo = (struct rt_serial_rx_fifo *) serial->serial_rx; + RT_ASSERT(rx_fifo != RT_NULL); + + rt_ringbuffer_putchar(&(rx_fifo->rb), (rt_uint8_t)p_args->data); + + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND); + } + + rt_interrupt_leave(); +} +#endif + +#ifdef BSP_USING_UART1 +void user_uart1_callback(uart_callback_args_t *p_args) +{ + rt_interrupt_enter(); + + struct rt_serial_device *serial = &uart_obj[UART1_INDEX].serial; + RT_ASSERT(serial != RT_NULL); + + if (UART_EVENT_RX_CHAR == p_args->event) + { + struct rt_serial_rx_fifo *rx_fifo; + rx_fifo = (struct rt_serial_rx_fifo *) serial->serial_rx; + RT_ASSERT(rx_fifo != RT_NULL); + + rt_ringbuffer_putchar(&(rx_fifo->rb), (rt_uint8_t)p_args->data); + + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND); + } + + rt_interrupt_leave(); +} +#endif + +#ifdef BSP_USING_UART2 +void user_uart2_callback(uart_callback_args_t *p_args) +{ + rt_interrupt_enter(); + + struct rt_serial_device *serial = &uart_obj[UART2_INDEX].serial; + RT_ASSERT(serial != RT_NULL); + + if (UART_EVENT_RX_CHAR == p_args->event) + { + struct rt_serial_rx_fifo *rx_fifo; + rx_fifo = (struct rt_serial_rx_fifo *) serial->serial_rx; + RT_ASSERT(rx_fifo != RT_NULL); + + rt_ringbuffer_putchar(&(rx_fifo->rb), (rt_uint8_t)p_args->data); + + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND); + } + + rt_interrupt_leave(); +} +#endif + +#ifdef BSP_USING_UART3 +void user_uart3_callback(uart_callback_args_t *p_args) +{ + rt_interrupt_enter(); + + struct rt_serial_device *serial = &uart_obj[UART3_INDEX].serial; + RT_ASSERT(serial != RT_NULL); + + if (UART_EVENT_RX_CHAR == p_args->event) + { + struct rt_serial_rx_fifo *rx_fifo; + rx_fifo = (struct rt_serial_rx_fifo *) serial->serial_rx; + RT_ASSERT(rx_fifo != RT_NULL); + + rt_ringbuffer_putchar(&(rx_fifo->rb), (rt_uint8_t)p_args->data); + + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND); + } + + rt_interrupt_leave(); +} +#endif + +#ifdef BSP_USING_UART4 +void user_uart4_callback(uart_callback_args_t *p_args) +{ + rt_interrupt_enter(); + + struct rt_serial_device *serial = &uart_obj[UART4_INDEX].serial; + RT_ASSERT(serial != RT_NULL); + + if (UART_EVENT_RX_CHAR == p_args->event) + { + struct rt_serial_rx_fifo *rx_fifo; + rx_fifo = (struct rt_serial_rx_fifo *) serial->serial_rx; + RT_ASSERT(rx_fifo != RT_NULL); + + rt_ringbuffer_putchar(&(rx_fifo->rb), (rt_uint8_t)p_args->data); + + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND); + } + + rt_interrupt_leave(); +} +#endif + +#ifdef BSP_USING_UART5 +void user_uart5_callback(uart_callback_args_t *p_args) +{ + rt_interrupt_enter(); + + struct rt_serial_device *serial = &uart_obj[UART5_INDEX].serial; + RT_ASSERT(serial != RT_NULL); + + if (UART_EVENT_RX_CHAR == p_args->event) + { + struct rt_serial_rx_fifo *rx_fifo; + rx_fifo = (struct rt_serial_rx_fifo *) serial->serial_rx; + RT_ASSERT(rx_fifo != RT_NULL); + + rt_ringbuffer_putchar(&(rx_fifo->rb), (rt_uint8_t)p_args->data); + + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND); + } + + rt_interrupt_leave(); +} +#endif + +#ifdef BSP_USING_UART6 +void user_uart6_callback(uart_callback_args_t *p_args) +{ + rt_interrupt_enter(); + + struct rt_serial_device *serial = &uart_obj[UART6_INDEX].serial; + RT_ASSERT(serial != RT_NULL); + + if (UART_EVENT_RX_CHAR == p_args->event) + { + struct rt_serial_rx_fifo *rx_fifo; + rx_fifo = (struct rt_serial_rx_fifo *) serial->serial_rx; + RT_ASSERT(rx_fifo != RT_NULL); + + rt_ringbuffer_putchar(&(rx_fifo->rb), (rt_uint8_t)p_args->data); + + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND); + } + + rt_interrupt_leave(); +} +#endif + +#ifdef BSP_USING_UART7 +void user_uart7_callback(uart_callback_args_t *p_args) +{ + rt_interrupt_enter(); + + struct rt_serial_device *serial = &uart_obj[UART7_INDEX].serial; + RT_ASSERT(serial != RT_NULL); + + if (UART_EVENT_RX_CHAR == p_args->event) + { + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND); + } else if (UART_EVENT_TX_COMPLETE == p_args->event) { + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_TX_DONE); + } + + rt_interrupt_leave(); +} +#endif + +#ifdef BSP_USING_UART8 +void user_uart8_callback(uart_callback_args_t *p_args) +{ + rt_interrupt_enter(); + + struct rt_serial_device *serial = &uart_obj[UART8_INDEX].serial; + RT_ASSERT(serial != RT_NULL); + + if (UART_EVENT_RX_CHAR == p_args->event) + { + struct rt_serial_rx_fifo *rx_fifo; + rx_fifo = (struct rt_serial_rx_fifo *) serial->serial_rx; + RT_ASSERT(rx_fifo != RT_NULL); + + rt_ringbuffer_putchar(&(rx_fifo->rb), (rt_uint8_t)p_args->data); + + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND); + } + + rt_interrupt_leave(); +} +#endif + +#ifdef BSP_USING_UART9 +void user_uart9_callback(uart_callback_args_t *p_args) +{ + rt_interrupt_enter(); + + struct rt_serial_device *serial = &uart_obj[UART9_INDEX].serial; + RT_ASSERT(serial != RT_NULL); + + if (UART_EVENT_RX_CHAR == p_args->event) + { + struct rt_serial_rx_fifo *rx_fifo; + rx_fifo = (struct rt_serial_rx_fifo *) serial->serial_rx; + RT_ASSERT(rx_fifo != RT_NULL); + + rt_ringbuffer_putchar(&(rx_fifo->rb), (rt_uint8_t)p_args->data); + + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND); + } + + rt_interrupt_leave(); +} +#endif + +static const struct rt_uart_ops ra_uart_ops = +{ + .init = ra_uart_init, + .configure = ra_uart_configure, + .control = ra_uart_control, + .putc = ra_uart_putc, + .getc = ra_uart_getc, + .flush = ra_uart_flush, + .is_int_txing = ra_int_txing, + .start_tx = ra_start_tx, + .stop_tx = ra_stop_tx, +}; + + +int rt_hw_usart_init(void) +{ + rt_err_t result = 0; + rt_size_t obj_num = sizeof(uart_obj) / sizeof(struct ra_uart); + + ra_uart_get_config(); + for (int i = 0; i < obj_num; i++) + { + /* init UART object */ + uart_obj[i].uart_config = &uart_config[i]; + uart_obj[i].serial.ops = &ra_uart_ops; + /* register UART device */ + result = rt_hw_serial_register(&uart_obj[i].serial, + uart_obj[i].uart_config->name, + RT_DEVICE_FLAG_RDWR | + RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX, + NULL); + RT_ASSERT(result == RT_EOK); + } + + return result; +} + +#endif /* RT_USING_SERIAL_X */ diff --git a/bsp/renesas/libraries/HAL_Drivers/drv_usartX.h b/bsp/renesas/libraries/HAL_Drivers/drv_usartX.h new file mode 100644 index 00000000000..2862c5c409f --- /dev/null +++ b/bsp/renesas/libraries/HAL_Drivers/drv_usartX.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-07-29 KyleChan first version + * 2022-06-08 THEWON first version for serialX + */ + +#ifndef __DRV_USART_V2_H__ +#define __DRV_USART_V2_H__ + +#include +#include +#include +#include +#include +#include + +/* renesas config class */ +struct ra_uart_config +{ + const char *name; + uart_ctrl_t * p_api_ctrl; + uart_cfg_t uart_cfg; +}; + +struct ra_uart +{ + struct rt_serial_device serial; + struct ra_uart_config *uart_config; + rt_bool_t intTxing; +}; + +int rt_hw_usart_init(void); + +#endif /* __DRV_USART_H__ */ diff --git a/bsp/stm32/libraries/HAL_Drivers/SConscript b/bsp/stm32/libraries/HAL_Drivers/SConscript index 7057495e6e4..9b11c368085 100644 --- a/bsp/stm32/libraries/HAL_Drivers/SConscript +++ b/bsp/stm32/libraries/HAL_Drivers/SConscript @@ -13,6 +13,8 @@ if GetDepend(['RT_USING_PIN']): if GetDepend(['RT_USING_SERIAL']): if GetDepend(['RT_USING_SERIAL_V2']): src += ['drv_usart_v2.c'] + elif GetDepend(['RT_USING_SERIAL_X']): + src += ['drv_usartX.c'] else: src += ['drv_usart.c'] diff --git a/bsp/stm32/libraries/HAL_Drivers/drv_usartX.c b/bsp/stm32/libraries/HAL_Drivers/drv_usartX.c new file mode 100644 index 00000000000..02781d5e454 --- /dev/null +++ b/bsp/stm32/libraries/HAL_Drivers/drv_usartX.c @@ -0,0 +1,939 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-04-10 THEWON first version for serialX + */ + +#include "board.h" +#include "drv_usartX.h" +#include "drv_config.h" + +#ifdef RT_USING_SERIAL + +//#define DRV_DEBUG +#define LOG_TAG "drv.usart" +#include + +#if !defined(BSP_USING_UART1) && !defined(BSP_USING_UART2) && !defined(BSP_USING_UART3) && \ + !defined(BSP_USING_UART4) && !defined(BSP_USING_UART5) && !defined(BSP_USING_UART6) && \ + !defined(BSP_USING_UART7) && !defined(BSP_USING_UART8) && !defined(BSP_USING_LPUART1) +#error "Please define at least one BSP_USING_UARTx" +/* this driver can be disabled at menuconfig -> RT-Thread Components -> Device Drivers */ +#endif + +#ifdef RT_SERIAL_USING_DMA +static void stm32_dma_rx_config(struct rt_serial_device *serial); +static void stm32_dma_tx_config(struct rt_serial_device *serial); +#endif + +enum +{ +#ifdef BSP_USING_UART1 + UART1_INDEX, +#endif +#ifdef BSP_USING_UART2 + UART2_INDEX, +#endif +#ifdef BSP_USING_UART3 + UART3_INDEX, +#endif +#ifdef BSP_USING_UART4 + UART4_INDEX, +#endif +#ifdef BSP_USING_UART5 + UART5_INDEX, +#endif +#ifdef BSP_USING_UART6 + UART6_INDEX, +#endif +#ifdef BSP_USING_UART7 + UART7_INDEX, +#endif +#ifdef BSP_USING_UART8 + UART8_INDEX, +#endif +#ifdef BSP_USING_LPUART1 + LPUART1_INDEX, +#endif +}; + +static struct stm32_uart_config uart_config[] = +{ +#ifdef BSP_USING_UART1 + UART1_CONFIG, +#endif +#ifdef BSP_USING_UART2 + UART2_CONFIG, +#endif +#ifdef BSP_USING_UART3 + UART3_CONFIG, +#endif +#ifdef BSP_USING_UART4 + UART4_CONFIG, +#endif +#ifdef BSP_USING_UART5 + UART5_CONFIG, +#endif +#ifdef BSP_USING_UART6 + UART6_CONFIG, +#endif +#ifdef BSP_USING_UART7 + UART7_CONFIG, +#endif +#ifdef BSP_USING_UART8 + UART8_CONFIG, +#endif +#ifdef BSP_USING_LPUART1 + LPUART1_CONFIG, +#endif +}; + +static struct stm32_uart uart_obj[sizeof(uart_config) / sizeof(uart_config[0])] = {0}; + +static rt_uint32_t stm32_uart_get_mask(rt_uint32_t word_length, rt_uint32_t parity) +{ + rt_uint32_t mask; + if (word_length == UART_WORDLENGTH_8B) + { + if (parity == UART_PARITY_NONE) + { + mask = 0x00FFU ; + } + else + { + mask = 0x007FU ; + } + } +#ifdef UART_WORDLENGTH_9B + else if (word_length == UART_WORDLENGTH_9B) + { + if (parity == UART_PARITY_NONE) + { + mask = 0x01FFU ; + } + else + { + mask = 0x00FFU ; + } + } +#endif +#ifdef UART_WORDLENGTH_7B + else if (word_length == UART_WORDLENGTH_7B) + { + if (parity == UART_PARITY_NONE) + { + mask = 0x007FU ; + } + else + { + mask = 0x003FU ; + } + } + else + { + mask = 0x0000U; + } +#endif + return mask; +} + +static rt_err_t stm32_configure(struct rt_serial_device *serial, struct serial_configure *cfg) +{ + struct stm32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + RT_ASSERT(cfg != RT_NULL); + + uart = rt_container_of(serial, struct stm32_uart, serial); + uart->handle.Instance = uart->uart_config->Instance; + uart->handle.Init.BaudRate = cfg->baud_rate; + uart->handle.Init.Mode = UART_MODE_TX_RX; + uart->handle.Init.OverSampling = UART_OVERSAMPLING_16; + + switch (cfg->flowcontrol) + { + case RT_SERIAL_FLOWCONTROL_NONE: + uart->handle.Init.HwFlowCtl = UART_HWCONTROL_NONE; + break; + case RT_SERIAL_FLOWCONTROL_CTSRTS: + uart->handle.Init.HwFlowCtl = UART_HWCONTROL_RTS_CTS; + break; + default: + uart->handle.Init.HwFlowCtl = UART_HWCONTROL_NONE; + break; + } + + switch (cfg->data_bits) + { + case DATA_BITS_8: + if (cfg->parity == PARITY_ODD || cfg->parity == PARITY_EVEN) + uart->handle.Init.WordLength = UART_WORDLENGTH_9B; + else + uart->handle.Init.WordLength = UART_WORDLENGTH_8B; + break; + case DATA_BITS_9: + uart->handle.Init.WordLength = UART_WORDLENGTH_9B; + break; + default: + uart->handle.Init.WordLength = UART_WORDLENGTH_8B; + break; + } + + switch (cfg->stop_bits) + { + case STOP_BITS_1: + uart->handle.Init.StopBits = UART_STOPBITS_1; + break; + case STOP_BITS_2: + uart->handle.Init.StopBits = UART_STOPBITS_2; + break; + default: + uart->handle.Init.StopBits = UART_STOPBITS_1; + break; + } + + switch (cfg->parity) + { + case PARITY_NONE: + uart->handle.Init.Parity = UART_PARITY_NONE; + break; + case PARITY_ODD: + uart->handle.Init.Parity = UART_PARITY_ODD; + break; + case PARITY_EVEN: + uart->handle.Init.Parity = UART_PARITY_EVEN; + break; + default: + uart->handle.Init.Parity = UART_PARITY_NONE; + break; + } + + uart->uart_config->mask = stm32_uart_get_mask(uart->handle.Init.WordLength, uart->handle.Init.Parity); + if (HAL_UART_Init(&uart->handle) != HAL_OK) + { + return -RT_ERROR; + } + + return RT_EOK; +} + +static rt_err_t stm32_init(struct rt_serial_device *serial) +{ + if (stm32_configure(serial, &serial->config) != RT_EOK) + { + return -RT_ERROR; + } + + return RT_EOK; +} + +static rt_err_t stm32_control(struct rt_serial_device *serial, int cmd, void *arg) +{ + struct stm32_uart *uart; + rt_ubase_t ctrl_arg = (rt_ubase_t)arg; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct stm32_uart, serial); + + switch (cmd) { + case RT_DEVICE_CTRL_OPEN: + __HAL_UART_DISABLE_IT(&(uart->handle), UART_IT_TXE); + UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_RXNE); + UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_TXE); + UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_TC); + /* enable interrupt */ + HAL_NVIC_SetPriority(uart->uart_config->irq_type, 1, 0); + HAL_NVIC_EnableIRQ(uart->uart_config->irq_type); +#ifdef RT_SERIAL_USING_DMA + uart->dmaTxing = RT_FALSE; +#endif + break; + case RT_DEVICE_CTRL_CLOSE: + HAL_NVIC_DisableIRQ(uart->uart_config->irq_type); +#ifdef RT_SERIAL_USING_DMA + HAL_NVIC_DisableIRQ(uart->uart_config->dma_conf_rx->dma_irq); + if (HAL_DMA_Abort(&(uart->dma_rx.handle)) != HAL_OK) { + RT_ASSERT(0); + } + if (HAL_DMA_DeInit(&(uart->dma_rx.handle)) != HAL_OK) { + RT_ASSERT(0); + } + + HAL_NVIC_DisableIRQ(uart->uart_config->dma_conf_tx->dma_irq); + if (HAL_DMA_Abort(&(uart->dma_tx.handle)) != HAL_OK) { + RT_ASSERT(0); + } + if (HAL_DMA_DeInit(&(uart->dma_tx.handle)) != HAL_OK) { + RT_ASSERT(0); + } +#endif + __HAL_UART_DISABLE_IT(&(uart->handle), UART_IT_RXNE); + __HAL_UART_DISABLE_IT(&(uart->handle), UART_IT_TXE); + if (HAL_UART_DeInit(&(uart->handle)) != HAL_OK ) { + } + break; + /* disable interrupt */ + case RT_DEVICE_CTRL_CLR_INT: + /* disable interrupt */ + if (ctrl_arg & RT_DEVICE_FLAG_INT_RX) { + __HAL_UART_DISABLE_IT(&(uart->handle), UART_IT_RXNE); + } + +#ifdef RT_SERIAL_USING_DMA + /* disable DMA */ + if (ctrl_arg & RT_DEVICE_FLAG_DMA_RX) { + HAL_NVIC_DisableIRQ(uart->uart_config->dma_conf_rx->dma_irq); + } + if(ctrl_arg & RT_DEVICE_FLAG_DMA_TX) { + HAL_NVIC_DisableIRQ(uart->uart_config->dma_conf_tx->dma_irq); + } +#endif + break; + /* enable interrupt */ + case RT_DEVICE_CTRL_SET_INT: + /* enable rx irq */ + if (ctrl_arg & RT_DEVICE_FLAG_INT_RX) { + __HAL_UART_ENABLE_IT(&(uart->handle), UART_IT_RXNE); + } + break; +#ifdef RT_SERIAL_USING_DMA + case RT_DEVICE_CTRL_CONFIG: + if (ctrl_arg & RT_DEVICE_FLAG_DMA_RX) { + stm32_dma_rx_config(serial); + } else if (ctrl_arg & RT_DEVICE_FLAG_DMA_TX) { + stm32_dma_tx_config(serial); + } + break; +#endif + default : + break; + } + return RT_EOK; +} + +static int stm32_putc(struct rt_serial_device *serial, char c) +{ + struct stm32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct stm32_uart, serial); + + while (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_TXE) == RESET); + UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_TC); +#if defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32F0) \ + || defined(SOC_SERIES_STM32L0) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32H7) \ + || defined(SOC_SERIES_STM32G4) || defined(SOC_SERIES_STM32MP1) || defined(SOC_SERIES_STM32WB) || defined(SOC_SERIES_STM32F3) + uart->handle.Instance->TDR = c; +#else + uart->handle.Instance->DR = c; +#endif + return 1; +} + +static int stm32_getc(struct rt_serial_device *serial) +{ + int ch = -1; + struct stm32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct stm32_uart, serial); + + if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_RXNE) == RESET) + { + return -1; + } +#if defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32F0) \ + || defined(SOC_SERIES_STM32L0) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32H7) \ + || defined(SOC_SERIES_STM32G4) || defined(SOC_SERIES_STM32MP1) || defined(SOC_SERIES_STM32WB)|| defined(SOC_SERIES_STM32F3) + ch = uart->handle.Instance->RDR & uart->uart_config->mask; +#else + ch = uart->handle.Instance->DR & uart->uart_config->mask; +#endif + return ch; +} + +static int stm32_flush(struct rt_serial_device *serial) +{ + struct stm32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct stm32_uart, serial); + while (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_TC) == RESET); + + return 1; +} + +static void stm32_start_tx(struct rt_serial_device *serial) +{ + struct stm32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct stm32_uart, serial); + __HAL_UART_ENABLE_IT(&(uart->handle), UART_IT_TXE); +} + +static void stm32_stop_tx(struct rt_serial_device *serial) +{ + struct stm32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct stm32_uart, serial); + __HAL_UART_DISABLE_IT(&(uart->handle), UART_IT_TXE); +} + +#ifdef RT_SERIAL_USING_DMA +static rt_bool_t stm32_is_dma_txing(struct rt_serial_device *serial) +{ + struct stm32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct stm32_uart, serial); + + return uart->dmaTxing; //RT_FALSE; +} + +static void stm32_start_dma_tx(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size) +{ + struct stm32_uart *uart; + HAL_StatusTypeDef status; + DMA_HandleTypeDef *hdma; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct stm32_uart, serial); + + do { // may fallin dead loop + status = HAL_UART_Transmit_DMA(&uart->handle, buf, size); + } while (status != HAL_OK); + uart->dmaTxing = RT_TRUE; +} + +static void stm32_stop_dma_tx(struct rt_serial_device *serial) +{ + struct stm32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct stm32_uart, serial); + + if ((uart->dma_tx.handle.Instance->CR & DMA_SxCR_EN) == DMA_SxCR_EN) { + return; + } + __HAL_DMA_DISABLE(&uart->dma_tx.handle); + uart->dmaTxing = RT_FALSE; +} +#endif + +static void stm32_enable_interrupt(struct rt_serial_device *serial) +{ + struct stm32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct stm32_uart, serial); + + HAL_NVIC_EnableIRQ(uart->uart_config->irq_type); +#ifdef RT_SERIAL_USING_DMA + if (uart->uart_dma_flag) { + HAL_NVIC_EnableIRQ(uart->uart_config->dma_conf_rx->dma_irq); + } +#endif +} + +static void stm32_disable_interrupt(struct rt_serial_device *serial) +{ + struct stm32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct stm32_uart, serial); + + HAL_NVIC_DisableIRQ(uart->uart_config->irq_type); +#ifdef RT_SERIAL_USING_DMA + if (uart->uart_dma_flag) { + HAL_NVIC_DisableIRQ(uart->uart_config->dma_conf_rx->dma_irq); + } +#endif +} + +/** + * Uart common interrupt process. This need add to uart ISR. + * + * @param serial serial device + */ +static void uart_isr(struct rt_serial_device *serial) +{ + struct stm32_uart *uart; +#ifdef RT_SERIAL_USING_DMA + rt_size_t dma_cnt; +#endif + + RT_ASSERT(serial != RT_NULL); + uart = rt_container_of(serial, struct stm32_uart, serial); + + /* UART in mode Receiver -------------------------------------------------*/ + if ((__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_RXNE) != RESET) && + (__HAL_UART_GET_IT_SOURCE(&(uart->handle), UART_IT_RXNE) != RESET)) + { + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND); + } + if ((__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_TXE) != RESET) && + (__HAL_UART_GET_IT_SOURCE(&(uart->handle), UART_IT_TXE) != RESET)) + { + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_TX_DONE); + } +#ifdef RT_SERIAL_USING_DMA + else if ((uart->uart_dma_flag) && (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_IDLE) != RESET) + && (__HAL_UART_GET_IT_SOURCE(&(uart->handle), UART_IT_IDLE) != RESET)) + { + __HAL_UART_CLEAR_IDLEFLAG(&uart->handle); + dma_cnt = RT_SERIAL_DMA_BUFSZ - __HAL_DMA_GET_COUNTER(&(uart->dma_rx.handle)); + + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (dma_cnt << 8)); + } + else if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_TC) && + (__HAL_UART_GET_IT_SOURCE(&(uart->handle), UART_IT_TC) != RESET)) + { + if ((serial->parent.open_flag & RT_DEVICE_FLAG_DMA_TX) != 0) + { + HAL_UART_IRQHandler(&(uart->handle)); + } + } +#endif + else + { + if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_ORE) != RESET) + { + __HAL_UART_CLEAR_OREFLAG(&uart->handle); + } + if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_NE) != RESET) + { + __HAL_UART_CLEAR_NEFLAG(&uart->handle); + } + if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_FE) != RESET) + { + __HAL_UART_CLEAR_FEFLAG(&uart->handle); + } + if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_PE) != RESET) + { + __HAL_UART_CLEAR_PEFLAG(&uart->handle); + } +#if !defined(SOC_SERIES_STM32L4) && !defined(SOC_SERIES_STM32WL) && !defined(SOC_SERIES_STM32F7) && !defined(SOC_SERIES_STM32F0) \ + && !defined(SOC_SERIES_STM32L0) && !defined(SOC_SERIES_STM32G0) && !defined(SOC_SERIES_STM32H7) \ + && !defined(SOC_SERIES_STM32G4) && !defined(SOC_SERIES_STM32MP1) && !defined(SOC_SERIES_STM32WB) +#ifdef SOC_SERIES_STM32F3 + if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_LBDF) != RESET) + { + UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_LBDF); + } +#else + if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_LBD) != RESET) + { + UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_LBD); + } +#endif +#endif + if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_CTS) != RESET) + { + UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_CTS); + } +// if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_TXE) != RESET) +// { +// UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_TXE); +// } +// if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_TC) != RESET) +// { +// UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_TC); +// } +// if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_RXNE) != RESET) +// { +// UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_RXNE); +// } + } +} + +#ifdef RT_SERIAL_USING_DMA +static void dma_isr(struct rt_serial_device *serial) +{ + struct stm32_uart *uart; + rt_size_t dma_cnt; + + RT_ASSERT(serial != RT_NULL); + uart = rt_container_of(serial, struct stm32_uart, serial); + + if ((__HAL_DMA_GET_IT_SOURCE(&(uart->dma_rx.handle), DMA_IT_TC) != RESET) || + (__HAL_DMA_GET_IT_SOURCE(&(uart->dma_rx.handle), DMA_IT_HT) != RESET)) + { + dma_cnt = RT_SERIAL_DMA_BUFSZ - __HAL_DMA_GET_COUNTER(&(uart->dma_rx.handle)); + + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (dma_cnt << 8)); + } +} +#endif + +#if defined(BSP_USING_UART1) +void USART1_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + uart_isr(&(uart_obj[UART1_INDEX].serial)); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#if defined(RT_SERIAL_USING_DMA) && defined(BSP_UART1_RX_USING_DMA) +void UART1_DMA_RX_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + HAL_DMA_IRQHandler(&uart_obj[UART1_INDEX].dma_rx.handle); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#endif /* defined(RT_SERIAL_USING_DMA) && defined(BSP_UART1_RX_USING_DMA) */ +#if defined(RT_SERIAL_USING_DMA) && defined(BSP_UART1_TX_USING_DMA) +void UART1_DMA_TX_IRQHandler(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + HAL_DMA_IRQHandler(&uart_obj[UART1_INDEX].dma_tx.handle); + + /* leave interrupt */ + rt_interrupt_leave(); +} +#endif /* defined(RT_SERIAL_USING_DMA) && defined(BSP_UART1_TX_USING_DMA) */ +#endif /* BSP_USING_UART1 */ + +#ifdef RT_SERIAL_USING_DMA +static void stm32_uart_get_dma_config(void) +{ +#ifdef BSP_USING_UART1 + uart_obj[UART1_INDEX].uart_dma_flag = 0; +#ifdef BSP_UART1_RX_USING_DMA + uart_obj[UART1_INDEX].uart_dma_flag |= RT_DEVICE_FLAG_DMA_RX; + static struct dma_config uart1_dma_rx = UART1_DMA_RX_CONFIG; + uart_config[UART1_INDEX].dma_conf_rx = &uart1_dma_rx; +#endif +#ifdef BSP_UART1_TX_USING_DMA + uart_obj[UART1_INDEX].uart_dma_flag |= RT_DEVICE_FLAG_DMA_TX; + static struct dma_config uart1_dma_tx = UART1_DMA_TX_CONFIG; + uart_config[UART1_INDEX].dma_conf_tx = &uart1_dma_tx; +#endif +#endif +} + +static void stm32_dma_rx_config(struct rt_serial_device *serial) +{ + DMA_HandleTypeDef *DMA_Handle; + struct dma_config *dma_config; + struct stm32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + uart = rt_container_of(serial, struct stm32_uart, serial); + + DMA_Handle = &uart->dma_rx.handle; + dma_config = uart->uart_config->dma_conf_rx; + + LOG_D("%s dma config start", uart->uart_config->name); + + { + rt_uint32_t tmpreg = 0x00U; +#if defined(SOC_SERIES_STM32F1) || defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32G0) \ + || defined(SOC_SERIES_STM32L0)|| defined(SOC_SERIES_STM32F3) || defined(SOC_SERIES_STM32L1) + /* enable DMA clock && Delay after an RCC peripheral clock enabling*/ + SET_BIT(RCC->AHBENR, dma_config->dma_rcc); + tmpreg = READ_BIT(RCC->AHBENR, dma_config->dma_rcc); +#elif defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) \ + || defined(SOC_SERIES_STM32G4)|| defined(SOC_SERIES_STM32H7) || defined(SOC_SERIES_STM32WB) + /* enable DMA clock && Delay after an RCC peripheral clock enabling*/ + SET_BIT(RCC->AHB1ENR, dma_config->dma_rcc); + tmpreg = READ_BIT(RCC->AHB1ENR, dma_config->dma_rcc); +#elif defined(SOC_SERIES_STM32MP1) + /* enable DMA clock && Delay after an RCC peripheral clock enabling*/ + SET_BIT(RCC->MP_AHB2ENSETR, dma_config->dma_rcc); + tmpreg = READ_BIT(RCC->MP_AHB2ENSETR, dma_config->dma_rcc); +#endif + +#if (defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) || defined(SOC_SERIES_STM32G4) || defined(SOC_SERIES_STM32WB)) && defined(DMAMUX1) + /* enable DMAMUX clock for L4+ and G4 */ + __HAL_RCC_DMAMUX1_CLK_ENABLE(); +#elif defined(SOC_SERIES_STM32MP1) + __HAL_RCC_DMAMUX_CLK_ENABLE(); +#endif + + UNUSED(tmpreg); /* To avoid compiler warnings */ + } + + __HAL_LINKDMA(&(uart->handle), hdmarx, uart->dma_rx.handle); + +#if defined(SOC_SERIES_STM32F1) || defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32L0)|| defined(SOC_SERIES_STM32F3) || defined(SOC_SERIES_STM32L1) + DMA_Handle->Instance = dma_config->Instance; +#elif defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) + DMA_Handle->Instance = dma_config->Instance; + DMA_Handle->Init.Channel = dma_config->channel; +#elif defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32G4) || defined(SOC_SERIES_STM32WB)\ + || defined(SOC_SERIES_STM32H7) || defined(SOC_SERIES_STM32MP1) + DMA_Handle->Instance = dma_config->Instance; + DMA_Handle->Init.Request = dma_config->request; +#endif + DMA_Handle->Init.PeriphInc = DMA_PINC_DISABLE; + DMA_Handle->Init.MemInc = DMA_MINC_ENABLE; + DMA_Handle->Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; + DMA_Handle->Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; + + DMA_Handle->Init.Direction = DMA_PERIPH_TO_MEMORY; + DMA_Handle->Init.Mode = DMA_CIRCULAR; + + DMA_Handle->Init.Priority = DMA_PRIORITY_MEDIUM; +#if defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32H7) || defined(SOC_SERIES_STM32MP1) + DMA_Handle->Init.FIFOMode = DMA_FIFOMODE_DISABLE; +#endif + if (HAL_DMA_DeInit(DMA_Handle) != HAL_OK) + { + RT_ASSERT(0); + } + + if (HAL_DMA_Init(DMA_Handle) != HAL_OK) + { + RT_ASSERT(0); + } + + /* enable interrupt */ + /* Start DMA transfer */ + if (HAL_UART_Receive_DMA(&(uart->handle), serial->serial_dma_rx, RT_SERIAL_DMA_BUFSZ) != HAL_OK) + { + /* Transfer error in reception process */ + RT_ASSERT(0); + } + CLEAR_BIT(uart->handle.Instance->CR3, USART_CR3_EIE); + __HAL_UART_ENABLE_IT(&(uart->handle), UART_IT_IDLE); + + /* DMA irq should set in DMA TX mode, or HAL_UART_TxCpltCallback function will not be called */ + HAL_NVIC_SetPriority(dma_config->dma_irq, 0, 0); + HAL_NVIC_EnableIRQ(dma_config->dma_irq); +} + +static void stm32_dma_tx_config(struct rt_serial_device *serial) +{ + DMA_HandleTypeDef *DMA_Handle; + struct dma_config *dma_config; + struct stm32_uart *uart; + + RT_ASSERT(serial != RT_NULL); + uart = rt_container_of(serial, struct stm32_uart, serial); + + DMA_Handle = &uart->dma_tx.handle; + dma_config = uart->uart_config->dma_conf_tx; + + LOG_D("%s dma config start", uart->uart_config->name); + + { + rt_uint32_t tmpreg = 0x00U; +#if defined(SOC_SERIES_STM32F1) || defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32G0) \ + || defined(SOC_SERIES_STM32L0)|| defined(SOC_SERIES_STM32F3) || defined(SOC_SERIES_STM32L1) + /* enable DMA clock && Delay after an RCC peripheral clock enabling*/ + SET_BIT(RCC->AHBENR, dma_config->dma_rcc); + tmpreg = READ_BIT(RCC->AHBENR, dma_config->dma_rcc); +#elif defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) \ + || defined(SOC_SERIES_STM32G4)|| defined(SOC_SERIES_STM32H7) || defined(SOC_SERIES_STM32WB) + /* enable DMA clock && Delay after an RCC peripheral clock enabling*/ + SET_BIT(RCC->AHB1ENR, dma_config->dma_rcc); + tmpreg = READ_BIT(RCC->AHB1ENR, dma_config->dma_rcc); +#elif defined(SOC_SERIES_STM32MP1) + /* enable DMA clock && Delay after an RCC peripheral clock enabling*/ + SET_BIT(RCC->MP_AHB2ENSETR, dma_config->dma_rcc); + tmpreg = READ_BIT(RCC->MP_AHB2ENSETR, dma_config->dma_rcc); +#endif + +#if (defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) || defined(SOC_SERIES_STM32G4) || defined(SOC_SERIES_STM32WB)) && defined(DMAMUX1) + /* enable DMAMUX clock for L4+ and G4 */ + __HAL_RCC_DMAMUX1_CLK_ENABLE(); +#elif defined(SOC_SERIES_STM32MP1) + __HAL_RCC_DMAMUX_CLK_ENABLE(); +#endif + + UNUSED(tmpreg); /* To avoid compiler warnings */ + } + + __HAL_LINKDMA(&(uart->handle), hdmatx, uart->dma_tx.handle); + + +#if defined(SOC_SERIES_STM32F1) || defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32L0)|| defined(SOC_SERIES_STM32F3) || defined(SOC_SERIES_STM32L1) + DMA_Handle->Instance = dma_config->Instance; +#elif defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) + DMA_Handle->Instance = dma_config->Instance; + DMA_Handle->Init.Channel = dma_config->channel; +#elif defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32G4) || defined(SOC_SERIES_STM32WB)\ + || defined(SOC_SERIES_STM32H7) || defined(SOC_SERIES_STM32MP1) + DMA_Handle->Instance = dma_config->Instance; + DMA_Handle->Init.Request = dma_config->request; +#endif + DMA_Handle->Init.PeriphInc = DMA_PINC_DISABLE; + DMA_Handle->Init.MemInc = DMA_MINC_ENABLE; + DMA_Handle->Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; + DMA_Handle->Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; + + DMA_Handle->Init.Direction = DMA_MEMORY_TO_PERIPH; + DMA_Handle->Init.Mode = DMA_NORMAL; + + DMA_Handle->Init.Priority = DMA_PRIORITY_MEDIUM; +#if defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32H7) || defined(SOC_SERIES_STM32MP1) + DMA_Handle->Init.FIFOMode = DMA_FIFOMODE_DISABLE; +#endif + if (HAL_DMA_DeInit(DMA_Handle) != HAL_OK) + { + RT_ASSERT(0); + } + + if (HAL_DMA_Init(DMA_Handle) != HAL_OK) + { + RT_ASSERT(0); + } + + /* DMA irq should set in DMA TX mode, or HAL_UART_TxCpltCallback function will not be called */ + HAL_NVIC_SetPriority(dma_config->dma_irq, 0, 0); + HAL_NVIC_EnableIRQ(dma_config->dma_irq); +} + +/** + * @brief UART error callbacks + * @param huart: UART handle + * @note This example shows a simple way to report transfer error, and you can + * add your own implementation. + * @retval None + */ +void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) +{ + struct stm32_uart *uart; + + RT_ASSERT(huart != NULL); + + uart = (struct stm32_uart *)huart; + LOG_D("%s: %s %d\n", __FUNCTION__, uart->uart_config->name, huart->ErrorCode); + UNUSED(uart); +} + +/** + * @brief Rx Transfer completed callback + * @param huart: UART handle + * @note This example shows a simple way to report end of DMA Rx transfer, and + * you can add your own implementation. + * @retval None + */ +void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) +{ + struct stm32_uart *uart; + + RT_ASSERT(huart != NULL); + + uart = (struct stm32_uart *)huart; + dma_isr(&uart->serial); +} + +/** + * @brief Rx Half transfer completed callback + * @param huart: UART handle + * @note This example shows a simple way to report end of DMA Rx Half transfer, + * and you can add your own implementation. + * @retval None + */ +void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart) +{ + struct stm32_uart *uart; + + RT_ASSERT(huart != NULL); + + uart = (struct stm32_uart *)huart; + dma_isr(&uart->serial); +} + +/** + * @brief HAL_UART_TxCpltCallback + * @param huart: UART handle + * @note This callback can be called by two functions, first in UART_EndTransmit_IT when + * UART Tx complete and second in UART_DMATransmitCplt function in DMA Circular mode. + * @retval None + */ +void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) +{ + struct stm32_uart *uart; + rt_size_t dma_cnt; + + RT_ASSERT(huart != NULL); + + uart = (struct stm32_uart *)huart; + + dma_cnt = __HAL_DMA_GET_COUNTER(&(uart->dma_tx.handle)); + + if (dma_cnt == 0) + { + rt_hw_serial_isr(&uart->serial, RT_SERIAL_EVENT_TX_DMADONE); + } +} +#endif /* RT_SERIAL_USING_DMA */ + +static const struct rt_uart_ops stm32_uart_ops = +{ + .init = stm32_init, + .configure = stm32_configure, + .control = stm32_control, + .putc = stm32_putc, + .getc = stm32_getc, + .flush = stm32_flush, + .start_tx = stm32_start_tx, + .stop_tx = stm32_stop_tx, +#ifdef RT_SERIAL_USING_DMA + .is_dma_txing = stm32_is_dma_txing, + .start_dma_tx = stm32_start_dma_tx, + .stop_dma_tx = stm32_stop_dma_tx, +#endif + .enable_interrupt = stm32_enable_interrupt, + .disable_interrupt = stm32_disable_interrupt, +}; + +int rt_hw_usart_init(void) +{ + rt_size_t obj_num = sizeof(uart_obj) / sizeof(struct stm32_uart); + rt_err_t result = 0; + +#ifdef RT_SERIAL_USING_DMA + stm32_uart_get_dma_config(); +#endif + + for (int i = 0; i < obj_num; i++) + { + /* init UART object */ + uart_obj[i].uart_config = &uart_config[i]; + uart_obj[i].serial.ops = &stm32_uart_ops; + + /* register UART device */ + result = rt_hw_serial_register(&uart_obj[i].serial, uart_obj[i].uart_config->name, + RT_DEVICE_FLAG_RDWR + | RT_DEVICE_FLAG_INT_RX + | RT_DEVICE_FLAG_INT_TX +#ifdef RT_SERIAL_USING_DMA + | uart_obj[i].uart_dma_flag +#endif + , NULL); + RT_ASSERT(result == RT_EOK); + } + + return result; +} + +#endif /* RT_USING_SERIAL */ diff --git a/bsp/stm32/libraries/HAL_Drivers/drv_usartX.h b/bsp/stm32/libraries/HAL_Drivers/drv_usartX.h new file mode 100644 index 00000000000..9e698477bed --- /dev/null +++ b/bsp/stm32/libraries/HAL_Drivers/drv_usartX.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-04-10 THEWON first version for serialX + */ + +#ifndef __DRV_USARTX_H__ +#define __DRV_USARTX_H__ + +#include +#include "rtdevice.h" +#include +#include +#include "drv_dma.h" + +int rt_hw_usart_init(void); + +#if defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32F1) || defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) \ + || defined(SOC_SERIES_STM32L0) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32G4) || defined(SOC_SERIES_STM32WB)|| defined(SOC_SERIES_STM32F3) +#define DMA_INSTANCE_TYPE DMA_Channel_TypeDef +#elif defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) \ + || defined(SOC_SERIES_STM32H7) || defined(SOC_SERIES_STM32MP1) +#define DMA_INSTANCE_TYPE DMA_Stream_TypeDef +#endif /* defined(SOC_SERIES_STM32F1) || defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) */ + +#if defined(SOC_SERIES_STM32F1) || defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) || defined(SOC_SERIES_STM32F2) \ + || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32L0) || defined(SOC_SERIES_STM32G0) \ + || defined(SOC_SERIES_STM32G4) || defined(SOC_SERIES_STM32WB)|| defined(SOC_SERIES_STM32F3) +#define UART_INSTANCE_CLEAR_FUNCTION __HAL_UART_CLEAR_FLAG +#elif defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32H7) \ + || defined(SOC_SERIES_STM32MP1) +#define UART_INSTANCE_CLEAR_FUNCTION __HAL_UART_CLEAR_IT +#endif + +/* stm32 config class */ +struct stm32_uart_config +{ + const char *name; + USART_TypeDef *Instance; + IRQn_Type irq_type; + rt_uint32_t mask; +#ifdef RT_SERIAL_USING_DMA + struct dma_config *dma_conf_rx; + struct dma_config *dma_conf_tx; +#endif +}; + +/* stm32 uart driver class */ +struct stm32_uart +{ + UART_HandleTypeDef handle; + struct rt_serial_device serial; + struct stm32_uart_config *uart_config; + +#ifdef RT_SERIAL_USING_DMA + rt_bool_t dmaTxing; + struct + { + DMA_HandleTypeDef handle; + } dma_rx; + struct + { + DMA_HandleTypeDef handle; + } dma_tx; + rt_uint16_t uart_dma_flag; +#endif +}; + +#endif /* __DRV_USART_H__ */ diff --git a/bsp/stm32/stm32f429-armfly-v6/board/Kconfig b/bsp/stm32/stm32f429-armfly-v6/board/Kconfig index 0aa90398d88..1a6bb753703 100644 --- a/bsp/stm32/stm32f429-armfly-v6/board/Kconfig +++ b/bsp/stm32/stm32f429-armfly-v6/board/Kconfig @@ -93,6 +93,11 @@ menu "On-chip Peripheral Drivers" bool "Enable UART1" default y + config BSP_UART1_TX_USING_DMA + bool "Enable UART1 TX DMA" + depends on BSP_USING_UART1 && RT_SERIAL_USING_DMA + default n + config BSP_UART1_RX_USING_DMA bool "Enable UART1 RX DMA" depends on BSP_USING_UART1 && RT_SERIAL_USING_DMA diff --git a/bsp/stm32/stm32f429-atk-apollo/board/Kconfig b/bsp/stm32/stm32f429-atk-apollo/board/Kconfig index 0e2fa4d8330..36be2e532c7 100644 --- a/bsp/stm32/stm32f429-atk-apollo/board/Kconfig +++ b/bsp/stm32/stm32f429-atk-apollo/board/Kconfig @@ -99,6 +99,11 @@ menu "On-chip Peripheral Drivers" bool "Enable UART1" default y + config BSP_UART1_TX_USING_DMA + bool "Enable UART1 TX DMA" + depends on BSP_USING_UART1 && RT_SERIAL_USING_DMA + default n + config BSP_UART1_RX_USING_DMA bool "Enable UART1 RX DMA" depends on BSP_USING_UART1 && RT_SERIAL_USING_DMA @@ -108,6 +113,11 @@ menu "On-chip Peripheral Drivers" bool "Enable UART2" default n + config BSP_UART2_TX_USING_DMA + bool "Enable UART2 TX DMA" + depends on BSP_USING_UART2 && RT_SERIAL_USING_DMA + default n + config BSP_UART2_RX_USING_DMA bool "Enable UART2 RX DMA" depends on BSP_USING_UART2 && RT_SERIAL_USING_DMA @@ -116,7 +126,12 @@ menu "On-chip Peripheral Drivers" config BSP_USING_UART3 bool "Enable UART3" default n + + config BSP_UART3_TX_USING_DMA + bool "Enable UART3 TX DMA" + depends on BSP_USING_UART3 && RT_SERIAL_USING_DMA depends on !BSP_USING_ETH + default n config BSP_UART3_RX_USING_DMA bool "Enable UART3 RX DMA" diff --git a/bsp/stm32/stm32f429-fire-challenger/board/Kconfig b/bsp/stm32/stm32f429-fire-challenger/board/Kconfig index 36af05fe924..8c5873c395f 100644 --- a/bsp/stm32/stm32f429-fire-challenger/board/Kconfig +++ b/bsp/stm32/stm32f429-fire-challenger/board/Kconfig @@ -97,6 +97,11 @@ menu "On-chip Peripheral Drivers" bool "Enable UART1" default y + config BSP_UART1_TX_USING_DMA + bool "Enable UART1 TX DMA" + depends on BSP_USING_UART1 && RT_SERIAL_USING_DMA + default n + config BSP_UART1_RX_USING_DMA bool "Enable UART1 RX DMA" depends on BSP_USING_UART1 && RT_SERIAL_USING_DMA @@ -106,6 +111,11 @@ menu "On-chip Peripheral Drivers" bool "Enable UART2" default n + config BSP_UART2_TX_USING_DMA + bool "Enable UART2 TX DMA" + depends on BSP_USING_UART2 && RT_SERIAL_USING_DMA + default n + config BSP_UART2_RX_USING_DMA bool "Enable UART2 RX DMA" depends on BSP_USING_UART2 && RT_SERIAL_USING_DMA diff --git a/bsp/stm32/stm32f429-st-disco/board/Kconfig b/bsp/stm32/stm32f429-st-disco/board/Kconfig index fddfaa96161..6934a0b4e58 100644 --- a/bsp/stm32/stm32f429-st-disco/board/Kconfig +++ b/bsp/stm32/stm32f429-st-disco/board/Kconfig @@ -32,6 +32,11 @@ menu "On-chip Peripheral Drivers" bool "Enable UART1" default y + config BSP_UART1_TX_USING_DMA + bool "Enable UART1 TX DMA" + depends on BSP_USING_UART1 && RT_SERIAL_USING_DMA + default n + config BSP_UART1_RX_USING_DMA bool "Enable UART1 RX DMA" depends on BSP_USING_UART1 && RT_SERIAL_USING_DMA @@ -41,6 +46,11 @@ menu "On-chip Peripheral Drivers" bool "Enable UART2" default n + config BSP_UART2_TX_USING_DMA + bool "Enable UART2 TX DMA" + depends on BSP_USING_UART2 && RT_SERIAL_USING_DMA + default n + config BSP_UART2_RX_USING_DMA bool "Enable UART2 RX DMA" depends on BSP_USING_UART2 && RT_SERIAL_USING_DMA diff --git a/components/drivers/Kconfig b/components/drivers/Kconfig index 1b6fc17ebf4..b0a5d70ef1e 100755 --- a/components/drivers/Kconfig +++ b/components/drivers/Kconfig @@ -38,6 +38,10 @@ menuconfig RT_USING_SERIAL bool "RT_USING_SERIAL_V1" config RT_USING_SERIAL_V2 bool "RT_USING_SERIAL_V2" + config RT_USING_SERIAL_X + bool "RT_USING_SERIAL_X" + help + A serial driver that supporting True NonBlock. endchoice config RT_SERIAL_USING_DMA bool "Enable serial DMA mode" @@ -45,8 +49,36 @@ menuconfig RT_USING_SERIAL config RT_SERIAL_RB_BUFSZ int "Set RX buffer size" - depends on !RT_USING_SERIAL_V2 + depends on RT_USING_SERIAL_V1 default 64 + + if RT_USING_SERIAL_X + config RT_SERIAL_FIFO_BUFSZ + int "Set SerialX FIFO buffer size" + default 128 + help + suggestion: RT_SERIAL_FIFO_BUFSZ == 2^N + and RT_SERIAL_FIFO_BUFSZ >= 2*RT_SERIAL_DMA_BUFSZ + + config RT_SERIAL_DMA_BUFSZ + int "Set SerialX DMA buffer size" + depends on RT_SERIAL_USING_DMA + default 64 + help + suggestion: RT_SERIAL_DMA_BUFSZ == 2^N + and RT_SERIAL_FIFO_BUFSZ >= 2*RT_SERIAL_DMA_BUFSZ + + config RT_SERIAL_HARD_FIFO + bool "Using Hard FIFO" + default n + help + Useful only if the chip supported + eg: NUC970 N9H30 serial chips + The UART1/2/4/6/8/10 is built-in with a 64-byte transmitter FIFO (TX_FIFO) + and a 64-byte receiver FIFO (RX_FIFO), + and the UART0/3/5/7/9 are equipped 16-byte transmitter FIFO (TX_FIFO) + and 16-byte receiver FIFO (RX_FIFO). + endif endif config RT_USING_TTY diff --git a/components/drivers/include/drivers/serialX.h b/components/drivers/include/drivers/serialX.h new file mode 100644 index 00000000000..75f34794f31 --- /dev/null +++ b/components/drivers/include/drivers/serialX.h @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-04-10 THEWON serialX first version + * 2022-06-08 THEWON add No TX Empty interrupt support + * 2023-02-15 THEWON add init ops + */ + +#ifndef __SERIALX_H__ +#define __SERIALX_H__ + +#include +#include + +#define BAUD_RATE_2400 2400 +#define BAUD_RATE_4800 4800 +#define BAUD_RATE_9600 9600 +#define BAUD_RATE_19200 19200 +#define BAUD_RATE_38400 38400 +#define BAUD_RATE_57600 57600 +#define BAUD_RATE_115200 115200 +#define BAUD_RATE_230400 230400 +#define BAUD_RATE_460800 460800 +#define BAUD_RATE_921600 921600 +#define BAUD_RATE_2000000 2000000 +#define BAUD_RATE_3000000 3000000 + +#define DATA_BITS_5 5 +#define DATA_BITS_6 6 +#define DATA_BITS_7 7 +#define DATA_BITS_8 8 +#define DATA_BITS_9 9 + +#define STOP_BITS_1 0 +#define STOP_BITS_2 1 +#define STOP_BITS_3 2 +#define STOP_BITS_4 3 + +#ifdef _WIN32 +#include +#else +#define PARITY_NONE 0 +#define PARITY_ODD 1 +#define PARITY_EVEN 2 +#endif + +#define BIT_ORDER_LSB 0 +#define BIT_ORDER_MSB 1 + +#define NRZ_NORMAL 0 /* Non Return to Zero : normal mode */ +#define NRZ_INVERTED 1 /* Non Return to Zero : inverted mode */ + +#ifndef RT_SERIAL_FIFO_BUFSZ +#define RT_SERIAL_FIFO_BUFSZ 128 +#endif + +#ifndef RT_SERIAL_DMA_BUFSZ +#define RT_SERIAL_DMA_BUFSZ 32 +#endif + +#if RT_SERIAL_DMA_BUFSZ < 32 +#define RT_SERIAL_DMA_BUFSZ 32 +#endif + +#if RT_SERIAL_FIFO_BUFSZ < (RT_SERIAL_DMA_BUFSZ*2) +#define RT_SERIAL_FIFO_BUFSZ RT_SERIAL_DMA_BUFSZ*2 +#endif + + +#define RT_SERIAL_EVENT_RX_IND 0x01 /* Rx indication */ +#define RT_SERIAL_EVENT_TX_DONE 0x02 /* Tx complete */ +#define RT_SERIAL_EVENT_RX_DMADONE 0x03 /* Rx DMA transfer done */ +#define RT_SERIAL_EVENT_TX_DMADONE 0x04 /* Tx DMA transfer done */ +#define RT_SERIAL_EVENT_RX_TIMEOUT 0x05 /* Rx timeout */ + +#define RT_SERIAL_DMA_RX 0x01 +#define RT_SERIAL_DMA_TX 0x02 + +#define RT_SERIAL_RX_INT 0x01 +#define RT_SERIAL_TX_INT 0x02 + +#define RT_SERIAL_ERR_OVERRUN 0x01 +#define RT_SERIAL_ERR_FRAMING 0x02 +#define RT_SERIAL_ERR_PARITY 0x03 + +#define RT_SERIAL_TX_DATAQUEUE_SIZE 2048 +#define RT_SERIAL_TX_DATAQUEUE_LWM 30 + +#define RT_SERIAL_FLOWCONTROL_CTSRTS 1 +#define RT_SERIAL_FLOWCONTROL_NONE 0 + +/* Default config for serial_configure structure */ +#define RT_SERIAL_CONFIG_DEFAULT \ +{ \ + BAUD_RATE_115200, /* 115200 bits/s */ \ + DATA_BITS_8, /* 8 databits */ \ + STOP_BITS_1, /* 1 stopbit */ \ + PARITY_NONE, /* No parity */ \ + BIT_ORDER_LSB, /* LSB first sent */ \ + NRZ_NORMAL, /* Normal mode */ \ + RT_SERIAL_FIFO_BUFSZ, /* Buffer size */ \ + RT_SERIAL_FLOWCONTROL_NONE, /* Off flowcontrol */ \ + 0 \ +} + +#define RT_SERIAL_EVENT_TXDONE (1 << 0) +#define RT_SERIAL_EVENT_RXDONE (1 << 1) + +//#define RT_SERIAL_USE_EVENT // Just for test, maybe using event in the future + +struct rt_serial_device; + +typedef int (*cb_serial_tx)(struct rt_serial_device *serial, const rt_uint8_t *data, int length); +typedef int (*cb_serial_rx)(struct rt_serial_device *serial, rt_uint8_t *data, int length); + +struct serial_configure +{ + rt_uint32_t baud_rate; + + rt_uint32_t data_bits :4; + rt_uint32_t stop_bits :2; + rt_uint32_t parity :2; + rt_uint32_t bit_order :1; + rt_uint32_t invert :1; + rt_uint32_t bufsz :16; // unused, move it to struct rt_serial_device + rt_uint32_t flowcontrol :1; + rt_uint32_t reserved :5; +}; + +/* + * Serial FIFO mode + */ +struct rt_serial_fifo +{ + rt_uint32_t buf_sz; + /* software fifo */ + rt_uint8_t *buffer; + + rt_uint16_t put_index, get_index; + + rt_bool_t is_full; +}; + +struct rt_serial_device +{ + struct rt_device parent; + + const struct rt_uart_ops *ops; + struct serial_configure config; + rt_uint32_t bufsz; + + void *serial_rx; + void *serial_tx; + +#ifdef RT_SERIAL_USING_DMA + rt_size_t dma_idx_rx; + rt_uint8_t serial_dma_rx[RT_SERIAL_DMA_BUFSZ]; + rt_uint8_t serial_dma_tx[RT_SERIAL_DMA_BUFSZ]; +#endif + + cb_serial_rx _cb_rx; + cb_serial_tx _cb_tx; +#ifndef RT_SERIAL_USE_EVENT + struct rt_completion completion_rx; + struct rt_completion completion_tx; +#else + rt_event_t rx_done; + rt_event_t tx_done; +#endif +}; +typedef struct rt_serial_device rt_serial_t; + +/** + * uart operators + */ +struct rt_uart_ops +{ + int (*init)(struct rt_serial_device *serial); + + rt_err_t (*configure)(struct rt_serial_device *serial, struct serial_configure *cfg); + rt_err_t (*control)(struct rt_serial_device *serial, int cmd, void *arg); + + int (*putc)(struct rt_serial_device *serial, char c); + int (*getc)(struct rt_serial_device *serial); + int (*flush)(struct rt_serial_device *serial); + +#if defined (RT_SERIAL_NO_TXEIT) + rt_bool_t (*is_int_txing)(struct rt_serial_device *serial); + void (*start_tx)(struct rt_serial_device *serial, rt_uint8_t ch); +#else + void (*start_tx)(struct rt_serial_device *serial); +#endif + void (*stop_tx)(struct rt_serial_device *serial); + +#ifdef RT_SERIAL_USING_DMA + rt_bool_t (*is_dma_txing)(struct rt_serial_device *serial); + void (*start_dma_tx)(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size); + void (*stop_dma_tx)(struct rt_serial_device *serial); +#endif + + void (*enable_interrupt)(struct rt_serial_device *serial); + void (*disable_interrupt)(struct rt_serial_device *serial); +}; + +void rt_hw_serial_isr(struct rt_serial_device *serial, int event); + +rt_err_t rt_hw_serial_register(struct rt_serial_device *serial, + const char *name, + rt_uint32_t flag, + void *data); + +#endif diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h index 92b2211243f..5b1530dbc61 100644 --- a/components/drivers/include/rtdevice.h +++ b/components/drivers/include/rtdevice.h @@ -7,6 +7,7 @@ * Date Author Notes * 2012-01-08 bernard first version. * 2014-07-12 bernard Add workqueue implementation. + * 2022-04-10 THEWON add serialX */ #ifndef __RT_DEVICE_H__ @@ -57,7 +58,9 @@ extern "C" { #endif /* RT_USING_USB_HOST */ #ifdef RT_USING_SERIAL -#ifdef RT_USING_SERIAL_V2 +#ifdef RT_USING_SERIAL_X +#include "drivers/serialX.h" +#elif defined RT_USING_SERIAL_V2 #include "drivers/serial_v2.h" #else #include "drivers/serial.h" diff --git a/components/drivers/serial/SConscript b/components/drivers/serial/SConscript index 81d791803a0..6eaa27a468a 100644 --- a/components/drivers/serial/SConscript +++ b/components/drivers/serial/SConscript @@ -7,6 +7,9 @@ if GetDepend(['RT_USING_SERIAL']): if GetDepend(['RT_USING_SERIAL_V2']): src = Glob('serial_v2.c') group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_SERIAL_V2'], CPPPATH = CPPPATH) + elif GetDepend(['RT_USING_SERIAL_X']): + src = Glob('serialX.c') + group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_SERIAL_X'], CPPPATH = CPPPATH) else: src = Glob('serial.c') group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_SERIAL'], CPPPATH = CPPPATH) diff --git a/components/drivers/serial/serialX-doc/serialX User Manual.md b/components/drivers/serial/serialX-doc/serialX User Manual.md new file mode 100644 index 00000000000..dad536d9192 --- /dev/null +++ b/components/drivers/serial/serialX-doc/serialX User Manual.md @@ -0,0 +1,124 @@ +## serialX User Manual + +serialX 驱动框架在尽量延续 v1 版使用方法的前提下,增加阻塞和非阻塞操作,同时对 DMA 收发缓冲机制进行了调整。 + +RT-Thread 论坛上有几篇介绍和讲解 serialX 的文章,使用 serialX 前请先了解它。 + +如果用分层理论解释 serialX ,那么它可以划分成应用层、中间驱动框架层、底层硬件层。这三层各有分工,又相互影响。 + +硬件是千变万化的,驱动的一个职责就是把千变万化的硬件进行包装,把共性的行为固化出来,交给应用层使用。 + + +### 串口驱动框架和硬件底层接口功能定义详解 + +``` +struct rt_uart_ops +{ + // 配置外设寄存器设置默认波特率等;外设时钟配置、引脚功能复用(如果还没配置过);启用外设等等 + int (*init)(struct rt_serial_device *serial); + // 仅仅用于配置外设波特率、数据位数、停止位等等 + rt_err_t (*configure)(struct rt_serial_device *serial, struct serial_configure *cfg); + // 用于使能禁用中断,初始配置 DMA + rt_err_t (*control)(struct rt_serial_device *serial, int cmd, void *arg); + // 串口外设“写数据寄存器”*为空*,把数据放入“写数据寄存器”。*不为空*,死等 + int (*putc)(struct rt_serial_device *serial, char c); + // 串口外设“读数据寄存器”*不为空*,读出“读数据寄存器”的值。*为空*,返回 -1 + int (*getc)(struct rt_serial_device *serial); + + // 启动发送,多数是使能串口外设“发送寄存器”*空*中断 + void (*start_tx)(struct rt_serial_device *serial); + // 结束发送,多数是禁止串口外设“发送寄存器”*空*中断 + void (*stop_tx)(struct rt_serial_device *serial); + +#ifdef RT_SERIAL_USING_DMA + // 判断 DMA 是否在发送过程中,必须有效检测 DMA 是否在发送数据中,有些芯片有寄存器位标志,可以用标志位判断,如果没有,使用变量标志。 + rt_bool_t (*is_dma_txing)(struct rt_serial_device *serial); + // 启动 DMA 发送,数据缓存首地址和数据长度由驱动框架提供。(最后置位 DMA tx 标志) + void (*start_dma_tx)(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size); + // 停止 DMA 发送,停用 DMA 发送,(最后复位 DMA tx 标志) + void (*stop_dma_tx)(struct rt_serial_device *serial); +#endif + // 使能串口外设中断,添加这个的初衷是减少全局中断开关操作。目前这部分代码已被注释并使用 rt_hw_interrupt_enable + void (*enable_interrupt)(struct rt_serial_device *serial); + // 禁用串口外设中断,添加这个的初衷是减少全局中断开关操作。目前这部分代码已被注释并使用 rt_hw_interrupt_disable + void (*disable_interrupt)(struct rt_serial_device *serial); +}; +``` + +其中,`control` 是一个多功能扩展接口,目前支持的 `cmd` 包括: +- `RT_DEVICE_CTRL_OPEN`: 先清理一些可能出现的中断,最后配置并使能外设中断 +- `RT_DEVICE_CTRL_CLOSE`: 禁止外设中断,清理中断,卸载外设 +- `RT_DEVICE_CTRL_CLR_INT`: 定向禁止中断,包括接收中断,接收通道 DMA 中断,发送通道 DMA 中断 +- `RT_DEVICE_CTRL_SET_INT`: 定向使能中断,包括接收中断,接收通道 DMA 中断,发送通道 DMA 中断 +- `RT_DEVICE_CTRL_CONFIG`: 这部分主要是初始化配置接收通道 DMA 和发送通道 DMA。 + +`enable_interrupt` `disable_interrupt` 两个接口是一组很细腻的操作,但是有些外设如果用中断方式,有些用 DMA 方式。这时候需要区别当前外设是否使用了 DMA ,是否需要操作 DMA 中断。(某外设 “是否支持 DMA 收发” 和外设 “使用了 DMA 收发” 是两个概念) + +PS: `control` 好像是无所不能的,有了它,为什么还要添加 `init` `start_tx` `stop_tx` `enable_interrupt` `disable_interrupt`? + +#### `rt_hw_serial_isr` 中断回调函数怎么调用? + +在 serialX 里只关心五种中断, +1. “接收寄存器不空” `rt_hw_serial_isr(serial, RT_SERIAL_EVENT_TX_DONE);` +2. “发送寄存器空” `rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);` +3. “接收通道空闲” `rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (dma_cnt << 8));` +4. “接收通道 DMA 半/全中断” `rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (dma_cnt << 8));` +5. “发送通道 DMA 发送完成中断” `rt_hw_serial_isr(&uart->serial, RT_SERIAL_EVENT_TX_DMADONE);` + +其中,dma_cnt 是 DMA 接收缓存中已接收数据字节数。 + +前两个用于中断收发,后三个用于 DMA 收发。 + +> 注:其它异常错误中断,或者清理掉,或者转入其它流程,这些不在 serialX 驱动考虑范围内。 + +### 重新定义 `rt_serial_device` 定义: + +``` +struct rt_serial_device +{ + struct rt_device parent; + + const struct rt_uart_ops *ops; + struct serial_configure config; + rt_uint32_t bufsz; // 驱动层收发缓存容量大小 + + void *serial_rx; // 串口接收缓存 + void *serial_tx; // 串口发送缓存 + +#ifdef RT_SERIAL_USING_DMA // 串口收发缓存和 DMA 使用的二级缓存分开 + rt_size_t dma_idx_rx; + rt_uint8_t serial_dma_rx[RT_SERIAL_DMA_BUFSZ]; // DMA 接收缓存 + rt_uint8_t serial_dma_tx[RT_SERIAL_DMA_BUFSZ]; // DMA 发送缓存 +#endif + + cb_serial_tx _cb_tx; // 写过程回调函数指针 + cb_serial_rx _cb_rx; // 读过程回调函数指针 + + struct rt_completion completion_tx; // 发送完成 + struct rt_completion completion_rx; // 接收到新数据 +}; +typedef struct rt_serial_device rt_serial_t; +``` + +在最近的一次修改中,把 `struct serial_configure` 中的 `bufsz` 成员挪到了 `struct rt_serial_device` 。就是为了 open 了设备之后还能修改波特率,而且,修改缓存大小的功能可以挪到 `control` 里,达到 open 设备之后还能修改缓存大小的目地。 + +### 之前写过的文章汇总 + +#### 理论类 + +- [rt-thread 驱动篇 之 串口驱动框架剖析及性能提升](https://club.rt-thread.org/ask/article/0ee3da5b6a9c347d.html) 这里是 serialX 的理论基础,之后的所有工作都是这些想法的实现。 +- [rt-thread 驱动篇 之 serialX 全网公测](https://club.rt-thread.org/ask/article/bfd92159ba11aef6.html) 这是第一次在 stm32 上实现并进行的测试,内含测试代码。 `struct rt_uart_ops` 结构体接口定义有很详细的注释,在新的芯片上写底层驱动时,这些注释说明很重要,一定严格按照这些接口定义的功能进行实现。 + +#### 实践类 + +- [rt-thread 驱动篇(五)serialX 小试牛刀](http://www.elecfans.com/d/1849301.html) 这是在控制台串口上使用 serialX 的实践。 +以及,[rt-thread 驱动篇(六)serialX弊端及解决方法](https://www.elecfans.com/d/1850548.html) 会告诉你在控制台上使用**中断/DMA**收发模式时,可能遇到的尴尬问题以及怎么去避免它们。 +- [测试 serialX 的 posix 支持](https://club.rt-thread.org/ask/article/e7b067264f6badfe.html) 这里是 posix 接口测试。内含测试代码 +- [基于 serialX 串口驱动移植 libmodbus](https://club.rt-thread.org/ask/article/91437d9031d4ac5a.html) 这篇是使用 serialX 驱动跑的 libmodbus 。开启了 posix 之后,serialX 驱动和 libmodbus 配合的还是蛮好的。 + +### 仓库 + +最后,serialX 的[源码仓库](https://gitee.com/thewon/serialX) ,里面有一份儿移植说明文档,以及两个测试程序。我们可以从测试程序里看到所有的用法。 + +目前已经实现了几种芯片底层驱动,想移植到其它芯片上,可以参考。 + diff --git a/components/drivers/serial/serialX.c b/components/drivers/serial/serialX.c new file mode 100644 index 00000000000..8c602255e5c --- /dev/null +++ b/components/drivers/serial/serialX.c @@ -0,0 +1,1637 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2006-03-13 bernard first version + * 2012-05-15 lgnq modified according bernard's implementation. + * 2012-05-28 bernard code cleanup + * 2012-11-23 bernard fix compiler warning. + * 2013-02-20 bernard use RT_SERIAL_RB_BUFSZ to define + * the size of ring buffer. + * 2014-07-10 bernard rewrite serial framework + * 2014-12-31 bernard use open_flag for poll_tx stream mode. + * 2015-05-19 Quintin fix DMA tx mod tx_dma->activated flag !=RT_FALSE BUG + * in open function. + * 2015-11-10 bernard fix the poll rx issue when there is no data. + * 2016-05-10 armink add fifo mode to DMA rx when serial->config.bufsz != 0. + * 2017-01-19 aubr.cool prevent change serial rx bufsz when serial is opened. + * 2017-11-07 JasonJia fix data bits error issue when using tcsetattr. + * 2017-11-15 JasonJia fix poll rx issue when data is full. + * add TCFLSH and FIONREAD support. + * 2018-12-08 Ernest Chen add DMA choice + * 2020-09-14 WillianChan add a line feed to the carriage return character + * when using interrupt tx + * 2020-12-14 Meco Man implement function of setting window's size(TIOCSWINSZ) + * 2021-08-22 Meco Man implement function of getting window's size(TIOCGWINSZ) + * 2022-04-10 THEWON serialX first version + * 2022-06-08 THEWON add No TX Empty interrupt support + * 2023-02-15 THEWON add init ops + */ + +#include +#include +#include + +#ifdef RT_USING_SERIAL + +#define DBG_TAG "UART" +#define DBG_LVL DBG_INFO +#include + +#ifdef RT_USING_POSIX_STDIO +#if RTTHREAD_VERSION <= RT_VERSION_CHECK(4, 0, 3) +#include +#include +#else +#include +#include +#include +#include +#include +#endif + +#ifdef RT_USING_POSIX_TERMIOS +#if RTTHREAD_VERSION <= RT_VERSION_CHECK(4, 0, 3) +#include +#else +#include +#endif +#endif + +/* it's possible the 'getc/putc' is defined by stdio.h in gcc/newlib. */ +#ifdef getc +#undef getc +#endif + +#ifdef putc +#undef putc +#endif + +static rt_err_t serial_fops_rx_ind(rt_device_t dev, rt_size_t size) +{ + rt_wqueue_wakeup(&(dev->wait_queue), (void*)POLLIN); + + return RT_EOK; +} + +/* fops for serial */ +static int serial_fops_open(struct dfs_fd *fd) +{ + rt_err_t ret = 0; + rt_uint16_t flags = 0; + rt_device_t device; + + device = (rt_device_t)fd->data; + RT_ASSERT(device != RT_NULL); + + switch (fd->flags & O_ACCMODE) + { + case O_RDONLY: + LOG_D("fops open: O_RDONLY!"); + flags = RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_RDONLY; + break; + case O_WRONLY: + LOG_D("fops open: O_WRONLY!"); + flags = RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_WRONLY; + break; + case O_RDWR: + LOG_D("fops open: O_RDWR!"); + flags = RT_DEVICE_FLAG_RDWR + | RT_DEVICE_FLAG_INT_RX + | RT_DEVICE_FLAG_INT_TX; + break; + default: + LOG_E("fops open: unknown mode - %d!", fd->flags & O_ACCMODE); + break; + } + + if ((fd->flags & O_NONBLOCK) != 0) { + flags |= RT_DEVICE_OFLAG_NONBLOCKING; + } + + ret = rt_device_open(device, flags); + if (ret == RT_EOK) { + if ((fd->flags & O_ACCMODE) != O_WRONLY) { + rt_device_set_rx_indicate(device, serial_fops_rx_ind); + } + return 0; + } + + return ret; +} + +static int serial_fops_close(struct dfs_fd *fd) +{ + rt_device_t device; + + device = (rt_device_t)fd->data; + + rt_device_close(device); + + return 0; +} + +static int serial_fops_ioctl(struct dfs_fd *fd, int cmd, void *args) +{ + rt_device_t device; + + device = (rt_device_t)fd->data; + switch (cmd) + { + case FIONREAD: + break; + case FIONWRITE: + break; + } + + return rt_device_control(device, cmd, args); +} + +static int serial_fops_read(struct dfs_fd *fd, void *buf, size_t count) +{ + int size = 0; + int flags = 0; + rt_device_t device; + + flags = fd->flags & O_ACCMODE; + if (flags == O_WRONLY) { + return -EIO; + } + + device = (rt_device_t)fd->data; + + do + { + size = rt_device_read(device, -1, buf, count); + if (size <= 0) + { + if (fd->flags & O_NONBLOCK) + { + size = -EAGAIN; + break; + } + + rt_wqueue_wait(&(device->wait_queue), 0, RT_WAITING_FOREVER); + } + }while (size <= 0); + + return size; +} + +static int serial_fops_write(struct dfs_fd *fd, const void *buf, size_t count) +{ + int flags = 0; + rt_device_t device; + + flags = fd->flags & O_ACCMODE; + if (flags == O_RDONLY) { + return -EIO; + } + + device = (rt_device_t)fd->data; + return rt_device_write(device, -1, buf, count); +} + +static int serial_fops_flush(struct dfs_fd *fd) +{ + rt_device_t device; + + device = (rt_device_t)fd->data; + return rt_device_flush(device); +} + +static int serial_fops_poll(struct dfs_fd *fd, struct rt_pollreq *req) +{ + int mask = 0; + int flags = 0; + rt_device_t device; + struct rt_serial_device *serial; + + device = (rt_device_t)fd->data; + RT_ASSERT(device != RT_NULL); + + serial = (struct rt_serial_device *)device; + + /* only support POLLIN */ + flags = fd->flags & O_ACCMODE; + if (flags == O_RDONLY || flags == O_RDWR) + { + rt_base_t level; + struct rt_serial_fifo* rx_fifo; + + rt_poll_add(&(device->wait_queue), req); + + rx_fifo = (struct rt_serial_fifo*) serial->serial_rx; + + level = rt_hw_interrupt_disable(); + if ((rx_fifo->get_index != rx_fifo->put_index) || (rx_fifo->get_index == rx_fifo->put_index && rx_fifo->is_full == RT_TRUE)) + { + mask |= POLLIN; + } + rt_hw_interrupt_enable(level); + } + + return mask; +} + +const static struct dfs_file_ops _serial_fops = +{ + serial_fops_open, + serial_fops_close, + serial_fops_ioctl, + serial_fops_read, + serial_fops_write, + serial_fops_flush, /* flush */ + RT_NULL, /* lseek */ + RT_NULL, /* getdents */ + serial_fops_poll, +}; +#endif + +/** + * Calculate fifo data length. + * + * @param fifo the data fifo of serial device + * + * @return length + */ +rt_inline rt_ssize_t _serial_fifo_calc_data_len(struct rt_serial_fifo *fifo) +{ + rt_ssize_t size; + if (fifo->put_index == fifo->get_index) { + size = (fifo->is_full == RT_FALSE) ? 0 : fifo->buf_sz; + } else if (fifo->put_index > fifo->get_index) { + size = fifo->put_index - fifo->get_index; + } else { + size = fifo->buf_sz - (fifo->get_index - fifo->put_index); + } + + return size; +} + +rt_inline void _serial_fifo_push_data(struct rt_serial_fifo *fifo, rt_uint8_t ch) +{ + fifo->buffer[fifo->put_index] = ch; + fifo->put_index += 1; + if (fifo->put_index >= fifo->buf_sz) fifo->put_index = 0; +} + +rt_inline rt_uint8_t _serial_fifo_pop_data(struct rt_serial_fifo *fifo) +{ + rt_uint8_t ch; + + ch = fifo->buffer[fifo->get_index]; + fifo->get_index += 1; + if (fifo->get_index >= fifo->buf_sz) fifo->get_index = 0; + + return ch; +} + +/* + * Serial poll routines + */ +rt_inline int _serial_poll_rx(struct rt_serial_device *serial, rt_uint8_t *data, int length) +{ + int ch; + int size; + + RT_ASSERT(serial != RT_NULL); + size = length; + + while (length) + { + ch = serial->ops->getc(serial); + if (ch == -1) break; + + *data = ch; + data++; length--; + } + + return size - length; +} + +rt_inline int _serial_poll_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length) +{ + int size; + rt_uint8_t last_char = 0; + + RT_ASSERT(serial != RT_NULL); + + size = length; + while (length) + { + /* + * to be polite with serial console add a line feed + * to the carriage return character + */ + if (*data == '\n' && + (serial->parent.open_flag & RT_DEVICE_FLAG_STREAM) == RT_DEVICE_FLAG_STREAM && + last_char != '\r') + { + serial->ops->putc(serial, '\r'); + + last_char = 0; + } else if (*data == '\r') { + last_char = '\r'; + } else { + last_char = 0; + } + + serial->ops->putc(serial, *data); + + data++; length--; + } + + return size - length; +} + +/* + * Serial interrupt routines + */ +rt_inline int _serial_fifo_rx(struct rt_serial_device *serial, rt_uint8_t *data, int length) +{ + rt_size_t len, size; + struct rt_serial_fifo* rx_fifo; + rt_base_t level; + + RT_ASSERT(serial != RT_NULL); + + if (length == 0) return 0; + + rx_fifo = (struct rt_serial_fifo*) serial->serial_rx; + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); +// serial->ops->disable_interrupt(serial); + + len = _serial_fifo_calc_data_len(rx_fifo); + + if ((len == 0) && // non-blocking io mode + (serial->parent.open_flag & RT_DEVICE_OFLAG_NONBLOCKING) == RT_DEVICE_OFLAG_NONBLOCKING) { + /* enable interrupt */ + rt_hw_interrupt_enable(level); +// serial->ops->enable_interrupt(serial); + return 0; + } + if ((len == 0) && // blocking io mode + (serial->parent.open_flag & RT_DEVICE_OFLAG_NONBLOCKING) != RT_DEVICE_OFLAG_NONBLOCKING) { + rt_err_t ret; + do { + /* enable interrupt */ + rt_hw_interrupt_enable(level); +// serial->ops->enable_interrupt(serial); + +#ifndef RT_SERIAL_USE_EVENT + ret = rt_completion_wait(&(serial->completion_rx), RT_WAITING_FOREVER); +#else + ret = rt_event_recv(serial->rx_done, RT_SERIAL_EVENT_RXDONE, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, RT_NULL); +#endif + if (ret == RT_EOK || ret == -RT_ETIMEOUT) { + } else { + return 0; + } + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); +// serial->ops->disable_interrupt(serial); + + len = _serial_fifo_calc_data_len(rx_fifo); + } while(len == 0); + } + + if (len > length) { + len = length; + } + + /* read from software FIFO */ + for (size = 0; size < len; size++) { + /* otherwise there's the data: */ + *data = _serial_fifo_pop_data(rx_fifo); + data++; + } + + rx_fifo->is_full = RT_FALSE; + + /* enable interrupt */ + rt_hw_interrupt_enable(level); +// serial->ops->enable_interrupt(serial); + + return size; +} + +rt_inline int _serial_int_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length) +{ + rt_size_t len, length_t, size; + struct rt_serial_fifo *tx_fifo; + rt_base_t level; + rt_uint8_t ch = 0; + static rt_uint8_t last_char = 0; + + RT_ASSERT(serial != RT_NULL); + + if (length == 0) return 0; + + tx_fifo = (struct rt_serial_fifo*) serial->serial_tx; + + size = 0; + do { + length_t = length - size; + /* disable interrupt */ + level = rt_hw_interrupt_disable(); +// serial->ops->disable_interrupt(serial); + + len = tx_fifo->buf_sz - _serial_fifo_calc_data_len(tx_fifo); + + if ((len == 0) && // non-blocking io mode + (serial->parent.open_flag & RT_DEVICE_OFLAG_NONBLOCKING) == RT_DEVICE_OFLAG_NONBLOCKING) { + /* enable interrupt */ + rt_hw_interrupt_enable(level); +// serial->ops->enable_interrupt(serial); + break; + } + + if ((len == 0) && // blocking io mode + (serial->parent.open_flag & RT_DEVICE_OFLAG_NONBLOCKING) != RT_DEVICE_OFLAG_NONBLOCKING) { + rt_err_t ret; + /* enable interrupt */ + rt_hw_interrupt_enable(level); +// serial->ops->enable_interrupt(serial); +#ifndef RT_SERIAL_USE_EVENT + ret = rt_completion_wait(&(serial->completion_tx), RT_WAITING_FOREVER); +#else + ret = rt_event_recv(serial->tx_done, RT_SERIAL_EVENT_TXDONE, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, RT_NULL); +#endif + if (ret == RT_EOK || ret == -RT_ETIMEOUT) { + continue; + } else { + return size; + } + } + + if (len > length_t) { + len = length_t; + } + /* copy to software FIFO */ + while (len > 0) { + /* + * to be polite with serial console add a line feed + * to the carriage return character + */ + if (*data == '\n' && + (serial->parent.open_flag & RT_DEVICE_FLAG_STREAM) == RT_DEVICE_FLAG_STREAM && + last_char != '\r') { + _serial_fifo_push_data(tx_fifo, '\r'); + + last_char = '\r'; + if (len == 1) { + break; + } else { + len--; + } + } else if (*data == '\r') { + last_char = '\r'; + } else { + last_char = 0; + } + + _serial_fifo_push_data(tx_fifo, *data); + + data++; len--; size++; + } + + /* if the next position is read index, discard this 'read char' */ + if (tx_fifo->put_index == tx_fifo->get_index) { + tx_fifo->is_full = RT_TRUE; + } + + // TODO: start tx +#if defined (RT_SERIAL_NO_TXEIT) + if (serial->ops->is_int_txing != RT_NULL && serial->ops->is_int_txing(serial) == RT_FALSE) { + ch = _serial_fifo_pop_data(tx_fifo); + serial->ops->start_tx(serial, ch); + } +#else + serial->ops->start_tx(serial); +#endif + + /* enable interrupt */ + rt_hw_interrupt_enable(level); +// serial->ops->enable_interrupt(serial); + } while(size < length); + + return size; +} + +#ifdef RT_SERIAL_USING_DMA +/* + * Serial DMA routines + */ +rt_inline int _serial_dma_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length) +{ + rt_size_t len, length_t, size, i; + struct rt_serial_fifo *tx_fifo; + rt_base_t level; + rt_uint8_t ch; + static rt_uint8_t last_char = 0; + + RT_ASSERT(serial != RT_NULL); + + if (length == 0) return 0; + + tx_fifo = (struct rt_serial_fifo*) serial->serial_tx; + + size = 0; + do { + length_t = length - size; + /* disable interrupt */ + level = rt_hw_interrupt_disable(); +// serial->ops->disable_interrupt(serial); + + len = tx_fifo->buf_sz - _serial_fifo_calc_data_len(tx_fifo); + + if ((len == 0) && // non-blocking io mode + (serial->parent.open_flag & RT_DEVICE_OFLAG_NONBLOCKING) == RT_DEVICE_OFLAG_NONBLOCKING) { + /* enable interrupt */ + rt_hw_interrupt_enable(level); +// serial->ops->enable_interrupt(serial); + break; + } + + if ((len == 0) && // blocking io mode + (serial->parent.open_flag & RT_DEVICE_OFLAG_NONBLOCKING) != RT_DEVICE_OFLAG_NONBLOCKING) { + rt_err_t ret; + /* enable interrupt */ + rt_hw_interrupt_enable(level); +// serial->ops->enable_interrupt(serial); + +#ifndef RT_SERIAL_USE_EVENT + ret = rt_completion_wait(&(serial->completion_tx), RT_WAITING_FOREVER); +#else + ret = rt_event_recv(serial->tx_done, RT_SERIAL_EVENT_TXDONE, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, RT_NULL); +#endif + if (ret == RT_EOK || ret == -RT_ETIMEOUT) { + continue; + } else { + return size; + } + } + + if (len > length_t) { + len = length_t; + } + /* copy to software FIFO */ + while (len > 0) { + /* + * to be polite with serial console add a line feed + * to the carriage return character + */ + if (*data == '\n' && + (serial->parent.open_flag & RT_DEVICE_FLAG_STREAM) == RT_DEVICE_FLAG_STREAM && + last_char != '\r') { + _serial_fifo_push_data(tx_fifo, '\r'); + + last_char = '\r'; + if (len == 1) { + break; + } else { + len--; + } + } else if (*data == '\r') { + last_char = '\r'; + } else { + last_char = 0; + } + + _serial_fifo_push_data(tx_fifo, *data); + + data++; len--; size++; + } + + /* if the next position is read index, discard this 'read char' */ + if (tx_fifo->put_index == tx_fifo->get_index) { + tx_fifo->is_full = RT_TRUE; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); +// serial->ops->enable_interrupt(serial); + + // TODO: start tx + /* disable interrupt */ + level = rt_hw_interrupt_disable(); +// serial->ops->disable_interrupt(serial); + if (serial->ops->is_dma_txing(serial) == RT_FALSE) { + /* calucate fifo data size */ + len = _serial_fifo_calc_data_len(tx_fifo); + + if (len > RT_SERIAL_DMA_BUFSZ) { + len = RT_SERIAL_DMA_BUFSZ; + } + /* read from software FIFO */ + for (i = 0; i < len; i++) { + /* pop one byte data */ + ch = _serial_fifo_pop_data(tx_fifo); + + serial->serial_dma_tx[i] = ch; + } + tx_fifo->is_full = RT_FALSE; + serial->ops->start_dma_tx(serial, serial->serial_dma_tx, len); + + } + /* enable interrupt */ + rt_hw_interrupt_enable(level); +// serial->ops->enable_interrupt(serial); + } while(size < length); + + return size; +} +#endif /* RT_SERIAL_USING_DMA */ + +/* RT-Thread Device Interface */ +/* + * This function initializes serial device. + */ +static rt_err_t rt_serial_init(struct rt_device *dev) +{ + rt_err_t result = RT_EOK; + struct rt_serial_device *serial; + + RT_ASSERT(dev != RT_NULL); + serial = (struct rt_serial_device *)dev; + + /* initialize rx/tx */ + serial->serial_rx = RT_NULL; + serial->serial_tx = RT_NULL; + + /* initialize hardware */ + if (serial->ops->init) + result = serial->ops->init(serial); + + return result; +} + +static rt_err_t rt_serial_open(struct rt_device *dev, rt_uint16_t oflag) +{ + rt_uint16_t stream_flag = 0; + struct rt_serial_device *serial; + + RT_ASSERT(dev != RT_NULL); + serial = (struct rt_serial_device *)dev; + + LOG_D("open serial device: 0x%08x with open flag: 0x%04x", + dev, oflag); + /* check device flag with the open flag */ + if ((oflag & RT_DEVICE_FLAG_DMA_RX) && !(dev->flag & RT_DEVICE_FLAG_DMA_RX)) + return -RT_EIO; + if ((oflag & RT_DEVICE_FLAG_DMA_TX) && !(dev->flag & RT_DEVICE_FLAG_DMA_TX)) + return -RT_EIO; + if ((oflag & RT_DEVICE_FLAG_INT_RX) && !(dev->flag & RT_DEVICE_FLAG_INT_RX)) + return -RT_EIO; + if ((oflag & RT_DEVICE_FLAG_INT_TX) && !(dev->flag & RT_DEVICE_FLAG_INT_TX)) + return -RT_EIO; + + /* keep steam flag */ + if ((oflag & RT_DEVICE_FLAG_STREAM) || (dev->open_flag & RT_DEVICE_FLAG_STREAM)) + stream_flag = RT_DEVICE_FLAG_STREAM; + + /* get open flags */ + dev->open_flag = oflag & 0xff; + + if (oflag & RT_DEVICE_FLAG_INT_RX) + { + /* initialize the Rx/Tx structure according to open flag */ + if (serial->serial_rx == RT_NULL) + { + struct rt_serial_fifo* rx_fifo; + + rx_fifo = (struct rt_serial_fifo*)rt_malloc(sizeof(struct rt_serial_fifo) + + serial->bufsz); + RT_ASSERT(rx_fifo != RT_NULL); + rx_fifo->buf_sz = serial->bufsz; + rx_fifo->buffer = (rt_uint8_t*) (rx_fifo + 1); + rt_memset(rx_fifo->buffer, 0, rx_fifo->buf_sz); + rx_fifo->put_index = 0; + rx_fifo->get_index = 0; + rx_fifo->is_full = RT_FALSE; + + serial->serial_rx = rx_fifo; + } + dev->open_flag |= RT_DEVICE_FLAG_INT_RX; + + serial->_cb_rx = _serial_fifo_rx; +#ifndef RT_SERIAL_USE_EVENT + rt_completion_init(&(serial->completion_rx)); +#else + serial->rx_done = rt_event_create("rx_done", RT_IPC_FLAG_PRIO); +#endif + /* configure low level device */ + serial->ops->control(serial, RT_DEVICE_CTRL_SET_INT, (void *)RT_DEVICE_FLAG_INT_RX); + } +#ifdef RT_SERIAL_USING_DMA + else if (oflag & RT_DEVICE_FLAG_DMA_RX) + { + /* initialize the Rx/Tx structure according to open flag */ + if (serial->serial_rx == RT_NULL) + { + struct rt_serial_fifo* rx_fifo; + + rx_fifo = (struct rt_serial_fifo*)rt_malloc(sizeof(struct rt_serial_fifo) + + serial->bufsz); + RT_ASSERT(rx_fifo != RT_NULL); + rx_fifo->buf_sz = serial->bufsz; + rx_fifo->buffer = (rt_uint8_t*) (rx_fifo + 1); + rt_memset(rx_fifo->buffer, 0, rx_fifo->buf_sz); + rx_fifo->put_index = 0; + rx_fifo->get_index = 0; + rx_fifo->is_full = RT_FALSE; + + serial->serial_rx = rx_fifo; + } + dev->open_flag |= RT_DEVICE_FLAG_DMA_RX; + + serial->dma_idx_rx = 0; + serial->_cb_rx = _serial_fifo_rx; + +#ifndef RT_SERIAL_USE_EVENT + rt_completion_init(&(serial->completion_rx)); +#else + serial->rx_done = rt_event_create("rx_done", RT_IPC_FLAG_PRIO); +#endif + /* configure fifo address and length to low level device */ + serial->ops->control(serial, RT_DEVICE_CTRL_CONFIG, (void *) RT_DEVICE_FLAG_DMA_RX); + } +#endif /* RT_SERIAL_USING_DMA */ + else + { + serial->serial_rx = RT_NULL; + serial->_cb_rx = _serial_poll_rx; + } + + if (oflag & RT_DEVICE_FLAG_INT_TX) + { + if (serial->serial_tx == RT_NULL) + { + struct rt_serial_fifo *tx_fifo; + + tx_fifo = (struct rt_serial_fifo*)rt_malloc(sizeof(struct rt_serial_fifo) + + serial->bufsz); + RT_ASSERT(tx_fifo != RT_NULL); + tx_fifo->buf_sz = serial->bufsz; + tx_fifo->buffer = (rt_uint8_t*) (tx_fifo + 1); + rt_memset(tx_fifo->buffer, 0, tx_fifo->buf_sz); + tx_fifo->put_index = 0; + tx_fifo->get_index = 0; + tx_fifo->is_full = RT_FALSE; +#ifndef RT_SERIAL_USE_EVENT + rt_completion_init(&(serial->completion_tx)); +#else + serial->tx_done = rt_event_create("tx_done", RT_IPC_FLAG_PRIO); +#endif + serial->serial_tx = tx_fifo; + } + dev->open_flag |= RT_DEVICE_FLAG_INT_TX; + /* configure low level device */ +// serial->ops->control(serial, RT_DEVICE_CTRL_SET_INT, (void *)RT_DEVICE_FLAG_INT_TX); + + serial->_cb_tx = _serial_int_tx; + } +#ifdef RT_SERIAL_USING_DMA + else if (oflag & RT_DEVICE_FLAG_DMA_TX) + { + if (serial->serial_tx == RT_NULL) + { + struct rt_serial_fifo *tx_fifo; + + tx_fifo = (struct rt_serial_fifo*)rt_malloc(sizeof(struct rt_serial_fifo) + + serial->bufsz); + RT_ASSERT(tx_fifo != RT_NULL); + tx_fifo->buf_sz = serial->bufsz; + tx_fifo->buffer = (rt_uint8_t*) (tx_fifo + 1); + rt_memset(tx_fifo->buffer, 0, tx_fifo->buf_sz); + tx_fifo->put_index = 0; + tx_fifo->get_index = 0; + tx_fifo->is_full = RT_FALSE; + +#ifndef RT_SERIAL_USE_EVENT + rt_completion_init(&(serial->completion_tx)); +#else + serial->tx_done = rt_event_create("tx_done", RT_IPC_FLAG_PRIO); +#endif + + serial->serial_tx = tx_fifo; + } + dev->open_flag |= RT_DEVICE_FLAG_DMA_TX; + + serial->_cb_tx = _serial_dma_tx; + /* configure low level device */ + serial->ops->control(serial, RT_DEVICE_CTRL_CONFIG, (void *)RT_DEVICE_FLAG_DMA_TX); + } +#endif /* RT_SERIAL_USING_DMA */ + else + { + serial->serial_tx = RT_NULL; + serial->_cb_tx = _serial_poll_tx; + } + + serial->ops->control(serial, RT_DEVICE_CTRL_OPEN, (void *)0); + + /* set stream flag */ + dev->open_flag |= stream_flag; + + return RT_EOK; +} + +static rt_err_t rt_serial_close(struct rt_device *dev) +{ + struct rt_serial_device *serial; + + RT_ASSERT(dev != RT_NULL); + serial = (struct rt_serial_device *)dev; + + serial->ops->control(serial, RT_DEVICE_CTRL_CLOSE, RT_NULL); + + if (dev->open_flag & RT_DEVICE_FLAG_INT_RX) + { + struct rt_serial_fifo *rx_fifo; + + /* configure low level device */ + serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void *)RT_DEVICE_FLAG_INT_RX); + + dev->open_flag &= ~RT_DEVICE_FLAG_INT_RX; + + rx_fifo = (struct rt_serial_fifo *)serial->serial_rx; + rt_free(rx_fifo); + + serial->serial_rx = RT_NULL; +#ifndef RT_SERIAL_USE_EVENT + rt_completion_init(&(serial->completion_rx)); +#else + rt_event_delete(serial->rx_done); +#endif + } +#ifdef RT_SERIAL_USING_DMA + else if (dev->open_flag & RT_DEVICE_FLAG_DMA_RX) + { + struct rt_serial_fifo *rx_fifo; + + /* configure low level device */ + serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void *) RT_DEVICE_FLAG_DMA_RX); + + dev->open_flag &= ~RT_DEVICE_FLAG_DMA_RX; + + rx_fifo = (struct rt_serial_fifo *)serial->serial_rx; + rt_free(rx_fifo); + + serial->serial_rx = RT_NULL; +#ifndef RT_SERIAL_USE_EVENT + rt_completion_init(&(serial->completion_rx)); +#else + rt_event_delete(serial->rx_done); +#endif + } +#endif /* RT_SERIAL_USING_DMA */ + + if (dev->open_flag & RT_DEVICE_FLAG_INT_TX) + { + struct rt_serial_fifo *tx_fifo; + + /* configure low level device */ + serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void *)RT_DEVICE_FLAG_INT_TX); + + dev->open_flag &= ~RT_DEVICE_FLAG_INT_TX; + + tx_fifo = (struct rt_serial_fifo *)serial->serial_tx; + rt_free(tx_fifo); + + serial->serial_tx = RT_NULL; +#ifndef RT_SERIAL_USE_EVENT + rt_completion_init(&(serial->completion_tx)); +#else + rt_event_delete(serial->tx_done); +#endif + } +#ifdef RT_SERIAL_USING_DMA + else if (dev->open_flag & RT_DEVICE_FLAG_DMA_TX) + { + struct rt_serial_fifo *tx_fifo; + + /* configure low level device */ + serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void *) RT_DEVICE_FLAG_DMA_TX); + + dev->open_flag &= ~RT_DEVICE_FLAG_DMA_TX; + + tx_fifo = (struct rt_serial_fifo *)serial->serial_tx; + rt_free(tx_fifo); + + serial->serial_tx = RT_NULL; +#ifndef RT_SERIAL_USE_EVENT + rt_completion_init(&(serial->completion_tx)); +#else + rt_event_delete(serial->tx_done); +#endif + } +#endif /* RT_SERIAL_USING_DMA */ + + dev->flag &= ~RT_DEVICE_FLAG_ACTIVATED; + + return RT_EOK; +} + +static rt_ssize_t rt_serial_read(struct rt_device *dev, + rt_off_t pos, + void *buffer, + rt_size_t size) +{ + struct rt_serial_device *serial; + + RT_ASSERT(dev != RT_NULL); + if (size == 0) return 0; + + serial = (struct rt_serial_device *)dev; + + return serial->_cb_rx(serial, (rt_uint8_t *)buffer, size); +} + +static rt_ssize_t rt_serial_write(struct rt_device *dev, + rt_off_t pos, + const void *buffer, + rt_size_t size) +{ + struct rt_serial_device *serial; + + RT_ASSERT(dev != RT_NULL); + if (size == 0) return 0; + + serial = (struct rt_serial_device *)dev; + + return serial->_cb_tx(serial, (const rt_uint8_t *)buffer, size); +} + +static rt_err_t rt_serial_flush(struct rt_device *dev) +{ + struct rt_serial_device *serial; + rt_size_t len; + struct rt_serial_fifo *tx_fifo, *rx_fifo; + rt_base_t level; + + RT_ASSERT(dev != RT_NULL); + + serial = (struct rt_serial_device *)dev; + + if((dev->open_flag & RT_DEVICE_FLAG_INT_RX) +#ifdef RT_SERIAL_USING_DMA + || (dev->open_flag & RT_DEVICE_FLAG_DMA_RX) +#endif /* RT_SERIAL_USING_DMA */ + ) { + rx_fifo = (struct rt_serial_fifo*) serial->serial_rx; + level = rt_hw_interrupt_disable(); + rx_fifo->get_index = rx_fifo->put_index = 0; + rx_fifo->is_full = RT_FALSE; + rt_hw_interrupt_enable(level); + } + + if ((dev->open_flag & RT_DEVICE_FLAG_INT_TX) +#ifdef RT_SERIAL_USING_DMA + || (dev->open_flag & RT_DEVICE_FLAG_DMA_TX) +#endif /* RT_SERIAL_USING_DMA */ + ) { + tx_fifo = (struct rt_serial_fifo*) serial->serial_tx; + + while(1) { + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + // serial->ops->disable_interrupt(serial); + + len = _serial_fifo_calc_data_len(tx_fifo); + + if (len == 0) { + /* enable interrupt */ + rt_hw_interrupt_enable(level); + // serial->ops->enable_interrupt(serial); + break; + } else { + /* enable interrupt */ + rt_hw_interrupt_enable(level); + // serial->ops->enable_interrupt(serial); +#ifndef RT_SERIAL_USE_EVENT + rt_completion_wait(&(serial->completion_tx), RT_WAITING_FOREVER); +#else + rt_event_recv(serial->tx_done, RT_SERIAL_EVENT_TXDONE, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, RT_NULL); +#endif + } + } + + if (dev->open_flag & RT_DEVICE_FLAG_INT_TX) { + serial->ops->flush(serial); + } +#ifdef RT_SERIAL_USING_DMA + else if (dev->open_flag & RT_DEVICE_FLAG_DMA_TX) { + while (serial->ops->is_dma_txing(serial) == RT_TRUE); + } +#endif /* RT_SERIAL_USING_DMA */ + } else { + serial->ops->flush(serial); + } + + return RT_EOK; +} + +#ifdef RT_USING_POSIX_TERMIOS +struct speed_baudrate_item +{ + speed_t speed; + int baudrate; +}; + +const static struct speed_baudrate_item _tbl[] = +{ + {B2400, BAUD_RATE_2400}, + {B4800, BAUD_RATE_4800}, + {B9600, BAUD_RATE_9600}, + {B19200, BAUD_RATE_19200}, + {B38400, BAUD_RATE_38400}, + {B57600, BAUD_RATE_57600}, + {B115200, BAUD_RATE_115200}, + {B230400, BAUD_RATE_230400}, + {B460800, BAUD_RATE_460800}, + {B921600, BAUD_RATE_921600}, + {B2000000, BAUD_RATE_2000000}, + {B3000000, BAUD_RATE_3000000}, +}; + +static speed_t _get_speed(int baudrate) +{ + int index; + + for (index = 0; index < sizeof(_tbl)/sizeof(_tbl[0]); index ++) + { + if (_tbl[index].baudrate == baudrate) + return _tbl[index].speed; + } + + return B0; +} + +static int _get_baudrate(speed_t speed) +{ + int index; + + for (index = 0; index < sizeof(_tbl)/sizeof(_tbl[0]); index ++) + { + if (_tbl[index].speed == speed) + return _tbl[index].baudrate; + } + + return 0; +} + +static void _tc_flush(struct rt_serial_device *serial, int queue) +{ + rt_base_t level; + int ch = -1; + struct rt_serial_fifo *rx_fifo = RT_NULL; + struct rt_device *device = RT_NULL; + + RT_ASSERT(serial != RT_NULL); + + device = &(serial->parent); + rx_fifo = (struct rt_serial_fifo *) serial->serial_rx; + + switch(queue) + { + case TCIFLUSH: + case TCIOFLUSH: + + RT_ASSERT(rx_fifo != RT_NULL); + + if((device->open_flag & RT_DEVICE_FLAG_INT_RX) || (device->open_flag & RT_DEVICE_FLAG_DMA_RX)) + { + RT_ASSERT(RT_NULL != rx_fifo); + level = rt_hw_interrupt_disable(); + rx_fifo->get_index = rx_fifo->put_index; + rx_fifo->is_full = RT_FALSE; + rt_hw_interrupt_enable(level); + } + else + { + while (1) + { + ch = serial->ops->getc(serial); + if (ch == -1) break; + } + } + + break; + + case TCOFLUSH: + break; + } + +} + +#endif + +static rt_err_t rt_serial_control(struct rt_device *dev, + int cmd, + void *args) +{ + rt_err_t ret = RT_EOK; + struct rt_serial_device *serial; + + RT_ASSERT(dev != RT_NULL); + serial = (struct rt_serial_device *)dev; + + switch (cmd) + { + case RT_DEVICE_CTRL_SUSPEND: + /* suspend device */ + dev->flag |= RT_DEVICE_FLAG_SUSPENDED; + break; + case RT_DEVICE_CTRL_RESUME: + /* resume device */ + dev->flag &= ~RT_DEVICE_FLAG_SUSPENDED; + break; + case RT_DEVICE_CTRL_BLOCKING: + if (((rt_uint32_t)args & RT_DEVICE_OFLAG_NONBLOCKING) == RT_DEVICE_OFLAG_NONBLOCKING) { + dev->open_flag |= RT_DEVICE_OFLAG_NONBLOCKING; + } else { + dev->open_flag &= ~RT_DEVICE_OFLAG_NONBLOCKING; + } + break; + case RT_DEVICE_CTRL_CONFIG: + if (args) + { + struct serial_configure *pconfig = (struct serial_configure *) args; + + /* serial device has been opened, to configure it */ + ret = serial->ops->configure(serial, pconfig); + if (ret == RT_EOK) { + /* set serial configure */ + serial->config = *pconfig; + } + } + break; +#ifdef RT_USING_POSIX_STDIO +#ifdef RT_USING_POSIX_TERMIOS + case TCGETA: + { + struct termios *tio = (struct termios*)args; + if (tio == RT_NULL) return -RT_EINVAL; + + tio->c_iflag = 0; + tio->c_oflag = 0; + tio->c_lflag = 0; + + /* update oflag for console device */ + if (rt_console_get_device() == dev) + tio->c_oflag = OPOST | ONLCR; + + /* set cflag */ + tio->c_cflag = 0; + if (serial->config.data_bits == DATA_BITS_5) + tio->c_cflag = CS5; + else if (serial->config.data_bits == DATA_BITS_6) + tio->c_cflag = CS6; + else if (serial->config.data_bits == DATA_BITS_7) + tio->c_cflag = CS7; + else if (serial->config.data_bits == DATA_BITS_8) + tio->c_cflag = CS8; + + if (serial->config.stop_bits == STOP_BITS_2) + tio->c_cflag |= CSTOPB; + + if (serial->config.parity == PARITY_EVEN) + tio->c_cflag |= PARENB; + else if (serial->config.parity == PARITY_ODD) + tio->c_cflag |= (PARODD | PARENB); + + cfsetospeed(tio, _get_speed(serial->config.baud_rate)); + } + break; + + case TCSETAW: + case TCSETAF: + case TCSETA: + { + int baudrate; + struct serial_configure config; + + struct termios *tio = (struct termios*)args; + if (tio == RT_NULL) return -RT_EINVAL; + + config = serial->config; + + baudrate = _get_baudrate(cfgetospeed(tio)); + config.baud_rate = baudrate; + + switch (tio->c_cflag & CSIZE) + { + case CS5: + config.data_bits = DATA_BITS_5; + break; + case CS6: + config.data_bits = DATA_BITS_6; + break; + case CS7: + config.data_bits = DATA_BITS_7; + break; + default: + config.data_bits = DATA_BITS_8; + break; + } + + if (tio->c_cflag & CSTOPB) config.stop_bits = STOP_BITS_2; + else config.stop_bits = STOP_BITS_1; + + if (tio->c_cflag & PARENB) + { + if (tio->c_cflag & PARODD) config.parity = PARITY_ODD; + else config.parity = PARITY_EVEN; + } + else config.parity = PARITY_NONE; + + serial->ops->configure(serial, &config); + } + break; + case TCFLSH: + { + int queue = (int)args; + + _tc_flush(serial, queue); + } + + break; + case TCXONC: + break; +#endif /*RT_USING_POSIX_TERMIOS*/ + case TIOCSWINSZ: + { + struct winsize* p_winsize; + + p_winsize = (struct winsize*)args; + rt_kprintf("\x1b[8;%d;%dt", p_winsize->ws_col, p_winsize->ws_row); + } + break; + case TIOCGWINSZ: + { + struct winsize* p_winsize; + p_winsize = (struct winsize*)args; + + if(rt_thread_self() != rt_thread_find("tshell")) + { + /* only can be used in tshell thread; otherwise, return default size */ + p_winsize->ws_col = 80; + p_winsize->ws_row = 24; + } + else + { + #define _TIO_BUFLEN 20 + char _tio_buf[_TIO_BUFLEN]; + unsigned char cnt1, cnt2, cnt3, i; + char row_s[4], col_s[4]; + char *p; + + rt_memset(_tio_buf, 0, _TIO_BUFLEN); + + /* send the command to terminal for getting the window size of the terminal */ + rt_kprintf("\033[18t"); + + /* waiting for the response from the terminal */ + i = 0; + while(i < _TIO_BUFLEN) + { + _tio_buf[i] = getchar(); + if(_tio_buf[i] != 't') + { + i ++; + } + else + { + break; + } + } + if(i == _TIO_BUFLEN) + { + /* buffer overloaded, and return default size */ + p_winsize->ws_col = 80; + p_winsize->ws_row = 24; + break; + } + + /* interpreting data eg: "\033[8;1;15t" which means row is 1 and col is 15 (unit: size of ONE character) */ + rt_memset(row_s,0,4); + rt_memset(col_s,0,4); + cnt1 = 0; + while(_tio_buf[cnt1] != ';' && cnt1 < _TIO_BUFLEN) + { + cnt1++; + } + cnt2 = ++cnt1; + while(_tio_buf[cnt2] != ';' && cnt2 < _TIO_BUFLEN) + { + cnt2++; + } + p = row_s; + while(cnt1 < cnt2) + { + *p++ = _tio_buf[cnt1++]; + } + p = col_s; + cnt2++; + cnt3 = rt_strlen(_tio_buf) - 1; + while(cnt2 < cnt3) + { + *p++ = _tio_buf[cnt2++]; + } + + /* load the window size date */ + p_winsize->ws_col = atoi(col_s); + p_winsize->ws_row = atoi(row_s); + #undef _TIO_BUFLEN + } + + p_winsize->ws_xpixel = 0;/* unused */ + p_winsize->ws_ypixel = 0;/* unused */ + } + break; + case FIONREAD: + { + rt_size_t recved = 0; + rt_base_t level; + + level = rt_hw_interrupt_disable(); + recved = _serial_fifo_calc_data_len(serial->serial_rx); + rt_hw_interrupt_enable(level); + + *(rt_size_t *)args = recved; + } + break; +#endif /*RT_USING_POSIX_STDIO*/ + default : + /* control device */ + ret = serial->ops->control(serial, cmd, args); + break; + } + + return ret; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops serial_ops = +{ + .init = rt_serial_init, + .open = rt_serial_open, + .close = rt_serial_close, + .read = rt_serial_read, + .write = rt_serial_write, + .flush = rt_serial_flush, + .control = rt_serial_control +}; +#endif + +/* + * serial register + */ +rt_err_t rt_hw_serial_register(struct rt_serial_device *serial, + const char *name, + rt_uint32_t flag, + void *data) +{ + rt_err_t ret; + struct rt_device *device; + RT_ASSERT(serial != RT_NULL); + + serial->config = RT_SERIAL_CONFIG_DEFAULT; + serial->bufsz = RT_SERIAL_FIFO_BUFSZ; + + device = &(serial->parent); + + device->type = RT_Device_Class_Char; + device->rx_indicate = RT_NULL; + device->tx_complete = RT_NULL; + +#ifdef RT_USING_DEVICE_OPS + device->ops = &serial_ops; +#else + device->init = rt_serial_init; + device->open = rt_serial_open; + device->close = rt_serial_close; + device->read = rt_serial_read; + device->write = rt_serial_write; + device->flush = rt_serial_flush; + device->control = rt_serial_control; +#endif + device->user_data = data; + + /* register a character device */ + ret = rt_device_register(device, name, flag); + +#if defined(RT_USING_POSIX_STDIO) + /* set fops */ + device->fops = &_serial_fops; +#endif + + return ret; +} + +/* ISR for serial interrupt */ +void rt_hw_serial_isr(struct rt_serial_device *serial, int event) +{ + switch (event & 0xff) + { + case RT_SERIAL_EVENT_RX_IND: + { + int ch = -1; + struct rt_serial_fifo* rx_fifo; + + /* interrupt mode receive */ + rx_fifo = (struct rt_serial_fifo*)serial->serial_rx; + +#if defined(RT_SERIAL_HARD_FIFO) + while (1) +#endif + { + ch = serial->ops->getc(serial); +#if defined(RT_SERIAL_HARD_FIFO) + if (ch == -1) break; +#endif + + /* if fifo is full, discard one byte first */ + if (rx_fifo->is_full == RT_TRUE) { + rx_fifo->get_index += 1; + if (rx_fifo->get_index >= rx_fifo->buf_sz) rx_fifo->get_index = 0; + } + /* push a new data */ + _serial_fifo_push_data(rx_fifo, ch); + + /* if put index equal to read index, fifo is full */ + if (rx_fifo->put_index == rx_fifo->get_index) + { + rx_fifo->is_full = RT_TRUE; + } + } +#ifndef RT_SERIAL_USE_EVENT + rt_completion_done(&(serial->completion_rx)); +#else + rt_event_send(serial->rx_done, RT_SERIAL_EVENT_RXDONE); +#endif + /* invoke callback */ + if (serial->parent.rx_indicate != RT_NULL) { + rt_size_t rx_length; + + /* get rx length */ + rx_length = _serial_fifo_calc_data_len(rx_fifo); + if (rx_length) { + serial->parent.rx_indicate(&serial->parent, rx_length); + } + } + } + break; + case RT_SERIAL_EVENT_TX_DONE: + { +#if defined(RT_SERIAL_HARD_FIFO) + rt_size_t fifo_sz; +#endif + rt_size_t len; + rt_uint8_t ch = 0; + struct rt_serial_fifo *tx_fifo; + + tx_fifo = (struct rt_serial_fifo*) serial->serial_tx; + + /* calucate fifo data size */ + len = _serial_fifo_calc_data_len(tx_fifo); + if (len == 0) { + // TODO: stop tx + serial->ops->stop_tx(serial); +#ifndef RT_SERIAL_USE_EVENT + rt_completion_done(&(serial->completion_tx)); +#else + rt_event_send(serial->tx_done, RT_SERIAL_EVENT_TXDONE); +#endif + /* invoke callback */ + if (serial->parent.tx_complete != RT_NULL) { + serial->parent.tx_complete(&serial->parent, (void*)len); + } + break; + } +#if defined(RT_SERIAL_HARD_FIFO) + fifo_sz = event >> 8; + if (len > fifo_sz) { + len = fifo_sz; + } + /* read from software FIFO */ + while (len > 0) { + /* pop one byte data */ + ch = _serial_fifo_pop_data(tx_fifo); + + serial->ops->putc(serial, ch); + len--; + } +#else + /* pop one byte data */ + ch = _serial_fifo_pop_data(tx_fifo); + + serial->ops->putc(serial, ch); +#endif + tx_fifo->is_full = RT_FALSE; + } + break; +#ifdef RT_SERIAL_USING_DMA + case RT_SERIAL_EVENT_RX_DMADONE: + { + int dma_idx, ch = -1; + struct rt_serial_fifo* rx_fifo; + + dma_idx = event >> 8; + + if (serial->dma_idx_rx == dma_idx) break; + + rx_fifo = (struct rt_serial_fifo*)serial->serial_rx; + + while (serial->dma_idx_rx != dma_idx) { + ch = serial->serial_dma_rx[serial->dma_idx_rx]; + + /* if fifo is full, discard one byte first */ + if (rx_fifo->is_full == RT_TRUE) { + rx_fifo->get_index += 1; + if (rx_fifo->get_index >= rx_fifo->buf_sz) rx_fifo->get_index = 0; + } + /* push a new data */ + _serial_fifo_push_data(rx_fifo, ch); + + /* if put index equal to read index, fifo is full */ + if (rx_fifo->put_index == rx_fifo->get_index) + { + rx_fifo->is_full = RT_TRUE; + } + serial->dma_idx_rx++; + if (serial->dma_idx_rx == RT_SERIAL_DMA_BUFSZ) { + serial->dma_idx_rx = 0; + } + } + serial->dma_idx_rx = dma_idx; +#ifndef RT_SERIAL_USE_EVENT + rt_completion_done(&(serial->completion_rx)); +#else + rt_event_send(serial->rx_done, RT_SERIAL_EVENT_RXDONE); +#endif + + /* invoke callback */ + if (serial->parent.rx_indicate != RT_NULL) { + rt_size_t rx_length; + + /* get rx length */ + rx_length = _serial_fifo_calc_data_len(rx_fifo); + if (rx_length) { + serial->parent.rx_indicate(&serial->parent, rx_length); + } + } + } + break; + case RT_SERIAL_EVENT_TX_DMADONE: + { + int i; + rt_size_t len; + rt_uint8_t ch = 0; + struct rt_serial_fifo *tx_fifo; + + tx_fifo = (struct rt_serial_fifo*) serial->serial_tx; + + /* calucate fifo data size */ + len = _serial_fifo_calc_data_len(tx_fifo); + if (len == 0) { + // TODO: stop tx + serial->ops->stop_dma_tx(serial); +#ifndef RT_SERIAL_USE_EVENT + rt_completion_done(&(serial->completion_tx)); +#else + rt_event_send(serial->tx_done, RT_SERIAL_EVENT_TXDONE); +#endif + /* invoke callback */ + if (serial->parent.tx_complete != RT_NULL) { + serial->parent.tx_complete(&serial->parent, (void*)len); + } + break; + } + + if (len > RT_SERIAL_DMA_BUFSZ) { + len = RT_SERIAL_DMA_BUFSZ; + } + /* read from software FIFO */ + for (i = 0; i < len; i++) { + /* pop one byte data */ + ch = _serial_fifo_pop_data(tx_fifo); + + serial->serial_dma_tx[i] = ch; + } + tx_fifo->is_full = RT_FALSE; + serial->ops->start_dma_tx(serial, serial->serial_dma_tx, len); + } + break; +#endif /* RT_SERIAL_USING_DMA */ + } +} +#endif /* RT_USING_SERIAL */ diff --git a/include/rtdef.h b/include/rtdef.h index e72f9226faa..3e239fda4f1 100644 --- a/include/rtdef.h +++ b/include/rtdef.h @@ -41,6 +41,7 @@ * 2022-01-01 Gabriel improve hooking method * 2022-01-07 Gabriel move some __on_rt_xxxxx_hook to dedicated c source files * 2022-01-12 Meco Man remove RT_THREAD_BLOCK + * 2022-04-10 THEWON add flush for device & some device flags * 2022-04-20 Meco Man change version number to v4.1.1 * 2022-04-21 THEWON add macro RT_VERSION_CHECK * 2022-06-29 Meco Man add RT_USING_LIBC and standard libc headers @@ -1165,6 +1166,10 @@ enum rt_device_class_type #define RT_DEVICE_OFLAG_WRONLY 0x002 /**< write only access */ #define RT_DEVICE_OFLAG_RDWR 0x003 /**< read and write */ #define RT_DEVICE_OFLAG_OPEN 0x008 /**< device is opened */ + +#define RT_DEVICE_OFLAG_BLOCKING 0x000 /**< blocking io mode */ +#define RT_DEVICE_OFLAG_NONBLOCKING 0x004 /**< non-blocking io mode */ + #define RT_DEVICE_OFLAG_MASK 0xf0f /**< mask of open flag */ /** @@ -1182,6 +1187,8 @@ enum rt_device_class_type #define RT_DEVICE_CTRL_CLR_INT 0x07 /**< clear interrupt */ #define RT_DEVICE_CTRL_GET_INT 0x08 /**< get interrupt status */ #define RT_DEVICE_CTRL_CONSOLE_OFLAG 0x09 /**< get console open flag */ +#define RT_DEVICE_CTRL_OPEN 0x0A /**< open device */ +#define RT_DEVICE_CTRL_BLOCKING 0x0B /**< blocking io */ #define RT_DEVICE_CTRL_MASK 0x1f /**< mask for contrl commands */ /** @@ -1220,6 +1227,7 @@ struct rt_device_ops rt_ssize_t (*read) (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size); rt_ssize_t (*write) (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size); rt_err_t (*control)(rt_device_t dev, int cmd, void *args); + rt_err_t (*flush) (rt_device_t dev); }; #endif /* RT_USING_DEVICE_OPS */ @@ -1264,6 +1272,7 @@ struct rt_device rt_ssize_t (*read) (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size); rt_ssize_t (*write) (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size); rt_err_t (*control)(rt_device_t dev, int cmd, void *args); + rt_err_t (*flush) (rt_device_t dev); #endif /* RT_USING_DEVICE_OPS */ #ifdef RT_USING_POSIX_DEVIO diff --git a/include/rtthread.h b/include/rtthread.h index 5cfa3c26b13..961993c0ef5 100644 --- a/include/rtthread.h +++ b/include/rtthread.h @@ -569,6 +569,7 @@ rt_ssize_t rt_device_write(rt_device_t dev, const void *buffer, rt_size_t size); rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg); +rt_err_t rt_device_flush(rt_device_t dev); #ifdef RT_USING_DM rt_err_t rt_device_bind_driver(rt_device_t device, rt_driver_t driver, void *node); rt_device_t rt_device_create_since_driver(rt_driver_t drv,int device_id); diff --git a/src/device.c b/src/device.c index 9665788c77c..4eafb4e3ba3 100644 --- a/src/device.c +++ b/src/device.c @@ -13,6 +13,7 @@ * 2013-07-09 Grissiom add ref_count support * 2016-04-02 Bernard fix the open_flag initialization issue. * 2021-03-19 Meco Man remove rt_device_init_all() + * 2022-04-10 THEWON add flush for device */ #include @@ -28,6 +29,7 @@ #define device_close (dev->ops->close) #define device_read (dev->ops->read) #define device_write (dev->ops->write) +#define device_flush (dev->ops->flush) #define device_control (dev->ops->control) #else #define device_init (dev->init) @@ -35,6 +37,7 @@ #define device_close (dev->close) #define device_read (dev->read) #define device_write (dev->write) +#define device_flush (dev->flush) #define device_control (dev->control) #endif /* RT_USING_DEVICE_OPS */ @@ -290,7 +293,11 @@ rt_err_t rt_device_close(rt_device_t dev) /* set open flag */ if (result == RT_EOK || result == -RT_ENOSYS) + { dev->open_flag = RT_DEVICE_OFLAG_CLOSE; + dev->rx_indicate = RT_NULL; + dev->tx_complete = RT_NULL; + } return result; } @@ -382,6 +389,33 @@ rt_ssize_t rt_device_write(rt_device_t dev, } RTM_EXPORT(rt_device_write); +/** + * @brief This function will flush a device's buffers. + * + * @param dev is the pointer of device driver structure. + * + * @return the result, RT_EOK on successfully. + */ +rt_err_t rt_device_flush(rt_device_t dev) +{ + RT_ASSERT(dev != RT_NULL); + RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device); + + if (dev->ref_count == 0) + { + return -RT_ERROR; + } + + /* call device_write interface */ + if (device_flush != RT_NULL) + { + return device_flush(dev); + } + + return -RT_ENOSYS; +} +RTM_EXPORT(rt_device_flush); + /** * @brief This function will perform a variety of control functions on devices. * @@ -475,13 +509,13 @@ rt_err_t rt_device_bind_driver(rt_device_t device, rt_driver_t driver, void *nod } device->drv = driver; -#ifdef RT_USING_DEVICE_OPS +#ifdef RT_USING_DEVICE_OPS device->ops = driver->dev_ops; -#endif +#endif device->dtb_node = node; return RT_EOK; -} +} RTM_EXPORT(rt_device_bind_driver); /**