[STM32F4] STM32f401的硬件I2C

[复制链接]
7448|11
 楼主| 强仔00001 发表于 2014-7-21 22:06 | 显示全部楼层 |阅读模式
本帖最后由 强仔00001 于 2014-7-22 01:33 编辑

    这两个星期都在调试stm32f4的硬件i2c,在这过程中遇到了各种蛋疼的问题,经过参考大量的例程和资料,今天终于把硬件的I2C调通了,我使用定时器310ms的中断,然后再10ms的中断里读取GY86MPU6050HMC5883L的数据,再经经过4元数法算出3个角。有人说stm32I2C用起来很难,但其实也不是很难的,我就是直接用官方的库函数实现的。下面来直入主题。
       硬件平台     NUCLEO-F401RE  
    首先是I2CIOI2C的配置的配置,这个至关重要,一旦初始化不正确,I2C就会出现各种状况。
下面贴上代码:

  1. void I2C_Congiguration(void)
  2. {
  3.         /****IO口的配置*******/
  4.         GPIO_InitTypeDef  GPIO_InitStructure;
  5.   I2C_InitTypeDef  I2C_InitStructure;
  6.         
  7.         RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOB,ENABLE);
  8.         
  9.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
  10.         
  11.         GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1);
  12.         GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1);

  13.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //GPIO_Pin_6 | GPIO_Pin_7
  14.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  15.         GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
  16.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  17.         GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

  18.         GPIO_Init(GPIOB, &GPIO_InitStructure);
  19.         
  20.         
  21.           /* I2C1 的配置 */
  22.         I2C_DeInit(I2C1);
  23.   I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
  24. // I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
  25. // I2C_InitStructure.I2C_OwnAddress1 = 0xd0;
  26.   I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
  27.   I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  28.   I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;
  29.   
  30.   /*使能 I2C1 */
  31.   I2C_Cmd(I2C1, ENABLE);

  32.   /* I2C1 初始化*/
  33.   I2C_Init(I2C1, &I2C_InitStructure);

  34.         允许1字节1应答模式*/
  35.         I2C_AcknowledgeConfig(I2C1, ENABLE);
  36.         
  37. }
  38.         
  39. }
   在用stm32F4的库函数前,我要补充一下stm32F1stm32F4的区别。
第一个,在F 1 里边你可以使用这样的方式配置时钟复用
R C C _ A P B 2 P e r i p h C l o c k C m d( R C C _ A P B 2 P e r i p h _ G P I O A | R C C _ A P B 2 P e r i p h _ G P IO B | R C C _ A P B 2 P e r i p h _ A F I O | R C C _ A P B 2 P e r i p h _ U SA R T 1 , E N A B L E ) ; / /
打开复用时
但是。。。在F 4 里边这个就不管用了。因为F 4 的变了,以前的那些变量不再爱你了。库里边根本就没有了这个变量R C C _ A P B 2 P e r i p h _ A F I O
第二,G P I O 的输入输出模式也变了。这样的用法:G P I O _ I n i t S t r u c t u r e . G P I O _ M o d e = G P I O _M o d e _ I N _ F L O A T I N G ; 。已经不行了。原因同上,
G P I O _ M o d e _ I N _ F L O A T I N G 这种模式已经不存在了。
F 4 的库里边只有这个模式:G P I O _ M o d e _ A F 。所有使用复用功能的引脚都应设置成这种模式。
这样比F 1 方便多了。你不用考虑这个引脚应该设置成浮空输入还是推挽输出. . . . 因为引脚设置错误基本可以消失。
}
引脚的复用功能要用GPIO_PinAFConfig();这个函数了。配置完这些后,就是I2C的协议程序了。代码我就不贴上了,大家就下载附件来看吧。
然后就是定时器的配置;代码如下:

  1. void TIM_Init()
  2. {        
  3.          TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

  4.     //Tim_Nvic_Init();

  5.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);      //    TIM3 clock enable


  6.     TIM_TimeBaseStructure.TIM_Period = 99;
  7.     TIM_TimeBaseStructure.TIM_Prescaler = 7199;
  8.     TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  9.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  10.    
  11.     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
  12.    

  13.                  
  14.                 TIM_ITConfig(TIM3,TIM_IT_Update, ENABLE);
  15.    
  16.     /* TIM3 enable counter */
  17.     TIM_Cmd(TIM3, ENABLE);
  18.                 TIM_ClearFlag(TIM3,TIM_FLAG_Update);

  19. }
