RT-Thread学习笔记(1):pin设备框架的简单解读

重要结构体介绍

rt_device_pin结构体

struct rt_device_pin
{
    struct rt_device parent;			/* 父类 */
    const struct rt_pin_ops *ops;		/* 操作函数集 */
};

rt_device结构体

struct rt_device
{
    struct rt_object          parent;                   /**< inherit from rt_object */

    enum rt_device_class_type type;                     /**< device type */
    rt_uint16_t               flag;                     /**< device flag */
    rt_uint16_t               open_flag;                /**< device open flag */

    rt_uint8_t                ref_count;                /**< reference count */
    rt_uint8_t                device_id;                /**< 0 - 255 */

    /* device call back */
    rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);
    rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);

#ifdef RT_USING_DEVICE_OPS
    const struct rt_device_ops *ops;
#else
    /* common device interface */
    rt_err_t  (*init)   (rt_device_t dev);
    rt_err_t  (*open)   (rt_device_t dev, rt_uint16_t oflag);
    rt_err_t  (*close)  (rt_device_t dev);
    rt_size_t (*read)   (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
    rt_size_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);
#endif

#if defined(RT_USING_POSIX)
    const struct dfs_file_ops *fops;
    struct rt_wqueue wait_queue;
#endif

    void                     *user_data;                /**< device private data */
};

rt_pin_ops结构体

struct rt_pin_ops
{
    void (*pin_mode)(struct rt_device *device, rt_base_t pin, rt_base_t mode);
    void (*pin_write)(struct rt_device *device, rt_base_t pin, rt_base_t value);
    int (*pin_read)(struct rt_device *device, rt_base_t pin);

    /* TODO: add GPIO interrupt */
    rt_err_t (*pin_attach_irq)(struct rt_device *device, rt_int32_t pin,
                      rt_uint32_t mode, void (*hdr)(void *args), void *args);
    rt_err_t (*pin_detach_irq)(struct rt_device *device, rt_int32_t pin);
    rt_err_t (*pin_irq_enable)(struct rt_device *device, rt_base_t pin, rt_uint32_t enabled);

};

重要的C文件介绍

drv_gpio.c

/* drv_gpio.c里面的函数 */

static void stm32_pin_write(rt_device_t dev, rt_base_t pin, rt_base_t value)
static int stm32_pin_read(rt_device_t dev, rt_base_t pin)
static void stm32_pin_mode(rt_device_t dev, rt_base_t pin, rt_base_t mode)
rt_inline rt_int32_t bit2bitno(rt_uint32_t bit)
rt_inline const struct pin_irq_map *get_pin_irq_map(uint32_t pinbit)
static rt_err_t stm32_pin_attach_irq(struct rt_device *device, rt_int32_t pin,
                                     rt_uint32_t mode, void (*hdr)(void *args), void *args)
static rt_err_t stm32_pin_dettach_irq(struct rt_device *device, rt_int32_t pin)
static rt_err_t stm32_pin_irq_enable(struct rt_device *device, rt_base_t pin,
                                     rt_uint32_t enabled)
const static struct rt_pin_ops _stm32_pin_ops =
{
    stm32_pin_mode,
    stm32_pin_write,
    stm32_pin_read,
    stm32_pin_attach_irq,
    stm32_pin_dettach_irq,
    stm32_pin_irq_enable,
};

int rt_hw_pin_init(void)

我们可以看出,drv_gpio.c这个文件里面主要是完成了 rt_pin_ops 结构体里面函数的实现,并且这些函数基本上也就是通过HAL库底层驱动来实现的,即这个文件是最接近底层的文件,当然官方HAL除外。

底层驱动函数

static void stm32_pin_write(rt_device_t dev, rt_base_t pin, rt_base_t value)
{
    const struct pin_index *index;

    index = get_pin(pin);
    if (index == RT_NULL)
    {
        return;
    }

    HAL_GPIO_WritePin(index->gpio, index->pin, (GPIO_PinState)value);
}

static int stm32_pin_read(rt_device_t dev, rt_base_t pin)
{
    int value;
    const struct pin_index *index;

    value = PIN_LOW;

    index = get_pin(pin);
    if (index == RT_NULL)
    {
        return value;
    }

    value = HAL_GPIO_ReadPin(index->gpio, index->pin);

    return value;
}

