STM32I2C中断传输方式服务程序,欢迎抛砖。

fengboning   2008-12-12 10:18 楼主

#define _I2C_DEBUG_
#ifdef _I2C_DEBUG_
  u32 gAu32Tmp[100];
#endif
// I2C1中断服务程序.
void I2C1_EV_IRQHandler(void)
{
  u32 lu32Event;
  static u8 lu8BusyCount = 0;
  static u16 lu16StaCounter = 0;
  
  lu32Event = I2C_GetLastEvent(I2C1);
  
#ifdef _I2C_DEBUG_
  if( lu32Event != I2C_EVENT_MASTER_BYTE_TRANSMITTING)
  {
    gAu32Tmp[lu16StaCounter++] = lu32Event;
    if( lu16StaCounter >= 100 )
    {
      lu16StaCounter = 0;
    }
  }
#endif
  if( (lu32Event & 0x00020000) == 0x00020000)
  {
    lu8BusyCount ++;
  }
  else
  {
    lu8BusyCount = 0;
  }
  
  switch ( lu32Event )
  {
    case I2C_EVENT_MASTER_MODE_SELECT:                 // 0x00030001. 发启动条件时产生的事件: EV5
     if( gstruI2C_ComInst.m_u8Direct == CNT_I2C_TRANSMITTER )
      {
        // Master Transmitter, then  Send slave Address for write.
        I2C_Send7bitAddress(I2C1, gstruI2C_ComInst.m_u8DevAdd, I2C_Direction_Transmitter);
      }
      else
      {
        // Master Receiver, Send slave Address for read.
        I2C_Send7bitAddress(I2C1, gstruI2C_ComInst.m_u8DevAdd, I2C_Direction_Receiver);
      }
      break;
        
    // Master Transmitter, then Test on I2C1 EV6 and first EV8 and clear them.
    case I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED:  // 0x00070082. 发出写指定I2C从设备时产生的事件:EV8 just after EV6
      // Send the first data.
      I2C_SendData(I2C1, gstruI2C_ComInst.m_Au8SndOrRecBuf[gstruI2C_ComInst.m_u8DatIdx]); 
      gstruI2C_ComInst.m_u8DatIdx++;
      break;
    case I2C_EVENT_MASTER_BYTE_TRANSMITTING:          // 0x00070080. 正在发送数据中......
      lu8BusyCount = 0;
      break;
    // Test on I2C1 EV8 and clear it.
    case I2C_EVENT_MASTER_BYTE_TRANSMITTED:            // 0x00070084. 一个字节数据发送完成.         
      if(gstruI2C_ComInst.m_u8DatIdx < gstruI2C_ComInst.m_u8SndOrRecLen)
      {
        // Transmit I2C1 data
        I2C_SendData(I2C1, gstruI2C_ComInst.m_Au8SndOrRecBuf[gstruI2C_ComInst.m_u8DatIdx]);
        gstruI2C_ComInst.m_u8DatIdx ++;
      }
      else
      {        
        // Send I2C1 STOP Condition
        I2C_GenerateSTOP(I2C1, ENABLE);
        delay( 50 );                    // NOTE: 非常关键哟,不同的器件,延时可能不一样.
        gstruI2C_ComInst.m_u8Finished = CNT_I2C_FINISHED_YES;
      }
      lu8BusyCount = 0;              // 发送了数据,不为busy.
      break;
    // Master Receiver
    case I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED:      // 0x00030002. 发出读指定I2C从设备时产生的事件:EV6
      if(gstruI2C_ComInst.m_u8SndOrRecLen == 1)
      {
         // Disable I2C1 acknowledgement
         I2C_AcknowledgeConfig(I2C1, DISABLE);
         // Send I2C1 STOP Condition
         I2C_GenerateSTOP(I2C1, ENABLE);
      }
      break;
  
    case I2C_EVENT_MASTER_BYTE_RECEIVED: //0x00030040. 主收到一个字节时产生的事件:EV7. // BUSY, MSL and RXNE flags. 
    case 0x00030044:                                    // BUSY, MSL and RXNE, BTF flags.   
      // Store I2C1 received data
      gstruI2C_ComInst.m_Au8SndOrRecBuf[gstruI2C_ComInst.m_u8DatIdx++] = I2C_ReceiveData(I2C1);
      
      if( gstruI2C_ComInst.m_u8DatIdx >= gstruI2C_ComInst.m_u8SndOrRecLen )
      {
        gstruI2C_ComInst.m_u8Finished = CNT_I2C_FINISHED_YES;
      }
      
      // Disable ACK and send I2C1 STOP condition before receiving the last data
      // 收到倒数第二个数后,应设置NACK和产生STOP标志.
      if( gstruI2C_ComInst.m_u8DatIdx == (gstruI2C_ComInst.m_u8SndOrRecLen - 1))
      {
        // Disable I2C1 acknowledgement.
        I2C_AcknowledgeConfig(I2C1, DISABLE);
        // Send I2C1 STOP Condition.
        I2C_GenerateSTOP(I2C1, ENABLE);
      }
      lu8BusyCount = 0;              // 收到数据,不为busy.
      break;
    case 0x00030201:
    case 0x00030401:
    case 0x00030501:
      I2C_GenerateSTOP(I2C1, ENABLE);
      break;
    default:
      if( lu8BusyCount > 200 )
      {
        lu8BusyCount = 0;
        I2C1_Configuration();
        I2C_Cmd( I2C1, DISABLE );
        I2C_Cmd( I2C1, ENABLE );
      }
      break;
  }  
}
 

