驱动分析

I2C设备驱动框架图:

  1. 我们先RT-Thread的I2C框架图(这是我自己理解的框架图,如果不对的地方,请指出):
  • 上图是我分析的RTT的I2C框架图。主要分为三层,驱动层-核心层-设备层。如果你分析过Linux的I2C框架,它的层次也是这样子。所以你了解了RTT的I2C之后再去看Linux的I2C框架,其实问题不大。
  • 驱动层:分为硬件I2C驱动和软件I2C驱动。
  • 核心层: ①其中bit_ops是RTT为软件I2C提供的中间层,它的作用:为底层模拟I2C驱动提供回调接口,为核心层提供统一I2C通信接口。②而硬件I2C则直接对接核心层,提供统一I2C通信接口。③RTT在核心层上,也像pin驱动那样,封装了一套API(虚线箭头),供用户直接使用。④dev是提供RTT设备驱动框架的统一的API(实现箭头)。⑤注意的是:模拟I2C驱动到核心层,增加了一层中间层。
  • 设备层:设备就是杂七杂八的使用I2C的总线的设备。而这些设备可以选择使用RTT驱动框架的API,也可以选择RTT封装好的API。
  1. 通过上述的描述,可能还没了解的很清晰。下面我根据两种不同方式的驱动,两种不同的API,逐一分析,并且会结合试验来验证。

driver 层:

  • RT-Thread的I2C驱动,分为两种类型:硬件I2C和软件I2C。在stm32的BSP中提供了软件I2C的驱动,不过为了全面介绍,硬件I2C的对接,作者也进行简单的对接和实现。

软件I2C:

  1. 软件I2C的层次图:
  1. drv_soft_i2c层: 主要进行软件I2C所用到scl引脚,sda引脚初始化。scl引脚和sda引脚的获取电平和设置电平接口和延时函数(udelay)。并对接bit_opt层提供的操作结构体:struct rt_i2c_bit_ops。并通过rt_i2c_bit_add_bus注册,提供给bit_opt层回调。
  • struct rt_i2c_bit_ops结构体:
struct rt_i2c_bit_ops{    void *data;    void (*set_sda)(void *data, rt_int32_t state);    void (*set_scl)(void *data, rt_int32_t state);    rt_int32_t (*get_sda)(void *data);    rt_int32_t (*get_scl)(void *data);    void (*udelay)(rt_uint32_t us);    rt_uint32_t delay_us;    rt_uint32_t timeout;};

函数指针 功能 void (set_sda)(void data, rt_int32_t state) 设置SDA电平 void (set_scl)(void data, rt_int32_t state); 设置SCL电平 rt_int32_t (get_sda)(void data); 获取SDA电平 rt_int32_t (get_scl)(void data); 获取SCL电平 void (*udelay)(rt_uint32_t us); 软件I2C时序所需要的的延时函数

  • rt_i2c_bit_add_bus接口,主要注册软件I2C的引脚操作的回调函数。
  1. bit_opt层:可以归纳为驱动层。其主要实现软件I2C的时序等逻辑,并提供对应的I2C的收发处理函数,为drv_soft_i2c层提供提供了(struct rt_i2c_bit_ops)注册接口和(rt_i2c_bit_add_bus)接口,为i2c_core层提供主机模式的数据处理函数。bit_opt层主要对接到i2c_core层提供操作结构体:struct rt_i2c_bus_device_ops以及i2c总线的注册函数rt_i2c_bus_device_register:
  • struct rt_i2c_bus_device_ops结构体:
struct rt_i2c_bus_device_ops{    rt_size_t (*master_xfer)(struct rt_i2c_bus_device *bus,                             struct rt_i2c_msg msgs[],                             rt_uint32_t num);    rt_size_t (*slave_xfer)(struct rt_i2c_bus_device *bus,                            struct rt_i2c_msg msgs[],                            rt_uint32_t num);    rt_err_t (*i2c_bus_control)(struct rt_i2c_bus_device *bus,                                rt_uint32_t,                                rt_uint32_t);};

函数指针 功能 master_xfer 主机模式的数据收发 slave_xfer 从机模式的数据收发 i2c_bus_control i2c总线的的操作参数等设置

  • rt_i2c_bus_device_register接口:主要注册I2C总线和收发数据的回调函数。
  1. 软件I2C驱动总结:rt-thread的软件I2C,如果要对接其他平台,只需要对接好结构体:struct rt_i2c_bit_ops。而软件I2C的逻辑完全不用理会,全部由bit_opt层管理。

