当前位置: 首页 > news >正文

《嵌入式驱动(十一):I2C子系统架构》

一、通信协议

        I2c,是一种硬件设备常用的通信方式。

        1. 几种通信形式的比较

**tips:

        串行:硬件结构简单、低速通信、超高速通信

        并行:硬件结构复杂、高速通信

        同步:通信双方时钟保持一致

        异步:双方各自拥有独立的时钟信号

        全双工:有收发俩根线

        半双工:只有一根线,不能同时收发

                1) UART:串行,异步,全双工通信;多台不同终端设备间的通信;波特率为115200时,速率为10k(一位起始信号+8位数据位+1位停止位);

                可以通过MAX232和MAX485芯片进行增压,实现远距离传输。

                RS232点对点(一对一),主要用于上位机和下位机的通信

                RS485总线(一对多)

                2) I2C:串行,同步,半双工通信;同一硬件平台,不同芯片间的通信;数据量较少时使用I2C(传感器);100k、400k、1M;

                3) SPI:串行,同步,全双工通信;同一硬件平台,不同芯片间的通信;数据量较大时使用SPI(存储、显示屏);1M - 十几M;

                4)速率更快的方式;并行传输;USB;网口(千M);

        2. I2C与SPI之间的区别

                1)通信速率:
SPI  > I2C
2. I2C占用的硬件资源较少
1) I2c需要SCL、SDA、GND
2) SPI占用的硬件资源较多SCL、MOSI、MISO、CS、 GND
3) SPI全双工、I2C半双工

        3. I2C基础知识

                        1)I2C有几根线?
SCL、SDA、GND

                        2)I2C硬件接线有哪些需要注意事项?
I2C SCL和SDA工作在开漏模式,必须加上拉电阻(1k - 10k,常用的有4.7k、10k)

                        3) 软件I2C还是硬件I2C?
拿GPIO模拟I2C时序,称为软件I2C;使用I2c控制器生成I2C时序,称为硬件I2C (我们使用硬件I2C)

                        4)IMX6uLL有几个I2c控制器?
4个I2C控制器

                        5)I2C总线传输?
通信时只支持一主多从,理论上支持多主多从

                        6) I2C通信时序?
主机提供SCL时钟信号
I2C通信只能由主机发起
一次I2C通信方向不能改变
起始信号:SCL为高,SDA由高到低
结束信号:SCL为高,SDA由低到高
发送数据信号:
SCL为高,SDA采样;SCL为低,SDA变化
I2C通信每传输1个字节,需要发送ACK/NACK

                        7) I2C总线上面可以挂多少个设备?
每个设备都要有从机地址,从机地址是7位, 2^7 -1 (有一位是广播地址)  127个从机

                        8) I2C通信过程

                                写1个字节 

                                写多个字节

                                读1个字节

                                读多个字节

                        9) 基于I2c UserSpace空间实现传感器读取:
I2c控制器驱动由芯片厂商已经写好了,所以我们编写应用层代码

                        10)基于I2c子系统编写 设备驱动:
编写I2c子系统驱动框架分为3个大的功能结构:
I2c控制器驱动(i2c_adapter),NXP已经写好,基于platform总线结构编写的
I2c核心层 (i2c_bus_type),Linux系统编写
I2c设备驱动(i2c_client),由我们编写,基于i2c子系统架构编写

二、连接方式

三、函数接口

        1.ioctl(fd, I2C_SLAVE, addr)
将指定的 I2C 设备文件描述符(fd)绑定到特定从机地址(addr),后续的读写操作均针对该地址设备,使用前需确保已包含 <linux/i2c-dev.h> 头文件。

        2.i2c_add_driver 向I2c子系统注册驱动,并触发设备匹配流程

        i2c_add_driver(driver);
3.i2c_del_driver 注销I2c驱动,释放资源并解除设备绑定

        void i2c_del_driver(struct i2c_driver *driver);
4.i2c_transfer 执行单次或多次I2C消息传输,适用于复杂时序操作

        int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
5.i2c_master_send 封装单次写操作,适用于简单数据发送

        int i2c_master_send(const struct i2c_client *client, const char *buf, int count);
6.i2c_master_recv 封装单次读操作,适用于简单数据读取

        int i2c_master_recv(const struct i2c_client *client, char *buf, int count);

四、步骤

  1. I2C驱动框架介绍(I2C子系统)

                i2c_hardware层:具体硬件层(I2C传感器)

                i2c_adapter:i2c适配器层,驱动厂家实现

                i2c_core:i2c核心层,提供i2c设备和驱动的注册、匹配及通信方法

                i2c_client/i2c_driver:遵循platform设备驱动分离思想,匹配成功之后执行probe函数

        I2C传感器挂载在对应的I2C总线上,I2C适配器层驱动由芯片厂家实现,I2C核心层用来管理I2C设备和驱动的注册、匹配以及I2C通信方法。设备驱动层通过将i2c总线上的设备和驱动匹配,执行probe函数。应用层调用驱动层接口实现i2c的读取写入

        2.I2C子系统中,device和driver匹配的过程

                I2c子系统中主要分为三部分:设备I2c_device、总线I2c_bus_type、驱动I2c_driver,总线中有一个match回调函数能够对设备与驱动进行匹配,匹配方法:
