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

【经验分享】STM32H7的SPI总线应用之驱动DAC8501(双路输出,16bit分辨率,0-5V)

[复制链接]
STMCU小助手 发布时间:2021-11-3 10:44
75.1 初学者重要提示( o7 N* Z; ]8 q5 Q; v
1、  学习本章节前,务必优先学习第72章。
# f" n! j9 l5 E# c* V( t. w0 E- k, ^; y( L* a
2、  DAC8501模块上带了两片8501,每片是单通道DAC,带片上输出缓冲运放,轨到轨输出,16bit分辨率,支持30MHz的SPI时钟速度。
* g/ q1 j2 e% T" w
" y9 L9 q* k, u9 O( m# q3 Z3、  本章涉及到的知识点比较多,需要大家掌握STM32H7的SPI , DMA,TIM,DMAMUX和DAC8501的一些细节用法。
) g6 ^: ~# w% i, t: A6 d( l2 F3 M
. T8 W9 x# E* `9 h9 q4、 H7的SPI + DMA驱动这类外设的灵活度,绝对可以媲美FPGA去控制:0 S2 I5 h% P7 v! V

( ?9 z% q" u" V4 y  H7的SPI外设比F4系列的灵活性强太多了,主要表现在两个方面:数据的传输支持了4-32bit,特别是那个NSS片选引脚,超强劲,可以做各种时间插入,灵活应对了市场上这类芯片的需求。+ D. O3 F+ y2 H+ F
  DMA这块相比F4系列,有了质的飞跃,支持了DMAMUX,这个DMAMUX除了带来灵活的触发源选择,还支持了各种触发事件和同步触发功能。本章配套例子的触发周期控制就是利用了DMAMUX的同步触发功能。
% |$ U4 s7 n5 T- Q, G  r* }- n' v5、  本章配套了中断和DMA两种更新方式的案例,DMA实现方式与中断更新方式完全不同,因为DMA方式要使用硬件SPI1 NSS片选引脚驱动DAC8501。而中断更新方式使用公共的总线驱动文件bsp_spi_bus.c,片选是通过通用IO方式控制,支持串行FLASH、TSC2046、VS1053、AD7705、ADS1256等SPI设备。大家在看例子的时候要注意。
* u, h! H' g1 r% H. l/ r  R* p( j
! ]/ H+ \/ b. h% W% j& g0 C6、  对于本章教程配套例子的SPI DMA方式,这里特别注意一点,定时器触发一次,就会让SPI以DMA方式传输24bit数据。3 b' @( ~! T3 G6 s: Q! s

5 j. E& l3 O! T8 N; r8 J) X7、  DAC8501数据手册,模块原理图和接线图都已经放到本章教程配置例子的Doc文件里。2 I" _% L1 d+ {, X

! q+ D/ X/ D) H; ^, G! X! w" v5 S. I75.2 DAC结构分类和技术术语
5 [: z: E2 a8 O4 R' j在本教程的第74章进行了详细说明。
1 B/ _1 q4 b% I- O  w. b- R6 a- F$ U8 b$ i4 m
75.3 DAC8501硬件设计, [  t& P" T! K, g
75.3.1 DAC8501模块规格
$ S6 e& D: N$ [  c- w: m. y! Z产品规格:
1 ?2 j( o! `" C! m' m9 \2 v' H
6 `& Q% D9 q1 [* t1、供电电压: 2.7 - 5.5V【3.3V供电时,输出电压也可以到5V】。
- _3 u( H- N4 k3 X" w+ H" L  b6 f4 `
6 P. E, I4 K6 |* g- M2、通道数: 2路  (通过2片DAC8501E实现)。
$ u% M* [- ?* H9 q( R2 k0 Q& v# w' T
3、输出电压范围 : 0 - 5V【零位 < 0.020V, 满位 > 4.970V】。( b$ ^% ?7 P" z5 Q* h

8 v) y! H3 E! l3 }( b" @6 t! d, n4、分辨率: 16位。+ @! E; q* S5 e/ w; C, S

: f, C; {* U* X7 }3 v5、功耗 :  小于10mA。
: |! ]0 C) C5 l* u7 v6 }$ z  ^( J$ x. C! _& K3 n; C. r
6、MCU接口 :高速 SPI (30M) 支持 3.3V和5V单片机。
  Y! d+ v2 z1 a7 R2 E, J1 ^6 T; @: l
. |5 L2 m) U( x4 V8 o7 Z7、DAC输出模拟带宽:350KHz。6 ]+ m( \, m: G

* U4 N3 f- g: H7 X7 J# V8、DAC输出响应: 10uS 到 0.003% FSR。
/ ~1 d  }8 y8 ^4 ?/ S. a9 |" G
" e6 a8 [/ L0 _: g( G% W/ {) i* W! P产品特点:
4 ~6 L8 h9 H- D5 ~: j; |* ~* n- k5 o- }* s4 Q: H
1、输出和供电电压无关;模块内带升压电路和5V基准。
) O; ^; _- D  m1 T, ]% F+ D2 z+ e1 ~  ^5 \# o: z
2、自适应单片机的电平(2.7 - 5V 均可以)。
/ _$ P3 y6 u& D, ^
* h4 z% Q3 u, H% @& y) `' A1 P3、输出电压轨到轨,最高电压可以到 4.970V 以上。* o4 T- m( J/ }3 H% p
4 A; `$ Q& R* h6 p& M7 x
产品效果:$ J) D% @8 V; t7 v7 Y- z

' b# I9 i. a- n% @+ c
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

6 G0 Q$ `5 q, }. ~2 d( b) q4 C! _' v) ?' N$ K4 D  j
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

' n4 P* }1 s9 \- r: U! T5 }7 T! V$ w5 U$ {' t5 C4 Z
75.3.2 DAC8501硬件接口
5 T7 [$ M) l. ]* ?- ]V7板子上DAC8501模块的插座的原理图如下:
2 {0 Q; n9 ~+ V
8 f! o( I2 ^) k  Q( \' u2 ~3 f
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

" F; ~8 U0 J( q8 Y" d9 W' `! n3 o$ b, _* Z0 h! z
实际对应开发的位置如下:1 i3 o2 o% W! |- W
& r' K" I- p+ _% T0 b8 Z! W
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

! C# B% M( e% y+ I
1 D/ e. C* E9 |" p9 t3 H. d* b75.4 DAC8501关键知识点整理(重要)# e8 \; }- h! {1 i
驱动DAC8501需要对下面这些知识点有个认识。
0 W; ?# [1 L& R8 k) u! Z! ?4 D$ L( W6 R. X9 o, h
75.4.1 DAC8501基础信息
! b$ G7 e! R& T  单通道DAC,带片上输出缓冲运放,轨到轨输出,16bit分辨率,支持30MHz的SPI时钟速度。/ N, k& \# J8 _2 x& a) j3 f
  模拟输出带宽350KHz。
4 d9 p# f4 Q5 @* n4 G/ @  供电范围2.7V到5.5V。1 a; R7 s& h. b$ I. Z5 K
  具有低功耗特性。
4 o( @. Q( N9 f9 S* ~  上电复位输出0V。
' ]" ?7 Q% `6 G: g5 `/ n0 e8 w# ]/ z* A+ K% J9 n

6 |* V7 z3 H1 G7 q0 [8 u* n( K75.4.2 DAC8501每个引脚的作用
/ Q( n0 B* k8 I/ rDAC8501的封装形式:, p' e3 Y: ~) d1 z9 c4 x/ d
3 n4 ]: D8 G0 c& z# s" @; J9 {: o
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
% e) y. ?7 a: X6 x& _4 c

; S, b9 O; g( J) m8 E1 j  Vdd  k% x. P, K! I! y
供电范围2.7-5.5V。' m! g/ D( F- J% S

( }! {# E9 X, e9 v' L. k  Vref/ b+ Y/ ~; b+ H9 ~" b8 @% _
稳压基准输入。
% W5 g: J7 ~- C6 P3 T2 M+ x4 _/ E8 _$ B0 r# c9 Q1 Z2 o
  Vfb
( l- k: F9 `/ z输出运放的反馈。$ T8 D0 A' I4 @# h, R+ }
, ~$ A; f* c5 Y* k, Y9 G
  Vout
6 w+ S. i" n* d) l. ?" l模拟输出电压,输出运放具有轨到轨特性。1 w  a5 D0 H% ^1 Q$ a; X, C  W& L' ]
+ _+ y, M+ C7 t3 L- ~+ h
  SYNC (片选)* C* c1 w7 w, @6 Q9 ?3 S# Z
低电平有效,当SYNC变为低电平时,它使能输入移位寄存器,并且数据采样在随后的时钟下降沿。 DAC输出在第24个时钟下降沿之后更新。 如果SYNC在第23个时钟沿之前变高,SYNC的上升沿将充当中断,并且DAC8501将忽略写序列。
; m5 V) w5 i" [1 X0 B2 Q2 d
# j" Z9 O/ s0 k  SCLK; z6 g  A& g2 H
时钟输入端,支持30MHz。7 Z. R& [& ]; F# b# {

# [9 R  g& z% f* f  Din+ }& v  s  _% w  C% ?8 I
串行时钟输入,每个时钟下降沿将数据写到的24bit的输入移位寄存器。
1 \, r! i4 P, G. l3 D, B+ M# C3 v$ a( z4 G6 q
  GND
. H. X7 |& C" V& U8 s接地端。
- n# j9 ~& u" ?7 E
8 O# E0 _" u7 g" V75.4.3 DAC8501输出电压计算公式5 M& o' H2 `# F
DAC8501的计算公式如下:
0 y' E1 |9 L8 q! U9 u; X/ |
2 B# s4 ]" Y5 }
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
: T3 a. f4 R- u( N( F

3 ]7 i8 |1 K9 Q9 K( C" h% w: J  D
9 g  \& I- d7 z% g  `& ^/ X配置DAC8501数据输出寄存器的数值,范围0 到2^16 – 1,即0到65535。
6 [* w, y3 |5 q+ n( e! |. w- O. c
* J1 n5 b' K8 M% A9 D* d: S  VREF
9 t6 H4 r+ G% q' n$ }使用外部参考电压,由VREFIN引脚的输入决定。# @" E0 _1 i$ d2 W0 X0 E- X
0 k0 \6 e$ H3 w; `& h- O2 O; P1 M
  Vout# n4 G% ]3 s: N) a
输出电压。0 b0 v2 c7 c# c" Q2 J$ F3 v
" \1 b# o  I: p/ X" R3 a% ]
75.4.4 DAC8501时序图
# u2 [# K, b; p1 y' [0 x4 @DAC8501的时序图如下:% Y  F; j" H8 `5 J% y; ^

) D2 |1 D9 l) ]5 ?4 O
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
! s1 _( |! E0 U
0 T9 o' L. @8 o" Y# s+ R
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

' a- n$ @% J' @. n) b. ]& c
0 ~3 u4 U- R; O; t9 F0 j' ^这个时序里面有三个参数尤其重要,后面时序配置要用到。
0 D) B5 i  N+ P/ W% p6 [1 _9 u7 X% K/ G/ Q0 t# G. H5 `7 t2 v
  f(1)
* ?* `& C4 }: t+ C供电2.7到3.6V时,最高时钟20MHz。
! X: Q* U7 T5 V$ l8 B# N
" y" J5 @# W& B+ _7 X: o$ a) |供电3.6到5.5V时,最高时钟30MHz。
' _; B- C: m+ P9 ?, Q& ?- `
8 Z: i  U6 r4 {; A! ]# z" v5 e  t(4)
: Y+ }; K: j, A' ZSYNC低电平有效到SCLK第1个上降沿信号的时间没有最小值限制,可以为0。
  e- A; I  L; e+ e* s. d! W' }2 ~# V* J( W
  t(8)
7 d) W, y7 S; O2 ?2 t: c每传输24bit数据后,SYNC要保持一段时间的高电平。
! t0 R$ J1 E! c# H
+ a8 b: {& V2 I, T* K' w: y% Z供电2.7到3.6V时,最小要求50ns。
" J! \7 d+ e2 B- S4 Q) d6 _2 W; N" D& q$ e( N  }' w
供电3.6到5.5V时,最小要求33ns。5 S; C, J2 D) u

/ T9 G" P; ^" Q5 e) S- D75.4.5 DAC8501寄存器配置
+ a) b! t: r4 r4 G9 j8 }0 U8 SDAC8501的寄存器配置是24bit格式:
4 T) W2 U) T, T' Y# _, E9 u% v# \
+ ~8 D9 R" c6 u$ U
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

- g) @1 o8 n2 k" a# W+ S/ k6 D7 Y1 `7 m4 v& S* q8 Z8 o+ F
控制DAC8501每次要传输24bit数据,高8bit控制位 + 16bit数据位。
9 V. o1 n/ e' E# ^$ C5 g' w0 _
' X$ L4 P; k* A0 q0 b! z# D控制位的PD1和PD0定义如下:
% Q  \: w9 l1 T' h+ Q5 e$ C  Y/ c' i& C, @/ B& I
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

