本帖最后由 强仔00001 于 2014-7-22 01:33 编辑
这两个星期都在调试stm32f4的硬件i2c,在这过程中遇到了各种蛋疼的问题,经过参考大量的例程和资料,今天终于把硬件的I2C调通了,我使用定时器3开10ms的中断,然后再10ms的中断里读取GY86的MPU6050和HMC5883L的数据,再经经过4元数法算出3个角。有人说stm32的I2C用起来很难,但其实也不是很难的,我就是直接用官方的库函数实现的。下面来直入主题。 硬件平台 NUCLEO-F401RE 首先是I2C的IO和I2C的配置的配置,这个至关重要,一旦初始化不正确,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的库函数前,我要补充一下stm32F1与stm32F4的区别。 第一个,在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的协议程序了。代码我就不贴上了,大家就下载附件来看吧。 然后就是定时器的配置;代码如下:
- 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->PLLCFGR = ( 20ul | /* PLL_M = 20 */
- (240ul << 6) | /* PLL_N = 240 */
- ( 0x0 << 16) | /* PLL_P = 2 */
- (RCC_PLLCFGR_PLLSRC_HSE) | /* PLL_SRC = HSE */
- ( 8ul << 24) ); /* PLL_Q = 8
-
- RCC->CR |= RCC_CR_PLLON; /* 使能 PLL */
- while((RCC->CR & RCC_CR_PLLRDY) == 0) __NOP(); /* 等待PLL启动 */
- RCC->CFGR &= ~RCC_CFGR_SW; /* 选择PLL作为系统时钟*/
- RCC->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; 如下图:
HSx可以选择外部高速晶振HSE和内部高速震荡器HSI,注意内部的高速震荡器是16M的,这个是经过本人用定时器开PWM算出来是正确的,注意系统时钟不能设得太高,否则就会起不了振的。 顺便说一下PLL_Q是用来给USB等外设的。 这一PLL_N和PLL_M,PLL_Q是有范围的,如手册就表明了范围了。
如果你没有外部晶振,就把上面的HSE全都替换成HSI ,这是你就要注意是按16M来算的了。如果你用多少M的外部晶振,要修改头文件里的HSE_VALUE的数值,头文件是默认25M的,我外部是12M的,所以就改成#define HSE_VALUE ((uint32_t)12000000);
说了这么多,现在说回时钟的配置吧。 设置完时钟后,就要设置中断的优先级了。
- 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会卡死的 现在已经运行了2个多小时一切良好: 这是我用示波器下看的I2C时序
TIM.zip
(342.99 KB, 下载次数: 197)
|