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

【经验分享】STM32H7的FMC总线应用之是32路高速IO扩展

[复制链接]
STMCU小助手 发布时间:2021-12-27 17:00
48.1 初学者重要提示
3 A; q2 ]5 v+ S. D  学习本章节前,务必优先学习第47章,需要对FMC的基础知识和HAL库的几个常用API有个认识。
' w5 S) Q  X. b# u: b  为什么要做IO扩展,不是已经用了240脚的H743XIH6吗?因为开发板使用了32位SDRAM和RGB888硬件接口,消耗IO巨大,所以必须得扩展了。0 t' ~7 |9 _+ r& E* v5 f
  扩展的32路高速IO非常实用,且使用简单,只需初始下FMC,32路IO就可以随意使用了。当前的扩展方式只支持高速输出。' k. t4 ?' O% Y# [/ [
  FMC总线扩展32路高速IO理解成GPIO的ODR寄存器就很简单了,其实就是一个东西。5 B7 R" R  s, w+ _8 I0 ~
FMC扩展IO是对地址0x60001000的32bit数据空间的0和1的操作。GPIOA的ODR寄存器是对地址 0x40000000 + 0x18020000 + 0x14 空间的操作。但只能操作16个引脚。
4 R! `0 ?: j- G2 Y1 p% I
& v) P; l2 L; y使用总线的优势就在这里了,相当于在GPIOA到GPIOK的基础上,又扩展出GPIOL和GPIOM。7 C2 b; i: \7 F% z/ x

$ V( O! X2 ]% V* W
  1. #define PERIPH_BASE            ((uint32_t)0x40000000)" {; W2 f9 |! {
  2. #define D3_AHB1PERIPH_BASE     (PERIPH_BASE + 0x18020000)2 m. \: j9 e4 p3 R; j4 {- Z
  3. #define GPIOA_BASE             (D3_AHB1PERIPH_BASE + 0x0000)3 M' F% U! q/ B7 z
  4. #define GPIOA                  ((GPIO_TypeDef *) GPIOA_BASE)
    : n; t  c! Z% P( M$ _6 N- ~( y
  5. " Z$ g& J; i- |8 |7 k& S; E/ @
  6. typedef struct# O- n8 I0 `5 b
  7. {
      I8 M% k9 u) }. d
  8.   __IO uint32_t MODER;    /*!< GPIO port mode register,               Address offset: 0x00      */
    ) l5 {$ T: c7 J9 _- z
  9.   __IO uint32_t OTYPER;   /*!< GPIO port output type register,        Address offset: 0x04      */# _8 C  C4 D  q3 }3 I- X
  10.   __IO uint32_t OSPEEDR;  /*!< GPIO port output speed register,       Address offset: 0x08      */$ L6 }1 s+ i/ x) a) I
  11.   __IO uint32_t PUPDR;    /*!< GPIO port pull-up/pull-down register,  Address offset: 0x0C      */
    9 R( n" X. Z/ }# J" W
  12.   __IO uint32_t IDR;      /*!< GPIO port input data register,         Address offset: 0x10      */; g8 a6 |6 b, C3 @* m4 c
  13.   __IO uint32_t ODR;      /*!< GPIO port output data register,        Address offset: 0x14      */  x/ M9 m: @0 R7 [7 }8 L
  14.   __IO uint16_t BSRRL;    /*!< GPIO port bit set/reset low register,  Address offset: 0x18      */
    ) x) V3 o" H$ U1 |
  15.   __IO uint16_t BSRRH;    /*!< GPIO port bit set/reset high register, Address offset: 0x1A      */. v4 s0 v2 A0 `" [2 ?
  16.   __IO uint32_t LCKR;     /*!< GPIO port configuration lock register, Address offset: 0x1C      */
    $ d; P& f/ S0 H1 F1 _1 w
  17.   __IO uint32_t AFR[2];   /*!< GPIO alternate function registers,     Address offset: 0x20-0x24 */
    7 I4 |2 S  \: ?9 P* b8 B
  18. } GPIO_TypeDef;
复制代码
( M: C% V1 V4 p* v& k- |
48.2 FMC扩展IO硬件设计% \: ]. `' Y- b3 a9 y* M
扩展IO涉及到的知识点稍多,下面逐一为大家做个说明。
! d- d7 D8 [9 w; a7 M& W. V
( V6 F( q3 x7 [( x2 \" B48.2.1 第1步,先来看FMC的块区分配
1 N& Y# c2 z3 k0 E" B+ z注,这个知识点在前面第47章的2.3小节有详细说明。7 E) E9 q$ M1 @* ^4 ~

! ?" V; G6 L2 e1 W+ zFMC总线可操作的地址范围0x60000000到0xDFFFFFFF,具体的框图如下:4 R4 h. b9 A7 ?2 g& G/ L$ k

  V1 U1 b$ I8 F* B% P3 S1 a
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png

6 m7 b' w# R7 l' J1 i
- O6 B- B0 [$ \! y从上面的框图可以看出,NOR/PSRAM/SRAM块区有4个片选NE1,NE2,NE3和NE4,但由于引脚复用,部分片选对应的引脚要用于其他功能,而且要控制的总线外设较多,导致片选不够用。因此需要增加译码器。# E+ k8 b2 J8 l0 G* h4 J  O5 M

6 p" \/ T& a* J48.2.2 第2步,增加译码器及其地址计算
$ s) ^7 C  k) Z6 y+ T有了前面的认识之后再来看下面的译码器电路:
' Z$ X" F5 X' x. @: b% p* k2 g' P' ]6 z( s& x! O% j
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png

: b% Y& U. U% E: b! c" w+ L# G8 o5 b# w; W2 t4 A% ]& f; @
SN74LVC1G139APWR是双2-4线地址译码器,也就是带了两个译码器。原理图上仅用了一个。下面是139的真值表和引脚功能:9 z; @+ L/ }) Q5 M

8 `+ E( w( N6 Z7 w2 Z/ s. |
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png
4 r( f9 Y: K$ u9 z8 t4 f/ L

7 |8 b  I2 P6 F( t4 d$ P; D  D! B# C
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png

+ _" C; R9 i& S- E% O; t' B7 `; U. P8 c
通过上面的原理图和真值表就比较好理解了,真值表的输出是由片选FMC_NE1和地址线FMC_A10、FMC_A11控制。+ w1 \) P1 h, Q  m/ s
" T: b- k* W9 Y/ y/ \* e' z
FMC_NE1 输出低电平:. G& E: d5 z% ?) H( Y  z  o

; t+ C' l) a: D* d; s* \, ^  FMC_A11(B),FMC_A10(A) = 00时,1Y0输出的低电平,选择的是OLED。
* j' v( M& h* Q% p0 K. K  FMC_A11(B),FMC_A10(A) = 01时,1Y1输出的低电平,选择的是74HC574。; q1 u  u6 n; V" Y, J  w3 `5 o
  FMC_A11(B),FMC_A10(A) = 10时,1Y2输出的低电平,选择的是DM9000。& S3 J! N5 x6 j3 c6 _
  FMC_A11(B),FMC_A10(A) = 11时,1Y3输出的低电平,选择的是AD7606。
! x* N. G( _; E3 V( M然后我们再计算译码器的地址,注意,这里地址的计算都是按照FMC的32bit访问模式计算的,因为我们的V7程序中是将NE1对应的FMC配置为32bit模式了。& M! x" W9 }% L" z2 }7 Z! A

0 B; ~! {! K0 H! L. e具体FMC的32bit访问模式,16bit访问模式和8bit访问模式的区别在第47章的2.4小节有详细讲解。
+ W* i" b$ J2 L: ?, d  E* D* y+ t# x3 ^6 T% K- D
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png
: M; [; B, I9 u5 r

$ K7 p% I- d0 Z/ t9 f: k32bit模式下,我们计算A10和A11的时候,实际上需要按HADDR12和HADDR13计算的。
6 C( V- a( n1 g3 M9 j; q
/ z, z& }2 Z# f- k9 s+ I如果来算NE1 + HADDR12 + HADDR13的四种组合地址就是如下:
* j* ]( a* q" ~! E1 N/ G) X% S: d4 i9 J% u8 [0 T. `
NE1 + HADDR13 + HADDR12 = 0x6000000 +  0<<13 + 0<<12 = 0x60000000
: a, Y- a. Q. X5 b5 P* ~. A; v, g% l$ a! F
NE1 + HADDR13 + HADDR12 = 0x6000000 +  0<<13 + 1<<12 = 0x60001000
) [; x- G, f8 I/ U2 |
. T/ C6 D; e5 n) j& mNE1 + HADDR13 + HADDR12 = 0x6000000 +  1<<13 + 0<<12 = 0x60002000/ q" k# _7 F, _# a

" c7 j  i( C1 }3 g0 QNE1 + HADDR13 + HADDR12 = 0x6000000 +  1<<13 + 1<<12 = 0x60003000( M; q$ W" b2 g2 D, b
4 u  t/ c' Q: m0 Y. O, A- C, \
这样一来,原理图里面给的地址就对应上了。同理如果配置为16位模式和8位模式,大家应该也都会计算了。  x4 R) p+ y; a! G. Q: P+ Y

" F- K* b3 L* n; f  G' d$ ?, l/ E48.2.3 第3步,FMC的IO扩展部分7 e( o0 z+ N% d/ m& o% u5 f7 A
先来看下IO扩展的原理图实现,如果不太了解FMC的通信时序和数字逻辑芯片的使用,可能会比较懵,下面逐一为大家说明。
4 R- E: X, F2 c8 ?% S4 N  s
$ I/ T. j; R' u0 n- M* J; a
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png
: f4 q: X# ?0 D2 m2 c
, N. u6 s( Z, k; ^7 D# L2 h
有了这个原理图,首先要做的就是了解74HC574和SN74HC02的功能。/ K0 S! Q  G' d  `" R, Y
# [( Z) C6 R6 n7 X. |4 B
74HC574是一款8位三态D触发器,起到锁存的功能,上升沿触发,对应的真值表如下(L表示低电平,H表示高电平,Z表示高阻):
4 |/ w/ k7 L. p) M( R/ X+ I) _+ f# p5 |5 X3 b- Z; ~
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png

, f3 E: y8 b# ~: c: w% D
$ L* H1 R0 b& v$ \* nSN74HC02是一款2输入或非门,一个芯片带了四组或非门,对应的真值表如下(L表示低电平,H表示高电平):
0 F- g0 ]& y: I5 ?  y9 B- {% k3 @$ v! k
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png

! }3 Q# \* z7 W
0 s  B" F" w2 O$ X% k有了这个认识后,我们再来看FMC的配置,V7开发板的BSP驱动包里面专门做了一个IO扩展的FMC配置,即文件bsp_fmc_io.c,配置方式是FMC_AccessMode_A,这种模式对应的写时序是:. Z, l: ~+ ]* y# z* R; l* v! g
5 H3 D; I. i! `% q( e  \
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png

: Q" ~& z( X, ?, j: K( q8 D6 h' d" w
5 ]5 v2 z* L& e5 O7 C. j那么问题来了,我们要实现的功能是通过FMC输出的数据要锁存在扩展IO的输出端,否则FMC时序信号消失了,扩展IO的输出数据也消失了,就起不到控制作用了。所以就用到74HC574的锁存功能,而锁存的实现需要一个上升沿触发,这个上升沿就是通过74HC02输出的。( {5 x$ q' D( ?5 n3 D9 \" M5 \

/ I% S0 s6 ]7 }1 Z7 K再结合上面FMC写时序图,在NE片选为低电平,NWE写使能信号为高电平期间,即地址建立时间段ADDSET内,74HC02是输出的低电平。
. b( F" l7 l6 `  u
+ b3 m; D5 [( J# f2 K5 Q1 G2 y. X. b进入到DATAST数据建立阶段,在NE片选为低电平,NWE写使能信号也为低电平时,74HC02输出高电平,正好是实现1个上升沿的变化,将数据总线上的数据锁存到74HC574的输出端了。这里隐含了一个知识点,数据还没有完全建立起来就锁存是不是会有问题。在下面的3.3小节配置具体时序参数时再为大家说明。0 M  n& m) v& f8 M

& }& L/ U* U3 W' ]! O# c/ E5 E48.2.4 第4步,举例扩展IO驱动LED应用
  W  K: L7 E! V& C/ Z进行到这里,再回过头来看LED驱动就比较好理解了。操作LED的亮灭就是操作FMC的数据引脚D8,D9,D10和D11。
& u% }$ ^$ t7 i1 Y) `2 o  R7 ^6 W. T8 X
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png

# F* D* I  e; F8 d7 T) G" u+ S4 X6 v  y, f$ N1 D* d
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png
! w& S+ v$ e+ z0 D( P
1 G1 N+ R$ ?2 D. w2 ~; T
对地址0x64001000发送数据就可以了,但是如何对这个地址发送数据呢? 反映到C语言的实现上就是通过固定地址的指针变量(跟我们操作寄存器是一样的),即
: y% Q9 {% {) O& p8 `$ u
7 f( i1 s- e1 Z#define  HC574_PORT     *(uint32_t *)0x64001000
* a4 B; [- _2 C3 C( l! T& m. F; i, n: j' N$ [! Y
如果要点亮LED1(低电平点亮),就是 HC574_PORT = 0x0000 0000。" }; }2 A# {- B8 \* a( O1 B

: d  b, E$ Z& n0 J& D如果要熄灭LED1就是HC574_PORT = 0x0000 0100,即操作FMC_D8的高低电平即可。7 t8 A( G" W7 b3 l- x
! ]* q9 c2 s, ^' x- r7 c1 a6 U' a7 a
48.3 FMC扩展IO驱动设计1 h' O- @/ W# ^9 l3 ]2 H
下面将程序设计中的相关问题逐一为大家做个说明。5 Q* f- b1 H" C' g- ]- s

+ @! G3 H; {! w48.3.1 FMC扩展IO所涉及到的GPIO配置
/ _0 a# ]9 [, }7 g这里仅需把用到的GPIO时钟、FMC时钟、GPIO引脚和复用配置好即可:8 e' \% t' v4 Y5 q+ J

: a# p5 X  _, a. `) C% j; k
  1. /*/ |, B% x9 L% p! H
  2. *********************************************************************************************************
    ! e  G# ^" L4 ]9 Z
  3. *    函 数 名: HC574_ConfigGPIO4 Y- W. g/ L3 z0 h
  4. *    功能说明: 配置GPIO,FMC管脚设置为复用功能
    : k& z; T5 m4 i+ `
  5. *    形    参:  无' ^3 P! |+ z- b! f
  6. *    返 回 值: 无; d6 H" Q7 d' d( k% i2 C
  7. *********************************************************************************************************& Z- m; \  `, L; j7 Q* F# ]
  8. */
    * u9 M( |5 L  ~9 d& O1 f
  9. static void HC574_ConfigGPIO(void)
    ! l$ \+ J# U3 R, c. k: C
  10. {
    7 c7 K9 J5 n$ K% U5 i
  11. /*
    9 U2 H3 x. N" {: u
  12.     安富莱STM32-H7开发板接线方法:4片74HC574挂在FMC 32位总线上。1个地址端口可以扩展出32个IO
    " e8 \) l) c* r* u
  13.     PD0/FMC_D28 B7 k( {4 M) E& q/ [
  14.     PD1/FMC_D3$ B' b7 u. r5 c$ x8 \
  15.     PD4/FMC_NOE        ---- 读控制信号,OE = Output Enable , N 表示低有效
    + u. u/ Z: b* j) U, K
  16.     PD5/FMC_NWE        -XX- 写控制信号,AD7606 只有读,无写信号& M0 t0 m& J7 M; C' t9 B9 A1 O
  17.     PD8/FMC_D13
    6 H! L% X6 S# Y6 v# Q
  18.     PD9/FMC_D14
    : `! b7 ^4 \! {) r9 o/ ^
  19.     PD10/FMC_D15
    % G. B7 q9 f) x- t) F* N: C
  20.     PD14/FMC_D0! l- f- A) h# A5 y+ m9 J
  21.     PD15/FMC_D1
    1 J% Q: m) _! D; z1 i

  22. 5 Q5 \9 f4 w& {. e
  23.     PE7/FMC_D43 G7 s& S# N' f- X, n9 i+ Z
  24.     PE8/FMC_D5
    & {! V8 W# X) Q8 A2 r: H
  25.     PE9/FMC_D69 k+ q" f! l0 @/ @4 E$ A
  26.     PE10/FMC_D7# c% q% o% @5 T# `3 H9 c
  27.     PE11/FMC_D8/ n5 w' x/ f4 \4 }* q, y3 C
  28.     PE12/FMC_D9. O/ i/ I3 |  g8 ?  m! `
  29.     PE13/FMC_D10
    % X( q+ [8 v2 f5 R  j* z
  30.     PE14/FMC_D11
    " H2 \7 b" l3 W2 b! {- O
  31.     PE15/FMC_D12
    3 ~# S3 X  ]" \  C! A+ Q
  32. ; g+ Y$ x( J* U% @" M
  33.     PG0/FMC_A10        --- 和主片选FMC_NE2一起译码
    8 h3 W/ `2 k4 Y, E3 f; q
  34.     PG1/FMC_A11        --- 和主片选FMC_NE2一起译码
    ) Q) t4 w& K+ {; Y
  35.     XX --- PG9/FMC_NE2        --- 主片选(OLED, 74HC574, DM9000, AD7606)    ( X& u5 q' Z: K/ a1 n
  36.      --- PD7/FMC_NE1        --- 主片选(OLED, 74HC574, DM9000, AD7606)   
    6 v1 M' Y' K8 y& l$ H* Q$ c

  37. ) z6 [, _6 T7 |6 L" Z0 b, c
  38.      +-------------------+------------------+
    : ?6 r3 L  D  g! B
  39.      +   32-bits Mode: D31-D16              +
    ( F2 n7 V! u% y8 d
  40.      +-------------------+------------------+/ L* }' u4 Z/ q& J2 M
  41.      | PH8 <-> FMC_D16   | PI0 <-> FMC_D24  |* D2 l! Z6 M) Q7 @3 t
  42.      | PH9 <-> FMC_D17   | PI1 <-> FMC_D25  |' s9 V: K' n5 z: v2 V5 ]
  43.      | PH10 <-> FMC_D18  | PI2 <-> FMC_D26  |
    / m* P4 y3 n" f7 Z, _
  44.      | PH11 <-> FMC_D19  | PI3 <-> FMC_D27  |0 w) }7 x# S- ^
  45.      | PH12 <-> FMC_D20  | PI6 <-> FMC_D28  |3 T2 L1 _- m/ V" F9 Q
  46.      | PH13 <-> FMC_D21  | PI7 <-> FMC_D29  |
    ( D9 Z( f4 f. [* M+ m5 [* J, A
  47.      | PH14 <-> FMC_D22  | PI9 <-> FMC_D30  |; Z$ b( x/ T3 ^. d& O
  48.      | PH15 <-> FMC_D23  | PI10 <-> FMC_D31 |3 f/ l- X5 i  ^
  49.      +------------------+-------------------+    5 Y3 S# {' ?7 `1 X; ~: q! C: \, [* q
  50. */    ; o0 @; h. Q, E% o* I2 a. Z

  51. , B7 W- X* }  j6 N
  52.     GPIO_InitTypeDef gpio_init_structure;- H! e+ O4 v! X3 {, t% x5 C
  53. / t9 y# [+ ^2 E& ]/ A$ Z
  54.     /* 使能 GPIO时钟 */$ |4 S# a2 f8 p. w3 s
  55.     __HAL_RCC_GPIOD_CLK_ENABLE();
    ' D4 x! l  i, m8 E. G/ a7 I
  56.     __HAL_RCC_GPIOE_CLK_ENABLE();
    4 A7 [4 g- V. f" N7 y% i6 }4 _
  57.     __HAL_RCC_GPIOG_CLK_ENABLE();
    # k3 @; Q0 l# e$ ?9 h
  58.     __HAL_RCC_GPIOH_CLK_ENABLE();. S! |( p( [! G, c4 y0 p: O
  59.     __HAL_RCC_GPIOI_CLK_ENABLE();
    # Z# \) [6 ?. r, ?+ [- [4 @
  60. & B% j9 j# q9 X0 z4 V" I( F/ e

  61. " {2 [/ a; z- @" t* h
  62.     /* 使能FMC时钟 */4 L. m) V- T9 a4 V3 R( X* h
  63.     __HAL_RCC_FMC_CLK_ENABLE();$ U. N: j- `2 H- d0 ]) U7 ~  m1 U
  64.   x$ s/ k! i& L* T) e1 l6 d
  65.     /* 设置 GPIOD 相关的IO为复用推挽输出 */; V5 L8 E5 H- Y# \7 r( v
  66.     gpio_init_structure.Mode = GPIO_MODE_AF_PP;
    8 @" r+ }5 y1 H. Q1 J- w1 ?
  67.     gpio_init_structure.Pull = GPIO_PULLUP;1 P1 x+ p- Y: E$ s7 }
  68.     gpio_init_structure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    / w. w' ]3 g" M4 L
  69.     gpio_init_structure.Alternate = GPIO_AF12_FMC;
    % w1 b: R2 ]7 H# Q2 q; U

  70. " @+ B/ s! Y% z- M! p
  71.     /* 配置GPIOD */
    4 E; [* i1 k8 d: f% s  H
  72.     gpio_init_structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_7 |
    6 P# Q' B! ?5 Q, |  a
  73.                                 GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_14 |
    7 e& k. Q/ b/ |, [4 j. A/ [* j
  74.                                 GPIO_PIN_15;. Y9 A7 [3 K7 O. G2 z6 o0 _
  75.     HAL_GPIO_Init(GPIOD, &gpio_init_structure);
    1 m" ]- I( s: C4 {

  76. 9 O: ?1 r* |" W* U4 _& E
  77.     /* 配置GPIOE */
    # ^* Y" t4 U+ |( Z6 F4 d
  78.     gpio_init_structure.Pin = GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 |4 w: p3 I6 L' J# _* n
  79.                                 GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 |
    0 w: ~( d+ c  u, ~% A3 k+ R3 v
  80.                                 GPIO_PIN_15;
    / b$ }9 X& u: ?+ E& d! l1 a. K
  81.     HAL_GPIO_Init(GPIOE, &gpio_init_structure);* H8 Z5 f( W2 K, p

  82. . y+ c( f6 C: i
  83.     /* 配置GPIOG */) q% t! f( o6 n. p3 v4 D3 M5 S
  84.     gpio_init_structure.Pin = GPIO_PIN_0 | GPIO_PIN_1;2 @. Y: y$ i3 b6 g0 r$ x
  85.     HAL_GPIO_Init(GPIOG, &gpio_init_structure);
    ) q/ z: U# L0 v3 e5 Z- V% J

  86. 7 z; S: l. X% U, e5 I# I
  87.     /* 配置GPIOH */
    " I% d" z2 h7 p7 ~/ G7 F
  88.     gpio_init_structure.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12
    % E" i& b/ E/ I9 H$ U0 x1 w
  89.                         | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
    5 }( v2 l* ]0 ^; a  b' ]$ \
  90.     HAL_GPIO_Init(GPIOH, &gpio_init_structure);4 y: i5 v# A( y* H4 s- n! C
  91. + b. |0 h% w6 Y. ?  L
  92.     /* 配置GPIOI */, g2 l, a: {$ |9 T( e+ ^8 i/ o8 H
  93.     gpio_init_structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_6; J* H, S' j- C$ c2 y: w# j
  94.                         | GPIO_PIN_7 | GPIO_PIN_9 | GPIO_PIN_10;
    1 b3 c8 s, ^! m3 n+ V
  95.     HAL_GPIO_Init(GPIOI, &gpio_init_structure);
    * O3 B2 R8 n7 {% L
  96. }2 a: p8 ~- l$ |! v/ \- [' @1 T( G
复制代码

* l2 J7 @* s6 I. k& j" H- d# j; c# k
48.3.2 FMC扩展IO时钟源选择
5 m2 t4 u" b/ s$ \, n: a8 d使用FMC可以选择如下几种时钟源HCLK3,PLL1Q,PLL2R和PER_CK:. Z3 Z! N, f5 l% R% b  t# g5 O
4 C6 H" b5 G* j; i7 j7 c! [
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png

  \  Y' X' [) l& u4 {' o6 k, L0 \7 ?4 F9 A1 j- v1 N5 [  S
我们这里直接使用HCLK3,配置STM32H7的主频为400MHz的时候,HCLK3输出的200MHz,这个速度是FMC支持的最高时钟,正好用于这里:
" f5 Z0 v2 R3 H; _  H5 o6 D' C9 c; ^# \4 w* b9 }$ c: H
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png
; Z2 W& p/ P  c0 m
7 }+ e6 M7 \) ?# R2 A+ }
48.3.3 时序配置(重要)) g1 T3 C# k+ ?9 ^0 b
这里要补充两个重要的知识点,74HC574的CP端接收到上升沿触发到Qn输出的时间参数:
* p. O. w7 K4 [4 f- }
, k. \  u9 d' H  q6 p: V$ T; _- [
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png

# ?* p6 H7 ^2 B- j6 N
: `6 s7 ^2 t( u) N# g& G1 q7 o
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png
2 O9 u9 A0 E& B9 D
3 ~' A5 Q/ K8 ~) m8 P
通过时序图和对应的参数要了解到以下几点:
& M0 n2 f: ~: r9 v' \$ t$ M/ [2 Q* o$ a( ]! w6 L  j$ C" ^; X1 b
  tpd传输延迟在这里等效于tPHL和tPLH。- I% F' R2 y0 V' w) h' j6 S
  V7开发板的74HC574有三片是3.3V供电,另外一片是5V供电。参数表格里面没有给3.3V供电时的参数,也没有最小值。
2 l: B% \6 m9 [7 y; W, U+ C) B7 X# [+ j/ Y+ ?

0 O1 |& @% Z( J" f0 C了解了74HC574,再来看SN74HC02:1 |8 l$ V/ E0 D) B1 m& `
$ a& ~7 p, t, b1 \. X6 E: ]
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png

4 U* `" }+ |5 p4 ~( v! d7 c8 Z7 L, w6 c
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png

; v% B( w% K2 \; N
7 c: [- [. v+ K& O, Q通过时序图和对应的参数要了解到以下几点:9 C3 S% Z' O2 f2 ]" w7 V

) T* ?5 v3 B! G  i4 ]' B  tpd传输延迟在这里等效于tPHL和tPLH。
) E  [# j0 o) {8 s) H  tt过渡时间等效于tr上升沿时间和tf下降沿时间。
& F6 y, K" i% ^5 V2 N% G/ G- R- _  V7开发板的74HC574有两片是3.3V供电,另外两片是5V供电。参数表格里面没有给3.3V和5V供电时的参数,也没有最小值。" h& ]$ t( V- b3 z
- m( C+ e" E/ o" C! h' B7 c% \
; @% y( N' b: S# W0 W
对应74HC574和74HC02的时序参数有个了解后,再来看本章2.3小节末尾的问题:) y7 w. c1 i1 X2 v2 c4 f9 J

* C8 ^4 T& {3 _- Z/ y
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png

* L. C8 D) j( H  Y* f: N1 M5 ]9 o1 m$ L9 ~) g! Z6 L$ L
当写使能信号NWE出现下降沿后,74H02或非门就会输出一个上升沿,然后触发74HC574做锁存。此时我们要考虑到一个重要的知识点,就是使用的数字逻辑芯片有个传输延迟问题,也就是要我们要保证74HC02的tpd传输延迟时间 + 74HC02的tr传输延迟时间 + 74HC574的tpd传输延迟时间的这段时间内,数据总线上要有数据,所以保证DATAST数据建立时间够大就行。实际测试FMC频率在200MHz的情况下,2-3个FMC时钟周期就已经可以正常使用。
' R  X9 a. l( U* Z, N8 ]8 k. R1 ]
$ G3 m( l2 q6 }2 t/ E2 F; Q有了这些认识后,再来看FMC的时序配置就比较好理解了:' d# |% I" p3 n3 C; i9 e
3 d8 i8 B2 Y1 v/ r, Q/ v
  1. 1.    /*
    8 e7 k( j; U, Y9 o/ k" O. q
  2. 2.    ******************************************************************************************************
    ' o/ `! ]5 w2 [" p; e
  3. 3.    *    函 数 名: HC574_ConfigFMC
    & M$ r- k4 |9 I2 x7 J6 m
  4. 4.    *    功能说明: 配置FMC并口访问时序
    6 Z6 x( K" U, C+ h# e
  5. 5.    *    形    参:  无9 j# H. X' d3 N2 `: v
  6. 6.    *    返 回 值: 无
    6 l) o& z4 Z6 }2 M! X0 A7 r) ^' M
  7. 7.    ******************************************************************************************************
    5 ~5 s5 b" ~( w6 Z6 D0 w; C
  8. 8.    */
    $ Q& M" L2 R3 X) Y
  9. 9.    static void HC574_ConfigFMC(void)
    7 E$ f2 E3 s2 ^) I! c% e% A
  10. 10.    {' B4 m: x) n) s! O. d
  11. 11.        SRAM_HandleTypeDef hsram = {0};& i: C* P0 p- X: d; h" Q' v
  12. 12.        FMC_NORSRAM_TimingTypeDef SRAM_Timing = {0};# s6 L7 V" i/ @. D; @2 f/ |# c
  13. 13.            
    - P' H6 `2 }" i( [$ M" t2 i' X/ B! C$ W
  14. 14.        hsram.Instance  = FMC_NORSRAM_DEVICE;
    1 P# O% k1 Q) r. Q) P$ \* J
  15. 15.        hsram.Extended  = FMC_NORSRAM_EXTENDED_DEVICE;+ U9 I! y6 ?/ _8 U8 v; c  i& a
  16. 16.    0 C* D! J  {" m3 v9 Q- z" s+ C4 y
  17. 17.        /* FMC使用的HCLK3,主频200MHz,1个FMC时钟周期就是5ns */3 P- @5 e0 ~2 F( ~, E: q/ \
  18. 18.        /* SRAM 总线时序配置 4-1-2-1-2-2 不稳定,5-2-2-1-2-2 稳定 */  . N5 d8 D. I4 N! e5 y4 r
  19. 19.        SRAM_Timing.AddressSetupTime   = 5;  /* 5*5ns=25ns,地址建立时间,范围0 -15个FMC时钟周期个数 */* O3 C* R4 W2 L  d! r
  20. 20.        SRAM_Timing.AddressHoldTime    = 2;  /* 地址保持时间,配置为模式A时,用不到此参数 范围1 -15个时/ N9 M( p+ U! x) C9 b$ W% \
  21. 21.                                                  钟周期个数 */3 k/ W6 |1 \3 d# e! X
  22. 22.        SRAM_Timing.DataSetupTime          = 2;  /* 2*5ns=10ns,数据保持时间,范围1 -255个时钟周期个数 */
    & b+ L8 ]8 f5 _) M2 N2 l! g5 \% M
  23. 23.        SRAM_Timing.BusTurnAroundDuration  = 1;  /* 此配置用不到这个参数 */
    $ t  E9 i& Z2 D! B2 {7 N
  24. 24.        SRAM_Timing.CLKDivision            = 2;  /* 此配置用不到这个参数 */' e) R; ?0 t2 t1 l. @
  25. 25.        SRAM_Timing.DataLatency            = 2;  /* 此配置用不到这个参数 */, F4 h' ^1 i" d6 I: v$ y
  26. 26.        SRAM_Timing.AccessMode             = FMC_ACCESS_MODE_A; /* 配置为模式A */0 d, b4 X; s# T, b" t0 t
  27. 27.    # e/ P- ]( F) G' @% d; O# w& D
  28. 28.        hsram.Init.NSBank             = FMC_NORSRAM_BANK1;   /* 使用的BANK1,即使用的片选FMC_NE1 */( i0 e6 _1 m0 H# w
  29. 29.        hsram.Init.DataAddressMux     = FMC_DATA_ADDRESS_MUX_DISABLE;   /* 禁止地址数据复用 */
    0 V9 y  K$ ~( J% K5 H9 t
  30. 30.        hsram.Init.MemoryType         = FMC_MEMORY_TYPE_SRAM;           /* 存储器类型SRAM */
    ! d: X" ]) ^" ^+ e% Z' Y3 ?
  31. 31.        hsram.Init.MemoryDataWidth    = FMC_NORSRAM_MEM_BUS_WIDTH_32;    /* 32位总线宽度 */9 j: n0 {9 ~; P) S5 H
  32. 32.        hsram.Init.BurstAccessMode    = FMC_BURST_ACCESS_MODE_DISABLE;  /* 关闭突发模式 */
    4 C) |2 r, ^" G# k
  33. 33.        hsram.Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_LOW;   /* 用于设置等待信号的极性,关闭突
    % T& t8 v& {7 J- C& y. [/ I2 y& S
  34. 34.                                                                            发模式,此参数无效 */* M# G- L" V, I, @) I. l% p0 n' m/ Z
  35. 35.        hsram.Init.WaitSignalActive   = FMC_WAIT_TIMING_BEFORE_WS;      /* 关闭突发模式,此参数无效 */, x  Y7 T# _: l0 o" o% V* k7 L% D! d  o
  36. 36.        hsram.Init.WriteOperation     = FMC_WRITE_OPERATION_ENABLE;     /* 用于使能或者禁止写保护 */
    * t5 A. k+ m& `7 f
  37. 37.        hsram.Init.WaitSignal         = FMC_WAIT_SIGNAL_DISABLE;        /* 关闭突发模式,此参数无效 */
    ( I7 f+ n  p9 K" C
  38. 38.        hsram.Init.ExtendedMode       = FMC_EXTENDED_MODE_DISABLE;      /* 禁止扩展模式 */# d' \( d& o! j) h" v5 A7 M+ Z3 t) l( z
  39. 39.        hsram.Init.AsynchronousWait   = FMC_ASYNCHRONOUS_WAIT_DISABLE;  /* 用于异步传输期间,使能或者禁止
    1 a" M8 z# |" ^5 G/ n, v
  40. 40.                                                                            等待信号,这里选择关闭 */+ Q3 d) o7 J3 H0 ]  y4 [
  41. 41.        hsram.Init.WriteBurst         = FMC_WRITE_BURST_DISABLE;        /* 禁止写突发 */! t7 _  i# b  N1 v2 `& G2 {% ]
  42. 42.        hsram.Init.ContinuousClock    = FMC_CONTINUOUS_CLOCK_SYNC_ONLY; /* 仅同步模式才做时钟输出 */
    7 g# X- ^2 J/ W+ i6 Z. B9 i
  43. 43.        hsram.Init.WriteFifo          = FMC_WRITE_FIFO_ENABLE;           /* 使能写FIFO */
    0 j0 L# y8 U  [2 _4 S3 M0 |% N6 H
  44. 44.   
    4 s" B1 F( v: _$ p
  45. 45.        /* 初始化SRAM控制器 */% N9 H' c* h3 i/ f
  46. 46.        if (HAL_SRAM_Init(&hsram, &SRAM_Timing, &SRAM_Timing) != HAL_OK). w5 v7 e) m) X' y/ B) Q
  47. 47.        {0 Q+ E1 y/ i# c& w5 `
  48. 48.            /* 初始化错误 */
    / A7 ]) v0 T- D
  49. 49.            Error_Handler(__FILE__, __LINE__);
    * K6 Q) M( q8 t: G; }
  50. 50.        }5 _- J/ R. C0 _. ?0 P8 O
  51. 51.    }
    0 _3 J* L- U% P" }1 p- r8 f+ T, q; s
  52. * w* Q- X- Q4 @6 q
复制代码

3 D6 F1 i: y1 ~) {这里把几个关键的地方阐释下:" Z1 r6 v; G- H2 ~" }0 o# m5 T
. S7 U" `4 A- i" @
  第11 - 12行,对作为局部变量的HAL库结构体做初始化,防止不确定值配置时出问题。; f3 ?6 [: Z$ B) Y0 o
  第19行,地址建立时间,对于FMC的IO扩展来说,这个地方取值0都可以,因为主要还是ADDST数据建立时间起作用。但是考虑到扩展IO外接了多个控制设备,这里取值5个FMC时钟周期,大家可以根据实际情况做减小出来。
& r1 N: g$ \' \) Y  第20行,地址保持时间,对于FMC模式A来说,此参数用不到。
  @$ L6 H4 ~6 U$ W* }" L5 r" k2 r" h  第22行,数据建立时间,实际测试2个FMC时钟周期就可以正常使用,大家可以根据情况加大此数值。' Z  S  s0 ~# Z8 Y2 P) Q: W
  第23 – 25行,当前配置用不到这三个参数。
- Q% E/ O/ T7 [% \8 {4 b" l  第28行,使用的BANK1,即使用的片选FMC_NE1。
0 @  P  R3 c6 V# m5 c* {! E  f  第31行,由于是扩展的32路IO,所以这里要配置为32位带宽。
" W  `' K/ M' c48.3.4 MPU配置
( A# a$ T+ |+ j# ^/ R/ `实际测试发现,使能FMC_NE1所管理的存储区的Cache功能后,会出现扩展IO的NE片选和NWE信号输出2次的问题。经过各种Cache方式配置、FMC带宽配置、操作FMC时的数据位宽设置,发现禁止了Cache功能就正常了,也就是说,设置FMC_NE1所管理的存储区MPU属性为Device或者Strongly Ordered即可。
+ W4 M; {2 G* A6 c* b8 [
0 w/ f( z$ N4 R" g1 v* e) z8 E
  1.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */# O9 J6 R# M1 v+ h' q9 D* A
  2.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    4 `. n$ h0 G, l, ^# d2 R& S
  3.     MPU_InitStruct.BaseAddress      = 0x60000000;  x+ i2 g7 v8 j* L8 l! ?* U* h
  4.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    2 t- I8 d) e0 ~) |. v( v8 e# O
  5.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;& f. j9 ^5 |3 `& o1 z- z7 K
  6.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    $ l7 i1 i1 r/ U
  7.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    ! N/ r! P/ z  ~! V% }4 |. Q
  8.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    9 D8 ~; j8 {: m  r' J
  9.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    6 p9 k: e: a) V9 U7 T' J4 w4 X: f
  10.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    5 z( {( S3 v" @6 ~2 H7 i
  11.     MPU_InitStruct.SubRegionDisable = 0x00;
    6 ~" ?8 D- o3 \+ P- B) k, |$ P
  12.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;, P, B8 ~/ `. q8 C8 k
  13. ( j; v6 O" {% m: U" u% ~
  14.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
复制代码
; `% i+ U/ H& R' {" s: F' Z8 [
MPU配置中直接从FMC_NE1的首地址开始配置,设置了64KB空间的属性。将FMC_NE1通过译码器所管理的所有设备地址全部设置为此配置:
" d: G6 ^$ ~4 R1 N- z$ |8 ~
) [# K, j& h# s$ a: l3 H" ?
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png
1 m; K0 @$ V# N3 s0 k; ?* H+ M" \
; }) D. f- Z$ U3 O. x! B
48.3.5 操作数据位宽注意事项
8 o0 q# o0 V9 B# B6 C在bsp_fmc_io.c文件开头有个宏定义#define  HC574_PORT  *(uint32_t *)0x60001000。特别注意,这里是要操作地址0x60001000上的32位数据空间,即做了一个强制转换uint32_t *,要跟FMC配置时设置的位宽一致。这样做的原因,在第47章的2.6小节有说明。
9 S$ t* ^3 W% D  y1 p7 b
4 ]; P2 m8 m2 {6 a48.4 FMC扩展IO板级支持包(bsp_fmc_io.c)% Y! I" j9 S/ X  z0 {% Q( t
驱动文件bsp_fmc_io.c提供了如下几个函数供用户调用:+ i  i" @" o" e+ w
# A2 ]9 i' k& w" N6 @
  bsp_InitExtIO# s. l  ?: `7 B5 E
  HC574_SetPin# \; k0 A6 d9 L# a
  HC574_TogglePin; L; b5 X9 V7 M
  HC574_GetPin6 w1 F0 A  B, {
48.4.1 函数bsp_InitExtIO
* d4 @+ h" G, i; p函数原型:
0 Z; J- E9 o3 k4 m% }/ I
3 b6 W% z. N7 T
  1. /*
    0 K6 A. V. _/ t
  2. *********************************************************************************************************
    / v1 R# @% O, T  B
  3. *    函 数 名: bsp_InitExtIO7 j0 C3 P* Z7 L( f% q. y
  4. *    功能说明: 配置扩展IO相关的GPIO. 上电只能执行一次。
    # t  r+ m/ E) {) K9 [
  5. *    形    参: 无
    - s' g! J2 W; r
  6. *    返 回 值: 无
    9 F2 \2 ~/ U. J& _' ^) N
  7. *********************************************************************************************************. q2 t$ P7 u, u+ I) n
  8. */8 n( r9 j, a( S1 Z# p
  9. void bsp_InitExtIO(void)
    ; e9 y5 t" L- N
  10. {
    $ P  j3 b, c3 v0 G& W
  11.     HC574_ConfigGPIO();
    ( |# I. C/ y5 R$ X+ `0 E  ?! q# X" }+ X
  12.     HC574_ConfigFMC();+ U5 L6 |9 `3 y7 X- V2 f: m) F
  13. 0 {( Y6 O: r4 y' C* p& c4 V) _
  14.     /* 将开发板一些片选,LED口设置为高 */
    : ?8 E% V3 x4 O" s7 F4 F1 z4 m
  15.     g_HC574 = (NRF24L01_CE | VS1053_XDCS | LED1 | LED2 | LED3 | LED4);
    + h) _; t- u/ ?8 i+ k
  16.     HC574_PORT = g_HC574;    /* 写硬件端口,更改IO状态 */. K6 J4 k9 M" M. H$ E
  17. }
    $ m( x* g4 _) i+ _9 u! l
  18. . |+ M- N# P/ V5 I/ I, M
复制代码

" p) C* n7 G, q5 O6 l3 \: f5 ^6 d& h函数描述:
$ Z# `7 p* g# d! l; K' B, l  s  p4 G4 J
此函数用于初始化FMC扩展IO所用到的GPIO和FMC的参数配置。
, p$ t/ G) y% P+ p7 I+ ~  R  Y% c. ~" R
使用举例:- s) t8 k, N( e
0 `9 r5 J4 ^5 k* C& b- i% t
作为初始化函数,直接在在bsp.c文件的bsp_Init函数里面调用即可。
. w" n0 K* e( b4 F/ x0 d2 G2 l1 H# n5 [6 T/ f$ w2 Z
48.4.2 函数HC574_SetPin' b6 f8 C* N  B2 i7 z' t$ e
函数原型:
% Y# `9 b. d6 D/ p- r0 l8 I4 r& M( [0 h3 F! L  v/ j8 x9 G
  1. /*8 E* ^8 O3 e3 s7 L1 G' _
  2. *********************************************************************************************************! K% ^6 J, G& l. c: `  M+ C8 V& t
  3. *    函 数 名: HC574_SetPin. D+ x& b0 E- t
  4. *    功能说明: 设置74HC574端口值# l& q- D" C1 f& u5 g- u% x% T
  5. *    形    参: _pin : 管脚号, 0-31; 只能选1个,不能多选
    + y1 Z, ]& S6 L& D: p% w
  6. *              _value : 设定的值,0或1: L7 u- |: L/ N" [9 ]- c7 a
  7. *    返 回 值: 无
    ( ?  q0 Q6 W( c, M8 y6 v" N
  8. *********************************************************************************************************  @4 w) {) U1 m" b  A8 n% u
  9. */: f# Y' t3 I* K* j" S$ Z
  10. void HC574_SetPin(uint32_t _pin, uint8_t _value)
    . {! [1 E# j$ Y3 B# [% x8 s
  11. {7 M  A) \* I3 W" ~; R9 y
  12.     if (_value == 0). x5 I+ m, r! Z: C1 C5 V/ J
  13.     {
    ; ]+ |1 N7 m' F6 d& b
  14.         g_HC574 &= (~_pin);' Z& \1 A7 D% x
  15.     }$ S1 m* W& K, t9 u9 {$ ^0 l/ \1 l; b
  16.     else# _0 ~9 ~. _! Q) O+ B, B
  17.     {- t8 v, A: K8 Q$ Z
  18.         g_HC574 |= _pin;
    + b+ M) Z; h  V$ g
  19.     }
    - i# M6 b1 @+ l$ E
  20.     HC574_PORT = g_HC574;1 i2 m3 o- U% B! B# G! h" x+ C  `5 I% w
  21. }
    $ ^* V6 ?0 s! b. m3 T
复制代码
4 y' |: O' R% l5 W; G2 }
4 Y( N5 N) w7 B) M
函数描述:
. o* u- Y3 H/ u) _+ k: V
( ~5 G3 Z: h" i1 f9 |2 ~& B此函数用于设置扩展IO的输出状态。调用此函数前,要保证调用了函数bsp_InitExtIO进行了初始化。1 f1 O- L* }8 z) D+ H
8 Z2 n* C% G. @* j, B! [/ I6 E
函数参数:$ R2 r0 l( R$ O( P
. c7 p0 c# O3 \2 X1 A
  第1个参数是扩展IO的引脚,支持的形参如下,每次仅支持调用下面1个,不支持多个IO一起操作。; u0 Q$ T& Z4 z* u
  1. #define GPIO_PIN_0                 ((uint16_t)0x0001)  /* Pin 0 selected */
    % v' Q% ^' u0 M4 t1 z) v/ y
  2. #define GPIO_PIN_1                 ((uint16_t)0x0002)  /* Pin 1 selected */, n! S, C+ [! u( l
  3. #define GPIO_PIN_2                 ((uint16_t)0x0004)  /* Pin 2 selected */
    % x8 o8 m) l4 p- W% c/ @
  4. #define GPIO_PIN_3                 ((uint16_t)0x0008)  /* Pin 3 selected */
    8 M. r$ Q6 ?; s# L$ F3 s
  5. #define GPIO_PIN_4                 ((uint16_t)0x0010)  /* Pin 4 selected */9 x* F) E0 F) q' r6 V
  6. #define GPIO_PIN_5                 ((uint16_t)0x0020)  /* Pin 5 selected */
    " X$ A& X# k/ u! w$ M# _4 Y4 H
  7. #define GPIO_PIN_6                 ((uint16_t)0x0040)  /* Pin 6 selected */
    ' d9 }% g# z+ e8 x+ q2 g
  8. #define GPIO_PIN_7                 ((uint16_t)0x0080)  /* Pin 7 selected */
    5 D% R5 ?( }. S8 E, n/ A0 |5 b
  9. #define GPIO_PIN_8                 ((uint16_t)0x0100)  /* Pin 8 selected */
    & j. X$ d0 t( I) K# M. u
  10. #define GPIO_PIN_9                 ((uint16_t)0x0200)  /* Pin 9 selected */) {7 f0 T% R# l
  11. #define GPIO_PIN_10                ((uint16_t)0x0400)  /* Pin 10 selected */
    8 o; u) I5 Y3 Q  F+ U  L; p/ }& |
  12. #define GPIO_PIN_11                ((uint16_t)0x0800)  /* Pin 11 selected */) v! `% b0 f6 W7 j
  13. #define GPIO_PIN_12                ((uint16_t)0x1000)  /* Pin 12 selected */+ P0 _. b: _. Q* `/ s' r
  14. #define GPIO_PIN_13                ((uint16_t)0x2000)  /* Pin 13 selected */
    ( B0 R$ H1 B% l& Y6 H2 R& h5 l
  15. #define GPIO_PIN_14                ((uint16_t)0x4000)  /* Pin 14 selected */* q9 F5 C1 S0 y* x" C8 D
  16. #define GPIO_PIN_15                ((uint16_t)0x8000)  /* Pin 15 selected */
    # S! ?. A6 h/ m0 E! V0 g# @& I
  17. #define GPIO_PIN_16                ((uint32_t)0x00010000)  /* Pin 16 selected */
    - ?7 B- V& H+ K$ f% D, |: Y/ Y
  18. #define GPIO_PIN_17                ((uint32_t)0x00020000)  /* Pin 17 selected */8 O" G! h" X8 S! d( B. z
  19. #define GPIO_PIN_18                ((uint32_t)0x00040000)  /* Pin 18 selected */
    $ j3 T, p+ L6 g6 H. d
  20. #define GPIO_PIN_19                ((uint32_t)0x00080000)  /* Pin 19 selected */
    9 B& \, B9 ?9 m8 }0 T
  21. #define GPIO_PIN_20                ((uint32_t)0x00100000)  /* Pin 20 selected */, F7 c( L' i+ e
  22. #define GPIO_PIN_21                ((uint32_t)0x00200000)  /* Pin 21 selected */
    ' Z+ h* R2 c9 M2 J& H
  23. #define GPIO_PIN_22                ((uint32_t)0x00400000)  /* Pin 22 selected */
    ( `! m6 m/ d4 h8 O# h
  24. #define GPIO_PIN_23                ((uint32_t)0x00800000)  /* Pin 23 selected */- J& {; d& L; z9 }9 ~4 }
  25. #define GPIO_PIN_24                ((uint32_t)0x01000000)  /* Pin 24 selected */
    7 Z6 U6 \8 F' t5 `2 v3 L
  26. #define GPIO_PIN_25                ((uint32_t)0x02000000)  /* Pin 25 selected */  ^4 P" C  k# K3 `6 M
  27. #define GPIO_PIN_26                ((uint32_t)0x04000000)  /* Pin 26 selected */
    0 v4 z3 C% M8 e1 n9 s
  28. #define GPIO_PIN_27                ((uint32_t)0x08000000)  /* Pin 27 selected */) j% e4 {, p; ^% M' j, O' ~
  29. #define GPIO_PIN_28                ((uint32_t)0x10000000)  /* Pin 28 selected */5 b* R0 D0 U1 p4 X
  30. #define GPIO_PIN_29                ((uint32_t)0x20000000)  /* Pin 29 selected */
    " \2 x# {4 H, N! e6 w" c+ L
  31. #define GPIO_PIN_30                ((uint32_t)0x40000000)  /* Pin 30 selected */+ H/ Z% Y6 X' V/ ~3 P# \
  32. #define GPIO_PIN_31                ((uint32_t)0x80000000)  /* Pin 31 selected */
复制代码
4 }& g0 m% Y' n3 c
- G( c2 _) {) g- [
  第2个参数用于设置指定扩展IO的高低电平,0表示输出低电平,1表示输出高电平。' ~  M  a/ x  y6 q
使用举例:+ ~2 c: t" K7 t* H! G2 ^& G! U: j

) ~& l- j8 n$ {2 w# }+ [9 L比如设置扩展IO引脚GPIO_PIN_23为高电平:HC574_SetPin(GPIO_PIN_23, 1)。+ A6 q! h, H& `+ v/ q

% {$ n, [) i2 M5 O* K: H  j2 v48.4.3 函数HC574_TogglePin9 R0 j/ i3 F# m! |% o( \( t( K
函数原型:
( @/ O2 m. a) y/ R3 n( k# u6 z  k
, r% L; Y( j1 R/ C2 G0 f2 P
  1. /*
    - j# w. a; h, p( e: F% |3 X
  2. *********************************************************************************************************4 Z: F7 U% g# S& ^& H
  3. *    函 数 名: HC574_TogglePin6 l7 W! d9 P) t. O
  4. *    功能说明: 饭庄74HC574端口值
    - A8 |2 t+ G4 B! X" J- s: n* E* ^4 c
  5. *    形    参: _pin : 管脚号, 0-31; 只能选1个,不能多选8 G* z8 T. Y' r+ h* W3 Y) z
  6. *    返 回 值: 无
    - h0 w8 }& @' P. p
  7. *********************************************************************************************************2 y9 I. r; r, b* T
  8. */. E* `! {- N+ A" r( l+ s) S
  9. void HC574_TogglePin(uint32_t _pin)
    & b# \& \4 U# {; S% v, {5 h
  10. {3 i; d5 F+ Y6 t
  11.     if (g_HC574 & _pin)
    $ _# x& }( W; Q+ v: t$ z  ~
  12.     {
    0 b' t7 h! z+ z5 f# q& r7 a2 F# q
  13.         g_HC574 &= (~_pin);; x3 K! t' V5 z; X
  14.     }
    7 ?4 z0 E4 u& A" \! _
  15.     else9 z  f: o. A) x3 p+ u
  16.     {% x; z0 ^0 u% Z
  17.         g_HC574 |= _pin;* }& b- e+ p; e8 H. \- A" C3 H
  18.     }
    1 K8 ]$ w8 p9 {! f
  19.     HC574_PORT = g_HC574;
    / I4 J. y" Q) _4 d/ J7 v% i
  20. }! a3 }% u( c% q1 l& l* q
复制代码
) b& A- l6 G( Y

- |# E3 C1 h; C+ ?2 ?函数描述:# s8 z2 v) j1 j6 H, @
, l2 {+ o& p+ m
此函数用于FMC扩展IO的翻转。调用此函数前,要保证调用了函数bsp_InitExtIO进行了初始化。1 |# `7 l& j4 f/ @: x% N8 ?
1 K3 {" y' V8 b* w! Y" A6 N1 J) J0 |
函数参数:7 K5 u. L# n! U" {. q& N
" J; K! Y( q) y! Q* A0 [: A: d+ Z
  第1个参数是扩展IO的引脚,支持的形参如下,每次仅支持调用下面1个,不支持多个IO一起操作。; U% b5 t* N3 B
  1. #define GPIO_PIN_0                 ((uint16_t)0x0001)  /* Pin 0 selected */- R. e- T% M% g( j! a' M6 p- i
  2. #define GPIO_PIN_1                 ((uint16_t)0x0002)  /* Pin 1 selected */
    0 }3 {2 ?7 U5 \6 U: A" `
  3. #define GPIO_PIN_2                 ((uint16_t)0x0004)  /* Pin 2 selected */+ c; p1 A# s" ^
  4. #define GPIO_PIN_3                 ((uint16_t)0x0008)  /* Pin 3 selected */! W) S! g4 _; u# y* u3 L4 y
  5. #define GPIO_PIN_4                 ((uint16_t)0x0010)  /* Pin 4 selected */
    / v: c+ I. \  L+ m$ X
  6. #define GPIO_PIN_5                 ((uint16_t)0x0020)  /* Pin 5 selected */& X$ T& N5 D& B+ m! ~5 W
  7. #define GPIO_PIN_6                 ((uint16_t)0x0040)  /* Pin 6 selected */
    / K, q. w# h5 S
  8. #define GPIO_PIN_7                 ((uint16_t)0x0080)  /* Pin 7 selected */+ z; e5 O, o# Z# A* ?( T
  9. #define GPIO_PIN_8                 ((uint16_t)0x0100)  /* Pin 8 selected */
    ) B7 c5 `6 k7 V# e. O
  10. #define GPIO_PIN_9                 ((uint16_t)0x0200)  /* Pin 9 selected */. a3 b1 C3 d; k- |
  11. #define GPIO_PIN_10                ((uint16_t)0x0400)  /* Pin 10 selected */# i6 k2 D3 E& o/ M  t3 m, P8 a; W
  12. #define GPIO_PIN_11                ((uint16_t)0x0800)  /* Pin 11 selected */
    % s+ c; t$ y% g' Q+ ^/ D4 U) _
  13. #define GPIO_PIN_12                ((uint16_t)0x1000)  /* Pin 12 selected */
    3 h# W) t. Q' s; b$ r
  14. #define GPIO_PIN_13                ((uint16_t)0x2000)  /* Pin 13 selected */7 G1 D8 z, \+ S+ Y+ v! v7 T
  15. #define GPIO_PIN_14                ((uint16_t)0x4000)  /* Pin 14 selected */4 X  ^! A" O& n  [! `( B% d
  16. #define GPIO_PIN_15                ((uint16_t)0x8000)  /* Pin 15 selected */
    8 m& a/ w. r( t5 C# f
  17. #define GPIO_PIN_16                ((uint32_t)0x00010000)  /* Pin 16 selected */, `  s0 e$ n) d$ I/ Z6 e
  18. #define GPIO_PIN_17                ((uint32_t)0x00020000)  /* Pin 17 selected */2 k( l2 f" J7 T4 W8 ?
  19. #define GPIO_PIN_18                ((uint32_t)0x00040000)  /* Pin 18 selected */
    # P  Y5 h) p. Q- ]1 k7 ^
  20. #define GPIO_PIN_19                ((uint32_t)0x00080000)  /* Pin 19 selected */* y1 U4 ~) \. k  e- s& H( h% F
  21. #define GPIO_PIN_20                ((uint32_t)0x00100000)  /* Pin 20 selected */
      \( ^' b' O, k7 {, A  F8 ^
  22. #define GPIO_PIN_21                ((uint32_t)0x00200000)  /* Pin 21 selected */# T1 V  ^5 x0 U0 ~9 N; Z
  23. #define GPIO_PIN_22                ((uint32_t)0x00400000)  /* Pin 22 selected */
    # F' }# W- {) U4 N
  24. #define GPIO_PIN_23                ((uint32_t)0x00800000)  /* Pin 23 selected */3 X' @& ~" R) o  x8 Y3 X0 h
  25. #define GPIO_PIN_24                ((uint32_t)0x01000000)  /* Pin 24 selected */0 T) Q! f0 L: \# D
  26. #define GPIO_PIN_25                ((uint32_t)0x02000000)  /* Pin 25 selected */! s2 [( i% a; U# e. e
  27. #define GPIO_PIN_26                ((uint32_t)0x04000000)  /* Pin 26 selected */4 ?  C3 k3 L5 {2 h2 A( x2 l
  28. #define GPIO_PIN_27                ((uint32_t)0x08000000)  /* Pin 27 selected */
    8 a+ m( Q) ^# _7 t( `
  29. #define GPIO_PIN_28                ((uint32_t)0x10000000)  /* Pin 28 selected */
    , c/ G7 ?! e" S( A
  30. #define GPIO_PIN_29                ((uint32_t)0x20000000)  /* Pin 29 selected */' I' A" A& }" G# |+ X* h9 o  I
  31. #define GPIO_PIN_30                ((uint32_t)0x40000000)  /* Pin 30 selected */1 R6 F* O$ I; k7 z8 g
  32. #define GPIO_PIN_31                ((uint32_t)0x80000000)  /* Pin 31 selected */
复制代码
, E& c* X' f# N
使用举例:
4 D6 Y- R) W1 L: e" Y5 l6 X! k7 S) e7 y, \5 m# [9 @: a' l
比如翻转扩展IO引脚GPIO_PIN_23为高电平:HC574_TogglePin(GPIO_PIN_23)。
$ t0 E: q/ X% H  t
. g) ]! b2 \$ |. J* Q. b48.4.4 函数HC574_GetPin3 l/ s' S$ o3 l6 ]8 ^3 \, P# k
函数原型:' W1 r: |! ^; t4 }
6 x/ ]3 W5 t, R1 J3 Q
  1. /*
    " l: a2 g. P4 k7 L
  2. *********************************************************************************************************9 H" }5 c9 f6 j$ ^
  3. *    函 数 名: HC574_GetPin
    : m2 A- _/ z4 D
  4. *    功能说明: 判断指定的管脚输出是1还是0
    $ u6 k9 T* K3 o# w* O
  5. *    形    参: _pin : 管脚号, 0-31; 只能选1个,不能多选% m* W4 J; [; ~
  6. *    返 回 值: 0或1
    2 {. P( n: v) S* i5 j9 @
  7. *********************************************************************************************************
    6 L  r. J  |4 I: Q" I( k
  8. */
    * X) _$ E5 s2 W: n
  9. uint8_t HC574_GetPin(uint32_t _pin)/ j: ^7 U# T, U+ a2 P! P
  10. {
    0 D2 S0 V6 `% G; k& x7 }2 k
  11.     if (g_HC574 & _pin)
    " L/ S& Z; X8 I- e6 y# i
  12.     {
    # V& K: X8 Y  W7 x8 h0 G9 o
  13.         return 1;
    ; v/ L7 B. _- ^& Z8 V5 m
  14.     }
    4 J6 L, E* {2 U) x  O
  15.     else
    " G3 j) }( W+ [! ]7 l
  16.     {
    ' m, ~( F* M+ I7 t
  17.         return 0;; e4 |1 B" A9 f0 y
  18.     }& S$ k2 j# m: R
  19. }. P( t" r- Y' ]$ o$ l. [! E5 [
复制代码

2 t) n, G' U9 c+ g
( e) |+ D3 M% o% N* D函数描述:
$ W; f% X. q+ X9 }7 t
# o0 a  \! u; l9 g# ^. ]此函数用于读取FMC扩展IO的状态。调用此函数前,要保证调用了函数bsp_InitExtIO进行了初始化。% M# W; r' }: ?6 A

; v. _/ o5 O% F5 e* B. F函数参数:% |% {- ]7 a5 n

# z# ?1 ^1 @7 f# M7 r3 f6 h  第1个参数是扩展IO的引脚,支持的形参如下,每次仅支持调用下面1个,不支持多个IO一起操作。9 Y7 a( Y, {# b. Y, [! ~0 r
  1. #define GPIO_PIN_0                 ((uint16_t)0x0001)  /* Pin 0 selected */
    + i% h% w; o4 p- T# M
  2. #define GPIO_PIN_1                 ((uint16_t)0x0002)  /* Pin 1 selected */
    1 a9 H+ h6 ^9 B. I
  3. #define GPIO_PIN_2                 ((uint16_t)0x0004)  /* Pin 2 selected */  J# g! O7 w! U4 n5 C
  4. #define GPIO_PIN_3                 ((uint16_t)0x0008)  /* Pin 3 selected */, X# _/ a1 s$ M8 a9 B" [
  5. #define GPIO_PIN_4                 ((uint16_t)0x0010)  /* Pin 4 selected */
    # B$ P8 W! \7 o2 R7 \: h/ G
  6. #define GPIO_PIN_5                 ((uint16_t)0x0020)  /* Pin 5 selected */
    ) b4 t, h+ z6 q8 u
  7. #define GPIO_PIN_6                 ((uint16_t)0x0040)  /* Pin 6 selected */
    , R/ t' o  Q: y, E4 u" O
  8. #define GPIO_PIN_7                 ((uint16_t)0x0080)  /* Pin 7 selected */* i+ F% p0 ^4 e. W# H3 R( j4 l
  9. #define GPIO_PIN_8                 ((uint16_t)0x0100)  /* Pin 8 selected */$ a. w: B! i7 `) b' Q4 |% n( n0 m
  10. #define GPIO_PIN_9                 ((uint16_t)0x0200)  /* Pin 9 selected */# W; v3 I4 Q% @8 o% G
  11. #define GPIO_PIN_10                ((uint16_t)0x0400)  /* Pin 10 selected */3 d# K3 q# P) a5 k) m( L
  12. #define GPIO_PIN_11                ((uint16_t)0x0800)  /* Pin 11 selected */
    / C" G! r; H, u2 T9 a- B
  13. #define GPIO_PIN_12                ((uint16_t)0x1000)  /* Pin 12 selected */
    . S' B3 ~; T. N" ^0 \
  14. #define GPIO_PIN_13                ((uint16_t)0x2000)  /* Pin 13 selected */) Y5 u3 v4 W1 l, z$ D9 [( D4 U
  15. #define GPIO_PIN_14                ((uint16_t)0x4000)  /* Pin 14 selected */3 p  J$ }- v5 a. q0 g+ v5 l
  16. #define GPIO_PIN_15                ((uint16_t)0x8000)  /* Pin 15 selected */
    ; K! K* K, F6 s6 S4 r7 K$ o1 L
  17. #define GPIO_PIN_16                ((uint32_t)0x00010000)  /* Pin 16 selected */
    7 B& j8 {( f! F. f
  18. #define GPIO_PIN_17                ((uint32_t)0x00020000)  /* Pin 17 selected */% `6 H7 m9 I* W5 z
  19. #define GPIO_PIN_18                ((uint32_t)0x00040000)  /* Pin 18 selected */
    / `% I* a2 p9 w6 b
  20. #define GPIO_PIN_19                ((uint32_t)0x00080000)  /* Pin 19 selected */5 k- A7 p# W2 d# r; K' B5 q1 D
  21. #define GPIO_PIN_20                ((uint32_t)0x00100000)  /* Pin 20 selected */: N/ v& R7 x- p& ?9 u% a
  22. #define GPIO_PIN_21                ((uint32_t)0x00200000)  /* Pin 21 selected */  N) i" L) F/ q% _$ p: y
  23. #define GPIO_PIN_22                ((uint32_t)0x00400000)  /* Pin 22 selected */
    7 G  k7 c3 p/ F
  24. #define GPIO_PIN_23                ((uint32_t)0x00800000)  /* Pin 23 selected */
    . @) M* G! [# M( Z8 S0 x% Q6 S( F
  25. #define GPIO_PIN_24                ((uint32_t)0x01000000)  /* Pin 24 selected */
    + D7 y$ n1 [5 b1 S2 Y
  26. #define GPIO_PIN_25                ((uint32_t)0x02000000)  /* Pin 25 selected */
    9 I5 j# W; i1 k* F* R4 `
  27. #define GPIO_PIN_26                ((uint32_t)0x04000000)  /* Pin 26 selected */1 i% K' r4 |4 a3 N" J- \% F
  28. #define GPIO_PIN_27                ((uint32_t)0x08000000)  /* Pin 27 selected */
    . l6 |4 `* j9 u1 R+ W7 U% b. E
  29. #define GPIO_PIN_28                ((uint32_t)0x10000000)  /* Pin 28 selected */% q6 H! Q4 i2 R7 f7 \& U# d
  30. #define GPIO_PIN_29                ((uint32_t)0x20000000)  /* Pin 29 selected */  a2 F7 K9 n4 H* ~8 D& U
  31. #define GPIO_PIN_30                ((uint32_t)0x40000000)  /* Pin 30 selected */6 L, O' N  d/ |6 v( l
  32. #define GPIO_PIN_31                ((uint32_t)0x80000000)  /* Pin 31 selected */
    # e- P( i  S( N! Q6 _* \8 \
复制代码

: T& k3 j# G* s5 V1 S) z% g( x8 f9 Z2 N' w
  返回值,返回0表示低电平,返回1表示高电平。0 O% n* v% Y1 X; I
使用举例:3 h/ C) t+ V) C; u; T! U$ a4 D

% |  Q) H4 P/ M0 R比如获取扩展IO的GPIO_PIN_23高低电平状态,调用函数HC574_GetPin(GPIO_PIN_23)获取即可。
8 I, t' K$ n' j1 P; s, V1 ]5 M/ s8 r# ^# s
48.5 FMC扩展IO驱动移植和使用
4 P( n2 V; f# w5 O* k
扩展IO的移植比较方便:
5 ~0 u& `( ]0 g3 J0 g# F) x' x# [8 q
  第1步:复制bsp_fmc_io.c和bsp_fmc_io.h到自己的工程目录,并添加到工程里面。
( P$ h# W0 \5 b  第2步:这几个驱动文件主要用到HAL库的GPIO和FMC驱动文件,简单省事些可以添加所有HAL库.C源文件进来。4 y. G7 k. W; ~  c! h1 e. C8 G
  第3步,应用方法看本章节配套例子即可,另外就是根据自己的需要做配置修改。4 Z( J' z8 E- Z; u# v5 G
8 L1 s8 d) p- c! _& K, @
48.6 实验例程设计框架7 G9 d: [3 s) j9 X8 K" K
通过程序设计框架,让大家先对配套例程有一个全面的认识,然后再理解细节,本次实验例程的设计框架如下:) w( U+ n( Y) N) l& O, a
: W! d/ P* f  E  K3 K3 ^
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png

" S( B( p8 C  A8 m  y
. I: f' }) z1 @3 J" Y7 I  第1阶段,上电启动阶段:9 X* F" l! t% n6 u3 C
& e# @+ g5 ^3 V1 N/ P: g6 [8 N
这部分在第14章进行了详细说明。& K! P: a* R/ n( s( c
  第2阶段,进入main函数:
9 j0 T) k0 s+ L0 ^
" _5 y" p# J3 O' O0 T  r- G5 @ 第1步,硬件初始化,主要是MPU,Cache,HAL库,系统时钟,滴答定时器,LED和串口。* z7 t- E' _0 y0 B" F
第2步,按键应用程序设计部分。定时器中断服务程序里面实现翻转FMC扩展引脚20和23。, i1 c1 y; \/ @! n9 h/ s

( U: c5 j# Z! Y1 s# c* y48.7 实验例程说明(MDK)
* B0 E- ?4 S2 W) W
配套例子:
/ L# g% k1 I0 {" `1 wV7-027-FMC总线扩展32路高速IO
+ `# N; |. \; ]+ i2 z* L% m! u. i: q( A
实验目的:
4 B: ^, {! \3 q& O学习FMC总线扩展32路高速IO。
& q' U" N) K) x! ^  a3 P1 F" r$ e. \% G9 y+ {
实验内容:% h/ ?3 q4 L) r$ T7 y
系统上电后驱动了1个软件定时器,每100ms翻转一次LED2。
2 ?1 P5 D3 w) E9 |6 K5 q( _) k+ s启动1个TIM6周期性中断,频率10KHz,在中断服务程序里面翻转FMC扩展引脚20和23。7 Y- U2 j" K; @

  Y$ l1 m% m7 i% ]$ W实验操作:: z5 n7 m4 @0 x* U2 O; d
K1按键按下,开启TIM6的周期性中断。6 G2 e/ q* t3 F7 d# R; w
K2按键按下,关闭TIM6的周期性中断。8 K+ n' `* G$ C8 @* c" q
, z& \$ Q0 e! z3 V4 E
FMC扩展引脚20和23的位置:$ A( T) `# v1 @. p" p2 x* j0 H
3 H) K; Y% d6 P: G+ V  d
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png
+ h$ r9 [7 g1 w

' k- a! K7 u5 Z上电后串口打印的信息:* c, k" E% o, J" e6 R+ V
- q$ B) ?- K! e! W2 ^: U8 B
波特率 115200,数据位 8,奇偶校验位无,停止位 1
! C4 `$ B0 |, n
* J' u  n1 A2 x0 q) T) z
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png
9 b0 {; k8 M' @5 [3 _$ [

$ s( s# y: u% T1 S( Q. b# ^程序设计:  E; B# r+ A3 \0 d
; o9 J6 l5 e( T( d
  系统栈大小分配:8 Q1 i4 A+ O0 y

" ?3 l: X. X0 K! z  o# l' ~% X
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png
4 K" {. E* U. K0 K
  L* f' J( m. K7 l4 r
  RAM空间用的DTCM:) D' y* H4 c2 ~7 ~7 e& t- _
0 n  ]# k: t6 L4 Y; w
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png

+ [8 m0 S4 {% S- G+ [- B2 f! `& x1 _/ w- l& l; q! h
  硬件外设初始化2 B" ^: B5 i4 J! ^( k) l

  B7 K, b/ u9 t, k8 X9 z硬件外设的初始化是在 bsp.c 文件实现:
0 V3 z. s8 L+ N/ V) K) x0 g+ Z- m' |7 q, O4 ]
  1. /*& y+ `( T8 g2 `# t6 P: c
  2. *********************************************************************************************************
    ) U- b4 L- Y0 B! _
  3. *    函 数 名: bsp_Init
    ' T$ I' F# ?: _1 C# {" r1 k
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
    5 |% c1 E% b5 T4 x  z/ O, C. j. u! \
  5. *    形    参:无- E: M8 n- j1 n% Q( Z. R
  6. *    返 回 值: 无
    5 Z& a# s5 h: g/ L. k2 s8 U( p
  7. *********************************************************************************************************
    0 [: b5 Y2 ]8 \; k3 Q; e
  8. */, r  Q" W+ H, H$ n! U- F
  9. void bsp_Init(void)
    3 Y* a! o& w' T* |* }
  10. {
    $ e  ~) @( r% T* O- p; l
  11.     /* 配置MPU */% E$ \6 h+ j  E, P/ z
  12.     MPU_Config();0 |/ l- \& s! ^& T/ t4 X
  13. 1 u8 q4 l, w" e+ t5 x8 B
  14.     /* 使能L1 Cache */: |3 b+ c0 x8 k6 N* y. d  Y
  15.     CPU_CACHE_Enable();
    : A- k0 F1 H& p% ]/ T
  16. . \* u; y$ r8 C& w( t
  17.     /*
    ! ^1 k1 n8 }4 ~6 N+ u
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:; m  h# Q3 F: h3 Y' j8 T
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。% y0 L: c+ Q0 ~* P/ K8 h2 a
  20.        - 设置NVIV优先级分组为4。# }! ]( i; U( i- n. Y! X
  21.      */
    * D9 t) M8 K0 o' M) R9 v0 _
  22.     HAL_Init();$ k# h6 _3 P' y! I
  23. ; Q* T- t2 J7 ]) T: A$ l
  24.     /*
    2 `& f! ]0 z+ s
  25.        配置系统时钟到400MHz8 }% c+ _  ^( Z% S# p
  26.        - 切换使用HSE。
    ; e0 {8 y& t/ W, V: B7 E' Z
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    ( k2 B8 k/ q4 W% b, F. Y# V9 z9 J
  28.     */
    , K) d5 P6 `# E& F6 v2 j& L) ?$ W( `- t
  29.     SystemClock_Config();3 G' e; \: f$ f4 o% _& z4 z6 I
  30. , r( H  K* d* Y5 q& |7 n* k
  31.     /* 0 N% w- j1 G. n7 I6 a
  32.        Event Recorder:: k# U2 |8 @6 r) S; L7 v1 K5 H8 L+ n4 ]
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。- ?% G' c2 D$ t. m3 S
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章
    2 A1 ]% X! j0 u% f
  35.     */    ; I2 l- v1 j2 c1 c' Y+ \
  36. #if Enable_EventRecorder == 1  ; Q  V* x( \8 |& \5 |2 n0 a
  37.     /* 初始化EventRecorder并开启 */! A0 Y6 X) s& ~  Q9 F5 A1 x; J
  38.     EventRecorderInitialize(EventRecordAll, 1U);  h: L" j  c: X; m! d$ D( w- j6 }! x. r3 w
  39.     EventRecorderStart();: J% H1 C3 R/ U: @
  40. #endif
    ; |# u& |8 ~, [
  41. ' D( X( Z# V7 W9 y
  42.     bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */$ t! \8 E/ R* W) g9 j
  43.     bsp_InitTimer();      /* 初始化滴答定时器 */0 _7 \# {8 [- \% }( {* ]) {
  44.     bsp_InitUart();    /* 初始化串口 */0 v* G5 Q$ r9 Q6 F/ p$ R/ z
  45.     bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */   
    9 o- b( g5 F* j6 L) _( o7 a
  46.     bsp_InitLed();        /* 初始化LED */    * R; G+ s9 ]3 m' o& A
  47. }
    6 }; V, [7 x5 i3 W( O

  48. + o& E. B- N, T& T1 k; G
复制代码

4 T3 c) d. U, z, W) ?0 M  MPU配置和Cache配置:
1 i. h4 i. |- z" e7 o
. U7 h4 l6 C0 q/ k1 N4 ~数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区。
& _' p8 b5 V! A6 s: l9 K) x# u; D1 C4 K; a. ?+ \! b- j2 `8 T) A
  1. /*7 Q4 w$ \7 J5 q2 F3 k7 G
  2. *********************************************************************************************************5 ]& U6 h8 k: y' Y9 @; ]3 G
  3. *    函 数 名: MPU_Config7 Y: n+ E/ a( H+ K
  4. *    功能说明: 配置MPU
    ( U! g( ], o- G" M
  5. *    形    参: 无
    + F8 y- {4 r# U3 @9 [" [- ?9 ~  T
  6. *    返 回 值: 无8 u- S4 L. |3 \: j) k. u2 m
  7. *********************************************************************************************************
    / V* O' O7 P% ?3 ^% Z
  8. */
    / O0 J. |" q1 k& l/ G7 }
  9. static void MPU_Config( void )6 j% a& `% z. c( o
  10. {3 T2 F6 k+ L8 z8 T
  11.     MPU_Region_InitTypeDef MPU_InitStruct;% K* y1 F) ~6 }3 u: d

  12. : u. V; o. H( k4 u" {+ v6 W
  13.     /* 禁止 MPU */% ~( k# y9 L: [7 y+ |
  14.     HAL_MPU_Disable();
    7 [  [. l+ k. T' n0 J
  15. / O* g: n( m* ^8 j1 O; x
  16.     /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
    ! \/ V2 A' J' }0 B0 q
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;  Z( R3 f! l/ L' ?2 f
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;  @5 B$ ~* |( `
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;) _7 J+ {, n2 ]5 p/ Y$ P. P% Y
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;; l! p/ F. _% }( q% M: Y3 h6 l# h" c
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    * y) A) i$ X3 m
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    * O  A" d4 c% k) S: K6 q
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;$ i6 f! b# j9 ?' E. w1 f
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;: o9 n+ Z) J' B8 G3 V! Z* @) ^% n# V% W) ?
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;) i  g( y* A# x! W; t
  26.     MPU_InitStruct.SubRegionDisable = 0x00;
    $ }( d9 ]# R0 r3 C& j' c
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;) q( ]: P  o7 l+ d

  28. + u2 Y, L: q; b. G5 w9 s9 R; Q* c
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);* l3 W: O3 x- g- Q0 X4 o* W
  30. - ^8 j! m# O& D- A) b
  31. , x! u, n( d' z; W5 f3 t
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */+ p6 Q6 Z, f# z: M
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    : u2 g( H  m5 @+ ^, R
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;6 ~7 _% \' M0 T7 y6 n0 ?
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;   
    ! O# E7 |$ p$ @- p  A% ~! h( l
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    + U; P! Z" c5 }  A
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    % Z/ i5 \& ^. `2 z3 Q% B
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;   
    ' A) J. b5 [1 e. Q: }% U( \0 K8 t
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    $ w5 A! @5 s3 f: m3 Y8 X
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;: t" P3 Z( T4 I- K$ @% B' o  |( Y
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    % \+ Q; u' [6 C4 P- U3 y1 {
  42.     MPU_InitStruct.SubRegionDisable = 0x00;
    8 E: n: Y6 z+ Z' _$ |7 A
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
      Q3 V( U1 S; `% `+ \, T! C7 Z5 H

  44. 2 a- b- D4 Q- g3 L* I
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);) `- [5 c1 G  e- V' w) H) p1 j
  46. " i, n8 a) N+ d( L; H
  47.     /*使能 MPU */
    1 h( p2 ?/ v; `0 i( d) d
  48.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
    $ M; L) S- o* l7 a% j, ~) |$ ~
  49. }6 O( {+ U! z$ g! N! P" E( p, [

  50. ' h- w  [) \5 O  t* n! S
  51. /*
    8 D( E2 G  f7 ~0 x! j
  52. *********************************************************************************************************+ F: k' e: z7 Y
  53. *    函 数 名: CPU_CACHE_Enable
    ' i8 j' f! Y  W; ~; s
  54. *    功能说明: 使能L1 Cache  T! C& v) P  v; m
  55. *    形    参: 无* M$ h# P5 y4 Q
  56. *    返 回 值: 无- |" N7 t* P6 v$ p5 p
  57. *********************************************************************************************************. L8 l5 I& N( e7 S9 ~6 |1 e
  58. */$ t, O$ A+ t3 Z7 _
  59. static void CPU_CACHE_Enable(void)
    % U! F5 |2 X" `' V0 T  U3 X
  60. {/ m1 O1 f0 S9 F) f
  61.     /* 使能 I-Cache */  @" {' p& J$ P
  62.     SCB_EnableICache();; F8 {( P% m; |9 z

  63. ) _2 q" o7 D7 _" E
  64.     /* 使能 D-Cache */
    0 ?0 k0 [/ h3 [% y. t! {
  65.     SCB_EnableDCache();3 F% k3 {$ W1 Z- C  D6 u
  66. }
复制代码
& s, x6 o6 q" C; x9 g
  主功能:  r: K- @& H9 p) q$ {: k
4 w( X: l! W4 `0 e8 f0 `5 d
主程序实现如下操作:
* A# |& `% M, {; P% F
8 T8 L0 M; c. m  r+ s' S  K1按键按下,开启TIM6的周期性中断。3 ^4 t% E" C4 d! u' q: }! g1 m4 w
K2按键按下,关闭TIM6的周期性中断。- F: |0 e6 ~3 t5 t# ^. W
  1. /*- Y% X4 t! c3 r6 [" T8 K
  2. *********************************************************************************************************% D7 e: o6 H. _. y1 G4 ^
  3. *    函 数 名: main2 H  j. |* ?& O! `6 Q
  4. *    功能说明: c程序入口' C' F' D' {9 ]& u4 i
  5. *    形    参: 无- h8 \1 u2 b; G" p" T# [1 o/ g7 E$ [
  6. *    返 回 值: 错误代码(无需处理)
    . @* l: H' m! g  L: A$ }0 F
  7. *********************************************************************************************************
    + ^( j8 u# B6 i! V0 |0 ^% M, d
  8. */
    5 }8 g! Z% h% s# l
  9. int main(void)" t3 @' Z' i9 a3 ~5 V
  10. {2 G+ ~- }* ?+ F" k
  11.     uint8_t ucKeyCode;        /* 按键代码 */
    ( n& u& ?2 H: P3 D/ S- e
  12. - Q+ }+ G3 w. i% ^2 u

  13. $ X6 ^  b& [  ~5 Z7 p  I' T
  14.     bsp_Init();        /* 硬件初始化 */6 b! F7 c9 ^# ^2 Z

  15. 1 L  m5 `# a6 ~2 Z
  16.     PrintfLogo();    /* 打印例程名称和版本等信息 */
    ) T/ o* T5 \0 ]: c9 b/ T( U' c
  17.     PrintfHelp();    /* 打印操作提示 */) j. c! b: A' N6 _7 z$ P+ ]1 E$ i
  18. 8 H5 i' d2 a7 G% Z* G* S& U$ E
  19.     bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */) w3 p' Y' d  }, W. }% a- I$ W

  20. ! q! o& X: Q- f% B5 d) W
  21.     bsp_SetTIMforInt(TIM6, 10000, 2, 0);    /* 设置为10KHz频率定时器中断*/   
    % f& J% N+ j& G2 I. c6 V
  22. & |; u9 H* j5 V6 p9 w
  23.     /* 进入主程序循环体 */
    . k! W4 U  [9 B7 h/ P( q
  24.     while (1)3 y( J$ a: t/ Q, d
  25.     {
    7 f% O& X) \+ H& F4 @
  26.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
    3 F3 x: ?; }% f7 k8 [
  27. 9 l' g9 q) T$ ^# m" h
  28.         /* 判断定时器超时时间 */2 s% U" [5 U4 P' V3 c' Q: U6 s! R" s
  29.         if (bsp_CheckTimer(0))   
    ( {9 u5 |' W1 @- B; M
  30.         {  y" x% s" D$ T" [* R( J
  31.             /* 每隔100ms 进来一次 */  # z' x& m" @' s- j# g; \- l
  32.             bsp_LedToggle(2);
    + J- T! J4 ?1 e  e; ?# {* L
  33.         }( z2 y# T( l  J
  34.   H% Z9 k7 c6 T' v+ R2 K' l, r; t
  35.         /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
    , t4 C* a- D, {( b6 C9 c
  36.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
    ( R6 o+ M& A$ W: b
  37.         if (ucKeyCode != KEY_NONE)
    0 y8 N) }9 G$ i/ y+ y, }3 q; @7 ~
  38.         {9 {8 W0 s  J# a5 v9 ]
  39.             switch (ucKeyCode)
    6 C+ ^, ~. j) t0 }$ E
  40.             {& K; p: r8 }: }8 y
  41.                 case KEY_DOWN_K1:            /* K1键按下,开启TIM6的周期性中断*/
    ) a3 {/ @4 S! a$ c1 ?
  42.                     TIM6->DIER |= TIM_IT_UPDATE;
    8 F4 _1 A1 M' F4 U+ o- y
  43.                     break;
    5 H6 n9 b9 R( w- y! Y

  44. $ I! d" c) H1 M; W3 U
  45.                 case KEY_DOWN_K2:            /* K2键按下,关闭TIM6的周期性中断*/
    / p$ K4 c9 i3 t; |+ q! k& P, @5 s% v
  46.                     TIM6->DIER &= ~TIM_IT_UPDATE;5 ~  V! J/ H9 Z- M' E
  47.                     break;
    9 [5 b* d5 p- u  S8 q
  48. & n3 e3 @: b1 E9 ]! U
  49.                 default:0 x: `/ R4 c6 o: J8 r. `7 \" D$ X
  50.                     /* 其它的键值不处理 */; N+ m, v: h3 B
  51.                     break;4 g0 [, k8 D" d$ h7 Q
  52.             }
    , \$ _( d  t& V' Z* A4 F$ {
  53.         }9 x4 U: N/ E  [
  54.     }
    0 z$ }9 o+ a' l; h) b
  55. }
复制代码

2 M2 i  Y' h3 O定时器6中断服务程序:; R6 u3 v. J8 W! o% A
8 V8 t% J- d7 _; d5 `/ M
  1. /*  L0 l' F! E0 Z+ }' a
  2. *********************************************************************************************************- p# B2 b% ?1 z- T; l
  3. *    函 数 名: TIM6_DAC_IRQHandler* {, t/ F7 i0 Y4 e
  4. *    功能说明: TIM6定时中断服务程序! S) e$ @* a# ^
  5. *    返 回 值: 无) K) N; \1 N  w' g: y6 q
  6. *********************************************************************************************************
    7 [; e! n: F+ `+ w* b
  7. */
    7 x4 z$ y2 J8 d$ Z+ w
  8. void TIM6_DAC_IRQHandler(void)- T) g9 c& k. M$ F
  9. {
    : e4 X" R: y: |& K% |# R
  10.     if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)* P. I; a+ u% K/ j
  11.     {
    - o6 G/ ^. e  U% Q. _
  12.         /* 清除更新标志 */7 _0 ^- b% @: N
  13.         TIM6->SR = ~ TIM_FLAG_UPDATE;
    " W8 c" G: U: N2 k
  14. 2 o2 D( i0 D1 I. g
  15.         /* 翻转FMC扩展引脚20和23脚 */& N$ P. t2 ^  V" A1 \
  16.         HC574_TogglePin(GPIO_PIN_23);
    ' f2 z% O8 ^* N9 ]+ h
  17.         HC574_TogglePin(GPIO_PIN_20);* [0 T7 Z/ R' v
  18.     }+ L6 `* T' w. ^
  19. }
复制代码
) z9 Q$ ?; H! o2 T, R
48.8 实验例程说明(IAR)- d" E$ `& f6 F; ]' @0 K
配套例子:
4 e( k5 [- B1 c. U. B+ ~7 m* ~V7-027-FMC总线扩展32路高速IO$ B5 s  s  g8 N4 |& I
; S' W9 k0 y  s5 s: G; C, }
实验目的:
" v4 R+ D% a& R: f5 D8 ?学习FMC总线扩展32路高速IO。2 a9 Z# i) x3 i( a! `
& X( t; {; S0 }
实验内容:
* E9 \5 W+ F! P; o! `; R系统上电后驱动了1个软件定时器,每100ms翻转一次LED2。6 D% X) m1 {6 o4 G- r7 o
启动1个TIM6周期性中断,频率10KHz,在中断服务程序里面翻转FMC扩展引脚20和23。; z& O$ e, v& ~) v1 }/ A% S
, _0 c) v, F9 n
实验操作:1 p" H- C% a8 h+ W" C' ^' b# s; P
K1按键按下,开启TIM6的周期性中断。
6 T- t% _" V+ N- `3 j& bK2按键按下,关闭TIM6的周期性中断。% d  F* k" T4 y3 q  h6 N

+ j7 _7 {  q9 v; x2 c4 dFMC扩展引脚20和23的位置:, O$ ^/ Z. f  w: f% h

% S1 [2 ~# O$ j' _: |) V9 J
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png
" @5 l' r3 d, Z
% |. ]# p/ F  ^' T
上电后串口打印的信息:
3 ?! ~" {2 @& B3 l) l7 j. I# M4 }" y3 a7 {' v+ l! Y3 u
波特率 115200,数据位 8,奇偶校验位无,停止位 1
& q: B% M) s; e/ n1 ]& j4 u$ n
/ k' @5 `' |! K
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png

- Y+ l0 A8 y0 ]3 k  g3 ~/ z# b- r" [; ~: E" A" a
程序设计:- Z$ J! P. R6 [5 o8 j; s3 n

: S6 r/ u0 |% z" K% O; v1 y  系统栈大小分配:" r1 S4 Y/ W4 @

+ D' B) X1 @! N2 t7 w( P" f+ R
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png

+ H- }# y2 Y8 [: m1 C
3 B( ^3 g/ ]- Q! U  RAM空间用的DTCM:
) e# V4 I& S; p
$ [# U3 ?1 f4 ]2 f
aHR0cHM6Ly9pbWcyMDE4LmNuYmxvZ3MuY29tL2NvbW1vbi8xMzc5MTA3LzIwMjAwMi8xMzc5MTA3LTIw.png
0 X- P- v- j: g! u' A, X& F
# ]$ v, A0 m% [
  硬件外设初始化
# I- b. ]" M$ h# c, h+ g
  }5 Y- I5 z5 A( U硬件外设的初始化是在 bsp.c 文件实现:% T4 v% f7 M7 ~8 v" q, G+ Y( n7 h
7 _. k/ H1 y6 B$ Z
  1. /*
    * J) u) Z( i$ G+ k" \7 O% x' e
  2. *********************************************************************************************************
    5 V3 h0 r+ M3 ^3 @! ?# f' e' z% _
  3. *    函 数 名: bsp_Init  b. F. _- ^/ o. K
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
    & J$ w$ l0 z8 W8 r- A/ ]- e, o/ K2 H$ O
  5. *    形    参:无
    ( D; G) w+ i* k- ^) J
  6. *    返 回 值: 无
    % _1 e% G# |3 j4 m1 Y7 ]3 `' q0 H
  7. *********************************************************************************************************
    * V" ^  W' C6 @
  8. */
    & Y7 j9 F8 x3 H$ O
  9. void bsp_Init(void)
    ' Y7 q( H( I! c: M
  10. {- R* H% s3 T9 z5 P% N7 j8 L- ~
  11.     /* 配置MPU */
    , U- ?* G. ~/ c* t6 \
  12.     MPU_Config();
    5 \0 ]) N' X5 {$ `. `- {4 }) p

  13. 1 a6 Q; D, M/ D* w" v7 B
  14.     /* 使能L1 Cache */8 B* X# D5 O' a( h* f- z
  15.     CPU_CACHE_Enable();6 k1 f* C# F4 z/ O' A, w- x

  16. ) V9 s' ]  u5 W. J. ~4 Y/ m
  17.     /* % H2 B* a( i' \8 A, B3 L
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:9 f5 b7 S9 T: p- b: L# D$ G/ [- b
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。7 T5 N4 t. r$ F
  20.        - 设置NVIV优先级分组为4。
    3 R' g1 L: Q3 _7 U# @! Q5 V
  21.      */
    ! O6 `- }! K, {* E$ {
  22.     HAL_Init();9 t/ b; C- T+ q  Q) E
  23. % ~( C8 U2 f. W8 |  G! u
  24.     /*
    / g: v" C# o5 X4 f  H
  25.        配置系统时钟到400MHz
    " `2 f. g0 {6 d0 v% }3 i
  26.        - 切换使用HSE。
    , J4 o- R% z$ c- `+ S5 c
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    : a& [( P/ B- A8 P8 o8 F
  28.     */8 {$ L2 F5 s! R0 D& f6 S4 b
  29.     SystemClock_Config();
    8 o( `3 q( d& U" Z) C
  30. % t9 T% V2 k0 v' k" b
  31.     /*
    % A9 e0 {& U: j8 c
  32.        Event Recorder:
    " F& b5 ?0 B$ }& z+ \5 @' p; j
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
    ; s/ k& D0 ^1 W
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章9 {0 {- ?. |) t% E+ X( x8 Z) T
  35.     */   
    ) s: z0 h2 O) T, e( y; Y# t* l8 D1 r
  36. #if Enable_EventRecorder == 1  
    ' l# c9 c$ b( l( Q! a  Y# |& X1 \* V
  37.     /* 初始化EventRecorder并开启 */
    0 Q* L. X& v* B+ w* l/ j8 F
  38.     EventRecorderInitialize(EventRecordAll, 1U);( p8 l( ?7 n* u1 d. k
  39.     EventRecorderStart();
    % t. Y5 K: {3 r2 p) {
  40. #endif/ W" ~3 L$ X/ ~% i
  41. ( _* ~, e9 i' t7 v' Y( Y
  42.     bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
    / u8 a! ?& V" |3 O2 \3 {( B
  43.     bsp_InitTimer();      /* 初始化滴答定时器 */
    ) P) S$ ]% s5 g) x3 u7 Z
  44.     bsp_InitUart();    /* 初始化串口 */
    + I8 T7 J+ w2 y$ [& s7 d; z) s, D& `
  45.     bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */    / F  p9 k+ u) J6 H7 t
  46.     bsp_InitLed();        /* 初始化LED */   
    5 ^# L3 f7 x! X. P
  47. }
    & S; D. [, ], k# @7 ~3 a/ \
复制代码
8 S3 }4 v0 g2 ^5 w- `+ q* o

. \4 J& [% E: ?. V, X; \  MPU配置和Cache配置:3 X; @. S& Z8 E. {/ m# t* E% C" y" _( Q$ w$ u
" S/ |4 |1 O0 Y
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区。: E4 @4 j$ L$ {
/ t" ^6 h$ w" c8 @6 t
  1. /*
    # n% g/ w2 R' d6 U9 L/ z: Y' L
  2. *********************************************************************************************************
    , M+ V" @+ K: K8 r. l
  3. *    函 数 名: MPU_Config
    5 O% j, n! E( ?9 z
  4. *    功能说明: 配置MPU
    ) l/ o9 Y9 i; \
  5. *    形    参: 无
    9 }- a* z6 D6 _6 y
  6. *    返 回 值: 无. p4 _8 a- ~  R
  7. *********************************************************************************************************7 m, K" E+ H+ ~/ A" A3 H
  8. */
    + X0 n: M! d2 a3 Z; E
  9. static void MPU_Config( void )
    / u7 @4 }- _, c% n+ O  D8 Q
  10. {
    3 ]; A7 p3 c8 H3 i8 Q! e( y
  11.     MPU_Region_InitTypeDef MPU_InitStruct;
    " _6 Q0 u/ E  L# a& r6 A
  12. , f5 m  z# a# L+ ~7 r
  13.     /* 禁止 MPU */
      N7 V4 y6 q1 O5 `
  14.     HAL_MPU_Disable();
    ( U2 `) T. {9 s2 w9 o

  15. : w5 T: B$ L! P- M
  16.     /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */) i( k1 }8 E# X& W8 k5 E
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    8 V2 \- D! f- [9 @, M
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;
    & L" U% }3 N3 W6 E; I
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;8 E( B& e6 e% Y
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;. A  Y( E4 C) {# J$ ]& J
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;% n# ~0 }) M, v5 m
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    : L' F/ T, Z/ m4 {: d
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    ; [7 ~1 m. h" ]5 t( n* C
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;3 `) c3 {: I+ V7 @1 S5 U7 \& K. ?
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    . C+ o& q9 Z6 y  ^
  26.     MPU_InitStruct.SubRegionDisable = 0x00;
    6 g/ K$ i9 g, I4 Z# t
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;& A! p. p) F, r8 l

  28. - `$ Q/ b4 p( Y/ ?$ E9 s/ C
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);( v1 l! d3 E9 m

  30. 5 @7 X+ b; T( x# g% m
  31. 9 Z& T7 {' J4 C8 a
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
    ; m% I' T' C5 A( P5 Q: |
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;6 ?# E% i' ~1 I4 n! z, ^1 E
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;
    ) O: u" B5 C; w6 @9 r: d
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;   
    - ?) E6 O: J3 \3 `  a+ v
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    / [; C; z3 W$ n: ^  b
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    1 e1 H6 C7 i2 C8 s
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    ) ]6 l) V$ b2 t* z! ]- ?  r' @2 {8 M5 r
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    8 b9 ~" N0 A0 I; u
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;& K8 e- W5 z, }
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;' f$ j1 D7 y! ^8 K6 C; s
  42.     MPU_InitStruct.SubRegionDisable = 0x00;5 B# v7 E! i$ j# A. b) g$ c2 Y8 T0 z
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;/ A6 V" z7 [. L
  44. 0 E$ ^  ]' o8 o, V
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    4 u: i1 A4 G% }: [, c  Y& j. I

  46. & ~; G0 n+ z# Q2 @! K
  47.     /*使能 MPU */
    4 J. x& f6 g- m6 v
  48.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);7 k; C6 |. `$ \( f
  49. }
    1 S. i8 Z1 T/ _6 o: U) ~: d! H& }
  50. 7 E# o8 i; M" L4 W
  51. /*
    ; o- _# l8 q' I8 F
  52. *********************************************************************************************************% m. `' G1 m, ~
  53. *    函 数 名: CPU_CACHE_Enable
    / X0 D0 M) t- j7 q) p# @* R: C" Z7 }
  54. *    功能说明: 使能L1 Cache
    ' S$ v; X* Z" j
  55. *    形    参: 无+ `! |% L5 i5 R  ]8 I; A% d% l
  56. *    返 回 值: 无
    * m4 b5 o( n2 J, ~' n9 G* s4 o+ W, n
  57. *********************************************************************************************************1 P, N9 r* {3 y9 E( J' t* H& }  y
  58. */6 T9 S' L* ?: Y: z
  59. static void CPU_CACHE_Enable(void)% G4 b* s$ o( f4 \/ C
  60. {
    ' b0 p7 v( s$ E8 U* Q
  61.     /* 使能 I-Cache */
    ' i8 Y  P* v- L
  62.     SCB_EnableICache();
    , b, E( ^* I  R2 u" Y, J+ a- A
  63. & n2 M4 Q! f% h, a  I
  64.     /* 使能 D-Cache */
    . `6 J# x# ]4 H
  65.     SCB_EnableDCache();* i- {- h8 w4 Q- f
  66. }
复制代码
3 H* ^4 x2 P% G* S7 ]

& d. W! [& y# b& z3 H* C* C  w6 N$ x/ t: I- t& i+ s& Y7 Q
主功能:
/ L: t1 R7 D# l/ _- i) m7 e9 L8 K" V4 e3 j! ]
主程序实现如下操作:
! r$ F& W. _/ X1 c9 {$ {
% Q! ~" y& O' ~2 ~3 d K1按键按下,开启TIM6的周期性中断。
: k; [% {! T7 |" f K2按键按下,关闭TIM6的周期性中断。* g# y1 ?; ^  Z2 ~* g) ~6 Z. S$ e
  1. /*
    2 h/ g% m  O( F7 t: |* F# X' M/ H
  2. *********************************************************************************************************
    . N# x6 U1 ?' H; B: ~$ G, a3 Y
  3. *    函 数 名: main
    1 @/ o2 U, z# [) \1 {, x, X
  4. *    功能说明: c程序入口# f) V) k2 Y/ q9 l# I+ E
  5. *    形    参: 无
    % P: \9 m  x0 Y$ ?8 x4 v* u
  6. *    返 回 值: 错误代码(无需处理)
    ) m3 _  K) P7 h) i" F. M' C& h
  7. *********************************************************************************************************
    / |: q" G0 C, _( V8 V4 L) w  v# G
  8. */7 @  N) d$ M4 K6 V. n
  9. int main(void)
      l0 O( p  b* C4 E
  10. {
    ) B: q5 g! h: a( N+ C" d
  11.     uint8_t ucKeyCode;        /* 按键代码 */" V$ E# u* j! C* G' U3 T

  12. 8 o. X& ~) K3 j+ l5 g

  13. / ~& H. d( o& D
  14.     bsp_Init();        /* 硬件初始化 */
    4 }7 k& z, k# o" I! i" b
  15. 9 V8 J& p# T% p$ T8 R& E$ B
  16.     PrintfLogo();    /* 打印例程名称和版本等信息 */
    1 |& {4 ~1 ~- X% B/ u" E0 H+ E
  17.     PrintfHelp();    /* 打印操作提示 */
    3 h& f$ m; H# h+ T/ ]& M" K

  18. ! X, D1 {1 C5 V% K+ m
  19.     bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */: ~$ Z8 P8 \- w* ?3 Y: U' m

  20. / v% l# o$ m* j- j, [
  21.     bsp_SetTIMforInt(TIM6, 10000, 2, 0);    /* 设置为10KHz频率定时器中断*/   
      c  P$ b" f" k" l! K7 s+ j( C' y
  22. ! C$ C9 e- \4 g  K" X8 [7 f8 l/ u0 T
  23.     /* 进入主程序循环体 */
    : v3 N6 F1 i) O/ s. n
  24.     while (1)
    2 K& N. s$ f) w2 x5 S
  25.     {
    : `# M8 Z( _3 s1 }" k. H
  26.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
    : j9 X6 E. M9 P

  27. 5 Z% s% {6 Q% K2 F1 n
  28.         /* 判断定时器超时时间 */% A, F; s) M+ ?  I* f
  29.         if (bsp_CheckTimer(0))   
    ' U+ E& F$ G/ X" Y8 q% Z
  30.         {; G0 f: P* s3 i
  31.             /* 每隔100ms 进来一次 */  6 O3 R$ ]* y1 S8 K; q# R
  32.             bsp_LedToggle(2);- S3 w7 W- s# e$ R
  33.         }3 h5 @6 }: m5 Q" ^0 Z
  34. 7 ?* P/ |, {! R' T! ?
  35.         /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
    ! C! ?$ g2 C+ F
  36.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */2 g' i' [/ f6 @* B7 e3 g
  37.         if (ucKeyCode != KEY_NONE)
    1 P: j4 M7 F3 ?. Y0 _9 Y' k
  38.         {4 b$ b$ w4 H: g# ?% m$ h
  39.             switch (ucKeyCode)9 D9 P& j; f- ]! I8 x* R& n/ D( H
  40.             {
    3 k2 v& S/ ~! u# ?! G
  41.                 case KEY_DOWN_K1:            /* K1键按下,开启TIM6的周期性中断*/
    * y0 b; W! G  C7 u
  42.                     TIM6->DIER |= TIM_IT_UPDATE;
    % c  o( |% M0 c
  43.                     break;. v" s0 b0 x0 w/ S. V6 C, U/ w
  44. 9 \9 Y, C4 m: b" {
  45.                 case KEY_DOWN_K2:            /* K2键按下,关闭TIM6的周期性中断*/# U3 H5 {" }! M, d! g
  46.                     TIM6->DIER &= ~TIM_IT_UPDATE;2 v9 o2 \# a( I( F( G8 @
  47.                     break;
    * r8 R& e" F. T+ [

  48. 6 `$ d( b( o3 x0 {3 j: i. n, |
  49.                 default:- h" f+ ?- `8 F& \0 l0 p, T7 p4 ?
  50.                     /* 其它的键值不处理 */5 w3 c: p1 X8 R1 x2 @- [2 B
  51.                     break;6 ]0 X5 Z7 E. X8 y9 a
  52.             }
    3 p9 R( Q9 U1 U) c, i
  53.         }
    ) i8 }" I) ~# O
  54.     }6 a7 `. g, \/ F1 p" i2 _# A
  55. }
复制代码
& {- h* f8 p+ p8 b
定时器6中断服务程序:& c1 \! v! W/ j! Y# A  P3 S8 M8 Y

+ ]9 \& e% P% p# \" U0 t
  1. /*
    4 {  h" W: P  u$ [1 O
  2. *********************************************************************************************************. ~2 [1 _5 f$ x- S- f- {$ L$ [
  3. *    函 数 名: TIM6_DAC_IRQHandler' U1 ?: v& U  g& X9 r) S8 U
  4. *    功能说明: TIM6定时中断服务程序
    5 g, B3 l, h  t, b+ ]
  5. *    返 回 值: 无
    9 V0 Q& @9 T: d3 W  E
  6. *********************************************************************************************************
      Q3 Z6 x5 ]* L
  7. */
    0 Z: p& ^0 K. p8 J
  8. void TIM6_DAC_IRQHandler(void)
    : i) \0 x" w: g7 L2 R; A
  9. {  Y8 f! s. f$ \
  10.     if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)
    1 h& G. R; w9 \  W2 l
  11.     {
    ; G- m- l+ v! |' t" ]/ @
  12.         /* 清除更新标志 */
    6 w6 Z: Q1 k* }6 x
  13.         TIM6->SR = ~ TIM_FLAG_UPDATE;
    * m1 w( [# d! P: ]: j  d) q
  14. + L" K8 ^- `+ r& d8 V
  15.         /* 翻转FMC扩展引脚20和23脚 */
    : u, B2 N7 ]+ J- @
  16.         HC574_TogglePin(GPIO_PIN_23);
    3 |$ T1 _2 f& ~8 f9 O! f7 y$ V
  17.         HC574_TogglePin(GPIO_PIN_20);
      l% f3 c8 u" n1 R5 y& e
  18.     }$ S) a3 o9 t' j5 a8 m6 ?3 e
  19. }
复制代码
" ^: j* Y; R2 ^* M
48.9 总结
6 P5 Q! ]% \- j) ?; m0 e本章节就为大家讲解这么多,由于FMC总线可以扩展出32路高速IO且使用简单,所以实际项目中也比较有实用价值,望初学者熟练掌握。" i& W6 B& F& l& [. }/ N; j

+ L' G) [$ A  g& q+ C% w& k$ a
% A8 W9 E$ x2 W! v4 m
$ i& r, K& \8 K" l# {% I( N
+ p! v3 j0 w) K  `) @  C
赞 收藏 评论0 发布时间:2021-12-27 17:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版