1 t9 C5 r- c5 d% t3 `) T/ }' a3 y; U* ]
PD1 PD0 决定4种工作模式+ z" ?4 ^9 ]- j* l

- _) B. N# U0 Y  P0   0  ---> 正常工作模式
0 O) @4 h6 X6 c6 F) ^' ?5 e5 j
& O8 a. R5 r6 E/ f% q2 {0   1  ---> 输出接1K欧到GND9 e; P+ {7 `! P8 v4 A1 l1 D! e

) G0 N1 @8 K& J3 L# ~" u1   0  ---> 输出100K欧到GND
( [8 i9 }  _9 ]
0 P0 E. `( u2 Z+ Z1   1  ---> 输出高阻- m) G* J  V' Z- K
5 J' h4 }6 h9 }* ]
75.5 DAC8501驱动设计(中断更新方式)
3 q9 {* C9 a; g$ E0 y- ]; eDAC8501的程序驱动框架设计如下:
3 y! ^& P4 e+ U, v! E5 w
  v0 j, e' J( g: A( C
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
+ m* B" N7 ?0 Y8 f) I- C: ]8 Q
! n; r$ Q2 y$ V( B
有了这个框图,程序设计就比较好理解了。
! p- ?" [( o1 D- x0 v, b, U: |; k2 H9 }
75.5.1 第1步:SPI总线配置
  P6 ]1 p. b7 k" ^! I8 sspi总线配置通过如下两个函数实现:; t; P4 H6 l) w

% i0 d' [  r/ g2 O  S. B
  1. /*2 V( B$ \5 J% S8 [' Q6 O
  2. *********************************************************************************************************- Y; r) [3 B; r0 c) S) S; ^) |. P
  3. *    函 数 名: bsp_InitSPIBus0 R) x: ^$ s' X/ z* O- m
  4. *    功能说明: 配置SPI总线。
    : g/ x) N$ A1 m
  5. *    形    参: 无
    * Z, ~- ^9 A0 @  u* R" {$ a
  6. *    返 回 值: 无
    5 N8 @" N+ j+ x" S" @% x1 S
  7. *********************************************************************************************************
    2 n" V. x: n, D' G4 X  h
  8. */4 p1 D4 f) b7 P- K; S& S( x
  9. void bsp_InitSPIBus(void)
    4 {' ~/ }5 n+ m' b) m* o4 l$ o
  10. {   
    , \! Q* I/ R' _
  11.     g_spi_busy = 0;
    & a; D0 K4 H- f' E' L: h

  12. : `, j* @( y& q
  13.     bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_1EDGE, SPI_POLARITY_LOW);/ O; E8 K% X9 g# C
  14. }
    " u. N" F& Z: Z/ G

  15. 5 f8 A) j2 C* j; }
  16. /*! v; b: @- c9 F# B
  17. *********************************************************************************************************& w) S9 b/ g  U( i$ n: k  L
  18. *    函 数 名: bsp_InitSPIParam7 I, q# t4 J: a9 m! z
  19. *    功能说明: 配置SPI总线参数,时钟分频,时钟相位和时钟极性。, H8 G: E/ F: B* G! p7 O/ a
  20. *    形    参: _BaudRatePrescaler  SPI总线时钟分频设置,支持的参数如下:
    $ h. F: G/ P( n5 P
  21. *                                 SPI_BAUDRATEPRESCALER_2    2分频
    6 E9 B; M, F5 U
  22. *                                 SPI_BAUDRATEPRESCALER_4    4分频. T; J  I7 o: C9 z4 F4 Y
  23. *                                 SPI_BAUDRATEPRESCALER_8    8分频* E( v8 W: `7 F$ l
  24. *                                 SPI_BAUDRATEPRESCALER_16   16分频
    3 h, n2 T* X5 m& |
  25. *                                 SPI_BAUDRATEPRESCALER_32   32分频4 \9 c6 B+ R9 t$ c. ]% \9 h
  26. *                                 SPI_BAUDRATEPRESCALER_64   64分频
    6 x/ [9 s- K, L. P
  27. *                                 SPI_BAUDRATEPRESCALER_128  128分频9 F# T8 f% C8 v
  28. *                                 SPI_BAUDRATEPRESCALER_256  256分频  i" O- X# f& R" o) C# @
  29. *                                                        + D6 c6 Z- N  Z/ _* C
  30. *             _CLKPhase           时钟相位,支持的参数如下:
    * l, O# o2 ?8 H& \- R* w
  31. *                                 SPI_PHASE_1EDGE     SCK引脚的第1个边沿捕获传输的第1个数据
    $ x, R' J5 v; S: m) {& E% M
  32. *                                 SPI_PHASE_2EDGE     SCK引脚的第2个边沿捕获传输的第1个数据/ I0 U  k9 d2 o" ~6 {
  33. *                                 
    3 Y4 f2 g3 X  o! N  ^* `
  34. *             _CLKPolarity        时钟极性,支持的参数如下:* A, Q6 T2 ~  |$ G% p
  35. *                                 SPI_POLARITY_LOW    SCK引脚在空闲状态处于低电平
    5 J  S5 Y1 b1 t' x0 A* o4 ~" X6 U
  36. *                                 SPI_POLARITY_HIGH   SCK引脚在空闲状态处于高电平: t2 C$ ?1 ^4 A) i( L
  37. *
    0 t7 q6 ]$ e: u4 i
  38. *    返 回 值: 无
    8 ~! Y5 i; d# v1 s- s' P9 ~, {
  39. *********************************************************************************************************% e; @3 S' n9 c+ i2 S1 ^* t# H
  40. *// B6 i- {# B9 K
  41. void bsp_InitSPIParam(uint32_t _BaudRatePrescaler, uint32_t _CLKPhase, uint32_t _CLKPolarity)
    - Z$ Y8 l& ~4 |, Z  j8 L, |& {
  42. {9 j, n# s' F. G+ x2 Y
  43.     /* 提高执行效率,只有在SPI硬件参数发生变化时,才执行HAL_Init */
      V3 s3 m4 q! v- `, E0 d6 H' E! z
  44.     if (s_BaudRatePrescaler == _BaudRatePrescaler && s_CLKPhase == _CLKPhase && s_CLKPolarity == _CLKPolarity)* {8 x6 G' k0 b. a+ k3 o% b
  45.     {        
    . `* V) [" ~8 s7 {% {( D
  46.         return;5 l  M0 w- S7 x2 w7 r
  47.     }/ x3 K" e( a$ N) N% r
  48. ! B5 K/ g: l9 _, S
  49.     s_BaudRatePrescaler = _BaudRatePrescaler;   
    ( {% I2 b5 o& x8 L+ y7 S( {
  50.     s_CLKPhase = _CLKPhase;5 m! G: x( i9 u# x+ m
  51.     s_CLKPolarity = _CLKPolarity;
    . I( t* ?# ?$ ~

  52. + r: z' c4 b. q' H; ?+ P8 s3 A+ e

  53. * Z; v" s9 R( d) M7 ?
  54.     /* 设置SPI参数 */  @1 E9 D- s' E& |  _* L
  55.     hspi.Instance               = SPIx;                   /* 例化SPI */& _( D6 q9 b9 {% j, F4 l
  56.     hspi.Init.BaudRatePrescaler = _BaudRatePrescaler;     /* 设置波特率 */
    8 L& f1 I# n" v: o6 P7 P- a
  57.     hspi.Init.Direction         = SPI_DIRECTION_2LINES;   /* 全双工 */: o( E7 o4 C/ J8 ?# _  Y1 M5 R
  58.     hspi.Init.CLKPhase          = _CLKPhase;              /* 配置时钟相位 */9 X0 w  X0 p, d! Z+ a
  59.     hspi.Init.CLKPolarity       = _CLKPolarity;           /* 配置时钟极性 */0 C5 I. k; P. o" D3 H- `' E
  60.     hspi.Init.DataSize          = SPI_DATASIZE_8BIT;      /* 设置数据宽度 */( Y8 t- C: a1 C  M7 F; k7 h' Y& a& m
  61.     hspi.Init.FirstBit          = SPI_FIRSTBIT_MSB;       /* 数据传输先传高位 */, S( @# R7 a* |/ l6 y
  62.     hspi.Init.TIMode            = SPI_TIMODE_DISABLE;     /* 禁止TI模式  */
    0 a  z' W2 q; ?. g0 `+ f) {+ c0 i
  63.     hspi.Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE; /* 禁止CRC */
    6 ]$ j6 k& w1 k* c. Q- t/ [
  64.     hspi.Init.CRCPolynomial     = 7;                       /* 禁止CRC后,此位无效 */
    ; U, Y5 Z6 z% F8 t2 Z! G
  65.     hspi.Init.CRCLength         = SPI_CRC_LENGTH_8BIT;     /* 禁止CRC后,此位无效 */6 V) A. n3 o3 n
  66.     hspi.Init.NSS               = SPI_NSS_SOFT;               /* 使用软件方式管理片选引脚 */
    ! j) B7 T  A2 a% j6 `" l+ F% c( }
  67.     hspi.Init.FifoThreshold     = SPI_FIFO_THRESHOLD_01DATA;  /* 设置FIFO大小是一个数据项 */( f: R& K- Y! v5 L8 x3 L; _
  68.     hspi.Init.NSSPMode          = SPI_NSS_PULSE_DISABLE;      /* 禁止脉冲输出 */
    # q2 o; O9 G! V/ `% _
  69.     hspi.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE; /* 禁止SPI后,SPI相关引脚保持当前状态 */  3 D) ?3 E. r* P8 E
  70.     hspi.Init.Mode             = SPI_MODE_MASTER;            /* SPI工作在主控模式 */
    ' C" S: S7 p. F: y. ?- z* w

  71. % R% {: Q( U2 O9 q
  72.     /* 复位配置 */: l) o) V7 f3 |: v3 `
  73.     if (HAL_SPI_DeInit(&hspi) != HAL_OK)5 U+ V' @) }& a
  74.     {! G7 e$ e# o1 K0 p  y& R
  75.         Error_Handler(__FILE__, __LINE__);( d+ W  E0 \$ z* }; L. Z
  76.     }   
    3 X+ l" _1 r, i# b* f

  77.   E' g2 x5 o& S, d6 Z- v/ X+ k4 `0 F
  78.     /* 初始化配置 */
    " X% \. R, L/ _# ]! h: C
  79.     if (HAL_SPI_Init(&hspi) != HAL_OK)
    $ V! s/ b  ^1 e: g% E8 s
  80.     {
    1 V! k& n$ n: n' f! [! W+ e
  81.         Error_Handler(__FILE__, __LINE__);% H( t% b0 w0 I% h( l/ @: B
  82.     }    * C7 Z- \" b  R& o  @* V
  83. }
    $ _/ n+ d8 }8 _! f
复制代码

+ \" z9 _1 Y# l7 v4 q, q& @2 i$ y8 l- W( }, }- h2 B7 C; @8 t
关于这两个函数有以下两点要做个说明:- h, F& P' Z/ i/ ]
' E1 B, w7 ~. |6 R# l; `2 m
  函数bsp_InitSPIBus里面的配置是个初始设置。实际驱动芯片时,会通过函数bsp_InitSPIParam做再配置。
, N6 W# }( O9 Y& L  函数bsp_InitSPIParam提供了时钟分频,时钟相位和时钟极性配置。驱动不同外设芯片时,基本上调整这三个参数就够。当SPI接口上接了多个不同类型的芯片时,通过此函数可以方便的切换配置。
' h3 [" T' B4 j: M75.5.2 第2步:SPI总线的查询,中断和DMA方式设置1 u9 [: b0 F" S, d
注:对于DAC8501,请使用查询方式。
; V: S: [. c  s
# ~3 h6 C! k* y5 ySPI驱动的查询,中断和DMA方式主要通过函数bsp_spiTransfer实现数据传输:4 I$ N7 f7 A% C9 s, l& W6 I8 K. l

/ M0 o4 F4 w. N1 |' i/ Y
  1. /*5 U2 {; r; K, Q, N, o$ i
  2. *********************************************************************************************************
    - _. {+ O8 X' s3 t8 z/ |4 M% n
  3. *                                 选择DMA,中断或者查询方式/ F8 _  J  ]1 Y" W
  4. *********************************************************************************************************) ^0 W' r, J$ K3 h% e5 ~
  5. */
    ) W, Y9 e8 u: u
  6. //#define USE_SPI_DMA    /* DMA方式  */1 y: h4 H5 y8 T
  7. //#define USE_SPI_INT    /* 中断方式 */& W6 ~7 z! o% l3 i
  8. #define USE_SPI_POLL   /* 查询方式 */
    2 U2 c3 E& ?+ V% ]; T
  9. / R+ j( W* t$ @# j. I( j$ O/ U7 w
  10. /* 查询模式 */
    - |% P% L+ x/ |& [1 m  k- l/ X6 G
  11. #if defined (USE_SPI_POLL)% Y" g) k' A: ^! J

  12. 4 S7 a0 |; `; ?# Y  |
  13. uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];  
    : G5 `- W" S2 K
  14. uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];( T8 B' D1 j2 _* L
  15. ! c/ C% E  m7 w) R9 h3 d7 k
  16. /* 中断模式 */! g# Q7 _9 h/ B
  17. #elif defined (USE_SPI_INT)" m. i# V4 A5 M" U( [( X: V
  18. " z' }, N; H8 b" M" y
  19. uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   
    ' a0 R4 \3 e! |+ _+ }
  20. uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];
    2 R# j9 V  }& A
  21. - \$ L0 @8 f# |# _# \, l
  22. /* DMA模式使用的SRAM4 */
    / B8 N! q% g/ G+ ]$ J
  23. #elif defined (USE_SPI_DMA)7 g1 u" s( I" C5 K" R- O
  24.     #if defined ( __CC_ARM )    /* IAR *******/
    $ A% G5 j1 V+ ?
  25.         __attribute__((section (".RAM_D3"))) uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   ! U) [1 n8 R, g/ u; v- N
  26.         __attribute__((section (".RAM_D3"))) uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];4 p0 e4 k" q: l2 e
  27.     #elif defined (__ICCARM__)   /* MDK ********/7 g  n1 r3 R' R, c# Z5 _# d
  28.         #pragma location = ".RAM_D3"
    " n+ s& @: k9 ]3 q
  29.         uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   + [, S/ @; N1 m, @$ A) q- i
  30.         #pragma location = ".RAM_D3"9 C9 d) K# N2 \) d
  31.         uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];
    0 ~1 R. {% j% L: f3 q  C/ {) ]/ ~
  32.     #endif
    ( }% U3 ^( i1 W5 D8 {" b; X
  33. #endif
    3 y* ?4 H3 S! I! `7 |4 p4 W& t

  34. " `- ?6 J9 {& l
  35. /*2 z, {+ S$ D2 |1 [1 W2 k) B
  36. *********************************************************************************************************, }9 L+ D" O3 @: J+ M5 B
  37. *    函 数 名: bsp_spiTransfer" ^% c6 i  s: I: x, j! H+ Q( l4 q- p
  38. *    功能说明: 启动数据传输
    & U* W) s5 G4 N: J
  39. *    形    参: 无3 y! E4 i4 _4 d: d+ K) i- T
  40. *    返 回 值: 无
    5 `0 [8 P7 P6 R; v7 D8 l
  41. *********************************************************************************************************. M1 N, m7 M6 o4 y
  42. */
    3 c6 Y# u7 _, O# C, I
  43. void bsp_spiTransfer(void)( {) l7 n3 ~9 G" {
  44. {& I" H6 j# G7 t/ [6 T4 [4 O; u  j
  45.     if (g_spiLen > SPI_BUFFER_SIZE)
    9 J2 x( w( R1 U, u6 R
  46.     {" {, F6 W% l/ s" |. k6 L
  47.         return;( s- v- Q4 T. }: }3 h! ]1 |, G$ R3 k
  48.     }
    3 y# K+ s9 q( b
  49. 4 [8 t( A; b% L0 G, `9 n
  50.     /* DMA方式传输 */
    : ], G' r" Z" u3 `
  51. #ifdef USE_SPI_DMA" Y  P0 ]9 ?8 N* [5 i* C$ I" }
  52.     wTransferState = TRANSFER_WAIT;
    ) k$ L) k- W5 R8 v& }4 W
  53. ! U5 h8 D6 n6 Z) F6 t
  54.     if(HAL_SPI_TransmitReceive_DMA(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK)   
    / N# D1 P7 c) R0 [4 K
  55.     {) a% ?# u8 [! k& a1 [  c
  56.         Error_Handler(__FILE__, __LINE__);6 ^7 X" [4 q2 i: V; f% x2 P
  57.     }
    8 U  |5 R# N9 T; Y5 |! N: t
  58.   v( O9 c; N) r% a1 h3 T
  59.     while (wTransferState == TRANSFER_WAIT)
    ; `0 j% ^7 X. g* T9 Y' j
  60.     {
    # i; t5 M2 H1 {0 v) ~% a3 i
  61.         ;
    ' Z2 }; j$ }, c, O3 z+ L
  62.     }
    , q: _2 v# i2 p, q) @0 B
  63. #endif
    6 O; J- B8 j5 r8 @
  64. 0 w7 h+ Q( Q  _/ f5 F" a9 z0 Q
  65.     /* 中断方式传输 */    4 J0 r  N8 r5 _" V
  66. #ifdef USE_SPI_INT' R3 s# m; H- n' o9 {/ ~" W; q
  67.     wTransferState = TRANSFER_WAIT;9 p6 [9 O4 I9 y& C. F0 o
  68. ( R, C0 a- |, B  c& \  _
  69.     if(HAL_SPI_TransmitReceive_IT(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK)   
    - O# S# B) p% s$ w
  70.     {
      k6 ~3 B; Z0 ]$ a
  71.         Error_Handler(__FILE__, __LINE__);  g! R+ w# J; B
  72.     }
    3 J/ c! f2 X: V' {  Z  N
  73. 3 p6 e5 Y% d6 j; x
  74.     while (wTransferState == TRANSFER_WAIT)
    8 Z2 v' B# h; Q* K
  75.     {
    + B. L( m, _" o* M, _& Z! C
  76.         ;
    - o. a; V5 L( @' u2 c/ U" o
  77.     }( V3 k% G$ e+ q* N4 E
  78. #endif0 l; Y; u* I# `6 U' q8 @( V/ [
  79. & F# [1 j. R) W/ u) [% f) `- u5 r. [
  80.     /* 查询方式传输 */    ' r4 r- Z4 D/ g( P! z8 ]! a) P% d
  81. #ifdef USE_SPI_POLL
    % ~* T+ D7 Q" x6 `
  82.     if(HAL_SPI_TransmitReceive(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen, 1000000) != HAL_OK)   
    3 B' p/ v2 X) Z2 `( z* Q- F
  83.     {
    . ?  S' {( p* G- E
  84.         Error_Handler(__FILE__, __LINE__);
    4 h* L! ?6 [4 d7 I& n+ M
  85.     }   
    3 s* u& U9 f: C1 B2 T
  86. #endif  x+ |& x8 y1 S- Y7 ^4 s3 a1 u& o
  87. }
    6 P* a$ ^- V# V' U& }
复制代码
: w, a" \$ i2 r% u3 I! J
% C5 [3 M6 L9 F9 b* N
通过开头宏定义可以方便的切换中断,查询和DMA方式。其中查询和中断方式比较好理解,而DMA方式要特别注意两点:
& y0 p2 k  @& C; _$ o& T; Y* y8 `  c2 Q. g
  通过本手册第26章的内存块超方便使用方式,将DMA缓冲定义到SRAM4上。因为本工程是用的DTCM做的主RAM空间,这个空间无法使用通用DMA1和DMA2。
: A8 W3 k; }% @, H. ~  由于程序里面开启了数据Cache,会造成DMA和CPU访问SRAM4数据不一致的问题,特此将SRAM4空间关闭Cache。
' D; j- _2 h2 A. }8 j/* 配置SRAM4的MPU属性为Non-cacheable */
, I8 v* Y; I3 P3 G    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;' _( C4 A' H( G, A  W2 c
    MPU_InitStruct.BaseAddress      = 0x38000000;
; z% i  F% {" S5 a    MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;
  [. S$ H: }; i    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;  @/ h# t9 v9 v% ^" D$ M* y+ \/ Q
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
, Y. `4 h# [. Q7 M0 \5 R% Y    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
! {  I  v8 }0 @& v# L/ Q: j; {- V    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
* x4 ^' j6 v1 W    MPU_InitStruct.Number           = MPU_REGION_NUMBER2;5 N. N6 @4 \5 P
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
) y4 R3 {& g+ R3 q. d    MPU_InitStruct.SubRegionDisable = 0x00;' p1 N% R  X$ X4 Y
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
( p7 V0 Q2 I& P' k6 y% |4 c1 U
5 O3 l; f$ B5 n: r" t    HAL_MPU_ConfigRegion(&MPU_InitStruct);
) A' ?; X! a4 R& {  I9 G" n( ~; r# ~+ w

; c0 _% `4 h1 k0 T9 a6 M75.5.3 第3步:DAC8501的时钟极性和时钟相位配置9 P9 s3 L# ^/ O1 f, D# B" t0 n
首先回忆下STM32H7支持的4种时序配置。- X6 B; R& t; W' Z8 ?9 w9 i( u* X& a
' L+ a' {7 ~/ ?8 M: j: W3 e& U
  当CPOL = 1, CPHA = 1时
2 Q! n7 N! f4 }; G3 ?0 sSCK引脚在空闲状态处于低电平,SCK引脚的第2个边沿捕获传输的第1个数据。
% L9 Y2 |- G) V! O! D6 e6 I% N
9 n. U! |1 C' o* {$ u  当CPOL = 0, CPHA = 1时) n2 P/ |; j) E; h: R  `
SCK引脚在空闲状态处于高电平,SCK引脚的第2个边沿捕获传输的第1个数据。
1 k) M7 w$ s; C5 z3 m
( d, x" o4 Q/ O6 `3 _- T5 }  当CPOL = 1, CPHA = 0时
+ G/ O3 o% t4 l- i4 ZSCK引脚在空闲状态处于低电平,SCK引脚的第1个边沿捕获传输的第1个数据。2 ^9 }, i. r+ r6 K

0 [2 T  M) M; D9 f) A0 W  当CPOL = 0 ,CPHA= 0时
7 K/ w) H) h. w5 oSCK引脚在空闲状态处于高电平,SCK引脚的第1个边沿捕获传输的第1个数据。5 A) S( a: E: f1 k! `

9 J4 T* m: O7 Y* O; `: \: @
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
& B: z6 G3 C* Z# u
! m; ]( T. S- j
有了H7支持的时序配置,再来看下DAC8501的时序图:  z% z' i$ F, w5 e5 X5 \
) h( G6 ^. ?# j8 F) w
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
* g. w: S( O" s5 U# E/ ~) k: k
. @8 H2 K' D% }$ Y
首先DAC8501是下降升沿做数据采集,所以STM32H7的可选的配置就是:5 q3 n' Q+ {. E. b0 Q. \& M

4 z# z( v& q" p% yCHOL = 0,  CPHA = 1
3 i" g" I/ A% \* ]8 R/ [5 |# I/ z) j
CHOL = 1,  CPHA = 0
( a4 F5 N8 i! q) K+ V
5 K' r8 g4 t& }对于这两种情况的主要区别是空闲状态下SCLK时钟选择高电平还是低电平,根据上面的时序图和DAC8501的数据手册,两种情况下都可以正常运行。经过实际测试,STM32H7使用这两个配置确实都可以正常运行。程序里面默认是选择CHOL = 0,  CPHA = 1。
7 `& {& p1 I( H7 C( n& R
6 ?; V" d) h( Q' d75.5.4 第4步:单SPI接口管理多个SPI设备的切换机制
/ N5 Z4 B  B1 x1 V; F! `单SPI接口管理多个SPI设备最麻烦的地方是不同设备的时钟分配,时钟极性和时钟相位并不相同。对此的解决解决办法是在片选阶段配置切换,比如DAC8501的片选:
/ J  ?+ {' I( y1 G( l  n+ N+ [; [8 E. z
  1. /*$ g, T+ \$ X* q6 a9 j9 a- ?7 v
  2. *********************************************************************************************************
    ) F$ j! G9 {6 i
  3. *    函 数 名: DAC8501_SetCS1
    & l" g5 A3 J9 q5 C! r4 A1 l" u5 T
  4. *    功能说明: DAC8501 片选控制函数
    2 g8 A* v. J9 f' v/ l5 C5 I
  5. *    形    参: 无0 B& d% ?3 X; a/ j4 h1 t; Y
  6. *    返 回 值: 无3 V! Q  X# \% y" q( O
  7. *********************************************************************************************************
      s' ~$ P% c# T2 K8 J) Z
  8. */5 G4 |8 Y' z: L: N3 d7 C4 H# d
  9. void DAC8501_SetCS1(uint8_t _Level)% P: e( l- y3 n
  10. {; m: w: R) ?9 N& B  y; \
  11.     if (_Level == 0). z1 e% ^& s8 w1 j( g
  12.     {6 Y- H2 H7 G. ~  m' e
  13.         bsp_SpiBusEnter();    /* 占用SPI总线  */    . a, I$ j# B& U& ~
  14.         bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);        
    " h+ }7 ?+ m; M' j
  15.         CS1_0();; c% F" e3 k2 S! k+ j
  16.     }
    + g" ?. r) a" m# h) e. V
  17.     else
    4 a3 m9 Z: c# s: V) G6 ?, f3 m1 Q
  18.     {        
    5 ?5 T; }! h' x3 C* V8 L
  19.         CS1_1();   
    2 r' q( E+ U- i* j" l: [
  20.         bsp_SpiBusExit();    /* 释放SPI总线 */
    " Q! {8 f* ]0 _$ r- U) S
  21.     }   
    # I. A, q0 j' C+ J% f' J
  22. }" r* s+ L7 H$ Z
  23. " ]' r3 y1 A. ]+ O
  24. /*
    " R6 _8 {0 B  F. E- ]
  25. *********************************************************************************************************; u* B8 B" F% e6 b4 _
  26. *    函 数 名: DAC8501_SetCS2(0)
    " d8 B4 F8 t* ^6 Q* @
  27. *    功能说明: 设置CS2。 用于运行中SPI共享。2 k' X+ o: R$ J5 t( k
  28. *    形    参: 无
    " h, S0 U2 W  H6 [
  29.     返 回 值: 无) H2 p2 U+ y( W; R9 T& L& I% {: c  e
  30. *********************************************************************************************************
    & j" f  d! c" t* w4 }' T  }
  31. */- f! g4 g% A/ q1 k* {# ^; l; }
  32. void DAC8501_SetCS2(uint8_t _level)
    ! Z6 V" {: j1 o( j
  33. {
    & x( i. |+ H9 K. V) T
  34.     if (_level == 0)" F3 l7 q% O" m
  35.     {2 C" V" T; ]* S* b8 s
  36.         bsp_SpiBusEnter();    /* 占用SPI总线  */; x8 S$ B: z4 {; o, Q
  37.         bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);        ' r/ D3 r# L& f$ A& K) A
  38.         CS2_0();2 ?( g  ]/ N6 V# ^! P7 _
  39.     }" L) x. `' m0 w% M9 e3 J" d
  40.     else
    0 \; p# B, B' t) |# D1 G: R3 c
  41.     {0 P# A8 P6 H* d
  42.         CS2_1();3 ?. R/ v5 V, k
  43.         bsp_SpiBusExit();    /* 释放SPI总线 */
    ! x1 ^/ t" x/ k  q8 s1 s
  44.     }2 E% Z2 H) N1 e5 Y5 v# P
  45. }
复制代码
/ _  C5 v% g- }" x! M
通过这种方式就有效的解决了单SPI接口管理多设备的问题。因为给每个设备都配了一个独立的片选引脚,这样就可以为每个设备都配置这么一个片选配置。
$ Z+ o6 i( w  }1 G6 U, w
+ k6 O# Z* p/ o% B* v但是频繁配置也比较繁琐,所以函数bsp_InitSPIParam里面做了特别处理。当前配置与之前配置相同的情况下无需重复配置。
: I7 I, F4 z) z1 |5 ?/ X/ [
  X1 V* W8 x, Q& N$ k( S75.5.5 第5步:DAC8501的数据更新
/ o: J! X; H0 _1 \; M* ^1 jDAC8501的双通道数据更新通过下面的函数实现:$ R8 z1 B$ ~8 O- {. e2 G  a

( Z( }- R4 R2 T( i- P& b
  1. /*. E, }+ Y8 ^8 Y, e7 S
  2. *********************************************************************************************************
    7 h3 I% n  P) X! A- D& z; M' s/ v
  3. *    函 数 名: DAC8501_SetDacData
    ! g/ {5 }% c7 q9 |" U) A+ z
  4. *    功能说明: 设置DAC数据
    / p0 `2 `% P# ^% O3 i% ?
  5. *    形    参: _ch, 通道,' Y" @+ a$ {" D- u# t/ H
  6. *             _data : 数据
    ' S, W' c" g* U8 q1 Z4 S
  7. *    返 回 值: 无. h3 R: Z9 z" U
  8. *********************************************************************************************************
    + L- {- m5 ?# |1 k
  9. */
    " f% a! m" ^2 o, I% L0 p" N
  10. void DAC8501_SetDacData(uint8_t _ch, uint16_t _dac)
    - W& L2 P, C) e
  11. {
    / r9 J1 `  ^9 q
  12.     uint32_t data;
      @% C) x. p  S

  13. # @2 F$ y3 K& v, |0 l: k
  14.     /*  S. L( e! p) [: O" }. F. {
  15.         DAC8501.pdf page 12 有24bit定义+ p9 L/ \: b+ Z! o
  16. 2 q: o6 Q3 U9 L4 g2 \
  17.         DB24:18 = xxxxx 保留
    . ?# t9 A. }3 Y" [/ N6 v. @
  18.         DB17: PD1% Y9 P, Z0 q* L, t4 A+ g0 q
  19.         DB16: PD06 M) ~+ l4 g$ T5 m) r' l
  20. 6 l6 C: g9 Q7 t5 ^  N) C/ e
  21.         DB15:0  16位数据7 U# d0 |8 y; x2 r/ g

  22. 6 j. O: y% Z+ u
  23.         其中 PD1 PD0 决定4种工作模式% k: H: a  J9 M  Q
  24.               0   0  ---> 正常工作模式
    - j& U, i5 e2 W% K! `" Y
  25.               0   1  ---> 输出接1K欧到GND
      h# |& n+ Q- @0 a# Y
  26.               1   0  ---> 输出100K欧到GND6 @& m5 E- P5 N1 r9 q1 w1 ?
  27.               1   1  ---> 输出高阻
    ) s. k/ c6 \- G& Q0 q9 k
  28.     */- K4 o2 K  F( m

  29. $ _8 W2 J, D& O# ]/ M, l( W% N
  30.     data = _dac; /* PD1 PD0 = 00 正常模式 */
    ) i* p  y  G4 J! M' c
  31. 9 F' |2 h9 V6 Q$ J' F
  32.     if (_ch == 0)
    7 V" P/ p" E" `9 c" f
  33.     {
    3 w/ s$ G# L; k% N
  34.         DAC8501_SetCS1(0);0 W3 i8 X) Z0 h( ~& {
  35.     }- H: \- e2 s: W5 X+ ]0 j
  36.     else
      x: d  o: |% O0 h6 I: H: k; |% V9 k+ p
  37.     {
    % V: w2 J- o4 o% L! f
  38.         DAC8501_SetCS2(0);
    # d: t1 m; K8 _+ R3 V% z* B
  39.     }$ t; k$ _2 o" h9 J, Y

  40. 2 J; h2 }  }- _: y% `3 V6 R& G/ B
  41.     /* DAC8501 SCLK时钟高达30M,因此可以不延迟 */' x- d# m/ h' k3 t" o( G
  42.     g_spiLen = 0;+ v' I1 T7 V' I6 s
  43.     g_spiTxBuf[g_spiLen++] = (data >> 16);7 t( W& h) m' i0 |  }; N
  44.     g_spiTxBuf[g_spiLen++] = (data >> 8);& j# t4 L" K4 j: T7 t6 v
  45.     g_spiTxBuf[g_spiLen++] = (data);
    ! L% M2 T! P9 Y4 ]9 h( o+ T
  46.     bsp_spiTransfer();   
    + e/ c" _! ^& h& V& F8 ^/ k- A  h
  47. ) t8 ]# A3 u4 Q) `8 f
  48.     if (_ch == 0), _1 |/ m0 S9 e0 Y1 u1 ?
  49.     {
    5 [. B9 e, v/ D; a( T
  50.         DAC8501_SetCS1(1);, P3 n+ \6 p5 C; ^- \; x- o
  51.     }
    ) i  C  T: _& D; |
  52.     else
    : b, g! Z! b, a# {
  53.     {2 M, }$ T* @% R( I* U5 l2 j
  54.         DAC8501_SetCS2(1);
    ; R: Q1 @0 P/ R8 U  a3 ?, W
  55.     }
    ' J$ \8 `$ `0 {6 N4 r
  56. }
复制代码
* s3 J& \, J/ k9 h5 g/ h3 a
函数实现比较简单,每次更新发送24bit数据即可。% h+ f) N3 R+ x  f+ @
" `# r" S; u  I! t( E
75.6 DAC8501驱动设计(SPI DMA更新方式)
4 H( N2 w1 I4 l7 E6 ~% T/ g* K2 ODAC8501的DMA驱动方式略复杂,跟中断更新方式完全不同,要使用硬件SPI1 NSS引脚驱动DAC8501的片选,所有专门做了一个驱动文件来实现,程序驱动框架设计如下:
) f+ I; B  |# T& M* S4 U  X7 i. J. p& g
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

) u1 q, }/ a# _
- t4 ?1 X% q$ ]) |4 ^. R3 i; w2 n  ~有了这个框图,程序设计就比较好理解了。
7 Y8 ~0 a& T, L8 f
, `: v4 A% [  \" f" ]75.6.1 第1步:SPI总线配置# s! G* R- f) L, A- Y/ M% l
spi总线配置通过如下两个函数实现:0 Q& {" P6 A) K  i3 n, F
9 [0 U( ]# I/ S* w
  1. /*/ X* F/ m1 G' e$ r5 ?
  2. *********************************************************************************************************+ J7 F8 ~# N; B
  3. *    函 数 名: bsp_InitDAC8501+ B" R3 @4 @, @) a7 k- b, d
  4. *    功能说明: 配置GPIO并初始化DAC8501寄存器+ T$ A, ]3 _  b5 U6 ~) T" F
  5. *    形    参: 无) q8 ]; G  N) `+ g  [- U- G
  6. *    返 回 值: 无9 o, h1 [% Q  O5 |- b9 M' X  s6 {
  7. *********************************************************************************************************
    1 j7 z/ [) X- F) G; ]+ u
  8. */. Y& k4 M* A& z# `5 q/ S- B
  9. void bsp_InitDAC8501(void)
    ' w; ^- x, t% u/ g
  10. {' v# h1 b1 K6 t) k2 o' C
  11.     s_SpiDmaMode = 0;  " y5 [  E7 E1 r- V4 G) G6 }( I5 I
  12. ' x$ u3 k; L4 {5 d0 {- Q7 ?9 y
  13.     /*##-1- 配置SPI DMA ############################################################*/
    # m7 ]! b6 t' v2 v  i  X! E( u4 G
  14.     bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);
    $ l: z5 L6 F! T7 G0 u

  15. / ^& ~, ~1 O& J8 q  h: B- v0 r# x
  16.     /*##-2- 默认输出0V ############################################################*/  ~- I1 K) G8 r5 N- R
  17.     DAC8501_SetDacData(0, 0);    /* CH1输出0 */
    9 e! V0 h2 i  _) z, q0 j% |
  18. }
    / B/ w; r0 P- {# d* n
  19. . e2 R3 m9 h0 |$ i
  20. /*
    # E  K5 }2 }& l. Z2 p6 {" C3 J
  21. *********************************************************************************************************
    , v+ }2 \" }/ e+ M5 t% \
  22. *    函 数 名: bsp_InitSPIParam
    9 r3 I! x0 x2 g4 P! w
  23. *    功能说明: 配置SPI总线参数,时钟分频,时钟相位和时钟极性。$ @$ H: ]9 q) x8 B4 a
  24. *    形    参: _BaudRatePrescaler  SPI总线时钟分频设置,支持的参数如下:0 k) N, L  [7 Y
  25. *                                 SPI_BAUDRATEPRESCALER_2    2分频# D% K5 R, b: {5 g3 Z
  26. *                                 SPI_BAUDRATEPRESCALER_4    4分频
    * k9 ~+ o6 N* w: }* V
  27. *                                 SPI_BAUDRATEPRESCALER_8    8分频
    ) w% a+ ^' g4 e. l8 [
  28. *                                 SPI_BAUDRATEPRESCALER_16   16分频7 X; s% I+ R. b& q
  29. *                                 SPI_BAUDRATEPRESCALER_32   32分频3 k; V8 W% }1 x
  30. *                                 SPI_BAUDRATEPRESCALER_64   64分频
    ( x8 ^' k" y2 [$ o
  31. *                                 SPI_BAUDRATEPRESCALER_128  128分频5 I2 ^- ]* q$ A- j& O" T! ]
  32. *                                 SPI_BAUDRATEPRESCALER_256  256分频
    4 h& l& m, A1 Z+ O% f
  33. *                                                        
    ( |  }" b3 K" r/ f. [+ k+ S
  34. *             _CLKPhase           时钟相位,支持的参数如下:. R9 E0 V* @: T: B, T7 z, S
  35. *                                 SPI_PHASE_1EDGE     SCK引脚的第1个边沿捕获传输的第1个数据
    + y/ w# D- G  H" ^. b, Z5 @$ v) `
  36. *                                 SPI_PHASE_2EDGE     SCK引脚的第2个边沿捕获传输的第1个数据/ W+ L4 w# n/ ~5 K! M2 k9 M9 R, J
  37. *                                 
    9 {0 ~. x1 H# y( A: B! O
  38. *             _CLKPolarity        时钟极性,支持的参数如下:- @1 @! X" F% ?& c. Q7 p2 y% S
  39. *                                 SPI_POLARITY_LOW    SCK引脚在空闲状态处于低电平
    # C( S  D; W6 u0 K1 u, Q8 _
  40. *                                 SPI_POLARITY_HIGH   SCK引脚在空闲状态处于高电平
    / n* k# B' P3 z: K* Y/ E! {
  41. *
    * L0 y2 H6 f, Z& ?4 X
  42. *    返 回 值: 无
    6 ]! t1 d; p2 b, K* j& v
  43. *********************************************************************************************************
    & e; |0 L8 F* u# ~# d
  44. */: r8 e& x6 \& u- @
  45. void bsp_InitSPIParam(uint32_t _BaudRatePrescaler, uint32_t _CLKPhase, uint32_t _CLKPolarity)/ ^  J3 X4 z9 p  ^
  46. {- ]$ a2 Q. N! _& V
  47. : s3 G9 a4 Z# h( t
  48.     /* 设置SPI参数 */+ C) m# h( @9 a/ P! N! y
  49.     hspi.Instance               = SPIx;                           /* 例化SPI */) H* t8 c0 M7 C. A3 o3 B0 r
  50.     hspi.Init.BaudRatePrescaler = _BaudRatePrescaler;             /* 设置波特率 */+ E4 A0 F* d+ B: z# n3 G
  51.     hspi.Init.Direction         = SPI_DIRECTION_2LINES_TXONLY;  /* 全双工 */" p2 }9 C) k( \  D( l
  52.     hspi.Init.CLKPhase          = _CLKPhase;                     /* 配置时钟相位 */9 m8 R. c4 M. @6 h6 Z
  53.     hspi.Init.CLKPolarity       = _CLKPolarity;                   /* 配置时钟极性 */
    8 f+ B% i0 ?: @! J* R( P
  54.     hspi.Init.DataSize          = SPI_DATASIZE_24BIT;               /* 设置数据宽度 */
    % S0 _$ A; S, V7 W- |) v( ^! L/ ?
  55.     hspi.Init.FirstBit          = SPI_FIRSTBIT_MSB;             /* 数据传输先传高位 */3 o! t# [/ V# e$ S
  56.     hspi.Init.TIMode            = SPI_TIMODE_DISABLE;             /* 禁止TI模式  */; \  @+ q" Q& |/ j5 [4 }! S
  57.     hspi.Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE;     /* 禁止CRC */" E' P0 F1 G$ Y: f
  58.     hspi.Init.CRCPolynomial     = 7;                               /* 禁止CRC后,此位无效 */' I8 G; a( _. P. e+ Z
  59.     hspi.Init.CRCLength         = SPI_CRC_LENGTH_8BIT;             /* 禁止CRC后,此位无效 */: g1 S7 L1 Z. X0 n3 P- _/ v/ F" _
  60.     hspi.Init.FifoThreshold     = SPI_FIFO_THRESHOLD_05DATA;    /* 设置FIFO大小是一个数据项 */: G+ e. E5 x) k/ [6 a" R/ u, e# N

  61. % Q) U8 v  t( A* m
  62.     hspi.Init.NSS         = SPI_NSS_HARD_OUTPUT;                 /* 使用软件方式管理片选引脚 */
    ( m& W: m- D. A
  63.     hspi.Init.NSSPMode    = SPI_NSS_PULSE_ENABLE;                /* 使能脉冲输出 */* g- x* x" Y2 ?
  64.     hspi.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;               /* 低电平有效 */
    6 p2 ]. i  F- b# L8 @

  65. 1 d8 Y7 |3 ~6 y; b2 I$ W1 d
  66. /* MSS, 插入到NSS有效边沿和第一个数据开始之间的额外延迟,单位SPI时钟周期个数 */# C. V0 h' P. F" F: V4 v5 J; a, z
  67.     hspi.Init.MasterSSIdleness        = SPI_MASTER_SS_IDLENESS_00CYCLE;   - ~6 @3 P- _8 e7 I( a
  68. # t, X2 V: h- U4 e3 c+ R
  69. /* MIDI, 两个连续数据帧之间插入的最小时间延迟,单位SPI时钟周期个数 */5 n  w+ E7 D+ @% I, s4 @  b
  70.     hspi.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_02CYCLE;
    6 F- b. S! z4 a6 T' b. X
  71. ' G0 x( m& r$ I
  72.     hspi.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE; /* 禁止SPI后,SPI相关引脚保持当前状态 */  1 q1 ?5 t& Q$ Y& v7 U' G
  73.     hspi.Init.Mode            = SPI_MODE_MASTER;                    /* SPI工作在主控模式 */" o7 G7 e" [( V/ H5 E& L
  74. 6 s; w! F9 ^- t  [6 o
  75.     /* 复位配置 */
    - d0 F+ H5 x% d
  76.     if (HAL_SPI_DeInit(&hspi) != HAL_OK)
    : \: A9 L4 n# C: g, q/ w
  77.     {
    # [; g) b6 n% l: F6 G, _
  78.         Error_Handler(__FILE__, __LINE__);
    2 ~( i: L& E0 h) y
  79.     }    + ~. M  h+ o/ o# j3 ~
  80.   s# e) n0 e% t# H9 }/ o# J3 |
  81.     /* 初始化配置 */- g! u9 g/ Z  C
  82.     if (HAL_SPI_Init(&hspi) != HAL_OK)
    7 {3 b) d$ O1 Z# S: t3 e
  83.     {
    4 B! F/ r( d3 g! Z4 L
  84.         Error_Handler(__FILE__, __LINE__);+ y0 U2 }# b0 _1 v* F
  85.     }    % F1 K9 O7 I; y1 p% S1 Q
  86. }
复制代码
1 n5 ^& x( s: R! ?: E! T6 z8 d

! X1 r7 M& x0 u- X9 m/ Z- h9 H3 [% X" N8 l5 a
这两个配置函数里面最重要的是置红的几个配置选项,这里依次为大家做个说明:
- Z/ W9 N% m+ W; J- U8 v+ y! q; V+ l3 ]  q' Q
  SPI_DIRECTION_2LINES_TXONLY* w# j4 c4 z" U8 t  |3 \
驱动DAC856X仅需要SPI写操作。
# r/ `2 l2 U' i: G& x5 t/ o
# ]4 o, u8 r% ^  SPI_DATASIZE_24BIT# U& [6 m% d# w/ `! R
STM32H7的SPI支持4-32bit数据传输,由于DAC856X需要24bit数据,所以这里配置为24即可。
6 f$ g4 T3 R$ L5 T: p$ n+ _0 h1 L7 s7 J6 M4 o0 q4 h( r
  SPI_FIFO_THRESHOLD_05DATA4 d4 }* J+ T/ M* D9 p
对于SPI1来说,里面的FIFO大小是16字节,那么SPI数据传输配置为24bit的话,FIFO最多可以存储5个24bit,因此这个fifo阀值要设置为5。
! Y  a/ Y" n% [$ K. i9 ^! N9 A) ^2 v
4 h6 L- R. V2 s& Z; A; O  SPI_NSS_HARD_OUTPUT) X) [: T: Q/ g6 S5 o# p& N  {
我们这里要使用SPI的硬件片选引脚SPI_NSS。8 Y& [* ^( k( j: s  B
7 g0 Z9 x0 S/ d0 ~3 Z# ^
  SPI_MASTER_SS_IDLENESS_00CYCLE2 C* O" O3 j6 ^9 b# o' D  s
插入到NSS有效边沿和第一个数据开始之间的额外延迟,单位SPI时钟周期个数。7 i3 _- z+ g4 C
3 K1 ]1 \8 v1 i* |7 k# C
根据本章75.4.4小节里面的t(4)要求,片选有效到SCLK第1个下降沿信号的时间,最小值为0。所以这里配置为0即可,也就是无需插入时间。
3 Z' J2 T7 ]" [9 O: G( J7 Q, z6 f3 v7 v$ i
  SPI_MASTER_INTERDATA_IDLENESS_10CYCLE
: b! g# h: P, b( ~1 G* Q3 Q% {两个连续数据帧之间插入的最小时间延迟,单位SPI时钟周期个数。
2 z2 `. m) G( M$ M3 ?1 D5 Y1 j1 i
根据本章75.4.4小节里面的t(5)要求,每传输24bit数据后,片选要保持一段时间的高电平,DAC856X要求至少要33ns(供电3.6到5.5V时),也是说,如果我们以25MHz驱动DAC856X,这里至少要配置为1个时钟周期,推荐值为2及其以上即可,我们这里直接配置为2个时钟周期(配置为1也没问题的)。: p. w4 I6 ]; v3 v& |

5 H2 `, P/ i. H75.6.2 第2步:TIM12周期性触发配置
& l8 Y0 U' o& n0 m+ T+ O5 O/ v这里特别注意一点,定时器触发一次,就会让SPI以DMA方式传输24bit输出。; ~" t+ ~) q) D2 ^' s

0 ~5 T+ [# V/ p" q# g" l8 H2 nTIM12的触发配置如下:( _; C. ~% Z; A' s; `

# V. h5 K6 W3 ^# [
  1. /*
    ! M8 K2 \1 d; Q5 C- _) k2 L9 X0 P
  2. *********************************************************************************************************
    5 p% J) ^. g6 w, |$ b
  3. *    函 数 名: TIM12_Config
    # x7 f1 ], _& I" ~' t* a1 Y
  4. *    功能说明: 配置TIM12,用于触发DMAMUX的请求发生器
    3 q4 K( ?" Q2 r. s6 t$ t! d5 O( Y
  5. *    形    参: _ulFreq  触发频率,推荐范围100Hz - 1MHz                              
    3 w! L1 h" [( r
  6. *    返 回 值: 无7 V! ?# u( ^3 |# n
  7. *********************************************************************************************************
    ! n: }0 P, D  w) J, i7 S
  8. */   
    0 X. {, ^  m$ z
  9. TIM_HandleTypeDef  htim ={0};1 m% D$ J! J7 s1 x% {) r
  10. TIM_MasterConfigTypeDef sMasterConfig = {0};
    ( `, \9 `2 w8 K9 T; R* y: R+ f7 `
  11. TIM_OC_InitTypeDef sConfig = {0};
    . D1 ?' c4 n  N# K: c
  12. void TIM12_Config(uint32_t _ulFreq)
    9 `# J, o- I8 C5 r: V' O
  13. {: N5 P* K6 u1 S
  14.     uint16_t usPeriod;0 P. f1 {7 H2 ~1 q" @2 c; n9 y
  15.     uint16_t usPrescaler;
    ; o, k6 F0 {$ F
  16.     uint32_t uiTIMxCLK;! Q  U5 B3 {% w4 ^" c5 n0 j9 ~
  17. ( A% Q/ D  L' x

  18. 4 P% M- }5 K+ x% G
  19.       /* 使能时钟 */  & k! u# `5 ]' d& I/ q
  20.       __HAL_RCC_TIM12_CLK_ENABLE();4 v/ P9 ]2 w8 k  g- C' J0 v
  21. ) y! _- h3 t+ R9 F
  22.     /*-----------------------------------------------------------------------
    & D' ^3 b' c- L! r# G+ Z
  23.         bsp.c 文件中 void SystemClock_Config(void) 函数对时钟的配置如下: - B  S! i( N# y. u8 L! N: h
  24. + t  t/ ]1 p" o; `% d* N
  25.         System Clock source       = PLL (HSE)1 T5 C/ J2 H: k# O1 ?3 s9 ]
  26.         SYSCLK(Hz)                = 400000000 (CPU Clock)' T1 c( J* J+ g/ ?8 p; [: d3 D
  27.         HCLK(Hz)                  = 200000000 (AXI and AHBs Clock)" U2 V; g2 d: i: D8 Q, F
  28.         AHB Prescaler             = 2
    & j& F1 d3 E8 S3 Q* W5 W
  29.         D1 APB3 Prescaler         = 2 (APB3 Clock  100MHz); b, \* r+ D: \: N* N! H
  30.         D2 APB1 Prescaler         = 2 (APB1 Clock  100MHz): T* l* H( Y7 V  b/ |
  31.         D2 APB2 Prescaler         = 2 (APB2 Clock  100MHz)
    ! F. Z) y; C2 N4 i+ n
  32.         D3 APB4 Prescaler         = 2 (APB4 Clock  100MHz)
    # L. m2 j, c- i- d

  33. % ^+ q3 k  R+ V) H! P3 i
  34.         因为APB1 prescaler != 1, 所以 APB1上的TIMxCLK = APB1 x 2 = 200MHz; 不含这个总线下的LPTIM1" r& O. C" h* X& t
  35.         因为APB2 prescaler != 1, 所以 APB2上的TIMxCLK = APB2 x 2 = 200MHz;
    9 f, A: G2 |! V- a$ W: C: H
  36.         APB4上面的TIMxCLK没有分频,所以就是100MHz;& i  q  W$ N& Y; `& R

  37. , J0 `. T: ]' r
  38.         APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14,LPTIM1" ~1 Z% ]% P$ U- s# Z
  39.         APB2 定时器有 TIM1, TIM8 , TIM15, TIM16,TIM17! u9 m  ~, S- Y( V

  40. - X" B" b  Z. o  T; y+ l, r
  41.         APB4 定时器有 LPTIM2,LPTIM3,LPTIM4,LPTIM5
    * F9 i: ?9 \) ]/ p
  42.     ----------------------------------------------------------------------- */
    " K- [3 z2 v, z% @3 ^. L$ T8 S
  43.     uiTIMxCLK = SystemCoreClock / 2;
    - d& Z, D, c: ^( L, E  E) l

  44. " o6 R" V$ N; D! F& O  b
  45.     if (_ulFreq < 100)9 L& j9 x" p. h' l9 D
  46.     {0 ]! |+ u9 b7 m0 R1 r/ z: m8 i
  47.         usPrescaler = 10000 - 1;                    /* 分频比 = 10000 */
    4 x5 r4 E- W3 ~& h9 i
  48.         usPeriod =  (uiTIMxCLK / 10000) / _ulFreq  - 1; /* 自动重装的值 */
    : y' m( i, [; i7 r$ G. I
  49.     }
    $ X2 @3 g" S5 L3 j8 B. G$ D
  50.     else if (_ulFreq < 3000)
    & f( V# F# V& C) \# K" B
  51.     {9 R! B# G/ i0 v' C' F
  52.         usPrescaler = 100 - 1;                    /* 分频比 = 100 */
    0 Y% ^; i$ d1 F  g3 j. T, K. N
  53.         usPeriod =  (uiTIMxCLK / 100) / _ulFreq  - 1;/* 自动重装的值 */: A' W+ |3 m8 b
  54.     }
    3 }# R: g$ b6 `! D7 d$ y9 K2 @# B: ~
  55.     else    /* 大于4K的频率,无需分频 */6 T" N4 u% E+ z
  56.     {2 M$ l, b' a) `) p& U$ [+ D) n! g
  57.         usPrescaler = 0;                    /* 分频比 = 1 */) ]7 {$ X6 e5 z  q+ h! G. h7 m
  58.         usPeriod = uiTIMxCLK / _ulFreq - 1;    /* 自动重装的值 */; u7 X6 ~5 M4 A  x7 q1 a
  59.     }* ~  ]. ]4 ?. u1 i9 c! m: w

  60. 7 k# f1 D6 K2 @( P' V
  61.     htim.Instance = TIM12;
    & l% O% ?$ j- |. T# s' l3 M
  62.     htim.Init.Period            = usPeriod;3 N" K3 p( }8 H5 A' c$ P
  63.     htim.Init.Prescaler         = usPrescaler;
    * h) o( _3 R# h' h! |# f( G) i
  64.     htim.Init.ClockDivision     = 0;
    ) f( N7 Q5 X( `7 `: W
  65.     htim.Init.CounterMode       = TIM_COUNTERMODE_UP;" n9 T+ {5 H4 O" \
  66.     htim.Init.RepetitionCounter = 0;9 z2 Q! X0 A6 }
  67. & h4 f' R( L6 y
  68.     if(HAL_TIM_Base_DeInit(&htim) != HAL_OK)6 H7 J9 c& p, [; J
  69.     {
    9 [! z! e: u8 f# |! s9 X) b# J
  70.         Error_Handler(__FILE__, __LINE__);          j' F) U' b5 P( n. x/ |& [
  71.     }9 S2 x3 ^- r6 d

  72. , w9 {* g8 B& b1 A5 R. H: I
  73.     if(HAL_TIM_Base_Init(&htim) != HAL_OK)
    1 b6 W. ^* Y* M. V) x; a3 ^7 O- z
  74.     {
      s9 t! j0 R+ }: c, l! z9 L
  75.         Error_Handler(__FILE__, __LINE__);        ; `  y) a2 X8 J* w
  76.     }* K6 F7 @9 A1 A! K6 Y6 k" D

  77. + X8 Y) s4 R2 x- c5 M* J& r
  78.     sConfig.OCMode     = TIM_OCMODE_PWM1;9 |: n  ?" r1 s# Y# w( o6 h
  79.     sConfig.OCPolarity = TIM_OCPOLARITY_LOW;8 k* x# m" @9 a' g7 H# s
  80.     sConfig.Pulse = usPeriod / 2;     /* 占空比50% */; s  [/ s1 g' v2 R) l* e
  81.     if(HAL_TIM_OC_ConfigChannel(&htim, &sConfig, TIM_CHANNEL_1) != HAL_OK)# ^* s8 G; g  T4 j' B' J
  82.     {; ^; g( K- V- }; ~  K  u! D' `
  83.         Error_Handler(__FILE__, __LINE__);
    7 _( z$ P: I  j5 ^% R) m8 Y& e2 Y
  84.     }9 \/ I- C7 Q+ L* T
  85. 0 E' X& y! e4 C: Y; Z0 ^
  86.     /* 启动OC1 */6 j& g" A. t0 ~; W" J
  87.     if(HAL_TIM_OC_Start(&htim, TIM_CHANNEL_1) != HAL_OK)
    - `/ `0 t- R$ R
  88.     {  B( s3 T7 a' k8 `% i
  89.         Error_Handler(__FILE__, __LINE__);5 x: E* G. G, O6 C9 ]- ~' w6 M
  90.     }
    ' j0 c$ ]1 f* S4 x4 A

  91. & O0 O, z/ ]# z6 b
  92.     /* TIM12的TRGO用于触发DMAMUX的请求发生器 */
    % c9 o8 N5 q& f- z, m* [; w
  93.     sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1REF;
    - v- M& P" S6 [2 q3 K4 T+ B
  94.     sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
    6 v! g2 C- G9 @. H: m% D
  95.     sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    ( R  b8 Y" U+ x8 ~* G

  96. 7 d( O4 T4 |" V; k/ l
  97.     HAL_TIMEx_MasterConfigSynchronization(&htim, &sMasterConfig);
    3 h8 S) v2 T/ }
  98. }* t, M- z1 r! M, l
  99. #endif
    5 C+ w: K( I7 R% {- E) Q1 @
复制代码

! z! K! G/ d. z4 _4 z( h( A: F. j+ q7 q6 [+ H0 L* f. h
这个函数支持的触发频率很宽,对于DAC856X来说,如果样本点设置为100个的话,此函数推荐的触发频率是100Hz到1MHz,具体可以支持到最高触发速度计算看本章4.7.7小节即可。- b' B! s) D; s; r) W
5 e$ K0 F  o) \" x* }! X, Z5 X2 X
75.6.3 第3步:DMAMUX同步触发SPI DMA传输4 w2 ?5 D! }1 l7 v
DMA和DMAMUX的配置如下:
' H5 F& W2 u3 j) D$ I8 W8 d/ l3 z# Z) E- L$ J0 d9 p
  1. /*
    0 ~. b) x, G/ M- ]7 k
  2. *********************************************************************************************************
    8 @3 c2 v. Z6 s. f/ G$ Z$ S
  3. *    函 数 名: bsp_spiDamStart6 A6 Q7 e$ J8 R1 C( W1 W# h) `8 y
  4. *    功能说明: 启动SPI DMA传输4 g. q; r" w0 u! `& k
  5. *    形    参: _ulFreq 范围推荐100Hz-1MHz
    , u- x( m, e. P* |7 q0 e
  6. *    返 回 值: 无" y; w- o2 [  G+ c
  7. *********************************************************************************************************2 P( [% u' G( k9 o+ J) Y
  8. */
    % g( X4 b: X$ G+ `9 A
  9. void bsp_spiDamStart(uint32_t _ulFreq)  h" G1 r$ l8 e9 G( s# d
  10. {
    6 o+ f% a. U- T3 ?
  11.     /* 设置模式,要切换到DMA CIRCULAR模式 */% ?7 [& ~" a: N2 ~' `  j0 g
  12.     s_SpiDmaMode = 1;) X, E9 Z+ y( j. y7 h  |
  13. % @. v  o0 T% K6 w% d+ D* m
  14.     bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_4, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);
    ' C  ~9 e% \. D9 l9 s; R* [

  15. % r1 J0 f8 J: K6 q3 W/ k# G' l, x
  16.     /* 使能DMA时钟 */
    3 B4 I# [7 f8 D$ [0 H% V
  17.     DMAx_CLK_ENABLE();      % ^) S+ q+ |/ {5 H. z
  18. 3 V1 h& }0 Q/ x0 m6 P1 `
  19.     /* SPI DMA发送配置 */        
    9 n6 L3 @; b) Q) `% }* F5 u
  20.     hdma_tx.Instance                 = SPIx_TX_DMA_STREAM;      /* 例化使用的DMA数据流 */. Z# S5 D8 D5 B2 }3 M
  21.     hdma_tx.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;     /* 使能FIFO */
    ; s7 z) a: S. c; M
  22.     hdma_tx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL; /* 用于设置阀值 */3 ~7 ^4 T% ]$ z
  23.     hdma_tx.Init.MemBurst            = DMA_MBURST_SINGLE;        /* 用于存储器突发 */: f% \6 \) }) b  n" s4 M
  24.     hdma_tx.Init.PeriphBurst         = DMA_PBURST_SINGLE;        /* 用于外设突发 */2 C5 |: u8 I" g7 h
  25.     hdma_tx.Init.Request             = SPIx_TX_DMA_REQUEST;     /* 请求类型 */  
    ) e+ k' w5 _& g6 n
  26.     hdma_tx.Init.Direction           = DMA_MEMORY_TO_PERIPH;    /* 传输方向是从存储器到外设 */  
    : v5 o0 Y* w# K$ Q
  27.     hdma_tx.Init.PeriphInc           = DMA_PINC_DISABLE;        /* 外设地址自增禁止 */
    % ?8 W* A* f9 p& N% M$ G* |1 Z) V
  28.     hdma_tx.Init.MemInc              = DMA_MINC_ENABLE;         /* 存储器地址自增使能 */  - S9 Y: V. j8 W4 O2 R# D
  29.     hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;     /* 外设数据传输位宽选择字节,即8bit */
    6 k6 Q1 |$ A( y' n
  30.     hdma_tx.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;     /* 存储器数据传输位宽选择字节,即8bit */    5 i, _6 L( w1 b! e
  31.     hdma_tx.Init.Mode                = DMA_CIRCULAR;            /* 正常模式 */3 F. _9 c& b  Z1 d
  32.     hdma_tx.Init.Priority            = DMA_PRIORITY_LOW;        /* 优先级低 */
    3 |# u/ G" f& N% j

  33. 2 w& C5 B$ J9 R, `2 [
  34.      /* 复位DMA */
    % H' j+ E: X7 `5 c( h; W) d
  35.     if(HAL_DMA_DeInit(&hdma_tx) != HAL_OK)$ ~: p" c- m( S% t# S1 L: e
  36.     {
    : y" J9 d# _% G' X2 ?% E) {, X5 v, h
  37.         Error_Handler(__FILE__, __LINE__);     
    % Y' M# f+ \* i4 M) {2 P& q
  38.     }
    1 M+ A; o) F. g  g# {" ?
  39. 9 K& }0 K& @. w; c1 L" ~: N7 ^8 h9 g; O9 I
  40.      /* 初始化DMA */2 _, D3 \6 n* x  A4 R' ?
  41.     if(HAL_DMA_Init(&hdma_tx) != HAL_OK): q  U1 n+ n: n- H3 b7 Y4 e$ Q7 @
  42.     {: r/ g. Z7 \7 ]0 z3 g: o% W4 P
  43.         Error_Handler(__FILE__, __LINE__);     
    5 R: ?" J9 \  Q5 k" P  h
  44.     }
    ( A' _2 c# X1 k/ M4 g$ r
  45. % `3 C& I4 F8 i
  46.     /* 关联DMA句柄到SPI */- k8 R: g9 ]" {+ k, E
  47.     __HAL_LINKDMA(&hspi, hdmatx, hdma_tx);    $ K1 o$ k- X) E1 x/ l2 T) f8 X

  48. + h6 p, s4 j- p% {7 q( t
  49.   D2 X; e" r; {; p
  50.     /* 关闭DMA发送中断 */6 G+ V) Y" m0 `' W5 N
  51.     HAL_NVIC_SetPriority(SPIx_DMA_TX_IRQn, 1, 0);; n' Y) n! |- C) R5 r" t3 L
  52.     HAL_NVIC_DisableIRQ(SPIx_DMA_TX_IRQn);, y( g6 y: Y2 ~' a6 ~+ d& j: [/ k

  53. 2 [  Z' i2 @% }7 Q" c8 H1 s
  54.     /* 关闭SPI中断 */" F, H# k+ F  k) R
  55.     HAL_NVIC_SetPriority(SPIx_IRQn, 1, 0);2 ~( \& B4 i* F6 a
  56.     HAL_NVIC_DisableIRQ(SPIx_IRQn);. D9 {( A: e; S3 y; z& J
  57. , e& I! Q" X" x3 J
  58.     /* 同步触发配置 */9 L6 b2 S  v4 L6 s
  59.     dmamux_syncParams.EventEnable   = ENABLE;                             3 p1 e+ a, i1 X/ P$ F& Z' m
  60.     dmamux_syncParams.SyncPolarity  = HAL_DMAMUX_SYNC_RISING;          " F5 o$ A" g4 H7 ^2 B- }- n, k
  61.     dmamux_syncParams.RequestNumber = 1;                  
    2 a1 d: s$ }6 c
  62.     dmamux_syncParams.SyncSignalID  = HAL_DMAMUX1_SYNC_TIM12_TRGO; 4 x" G/ O% f) U/ g! X. [$ T
  63.     dmamux_syncParams.SyncEnable    = ENABLE;    3 s4 U6 V8 n* r) A, I+ u
  64. , N% M' k+ b) J0 q9 y1 G
  65.     HAL_DMAEx_ConfigMuxSync(&hdma_tx, &dmamux_syncParams);
    ' N% R3 M. ^4 _' R/ r, i3 a: P2 s" e$ C

  66. + Y( I; Y3 R# @+ K4 D
  67.     //LPTIM_Config(_ulFreq);+ E* \, C, K9 S% P5 P6 V- t/ p

  68. 5 N+ R# E2 ?; e6 J* M
  69.     TIM12_Config(_ulFreq);" }8 H8 M4 q2 C- {
  70. 1 l1 q! q" H6 O" c
  71.     /* 启动DMA传输 */
    ) |( Z0 |9 s8 d" [. O& X( G* q
  72.     if(HAL_SPI_Transmit_DMA(&hspi, (uint8_t*)g_spiTxBuf, g_spiLen/4)!= HAL_OK)    ! O( o, H; W# p$ h% u* t
  73.     {
    , P( ^1 y" Y9 @4 I9 A
  74.         Error_Handler(__FILE__, __LINE__);. ^, b( |" b1 C0 @5 a& ]! R+ B& x; [
  75.     }+ p/ e- \. n; O4 N7 J6 s
  76. }
    ' U0 X) p3 {+ Z; J  ~
复制代码

4 g9 P0 R$ h2 \1 ^( y, X1 p5 T% X1 t- _3 P5 {9 k3 ]) X
这段程序里面最关键的就是置红的部分。作用是配置DMAMUX的同步触发功能,触发周期由TIM12控制。8 k0 i) }* c6 l% i" R; L
' B5 }+ T7 G# F( A4 I/ T6 K, ^
75.6.4 第4步:24bit数据的DMA传输解决办法
& f& J$ Q4 }0 y$ ]* Q5 P由于通用DMA1和DMA2仅支持8bit,16bit和32bit数据传输,我们这里要传输24bit数据,解决的关键就是配置DMA为传输宽度为32bit,并将传输的数据由24bit再补一个8bit的任意值组成32bit即可,实际的传输会由SPI完成。; m2 L" b& F* a
& @; {% j6 W$ [) N
  1. /*' Y* t) A. @- ~  P
  2. *********************************************************************************************************
    % G8 M3 N& N6 r
  3. *    函 数 名: DAC8501_SetDacDataDMA
    3 P/ O% p3 s; Y+ x8 k5 J
  4. *    功能说明: DAC8501数据发送,DMA方式
    4 \! j/ w; W# n: |" K$ A
  5. *    形    参: _ch         1表示通道1输出* F+ l3 h7 Q: {/ i, C" g+ i
  6. *             _pbufch1    通道1数据缓冲地址
    , x2 j5 s" L* N
  7. *             _sizech1    通道1数据大小1 @+ h+ J* S& Z5 t( _3 A( e2 `/ q
  8. *             _ulFreq     触发频率,推荐范围100Hz- 1MHz,注意这个参数是触发频率,并不是波形周期。7 a5 i' V! @' R* }9 p8 T! @
  9. *                         这里触发一次,SPI DMA传输一次24bit数据。
    2 P% h2 _: ]# B+ D+ @+ h8 _
  10. *    返 回 值: 无
    , R6 D) F. C9 \2 f8 T- {0 d% m
  11. *********************************************************************************************************8 z: b& ]/ Z% @' Y
  12. */2 b3 ?) l6 U3 G0 u
  13. void DAC8501_SetDacDataDMA(uint8_t _ch, uint16_t *_pbufch1, uint32_t _sizech1, uint32_t _ulFreq)7 k% M7 J( ~, U8 l. P
  14. {8 ?9 A. {; P& X+ E7 E
  15.     uint32_t i;) C- m: C7 g! y4 \9 p
  16.     uint32_t _cmd;
    * K% c4 I2 q" D% ]
  17. + P( q$ _) y- y: X& K3 f3 M
  18.     g_spiLen = 0;- C- {% V/ {/ f- u
  19. 5 [2 D5 h1 b! f2 W) U
  20.     switch (_ch)
    9 E" n( ]/ m! t* m  c
  21.     {
    & h4 G1 f. C9 K; v/ a( s9 n. G
  22. 6 c" T# p; G% m8 U
  23.         /*. ?& f- l) T" P' x) A% ~; ?' v' Y
  24.             DAC8501.pdf page 12 有24bit定义. ^3 }& \( m% f$ X& l* z& F& P' L( `

  25. 2 |+ `( v% X7 Q4 H
  26.             DB24:18 = xxxxx 保留
    ' w, E3 L& }0 Y! a& O. [3 J
  27.             DB17: PD11 H1 n4 v! R0 R% F
  28.             DB16: PD0, }" s% r/ y9 k, g- F
  29.   J4 ^& z& M; l- n  N) E
  30.             DB15:0  16位数据. J6 P' e- V1 u' t3 O7 d3 I2 V
  31. 1 u5 g) F' c5 p
  32.             其中 PD1 PD0 决定4种工作模式8 f" _4 F6 }- U3 B7 {; F
  33.                   0   0  ---> 正常工作模式" h3 U; x4 C7 M( O  c! q# P/ p
  34.                   0   1  ---> 输出接1K欧到GND
    " E* W( B% T# K3 q  G- Z
  35.                   1   0  ---> 输出100K欧到GND1 b/ W1 F) g6 c( k: n5 _0 N3 \% r
  36.                   1   1  ---> 输出高阻5 s) w  C; ]  ?" Z& `
  37.         */+ V* S0 E9 J% z) C2 h& W9 q3 t

  38. ; c3 q* e! Z& f; m$ E4 M* E7 o
  39.         /* 通道1数据发送 */2 @6 F8 K/ v6 x
  40.         case 1:
    $ C( N$ v) W- \0 N) a) T4 L% `% l4 L+ k
  41.             for(i = 0; i < _sizech1; i++)
    7 f9 i) ]( K$ ?# N6 _7 m; b* m" T
  42.             {/ a* ~6 J& G/ D6 Y
  43.                 /* 更新需要配置PD1和PD0,当前是选择的正常工作模式 */
    & @1 x+ d9 Z4 n$ r( S/ I
  44.                 _cmd = (0 << 16) | (_pbufch1 << 0);
    . N0 e2 l1 z* f
  45. 5 I4 v2 |- X( P% a$ X( t0 D
  46.                 g_spiTxBuf[g_spiLen++] = (uint8_t)(_cmd);: G( H, n$ N' J2 ]
  47.                 g_spiTxBuf[g_spiLen++] = (uint8_t)(_cmd >> 8);- F0 C3 S" n* `  x  `2 i
  48.                 g_spiTxBuf[g_spiLen++] = (uint8_t)(_cmd >> 16);& u( D5 Q' o1 T8 i9 ?/ a
  49.                 g_spiTxBuf[g_spiLen++] = 0;
    ; C" S4 x# W+ R4 q
  50.             }, j6 e. n8 X+ v) \* V) f, U
  51.             break;
    ! g, Q( c- b% u$ R6 V

  52. + d( p$ D7 C+ O% }: D! g: m
  53.         default:# s% T) q3 S+ p3 g- U1 G& T
  54.             break;- h- R+ g% v# F: I. g* b0 {
  55. # G2 v* y9 ^1 o6 a; O3 |
  56.     }
    5 d( _+ Q+ K1 A4 B
  57. - m6 I2 c, K* Y$ S, [. `
  58.     bsp_spiDamStart(_ulFreq);( r1 v' p8 }% ~3 ]+ A
  59. }
复制代码
: _3 r& M  f; Z  e- {7 [8 k
75.6.5 第5步:DMA缓冲区的MPU配置! p  M8 Y2 A/ b/ v+ \. q, |+ B
因为工程是用的DTCM做的主RAM空间,这个空间无法使用通用DMA1和DMA2,通过本手册第26章的内存块超方便使用方式,将DMA缓冲定义到SRAM4上:( ]+ Z; S' [! s& ~7 k: j
( z; _: s; A6 i$ v1 Z! z+ {" l
  1. #if defined ( __CC_ARM )    /* MDK *******/7 k" C# d; @  f0 Q! k7 \  V
  2.     __attribute__((section (".RAM_D3"))) uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   
    5 R+ l) Q& o& e& r$ J% S
  3.     __attribute__((section (".RAM_D3"))) uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];  n$ X" w/ {9 `
  4. #elif defined (__ICCARM__)  /* IAR ********/: u: M  i$ k: n" J
  5.     #pragma location = ".RAM_D3", }. b/ _; K; O, z
  6.     uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   8 ?+ M. L9 w. Z4 u2 f3 K" k
  7.     #pragma location = ".RAM_D3"% `9 `1 M8 H' e- I$ Q5 Z
  8.     uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];3 n1 @+ P7 h6 i8 z8 |/ Z; }. h
  9. #endif
复制代码

% i# N5 t1 x2 f由于程序里面开启了数据Cache,会造成DMA和CPU访问SRAM4数据不一致的问题,特此将SRAM4空间关闭Cache。
- ^- n/ ^2 H: m7 J* I0 Q+ ?9 l    /* 配置SRAM4的MPU属性为Non-cacheable */
  [. G$ [; L# U9 D3 N6 z( X  m
  1.   MPU_InitStruct.Enable           = MPU_REGION_ENABLE;3 k/ K0 U) p. D& p5 s
  2.     MPU_InitStruct.BaseAddress      = 0x38000000;( ^1 k) M8 W5 {# o. d# |' h
  3.     MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;
    8 _5 H; D) I( ]+ `
  4.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    + ]. \# g+ M1 i
  5.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;* U2 T8 U# m: ]3 K4 \; e
  6.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;, f$ Z; x( ^3 m3 o4 |, E
  7.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    " |$ D; U9 D- u! H* e9 `
  8.     MPU_InitStruct.Number           = MPU_REGION_NUMBER2;( T; [2 P7 c- d5 Z+ J1 s
  9.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;) N+ K6 X0 |+ ~5 H4 N
  10.     MPU_InitStruct.SubRegionDisable = 0x00;
    , T6 [" P  T# Z% P4 q9 l% `
  11.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;  v; W0 h& ]  ^1 N0 p: T- T3 A8 J
  12. " i& n& L, G& A+ u
  13.     HAL_MPU_ConfigRegion(&MPU_InitStruct)
复制代码
由于程序里面开启了数据Cache,会造成DMA和CPU访问SRAM4数据不一致的问题,特此将SRAM4空间关闭Cache。( e: ]: T' S8 e8 \

  A8 t8 N: O* o75.6.6 第6步:DAC8501的时钟极性和时钟相位配置
1 s( \$ w( c) p注:与本章74.5.3小节内容是一样的。  t6 d) E% m: v4 a0 L
. b# A& ]5 R6 T' I4 E! H
首先回忆下STM32H7支持的4种时序配置。
4 |9 u3 G& |/ }" D. R/ U( C
& O4 v1 ~; T7 Q7 S  当CPOL = 1, CPHA = 1时
9 ^: [' Z" y  F$ nSCK引脚在空闲状态处于低电平,SCK引脚的第2个边沿捕获传输的第1个数据。' w4 u' I$ `4 \# I+ T
7 w( S& M7 w! _$ d) [7 m8 V
  当CPOL = 0, CPHA = 1时) W9 G7 {% z' p' s
SCK引脚在空闲状态处于高电平,SCK引脚的第2个边沿捕获传输的第1个数据。& Y) p* w( F7 Y5 H8 O. I  T9 X

  Z1 q' S: @, a7 z5 C  当CPOL = 1, CPHA = 0时, Z4 t$ g% r0 V3 W
SCK引脚在空闲状态处于低电平,SCK引脚的第1个边沿捕获传输的第1个数据。
' @2 O( P5 Z" T% I- O/ Q' o* F* |4 I6 \' u1 H! j3 |' ?
  当CPOL = 0 ,CPHA= 0时# w, V1 W* B( G9 R- H# f. @6 o
SCK引脚在空闲状态处于高电平,SCK引脚的第1个边沿捕获传输的第1个数据。
5 O: o- {5 r. W/ {% E/ x, k3 b5 S0 @1 [" L
9 v: [9 G4 @1 `2 B" w( \: ^
, T  ~. @6 Q# m- T$ @, f2 l% b
有了H7支持的时序配置,再来看下DAC8501的时序图:
! y; r% v/ X$ c  ~. U$ W) H0 k/ C& g1 `+ b( W) l
) B; q7 a+ G& z  T3 x+ q1 S

& i' z9 O6 r6 g* N首先DAC8501是下降升沿做数据采集,所以STM32H7的可选的配置就是:' c' ^$ m0 |+ u, e/ x) f
6 j6 f" ~- F. h# }& J0 N& [" c
CHOL = 0,  CPHA = 1
0 `" y; u$ S7 e, @1 @1 r! N
: P: t+ g7 u6 T( }CHOL = 1,  CPHA = 0  J- J& N# H# z

- W3 H7 A$ @1 k% l% W对于这两种情况的主要区别是空闲状态下SCLK时钟选择高电平还是低电平,根据上面的时序图和DAC8501的数据手册,两种情况下都可以正常运行。经过实际测试,STM32H7使用这两个配置确实都可以正常运行。程序里面默认是选择CHOL = 0,  CPHA = 1。
6 T, p2 t0 m9 N
* C( f6 I5 X. q+ S75.6.7 第7步:DAC8501的最高更新速度计算
) I$ X& `+ M3 D8 m这里特别注意一点,定时器触发一次,就会让SPI以DMA方式传输24bit数据。9 J" r. d2 H) w5 \$ ~
" Y1 p2 l( V4 c8 k* d6 A0 y
配置条件:3 `( D! A) s7 R3 I7 |" C

+ q# u: C' s# x, L  SPI时钟是25MHz,SPI数据传为24bit,每个bit需要时间40ns。* ^, C: m' f  C& h5 |; t
  hspi.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE9 G! Q6 t; }* y
插入到NSS有效边沿和第一个数据开始之间的额外延迟,单位SPI时钟周期个数,即40ns。, i: _6 b7 P' E0 A- t) q

! n- R' v% K/ }6 A0 j) ^  hspi.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_10CYCLE: e; v1 U6 |4 w  ]9 P- W% |
两个连续数据帧之间插入的最小时间延迟,单位SPI时钟周期个数,即40ns。" |5 o/ G- q- u9 F% \4 q

7 N! w' q4 y; p& Y  ], U- G6 q: C根据上面的配置,传输一帧(24bit)数据需要的时间:( V& ?8 F1 C4 Z+ J

8 }; Z$ B% i6 M: k24bit * 20ns+ SPI_MASTER_SS_IDLENESS_00CYCLE * 20ns
: q. [) \* B, P* B5 l
/ ?, f0 h, N! g" W+ SPI_MASTER_INTERDATA_IDLENESS_02CYCLE * 20ns) n6 p  x! r* z6 m. b

- P+ Y- N) o2 `& p# |9 A= 24bit * 40ns + 0 * 40ns + 2 * 40ns+ H, c' z1 ^4 O+ J: F

4 U1 b2 l" C% B2 i6 {= 1040ns。
- V* |4 _6 ^$ R4 G0 n" L
8 G2 R; ?& c* Y  x. Y/ @那么这种配置下,可以支持最高触发速度是1 / 1040ns = 0.961MHz,如果想速度再提升些,可以降低参数hspi.Init.MasterInterDataIdleness,推荐的最小值是1个时钟周期,那么可以支持的最高触发速度是1/1000ns = 1MHz。4 b: z$ g( g0 `1 k! n; A
3 ~$ G! r/ @6 m# m  D- @+ f! i
认识到这些后,实际输出的波形周期也比较好算了,比如我们设置10个样本点为一个周期,那么触发速度为1MHz的时候,那么波形周期就是100KHz。
; e; j0 e3 M: j$ Q1 K7 C# D/ `( o
( V' Z$ ]- k5 g4 Y' Q75.6.8 第8步:DAC值和电压值互转
5 Q& j1 J6 E# w( H8 W" b2 h2 xDAC8501模块的输出电压范围是0V到5V,对应的编码值范围是0到65535,为了方便大家做互转,专门做了两个函数:$ Z& h& }" }) A4 I

5 S, I# P0 `  {) n( s! F; j
  1. /*- z: Z9 l1 d; g4 }
  2. *********************************************************************************************************# \, {7 j- r/ q1 t+ p
  3. *    函 数 名: DAC8501_DacToVoltage
    6 h4 d! ~& W" y' @% Q  C
  4. *    功能说明: 将DAC值换算为电压值,单位0.1mV
    - K! d" B3 ^& f0 O2 @; ^( }
  5. *    形    参: _dac  16位DAC字
    ; j3 Y) s4 G' U$ P7 y! S* |' o
  6. *    返 回 值: 电压,单位0.1mV0 X* T) K' [5 p4 p+ g+ o3 E
  7. *********************************************************************************************************7 ?& A3 N9 ~$ V# I2 P- `* b
  8. */* Y' F& u" Q6 C' C- x3 B, L
  9. int32_t DAC8501_DacToVoltage(uint16_t _dac)$ Y9 E7 k. @5 U  b6 n  H% |" {
  10. {
      [" G3 T. t5 m: B) r5 T6 Q3 b* z
  11.     int32_t y;6 Q- v1 G6 Z; m1 A) }/ u
  12. 3 U2 {$ W  J5 Z, i- c# A0 c) y1 p3 @
  13.     /* CaculTwoPoint(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x);*/
    ! E: v+ j% U! _4 N" ^: U
  14.     y =  CaculTwoPoint(X1, Y1, X2, Y2, _dac);' {3 Y; e3 t1 E$ `0 C% U. }# H
  15.     if (y < 0)
    : I$ p/ G" X7 }7 \* I9 H( ~4 q
  16.     {; @3 @& F' s* H
  17.         y = 0;6 Q( S2 ~2 v1 A" z( N# ?4 Z
  18.     }
    : u' ~3 Q8 ^; Y" J" I' A9 M7 Z. ]
  19.     return y;2 u; ~# F4 Z+ ~3 h/ R2 f- c
  20. }
    ' Y3 W; u+ p( P( L8 A
  21. * }1 L) d) `( T# \/ M% ~
  22. /*
    # B0 O( |  @1 b0 n; _
  23. *********************************************************************************************************
    4 L9 w* ?% h$ Z
  24. *    函 数 名: DAC8501_DacToVoltage
    + T+ w9 ~" ~6 C; V2 q  T
  25. *    功能说明: 将DAC值换算为电压值,单位 0.1mV7 v- o# A& \: p: k4 u! R% r
  26. *    形    参: _volt 电压,单位0.1mV/ _7 W# j  t4 i
  27. *    返 回 值: 16位DAC字
    : Q- s1 |* E) O) U: [: ~8 c& T; H
  28. *********************************************************************************************************, y6 O, J& |2 \$ C
  29. */8 V% R5 m# H: H& M- n2 O; r
  30. uint32_t DAC8501_VoltageToDac(int32_t _volt)
    # ?; T" R  C5 u; q# c9 m8 n6 ~7 ]
  31. {" K" `9 ^* N3 n
  32.     /* CaculTwoPoint(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x);*/; c+ X# \) R( u2 A
  33.     return CaculTwoPoint(Y1, X1, Y2, X2, _volt);  f) W: }1 Y( G. O" i
  34. }
    ) c" E0 b2 P& v( M
复制代码
/ Q& e; }" T. q0 D5 [. ?( ]
" T# s# L2 }# {: V
75.7 SPI总线板级支持包(bsp_spi_bus.c)
- O& }1 ?1 e* r3 H- u) ?. qSPI总线驱动文件bsp_spi_bus.c主要实现了如下几个API供用户调用:
- b6 ~# J! S4 P# R& D+ K  D+ k" ^% x. W& X" `
  bsp_InitSPIBus% c6 o9 T' L) F2 E/ j/ ~
  bsp_InitSPIParam
7 [* Y& s6 Y1 g, y2 o- A( H  bsp_spiTransfer
4 |" D3 g8 a( T8 N75.7.1 函数bsp_InitSPIBus
8 b" f3 K) B3 I/ I1 K函数原型:
5 k% J5 T2 H: k4 o7 N- n3 t' ?) i6 s/ i( y1 ?! H
void bsp_InitSPIBus(void)
% r1 u% A8 P1 e" U
' q% M1 w# W0 s; c- e5 y" s- D; H$ i函数描述:, A4 C) j8 p1 x- w7 z1 k
( M, x/ x& v8 L# h
此函数主要用于SPI总线的初始化,在bsp.c文件调用一次即可。* T( a% O! c+ C( j6 v

2 Z( z# O( ]( W) Q; ]) P( M+ `75.7.2 函数bsp_InitSPIParam
9 H/ ^* S# {& R1 O' s4 ?2 B' b函数原型:
3 O9 t3 W+ F" R( |( V+ y. r& ^0 S
void bsp_InitSPIParam(uint32_t _BaudRatePrescaler, uint32_t _CLKPhase, uint32_t _CLKPolarity)
4 J4 b6 j. i. j( T( ~
1 }0 |1 O+ A# v: s  T函数描述:
! M" _0 p, Y- G: o4 r5 A$ ^: d& f; k3 ]# Y5 h, O# c
此函数用于SPI总线的配置。! P9 d! |+ g: V; }8 C  ]
% C1 ?# d$ }9 F, h
函数参数:
* t: G* Z: i& o- B. d6 j/ g3 @* a' J/ ?. @/ n7 V5 r6 n
  第1个参数SPI总线的分频设置,支持的参数如下:4 Q, E0 N1 S/ X3 l
SPI_BAUDRATEPRESCALER_2    2分频
( b- M+ `1 @" O7 c& H# i
: |: n( p; ]9 R3 RSPI_BAUDRATEPRESCALER_4    4分频
8 d: D- t4 v3 l8 O5 _
- N" D! o& |: ?! z" b3 Q; h& ]- ?SPI_BAUDRATEPRESCALER_8    8分频# h6 v% T+ a- `1 m/ j* ^" U8 H' ?
$ }( y- y0 E' j3 H: {
SPI_BAUDRATEPRESCALER_16   16分频- u) r; q4 f, w

9 P$ ?- Z  [3 x% \; fSPI_BAUDRATEPRESCALER_32   32分频
" G; r! T" ]  l9 R* ?: T0 R( X3 m# ~4 Z2 S/ ^
SPI_BAUDRATEPRESCALER_64   64分频3 k& Z# }7 ?2 g( A% V- E
; l, s- B' W& ^% y/ q5 p9 `' z- c
SPI_BAUDRATEPRESCALER_128  128分频
* I* }5 }3 u' \  V4 q, i7 |* n0 t( }. P  f/ {4 Y* N0 c
SPI_BAUDRATEPRESCALER_256  256分频
. P9 B6 M% c3 P/ z. ~3 S4 b  [. Z0 G3 c5 }
  第2个参数用于时钟相位配置,支持的参数如下:) r" [$ a0 G, D5 M0 g
SPI_PHASE_1EDGE     SCK引脚的第1个边沿捕获传输的第1个数据! {. s! O: x' E! O  r( a' u
7 S6 @% ^6 {' s; @8 b+ a
SPI_PHASE_2EDGE     SCK引脚的第2个边沿捕获传输的第1个数据
1 s0 Q6 _( V/ H6 t2 t
+ A7 E$ \3 P4 F( z4 K  第3个参数是时钟极性配置,支持的参数如下:
6 A9 s3 |( X/ l" c$ zSPI_POLARITY_LOW   SCK引脚在空闲状态处于低电平
3 l8 y- T: I/ W- F0 c4 V/ d- A7 C/ P& j  i; Y; ]- ~- i
SPI_POLARITY_HIGH   SCK引脚在空闲状态处于高电平, N5 |% A8 g. g1 @

6 ?, j! u/ ^1 H; p; d' l$ K3 E! Y75.7.3 函数bsp_spiTransfer
3 `' X. r* R; A7 U) ?( Y函数原型:/ D* h) [3 T0 Q9 v' V1 {) ~  J" }
- c1 t# i$ O% C
void bsp_spiTransfer(void)
4 F5 V5 t7 O6 C4 A2 H* d& V% U" N! ^
函数描述:6 x; @; D5 S. M( e2 Q

' J8 W! L% `  z" U$ _此函数用于启动SPI数据传输,支持查询,中断和DMA方式传输。
" Q" X  t% y; c' R/ L
6 m: B5 ]8 Q1 E2 h7 f: k75.8 DAC8501支持包中断方式(bsp_spi_dac8501.c)
, _# w! C$ t  u3 E- l- eDAC8501驱动文件bsp_spi_dac8501.c主要实现了如下几个API供用户调用:
8 C- ~. G7 i% U, B" ]6 q6 ^
7 }  c& W: S+ d  bsp_InitDAC8501
1 h3 }- B" h7 M; V  DAC8501_SetCS1$ l2 q, b* F9 l  k4 p
  DAC8501_SetCS2/ f* _+ y9 c) U& h2 i/ W3 e2 T
  DAC8501_SetDacData
: H' |+ _& \+ c7 ]+ l  DAC8501_DacToVoltage$ Z) g3 V0 k3 E
  DAC8501_VoltageToDac
5 M( D0 z. j0 X3 U; U3 O' O. O75.8.1 函数bsp_InitDAC8501
5 [: T& }: `0 u* m函数原型:# s. j2 |8 H: f. V5 |) k
# G: Y! Q5 B9 `: R$ ]+ A  K+ X
void bsp_InitDAC8501(void); g; h, c, B) i6 ]) F
' n+ e" k; W" L. U1 s/ P+ ~' o
函数描述:
* z/ k' j  d5 O; R. Y4 o5 u$ t& K7 N, F0 d
主要用于DAC8501的初始化,调用前务必先调用函数bsp_InitSPIBus初始化SPI外设。) i! t2 G: [6 E7 U6 L; k

, }% _1 e; x/ y, G6 ]75.8.2 函数DAC8501_SetCS1+ W, C3 v% G3 P3 c" S
函数原型:. H& t5 Y+ G: j8 ?# ?) y
, i3 L. N8 l5 G1 Z4 r* }! G2 P
void DAC8501_SetCS1(uint8_t _Level)
6 m) g$ i7 S# B2 z! O3 \/ t# M5 W& G
函数描述:4 p# P. a& U8 ?6 B
% J7 W1 ]2 ~8 L% Q  K/ ?/ X, q
此函数用于片选DAC8501模块上的第1片8501。
& I( P9 {+ g- j4 ?
! c2 N& ~) Y' G& x/ d函数参数:
. l- q7 F' l* b' r1 L! I6 x
7 x( [1 P: n" e0 X# n  第1个参数为0表示选中,为1表示取消选中。+ N8 ]" x3 b, `  T# Z: s4 z
75.8.3 函数DAC8501_SetCS22 D' {; j1 ~+ k) z
函数原型:
/ }  F$ b+ `4 L% X' K) y) e- D  Y3 l; u4 O. Y) [& l( A; w
void DAC8501_SetCS2(uint8_t _Level)0 z1 k/ k5 r  y0 \
, o4 n% y9 U' s$ h/ P
函数描述:
& H: X8 m$ d6 C; d; J  i. u+ H9 c8 ]. j: S& F3 i$ P4 l
此函数用于片选DAC8501模块上的第2片8501。2 H; @, ^4 ^5 Y

1 z2 i& P  ]3 b. Q) F: f函数参数:2 t9 y, s. J' h0 r- Q

3 J. W# h4 q4 ]4 N( E$ t9 A* ~  第1个参数为0表示选中,为1表示取消选中
. }2 {$ p1 L" J5 G' R- r75.8.4 函数DAC8501_SetDacData( j3 P# e: A2 }  ?: k/ s# ?
函数原型:* ?" R2 g. M# Z0 z

: P" j  @3 n4 ]4 v! Zvoid DAC8501_SetDacData(uint8_t _ch, uint16_t _dac)
. {$ {: q: Z, S& z: Q2 E6 p' k/ i1 P% w4 i
函数描述:
, w1 N1 ~, o( ]! W) P! E3 H  ~# A% u5 {' A6 f  t
此函数用于设置DAC输出,并立即更新。
# L4 ^' c: C- j7 z; X; |0 z' X' Y1 e! F
函数参数:$ g9 k/ X, \  X9 U3 J
+ s& P+ Y2 f7 M) y, j) q/ M
  第1个参数为0表示通道1,为1表示通道2。
