Edit online

设计说明

31 Oct 2024
Read time: 13 minute(s)

信息配置

SDK 中 UART 的配置信息包括

  • SoC 配置: Kconfig.chip

  • 开发板配置:Kconfig.board

  • Pimux 配置:pinmux.c

  • 设备配置:Kconfig.dev

SoC 配置

SoC 的 UART 模块的基础信息配置在 bsp/artinchip/sys/soc name/Kconfig.chip 中设置

  • 驱动版本信息

  • UART 数目信息

config AIC_UART_DRV
bool
default n

config AIC_UART_DRV_V10
bool
default y if AIC_UART_DRV

config AIC_UART_DRV_VER
string
default "10" if AIC_UART_DRV_V10

config AIC_UART_DEV_NUM
int
default 8 if AIC_UART_DRV
开发板配置

在 target/soc name/board name/Kconfig.board 中完成某一开发板中 UART 的相关信息设置, 一般会配置需要的 UART 端口

config AIC_USING_UART0
bool "Using uart0"
default n
select AIC_UART_DRV

config AIC_USING_UART1
bool "Using uart1"
default n
select AIC_UART_DRV
Pinmux

在 target/soc name/board name/pinmux.c 中配置 UART 端口的 pinmux

struct aic_pinmux aic_pinmux_config[] = {
...
#ifdef AIC_USING_UART0
    /* uart0 */
    {5, PIN_PULL_DIS, 3, "PA.0"},
    {5, PIN_PULL_DIS, 3, "PA.1"},
#endif
设备配置

在 bsp/artinchip/drv/uart/Kconfig.dev 中设置设备的工作参数

  • clock

  • baudrate

  • data bites

  • stop bits

  • parity

  • function

config AIC_DEV_UART0_BAUDRATE
int "uart0 baudrate"
default 115200

config AIC_DEV_UART0_DATABITS
int "uart0 data bits"
range 0 15
default 8

config AIC_DEV_UART0_STOPBITS
int "uart0 stop bits"
range 0 3
default 1

源码说明

UART 的功能通过三层包装实现:

  • HAL 层: bsp/artinchip/hal/uart/aic_hal_uart.c

  • 驱动层: bsp/artinchip/drv/uart/aic_drv_uart.c

