最近在学习华大的170处理器。该处理器是一款M0+的低功耗处理器。本着学啥记啥的原则写一下它的相关模块:
1、IIC模块
1.1 IIC基本概念
IIC是通过两根线实现的通讯机制,一根时钟线,一根信号线。通讯时序如图一所示。该部分通讯包括如下几部分:
1.1.1 起始信号。该信号是在CLK信号高的情况下数据信号由高变低,该信号是不会重复的,为什么呢?因为IIC规定CLK信号在高的时候DAT是不能变化的,当CLK变成低的时候DAT才能变化。
1.1.2 数据位。一般是8个数据位,当CLK信号在高的时候DAT是不能变化的,当CLK变成低的时候DAT才能变化。
1.1.3 ACK 。该位算是有一种反馈机制。当接收方接收一个字节后要将总线拉低作为一种反馈。
1.1.4停止位。该位和起始位刚好相反。当CLK在高的时候DAT由低到高。
图一 IIC时序图
1.2华大处理器的IIC
明白了IIC的基本机制以后第二个问题就来了。华大170是如何实现该功能的。
图二 华大170配置寄存器
图三 华大处理器状态码
图二和图三是华大170处理器要实现该功能主要参考的寄存器。我们先看图二,华大170每步都是可控的。我们来按照图一的时序梳理一遍图二的寄存器。当我们要发送数据的时候首先要发送起始位,对应的程序就要将图二中的BIT5置位,这样IIC模块就会发送一个起始信号到总线上。然后程序要等待终端标准会置1,这时候说明IIC模块执行了一个动作,但是执行了什么动作呢?这个要读取IIC的状态寄存器,返回值代表的意思参考图三,根据图三做后续动作。当状态值表示已经发送了起始位以后要发送从机地址寄存器了。该位是7位值,bit0是读写位(R:1;W:0)。要实现该动作首先要将图二中的起始标志位清零,然后再IIC数据寄存器中写入数据(7位地址,1位读写),然后将,最后最重要的是要将图二中的中断标志位清零,这个动作非常重要,手册也写的很清楚,如果不清零动作无法执行。然后就是其他数据的传送了。只要根据状态寄存器的值进行操作就好了。
1.3读写代码
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,AHT20_SLAVEADDR|0x01);
break;
case 0x10: ///< 已发送重复起始条件
I2C_ClearFunc(I2CX,I2cStart_En);
I2C_WriteByte(I2CX,AHT20_SLAVEADDR|0x01);///< 发送SLA+R,开始从从机读取数据
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;
}
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);
// I2C_ClearIrq(I2CX);
while(1)
{
while(0 == I2C_GetIrq(I2CX))
{;}
u8State = I2C_GetState(I2CX);
switch(u8State)
{
case 0x08: ///< 已发送起始条件
I2C_ClearFunc(I2CX,I2cStart_En);
I2C_WriteByte(I2CX,AHT20_SLAVEADDR); ///< 从设备地址发送
break;
case 0x18: ///< 已发送SLA+W,并接收到ACK
I2C_WriteByte(I2CX,pu8Data[u8i++]); ///< 继续发送数据
break;
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;
}
以上是我根据需要在官方给的例程基础上做的修改,大家可以参考一下框架,便于理解它的实现机制。以上就是华大170处理器 IIC模块操作的基本内容,以后用到那个模块会继续做补充。