由于 I2C 可以控制多从机的属性,设备驱动模型分为  I2C总线设备(类似与Linux里面的I2C适配器) + I2C从设备;

系统I2C设备驱动主要实现 I2C 总线设备驱动,而具体的I2C 从设备的实现则调用I2C总线设备ops

访问 I2C 总线设备

一般情况下 MCU 的 I2C 器件都是作为主机和从机通讯,在 RT-Thread 中将 I2C 主机虚拟为 I2C总线设备,I2C 从机通过 I2C 设备接口和 I2C 总线通讯,相关接口如下所示:

函数 描述
rt_device_find() 根据 I2C 总线设备名称查找设备获取设备句柄
rt_i2c_transfer() 传输数据

使用方式参考官方文档即可,在此不做赘述。

驱动源码分析

i2c_core.c  i2c总线协议控制的核心实现

i2c_dev.c   i2c总线设备框架输线

i2c-bit-ops.c    I/O模拟I2C的驱动实现

drv_soft_i2c.c    I/O模拟I2C的底层硬件支持

先分析 I2C 总线设备注册的流程

在 drv_soft_i2c.c 中

INIT_BOARD_EXPORT(rt_hw_i2c_init);

则 OS 运行时会自启动 rt_hw_i2c_init 进行 模拟I2C 相关硬件IO的初始化

rt_hw_i2c_init -> rt_i2c_bit_add_bus -> rt_i2c_bus_device_register -> rt_i2c_bus_device_device_init -> rt_device_register

初始的配置

#ifdef BSP_USING_I2C1
#define I2C1_BUS_CONFIG \
{ \
.scl = BSP_I2C1_SCL_PIN, \
.sda = BSP_I2C1_SDA_PIN, \
.bus_name = "i2c1", \
}
#endif

这样使用时就可以通过 "i2c1" 来控制从设备了

I2C传输功能源码分析

rt_i2c_transfer -> i2c_bit_xfer

static rt_size_t i2c_bit_xfer(struct rt_i2c_bus_device *bus,
struct rt_i2c_msg msgs[],
rt_uint32_t num)
{
struct rt_i2c_msg *msg;
struct rt_i2c_bit_ops *ops = bus->priv;
rt_int32_t i, ret;
rt_uint16_t ignore_nack; bit_dbg("send start condition\n");
i2c_start(ops);
for (i = ; i < num; i++)
{
msg = &msgs[i];
ignore_nack = msg->flags & RT_I2C_IGNORE_NACK;
if (!(msg->flags & RT_I2C_NO_START)) // 没有RT_I2C_NO_START
{
if (i) // 主要用于读操作
{
i2c_restart(ops);
}
ret = i2c_bit_send_address(bus, msg); //发送器件地址
if ((ret != RT_EOK) && !ignore_nack)
{
bit_dbg("receive NACK from device addr 0x%02x msg %d\n",
msgs[i].addr, i);
goto out;
}
}
if (msg->flags & RT_I2C_RD) //读取数据
{
ret = i2c_recv_bytes(bus, msg);
if (ret >= )
bit_dbg("read %d byte%s\n", ret, ret == ? "" : "s");
if (ret < msg->len)
{
if (ret >= )
ret = -RT_EIO;
goto out;
}
}
else //发送数据
{
ret = i2c_send_bytes(bus, msg);
if (ret >= )
bit_dbg("write %d byte%s\n", ret, ret == ? "" : "s");
if (ret < msg->len)
{
if (ret >= )
ret = -RT_ERROR;
goto out;
}
}
}
ret = i; out:
bit_dbg("send stop condition\n");
i2c_stop(ops); return ret;
}

我们以 24c02 的 读写 来分析 i2C驱动

