再问STM32的硬件I2C与软件模拟

2019-12-12 18:09发布

坛里的帖子看了很多,大致了解了STM32的硬件I2C的确有一些BUG,或者说使用时有很多不方便满足的要求,比如DMA加最高中断。所以思考是否使用模拟方式替代。但有种观点是模拟I2C太耗资源。我认真分析了下,其实大部分I2C读写操作在程序中都是顺序类型的,及当读、写I2C从机时,必须读、写到想要的数据才能继续往下执行,如果其中出错,还必须重新来过。所以不管是用硬件查询,中断,DMA等方式还是模拟方式,都必须等待从机完成,所花时间决定与I2C速度和从机响应速度。如果可以这么理解,是否可以说其实模拟方式没有任何风险,速度差不多,故而是最好的选择?

望对这个问题有经验的朋友们不吝赐教!
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
93条回答
godsend
1楼-- · 2019-12-18 11:00
本帖最后由 godsend 于 2017-3-11 09:06 编辑
int 发表于 2017-3-10 14:13
好的,谢谢老哥!


uint8_t I2C1_TransferFlag = 0;
uint8_t I2C1_Step = 0;
uint8_t I2C1_Mode = 0;
uint8_t I2C1_DeviceAddress = 0;
uint8_t I2C1_RegisterAddress = 0;
uint8_t I2C1_DeviceMeneyType = 0;
uint8_t I2C1_DataLength = 0;
uint8_t I2C1_RxBuff[I2C1_RxMax];
uint8_t I2C1_TxBuff[I2C1_TxMax];


void I2C1_InitStructure(void)
{
  I2C_InitTypeDef I2C1_InitSturcture;
  DMA_InitTypeDef DMA_InitStrecture;
  
  I2C_DeInit(I2C1);
  DMA_DeInit(DMA1_Stream0);
  DMA_DeInit(DMA1_Stream6);

  I2C1_InitSturcture.I2C_ClockSpeed = 100000;
  I2C1_InitSturcture.I2C_Mode = I2C_Mode_I2C;
  I2C1_InitSturcture.I2C_DutyCycle = I2C_DutyCycle_2;
  I2C1_InitSturcture.I2C_OwnAddress1 = 0x00;
  I2C1_InitSturcture.I2C_Ack = I2C_Ack_Enable;
  I2C1_InitSturcture.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;

  I2C_ITConfig(I2C1, I2C_IT_EVT|I2C_IT_ERR, ENABLE);
  I2C_Init(I2C1, &I2C1_InitSturcture);
  I2C_DMACmd(I2C1, ENABLE);
  I2C_Cmd(I2C1, ENABLE);
  
  DMA_InitStrecture.DMA_Channel = DMA_Channel_1;//I2C1_RX
  DMA_InitStrecture.DMA_PeripheralBaseAddr = (uint32_t)(&(I2C1->DR));
  DMA_InitStrecture.DMA_Memory0BaseAddr = (uint32_t)I2C1_RxBuff;
  DMA_InitStrecture.DMA_DIR = DMA_DIR_PeripheralToMemory;
  DMA_InitStrecture.DMA_BufferSize = I2C1_RxMax;
  DMA_InitStrecture.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStrecture.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStrecture.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  DMA_InitStrecture.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  DMA_InitStrecture.DMA_Mode = DMA_Mode_Normal;
  DMA_InitStrecture.DMA_Priority = DMA_Priority_High;
  DMA_InitStrecture.DMA_FIFOMode = DMA_FIFOMode_Disable;
  DMA_InitStrecture.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  DMA_InitStrecture.DMA_MemoryBurst = DMA_MemoryBurst_Single;
  DMA_InitStrecture.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
  
  //DMA_ITConfig(DMA1_Stream0, DMA_IT_TC, ENABLE);
  DMA_ITConfig(DMA1_Stream0, DMA_IT_HT, ENABLE);
  DMA_Init(DMA1_Stream0, &DMA_InitStrecture);
  DMA_Cmd(DMA1_Stream0, DISABLE);  
}


void I2C1_SendData(uint8_t DeviceAddress, uint16_t RegisterAddress, uint8_t DeviceMeneyType, uint8_t DataLength)
{
  if(!I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY))
  {
    I2C1_TransferFlag = 1;
   
    I2C_ClearFlag(I2C1, I2C_FLAG_BUSY);
    I2C_ClearFlag(I2C1, I2C_FLAG_BERR);

    I2C1_Step = 0;
    I2C1_Mode = I2C1_WriteMode;
    I2C1_DeviceAddress = DeviceAddress;
    I2C1_RegisterAddress = RegisterAddress;
    I2C1_DataLength = DataLength;
   
    I2C_AcknowledgeConfig(I2C1, ENABLE);
    I2C_GenerateSTART(I2C1, ENABLE);
  }
}


void I2C1_ReceiveData(uint8_t DeviceAddress, uint16_t RegisterAddress, uint8_t DeviceMeneyType, uint8_t DataLength)
{
  if(!I2C1_TransferFlag)
  {
    if(!I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY))
    {
      I2C1_TransferFlag = 1;
      
      I2C1_Step = 0;
      I2C1_Mode = I2C1_ReadMode;
      I2C1_DeviceAddress = DeviceAddress;
      I2C1_RegisterAddress = RegisterAddress;
      I2C1_DeviceMeneyType = DeviceMeneyType;
      I2C1_DataLength = DataLength;
      
      I2C_ReceiveData(I2C1);
      I2C_AcknowledgeConfig(I2C1, ENABLE);
      I2C_ITConfig(I2C1, I2C_IT_EVT|I2C_IT_ERR, ENABLE);
      I2C_GenerateSTART(I2C1, ENABLE);
    }
  }
}