  • 应用层: kernel/rt-thread/components/drivers/serial/serial.c

HAL 层

HAL 层主要完成对寄存的操作,对基础功能块的封装

寄存器
代码中通过一个数据结构来完整的保存寄存器的数据,对数据结构变量的操作将直接操作到对寄存器
typedef struct
{
    union
    {
        __IM uint32_t RBR;              /* Offset: 0x000 (R/ )  Receive buffer register */
        __OM uint32_t THR;              /* Offset: 0x000 ( /W)  Transmission hold register */
        __IOM uint32_t DLL;             /* Offset: 0x000 (R/W)  Clock frequency division low section register */
    };
    union
    {
        __IOM uint32_t DLH;             /* Offset: 0x004 (R/W)  Clock frequency division high section register */
        __IOM uint32_t IER;             /* Offset: 0x004 (R/W)  Interrupt enable register */
    };
    union
    {
        __IM uint32_t IIR;              /* Offset: 0x008 (R/ )  Interrupt indicia register */
        __IOM uint32_t FCR;             /* Offset: 0x008 (W)    FIFO control register */
    };
    __IOM uint32_t LCR;                 /* Offset: 0x00C (R/W)  Transmission control register */
    __IOM uint32_t MCR;                 /* Offset: 0x010 (R/W)  Modem Control register */
    __IM uint32_t LSR;                  /* Offset: 0x014 (R/ )  Transmission state register */
    __IM uint32_t MSR;                  /* Offset: 0x018 (R/ )  Modem state register */
    uint32_t RESERVED1[24];             /**/
    __IM uint32_t USR;                  /* Offset: 0x07c (R/ )  UART state register */
    uint32_t RESERVED2[1];
    __IM uint32_t RFL;                  /* Offset: 0x084 (R/ )  UART rx fifo level register */
    __IOM uint32_t HSK;                 /* Offset: 0x088 (R/W)  UART dma hsk register */
    uint32_t RESERVED3[6];
    __IOM uint32_t HALT;                /* Offset: 0x0A4 */
} aic_usart_reg_t;
扩展寄存器
扩展寄存器是 ArtInChip 的特殊寄存器,从 0XB8 地址开始,采用和寄存一样的使用方式
typedef struct
{
    __IOM uint32_t RS485DE;             /* Offset: 0x0B8 (R/W ) RS485 DE Time register*/
    uint32_t RESERVED0;
    __IOM uint32_t RS485CTL;            /* Offset: 0x0C0 (R/W ) RS485 Control and Status register*/
    __IOM uint32_t RS485AM;            /* Offset: 0x0C4 (R/W ) RS485 Address Match register*/
    __IOM uint32_t RS485BIC;            /* Offset: 0x0C8 (R/W ) RS485 Bus Idle Check register*/
} aic_usart_exreg_t;

接口设计

1. hal_usart_initialize
函数原型 static int hal_usart_initialize(int32_t idx, usart_event_cb_t cb_event, void *handler)
功能说明 初始化 UART 端口
参数定义
index - UART 端口号
cb_event - event call back
handler - 生成的工作句柄
返回值 0,成功; < 0,失败
注意事项 -
2. hal_usart_uninitialize
函数原型 int hal_usart_uninitialize(void *handler)
功能说明 关闭 UART 端口
参数定义
handler - UART 句柄
cb_event - event call back
handler - 生产的工作句柄
返回值 0,成功; < 0,失败
注意事项 -
3. hal_usart_config
函数原型 int hal_usart_config(usart_handle_t handle, ……)
功能说明 配置 UART 的工作参数
参数定义
handle - UART 句柄
baud - 波特率
mode - 工作模式,同步还是异步
parity - 极性
stopbits - 停止位
bits - 数据位
func - 功能,RS232,RF485 等
返回值 0,成功; < 0,失败
注意事项 -
4. hal_usart_send
函数原型 int32_t al_usart_send(usart_handle_t handle, const void *data, uint32_t num)
功能说明 发送数据
参数定义
handle - UART 句柄
data - 待发送数据
num - 数据长度
返回值 0,成功; < 0,失败
注意事项 -
5. hal_usart_receive
函数原型 int32_t hal_usart_receive(usart_handle_t handle, void *data, uint32_t num)
功能说明 接收数据
参数定义
handle - UART 句柄
data - 接收数据 buffer
num - 要接收的数据长度
返回值 0,成功; < 0,失败
注意事项 -
6. hal_usart_receive_query
函数原型 int32_t hal_usart_receive_query(usart_handle_t handle, void *data, uint32_t num)
功能说明 查询要接收的数据的长度
参数定义
handle - UART 句柄
data - 接收数据 buffer
num - buffer 的大小
返回值 长度
注意事项 -
7. hal_usart_get_status
函数原型 usart_status_t hal_usart_get_status(usart_handle_t handle)
功能说明 获取 UART 的状态
参数定义 handle - UART 句柄
返回值 状态值
注意事项 -
8. hal_usart_flush
函数原型 int32_t hal_usart_flush(usart_handle_t handle, usart_flush_type_e type)
功能说明 flush 发送或接收的数据
参数定义
handle - UART 句柄
type - 发送或者接收
返回值 0,成功; < 0,失败
注意事项 -
9. hal_usart_config_flowctrl
函数原型 int32_t hal_usart_config_flowctrl(usart_handle_t handle, usart_flowctrl_type_e flowctrl_type)
功能说明 配置 UART 的流控
参数定义
handle - UART 句柄
flowctrl_type - 流控类型
返回值 0,成功; < 0,失败
注意事项 -
10. hal_uart_set_fifo
函数原型 int32_t hal_uart_set_fifo(usart_handle_t handle)
功能说明 设置 UART-DMA 模式下 rx_fifo 和 tx_fifo 的触发阈值
参数定义
handle - UART 句柄
返回值 0,成功
注意事项 -
11. hal_usart_set_hsk
函数原型 int32_t hal_usart_set_hsk(usart_handle_t handle)
功能说明 配置 UART-DMA 握手模式
参数定义
handle - UART 句柄
返回值 0,成功
注意事项 -
12. hal_usart_get_rx_fifo_num
函数原型 int32_t hal_usart_get_rx_fifo_num(usart_handle_t handle)
功能说明 读取 rx_fifo 里面的数据量
参数定义
handle - UART 句柄
返回值 fifo_num,数据的多少
注意事项 -
13. hal_uart_rx_dma_config
函数原型 int32_t hal_uart_rx_dma_config(usart_handle_t handle, uint8_t *buf, uint32_t size)
功能说明 配置 UART-DMA 接收的参数
参数定义
handle - UART 句柄
buf - DMA 搬运的目标地址
size - 搬运数据的大小
返回值 0,成功
注意事项 -
14. hal_uart_send_by_dma
函数原型 int32_t hal_uart_send_by_dma(usart_handle_t handle, uint8_t *buf, uint32_t size)
功能说明 配置 UART-DMA 发送的参数
参数定义
handle - UART 句柄
buf - DMA 搬运的源端地址
size - 搬运数据的大小
返回值 0,成功
注意事项 -
15. hal_usart_halt_tx_enable
函数原型 int32_t hal_usart_halt_tx_enable(usart_handle_t handle, uint8_t halt_tx_enable)
功能说明 控制设备发送数据的开关
参数定义
handle - UART 句柄
halt_tx_enable - 禁止发送使能标志
返回值 0,成功
注意事项 -

驱动层

驱动层通过调用 HAL 层的接口,完成对 UART 设备的驱动实现, 代码在 bsp/artinchip/drv/uart/aic_drv_uart.c

驱动参数
驱动的参数配置通过在 Kconfig 中的宏来初始化
struct drv_uart_dev_para
{
    uint32_t index                   :4;
    uint32_t data_bits               :4;
    uint32_t stop_bits               :2;
    uint32_t parity                  :2;
    uint32_t flag;
    uint32_t baud_rate;
    uint32_t clk_freq;
    uint32_t function;
    char * name;
    char * uart_rts_name;
    char * uart_cts_name;
};

const struct drv_uart_dev_para uart_dev_paras[] =
{
#ifdef AIC_USING_UART0
    {0, AIC_DEV_UART0_DATABITS, AIC_DEV_UART0_STOPBITS, AIC_DEV_UART0_PARITY, AIC_UART0_FLAG,
    AIC_DEV_UART0_BAUDRATE, AIC_CLK_UART0_FREQ, AIC_DEV_UART0_MODE, "uart0", AIC_UART0_RTS_NAME, AIC_UART0_CTS_NAME},
#endif
#ifdef AIC_USING_UART1
    {1, AIC_DEV_UART1_DATABITS, AIC_DEV_UART1_STOPBITS, AIC_DEV_UART1_PARITY, AIC_UART1_FLAG,
    AIC_DEV_UART1_BAUDRATE, AIC_CLK_UART1_FREQ, AIC_DEV_UART1_MODE, "uart1", AIC_UART1_RTS_NAME, AIC_UART1_CTS_NAME},
#endif
#ifdef AIC_USING_UART2
    {2, AIC_DEV_UART2_DATABITS, AIC_DEV_UART2_STOPBITS, AIC_DEV_UART2_PARITY, AIC_UART2_FLAG,
    AIC_DEV_UART2_BAUDRATE, AIC_CLK_UART2_FREQ, AIC_DEV_UART2_MODE, "uart2", AIC_UART2_RTS_NAME, AIC_UART2_CTS_NAME},
#endif
#ifdef AIC_USING_UART3
    {3, AIC_DEV_UART3_DATABITS, AIC_DEV_UART3_STOPBITS, AIC_DEV_UART3_PARITY, AIC_UART3_FLAG,
    AIC_DEV_UART3_BAUDRATE, AIC_CLK_UART3_FREQ, AIC_DEV_UART3_MODE, "uart3", AIC_UART3_RTS_NAME, AIC_UART3_CTS_NAME},
#endif
#ifdef AIC_USING_UART4
    {4, AIC_DEV_UART4_DATABITS, AIC_DEV_UART4_STOPBITS, AIC_DEV_UART4_PARITY, AIC_UART4_FLAG,
    AIC_DEV_UART4_BAUDRATE, AIC_CLK_UART4_FREQ, AIC_DEV_UART4_MODE, "uart4", AIC_UART4_RTS_NAME, AIC_UART4_CTS_NAME},
#endif
#ifdef AIC_USING_UART5
    {5, AIC_DEV_UART5_DATABITS, AIC_DEV_UART5_STOPBITS, AIC_DEV_UART5_PARITY, AIC_UART5_FLAG,
    AIC_DEV_UART5_BAUDRATE, AIC_CLK_UART5_FREQ, AIC_DEV_UART5_MODE, "uart5", AIC_UART5_RTS_NAME, AIC_UART5_CTS_NAME},
#endif
#ifdef AIC_USING_UART6
    {6, AIC_DEV_UART6_DATABITS, AIC_DEV_UART6_STOPBITS, AIC_DEV_UART6_PARITY, AIC_UART6_FLAG,
    AIC_DEV_UART6_BAUDRATE, AIC_CLK_UART6_FREQ, AIC_DEV_UART6_MODE, "uart6", AIC_UART6_RTS_NAME, AIC_UART6_CTS_NAME},
#endif
#ifdef AIC_USING_UART7
    {7, AIC_DEV_UART7_DATABITS, AIC_DEV_UART7_STOPBITS, AIC_DEV_UART7_PARITY, AIC_UART7_FLAG,
    AIC_DEV_UART7_BAUDRATE, AIC_CLK_UART7_FREQ, AIC_DEV_UART7_MODE, "uart7", AIC_UART7_RTS_NAME, AIC_UART7_CTS_NAME},
#endif
};
初始化
初始化工作通过调用 RT-Thread 的标准初始化接口实现
INIT_BOARD_EXPORT(drv_usart_init);

int drv_usart_init(void)
{
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
    int u = 0;
    int i = 0;

    for (i=0; i<sizeof(uart_dev_paras)/sizeof(struct drv_uart_dev_para); i++) {
        u = uart_dev_paras[i].index;
        g_serial[u].ops                 = & drv_uart_ops;
        g_serial[u].config              = config;
        g_serial[u].config.bufsz        = 2048;
        g_serial[u].config.baud_rate    = uart_dev_paras[i].baud_rate;
        g_serial[u].config.data_bits    = uart_dev_paras[i].data_bits;
        g_serial[u].config.stop_bits    = uart_dev_paras[i].stop_bits - 1;
        g_serial[u].config.parity       = uart_dev_paras[i].parity;
        g_serial[u].config.function     = uart_dev_paras[i].function;
        g_serial[u].config.flag         = uart_dev_paras[i].flag;
        g_serial[u].config.uart_index   = uart_dev_paras[i].index;

        hal_clk_set_freq(CLK_UART0 + u, uart_dev_paras[i].clk_freq);
        hal_clk_enable(CLK_UART0 + u);
        hal_reset_assert(RESET_UART0 + u);
        aic_udelay(10000);
        hal_reset_deassert(RESET_UART0 + u);

#ifdef FINSH_POLL_MODE
        uart_handle[u] = hal_usart_initialize(u, NULL, NULL);
#else
        uart_handle[u] = hal_usart_initialize(u, NULL, drv_usart_irqhandler);
#endif
        rt_hw_serial_register(&g_serial[u],
                            uart_dev_paras[i].name,
#ifdef FINSH_POLL_MODE
                            RT_DEVICE_FLAG_RDWR,
#else
                            uart_dev_paras[i].flag,
#endif
                            uart_handle[u]);

        if (uart_dev_paras[i].flag == AIC_UART_DMA_FLAG) {
            hal_uart_set_fifo((aic_usart_priv_t *)g_serial[u].parent.user_data);
            hal_uart_attach_callback((aic_usart_priv_t *)g_serial[u].parent.user_data,
                                        drv_uart_callback, NULL);
            hal_usart_set_ier((aic_usart_priv_t *)g_serial[u].parent.user_data, 1);
        }

        drv_usart_function_init(i, u);
    }

    return 0;
}
对外接口
UART 驱动对外的接口主要是实现了 rt_uart_ops 的标准接口,使用 dma_transmit 需要在 menuconfig 打开相应的 uart-dma 使能开关
const struct rt_uart_ops drv_uart_ops =
{
    drv_uart_configure,
    drv_uart_control,
    drv_uart_putc,
    drv_uart_getc,
#if defined (RT_SERIAL_USING_DMA)
    drv_uart_dma_transmit,
#endif
};

应用层

UART 在应用层被封装成了一个标准的设备,通过读写进行数据的发送和接收

设备实现代码:kernel/rt-thread/components/drivers/serial/serial.c 使用示例代码:bsp/examples/test-uart/test_uart.c

16. rt_device_find
函数原型 rt_device_t rt_device_find(const char *name);
功能说明 找到相应的设备
参数定义 name - 设备名
返回值 设备变量或者 NULL
注意事项 -
17. rt_device_open
函数原型 rt_err_t rt_device_open (rt_device_t dev, rt_uint16_t oflag);
功能说明 打开设备
参数定义
dev - UART device
oflag - 打开参数
返回值 0,成功; 其他,失败
注意事项 -
18. rt_device_close
函数原型 rt_err_t rt_device_close(rt_device_t dev);
功能说明 关闭设备
参数定义 dev - 设备名
返回值 0,成功; 其他,失败
注意事项 -
19. rt_device_read
函数原型 rt_size_t rt_device_read (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
功能说明 接收信息
参数定义
dev - UART device
pos - 起始位置
buffer - 数据存储区
size - 大小
返回值 接收数据的大小
注意事项 -
20. rt_device_write
函数原型 rt_size_t rt_device_write (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
功能说明 发送信息
参数定义
dev - UART device
pos - 起始位置
buffer - 数据存储区
size - 大小
返回值 发送数据的大小
注意事项 -

使用示例

示例可以参考 bsp/examples/test-uart/test_uart.c
static rt_device_t serial = rt_device_find("uart1");
rt_device_open(serial, RT_DEVICE_FLAG_RDONLY);
rt_device_write(serial, 0, str_send, (sizeof(str_send) - 1));
rt_device_read(serial, 0, str_send, 1);