i2c管脚
SCL:信号时钟
SDA:数据线
i2c速率
普通设备I2C最高速率一般为400K,通常使用速率为100K。也存在其他高速I2C,例如有一种super iic支持10M或几M速率。
i2c地址
i2c总线一般一个主设备(Master)以及多个从设备(slave),设备的访问通过地址实现。一般地址为7bit,不同的芯片可配置位不同。一些芯片通过硬件电平来配置地址。一些芯片通过固定的rom来配置存储地址。另外一位通常用来指定读或者写。
i2c协议
i2c协议主要包含几部分:start,stop,ack,NACK,以及数据部分。
START(启始信号):SCL为高,SDA由H->L
STOP:SCL为高,SDA由H->L
ACK:传输完8bit数据后,第9bit需要传输响应信号,当数据由Slave–>Master时,由Master发送ACK,当Master–>Slave时,由Slave发送ACK。一般以低电平作为ACK信号。
NACK:一般在读操作下整个数据包读完的时候使用,即是不需要响应。直接停止传输即可。
传输数据:传输数据最小单元一般按字节传输,一般传输字节长度自行定义,协议以start开始,stop结束。一般为上升沿传输数据,故常在低电平向SDA写数据,上升沿传输。高电平读数据。
例如芯片的读写(参考于MLX90640和ATM7410,并不适用与所有芯片):
写操作:
第一个字节一般为从设备地址(7bit)和写控制位(基本上写为0)构成,
第二字节为需要写入的地址(具体多少字节不同芯片不同,有可能旁边会根据需求增加配置等字节),
后面为要写入的数据,具体不同,有些芯片为单一寄存器地址,有些芯片支持流操作,会根据前面写入的配置如何传入多字节数据。
在写地址和写数据见无需添加额外的控制信息。ACK全部为slave发送到master。
START slave_addr|W ACK write_addr ACK write_data STOP
读操作:
与写不同的是读控制位一般为1。
第一个、第二个和第三个ACK为Slave-->Master。后面的为Master-->slave。
在start前面不能添加stop,也不能去掉中间的stop。
START slave_addr|W ACK write_addr ACK START slave_addr|R ACK read_data_MSB ACK read_data_MSB NACK STOP
注意:在一次传输过程中,当从写入转到读取时,在变化读写中间使用了一次START信号,但不能使用STOP,一次意外的使用到值无法正常读取芯片。STOP会停止前面的操作,让一切时序操作冲头开始。
根据同事提示,在中间如果丢失使用的start信号同样会导致无法正常的读取数据。
实例
平台为树莓派+MLX90640,由于在使用树莓派官方提供的系统中,添加I2C总线驱动能够正常使用mlx90640.但由于i2c不够,抵用gpio模拟i2c口,树莓派系统本身是一个linux,其中字需要见但配置便支持使用gpio模拟的I2c口。但实际使用中,发现系统中提供的gpio模拟的I2C口使用上,读写mlx90640寄存器出现莫名其妙修改eprom中参数情况。查看代码无果,怀疑可能与树莓派官方提供的系统驱动有关。故使用自身wiringPi中gpio模拟i2c测试。暂不考虑写成linux下驱动模块。
//代码格式较乱,暂不修改
#include <wiringPi.h>
#include <stdio.h>
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
//IO方向设置
#define SDA_IN(x) pinMode(x,INPUT)
#define SDA_OUT(x) pinMode(x,OUTPUT)
typedef struct
{
unsigned char scl;
unsigned char sda;
} s_i2c_gpio;
typedef struct
{
int num;
s_i2c_gpio gpio[0];
}s_i2c_gpios;
void delay_u(int x)
{
while(x--)
for(int i=0;i<5500;i++);
}
//初始化i2c
void i2c_Init(s_i2c_gpio i2c_gpio)
{
wiringPiSetup();
pinMode(i2c_gpio.scl,OUTPUT);
pinMode(i2c_gpio.sda,OUTPUT);
digitalWrite(i2c_gpio.sda,HIGH);
digitalWrite(i2c_gpio.scl,HIGH);
delay_u(10);
}
//产生i2c起始信号
void i2c_Start(s_i2c_gpio i2c_gpio)
{
SDA_OUT(i2c_gpio.sda); //sda线输出
digitalWrite(i2c_gpio.sda,HIGH);
digitalWrite(i2c_gpio.scl,HIGH);
delay_u(4);
digitalWrite(i2c_gpio.sda,LOW);//START:when CLK is high,DATA change form high to LOW
delay_u(4);
digitalWrite(i2c_gpio.scl,LOW);//钳住I2C总线,准备发送或接收数据
}
//产生i2c停止信号
void i2c_Stop(s_i2c_gpio i2c_gpio)
{
SDA_OUT(i2c_gpio.sda);//sda线输出
digitalWrite(i2c_gpio.scl,LOW);
digitalWrite(i2c_gpio.sda,LOW);//STOP:when CLK is high DATA change form LOW to high
delay_u(4);
digitalWrite(i2c_gpio.scl,HIGH);
digitalWrite(i2c_gpio.sda,HIGH);//发送I2C总线结束信号
delay_u(4);
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
unsigned char i2c_Wait_Ack(s_i2c_gpio i2c_gpio)
{
unsigned char ucErrTime=0;
SDA_IN(i2c_gpio.sda); //SDA设置为输入
digitalWrite(i2c_gpio.sda,HIGH);delay_u(1);
//SDA_IN(i2c_gpio.sda);
digitalWrite(i2c_gpio.scl,HIGH);//lay(1);
while(digitalRead(i2c_gpio.sda))
{
ucErrTime++;
if(ucErrTime>250)
{
i2c_Stop(i2c_gpio);
return 1;
}
}
digitalWrite(i2c_gpio.scl,LOW);//时钟输出0
return 0;
}
//产生ACK应答
void i2c_Ack(s_i2c_gpio i2c_gpio)
{
digitalWrite(i2c_gpio.scl,LOW);
SDA_OUT(i2c_gpio.sda);
digitalWrite(i2c_gpio.sda,LOW);
delay_u(2);
digitalWrite(i2c_gpio.scl,HIGH);
delay_u(2);
digitalWrite(i2c_gpio.scl,LOW);
}
//不产生ACK应答
void i2c_NAck(s_i2c_gpio i2c_gpio)
{
digitalWrite(i2c_gpio.scl,LOW);
SDA_OUT(i2c_gpio.sda);
digitalWrite(i2c_gpio.sda,HIGH);
delay_u(2);
digitalWrite(i2c_gpio.scl,HIGH);
delay_u(2);
digitalWrite(i2c_gpio.scl,LOW);
}
//i2c发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void i2c_Send_Byte(s_i2c_gpio i2c_gpio,unsigned char txd)
{
unsigned char t;
SDA_OUT(i2c_gpio.sda);
digitalWrite(i2c_gpio.scl,LOW);//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
digitalWrite(i2c_gpio.sda,(txd&0x80)>>7);
//digitalWrite(i2c_gpio.scl,HIGH);
txd<<=1;
delay_u(2); //对TEA5767这三个延时都是必须的
digitalWrite(i2c_gpio.scl,HIGH);
delay_u(2);
digitalWrite(i2c_gpio.scl,LOW);
delay_u(2);
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
unsigned char i2c_Read_Byte(s_i2c_gpio i2c_gpio,unsigned char ack)
{
int ucErrTime=0;
unsigned char i,receive=0;
SDA_IN(i2c_gpio.sda);//SDA设置为输入
for(i=0;i<8;i++ )
{
ucErrTime=0;
digitalWrite(i2c_gpio.scl,LOW);
delay_u(2);
digitalWrite(i2c_gpio.scl,HIGH);
receive=receive<<1;
delay_u(2);
while(!digitalRead(i2c_gpio.sda))
{
ucErrTime++;
if(ucErrTime>250)
{
break;
}
}
if(ucErrTime<250)
{
receive++;
printf("H ");
}
else
{
printf("L ");
}
delay_u(1);
//digitalWrite(i2c_gpio.scl,LOW);
//delay_u(1);
}
if (!ack)
i2c_NAck(i2c_gpio);//发送nACK
else
i2c_Ack(i2c_gpio); //发送ACK
return receive;
}
#define DEV_NUM 1
s_i2c_gpios *i2c_dev;
void MLX90640_GPIO_I2CInit(uint8_t gpio_num)
{
//i2c.stop();
i2c_Init(i2c_dev->gpio[gpio_num]);
}
void i2cDev_init()
{
i2c_dev=(s_i2c_gpios*)malloc(sizeof(s_i2c_gpios)+sizeof(s_i2c_gpio)*DEV_NUM);
i2c_dev->num=DEV_NUM;
for (int i = 0; i < i2c_dev->num; i++)
{
i2c_dev->gpio->scl=13;//i+2;
i2c_dev->gpio->sda=12;//i*2+3;
}
for (int i = 0; i <i2c_dev->num ; i++)
{
MLX90640_GPIO_I2CInit(i);
}
}
uint8_t MLX90640_GPIO_I2CReadOne(uint8_t gpio_num,uint8_t slaveAddr, uint16_t address,uint16_t tmp)
{
uint16_t data=0;
slaveAddr=slaveAddr<<1;
i2c_Start(i2c_dev->gpio[gpio_num]);
i2c_Send_Byte(i2c_dev->gpio[gpio_num],slaveAddr);
if(i2c_Wait_Ack(i2c_dev->gpio[gpio_num]))
{
printf("error addr\r\n");
return -1;
}
i2c_Send_Byte(i2c_dev->gpio[gpio_num],address>>8);
if(i2c_Wait_Ack(i2c_dev->gpio[gpio_num]))
{
printf("reg error\r\n");
return -1;
}
i2c_Send_Byte(i2c_dev->gpio[gpio_num],address&0xff);
if(i2c_Wait_Ack(i2c_dev->gpio[gpio_num]))
{
printf("reg L error\r\n");
return -1;
}
slaveAddr = slaveAddr | 0x01;
i2c_Start(i2c_dev->gpio[gpio_num]);
i2c_Send_Byte(i2c_dev->gpio[gpio_num],slaveAddr);
if(i2c_Wait_Ack(i2c_dev->gpio[gpio_num]))
{
return -1;
}
data=i2c_Read_Byte(i2c_dev->gpio[gpio_num],1);
data=data<<8;
data=data|i2c_Read_Byte(i2c_dev->gpio[gpio_num],0);
i2c_Stop(i2c_dev->gpio[gpio_num]);//产生一个停止条件
return 0;
}
int MLX90640_GPIO_I2CRead(uint8_t gpio_num,uint8_t slaveAddr, uint16_t startAddress, uint16_t nMemAddressRead, uint16_t *data)
{
int ret=0;
for(int i=0;i<nMemAddressRead;i++)
{
ret=MLX90640_GPIO_I2CReadOne( gpio_num,slaveAddr,startAddress+i,data[i]);
if(ret)
{
return -1;
}
else
{
// printf("%x ",data[i]);
}
}
// printf("\r\n");
return 0;
}
void MLX90640_GPIO_I2CFreqSet(uint8_t gpio_num,int freq)
{
// i2c.frequency(1000*freq);
}
int MLX90640_GPIO_I2CWrite(uint8_t gpio_num,uint8_t slaveAddr, uint16_t writeAddress, uint16_t data)
{
i2c_Start(i2c_dev->gpio[gpio_num]);
i2c_Send_Byte(i2c_dev->gpio[gpio_num],slaveAddr<<1);
if(i2c_Wait_Ack(i2c_dev->gpio[gpio_num]))
{
return -1;
}
i2c_Send_Byte(i2c_dev->gpio[gpio_num],writeAddress>>8);
if(i2c_Wait_Ack(i2c_dev->gpio[gpio_num]))
{
return -1;
}
i2c_Send_Byte(i2c_dev->gpio[gpio_num],writeAddress&0xff);
if(i2c_Wait_Ack(i2c_dev->gpio[gpio_num]))
{
return -1;
}
i2c_Send_Byte(i2c_dev->gpio[gpio_num],data>>8);
if(i2c_Wait_Ack(i2c_dev->gpio[gpio_num]))
{
return -1;
}
i2c_Send_Byte(i2c_dev->gpio[gpio_num],data&0xff);
if(i2c_Wait_Ack(i2c_dev->gpio[gpio_num]))
{
return -1;
}
i2c_Stop(i2c_dev->gpio[gpio_num]);//产生一个停止条件
return 0;
}
#define MLX_I2C_ADDR 0x33
int main(){
#if 1
uint16_t data[10]={0};
int ret=0;
i2cDev_init();
ret=MLX90640_GPIO_I2CRead(0,MLX_I2C_ADDR,0x240c,10,data);
if(ret!=0)
{
printf("no ack\r\n");
}
delay(100);
//MLX90640_GPIO_I2CWrite();
for (int i = 0; i < 10; i++)
{
printf("%x ",data[i]);
}
printf("moinoihuinoimpo\r\n");
ret=MLX90640_GPIO_I2CWrite(0,0x33,0x240d,00);
if(ret!=0)
{
printf("no ack\r\n");
}
delay(100);
ret=MLX90640_GPIO_I2CWrite(0,0x33,0x240d,0x3aaa);
if(ret!=0)
{
printf("no ack\r\n");
}
delay(100);
#endif
return 0;
}