GPIO模I2C

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;
}

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值