这里展示其中两个代码,HAL_GPIO_WritePin(index->gpio, index->pin, (GPIO_PinState)value)、HAL_GPIO_ReadPin(index->gpio, index->pin)这两个函数相信大家学习过stm32都应该知道。

rt_hw_pin_init(重要)

int rt_hw_pin_init(void)
{
#if defined(__HAL_RCC_GPIOA_CLK_ENABLE)
    __HAL_RCC_GPIOA_CLK_ENABLE();
#endif
    
#if defined(__HAL_RCC_GPIOB_CLK_ENABLE)
    __HAL_RCC_GPIOB_CLK_ENABLE();
#endif
    
#if defined(__HAL_RCC_GPIOC_CLK_ENABLE)
    __HAL_RCC_GPIOC_CLK_ENABLE();
#endif
    
#if defined(__HAL_RCC_GPIOD_CLK_ENABLE)
    __HAL_RCC_GPIOD_CLK_ENABLE();
#endif

#if defined(__HAL_RCC_GPIOE_CLK_ENABLE)
    __HAL_RCC_GPIOE_CLK_ENABLE();
#endif

#if defined(__HAL_RCC_GPIOF_CLK_ENABLE)
    __HAL_RCC_GPIOF_CLK_ENABLE();
#endif

#if defined(__HAL_RCC_GPIOG_CLK_ENABLE)
    #ifdef SOC_SERIES_STM32L4
        HAL_PWREx_EnableVddIO2();
    #endif
    __HAL_RCC_GPIOG_CLK_ENABLE();
#endif

#if defined(__HAL_RCC_GPIOH_CLK_ENABLE)
    __HAL_RCC_GPIOH_CLK_ENABLE();
#endif

#if defined(__HAL_RCC_GPIOI_CLK_ENABLE)
    __HAL_RCC_GPIOI_CLK_ENABLE();
#endif

#if defined(__HAL_RCC_GPIOJ_CLK_ENABLE)
    __HAL_RCC_GPIOJ_CLK_ENABLE();
#endif

#if defined(__HAL_RCC_GPIOK_CLK_ENABLE)
    __HAL_RCC_GPIOK_CLK_ENABLE();
#endif

    return rt_device_pin_register("pin", &_stm32_pin_ops, RT_NULL);
}

上面代码主要实现了打开对应gpio的时钟以及调用rt_device_pin_register函数。&_stm32_pin_ops这个参数很重要,看一下这个函数原型:

int rt_device_pin_register(const char *name, const struct rt_pin_ops *ops, void *user_data);

注册PIN设备

此函数可以注册PIN设备到PIN驱动框架中。

参数

  • name PIN设备名称
  • ops PIN设备操作函数对象指针
  • user_data 用户数据,一般设为RT_NULL

返回

  • RT_EOK 注册成功;-RT_ERROR 注册失败,已有其他驱动使用该name注册。

pin.c

/* pin.c里面的函数 */

static struct rt_device_pin _hw_pin;
static rt_size_t _pin_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
static rt_size_t _pin_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
static rt_err_t  _pin_control(rt_device_t dev, int cmd, void *args)
int rt_device_pin_register(const char *name, const struct rt_pin_ops *ops, void *user_data)
rt_err_t rt_pin_attach_irq(rt_int32_t pin, rt_uint32_t mode,
                             void (*hdr)(void *args), void  *args)
rt_err_t rt_pin_detach_irq(rt_int32_t pin)
rt_err_t rt_pin_irq_enable(rt_base_t pin, rt_uint32_t enabled)
void rt_pin_mode(rt_base_t pin, rt_base_t mode)
oid rt_pin_write(rt_base_t pin, rt_base_t value)
int  rt_pin_read(rt_base_t pin)
    

现在我们再来看看上面说的 rt_device_pin_register 函数