我这个板子使用外部晶振的,我用的是12M,经过PLL后得出72M的时钟频率。
  1. void Sysclock_Init()
  2. {
  3.         
  4.   RCC->CR |= ((uint32_t)RCC_CR_HSEON);                      /* 使能HSE                       */
  5.   while ((RCC->CR & RCC_CR_HSERDY) == 0);                   /* 等待HSE               */

  6.   RCC->CFGR = RCC_CFGR_SW_HSE;  
  7.         while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSE);
  8.         
  9.         FLASH->ACR  = FLASH_ACR_PRFTEN;                           /* Enable Prefetch Buffer            */
  10.   FLASH->ACR |= FLASH_ACR_ICEN;                             /* Instruction cache enable          */
  11.   FLASH->ACR |= FLASH_ACR_DCEN;                             /* Data cache enable                 */
  12.   FLASH->ACR |= FLASH_ACR_LATENCY_5WS;                     
  13.         
  14.         RCC->CFGR |= RCC_CFGR_HPRE_DIV1;                          /* HCLK = SYSCLK                     */
  15.   RCC->CFGR |= RCC_CFGR_PPRE1_DIV1;                         /* APB1 = HCLK                     */
  16.   RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;  
  17.         
  18.          RCC->CR &= ~RCC_CR_PLLON;
  19.         
  20.   RCC->PLLCFGR = ( 20ul                   |                 /* PLL_M =  20                       */
  21.                  (240ul <<  6)            |                 /* PLL_N = 240                       */
  22.                  (  0x0 << 16)            |                 /* PLL_P =   2                       */
  23.                  (RCC_PLLCFGR_PLLSRC_HSE) |                 /* PLL_SRC = HSE                    */
  24.                  (  8ul << 24)             );                     /* PLL_Q = 8
  25.         

  26.         RCC->CR |= RCC_CR_PLLON;                                  /* 使能 PLL                        */
  27.   while((RCC->CR & RCC_CR_PLLRDY) == 0) __NOP();            /* 等待PLL启动           */

  28.   RCC->CFGR &= ~RCC_CFGR_SW;                                /* 选择PLL作为系统时钟*/
  29.   RCC->CFGR |=  RCC_CFGR_SW_PLL;

  30.   while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);   


  31. }
关于系统时钟的设置可以参考stm32f401参考手册第99页。
计算的公式是 SystemCoreClock = HSx*PLL_N/PLL_M))/PLL_P;
如下图:

系统时钟配置1.png
HSx可以选择外部高速晶振HSE和内部高速震荡器HSI,注意内部的高速震荡器是16M的,这个是经过本人用定时器开PWM算出来是正确的,注意系统时钟不能设得太高,否则就会起不了振的。
顺便说一下PLL_Q是用来给USB等外设的。
这一PLL_NPLL_M,PLL_Q是有范围的,如手册就表明了范围了。

系统时钟配置.png
     如果你没有外部晶振,就把上面的HSE全都替换成HSI  ,这是你就要注意是按16M来算的了。如果你用多少M的外部晶振,要修改头文件里的HSE_VALUE的数值,头文件是默认25M的,我外部是12M的,所以就改成#define HSE_VALUE    ((uint32_t)12000000)
