请选择 进入手机版 | 继续访问电脑版
搜索
查看: 9430|回复: 26

[原创] 【基于stm32的rt-thread IIC 驱动框架代码阅读】

  [复制链接]

该用户从未签到

34

主题

1426

帖子

26

蝴蝶豆

论坛元老

最后登录
2019-10-23
发表于 2019-5-16 13:23:26 | 显示全部楼层 |阅读模式
本帖最后由 andeyqi 于 2019-6-21 11:50 编辑

rt-thread 4.0.1版本已经集成了基于stm32 IIC驱动框架,美中不足的是官方代码的驱动框架是基于模拟IIC的方式实现的,要是集成的是硬件的方式就更棒了,相信不久以后的RTT会有的,主要涉及的文件如下,以下指示个人的一点理解,理解的有不对的地方欢迎各位拍砖。

\rt-thread\components\drivers\i2c\i2c_dev.c
\rt-thread\components\drivers\i2c\i2c_core.c
\rt-thread\components\drivers\i2c\i2c-bit-ops.c
\rt-thread\bsp\stm32\libraries\HAL_Drivers\drv_soft_i2c.c
我们由下而上的了解熟悉各个文件的作用:
①drv_soft_i2c.c:提供BSP相关的硬件操作函数,操作IIC 的SDA,和SCL总线电平,提供上层模拟IIC总线使用完成IIC信号的物理层操作。
向上层提供的操作函数如下:
  1. struct rt_i2c_bit_ops
  2. {
  3.     void *data;            /* private data for lowlevel routines */
  4.     void (*set_sda)(void *data, rt_int32_t state);
  5.     void (*set_scl)(void *data, rt_int32_t state);
  6.     rt_int32_t (*get_sda)(void *data);
  7.     rt_int32_t (*get_scl)(void *data);

  8.     void (*udelay)(rt_uint32_t us);

  9.     rt_uint32_t delay_us;  /* scl and sda line delay */
  10.     rt_uint32_t timeout;   /* in tick */
  11. };
复制代码
上述的void *data;指向如下的内部私有数据,配置设置对应硬件的ID,和向上注册的总线name。rt-thread将引脚的抽象成对应芯片封装的引脚号,具体再次不具体讲述,感兴趣可以查看RTT的gpio框架。如下链接是之前写的自己的认识。http://www.stmcu.org.cn/module/forum/thread-616950-1-1.html
  1. /* stm32 config class */
  2. struct stm32_soft_i2c_config
  3. {
  4.     rt_uint8_t scl;
  5.     rt_uint8_t sda;
  6.     const char *bus_name;
  7. };
复制代码
该文件的入口函数如下:该函数会在系统启动完成后自动调用。
stm32_i2c_gpio_init==》设置GPIO默认电平总线空闲状态,SDA,SCL为高电平。
rt_i2c_bit_add_bus==》调用(\rt-thread\components\drivers\i2c\i2c-bit-ops.c)中的函数,向上注册驱动层IO操作函数,注册的结构如下,其中的priv成员指向底层的IO操作函数完成总线设备和底层的IO操作函数的绑定,从而上层使用rt_i2c_bus_device 对应的对象就能完成对总线的操作。
  1. /*for i2c bus driver*/
  2. struct rt_i2c_bus_device
  3. {
  4.     struct rt_device parent;
  5.     const struct rt_i2c_bus_device_ops *ops;
  6.     rt_uint16_t  flags;
  7.     rt_uint16_t  addr;
  8.     struct rt_mutex lock;
  9.     rt_uint32_t  timeout;
  10.     rt_uint32_t  retries;
  11.     void *priv;
  12. };
复制代码

stm32_i2c_bus_unlock==》按照注释是解锁总线,具体的原理现在还没有搞明白什么情况下总线会被锁状态,希望路过有了解的留下脚印讲解下。
  1. /* I2C initialization function */
  2. int rt_hw_i2c_init(void)
  3. {
  4. ......
  5.         stm32_i2c_gpio_init(&i2c_obj[i]);
  6.         result = rt_i2c_bit_add_bus(&i2c_obj[i].i2c2_bus, soft_i2c_config[i].bus_name);
  7.         RT_ASSERT(result == RT_EOK);
  8.         stm32_i2c_bus_unlock(&soft_i2c_config[i]);
  9. ......
  10. }
  11. INIT_BOARD_EXPORT(rt_hw_i2c_init);
复制代码

