RT-Thread 设备驱动UART浅析

OS版本:RT-Thread 4.0.0

芯片:STM32F407

RT-Thread的串口驱动框架与Linux相识,分成 I/O设备框架 + 设备底层驱动;

1. serial设备初始化及使用

将配置使能的 uart_obj[ ] 进行设备注册

rtthread_startup --> rt_hw_usart_init() --> rt_hw_serial_register --> rt_device_register

设备注册之后就可使用设备操作方式来使用串口

rt_device_find("uart3") -->  rt_device_open(serial, RT_DEVICE_FLAG_DMA_RX) -->  rt_device_set_rx_indicate(serial, uart_dma_rx_handle)

2. serial设备

设备层 rt_device 注册及 ops 实现

const static struct rt_device_ops serial_ops = 
{
    rt_serial_init,
    rt_serial_open,
    rt_serial_close,
    rt_serial_read,
    rt_serial_write,
    rt_serial_control
};

 

而serial设备 rt_serial_device 为 rt_device 的一个子类

struct rt_serial_device
{
  struct rt_device parent;

  const struct rt_uart_ops *ops;
  struct serial_configure config;

  void *serial_rx;
  void *serial_tx;
};

其中 rt_serial_device 中的 ops 通过 stm32_uart_ops 实现,这样 rt_device、rt_serial_device 和 uart底层就都关联起来了

 

硬件驱动层文件

drv_usart.c

drv_usart.h

stm32f4xx_hal_msp.c

主要内容 ops 实现,中断处理

static const struct rt_uart_ops stm32_uart_ops =
{
    .configure = stm32_configure,  //默认配置
    .control = stm32_control,
    .putc = stm32_putc,
    .getc = stm32_getc,
};

串口硬件初始化  HAL_UART_MspInit  对串口引脚和时钟的初始化 在 stm32f4xx_hal_msp.c 中,通过配置CubeMX生成;

 

3. 驱动分析

serial 的 control 操作 设计成 不能设置中断,即缺少 RT_DEVICE_CTRL_SET_INT 和 RT_DEVICE_CTRL_CLR_INT 操作, 这样可以避免误设置;

同时也是由于串口接收已经设计成三选一方式:中断、DMA、轮询;

说一下DMA,因为这也是我们最常用的串口数据接收处理方式;

RTT的 serial 的DMA接收,采用的 IDLE 中断来控制DMA接收数据的,

在 drv_usart.c 中

static void stm32_dma_config(struct rt_serial_device *serial)
{
    ......   

    /* enable interrupt */
    __HAL_UART_ENABLE_IT(&(uart->handle), UART_IT_IDLE);  //使能了空闲中断和DMA中断
    
    /* enable rx irq */
    HAL_NVIC_SetPriority(uart->config->dma_rx->dma_irq, 0, 0);
    HAL_NVIC_EnableIRQ(uart->config->dma_rx->dma_irq);
    
    HAL_NVIC_SetPriority(uart->config->irq_type, 1, 0);
    HAL_NVIC_EnableIRQ(uart->config->irq_type);

    ....
}

在中断处理函数 uart_isr 中 实现了

static void uart_isr(struct rt_serial_device *serial)
{
    ....

    #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))  //IDLE空闲中断
    {
        level = rt_hw_interrupt_disable();
        recv_total_index = serial->config.bufsz - __HAL_DMA_GET_COUNTER(&(uart->dma.handle));
        recv_len = recv_total_index - uart->dma.last_index;
        uart->dma.last_index = recv_total_index;
        rt_hw_interrupt_enable(level);

        if (recv_len)
        {
            rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (recv_len << 8));
        }
        __HAL_UART_CLEAR_IDLEFLAG(&uart->handle);
    }
#endif

.....  
}
void rt_hw_serial_isr(struct rt_serial_device *serial, int event)
{

        case RT_SERIAL_EVENT_RX_DMADONE:
        {
            int length;
            rt_base_t level;

            /* get DMA rx length */
            length = (event & (~0xff)) >> 8;

            if (serial->config.bufsz == 0)
            {
                struct rt_serial_rx_dma* rx_dma;

                rx_dma = (struct rt_serial_rx_dma*) serial->serial_rx;
                RT_ASSERT(rx_dma != RT_NULL);

                RT_ASSERT(serial->parent.rx_indicate != RT_NULL);
                serial->parent.rx_indicate(&(serial->parent), length);
                rx_dma->activated = RT_FALSE;
            }
            else
            {
                /* disable interrupt */
                level = rt_hw_interrupt_disable();
                /* update fifo put index */
                rt_dma_recv_update_put_index(serial, length);
                /* calculate received total length */
                length = rt_dma_calc_recved_len(serial);
                /* enable interrupt */
                rt_hw_interrupt_enable(level);
                /* invoke callback */
                if (serial->parent.rx_indicate != RT_NULL)
                {
                    serial->parent.rx_indicate(&(serial->parent), length);  //应用回调接收处理函数
                }
            }
            break;
        }


}

 

如果进行大数据传输,发现默认缓存(64字节)不够用 可修改 RT_SERIAL_RB_BUFSZ 值

#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_RB_BUFSZ, /* Buffer size */  \
    0                                      \
}

#define RT_SERIAL_RB_BUFSZ              64

 

转载于:https://www.cnblogs.com/silencehuan/p/10917650.html

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RT-Thread Studio 中使用串口设备,可以按照以下步骤进行: 1. 在设备管理器中找到串口设备,并记录下其名称和端口号。 2. 在 RT-Thread Studio 中创建一个名为 uart设备对象,并设置好其配置参数,如波特率、数据位、停止位和校验位等。 3. 在应用程序中调用相应的 API 函数来进行串口通信,如发送数据、接收数据等。 以下是一个使用串口设备的示例代码: ```c #include <rtthread.h> #include <rthw.h> #define UART_NAME "uart1" // 串口设备名称 #define UART_BUFSZ 128 // 缓冲区大小 static rt_device_t uart_dev; // 串口设备对象 static char uart_buffer[UART_BUFSZ];// 串口数据缓冲区 static void uart_thread_entry(void *parameter) { rt_size_t rx_len; while (1) { rt_memset(uart_buffer, 0, UART_BUFSZ); // 从串口设备中读取数据 rx_len = rt_device_read(uart_dev, 0, uart_buffer, UART_BUFSZ); if (rx_len > 0) { // 处理接收到的数据 rt_kprintf("Received: %s\n", uart_buffer); } // 延时一段时间 rt_thread_mdelay(10); } } int main(void) { // 初始化串口设备 uart_dev = rt_device_find(UART_NAME); rt_device_open(uart_dev, RT_DEVICE_FLAG_RDWR); // 创建串口线程 rt_thread_t tid = rt_thread_create("uart", uart_thread_entry, RT_NULL, 1024, 25, 5); rt_thread_startup(tid); return 0; } ``` 在上述代码中,首先通过 rt_device_find() 函数找到名为 uart1 的串口设备,并通过 rt_device_open() 函数打开该设备。然后创建一个名为 uart 的线程,该线程不断从串口设备中读取数据,并进行处理。可以根据实际需要修改线程的优先级、堆栈大小等参数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值