回复评论 (13)

I2C 传输的结构定义及常量

#define CNT_I2C_TRANSMITTER       0    // I2C的行为为发送.
#define CNT_I2C_RECEIVER      1    // I2C的行为为接收.
#define CNT_I2C_REC_SND_BUF       128 // 定义I2C的缓冲区大小.
#define CNT_I2C_FINISHED_NO       0   // 本次的I2C操作未结束
#define CNT_I2C_FINISHED_YES   1  // 本次的I2C操作已结束.

// I2C的状态. 用于中断方式

// 定义的I2C收发结构.
typedef struct 
{
   u8 m_u8DevAdd;            // 设备地址.
   u8 m_u8SndOrRecLen;    // I2C需接收或发送的数据长度.
   u8 m_u8DatIdx;         // 接收到数据的下标.
   u8 m_Au8SndOrRecBuf[CNT_I2C_REC_SND_BUF];    // 收发缓冲区
   u8 m_u8Direct;         // I2C的数据流向方向.是接收还是发送.
   u8 m_u8Finished;       // 本次的I2C操作结束否?
   u8 m_u8I2CStatue;      // I2C 的状态?
} struI2C_Com;
点赞  2008-12-12 10:22

据说多发贴子挣分多。I2C结构的填写及启动发或收

/*******************************************************************************
* Function Name  : RTC_WriteReg
* Description    : 用中断方式对I2C寄存器设置.
* Input          : -u8RegAdd: 寄存器地址.
*                : -pu8Dat: 数据指针
*                : -u8Count: 数据个数
*                : -u16DelayMs: 超时时间.
* Output         : 无.
* Return         : 1: 成功
*                : 0: 失败
*******************************************************************************/
u8 RTC_WriteReg( u8 u8RegAdd, u8 * pu8Dat, u8 u8Count, u16 u16DelayMs )
{
  // 把寄存器地址写下去.
  gstruI2C_ComInst.m_Au8SndOrRecBuf[0] = u8RegAdd;

  // 把要发送的数据存放在I2C的发送缓冲区中.
  if( u8Count > 0 )
  {
    memmove( &gstruI2C_ComInst.m_Au8SndOrRecBuf[1], &pu8Dat[0], u8Count );
  }

  // 记录要发送的数据长度.
  gstruI2C_ComInst.m_u8SndOrRecLen = u8Count + 1;
  
  // 设置发送完成标志为NO.
  gstruI2C_ComInst.m_u8Finished = CNT_I2C_FINISHED_NO;

  // 设置I2C方向为发送.
  gstruI2C_ComInst.m_u8Direct = CNT_I2C_TRANSMITTER;

  // 设置I2C的从设备地址为ISL12022M的地址
  gstruI2C_ComInst.m_u8DevAdd = CNT_ISL12022M_ADD;

   // 收发数据的下标
  gstruI2C_ComInst.m_u8DatIdx = 0;

  // 启动发送.
  I2C_GenerateSTART(I2C1, ENABLE);
  
   // 是否已写完寄存器数据. 10ms内是否已写完寄存器数据.
   if ( delayMsUnitl( &gstruI2C_ComInst.m_u8Finished, CNT_I2C_FINISHED_YES, u16DelayMs ) == 1 )
   {
     return 1;
   }
   else
   {
     return 0;
   }
}
点赞  2008-12-12 10:25

再来I2C配置。

