前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《rt-thread驱动框架分析》-pin驱动

《rt-thread驱动框架分析》-pin驱动

作者头像
Rice加饭
发布2022-05-10 17:36:25
1.3K0
发布2022-05-10 17:36:25
举报
文章被收录于专栏:Rice嵌入式

简要

  • 接下来做一个专辑《rt-thread驱动框架分析》,我会按照自己的理解来描述每一个驱动。有不对的欢迎随时来怼我。
  • rt-thread的版本分为两大类,一个是完整版本,一个是nano版本。而驱动框架是相对于完整版本的。所以要了解驱动框架,只能在完整版上了解。
  • rt-thread提供了很多驱动框架,比如常见的外设驱动:I2C, SPI等。还有网络相关的WLAN驱动等。
  • 驱动框架分析,主要以STM32来分析。

驱动分析

API简要说明
  • rt-thread的pin驱动为上层应用提供两套不同的API,一套是对接设备驱动框架。一套是封装好的API,用户层可以直接使用。接下来我们来分析一下这两套API的使用,以及实现。
pin框架层次
  1. 用户访问的方式的接口不同,访问的层次是不一样的。
  2. 层次结构如下:
  1. 从上面的图可以看出,对于不同芯片,用户层的接口是统一的,而对于驱动层来说,只需要对接好相应的回调函数。
  2. 通过统一的接口,应用开发人不需要知道底层驱动,也减少造轮子的时间。
GPIO驱动层
  1. 驱动层的任务主要有:①对接底层硬件,②对芯片的GPIO统一编号,③注册下面描述的6个回调函数。
  2. 驱动层中,我们特别关注一个结构体rt_pin_ops,如下:
代码语言:javascript
复制
/* pin.h */
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);
};
  1. 我们只需要对接好这几个6个回调函数就好了,描述如下:

API

描述

pin_mode

设置引脚模式

pin_write

设置引脚电平

pin_read

读取引脚电平

pin_attach_irq

绑定引脚中断回调函数

pin_irq_enable

使能引脚中断

pin_detach_irq

脱离引脚中断回调函数

  1. stm32为例,对接相应的OPS结构体,如下:
代码语言:javascript
复制
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,
};
  1. 注册pin相应的回调函数,如下:
代码语言:javascript
复制
int rt_hw_pin_init(void)
{
    ......

    return rt_device_pin_register("pin", &_stm32_pin_ops, RT_NULL);
}
PIN设备驱动层
  1. 这一层主要起到承上启下的,为上层应用提供统一的API,为下层驱动,提供注册函数。
  2. 其中注册函数如下:
代码语言:javascript
复制
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;
    _hw_pin.parent.write        = _pin_write;
    _hw_pin.parent.control      = _pin_control;
#endif

    _hw_pin.ops                 = ops;
    _hw_pin.parent.user_data    = user_data;

    /* register a character device */
    rt_device_register(&_hw_pin.parent, name, RT_DEVICE_FLAG_RDWR);

    return 0;
}
  1. 前面我们谈到RT-Thread的pin设备提供了两套API,但是最终调用的接口都是执行注册的回调函数。
  2. 简单的理解就是两套API,只是不同的访问方式。
RTT 为上层应用封装好的API描述
  1. 从官方的文档中心中,有详细的描述这套API的使用方法。这套API是直接暴露给用户层调用的。
  2. RT-Thread提供的pin设备管理接口来访问GPIO,接口如下:

API

描述

rt_pin_mode()

设置引脚模式

rt_pin_write()

设置引脚电平

rt_pin_read()

读取引脚电平

rt_pin_attach_irq()

绑定引脚中断回调函数

rt_pin_irq_enable()

使能引脚中断

rt_pin_detach_irq()

脱离引脚中断回调函数

  1. 该接口访问的层次如下:
  1. 应用,通过点亮一颗灯来描述:
代码语言:javascript
复制
#define LED0_PIN    GET_PIN(I, 13)

int main(void)
{
    int count = 1;
    /* set LED0 pin mode to output */
    rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);

    while (count++)
    {
        rt_pin_write(LED0_PIN, PIN_HIGH);
        rt_thread_mdelay(500);
        rt_pin_write(LED0_PIN, PIN_LOW);
        rt_thread_mdelay(500);
    }
    return RT_EOK;
}
通过设备驱动框架访问pin设备描述
  1. RTT设备驱动框架提供了统一的API:

API

说明

rt_err_t rt_device_init (rt_device_t dev)

设备初始化

rt_err_t rt_device_open (rt_device_t dev, rt_uint16_t oflag)

打开设备

rt_err_t rt_device_close(rt_device_t dev)

关闭设备

rt_size_t rt_device_read (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)

读设备

rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)

写设备

rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg)

控制设备

  1. 如果你学习过Linux,你是否听过一句话,一切设备皆文件。在Linux中对设备的访问有如下接口open,read,write,close等,其实RTT提供的设备驱动API也是如此。
  2. 该接口访问的层次如下:
  1. 如上图所示,_pin_control()应该包含:GPIO的模式设置,中断关联,中断使能,中断分离。但是从实际的上_pin_control()中,只实现了GPIO的模式设置,如果要使用这组API需要自己增加相关实现:
代码语言:javascript
复制
static rt_err_t  _pin_control(rt_device_t dev, int cmd, void *args)
{
    struct rt_device_pin_mode *mode;
    struct rt_device_pin *pin = (struct rt_device_pin *)dev;

    /* check parameters */
    RT_ASSERT(pin != RT_NULL);

    mode = (struct rt_device_pin_mode *) args;
    if (mode == RT_NULL) return -RT_ERROR;

    pin->ops->pin_mode(dev, (rt_base_t)mode->pin, (rt_base_t)mode->mode);

    return 0;
}

  1. 应用,通过点亮一颗灯来描述:
代码语言:javascript
复制
#define LED_PIN     GET_PIN(I, 13)

int main(void)
{
    int count = 1;
    struct rt_device_pin *pin_dev = RT_NULL;

    pin_dev = (struct rt_device_pin *)rt_device_find("pin");
    rt_device_open((rt_device_t)pin_dev, RT_DEVICE_OFLAG_RDWR);
    pin_dev->ops->pin_mode(&pin_dev->parent, LED_PIN, PIN_MODE_OUTPUT);

    while (count++)
    {
        pin_dev->ops->pin_write(&pin_dev->parent, LED_PIN, PIN_HIGH);
        rt_thread_mdelay(1000);
        pin_dev->ops->pin_write(&pin_dev->parent, LED_PIN, PIN_LOW);
        rt_thread_mdelay(1000);
    }

    return RT_EOK;
}

总结

  • 其实很多人都在讨论说,没有Linux基础,学RTT很痛苦。但是直接学Linux,如果你不去了解内核驱动代码,会少很多乐趣。但是Linux的驱动框架更加复杂,分析更加痛苦。所以作者认为,如果你学了RTT,再去学习Linux,分析驱动框架会更加简单方便。
  • 作为RTT的爱好者,我将对RTT驱动框架分析作为一个系列。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-10-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Rice 嵌入式开发技术分享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简要
  • 驱动分析
    • API简要说明
      • pin框架层次
        • GPIO驱动层
          • PIN设备驱动层
            • RTT 为上层应用封装好的API描述
              • 通过设备驱动框架访问pin设备描述
              • 总结
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档