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

【MCU实战经验】STM32F407 Discovery开发实例心得分享

[复制链接]
仙人掌 发布时间:2014-4-19 11:11
      本人一直以来都在接触STM32方面的软硬件开发工作,最初的是做基于STM32F1的汽车电子相关的软件研发工作,有用到F1的一些基本功能,例如USART串口通信,IIC存储EEPROM芯片,SPI读写FLASH等等,内核的SYSTICKNVIC,中断,强大的定时器功能也总是用到,尤其是CAN总线,其稳定的工作机制和强大的纠错,适应能力,最适合汽车电子等工作环境不稳定的场合。最开始的这些研发工作使我对STM32情有独钟,其强大的外设资源,能满足绝大部分的应用需求,丰富的系列产品,能满足预算和功能的协调统一。; ~( m: H% _! @3 H- A
      有了一些基础之后,我在网上看到了STM32F4 Discovery的出现,高达168MHz的主频,1M的片内FLASH192KSRAM,内置RNGFPU等等,并且包含了F1系列的几乎所有功能,尤其支持USB OTG,强化了音视频解码,能做出更多的功能和有意思的探索。接下来就STM32F4 Discovery官方开发板,本人自己研究的一些过程经验和体会,分享给大家,限于本人能力有限,仅代表个人观点,还请大家多多指教。* h/ k7 r* e% e. d6 k
   首先,对于开发方式,本人采用“MDK4.72 + 寄存器”进行开发,首先,此版本的MDK(keil)有在线纠错功能,让你能时刻看到自己写的语句是否有问题,虽然有时候的软件提示有少许BUG,但是瑕不掩瑜,还是很不错的功能,其次,新版本的MDK支持的芯片更多,可以方便你移植代码到其他平台上去。再就是用寄存器方式开发,这个纯属个人兴趣,我的方法是:比如我要写某个功能,我会先看官方或者网上一些高手写的库函数的代码,慢慢去分析他们的思路,和一些初始化或者配置的过程,最后总结出一个规律或者套路,再去看芯片的手册,找到相关寄存器,分析用到的每个位的作用,写出寄存器配置的代码,这个过程虽然枯燥无味,却能真正然你理解其中含义,非常清晰的看到一步步配置的过程,不会在云里雾里,所以我采用寄存器方式开发。这个适合时间充裕,想真正了解和学习内核的人。当然,萝卜白菜各有所爱。相关寄存器配置资料:
0 D' Z4 L$ D9 W# O& Q, uSTM32F4_DISCOVERY    --   官方开发板的说明手册,外设介绍等等- o! j* l' P' o# d2 V
RM0090 Reference Manual  --  寄存器手册,也是寄存器方式开发重点要看的
+ m& Z9 T  \# QSTM32F407数据手册        --   主芯片STM32F407VGT6的手册,主要看管脚对应功能,外设配置简介等, j4 ?) w, O+ r+ \# F* n
Cortex-M4 Devices Generic User Guide  --  Cortex-M4内核手册,包含NVIC,SYSTICK等等内核的详细说明
% n2 r$ y; j) B. a5 ^% G" g还可以去ST官网下载3.5的库函数,以及STM32F4 Discovery板的例程,经常看一看,借鉴学习下,非常有好处。' C+ S0 |2 {* n$ v8 u/ j$ r
F4系列和F1系列的寄存器有少许差别,大家使用时可参考F1的相关例程,对照修改即可。* P! l8 V/ |) H& x* g; _
# C% F  i# Q6 O' k' u
以下发表一些自己研究过程中的心得体会:若有错误,还请指正(以下代码均对应官方的STM32F4 Discovery板子,部分代码仅贴出最关键部分)
5 a8 j9 f, u) o2 ]一,IO$ k, C0 Q# N1 n
最开始莫过于控制IO口的电平高低了,从这里开始,F4的寄存器就不同于F1,例如初始化LED的代码:
; O, @" q: @; ]# Q//PD12:绿灯,PD13:橙灯,PD14:红灯,PD15:蓝灯" D5 E8 Q3 I3 N$ A: [8 b+ m$ Q2 ^
void LED_Init(void)
# h% T# f4 s& K& Z/ Y7 A; q+ u{0 s+ U0 l- D* \- N2 |* O6 U
   RCC->AHB1ENR |= 1OTYPER &= 0xFFFF0FFF;//设置PD12,13,14,15推挽输出
1 \' u8 X+ A2 I5 ~- p  GPIOD->OSPEEDR &= 0x00FFFFFF;//PD12,13,14,15速度100M5 ?# k& x" o0 f0 d$ U& r
  GPIOD->OSPEEDR |= 0xFF000000;
7 @3 s$ \8 t0 b: h5 T9 w. h" [9 g  SYSCFG->CMPCR = 0x00000001;//GPIO速度超过50M的时候,使用IO补偿单元   % h/ r6 b" C: ^$ t* ]: M  Y# F+ Z
  GPIOD->UPDR &= 0x00FFFFFF;//PD12,13,14,15无上拉无下拉; }! L' B$ M& e/ x1 `5 H9 \( q
  GPIOD->BSRRH = 0xF000;//配置完成后使所有等保持熄灭状态1 ^7 L) u) U( Y% g3 K
}- e+ z" q+ o. `; x) v; j# M
配置过程:
0 m% a! Z  Q8 |" j! P( \% ~9 V0 z1,开启GPIOD时钟 2,配置IO为通用输出模式  3,配置IO为推挽输出  4,配置IO输出速度  5,配置上下拉模式  (其中,使用IO补偿单元不是必须,只是官方历程中写到,实测不加此句也可以)                                                                                                                                 2,相关寄存器为:GPIOD->MODER      GPIOD->OTYPER       GPIOD->OSPEEDR       GPIOD->UPDR
1 f; v. h. q6 D: h+ q- n二,USART
* I: g7 t! |3 {4 X6 w+ SUSART串口也是调试过程中,或者以后产品运行时与上位机软件通信的手段之一,十分重要,可以官方的开发板只有micro USB接到了USB OTG,虽说也可以配置通信,但是太复杂了,本人还没调通,暂时用把排针引线接到其他板子的USB转串口的芯片,再连接到电脑。! V/ P, x6 v7 x
注意:一开始我连接到的是USART1,也就是PA9,PA10,但是怎么也调试不通,一直在串口调试软件中显示乱码,一开始以为是波特率有问题,可是怎么也调不成功。后来我看官方开发板的原理图,才发现PA9,PA10已经连接到了板载的一个芯片上,可能是做USB设备的驱动芯片吧。所以我换成了USART3,对应发送接收引脚PD8,PD9,检查无冲突之后,仅更改引脚,使用之前的相同配置,调试通过。代码如下:
7 j5 b( G5 z- f: k+ \$ oUSART初始化部分:
; e8 `  P1 K+ _" w8 [//初始化串口3, S) @3 Q' {+ B, w7 _
//USART1,6 84MHz  USART2,3,4,5 42MHz
- \, Q( J! j5 A- l9 h- p! W) w( Y//波特率不通过参数传入,直接通过计算得到,并写入USART_BRR
) G6 M# c* O7 vvoid MYUSART_Init(void)
% H4 P5 @5 d% i2 [  B3 L) ?- M% v{
7 L% }' ]2 o! W3 v8 v1 t8 h9 k) I5 F         //使能PORTDUSART3时钟
: Z/ ]  Q4 Z5 `$ M         RCC->AHB1ENR |= 1OTYPER &= ~(1PUPDR |= 0x00040000;  //PD9 上拉 9 B/ T( \3 L4 S
         //复位USART3
( n* c( m6 \$ E6 F1 ]         RCC->APB1RSTR |= 1CFGR = 0x00000000;7 k6 r  Q! U# ?
  /* Reset HSEON, CSSON and PLLON bits */
# U* C" s% i4 z* I* }  RCC->CR &= (uint32_t)0xFEF6FFFF;
6 D2 r3 y$ S( j/ G0 L% q  /* Reset PLLCFGR register */
7 \9 j; ^1 T- n: ?3 W  f  RCC->LLCFGR = 0x24003010;
8 a3 m! d: F7 _2 ]  /* Reset HSEBYP bit */3 d* W8 F, S. S
  RCC->CR &= (uint32_t)0xFFFBFFFF;0 C% p' P) G& E- q8 b
  /* Disable all interrupts */2 P. S6 j7 V' ]: z$ W' l
  RCC->CIR = 0x00000000;" n9 h/ E9 I7 \% u9 y
}% {) w7 u& T/ o) C9 t

2 _# Q. `+ R5 j//STM32F4时钟配置9 J* {2 ^, q: F- u
//关键是粗体部分的参数配置,此四个参数决定了配置后的MCU的主频,进而决定了整个系统的时基) z2 H+ {. b- D
//配置完成后系统主频为168MHz,注意,板载是8MHz晶振,所以要在MDK的设置里写为8$ S; D/ F% [/ `( D
//还需要在文件stm32f4xx.h中第92中把25000000改为8000000" ]! y; }" h+ e
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
' k) [. E8 z. R#define PLL_M      8
  i7 e$ t" j  N#define PLL_N      336# ~+ _3 l, \" r6 P. g" @. {
/* SYSCLK = PLL_VCO / PLL_P */  ~% a* ~6 ^/ X. c" Q
#define PLL_P      2
7 U9 [+ `: E2 N, |  t8 r4 F6 t/* USB OTG FS, SDIO and RNG Clock =  PLL_VCO / PLLQ */2 \1 n/ l0 i; ]: ]6 [
#define PLL_Q      77 {0 |% J* D) e) T9 b  ~
void F4_Clock_Init(void)
' L/ y* f3 \! V6 ^# j{
5 b4 {. z6 h! g9 C, U4 w5 @! R: a         /* PLL (clocked by HSE) used as System clock source */' A0 \3 s" g" A
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
8 S9 u# U, H+ v         # [- ^( l& s0 a  P( Q
         F4_Clock_DeInit();
( o% Y) v2 N, O! R3 ^3 t* A         
7 ~" G2 P: t5 M         /* Enable HSE */
$ r2 b+ {& b/ X/ [( o0 e  RCC->CR |= ((uint32_t)RCC_CR_HSEON);, Y# b) _3 R8 ?) ^3 k
         
9 m% e  A# c- _         /* Wait till HSE is ready and if Time out is reached exit */9 |0 \- I( L- @* H. M7 g0 o
  do& K1 b9 d" I) Y8 s1 |
  {
9 w: i+ G& G2 J+ E    HSEStatus = RCC->CR & RCC_CR_HSERDY;2 z! t, E8 d3 x9 N' C
    StartUpCounter++;  Z: [7 C$ C2 E- G8 _
  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));2 S/ N+ ~) ?6 L' b5 A4 P
         
: V0 h$ n: Y: J1 L# K* u( }         if ((RCC->CR & RCC_CR_HSERDY) != RESET)
( C  I  j+ I1 C0 Z+ B  {
" Y( i& d* ?: ]4 G3 R    HSEStatus = (uint32_t)0x01;3 c, ]+ N& f2 C" y
  }+ D0 D" Y" I% E( r
  else
5 K- W7 b& G# k  {
% a" u, P3 Y' h& a; U7 v2 x- ^    HSEStatus = (uint32_t)0x00;9 K% f% C5 t& w/ o0 k
  }- P5 O4 A* f$ G3 T( t8 E

& |# V" i8 q! x0 r: G' Z  if (HSEStatus == (uint32_t)0x01)$ Q0 Q8 u& l( ]$ v, R7 d
  {' C& O7 K5 K2 a: o, }7 k
    /* Enable high performance mode, System frequency up to 168 MHz */. K; v+ i" I8 H8 Z1 n
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;! F, [5 y/ _" u: {+ b# N! I1 `7 k# Y
    PWR->CR |= PWR_CR_PMODE;  ; O. K/ Q3 s8 v! I" O' X5 Z

4 O# \& x. r1 f    /* HCLK = SYSCLK / 1*/  w3 i0 g6 Q; V& H0 _; t3 ?/ l$ t
    RCC->CFGR |= RCC_CFGR_HPRE_DIV1;* n" ^. ~& P  X, r
      
$ s' }* J  @6 ]: P$ l$ I" E    /* PCLK2 = HCLK / 2*/5 i/ R+ V7 }0 l# Z/ A
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
; P/ ?$ U: j/ N/ P5 S   
' x' a, }+ T4 G    /* PCLK1 = HCLK / 4*/
. S5 _6 F# I7 c8 {- ~1 ~2 H    RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;3 }0 ~$ T- |4 Z/ x9 v4 q

: i9 U6 p( ^: G$ d    /* Configure the main PLL */
  W: H$ ^/ w8 l; t, P6 r<strong>    RCC->LLCFGR = PLL_M | (PLL_N > 1) -1) CR & RCC_CR_PLLRDY) == 0)' ^. L# x6 U/ s2 ^8 I/ Q: }4 ?- G
    {
6 E  M0 N8 H! w- T. J    }
% {: s* G7 w9 U# {" a( q0 {                   : g: P! @+ y4 ]6 W9 E
                   /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
: c' o9 B) F# m# ~" e* D  c% @    FLASH->ACR = FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
% w  R$ {  C, |% G: g
$ j' o: |4 r1 R    /* Select the main PLL as system clock source */8 h; C  o% j3 G( ^5 F7 L) o
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));0 q. v+ S2 E% A. x! L9 p: B
    RCC->CFGR |= RCC_CFGR_SW_PLL;
; E6 a$ @- z/ l6 W9 F8 r3 N+ a : H# _( q/ P; o1 l8 \
    /* Wait till the main PLL is used as system clock source */4 p: ~) }+ ]$ ^0 h. F( U" f/ `
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);, u; i  [7 Z9 \8 k1 z: _+ Z
    {! p1 z" T8 t* `* T- e
    }7 ]! ?( q4 J  y  a. Y2 |
  }
