在硬件资源不够的情况下,通常需要使用GPIO口模似I2C。
1:初始化硬件GPIO口,不同硬件方法在同小异常
void SW_I2C_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
2:根据I2C协议写:Start,Stop,Wait_Ack函数
//起始位:SCL为高时,SDA产生下降沿
static int I2C_Start(void)
{
//1:确保SDA为输出模式
SET_SDA_OUT();
//2:拉高SDA,SCL
I2C_SDA_H();
if(!READ_SDA_PIN) return 0;
I2C_SCL_H();
delay_us(SW_I2C_DELAY);
//3:拉低SDA
I2C_SDA_L();
if(READ_SDA_PIN) return 0;
delay_us(SW_I2C_DELAY);
//4:拉低SCL,钳住I2C总线,准备发送数据
I2C_SCL_L();
return 1;
}
//停止位:SCL为高时,SDA产生上升沿
static void I2C_Stop(void)
{
//1:确保SDA为输出模式
SET_SDA_OUT();
//2: 拉低SCL,SDA
I2C_SCL_L();
I2C_SDA_L();
delay_us(SW_I2C_DELAY);
//3:拉高SCL,SDA
I2C_SCL_H();
I2C_SDA_H();
delay_us(SW_I2C_DELAY);
}
//应答信号:0:Ack,1:NoAck
static u8 I2C_Wait_Ack(void)
{
u8 timeout = 0;
//1:SDA设为输入,拉高SCL
SET_SDA_IN();
I2C_SDA_H();
delay_us(SW_I2C_DELAY);
I2C_SCL_H();
delay_us(SW_I2C_DELAY);
//2:读SDA
while(READ_SDA_PIN)
{
timeout++;
if(timeout > 50)
{
I2C_Stop();
printf("[%s]:[Error]: there is no Ack!!!\r\n",__func__);
return 0;
}
delay_us(SW_I2C_DELAY);
}
//3:拉低SCL
I2C_SCL_L();
return 1;
}
3:I2C读字节
// I2C_Addr:从设置地址,Reg_Addr:寄存器地址,res:读取的数据
u8 I2C_Read_OneByte(u8 I2C_Addr,u8 Reg_Addr)
{
u8 res = 0;
//1:I2C开始
I2C_Start();
//2:发送设备地址
I2C_Send_Byte(I2C_Addr);
I2C_Wait_Ack();
//3:发送寄存器地址
I2C_Send_Byte(Reg_Addr);
I2C_Wait_Ack();
I2C_Stop();
//4:接入接收模式
I2C_Start();
I2C_Send_Byte(I2C_Addr+1);
I2C_Wait_Ack();
//5:接收数据
res = I2C_Receive_Byte(0);
I2C_Stop();
return res;
}
static u8 I2C_Receive_Byte(u8 ack)
{
u8 i,receive = 0;
SET_SDA_IN();
for(i=0;i<8;i++ )
{
I2C_SCL_L();
delay_us(SW_I2C_DELAY);
I2C_SCL_H();
receive <<= 1;
if(READ_SDA_PIN)
{
receive++;
}
delay_us(SW_I2C_DELAY);
}
if (ack)
I2C_Ack();
else
I2C_NAck();
return receive;
}
4:I2C写字节
static void I2C_Send_Byte(u8 Byte)
{
u8 i;
//1:确保SDA为输出状态
SET_SDA_OUT();
//2: SCL拉低
I2C_SCL_L();
//3: SDA准备数据
for(i=0;i<8;i++)
{
if((Byte & 0x80) == 0)
{
I2C_SDA_L(); //输出:0
}
else
{
I2C_SDA_H(); //输出:1
}
Byte <<= 1;
delay_us(SW_I2C_DELAY);
//4:SCL拉高,锁定SDA数据
I2C_SCL_H();
delay_us(SW_I2C_DELAY);
//5:SCL拉低,准备下一个数据
I2C_SCL_L();
delay_us(SW_I2C_DELAY);
}
}
#define SW_I2C_DELAY 1
所有延时用的都是1us。