static struct rt_device_pin _hw_pin;
int rt_device_pin_register(const char *name, const struct rt_pin_ops *ops, void *user_data)
{
    _hw_pin.parent.type         = RT_Device_Class_Miscellaneous;/* 声明为杂类设备类型 */
    _hw_pin.parent.rx_indicate  = RT_NULL;
    _hw_pin.parent.tx_complete  = RT_NULL;

#ifdef RT_USING_DEVICE_OPS
    _hw_pin.parent.ops          = &pin_ops;
#else
    _hw_pin.parent.init         = RT_NULL;
    _hw_pin.parent.open         = RT_NULL;
    _hw_pin.parent.close        = RT_NULL;
    _hw_pin.parent.read         = _pin_read;		/* 把设备的read操作绑定在pin.c的_pin_read函数 */
    _hw_pin.parent.write        = _pin_write;		/* 把设备的write操作绑定在pin.c的_pin_write函数 */
    _hw_pin.parent.control      = _pin_control;		/* 把设备的control操作绑定在pin.c的_pin_control函数 */
#endif

    _hw_pin.ops                 = ops;				/* 把drv_gpio.c所实现的_stm32_pin_ops绑定在_hw_pin.ops上 */
    _hw_pin.parent.user_data    = user_data;		/* 私有数据 */

    /* register a character device */
    rt_device_register(&_hw_pin.parent, name, RT_DEVICE_FLAG_RDWR);/* 将其注册进device设备框架中 */

    return 0;
}

当程序运行到这儿的时候,设备框架里面已经注册了一个名为"pin"的设备。然后也初始化好了_hw_pin这个结构体:

在这里插入图片描述

如何实现对接

pin设备框架提供的接口:

rt_err_t rt_pin_attach_irq(rt_int32_t pin, rt_uint32_t mode,
                             void (*hdr)(void *args), void  *args)
rt_err_t rt_pin_detach_irq(rt_int32_t pin)
rt_err_t rt_pin_irq_enable(rt_base_t pin, rt_uint32_t enabled)
void rt_pin_mode(rt_base_t pin, rt_base_t mode)
oid rt_pin_write(rt_base_t pin, rt_base_t value)
int  rt_pin_read(rt_base_t pin)

在这里我就拿最简单的mode函数做讲解了:

void rt_pin_mode(rt_base_t pin, rt_base_t mode)
{
    RT_ASSERT(_hw_pin.ops != RT_NULL);
    _hw_pin.ops->pin_mode(&_hw_pin.parent, pin, mode);
}

分析如下

rt_pin_mode(rt_base_t pin, rt_base_t mode)
	->_hw_pin.ops->pin_mode(&_hw_pin.parent, pin, mode);
		->stm32_pin_mode(rt_device_t dev, rt_base_t pin, rt_base_t mode)

综上,pin设备框架的流程大概如下:

  • 先实现底层的gpio驱动函数
  • 把这些函数通过rt_device_pin_register注册进pin设备驱动框架
  • 最后就可以调用pin设备框架提供访问api进行操作了。

一些补充

看到这儿,大家可能会有疑问

static rt_size_t _pin_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
static rt_size_t _pin_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
static rt_err_t  _pin_control(rt_device_t dev, int cmd, void *args)

这几个函数有什么作用????

_hw_pin.parent.read         = _pin_read;		/* 把设备的read操作绑定在pin.c的_pin_read函数 */
_hw_pin.parent.write        = _pin_write;		/* 把设备的write操作绑定在pin.c的_pin_write函数 */
_hw_pin.parent.control      = _pin_control;		/* 把设备的control操作绑定在pin.c的_pin_control函数 */

这里我们可以看出来,这些函数被绑定在device框架的标准设备操作函数,那说明我们也是可以通过rtthread提供的I/O设备模型的标准device操作函数去访问pin设备。

关于device设备框架和pin设备提供的这三个接口的绑定,其实和上面pin和drv_gpio提供的底层操作接口绑定很类似。这里我说一下device是如何调用到底层pin的:

rt_size_t rt_device_read(rt_device_t dev,
                         rt_off_t    pos,
                         void       *buffer,
                         rt_size_t   size)
{
    RT_ASSERT(dev != RT_NULL);
    RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);

    if (dev->ref_count == 0)
    {
        rt_set_errno(-RT_ERROR);
        return 0;
    }

    /* call device_read interface */
    if (device_read != RT_NULL)
    {
        return device_read(dev, pos, buffer, size);
    }

    /* set error code */
    rt_set_errno(-RT_ENOSYS);

    return 0;
}

一些隐藏宏定义:

#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_device_read
	->device_read(dev, pos, buffer, size);//(dev->read(dev, pos, buffer, size);)
		->_pin_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
            ->pin->ops->pin_read(dev, status->pin);
				->stm32_pin_read(rt_device_t dev, rt_base_t pin)

总结

无论RT-Thread写的多么花花,都是想尽一切办法去调用底层代码,慢慢分析!!!

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值