小楠实习笔记(基于华大单片机的IIC)

写过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)停止信号;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值