static rt_size_t at24cxx_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
struct at24cxx_device *at24cxx;
const struct at24cxx_config *cfg;
struct rt_i2c_msg msg[];
rt_uint8_t mem_addr[] = {,};
rt_size_t ret = ;
RT_ASSERT(dev != ); at24cxx = (struct at24cxx_device *) dev; RT_ASSERT(at24cxx->parent.user_data != );
cfg = (const struct at24cxx_config *) at24cxx->parent.user_data; if(pos > cfg->size)
{
return ;
} if(pos + size > cfg->size)
{
size = cfg->size - pos;
} // 写入寻址地址
msg[].addr = cfg->addr;
msg[].flags = cfg->flags | RT_I2C_WR;
if(cfg->size < ) // at24c01 at24c02, 一页8字节,寻址地址8位
{
mem_addr[] = (rt_uint8_t) pos;
msg[].buf = (rt_uint8_t *) mem_addr;
msg[].len = ;
}
else // at24c04/08/16 一页16字节,寻址地址9/10/11位
{
mem_addr[] = (pos >> );
mem_addr[] = (rt_uint8_t) pos;
msg[].buf = (rt_uint8_t *) mem_addr;
msg[].len = ;
} // 使用RT_I2C_NO_START,直接写入buffer数据
msg[].addr = cfg->addr;
msg[].flags = cfg->flags | RT_I2C_WR | RT_I2C_NO_START;
msg[].buf = (rt_uint8_t *) buffer;
msg[].len = size; ret = rt_i2c_transfer(at24cxx->bus, msg, );
return (ret == ) ? size : ;
}
static rt_size_t at24cxx_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
struct at24cxx_device *at24cxx;
const struct at24cxx_config *cfg;
struct rt_i2c_msg msg[];
rt_uint8_t mem_addr[] = {,};
rt_size_t ret = ;
RT_ASSERT(dev != ); at24cxx = (struct at24cxx_device *) dev; RT_ASSERT(at24cxx->parent.user_data != );
cfg = (const struct at24cxx_config *) at24cxx->parent.user_data; if(pos >= cfg->size) //寻址地址超标
{
return ;
} if(pos + size > cfg->size) // size超标
{
size = cfg->size - pos;
} msg[].addr = cfg->addr;
msg[].flags = cfg->flags | RT_I2C_WR;
if(cfg->size < ) // at24c01 at24c02, 一页8字节,寻址地址8位
{
mem_addr[] = (rt_uint8_t) pos;
msg[].buf = (rt_uint8_t *) mem_addr;
msg[].len = ;
}
else // at24c04/08/16 一页16字节,寻址地址9/10/11位
{
mem_addr[] = (pos >> );
mem_addr[] = (rt_uint8_t) pos;
msg[].buf = (rt_uint8_t *) mem_addr;
msg[].len = ;
} msg[].addr = cfg->addr;
msg[].flags = cfg->flags | RT_I2C_RD;
msg[].buf = (rt_uint8_t *) buffer;
msg[].len = size; ret = rt_i2c_transfer(at24cxx->bus, msg, );
return (ret == ) ? size : ; }

可以看到 i2c 读写 EEPROM 通过发送多个 msg 来实现 写寻址地址在进行读写操作,同时通过 RT_I2C_NO_START 使用读写场景

i2C设备应用实例

24c02设备实例代码