7 E! r  x* d9 C# m3 ~* y5 F0 B  第2个参数是DAC数值设置,范围0到65535,0对应最小电压值,65535对应最大电压值。
# y3 ~! J: t% x( q) I. k6 g' O75.8.5 函数DAC8501_DacToVoltage
. ], F+ u) T( [* v! P; b# J, |函数原型:
7 N- U- B. e  K/ Z
: j) e/ \# I+ @int32_t DAC8501_DacToVoltage(uint16_t _dac)2 K# [. @, f8 j! c4 t' V

  v/ w3 g( Z, _, L+ M" O函数描述:
. g9 h( @5 V. j& o! ^2 w' l
7 [; l3 C9 T7 _7 z# [此函数用于将DAC值换算为电压值,单位0.1mV。
  C4 \1 Z4 G% L
9 {$ j* i$ y6 y0 }4 J4 @* P7 ^0 E, K函数参数:9 C. L! _4 q. x  r) ?0 t% l4 v; U6 `
. P: {- E4 p9 r  W  f2 f
  第1个参数DAC数值,范围0到65535。! h( b6 j' Q) v" I3 o. M) m
  返回值,返回电压值,单位0.1mV。! [, A; r5 ~$ _( W; V8 k
75.8.6 函数DAC8501_VoltageToDac7 J/ a# q& D' v) Y( v
函数原型:1 W8 {( b3 s  H6 o4 E
3 w2 J$ O- V0 P& H2 E
uint32_t DAC8501_VoltageToDac(int32_t _volt)
0 q# Z& V+ g) b8 F& e& p+ f" ~* _6 x  |; t, s
函数描述:! }' G6 h) A  c& l

/ N) x0 x9 c' y% ^7 M此函数用于将电压值转换为DAC值。
; M8 d9 H: ~- ]1 k1 }* {
, e8 Z' ~7 y+ \( N' |' Z. F; E+ C% m函数参数:
, E" \- r3 S/ R6 k. |/ [
6 k% ]; g; C& }6 M' R  第1个参数是电压值,范围0到50000,单位0.1mV。
: L: j6 h9 s1 c) I- G" ?/ c! W. h3 ^  返回值,返回DAC值。+ w) G$ ~: J* P: h* V
75.9 DAC8501支持包DMA方式(bsp_spidma_dac8501.c)
/ K6 k7 U: \' r% o0 _& {DAC8501驱动文件bsp_spidma_dac8501.c涉及到的函数比较多,我们主要介绍用到的如下几个函数:0 m( I8 r7 g3 N  j
7 _) l* `! ]: Q' l0 u; W
  bsp_InitDAC85015 o; k$ e0 [' \
  DAC8501_SetDacDataDMA- C  U8 r& z6 Y" g- l+ V1 M
  DAC8501_SetDacData, J8 N# y. t# q* S8 Z. ]
75.9.1 函数bsp_InitDAC85014 _8 Z  Q+ C7 `: q+ Q( e/ f
函数原型:
7 t& j; {) Y4 E6 y# ~8 Z' ]* @3 E: \+ _
void bsp_InitDAC8501(void)
! M6 A* U6 o5 U, p4 D  V0 r& w* s; N2 L4 i
函数描述:" d( K4 a  B, y% }( g/ I3 a! b
0 ~: l" H. v  g# F; w
主要用于DAC8501的初始化。" U: o$ }' V  X# G

8 g: d  \4 T. b75.9.2 函数DAC8501_SetDacDataDMA
+ `/ L  ^) O9 Z. X3 ]8 ~8 Q函数原型:) ^: M  J5 S# }+ Q- Q0 @% U7 @