! C/ z, B+ _! Q         else
9 l0 a& P9 J1 \' O  { /* If HSE fails to start-up, the application will have wrong clock9 k: v2 I3 \! a" o) C/ Q9 M3 n
         configuration. User can add here some code to deal with this error */8 l) Y$ H5 |2 v# O" U  \" Y
  }
& w3 c9 Q# P2 ]- m 2 b6 ~' i' }- j! Q. |6 V" O

- p2 H' B( r3 [说明:以上是初始化的关键部分,SYSTICK频率默认为系统时钟的8分频,配置好了时钟,自然就可以轻松设置SYSTICK了。
! l# ?; Q! V% I四,定时器 PWM 输出
$ U: Q3 y" E' B- a5 h7 W8 m7 Y- zSTM32的定时器很复杂功能也很多,此处仅说说TIM4作为PWM输出时的配置,效果是控制LED产生呼吸灯的效果。                                  此处仅配置TIM4-CH4的输出,其余几路输出同理:# W0 _, [, T! n$ y3 F6 c
//PD15 BLUE   TIM4-CH4. g" X2 ]5 P" z* m
u16 TIM4_CCR4_Val = 0;, s$ o! h; X4 L% V
u16 PrescalerValue = 0;//预分频器值
9 h1 ~3 ?4 n+ ?9 _+ xu16 pwm_tempvalue=1;9 g( D* v9 d& \" n
//TIM4-CH4 PWM输出初始化
1 `1 c9 B" }3 {9 J' G//必须对PD15的IO口进行重新配置/ |. V: R# z$ T' ]; Z& v0 y
void TIM4_CH4_PWM_Init(u16 arr)! {6 r* r! D! ]& l
{
$ m' J+ X$ V+ X/ Z        //TIM4时钟使能
7 u: j& P: ?3 S        RCC->APB1ENR |= 1OSPEEDR |= 0xC0000000;3 \0 ]0 c1 f9 K! D5 y
        //当速度超过50MHz时使用IO补偿单元
" F7 l7 u4 p7 x, Q& ]" Q        SYSCFG->CMPCR = 0x00000001;$ K! [9 ~" q$ e& M' \! k2 l
        //PD15上拉,PUPDR15[1:0]即31,30位  01ull-up
. M% l( K, m, n8 C$ e* _        GPIOD->UPDR |= 0x40000000;
. P) B8 @2 ]1 n! y        //配置第二功能寄存器(必须开启),PD15对应GPIOD->AFRH
6 ~, m+ r! }. H* N& P! M: W# d( T        //31,30,29,28位 AF2:0010& h& O; u# Q6 J+ I3 _
        GPIOD->AFR[1] |= 0x20000000;
; J5 l$ @. v' P( E
7 y9 \# w% ?3 y; M' K3 S
        //计算预分频器值, F: T) r  L1 a6 K1 T, `
        PrescalerValue = (u16)((SystemCoreClock/2)/28000000)-1;        . `+ @! ]- C- J: r9 t% y- t. u% _
        //设置计数器自动重装载值
- z* F5 F$ T: i2 U: |: ?        TIM4->ARR = arr;
5 Y" m5 s: G4 g2 n$ y* r        //设置预分频器分频值+ ^% ]% Z4 n) P8 |# @. V' z
        TIM4->SC = PrescalerValue;5 V4 j: e: U& }0 o- [# L% v
        //时基配置:Bits 9:8 00:t(DTS)=t(CK_INT)
; F4 C7 Z' G3 g7 _" _6 ?& i        //TIM4->CR1 &= 0xFCFF;
4 s* b9 x4 a$ s8 f' V: Z1 b. A4 n5 `        //计数模式配置:Bits 6:5 00  q" D! O/ d1 s# I: ]/ c
        //TIM4->CR1 &= 0xFF9F;        , z- d1 I/ S  M: j: X6 }; A
        //配置TIM4的CH4为WM1模式输出" `: f9 y0 W8 ^
        //配置TIM4_CCMR2的OC4M(位14,13,12)为:110
' L6 C7 V1 U& V/ }: s6 Y+ ~, r        TIM4->CCMR2 |= 6CCR4的值来该表占空比,从而控制LED的亮度/ @. q1 x; g. `7 S1 n) N
        TIM4->CCR4 = TIM4_CCR4_Val;
6 M- n5 {: w5 x8 D}4 T- n4 j+ `1 K

/ N5 T, ?& g) r. A  ~5 ^主函数中:
: W8 A7 k" Z3 ?( g7 u% ?; t! vTIM4_CH4_PWM_Init(800);//TIM4 CH4 PWM输出初始化3 N; Z- b+ M# V  x/ t" |& k
循环里调用TIM4_CH4_PWM_OutPut();  即可
& @6 q& b! l: F. c% N" @6 e, E 0 m4 v8 S3 x0 n6 Q3 F1 V' p& V" x+ g
说明:1,粗体部分是重点    2,IO要配置成第二功能模式     3,延时控制了呼吸灯的节奏,占空比控制了LED亮和灭的范围,这个可以自己多次设置后调试体会。
1 v+ b; p3 U8 V( T( n
8 e$ u# t# l5 T, a( k9 }五,RNG 随机数生成器
- ^4 ]2 z; X4 I4 d这个是STM32F4比较有意思的一个功能,可以硬件产生一个随机数,更加方便易用,配置也相对简单:+ d& M+ o, y& V2 e# b
void RNG_Init(void)//随机数寄存器初始化
  A' u) u+ y( N  i{* Y0 T9 N7 u# s3 w
  RCC->AHB2ENR |= 1CR |= 0x00000004;
( p, r3 T4 d( l/ B% p5 E  RNG->SR &= 0xFFFFFFFE;
5 W; U; e& k* b  RNG->SR |= 0x00000001;  ( K: z- E4 x! v* _2 C) v, i  b
}
& ~* K- `+ K+ |0 j1 z0 [2 n % q: p$ w; \0 Z" g
int RNG_Generate(int y)//生成随机数. G) A. ^2 B& W# r- V: R* F. q/ d, z
{: @# q0 z4 q+ _5 Z$ W
  int x;
8 ?0 _9 q7 w$ Y: @, \% i$ d  x=(RNG->DR) % y;  
( V$ D) T* h) j+ ?+ U# F7 d: r  return x;/ C( i+ M4 p3 w- B% a7 h! p
}
, y9 r" \4 v( L. p$ W1 n  E说明:开启相应时钟,简单的配置几个寄存器,然后再main中初始化:RNG_Init()就OK了,若要在其他外设文件中使用随机数,包含头文件rng.h,然后定义变量接收函数RNG_Generate(int y)的值即可,简单易用。
+ \# M: Q: h9 {+ e& Z------------END
% B( c* d4 ]1 k1 {对于STM32F429,我还是很期待的,比较自带了LCD,还有SDRAM,更多的应用可以被做出来,更流畅的跑系统和需要大内存的应用了,本想自己画个板子,接LCD,然后插在STM32F407 Discovery板子上的,可是硬件设计又是个难题,万一设计错误就无法使用了,所以STM32F429是个不错的选择,官方的开发板,外设丰富很超值。1 l& b9 Q0 V/ k' p- [" t
以上是本人研究的一些东西,给大家分享,本人也是菜鸟,希望大家多指导,共同进步
2 ?  \$ @1 q  F8 a1 H
收藏 2 评论4 发布时间:2014-4-19 11:11

举报

4个回答
仙人掌 回答时间:2014-4-24 21:25:40

RE:【MCU实战经验】STM32F407 Discovery开发实例心得分享

最近在研究F4的ADC功能,发现跟F1的寄存器不同,比如:
# J) s- G) _7 U* g0 V3 @1,选择触发方式为软件触发时,F1的寄存器有对应设置的位,但是F4中我没有找到,也不清楚到底有没有?
( k7 L1 ?8 X! w2,ADC校准,在F4的寄存器手册中找不到对应的寄存器的位,不校准,岂不是精度很差?' l" h, k8 ]. Y! P5 O% T
以上两个问题,请教下大家!希望有人能发表相关意见和经验!
bjybjy 回答时间:2014-6-18 17:12:57

RE:【MCU实战经验】STM32F407 Discovery开发实例心得分享

楼主你好,请教一下,STM32F407 Discovery USART CTS/RTS该怎么用,谢谢
youxiaoxiang 回答时间:2014-11-24 12:44:44
楼主你好。看了你的PWM 输出 做呼吸灯的问题。有一点疑惑。+ j" @0 F, x4 C5 A
具体是如何调节的?
巅峰残狼 回答时间:2014-11-24 13:01:16
谢谢分享
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版