由于 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. ndk编译libpcap 1.7.4(最终解决方法)

    具体方法和测试见上一篇. 后来才知道,LIBPCAP本来就写了几套获取网卡的函数,这样就简单了.和上篇一样,把那5个文件从LINUX下托下来. 上篇忘了说了,还要将LINUX中/usr/include ...

  2. 洛谷 4364 [九省联考2018]IIIDX

    [题解] 一眼可以想到一个类似二叉树后序遍历的贪心做法,然而这个做法在有相同数字的情况下是错误的.最简单的反例就是n=4,d={1,1,1,2},正解是1,1,2,1,而贪心是1,1,1,2. 所以这 ...

  3. 【Codeforces 522B】Photo to Remember

    [链接] 我是链接,点我呀:) [题意] 题意 [题解] 模拟题.用set模拟下就好 [代码] import java.io.*; import java.util.*; public class M ...

  4. Contemplation! Algebra 矩阵快速幂

    Given the value of a+b and ab you will have to find the value of a n + b n Input The input file cont ...

  5. Eureka集群的那些坑

    今天遇到一个Eureka集群的一个坑. 问题现场类似是这样的:两台Eureka组成的服务注册中心集群,两台服务提供方server1.server2,两个服务调用方client1.client2. 正常 ...

  6. Ubuntu 16.04安装PDF阅读器FoxitReader

    下载: https://www.foxitsoftware.cn/downloads/ 安装: tar zxvf FoxitReader2.4.1.0609_Server_x64_enu_Setup. ...

  7. SQL Server memory – Internals

    https://blogs.msdn.microsoft.com/karthick_pk/2013/03/16/sql-server-memory-internals/

  8. subclipse 和 eclipse结合遇到的问题

    subclipse是eclipse的一个SVN插件.但是我在使用的时候不断的报出下面的错误: the applet is attempting to access the "exists&q ...

  9. Windows 注册表常用操作

    1 添加一个主键(比如在HKEY_LOCAL_MACHINE\SOFTWARE\中添加一个ABCEDFGHIJKLMN主键) Windows Registry Editor Version 5.00 ...

  10. iOS常用的宏定义总结

    字符串是否为空 1   #define kStringIsEmpty(str) ([str isKindOfClass:[NSNull class]] || str == nil || [str le ...