②i2c-bit-ops.c:封装底层提供的IO操作函数,实现IIC协议层相关信号操作,提供IIC core核心层对应的操作函数,向IIC core提供的操作方法如下。
  1. struct rt_i2c_bus_device_ops
  2. {
  3.     rt_size_t (*master_xfer)(struct rt_i2c_bus_device *bus,
  4.                              struct rt_i2c_msg msgs[],
  5.                              rt_uint32_t num);
  6.     rt_size_t (*slave_xfer)(struct rt_i2c_bus_device *bus,
  7.                             struct rt_i2c_msg msgs[],
  8.                             rt_uint32_t num);
  9.     rt_err_t (*i2c_bus_control)(struct rt_i2c_bus_device *bus,
  10.                                 rt_uint32_t,
  11.                                 rt_uint32_t);
  12. };
复制代码

该文件中只实现master_xfer函数,另外两个函数指针设置为NULL。
  1. static const struct rt_i2c_bus_device_ops i2c_bit_bus_ops =
  2. {
  3.     i2c_bit_xfer,
  4.     RT_NULL,
  5.     RT_NULL
  6. };
复制代码
i2c_bit_xfer 函数我们可以看出,该函数只是只是对底层提供函数的操作的进一步封住,提供核心层(core)使用,struct rt_i2c_bit_ops *ops = bus->priv 取出底层的函数来按照IIC协议来发送数据
  1. static rt_size_t i2c_bit_xfer(struct rt_i2c_bus_device *bus,
  2.                               struct rt_i2c_msg         msgs[],
  3.                               rt_uint32_t               num)
  4. {
  5.     struct rt_i2c_msg *msg;
  6.     struct rt_i2c_bit_ops *ops = bus->priv;
  7. ......
  8.     i2c_start(ops);
  9. ......
  10.     i2c_stop(ops);

  11.     return ret;
  12. }
复制代码
③i2c_core.c:抽象通用的IIC操作函数供应用层使用,调用i2c-bit-ops.c注册的函数指针master_xfer 完成数据的处理
  1. rt_size_t rt_i2c_transfer(struct rt_i2c_bus_device *bus,
  2.                           struct rt_i2c_msg         msgs[],
  3.                           rt_uint32_t               num)
  4. {
  5.     rt_size_t ret;

  6.     if (bus->ops->master_xfer)
  7.     {
  8. #ifdef RT_I2C_DEBUG
  9.         for (ret = 0; ret < num; ret++)
  10.         {
  11.             i2c_dbg("msgs[%d] %c, addr=0x%02x, len=%d\n", ret,
  12.                     (msgs[ret].flags & RT_I2C_RD) ? 'R' : 'W',
  13.                     msgs[ret].addr, msgs[ret].len);
  14.         }
  15. #endif

  16.         rt_mutex_take(&bus->lock, RT_WAITING_FOREVER);
  17.         ret = bus->ops->master_xfer(bus, msgs, num);
  18.         rt_mutex_release(&bus->lock);

  19.         return ret;
  20.     }
  21.     else
  22.     {
  23.         i2c_dbg("I2C bus operation not supported\n");

  24.         return 0;
  25.     }
  26. }
复制代码

④i2c_dev.c 文件只是对core层文件的简单处理,BUS注册到驱动的IO设备层,供用户调用使用。

框架的注册层级关系如下:
  1. 注册流程:
  2. rt_hw_i2c_init(void)
  3. ==>>rt_i2c_bit_add_bus()
  4.          ==>rt_i2c_bus_device_register()
  5.                ==>rt_i2c_bus_device_device_init()
  6.                       ==>rt_device_register()
  7. 调用关系:
  8. rt_device_find()
  9. i2c_bus_device_read()
  10. ==>rt_i2c_master_recv()
  11.      ==>rt_i2c_transfer()
  12.            ==>  bus->ops->master_xfer(bus, msgs, num)<==>i2c_bit_xfer(bus, msgs, num)
复制代码

rt.png

1.PNG

编译时选上IIC,启动后就会发现设备列表里就出现了我们注册的IIC设备了。
iic1.png

上述解索的原因在万能的度上找到答案了贴出来和大家分享下:
在I2C主设备中增加I2C总线恢复程序。
每次I2C主设备复位后,如果检测到SDA数据线被拉低,则控制I2C中的SCL时钟线产生9个时钟脉冲(针对8位数据的情况,“9个clk可以激活”的方法来自NXP的文档,NXP(Philips)作为I2C总线的鼻祖,这样的说法是可信的),这样I2C从设备就可以完成被挂起的读操作,从死锁状态中恢复过来


最后推荐两本RT-THREAD的书籍仅供参考:
一本是官方出品的:《嵌入式实时操作系统——RT-Thread设计与实现》
book1.png
和之前论坛活动送的野火的书《RT-Thread内核实现与应用开发实战指南》
book2.png



回复

使用道具 举报

该用户从未签到

7

主题

129

帖子

