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

STM32f401的硬件I2C

[复制链接]
强仔00001 发布时间:2014-7-21 22:48
         这两个星期都在调试stm32f4的硬件i2c,在这过程中遇到了各种蛋疼的问题,经过参考大量的例程和资料,今天终于把硬件的I2C调通了,我使用定时器3开10ms的中断,然后再10ms的中断里读取GY86的MPU6050和HMC5883L的数据,再经经过4元数法算出3个角。有人说stm32的I2C用起来很难,但其实也不是很难的,我就是直接用官方的库函数实现的。下面来直入主题。
9 l% p2 S7 z% i2 e1 R8 q  硬件平台: UNCLEO-F401RE: A# i6 t6 Y8 F
 
6 u  U! Z0 l1 m0 p首先是I2C的IO和I2C的配置的配置,这个至关重要,一旦初始化不正确,I2C就会出现各种状况。
. P4 L0 @& e3 {. l" u下面贴上代码:
, [! Y4 D" c% S4 m/ h1 Rvoid I2C_Congiguration(void)) v5 J1 Z6 h# R3 X& S
{2 k: X! }) J/ z# _
         /****IO口的配置*******/
' |: \9 z$ E. R1 r* j7 u         GPIO_InitTypeDef  GPIO_InitStructure;
8 h! f. {$ v9 K! U  I2C_InitTypeDef  I2C_InitStructure;
; [; B  g( l; a         # n( G- H! n. ?
         RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOB,ENABLE);) X) u+ A% `9 ^. ?- o! O
         6 ?' I* h8 v6 r; B. G
         RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);7 G( u! n4 ^! n' G4 f  ?/ T3 U% h
         ! S0 f; p& @' c, @* D5 H. w3 a) }
         GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1);
4 z, b* [% B" B# q         GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1);
; [8 t- H# w/ G. _3 A/ [0 `) O   \) v5 b# g" l3 r4 H: v" y! @
         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //GPIO_Pin_6 | GPIO_Pin_7, h; R5 Q6 e5 a( r, t; j8 n1 A
         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
