你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32f401的硬件I2C

[复制链接]
强仔00001 发布时间:2014-7-21 22:48
         这两个星期都在调试stm32f4的硬件i2c,在这过程中遇到了各种蛋疼的问题,经过参考大量的例程和资料,今天终于把硬件的I2C调通了,我使用定时器310ms的中断,然后再10ms的中断里读取GY86MPU6050HMC5883L的数据,再经经过4元数法算出3个角。有人说stm32I2C用起来很难,但其实也不是很难的,我就是直接用官方的库函数实现的。下面来直入主题。
  硬件平台: UNCLEO-F401RE
 
首先是I2CIOI2C的配置的配置,这个至关重要,一旦初始化不正确,I2C就会出现各种状况。
下面贴上代码:
void I2C_Congiguration(void)
{
         /****IO口的配置*******/
         GPIO_InitTypeDef  GPIO_InitStructure;
  I2C_InitTypeDef  I2C_InitStructure;
        
         RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOB,ENABLE);
        
         RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
        
         GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1);
         GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1);
 
         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //GPIO_Pin_6 | GPIO_Pin_7
         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
         GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
         GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
 
         GPIO_Init(GPIOB, &GPIO_InitStructure);
        
        
           /* I2C1 的配置 */
         I2C_DeInit(I2C1);
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
 // I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
 // I2C_InitStructure.I2C_OwnAddress1 = 0xd0;
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
  I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;
 
  /*使能 I2C1 */
  I2C_Cmd(I2C1, ENABLE);
 
  /* I2C1 初始化*/
  I2C_Init(I2C1, &I2C_InitStructure);
 
         允许1字节1应答模式*/
         I2C_AcknowledgeConfig(I2C1, ENABLE);
        
}
        
}
 
在用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 I O 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 S A 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的协议程序了。代码我就不贴上了,大家就下载附件来看吧。
然后就是定时器的配置;代码如下:
void TIM_Init()
{        
          TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
                  
    //Tim_Nvic_Init();
 
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);      //    TIM3 clock enable
 
 
    TIM_TimeBaseStructure.TIM_Period = 99;
    TIM_TimeBaseStructure.TIM_Prescaler = 7199;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
   
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
   
 
                    
                   TIM_ITConfig(TIM3,TIM_IT_Update, ENABLE);
   
    /* TIM3 enable counter */
    TIM_Cmd(TIM3, ENABLE);
                   TIM_ClearFlag(TIM3,TIM_FLAG_Update);
 
}
 
我这个板子使用外部晶振的,我用的是12M,经过PLL后得出72M的时钟频率。
void Sysclock_Init()
{
        
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);                      /* 使能HSE                       */
  while ((RCC->CR & RCC_CR_HSERDY) == 0);                   /* 等待HSE               */
 
  RCC->CFGR = RCC_CFGR_SW_HSE; 
         while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSE);
        
         FLASH->ACR  = FLASH_ACR_PRFTEN;                           /* Enable Prefetch Buffer            */
  FLASH->ACR |= FLASH_ACR_ICEN;                             /* Instruction cache enable          */
  FLASH->ACR |= FLASH_ACR_DCEN;                             /* Data cache enable                 */
  FLASH->ACR |= FLASH_ACR_LATENCY_5WS;                     
        
         RCC->CFGR |= RCC_CFGR_HPRE_DIV1;                          /* HCLK = SYSCLK                     */
  RCC->CFGR |= RCC_CFGR_PPRE1_DIV1;                         /* APB1 = HCLK                     */
  RCC->CFGR |= RCC_CFGR_PPRE2_DIV1; 
        
          RCC->CR &= ~RCC_CR_PLLON;
        
  RCC-&gtLLCFGR = ( 20ul                   |                 /* PLL_M =  20                       */
                 (240ul CFGR |=  RCC_CFGR_SW_PLL;
 
  while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);  
 
 
}
 
关于系统时钟的设置可以参考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
 
说了这么多,现在说回时钟的配置吧。
设置完时钟后,就要设置中断的优先级了。
void Tim_Nvic_Init(void)
{         
    NVIC_InitTypeDef NVIC_InitStructure;
   
           NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
        
    /* Enable the TIM3 gloabal Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}
当中断多的时候,这里就有很大的学问了,想了解的可以上去百度或google一下。我这里就不多说了。
中断的里的程序:
void TIM3_IRQHandler(void)
{
         if(TIM_GetITStatus(TIM3,TIM_IT_Update) != RESET)
         {                                   
                   TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
                   Read_Filter_MPU6050();
                   //Read_HMC5883L();
                   Get_Attitude();
                   //ACC_ANGLE();               
         }
}
关键的主程序部分了
int main()
{
        
        
         uint8_t counter;
         short value;              
         Sysclock_Init();
         Tim_Nvic_Init();
         delay_init(72);
         I2C_Congiguration();
         MPU6050_Init();
         HMC5883L_Init();

         TIM_Init();
         while (1)
         {
         }
                  
}
注意时钟的配置要放到最后,否则会影响I2C会卡死的
现在已经运行了3个小时一起良好:
这是我用示波器下看的I2C时序
IMG_20140721_195147.jpg
 
转载请注!!                                    强仔00001 2014-7-21
TIM.zip (342.99 KB, 下载次数: 452)
IMG_20140721_211408.jpg
收藏 1 评论6 发布时间:2014-7-21 22:48

举报

6个回答
aaa999kk 回答时间:2014-7-24 16:29:36

RE: STM32f401的硬件I2C

初始化参数写错, 是有很多麻烦
巅峰残狼 回答时间:2014-7-25 21:57:50

RE: STM32f401的硬件I2C

这明明是分享帖嘛,怎么放在求助帖里呢?不过感觉挺好的,学习了
霹雳之火 回答时间:2014-7-28 16:34:14

回复: STM32f401的硬件I2C

回复第 3 楼 于2014-07-25 21:57:50发表:
这明明是分享帖嘛,怎么放在求助帖里呢?不过感觉挺好的,学习了 

是的看完之后楼主自己解决的问题,是分享帖
feixiang20 回答时间:2014-7-29 01:50:12

RE: STM32f401的硬件I2C

红笔部分是不是有什么问题需要解释呢
zzzhxw 回答时间:2016-3-22 13:43:06
楼主   好东西啊
wdshuang09 回答时间:2016-11-20 15:11:08
最近用寄存器调STM32F103  的I2C,也调通了;确实STM32的I2C不好调呀

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版