硬件I2C

  1. 硬件I2C的层次图:
  1. drv_hw_i2c层:没有软件I2C的bit_opt层,而是直接对接i2c_core层提供的结构体:struct rt_i2c_bus_device_ops。作者为了简单说明,写了个例子(简单粗暴的例子):
struct rt_i2c_bus_device i2c1_bus;I2C_HandleTypeDef hi2c1;static rt_err_t i2c_hw_init(void){    hi2c1.Instance = I2C1;    hi2c1.Init.ClockSpeed = 100000;    hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;    hi2c1.Init.OwnAddress1 = 0;    hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;    hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;    hi2c1.Init.OwnAddress2 = 0;    hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;    hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;    if (HAL_I2C_Init(&hi2c1) != HAL_OK)    {      Error_Handler();    }    return RT_EOK;}static rt_size_t i2c_xfer(struct rt_i2c_bus_device *bus, struct rt_i2c_msg msgs[], rt_uint32_t num){    struct rt_i2c_msg *msg;    rt_int32_t i, ret;    for(i = 0; i< num; i++)    {        msg = &msgs[i];        if(msg->flags & RT_I2C_RD)        {            HAL_I2C_Master_Receive(&hi2c1, msg->addr, msg->buf, msg->len, 100);        }        else        {            HAL_I2C_Master_Transmit(&hi2c1, msg->addr, msg->buf, msg->len, 100);        }    }    ret = i;    return ret;}static const struct rt_i2c_bus_device_ops i2c_bus_ops ={    i2c_xfer,    RT_NULL,    RT_NULL};int rt_i2c_hw_init(void){    i2c_hw_init();    i2c1_bus.ops = &i2c_bus_ops;    rt_i2c_bus_device_register(&i2c1_bus, "hw_i2c");    return RT_EOK;}INIT_DEVICE_EXPORT(rt_i2c_hw_init);
  1. 以上硬件I2C对接,我只是对接了master_xfer。
  2. 硬件I2C驱动总结:如果你是采用硬件I2C,那么就不需要去关乎bit_opt层,其实通过上面的描述,你不难理解bit_opt层和drv_hw_i2c层其实对接都是i2c_core层的结构。这也是我为什么把bit_opt层归纳为驱动层来讲解了。

core 层:

  1. i2c_core层为驱动层提供结构体:struct rt_i2c_bus_device_ops。为设备层提供I2C的数据收发处理函数。
  2. 其实i2c_core层主要是封装了一层API直接提供给用户层设备调用。

函数 说明 rt_i2c_bus_device_find 查找i2c总线 rt_i2c_transfer 主机模式的i2c数据传输 rt_i2c_master_send 主机模式的i2c数据发送 rt_i2c_master_recv 主机模式的i2c数据接受

  1. 其中rt_i2c_master_send函数和rt_i2c_master_recv函数是调用rt_i2c_transfer函数,而rt_i2c_transfer函数是调用master_xfer回调函数。如果你是使用这些接口,那么device层可以不用理会。

device 层:

  1. i2c_dev层,对接rt-thread设备驱动框架。提供read,write,control函数。并通过函数rt_device_register注册到设备驱动框架。下面我也会使用这些接口来实现控制OLED。

函数 说明 i2c_bus_device_read I2C读操作 i2c_bus_device_write I2C写操作 i2c_bus_device_control I2C总线的控制

软件I2C设计:

  • 关键代码, 注意软件I2C的地址需要偏移一位:
struct rt_i2c_bus_device *i2c_bus;#define OLED_I2C_BUS_NAME       "i2c1"#define OLED_ADDRESS            0x3cstatic rt_err_t write_reg(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint8_t data){    rt_uint8_t buf[2];    struct rt_i2c_msg msgs;    buf[0] = reg;    buf[1] = data;    msgs.addr = OLED_ADDRESS;    msgs.flags = RT_I2C_WR;    msgs.buf = buf;    msgs.len = 2;    /@@* 调用I2C设备接口传输数据 */    rt_i2c_transfer(bus, &msgs, 1);    return RT_EOK;}static rt_err_t read_regs(struct rt_i2c_bus_device *bus, rt_uint8_t len, rt_uint8_t *buf){    struct rt_i2c_msg msgs;    msgs.addr = OLED_ADDRESS;    msgs.flags = RT_I2C_RD;    msgs.buf = buf;    msgs.len = len;    /@@* 调用I2C设备接口传输数据 */    rt_i2c_transfer(bus, &msgs, 1);    return RT_EOK;}int oled_init(void){   i2c_bus = rt_i2c_bus_device_find(OLED_I2C_BUS_NAME);   if(i2c_bus == RT_NULL)   {      rt_kprintf("find i2c bus fail!");      return RT_ERROR;   }   rt_kprintf("find i2c bus success!");   ......}
  • 效果图:

硬件I2C设计:

  • 关键代码,注意硬件I2C的地址不需要偏移:
struct rt_i2c_bus_device *i2c_bus;#define OLED_I2C_BUS_NAME       "hw_i2c"#define OLED_ADDRESS            0x78static rt_err_t write_reg(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint8_t data){    rt_uint8_t buf[2];    struct rt_i2c_msg msgs;    buf[0] = reg;    buf[1] = data;    msgs.addr = OLED_ADDRESS;    msgs.flags = RT_I2C_WR;    msgs.buf = buf;    msgs.len = 2;    /@@* 调用I2C设备接口传输数据 */    rt_i2c_transfer(bus, &msgs, 1);    return RT_EOK;}static rt_err_t read_regs(struct rt_i2c_bus_device *bus, rt_uint8_t len, rt_uint8_t *buf){    struct rt_i2c_msg msgs;    msgs.addr = OLED_ADDRESS;    msgs.flags = RT_I2C_RD;    msgs.buf = buf;    msgs.len = len;    /@@* 调用I2C设备接口传输数据 */    rt_i2c_transfer(bus, &msgs, 1);    return RT_EOK;}int oled_init(void){   i2c_bus = rt_i2c_bus_device_find(OLED_I2C_BUS_NAME);   if(i2c_bus == RT_NULL)   {      rt_kprintf("find i2c bus fail!");      return RT_ERROR;   }   rt_kprintf("find i2c bus success!");   ......}

使用驱动框架API实现:

  • 关键代码:
#define OLED_I2C_BUS_NAME   "hw_i2c"struct rt_device *dev_i2c;#define OLED_ADDRESS        0x78static rt_err_t write_reg(struct rt_device *dev, rt_uint8_t reg, rt_uint8_t data){    rt_uint8_t buf[2];    rt_off_t pos;    rt_uint16_t addr = OLED_ADDRESS;    rt_uint16_t flags = RT_I2C_WR;    buf[0] = reg;    buf[1] = data;    pos = (flags << 16) | addr;    rt_device_write(dev, pos, buf, 2);    return RT_EOK;}static rt_err_t read_regs(struct rt_device *dev, rt_uint8_t len, rt_uint8_t *buf){    rt_off_t pos;    rt_uint16_t addr = OLED_ADDRESS;    rt_uint16_t flags = RT_I2C_WR;    pos = (flags << 16) | addr;    rt_device_write(dev, pos, buf, 2);    return RT_EOK;}int oled_init(void){    dev_i2c = rt_device_find(OLED_I2C_BUS_NAME);    if(dev_i2c == RT_NULL)    {        rt_kprintf("find i2c bus fail!");        return RT_ERROR;    }    rt_kprintf("find i2c bus success!");    rt_device_open(dev_i2c, RT_DEVICE_OFLAG_RDWR);    ....    return RT_EOK;}

总结

  • 有了I2C驱动框架,对于上层来说,不管硬件I2C还是软件I2C调用接口都是一样的。
  • rt-thread为了方便,直接在核心层提供了一套API,这样用户层调用就更加方便。