#include <rtthread.h>
#include <rtdevice.h>
#include "at24cxx.h" /** at24cxx设备结构体 */
struct at24cxx_device
{
struct rt_device parent;
struct rt_i2c_bus_device *bus;
}; /* RT-Thread device interface */
static rt_err_t at24cxx_init(rt_device_t dev)
{
return RT_EOK;
} static rt_err_t at24cxx_open(rt_device_t dev, rt_uint16_t oflag)
{
return RT_EOK;
} static rt_err_t at24cxx_close(rt_device_t dev)
{
return RT_EOK;
} static rt_err_t at24cxx_control(rt_device_t dev, int cmd, void *args)
{
return RT_EOK;
} /**
* @brief at24cxx设备读操作
* @param[in] dev 设备句柄
* @param[in] pos i2c写寻址地址
* @param[in] *buffer 读出数据的指针
* @param[in] size 读出数据的长度
* @return 返回读出成功的字节数
* - 0 读出失败
* - Others 读出成功的字节数
*/
static rt_size_t at24cxx_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
struct at24cxx_device *at24cxx;
const struct at24cxx_config *cfg;
struct rt_i2c_msg msg[];
rt_uint8_t mem_addr[] = {,};
rt_size_t ret = ;
RT_ASSERT(dev != ); at24cxx = (struct at24cxx_device *) dev; RT_ASSERT(at24cxx->parent.user_data != );
cfg = (const struct at24cxx_config *) at24cxx->parent.user_data; if(pos >= cfg->size) //寻址地址超标
{
return ;
} if(pos + size > cfg->size) // size超标
{
size = cfg->size - pos;
} msg[].addr = cfg->addr;
msg[].flags = cfg->flags | RT_I2C_WR;
if(cfg->size < ) // at24c01 at24c02, 一页8字节,寻址地址8位
{
mem_addr[] = (rt_uint8_t) pos;
msg[].buf = (rt_uint8_t *) mem_addr;
msg[].len = ;
}
else // at24c04/08/16 一页16字节,寻址地址9/10/11位
{
mem_addr[] = (pos >> );
mem_addr[] = (rt_uint8_t) pos;
msg[].buf = (rt_uint8_t *) mem_addr;
msg[].len = ;
} msg[].addr = cfg->addr;
msg[].flags = cfg->flags | RT_I2C_RD;
msg[].buf = (rt_uint8_t *) buffer;
msg[].len = size; ret = rt_i2c_transfer(at24cxx->bus, msg, );
return (ret == ) ? size : ; } /**
* @brief at24cxx设备写操作
* @param[in] dev 设备句柄
* @param[in] pos i2c写寻址地址
* @param[in] *buffer 写入数据的指针
* @param[in] size 写入数据的长度
* @return 返回写入成功的字节数
* - 0 写入失败
* - Others 写入成功的字节数
*/
#if 0 // 连续页写测试
static rt_size_t at24cxx_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
struct at24cxx_device *at24cxx;
const struct at24cxx_config *cfg;
struct rt_i2c_msg msg[];
rt_uint8_t mem_addr[] = {,};
rt_size_t ret = ;
RT_ASSERT(dev != ); at24cxx = (struct at24cxx_device *) dev; RT_ASSERT(at24cxx->parent.user_data != );
cfg = (const struct at24cxx_config *) at24cxx->parent.user_data; if(pos > cfg->size)
{
return ;
} if(pos + size > cfg->size)
{
size = cfg->size - pos;
} /*计算出要写的页数和分页*/
rt_uint8_t NumOfPage = ; // 总页写数,包括写的不完整页
rt_uint8_t Addr = ; // 页写的寻址地址
rt_uint8_t count = ; // 页写入的字节数
rt_uint8_t i; if(size > (PAGE_SIZE - pos%PAGE_SIZE))
{
count = PAGE_SIZE - pos%PAGE_SIZE; // 写入的第一页长度
NumOfPage = (size - count)/PAGE_SIZE; // 剩余写入的完整页数
if((size - (count + NumOfPage*PAGE_SIZE)) > )
NumOfPage = NumOfPage + ;
else
NumOfPage = NumOfPage + ;
}
else
{
NumOfPage = ;
} for(i=; i<NumOfPage; i++)
{
if(i == )
{
Addr = pos;
if(NumOfPage == )
{
count = size;
}
else
{
count = PAGE_SIZE - pos%PAGE_SIZE;
}
}
else if((i == NumOfPage-) && (NumOfPage > ))
{
Addr = pos - pos%PAGE_SIZE + i*PAGE_SIZE;
count = pos + size - Addr;
}
else
{
Addr = pos - pos%PAGE_SIZE + i*PAGE_SIZE;
count = PAGE_SIZE;
} // 写入寻址地址
msg[].addr = cfg->addr;
msg[].flags = cfg->flags | RT_I2C_WR;
if(cfg->size < ) // at24c01 at24c02, 一页8字节,寻址地址8位
{
mem_addr[] = (rt_uint8_t) Addr;
msg[].buf = (rt_uint8_t *) mem_addr;
msg[].len = ;
}
else // at24c04/08/16 一页16字节,寻址地址9/10/11位
{
mem_addr[] = (Addr >> );
mem_addr[] = (rt_uint8_t) Addr;
msg[].buf = (rt_uint8_t *) mem_addr;
msg[].len = ;
} // 使用RT_I2C_NO_START,直接写入buffer数据
msg[].addr = cfg->addr;
msg[].flags = cfg->flags | RT_I2C_WR | RT_I2C_NO_START;
msg[].buf = (rt_uint8_t *) buffer+count;
msg[].len = count; ret = rt_i2c_transfer(at24cxx->bus, msg, );
if(ret != )
return ;
}
return count;
}
#endif #if 1 // 不支持连续页写
static rt_size_t at24cxx_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
struct at24cxx_device *at24cxx;
const struct at24cxx_config *cfg;
struct rt_i2c_msg msg[];
rt_uint8_t mem_addr[] = {,};
rt_size_t ret = ;
RT_ASSERT(dev != ); at24cxx = (struct at24cxx_device *) dev; RT_ASSERT(at24cxx->parent.user_data != );
cfg = (const struct at24cxx_config *) at24cxx->parent.user_data; if(pos > cfg->size)
{
return ;
} if(pos + size > cfg->size)
{
size = cfg->size - pos;
} // 写入寻址地址
msg[].addr = cfg->addr;
msg[].flags = cfg->flags | RT_I2C_WR;
if(cfg->size < ) // at24c01 at24c02, 一页8字节,寻址地址8位
{
mem_addr[] = (rt_uint8_t) pos;
msg[].buf = (rt_uint8_t *) mem_addr;
msg[].len = ;
}
else // at24c04/08/16 一页16字节,寻址地址9/10/11位
{
mem_addr[] = (pos >> );
mem_addr[] = (rt_uint8_t) pos;
msg[].buf = (rt_uint8_t *) mem_addr;
msg[].len = ;
} // 使用RT_I2C_NO_START,直接写入buffer数据
msg[].addr = cfg->addr;
msg[].flags = cfg->flags | RT_I2C_WR | RT_I2C_NO_START;
msg[].buf = (rt_uint8_t *) buffer;
msg[].len = size; ret = rt_i2c_transfer(at24cxx->bus, msg, );
return (ret == ) ? size : ;
}
#endif #ifdef RT_USING_DEVICE_OPS
/** at24cxx设备操作ops */
const static struct rt_device_ops at24cxx_ops =
{
at24cxx_init,
at24cxx_open,
at24cxx_close,
at24cxx_read,
at24cxx_write,
at24cxx_control
};
#endif /**
* @brief at24cxx设备注册
* @param[in] *fm_device_name 设备名称
* @param[in] *i2c_bus i2c总线设备名称
* @param[in] *user_data 用户数据 at24cxx_config
* @return 函数执行结果
* - RT_EOK 执行成功
* - Others 失败
*/
rt_err_t at24cxx_register(const char *fm_device_name, const char *i2c_bus, void *user_data)
{
static struct at24cxx_device at24cxx_drv;
struct rt_i2c_bus_device *bus; bus = rt_i2c_bus_device_find(i2c_bus);
if (bus == RT_NULL)
{
return RT_ENOSYS;
} at24cxx_drv.bus = bus;
at24cxx_drv.parent.type = RT_Device_Class_Block;
#ifdef RT_USING_DEVICE_OPS
at24cxx_drv.parent.ops = &at24cxx_ops;
#else
at24cxx_drv.parent.init = at24cxx_init;
at24cxx_drv.parent.open = at24cxx_open;
at24cxx_drv.parent.close = at24cxx_close;
at24cxx_drv.parent.read = at24cxx_read;
at24cxx_drv.parent.write = at24cxx_write;
at24cxx_drv.parent.control = at24cxx_control;
#endif at24cxx_drv.parent.user_data = user_data; return rt_device_register(&at24cxx_drv.parent, fm_device_name, RT_DEVICE_FLAG_RDWR);
}
/** at24cxx设备用户操作配置结构体 */
struct at24cxx_config
{
rt_uint32_t size; //设备的总容量
rt_uint16_t addr; //设备地址
rt_uint16_t flags; //I2C操作标志
}; /**
* @brief at24cxx设备注册
* @param[in] *fm_device_name 设备名称
* @param[in] *i2c_bus i2c总线设备名称
* @param[in] *user_data 用户数据 at24cxx_config
* @return 函数执行结果
* - RT_EOK 执行成功
* - Others 失败
*/
extern rt_err_t at24cxx_register(const char *e2m_device_name, const char *i2c_bus, void *user_data);