系统时钟配置2.png
说了这么多,现在说回时钟的配置吧。
设置完时钟后,就要设置中断的优先级了。

  1. void Tim_Nvic_Init(void)
  2. {
  3.     NVIC_InitTypeDef NVIC_InitStructure;
  4.    
  5.           NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  6.         
  7.     /* Enable the TIM3 gloabal Interrupt */
  8.     NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
  9.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  10.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  11.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  12.     NVIC_Init(&NVIC_InitStructure);
  13. }

   当中断多的时候,这里就有很大的学问了,想了解的可以上去百度或google一下。我这里就不多说了。
中断的里的程序:
  1. void TIM3_IRQHandler(void)
  2. {
  3.         if(TIM_GetITStatus(TIM3,TIM_IT_Update) != RESET)
  4.         {                                
  5.                 TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
  6.                 Read_Filter_MPU6050();
  7.                 //Read_HMC5883L();
  8.                 Get_Attitude();
  9.                 //ACC_ANGLE();               
  10.         }
  11. }
关键的主程序部分了
  1. int main()
  2. {
  3.         
  4.         
  5.         uint8_t counter;
  6.         short value;               
  7.         Sysclock_Init();
  8.         Tim_Nvic_Init();
  9.         delay_init(72);
  10.         I2C_Congiguration();
  11.         MPU6050_Init();
  12.         HMC5883L_Init();
  13.         TIM_Init();
  14.         while (1)
  15.         {
  16.         }
  17.                
  18. }
注意时钟的配置要放到最后,否则会影响I2C会卡死的
现在已经运行了2个多小时一切良好:
这是我用示波器下看的I2C时序
IMG_20140721_195147.jpg IMG_20140721_211408.jpg
TIM.zip (342.99 KB, 下载次数: 197)


WoodBridge 发表于 2014-9-21 14:09 | 显示全部楼层
楼主我想问下我用的芯片是STM32F303在写IIC的写字节函数时,我看见你的有I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)但这个函数在F3库里面没有定义,但是在F1,F4里面都有,数据手册很多也看不懂,请问能指导下吗?
gowow 发表于 2014-9-22 09:32 | 显示全部楼层
F1 F4的I2C:
第一、I2C中断要最高优先级,第二、要在I2C中断里执行while()等待。
这两样东西组合起来,光板+I2C跑跑也许几天都没问题。
等你各种中断和任务加上去,就是你的死期到了。
zhangjun62 发表于 2015-5-22 21:29 | 显示全部楼层
写硬件I2C时读mpu6050时 ,stm32f103也需要在Tim中断函数中读数据么
yinhaix 发表于 2015-11-11 11:34 | 显示全部楼层
foxglove 发表于 2015-11-11 13:56 | 显示全部楼层
STM32f401的硬件I2C
yinhaix 发表于 2015-11-11 15:25 | 显示全部楼层
摩天轮1111 发表于 2016-2-18 18:51 | 显示全部楼层
gowow 发表于 2014-9-22 09:32
F1 F4的I2C:
第一、I2C中断要最高优先级,第二、要在I2C中断里执行while()等待。
这两样东西组合起来, ...

现在个人体验中,你说的很对,我现在就是,光板iic,跑几天都没事,现在加入各种任务还有中断之后,常有很怪异的问题出现,现在在各种排查解,关键还是不常出现,尤其是有高优先级的中断打断iic的通讯时候,就容易出现问题了,下午跑了快两小时,死了,现在又在找了
j814462570 发表于 2017-10-31 10:53 | 显示全部楼层
十分感谢分享,真的是帮我大忙啦。。。。嘿嘿
baiyang314 发表于 2017-11-13 22:46 | 显示全部楼层
ZHICHI  zhich !!!
hbchf 发表于 2017-11-26 21:37 | 显示全部楼层
感谢分享
一路向北lm 发表于 2017-11-26 22:02 | 显示全部楼层
感谢分享,资料不错。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

65

主题

799

帖子

8

粉丝
快速回复 在线客服 返回列表 返回顶部