最近完成了一个产品电流采集测试方案,有点心得,记录一下,分两篇,一篇IIC通讯,一篇INA219电流采集模块使用介绍。
一.通讯协议简介
芯片间总线(Inter Interface Circuit,IIC),是应用广泛的芯片间串行扩展总线。目前世界上采用的IIC总线一共有两个规范,分别由荷兰飞利浦公司和日本索尼公司提出的,现在基本采用荷兰飞利浦的IIC总线的技术规范。
IIC通讯主要通过SCL(时钟)和SDA(数据)达到通讯的功能,SCL为时钟线,保证通讯信号的时许,SDA为数据线,数据主要通过此线来读取;
二.通讯详解
IIC总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定;只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。,但开始信号和结束信号不在这个限制范围内;
开始信号:在SCL和SDA同时处于高电平的情况下,拉低SDA,即为开始信号,可以开始数据传输;
结束信号:结束信号和开始信号相反,在SCL处于高电平,SDA处于低电平的情况下,拉高SDA,即为结束信号,结束数据传输;
应答信号:IIC通讯中,在SCL处于高电平时,读取数据;在八个周期后,接受端需要给出应答,即在SCL第九个高电平前,拉低SDA,即给出应答;一般发送数据时,需要判断设备是否有应答,来确认数据传输正常;
非应答信号:即数据传输结束时,不应答,跟应答相反就行;
三.代码实现
还有很多细节没有讲到,直接通过实例代码讲解会容易理解一点
1.IIC外设写数据
大概流程为:
1.写入7位设备地址+1位读写状态,1为读,0为写;假设设备地址为1111111,则写地址为:11111110,读地址为11111111;
2.IIC外设初始化,根据datasheet来定;一般是写入寄存器地址,然后再写入规定初始化的数据;
3.写入数据,初始化完成,再向目标寄存器写数据,步骤和初始化差不多,也是先写寄存器地址,再写数据;
第一步代码实现:
SET_SCL;
SET_SDA;
sys_delay_iic();
CLR_SDA; // 开始信号
sys_delay_iic();
CLR_SCL; // 数据传输
for(i = 0;i<8;i++)
{
control_SDA(DEVICE_WRITE >> (7-i) & 0x1);
sys_delay_iic();
SET_SCL;
sys_delay_iic();
CLR_SCL;
}
sys_delay_iic();
SET_SDA;
sys_delay_iic();
SET_SCL;
while(SDA); // 等待应答,这里需要优化,应该封装ACK函数判断,避免阻塞
开始信号和设备写地址0x8A
第二步其实就是第一步的重复:
CLR_SCL; //write INIT addr
sys_delay_iic();
for(i = 0;i<8;i++)
{
control_SDA(reg >> (7-i) & 0x1);
sys_delay_iic();
SET_SCL;
sys_delay_iic();
CLR_SCL;
sys_delay_iic();
}
SET_SCL;
sys_delay_iic();
SET_SDA;
while(SDA);
CLR_SCL; //write init high byte
for(i = 0;i<8;i++)
{
control_SDA(write_data >> (15-i) & 0x1);
sys_delay_iic();
SET_SCL;
sys_delay_iic();
CLR_SCL;
sys_delay_iic();
}
SET_SCL;
sys_delay_iic();
SET_SDA;
while(SDA);
CLR_SCL; //write write init low byte
for(i = 0;i<8;i++)
{
control_SDA(write_data >> (7-i) & 0x1);
sys_delay_iic();
SET_SCL;
sys_delay_iic();
CLR_SCL;
sys_delay_iic();
}
SET_SCL;
sys_delay_iic();
SET_SDA;
while(SDA);
CLR_SCL;
sys_delay_iic();
SET_SCL;
sys_delay_iic();
SET_SDA; // 应答
寄存器地址:0x00,写入数据0x399f,然后结束信号结尾
第三步和第二步差不多,就不赘述了;
2.IIC外设读数据
读数据会比写数据复杂一点,大概流程为:
1.写入7位设备地址+1位读写状态,1为读,0为写;
2.IIC外设初始化,根据datasheet来定;
3.写入设备写地址(根据实际情况,需要重新发起通讯则需要这步,如果和上面两步连在一起,则省略);
4.写入目标寄存器地址;
5.重新发送开始信号,写入设备读地址;
6.释放SDA线,根据SCL读取数据;
前面三步和写差不多,直接从第5步开始,代码如下:
sys_delay_iic();
SET_SDA;
sys_delay_iic();
SET_SCL;
sys_delay_iic();
CLR_SDA; // 开始信号
sys_delay_iic();
CLR_SCL; // 写入设备读地址
sys_delay_iic();
for(i = 0;i<8;i++)
{
control_SDA(DEVICE_READ >> (7-i) & 0x1);
SET_SCL;
sys_delay_iic();
CLR_SCL;
sys_delay_iic();
}
SET_SCL;
sys_delay_iic();
SET_SDA;
while(SDA);
开始信号+设备读地址0x8B,和写地址波形区别在于最后一位
第六步:
sys_delay_iic();
CLR_SCL;
for(i = 0;i<8;i++)
{
sys_delay_iic();
SET_SCL;
sys_delay_iic();
get_data_1 <<= 1;
if(SDA)
get_data_1++;
CLR_SCL;
}
sys_delay_iic();
CLR_SDA;
sys_delay_iic();
SET_SCL;
sys_delay_iic();
CLR_SCL;
for(i = 0;i<8;i++)
{
sys_delay_iic();
SET_SCL;
sys_delay_iic();
get_data_2 <<= 1;
if(SDA)
get_data_2++;
CLR_SCL;
}
sys_delay_iic();
CLR_SDA;
获取的数据为0x0700
以上便是整个实现软件IIC的流程了,本来是合在一起的逻辑,被拆开用来讲解了,有不对或疑问的地方欢迎大家提出探讨。