2 g- F. D" N8 ]0 B1 @1 \# zvoid DAC8501_SetDacDataDMA(uint8_t _ch, uint16_t *_pbufch1, uint32_t _sizech1, uint32_t _ulFreq)9 G% a; l4 A- W( m* b/ r
% h; _  I3 @% W6 x
函数描述:) Q' g# t, m) \1 {

5 f- G) t( p1 A  u2 `; v- f& O此函数用于SPI DMA方式数据发送。& ~( W, N" X9 ?- g* Q# V

; t; y7 l1 L6 E# j/ n. ?函数参数:4 o; f+ B' v( N" g6 \( B

. {' D. ?7 C) ^) E" I# T  第1个参数用于选择的通道: 1表示通道1输出
  J3 c- f" w5 {  j, A  e  第2个参数表示通道1数据缓冲地址。. W/ h% v; l! R9 `
  第3个参数表示通道1数据大小。
2 {0 c; x/ _3 |3 X8 r" {  第4个参数表示触发频率,推荐范围100Hz- 1MHz,注意这个参数是触发频率,并不是波形周期。这里触发一次,SPI DMA传输一次24bit数据。
! H6 k5 \0 `2 ?75.9.3 函数DAC8501_SetDacData( S! {4 K6 D+ ^! A% i2 y& E
函数原型:( O; Q" E: R9 H3 p

2 d2 j& e# {/ ^5 A- vvoid DAC8501_SetDacData(uint8_t _ch, uint16_t _dac)
5 Y% _* h# ^2 T1 d: z
. u- [; W$ v# U" [函数描述:
! X, t( X3 r% b# E( ^. E% z
$ c2 a& c* x3 D7 e9 K2 d此函数用于设置DAC输出,并立即更新。! R7 q: |. O! ^
, T6 u% `& y8 X5 Z2 h
函数参数:1 G/ g9 T+ a  B1 b

; I; {" K9 j* |1 V  第1个参数为0表示通道1,为1表示通道2(对于SPI DMA方式,仅支持通道1)。6 Q' U8 |% n, Y  s  {* X' o* `' w
  第2个参数是DAC数值设置,范围0到65535,0对应最小电压值,65535对应最大电压值。
* [, L$ [% t: P9 C! F9 _+ S75.10          DAC8501驱动移植和使用(中断更新方式)) }2 r, f0 W8 R9 b
DAC8501移植步骤如下:
) `# t0 c( U+ L& ^1 o% g' E; q, u4 Y1 @2 A' s/ m
  第1步:复制bsp_spi_bus.c,bsp_spi_bus.h,bsp_spi_dac8501.c,bsp_spi_dac8501.h到自己的工程目录,并添加到工程里面。
0 g/ `7 C- h/ [0 Y4 K- B  O; k  第2步:根据使用的第几个SPI,SPI时钟,SPI引脚和DMA通道等,修改bsp_spi_bus.c文件开头的宏定义
9 g, H, X& a# v9 l$ F
  1. /*
    " ~! g( x+ N# c4 y
  2. *********************************************************************************************************
    5 m! S: s( |5 X1 y5 m
  3. *                                时钟,引脚,DMA,中断等宏定义% H$ l7 B- w  P$ ?
  4. *********************************************************************************************************" ~/ T) p1 h8 }0 i; k0 r: b' e0 n
  5. */
    - H0 L4 C6 c) d3 t
  6. #define SPIx                            SPI1
    * l- o( q! _. _
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()5 D! x) [! `0 F5 S
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()
    ( `- n* m0 k! N( q$ Z8 Z4 d( v. i

  9. 4 K" l6 W, ]) p7 X. }: T1 B# x
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()! @1 m6 x8 H' {& ^, P# Z# _0 O0 b
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()! j# g* g9 o, p
  12. % x0 |1 g6 E2 Y9 M+ S& G8 p
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()- ?/ m( r; z: r: [
  14. #define SPIx_SCK_GPIO                    GPIOB$ Z- f: x2 P  f# ~5 b* H: G
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3  S. f8 \' B0 J1 _
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1
    8 c# W  |% B/ b1 A3 F8 v( Q

  17. 8 v/ l0 [6 f/ |& D$ d) I
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE(); [* Y, H3 S" l
  19. #define SPIx_MISO_GPIO                    GPIOB
    0 b; g& t; p3 x! l( }7 S3 s) P
  20. #define SPIx_MISO_PIN                     GPIO_PIN_46 y/ B% z/ l+ y' R/ ?% S8 T5 [9 \
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI1
    ! v, K; p! e9 M
  22. 5 K! H6 B2 J: W: R- D9 r; @* d
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    " s: i. s% T2 {1 x
  24. #define SPIx_MOSI_GPIO                    GPIOB
    5 }) c3 Y& e7 G* I/ h5 q1 H
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_5
    . }3 K' b; d3 ]0 m
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1# a0 [0 O( \) Q" W) L: [

  27. 7 r: S  P9 Y: z; ^1 s
  28. #define SPIx_TX_DMA_STREAM               DMA2_Stream3
    1 q2 Y0 X8 m7 C+ h
  29. #define SPIx_RX_DMA_STREAM               DMA2_Stream2
    + ]5 r8 z& Z% V# l2 q

  30. ! t  x. j4 N: Z4 t9 y( j5 Y
  31. #define SPIx_TX_DMA_REQUEST              DMA_REQUEST_SPI1_TX
    ; c0 X! M1 I# L+ R* ^9 l: K! t* B' d
  32. #define SPIx_RX_DMA_REQUEST              DMA_REQUEST_SPI1_RX* D; y  B8 i% v  c' z

  33. ! K' O& I* C/ S4 f9 q
  34. #define SPIx_DMA_TX_IRQn                 DMA2_Stream3_IRQn% Q: Z) v: m2 v& n, }
  35. #define SPIx_DMA_RX_IRQn                 DMA2_Stream2_IRQn  H; d! T" i0 s  |

  36. $ G  O2 Z; P& ^
  37. #define SPIx_DMA_TX_IRQHandler           DMA2_Stream3_IRQHandler
    , b7 O+ y$ v+ u' H/ k/ M- Y
  38. #define SPIx_DMA_RX_IRQHandler           DMA2_Stream2_IRQHandler
    5 Z0 r+ o0 k  g' }9 @7 v2 b
  39. / ^( u6 g7 Z$ g% G; p  i# }0 b
  40. #define SPIx_IRQn                        SPI1_IRQn
    & l4 W% G9 m/ R$ B
  41. #define SPIx_IRQHandler                  SPI1_IRQHandler
复制代码
% E  x7 e1 v8 Q8 m+ k
  第3步:根据芯片支持的时钟速度,时钟相位和时钟极性配置函数DAC8501_SetCS1和DAC8501_SetCS2。- f# L7 Z) X: X/ ]1 S9 x- N- B
DAC8501_SetCS1和DAC8501_SetCS2。+ p( \3 K! d' W/ n
  1. /*
    ) z3 f2 X& {. k7 B6 Y  l' n
  2. *********************************************************************************************************
    - ~7 \( Q  s: N& D
  3. *    函 数 名: DAC8501_SetCS1
    " s( k2 r0 p/ A1 V) `* Z" h! j& `4 Q
  4. *    功能说明: DAC8501 片选控制函数
    " N- K- u+ i! v
  5. *    形    参: 无1 B6 ~8 V) {; o$ N3 N
  6. *    返 回 值: 无
    * o6 b; n! C# U0 x
  7. *********************************************************************************************************- l6 R9 U0 K0 h) M! r: z/ ^
  8. */& @7 d' F5 X6 @4 p5 I# p
  9. void DAC8501_SetCS1(uint8_t _Level)2 w* w, T3 c) j9 Y- `( ^
  10. {" p$ c* ?- z3 V9 p
  11.     if (_Level == 0)& g- I8 B- N8 p' K& O
  12.     {
    . K- S" E. L5 `3 Y; n
  13.         bsp_SpiBusEnter();    /* 占用SPI总线  */   
    & E2 T- n( u; i6 _4 e" x
  14.         bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);        8 {# u. G7 G- k8 n
  15.         CS1_0();/ z% k! J, G! Q/ r
  16.     }
    $ U% U% P+ }0 L7 i/ g
  17.     else
    6 g1 g* Z" v9 z* I
  18.     {          q; ~0 r  k0 F  k0 z9 V2 u4 }
  19.         CS1_1();   
    8 ]* o" x& e$ |
  20.         bsp_SpiBusExit();    /* 释放SPI总线 */1 {& `8 e. L; e' `$ S
  21.     }   
    5 N  c( L& ]9 @6 V8 C( R. C
  22. }
    " v! E6 |; P% }+ v3 V2 f( b7 W

  23. 6 R$ m" X. v( }$ e* p' J5 z4 [
  24. /*
    + \4 P4 m  h1 q+ ^+ f( Z2 M* @
  25. *********************************************************************************************************# D2 \2 x) p- |, M" }+ O  y
  26. *    函 数 名: DAC8501_SetCS2(0)& N# D7 l# f7 t; o
  27. *    功能说明: 设置CS2。 用于运行中SPI共享。9 m3 }+ @4 m" ^6 N
  28. *    形    参: 无3 R( y! T. K! v1 `  a) q" V1 h2 ^
  29.     返 回 值: 无
    , ]% Q6 g  p( h! Y8 f# W( o
  30. *********************************************************************************************************! r% x8 j2 B" A$ c# U/ B1 N9 A
  31. */5 E1 w' D: y5 g. a! v
  32. void DAC8501_SetCS2(uint8_t _level)
    0 G6 }' m0 e% c  ]
  33. {1 k$ E- Q7 X  {8 z+ i$ T
  34.     if (_level == 0)
    5 c- e  k2 D& ~# w/ c
  35.     {
    " k* ^! A+ @: B8 L! y
  36.         bsp_SpiBusEnter();    /* 占用SPI总线  */
    8 j: v  E& j3 M' ^, G. U  l' N& S
  37.         bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);        
    4 ~2 Q4 c* _. K
  38.         CS2_0();1 X% R- i' \+ p8 ^# f
  39.     }  V- c  n, j! X8 }+ x6 C% ~" W. u* e
  40.     else+ f! m# C, K! H6 X& G+ u& v
  41.     {+ ?: C2 x! y: ]: s
  42.         CS2_1();
    : _$ l# r" Y4 i, k' b
  43.         bsp_SpiBusExit();    /* 释放SPI总线 */
    . d) A4 G3 [$ D7 I) `0 d
  44.     }; x$ ~# K- N7 Q
  45. }
    ) T0 N, C7 P1 a2 X3 D! e0 l
复制代码
0 q! Q5 z* A1 y3 O2 T3 E. @
$ J; M) N& K  w" `& w
  第4步:根据使用的片选引脚,修改bsp_spi_dac8562.c文件开头的宏定义。
3 _: t0 f2 y" E6 P; R& B
  1. #define CS1_CLK_ENABLE()     __HAL_RCC_GPIOG_CLK_ENABLE()0 O. r4 n2 R& Q1 }; Y9 H) E% g, F
  2. #define CS1_GPIO            GPIOG
    / P3 [$ X  X7 |3 ]9 K7 P/ f
  3. #define CS1_PIN            GPIO_PIN_10. L, e. F: x5 y1 Z" f8 ~8 Z
  4. 4 R( C# J0 G  l; r
  5. #define CS1_1()            CS1_GPIO->BSRR = CS1_PIN
    ) q5 I$ N5 k3 c5 N& m3 c: D+ {$ A. `
  6. #define CS1_0()            CS1_GPIO->BSRR = ((uint32_t)CS1_PIN << 16U)
    ) f; M$ {5 D* H7 h2 w

  7. : Q! P6 D& H7 k1 H% h+ G$ e
  8. /*特别注意,我们这里是用的扩展IO控制的 */   
    2 W5 y5 z2 F6 p; k. |
  9. #define CS2_1()            HC574_SetPin(NRF24L01_CE, 1);* h! W' I8 w, l& U5 M% R' q
  10. #define CS2_0()            HC574_SetPin(NRF24L01_CE, 0);
复制代码
6 Y/ D/ t+ Z7 E: O$ t. ]1 f
  第5步:如果使用DMA方式的话,请不要使用TCM RAM,因为通用DMA1和DMA2不支持。并为了防止DMA和CPU同时访问DMA缓冲造成的数据一致性问题,将这块空间关闭Cache处理,比如使用的SRAM4:
7 j" O1 l& S5 H: ~
  1. /* 配置SRAM4的MPU属性为Non-cacheable */
    " a8 [( _: t$ `3 z9 M
  2. MPU_InitStruct.Enable           = MPU_REGION_ENABLE;  U5 ]2 Y$ p. d6 B& u
  3. MPU_InitStruct.BaseAddress      = 0x38000000;
    6 S7 w& a2 y: v- D
  4. MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;
    7 H: I0 X; E6 z7 }
  5. MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;5 A$ z# a! b/ X. X- Z$ N1 i% D
  6. MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;. e9 _6 J6 @& W% k2 O2 j1 r
  7. MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    $ B1 q( ?9 F( a7 _
  8. MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;. ~  C4 S: `% H- z/ h" M# _9 N. T: E
  9. MPU_InitStruct.Number           = MPU_REGION_NUMBER2;" h8 t1 ?0 Q/ y/ u3 i0 _6 ^
  10. MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;: t1 o* {7 ^; W2 h$ ^5 K7 x
  11. MPU_InitStruct.SubRegionDisable = 0x00;, x! s4 ?0 d! @
  12. MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;5 J& O6 ?  o1 J3 n1 t
  13. 1 e8 d1 Y' w( _. \! r
  14. HAL_MPU_ConfigRegion(&MPU_InitStruct);
复制代码

2 `) h! ?& |3 N: e  第6步:初始化SPI。
3 b0 X& T( v: C4 z$ F* C$ Y
  1. /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */
    $ Z& O1 R5 w) n" x: x, {7 O2 j) ^4 \
  2. bsp_InitSPIBus();    /* 配置SPI总线 */        & u+ `+ d3 N' O. p. f0 b4 R' U
  3. bsp_InitDAC8501(); /* 初始化配置DAC8501 */
复制代码
, l. K* T+ H6 R3 J. m) d+ ?+ H
  第7步:DAC8501驱动主要用到HAL库的SPI驱动文件,简单省事些可以添加所有HAL库C源文件进来。
