RT-Thread学习笔记(2):serial驱动框架简要分析

rt_hw_usart_init函数干了些什么??

之前写了一篇pin设备框架分析pin设备框架分析,简单理了一下pin设备框架实现的思想,其实今天要说的串口基本类似,今天咱们就长话短说,我介绍几个重要的部分。

首先就是标题的这个函数。包括上一节内容的**int rt_hw_pin_init(void)**函数。我们都是围绕这个进行介绍的。

int rt_hw_usart_init(void)
{
    rt_size_t obj_num = sizeof(uart_obj) / sizeof(struct stm32_uart);/* 当前的串口设备数量 */
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;/* 串口的默认参数(包括波特率,数据长度,停止位等) */
    rt_err_t result = 0;

    stm32_uart_get_dma_config();					/* 配置串口DMA */
    
    for (int i = 0; i < obj_num; i++)
    {
        uart_obj[i].config = &uart_config[i];		/* 串口的底层配置(包括寄存器句柄,中断相关,和设备名字) */
        uart_obj[i].serial.ops    = &stm32_uart_ops;/* 将serial.ops集和底层的操作函数绑定 */
        uart_obj[i].serial.config = config;			/* 配置默认参数RT_SERIAL_CONFIG_DEFAULT */
        /* register UART device */
        result = rt_hw_serial_register(&uart_obj[i].serial, uart_obj[i].config->name,
                                       RT_DEVICE_FLAG_RDWR
                                       | RT_DEVICE_FLAG_INT_RX
                                       | RT_DEVICE_FLAG_INT_TX
                                       | uart_obj[i].uart_dma_flag
                                       , NULL);
        RT_ASSERT(result == RT_EOK);
    }
    
    return result;

}
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);

    device = &(serial->parent);

    device->type        = RT_Device_Class_Char;/* 设备类型为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;	/* 设备的init操作绑定在sensor.c的rt_serial_init函数 */
    device->open        = rt_serial_open;	/* 设备的open操作绑定在sensor.c的rt_serial_open函数 */
    device->close       = rt_serial_close;	/* 设备的close操作绑定在sensor.c的rt_serial_close函数 */
    device->read        = rt_serial_read;	/* 设备的read操作绑定在sensor.c的rt_serial_read函数 */
    device->write       = rt_serial_write;	/* 设备的write操作绑定在sensor.c的rt_serial_write函数 */
    device->control     = rt_serial_control;/* 设备的control操作绑定在sensor.c的rt_serial_control函数 */
#endif
    device->user_data   = data;/* 私有数据 */

    /* register a character device */
    ret = rt_device_register(device, name, flag);

#if defined(RT_USING_POSIX)
    /* set fops */
    device->fops        = &_serial_fops;
#endif

    return ret;
}

基本上必要的注释,我都在代码上写了,大家这么看可能不太直观,我还是上个图:

在这里插入图片描述

上层接口是如何对接的??

隐藏宏定义

#define device_init     (dev->init)
#define device_open     (dev->open)
#define device_close    (dev->close)
#define device_read     (dev->read)
#define device_write    (dev->write)
#define device_control  (dev->control)
rt_err_t rt_device_close(rt_device_t dev)
    ->result = device_close(dev);/* result = (dev->close)(dev); */
	=>rt_serial_close(struct rt_device *dev)/* 根据上面图,dev->close和rt_serial_close绑定 */
        -> serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void *)RT_DEVICE_FLAG_INT_RX);

等等,到这儿就有一个重点了!!!!!!!!!!!!!!!!!!!!

在前面的注册函数和初始过程中,我们似乎没有对rt_serial_init、rt_serial_open与(drv_usart.cart.c)里面的stm32_uart_ops里面的操作函数对接,那这里为什么可以直接调用serial->ops->control呢?????

大家仔细观察会发现,在rt_serial_init、rt_serial_open、rt_serial_write等函数中,前面会有这么几条语句!

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;
    ……
}

这个函数我们传进去的参数是一个struct rt_device*结构类型的变量dev,在函数里面做了一个类型转换:

struct rt_serial_device *serial;
serial = (struct rt_serial_device *)dev;

这里大家可能得回顾一个知识点:

结构体成员的第一个元素的地址和结构体的首地址相同。

我们再看这个struct rt_serial_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;
};

所以通过前面一转换,虽然我们在注册的时候只是注册了device设备,但是我们可以根据这个“父亲(rt_serial_device)”来找到他的“孩子(rt_serial_device)”,然后通过调用serial->ops去对接device设备的操作函数。

同理,大家去仔细观察一下其他device操作函数,都是这样一层一层的调用的。

补充

如果大家是新作一个bsp的话,在配置各种各样的驱动的时候,我们都需要使用CubeMX工具去生成一个驱动,获得如下这个函数

void HAL_XXX_MspInit(XXX_HandleTypeDef* huart)

这个函数是关于外设时钟和引脚配置的,大家有没有想过,本节的**void HAL_UART_MspInit(UART_HandleTypeDef* huart)**这个函数又是在哪儿调用的呢????

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;

    /* apply configuration */
    if (serial->ops->configure)
        result = serial->ops->configure(serial, &serial->config);

    return result;
}

在这个函数中serial->ops->configure

static rt_err_t stm32_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
{
  	……
  	……
    if (HAL_UART_Init(&uart->handle) != HAL_OK)
    {
        return -RT_ERROR;
    }

    return RT_EOK;
}

HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)
{
	……
    HAL_UART_MspInit(huart);
	……
}
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值