59

蝴蝶豆

高级会员

最后登录
2019-10-23
发表于 2019-5-21 08:08:17 | 显示全部楼层
我单片机模拟个IIC 不要 30行代码,用你这 我不得 300行吗
回复 支持 1 反对 0

使用道具 举报

该用户从未签到

3

主题

957

帖子

352

蝴蝶豆

版主

最后登录
2019-10-23
发表于 2019-5-16 13:33:34 | 显示全部楼层
凑合吧!!!
回复

使用道具 举报

该用户从未签到

34

主题

1426

帖子

26

蝴蝶豆

论坛元老

最后登录
2019-10-23
 楼主| 发表于 2019-5-16 13:38:05 | 显示全部楼层

就当自己理解代码的时候留个笔记,回头再看的时候也能找到个记录,登不了啥大雅之堂
回复 支持 反对

使用道具 举报

该用户从未签到

3

主题

957

帖子

352

蝴蝶豆

版主

最后登录
2019-10-23
发表于 2019-5-17 09:51:51 | 显示全部楼层
andeyqi 发表于 2019-5-16 13:38
就当自己理解代码的时候留个笔记,回头再看的时候也能找到个记录,登不了啥大雅之堂 ...

具体代买我还没看,因为我自己得I2C模拟意见到了无可匹敌得地步。。。当然了,也有可能我和他得思想一样!!!
回复 支持 反对

使用道具 举报

该用户从未签到

2

主题

239

帖子

19

蝴蝶豆

金牌会员

最后登录
2019-10-23
发表于 2019-5-17 10:48:37 | 显示全部楼层
借牛头哥的宝地,也把我前面写的RTT IIC笔记在这贴个链接,我的是基于LM75传感器写的
https://www.rt-thread.org/qa/thread-10280-1-1.html
回复 支持 反对

使用道具 举报

该用户从未签到

34

主题

1426

帖子

26

蝴蝶豆

论坛元老

最后登录
2019-10-23
 楼主| 发表于 2019-5-17 13:49:59 | 显示全部楼层
3111272 发表于 2019-5-17 10:48
借牛头哥的宝地,也把我前面写的RTT IIC笔记在这贴个链接,我的是基于LM75传感器写的
https://www.rt-threa ...

写的不错,看了你的文章,早点发现就不自己分析了。
回复 支持 反对

使用道具 举报

该用户从未签到

34

主题

1426

帖子

26

蝴蝶豆

论坛元老

最后登录
2019-10-23
 楼主| 发表于 2019-5-17 13:52:59 | 显示全部楼层
MrJiu 发表于 2019-5-17 09:51
具体代买我还没看,因为我自己得I2C模拟意见到了无可匹敌得地步。。。当然了,也有可能我和他得思 ...

里面的SDA被拉低了,IIC总线被锁了,连续拉高拉低SCL线九个周期的解锁总线的是什么原理,了解吗大牛,求指点江山。
回复 支持 反对

使用道具 举报

该用户从未签到

3

主题

957

帖子

352

蝴蝶豆

版主

最后登录
2019-10-23
发表于 2019-5-17 14:24:11 | 显示全部楼层
andeyqi 发表于 2019-5-17 13:52
里面的SDA被拉低了,IIC总线被锁了,连续拉高拉低SCL线九个周期的解锁总线的是什么原理,了解吗大牛,求 ...

其实主要是看从机得搭配。。。至于SCL9个周期解锁总线,是有点扯蛋。。。估计是想搞个应答吧!!但是从机不配合,也没卵用!!!
回复 支持 反对

使用道具 举报

该用户从未签到

34

主题

1426

帖子

26

蝴蝶豆

论坛元老

最后登录
2019-10-23
 楼主| 发表于 2019-5-20 23:12:20 | 显示全部楼层
MrJiu 发表于 2019-5-17 14:24
其实主要是看从机得搭配。。。至于SCL9个周期解锁总线,是有点扯蛋。。。估计是想搞个应答吧!!但是从机 ...

网上搜索有这么解释的,应该是IIC协议上的规定,感觉应该是靠谱的;
”每次I2C主设备复位后,如果检测到SDA数据线被拉低,则控制I2C中的SCL时钟线产生9个时钟脉冲(针对8位数据的情况,“9个clk可以激活”的方法来自NXP的文档,NXP(Philips)作为I2C总线的鼻祖,这样的说法是可信的),这样I2C从设备就可以完成被挂起的读操作,从死锁状态中恢复过来。“
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

小黑屋|手机版|Archiver|论坛-意法半导体STM32/STM8技术社区

GMT+8, 2019-10-24 03:19 , Processed in 0.088114 second(s), 23 queries , MemCache On.

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表