写过51的应该应该都知道iic协议,通过sda和scl的拉高延时,等进行数据传输。
但是华大单片机的iic有点不太相同,接下来就主机发送代码进行分析。(历程库里的主机发收模式)
1.时钟设置
void App_ClkCfg(void)
{
Sysctrl_ClkSourceEnable(SysctrlClkRCL,TRUE);
Sysctrl_SysClkSwitch(SysctrlClkRCL);
Sysctrl_SetRCHTrim(SysctrlRchFreq24MHz); //配置为外部24MHz时钟
Sysctrl_SysClkSwitch(SysctrlClkRCH);
Sysctrl_ClkSourceEnable(SysctrlClkRCL,FALSE);
}
//这里是时钟设置
2.gpio的端口初始化
///< IO端口配置
void App_PortCfg(void)
{
stc_gpio_cfg_t stcGpioCfg;
DDL_ZERO_STRUCT(stcGpioCfg);
Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio,TRUE); //开启GPIO时钟门控
stcGpioCfg.enDir = GpioDirOut; ///< 端口方向配置->输出
stcGpioCfg.enOD = GpioOdEnable; ///< 开漏输出
stcGpioCfg.enPu = GpioPuEnable; ///< 端口上拉配置->使能
stcGpioCfg.enPd = GpioPdDisable; ///< 端口下拉配置->禁止
stcGpioCfg.bOutputVal = TRUE;
Gpio_Init(GpioPortB,GpioPin13,&stcGpioCfg); ///< 端口初始化
Gpio_Init(GpioPortB,GpioPin14,&stcGpioCfg);
Gpio_SetAfMode(GpioPortB,GpioPin13,GpioAf2); ///< 配置PB08为SCL
Gpio_SetAfMode(GpioPortB,GpioPin14,GpioAf2); ///< 配置PB09为SDA
}
这里需要设置为上拉开漏输出,复用功能是查数据手册可以得知,是复用功能2。
3.IIC初始化
void App_I2cCfg(void)
{
stc_i2c_cfg_t stcI2cCfg;
DDL_ZERO_STRUCT(stcI2cCfg); ///< 初始化结构体变量的值为0
Sysctrl_SetPeripheralGate(SysctrlPeripheralI2c0,TRUE); ///< 开启I2C0时钟门控
stcI2cCfg.u32Pclk = Sysctrl_GetPClkFreq(); ///< 获取PCLK时钟
stcI2cCfg.u32Baud = 1000000; ///< 1MHz
stcI2cCfg.enMode = I2cMasterMode; ///< 主机模式
stcI2cCfg.u8SlaveAddr = 0x55; ///< 从地址,主模式无效
stcI2cCfg.bGc = FALSE; ///< 广播地址应答使能关闭
I2C_Init(M0P_I2C0,&stcI2cCfg); ///< 模块初始化
}
调用库里定义好的结构体,对IIC进行设置。
4.收发函数
/**
******************************************************************************
** \brief 主机接收函数
**
** \param u8Addr从机内存地址,pu8Data读数据存放缓存,u32Len读数据长度
**
** \retval 读数据是否成功
**
******************************************************************************/
en_result_t I2C_MasterReadData(M0P_I2C_TypeDef* I2CX,uint8_t *pu8Data,uint32_t u32Len)
{
en_result_t enRet = Error;
uint8_t u8i=0,u8State;
I2C_SetFunc(I2CX,I2cStart_En);
while(1)
{
while(0 == I2C_GetIrq(I2CX))
{;}
u8State = I2C_GetState(I2CX);
switch(u8State)
{
case 0x08: //已发送起始条件,将发送SLA+R
I2C_ClearFunc(I2CX,I2cStart_En);
I2C_WriteByte(I2CX,(I2C_DEVADDR<<1)|0x01);//发送SLA+W
break;
case 0x18: //已发送SLA+W,并接收到ACK
I2C_WriteByte(I2CX,0); //发送内存地址
break;
case 0x28: //已发送数据,接收到ACK
I2C_SetFunc(I2CX,I2cStart_En);
break;
case 0x10: //已发送重复起始条件
I2C_ClearFunc(I2CX,I2cStart_En);
I2C_WriteByte(I2CX,(I2C_DEVADDR<<1)|0x01);//读命令发送
break;
case 0x40: //已发送SLA+R,并接收到ACK
if(u32Len>1)
{
I2C_SetFunc(I2CX,I2cAck_En);
}
break;
case 0x50: //已接收数据字节,并已返回ACK信号
pu8Data[u8i++] = I2C_ReadByte(I2CX);
if(u8i==u32Len-1)
{
I2C_ClearFunc(I2CX,I2cAck_En); //读数据时,倒数第二个字节ACK关闭
}
break;
case 0x58: //已接收到最后一个数据,NACK已返回
pu8Data[u8i++] = I2C_ReadByte(I2CX);
I2C_SetFunc(I2CX,I2cStop_En); //发送停止条件
break;
case 0x38: //在发送地址或数据时,仲裁丢失
I2C_SetFunc(I2CX,I2cStart_En); //当总线空闲时发起起始条件
break;
case 0x48: //发送SLA+R后,收到一个NACK
I2C_SetFunc(I2CX,I2cStop_En);
I2C_SetFunc(I2CX,I2cStart_En);
break;
default: //其他错误状态,重新发送起始条件
I2C_SetFunc(I2CX,I2cStart_En); //其他错误状态,重新发送起始条件
break;
}
I2C_ClearIrq(I2CX); //清除中断状态标志位
if(u8i==u32Len) //数据全部读取完成,跳出while循环
{
break;
}
}
enRet = Ok;
return enRet;
}
/**
******************************************************************************
** \brief 主机发送函数
**
** \param u8Addr从机内存地址,pu8Data写数据,u32Len写数据长度
**
** \retval 写数据是否成功
**
******************************************************************************/
en_result_t I2C_MasterWriteData(M0P_I2C_TypeDef* I2CX,uint8_t *pu8Data,uint32_t u32Len)
{
en_result_t enRet = Error;
uint8_t u8i=0,u8State;
I2C_SetFunc(I2CX,I2cStart_En);
while(1)
{
while(0 == I2C_GetIrq(I2CX))
{;}
u8State = I2C_GetState(I2CX);
switch(u8State)
{
case 0x08: ///已发送起始条件
I2C_ClearFunc(I2CX,I2cStart_En);
I2C_WriteByte(I2CX,(I2C_DEVADDR<<1)); ///从设备地址发送
break;
case 0x18: ///已发送SLA+W,并接收到ACK
case 0x28: ///上一次发送数据后接收到ACK
I2C_WriteByte(I2CX,pu8Data[u8i++]);
break;
case 0x20: ///上一次发送SLA+W后,收到NACK
case 0x38: ///上一次在SLA+读或写时丢失仲裁
I2C_SetFunc(I2CX,I2cStart_En); ///当I2C总线空闲时发送起始条件
break;
case 0x30: ///已发送I2Cx_DATA中的数据,收到NACK,将传输一个STOP条件
I2C_SetFunc(I2CX,I2cStop_En); ///发送停止条件
break;
default:
break;
}
if(u8i>u32Len)
{
I2C_SetFunc(I2CX,I2cStop_En); ///此顺序不能调换,出停止条件
I2C_ClearIrq(I2CX);
break;
}
I2C_ClearIrq(I2CX); ///清除中断状态标志位
}
enRet = Ok;
return enRet;
}
这里比较难理解,所以我们慢慢来看。
华大单片机不再是传统的IIC start()等函数。而是自定义了结构体。
typedef enum en_i2c_func
{
I2cModule_En = 6u, ///<I2C模块使能
I2cStart_En = 5u, ///<开始信号
I2cStop_En = 4u, ///<结束信号
I2cAck_En = 2u, ///<应答信号
I2cHlm_En = 0u, ///<高速使能
}en_i2c_func_t;
所以我们只需要设置功能即可,对应的函数为
en_result_t I2C_SetFunc(M0P_I2C_TypeDef* I2Cx,en_i2c_func_t enFunc);
M0P_I2C_TypeDef* I2Cx需要去查数据手册,看看用的是C0还是C1。PB13和14是C1,所以是M0P_I2C1。
开始发送后等待应答,然后检测状态。
uint8_t I2C_GetState(M0P_I2C_TypeDef* I2Cx);
查数据手册可得
相当于定义了状态机,然后跳转状态。下面是iic的收发模式。
IIC的收发流程(多字节)
1.发送模式
(1)开始信号;
(2)发送从机地址;
(3)等待应答;
(4)发送寄存器地址(指令字);
(5)等待应答;
(6)发送寄存器地址(数据);
(7)等待应答;
(8)停止信号;
2.接收模式
(1)开始信号;
(2)发送从机地址;
(3)等待应答;
(4)发送寄存器地址(指令字);
(5)等待应答;
(6)重新开始信号;
(7)改为读信号;
(8)等待应答;
(9)读数据;
(10)停止信号;