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

STM32f401的硬件I2C

[复制链接]
强仔00001 发布时间:2014-7-21 22:48
         这两个星期都在调试stm32f4的硬件i2c,在这过程中遇到了各种蛋疼的问题,经过参考大量的例程和资料,今天终于把硬件的I2C调通了,我使用定时器310ms的中断,然后再10ms的中断里读取GY86MPU6050HMC5883L的数据,再经经过4元数法算出3个角。有人说stm32I2C用起来很难,但其实也不是很难的,我就是直接用官方的库函数实现的。下面来直入主题。
- d0 o! G- b) }2 s  硬件平台: UNCLEO-F401RE  J. P) e% e3 S3 z
 
1 f0 @% O3 `! S. |首先是I2CIOI2C的配置的配置,这个至关重要,一旦初始化不正确,I2C就会出现各种状况。
5 }* l1 ~# S1 U: t0 X2 H下面贴上代码:! 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口的配置*******/
' G# u. M3 X) p8 K7 c/ Y         GPIO_InitTypeDef  GPIO_InitStructure;
! D. G5 F$ f, K7 j: j2 a5 @  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
        
6 Q  R5 l2 d* {' I! G         GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1);% |# K  x. t7 O9 O
         GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1);
% I& g0 m/ S$ p2 y2 k ; I" l- L9 ^- p# P9 i0 o
         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //GPIO_Pin_6 | GPIO_Pin_7
' f% s8 H* t2 T6 e4 y7 O         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;
, W7 k) `0 F9 R, V. V, ]6 O 
$ e( i& v' }7 W1 t; k# g- E! w6 v         GPIO_Init(GPIOB, &GPIO_InitStructure);
3 u, p+ `8 o9 E3 [1 ~" C         ! J4 X0 R6 J  B  U1 D. k
         7 M: H" a5 S& R% R) J9 K
           /* I2C1 的配置 */
/ v' v2 `& w4 U! W* L1 c" u7 X4 M         I2C_DeInit(I2C1);
7 J5 M1 T2 R$ H0 I; t  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;
  Y9 l; \$ p2 h  I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;& E0 |0 @2 u6 Q
  I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;
) K+ s% Y% n4 c 
* A/ P1 {$ j! @7 f  /*使能 I2C1 */7 }0 B! F- f+ v- q# O
  I2C_Cmd(I2C1, ENABLE);: ~$ }6 B, V5 ]" t; |! F( t
 
1 m8 c7 L( R) B  /* I2C1 初始化*/
& c; K6 q6 J. ^! y  I2C_Init(I2C1, &I2C_InitStructure);$ R, z5 ^! P$ F8 r' R  K& K! ~
 
