
这两个星期都在调试stm32f4的硬件i2c,在这过程中遇到了各种蛋疼的问题,经过参考大量的例程和资料,今天终于把硬件的I2C调通了,我使用定时器3开10ms的中断,然后再10ms的中断里读取GY86的MPU6050和HMC5883L的数据,再经经过4元数法算出3个角。有人说stm32的I2C用起来很难,但其实也不是很难的,我就是直接用官方的库函数实现的。下面来直入主题。 硬件平台: UNCLEO-F401RE J. P) e% e3 S3 z 首先是I2C的IO和I2C的配置的配置,这个至关重要,一旦初始化不正确,I2C就会出现各种状况。 下面贴上代码:! h# r7 m/ H5 u/ d' ]6 ]0 J void I2C_Congiguration(void)% L! y) _1 n6 _% S& o {9 e( P. B5 [, d /****IO口的配置*******/ GPIO_InitTypeDef GPIO_InitStructure; I2C_InitTypeDef I2C_InitStructure; ) k6 h. w- L' m8 k/ f) n5 o $ {4 I: u# ?" ^! ~ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOB,ENABLE);. m* h/ Q" V: x / w, d3 K( a- T+ Z0 j RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);0 C/ f5 } V, C* m1 m7 |- X( J5 D6 h GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1);% |# K x. t7 O9 O GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1); ; I" l- L9 ^- p# P9 i0 o GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //GPIO_Pin_6 | GPIO_Pin_7 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; " o5 ?0 |& X( q% l$ q u1 S: m GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;7 x; ?1 X8 y1 k: Y4 n$ i3 T3 O2 ~ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;$ W6 h: y; x9 Y GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOB, &GPIO_InitStructure); ! J4 X0 R6 J B U1 D. k 7 M: H" a5 S& R% R) J9 K /* I2C1 的配置 */ I2C_DeInit(I2C1); I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;6 `3 k; X! ?2 Q/ S5 o$ U2 p/ S; x' _ // I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;. w2 I- x# C6 P$ m# s // I2C_InitStructure.I2C_OwnAddress1 = 0xd0;7 ~, q: Q. r7 Y: Y' ?; q2 }2 s I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;& E0 |0 @2 u6 Q I2C_InitStructure.I2C_ClockSpeed = I2C_Speed; /*使能 I2C1 */7 }0 B! F- f+ v- q# O I2C_Cmd(I2C1, ENABLE);: ~$ }6 B, V5 ]" t; |! F( t /* I2C1 初始化*/ I2C_Init(I2C1, &I2C_InitStructure);$ R, z5 ^! P$ F8 r' R K& K! ~ 允许1字节1应答模式*/7 k# U: L3 w7 d S I2C_AcknowledgeConfig(I2C1, ENABLE); ! H- B0 D' P" s4 \ }4 ]2 m& t* o; t6 a# A2 L# H1 z } , u) A1 K' D) x- y+ I8 c+ o 在用stm32F4的库函数前,我要补充一下stm32F1与stm32F4的区别。( |) k$ Z7 [: k4 E/ z' z7 C' c8 g4 o 第一个,在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 ) ; / /0 M3 _ F2 k) ]$ h- b' }* L 打开复用时* p: y3 Q7 x) P. y" ~* |( E/ ~ 但是。。。在F 4 里边这个就不管用了。因为F 4 的变了,以前的那些变量不再爱你了。库里边根本就没有了这个变量R C C _ A P B 2 P e r i p h _ A F I O D; J' g0 T* H+ K 第二,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 。所有使用复用功能的引脚都应设置成这种模式。$ c, N" Q! [) V, I 这样比F 1 方便多了。你不用考虑这个引脚应该设置成浮空输入还是推挽输出. . . . 因为引脚设置错误基本可以消失。 } 引脚的复用功能要用GPIO_PinAFConfig();这个函数了。配置完这些后,就是I2C的协议程序了。代码我就不贴上了,大家就下载附件来看吧。 然后就是定时器的配置;代码如下: void TIM_Init() { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;9 `+ J9 e6 J) t( r6 T1 A" a //Tim_Nvic_Init(); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // TIM3 clock enable + u+ q0 i1 [3 y1 ~# G7 ?- { : ]* S+ f5 {# J( z% G TIM_TimeBaseStructure.TIM_Period = 99; TIM_TimeBaseStructure.TIM_Prescaler = 7199;2 R7 j8 u, X, {2 }* Q" x& [' a TIM_TimeBaseStructure.TIM_ClockDivision = 0;! J. Q. V. Y- O$ A TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;) j) G8 }- G) Y& z8 X 6 Y, Z2 v; ]9 Y TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); ( i! t- W' d0 [' |/ K+ p3 w TIM_ITConfig(TIM3,TIM_IT_Update, ENABLE); /* TIM3 enable counter */ TIM_Cmd(TIM3, ENABLE); 7 T5 T& J) O3 E; h1 x TIM_ClearFlag(TIM3,TIM_FLAG_Update); }, l6 B/ s- K9 P# T9 d' V; X 我这个板子使用外部晶振的,我用的是12M,经过PLL后得出72M的时钟频率。 void Sysclock_Init() {2 ^( ~! J5 y- a/ B7 u ) E* J0 d6 i s S$ m RCC->CR |= ((uint32_t)RCC_CR_HSEON); /* 使能HSE */" Q, k" a# ]1 J+ j, i' s6 U while ((RCC->CR & RCC_CR_HSERDY) == 0); /* 等待HSE */ 3 h, q) j3 l' {6 ` RCC->CFGR = RCC_CFGR_SW_HSE; + D9 T# c# Y. b( Q3 f2 i; Z while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSE); ( v) w- s4 a W+ D4 u: p" d: t8 { H FLASH->ACR = FLASH_ACR_PRFTEN; /* Enable Prefetch Buffer */% P2 X$ b' Q# [8 X/ p6 s6 a! @ FLASH->ACR |= FLASH_ACR_ICEN; /* Instruction cache enable */% W: |2 g; ]7 g' s; C 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 */" C5 t' M+ x( N) g) I- z* }/ n! b RCC->CFGR |= RCC_CFGR_PPRE2_DIV1; ' d5 |3 g5 b, K' v, O 7 \ T) R/ M( H4 R. j RCC->CR &= ~RCC_CR_PLLON; 5 g1 y# u& A8 F: m RCC-> ![]() (240ul CFGR |= RCC_CFGR_SW_PLL; while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); ( U/ a: M* N4 p1 M u' U }, Q/ } S; D! Y/ v" N8 r( r& y, J2 i / I! l! [+ }# V4 @; L/ D+ Z 关于系统时钟的设置可以参考stm32f401参考手册第99页。 计算的公式是 : SystemCoreClock = (HSx*(PLL_N/PLL_M))/PLL_P; 如下图:# s6 m1 Q# }) }3 @1 p6 @" { ![]() 1 b5 N, M1 S- l HSx可以选择外部高速晶振HSE和内部高速震荡器HSI,注意内部的高速震荡器是16M的,这个是经过本人用定时器开PWM算出来是正确的,注意系统时钟不能设得太高,否则就会起不了振的。 顺便说一下PLL_Q是用来给USB等外设的。. n: _9 Z7 o/ V! Z$ p. H0 p 这一PLL_N和PLL_M,PLL_Q是有范围的,如手册就表明了范围了。 ![]() 如果你没有外部晶振,就把上面的HSE全都替换成HSI ,这是你就要注意是按16M来算的了。如果你用多少M的外部晶振,要修改头文件里的HSE_VALUE的数值,头文件是默认25M的,我外部是12M的,所以就改成#define HSE_VALUE ((uint32_t)12000000);; P: ]& r7 o; I+ }4 c2 U ![]() 说了这么多,现在说回时钟的配置吧。: b# y- n! _! C$ V 设置完时钟后,就要设置中断的优先级了。 void Tim_Nvic_Init(void) { / F' x5 }- O& W" g0 V NVIC_InitTypeDef NVIC_InitStructure;$ T2 W+ r5 y7 v! A" U2 i& \ 9 B s( O) t* H# [4 G" m. p NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);& T7 @. X4 x/ X C0 w& t1 k /* Enable the TIM3 gloabal Interrupt */. Q: d% S5 g! F+ w NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;5 w' M7 I4 h: o9 H NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;4 o& o, f0 @2 w/ a" G+ j" t NVIC_Init(&NVIC_InitStructure); }/ f% g: i$ n: X0 b/ N1 l- K* s 当中断多的时候,这里就有很大的学问了,想了解的可以上去百度或google一下。我这里就不多说了。" c" a5 u9 r4 _9 p7 U 中断的里的程序: void TIM3_IRQHandler(void) {1 G3 \. C" D- b( v" r) | P2 p if(TIM_GetITStatus(TIM3,TIM_IT_Update) != RESET)3 C+ A5 V# H& C { TIM_ClearITPendingBit(TIM3,TIM_IT_Update);% L) I/ P+ C7 X. w9 E6 s Read_Filter_MPU6050();# V" @, z5 u6 p8 z: e/ M, j8 ~( ]' K //Read_HMC5883L();! s3 l# L p( l* B- n Get_Attitude(); //ACC_ANGLE(); }" \: u+ R0 }! {5 Z; c } 关键的主程序部分了 int main() \8 `) j! Y+ [5 f {& A, I, |8 n9 v$ I; U7 I 2 R+ j7 h( ]2 d4 L: Q. Z uint8_t counter;! O: n( T4 k+ R: _, d. c K short value; Sysclock_Init(); Tim_Nvic_Init();7 k1 D6 m9 K1 Z- w delay_init(72); I2C_Congiguration(); MPU6050_Init(); HMC5883L_Init();" O: f+ _9 d u7 k/ p TIM_Init();: r) B! J- V) n; |! ]4 ]1 @7 S while (1) {; s$ Q* d# D3 n }9 d) o" F- B; ?) |) Q3 @3 | & Z* W' |' e4 P0 @2 q2 z! X } 注意时钟的配置要放到最后,否则会影响I2C会卡死的1 i* K& m q) V 现在已经运行了3个小时一起良好:! A# e3 a$ v m1 V4 A/ Z: U 这是我用示波器下看的I2C时序* y5 \# b# K8 K, Z ![]() 转载请注!! 强仔00001 2014-7-21写 ![]() |
RE: STM32f401的硬件I2C
RE: STM32f401的硬件I2C
回复: STM32f401的硬件I2C
这明明是分享帖嘛,怎么放在求助帖里呢?不过感觉挺好的,学习了
是的看完之后楼主自己解决的问题,是分享帖
RE: STM32f401的硬件I2C