: z- k( Z8 {$ a/ w6 \2 Z8 d  第8步:应用方法看本章节配套例子即可。! R. }# ]# q6 I* b2 P. ]+ i
75.11          DAC8501驱动移植和使用(SPI DMA更新方式)! m1 X: h6 Z- t
DAC8501移植步骤如下:9 r0 v* |' a( _0 ^! {9 K4 a7 d

! Z% p( `# ]1 y+ E5 m8 \" h  第1步:复制bsp_spidma_dac8501.c,bsp_spidma_dac8501.h到自己的工程目录,并添加到工程里面。
4 p& A7 {* D" S3 U. ~; ]6 m  第2步:根据使用的第几个SPI,SPI时钟,SPI引脚和DMA通道等,修改bsp_spidma_dac8501.c文件开头的宏定义7 V0 Y2 M( X" W
  1. /*
    + K/ H2 J- e: |4 [
  2. *********************************************************************************************************
    8 M* u. ?0 l/ S$ }: R( p# \
  3. *                                时钟,引脚,DMA,中断等宏定义
    3 E1 _4 S2 ?; M' e  \9 r
  4. *********************************************************************************************************6 F4 B/ J8 s( ~4 w& z& {
  5. */
    , ]0 W7 z* c8 {. h9 \6 J6 c5 w
  6. #define SPIx                            SPI1
    # \% G5 `  x" O# J9 j
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()  u  a1 S+ D3 G$ N- @) f* t; u6 e! r/ k
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()! ]- U" Q+ d/ N' ], H; a; n

  9. ) g, Y0 K+ C: T3 C- G9 ?
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()( {' _9 d9 F3 C
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()
    # U/ _+ G4 b  `& k$ }, z

  12. 8 X2 g2 A# y8 f' ]! h
  13. /* SYNC, 也就是CS片选 */    : L  U% _( p( P* e' K5 L
  14. #define SPIx_NSS_CLK_ENABLE()             __HAL_RCC_GPIOG_CLK_ENABLE()
    ) M3 e" S7 W, [1 }. T/ Y2 m$ U
  15. #define SPIx_NSS_GPIO                    GPIOG
    2 n8 c3 N  X  u: T/ \
  16. #define SPIx_NSS_PIN                    GPIO_PIN_10
    % s5 `& }6 H6 ~) n1 l! T( j7 ^
  17. #define SPIx_NSS_AF                    GPIO_AF5_SPI1  v. R) v/ C: l4 ^

  18. # `) M, {$ I; g. [) d- P: u
  19. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE(). ?: k! m% \) @" |4 j
  20. #define SPIx_SCK_GPIO                    GPIOB6 e1 w0 I4 f' J5 y( \8 g! S
  21. #define SPIx_SCK_PIN                    GPIO_PIN_38 p) s" P5 p$ K
  22. #define SPIx_SCK_AF                    GPIO_AF5_SPI1
    - p$ i+ a) P; \: _8 C& I8 C. [. Y

  23. 5 ]* V) Q. l: J, {
  24. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    5 b% f# w$ t3 [, K9 @+ L6 V+ ]; P
  25. #define SPIx_MISO_GPIO                    GPIOB7 O; N+ V: S' \. {1 U
  26. #define SPIx_MISO_PIN                     GPIO_PIN_4
    2 h3 O4 A4 p4 g) j0 j0 B
  27. #define SPIx_MISO_AF                    GPIO_AF5_SPI1
    / d# Z1 q2 _1 h7 U  n6 g

  28. / ?- @- c3 D+ Q  z; {6 t  H6 D7 @, ?
  29. #define SPIx_MOSI_CLK_ENABLE()          __HAL_RCC_GPIOB_CLK_ENABLE()# l  N2 \6 a& E3 d
  30. #define SPIx_MOSI_GPIO                  GPIOB
    ! X$ h" b  z9 U
  31. #define SPIx_MOSI_PIN                   GPIO_PIN_5% i% Z" U  b, I
  32. #define SPIx_MOSI_AF                  GPIO_AF5_SPI1* e# J' R0 ~* w' o

  33. 8 h; D0 T& N& q& Y3 ]% x
  34. #define SPIx_TX_DMA_STREAM              DMA2_Stream3
    ( \) b7 L' n  }) q8 O3 `4 l2 G. H% t
  35. #define SPIx_RX_DMA_STREAM              DMA2_Stream2( O, n: g- n7 F1 ^# O& l+ `
  36.   ?' d6 u* u' X
  37. #define SPIx_TX_DMA_REQUEST             DMA_REQUEST_SPI1_TX( M: g/ F$ t% p
  38. #define SPIx_RX_DMA_REQUEST             DMA_REQUEST_SPI1_RX# V$ I! y6 }. ^/ n! t: |) l

  39. 9 i1 s$ z4 v) Z) `6 Z  Z7 ?  W% I
  40. #define SPIx_DMA_TX_IRQn                DMA2_Stream3_IRQn8 {, ?$ U" z5 G3 q& J
  41. #define SPIx_DMA_RX_IRQn                DMA2_Stream2_IRQn
    / f5 R  e( E, o* N
  42. / p- i( n& Q: Q6 w- A) `& U8 f
  43. #define SPIx_DMA_TX_IRQHandler          DMA2_Stream3_IRQHandler- B6 O  ?4 f! N8 S  L* n. D+ t
  44. #define SPIx_DMA_RX_IRQHandler          DMA2_Stream2_IRQHandler
    3 X  y4 i4 b& I  |$ y& @+ Q
  45. 8 N# @- o- I8 ]0 ?1 A% ~' i
  46. #define SPIx_IRQn                       SPI1_IRQn2 r5 d6 R  ]1 x
  47. #define SPIx_IRQHandler                 SPI1_IRQHandler
    9 j: V. Y+ D9 H
