项目:使用模拟iic与ov2640通讯
环境:F407
问题:读取的ID号总是0xff,自己调了调发现在读从机数据时,SDA线上并未有信号变化。
自己是初学,也不知道时序之类的有没有搞对,望解决。
下面是我的IIC基本操作函数
/**
* @brief 产生I2C通讯的起始信号
* @note IIC起始信号 当SCL高电平时,SDA出现一个下降沿表示I2C总线启动信号
* @param None
* @retval None
*/
void I2C_GPIO_MakeStart(void)
{
I2C_GPIO_SCL_High();
I2C_GPIO_SDA_High();
Delay_us(4);
I2C_GPIO_SDA_Low();
Delay_us(4);
I2C_GPIO_SCL_Low(); //准备发送或接收数据
Delay_us(2);
}
/**
* @brief 产生I2C通讯的结束信号
* @note IIC停止信号 当SCL高电平时,SDA出现一个上升沿表示I2C总线停止信号
* @param None
* @retval None
*/
void I2C_GPIO_MakeEnd(void)
{
I2C_GPIO_SDA_Low();
I2C_GPIO_SCL_High();
Delay_us(2);
I2C_GPIO_SDA_High(); //发送I2C总线结束信号
}
/**
* @brief MCU等待从设备应答信号到来
* @param 无
* @retval 1:接收应答失败 0:接收应答成功
*/
uint8_t I2C_GPIO_MCUwaitACK(void)
{
uint8_t waitcount=0;
I2C_GPIO_SDA_IN();
I2C_GPIO_SDA_High();
Delay_us(1);
//SCL在高电平下SDA数据有效
I2C_GPIO_SCL_High();
Delay_us(1);
while(I2C_GPIO_SDA_Read())
{
waitcount++;
if(waitcount > 250 ){
return 1;}
}
I2C_GPIO_SCL_Low();
Delay_us(1);
I2C_GPIO_SDA_OUT();
return 0;
}
/**
* @brief MCU产生应答
* @note 一般不使用MCU产生应答信号,但连续读写的时候会使用。
* 经常使用的是MCU产生非应答,来结束一次读数据操作。
* @param none
* @retval none
*/
void I2C_GPIO_MCUmakeACK(void)
{
I2C_GPIO_SDA_Low();
Delay_us(2);
I2C_GPIO_SCL_High();
Delay_us(2);
I2C_GPIO_SCL_Low();
Delay_us(2);
I2C_GPIO_SDA_High();
}
/**
* @brief MCU产生非应答
* @note 当主机接收数据时,它收到最后一个字节后,必须向从机发出一个结束传送的信号。
* 这个信号是通过对从机的“非应答信号”来实现的。
* 在SCL为高电平期间,SDA为高电平,即从机释放SDA线,允许主机产生一个停止信号。
* @param none
* @retval none
*/
void I2C_GPIO_MCUmakeNACK(void)
{
I2C_GPIO_SCL_Low();
I2C_GPIO_SDA_High();
Delay_us(2);
I2C_GPIO_SCL_High();
Delay_us(2);
I2C_GPIO_SCL_Low();
}
/**
* @brief I2C写一个字节
* @note
* @param 无
* @retval 暂无
*/
void I2C_GPIO_writebyte(uint8_t byte)
{
uint8_t k;
I2C_GPIO_SCL_Low(); //拉低时钟开始数据传输
for(k=0;k<8;k++)
{
if( byte & 0x80){
I2C_GPIO_SDA_High();
}
else{
I2C_GPIO_SDA_Low();
}
byte<<=1; //byte左移一位赋值给byte
Delay_us(2);
I2C_GPIO_SCL_High();
Delay_us(2);
I2C_GPIO_SCL_Low();
Delay_us(2);
}
//释放SDA总线
I2C_GPIO_SDA_High();
}
/**
* @brief IIC读取一个字节
* @note
* @param ack=1,发送ACK,ack=0,发送nACK
* @retval 读到的8位数据
*/
uint8_t I2C_GPIO_readbyte(uint8_t ack)
{
uint8_t i=0;
uint8_t receive_data=0;
I2C_GPIO_SDA_High();
I2C_GPIO_SDA_IN();
for(i=0;i<8;i++ )
{
I2C_GPIO_SCL_Low();
Delay_us(2);
I2C_GPIO_SCL_High();
receive_data = (receive_data<<1)|I2C_GPIO_SDA_Read();
Delay_us(2);
}
I2C_GPIO_SDA_OUT();
if (!ack){
I2C_GPIO_MCUmakeNACK();//发送nACK
}
else{
I2C_GPIO_MCUmakeACK(); //发送ACK
}
return receive_data;
}
uint8_t OV2640_WriteReg(uint8_t Addr, uint8_t Data)
{
I2C_GPIO_SCL_Init();
I2C_GPIO_SDA_OUT();
//发送开始信号
I2C_GPIO_MakeStart();
//发送从设备地址
I2C_GPIO_writebyte(OV2640_DEVICE_WRITE_ADDRESS);
//等待从机的应答
if(I2C_GPIO_MCUwaitACK()) code = 1;
//发送要写入寄存器的地址
I2C_GPIO_writebyte(Addr);
if(I2C_GPIO_MCUwaitACK()) code = 2;
//发送数据
I2C_GPIO_writebyte(Data);
if(I2C_GPIO_MCUwaitACK()) code = 3;
//发送停止信号
I2C_GPIO_MakeEnd();
//如果一切正常,返回0
code =0;
return code;
}
/**
* @brief 从OV2640寄存器中读取一个字节的数据
* @param Addr: 寄存器地址
* @retval 返回读取得的数据
*/
uint8_t OV2640_ReadReg(uint8_t Addr)
{
uint8_t Data = 0;
I2C_GPIO_SCL_Init();
I2C_GPIO_SDA_OUT();
//发送开始信号
I2C_GPIO_MakeStart();
//发送从设备地址(写)
I2C_GPIO_writebyte(OV2640_DEVICE_WRITE_ADDRESS);
I2C_GPIO_MCUwaitACK();
//发送要读取寄存器的地址
I2C_GPIO_writebyte(Addr);
I2C_GPIO_MCUwaitACK();
//发送重复开始信号
I2C_GPIO_MakeStart();
//发送从设备地址(读)
I2C_GPIO_writebyte(OV2640_DEVICE_READ_ADDRESS);
I2C_GPIO_MCUwaitACK();
//这里接收数据的同时发送了NACK
Data = I2C_GPIO_readbyte(0);
//发送停止信号
I2C_GPIO_MakeEnd();
/* return the read data */
return Data;