本人使用stm32单片机很多年了,一直都用模拟i2c(原因大家都知道),最近使用从103上拷贝过来的模拟i2c使用到STM32F4xx上,调试无法通过,读取不到应答,困扰了两天,开始以为是时钟速度过快,但是用示波器测试,其实速度很低,只有40K,时序也不应该有问题的,在103上面一直都正常使用,问题出在哪里呢?下面为大家慢慢分析。
先贴一段代码
GPIO_Struct.GPIO_Pin = GPIO_PIN_SCL | GPIO_PIN_SDA;
GPIO_Struct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_Struct.GPIO_OType = GPIO_OType_OD;
GPIO_Struct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Struct.GPIO_Speed = GPIO_I2C_SPEED;
GPIO_Init(GPIO_I2C, &GPIO_Struct);
#define Read_Bit() GPIO_ReadOutputDataBit(GPIO_I2C,GPIO_PIN_SDA)
相信很多人跟我一样,引脚都是这个配置。
再看字节发送
uint8_t I2C_Send_Byte(uint8_t Data)
{
uint8_t i;
for(i=0;i<8;i++)
{
if(Data&0x80)
I2C_SDA_H;
else
I2C_SDA_L;
I2C_Delay_us(2);
I2C_SCL_H;
I2C_Delay_us(2);
Data<<=1;
I2C_SCL_L;
I2C_Delay_us(2);
}
I2C_SDA_H;
I2C_Delay_us(2);
I2C_SCL_H;
I2C_Delay_us(2);
if(Read_Bit())
return I2C_ERR;
I2C_SCL_L;
I2C_Delay_us(2);
I2C_SDA_L;
I2C_Delay_us(2);
return I2C_OK;
}
你是否跟我一样呢,一样就对了。
单步调试发现,问题出在
if(Read_Bit())
return I2C_ERR;
这个地方,一直都返回错误,用示波器一看,SDA引脚是为低电平啊,这就是是问题关键点。
这个程序的写法在103上他确实没有问题,在F4上为什么就行不通呢。在查看引脚功能图上,没看出端倪。
然后我加了这样的代码
#define Read_Bit() GPIO_ReadInputDataBit(GPIO_I2C,GPIO_PIN_SDA) //读SDA引脚电平
void I2C_SDA_Out(void)
{
GPIO_InitTypeDef GPIO_Struct;
GPIO_Struct.GPIO_Pin = GPIO_PIN_SDA;
GPIO_Struct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_Struct.GPIO_OType = GPIO_OType_OD;
GPIO_Struct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Struct.GPIO_Speed = GPIO_I2C_SPEED;
GPIO_Init(GPIO_I2C, &GPIO_Struct);
}
void I2C_SDA_In(void)
{
GPIO_InitTypeDef GPIO_Struct;
GPIO_Struct.GPIO_Pin = GPIO_PIN_SDA;
GPIO_Struct.GPIO_Mode = GPIO_Mode_IN;
// GPIO_Struct.GPIO_OType = GPIO_OType_OD;
GPIO_Struct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Struct.GPIO_Speed = GPIO_I2C_SPEED;
GPIO_Init(GPIO_I2C, &GPIO_Struct);
}
uint8_t I2C_Send_Byte(uint8_t Data)
{
uint8_t i;
for(i=0;i<8;i++)
{
if(Data&0x80)
I2C_SDA_H;
else
I2C_SDA_L;
I2C_Delay_us(2);
I2C_SCL_H;
I2C_Delay_us(2);
Data<<=1;
I2C_SCL_L;
I2C_Delay_us(2);
}
I2C_SDA_H;
I2C_Delay_us(2);
I2C_SDA_In();
I2C_SCL_H;
I2C_Delay_us(2);
if(Read_Bit())
return I2C_ERR;
I2C_SDA_Out();
I2C_SCL_L;
I2C_Delay_us(2);
I2C_SDA_L;
I2C_Delay_us(2);
return I2C_OK;
}
经测试,OK,没问题了。问题就出在SDA端口的状态。因此,F4系列在读取SDA应答的时候,必须要将GPIO变为输入模式,去读取输入寄存器的值,需要输出的时候,又必须将SDA端口改为OD输出。
按理,F1也必须这么做才可以的,为什么配置为OD也行,推测应该是工艺结构上的变化,知道的朋友解答下吧。
|