24c02设备驱动使用示例

static struct at24cxx_config at24c02_config =
{
.size = , // 容量,单位字节
.addr = 0x50, // 注意该地址为没有移位之前的地址不是0xA0
.flags = ,
}; static void at24c02_sample(int argc, char *argv[])
{
rt_err_t ret;
rt_uint8_t test_data[] = {0x12, 0x34, 0x56, 0x78};
rt_uint8_t tmp_data[]; // memset(test_data, 0x55, 100); ret = at24cxx_register("at24c02", "i2c1", &at24c02_config); rt_device_t at24c02_dev = rt_device_find("at24c02");
if (at24c02_dev == RT_NULL)
{
rt_kprintf("at24c02 sample run failed! can't find %s device!\n", "at24c02");
return;
}
rt_device_open(at24c02_dev, RT_DEVICE_FLAG_RDWR); ret = rt_device_write(at24c02_dev, , test_data, );
if(ret != )
{
rt_kprintf("at24c02 write error %d\n", ret);
}
rt_thread_mdelay();
ret = rt_device_read(at24c02_dev, , tmp_data, );
if(ret != )
{
rt_kprintf("at24c02 read error %d\n", ret);
}
else
{
rt_kprintf("at24c02 read data %02x %02x %02x %02x\n", tmp_data[], tmp_data[], tmp_data[], tmp_data[]);
}
}

