使用官方demo,硬件IIC可以正常与WM8994通讯,而我这个使用了几年的软件IIC却无法正常通讯,表现为设备地址0x34能正常发送,能正确响应ACK,如果后续的16bit(2个字节)的地址与16bit(2个字节)的数据,BIT0,BIT8有一个1的话,就无法完成通讯。
比如发送0xFEFE是没问题的,如果是0xFFFF或者0x0001或者0x0100之类,只要BIT0或BIT8有1则不会收到ACK,时序确认了无数次,其它各种IIC芯片都能正常读写,唯独这个WM8994死活无法读取,已经折腾1个多星期了。
之前的软件IIC代码在这:https://blog.csdn.net/cp1300/article/details/75644988 已经用了好几年了。
然后我还重新编写了一个IIC的通讯接口,问题依旧。
//SDA-PH8 SCL-PH7
#define WM8994_SDA_OUT_MODE() SYS_GPIOx_OneInit(GPIOH, 8, OUT_OD, SPEED_50M) //SDA输出模式
#define WM8994_SDA_IN_MODE() SYS_GPIOx_OneInit(GPIOH, 8, IN_IPU, IN_NONE) //SDA输入模式
#define IIC_SDA_Set(x) (PHout(8) = x)
#define IIC_SCL_Set(x) (PHout(7) = x)
#define IIC_SDA_Get() (PHin(8))
//IIC启动序列(结果:SDA=0,SCL=0)
void IIC_Start(void)
{
IIC_SDA_Set(1);
IIC_SCL_Set(1);
Delay_US(3);
IIC_SDA_Set(0);
Delay_US(3);
IIC_SCL_Set(0);
}
//IIC结束序列(结果:SDA=1,SCL=1)
void IIC_Stop(void)
{
Delay_US(1);
IIC_SCL_Set(1);
Delay_US(1);
IIC_SDA_Set(0);
Delay_US(1);
IIC_SDA_Set(1);
Delay_US(1);
}
//IIC结束序列(结果:SDA=x,SCL=0)
void IIC_SendByte(u8 data)
{
u8 i;
for(i = 0;i < 8;i ++)
{
IIC_SDA_Set((data & 0x80)?1:0); //MSB
data <<= 1;
//产生时钟
Delay_US(1);
IIC_SCL_Set(1); //上升沿
Delay_US(2);
IIC_SCL_Set(0); //下降沿
Delay_US(1);
}
}
//等待ACK(结果:SDA=x,SCL=0)
bool IIC_WaitACK(void)
{
u8 retry = 0;
WM8994_SDA_IN_MODE(); //SDA设置为输入
Delay_US(1);
IIC_SCL_Set(1); //上升沿
Delay_US(2);
while(IIC_SDA_Get())
{
retry++;
if(retry>250)
{
IIC_SCL_Set(0); //下降沿
WM8994_SDA_OUT_MODE(); //SDA设置为输出
IIC_Stop();
return FALSE;
}
}
Delay_US(1);
IIC_SCL_Set(0); //下降沿
Delay_US(1);
WM8994_SDA_OUT_MODE(); //SDA设置为输出
return TRUE;
}
/*************************************************************************************************************************
*函数 : bool WM8994_WriteOneReg(WM8994_HANDLE *pHandle,u16 RegAddr,u8 data)
*功能 : WM8994写一个寄存器
*参数 : pHandle:句柄;RegAddr:寄存器地址;data:要写入的值
*返回 : 无
*依赖 : 底层宏定义
*作者 : cp1300@139.com
*时间 : 2019-02-13
*最后修改时间 : 2019-02-13
*说明 :
*************************************************************************************************************************/
bool WM8994_WriteOneReg(WM8994_HANDLE *pHandle,u16 RegAddr,u16 data)
{
u8 SlaveAddr = pHandle->SlaveAddr;
IIC_Start(); //发送起始信号
IIC_SendByte(SlaveAddr); //发送一字节
if(IIC_WaitACK() == FALSE)
{
DEBUG("[IIC写错误]:1\r\n");
return FALSE;
}
IIC_SendByte(RegAddr>>8); //发送一字节
if(IIC_WaitACK() == FALSE)
{
DEBUG("[IIC写错误]:2\r\n");
return FALSE;
}
IIC_SendByte(RegAddr); //发送一字节
if(IIC_WaitACK() == FALSE)
{
DEBUG("[IIC写错误]:3\r\n");
return FALSE;
}
IIC_SendByte(data>>8); //发送一字节
if(IIC_WaitACK() == FALSE)
{
DEBUG("[IIC写错误]:4\r\n");
return FALSE;
}
IIC_SendByte(data); //发送一字节
if(IIC_WaitACK() == FALSE)
{
DEBUG("[IIC写错误]:5\r\n");
return FALSE;
}
IIC_Stop();
return TRUE;
}
//测试代码
while(1)
{
if(WM8994_WriteOneReg(pHandle, 0, BIT6) == TRUE)
{
uart_printf("WM8994写入成功\r\n");
}
SYS_DelayMS(1);
LED1 = PDin(6);
SYS_DelayMS(500);
WM8994_WriteOneReg(pHandle, 0xFEFF, BIT0|BIT6);
SYS_DelayMS(1);
LED1 = PDin(6);
SYS_DelayMS(500);
//temp = WM8994_ReadOneReg(pHandle, 1792);
//uart_printf("reg%02d=0x%X\r\n",1792, temp);
}
//结果,只要BIT0或BIT8不为1就能正常写入,并且有正确的ACK响应
依旧不甘心,测试时在8个bit发送完成后,在SCL低电平期间将SDA设置为高电平,所有通讯都会故障,这个就解释不通了,理论上IIC在SCL为低电平时,SDA是可以变动的,但是这个1貌似会影响到之后的ACK低电平。
测试结果是所有通讯都异常了,没法接收ACK(已经测试过,后面无论延时多久都不会收到ACK,所以可以排除是时间导致的ACK错过,只要接收ACK之前SDA输出了1,就不会再收到ACK了,而且SDA的变化时在SCL=0时进行的,是符合IIC标准的,理论上不会影响后续数据)
全部返回错误1,也就是第一个字节发送出去就没有ACK了
而且与时间无关,就算延时1秒,依旧无法读取
从WM8994的手册中,看到时钟只限制了最大速度400K,并没有最低限制,同步通讯,理论上这个地方的延时都不会有影响,而且在SCL为低电平期间,是允许SDA变化的
同样的代码,读取FT5336,一次就成功了
/*************************************************************************************************************************
*函数 : u8 FT5336_ReadOneReg(FT5336_HANDLE *pHandle,u8 RegAddr)
*功能 : FT5336读取一个8bit寄存器
*参数 : pHandle:句柄;RegAddr:寄存器地址
*返回 : 读取的寄存器值
*依赖 : 底层宏定义
*作者 : cp1300@139.com
*时间 : 2019-02-13
*最后修改时间 : 2019-02-13
*说明 :
*************************************************************************************************************************/
u8 FT5336_ReadOneReg(FT5336_HANDLE *pHandle,u8 RegAddr)
{
u16 data;
u8 SlaveAddr = pHandle->SlaveAddr;
SIIC_Start(&pHandle->IIC_Handle); //产生IIC起始信号
if(SIIC_SendByte(&pHandle->IIC_Handle, SlaveAddr) == FALSE) //发送设备地址+写信号
{
DEBUG("没有收到ACK-01\r\n");
}
if(SIIC_SendByte(&pHandle->IIC_Handle, RegAddr) == FALSE) //发送寄存器地址
{
DEBUG("没有收到ACK-02\r\n");
}
SIIC_Start(&pHandle->IIC_Handle); //产生IIC起始信号
if(SIIC_SendByte(&pHandle->IIC_Handle, SlaveAddr|BIT0) == FALSE) //发送设备地址+读信号
{
DEBUG("没有收到ACK-03\r\n");
}
data = SIIC_ReadByte(&pHandle->IIC_Handle, TRUE); //SIIC读取一个字节-高位
SIIC_Stop(&pHandle->IIC_Handle); //产生IIC停止信号
return data;
}
/*************************************************************************************************************************
*函数 : FT5336_Init(FT5336_HANDLE *pHandle, u8 SlaveAddr)
*功能 : FT5336初始化
*参数 : pHandle:句柄;SlaveAddr通讯地址;
*返回 : TRUE:初始化成功;FALSE:初始化失败
*依赖 : 底层宏定义
*作者 : cp1300@139.com
*时间 : 2019-01-30
*最后修改时间 : 2019-01-30
*说明 : 需要先提前初始化IIC接口
*************************************************************************************************************************/
bool FT5336_Init(FT5336_HANDLE *pHandle, u8 SlaveAddr)
{
u8 id;
if(pHandle == NULL) return FALSE;
pHandle->SlaveAddr = SlaveAddr; //通讯地址
while(1)
{
id = FT5336_ReadOneReg(pHandle, 0xA8);
uart_printf("FT5336 id = 0x%X\r\n", id);
SYS_DelayMS(1000);
}
return TRUE;
}
//软件IIC读取数据测试
uart_printf("FT5336测试开始\r\n");
{
//软件IIC初始化
if(SIIC_Init(&g_SysGlobal.mFT5336_Handle.IIC_Handle, GPIOH, GPIOH, 8, 7, 5) == FALSE)
{
DEBUG("软件IIC初始化失败!\r\n");
}
FT5336_Init(&g_SysGlobal.mFT5336_Handle, 0x70);
//OSTimeDlyHMSM(1,0,0,0);
}
来源:CSDN
作者:cp1300
链接:https://blog.csdn.net/cp1300/article/details/104310993