复制代码

6 n: K- O: [) j! Q- T- o* u( v) d0 r  p
  第3步:如果使用DMA方式的话,请不要使用TCM RAM,因为通用DMA1和DMA2不支持。并为了防止DMA和CPU同时访问DMA缓冲造成的数据一致性问题,将这块空间关闭Cache处理,比如使用的SRAM4:
2 f2 F% p* k+ x+ w" O7 J
  1. /* 配置SRAM4的MPU属性为Non-cacheable */
    ' h0 e. X: [8 H" x5 o- |
  2. MPU_InitStruct.Enable           = MPU_REGION_ENABLE;' Y* |4 H6 m9 \5 `) m" [, K
  3. MPU_InitStruct.BaseAddress      = 0x38000000;, Q/ m' H* F3 _# J9 j1 y
  4. MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;
    6 _. P& E- k9 E$ t8 t! y; R
  5. MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    ) [+ L; _. X* q* O1 p8 X
  6. MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;0 @) Z7 Q; x5 W+ i2 m  M8 U
  7. MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;, v& x( G$ n3 r9 W& d5 |
  8. MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;5 K# m! l& r. B+ S, P
  9. MPU_InitStruct.Number           = MPU_REGION_NUMBER2;8 [3 }' y# m: I: n- }
  10. MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;) ]9 P: `  k" d  |- v
  11. MPU_InitStruct.SubRegionDisable = 0x00;3 P9 ^' u2 i9 Q) c7 F0 u
  12. MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    . F& o) k; ~- O# X  G8 J- q
  13. 4 G& N$ \( r9 C4 |# W# w( z
  14. HAL_MPU_ConfigRegion(&MPU_InitStruct);
复制代码
第4步:初始化SPI。
; A  K  c) z: B% S  n) }( R
  1. /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */9 j) v# D" e& Z, b* N4 i# k4 l
  2. bsp_InitDAC8501();    /* 初始化配置DAC8501 */
复制代码
% L/ `( t5 O6 q
  第5步:DAC8501驱动主要用到HAL库的SPI驱动文件,简单省事些可以添加所有HAL库C源文件进来。
7 L$ z% \6 Y) q  第6步:应用方法看本章节配套例子即可2 b3 Q1 ]+ M" E9 p9 I$ L9 R
75.12          实验例程设计框架
8 d9 `8 h" s0 Q5 n; o) n% i通过程序设计框架,让大家先对配套例程有一个全面的认识,然后再理解细节,本次实验例程的设计框架如下:+ f, K- F, k' M- W* y
/ I/ k: u0 Y6 {7 |
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
# C; O6 n/ P2 u: g  R2 u

* T9 j4 S5 i: V: D. M  ^  第1阶段,上电启动阶段:
2 u. Q" }1 v3 m4 A: b) \/ |# E# @( _! S! A9 _4 {
这部分在第14章进行了详细说明。
* W7 f( U8 Y( |8 v  第2阶段,进入main函数:" Z% g/ {! \# B' `* }5 Z6 g# d

9 E+ Q/ G: R6 `, {: Q# ]4 z  第1部分,硬件初始化,主要是MPU,Cache,HAL库,系统时钟,滴答定时器和LED。
8 s- e( G/ B. r- ?4 p+ S9 O/ Q  第2部分,应用程序设计部分,实现DAC8501的简易信号发生器功能。。
7 X2 x) w) C$ F% W% ^75.13          实验例程说明(MDK)+ E( e* r% M. g, y3 K+ n2 P9 Y% _
注:本章是配套了两个例子的,这里我们以SPI DMA方式进行说明。
5 O  A; S* D! O6 r2 i, f+ E
* e# U% [+ ?7 ]. Z; ]& b+ f配套例子:
9 ?: V. G8 @5 S- f- O# A* O1 j( x: u& l9 G: i
V7-054_DAC8501简易信号发生器(单通道SPI DMA方式,16bit分辨率, 0-5V输出)
$ [6 ~( J, w- R% m# }
5 q  ^1 P1 L+ a, tV7-055_DAC8501简易信号发生器(双通道SPI查询方式,16bit分辨率, 0-5V输出)
) t: Y% @6 E" H) F: H4 a/ K7 b8 W% h& s! z5 M1 Y5 H* a9 x! P
实验目的:" P) k& a3 ^5 r" w9 Z
: _  k7 l) d' l9 k
学习DAC8501的SPI DMA驱动方式实现。
2 @" F0 i& @% z, `- I6 `实验内容:
4 k% j* a- c8 Y5 ~, A9 h
5 B* R) \  L2 i, FDAC8501模块上带了两片8501,每片是单通道DAC,片上输出缓冲运放,轨到轨输出,16bit分辨率,支持30MHz的SPI时钟速度。/ [/ l$ S2 Y; Q; p  V
DAC8501本身仅支持一路输出,而模块上是带了两片DAC8501,其中只有一路的片选可以支持SPI NSS复用, 所以只有一路支持SPI DMA。
# p4 x5 K$ O/ {: I, @! N& o' qDAC8501供电电压2.7-5.5V,模拟输出带宽350KHz。& f" H% D( h* T2 b+ `! w4 C
实验操作:* _: }* P7 i- k+ V7 |! r

, r% ^5 m  s6 c启动一个自动重装软件定时器,每100ms翻转一次LED2。9 \! U6 r% q: ]# ?; f( c
K1键按下,通道1输出方波。
9 O/ L0 n6 b" w3 ?, ZK2键按下,通道1输出正弦波。
' q, q8 z% t) v! q# z# rK3键按下,通道1输出直流。
4 l: F5 p) E2 q) q$ M上电后串口打印的信息:8 _% D3 A, Y( d+ \4 U3 V4 s

- R& U8 k) @' i5 s7 u! W' l波特率 115200,数据位 8,奇偶校验位无,停止位 1。
5 m1 O9 Q: b3 B) s6 z- ^4 x
0 P( S& N# j7 u
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

: W+ `4 \9 f  V# ^3 d+ B, I! F0 t! M7 @4 G( i9 [
波形效果:
) P) O3 r5 b! E# D/ J) E7 i( w# i+ O" u0 I8 C+ B) I& R. L
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
# e) q" m+ Z3 i% u

: x9 O5 G7 @- U) Q# T- Q( \% ^" F模块插入位置:
6 H5 X% P! C& H& @- M" d5 d. ^1 |0 G! I
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

- y* Z! x9 U8 i: ~& Z
; S' G. J- o% E) g程序设计:  g2 k8 @' v9 d, S6 j( E1 x) N6 @- _
: i. S7 o+ d& n) I- b5 q* S9 Y. i
  系统栈大小分配:
" x+ b; B6 v# o( \, C9 {8 O7 u6 \5 y9 z4 F
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
6 S7 H4 j! p5 ~" U+ s2 O4 `

& L4 O; O7 ]2 w; G  RAM空间用的DTCM:0 I- N# k* l2 L3 V  U3 z6 b- ^

# Y! B7 u2 Y8 v: M$ {
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
4 a2 E* |; J; v1 Y. M