RT-Thread 设备驱动I2C浅析及使用的更多相关文章

  1. RT thread 设备驱动组件之USART设备

    本文以stm32f4xx平台介绍串口驱动,主要目的是:1.RTT中如何编写中断处理程序:2.如何编写RTT设备驱动接口代码:3.了解串行设备的常见处理机制.所涉及的主要源码文件有:驱动框架文件(usa ...

  2. RT-Thread 设备驱动SPI浅析及使用

    OS版本:RT-Thread 4.0.0 测试BSP:STM32F407 SPI简介 SPI总线框架其实和I2C差不多,可以说都是总线设备+从设备,但SPI设备的通信时序配置并不固定,也就是说控制特定 ...

  3. linux设备驱动模型-浅析-转

    1.  typeof typeof并非ISO C的关键字,而是gcc对C的一个扩展.typeof是一个关键字(类似sizeof),用于获取一个表达式的类型. 举个简单的例子: char tt; typ ...

  4. RT-Thread 设备驱动UART浅析

    OS版本:RT-Thread 4.0.0 芯片:STM32F407 RT-Thread的串口驱动框架与Linux相识,分成 I/O设备框架 + 设备底层驱动: 1. serial设备初始化及使用 将配 ...

  5. RT-Thread 设备驱动ADC浅析与改进

    OS版本:RT-Thread 4.0.0 芯片:STM32F407 下面时官方ADC提供的参考访问接口 访问 ADC 设备 应用程序通过 RT-Thread 提供的 ADC 设备管理接口来访问 ADC ...

  6. 乾坤合一~Linux设备驱动之I2C核心、总线以及设备驱动

    我思念的城市已是黄昏 为何我总对你一往情深 曾经给我快乐 也给我创伤 曾经给我希望 也给我绝望 我在遥远的城市 陌生的人群 感觉着你遥远的忧伤 我的幻想 你的忧伤,像我的的绝望,那样漫长,,,,,这是 ...

  7. Linux I2C核心、总线和设备驱动

    目录 更新记录 一.Linux I2C 体系结构 1.1 Linux I2C 体系结构的组成部分 1.2 内核源码文件 1.3 重要的数据结构 二.Linux I2C 核心 2.1 流程 2.2 主要 ...

  8. LinuxI2C核心、总线驱动与设备驱动

    I2C体系结构分为三个部分:I2C核心.总线驱动.设备驱动 I2C核心: I2C核心提供了一组不依赖硬件的接口函数,I2C总线驱动和设备驱动之间依赖于I2C核心作为纽带 (1)增加/删除i2c_ada ...

  9. RT Thread的SPI设备驱动框架的使用以及内部机制分析

    注释:这是19年初的博客,写得很一般,理解不到位也不全面.19年末得空时又重新看了RTThread的SPI和GPIO,这次理解得比较深刻.有时间时再整理上传. -------------------- ...

随机推荐

  1. 关于vuex自己理解的三幅图

  2. 洛谷 2176 [USACO14FEB]路障Roadblock

    [题意概述] 修改图中任一一条边的边权,使其加倍,问怎样使修改后图中的1~n的最短路最大.输出最短路的增量. [题解] 先跑一遍dijkstra求出1~n的路径长度,记录下经过的边.枚举这些边进行修改 ...

  3. 洛谷 1071 潜伏者(NOIp2009提高组)

    [题意概述] 给出三行字符串,前两行代表密码与明文的对应关系,第三行为待翻译的文本.要求按照对应关系翻译文本. [题解] 直接模拟即可. 注意判断Failed的情况. #include<cstd ...

  4. idea中找不到maven projects的集中解决办法

    今天正常打开idea,却发现maven窗口找不到了:试了这些方法 首先idea自带了maven控件,不像Eclipse还需要下载控件,如果你以前有maven在右边,出于某种原因,消失找不到 了,你可以 ...

  5. python之MD5、base64\base32解密

    # -*- coding:utf-8 -*- import hashlib import base64 # 求最大公约数gys # def gys(m, n): # c = 1 # while(c ! ...

  6. hdu 5015 矩阵快速幂(可用作模板)

    转载:http://blog.csdn.net/wdcjdtc/article/details/39318847 之前各种犯傻 推了好久这个东西.. 后来灵关一闪  就搞定了.. 矩阵的题目,就是构造 ...

  7. jQuery对象是怎么创建的

    一.jQuery源码 在jQuery中,$是jQuery的别名,执行“$()”就是执行“jQuery()”,执行“$()”返回的是一个jQuery对象,在源码中,它是这样定义的: ... var jQ ...

  8. Java使用JNative调用DLL库

    编写测试DLL文件,源文件参照JNA调用用的DLL文件.地址:http://www.cnblogs.com/vevy/p/9076941.html (很重要)右键项目 --> 属性 --> ...

  9. SVM学习(续)核函数 & 松弛变量和惩罚因子

    SVM的文章可以看:http://www.cnblogs.com/charlesblc/p/6193867.html 有写的最好的文章来自:http://www.blogjava.net/zhenan ...

  10. ExtJs--13-- Ext.apply(src,apply) 和 Ext.applyIf(src,apply) 两个方法的使用和差别比較

    Ext.onReady(function(){ /* * Ext.apply(src,apply) 和 Ext.applyIf(src,apply) 两个方法的使用和差别比較 */ //Ext.app ...