调试总结—— IIC主从机交互驱动总结

以前玩IIC都是停留在EEPROM里,知道协议的流程,但是一旦使用的时候总是会忘记,理解的不够透彻。今天在做两块MCU的IIC通讯的时候,写个调试总结。

1.两个MCU通讯的理解

借用EEPROM的IIC时序图:
调试总结—— IIC主从机交互驱动总结_第1张图片
调试总结—— IIC主从机交互驱动总结_第2张图片
这两个时序只能说很有代表性,在两个MCU通讯的时候也可以借鉴这个时序图。
以下是本次的协议图:
调试总结—— IIC主从机交互驱动总结_第3张图片

把其中一个单片机设置为从机,一个单片机设置为主机,简单的通讯的话,不需要翻转两者的主从关系。也就是把第一个单片机当做EEPROM用

2.如何编写及调试

(1)编写主机MCU

直接按照普通IIC配置初始化,有的单片机是必须要配置成master mode。这时初始化配置slave 地址,即使配置了也是无效的。
主机调试注意点:

①IIC写函数中,最好要用示波器抓写的波形,千万要仔细对着时序图,查看高低电平,什么时候应该是什么电平要对比好,我就是在找没有ack回复问题的时候,才发现,起始SCL和SDA居然是低电平

从机的地址要配置好,有的写函数中调用的地址是偏移过以为的地址,有的则不是,所以我们要确认好我们从机配置的地址,注意:7位(我用的)的地址,我总把描述8位从机地址当做未偏移的从机地址,实际上只有高7位才是实际地址,最后一个是读写位。

③在IIC读写函数中,实际有很多的状态,我们可以的在不同状态下处理一些函数,比如华大的IIC发送函数,其中获取到的u8State都代表了不同状态,这个在数据手册是有描述的。

en_result_t I2C_MasterWriteData(M0P_I2C_TypeDef* I2CX,uint8_t *pu8Data,uint32_t u32Len,uint32_t Slave_addr)
{
    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);
				//printf("u8State = %x\n",u8State);	
        switch(u8State)
        {
					//step1:
            case 0x08:                                 ///已发送起始条件
                I2C_ClearFunc(I2CX,I2cStart_En);
                I2C_WriteByte(I2CX,Slave_addr);   ///从设备地址发送
                break;
						//step2:
            case 0x18:                                 ///已发送SLA+W,并接收到ACK
							//step3:
							//printf("ack\n");
            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;
}
en_result_t I2C_MasterReadData(M0P_I2C_TypeDef* I2CX,uint8_t *pu8Data,uint32_t u32Len,uint32_t Slave_addr)
{
    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,Slave_addr);//发送SLA+W
                break;
            case 0x18:                                    //已发送SLA+W,并接收到ACK
               // I2C_WriteByte(I2CX,0);                    //发送内存地址
              //  I2C_SetFunc(I2CX,I2cStop_En);
              I2C_SetFunc(I2CX,I2cStart_En);
								break;
            case 0x28:                                    //已发送数据,接收到ACK
                //I2C_SetFunc(I2CX,I2cStart_En);
                break;
            case 0x10:                                    //已发送重复起始条件
                I2C_ClearFunc(I2CX,I2cStart_En);
                I2C_WriteByte(I2CX,Slave_addr|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;
}

(2)编写从机MCU

从机MCU需要配置好从机地址,如果主机是写指令,初始化配置好后,一般会自动回复ACK。
但是数据的读取是需要从机发送的,什么时候发送数据,这个沟通的协议就要从机来进行配置。

从机调试注意点:

①如果是硬件的IIC,初始化ENABLE ACK会自动回复ACK

②从机配置是两个MCU协议最重要的地方,读写的协议都是根据从设备的配置来设定的。从设备主要是接收,只有在主设备读数据的时候,从设备才会调用发送函数,比如GD作为从机的中断函数:

void I2C0_EventIRQ_Handler(void)
{
  if(i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_ADDSEND))  //成功接受到了主设备发过来的地址
	{
    /* clear the ADDSEND bit */
    i2c_interrupt_flag_clear(I2C0, I2C_INT_FLAG_ADDSEND);
		I2C_rxbytes = 0;
	
		if(SUCCESS == rx_success) //第二次收到地址讯息,代表主设备要来读数据了
		{
			restart_num++;
			send_ok = 0;
			//if(restart_num == 2)
			//{
				//nvic_irq_disable(I2C0_EV_IRQn);
			//}
		}
		else
		{
			restart_num = 0;
		}
		iic_count = 0;
  }
	
	else if(i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_RBNE)) //主设备写数据,这边接收发过来的数据
 {
    restart_num = 0;
    /* if reception data register is not empty ,I2C0 will read a data from I2C_DATA */
    i2c_rxbuffer[I2C_rxbytes++] = i2c_data_receive(I2C0);
	I2C_rxbytes = I2C_rxbytes;//%I2C_nBytes;
  }
	
	else if((i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_TBE))&&(iic_count<I2C_txbytes))
	{
		if(restart_num == 2) 
   		 {
    		rx_success = 0;
    		/* send a data byte */
    		i2c_data_transmit(I2C0, i2c_txbuffer[iic_count++]); //主设备读取,从设备发送数据的地方
		}
 	 }
  	else if(i2c_interrupt_flag_get(I2C0, I2C_INT_FLAG_STPDET))//获取到停止信号的中断
	{
		rx_end = SUCCESS;	
		rx_success = SUCCESS;
		restart_num = 0;

		/////////////////////////////
		//I2C_txbytes = 0;
		//i2c_txbuffer = (void *)0;
		/////////////////////////////
    /* clear the STPDET bit */
    i2c_enable(I2C0);

你可能感兴趣的:(调试总结)