打印

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

[复制链接]
13780|25
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
静默|  楼主 | 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;
  }  
}
 
沙发
静默|  楼主 | 2008-12-12 10:22 | 只看该作者

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:25 | 只看该作者

据说多发贴子挣分多。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:31 | 只看该作者

再来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 );
}

使用特权

评论回复
5
静默|  楼主 | 2008-12-12 10:38 | 只看该作者

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

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

使用特权

评论回复
6
lixg1970| | 2008-12-12 13:23 | 只看该作者

谢谢分享

使用特权

评论回复
7
静默|  楼主 | 2008-12-12 14:13 | 只看该作者

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

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;
  }
}

使用特权

评论回复
8
eydj2008| | 2010-8-13 08:36 | 只看该作者
能行吗 上传一个I2C项目文件吧 好心人
我试了百次(我自己参考ST库写的),就只能取到第一次的数据是正常的 (LM75收二个字节)
加了 停止位
I2C_GenerateSTOP(I2C1, ENABLE);
我用示波器看 就没信号了

使用特权

评论回复
9
pkat| | 2010-8-13 14:26 | 只看该作者
程序太多,看的有点晕,不过还是要谢谢楼主共享

使用特权

评论回复
10
秋天落叶| | 2010-8-13 15:01 | 只看该作者
建议楼主以文件的形式共享:lol

使用特权

评论回复
11
sinadz| | 2010-8-14 09:43 | 只看该作者
很不错

使用特权

评论回复
12
xsgy123| | 2010-8-14 09:59 | 只看该作者
很有参考价值的资料,多谢共享

使用特权

评论回复
13
linfuchi| | 2010-8-29 17:09 | 只看该作者
谢谢分享!

使用特权

评论回复
14
xinjie1023| | 2011-5-10 15:06 | 只看该作者
学习,收藏。

使用特权

评论回复
15
xsgy123| | 2011-5-10 16:24 | 只看该作者
先测试一下,再拍:lol

使用特权

评论回复
16
hotpower| | 2011-5-11 08:39 | 只看该作者
不错

使用特权

评论回复
17
dfsa| | 2011-5-11 16:48 | 只看该作者
很不错

使用特权

评论回复
18
秋天落叶| | 2011-5-12 16:31 | 只看该作者
值得参考

使用特权

评论回复
19
hitler617| | 2012-8-7 14:45 | 只看该作者
很详尽啊。stm32f107互联型也通用么?

使用特权

评论回复
20
hitler617| | 2012-9-3 09:49 | 只看该作者
case I2C_EVENT_MASTER_BYTE_TRANSMITTING:          // 0x00070080. 正在发送数据中......
      lu8BusyCount = 0;
      break;
static u8 lu8BusyCount = 0;
  static u16 lu16StaCounter = 0;

没看明白这2个变量的作用啊
还有6,7脚不需要重映射吗?和fsmc时钟共用了。也没看到停止fsmc时钟的代码呀

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

15

主题

171

帖子

1

粉丝