你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

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

[复制链接]
andeyqi 发布时间:2019-5-16 13:23
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框架。如下链接是之前写的自己的认识。https://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


I2C_接口进入_Busy_状态不能退出.pdf (43.75 KB, 下载次数: 2)
收藏 2 评论25 发布时间:2019-5-16 13:23

举报

25个回答
天臆弄人 回答时间:2019-5-21 08:08:17
我单片机模拟个IIC 不要 30行代码,用你这 我不得 300行吗
MrJiu 回答时间:2019-5-16 13:33:34
凑合吧!!!
andeyqi 回答时间:2019-5-16 13:38:05

就当自己理解代码的时候留个笔记,回头再看的时候也能找到个记录,登不了啥大雅之堂
MrJiu 回答时间:2019-5-17 09:51:51
andeyqi 发表于 2019-5-16 13:38
就当自己理解代码的时候留个笔记,回头再看的时候也能找到个记录,登不了啥大雅之堂 ...

具体代买我还没看,因为我自己得I2C模拟意见到了无可匹敌得地步。。。当然了,也有可能我和他得思想一样!!!
3111272 回答时间:2019-5-17 10:48:37
借牛头哥的宝地,也把我前面写的RTT IIC笔记在这贴个链接,我的是基于LM75传感器写的
http://www.rt-thread.org/qa/thread-10280-1-1.html
andeyqi 回答时间:2019-5-17 13:49:59
3111272 发表于 2019-5-17 10:48
借牛头哥的宝地,也把我前面写的RTT IIC笔记在这贴个链接,我的是基于LM75传感器写的
http://www.rt-threa ...

写的不错,看了你的文章,早点发现就不自己分析了。
andeyqi 回答时间:2019-5-17 13:52:59
MrJiu 发表于 2019-5-17 09:51
具体代买我还没看,因为我自己得I2C模拟意见到了无可匹敌得地步。。。当然了,也有可能我和他得思 ...

里面的SDA被拉低了,IIC总线被锁了,连续拉高拉低SCL线九个周期的解锁总线的是什么原理,了解吗大牛,求指点江山。
MrJiu 回答时间:2019-5-17 14:24:11
andeyqi 发表于 2019-5-17 13:52
里面的SDA被拉低了,IIC总线被锁了,连续拉高拉低SCL线九个周期的解锁总线的是什么原理,了解吗大牛,求 ...

其实主要是看从机得搭配。。。至于SCL9个周期解锁总线,是有点扯蛋。。。估计是想搞个应答吧!!但是从机不配合,也没卵用!!!
andeyqi 回答时间: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从设备就可以完成被挂起的读操作,从死锁状态中恢复过来。“
123下一页

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版