void I2C1_Configuration( void )
{
  I2C_InitTypeDef I2C_InitStructure;

  // I2C外设复位.
  RCC_APB1PeriphResetCmd( RCC_APB1Periph_I2C1, ENABLE );
//  delay( 100 );
  RCC_APB1PeriphResetCmd( RCC_APB1Periph_I2C1, DISABLE );

  I2C1_SCK_SDA_Reset();  // 做为IO口置高.
    
  // I2C I0口初始化.
  // Configure I2C1 pins: SCL and SDA ---------------------------------------
  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6 | GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
  GPIO_Init(GPIOB, &GPIO_InitStructure);

  // I2C配置.
  I2C_DeInit( I2C1 );
  // I2C1 configuration ---------------------------------------------
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
  I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
  I2C_InitStructure.I2C_OwnAddress1 = 0x88;      // cortex的I2C地址为0x88;
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
  I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  I2C_InitStructure.I2C_ClockSpeed = 400000;

  I2C_Cmd(I2C1, ENABLE );
        
  I2C_Init(I2C1, &I2C_InitStructure);
  
  I2C_ITConfig( I2C1, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, ENABLE );
}
点赞  2008-12-12 10:31

以下代码可以保证用镊子碰碰I2C总线

    if( GetISL12022MAllData( &lstrISL12022M ) > 0 )
    {
      lintI2CFailCount = 0;
      SendStringAddCrlf( "读ISL12022M成功!" );
    }
    else
    {
      SendStringAddCrlf( "读ISL12022M失败!" );
      if( lintI2CFailCount ++ > 5 )
      {
        lintI2CFailCount = 0;
        I2C1_Configuration( );   // 不行了咱重来,说得挺悬乎,不就是重新初始化嘛。
      } 
    }
点赞  2008-12-12 10:38

我来顶一下楼主1

                                 这 与 周老板那个ARM7中断方式读I2C比较接近的。
点赞  2008-12-12 10:44

谢谢分享

                                  
点赞  2008-12-12 13:23

靠,穷怕了今天有裤子穿了,那得再来一段取数的。

u8 GetISL12022MAllData( struISL12022M_RTC * pstruISL12022M )
{
  // 取所有的值需要2个步骤, 1 发送起始寄存器地址; 2 接收0x30个字节数据.
  // 1. 发送起始寄存器地址
  if( RTC_WriteReg( CNT_ISL12022M_RTC, NULL, 0, 50 ) == 1 )
  {
    // 发送成功.
    // 2. 接收48个字节数据
    // 记录要接收的数据长度.
    gstruI2C_ComInst.m_u8SndOrRecLen = 48;
    // 设置接收完成标志为NO.
    gstruI2C_ComInst.m_u8Finished = CNT_I2C_FINISHED_NO;
    // 设置I2C方向为接收.
    gstruI2C_ComInst.m_u8Direct = CNT_I2C_RECEIVER;
    // 设置I2C的从设备地址为ISL12022M的地址
    gstruI2C_ComInst.m_u8DevAdd = CNT_ISL12022M_ADD;
    
    gstruI2C_ComInst.m_u8DatIdx = 0;
    
    // I2C 应回应ACK.
    I2C_AcknowledgeConfig(I2C1, ENABLE );
    // 启动发送.
    I2C_GenerateSTART(I2C1, ENABLE);
    
    if( delayMsUnitl( &gstruI2C_ComInst.m_u8Finished, CNT_I2C_FINISHED_YES, 100 ) == 1 )
    {        
      // 取到了48个字节数据.
      memmove( pstruISL12022M, &gstruI2C_ComInst.m_Au8SndOrRecBuf[0], gstruI2C_ComInst.m_u8SndOrRecLen ); 
      return 1;
    }
    else
    {
      return 0;
    }
  }
  else
  {
    return 0;
  }
}
点赞  2008-12-12 14:13
能行吗 上传一个I2C项目文件吧 好心人
我试了百次(我自己参考ST库写的),就只能取到第一次的数据是正常的 (LM75收二个字节)
加了 停止位
I2C_GenerateSTOP(I2C1, ENABLE);
我用示波器看 就没信号了
点赞  2010-8-13 08:36
                                 程序太多,看的有点晕,不过还是要谢谢楼主共享
点赞  2010-8-13 14:26
                                 建议楼主以文件的形式共享
点赞  2010-8-13 15:01
                                 很不错
点赞  2010-8-14 09:43
                                 很有参考价值的资料,多谢共享
点赞  2010-8-14 09:59
                                 谢谢分享!
点赞  2010-8-29 17:09
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复