记录使用模拟 I2C 所遇到的问题
首先贴上代码,我所经常使用的模拟I2C代码
#include "myiic.h"
#define device_addr 0x52
/*
模拟IIC引脚方向配置,这里用的是华大的单片机,根据需要更改
*/
void Analog_IIC_Pin_Init(void)
{
stc_gpio_cfg_t stcGpioCfg;
///< 打开GPIO外设时钟门控
Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
///< 端口方向配置->输出(其它参数与以上(输入)配置参数一致)
stcGpioCfg.enDir = GpioDirOut;
///< 端口上下拉配置->无
stcGpioCfg.enPu = GpioPuDisable;
stcGpioCfg.enPd = GpioPdDisable;
///< GPIO IO 端口初始化
Gpio_Init(SDA_GPIO_PORT, SDA_GPIO_PIN, &stcGpioCfg);
Gpio_Init(SCL_GPIO_PORT, SCL_GPIO_PIN, &stcGpioCfg);
///< 端口方向配置->输出(其它参数与以上(输入)配置参数一致)
stcGpioCfg.enDir = GpioDirIn;
///< 端口驱动能力配置->高驱动能力
stcGpioCfg.enDrv = GpioDrvL;
///< 端口上下拉配置->无
stcGpioCfg.enPu = GpioPuEnable;//Enable
stcGpioCfg.enPd = GpioPdDisable;
///< 端口开漏输出配置->开漏输出关闭
stcGpioCfg.enOD = GpioOdDisable;
///< 端口输入/输出值寄存器总线控制模式配置->AHB
stcGpioCfg.enCtrlMode = GpioAHB;
Gpio_Init(INTN_GPIO_PORT, INTN_GPIO_PIN, &stcGpioCfg);
///< 打开并配置GPIO2为上升沿中断
Gpio_EnableIrq(INTN_GPIO_PORT, INTN_GPIO_PIN, GpioIrqFalling);//GpioIrqRising GpioIrqFalling
///< 使能端口PORTA系统中断
EnableNvic(PORTA_IRQn, IrqLevel0, TRUE);
IIC_SCL_1; //拉高时钟线
IIC_SDA_1; //拉高数据线
}
void IIC_SDA_Dir(unsigned char d)
{
stc_gpio_cfg_t stcGpioCfg;
if (d == 1) //输出
{
///< 端口方向配置->输出(其它参数与以上(输入)配置参数一致)
stcGpioCfg.enDir = GpioDirOut;
///< 端口上下拉配置->无
stcGpioCfg.enPu = GpioPuDisable;
stcGpioCfg.enPd = GpioPdDisable;
///< GPIO IO 端口初始化
Gpio_Init(SDA_GPIO_PORT, SDA_GPIO_PIN, &stcGpioCfg);
}
else if (d == 0) //输入
{
///< 端口方向配置->输出(其它参数与以上(输入)配置参数一致)
stcGpioCfg.enDir = GpioDirIn;
///< 端口上下拉配置->无
stcGpioCfg.enPu = GpioPuDisable;
stcGpioCfg.enPd = GpioPdDisable;
///< GPIO IO 端口初始化
Gpio_Init(SDA_GPIO_PORT, SDA_GPIO_PIN, &stcGpioCfg);
}
}
//产生IIC起始信号
void IIC_Start(void)
{
IIC_SDA_Dir(1);//IIC_SDA线输出
IIC_SDA_1; //拉高数据线
IIC_SCL_1; //拉高时钟线
delay10us(2);
IIC_SDA_0; //拉低数据线
delay10us(2);
IIC_SCL_0; //拉低时钟线 发送IIC总线开始信号
}
//产生IIC停止信号
void IIC_Stop(void)
{
IIC_SDA_Dir(1);//IIC_SDA线输出
IIC_SCL_0; //拉低时钟线
IIC_SDA_0; //拉低数据线
delay10us(2);
IIC_SCL_1; //拉高时钟线
delay10us(2);
IIC_SDA_1; //拉高数据线 发送IIC总线停止信号
delay10us(2);
}
//产生ACK应答
void IIC_Ack(void)
{
IIC_SCL_0; //拉低时钟线
IIC_SDA_Dir(1);//IIC_SDA线输出
IIC_SDA_0; //拉低数据线
delay10us(2);
IIC_SCL_1; //拉高时钟线
delay10us(2);
IIC_SCL_0; //拉低时钟线
}
//不产生ACK应答
void IIC_NAck(void)
{
IIC_SCL_0; //拉低时钟线
IIC_SDA_Dir(1);//IIC_SDA线输出
IIC_SDA_1; //拉高数据线
delay10us(2);
IIC_SCL_1; //拉高时钟线
delay10us(2);
IIC_SCL_0; //拉低时钟线
}
//等待应答信号到来
//返回值:1 接收应答失败
// 0 接收应答成功
unsigned char IIC_Wait_Ack(void)
{
unsigned char Wait_TOut_Cnt = 0;//设置等待应答信号超时计数
IIC_SDA_Dir(0); //IIC_SDA线输入
IIC_SDA_1; //拉高数据线
delay10us(2);
IIC_SCL_1; //拉高时钟线 等待应答信号
delay10us(2);
while (IIC_SDA)
{
Wait_TOut_Cnt++;
if (Wait_TOut_Cnt > 250)
{
IIC_Stop(); //等待应答信号超时 发送IIC总线停止信号
return 1;
}
}
IIC_SCL_0; //拉低时钟线 结束应答信号
return 0;
}
//IIC发送一个字节
void IIC_Write_Byte(unsigned char WByte)
{
unsigned char Wb_Cnt = 0; //写数据位计数
IIC_SDA_Dir(1);//IIC_SDA线输出
IIC_SCL_0; //拉低时钟线 开始数据传输
for (Wb_Cnt = 0; Wb_Cnt < 8; Wb_Cnt++)
{
if (WByte & 0x80)
{
IIC_SDA_1;
}
else
{
IIC_SDA_0;
}
WByte <<= 1; //数据移位
delay10us(2);
IIC_SCL_1; //拉高时钟线
delay10us(2);
IIC_SCL_0; //拉低时钟线 准备开始传送数据位
delay10us(2);
}
}
//IIC读取一个字节
//参数值:1 发送Ack
// 0 不发送Ack
unsigned char IIC_Read_Byte(unsigned char SF_Ack)
{
unsigned char Rb_Cnt = 0; //读数据位计数
unsigned char RByte = 0; //读字节
IIC_SDA_Dir(0);//SDA设置为输入
for (Rb_Cnt = 0; Rb_Cnt < 8; Rb_Cnt++)
{
IIC_SCL_0; //拉低时钟线 准备开始传送数据位
delay10us(2);
IIC_SCL_1; //拉高时钟线
RByte <<= 1; //数据移位
if (IIC_SDA)
{
RByte++;
}
delay10us(2);
}
if (!SF_Ack) //0 不发送Ack
{
IIC_NAck(); //发送NAck
}
else //1 发送Ack
{
IIC_Ack(); //发送Ack
}
return RByte;
}
/*
写一个字节
*/
void Sensor_Write_Byte(unsigned char sensor_addr, unsigned char RAddr, unsigned char* WData)
{
IIC_Start(); //发送IIC起始信号
IIC_Write_Byte(sensor_addr);//发送IIC写地址
IIC_Wait_Ack(); //等待IIC应答信号
IIC_Write_Byte(RAddr); //发送IIC寄存器地址
IIC_Wait_Ack(); //等待IIC应答信号
IIC_Write_Byte(*WData); //发送写入寄存器的数据
IIC_Wait_Ack(); //等待IIC应答信号
IIC_Stop(); //发送IIC停止信号
}
/*
写N个字节
*/
void Sensor_Write_NByte(unsigned char sensor_addr, unsigned char RAddr, unsigned char* WData, unsigned char WLen)
{
unsigned char WB_Cnt = 0;
IIC_Start(); //发送IIC起始信号
IIC_Write_Byte(sensor_addr);//发送IIC写地址
IIC_Wait_Ack(); //等待IIC应答信号
IIC_Write_Byte(RAddr); //发送IIC寄存器地址
IIC_Wait_Ack(); //等待IIC应答信号
for (WB_Cnt = 0; WB_Cnt < WLen; WB_Cnt++)
{
IIC_Write_Byte(WData[WB_Cnt]);//连续读取寄存器的数据 等待应答信号
IIC_Wait_Ack(); //等待IIC应答信号
}
IIC_Stop(); //发送IIC停止信号
}
/*
读一个字节
*/
void Sensor_Read_Byte(unsigned char sensor_addr, unsigned char RAddr, unsigned char* RData)
{
IIC_Start(); //发送IIC起始信号
IIC_Write_Byte(sensor_addr); //发送IIC写地址
IIC_Wait_Ack(); //等待IIC应答信号
IIC_Write_Byte(RAddr); //发送IIC寄存器地址
IIC_Wait_Ack(); //等待IIC应答信号
IIC_Start(); //发送IIC起始信号
IIC_Write_Byte(sensor_addr + 1);//发送IIC读地址
IIC_Wait_Ack(); //等待IIC应答信号
*RData = IIC_Read_Byte(0); //读取寄存器的数据
IIC_Stop(); //发送IIC停止信号
}
/*
读N个字节
*/
void Sensor_Read_NByte(unsigned char sensor_addr, unsigned char RAddr, unsigned char* RData, unsigned char RLen)
{
unsigned char RB_Cnt = 0; //读字节计数
IIC_Start(); //发送IIC起始信号
IIC_Write_Byte(sensor_addr); //发送IIC写地址
IIC_Wait_Ack(); //等待IIC应答信号
IIC_Write_Byte(RAddr); //发送IIC寄存器地址
IIC_Wait_Ack(); //等待IIC应答信号
IIC_Start(); //发送IIC起始信号
IIC_Write_Byte(sensor_addr + 1); //发送IIC读地址
IIC_Wait_Ack(); //等待IIC应答信号
for (RB_Cnt = 0; RB_Cnt < (RLen - 1); RB_Cnt++)
{
RData[RB_Cnt] = IIC_Read_Byte(1);//连续读取寄存器的数据 等待应答信号
}
RData[RB_Cnt] = IIC_Read_Byte(0); //读取寄存器最后一个数据 不等待应答信号
IIC_Stop(); //发送IIC停止信号
}
①
https://www.cnblogs.com/KingZhan/p/16330358.html
②
在一次使用颜色传感器TCS3472的过程中,发现怎么都通信不了。最后在网上找到说明,规格书上有特别说明
即输入的所有地址,最后一位都得是1
也就是说,我上面的模拟I2C的代码中,最后的四个函数,都要加上使寄存器的地址最高为置1,这样才能正常通信。
好吧,规格书还是要好好看的,每个传感器都有自己的特点