0 x! X5 k, X# F2 {3 m9 ?  硬件外设初始化- S9 P8 `& f  A
$ s) G4 N+ ~3 f
硬件外设的初始化是在 bsp.c 文件实现:
/ c. E# w, X. a/ k* F6 j- B% k7 O& v# A0 S8 [  {
  1. /*8 y1 b" P; U1 ]+ `& Q
  2. *********************************************************************************************************
      N! v( I) w2 `9 o
  3. *    函 数 名: bsp_Init
    + D# t9 f& H( B6 y
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
    - A- L( J& N1 i' p) b' x2 T
  5. *    形    参:无5 F5 R1 p, h9 Y, }2 v  R- c
  6. *    返 回 值: 无! K3 z' {& `4 g: x; i% \
  7. *********************************************************************************************************
    ) N* {4 y+ C- x" s! u
  8. */. R7 u+ L; l# S3 ^! ~. y) g" O
  9. void bsp_Init(void)
    ( k, Z, n) e0 H( c7 e+ Y
  10. {" F7 ^$ ?, }* L( J  X6 a5 O  a: y
  11.     /* 配置MPU */
    , n0 F6 w+ _; y7 |: S
  12.     MPU_Config();5 w3 Y- W  t4 f' T" a6 y
  13. ) ~9 _; `+ X4 Z# U; k( J1 |" O
  14.     /* 使能L1 Cache */
    * Q+ D% P# i' N; s% A0 f1 L/ v
  15.     CPU_CACHE_Enable();* \3 t! T3 J: P: _

  16. 4 b) b6 ~. l. _
  17.     /* / v9 H9 S* s% |' _* v4 l: D$ n
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
    ) u5 \1 V6 X/ W: E
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
      \' P+ @: O" l' Z/ L9 m( M( T. Q
  20.        - 设置NVIV优先级分组为4。
    5 d" |' ?( ]/ s* }, x
  21.      */
    1 ^+ ]! N- q( z8 y4 D! t
  22.     HAL_Init();6 E$ w" s- @) d& _, b* r

  23. ! W! _9 B  _8 ?# W# A
  24.     /* , l- `$ Z" W5 x
  25.        配置系统时钟到400MHz! X. {7 j$ m+ I  j* u+ [  U
  26.        - 切换使用HSE。
    % r* w: v3 [* t6 m& Q; u* p6 D# b4 q  O
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    ( g! [% ]& d2 k9 D' Z
  28.     */
      @) P) o  O7 |8 p( n5 h+ j' C8 L
  29.     SystemClock_Config();
    ( a" Q! |- b  p& K+ d# P

  30. 5 B( c, F6 V3 K1 w+ q  a7 u( o( N
  31.     /*
    3 C# d- j) j- \+ ~: r% w
  32.        Event Recorder:
    * P1 ~6 i. @% N+ Y1 O
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。4 u; F: p4 |5 Y
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章" b) U* ~6 U* R( K/ j7 m8 a
  35.     */    1 _1 y3 ~; ^5 W4 c
  36. #if Enable_EventRecorder == 1  3 C# N0 S4 `5 g1 j: W
  37.     /* 初始化EventRecorder并开启 */
    4 S# E9 M" B. Q& X/ l! u: M6 b# S! C
  38.     EventRecorderInitialize(EventRecordAll, 1U);
    5 n! n! t4 S3 L! t7 T; c: s2 q5 W
  39.     EventRecorderStart();
    . p9 @4 ~) Z3 |) D4 @
  40. #endif
    " T7 t* w7 B8 c4 e, ^
  41. ! C: L* M. d  B" z+ J2 p) k+ x2 v5 [% h/ R
  42. bsp_InitDWT();      /* 初始化DWT时钟周期计数器 */       & _% u, b# c) n7 m
  43.     bsp_InitKey();         /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */- X1 O7 `8 P9 x) k  z* S$ q
  44.     bsp_InitTimer();       /* 初始化滴答定时器 */
    ( f7 n& \/ {# x1 p9 `0 V
  45.     bsp_InitLPUart();     /* 初始化串口 */
    8 S0 V# |1 {8 i! m. x- b
  46.     bsp_InitExtIO();     /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */   
    , N9 Y6 q5 z% b1 X2 t  z
  47.     bsp_InitLed();         /* 初始化LED */   
    8 ?  ]1 A  N( }' D
  48. bsp_InitExtSDRAM(); /* 初始化SDRAM */8 ?( X7 o9 d! q

  49. % W& g4 g* M8 m# V6 }$ t2 N: C
  50.     /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */   
    ( U: \$ {  R* X5 ^
  51.     bsp_InitDAC8501();  /* 初始化配置DAC8501 */! v  K8 |# p9 j: W1 m+ o& M0 C
  52. }
复制代码

, C# ^0 Q% g' o# Y1 ~  M; }/ |; e7 P' Q
  I# F( Q" j: f# C0 d* t4 M6 D
  MPU配置和Cache配置:0 |  T9 T1 d* O( j7 @1 k. b# B. N
$ K+ |0 Y* M( T  z
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区以及SRAM45 U: `. d$ b+ q
! w4 s/ H2 a# s, g% _, y: }
  1. /*9 U2 L) ~0 r0 B" P
  2. *********************************************************************************************************
    + p1 w1 ]4 p. k* t1 [/ y
  3. *    函 数 名: MPU_Config
    6 T4 W6 t. X$ T- L; u% l# M  ?
  4. *    功能说明: 配置MPU
    ; X3 F& e- ^) D% h
  5. *    形    参: 无
    - [9 C0 P/ ?) c4 Y; y2 }. X
  6. *    返 回 值: 无# u+ z+ G% R4 q( A; }9 T8 h
  7. *********************************************************************************************************9 ?% ?( G2 [5 A( ^3 n. X
  8. */
    % k9 K* V' F0 S8 r5 Y
  9. static void MPU_Config( void )
    8 w+ O1 a% Y: [- i3 ]1 ^
  10. {/ l8 v$ {( a, d* K7 L* U# ^) T
  11.     MPU_Region_InitTypeDef MPU_InitStruct;  V6 S; M3 B+ D' y; h4 b
  12. 8 Y, b$ t+ t9 D4 O5 @/ R- b1 k
  13.     /* 禁止 MPU */% y* R( e. v* X* j. y- h9 \3 V
  14.     HAL_MPU_Disable();, \# ^$ `/ ^* z/ u

  15. - X" Q/ I$ {6 w" r5 p0 A6 C# W
  16.     /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */7 @) F% V- `1 @8 r! Q! U$ T
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    & v  X# }, s( g6 C) h& r9 z4 M8 R6 A) U9 c
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;
    6 ~5 i5 o% w$ r5 T+ c
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    & r1 o0 f# }$ w) z& }+ v, Q
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    : C* l5 Q5 {' m, h
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    # P/ q0 I( b4 ]( G
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;% U: ?2 ]$ r" d; U$ F; V
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    - ~0 l0 @# m0 F
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    1 X& E' e( S( Z& M" O/ A
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;$ P; W$ o; e; f! G. v  y
  26.     MPU_InitStruct.SubRegionDisable = 0x00;
    ( Q2 B6 h/ I) H. L& c- e
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    * [, b1 l* W3 X& @$ P

  28. 8 @7 Q. e3 p' K4 |4 d. t2 F
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
      p# H. G; |7 g

  30. 2 z- {6 x# ^) k: y2 X6 l  P
  31. ; k2 Z2 _6 c/ ?) w% O% k/ N
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */$ J  B4 {% @" O/ C
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    7 F6 M* _4 h$ s; ~) L) R2 v
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;8 N! H4 O* i2 ^# X6 ]( ^
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    * d+ s9 q4 I2 h& _) w' \& j
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;* y, R9 A# L5 l7 P6 H+ W
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;( W7 |7 S0 x1 Z. J
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    . C$ S/ }$ F0 M- e+ a" j1 t7 P' i
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    2 J" \6 Z+ s8 Y3 R2 t
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;/ H$ P2 @) j( _" I& \- X
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    6 S3 |1 ^) D/ F; x6 \9 B/ }
  42.     MPU_InitStruct.SubRegionDisable = 0x00;0 P" N: [4 y1 @2 [% K
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    / f' X3 ~- k; M- Z. h# f9 Z
  44. " B9 h; Y8 t0 l; `
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);2 N) l- U+ j% A+ o' u/ h5 b
  46. . M% ^4 p6 c2 _4 o! }
  47.     /* 配置SRAM4的MPU属性为Non-cacheable */
    & _0 H$ G$ I+ u& ^5 V* w5 h
  48.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    5 O7 N% i2 r' A. I- k
  49.     MPU_InitStruct.BaseAddress      = 0x38000000;4 ^1 g2 v8 k# v! s8 X
  50.     MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;
    * T0 S3 c& J, G$ }# a: L: Q+ r
  51.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;9 P4 \3 T5 i- X  l/ t1 ?2 `
  52.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;# y- O; L$ m1 s1 T4 B# J: Z
  53.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;: j; l, p1 @" b4 B! N/ R3 {
  54.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;: n. s* J; c7 |4 p; T' S: @; S8 v
  55.     MPU_InitStruct.Number           = MPU_REGION_NUMBER2;" C/ \. B0 q# l% M
  56.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;  k5 m. Y9 C# X* L4 p, l! h& V9 w
  57.     MPU_InitStruct.SubRegionDisable = 0x00;
      G; q$ h  s5 h$ X
  58.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    5 N* l+ H9 |- T. K+ e  t

  59. 7 H( u! C  X5 z0 ]" _& @7 h' M
  60.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    6 H/ p# w4 m9 }4 A

  61. 6 A. h* r% H' G+ R
  62.     /*使能 MPU */
    8 m4 X4 t( e; }" F0 g0 e
  63.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);9 f9 f  X5 g! r
  64. }
    * b# z; b* j+ s/ d: K
  65. ' m1 }! S4 P+ }8 s- [
  66. /*$ h) D. S) Y- z; [% E* E
  67. *********************************************************************************************************! o7 i# O5 G& V& h3 e- W6 B
  68. *    函 数 名: CPU_CACHE_Enable
    9 ~; p" l! |% s& r1 t( B2 g( B0 c
  69. *    功能说明: 使能L1 Cache
    % ?* f" ~4 U4 y8 A+ J# R7 a
  70. *    形    参: 无
    ; s/ a3 X' {9 X- I* W: j2 N
  71. *    返 回 值: 无
    4 I# h1 V) G; o: A
  72. *********************************************************************************************************! K* E8 }& ?  W2 X! [( A/ G
  73. */, P5 n  A' l+ m
  74. static void CPU_CACHE_Enable(void)
    # N. n5 K+ @- X$ [# ?9 j
  75. {' a  W) A( P3 |9 z+ R" Q
  76.     /* 使能 I-Cache */: Q; ~7 h: g3 g9 a3 T  m
  77.     SCB_EnableICache();
    : M! z3 G' S) J1 ~- v

  78. 8 A; [8 Y, E3 L- W
  79.     /* 使能 D-Cache */5 V: g+ G* E; ?$ G1 y& O1 x
  80.     SCB_EnableDCache();' h$ ?+ i2 J6 X4 n& m4 H1 ^9 \
  81. }3 j; }* f% e# Y
复制代码

. S$ h8 Z; T) X- b8 ?
2 i3 E, j: M% c$ S& g! g( \6 u  每10ms调用一次按键处理:- Q9 \8 e# [+ K( q6 a) k& e
( B  f: L+ J. X% P- {$ A( j) \
按键处理是在滴答定时器中断里面实现,每10ms执行一次检测。: g: r: X) j1 C0 v( |& L. N1 s

2 }: J2 ^9 F8 Q) L, q
  1. /*) d. E  r7 a9 e& `7 M
  2. *********************************************************************************************************
    ( q3 u: d9 P$ e2 |: d
  3. *    函 数 名: bsp_RunPer10ms
    : t, k- o, T$ y  J) N  l* g0 L
  4. *    功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求
    $ _. E. x8 c# l% A+ x
  5. *              不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。3 G) L$ d# o' x  ]* ]' p# K" p
  6. *    形    参: 无4 ]9 g: E2 v: A, H
  7. *    返 回 值: 无
    ! t' T% S' p$ D0 U2 D3 o: V, K3 p/ k
  8. *********************************************************************************************************' Y, G) G6 n. c: e7 H! r& m
  9. */) @+ m- X, L1 w9 m( D
  10. void bsp_RunPer10ms(void)
    6 }! E$ O$ {$ N3 g
  11. {, D1 D1 D, Z, T5 ^  K6 ^3 ?
  12.     bsp_KeyScan10ms();
    ) e4 ^2 E8 ], ]5 p( B8 I1 ?+ S5 ~
  13. }. j$ s6 J, o. _5 I. p& Q6 [0 d3 |
复制代码
4 q1 J* x& ^) o2 O$ O( z$ j+ T

2 `" C6 ^5 U2 b; u0 @) C8 o0 u  主功能:
) }; }; ]* v$ S8 e4 M1 O/ ]3 \. K7 X8 e( ^
主程序实现如下操作:1 A: l3 ~# r0 a* b+ g5 l

/ e6 I8 ^' |& p4 o  启动一个自动重装软件定时器,每100ms翻转一次LED2。
. H  p; ~6 H5 T  K1键按下,通道1输出方波。
" o9 M# p2 v. Z, [  K2键按下,通道1输出正弦波。
* }+ M0 `& J2 p# g. B  K3键按下,通道1输出直流。; A$ i1 {1 }0 m: F0 i) Z: E! V
  1. /*
    , ^7 G$ @/ g+ l6 \* \4 `
  2. *********************************************************************************************************
    * O% o7 C! G5 f. W  F0 \
  3. *    函 数 名: main
    # ?/ n: M; X5 S  W" k0 Y9 W' X
  4. *    功能说明: c程序入口# W& I) E7 x  p0 n
  5. *    形    参: 无/ w, J$ Y; r2 l
  6. *    返 回 值: 错误代码(无需处理)
    3 P( N& J4 b$ h) j0 Z! q
  7. *********************************************************************************************************
    * q$ [. ^* Q3 M: P4 m; J: U) ~1 q
  8. *// |6 x- g0 c4 [
  9. int main(void)
    2 q! }! y! p  z: J& M9 U( j
  10. {' }; y. q1 W8 I2 T# e
  11.     bsp_Init();        /* 硬件初始化 */& g% r/ ^; H) I; F, i/ T2 d  d
  12. ( B' j2 n' z5 W/ X- q
  13.     PrintfLogo();    /* 打印例程名称和版本等信息 */# }! E* u# l& X3 _# [
  14. % F; ~3 l/ A8 O  J- b
  15.     DemoSpiDac();   /* SPI DAC测试 */, _+ b5 G1 ~1 |% V2 U
  16. }- o% C  ~4 B* Y% ]9 l) K

  17. 8 h- b9 z! O% S
  18. /*9 a. R( `2 _. b" E, u  @8 T
  19. *********************************************************************************************************
    6 n- k! n2 @( L
  20. *    函 数 名: DemoSpiDac
    0 D  g; }" \. j0 [9 d% V0 M7 {
  21. *    功能说明: DAC8501测试
    ; i( l2 J- b! o* c
  22. *    形    参: 无
    3 Y/ i  X7 m6 k. X& @9 j
  23. *    返 回 值: 无1 w% E4 u5 w+ P" E' H
  24. *********************************************************************************************************; ^$ U/ G$ K! e& Z  Q: ^
  25. */# z; X6 W; B$ L8 p% |/ a* `
  26. void DemoSpiDac(void)
    & }) U  _( C1 g5 K
  27. {( |/ V5 L7 E6 n' ]5 V
  28.     uint8_t i=0;
    - D! E9 h- s2 E
  29.     uint8_t ucKeyCode;    /* 按键代码 */3 v9 P5 \1 q+ V& W$ W

  30. ! P* }+ v' ^/ u. i
  31.     sfDispMenu();        /* 打印命令提示 */* r! Q& Z  \/ U/ Y% c
  32. # M. Z( h+ Y  s* Y
  33.     bsp_StartAutoTimer(0, 200);    /* 启动1个100ms的自动重装的定时器 */
    9 C/ n9 @- L# A" ^0 s: l- [
  34. : f1 G4 q6 o' \& o

  35. 7 M- E1 R; V( l7 k0 X
  36.     /* 生成方波数据 */% P/ O. X. x1 f3 ~. V3 W
  37.     MakeSinTable(ch1buf, 100, 0, 65535);& {% a1 N* @- A5 X/ C* r
  38. . Y; q& I8 k" u. i7 T1 r
  39.     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);
    6 F5 B* n4 Z# M2 R

  40. # w# _: q- d3 S, ]1 a5 _% Q
  41.     while(1)
    ! u# a+ ^, i$ p. e* J) k( o6 @
  42.     {. s- C/ V; |& Y( _# u
  43.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */+ l; ?3 O  i# N3 E( [! j$ B

  44. # {0 C4 g; H/ T' U# @5 U
  45.         /* 判断定时器超时时间 */1 V1 C' Q8 H- Y2 H; v1 b! v& }
  46.         if (bsp_CheckTimer(0))   
    $ u' M3 Y' p" q) a6 p, Z' K
  47.         {
    9 K" G( s( M$ z* C; r5 K+ X
  48.             /* 每隔100ms 进来一次 */  
    . [" @8 \) c0 j1 J( q: ]5 b
  49.             bsp_LedToggle(2);
    6 S4 t& @3 I$ }4 ?) ^/ ^( e
  50.         }
    ' e/ o0 x$ O3 ^6 K- H

  51. 5 [* A3 M% \  K0 n
  52.         /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */+ s- \: b6 [1 q/ s, i
  53.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */# n2 }. h- _2 T; u$ m) x( T6 w; o( C9 M
  54.         if (ucKeyCode != KEY_NONE)0 w! h3 t# ^4 j- W
  55.         {
    " q) Z3 R* P2 |' W" v
  56.             switch (ucKeyCode)  I5 `1 v0 m( Z3 Y: R
  57.             {: A  z0 ?! z2 J% S
  58.                 case KEY_DOWN_K1:            /* K1键按下,通道1输出方波 */; t1 g4 f& O9 b+ t. I
  59.                     /* 生成方波数据 */4 K1 O: q$ y: ~6 ~
  60.                     for(i =0; i< 50; i++)
    ! `2 Y9 W. z0 v3 F& t
  61.                     {
    - i6 F$ Y5 C  r* X9 H2 s6 i( U2 H/ ~9 Z
  62. <span style="font-style: italic;"><span style="font-style: normal;">                        ch1buf = 0;
    ' {- P( i) u5 Z: Q# C
  63.                     }9 {* \+ t0 a5 m0 I3 ?; Q* @

  64. 3 I6 A2 @/ w7 m  A' w
  65.                     for(i =50; i< 100; i++)$ N' g6 Z* w; Y- v4 j6 t+ P
  66.                     {
    + g8 N! o2 k. f9 [% B/ T
  67.                         ch1buf</span><span style="font-style: normal;"> = 65535;
    6 j( ^4 z9 M9 ^/ i# o/ m
  68.                     }9 _  ]) Y2 [, y
  69. 0 W# V# y# d) v$ j, j  g7 e' {2 c
  70.                     /* 触发速度1MHz */
    6 M; _+ }. l$ u
  71.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);$ @3 S; c& V: N2 T% t
  72.                     break;% t2 I" m% b4 O! V
  73. % Z. C1 ], a9 L# s
  74.                 case KEY_DOWN_K2:            /* K2键按下,通道1输出正弦波 */
    8 G" |; F* J9 a4 U
  75.                     /* 生成正弦波数据 */   
    ' ^8 @% d. H4 Z" O& F6 W) u
  76.                     MakeSinTable(ch1buf, 100, 0, 65535);
    $ q& d* D, w; s# U  W

  77.   p7 b! L% y% U: H
  78.                     /* 触发速度1MHz */
    . c  m0 L' I/ V% e% D4 k7 i
  79.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);2 d) @: w3 |6 ~. K
  80.                     break;) T: Q3 S& S4 @' L$ B7 g$ `: n, v8 V
  81. + p- p0 [. Q: e
  82.                 case KEY_DOWN_K3:            /* K3键按下,通道1输出直流 */
    + A% ]" D* p1 L' N* v

  83. 6 D; t) u4 C" f( z1 P9 h4 b8 f
  84.                     /* 生成方波数据 */
    2 i; M3 n* B: X. L
  85.                     for(i =0; i< 100; i++)
    2 x. Y# U" R# n  A% q" p
  86.                     {
    # S) D' [& M2 w1 c" J
  87.                         ch1buf</span><span style="font-style: normal;"> = 65535;9 k4 J" x/ ?& h* }4 |! h& L
  88.                     }
    # R" J" U0 V9 |% C" e5 a

  89. ! x& a2 {: [* o* w; x+ o: h" L* u
  90.                     /* 触发速度1MHz */; k$ F7 C) Q! k/ X
  91.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000);  o# n" V$ v, A8 V6 r  D! |( v% F
  92.                     break;+ Q2 A/ b5 ^  I8 o

  93. 5 Q+ J( C$ u( ~' K( O
  94.                 default:
    , e0 o1 E# b; N
  95.                     /* 其它的键值不处理 */
    6 v- m, W0 v# g, l0 S% F
  96.                     break;, O7 r4 N8 G8 c" `, m' v! J
  97.             }
    ) F8 `2 y) B# ~: ]" V0 Q
  98.         }8 H. U& W7 M% i9 M% t
  99.     }
    ' x+ z) i3 {( y/ N  B& i9 f0 P
  100. }</span></span>
复制代码

  T) z( y: z2 d. S% [8 A# I( h% u
" W6 C1 V5 ]5 A9 F1 _" Q3 {1 w, a! C3 J* F  d( i2 ~
75.14          实验例程说明(IAR)
$ h8 S$ w& |( W! r* W8 K7 [6 k注:本章是配套了两个例子的,这里我们以SPI DMA方式进行说明。4 Y. f" _4 Q( f4 l5 D

5 g7 j$ S* C/ m: x; Y  ?% S9 o配套例子:  B: F; v1 ]2 z7 N) E

' n  G/ q( S. o$ }/ W) aV7-054_DAC8501简易信号发生器(单通道SPI DMA方式,16bit分辨率, 0-5V输出)
- l5 S1 c! {( t: ^- B4 Z2 ^1 k- a$ S/ K
V7-055_DAC8501简易信号发生器(双通道SPI查询方式,16bit分辨率, 0-5V输出)' @# s) M+ K) n  l; S1 P

3 c( }- S4 J& W9 h3 a/ |: n实验目的:
! l) ~$ w) K- \, P* T
- n5 |4 T2 l- @学习DAC8501的SPI DMA驱动方式实现。
2 p5 U/ j: X4 V实验内容:) A+ T% F+ c( I5 t
; l1 G. \5 @: i" U( `" [$ f: ^7 N
DAC8501模块上带了两片8501,每片是单通道DAC,片上输出缓冲运放,轨到轨输出,16bit分辨率,支持30MHz的SPI时钟速度。
# e! d' q3 M8 \5 }/ iDAC8501本身仅支持一路输出,而模块上是带了两片DAC8501,其中只有一路的片选可以支持SPI NSS复用, 所以只有一路支持SPI DMA。
; K- I3 G& a: YDAC8501供电电压2.7-5.5V,模拟输出带宽350KHz。/ w* E" r0 E" q, s& ]
实验操作:# k) `  Q6 |" l  [

8 J! g0 V" P8 F$ L% m; Q启动一个自动重装软件定时器,每100ms翻转一次LED2。% h% n. X1 E" y7 ~  u4 r
K1键按下,通道1输出方波。* z+ F) W$ @8 d, V; X% b
K2键按下,通道1输出正弦波。
  ?' I1 ^, l- R9 I: NK3键按下,通道1输出直流。
/ r  Z8 ^4 K2 ~8 o5 H上电后串口打印的信息:
& O# n; B% J3 H# Z8 N5 C; {/ X2 m8 I( z! }: e
波特率 115200,数据位 8,奇偶校验位无,停止位 1。
  b2 u/ W1 a6 m: ~$ a2 U) K1 n4 H1 l! h+ [
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
7 n; F8 N7 h" e& m9 K
+ ]# {) B0 q1 S) U( _: F+ L
波形效果:/ F  d  b# G% R. e) \

& s( j/ _9 i% \- |; Q7 O7 W) H
$ K. d, s/ i% Z9 F* R* s
) V0 c! l$ k) O- e( W6 Y4 {' j
模块插入位置:9 ?( t, E: c% d
- d% B1 x/ k- n4 J/ A/ w. u$ Z
1 u7 @) d1 R& f- p
9 v- k( o# z" n
程序设计:
( R1 e8 Y/ [# u" p2 r4 o* M! Z. g; B( i% L' S
  系统栈大小分配:
9 ?/ Z. G7 B$ J5 j! z. O% ?
& k  m% w: z( L( p! \& V9 {
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

9 B  r5 M, i- ]. s" }) x; t! a" [3 @: j8 K" t
  RAM空间用的DTCM:/ i& ]. z) f5 \. j5 L4 |
# u1 W8 h5 O1 X1 d& ?% X
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
; G+ P1 q! N: d5 b0 A; C2 }5 L
! [2 c9 J& V4 g# A% V' Q1 G% z
  硬件外设初始化
5 }7 _6 R& y) n8 u' p. e: u" R/ G$ s5 Z
硬件外设的初始化是在 bsp.c 文件实现:9 _4 L- R* h- v3 {! c

$ O3 b  \% R1 g( a! j7 x" [1 V8 w1 x
  1. /*# k# L5 B+ B+ q8 ]/ i# q
  2. *********************************************************************************************************
    5 M2 L& C! ~4 u% F9 h
  3. *    函 数 名: bsp_Init
    $ j3 |% s+ b9 R& b) O9 ~
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次4 E; f5 J5 {/ L6 v- k
  5. *    形    参:无
    ( y, c/ Z; k; S* B* d& p
  6. *    返 回 值: 无
    ' A' |) U1 t" @8 Y0 z8 C* L; |
  7. *********************************************************************************************************- \: S, v: g7 |, g
  8. */
    ! |+ v/ \) Q! z8 P- |: g- P
  9. void bsp_Init(void)+ ~) M6 F4 d3 q- Z% {
  10. {
    7 X9 c8 V: r4 i
  11.     /* 配置MPU */
      i0 O, j  [3 f, q# H$ L
  12.     MPU_Config();9 z! H; m: w8 `

  13. + Z4 M- e" h$ L: v  ?
  14.     /* 使能L1 Cache */
    1 F" v; j4 Q9 o' A
  15.     CPU_CACHE_Enable();  E- M/ D; w, N: A! C

  16. : n; F- L. i* `! \( M8 F' s! r, x' C
  17.     /* ; R7 S% ~' i8 s3 |' z
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:! `5 K& c5 `/ v) [" Z. [2 d* h
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。, r' t9 b4 L, ?! F  S
  20.        - 设置NVIV优先级分组为4。
    ) q- v8 m; |( E  O9 Z
  21.      */
      H( f$ y% o% j/ Z
  22.     HAL_Init();/ i+ G2 T4 K& X$ \
  23. 9 S8 z, b7 s, C* [2 I2 ]& C" K
  24.     /* 0 S5 K9 E8 m5 l/ L& O& T; B
  25.        配置系统时钟到400MHz
    / a8 J, u" \+ {
  26.        - 切换使用HSE。
    7 n$ Z8 e! e, h( Y7 s
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    : V5 e* n) y6 `
  28.     */
    6 U3 B2 u% T# B6 `  c
  29.     SystemClock_Config();
    0 r- @6 N' q( s; Y0 h
  30. $ s9 \2 i9 f! F4 _+ J' A
  31.     /*
    6 B% o2 f3 M% Y  U4 ^% E: T
  32.        Event Recorder:
    2 H9 O$ n3 _6 J; }4 D
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
    / F3 d$ Z0 u" B' }
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章
    " S3 V- y8 v4 i' ?
  35.     */   
    7 A; I# O" x% w8 V0 U6 z; ^% i
  36. #if Enable_EventRecorder == 1  ) ]* _" m" R5 j0 b, q
  37.     /* 初始化EventRecorder并开启 */
    5 {5 J' K: v/ B
  38.     EventRecorderInitialize(EventRecordAll, 1U);$ ~- G% y5 ]; F- W% {
  39.     EventRecorderStart();1 K6 P2 Z2 B: C" H& _
  40. #endif
    6 o5 e' W1 }% h$ `7 r
  41. - O5 Z- u7 Y7 C' {0 }9 w1 y8 I& k
  42. bsp_InitDWT();      /* 初始化DWT时钟周期计数器 */      
    6 ~, \6 r; k: p) S
  43.     bsp_InitKey();         /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
    1 f+ h5 z7 r% A2 g# I. i& L
  44.     bsp_InitTimer();       /* 初始化滴答定时器 */
    , c/ F# {$ k0 v  j1 c; n
  45.     bsp_InitLPUart();     /* 初始化串口 */
    " A9 k: ]4 ]9 l
  46.     bsp_InitExtIO();     /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */   
    9 F! }) L# y: L2 z+ P$ P
  47.     bsp_InitLed();         /* 初始化LED */   
    1 O! g% }( I$ C0 x% |% B
  48. bsp_InitExtSDRAM(); /* 初始化SDRAM */! ]( P4 W# S" K6 t$ V% W
  49. * A. o" o  J& a" w
  50.     /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */   
    2 [8 O6 N7 G: N  `8 {4 P
  51.     bsp_InitDAC8562();    /* 初始化配置DAC8562/8563 */2 ?$ z' x* l9 d4 H  U
  52. }7 c. ?6 Q9 |, r: I! }7 ?7 v: A
复制代码
0 w* a/ H+ e- |, v: M

7 O' T7 I) z0 m) d7 T, K& y1 r( b0 d  MPU配置和Cache配置:5 o& @+ p: @/ W* S- R2 j- a

) y- U. Y4 p% P( p$ A4 V数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区以及SRAM4; D/ i" R* t2 a5 `, R

8 X( ]1 c' }- \5 X' g0 r
  1. /*
    7 s9 v( Y, K' }$ o
  2. *********************************************************************************************************
    % Z4 z  I5 ~9 y: B
  3. *    函 数 名: MPU_Config
    ( X) N8 m9 [) T# ^4 k0 G6 w+ S
  4. *    功能说明: 配置MPU  c" h: _+ L- j) \
  5. *    形    参: 无
    - E; I. d- i$ `: {
  6. *    返 回 值: 无. s5 v4 b% O1 D9 x0 W
  7. *********************************************************************************************************' [: d( K6 W) ^* {
  8. */
    , M- l: f& \) ?7 D: B7 u  {
  9. static void MPU_Config( void )
    / f! M% h; _, e. q% W( g: {
  10. {' i' ?' j4 F1 S! d* J, x
  11.     MPU_Region_InitTypeDef MPU_InitStruct;# W- U0 v$ M  K7 G; ?

  12. . B# _, b6 C2 I) N8 ~$ F
  13.     /* 禁止 MPU */: ~8 U& r' J5 E2 k% X
  14.     HAL_MPU_Disable();* w4 t" p: ]$ l- a

  15. * w0 x1 [- n. X' i- w2 X  h4 l
  16.     /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
    # l% f7 i) W+ ~4 r, |, C  ~
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    * p2 X& ^! a4 w1 n  z; U/ S
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;
    8 t4 E1 i. j& C, c3 A# w
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;1 Y7 D* A) \+ ~4 T+ g& A5 b1 q
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    " {( T2 r% ]8 D& \
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;' [- ?0 N3 \, N! r" ]
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;- {% U- q1 q4 `( R; C0 a5 _: s$ R
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;( {) T' \. c3 V" |7 Z
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    4 a6 m. N* S3 {8 g6 ~6 d7 {# H& `
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;" \+ k9 O) x6 C& I1 F. x7 f; |
  26.     MPU_InitStruct.SubRegionDisable = 0x00;9 e. c# ^& b1 w# a" S
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    0 ]8 |0 s! Y# z9 Y

  28. 5 r, ^" f: h' X1 Q
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    3 x2 w3 ~* T) \
  30. 4 t5 |- _. c# R4 L7 y6 [. T

  31. 7 [8 l( d* I2 O( l+ a6 D3 S" _, ^
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
    ! k3 e7 P9 T2 I4 D
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;, E, p! ~4 C" C! L5 Q5 F3 F! _6 Y
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;" [% V0 R( k/ E4 ?9 a" l: x
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    $ }% G7 U: O5 V* \5 x1 K5 z
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;- _- k  U6 i- {. g
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;5 `) T1 y0 Q3 s9 X$ ], H7 C0 R
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    . I5 a" b3 |2 k2 c( e# A/ r
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;# s( E! s2 o  [9 V
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    7 _+ f3 p& M5 Y
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;! x4 o( j6 q1 {
  42.     MPU_InitStruct.SubRegionDisable = 0x00;
    7 g. D& E3 T% F5 T, a3 O2 d+ t
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;2 ^: h( G# s0 s. d

  44. # _0 \9 y$ g# v, T1 v
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    9 E+ t+ j  I/ p5 N4 G6 t/ q& K
  46.   U6 S% J+ d! u. K
  47.     /* 配置SRAM4的MPU属性为Non-cacheable */
    3 `+ V( U8 W. i  i0 B4 w, `8 I. C
  48.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    3 p, s# |/ Q+ j* |0 v* `* S* m/ ~
  49.     MPU_InitStruct.BaseAddress      = 0x38000000;
    , O* H# b  ^/ G6 @
  50.     MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;2 j* c! c8 h# w' g
  51.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;" G+ n6 E' X8 T8 d. v
  52.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
    7 q% \2 R' }7 [+ e
  53.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    * a3 M( t, g4 A% \
  54.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    ; N' c* Q( q; T5 j* l6 Z$ }
  55.     MPU_InitStruct.Number           = MPU_REGION_NUMBER2;( }3 ?# O) q9 D" s- e1 P3 D0 x
  56.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;) F3 _0 }4 n. s
  57.     MPU_InitStruct.SubRegionDisable = 0x00;
    & o3 u$ {! X4 O" e, m9 Y
  58.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;. P3 q! c6 |: e! T8 \+ W1 w" w4 y
  59. 3 |, z0 S/ s4 l/ E; G. ?
  60.     HAL_MPU_ConfigRegion(&MPU_InitStruct);$ z  f5 ?+ N/ y; E  B7 H2 d' K) F
  61. # Z# G: |$ K7 L! T
  62.     /*使能 MPU */
    / h; E4 S1 H& o3 c/ L* q1 t
  63.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
    0 }) a7 ~$ f) |5 ^, ~+ t! M; A
  64. }
    : S. Q- w0 o$ c

  65. % P4 ]/ Z% ~0 p/ `8 E+ e# }
  66. /*5 U, K% I# L  k; R
  67. *********************************************************************************************************
      ]; a: q, ^" u9 j6 H
  68. *    函 数 名: CPU_CACHE_Enable
    8 a1 b2 f) o& O, e# c; I% c4 I
  69. *    功能说明: 使能L1 Cache
    % K% B/ B8 P( ~! R2 b
  70. *    形    参: 无
    . ?+ E4 J  Q; h4 _3 U% C
  71. *    返 回 值: 无
    ( s# ?4 I; t2 |! @
  72. *********************************************************************************************************2 r: T/ B4 q8 F( `
  73. */3 r4 q$ V4 ]! n( j% U% M" k8 U
  74. static void CPU_CACHE_Enable(void)
    ( [- D& I& ^1 P& z6 s0 u, y8 X
  75. {7 D& F- e9 W: [- F1 K2 Y( w0 ~/ B+ m7 m
  76.     /* 使能 I-Cache */' c/ l0 I( [+ t- t
  77.     SCB_EnableICache();
    4 l5 S& ~: w6 h, I/ c6 N6 F
  78. # ~0 y& @/ V/ t7 w1 c8 R& ?/ X
  79.     /* 使能 D-Cache */! y) G; {2 X- m% L) U, C' ^
  80.     SCB_EnableDCache();
    6 F1 o  n# k4 Q: U7 E
  81. }
    : [; n0 b* U' y  s. N* y
复制代码
1 k0 z; S* [' f9 @1 q* K. v
) |4 H% ]7 [: }" C- E
  每10ms调用一次按键处理:+ C  o+ {& b: \3 F. q- c. a
& g1 n, q3 B! E, I6 }$ r( O
按键处理是在滴答定时器中断里面实现,每10ms执行一次检测。
. q( d3 m- }/ U( S6 C6 u3 q" T+ X4 C8 ]& f/ Q
  1. /*
    ! N7 y6 _) _- F- H/ F, w
  2. *********************************************************************************************************" Z# G! v+ A+ [/ Z0 v7 H
  3. *    函 数 名: bsp_RunPer10ms
    ) ^. B  a# b* l7 m5 I1 f* \
  4. *    功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求
    8 U+ j$ X# w/ d8 }1 B' W
  5. *              不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。
    ' y4 w8 j* d2 Z, e0 v8 T
  6. *    形    参: 无/ P' A4 m9 N. D& [' r/ E- C
  7. *    返 回 值: 无5 m. H! m) g% R9 l/ B
  8. *********************************************************************************************************
    6 F5 W! s( S; o- e3 O, x
  9. */
    & z6 f8 f! W( E) V, _7 ]0 R& Y/ O* W& B
  10. void bsp_RunPer10ms(void)
    $ l4 _% g; y4 ~6 y' q0 s
  11. {% j) c' s* A- X. a! k" \
  12.     bsp_KeyScan10ms();! q! q; k7 x; C# U: D3 I
  13. }
复制代码

% y) T' ~/ |! l+ @/ e. M! X$ F( D! D5 u, j3 O4 c

  I$ W. n3 D" ?* F; y* w. _  主功能:$ P# R" g: ~/ H( f1 p( M) X4 A

. T* z2 j5 T8 S主程序实现如下操作:
; U1 e0 t3 }+ D& S  _7 j) y2 l5 ?9 R: I  g; F5 Z
  启动一个自动重装软件定时器,每100ms翻转一次LED2。
1 z1 I% d. T: [( g0 n0 \; V  K1键按下,通道1输出方波。
6 C8 e* O$ @, W  K2键按下,通道1输出正弦波。: J4 z% H# s* ]: D% I
  K3键按下,通道1输出直流。& N$ J/ j7 Z7 x8 [. F) _+ q
  1. /*, c# _1 Z3 h0 o0 m  m# W4 x
  2. *********************************************************************************************************
    5 [5 N% p( l2 [3 n7 F1 o% f
  3. *    函 数 名: main/ d1 o' N" F7 w
  4. *    功能说明: c程序入口6 s# j+ e5 `; p, c
  5. *    形    参: 无; S) F( ~/ U5 j  N! x$ _2 o
  6. *    返 回 值: 错误代码(无需处理)4 {# {) X# ?' V) Q! F' R
  7. *********************************************************************************************************
    8 }- K8 Q: i* W/ r2 h4 U* }
  8. */
    4 _# N$ R! Y& x8 b, x3 }: b3 Y6 b
  9. int main(void)1 a: A* z! x4 S( [& {
  10. {- V: x3 W1 Z% N" w3 ^, t
  11.     bsp_Init();        /* 硬件初始化 */
    3 k  i& q& S* C# U& V: P6 F! Y) }+ s

  12. ; G, P0 [! d$ V( I0 G
  13.     PrintfLogo();    /* 打印例程名称和版本等信息 */
    6 E) Q- ~( U6 L+ {
  14. 3 K8 r* w, G" M5 x, Y1 l$ j8 l% S
  15.     DemoSpiDac();   /* SPI DAC测试 */
    6 e# [  X6 j( W
  16. }
    / ]* _1 T$ ~$ \; [) I% i2 b
  17. : T* O$ ]* R( o1 K6 K6 P- k# g: I
  18. /*
    5 d6 k$ w! C; v% \
  19. *********************************************************************************************************
    ' }* }3 h) R- l3 h9 s+ y; y
  20. *    函 数 名: DemoSpiDac
    ! V- i9 V3 x4 u' T
  21. *    功能说明: DAC8501测试
    # e0 ^# U2 ~) X; Z
  22. *    形    参: 无: W% q, l+ ]+ x# U6 m
  23. *    返 回 值: 无
    7 F1 F" I1 f1 ]: |# E4 p
  24. *********************************************************************************************************
    9 A: d, H) q, }
  25. */
    + @7 J0 M3 J( G% G, N0 I
  26. void DemoSpiDac(void)6 S8 g$ x$ {# }$ L. v( `5 |
  27. {, \! [: V: i8 r% P' v- z
  28.     uint8_t i=0;
    " t! ^+ O3 F6 p$ @4 d# F5 w5 l
  29.     uint8_t ucKeyCode;    /* 按键代码 */
    9 _9 j7 q; q/ G# S& I7 Q" Q

  30. " @9 s5 Z% H% [! o# w& U
  31.     sfDispMenu();        /* 打印命令提示 */
    / n& g0 W6 Z! L8 c/ F
  32. & S, f) k2 t* _( Z# t
  33.     bsp_StartAutoTimer(0, 200);    /* 启动1个100ms的自动重装的定时器 */
    7 |! X6 g/ k. R) d/ e8 V- W
  34. 5 ~8 `1 l: U: O" t5 m( z6 m
  35. ; o+ h7 Y# o0 e5 U* C: A4 h' Z0 j
  36.     /* 生成方波数据 */
    7 a! j- T, s% ^( k0 k
  37.     MakeSinTable(ch1buf, 100, 0, 65535);$ ^+ W. s6 J* L: M" ?# F9 }

  38. . K$ J7 C$ U( ^, ^- e
  39.     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);
    % |1 @" ?- P6 n+ f# p/ E% L6 ^4 T0 J
  40. . t9 t' @$ Q! y5 O6 W
  41.     while(1)6 v% J5 a3 a7 i4 U8 @) N
  42.     {# C4 O) |* d' M! ]9 h
  43.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
    7 A2 c( H! [0 m3 p5 q. m$ _
  44. # T6 w+ z* j5 Y2 L5 `0 U1 `& l
  45.         /* 判断定时器超时时间 */
    5 h% ^: H: `2 B
  46.         if (bsp_CheckTimer(0))    2 z& |5 f- [' M- D  n1 B5 l: `
  47.         {* c1 \) X& ^$ p! f. J/ O! Z5 S4 M, H6 T
  48.             /* 每隔100ms 进来一次 */  
    - p# O0 z  M- P4 i$ [9 I/ q% U
  49.             bsp_LedToggle(2);- R2 I! N7 Q4 X% p
  50.         }# p6 A1 s" l" {
  51. 8 d$ j0 f/ X' y7 O( ]
  52.         /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */. j, n7 E1 @3 H6 m3 O
  53.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
    " x5 k7 J7 m' A/ Z
  54.         if (ucKeyCode != KEY_NONE)
    4 x1 u/ l( @6 i8 J, I, u. W
  55.         {- i) L$ n8 Q+ O! i6 Y
  56.             switch (ucKeyCode)
    # G  t# a% x1 E, q3 V$ W! y
  57.             {
    4 u/ I  D6 }7 v# }
  58.                 case KEY_DOWN_K1:            /* K1键按下,通道1输出方波 */) j4 E$ r: E' s% [
  59.                     /* 生成方波数据 */; t3 E# Q  A0 X6 |1 Y  r- z
  60.                     for(i =0; i< 50; i++)$ |/ G  p- G& u7 n" |5 M1 N
  61.                     {
    # y/ C* J, a. n& x5 L; @7 f
  62.                         ch1buf = 0;
    4 F+ X; [% }+ m  w2 a' m8 I
  63.                     }, |& t. [0 z3 P3 c( L# R
  64. ! G! T6 k0 i2 L2 R/ P8 U5 H
  65.                     for(i =50; i< 100; i++)
    ' W% v* A- W, N2 ?6 n
  66.                     {
    ) L' l* \6 b* z* ^' c" Z
  67.                         ch1buf = 65535;
    ; L; }& N- y& J2 x
  68.                     }6 v' n+ a' C* n' E( p

  69. * F  R3 u4 U+ m" k. }) r
  70.                     /* 触发速度1MHz */3 p( ]) F) D' B
  71.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);% A4 U, f+ \7 ]. a( l+ ]
  72.                     break;4 X8 \  t8 Z/ r9 b' Q6 X! M6 y

  73. ) U7 J: g& G7 i3 ~; `$ u  v
  74.                 case KEY_DOWN_K2:            /* K2键按下,通道1输出正弦波 */
    ; w' a7 E" U, }
  75.                     /* 生成正弦波数据 */   
    9 _5 R& V5 N. V7 G% J' }
  76.                     MakeSinTable(ch1buf, 100, 0, 65535);1 W1 `* T, Z9 A

  77. ( H2 o9 A3 b% a: e+ R, m7 @
  78.                     /* 触发速度1MHz */
    8 G* Y; k* }6 `& Z! c6 f+ Y
  79.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);: U2 v7 h/ B/ V, W% R; |8 ^
  80.                     break;
    * |# p% Z. g! z

  81. # S+ G9 x+ K3 q
  82.                 case KEY_DOWN_K3:            /* K3键按下,通道1输出直流 */
    " e% W. M" W" m2 Q: N+ T0 m! |
  83. . _, @( r4 G' i5 p8 E  s1 v" O  q. I! \
  84.                     /* 生成方波数据 */; H+ g+ p+ n- l% |, v7 X
  85.                     for(i =0; i< 100; i++)7 ^0 V- p( R# _+ J+ y
  86.                     {7 T% R* Y$ x* ?9 q- G4 H2 f
  87.                         ch1buf = 65535;4 u0 T2 c, p' t
  88.                     }2 A$ v$ F! E- B7 s

  89. , {2 G# T+ N: F- b- M3 j. q- s  k
  90.                     /* 触发速度1MHz */( H; D1 M0 K4 \* l6 ~& S
  91.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000);8 L) J- {; b3 Q9 [2 G3 I! d
  92.                     break;
    4 X% J9 l8 Y& w% ~
  93. 3 B( K7 [; R: y& e' L
  94.                 default:1 \6 s3 c' h8 V% u/ e3 n7 f, e- l7 ~
  95.                     /* 其它的键值不处理 */$ \& o7 K$ v" r6 |" s: c, k( T
  96.                     break;
    / k4 N4 D/ K8 U/ I5 s' M
  97.             }) e) V9 d* @5 G8 ^- R
  98.         }' P1 |, y' b, {
  99.     }
    9 I% {3 R( g# E" U9 g3 p
  100. }
    # {; B, N$ y! L
复制代码

% d2 t8 K# U3 h7 L& Z, o+ C/ J& m; Q' [3 G
75.15   总结$ s, Z5 ]. L" W
本章节涉及到的知识点非常多,特别是SPI DMA方式驱动的实现方法,需要大家稍花点精力去研究。3 h; S6 ~' q( J  a9 ^# Y( N

! l2 K8 p% S! D- L6 m; \& y; O2 F' p) c
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
收藏 评论0 发布时间:2021-11-3 10:44

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版