/ c3 c8 q! v+ p& L0 {% |         GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;, o3 G, K  L% {  I; f
         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;2 |4 \9 h7 d  C6 E3 O
         GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
) G7 `- z/ N% ~( W 
6 {3 D' m- `+ T  [# p5 \3 o         GPIO_Init(GPIOB, &GPIO_InitStructure);
# S- A' x- z$ R- l+ [        
- E# m1 p5 v$ r         % m+ k* y" G/ L% J# ?' @; @
           /* I2C1 的配置 */
- B# A: T8 ?# q% v& e9 W         I2C_DeInit(I2C1);6 e" C) j9 |# x3 R' U
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
, x4 b+ H: s$ H7 Q // I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
1 @- @  F' b* |1 h; S // I2C_InitStructure.I2C_OwnAddress1 = 0xd0;) v8 z* i( A9 o* B4 p5 v% r
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;( e: j6 C) Q* M- O5 M  J+ l$ f( X' g
  I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
; b  S6 M( h" d7 y5 Q+ {- Y  I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;
. J) }, h6 R. `! }1 W8 w* f- O  0 l2 Z% s3 t4 t6 y  j" X8 q
  /*使能 I2C1 */
1 V6 ~% |5 {3 C* W+ N6 j  I2C_Cmd(I2C1, ENABLE);
. w7 i: \* u7 [8 _ 3 \2 c3 N3 C) g, e2 ~5 _
  /* I2C1 初始化*/
4 Q" L! ~2 Y/ T# E  I2C_Init(I2C1, &I2C_InitStructure);6 A) ]" G3 {2 Y
 % t! ]7 h5 W# d6 C- r
         允许1字节1应答模式*/, d. W; ?; |8 S. p6 W
         I2C_AcknowledgeConfig(I2C1, ENABLE); 3 q) A6 H, j+ M7 c( m
        
% l/ s, n- r; \}
9 ^4 V# ~* x8 E; ~( w* M         / Q2 ?. r, J% R; E+ T0 `! R, [- L
}" s8 R" O$ u  Z! s1 w* Q# ?
 ) C8 O+ \; w( o+ E- q) o3 Z* {, n
在用stm32F4的库函数前,我要补充一下stm32F1与stm32F4的区别。
. U; E$ {# S) g+ I! J8 g  e6 F) R第一个,在F 1 里边你可以使用这样的方式配置时钟复用
' k# I5 I8 g/ W+ nR 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% \( m8 }4 I& r: v9 h0 T
打开复用时, A+ o' p. ?' L8 e/ p! q0 @
但是。。。在F 4 里边这个就不管用了。因为F 4 的变了,以前的那些变量不再爱你了。库里边根本就没有了这个变量R C C _ A P B 2 P e r i p h _ A F I O
( @1 C: g4 x5 t; G5 N第二,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 ; 。已经不行了。原因同上,
& ^3 h5 a( @8 t  l) k- W7 j! mG P I O _ M o d e _ I N _ F L O A T I N G 这种模式已经不存在了。
5 r7 u( w) R; v; Y1 Z0 y( P. i在F 4 的库里边只有这个模式:G P I O _ M o d e _ A F 。所有使用复用功能的引脚都应设置成这种模式。
3 ~5 [2 {% d9 m* J这样比F 1 方便多了。你不用考虑这个引脚应该设置成浮空输入还是推挽输出. . . . 因为引脚设置错误基本可以消失。  Y5 O9 q- B! Q
}
5 G5 k9 Q1 t0 R- o引脚的复用功能要用GPIO_PinAFConfig();这个函数了。配置完这些后,就是I2C的协议程序了。代码我就不贴上了,大家就下载附件来看吧。2 @9 ]6 \9 N6 y7 ~! w/ c% Z( L. H
然后就是定时器的配置;代码如下:+ E$ J7 \+ j4 W
void TIM_Init()4 x! v$ C; r3 D+ Q: F
{        
; e$ v+ t+ z! _          TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
; B. a' c: G, ]4 K3 x                  
9 r6 g, g$ u3 E* C' k- q2 F3 F    //Tim_Nvic_Init();
! d. J5 p3 E0 F; V 
7 y5 E8 C  |% f    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);      //    TIM3 clock enable   H; j8 N  r# Q1 t2 I9 Q
 7 d6 H1 T: v( S! n+ n6 `& k& x
 
0 z1 l7 G$ m1 N% s9 N& G5 P" j    TIM_TimeBaseStructure.TIM_Period = 99;
% X5 S( C. ~2 t8 M' T% J( p9 t    TIM_TimeBaseStructure.TIM_Prescaler = 7199;8 `3 u! `& S( i, L. |" I
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
) f, A2 r4 s) T1 z. o5 O    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
5 v) J2 T) Z" Y( o! d2 n7 L" Q   
& j. l& u+ |0 N8 a) s. K8 |    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
! n! n2 P7 M; e4 M) h4 m4 B" q/ |   
+ S: g2 r2 W, a * i1 o6 c, u0 B
                    
! g, ]  D5 i2 S+ A5 ^8 P2 o                   TIM_ITConfig(TIM3,TIM_IT_Update, ENABLE);
5 @! W: \1 P2 R3 E: K! c   
4 t8 |  P5 L0 l0 E+ {7 n    /* TIM3 enable counter */6 n  P6 w7 Y% D4 {  y1 v( X4 \
    TIM_Cmd(TIM3, ENABLE);
0 R8 |/ c3 @1 N' E% j; r                   TIM_ClearFlag(TIM3,TIM_FLAG_Update); ( K# b3 l& \6 V" P! s6 l: F
 
! t$ E1 s& t3 N4 K- Q0 {8 Q1 ?}8 N- u, H1 M/ ~, W6 S7 T) `
 
1 v( ?7 {3 g3 K: q" R我这个板子使用外部晶振的,我用的是12M,经过PLL后得出72M的时钟频率。# C- A7 d1 M3 l* z( p
void Sysclock_Init()
- K' A! Z8 P" y. b( A. p* @* Q8 ]{
3 ?' N( ?9 a" H* H        
% C& V9 L" q, \5 k# k) I  RCC->CR |= ((uint32_t)RCC_CR_HSEON);                      /* 使能HSE                       *// Q9 h! C! [8 s" V' ~+ ^7 |9 w
  while ((RCC->CR & RCC_CR_HSERDY) == 0);                   /* 等待HSE               */
9 v( p% c: \- w1 S 4 l4 l; Q9 [# O  v2 c( Z0 \- d6 U9 A; {
  RCC->CFGR = RCC_CFGR_SW_HSE;  % |; s4 |- m$ b* L
         while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSE);
' d8 s& \' f/ @        
; ]% T$ f% X& V+ R0 n8 l         FLASH->ACR  = FLASH_ACR_PRFTEN;                           /* Enable Prefetch Buffer            */9 L6 q! d1 v* u8 `
  FLASH->ACR |= FLASH_ACR_ICEN;                             /* Instruction cache enable          */
1 \% {$ f5 b1 q5 |% J7 H2 Y2 f  FLASH->ACR |= FLASH_ACR_DCEN;                             /* Data cache enable                 */5 l: ?3 R: Z& b8 U
  FLASH->ACR |= FLASH_ACR_LATENCY_5WS;                     
/ M5 `; Q0 y8 Y! i: T        
# [9 B1 `2 G* \         RCC->CFGR |= RCC_CFGR_HPRE_DIV1;                          /* HCLK = SYSCLK                     */
$ v# a- O7 W1 b  RCC->CFGR |= RCC_CFGR_PPRE1_DIV1;                         /* APB1 = HCLK                     */
- ]% Y( ~9 _4 h0 H# B  RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;  * D1 i  T6 ^8 P  P2 _! g$ R+ C
         1 ]. a- @; u2 s/ G7 S
          RCC->CR &= ~RCC_CR_PLLON;
$ M  L6 X5 f. [: F0 B        
" j/ G3 Z' c5 i* Y  `7 W  RCC-&gtLLCFGR = ( 20ul                   |                 /* PLL_M =  20                       */
7 o* Y2 c: p$ `+ G/ `4 }- I                 (240ul CFGR |=  RCC_CFGR_SW_PLL;  O0 r6 B4 s1 Z( X4 X
 ! i# E9 X# D! m) a9 I% F6 G
  while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);  
0 T$ q$ X  s( i 
' r, J0 p  I6 M0 I1 ?; v" q+ e 0 U$ m' n2 x8 X: |8 f) U: F
}; L) U5 \7 g2 o0 f( a
 
) b; ^7 \9 l! V# r关于系统时钟的设置可以参考stm32f401参考手册第99页。
( r- m! M& |4 c8 u5 i7 I9 C5 T" V+ `计算的公式是 : SystemCoreClock = (HSx*(PLL_N/PLL_M))/PLL_P;: R( R4 R( N) ], e. \! r$ ~4 ~) o
如下图:
/ ~" I; @- L# z' {  V 系统时钟配置1.png ' x+ K4 {/ u6 Z6 j2 f
 0 v& W7 |2 W9 t3 N. W& x" H
HSx可以选择外部高速晶振HSE和内部高速震荡器HSI,注意内部的高速震荡器是16M的,这个是经过本人用定时器开PWM算出来是正确的,注意系统时钟不能设得太高,否则就会起不了振的。
5 u$ i( H/ F/ A' C顺便说一下PLL_Q是用来给USB等外设的。
; N2 A1 p1 p3 u- C  k这一PLL_N和PLL_M,PLL_Q是有范围的,如手册就表明了范围了。: j# `4 |: p! d1 F2 L$ P# ?0 o
系统时钟配置.png
! o" f' k  G1 J4 a1 e / e8 _3 k. b0 j1 T  w% ]3 U
 如果你没有外部晶振,就把上面的HSE全都替换成HSI         ,这是你就要注意是按16M来算的了。如果你用多少M的外部晶振,要修改头文件里的HSE_VALUE的数值,头文件是默认25M的,我外部是12M的,所以就改成#define HSE_VALUE    ((uint32_t)12000000);  X- Y+ I$ Y( j$ B  \
系统时钟配置2.png
- f2 I) q9 c  M0 C" I/ x- k$ \ 7 b; y& h) E: G# _# G/ ~
说了这么多,现在说回时钟的配置吧。
1 ^% S& O0 E7 d' j1 n% p设置完时钟后,就要设置中断的优先级了。( A, F: v) o/ h  J6 A
void Tim_Nvic_Init(void)) P- o. y$ |% Y3 l( c
{         
. K" @: S4 i3 l0 Q0 ~1 p    NVIC_InitTypeDef NVIC_InitStructure;
+ t1 @( k9 W5 l. Y4 D# F0 C4 {   
5 z, K  o2 i- c0 Z- ~; L! a5 p& P           NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
) u; @9 L9 Q1 [( C           C  f1 C$ f: B
    /* Enable the TIM3 gloabal Interrupt */; |7 ^9 z3 V. I6 t  \! v% D
    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
7 `9 E4 g" G: L- k# d9 m& R" m6 M    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;4 k. ~1 g6 K, r' C% `3 @% [7 u
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;" S3 n8 g7 c3 g% P7 P: `8 f
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
. f$ i- k1 a7 ?2 k; r8 o; f    NVIC_Init(&NVIC_InitStructure);  b4 L6 V% A! t( b
}% H4 [/ N% z5 Y6 `, C& v
当中断多的时候,这里就有很大的学问了,想了解的可以上去百度或google一下。我这里就不多说了。/ H1 \' O. W. G6 S
中断的里的程序:. z! F9 T7 U/ T: t
void TIM3_IRQHandler(void)
8 \9 m4 M' L' h' |& A{3 q8 ]+ ~. H$ ]# M4 S. G
         if(TIM_GetITStatus(TIM3,TIM_IT_Update) != RESET)
  E! U  R. ^" K. L) n4 i. n% N         {                                   
. \( D( C0 ?. v5 e                   TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
; k& |/ {, _5 C- P$ a5 R                   Read_Filter_MPU6050();
4 \+ B3 v( k3 w, x                   //Read_HMC5883L();, z+ d! ~: L1 G
                   Get_Attitude();. [- V  m& N+ _7 [5 A
                   //ACC_ANGLE();                " I5 h! x, u5 [7 O, |, R
         }
/ e7 F% f+ X( O# n. e; z}
# _8 E; y) b5 |) }关键的主程序部分了
0 S7 N0 P4 L9 k0 u( zint main()4 Z; P  `5 P! i
{- n( _4 F$ j  h1 D' o
         4 W5 V: k7 F4 K% a- c. w3 l& G
         0 z( o$ N( i" M# `  j( q; p; {* b
         uint8_t counter;) }/ a: f: U' J4 }
         short value;              
& M" C( V5 ^) h5 J" z         Sysclock_Init();
6 j" i7 R' X: i         Tim_Nvic_Init();
5 b0 u- l% O4 t! o  ~         delay_init(72);1 W; U$ j5 I: j9 c
         I2C_Congiguration();0 P' ]" v+ a; E0 F# w
         MPU6050_Init();
, r6 u; e9 S' U         HMC5883L_Init();
- @7 @0 t+ p5 z9 d+ ?5 X! Q( ~& U8 C+ c1 K
         TIM_Init();! h, Z# b9 k& D
         while (1); O7 r9 w  I% ~2 v9 S1 l) O+ a
         {: T, j# w, B- k- Z4 L
         }$ L6 K1 I" V: `2 F& I- [! r
                   ( C$ ~/ f) x% H0 H' l
}+ `' V, {0 |# ~! Q9 T3 Y- u
注意时钟的配置要放到最后,否则会影响I2C会卡死的
5 |" f5 _$ O3 K; J1 D' O现在已经运行了3个小时一起良好:# W: G, t% E) C
这是我用示波器下看的I2C时序9 c+ F6 E- j4 i) M. p
IMG_20140721_195147.jpg ( R3 h& H  y( Z  p- |
 & |; l) N- t2 t7 z4 l
转载请注!!                                    强仔00001 2014-7-21写: p8 G2 D9 U4 m# f! T9 F; ]
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发表:0 y9 F5 l9 ]5 l& z0 H* i. \
这明明是分享帖嘛,怎么放在求助帖里呢?不过感觉挺好的,学习了 6 v! D" ~/ J- ^3 S4 X, H" x* u

8 h' X6 G: n7 c  s+ Y# N是的看完之后楼主自己解决的问题,是分享帖
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管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版