void I2C1_EV_IRQHandler(void)
{
  LastEven = I2C_GetLastEvent(I2C1);
  if(I2C1_Mode == I2C1_ReadMode)
  {
    switch(LastEven)
    {
      case I2C_EVENT_MASTER_MODE_SELECT:
           if(I2C1_Step == 0)
           {
            I2C_Send7bitAddress(I2C1, I2C1_DeviceAddress, I2C_Direction_Transmitter);
           }
           
           if(I2C1_DeviceMeneyType == I2C1_MeneyType8)
           {
             if(I2C1_Step == 4)
             {
              I2C_Send7bitAddress(I2C1, I2C1_DeviceAddress, I2C_Direction_Receiver);
             }
           }
           else
           {
             if(I2C1_Step == 5)
             {
              I2C_Send7bitAddress(I2C1, I2C1_DeviceAddress, I2C_Direction_Receiver);
             }
           }
           
           I2C1_Step++;
           break;
      case I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED:
           I2C_SendData(I2C1, ((uint8_t) (I2C1_RegisterAddress)));
           I2C1_Step++;
           break;
      case I2C_EVENT_MASTER_BYTE_TRANSMITTED:
           if(I2C1_DeviceMeneyType == I2C1_MeneyType8)
           {
             if(I2C1_Step == 3)
             {
              I2C_GenerateSTART(I2C1, ENABLE);
             }
             else
             {
              //I2C_SendData(I2C1, RegisterAddress);
             }
           }
           else
           {
            if(I2C1_Step == 3)
            {
              I2C_SendData(I2C1, ((uint8_t) (I2C1_RegisterAddress >> 8)));
            }
        
            if(I2C1_Step == 4)
            {
              I2C_GenerateSTART(I2C1, ENABLE);
            }
           I2C1_Step++;
           break;
      case I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED:
           DMA_ClearITPendingBit(DMA1_Stream0, DMA_IT_HTIF0);
           DMA_ClearITPendingBit(DMA1_Stream0, DMA_IT_TCIF0);
           DMA_ITConfig(DMA1_Stream0, DMA_IT_HT, ENABLE);
           DMA_ITConfig(DMA1_Stream0, DMA_IT_TC, ENABLE);
           DMA_SetCurrDataCounter(DMA1_Stream0, I2C1_DataLength*2);
           DMA_Cmd(DMA1_Stream0, ENABLE);
           I2C_ITConfig(I2C1, I2C_IT_EVT, DISABLE);
           I2C1_Step++;
           break;
      default:I2C1_Step = 0; break;
      }
    }
   }
  
  if(I2C1_Mode == I2C1_WriteMode)
  {
  
  }
}



void I2C1_ER_IRQHandler(void)
{
  if(I2C_GetITStatus(I2C1, I2C_IT_TIMEOUT))
  {
    I2C_ClearITPendingBit(I2C1, I2C_IT_TIMEOUT);
  }
  
  if(I2C_GetITStatus(I2C1, I2C_IT_PECERR))
  {
    I2C_ClearITPendingBit(I2C1, I2C_IT_PECERR);
  }
  
  if(I2C_GetITStatus(I2C1, I2C_IT_AF))
  {
    I2C_ClearITPendingBit(I2C1, I2C_IT_AF);
  }
  
  if(I2C_GetITStatus(I2C1, I2C_IT_BERR))
  {
    I2C_ClearITPendingBit(I2C1, I2C_IT_BERR);
  }
  I2C_GenerateSTOP(I2C1, ENABLE);
}




程序就是这个样子,只写了8位地址,16位的没有写。你看看有什么不对的地方还请支出共同学习。
int
2楼-- · 2019-12-18 15:59
godsend 发表于 2017-3-11 09:04
uint8_t I2C1_TransferFlag = 0;
uint8_t I2C1_Step = 0;
uint8_t I2C1_Mode = 0;

谢谢。DMA中断应该还需要设置标志吧。
godsend
3楼-- · 2019-12-18 20:08
int 发表于 2017-3-11 15:01
谢谢。DMA中断应该还需要设置标志吧。

我没有使用DMA中断,发送之前使用I2C_BUSY来检查总线
szszjdb
4楼-- · 2019-12-18 21:09
security 发表于 2017-3-10 14:43
应该是在 I2C 通讯的时间内,不要去主动进行任务切换,这时间太小,不要主动加大系统负担。
而被动被剥夺 ...

看了您推荐的链接,讲的非常清楚,多谢!  我这里提出的观点是用模拟方式相比硬件方式,对系统效率的影响不是很大,两者都要等待I2C从机将数据返回。虽然DMA不用傻等,但在等的时候也不好安排别的任务。不知您看法如何? 另外,如果STM32用系统, 您觉得最好的方式是哪种?  多谢!
szszjdb
5楼-- · 2019-12-19 01:29
 精彩回答 2  元偷偷看……
szszjdb
6楼-- · 2019-12-19 02:12
cpholr1 发表于 2017-3-10 13:52
网上找过几个STM32硬件iic的例程,不能正常运行,或者运行几下就挂了。

后来仔细分析协议,发现还是写例程 ...

你没有使用中断吧,一旦有中断,I2C就容易出问题了。

一周热门 更多>