由于 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. slf4j-api、slf4j-log4j12、log4j的关系

    在网上找到一篇关于这三个jar包的关系的博客,讲的很好,所以就转载了: https://blog.csdn.net/tengdazhang770960436/article/details/18006 ...

  2. Linux修改系统时间与时区

    GMT  (Greewich Mean Time) 格林威治标准时间:GMT是老的时间计量标准,根据地球的自转和公转来计算时间,也就是太阳每天经过位于英国伦敦郊区的皇家格林尼治天文台的标准时间就是中午 ...

  3. 利用WebUploader进行图片批量上传,在页面显示后选择多张图片压缩至指定路径【java】

    WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件.在现代的浏览器里面能充分发挥HTML5的优势,同时又不摒弃主流IE浏览 ...

  4. 关于图片预览使用base64在chrome上的性能问题解决方法

    在开发后台上传图片的功能时候使用base64预览图片,结果在传入大量图片后导致chrome崩溃,代码如下 var img = new Image(); var render = new FileRea ...

  5. Django-REST-Framework JWT 实现SSO认证(下)

    在上一篇博客中,我已经对JSON Web 认证做了简单的解释,这篇博客是续篇,若不了解,请看上一篇博客:https://www.cnblogs.com/yushenglin/p/10863184.ht ...

  6. BZOJ 2806 Luogu P4022 [CTSC2012]Cheat (广义后缀自动机、DP、二分、单调队列)

    题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=2806 (luogu) https://www.luogu.org/pro ...

  7. 联想YOGA3一键恢复系统教程

  8. POJ2481:Cows(树状数组)

    Description Farmer John's cows have discovered that the clover growing along the ridge of the hill ( ...

  9. CF 445B(DZY Loves Chemistry-求连通块)

    B. DZY Loves Chemistry time limit per test 1 second memory limit per test 256 megabytes input standa ...

  10. 分布式消息服务DMS如何实现死信消息的消费

    本文部分内容节选自华为云帮助中心的分布式消息服务(DMS)服务的产品介绍 死信消息是什么 死信消息是指无法被正常消费的消息.分布式消息服务DMS支持对消息进行异常处理.当消息进行多次重复消费仍然失败后 ...