获取另一个驱动的设备结构体_《rt-thread驱动框架分析》-i2c驱动相关推荐

  1. 获取另一个驱动的设备结构体_字符设备驱动的另一种写法

    字符设备驱动的另一种写法 在Linux2.6内核中,使用cdev结构体描述一个字符设备; cdev结构体(include/linux/cdev.h)定义如下: struct cdev { struct ...

  2. 获取另一个驱动的设备结构体_Linux 驱动开发 / 设备模型快速入门

    背 景 Read the fucking source code!  --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版 ...

  3. linux 触摸结构体,xboot-x4412ibox项目实战54-Linux触摸屏驱动之I2C驱动实验 - Powered by Discuz!...

    前面我们分析了linux触摸屏驱动的input子系统机制,本章节分析linux触摸屏驱动的i2c机制. 驱动源码路径: kernel/drivers/input/touchscreen/ft5x06_ ...

  4. 定义一个表示教师的结构体变量,教师信息包含:编号,姓名,年龄,职称。编写程序从键盘输入一个教师的信息,然后将该教师的信息显示在屏幕上。

    定义一个表示教师的结构体变量,教师信息包含:编号,姓名,年龄,职称.编写程序从键盘输入一个教师的信息,然后将该教师的信息显示在屏幕上. 解析: #include <stdio.h>type ...

  5. 算法训练 - P1101 ——有一份提货单,其数据项目有:商品名(MC)、单价(DJ)、数量(SL)。定义一个结构体prut,其成员是上面的三项数据。在主函数中定义一个prut类型的结构体数组,输入每

    问题描述 有一份提货单,其数据项目有:商品名(MC).单价(DJ).数量(SL).定义一个结构体prut,其成员是上面的三项数据.在主函数中定义一个prut类型的结构体数组,输入每个元素的值,计算并输 ...

  6. CMOS摄像头驱动分析-i2c驱动

    CMOS摄像头驱动分析-i2c驱动 文章目录 CMOS摄像头驱动分析-i2c驱动 设备树内容 module_i2c_driver宏分析 ov2640_i2c_driver ov2640_probe 设 ...

  7. 【驱动】使用结构体 file_operations封装驱动设备的操作 | 结构体初始化

    -----第一部分----- 最近学习到了Linux驱动章节的课程,对设备的对应驱动的注册有些困惑,看了下发现是把设备的所有操作方法封装到结构体 file_operations 中,这个结构体为所有的 ...

  8. 网络驱动之net_device结构体

    在Linux系统中,网络设备都被抽象为struct net_device结构体.它是网络设备硬件与上层协议之间联系的接口,了解它对编写网络驱动程序非常有益,所以本文将着手简要介绍linux-2.6.3 ...

  9. 获取网络接口信息——ioctl()函数与结构体struct ifreq、 struct ifconf

    http://blog.csdn.net/windeal3203/article/details/39320605 Linux 下 可以使用ioctl()函数 以及 结构体 struct ifreq ...

最新文章

  1. cmd查看所有数据库 db2_DB2数据库常用命令集
  2. 初学者学python看什么书-python初学者看什么书
  3. 计算机网络:Socket网络通信底层数据传输
  4. linux 没权限dev null,Linux mint cinnamon 64位找不到/dev/null
  5. 基于OpenCV的图像阴影去除,你会吗?
  6. php 邮箱开发教程,php开发中表单验证邮箱及URL的教程
  7. JavaGC(1)—深入浅出Java垃圾回收机制
  8. java系列3:数组初始化(基本格式)
  9. CMU 15-213 Introduction to Computer Systems学习笔记(5) Machine-Level Programming-Control
  10. flask mysql sql注入_Python 中如何防止sql注入
  11. 【db2】db2错误代码大全-SQLCODE
  12. 黑马程序员—黑马的学习环境没有亲身经历的人是很难体会的!!!
  13. Oracle执行计划
  14. Qt 之 QQ系统表情(二)
  15. 解决Ubuntu16.04解压cudnn文件时报错could not create a hard link file
  16. Matrix Operations -- Transpose +Determinant + Adjugate+ Inverse + Gram-Schimidt +LUP + QR + Eigen
  17. 实现虚拟机与板卡/树莓派网络互联,并通过笔记本wifi共享上网
  18. 【实习日志】The last Day总结篇
  19. linux内核漏洞查询,GitHub - F1uYu4n/linux-kernel-exploits: linux-kernel-exploits Linux平台提权漏洞集合...
  20. Netty--菜鸟入门--Netty组件

热门文章

  1. 走近人脸检测:从VJ到深度学习(下)
  2. 教你怎么用WIN7系统自带工具调整硬盘分区
  3. TCP通讯处理粘包详解
  4. [Google Guava] 6-字符串处理:分割,连接,填充
  5. java提高篇(八)----详解内部类
  6. elasticsearch2.3安装以及集群部署
  7. HTML特殊转义字符对照表
  8. Java程序员从笨鸟到菜鸟之(十)枚举,泛型详解
  9. 八成Java开发者解答不了的问题
  10. Python和OpenCV环境配置