2 F, [- u( ]& i& ^' D! A         允许1字节1应答模式*/7 k# U: L3 w7 d  S
         I2C_AcknowledgeConfig(I2C1, ENABLE);
8 R: G; `& T9 U         ! H- B0 D' P" s4 \
}4 ]2 m& t* o; t6 a# A2 L# H1 z
        
: ~% N' ]# C) @8 q}
" b1 w5 S2 f' L, F& H+ h , u) A1 K' D) x- y+ I8 c+ o
在用stm32F4的库函数前,我要补充一下stm32F1stm32F4的区别。( |) k$ Z7 [: k4 E/ z' z7 C' c8 g4 o
第一个,在F 1 里边你可以使用这样的方式配置时钟复用
) ~# E( W" w& l% t7 YR 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 ; 。已经不行了。原因同上,
  w* }( f: k# S! }+ J8 vG P I O _ M o d e _ I N _ F L O A T I N G 这种模式已经不存在了。
4 A1 C6 u4 D/ X4 O9 L, Z3 RF 4 的库里边只有这个模式:G P I O _ M o d e _ A F 。所有使用复用功能的引脚都应设置成这种模式。$ c, N" Q! [) V, I
这样比F 1 方便多了。你不用考虑这个引脚应该设置成浮空输入还是推挽输出. . . . 因为引脚设置错误基本可以消失。
; ]/ ?) O# u$ O% o3 f: x( y& V}
1 a" T: |1 D8 F) X引脚的复用功能要用GPIO_PinAFConfig();这个函数了。配置完这些后,就是I2C的协议程序了。代码我就不贴上了,大家就下载附件来看吧。
8 l" n6 v' W, s( v然后就是定时器的配置;代码如下:
% y. g6 A' F8 }7 b- ]" k/ I) Dvoid TIM_Init()
0 x0 s  R/ i& }" Y+ B- x{        
! X! P7 r& |$ H0 B8 `          TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;9 `+ J9 e6 J) t( r6 T1 A" a
                  
+ P' s6 B* t' `( V- W# Y- i    //Tim_Nvic_Init();
1 r8 d( m  ]+ l# a0 R' u, R0 H 
* r$ x0 B+ I5 ]( E6 U( I5 E    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);      //    TIM3 clock enable + u+ q0 i1 [3 y1 ~# G7 ?- {
 : ]* S+ f5 {# J( z% G
 
( c, A; n0 g; x/ b1 D3 a* R1 Q    TIM_TimeBaseStructure.TIM_Period = 99;
+ J: d: D* ?" F    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);
% |' A5 ]7 F& G2 o   
+ w7 m5 b6 w* }, k- b 
" _4 ]. f) ?  z. d% f                    ( i! t- W' d0 [' |/ K+ p3 w
                   TIM_ITConfig(TIM3,TIM_IT_Update, ENABLE);
, W' X! }; L1 [7 O   
" n7 n/ }1 N$ V( `    /* TIM3 enable counter */
! i% f9 e, }' B5 V7 ?* [5 R    TIM_Cmd(TIM3, ENABLE); 7 T5 T& J) O3 E; h1 x
                   TIM_ClearFlag(TIM3,TIM_FLAG_Update);
- H5 z) F5 O$ H' X! \. d# S 
: [& a  U$ u2 Q& `9 F$ J}, l6 B/ s- K9 P# T9 d' V; X
 
% p7 P, U& M7 l; z/ b我这个板子使用外部晶振的,我用的是12M,经过PLL后得出72M的时钟频率。
/ s* n% x! Q- g9 bvoid Sysclock_Init()
% f& z. f# b0 W7 |7 Y  O5 s* `' B{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               */
: ?. v! t" [' |- Z) b 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);
* U1 ^: E  t  T2 x6 Q         ( 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                 */
2 I3 }( T4 m6 ?5 Z* q! F  FLASH->ACR |= FLASH_ACR_LATENCY_5WS;                     
$ C+ i' r. R1 Z" L        
& Z/ w) O% C: p5 e3 y         RCC->CFGR |= RCC_CFGR_HPRE_DIV1;                          /* HCLK = SYSCLK                     */
9 a9 Y+ l% N6 O6 h5 P; C  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
        
% r3 K# Z3 \. L3 u  RCC-&gtLLCFGR = ( 20ul                   |                 /* PLL_M =  20                       */) @* I& \2 ]7 O0 w8 \
                 (240ul CFGR |=  RCC_CFGR_SW_PLL;
. r. I1 F$ q: ^ 
& Z- m# i2 D. G+ M  while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);  
7 v9 e  r7 L7 [0 S2 D. K ( U/ a: M* N4 p1 M  u' U
 
: S3 r3 M. @, T' t2 L4 p/ K}, Q/ }  S; D! Y/ v" N8 r( r& y, J2 i
 / I! l! [+ }# V4 @; L/ D+ Z
关于系统时钟的设置可以参考stm32f401参考手册第99页。
* Y! [/ q- _( l* I. Y+ G计算的公式是 SystemCoreClock = HSx*PLL_N/PLL_M))/PLL_P;
0 _$ M5 ]- \. n如下图:# s6 m1 Q# }) }3 @1 p6 @" {
系统时钟配置1.png 5 s; n. N) c1 A0 K; ~' v) l
 1 b5 N, M1 S- l
HSx可以选择外部高速晶振HSE和内部高速震荡器HSI,注意内部的高速震荡器是16M的,这个是经过本人用定时器开PWM算出来是正确的,注意系统时钟不能设得太高,否则就会起不了振的。
' m, @  j" A; T1 ~, E  C0 ?顺便说一下PLL_Q是用来给USB等外设的。. n: _9 Z7 o/ V! Z$ p. H0 p
这一PLL_NPLL_M,PLL_Q是有范围的,如手册就表明了范围了。
! T! Q# Y' D. w4 v: F 系统时钟配置.png 6 Y( ]7 T' X% A* x
 
0 R! l+ _: ]. Z: y$ ^5 f2 U2 | 如果你没有外部晶振,就把上面的HSE全都替换成HSI         ,这是你就要注意是按16M来算的了。如果你用多少M的外部晶振,要修改头文件里的HSE_VALUE的数值,头文件是默认25M的,我外部是12M的,所以就改成#define HSE_VALUE    ((uint32_t)12000000); P: ]& r7 o; I+ }4 c2 U
系统时钟配置2.png
8 B1 W0 P  U+ I 
8 {" D- ], Y8 i0 s- g( a说了这么多,现在说回时钟的配置吧。: b# y- n! _! C$ V
设置完时钟后,就要设置中断的优先级了。
4 w, z: Q+ S# U+ s# bvoid Tim_Nvic_Init(void)
5 @0 C. c( X$ j, G8 O{          / 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
        
$ d- {2 E: o/ G$ L    /* Enable the TIM3 gloabal Interrupt */. Q: d% S5 g! F+ w
    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
+ R2 Z, }2 c& i. e0 {) m7 c    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;5 w' M7 I4 h: o9 H
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
- k- L+ Q3 Y6 S" p1 r3 c    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;4 o& o, f0 @2 w/ a" G+ j" t
    NVIC_Init(&NVIC_InitStructure);
; y% ?8 A- |0 F2 i}/ f% g: i$ n: X0 b/ N1 l- K* s
当中断多的时候,这里就有很大的学问了,想了解的可以上去百度或google一下。我这里就不多说了。" c" a5 u9 r4 _9 p7 U
中断的里的程序:
" R6 c$ U6 _# ^; j( m' \void TIM3_IRQHandler(void)
% f  K; N& K: E6 \4 P{1 G3 \. C" D- b( v" r) |  P2 p
         if(TIM_GetITStatus(TIM3,TIM_IT_Update) != RESET)3 C+ A5 V# H& C
         {                                   
6 I; W9 P% R2 \" G- F: X4 n                   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();
( ?( \+ r# s2 ~0 ?                   //ACC_ANGLE();               
% p5 H* }, Q* g         }" \: u+ R0 }! {5 Z; c
}
" J  O9 {4 A4 k4 I+ i关键的主程序部分了
1 a: [% E0 `3 y5 A1 v% oint main()  \8 `) j! Y+ [5 f
{& A, I, |8 n9 v$ I; U7 I
         2 R+ j7 h( ]2 d4 L: Q. Z
        
2 q6 J# d4 I7 J' }1 \" d9 v         uint8_t counter;! O: n( T4 k+ R: _, d. c  K
         short value;              
6 R# i% V- r: r         Sysclock_Init();
$ M! r; f+ L$ ]" y  |2 x3 q         Tim_Nvic_Init();7 k1 D6 m9 K1 Z- w
         delay_init(72);
5 x7 m, Q" r3 k7 s         I2C_Congiguration();
4 O6 r; P1 @: l         MPU6050_Init();
) G) {. }$ |$ m  h         HMC5883L_Init();" O: f+ _9 d  u7 k/ p

0 I; v0 u5 Z  s6 ~         TIM_Init();: r) B! J- V) n; |! ]4 ]1 @7 S
         while (1)
. d2 G6 |. p$ [0 N         {; s$ Q* d# D3 n
         }9 d) o" F- B; ?) |) Q3 @3 |
                   & Z* W' |' e4 P0 @2 q2 z! X
}
1 B1 w3 w6 t5 Q8 n; _" S9 P, E8 r注意时钟的配置要放到最后,否则会影响I2C会卡死的1 i* K& m  q) V
现在已经运行了3个小时一起良好:! A# e3 a$ v  m1 V4 A/ Z: U
这是我用示波器下看的I2C时序* y5 \# b# K8 K, Z
IMG_20140721_195147.jpg
( w; o9 n3 E; g0 \( C1 u 
4 C  e7 W! G" _0 A转载请注!!                                    强仔00001 2014-7-21
. a( l1 T1 T1 Z3 m 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发表:; f* R! t4 }& w$ {4 e2 ]& N
这明明是分享帖嘛,怎么放在求助帖里呢?不过感觉挺好的,学习了 
4 U( X. i( S- z6 @
" }/ g  t1 t# m3 E' }+ u是的看完之后楼主自己解决的问题,是分享帖
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 手机版