调试了蛮久,最后发现是开发板原理图看错了,总的来说还是比较简单的,就是需要使用状态机方式编程,与传统的STM32那种有点不一样,状态图如下:
然后寄存器STA里面就是状态值,实测会出现0xF9 异常状态,开发板上面的HYM8563在IIC没有正常结束时,会一直出现SCL=1,SDA=0,并且无法软件恢复,必须断电重启,而且这个RTC芯片挑晶振厉害。
代码支持中断模式与查询模式,实际代码几乎一样,一个是中断中处理状态机,一个是在轮训中判断是否有中断,然后处理状态机,非OS模式下,如果需要使用轮训模式,需要自己处理一个软件超时,避免出现死循环。
#define _IIC_INT_ENABLE 1 //是否使能中断模式
上代码,因为是裸机,寄存器是自己定义的但是与手册一致,底层的寄存器需要自己定义,然后中断模式需要GIC支持。
/*************************************************************************************************************
* 文件名 : t113_iic.h
* 功能 : t113芯片IIC主机通讯
* 作者 : cp1300@139.com
* 创建时间 : 2024-07-10
* 最后修改时间 : 2024-07-10
* 详细 : 只支持IIC主机模式
*************************************************************************************************************/
#ifndef __T113_IIC_H___
#define __T113_IIC_H___
#include "typedef.h"
#include "t113_system.h"
#include "t113_map.h"
#define _IIC_INT_ENABLE 1 //是否使能中断模式
//IIC硬件接口选择
typedef enum
{
IIC_CH0 = 0, //IIC0
IIC_CH1 = 1, //IIC1
IIC_CH2 = 2, //IIC2
IIC_CH3 = 3, //IIC3
}IIC_CH_Type;
#define IIC_CH_COUNT 4 //4个IIC
typedef enum
{
IIC_SPEED_100KHz = 0,
IIC_SPEED_200KHz = 1,
IIC_SPEED_300KHz = 2,
IIC_SPEED_400KHz = 3,
}IIC_SPEED_TYPE;
//通讯错误状态
typedef enum
{
IIC_OK = 0, //没有错误
IIC_PARAMETER_ERROR = 1, //参数错误
IIC_TIMEOUT = 2, //超时错误,也可能是底层错误
IIC_HAL_ERROR = 3, //底层错误
IIC_STOP_ERROR = 4, //等待结束错误
IIC_BUSY = 5, //硬件忙
IIC_NACK = 6, //没有收到ACK
}IIC_ERROR;
bool IIC_Init(IIC_CH_Type ch, IIC_SPEED_TYPE Speed, u16 TimeOutUs);//硬件IIC初始化
IIC_ERROR IIC_MasterReadReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr, u8* pDataBuff, u16 ReadByteNum); //IIC读取寄存器(可以读取1个或者多个寄存器)
IIC_ERROR IIC_MasterWriteReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr, u8 *pDataBuff, u16 WriteByteNum); //IIC写寄存器(可以写1个或者多个寄存器)
#endif /* __T113_IIC_H___ */
/*************************************************************************************************************
* 文件名 : t113_iic.c
* 功能 : t113芯片IIC主机通讯
* 作者 : cp1300@139.com
* 创建时间 : 2024-07-10
* 最后修改时间 : 2024-07-10
* 详细 : 只支持IIC主机模式,10bit从机地址未测试
*************************************************************************************************************/
#include "t113_system.h"
#include "t113_map.h"
#include "typedef.h"
#include "t113_iic.h"
#include "t113_const.h"
#define IIC_10BIT_ADD_MASK (0x1E<<10) //如果是10bit地址,最高位需要进行填充
//IIC下一个任务指示,用于指示下一次中断中的任务操作
typedef enum
{
IIC_NEXT_TASK_IDLE = 0, //无任务->发送开始信号
IIC_NEXT_TASK_WAIT_START = 1, //等待开始信号发送完成发送高从机地址 发送低从机地址
IIC_NEXT_TASK_WAIT_10BIT_ADDR_H = 2, //等待10bit从机高地址发送完成-写入
IIC_NEXT_TASK_WAIT_7BIT_ADDR = 3, //等待7bit从机地址发送完成-写入
IIC_NEXT_TASK_WAIT_16BIT_REG_H = 4, //等待发送16bit寄存器地址高位
IIC_NEXT_TASK_WAIT_8BIT_REG = 5, //等待发送8bit寄存器地址
IIC_NEXT_TASK_WAIT_RSTART = 6, //等待重复开始信号发送完成
IIC_NEXT_TASK_RR_ADDR = 7, //下次发送低重复7bit地址或高2bit地址-用于读取数据
IIC_NEXT_TASK_WAIT_WRITE_DATA = 8, //等待发送完成
IIC_NEXT_TASK_READ_DATA = 9, //下次读取数据
IIC_NEXT_TASK_END = 0xFF,//任务结束了
}IIC_NEXT_TASK_OPERATE;
//IIC数据传输任务句柄-用于中断中发送数据
typedef struct
{
u16 SlaveAddr; //从设备地址,7bit或者10bit
bool is10BitSlaveAddr; //是否为10bit地址,否则为7bit
u16 RegAddr; //寄存器地址
bool is8bitRegAddr; //是否为8bit寄存器地址,否则为16bit
u16 NeedCount; //要传输的数据数量
u8* pDataBuff; //要传输的数据缓冲区
u16 TranCount; //已经传输的数据计数
bool isRead; //是否为读取数据,否则为写入数据
IIC_NEXT_TASK_OPERATE NextOperate; //下次中断中执行的任务操作
bool isBusy; //是否忙
}IIC_TRAN_TASK;
//主机相关状态
typedef enum
{
IIC_STAT_BUSS_ERR = 0x00, //总线错误
IIC_STAT_START = 0x08, //START条件已传输
IIC_STAT_RSTART = 0x10, //发送重复START条件
IIC_STAT_AW_ACK = 0x18, //发送地址 + 写入位,收到ACK
IIC_STAT_AW_NACK = 0x20, //发送地址 + 写入位,未收到ACK
IIC_STAT_WDATA_ACK = 0x28, //主模式下发送的数据字节,收到ACK
IIC_STAT_WDATA_NACK = 0x30, //主模式下传输的数据字节,未收到ACK
IIC_STAT_ARB_ERR = 0x38, //仲裁在地址或数据字节中丢失
IIC_STAT_AR_ACK = 0x40, //发送地址 + 读取位,收到ACK
IIC_STAT_AR_NACK = 0x48, //发送地址 + 读取位,未收到ACK
IIC_STAT_RDATA_ACK = 0x50, //主模式下接收到数据字节,发送ACK
IIC_STAT_RDATA_NACK = 0x58, //主模式下接收到数据字节,未发送ACK
//IIC_STAT_BUSS_ERR = 0x60, //接收到从地址 + 写入位,发送ACK
//IIC_STAT_BUSS_ERR = 0x68, //仲裁在作为主地址、从地址的地址中丢失 + 收到写入位,发送ACK
//IIC_STAT_BUSS_ERR = 0x70, //接收到通用呼叫地址,发送ACK
//IIC_STAT_BUSS_ERR = 0x78, //仲裁在作为主机的地址中丢失,常规呼叫收到地址,发送ACK
//IIC_STAT_BUSS_ERR = 0x80, //接收到从地址后接收到的数据字节,ACK传输的
//IIC_STAT_BUSS_ERR = 0x88, //接收到从地址后接收到数据字节,不是ACK传输的
//IIC_STAT_BUSS_ERR = 0x90, //接收到普通呼叫后接收到的数据字节,ACK传输的
//IIC_STAT_BUSS_ERR = 0x98, //接收到普通呼叫后接收到的数据字节,不是ACK传输的
//IIC_STAT_BUSS_ERR = 0xA0, //在从属模式下接收到STOP或重复START条件
//IIC_STAT_BUSS_ERR = 0xA8, //接收到从地址 + 读取位,发送ACK
//IIC_STAT_BUSS_ERR = 0xB0, //仲裁在作为主地址、从地址的地址中丢失 + 接收到读取位,发送ACK
//IIC_STAT_BUSS_ERR = 0xB8, //从模式下发送的数据字节,收到ACK
//IIC_STAT_BUSS_ERR = 0xC0, //从机模式下发送的数据字节,未收到ACK
//IIC_STAT_BUSS_ERR = 0xC8, //从模式下发送的最后一个字节,收到ACK
IIC_STAT_A2W_ACK = 0xD0, //第二地址字节 + 发送写入位,收到ACK
IIC_STAT_A2W_NACK = 0xD8, //第二个地址字节 + 写入位已发送,ACK未发送收到
IIC_STAT_NOT_STATUS = 0xF8, //无相关状态信息,INT_FLAG = 0
IIC_STAT_IIC_ERROR = 0xF9, //实测的时候出现F9状态,就是SCL=1,SDA=0,总线出现了异常,IIC从机死机不断电总线不可恢复
}IIC_STAT_Type;
typedef enum
{
IIC_OPER_ING = 0, //正常操作中
IIC_OPER_END = 1, //正常操作结束
IIC_OPER_NACK = 2, //未收到ACK,异常结束
IIC_OPER_ERROR = 3, //总线错误,异常结束
IIC_OPER_ARB_ERROR = 4, //总线仲裁错误,异常结束
}IIC_OPER_STATE;
//IIC句柄
typedef struct
{
IIC_CH_Type ch; //当前通道
IIC_TypeDef* IICx; //当前通道外设结构体
IIC_SPEED_TYPE Speed;
bool isMasterMode; //是否为主设备模式-目前只支持主设备模式
IIC_TRAN_TASK TranTask; //传输任务缓冲区
IIC_STAT_Type IntStatus; //中断状态
bool isDebug; //是否输出调试信息
IIC_OPER_STATE OperState; //当前操作状态,在中断中更新
}IIC_HANDLE;
//IIC通道句柄定义
static IIC_HANDLE sg_IIC_Handle[IIC_CH_COUNT];
//IIC外设结构指针
static const IIC_TypeDef* const scg_IICx_Base[IIC_CH_COUNT] = { IIC0,IIC1,IIC2,IIC3 };
static void IIC_IntCallBack(IIC_CH_Type ch);//IIC中断函数
#if(_IIC_INT_ENABLE) //中断方式
#include "irq_gic400.h"
static void IIC0_IRQHandler(void) {IIC_IntCallBack(0);} //IIC0中断回调
static void IIC1_IRQHandler(void) { IIC_IntCallBack(1); } //IIC1中断回调
static void IIC2_IRQHandler(void) { IIC_IntCallBack(2); } //IIC2中断回调
static void IIC3_IRQHandler(void) { IIC_IntCallBack(3); } //IIC3中断回调
static const GIC_IRQ_Typedef scg_IICIrqType[IIC_CH_COUNT] = { GIC_IRQ_I2C0, GIC_IRQ_I2C1, GIC_IRQ_I2C2, GIC_IRQ_I2C3 }; //中断编号
static const void* scg_pIICIrqHandle[IIC_CH_COUNT] = { (const void*)IIC0_IRQHandler,(const void*)IIC1_IRQHandler,(const void*)IIC2_IRQHandler,(const void*)IIC3_IRQHandler}; //中断服务程序
#endif //_IIC_INT_ENABLE
/*************************************************************************************************************************
* 函数 : static IIC_STAT_Type IIC_WaitStatus(IIC_HANDLE* pHandle)
* 功能 : 等待IIC中断有效
* 参数 : pHandle:句柄
* 返回 : IIC_STAT_Type
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2022-01-18
* 最后修改时间: 2022-01-18
* 说明 :
*************************************************************************************************************************/
static IIC_STAT_Type IIC_WaitStatus(IIC_HANDLE* pHandle)
{
return pHandle->IICx->STAT;
}
//使能ACK
void IIC_EnableACK(IIC_HANDLE* pHandle)
{
pHandle->IICx->CNTR |= BIT2; //收到数据后自动发送ACK
}
//禁止ACK
void IIC_DisableACK(IIC_HANDLE *pHandle)
{
pHandle->IICx->CNTR &= ~BIT2; //收到数据后不自动发送ACK
}
//发送STOP
void IIC_SendStop(IIC_HANDLE* pHandle)
{
//pHandle->IICx->CNTR |= BIT3; //清除中断标志
pHandle->IICx->CNTR |= BIT4; //发送完成后将会自动清零
}
//发送START
void IIC_SendStart(IIC_HANDLE *pHandle)
{
pHandle->IICx->CNTR |= BIT3; //清除中断标志
pHandle->IICx->CNTR |= BIT5; //发送完成后将会自动清零
}
//IIC软复位
void IIC_SoftReset(IIC_HANDLE* pHandle)
{
u16 TimeOut = 10;
pHandle->IICx->SRST = 1; //复位
while (pHandle->IICx->SRST && TimeOut)
{
TimeOut--;
SYS_DelayMS(1);
}
if (pHandle->IICx->SRST)
{
ERROR_S("IIC%d软复位超时\r\n", pHandle->ch);
}
}
/*************************************************************************************************************************
* 函数 : bool IIC_Init(IIC_CH_Type ch, IIC_SPEED_TYPE Speed, u16 TimeOutUs)
* 功能 : 硬件IIC初始化
* 参数 : ch:IIC通道;Speed_KHz:速度;TimeOutUs:操作超时us(兼容性参数,无作用)
* 返回 : IIC_ERROR
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2024-07-18
* 最后修改时间 : 2024-07-18
* 说明 : 速度只是个大概的计算值,正常情况下只要时钟速度符合要求,
时钟上升沿到来前数据以及稳定的切换了即可保证通讯稳定性
*************************************************************************************************************************/
bool IIC_Init(IIC_CH_Type ch, IIC_SPEED_TYPE Speed, u16 TimeOutUs)
{
IIC_HANDLE *pHandle;
switch (ch)
{
case IIC_CH0: //IIC0
{
r_TWI_BGR_REG |= BIT0; //IIC0时钟使能
r_TWI_BGR_REG &= ~BIT16; //IIC0复位
nop; nop; nop; nop;
r_TWI_BGR_REG |= BIT16; //IIC0复位结束
}break;
case IIC_CH1: //IIC1
{
r_TWI_BGR_REG |= BIT1; //IIC1时钟使能
r_TWI_BGR_REG &= ~BIT17; //IIC1复位
nop; nop; nop; nop;
r_TWI_BGR_REG |= BIT17; //IIC1复位结束
}break;
case IIC_CH2: //IIC2
{
r_TWI_BGR_REG |= BIT2; //IIC2时钟使能
r_TWI_BGR_REG &= ~BIT18; //IIC2复位
nop; nop; nop; nop;
r_TWI_BGR_REG |= BIT18; //IIC2复位结束
}break;
case IIC_CH3: //IIC3
{
r_TWI_BGR_REG |= BIT3; //IIC3时钟使能
r_TWI_BGR_REG &= ~BIT19; //IIC3复位
nop; nop; nop; nop;
r_TWI_BGR_REG |= BIT19; //IIC3复位结束
}break;
default:
{
ERROR_S("初始化IIC失败:无效的IIC通道%d\r\n", ch);
return FALSE;
}
}
pHandle = &sg_IIC_Handle[ch]; //获取相关通道的句柄
pHandle->IICx = (IIC_TypeDef *)scg_IICx_Base[ch]; //外设指针
pHandle->Speed = Speed; //记录速度
pHandle->ch = ch; //记录通道
pHandle->isDebug = FALSE;
pHandle->IntStatus = pHandle->IICx->STAT;
pHandle->IICx->CNTR = 0; //总线禁止
switch (Speed) //时钟设置
{
case IIC_SPEED_200KHz:
{
pHandle->IICx->CCR = BIT7 | ((6 - 1) << 3) | (1 << 0); //CLK_N=1;CLK_M=5,12MHz/60
}break;
case IIC_SPEED_300KHz:
{
pHandle->IICx->CCR = BIT7 | ((4 - 1) << 3) | (1 << 0); //CLK_N=1;CLK_M=5,12MHz/40
}break;
case IIC_SPEED_400KHz:
{
pHandle->IICx->CCR = BIT7 | ((3 - 1) << 3) | (1 << 0); //CLK_N=1;CLK_M=5,12MHz/30
}break;
default: //100KHz
{
pHandle->IICx->CCR = BIT7 | ((12 - 1) << 3) | (1 << 0); //CLK_N=1;CLK_M=5,12MHz/120
}break;
}
pHandle->IICx->LCR = 0;
pHandle->IICx->CNTR = BIT6; //使能IIC
IIC_SoftReset(pHandle); //进行软复位
memset(&pHandle->TranTask, 0, sizeof(IIC_TRAN_TASK)); //任务缓冲区清零
#if(_IIC_INT_ENABLE) //使用中断模式
GIC_SetIrqPriority(scg_IICIrqType[ch], 3); //GIC设置一个中断的优先级
GIC_SetIrqEdgeTriggered(scg_IICIrqType[ch], TRUE); //GIC设置一个中断为边沿触发
GIC_RegisterIRQHandler(scg_IICIrqType[ch], (void (*)(void))scg_pIICIrqHandle[ch]); //注册中断服务程序
GIC_IrqEnable(scg_IICIrqType[ch], TRUE); //GIC中断使能-中断使能
#else //查询模式
#endif //_IIC_INT_ENABLE
return TRUE;
}
/*************************************************************************************************************************
* 函数 : static IIC_ERROR IIC_WaitCompletion(IIC_HANDLE* pHandle)
* 功能 : 等待IIC通讯完成
* 参数 : pHandle:句柄
* 返回 : IIC_ERROR
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2024-07-18
* 最后修改时间: 2024-07-18
* 说明 :
*************************************************************************************************************************/
static IIC_ERROR IIC_WaitCompletion(IIC_HANDLE* pHandle)
{
IIC_ERROR Error = IIC_OK;
u32 TimeOutMs = (pHandle->TranTask.NeedCount+5)*10 / 100;
if (TimeOutMs < 2) TimeOutMs = 2;
#if(_IIC_INT_ENABLE) //使用中断模式
u32 i;
if (pHandle->isDebug) //如果开启了调试模式,多给超时时间
{
TimeOutMs += pHandle->TranTask.NeedCount + 50;
}
for (i = 0; i < TimeOutMs; i++)
{
SYS_DelayMS(1); //延时1ms等待结果
if (pHandle->OperState != IIC_OPER_ING) //结束了
{
IIC_SendStop(pHandle); //发送停止
switch (pHandle->OperState)
{
case IIC_OPER_NACK: //未收到ACK,异常结束
{
Error = IIC_NACK;
goto end_loop;
}
case IIC_OPER_ERROR: //总线错误,异常结束
{
Error = IIC_HAL_ERROR;
goto end_loop;
}
case IIC_OPER_ARB_ERROR: //总线仲裁错误,异常结束
{
Error = IIC_HAL_ERROR;
goto end_loop;
}
case IIC_OPER_END: //传输完成了
{
if (pHandle->TranTask.TranCount == pHandle->TranTask.NeedCount) //数据读取完成了
{
Error = IIC_OK;
goto end_loop;
}
else
{
ERROR_S("IIC%d超时,传输完成,但是数据不对:%d!=!d\r\n", pHandle->ch, pHandle->TranTask.TranCount, pHandle->TranTask.NeedCount);
Error = IIC_STOP_ERROR;
goto end_loop;
}
}
default:
{
Error = IIC_STOP_ERROR; break;
goto end_loop;
}
}
}
}
//超时了
ERROR_S("IIC%d超时,中断状态:0x%02X 状态机:%d\r\n", pHandle->ch, pHandle->IICx->STAT, pHandle->TranTask.NextOperate);
IIC_SendStop(pHandle); //发送停止
Error = IIC_TIMEOUT;
goto end_loop;
#else //查询模式
u64 RunTime;
if (pHandle->isDebug) //如果开启了调试模式,多给超时时间
{
TimeOutMs += pHandle->TranTask.NeedCount + 50;
}
RunTime = SYS_GetOSRunTime() + TimeOutMs; //计算超时时间
while (1)
{
while (((pHandle->IICx->CNTR & BIT3) == 0 || pHandle->IICx->STAT == 0) && (pHandle->OperState == IIC_OPER_ING)) //等待中断,没有错误或结束的前提下
{
if (SYS_GetOSRunTime() > RunTime)
{
ERROR_S("IIC%d超时,中断状态:0x%02X 状态机:%d\r\n", pHandle->ch, pHandle->IICx->STAT, pHandle->TranTask.NextOperate);
IIC_SendStop(pHandle); //发送停止
Error = IIC_TIMEOUT;
goto end_loop;
}
}
if (pHandle->OperState == IIC_OPER_ING)
{
IIC_IntCallBack(pHandle->ch); //执行中断
}
else //报错或结束了
{
IIC_SendStop(pHandle); //发送停止
switch (pHandle->OperState)
{
case IIC_OPER_NACK: //未收到ACK,异常结束
{
Error = IIC_NACK;
goto end_loop;
}
case IIC_OPER_ERROR: //总线错误,异常结束
{
Error = IIC_HAL_ERROR;
goto end_loop;
}
case IIC_OPER_ARB_ERROR: //总线仲裁错误,异常结束
{
Error = IIC_HAL_ERROR;
goto end_loop;
}
case IIC_OPER_END: //传输完成了
{
if (pHandle->TranTask.TranCount == pHandle->TranTask.NeedCount) //数据读取完成了
{
Error = IIC_OK;
goto end_loop;
}
else
{
ERROR_S("IIC%d超时,传输完成,但是数据不对:%d!=!d\r\n", pHandle->ch, pHandle->TranTask.TranCount, pHandle->TranTask.NeedCount);
Error = IIC_STOP_ERROR;
goto end_loop;
}
}
default:
{
Error = IIC_STOP_ERROR; break;
goto end_loop;
}
}
}
if (SYS_GetOSRunTime() > RunTime)
{
ERROR_S("IIC%d超时,中断状态:0x%02X 状态机:%d\r\n", pHandle->ch, pHandle->IICx->STAT, pHandle->TranTask.NextOperate);
IIC_SendStop(pHandle); //发送停止
Error = IIC_TIMEOUT;
goto end_loop;
}
}
#endif //_IIC_INT_ENABLE
end_loop:
return Error;
}
/*************************************************************************************************************************
* 函数 : IIC_ERROR IIC_MasterReadReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr,
u8 *pDataBuff, u16 ReadByteNum)
* 功能 : IIC读取寄存器(可以读取1个或者多个寄存器)
* 参数 : ch:IIC通道,见IIC_CH_Type;SlaveAddr:从机地址;RegAddr:要读取的寄存器地址;is8bitRegAddr:TRUE:8BIT寄存器地址,FALSE:16bit寄存器地址;
pDataBuff:接收的字节数据缓冲区;ReadByteNum:要读取的寄存器数量;
* 返回 : IIC_ERROR
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2024-07-20
* 最后修改时间 : 2024-07-20
* 说明 : 测试发现HYM8563会异常,出现中断状态0xF9 SCL=1 SDA=0,复位芯片都不行,SDA一直为0
*************************************************************************************************************************/
IIC_ERROR IIC_MasterReadReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr, u8* pDataBuff, u16 ReadByteNum)
{
IIC_ERROR Error = IIC_OK;
IIC_HANDLE* pHandle;
if (ch > (IIC_CH_COUNT - 1) || pDataBuff == NULL || ReadByteNum == 0)
{
DEBUG("IIC错误:无效的参数\r\n");
return IIC_PARAMETER_ERROR;
}
pHandle = &sg_IIC_Handle[ch]; //获取相关通道的句柄
pHandle->IICx->CNTR |= BIT3; //清除中断标志
if (pHandle->IICx->CNTR & BIT4) //如果停止位还没发送完成,等待一下
{
INFO_S("IIC%d上次停止位还未发送完成\r\n", pHandle->ch);
SYS_DelayMS(1);
}
//获取总线状态
if (pHandle->IICx->STAT != IIC_STAT_NOT_STATUS)
{
INFO_S("IIC%d总线故障,进行复位\r\n", pHandle->ch);
IIC_SoftReset(pHandle);
SYS_DelayMS(1);
}
if (TRUE == pHandle->TranTask.isBusy)
{
ERROR_S("IIC%d任务忙,无法进行发送操作\r\n", pHandle->ch);
return IIC_BUSY;
}
//准备好任务
pHandle->TranTask.isBusy = TRUE; //忙
pHandle->TranTask.SlaveAddr = SlaveAddr; //从设备地址,7bit或者10bit
pHandle->TranTask.is10BitSlaveAddr = (SlaveAddr & IIC_10BIT_ADD_MASK) ? TRUE : FALSE; //是否为10bit地址,否则为7bit
pHandle->TranTask.RegAddr = RegAddr; //寄存器地址
pHandle->TranTask.is8bitRegAddr = is8bitRegAddr; //是否为8bit寄存器地址,否则为16bit
pHandle->TranTask.NeedCount = ReadByteNum; //要传输的数据数量
pHandle->TranTask.pDataBuff = pDataBuff; //要传输的数据缓冲区
pHandle->TranTask.TranCount = 0; //已经传输的数据计数
pHandle->TranTask.isRead = TRUE; //是否为读取数据,否则为写入数据
pHandle->TranTask.NextOperate = IIC_NEXT_TASK_WAIT_START; //下一个状态为,等待起始信号发送完成
pHandle->OperState = IIC_OPER_ING; //通讯中
pHandle->IICx->EFR = 0;
if (pHandle->TranTask.NeedCount > 1) //需要读取多个字节,则使能自动ACK
{
IIC_EnableACK(pHandle); //使能读取数据后需要发送ACK
}
else
{
IIC_DisableACK(pHandle); //读取一个字节,不需要发送ACK
}
pHandle->IntStatus = pHandle->IICx->STAT; //获取中断状态
pHandle->IICx->CNTR |= BIT3; //清除中断标志
#if(_IIC_INT_ENABLE) //使用中断模式
pHandle->IICx->CNTR |= BIT7; //使能中断
#else //查询模式
pHandle->IICx->CNTR &= ~BIT7; //关闭中断
#endif //_IIC_INT_ENABLE
if (pHandle->IICx->STAT != IIC_STAT_NOT_STATUS)
{
INFO_S("IIC%d中断状态:0x%02X SCL=%d,SDA=%d\r\n", pHandle->ch, pHandle->IntStatus,
(pHandle->IICx->LCR & BIT5) ? 1 : 0, (pHandle->IICx->LCR & BIT4) ? 1 : 0);
}
IIC_SendStart(pHandle); //发送开始
Error = IIC_WaitCompletion(pHandle); //等待IIC通讯完成
#if(_IIC_INT_ENABLE) //使用中断模式
pHandle->IICx->CNTR &= ~BIT7; //关闭中断
#endif //_IIC_INT_ENABLE
pHandle->TranTask.isBusy = FALSE; //空闲
return Error;
}
/*************************************************************************************************************************
* 函数 : IIC_ERROR IIC_MasterWriteReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr,
u8 *pDataBuff, u16 WriteByteNum)
* 功能 : IIC写寄存器(可以写1个或者多个寄存器)
* 参数 : ch:IIC通道,见IIC_CH_Type;SlaveAddr:从机地址;RegAddr:要写入的寄存器地址;is8bitRegAddr:TRUE:8BIT寄存器地址,FALSE:16bit寄存器地址;
pDataBuff:写入的字节数据缓冲区;WriteByteNum:要写入的寄存器数量;
* 返回 : IIC_ERROR
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2024-07-20
* 最后修改时间 : 2024-07-20
* 说明 : 写入的数据都是小端模式,如果是16bit的寄存器,请写入偶数个数据,并且需要提前进行高低字节对调最后组成高字节在前的数据buff
*************************************************************************************************************************/
IIC_ERROR IIC_MasterWriteReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr, u8* pDataBuff, u16 WriteByteNum)
{
IIC_ERROR Error = IIC_OK;
IIC_HANDLE* pHandle;
if (ch > (IIC_CH_COUNT - 1) || pDataBuff == NULL || WriteByteNum == 0)
{
DEBUG("IIC错误:无效的参数\r\n");
return IIC_PARAMETER_ERROR;
}
pHandle = &sg_IIC_Handle[ch]; //获取相关通道的句柄
if (pHandle->IICx->CNTR & BIT4) //如果停止位还没发送完成,等待一下
{
INFO_S("IIC%d上次停止位还未发送完成\r\n", pHandle->ch);
SYS_DelayMS(1);
}
pHandle->IICx->CNTR |= BIT3; //清除中断标志
//获取总线状态
if (pHandle->IICx->STAT != IIC_STAT_NOT_STATUS)
{
INFO_S("IIC%d总线故障,进行复位\r\n", pHandle->ch);
IIC_SoftReset(pHandle);
SYS_DelayMS(1);
}
if (TRUE == pHandle->TranTask.isBusy)
{
ERROR_S("IIC%d任务忙,无法进行发送操作\r\n", pHandle->ch);
return IIC_BUSY;
}
//准备好任务
pHandle->TranTask.isBusy = TRUE; //忙
pHandle->TranTask.SlaveAddr = SlaveAddr; //从设备地址,7bit或者10bit
pHandle->TranTask.is10BitSlaveAddr = (SlaveAddr & IIC_10BIT_ADD_MASK) ? TRUE : FALSE; //是否为10bit地址,否则为7bit
pHandle->TranTask.RegAddr = RegAddr; //寄存器地址
pHandle->TranTask.is8bitRegAddr = is8bitRegAddr; //是否为8bit寄存器地址,否则为16bit
pHandle->TranTask.NeedCount = WriteByteNum; //要传输的数据数量
pHandle->TranTask.pDataBuff = pDataBuff; //要传输的数据缓冲区
pHandle->TranTask.TranCount = 0; //已经传输的数据计数
pHandle->TranTask.isRead = FALSE; //是否为读取数据,否则为写入数据
pHandle->TranTask.NextOperate = IIC_NEXT_TASK_WAIT_START; //下一个状态为,等待起始信号发送完成
pHandle->OperState = IIC_OPER_ING; //通讯中
pHandle->IICx->EFR = 0;
if (pHandle->TranTask.NeedCount > 1) //需要读取多个字节,则使能自动ACK
{
IIC_EnableACK(pHandle); //使能读取数据后需要发送ACK
}
else
{
IIC_DisableACK(pHandle); //读取一个字节,不需要发送ACK
}
pHandle->IntStatus = pHandle->IICx->STAT; //获取中断状态
pHandle->IICx->CNTR |= BIT3; //清除中断标志
#if(_IIC_INT_ENABLE) //使用中断模式
pHandle->IICx->CNTR |= BIT7; //使能中断
#else //查询模式
pHandle->IICx->CNTR &= ~BIT7; //关闭中断
#endif //_IIC_INT_ENABLE
if (pHandle->IICx->STAT != IIC_STAT_NOT_STATUS)
{
INFO_S("IIC%d中断状态:0x%02X SCL=%d,SDA=%d\r\n", pHandle->ch, pHandle->IntStatus,
(pHandle->IICx->LCR & BIT5) ? 1 : 0, (pHandle->IICx->LCR & BIT4) ? 1 : 0);
}
IIC_SendStart(pHandle); //发送开始
Error = IIC_WaitCompletion(pHandle); //等待IIC通讯完成
#if(_IIC_INT_ENABLE) //使用中断模式
pHandle->IICx->CNTR &= ~BIT7; //关闭中断
#endif //_IIC_INT_ENABLE
pHandle->TranTask.isBusy = FALSE; //空闲
return Error;
}
/*************************************************************************************************************************
* 函数 : IIC_ERROR IIC_MasterReadReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr,
u8 *pDataBuff, u16 ReadByteNum)
* 功能 : IIC读取寄存器(可以读取1个或者多个寄存器)
* 参数 : ch:IIC通道,见IIC_CH_Type;SlaveAddr:从机地址;RegAddr:要读取的寄存器地址;is8bitRegAddr:TRUE:8BIT寄存器地址,FALSE:16bit寄存器地址;
pDataBuff:接收的字节数据缓冲区;ReadByteNum:要读取的寄存器数量;
* 返回 : IIC_ERROR
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-11-03
* 最后修改时间 : 2020-11-03
* 说明 : 测试发现HYM8563会异常,出现中断状态0xF9 SCL=1 SDA=0,复位芯片都不行,SDA一直为0
*************************************************************************************************************************/
void IIC_IntCallBack(IIC_CH_Type ch)
{
IIC_HANDLE* pHandle;
u8 data;
if (ch > (IIC_CH_COUNT - 1))
{
return;
}
pHandle = &sg_IIC_Handle[ch]; //获取相关通道的句柄
pHandle->IntStatus = pHandle->IICx->STAT; //获取中断状态
switch (pHandle->IntStatus)
{
case IIC_STAT_BUSS_ERR :// 0x00, //总线错误
{
pHandle->OperState = IIC_OPER_ERROR; //总线错误,可以结束了
}break;
case IIC_STAT_START:// 0x08, //START条件已传输
{
//写入地址,如果是10bit,就先发送高地址
if (pHandle->TranTask.is10BitSlaveAddr)
{
data = pHandle->TranTask.SlaveAddr >> 8; //写高2bit地址
data <<= 1; //最低位为0,写信号
pHandle->IICx->DATA = data; //发送数据
if (pHandle->isDebug) INFO_S("IIC%d发送高位从机地址(写):0x%02X\r\n", pHandle->ch, data);
pHandle->TranTask.NextOperate = IIC_NEXT_TASK_WAIT_10BIT_ADDR_H; //等待10bit从机高地址发送完成-写入
}
else //直接发送7bit地址
{
data = ((pHandle->TranTask.SlaveAddr & 0x7F) << 1) | 0; //写7bit地址
pHandle->IICx->DATA = data; //发送数据
if (pHandle->isDebug) INFO_S("IIC%d发送7bit从机地址(写):0x%02X\r\n", pHandle->ch, data);
pHandle->TranTask.NextOperate = IIC_NEXT_TASK_WAIT_7BIT_ADDR; //等待7bit从机地址发送完成-写入
}
}break;
case IIC_STAT_RSTART:// 0x10, //发送重复START条件,后面发送从机地址,10bit就发送高2bit,7bit就发送低7bit,并且最低bit为1,代表读取
{
if (pHandle->TranTask.is10BitSlaveAddr) //如果是10bti地址就是发送高2bit
{
data = pHandle->TranTask.SlaveAddr >> 8;
data <<= 1;
data |= BIT0; //读信号
pHandle->IICx->DATA = data; //发送数据
if (pHandle->isDebug) INFO_S("IIC%d重复发送高2bit从机地址(读):0x%02X\r\n", pHandle->ch, data);
}
else //7bit地址,直接发送
{
data = ((pHandle->TranTask.SlaveAddr & 0x7F) << 1) | BIT0; //7bit地址
pHandle->IICx->DATA = data; //发送数据
if (pHandle->isDebug) INFO_S("IIC%d重复发送7bit从机地址(读):0x%02X\r\n", pHandle->ch, data);
}
pHandle->TranTask.NextOperate = IIC_NEXT_TASK_READ_DATA; //后面开始读取数据了
if (pHandle->TranTask.TranCount < (pHandle->TranTask.NeedCount - 1))
{
IIC_EnableACK(pHandle); //读取数据后需要发送ACK
}
else
{
IIC_DisableACK(pHandle); //当前是最后一个字节,读取完成后发送NACK
}
}break;
case IIC_STAT_AW_ACK:// 0x18, //发送地址 + 写入位,收到ACK
{
if (pHandle->TranTask.is10BitSlaveAddr) //如果是10bit地址,则写低8bit地址
{
data = pHandle->TranTask.SlaveAddr; //低8bit地址
pHandle->IICx->DATA = data; //发送数据
if (pHandle->isDebug) INFO_S("IIC%d发送低8bit从机地址:0x%02X\r\n", pHandle->ch, data);
pHandle->TranTask.NextOperate = IIC_NEXT_TASK_WAIT_7BIT_ADDR; //等待7bit从机地址发送完成-写入
}
else //否则开始写入寄存器地址,寄存器地址又分为16bit还是8bit
{
if (pHandle->TranTask.is8bitRegAddr) //8bit寄存器地址,只需要发一次即可
{
data = pHandle->TranTask.RegAddr;
pHandle->IICx->DATA = data; //发送数据
if (pHandle->isDebug) INFO_S("IIC%d发送8bit寄存器地址:0x%02X\r\n", pHandle->ch, data);
pHandle->TranTask.NextOperate = IIC_NEXT_TASK_WAIT_8BIT_REG; //等待发送8bit寄存器地址
}
else //需要先发送高8bit地址
{
data = pHandle->TranTask.RegAddr>>8;
pHandle->IICx->DATA = data; //发送数据
if (pHandle->isDebug) INFO_S("IIC%d发送16bit寄存器地址高8bit:0x%02X\r\n", pHandle->ch, data);
pHandle->TranTask.NextOperate = IIC_NEXT_TASK_WAIT_16BIT_REG_H; //等待发送16bit寄存器地址高位
}
}
}break;
case IIC_STAT_AW_NACK:// 0x20, //发送地址 + 写入位,未收到ACK
{
pHandle->OperState = IIC_OPER_NACK; //没有收到ACK,可以结束了
}break;
case IIC_STAT_WDATA_ACK:// 0x28, //主模式下发送的数据字节,收到ACK
{
switch (pHandle->TranTask.NextOperate)
{
case IIC_NEXT_TASK_WAIT_16BIT_REG_H: //等待发送16bit寄存器地址高位-发完了,就要开始发低8bit寄存器地址了
{
data = pHandle->TranTask.RegAddr;
pHandle->IICx->DATA = data; //发送数据
if (pHandle->isDebug) INFO_S("IIC%d发送低8bit寄存器地址:0x%02X\r\n", pHandle->ch, data);
pHandle->TranTask.NextOperate = IIC_NEXT_TASK_WAIT_8BIT_REG; //等待发送8bit寄存器地址
}break;
case IIC_NEXT_TASK_WAIT_8BIT_REG: //等待低8bit地址发送完成,发送完成了就要根据读写来决定下一步操作了
{
if (pHandle->TranTask.isRead) //读取数据,那么就还要发送一次开始信号
{
IIC_SendStart(pHandle); //发送重复的开始信号
if (pHandle->isDebug) INFO_S("IIC%d重复发送开始信号\r\n", pHandle->ch);
pHandle->TranTask.NextOperate = IIC_NEXT_TASK_WAIT_RSTART; //等待重复开始信号
}
else //是写入数据,则直接可以开始写入第一个字节啦
{
data = pHandle->TranTask.pDataBuff[pHandle->TranTask.TranCount++];
pHandle->IICx->DATA = data;
if (pHandle->isDebug) INFO_S("IIC%d写入数据:0x%02X(%d)\r\n", pHandle->ch, data, pHandle->TranTask.TranCount);
pHandle->TranTask.NextOperate = IIC_NEXT_TASK_WAIT_WRITE_DATA; //等待发送完成,正常会收到ACK,收到ACK后发送下一个字节
}
}break;
case IIC_NEXT_TASK_WAIT_WRITE_DATA: //发送数据收到ACK了
{
if (pHandle->TranTask.TranCount < pHandle->TranTask.NeedCount) //还要继续写入
{
data = pHandle->TranTask.pDataBuff[pHandle->TranTask.TranCount++];
pHandle->IICx->DATA = data;
if (pHandle->isDebug) INFO_S("IIC%d写入数据:0x%02X(%d)\r\n", pHandle->ch, data, pHandle->TranTask.TranCount);
pHandle->TranTask.NextOperate = IIC_NEXT_TASK_WAIT_WRITE_DATA; //等待发送完成,正常会收到ACK,收到ACK后发送下一个字节
}
else //发送完成了,发送stop
{
pHandle->TranTask.NextOperate = IIC_NEXT_TASK_END;
IIC_SendStop(pHandle); //发送停止
pHandle->OperState = IIC_OPER_END; //传输完成了
if (pHandle->isDebug) INFO_S("IIC%d写入数据完成,发送停止位\r\n");
}
}break;
default: break; //正常不应该执行到此处
}
}break;
case IIC_STAT_WDATA_NACK:// 0x30, //主模式下传输的数据字节,未收到ACK
{
pHandle->OperState = IIC_OPER_NACK; //没有收到ACK,可以结束了
}break;
case IIC_STAT_ARB_ERR:// 0x38, //仲裁在地址或数据字节中丢失
{
pHandle->OperState = IIC_OPER_ARB_ERROR; //总线仲裁错误,可以结束了
}break;
case IIC_STAT_AR_ACK:// 0x40, //发送地址 + 读取位,收到ACK
{
//开始准备读取数据了,这一步是正常的,啥都不用干,后面清除中断, 之后等待下次中断,就可以读取数据了
}break;
case IIC_STAT_AR_NACK:// 0x48, //发送地址 + 读取位,未收到ACK
{
pHandle->OperState = IIC_OPER_NACK; //没有收到ACK,可以结束了
}break;
case IIC_STAT_RDATA_ACK:// 0x50, //主模式下接收到数据字节,发送ACK
{
data = pHandle->IICx->DATA;
pHandle->TranTask.pDataBuff[pHandle->TranTask.TranCount++] = data;
if (pHandle->isDebug) INFO_S("IIC%d读取数据:0x%02X(%d)\r\n", pHandle->ch, data, pHandle->TranTask.TranCount);
if (pHandle->TranTask.TranCount < (pHandle->TranTask.NeedCount - 1))
{
IIC_EnableACK(pHandle); //读取数据后需要发送ACK
}
else
{
IIC_DisableACK(pHandle); //当前是最后一个字节,读取完成后发送NACK
}
pHandle->TranTask.NextOperate = IIC_NEXT_TASK_READ_DATA;
}break;
case IIC_STAT_RDATA_NACK:// 0x58, //主模式下接收到数据字节,未发送ACK
{
data = pHandle->IICx->DATA;
pHandle->TranTask.pDataBuff[pHandle->TranTask.TranCount++] = data;
if (pHandle->isDebug) INFO_S("IIC%d读取最后数据:0x%02X(%d)\r\n", pHandle->ch, data, pHandle->TranTask.TranCount);
pHandle->TranTask.NextOperate = IIC_NEXT_TASK_END;
IIC_SendStop(pHandle); //发送停止
pHandle->OperState = IIC_OPER_END; //传输完成了
}break;
case IIC_STAT_A2W_ACK:// 0xD0, //第二地址字节 + 发送写入位,收到ACK
{
//写入寄存器地址,分为16bit还是8bit寄存器地址
if (pHandle->TranTask.is8bitRegAddr) //8bit寄存器地址,只需要发一次即可
{
data = pHandle->TranTask.RegAddr;
pHandle->IICx->DATA = data; //发送数据
if (pHandle->isDebug) INFO_S("IIC%d发送8bit寄存器地址:0x%02X\r\n", pHandle->ch, data);
pHandle->TranTask.NextOperate = IIC_NEXT_TASK_WAIT_8BIT_REG; //等待发送8bit寄存器地址
}
else //需要先发送高8bit地址
{
data = pHandle->TranTask.RegAddr >> 8;
pHandle->IICx->DATA = data; //发送数据
if (pHandle->isDebug) INFO_S("IIC%d发送16bit寄存器地址高8bit:0x%02X\r\n", pHandle->ch, data);
pHandle->TranTask.NextOperate = IIC_NEXT_TASK_WAIT_16BIT_REG_H; //等待发送16bit寄存器地址高位
}
}break;
case IIC_STAT_A2W_NACK:// 0xD8, //第二个地址字节 + 写入位已发送,ACK未发送收到
{
pHandle->OperState = IIC_OPER_NACK; //没有收到ACK,可以结束了
}break;
case IIC_STAT_NOT_STATUS:// 0xF8, //无相关状态信息,INT_FLAG = 0
{
}break;
case IIC_STAT_IIC_ERROR:// 0xF9, //实测的时候出现F9状态,就是SCL=1,SDA=0,总线出现了异常,IIC从机死机不断电总线不可恢复
{
pHandle->OperState = IIC_OPER_ERROR; //总线错误,可以结束了
}break;
default:
{
}break;
}
pHandle->IICx->CNTR |= BIT3; //清除中断标志
}
然后是测试代码
//T113-S3 IIC测试
void IIC_Test(void)
{
IIC_ERROR iic_error;
u8 buff[32];
IIC_Init(IIC_CH2, IIC_SPEED_400KHz, 500);//硬件IIC初始化
SYS_GPIOx_OneInit(GPIOG, 14, AF_FUN, 3, IO_LEVEL0); //PG14 IIC2 SCL
SYS_GPIOx_OneInit(GPIOG, 15, AF_FUN, 3, IO_LEVEL0); //PG15 IIC2 SDA
buff[0] = 0; //测试写入HYM8563寄存器0
iic_error = IIC_MasterWriteReg(IIC_CH2, 0x51, 0X00, TRUE, buff, 1); //IIC写入取寄存器(可以写入1个或者多个寄存器)
if (iic_error == IIC_OK)
{
INFO_S("IIC2写入:0x%02X 完成\r\n", buff[0]);
}
else
{
INFO_S("IIC2写入失败,错误:%d\r\n", iic_error);
}
buff[0] = 0; //测试写入HYM8563寄存器0x0D
iic_error = IIC_MasterWriteReg(IIC_CH2, 0x51, 0X0D, TRUE, buff, 1); //IIC写入取寄存器(可以写入1个或者多个寄存器)
if (iic_error == IIC_OK)
{
INFO_S("IIC2写入:0x%02X 完成\r\n", buff[0]);
}
else
{
INFO_S("IIC2写入失败,错误:%d\r\n", iic_error);
}
buff[0] = 0; //清除数据
iic_error = IIC_MasterReadReg(IIC_CH2, 0x51, 0X00, TRUE, buff, 1); //IIC读取寄存器(可以读取1个或者多个寄存器)
if (iic_error == IIC_OK)
{
INFO_S("IIC2读取成功:0x%02X\r\n", buff[0]);
}
else
{
INFO_S("IIC2读取失败,错误:%d\r\n", iic_error);
}
while (1)
{
iic_error = IIC_MasterReadReg(IIC_CH2, 0x51, 0X02, TRUE, buff, 3); //IIC读取寄存器(可以读取1个或者多个寄存器)
if (iic_error == IIC_OK)
{
INFO_S("IIC2读取成功:0x%02X 0x%02X 0x%02X\r\n", buff[0], buff[1], buff[2]);
}
else
{
INFO_S("IIC2读取失败,错误:%d\r\n", iic_error);
}
SYS_DelayMS(1000);
}
}
测试是能正常读取数据,有正常的ACK,但是时间没有走,怀疑是RTC芯片挑晶振,之前使用linux的时候也是硬件RTC时间不走,最后换了芯片才解决。
GICC->->IIDR:0x202143B
[ 2.713414] [INFO] IIC2写入:0x00 完成
[ 2.713677] [INFO] IIC2写入:0x00 完成
[ 2.713993] [INFO] IIC2读取成功:0x00
[ 2.714359] [INFO] IIC2读取成功:0x00 0x00 0x01
[ 3.714503] [INFO] IIC2读取成功:0x00 0x00 0x01
[ 4.714502] [INFO] IIC2读取成功:0x00 0x00 0x01
[ 5.714503] [INFO] IIC2读取成功:0x00 0x00 0x01
[ 6.714504] [INFO] IIC2读取成功:0x00 0x00 0x01
[ 7.714502] [INFO] IIC2读取成功:0x00 0x00 0x01
[ 8.714502] [INFO] IIC2读取成功:0x00 0x00 0x01
[ 9.714502] [INFO] IIC2读取成功:0x00 0x00 0x01
[ 10.714501] [INFO] IIC2读取成功:0x00 0x00 0x01
[ 11.714503] [INFO] IIC2读取成功:0x00 0x00 0x01
如果需要详细的调试信息,可以开启调试
pHandle->isDebug = FALSE; //调试开关