(1)根据设备树中的compatible字段与驱动中的of_match_table匹配

        (2)ACPI匹配,常用于X86平台

        (3)驱动中id_table与设备名匹配

        3.流程

         I2c控制器驱动(NXP写好),我们要写的是 I2c设备驱动。控制器驱动:基于platform总线架构NXP写的驱动,然后设备树中配置节点,把状态设置为ok,匹配成功后执行probe,最终在I2C核心层(I2C-core)中生成i2c_adapter。接着在设备树中配置自己的设备,和我们自己写的驱动进行匹配,就会生成I2C_client给我们自己的驱动,我们可以通过重写fops来对i2c_client完成一些通信的编写。最后应用层就可以调用他。

五、代码

        1.lm75a_drv.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>static struct i2c_client *lm75_client = NULL;static ssize_t lm75_read(struct file *fp, char __user *puser, size_t n, loff_t *off)
{   int ret = 0;unsigned long nret = 0;struct i2c_msg sendmsg;struct i2c_msg recvmsg;unsigned char regaddr = 0x00;unsigned char tempdata[2] = {0};sendmsg.addr = lm75_client->addr;       //lm75从机地址sendmsg.flags = 0;                      //读写标志 0 表示写sendmsg.buf = &regaddr;                 //发送数据(LM75存放温度的寄存器地址)sendmsg.len = 1;                        //发送数据长度 1ret = i2c_transfer(lm75_client->adapter, &sendmsg, 1); if (nret < 0) {pr_info("i2c_transfer failed\n");return -1;}recvmsg.addr = lm75_client->addr;       //lm75从机地址recvmsg.flags |= I2C_M_RD;              //读写标志 I2C_M_RD表示读recvmsg.buf = tempdata;                 //存放读取到的数据空间recvmsg.len = sizeof(tempdata);         //读取数据的字节数ret = i2c_transfer(lm75_client->adapter, &recvmsg, 1); if (nret < 0) {pr_info("i2c_transfer failed\n");return -1;}nret = copy_to_user(puser, tempdata, 2);if (nret) {pr_info("copy_to_user failed\n");return -1;}return 2;
}static struct file_operations fops = {.owner = THIS_MODULE,.read = lm75_read,
};static struct miscdevice misc_lm75 = {.minor = MISC_DYNAMIC_MINOR,.name = "misc_lm75",.fops = &fops,
};static int lm75_probe(struct i2c_client *pclient, const struct i2c_device_id *pdevid)
{int ret = 0;lm75_client = pclient;ret = misc_register(&misc_lm75);if (ret) {pr_info("misc_register failed\n");return -1;}pr_info("probe ok\n");return 0;
}static int lm75_remove(struct i2c_client *pclient)
{int ret = 0;ret = misc_deregister(&misc_lm75);if (ret) {pr_info("misc_deregister failed\n");return -1;}pr_info("remove ok\n");return 0;
}static struct i2c_device_id lm75_id_table[] = {{.name = "putelm75"},{},
};static struct of_device_id lm75_of_match_table[] = {{.compatible = "pute,putelm75"},{},
};static struct i2c_driver lm75_drv = {.probe = lm75_probe,.remove = lm75_remove,.driver = {.name = "putelm75",.of_match_table = lm75_of_match_table,},.id_table = lm75_id_table,
};module_i2c_driver(lm75_drv)MODULE_LICENSE("GPL");
MODULE_AUTHOR("pute");

        2.lm75a_app.c

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>int main(void)
{int fd = 0;unsigned char data[2] = {0};double temp = 0.0;fd = open("/dev/misc_lm75", O_RDWR);if (-1 == fd){perror("fail to open");return -1;}while (1){read(fd, data, 2);temp = ((((data[0] << 8)) | data[1]) >> 7) * 0.5;printf("temp = %.2lf\n", temp);sleep(1);}close(fd);return 0;
}
http://www.dtcms.com/a/578214.html

相关文章:

  • Android 多版本Toast版本区别以及使用可能遇到的问题
  • 集团高端网站建设公司wordpress侧边栏文件
  • 【Java面向对象编程入门:接口、继承与多态】
  • 百度联盟的网站怎么做企业网站的设计要求有哪些
  • 别墅花园装修设计公司wordpress 网址优化
  • 云服务器可以做虚拟机吗?
  • 计算机操作系统:文件存储空间的管理
  • 【stm32协议外设篇】- PAJ7620手势识别传感器
  • 网站增加权重吗免费ip地址
  • “工业数据库怎么选”之一:深度解析 PI System vs TDengine
  • 如何做好网站盘锦网站建设策划
  • k8s kubelet Nameserver limits exceeded
  • 供暖季技术实战:益和热力用 TDengine 时序数据库破解热力数据处理难题
  • 蔡甸建设局网站jsp源码做网站
  • AI代码开发宝库系列:Dify本地化部署和应用
  • 推荐高性能MCU微控制器N32H785EC(MCU单片机特征)
  • Bayes/BO-CNN-GRU、CNN-GRU、GRU三模型多变量回归预测Matlab
  • 免费做国际贸易的网站打广告专用图
  • 云南固恒建设集团有限公司网站wordpress主题结合
  • 深度与高程计算:OpenGL RTT技术解析
  • Rust 练习册 10:多线程基础与并发安全
  • 电子商务网站建设评估工具办公宽带多少钱一年
  • Razor VB 变量详解
  • 输入一个故事主题,使用大语言模型生成故事视频【视频中包含大模型生成的图片、故事内容,以及音频和字幕信息】
  • 英文网站首页优化中信建设有限责任公司招投标
  • 前端浏览器设置input不记住密码、不自动填充密码,举例jquery
  • 二级域名免费申请网站环球资源网站网址
  • 网站建设要学多久网站建设与管理 市场分析
  • 潍坊网站建设排行房地产销售人员网站怎么做
  • 如何为你的项目选择合适的加速度计?