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

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

[复制链接]
STMCU小助手 发布时间:2021-11-3 10:44
75.1 初学者重要提示$ w" Y( ^- S0 z6 V2 i& M
1、  学习本章节前,务必优先学习第72章。
1 E7 P/ N* F! j4 ~: p* h
3 X& C: {4 o2 A  z- d2、  DAC8501模块上带了两片8501,每片是单通道DAC,带片上输出缓冲运放,轨到轨输出,16bit分辨率,支持30MHz的SPI时钟速度。
3 L# P4 \- A$ _) K' P# X9 b( Q* g: J( O! n: q" I0 p8 s# W
3、  本章涉及到的知识点比较多,需要大家掌握STM32H7的SPI , DMA,TIM,DMAMUX和DAC8501的一些细节用法。8 Z- [: f4 _- i& x. E" S
5 S% z, z8 `$ R) t- b6 w
4、 H7的SPI + DMA驱动这类外设的灵活度,绝对可以媲美FPGA去控制:
8 @3 N2 s& H# Z" R2 h7 R' j" b4 x3 Z2 r- d
  H7的SPI外设比F4系列的灵活性强太多了,主要表现在两个方面:数据的传输支持了4-32bit,特别是那个NSS片选引脚,超强劲,可以做各种时间插入,灵活应对了市场上这类芯片的需求。
+ ]! f/ b! t2 m+ M  DMA这块相比F4系列,有了质的飞跃,支持了DMAMUX,这个DMAMUX除了带来灵活的触发源选择,还支持了各种触发事件和同步触发功能。本章配套例子的触发周期控制就是利用了DMAMUX的同步触发功能。
% c; g# P1 a/ u+ Q" w" d0 u; f( u5、  本章配套了中断和DMA两种更新方式的案例,DMA实现方式与中断更新方式完全不同,因为DMA方式要使用硬件SPI1 NSS片选引脚驱动DAC8501。而中断更新方式使用公共的总线驱动文件bsp_spi_bus.c,片选是通过通用IO方式控制,支持串行FLASH、TSC2046、VS1053、AD7705、ADS1256等SPI设备。大家在看例子的时候要注意。
9 G5 `" o) E8 N. y4 U1 a* W& o: S
6 Z0 p2 f9 u# {" m3 c- \6、  对于本章教程配套例子的SPI DMA方式,这里特别注意一点,定时器触发一次,就会让SPI以DMA方式传输24bit数据。- P; d+ v+ _2 n7 B9 u
( _; T# j. G# P
7、  DAC8501数据手册,模块原理图和接线图都已经放到本章教程配置例子的Doc文件里。8 \5 P5 q# [! w* t3 {
( E7 [) z# t& g3 e; `3 c
75.2 DAC结构分类和技术术语" }6 R2 q8 O* z' ?  i& S
在本教程的第74章进行了详细说明。
5 r6 L8 _; b; G/ T' \. ]
$ d. u- J2 {3 z0 v) k6 k% N1 B75.3 DAC8501硬件设计0 F1 `" P$ F  g# A0 S
75.3.1 DAC8501模块规格
1 ]7 U5 ]" e/ Q( ~产品规格:
5 O0 W8 _3 F' Q5 S  x& r6 T' q! L5 u
1、供电电压: 2.7 - 5.5V【3.3V供电时,输出电压也可以到5V】。
9 O" ?' h" L* ^2 o# F, n4 ~  g9 G& a
7 t" F* k3 L+ M$ i6 N2、通道数: 2路  (通过2片DAC8501E实现)。
+ d$ Q3 X" i1 i0 z% I) h; Y4 e4 @3 a) b8 @8 ?
3、输出电压范围 : 0 - 5V【零位 < 0.020V, 满位 > 4.970V】。$ m0 Q8 ]( {  A
8 X% O9 s& \- O/ T. o
4、分辨率: 16位。
1 w  }+ q+ y8 I3 i4 O
0 H9 G* v2 q: y5 J) H6 E* i7 Q, r5、功耗 :  小于10mA。9 `" H1 S1 w7 }7 \% H. w: a
2 ]$ _: J7 [! {* u% T( X
6、MCU接口 :高速 SPI (30M) 支持 3.3V和5V单片机。' y) }! Q# e3 ?" A2 I  w/ _- D

% C8 k5 h# T4 \9 |. o5 q6 R5 D7、DAC输出模拟带宽:350KHz。2 n6 J2 A# ^; F8 M! n& e# D
$ X! _/ v, d( H" q5 U1 B) G
8、DAC输出响应: 10uS 到 0.003% FSR。  |  ^/ y4 E+ T' q4 L4 ]4 b0 w

5 g, Y0 }* X7 S/ p产品特点:
; Z- K. M9 T  q3 N2 p2 J- j+ k
0 [7 F7 o. f! |5 x1、输出和供电电压无关;模块内带升压电路和5V基准。
- x& ^! m. R7 \8 @- L% K4 Y2 ~4 L$ l8 I9 F' O
2、自适应单片机的电平(2.7 - 5V 均可以)。4 }, W' D. |) Z* E* r4 N$ d
) v5 N- Q6 b# w6 p
3、输出电压轨到轨,最高电压可以到 4.970V 以上。
) E# }- y( D9 p7 s* i# T2 R+ }" ]; a* D# r% Q  }
产品效果:, B% m$ w5 T1 t0 f8 r& T
+ R' z+ }* K9 n2 {9 u# v4 U2 V
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

0 _, s( w' T  O/ ?( @" \
' `; n8 x7 g3 W2 d9 H# Q' E( f
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

6 x: c4 E9 W5 ?( u2 \$ F. i
% j0 q7 U& M) i4 N75.3.2 DAC8501硬件接口# o+ F5 Q% @1 D7 w) J) }
V7板子上DAC8501模块的插座的原理图如下:
: _7 Z" q, N! G  z( w* {! R* i+ b2 J9 g" m
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
" y4 i% e- ?; G1 w( s! e# x, J

! ^3 \! s" G4 W. n实际对应开发的位置如下:; A  N( i7 V% k  ?# g
4 `: z- z& u0 a. s3 r) D
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

/ d% L, L; s% |. B% E
, R6 d: R, `" t5 R  Z: z75.4 DAC8501关键知识点整理(重要)
9 W' @$ }2 T8 H! r8 i9 |驱动DAC8501需要对下面这些知识点有个认识。" i# C4 D3 h, _- G6 }3 b, O; z3 H% e
1 s+ g# @( j& l3 M* \
75.4.1 DAC8501基础信息8 @2 S8 H! ]" G9 L3 G
  单通道DAC,带片上输出缓冲运放,轨到轨输出,16bit分辨率,支持30MHz的SPI时钟速度。
. P* [8 D! V, _5 [9 Q% \- d  模拟输出带宽350KHz。' ?. h% K' w' {, M6 c
  供电范围2.7V到5.5V。
- C4 D: K/ l) y4 k  具有低功耗特性。) O6 @# _# p9 J3 c  O, }1 V
  上电复位输出0V。" t/ I+ ?* B  f
, ~8 ?/ o3 |3 y$ m/ i# V/ \
7 n2 X* j* i  U" f
75.4.2 DAC8501每个引脚的作用4 [5 z" s/ r6 d! [- J  D" T
DAC8501的封装形式:/ N  z; B7 t  }) j" r# Z

; ^  R4 ~5 i% Q9 ?, U
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

& b: n; B. m) A" R/ Z. \+ x: U2 j2 a7 d
  Vdd
  Z1 w; G7 d( D' ~供电范围2.7-5.5V。& ?. {6 ^* E; L% u; C1 ?- u
2 z* I# m  u& u
  Vref
4 p+ @( u; a9 W稳压基准输入。& ~' F5 ]4 L+ C+ K
: S- t4 x& q; u
  Vfb
2 A/ H& W: {8 A2 j: M5 m$ O+ K3 r输出运放的反馈。
+ I3 A( `8 ?7 p; I% v: m* a: T# p- X
  Vout
8 K. y# v6 \, X模拟输出电压,输出运放具有轨到轨特性。! l% Q  h% a5 |; T# I9 I, H' W* K

9 Z& d6 _+ r! a5 N$ C  SYNC (片选)5 l  N6 k; T9 K; {) N% @
低电平有效,当SYNC变为低电平时,它使能输入移位寄存器,并且数据采样在随后的时钟下降沿。 DAC输出在第24个时钟下降沿之后更新。 如果SYNC在第23个时钟沿之前变高,SYNC的上升沿将充当中断,并且DAC8501将忽略写序列。
# S' J& a! p2 j( Z( k
; q1 v! W4 A8 {; t2 Y5 c7 q  SCLK
( f* @  T1 \7 ~1 ~  X* `% ^时钟输入端,支持30MHz。
& J9 y& t, C* d$ A* t* P5 p' t- i
  Din3 \3 P5 _$ @  g, o' w0 F  Z- [
串行时钟输入,每个时钟下降沿将数据写到的24bit的输入移位寄存器。. G) f7 [$ S3 z* @3 w5 }% x7 T

) L4 G+ x% m0 {0 w  GND7 E6 o+ }4 g( j7 ^6 e
接地端。
8 d: j2 H& _: n4 s" M! u8 b5 M$ x8 h6 M$ {9 z: v
75.4.3 DAC8501输出电压计算公式
/ m# d" Q6 B, M9 _" W2 HDAC8501的计算公式如下:
8 @& c" g0 i0 X  q- e
  J" q/ ?1 j- C6 c$ l% J+ u
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

: N- N- p' \$ ~8 i, W! `
# }" L  P5 ~+ H. ~  D
- _( M) ?1 ]! c! \7 A6 F. l7 g  z配置DAC8501数据输出寄存器的数值,范围0 到2^16 – 1,即0到65535。6 ~# V/ V2 w4 h! P( c& h5 j5 ]( Z

4 f. M2 A2 v6 @: d* Y  \0 a  VREF
1 G3 a( V8 R) ]使用外部参考电压,由VREFIN引脚的输入决定。+ i6 C) w5 H! r0 ~

) W. F# m5 e+ ^4 K3 h& T  Vout
3 R1 x7 Q) T: @; b, a输出电压。
6 e6 @( W! v4 B" Q" p+ C0 R. d- M( c8 k" A8 `* G/ k: ^0 T# p0 h  p
75.4.4 DAC8501时序图
& E: C: J, ^' t3 H7 }. ~DAC8501的时序图如下:
& G  X0 b6 Z& b0 H! Y# f, l
# ^) d/ q2 B* j7 h: L0 W
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

) s5 Q) j* c! T8 N* Y
; {6 X8 b7 M# a% j, {1 n6 L
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
0 W% h( A7 r# q' c) L/ a' l! v
1 w# F3 c9 Z9 B( N3 y, Q0 }
这个时序里面有三个参数尤其重要,后面时序配置要用到。
) L* ^* I$ q7 ?' B: A% X
* @8 m$ l9 C6 j& E# j  f(1)
  C) r4 Z! b4 \, k+ Q供电2.7到3.6V时,最高时钟20MHz。
1 c2 o( Q$ h" n& x
+ s- g' e0 X' f供电3.6到5.5V时,最高时钟30MHz。$ M4 F2 @% T4 i, T# h

. e* @: @& f6 |- O7 s, s1 Z  t(4)
* ~, p% a5 E& j/ c9 j9 d- P( i' {SYNC低电平有效到SCLK第1个上降沿信号的时间没有最小值限制,可以为0。
, D* N& |8 u7 W; `! |5 B1 a. ^
/ f5 b  a7 Y- x& j- l7 x  t(8)
6 x. u) J" N0 b3 r每传输24bit数据后,SYNC要保持一段时间的高电平。7 G2 a6 Q3 |! N

. K! G$ N% \2 a( r( [+ l供电2.7到3.6V时,最小要求50ns。
7 G, L# Y8 v  [6 k1 p: |. y- d- C9 V
供电3.6到5.5V时,最小要求33ns。& @4 V8 a' P' C8 s, I  {. K: W9 ?
% b! u: y* _2 \3 b& ?
75.4.5 DAC8501寄存器配置+ Y4 G8 \: m- d* _5 n% p. |
DAC8501的寄存器配置是24bit格式:, |) l- y$ `# {1 h
/ f; l. O# k7 I% S0 T, i! `
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
$ N( [# S5 ]9 [4 Z+ N! o
% e0 j+ _) k9 e: \- _% T9 W6 ~2 {7 ~
控制DAC8501每次要传输24bit数据,高8bit控制位 + 16bit数据位。3 \- ^- j$ F: h- q. E

; H. g/ r! r9 i% e: x$ G& H0 S6 R' l控制位的PD1和PD0定义如下:4 x3 h0 I) F+ k; t2 i8 X/ O) P. K4 ~

: S% X% |7 c/ v" I
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

5 L) s5 c3 T# f. \: }* B1 \; n9 ^# G/ {, L' b
PD1 PD0 决定4种工作模式  T& T( {- c* B- D6 X; }

! M5 c* r4 n! @/ F& N- z0   0  ---> 正常工作模式7 }+ A' z  ^+ i, i8 g+ A2 F

6 n5 w5 I8 R( v' o/ k0   1  ---> 输出接1K欧到GND* ]5 @, v# r2 E$ {. r
" S0 `! b# H" i$ l$ Y
1   0  ---> 输出100K欧到GND
1 J" H5 R$ i! Q6 M: R# a; f" `1 F
4 U& ]9 u, L0 s9 p  g2 q1   1  ---> 输出高阻4 s. B; H7 H; B. A4 z& R

2 a3 {& U0 A1 P75.5 DAC8501驱动设计(中断更新方式)
; f. l5 p  L' R0 M/ J& eDAC8501的程序驱动框架设计如下:. t# C4 G- J! w% d4 ~. z
% E+ i7 o! }+ u; ]
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

1 ~+ L! @+ N& I7 R4 ]  H2 }. V0 a# a) X4 w# s
有了这个框图,程序设计就比较好理解了。: U( H) i! k- |5 ~

6 }* e/ \, h" C" a/ @5 i75.5.1 第1步:SPI总线配置
0 H% l1 M: v$ ~$ v# M% B1 e# gspi总线配置通过如下两个函数实现:
8 v! g' Y( W; v/ Z  `: U+ {2 l. }% g$ G& ]8 j$ S. G
  1. /*" D, P5 D! v5 L# W. F4 w4 r
  2. *********************************************************************************************************
    0 a) ?% X4 C8 D; T! `' u; f
  3. *    函 数 名: bsp_InitSPIBus
    $ _7 E* q+ W  O. W  c/ ~
  4. *    功能说明: 配置SPI总线。
    ( G0 ?% P% H2 b' i
  5. *    形    参: 无
    0 ?, R: [# U" D  I8 d1 o2 q) Z5 K
  6. *    返 回 值: 无
    8 ]* s1 h( h3 H
  7. *********************************************************************************************************
    9 ?" |; L4 T: N: H6 V
  8. */3 `9 v8 }/ ?/ \9 Y2 Q$ U) m
  9. void bsp_InitSPIBus(void)% x7 R( i; ], S
  10. {    . S, \& \8 n6 h" b
  11.     g_spi_busy = 0;
    ' {( S/ S4 @7 R4 p( I9 d: M& c
  12. $ z! @( j, m1 V: y2 a8 o6 K( H3 _! s
  13.     bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_1EDGE, SPI_POLARITY_LOW);
    0 S# P; Z; f+ `- s) M$ n4 N
  14. }
    : Y7 d* n  J$ P
  15. 2 b4 M9 q% }& @+ h
  16. /*
    4 ^0 ^, ]+ u5 X2 L  E
  17. *********************************************************************************************************
      c' y, R+ K4 S
  18. *    函 数 名: bsp_InitSPIParam3 X( v+ I3 _! z. ^1 P
  19. *    功能说明: 配置SPI总线参数,时钟分频,时钟相位和时钟极性。
    / i0 x6 {' k. p
  20. *    形    参: _BaudRatePrescaler  SPI总线时钟分频设置,支持的参数如下:8 O8 w5 U5 D: L# W% }% t0 ~/ s
  21. *                                 SPI_BAUDRATEPRESCALER_2    2分频
    / R' v" U( z2 T) q( g8 }
  22. *                                 SPI_BAUDRATEPRESCALER_4    4分频
    1 P( o. N. x, \0 e6 [9 A
  23. *                                 SPI_BAUDRATEPRESCALER_8    8分频% l/ n  t5 }+ S0 O* E% v- Z
  24. *                                 SPI_BAUDRATEPRESCALER_16   16分频- l2 r2 y. E5 t4 ?+ V8 {3 I/ s
  25. *                                 SPI_BAUDRATEPRESCALER_32   32分频% e! x4 D- A% x5 P
  26. *                                 SPI_BAUDRATEPRESCALER_64   64分频
      O0 i5 S3 W/ p+ a; N5 _
  27. *                                 SPI_BAUDRATEPRESCALER_128  128分频
    # j# ~8 ^% }5 s& v% g# r9 x
  28. *                                 SPI_BAUDRATEPRESCALER_256  256分频' Z3 }7 f. M- ]: i9 J
  29. *                                                        
    & \% ?  g% V" _6 n
  30. *             _CLKPhase           时钟相位,支持的参数如下:
    ! `7 i  \/ s$ K
  31. *                                 SPI_PHASE_1EDGE     SCK引脚的第1个边沿捕获传输的第1个数据0 D( z  b1 `# B* n
  32. *                                 SPI_PHASE_2EDGE     SCK引脚的第2个边沿捕获传输的第1个数据2 E" z1 E) P/ G# C* i- S3 N( A. L$ K
  33. *                                 
    " t$ }8 U9 u! x+ o- k8 n
  34. *             _CLKPolarity        时钟极性,支持的参数如下:
    9 U2 n$ Z# n5 o& b& {
  35. *                                 SPI_POLARITY_LOW    SCK引脚在空闲状态处于低电平; L! P6 j" F8 G' k+ `! v8 _
  36. *                                 SPI_POLARITY_HIGH   SCK引脚在空闲状态处于高电平
    5 \1 r, H' b3 ?. I% ]8 U
  37. *
    ' F6 q- }" Z, l8 ?) F
  38. *    返 回 值: 无& A; k9 |! ~9 i% V- A1 s+ Z
  39. *********************************************************************************************************- x$ W: g  U/ A/ d. }8 \
  40. */
    " V& k5 N! h- p# @
  41. void bsp_InitSPIParam(uint32_t _BaudRatePrescaler, uint32_t _CLKPhase, uint32_t _CLKPolarity)6 q, D6 Z  o& c
  42. {
    $ y0 j/ r2 @) u* ~. p
  43.     /* 提高执行效率,只有在SPI硬件参数发生变化时,才执行HAL_Init */
    - p% \8 z% ^+ I1 i$ P1 Q3 y4 o
  44.     if (s_BaudRatePrescaler == _BaudRatePrescaler && s_CLKPhase == _CLKPhase && s_CLKPolarity == _CLKPolarity)- W) ^3 B: {( W7 ~' H0 A
  45.     {        . Z1 b1 B+ c& \7 k4 I6 T
  46.         return;, t1 o& ~% j# Y( [0 R
  47.     }
    / x  ?' i/ V/ [8 b% t

  48. ( R$ V3 o' T& b" s* _
  49.     s_BaudRatePrescaler = _BaudRatePrescaler;    1 ]+ s% Z9 K! ]3 R- j  G; Z5 S
  50.     s_CLKPhase = _CLKPhase;
    3 k7 ]; D% w# u& F5 b/ l8 a
  51.     s_CLKPolarity = _CLKPolarity;
    8 r! @8 j& l, V2 N6 o2 E
  52. & u, u' N- h8 q% ]  i' R
  53. . ~2 L9 \7 k7 D
  54.     /* 设置SPI参数 */
    3 h/ ^/ l- b4 ^! e* c1 R% J
  55.     hspi.Instance               = SPIx;                   /* 例化SPI */0 g  o# A8 ]0 ?6 y3 X
  56.     hspi.Init.BaudRatePrescaler = _BaudRatePrescaler;     /* 设置波特率 */9 ?& g2 ^: u" \/ K, A1 p
  57.     hspi.Init.Direction         = SPI_DIRECTION_2LINES;   /* 全双工 */" B- Z; t. Z9 |3 S" _
  58.     hspi.Init.CLKPhase          = _CLKPhase;              /* 配置时钟相位 */
    # |1 d& I- l$ f
  59.     hspi.Init.CLKPolarity       = _CLKPolarity;           /* 配置时钟极性 */
    ; r- v$ B  J) V; Y( u
  60.     hspi.Init.DataSize          = SPI_DATASIZE_8BIT;      /* 设置数据宽度 */8 j' U- w$ W( l! P' \
  61.     hspi.Init.FirstBit          = SPI_FIRSTBIT_MSB;       /* 数据传输先传高位 */1 g: X6 K8 }$ m* I& F  e$ A
  62.     hspi.Init.TIMode            = SPI_TIMODE_DISABLE;     /* 禁止TI模式  */
    + F& X3 r1 V8 N3 U, C% h
  63.     hspi.Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE; /* 禁止CRC */
    : x2 c8 `8 s! K
  64.     hspi.Init.CRCPolynomial     = 7;                       /* 禁止CRC后,此位无效 */
    * _% R  E* P7 r' j$ t
  65.     hspi.Init.CRCLength         = SPI_CRC_LENGTH_8BIT;     /* 禁止CRC后,此位无效 */' J5 A: A8 T" j% W0 a8 C
  66.     hspi.Init.NSS               = SPI_NSS_SOFT;               /* 使用软件方式管理片选引脚 */
    3 x. k8 z0 l: _) E, g
  67.     hspi.Init.FifoThreshold     = SPI_FIFO_THRESHOLD_01DATA;  /* 设置FIFO大小是一个数据项 */$ f/ [: A1 Z7 x8 \" _! D! M( Y6 t
  68.     hspi.Init.NSSPMode          = SPI_NSS_PULSE_DISABLE;      /* 禁止脉冲输出 */
    + i6 V8 |- ]5 {' g8 s9 X3 {8 s
  69.     hspi.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE; /* 禁止SPI后,SPI相关引脚保持当前状态 */  7 Q7 d: N9 Q! I
  70.     hspi.Init.Mode             = SPI_MODE_MASTER;            /* SPI工作在主控模式 */7 G  `6 o, E0 w; `; B) B7 M

  71. 0 Q% q- S9 Q; Z* f
  72.     /* 复位配置 */' [5 g% c2 C( K: j
  73.     if (HAL_SPI_DeInit(&hspi) != HAL_OK)
    4 R. O4 x9 v8 Y7 o  _. I8 p
  74.     {
    - z; {, d( L: C0 E" D3 ?* `
  75.         Error_Handler(__FILE__, __LINE__);
    - M3 N  K( H3 x1 H. O
  76.     }   
    & N0 s  C( R+ h- Z% X

  77. * [# P! r+ b% m9 Q
  78.     /* 初始化配置 */6 ^7 x, k# N: ?0 ~& }6 \
  79.     if (HAL_SPI_Init(&hspi) != HAL_OK)
      \' B. C; K' q4 `
  80.     {7 t2 y0 C$ l" s8 l9 m0 O3 M
  81.         Error_Handler(__FILE__, __LINE__);% j+ E- T4 n4 v* o. f+ K6 D
  82.     }   
    7 n. S$ ]. s8 c" _
  83. }+ o, `+ b( K: Z$ U
复制代码

, @2 F& U; T" F! ?
6 a* `0 x, G) x关于这两个函数有以下两点要做个说明:
9 ~4 O/ F! j6 F/ _) @  U0 I  `, h: A7 k  e
  函数bsp_InitSPIBus里面的配置是个初始设置。实际驱动芯片时,会通过函数bsp_InitSPIParam做再配置。
0 ?) V7 T. Z' }2 E# d) S, D  函数bsp_InitSPIParam提供了时钟分频,时钟相位和时钟极性配置。驱动不同外设芯片时,基本上调整这三个参数就够。当SPI接口上接了多个不同类型的芯片时,通过此函数可以方便的切换配置。6 B, i9 m+ X6 o! j' Y& F3 O9 h! O' m  J
75.5.2 第2步:SPI总线的查询,中断和DMA方式设置/ B# A; p6 ~, K1 d% |
注:对于DAC8501,请使用查询方式。
: l' k" Z$ v0 l
: m( R( Y! l0 s. ^SPI驱动的查询,中断和DMA方式主要通过函数bsp_spiTransfer实现数据传输:
4 G! e, X3 r  [. v2 i& A- P! e0 ?: N" o" m+ `- G8 ^
  1. /*# J( h# h! z1 B4 O0 `
  2. *********************************************************************************************************4 F% {* c% _3 Z" ~2 K5 U
  3. *                                 选择DMA,中断或者查询方式
    3 W; p8 D1 l3 O, ?- s1 g/ Y% f8 z
  4. *********************************************************************************************************9 Y9 f& C" D. @; n' R  n1 T& _
  5. */- }1 e1 M, B% \1 f2 X( Q! R
  6. //#define USE_SPI_DMA    /* DMA方式  */
    + H, d2 u$ C$ y1 @: k  D' C. N
  7. //#define USE_SPI_INT    /* 中断方式 */
    3 M. a1 D( i7 {# S
  8. #define USE_SPI_POLL   /* 查询方式 */
    ) }; \: P7 ]7 I& F' g/ V) x

  9. . O0 e  q7 D2 m# F- F( V7 i: a& r
  10. /* 查询模式 */
    - g9 N5 f$ @- F
  11. #if defined (USE_SPI_POLL)
      h1 |3 O8 i3 N1 N+ r

  12. ! [6 @: A# J$ ]- G3 ?
  13. uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];  0 U7 i6 L8 N! F% t% I. k
  14. uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];
    , D/ d' ~& T6 r& o3 k+ y! I
  15. + A% H7 A" ^! J  r
  16. /* 中断模式 */
    * Z# ^* D" |5 S/ z0 L$ a' T9 `
  17. #elif defined (USE_SPI_INT)& A& k. L( U7 }) u7 t+ F/ }) f

  18. 9 a1 s# M& d" M  N: x
  19. uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   
    0 C6 a' z: a  T5 @
  20. uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];# k/ o% V, B- T$ D% q: ~

  21. 0 D. I1 q$ }2 @5 L, \. L5 I
  22. /* DMA模式使用的SRAM4 *// l8 P4 y4 T: ~& l* r8 ^( f/ _
  23. #elif defined (USE_SPI_DMA)
    " E! ~6 x- I. w' }6 [5 N- f5 w  a
  24.     #if defined ( __CC_ARM )    /* IAR *******/
    1 `' A; q* g( F( e' @  f/ o
  25.         __attribute__((section (".RAM_D3"))) uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   & Q: ]4 _- T( K  v
  26.         __attribute__((section (".RAM_D3"))) uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];# T: S2 ~$ ?5 K2 G$ l. q
  27.     #elif defined (__ICCARM__)   /* MDK ********/
    ' U( _) f0 i! f, l4 ?% `5 J
  28.         #pragma location = ".RAM_D3"
    + m$ F: v4 P9 X8 \4 q
  29.         uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   : U' f( t$ g$ X% p% i
  30.         #pragma location = ".RAM_D3"
    # I% C9 L6 M- i$ Z+ s: A
  31.         uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];! X% C4 g% F6 b* {1 c6 R
  32.     #endif
    , V  v# o5 t9 o$ z: `" p) r$ B
  33. #endif
    6 l+ a2 Y3 ^4 M$ f: F

  34. ! l* h+ D) e/ }
  35. /*$ d; V4 L5 h1 `' S4 p  G5 B
  36. *********************************************************************************************************6 L& s" j. |7 r$ Q! I9 B4 t
  37. *    函 数 名: bsp_spiTransfer
    # A# @: c, Y" b. G5 S: j! M
  38. *    功能说明: 启动数据传输. ?& D5 s' Q* F2 i9 z9 g+ n
  39. *    形    参: 无
    . z. N7 u" |9 D5 a. X( C
  40. *    返 回 值: 无. f' Y2 N0 _$ o& R& R& L
  41. *********************************************************************************************************) M3 H, f3 i' ~, o: \$ F+ [
  42. */
    3 ?8 J% t- b1 C7 S1 G
  43. void bsp_spiTransfer(void)& g: e& ~1 K9 ^5 I. Y
  44. {5 D2 k0 B3 f: Z3 H7 n
  45.     if (g_spiLen > SPI_BUFFER_SIZE)
    $ B: M, P6 ^9 q
  46.     {
    ' e5 `3 U1 ^1 E8 l, ]' D
  47.         return;. g! I! I1 W* |" D2 Z% I( [/ D
  48.     }# _( O. g1 f. t5 J8 r* e$ j: h

  49. : u2 t6 W( e' f; z* }1 b" p) l
  50.     /* DMA方式传输 */
    , S+ d7 s( \6 z+ k( m( [- L
  51. #ifdef USE_SPI_DMA9 g. U: I9 T, X3 i: F
  52.     wTransferState = TRANSFER_WAIT;6 k* Z, f  o$ J1 s, j
  53. ' d: o) x3 G' }" {0 w# k( \# R# b
  54.     if(HAL_SPI_TransmitReceive_DMA(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK)    $ S& k$ i9 a5 v) f. Z
  55.     {
    % @1 P0 o- n  {- l1 n
  56.         Error_Handler(__FILE__, __LINE__);
    & n" B5 Q1 `2 l; o
  57.     }2 t# U2 c( Y% W

  58. 2 {) p' N5 j% k7 K4 B  ~
  59.     while (wTransferState == TRANSFER_WAIT)" z0 D! |! j+ G% J
  60.     {+ _9 w$ M/ @6 t# |/ O- ]4 ]5 V
  61.         ;7 ?% i  K" g& X; m7 c2 g+ g
  62.     }
    2 D. @2 \5 h. a
  63. #endif( R2 G$ e5 j0 A  H8 F$ n

  64. ' l4 N' C, x0 v8 v- B5 u, N& D
  65.     /* 中断方式传输 */   
    ( z, x! k$ k. ~+ Z
  66. #ifdef USE_SPI_INT9 b1 Q' ~6 {$ ?: E8 ?+ E4 g
  67.     wTransferState = TRANSFER_WAIT;# @0 t" k' C5 q- `

  68. 1 t2 t! K) J* w) d  h
  69.     if(HAL_SPI_TransmitReceive_IT(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK)   
    5 A+ n& ]  W1 [( B8 Z  \; d/ s3 g# W
  70.     {
    ) P5 f9 \% Y: v' k$ W
  71.         Error_Handler(__FILE__, __LINE__);
    - N( b! V! U/ W6 j4 V  `5 a$ ?
  72.     }
      H* f' ]! X% N0 U( v& n/ ]9 |
  73. - ?& F& c1 c+ X& l
  74.     while (wTransferState == TRANSFER_WAIT)
    . @9 C! }8 Q6 W1 l" b+ `
  75.     {
    ) h1 t8 X" O9 r  R- \
  76.         ;" L, ^1 W% f+ u" y: w( P: x
  77.     }
    6 g# X9 U# }5 c
  78. #endif
    ) B' A/ m, i: V4 h5 E6 d2 I6 I
  79. 9 h/ J' R+ {3 K5 Y6 ^. l5 c! P! s
  80.     /* 查询方式传输 */   
    ; a$ h7 C1 \* y0 B$ E9 j( o2 K
  81. #ifdef USE_SPI_POLL) g8 m4 f! u# H& Z& g
  82.     if(HAL_SPI_TransmitReceive(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen, 1000000) != HAL_OK)   
    8 h- B3 K( N1 ]- ]  }7 d& N. k
  83.     {: K; ]8 M- w4 C0 L- t; Z% b6 V+ L
  84.         Error_Handler(__FILE__, __LINE__);( k; U) \$ }% w3 {6 \8 e& E
  85.     }   
      a1 G, X* v  J
  86. #endif6 L) O  K! O3 \
  87. }
    7 t9 k' h4 V; w0 d! [3 ~
复制代码

, L: \1 P& z$ F+ E- Q
& R: `% o- c4 |) {通过开头宏定义可以方便的切换中断,查询和DMA方式。其中查询和中断方式比较好理解,而DMA方式要特别注意两点:
6 O  I6 `7 t5 q& G% z  a# D
; c" U. X) @( B& {" q  通过本手册第26章的内存块超方便使用方式,将DMA缓冲定义到SRAM4上。因为本工程是用的DTCM做的主RAM空间,这个空间无法使用通用DMA1和DMA2。
2 y) ~2 }( l, [. O# @  由于程序里面开启了数据Cache,会造成DMA和CPU访问SRAM4数据不一致的问题,特此将SRAM4空间关闭Cache。1 S9 N" z5 R) [
/* 配置SRAM4的MPU属性为Non-cacheable */% \' }# X* }3 Q
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;3 s9 @5 |" ?" i
    MPU_InitStruct.BaseAddress      = 0x38000000;
; f1 N) l/ h! p! v% B0 V    MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;) h# e* Y# r: W; l
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;9 d4 s# U* T1 v+ z8 F9 j
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
, R! G& r: Y5 v5 G' \* W8 Q    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;. |' }! Y% L1 c6 T4 G
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;( h' S' p7 D* y' t
    MPU_InitStruct.Number           = MPU_REGION_NUMBER2;$ T2 K7 {3 b' U8 D" M
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
3 W* v5 o7 Q5 a+ f3 C7 Y    MPU_InitStruct.SubRegionDisable = 0x00;. J9 K! e" m  m: Z; J' Y" u  I5 X. z
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;( u3 p9 C7 X, a* a, l+ N

& N: M' q6 G9 F2 W, a9 H3 T    HAL_MPU_ConfigRegion(&MPU_InitStruct);9 ~. Y8 k4 a+ H1 A6 n$ K" _
7 R. i* r% ]1 e+ Q' |6 p" V& [! J

2 t' j. V7 Z% k, n. ?& p75.5.3 第3步:DAC8501的时钟极性和时钟相位配置) S- `5 J/ E; z- y3 g
首先回忆下STM32H7支持的4种时序配置。- b2 J! Z. I. d) d0 S2 i* u: l

+ a1 I6 l' D: P+ Z  当CPOL = 1, CPHA = 1时
) k  T& H+ }$ N3 N# p5 E0 B& r# eSCK引脚在空闲状态处于低电平,SCK引脚的第2个边沿捕获传输的第1个数据。, t$ N9 G4 D" @3 u0 A
% F+ L9 H( Z* N+ {$ ?2 ?! ~4 s9 Z
  当CPOL = 0, CPHA = 1时6 `, z4 \1 T" N" u8 O  }/ ~( w
SCK引脚在空闲状态处于高电平,SCK引脚的第2个边沿捕获传输的第1个数据。
2 u4 @# Y, ~) d8 C4 Z$ s! d7 j
$ O( S% A8 Y  O  当CPOL = 1, CPHA = 0时0 V  |" Q; S9 ~$ {! r
SCK引脚在空闲状态处于低电平,SCK引脚的第1个边沿捕获传输的第1个数据。
3 a+ u, [0 i% h- I; N+ w3 ]9 G9 u: Q* h8 w1 q9 F. u. P
  当CPOL = 0 ,CPHA= 0时' U! Z" s# B9 j5 l3 S5 S6 A& ^4 B
SCK引脚在空闲状态处于高电平,SCK引脚的第1个边沿捕获传输的第1个数据。' n, \6 A0 z, R

) c# L" w- E$ {
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
% `2 v/ M$ g: C7 y
5 D. e, U9 A$ ]) ^1 t  n% W" ^) M
有了H7支持的时序配置,再来看下DAC8501的时序图:
6 o! f. q0 e4 y  G4 k
, I8 ~. c+ i. K2 w. |9 y5 x  e. D- E
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
8 y9 ~, @" Y4 i1 y  a% V& G0 ~, L5 f

' u* ]( p- X: T首先DAC8501是下降升沿做数据采集,所以STM32H7的可选的配置就是:
! @+ u3 i, H$ f* L! e0 }, N! j6 N& l. ]2 D! E  r
CHOL = 0,  CPHA = 1
1 c/ J2 y7 I% x1 X! D- N
; ^$ S4 e  n8 \( kCHOL = 1,  CPHA = 0
- A( S: N4 S7 t4 G
, O. s! y- j% F# W对于这两种情况的主要区别是空闲状态下SCLK时钟选择高电平还是低电平,根据上面的时序图和DAC8501的数据手册,两种情况下都可以正常运行。经过实际测试,STM32H7使用这两个配置确实都可以正常运行。程序里面默认是选择CHOL = 0,  CPHA = 1。: v9 o' K; j# g  r+ I6 l7 n+ g' G

" {9 R" \& j1 A2 x1 ^5 [75.5.4 第4步:单SPI接口管理多个SPI设备的切换机制6 P0 ^( e. P+ a+ H) a
单SPI接口管理多个SPI设备最麻烦的地方是不同设备的时钟分配,时钟极性和时钟相位并不相同。对此的解决解决办法是在片选阶段配置切换,比如DAC8501的片选:( D( J/ q4 D3 e7 p) S1 u' I) f

" M7 B& S# {# g4 R
  1. /*: g- u9 ?2 A4 x" F+ k- q) Q
  2. *********************************************************************************************************. W- j* [$ M" M! \4 q
  3. *    函 数 名: DAC8501_SetCS1' k" S! ]7 L# x& |  d
  4. *    功能说明: DAC8501 片选控制函数
      o8 u  U' d% h2 X! M  _+ N
  5. *    形    参: 无9 i5 }; f' H: u
  6. *    返 回 值: 无8 O/ C& F* b; i5 ^7 w0 D
  7. *********************************************************************************************************
    ( C3 m  `6 |: X
  8. */
    4 Y0 J: {7 Q; Z7 }
  9. void DAC8501_SetCS1(uint8_t _Level)6 K4 M- `# L/ E8 o
  10. {- Z1 g' L6 Y5 d0 n( J8 k+ I4 a
  11.     if (_Level == 0)( i. S4 H  f) R/ c4 N9 m
  12.     {
    # i) y: N" e+ ~" K& f
  13.         bsp_SpiBusEnter();    /* 占用SPI总线  */    7 y0 k' |1 l- B" i, R" u
  14.         bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);        4 L6 X% u& P8 B
  15.         CS1_0();8 J- r# D3 J9 I7 P, ^
  16.     }
    * J1 V5 J" S' S6 ~
  17.     else6 }' V0 k1 }) |' A5 L
  18.     {        
    ' I3 ~! L7 p% c
  19.         CS1_1();    0 v) T! X" J) b5 ]
  20.         bsp_SpiBusExit();    /* 释放SPI总线 */6 R8 u$ _9 D% H, S/ s4 z
  21.     }    5 N7 b2 }0 z, V; ~4 g; L
  22. }
    : P1 d+ _7 d' y0 J8 @% R6 `
  23. 1 z+ R0 v$ m& G5 d& J) I( p
  24. /*
    * Z# P; u% G! V! i( F. b' U
  25. *********************************************************************************************************) {. E5 u# P' n# G
  26. *    函 数 名: DAC8501_SetCS2(0)
    7 a1 g- x  J: N% A7 U/ _
  27. *    功能说明: 设置CS2。 用于运行中SPI共享。
    5 R' T- ^) O) I& e) V, G5 f9 A3 a
  28. *    形    参: 无
    + [2 g( k/ r8 o4 `5 U+ ^- ]
  29.     返 回 值: 无
    2 R0 p% s6 b! j# l& A' Q
  30. *********************************************************************************************************
    & W: @" s( ?0 |( y& c0 i6 N
  31. */
    ; y: c; a. \- `6 e& V5 K6 R( x  @
  32. void DAC8501_SetCS2(uint8_t _level)/ f: D% W0 [0 L
  33. {
    / u( p& W4 n2 X: n6 P- D
  34.     if (_level == 0)
    ' R2 }; @6 [+ k4 T+ l. p1 b
  35.     {
    / H) @  h* j$ [( d$ m7 J
  36.         bsp_SpiBusEnter();    /* 占用SPI总线  */& L  ~) K4 `/ F5 T
  37.         bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);        ) v3 V& q+ l3 L; U, P( j
  38.         CS2_0();
    : j- E% c. _1 U% ^
  39.     }
    # Z/ L& G. j( D! b6 b& H: C
  40.     else
    $ Q& M& n# \8 J/ `' ^
  41.     {
    : I9 Z( s3 U& E- q8 b( Z
  42.         CS2_1();
    " y1 k  x; I7 G
  43.         bsp_SpiBusExit();    /* 释放SPI总线 */
    3 \6 g- \2 Z5 W8 D
  44.     }
    6 R/ |# N' n5 E, y
  45. }
复制代码

+ o% [* K# k0 k9 i. `通过这种方式就有效的解决了单SPI接口管理多设备的问题。因为给每个设备都配了一个独立的片选引脚,这样就可以为每个设备都配置这么一个片选配置。1 |3 I! d. l' I. k

7 P7 w8 K/ j8 r4 E但是频繁配置也比较繁琐,所以函数bsp_InitSPIParam里面做了特别处理。当前配置与之前配置相同的情况下无需重复配置。4 f7 H" w  v, {6 r& q! @- y5 P

! j( t  x6 g2 R0 ~9 g: U2 \75.5.5 第5步:DAC8501的数据更新- c& i6 L- x# I. q+ ]3 P
DAC8501的双通道数据更新通过下面的函数实现:$ @: @; J2 [6 @
/ e) E3 K5 L4 D" w5 O; I* G& M
  1. /*
    . C5 A9 _4 t0 N0 H
  2. *********************************************************************************************************
    ' T( v2 r7 l+ P( d1 C& `
  3. *    函 数 名: DAC8501_SetDacData
    * Y) L+ ^4 H0 |/ D1 S
  4. *    功能说明: 设置DAC数据) X& M2 j( d3 A+ R2 B" [* u' {
  5. *    形    参: _ch, 通道,8 F! @7 X2 `" l8 Q
  6. *             _data : 数据
    0 A' h( I7 h- g9 G  ]. z
  7. *    返 回 值: 无! X' t0 v8 ~) p" P: _$ f* c/ T
  8. *********************************************************************************************************( z0 g  r" \1 n5 P1 J# P" S
  9. */5 ]- Z3 M! D% {8 R* n- A( }
  10. void DAC8501_SetDacData(uint8_t _ch, uint16_t _dac)
    % v% N; i6 ~: a
  11. {
    " ~, i: P- [3 K5 `# L7 P
  12.     uint32_t data;, I1 ^- [* q9 j; M- r) j/ }) @6 X

  13. . n6 o4 c$ h$ w7 p! V
  14.     /*
    * l" E9 \/ S. Z# Q
  15.         DAC8501.pdf page 12 有24bit定义& F0 p: Q9 r5 n8 R4 k
  16. . I+ k9 y1 J! E* j
  17.         DB24:18 = xxxxx 保留
    ; i; I; W# ~: v, ]
  18.         DB17: PD1( G; |$ \& O9 t  K
  19.         DB16: PD01 v* W  ~* d2 Q; D

  20. . k# D/ I/ ~( i+ X) }" H, D
  21.         DB15:0  16位数据
    6 k& `: a6 N0 E% j9 I. P# r% v' |2 N

  22. , O: {# m+ G( S- |
  23.         其中 PD1 PD0 决定4种工作模式
    ; ]. _3 `1 I$ |6 e  ~+ J
  24.               0   0  ---> 正常工作模式) e3 ?/ |4 `4 H" ]* r/ `/ k2 g& `
  25.               0   1  ---> 输出接1K欧到GND: m1 _. Z0 X+ w) r$ V. S
  26.               1   0  ---> 输出100K欧到GND
    . K' |+ W9 j5 a% i& ]: c7 |
  27.               1   1  ---> 输出高阻$ e3 F  [* Z% B9 b% X
  28.     */- ^% J( i' D4 j( V* a6 n
  29. 8 c6 _2 x% }8 z
  30.     data = _dac; /* PD1 PD0 = 00 正常模式 */
    9 D! R' M4 y& {" |6 D& Y$ Q
  31. 4 `  x  g5 a" v! v2 o" h
  32.     if (_ch == 0). o8 p+ I8 I5 F. W6 I* @2 Z
  33.     {1 r: @" {% q' K& K" E
  34.         DAC8501_SetCS1(0);
    ( g0 V$ q8 u" `+ V. {+ X0 g
  35.     }
    5 ~; Y6 b6 X$ v. d+ q' h, e9 J" e
  36.     else2 g7 f) ]3 [* P' ~3 q
  37.     {
    0 J+ x4 C, J( v# W  N8 V' o# i, M& V
  38.         DAC8501_SetCS2(0);
    6 q, q+ r* t9 ?( y5 h8 e
  39.     }& k( Y& G1 d% n4 ?' n; n, Y

  40. * L+ U! U* r& e* y: I
  41.     /* DAC8501 SCLK时钟高达30M,因此可以不延迟 */
    1 N6 U1 _8 N* e% B" O: Q) e* [1 ?* K; M5 m
  42.     g_spiLen = 0;, G( W" Q0 b! j. _: N; P3 W# l7 c
  43.     g_spiTxBuf[g_spiLen++] = (data >> 16);
    3 a  h9 X6 `' N7 V* G1 n
  44.     g_spiTxBuf[g_spiLen++] = (data >> 8);
    7 R( F( p' X4 Z3 w
  45.     g_spiTxBuf[g_spiLen++] = (data);1 G2 r  l( H; p; s8 u% y
  46.     bsp_spiTransfer();   
    / b: B5 p  S: n. n, x+ G9 C
  47. . D/ h& r2 [5 s, Z1 b# V1 @
  48.     if (_ch == 0)
    3 J% F" E1 ^( W  |* O
  49.     {
    , ^; `% E& M  [1 }7 m; b$ t0 ~
  50.         DAC8501_SetCS1(1);, U5 C3 Q- Z! L2 k& b- h
  51.     }1 J2 ~* h9 u1 x- m
  52.     else
    ' r) ^6 k! [% g! ]8 u! U
  53.     {0 d- e) r9 ?( j. X9 n8 }
  54.         DAC8501_SetCS2(1);' G# W8 K9 m' a. C/ G5 a
  55.     }3 p, F/ e6 d4 K( l! N- G
  56. }
复制代码
5 ^  t/ I8 Q* u0 ~! v
函数实现比较简单,每次更新发送24bit数据即可。. F$ d: C9 F1 V  ^6 C  h

  q; e+ N! `5 \) Y75.6 DAC8501驱动设计(SPI DMA更新方式)
6 |) Z. T/ g5 Z& B; B- ]DAC8501的DMA驱动方式略复杂,跟中断更新方式完全不同,要使用硬件SPI1 NSS引脚驱动DAC8501的片选,所有专门做了一个驱动文件来实现,程序驱动框架设计如下:5 k( I1 v$ C- }! ]) q

: {  j+ [( y) e4 O3 E4 Z
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

; R8 x* J% h2 f6 K! s( `* n- ~$ A# A( V3 i. s" H% S
有了这个框图,程序设计就比较好理解了。
1 b! O1 [& M$ S" o4 L
% P$ a7 M1 v' K3 s75.6.1 第1步:SPI总线配置
3 w2 a; Q, E/ lspi总线配置通过如下两个函数实现:4 L. N/ {) u: f% F
) l# f% _9 A0 A4 H  y
  1. /*
    8 L: Y$ @9 P& z2 Q
  2. *********************************************************************************************************/ C% P8 o5 @9 c$ M
  3. *    函 数 名: bsp_InitDAC8501
      }9 l$ d5 c2 |' p9 ~8 j
  4. *    功能说明: 配置GPIO并初始化DAC8501寄存器
    $ R9 O8 M, n7 Q4 g
  5. *    形    参: 无
    ; G1 ]) T6 L  t5 }
  6. *    返 回 值: 无
    ' H0 `5 o  h& k  r
  7. *********************************************************************************************************' Y/ T6 [1 p7 D  l/ e2 Z
  8. */  S0 |& |6 Y: G) n5 \7 K
  9. void bsp_InitDAC8501(void)3 z3 M0 l" |$ g5 |, @4 x! h& W* d
  10. {
    ) M" T. Y! R. ~5 N- t7 f
  11.     s_SpiDmaMode = 0;  - o; T0 T( z) M+ c8 Y

  12. * z, A* f7 N9 L# R2 L
  13.     /*##-1- 配置SPI DMA ############################################################*/! v5 @6 h" I  Q* V
  14.     bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);
    : m# J3 d) @: L% g
  15. 8 j! T& C/ b% ]( w
  16.     /*##-2- 默认输出0V ############################################################*/; s# C5 N' [2 `7 j2 z
  17.     DAC8501_SetDacData(0, 0);    /* CH1输出0 */: t$ {7 w1 R, |8 @" k
  18. }
    ) a3 x' H% C7 V& h1 f

  19. + U6 i. W+ M0 s; f
  20. /*% i* j7 }0 l+ p1 K0 P& P; y
  21. *********************************************************************************************************
    ) t* s' ?8 n& `4 b
  22. *    函 数 名: bsp_InitSPIParam
    ( m( v% z: D% q; K- z3 y7 ~
  23. *    功能说明: 配置SPI总线参数,时钟分频,时钟相位和时钟极性。
    4 |: X$ {: k3 x. ]/ P
  24. *    形    参: _BaudRatePrescaler  SPI总线时钟分频设置,支持的参数如下:; d/ }; B+ I8 p9 Q; W4 Z
  25. *                                 SPI_BAUDRATEPRESCALER_2    2分频
    0 k3 p5 J4 g- G5 I# _
  26. *                                 SPI_BAUDRATEPRESCALER_4    4分频1 H1 T  k0 R7 G3 u. @
  27. *                                 SPI_BAUDRATEPRESCALER_8    8分频
    / }9 M/ m6 a, D- W2 K
  28. *                                 SPI_BAUDRATEPRESCALER_16   16分频' P0 z6 p7 K/ n' h/ L, Z" F" z3 X
  29. *                                 SPI_BAUDRATEPRESCALER_32   32分频
    * @. x+ z5 j; C( S- B0 K- _
  30. *                                 SPI_BAUDRATEPRESCALER_64   64分频
    - q$ A9 n& ]% y) \2 e
  31. *                                 SPI_BAUDRATEPRESCALER_128  128分频
    . [" H$ [. ~% D' n
  32. *                                 SPI_BAUDRATEPRESCALER_256  256分频
    # V' ]. ~! p6 B2 u
  33. *                                                        
    * g8 J0 P5 H8 k3 _/ J- W, w
  34. *             _CLKPhase           时钟相位,支持的参数如下:
    " ]6 G$ l% h8 Y2 K/ F; |1 ?) M
  35. *                                 SPI_PHASE_1EDGE     SCK引脚的第1个边沿捕获传输的第1个数据
    9 N6 [2 m# ?6 i: D/ t, x
  36. *                                 SPI_PHASE_2EDGE     SCK引脚的第2个边沿捕获传输的第1个数据
    7 K1 G. S3 u" S
  37. *                                 
    $ @: G1 g3 h+ _' b7 p" Y
  38. *             _CLKPolarity        时钟极性,支持的参数如下:
    ; P3 o" H0 ^# X4 I7 g6 U8 O5 E
  39. *                                 SPI_POLARITY_LOW    SCK引脚在空闲状态处于低电平
    0 R* L7 A) r+ D; i2 @
  40. *                                 SPI_POLARITY_HIGH   SCK引脚在空闲状态处于高电平3 ?+ m2 ]" W/ }8 b6 x; E
  41. *' B/ f# P( v: _: [+ v, `
  42. *    返 回 值: 无
    ) ~; Q" {' H+ G$ B: a
  43. *********************************************************************************************************0 z, D: i$ }+ i
  44. */
    4 F' d% c! f- |
  45. void bsp_InitSPIParam(uint32_t _BaudRatePrescaler, uint32_t _CLKPhase, uint32_t _CLKPolarity)
    - ]3 B0 ~  Z  W7 G8 X
  46. {$ B6 E0 e! z7 b% ?0 s

  47. 7 [( r- B) d8 c2 v% F  ]4 K' q: B2 }
  48.     /* 设置SPI参数 */( D* M' g/ t- A3 ^; P6 y7 x
  49.     hspi.Instance               = SPIx;                           /* 例化SPI */
    0 x; E# c! c5 Z8 I$ }' P3 o
  50.     hspi.Init.BaudRatePrescaler = _BaudRatePrescaler;             /* 设置波特率 */
    ' y4 b# ]# a' d: u$ Q2 O0 X
  51.     hspi.Init.Direction         = SPI_DIRECTION_2LINES_TXONLY;  /* 全双工 */
    : O& M. C' ]; C" `* Q! m, t. [
  52.     hspi.Init.CLKPhase          = _CLKPhase;                     /* 配置时钟相位 */& K0 r! f3 K! `. g7 V
  53.     hspi.Init.CLKPolarity       = _CLKPolarity;                   /* 配置时钟极性 */; K4 z" f7 m& _9 c  e+ w6 i: i
  54.     hspi.Init.DataSize          = SPI_DATASIZE_24BIT;               /* 设置数据宽度 */8 p- \- J2 P# P1 @# b. C1 b
  55.     hspi.Init.FirstBit          = SPI_FIRSTBIT_MSB;             /* 数据传输先传高位 */
    ) s: g' c' z: |2 \
  56.     hspi.Init.TIMode            = SPI_TIMODE_DISABLE;             /* 禁止TI模式  */. w, S5 L$ ^5 c  a' p1 u
  57.     hspi.Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE;     /* 禁止CRC */$ p9 H3 }- \& o5 o. V6 F" f
  58.     hspi.Init.CRCPolynomial     = 7;                               /* 禁止CRC后,此位无效 */
    8 b: @8 u: D$ r! n
  59.     hspi.Init.CRCLength         = SPI_CRC_LENGTH_8BIT;             /* 禁止CRC后,此位无效 */
    ) k5 g: H% O; q" M9 I1 \4 E0 Z. _
  60.     hspi.Init.FifoThreshold     = SPI_FIFO_THRESHOLD_05DATA;    /* 设置FIFO大小是一个数据项 */
    ( q- w0 n4 x, C) q6 ^. d+ y" i2 W& o

  61. ) x7 n3 W1 o0 L# C5 ~1 i7 d& {, F
  62.     hspi.Init.NSS         = SPI_NSS_HARD_OUTPUT;                 /* 使用软件方式管理片选引脚 */
    * L+ @: e# G, X/ y! ]
  63.     hspi.Init.NSSPMode    = SPI_NSS_PULSE_ENABLE;                /* 使能脉冲输出 */
    # C" X" S+ l, X
  64.     hspi.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;               /* 低电平有效 */4 {; }2 x! Y( I( B( Z: W. _) Z
  65. : J: ^. m$ k9 s4 \' `7 `& a0 B
  66. /* MSS, 插入到NSS有效边沿和第一个数据开始之间的额外延迟,单位SPI时钟周期个数 */
    0 ?6 j  B. m8 X* f# x8 V
  67.     hspi.Init.MasterSSIdleness        = SPI_MASTER_SS_IDLENESS_00CYCLE;   4 P( J, i/ G4 ~, b1 q: ^
  68. . P5 r) Q$ v% {' C
  69. /* MIDI, 两个连续数据帧之间插入的最小时间延迟,单位SPI时钟周期个数 */5 w' g4 r8 ^+ `4 u+ G5 }
  70.     hspi.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_02CYCLE; : G  z% I2 e/ G) [- a' T

  71. , Z/ K( u$ a# A8 Q* K0 b& O" d- @
  72.     hspi.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE; /* 禁止SPI后,SPI相关引脚保持当前状态 */  
    0 ]% n- j3 u0 W. M+ Q8 w, g
  73.     hspi.Init.Mode            = SPI_MODE_MASTER;                    /* SPI工作在主控模式 */( o' y/ F/ x+ {# ]2 `$ X
  74. ! O) l5 R; w( _! c5 j" n/ l4 k  B; R
  75.     /* 复位配置 */
    1 }% f1 F- i9 c5 ?3 H
  76.     if (HAL_SPI_DeInit(&hspi) != HAL_OK)
    ! M2 c  ~0 B; z5 }3 p9 W
  77.     {! V' S3 q" t9 n1 S1 `4 ?  T) j
  78.         Error_Handler(__FILE__, __LINE__);0 `) k/ m' G+ m, u
  79.     }   
    " a( o7 q1 \5 C4 s& l

  80. 6 L3 _- E$ R$ U1 G4 z$ z6 B
  81.     /* 初始化配置 */
    . H! Y; x, q+ a5 w$ [& C) {" P
  82.     if (HAL_SPI_Init(&hspi) != HAL_OK). ~2 T9 B2 s3 Y! T
  83.     {
    & o0 U- f9 Q4 {  K
  84.         Error_Handler(__FILE__, __LINE__);
    ! c0 R7 S& Y/ w1 W
  85.     }    & v- I$ n' p3 u0 ]. H
  86. }
复制代码

9 c. c4 D; T% M% s& v4 @& d/ m
/ `( f7 C6 L/ y+ G/ r* h
! v! e! n" `0 T0 _, E( v这两个配置函数里面最重要的是置红的几个配置选项,这里依次为大家做个说明:
0 _# {% t, \! J' p) A, @2 L" }
' |; w! q% |: z/ n* I  SPI_DIRECTION_2LINES_TXONLY
# K4 n3 {. m9 F* j1 _) i驱动DAC856X仅需要SPI写操作。. n2 N" Y1 N  m5 d- |1 D

  c2 l- H6 N1 g: e0 S  SPI_DATASIZE_24BIT$ {# P  R- y3 p
STM32H7的SPI支持4-32bit数据传输,由于DAC856X需要24bit数据,所以这里配置为24即可。
8 ]8 Y$ X& z; G9 K3 K
$ U% s+ }; {6 ?. B* v7 O  SPI_FIFO_THRESHOLD_05DATA
, K& [* q1 r7 s; J/ b) a, S) p对于SPI1来说,里面的FIFO大小是16字节,那么SPI数据传输配置为24bit的话,FIFO最多可以存储5个24bit,因此这个fifo阀值要设置为5。4 ?) r2 j0 ?6 S$ e7 a! i
3 P! `* Z- q4 ^9 D
  SPI_NSS_HARD_OUTPUT
* b+ F. y5 d' b我们这里要使用SPI的硬件片选引脚SPI_NSS。
+ |/ ~2 m/ W9 O* j" `1 V- P" i! _' G- g: S7 c3 O7 h7 u# z) d
  SPI_MASTER_SS_IDLENESS_00CYCLE1 E, R! R, p  p5 t
插入到NSS有效边沿和第一个数据开始之间的额外延迟,单位SPI时钟周期个数。+ j3 {, w6 r) P4 [  Z3 n+ z: z
: z5 Z0 i: \' S: F, U$ m
根据本章75.4.4小节里面的t(4)要求,片选有效到SCLK第1个下降沿信号的时间,最小值为0。所以这里配置为0即可,也就是无需插入时间。
' ]* D& S7 ]2 E! y
. T* n% |4 p) n9 `; o  SPI_MASTER_INTERDATA_IDLENESS_10CYCLE
* c) }9 e! D) x5 }9 c两个连续数据帧之间插入的最小时间延迟,单位SPI时钟周期个数。! U6 }, f$ l, T+ ?
- C0 ]! e4 O; T+ X: k4 F& r
根据本章75.4.4小节里面的t(5)要求,每传输24bit数据后,片选要保持一段时间的高电平,DAC856X要求至少要33ns(供电3.6到5.5V时),也是说,如果我们以25MHz驱动DAC856X,这里至少要配置为1个时钟周期,推荐值为2及其以上即可,我们这里直接配置为2个时钟周期(配置为1也没问题的)。
* r. c. c: i8 w. E7 h% b# J
- H' {% t$ t' O" c+ d/ B75.6.2 第2步:TIM12周期性触发配置
7 A. O( e) N5 ~, i( }( \% Q3 }这里特别注意一点,定时器触发一次,就会让SPI以DMA方式传输24bit输出。
7 r6 b# C5 H- G2 D- }9 V/ z, O% A9 b# H% O- `
TIM12的触发配置如下:
7 Y- \, n/ o2 Q$ ]! ]7 W3 n) `; o" T9 p/ P, I& x
  1. /*4 S8 ]: q9 U% h; _1 k
  2. *********************************************************************************************************
    / ]% w) H+ D8 m' d+ ]0 Z( v1 o7 E
  3. *    函 数 名: TIM12_Config: ^  d( Y2 h0 H" C# \
  4. *    功能说明: 配置TIM12,用于触发DMAMUX的请求发生器
    / s% C0 L- y0 g9 m
  5. *    形    参: _ulFreq  触发频率,推荐范围100Hz - 1MHz                              
    6 R9 {2 x# q/ j- l* `
  6. *    返 回 值: 无* R! r. P& S( ?$ h( f- R$ K
  7. *********************************************************************************************************
    ! K* Q# Y4 a+ o' k7 E9 ?
  8. */   0 d9 _2 _" k+ m3 h
  9. TIM_HandleTypeDef  htim ={0};
    ( R; i% C6 U+ u9 u5 i( b8 Z, ^7 B
  10. TIM_MasterConfigTypeDef sMasterConfig = {0};# \- f! o6 b: W/ W& R" q
  11. TIM_OC_InitTypeDef sConfig = {0};
    , j& f* w' U' D- ?) n* k0 \
  12. void TIM12_Config(uint32_t _ulFreq)* s# b' J% E$ g/ S3 C
  13. {6 j% K6 `  O/ d  ]" ^/ v
  14.     uint16_t usPeriod;
    4 Y, ?' a' o+ ?" I% z: |
  15.     uint16_t usPrescaler;
    5 X% M# a  \9 ]- x) u4 X
  16.     uint32_t uiTIMxCLK;
    + E& `0 W( W+ F: k

  17. ( N2 u  Z7 C) C: D& f6 B
  18. # q& \  S0 J0 c/ F) g
  19.       /* 使能时钟 */  8 P7 I2 q6 p* \3 d% h5 w' I  i" g1 z
  20.       __HAL_RCC_TIM12_CLK_ENABLE();: n$ E$ i4 b9 J. |2 }  X0 p8 j$ n

  21. $ L1 S* M+ `* i
  22.     /*-----------------------------------------------------------------------6 z; P& `9 W9 Y+ f. I
  23.         bsp.c 文件中 void SystemClock_Config(void) 函数对时钟的配置如下:
    6 @0 i, y3 v  \  ?' j
  24. 0 Q' V. k8 U5 \* H0 w$ ]4 O8 O
  25.         System Clock source       = PLL (HSE)
    ! t; R7 c/ l. \$ N3 T2 H
  26.         SYSCLK(Hz)                = 400000000 (CPU Clock)7 L" b( o7 _- F7 t! o% Z5 V
  27.         HCLK(Hz)                  = 200000000 (AXI and AHBs Clock)7 Z, b: }1 i4 P# k: r" S
  28.         AHB Prescaler             = 2  Z6 S% l# o3 B, h8 S5 \, s
  29.         D1 APB3 Prescaler         = 2 (APB3 Clock  100MHz): ?& J: B" d  K% G! T3 @/ j- D
  30.         D2 APB1 Prescaler         = 2 (APB1 Clock  100MHz)! w8 V$ l  M, T
  31.         D2 APB2 Prescaler         = 2 (APB2 Clock  100MHz)+ l$ T& f; x& ?/ q3 c5 T+ j9 E) o
  32.         D3 APB4 Prescaler         = 2 (APB4 Clock  100MHz)& i$ b) r& I' F& }

  33. - G2 A* g4 y  ]" N1 y# h6 L) Y
  34.         因为APB1 prescaler != 1, 所以 APB1上的TIMxCLK = APB1 x 2 = 200MHz; 不含这个总线下的LPTIM1# z% W2 I+ w+ M& b+ O* y8 R+ I
  35.         因为APB2 prescaler != 1, 所以 APB2上的TIMxCLK = APB2 x 2 = 200MHz;) a! v4 C9 d6 @# ?3 `
  36.         APB4上面的TIMxCLK没有分频,所以就是100MHz;4 Y4 _8 O/ [/ I
  37. + B& F! O) [. Z! |  g
  38.         APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14,LPTIM1
    7 h/ o) r/ \5 l
  39.         APB2 定时器有 TIM1, TIM8 , TIM15, TIM16,TIM17
      i8 h  W! e% f

  40. 8 u: {& {" Z6 b# I% J3 a" a2 |
  41.         APB4 定时器有 LPTIM2,LPTIM3,LPTIM4,LPTIM59 w) d% k8 {/ Q  c( M. m
  42.     ----------------------------------------------------------------------- */
    3 F! t5 f) c7 X0 o  I0 ^& h: N
  43.     uiTIMxCLK = SystemCoreClock / 2;
    % w& e& k' s; l& ~! ^& v
  44. ( {. r1 ^1 \  G$ H  \5 P; U. q
  45.     if (_ulFreq < 100)- N- z% I! Y/ }
  46.     {8 o# c+ P0 q9 `) E
  47.         usPrescaler = 10000 - 1;                    /* 分频比 = 10000 */
    " W  w1 G3 M/ X2 n2 }- P
  48.         usPeriod =  (uiTIMxCLK / 10000) / _ulFreq  - 1; /* 自动重装的值 */  B1 W# i. l; j7 T% ~3 m' h2 l
  49.     }
    : Z  R2 x" x: b/ |/ @! J" r
  50.     else if (_ulFreq < 3000), H# c1 I9 s1 a8 o. C! v/ N: y
  51.     {. S6 u) u  e% {7 U7 Z% Y! W
  52.         usPrescaler = 100 - 1;                    /* 分频比 = 100 */
    , D1 [$ m3 g% [4 B2 I5 F/ z4 H, J
  53.         usPeriod =  (uiTIMxCLK / 100) / _ulFreq  - 1;/* 自动重装的值 */
    7 C( \1 G. c& G# v+ J# g
  54.     }5 M: t) A$ A3 Y/ w, c* Z
  55.     else    /* 大于4K的频率,无需分频 */% O- O! d$ k. \' {& I
  56.     {
    % I: o: A+ N0 M3 O+ i# P/ u
  57.         usPrescaler = 0;                    /* 分频比 = 1 */9 v+ Y6 k1 u2 ]; K! v
  58.         usPeriod = uiTIMxCLK / _ulFreq - 1;    /* 自动重装的值 */
    - R$ B# z$ w4 X- T& ^# c5 W
  59.     }
    $ x0 k( u# [" w0 _) p+ }

  60. 7 g" v5 ^/ u9 E2 @4 [  x6 G4 }
  61.     htim.Instance = TIM12;8 T' d$ b' n% F% ~6 A8 u" h
  62.     htim.Init.Period            = usPeriod;
    , n; s* Y2 Q* V. v
  63.     htim.Init.Prescaler         = usPrescaler;
    1 k* M4 B5 r. N$ I5 S+ a
  64.     htim.Init.ClockDivision     = 0;$ p) h" o/ ?1 L( e
  65.     htim.Init.CounterMode       = TIM_COUNTERMODE_UP;
    0 A0 J  M* T6 @, }" T+ x
  66.     htim.Init.RepetitionCounter = 0;! X. n4 `, @+ e

  67. ' s8 z( N7 j. |
  68.     if(HAL_TIM_Base_DeInit(&htim) != HAL_OK): M* A/ p/ d. I8 Z9 Q
  69.     {9 L+ p8 z+ D" I) j5 Y1 R/ Y2 G" l1 t
  70.         Error_Handler(__FILE__, __LINE__);        ( r+ y  L# f' h$ [6 Z7 m3 L
  71.     }# l' E$ g! m7 T6 @; D

  72. 1 y# T; l/ O+ d9 B- d; v7 b
  73.     if(HAL_TIM_Base_Init(&htim) != HAL_OK)
    + s9 w' |6 n( r( Q
  74.     {, u( N" A: D% R+ j8 [4 f+ i0 P
  75.         Error_Handler(__FILE__, __LINE__);        ; a$ |, |, Y+ ^
  76.     }- `" G5 I' E* s* C% B1 d# T+ g2 u; ^

  77. 3 {; \2 A; K: k7 T' [2 E4 z  c, O
  78.     sConfig.OCMode     = TIM_OCMODE_PWM1;
    " Q* V3 p8 S$ Y; T- c4 B; T
  79.     sConfig.OCPolarity = TIM_OCPOLARITY_LOW;
    # r& B: _& s! T2 z
  80.     sConfig.Pulse = usPeriod / 2;     /* 占空比50% */$ r! ?" w6 I* O
  81.     if(HAL_TIM_OC_ConfigChannel(&htim, &sConfig, TIM_CHANNEL_1) != HAL_OK)2 O+ H* J. ~- A
  82.     {
    * \& h0 V) w( D. t! o. E2 ^
  83.         Error_Handler(__FILE__, __LINE__);
    9 m  ^( v7 Y; n% _" @9 ?9 U0 r4 }) H
  84.     }' M3 Z" P, \9 A2 G3 E5 K5 M

  85. : u0 ~; A+ q2 L
  86.     /* 启动OC1 */
    / x7 w6 A/ g7 s- m0 x' _! G
  87.     if(HAL_TIM_OC_Start(&htim, TIM_CHANNEL_1) != HAL_OK)
    ; M7 J: ]. C6 z% h, z% a8 ]
  88.     {
    6 F/ q3 e! v9 {6 J, {
  89.         Error_Handler(__FILE__, __LINE__);) b8 D7 w# O; z" v4 f( ]4 a  U
  90.     }
    & }4 j+ N  q. O4 @  u3 o

  91. ; Z9 ]. A! c  P, \2 _; @( ]# U
  92.     /* TIM12的TRGO用于触发DMAMUX的请求发生器 */$ F9 v6 R3 U( ^" D1 I" z  Q. |2 N
  93.     sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1REF;9 G! _2 _7 ~1 o5 a* b2 y
  94.     sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;5 i/ L# ]7 h/ W6 o, d
  95.     sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    7 a* K8 Z/ g6 {9 c0 a# E

  96. 5 b% t0 |& w6 k) F7 Z  r
  97.     HAL_TIMEx_MasterConfigSynchronization(&htim, &sMasterConfig);
    % e+ I- H2 \! W8 H; K) q+ t* m
  98. }) W' p4 g; q* C  D- v; A
  99. #endif
    ) ]+ x+ i5 |% E" R
复制代码
; v% N9 ^& s, R2 |9 h0 ~
) x) F) ]; u0 j' k  b6 ]
这个函数支持的触发频率很宽,对于DAC856X来说,如果样本点设置为100个的话,此函数推荐的触发频率是100Hz到1MHz,具体可以支持到最高触发速度计算看本章4.7.7小节即可。
5 d- b+ Z* y" \& }' S7 i: P
" A# A3 t. G' U/ U% x75.6.3 第3步:DMAMUX同步触发SPI DMA传输1 b/ m! G  n; }& |
DMA和DMAMUX的配置如下:! Z, y' e. B1 S, r$ H2 O
: B4 @2 y' z* `: {2 |
  1. /*
    % w2 O- V" N0 h& C" L9 }; [
  2. *********************************************************************************************************
    + c% z( O- ~' x; p. J) {6 y
  3. *    函 数 名: bsp_spiDamStart
    # C1 e4 U3 s( M3 i# p
  4. *    功能说明: 启动SPI DMA传输
    / }1 z; R  J9 l. f# `0 R
  5. *    形    参: _ulFreq 范围推荐100Hz-1MHz% Y5 ~' P' T& J1 A
  6. *    返 回 值: 无+ a3 O* ^3 O( w8 P/ X, G# }+ e; V
  7. *********************************************************************************************************
      B/ P% \: `' n$ U
  8. */
    1 K4 D" M  Z: r2 V- u
  9. void bsp_spiDamStart(uint32_t _ulFreq)
    8 ]8 T/ N; L* i, q- X* u% j9 K
  10. {  @+ b0 y. Y; N5 e) [& R
  11.     /* 设置模式,要切换到DMA CIRCULAR模式 */  u/ [" a" h4 }2 o: u2 }9 o6 |
  12.     s_SpiDmaMode = 1;' t. y/ z4 v9 z

  13. " s) G- }- H- c4 r
  14.     bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_4, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);$ v2 j9 r1 Z2 y  {0 O( K# D
  15. 4 F! F3 a8 }1 k( ^( u# d! A/ P
  16.     /* 使能DMA时钟 */8 O% o- B  I# G3 {2 F# r# B
  17.     DMAx_CLK_ENABLE();      
    , U% l0 n/ O( L
  18. 0 I8 Z9 L& I/ t
  19.     /* SPI DMA发送配置 */        3 i) e% {* H- K+ o
  20.     hdma_tx.Instance                 = SPIx_TX_DMA_STREAM;      /* 例化使用的DMA数据流 */  {1 a7 ~7 V; d8 |/ E) i
  21.     hdma_tx.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;     /* 使能FIFO */* |( z4 n6 \# Y
  22.     hdma_tx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL; /* 用于设置阀值 */
    % \6 f+ Q, |# `! j) r1 E5 Y
  23.     hdma_tx.Init.MemBurst            = DMA_MBURST_SINGLE;        /* 用于存储器突发 */
    : q* h" M/ Q0 f4 Q( r
  24.     hdma_tx.Init.PeriphBurst         = DMA_PBURST_SINGLE;        /* 用于外设突发 */4 ^" C4 ]$ G7 n% v' H6 w
  25.     hdma_tx.Init.Request             = SPIx_TX_DMA_REQUEST;     /* 请求类型 */  
    ' |% H! \( C$ N: K4 x' G
  26.     hdma_tx.Init.Direction           = DMA_MEMORY_TO_PERIPH;    /* 传输方向是从存储器到外设 */  6 v/ z. o/ u& S9 c' D3 r5 v. L8 J
  27.     hdma_tx.Init.PeriphInc           = DMA_PINC_DISABLE;        /* 外设地址自增禁止 */
    + i) O2 K! C6 ?. b% Z1 I. v8 x; O
  28.     hdma_tx.Init.MemInc              = DMA_MINC_ENABLE;         /* 存储器地址自增使能 */  
    2 K% J# @  u8 Y7 H% r" L( D; R6 R# D0 _
  29.     hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;     /* 外设数据传输位宽选择字节,即8bit */
    3 U" n' M, G# l7 l9 U7 T6 C
  30.     hdma_tx.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;     /* 存储器数据传输位宽选择字节,即8bit */   
    6 D) N. B& d; M% V
  31.     hdma_tx.Init.Mode                = DMA_CIRCULAR;            /* 正常模式 */
      x, ~5 k0 G7 w8 K' ^. E# @5 U! x
  32.     hdma_tx.Init.Priority            = DMA_PRIORITY_LOW;        /* 优先级低 */
    ! ?! d8 @2 X3 N; g

  33. ) m$ ]( F* e  |3 x9 O
  34.      /* 复位DMA */
    9 ?. c' z# {7 y, c( K
  35.     if(HAL_DMA_DeInit(&hdma_tx) != HAL_OK)/ D9 ^+ E. |3 a6 N+ K
  36.     {6 W+ j. {0 h1 l% |
  37.         Error_Handler(__FILE__, __LINE__);     
    ) |2 B$ O) o8 k( k+ r. g
  38.     }
    8 v' ^2 j& h/ j; C  W

  39. ( ?2 ?, c6 U6 N5 Z6 N/ b
  40.      /* 初始化DMA */+ K$ E" J. [: q) U5 a: Q) U
  41.     if(HAL_DMA_Init(&hdma_tx) != HAL_OK)# z2 J& q6 w: ~  X5 c0 e
  42.     {# |0 `- |" k% n+ R  J$ B. F
  43.         Error_Handler(__FILE__, __LINE__);     
    0 i- s$ }( |1 X# D& ^7 @
  44.     }
    ! P5 t! n# A+ i% F( _# M* j
  45. " c" O' Q% l* a% q
  46.     /* 关联DMA句柄到SPI */
    - \+ X% R3 j, m! @1 b0 C
  47.     __HAL_LINKDMA(&hspi, hdmatx, hdma_tx);    * ^9 f5 M* X: r% }. h
  48. % [# M5 j, ^- p* h" f% f* f6 o
  49. 3 ^1 H- _+ y! C" D' |
  50.     /* 关闭DMA发送中断 */3 o+ q% n( ?6 \  P' {
  51.     HAL_NVIC_SetPriority(SPIx_DMA_TX_IRQn, 1, 0);+ U% j! \. w" r) K" G
  52.     HAL_NVIC_DisableIRQ(SPIx_DMA_TX_IRQn);/ {, i% B6 M; f3 B
  53. 5 Y# z& n, K. L! ^
  54.     /* 关闭SPI中断 */
    ' C+ ^8 a1 s4 H! ^+ u0 Y
  55.     HAL_NVIC_SetPriority(SPIx_IRQn, 1, 0);
    . b: T: B. i8 j2 @
  56.     HAL_NVIC_DisableIRQ(SPIx_IRQn);
    / f, t0 V9 A/ E* G" X, L8 r
  57. ( j' q3 Q3 T7 |* \7 N4 K! M4 J
  58.     /* 同步触发配置 */
    6 E1 e% s* \- ?# R
  59.     dmamux_syncParams.EventEnable   = ENABLE;                               d/ U5 b( }/ Y& W- R
  60.     dmamux_syncParams.SyncPolarity  = HAL_DMAMUX_SYNC_RISING;          7 o6 E4 ?& M" v3 F3 W$ m
  61.     dmamux_syncParams.RequestNumber = 1;                   4 f0 k7 q# @5 b* w4 g2 c
  62.     dmamux_syncParams.SyncSignalID  = HAL_DMAMUX1_SYNC_TIM12_TRGO; 0 J% }7 b+ X, d5 q" g! R  m8 j( p
  63.     dmamux_syncParams.SyncEnable    = ENABLE;    - |0 z9 z. K- w4 \
  64. # E6 f8 X" V) |" N: m
  65.     HAL_DMAEx_ConfigMuxSync(&hdma_tx, &dmamux_syncParams);! y4 |& C# O' |1 L& ?5 b
  66. ) R; N5 E7 |( {# ~( l; ]3 I4 c
  67.     //LPTIM_Config(_ulFreq);- M% k6 c" H7 N1 R  B# T8 |
  68. / F& u( z& K8 B8 j+ ]8 q
  69.     TIM12_Config(_ulFreq);; Q/ P4 M: R4 H" ?5 V2 O

  70. 5 s! p8 ~1 h, W/ ?# J. g; V# g
  71.     /* 启动DMA传输 */; k5 L' c; ~+ Y2 i& l
  72.     if(HAL_SPI_Transmit_DMA(&hspi, (uint8_t*)g_spiTxBuf, g_spiLen/4)!= HAL_OK)   
    , G# \8 x- d$ O2 {: n
  73.     {
    ' L8 u# e/ e( a8 w
  74.         Error_Handler(__FILE__, __LINE__);) s' C" J5 o. v' B0 V  x
  75.     }/ [* C) Q, K6 f$ R3 n
  76. }# U$ ]# j5 e6 N3 s" d3 J2 `) s
复制代码

$ \! B# W" r  m# f7 N8 c+ ?
5 k7 Y1 n8 K# f5 ~这段程序里面最关键的就是置红的部分。作用是配置DMAMUX的同步触发功能,触发周期由TIM12控制。
- T: y2 O; L) p) |" r7 N) b2 N4 k) M: u4 `, J
75.6.4 第4步:24bit数据的DMA传输解决办法
: T/ T$ C! r7 {6 h由于通用DMA1和DMA2仅支持8bit,16bit和32bit数据传输,我们这里要传输24bit数据,解决的关键就是配置DMA为传输宽度为32bit,并将传输的数据由24bit再补一个8bit的任意值组成32bit即可,实际的传输会由SPI完成。, v! |9 j- D4 p- W+ M# e

! z! [- \8 x, Y+ C
  1. /*: q8 P& S- j4 A% M/ _
  2. *********************************************************************************************************
    % [6 I' X3 I- w+ w1 e1 @
  3. *    函 数 名: DAC8501_SetDacDataDMA
    4 u& S2 t* r5 q) y+ q
  4. *    功能说明: DAC8501数据发送,DMA方式
      X6 E) w9 i: @8 Y
  5. *    形    参: _ch         1表示通道1输出* `3 r/ `3 [% H6 w% @% m* X; H
  6. *             _pbufch1    通道1数据缓冲地址' a( k/ f. e$ ]/ t, o+ s1 d
  7. *             _sizech1    通道1数据大小
    / O( K; x* |+ E. w8 G# M  v2 y
  8. *             _ulFreq     触发频率,推荐范围100Hz- 1MHz,注意这个参数是触发频率,并不是波形周期。
    ) D/ I$ j3 W/ Q) e. A
  9. *                         这里触发一次,SPI DMA传输一次24bit数据。
    * m: g+ i+ k5 `5 \( Z' A1 q
  10. *    返 回 值: 无4 R8 O3 A$ E0 J8 q1 T# @% g
  11. *********************************************************************************************************
    1 J9 p; V# A# `! o4 i$ U% V
  12. */! f: L( ]; P+ K' g7 x" r
  13. void DAC8501_SetDacDataDMA(uint8_t _ch, uint16_t *_pbufch1, uint32_t _sizech1, uint32_t _ulFreq)/ i7 }( J7 {# A$ K( i3 H3 Z4 G
  14. {% @/ k7 D2 p+ n& _4 W# w
  15.     uint32_t i;
    ' E4 ?, S: x. e0 n7 I2 q6 A* _9 u
  16.     uint32_t _cmd;, J7 B0 V! b+ V, h! y. e! ^7 ]
  17. - S, C$ j8 w" @7 _/ q7 }1 F
  18.     g_spiLen = 0;
    $ Q; w& h+ d9 q! s1 l, W

  19. 0 N, H$ `$ q% U: g" ~' }
  20.     switch (_ch)
    . P* ~' X+ w; ^4 g1 V  K
  21.     {
    # t1 ?4 Z8 \( @# U& ]
  22. 9 T6 E) y3 a! T# c
  23.         /*; b, k! ?9 g8 p) t" L; x
  24.             DAC8501.pdf page 12 有24bit定义+ m9 Q1 P; Y& U5 X& F
  25. / B% L7 m% y9 v, I! @4 X
  26.             DB24:18 = xxxxx 保留: K- P4 v) _/ `! }' D
  27.             DB17: PD14 a# F% K7 r$ M5 ]$ h
  28.             DB16: PD0- l+ f( m  s/ a8 B2 c' J& ]
  29. ! a" `" I3 T- H( l0 Q6 P  x
  30.             DB15:0  16位数据3 z( g5 P1 k& E( Y7 `; n
  31.   v; I3 Y( L; c
  32.             其中 PD1 PD0 决定4种工作模式8 W& c, |" _7 X; l: ^
  33.                   0   0  ---> 正常工作模式
    6 L/ y) C& K1 e2 w- F' L* w
  34.                   0   1  ---> 输出接1K欧到GND: x5 e5 h5 ?6 Q9 V% t5 }
  35.                   1   0  ---> 输出100K欧到GND
    ; ~) d: p% ?9 p
  36.                   1   1  ---> 输出高阻2 O. j/ m4 F  r- q# A
  37.         */  |$ K8 u3 n0 ~& m

  38. & |" N4 F3 S9 C0 c
  39.         /* 通道1数据发送 */: W; ^" D1 R" V  c
  40.         case 1:
    2 T+ z: m2 l& ]
  41.             for(i = 0; i < _sizech1; i++)& l( \( D5 [' `% ?8 O8 V' e/ a
  42.             {! `3 k3 f2 ^$ r* y" w6 ^
  43.                 /* 更新需要配置PD1和PD0,当前是选择的正常工作模式 */
    & Q- p, R: b( O. D
  44.                 _cmd = (0 << 16) | (_pbufch1 << 0);' _0 i  p* Y0 H
  45. 6 A' [% q; t7 M# I+ }6 Y
  46.                 g_spiTxBuf[g_spiLen++] = (uint8_t)(_cmd);! G; f/ j; y& @- e: F% D) s4 [
  47.                 g_spiTxBuf[g_spiLen++] = (uint8_t)(_cmd >> 8);
    ( |! v+ K0 n' H. s! }3 E  W. e
  48.                 g_spiTxBuf[g_spiLen++] = (uint8_t)(_cmd >> 16);
    2 _$ @0 {% s4 {- o1 M7 j$ B% {
  49.                 g_spiTxBuf[g_spiLen++] = 0;
    0 f  N0 d; U% t- W
  50.             }
    7 F2 N, \, K$ P' z9 d! f% ?& O
  51.             break;& J$ T7 D) x  F7 q5 z" \
  52. 0 t! a3 j+ J# j! B% f  C; `  T
  53.         default:" ?5 p1 S2 K# I6 W! x3 P/ g
  54.             break;, G' Q6 Z. d9 p. Q9 I) X

  55. ( |3 c- V2 Z# a/ e2 _6 U. c
  56.     }
    0 E1 H0 E0 t6 V6 I/ S
  57. % \+ q6 C! A5 l* k& y
  58.     bsp_spiDamStart(_ulFreq);
    , u7 H; }, J% R
  59. }
复制代码

# n/ x1 v+ [  O( Z1 X& m75.6.5 第5步:DMA缓冲区的MPU配置
" a# U! e. P# v: C# B% A$ j因为工程是用的DTCM做的主RAM空间,这个空间无法使用通用DMA1和DMA2,通过本手册第26章的内存块超方便使用方式,将DMA缓冲定义到SRAM4上:5 Q% O& }. H- n. {7 U9 t
. ~* t1 {- y: p1 ^" ^; j
  1. #if defined ( __CC_ARM )    /* MDK *******/
    " l. _6 t% S6 ^; g
  2.     __attribute__((section (".RAM_D3"))) uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   0 P  {3 Q" L7 C  D% |5 Z* j( n5 w4 S
  3.     __attribute__((section (".RAM_D3"))) uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];
    ( t  `2 c: c# o. M5 T2 b5 V! b* z7 D
  4. #elif defined (__ICCARM__)  /* IAR ********/
    : }  @$ G9 O. n1 {& V
  5.     #pragma location = ".RAM_D3"
    0 W! h3 [7 O3 o0 K% F6 W) O3 O
  6.     uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   
    . M( U6 W- d% z8 ?. d& a& {
  7.     #pragma location = ".RAM_D3"
    ) o% u0 R+ f5 }1 j/ H
  8.     uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];
    $ p- ~* g' C1 ?( U8 t+ z; X7 Q, p' p
  9. #endif
复制代码

9 ~+ J% ?' l3 R1 h. [, f由于程序里面开启了数据Cache,会造成DMA和CPU访问SRAM4数据不一致的问题,特此将SRAM4空间关闭Cache。. h9 w$ J0 o% A# F  p( j! |7 ~( a
    /* 配置SRAM4的MPU属性为Non-cacheable */
# M# Q( K. V* f/ z6 h
  1.   MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    6 q# R# Y) Q; n/ O
  2.     MPU_InitStruct.BaseAddress      = 0x38000000;5 c$ ~. |+ V: w, L
  3.     MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;
    ! A  o1 t% U" z5 R3 o* [- F
  4.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;4 o+ r1 j  C0 T# S/ b: z
  5.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;" v2 H$ n2 [" V
  6.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    0 Q( ~5 L: ]5 S! u; v8 O
  7.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    ' j* ^) j  n4 U
  8.     MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
    : q( w' v* n: Q/ B
  9.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    : X$ Z: \& d; W6 C1 ?9 j$ i
  10.     MPU_InitStruct.SubRegionDisable = 0x00;
    & _  R* B1 m9 S; z4 Z! E6 f( n
  11.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    7 T: Q, G6 z6 L5 u6 b9 C  R

  12. - c8 V1 X  }3 m8 W/ y
  13.     HAL_MPU_ConfigRegion(&MPU_InitStruct)
复制代码
由于程序里面开启了数据Cache,会造成DMA和CPU访问SRAM4数据不一致的问题,特此将SRAM4空间关闭Cache。% @$ j8 r' c: s' S' Q# I. A7 u' [

* F( D7 w: e3 C& @; Q75.6.6 第6步:DAC8501的时钟极性和时钟相位配置: p4 _3 A" V. V
注:与本章74.5.3小节内容是一样的。/ `) G, R4 R  r: P1 G4 P' v$ E

1 c. G" N  l3 C! F2 w4 W9 a$ R首先回忆下STM32H7支持的4种时序配置。. h( m* Z& D: l
4 R" ]' Z1 V9 Y- v- g$ ]+ u+ ~, j/ d
  当CPOL = 1, CPHA = 1时6 p3 f8 h$ M( ?& j, l1 K# a% p
SCK引脚在空闲状态处于低电平,SCK引脚的第2个边沿捕获传输的第1个数据。- X6 t. X/ s' `. g  D- g

5 C0 b" G. ^2 {8 d: ^  当CPOL = 0, CPHA = 1时9 M  [( t/ o, a
SCK引脚在空闲状态处于高电平,SCK引脚的第2个边沿捕获传输的第1个数据。
) c3 [" `5 s, I% ]
9 |0 g% {  V  c& I  N/ J% S  当CPOL = 1, CPHA = 0时* p9 ~( O; L% \; }# N1 n9 v) J
SCK引脚在空闲状态处于低电平,SCK引脚的第1个边沿捕获传输的第1个数据。
, i3 H. x9 h8 @: {$ y  Q+ `2 ^% A4 }# f% C. L0 ?% f4 I. e$ ^
  当CPOL = 0 ,CPHA= 0时, I# f5 ^$ M' ]
SCK引脚在空闲状态处于高电平,SCK引脚的第1个边沿捕获传输的第1个数据。
' e1 Y$ D3 w1 H; v( T6 ]2 r
# t6 z  \2 N  Y

' m3 K% H' \2 T& X* n, N7 y% y
4 v4 \7 Z/ O$ `/ {4 K有了H7支持的时序配置,再来看下DAC8501的时序图:0 y% u& X0 w9 ]+ w3 \& E
6 }; B% _. }4 q( H9 Y- |6 A

- r5 _  h( e* k9 _6 Z8 g
* Y3 L3 i2 i" b2 P$ i/ J首先DAC8501是下降升沿做数据采集,所以STM32H7的可选的配置就是:
' R0 R7 u* k: z, K; v6 g! M$ r, {( n" q4 ]* K; z' L. W! u
CHOL = 0,  CPHA = 16 v( ~, \) N/ k! o) }

1 k3 T) ~, H* nCHOL = 1,  CPHA = 0
& c3 m2 _  a2 H! e, f! m
* E% u5 D7 V. L3 t. @对于这两种情况的主要区别是空闲状态下SCLK时钟选择高电平还是低电平,根据上面的时序图和DAC8501的数据手册,两种情况下都可以正常运行。经过实际测试,STM32H7使用这两个配置确实都可以正常运行。程序里面默认是选择CHOL = 0,  CPHA = 1。- w" ~" u+ S8 {) l- i( ?; C3 f

) v' p; L+ H7 |4 H3 [6 c( d75.6.7 第7步:DAC8501的最高更新速度计算
2 j: o  r% P+ O& h. G这里特别注意一点,定时器触发一次,就会让SPI以DMA方式传输24bit数据。8 N% q1 E# _; r: B( x( e
2 R9 N0 J5 T7 ]' T8 x- ?
配置条件:2 }+ D, m: ^; S( k
9 Q& a7 _# O4 V" g9 ]
  SPI时钟是25MHz,SPI数据传为24bit,每个bit需要时间40ns。
! L( {: J3 t! r6 c9 A6 F  hspi.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE0 M5 U! @# E' N) v
插入到NSS有效边沿和第一个数据开始之间的额外延迟,单位SPI时钟周期个数,即40ns。
- x( }# R' Y0 s! Q: T1 Z5 Q+ b
( E1 U/ p' I/ F! D1 |) Z0 P" d  hspi.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_10CYCLE
1 z% H& G& f$ L两个连续数据帧之间插入的最小时间延迟,单位SPI时钟周期个数,即40ns。: @* T% p; g+ T* Z% j
# x6 f/ m1 e9 P0 a, G% u  U3 z* E0 D
根据上面的配置,传输一帧(24bit)数据需要的时间:
+ M) u# }# _- D! T; ~: f7 n4 z6 w8 s. ?7 Z2 W1 t- v# z
24bit * 20ns+ SPI_MASTER_SS_IDLENESS_00CYCLE * 20ns
6 `& B8 i, O  Z9 [2 E
; I9 Z2 O3 j2 z6 L. o% l, y! m+ SPI_MASTER_INTERDATA_IDLENESS_02CYCLE * 20ns
: x" g+ s9 g' @; ]. c7 f! ~  c9 u
= 24bit * 40ns + 0 * 40ns + 2 * 40ns
( f) K" ]- j% K8 k( R
( V+ P' x) w* B7 ]= 1040ns。  y. S+ I: O1 Y4 a* K, Q

& a! Q0 K1 S; r( [) m) X$ o: J1 M1 e那么这种配置下,可以支持最高触发速度是1 / 1040ns = 0.961MHz,如果想速度再提升些,可以降低参数hspi.Init.MasterInterDataIdleness,推荐的最小值是1个时钟周期,那么可以支持的最高触发速度是1/1000ns = 1MHz。
) t( Z  D9 N& Y  _3 M- Y: ?: _! r* m+ f' n, N& \
认识到这些后,实际输出的波形周期也比较好算了,比如我们设置10个样本点为一个周期,那么触发速度为1MHz的时候,那么波形周期就是100KHz。
+ F. _" |7 f+ p+ K: R+ M3 S0 T# w3 C/ h8 H8 n
75.6.8 第8步:DAC值和电压值互转
5 S) l, `/ k$ q- N8 z9 s8 A; t2 uDAC8501模块的输出电压范围是0V到5V,对应的编码值范围是0到65535,为了方便大家做互转,专门做了两个函数:
2 [( A* ]7 ^8 ]' t' V+ v+ N$ I. x! z; T5 k
  1. /*
    ' C8 e4 P9 N: N7 X' b
  2. *********************************************************************************************************- t! x+ q; p9 I! Z" C! }3 K
  3. *    函 数 名: DAC8501_DacToVoltage& K. K. k, o6 d) B/ J6 t
  4. *    功能说明: 将DAC值换算为电压值,单位0.1mV
    $ F# W6 p8 s) K3 O* V' X, M( O
  5. *    形    参: _dac  16位DAC字! P  `" t* _6 `! i. G9 ^3 h
  6. *    返 回 值: 电压,单位0.1mV9 u' z3 U# f3 z- p9 M
  7. *********************************************************************************************************
    + z- g6 S$ L5 ?) Y& D
  8. */
    / A( H7 d- _! g" @% }& z% [5 R
  9. int32_t DAC8501_DacToVoltage(uint16_t _dac)  i) c+ z$ l/ V+ l4 T  U/ `, z
  10. {4 B1 z; l" c6 V! N9 p: X! }
  11.     int32_t y;/ u7 T. s& _+ E" l5 ]. G" Z

  12.   L0 S7 `8 D! N4 d' ]) T) p
  13.     /* CaculTwoPoint(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x);*/
    " [& Z: ~8 B- G7 @  A
  14.     y =  CaculTwoPoint(X1, Y1, X2, Y2, _dac);" H7 U# B# F# Y3 D, a$ Z
  15.     if (y < 0)
    9 n  y; h$ Y& N  V* k' L
  16.     {1 a# W0 M8 B4 p  f
  17.         y = 0;& Y1 b) f. y9 }; |8 p) I: e
  18.     }) Y" ^8 }6 K$ y$ b1 c
  19.     return y;$ V, X/ w. n! H
  20. }
    6 R- B, ]0 [0 [, Q2 O5 F, w: `
  21. $ i. w) j- O, G- A7 M
  22. /*5 Z6 n0 N: ]! U, ?: n
  23. *********************************************************************************************************
      }- v4 k* ~9 [6 l
  24. *    函 数 名: DAC8501_DacToVoltage
    1 J- \& f, q) h" m2 ^: ~
  25. *    功能说明: 将DAC值换算为电压值,单位 0.1mV
    . ?" a" I( R* j# r+ Z/ K& ?/ }+ }) ?2 j
  26. *    形    参: _volt 电压,单位0.1mV  n0 J$ Y2 I* H% b
  27. *    返 回 值: 16位DAC字
    4 n, D; B6 H' [* Y7 T! N
  28. *********************************************************************************************************
    1 {" A, E  B6 G, |2 g  J
  29. */: ?& ?6 `+ X9 \4 m$ m, y
  30. uint32_t DAC8501_VoltageToDac(int32_t _volt)
    7 E( V, G+ f" j. P% q, j
  31. {
    ; Z. I/ c  d* I" R4 l' d
  32.     /* CaculTwoPoint(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x);*/
      C% H6 h4 V& k$ A) S
  33.     return CaculTwoPoint(Y1, X1, Y2, X2, _volt);% D. G# g* G2 a( h% x
  34. }0 }* v/ @! M) x( D( h' i
复制代码

1 T; Z5 f. k: \! _# H: j$ N, \# E, Y; _% T# j
75.7 SPI总线板级支持包(bsp_spi_bus.c): K7 c, r  R/ _7 V* x" x  Y8 Q
SPI总线驱动文件bsp_spi_bus.c主要实现了如下几个API供用户调用:
1 `# E: F, {) g5 c% P2 E
/ \/ a  i, X! t, \8 |, R  bsp_InitSPIBus
( P$ Z) o2 D7 x: |/ e  bsp_InitSPIParam
& x7 k6 w. X2 W  bsp_spiTransfer
* Z1 L, [" X" ]% d75.7.1 函数bsp_InitSPIBus6 A& S! u5 M0 N) p& i( i
函数原型:
* B. u. P8 j; W1 O- }& C
% y9 T' T0 E2 Vvoid bsp_InitSPIBus(void)
8 c+ ~& K/ l7 _8 T
2 D( \( ?1 k+ S& U函数描述:
- l# D. p; |/ K0 k2 C: H. P
# h# B6 G9 N1 i2 t此函数主要用于SPI总线的初始化,在bsp.c文件调用一次即可。
7 W/ q3 W# x8 x/ \! G4 Z3 s+ T1 q% j7 L0 j" Y. ]
75.7.2 函数bsp_InitSPIParam' B; }. ]( A2 N/ G* B( r4 }0 ^9 X
函数原型:
' P5 o0 E7 N4 r5 ~1 S! B6 D
2 G! W( V2 _, R: yvoid bsp_InitSPIParam(uint32_t _BaudRatePrescaler, uint32_t _CLKPhase, uint32_t _CLKPolarity)
# |5 |3 j- G5 i: h' K# F
9 Y! T3 d3 J. v3 v5 U函数描述:
, q  I  ?! Z- u* v% r1 w# D. w; @( Q/ z/ d7 {. I8 D
此函数用于SPI总线的配置。6 O2 q. w9 ^7 Y4 d; t0 r; {7 G7 j1 d
3 f3 b) k, N( t
函数参数:
3 K  B( w0 k, b  B* n
4 ]! T7 [1 ~. K6 L  p; C9 v# X  第1个参数SPI总线的分频设置,支持的参数如下:: p2 E/ n& g1 A& L" R, B( X8 o
SPI_BAUDRATEPRESCALER_2    2分频$ Q. R$ x! `6 `' |, `: p
6 P2 B7 x) S5 y- V% x5 b( H2 A
SPI_BAUDRATEPRESCALER_4    4分频
1 w1 O* f' e/ P4 Z; J9 x7 N0 U, @, S) C/ s) E# M) j2 k
SPI_BAUDRATEPRESCALER_8    8分频- O9 O6 b/ i: O8 H

8 T' z" c7 G5 ?- l$ rSPI_BAUDRATEPRESCALER_16   16分频& T' B4 o; L/ S* ]
; w+ A( c' [6 ?4 _2 V
SPI_BAUDRATEPRESCALER_32   32分频- T5 @6 a; a  X) @3 R2 ?
) u2 G7 n! ]! T7 f# O
SPI_BAUDRATEPRESCALER_64   64分频
9 m' H- e9 W# N; z- k) @1 M8 r) K& [% I% d/ E
SPI_BAUDRATEPRESCALER_128  128分频
. X" l6 G5 Z# r, _$ m, j
, c/ _0 D3 D3 d* j8 I8 M& mSPI_BAUDRATEPRESCALER_256  256分频- ^. w+ X! l4 y) A6 V. [" L
) o( G5 p) ]7 R: p
  第2个参数用于时钟相位配置,支持的参数如下:
7 g$ i, W" b+ ]SPI_PHASE_1EDGE     SCK引脚的第1个边沿捕获传输的第1个数据
4 U( T* F% U  u- V3 L! v; A! M
. o5 t( U' g+ [$ a6 ]SPI_PHASE_2EDGE     SCK引脚的第2个边沿捕获传输的第1个数据3 f4 v9 T3 J( m1 V, }+ E" b: Q3 f
5 }" _9 K  ?9 s% E" d! @
  第3个参数是时钟极性配置,支持的参数如下:
- s% u% S1 V* x' p0 }8 pSPI_POLARITY_LOW   SCK引脚在空闲状态处于低电平( n$ U0 Q3 z5 L
( c8 ?) R# r; d+ e. s
SPI_POLARITY_HIGH   SCK引脚在空闲状态处于高电平! ]. b$ z0 `1 Y( P
: ~1 `" N+ @+ L' ^" N
75.7.3 函数bsp_spiTransfer2 r7 L; J( }: |& e7 g
函数原型:% q; E$ a4 f% a5 \2 q9 B! a- X

" R6 X, D2 Y, y6 Zvoid bsp_spiTransfer(void)3 Z. p9 Y# Y/ W1 l' _& i+ x( N
  T" n+ j. H, ~4 @, v- a
函数描述:: ]( Y8 T3 U4 X( `, u

% q8 Z: g  |, ^! _) v0 T此函数用于启动SPI数据传输,支持查询,中断和DMA方式传输。
8 m  K; s+ o& H/ s
+ K4 S! J( F  G4 i  p5 T, |; A6 B75.8 DAC8501支持包中断方式(bsp_spi_dac8501.c)  j9 K$ L; a8 J+ v2 v4 i: {
DAC8501驱动文件bsp_spi_dac8501.c主要实现了如下几个API供用户调用:
- ]8 J1 O* ]9 O% a0 ^; v: j8 E- w" J0 o
  bsp_InitDAC8501
4 ^) R: ]# a6 U$ \. A  DAC8501_SetCS1
6 Y1 T9 t! {2 r; b2 \$ f7 U  DAC8501_SetCS2
, V" D' F! O5 J. Z  DAC8501_SetDacData$ ]7 m+ ?* s. t9 P
  DAC8501_DacToVoltage4 Q* h% f/ D9 C  u. _
  DAC8501_VoltageToDac
* ?! [6 U* |& g/ y$ T; q75.8.1 函数bsp_InitDAC8501
) K& h7 \* O, F9 L6 ^4 K; {函数原型:
1 M' r% m3 H2 E/ e( D: R; p% Q4 S1 {
void bsp_InitDAC8501(void)5 E; P* g  r* O: O2 j
3 n; o* F% \1 M' }6 q( B) L3 O' }
函数描述:+ u6 V+ r. l& |
+ i6 b+ j) u& _
主要用于DAC8501的初始化,调用前务必先调用函数bsp_InitSPIBus初始化SPI外设。4 V/ z! a' n0 t8 v
4 ]- x/ v4 K2 T3 m; l1 z
75.8.2 函数DAC8501_SetCS1# j/ `: b. N2 {5 b4 ^
函数原型:
7 y2 P7 E1 j+ I5 [6 P' g
0 O8 q3 z0 V% @1 e% Svoid DAC8501_SetCS1(uint8_t _Level)
3 k0 l3 U0 Q! n" b
1 r4 g, X6 j/ d2 o函数描述:
* X( O! [$ R5 |3 M6 l. O/ D; V6 Z! h( v& y/ v
此函数用于片选DAC8501模块上的第1片8501。
% f& A8 o7 B2 A  t: A
6 H, w+ d+ S3 }函数参数:
* f/ ?3 r. l! [: o  n% _' i
- ?1 s4 i+ g) ~, u  第1个参数为0表示选中,为1表示取消选中。
5 F. Y% F6 g; L$ r: `, b3 q# E75.8.3 函数DAC8501_SetCS2
" j. R* ~% }0 p4 P函数原型:
2 ^4 R# B! I, @, y9 ~$ x- e# c0 ]3 z- n0 m9 |8 F' K. @/ ~
void DAC8501_SetCS2(uint8_t _Level)
' v5 `: F- s' V5 f: z1 R8 e& f7 W) J- G  ^, M: l! U& O
函数描述:* e" Y% o3 _4 ]6 f
3 W3 g' U7 P' ~/ Y* {
此函数用于片选DAC8501模块上的第2片8501。
" X8 o% i9 \* }: D: y2 o! Q- h) i& n$ H# Y5 {: W: `) }
函数参数:
( ?7 a& s" w; ~$ v* k$ \2 I; ~
/ \/ Y0 S. g, }+ J2 S6 N  第1个参数为0表示选中,为1表示取消选中9 E* Q# M# m# @: M. k$ Q
75.8.4 函数DAC8501_SetDacData/ o3 W7 V5 a- z0 F0 d- ]
函数原型:4 `  S: Q4 M: m$ e% e

7 h5 v- j3 i9 n, G& }0 y% Xvoid DAC8501_SetDacData(uint8_t _ch, uint16_t _dac): p. o6 Q- i+ W( C& w

8 i. [. f$ H6 V4 J; g  z函数描述:
7 e6 T; X8 I) {: k9 l! W
8 ?7 g+ P; k1 r# `; ]) \- l此函数用于设置DAC输出,并立即更新。
7 C2 D) a# u7 n6 F4 s; `
: n' D4 T3 ]  w0 L1 u3 I1 |# I' W& M函数参数:9 L9 e. u9 C5 `& z! j

9 w( H7 ?6 I1 f5 d* x; N& x3 X) V; `# G8 F  第1个参数为0表示通道1,为1表示通道2。( T# g0 H3 R7 i( U' r% A1 z6 a
  第2个参数是DAC数值设置,范围0到65535,0对应最小电压值,65535对应最大电压值。0 h0 \  m. L4 {5 M: a+ H5 }
75.8.5 函数DAC8501_DacToVoltage
  A* W) |0 `$ A  k7 b2 R函数原型:
  k' t0 U* p% g4 Y2 g- r
+ k9 C+ C+ q+ Q' j+ E) pint32_t DAC8501_DacToVoltage(uint16_t _dac)
5 ?& i' u. d; X" [# u& u0 p7 o+ z
函数描述:" L" T' d7 Q9 ]9 E

- m$ G) v1 a$ z: W, W此函数用于将DAC值换算为电压值,单位0.1mV。
" W) G, `4 ~8 X6 ?0 ~( x+ s7 R2 N. _9 p7 M
函数参数:3 j/ f) s; y( Z+ X" D) W
1 |8 a5 L- |% N! ^2 h) `  C7 |
  第1个参数DAC数值,范围0到65535。9 x6 t: D1 x) g* z% p2 d
  返回值,返回电压值,单位0.1mV。
1 C8 b/ s% s& @9 K75.8.6 函数DAC8501_VoltageToDac1 @( C9 o# _5 _; d
函数原型:
: {2 T4 n! M- b# v2 O* @0 `+ T2 ?& V* s5 v0 `
uint32_t DAC8501_VoltageToDac(int32_t _volt)
0 ^6 X; ^- @* o5 v
3 ^* q5 t# S. H% {! g函数描述:
. C' [, s, d' d$ Y5 a
" X( ]. Q" r: R3 \9 k此函数用于将电压值转换为DAC值。
6 J4 t3 Q1 ?& Y5 |! m* d
3 Z/ o6 _" \& T8 `' O函数参数:+ |( N7 C4 W0 l) P0 G7 ~9 C

4 C. q( {0 ?6 Q7 y  q6 c" A3 v* k, T  第1个参数是电压值,范围0到50000,单位0.1mV。+ Z0 g" o- c: Z/ K1 }9 t
  返回值,返回DAC值。. L7 f, F# ?3 z% {: x) p/ {5 w6 R! w
75.9 DAC8501支持包DMA方式(bsp_spidma_dac8501.c)
7 S$ E$ V  b% d0 _& i* \% ?% {0 \1 SDAC8501驱动文件bsp_spidma_dac8501.c涉及到的函数比较多,我们主要介绍用到的如下几个函数:2 s0 A# {4 m0 Z: T# ?" ?2 }6 @
9 j, q: Q- y8 y  i5 x, J/ Z
  bsp_InitDAC85011 m8 ]2 `+ D- K+ H4 S
  DAC8501_SetDacDataDMA
8 {" F  H" h6 K  DAC8501_SetDacData: I7 @: k+ p" h( L$ b( @( a) o
75.9.1 函数bsp_InitDAC8501  w, B% l" B% F- ~
函数原型:9 \: b# C/ d3 R% G& u
2 |6 a! _1 b! ~5 B4 w
void bsp_InitDAC8501(void)
8 T. z; W. }# z
2 F/ j; R8 k. A$ |$ i; l  ?& L1 p函数描述:
$ v  ~$ W( }! v  n3 T" y, g. K  J: U2 `! X% U3 i" U6 u
主要用于DAC8501的初始化。
8 P8 l7 ?) @2 @! U6 g* |% S2 Q- z; ~6 t
75.9.2 函数DAC8501_SetDacDataDMA
' w  @: F, l" A$ L, g函数原型:0 j8 z& q  q% F$ h4 O* ^- \

! M' U! g$ G6 w: _void DAC8501_SetDacDataDMA(uint8_t _ch, uint16_t *_pbufch1, uint32_t _sizech1, uint32_t _ulFreq)7 h# v$ }' _5 j& A1 `8 f/ w

- M+ P3 W3 Z2 ^& v% I* K. E函数描述:
7 G( b. N! a" f, I2 J7 s  h' T2 B. n" G  O8 _, }  W
此函数用于SPI DMA方式数据发送。
, d! }' d& L) V& }9 o9 F
( x/ z4 p8 J6 L, C+ Q( y6 c6 y' Y函数参数:! u5 Z1 T6 @* [* k+ U

9 e5 |0 e5 x. n% n  第1个参数用于选择的通道: 1表示通道1输出
1 x4 A$ J% e" L5 Q& i1 t- [% t  第2个参数表示通道1数据缓冲地址。
) Z& K$ [/ X+ v& k+ r% N  第3个参数表示通道1数据大小。
. ^1 B0 k, d" l- J0 Q8 E  第4个参数表示触发频率,推荐范围100Hz- 1MHz,注意这个参数是触发频率,并不是波形周期。这里触发一次,SPI DMA传输一次24bit数据。8 c' G9 T) m/ L0 ^4 X
75.9.3 函数DAC8501_SetDacData
8 D& ]  D- j9 [( t函数原型:, \5 `  R( S' n" u
0 p  m) Q% v6 ]: {1 A
void DAC8501_SetDacData(uint8_t _ch, uint16_t _dac)
$ U7 N7 d( \; |
1 Y/ [7 w# F$ V6 Y+ l% D函数描述:% J. Q7 \% r% K# A7 ]- ]

6 y+ a) m3 `" t4 w* Y- Z* O2 W此函数用于设置DAC输出,并立即更新。
. k1 J7 i( u( W% b( L+ U
9 k/ D0 o9 P5 i函数参数:
6 \" |; p& ?) n) _5 W& k1 Z9 X6 y: n0 c# x  K7 ^) e+ j/ ~
  第1个参数为0表示通道1,为1表示通道2(对于SPI DMA方式,仅支持通道1)。
8 j# K2 J! v/ T# f  第2个参数是DAC数值设置,范围0到65535,0对应最小电压值,65535对应最大电压值。0 `- d) z; ]* R
75.10          DAC8501驱动移植和使用(中断更新方式)6 q( S" r' m5 }4 T4 l' z
DAC8501移植步骤如下:
' V3 ]+ U* x& k& F# |
% @4 Y5 X' Y2 C& H" c2 d  第1步:复制bsp_spi_bus.c,bsp_spi_bus.h,bsp_spi_dac8501.c,bsp_spi_dac8501.h到自己的工程目录,并添加到工程里面。
' g2 O& h5 I  x6 a9 ^/ D  第2步:根据使用的第几个SPI,SPI时钟,SPI引脚和DMA通道等,修改bsp_spi_bus.c文件开头的宏定义
. _6 I* A  k. r# ]* h7 o, E' `- s
  1. /*5 i. ?+ L1 ~/ B
  2. *********************************************************************************************************8 Q9 _0 P+ {, b" Q; V$ N( O
  3. *                                时钟,引脚,DMA,中断等宏定义
    % F5 f1 D0 }% W9 d
  4. *********************************************************************************************************: u. b5 l2 A. d! j- e* Q
  5. */, m, o: d2 P: X" K: Q
  6. #define SPIx                            SPI1
    2 V4 y, ]* m7 W( T* Q
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()# a% H, P% y" T: {
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()' O2 A1 t5 m( T1 T8 {  ~

  9. 7 }$ q% D: k' J1 }
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()8 N" M* c3 |2 M
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()
    $ Z5 j" V0 g) Q. w
  12. 5 c$ P5 {  k  r7 A+ r4 ~3 M- J
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()5 Z- J. R1 L3 T  Q: F5 j  Y
  14. #define SPIx_SCK_GPIO                    GPIOB
    4 C- J/ ?8 [. C2 a! U! ?. C  c* `
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3
    $ s6 }  z" Q  \* e) m* c, Y1 O$ ^- L
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1
    1 i) A* h5 |0 C( Y3 V5 d$ y, i
  17. 7 Y, e* b: R& b& R
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()+ P% f- g; y; o' `9 M8 y1 P
  19. #define SPIx_MISO_GPIO                    GPIOB
    5 ^3 [5 H: d! A! {: P6 y
  20. #define SPIx_MISO_PIN                     GPIO_PIN_4# |  Y" o3 B: {
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI1
    & z/ n  X7 D. l) R7 t  J, d

  22. + d5 g" @" j, j$ C4 S7 B
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()6 {7 m0 i6 K: F. X& e" W0 y6 m1 Z$ M
  24. #define SPIx_MOSI_GPIO                    GPIOB! g# x4 r0 o$ {
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_57 |/ a2 E* c0 {7 s4 i
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1
    4 J7 Z8 A/ s* u

  27. 7 W; H: Z7 N  }& X  I: J. d
  28. #define SPIx_TX_DMA_STREAM               DMA2_Stream3' A& U! W& T) o2 C
  29. #define SPIx_RX_DMA_STREAM               DMA2_Stream2$ Q5 T0 n1 m% _7 x! N/ P

  30.   C9 s) o# f0 b, R* o4 d
  31. #define SPIx_TX_DMA_REQUEST              DMA_REQUEST_SPI1_TX* l: c- k  s8 z/ }
  32. #define SPIx_RX_DMA_REQUEST              DMA_REQUEST_SPI1_RX: f* j5 E. w1 A" {2 z  g0 Y

  33. # `4 S. Z* S+ j' T/ F6 U6 i# ?
  34. #define SPIx_DMA_TX_IRQn                 DMA2_Stream3_IRQn5 C/ N  M% e8 }: c+ m. I# y
  35. #define SPIx_DMA_RX_IRQn                 DMA2_Stream2_IRQn
    / n2 q8 _! e) h: l3 B' I! f
  36. 3 m0 m7 S' a! a! _; V' @. r: {
  37. #define SPIx_DMA_TX_IRQHandler           DMA2_Stream3_IRQHandler5 z: I2 b4 J3 L: i! |
  38. #define SPIx_DMA_RX_IRQHandler           DMA2_Stream2_IRQHandler
    , D1 z" L$ l" v4 J0 O& F

  39. 1 d9 D3 D8 p5 R% \
  40. #define SPIx_IRQn                        SPI1_IRQn
    ( Y; u+ w+ R' H) T  {
  41. #define SPIx_IRQHandler                  SPI1_IRQHandler
复制代码

8 f3 J% l- Q( O! W- ?# u  第3步:根据芯片支持的时钟速度,时钟相位和时钟极性配置函数DAC8501_SetCS1和DAC8501_SetCS2。
, d2 V' [& n$ x. q1 U. N! |DAC8501_SetCS1和DAC8501_SetCS2。! A* W/ y; x+ b/ z) u. D* C
  1. /*
    7 k9 P/ ~3 ^/ O6 y' c* S# N8 B
  2. *********************************************************************************************************
    0 ?" k* t# ]3 l! j. b" R
  3. *    函 数 名: DAC8501_SetCS1
    - T" l+ i2 x( D" M  d
  4. *    功能说明: DAC8501 片选控制函数& i5 t/ b1 }3 d& j, J
  5. *    形    参: 无  X1 H6 L1 {" ?# U
  6. *    返 回 值: 无
    - P) T( ]! R6 L2 l: L8 J! `- \
  7. *********************************************************************************************************1 [1 }0 I& L/ V/ E0 N& N
  8. */
    $ Y: t0 d  J7 @- n0 G* J% ?
  9. void DAC8501_SetCS1(uint8_t _Level)2 e1 M1 {. S! \( I- p' A
  10. {8 Z, T  O) U! l0 T8 w2 }
  11.     if (_Level == 0)
    ; t8 ?! Z5 ]" _5 R* ^$ N$ ~. q
  12.     {
    : I7 w6 R( ?4 k0 Y# t: ~
  13.         bsp_SpiBusEnter();    /* 占用SPI总线  */    ! h/ R) k0 B1 Q+ u
  14.         bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);        $ @! u4 F( O& A; ]) e5 m
  15.         CS1_0();
    5 d7 l1 L7 N4 n& T2 ^
  16.     }- a/ G6 L0 J9 U- H+ ~7 {
  17.     else5 |: i! {. I1 q: A/ F
  18.     {        
    . J# U( @6 u. ?8 p$ J( H; w' _9 ?" q
  19.         CS1_1();   
    / i+ O' {: X' s' \. q/ W4 ^7 g* Z
  20.         bsp_SpiBusExit();    /* 释放SPI总线 */
    : J2 w+ \  d1 Q# o3 |) D) t8 ^; R
  21.     }   
    8 O" h. @& h; x
  22. }
    ! j) d* I* `6 s2 X) F9 _

  23. 6 M1 ]! R. c2 `9 Q' u
  24. /*
    $ P+ n! ?' h6 Z9 l6 V1 G
  25. *********************************************************************************************************
    ( O) E( ~+ X# U4 C) h
  26. *    函 数 名: DAC8501_SetCS2(0)
    8 |6 {! a: _4 e2 y8 |. {
  27. *    功能说明: 设置CS2。 用于运行中SPI共享。
    . C: t, o; d9 W. n' {
  28. *    形    参: 无
    9 @/ I. Z7 I1 u
  29.     返 回 值: 无
    8 M, y0 ]! X4 z: y- s
  30. *********************************************************************************************************4 L: l# u( h. F
  31. */
    * e5 b/ ^% u( d# ?& Y  {3 I
  32. void DAC8501_SetCS2(uint8_t _level)
    ! \2 y2 {7 J3 {2 N& E9 u0 w
  33. {# k5 w3 {( x2 u' M4 @
  34.     if (_level == 0)
    ( R* r" A) Z0 H
  35.     {
    2 m' G' _! E1 l1 X9 O$ B+ D) \
  36.         bsp_SpiBusEnter();    /* 占用SPI总线  */
    : h- r7 b2 w  f/ [
  37.         bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);        ' z4 @% K4 w& h3 _
  38.         CS2_0();$ U: G' g2 |5 F' f5 r* l1 E
  39.     }, `8 y# V$ w0 v, s/ _2 ^' I6 T
  40.     else* \$ U$ E7 S1 `  p2 }( n/ A
  41.     {
    9 Z# X6 v' O$ B2 ^
  42.         CS2_1();: ]( V. U1 @, K0 s# H6 h1 u
  43.         bsp_SpiBusExit();    /* 释放SPI总线 */
    ' G6 N9 \$ {' {9 w; E# Q
  44.     }/ ]# `8 |' c2 c8 V! v/ \
  45. }$ a, y4 R  x) r, u
复制代码

* Y! E1 N# k: C- q
- i# A: j4 S. G: e  第4步:根据使用的片选引脚,修改bsp_spi_dac8562.c文件开头的宏定义。7 F& b5 @6 q7 o* Z7 C; `
  1. #define CS1_CLK_ENABLE()     __HAL_RCC_GPIOG_CLK_ENABLE()% }- G$ x' X6 Y1 U$ U8 G/ j* g
  2. #define CS1_GPIO            GPIOG
    - k* w9 P7 s6 [
  3. #define CS1_PIN            GPIO_PIN_10
    + n; G$ C  b6 d* ~3 g

  4. , h" {; B( J5 c9 z1 \; |
  5. #define CS1_1()            CS1_GPIO->BSRR = CS1_PIN
    1 Y3 [$ {2 L0 e# {
  6. #define CS1_0()            CS1_GPIO->BSRR = ((uint32_t)CS1_PIN << 16U)1 I, ^6 g% o0 M2 c* I) T

  7.   |0 b5 D* [5 |# Z- a
  8. /*特别注意,我们这里是用的扩展IO控制的 */   
    4 z" {) h' g* ^3 p, O5 r0 r; K" V
  9. #define CS2_1()            HC574_SetPin(NRF24L01_CE, 1);( P7 x, H; `: {% J5 l( s: |
  10. #define CS2_0()            HC574_SetPin(NRF24L01_CE, 0);
复制代码
% _7 H7 i* D' [& S6 u; m4 W$ F: P
  第5步:如果使用DMA方式的话,请不要使用TCM RAM,因为通用DMA1和DMA2不支持。并为了防止DMA和CPU同时访问DMA缓冲造成的数据一致性问题,将这块空间关闭Cache处理,比如使用的SRAM4:
/ b( o1 S% s  j) Q+ C: P1 Q
  1. /* 配置SRAM4的MPU属性为Non-cacheable */
    3 n: {* c1 R: L6 o9 |  ~
  2. MPU_InitStruct.Enable           = MPU_REGION_ENABLE;# f5 M& I; m* ?( z
  3. MPU_InitStruct.BaseAddress      = 0x38000000;: C( J. Z8 P* [& W) f0 z
  4. MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;0 u. f3 q$ O# a3 e
  5. MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    ) w9 T4 H1 t! ?' g
  6. MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
    / X# B  h/ R0 V1 @  H5 T7 y6 W
  7. MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;  _! s" U0 O# j1 y
  8. MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    $ b& h: q; T) u6 Z4 t1 u- o+ ?5 u
  9. MPU_InitStruct.Number           = MPU_REGION_NUMBER2;3 n, L' q9 I, @: b2 L" W: K
  10. MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    & ?! O& _7 l% B# a
  11. MPU_InitStruct.SubRegionDisable = 0x00;- i5 T1 E; b4 ^8 @
  12. MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;# b2 ?% g$ h; T

  13. 2 P% w' U7 z9 g  ]: x& x) [8 ^
  14. HAL_MPU_ConfigRegion(&MPU_InitStruct);
复制代码

4 F+ A: k9 N0 d' z! c9 n  k+ c  第6步:初始化SPI。
4 d! K9 P( ?6 p2 J
  1. /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */
    ' W9 V: }& p! E0 F  `
  2. bsp_InitSPIBus();    /* 配置SPI总线 */        ; s& y9 r1 Z/ Z( Q( Z' g$ _
  3. bsp_InitDAC8501(); /* 初始化配置DAC8501 */
复制代码

4 @- Y( J) n3 Y* F1 P" C+ |7 E" M  第7步:DAC8501驱动主要用到HAL库的SPI驱动文件,简单省事些可以添加所有HAL库C源文件进来。& }; v% K$ m, b
  第8步:应用方法看本章节配套例子即可。  V+ z' {" ?' j' [
75.11          DAC8501驱动移植和使用(SPI DMA更新方式)
- F$ t" [2 d8 ?$ X& ^' dDAC8501移植步骤如下:7 ]: O1 q+ d& G- \" }6 s

* R( u- i1 n/ r: B4 J8 X; N/ e  第1步:复制bsp_spidma_dac8501.c,bsp_spidma_dac8501.h到自己的工程目录,并添加到工程里面。/ e+ W2 M% n* P! b
  第2步:根据使用的第几个SPI,SPI时钟,SPI引脚和DMA通道等,修改bsp_spidma_dac8501.c文件开头的宏定义
) }! T" j; X& u! b5 h3 b
  1. /*
    * x# I; P0 p5 Y
  2. *********************************************************************************************************
    9 @" @* x2 k. d& o" v
  3. *                                时钟,引脚,DMA,中断等宏定义7 ?3 |# }  e1 _+ V7 o: T) p: a
  4. *********************************************************************************************************; [2 o, M" |8 i9 B) o1 o
  5. */& s3 l& J* c: W! l. q# f' S7 v% |6 M
  6. #define SPIx                            SPI1
    1 |1 i* v0 Q2 q* M1 |8 B
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()! q" I: w$ }; X, k0 Z
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()8 D$ B; z5 d- H" R) z% s
  9. 2 g0 S# U, I, j) N$ O% ^
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()4 l& P/ R  v* S2 T8 J4 O
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()8 A* q' }1 R6 {: N
  12. " Z' O5 S- w4 s2 `* @2 @% \/ {6 r
  13. /* SYNC, 也就是CS片选 */   
    6 x5 @0 B0 ]* {* p  N* T: L
  14. #define SPIx_NSS_CLK_ENABLE()             __HAL_RCC_GPIOG_CLK_ENABLE()
    * f' g4 [1 c& {# m; J# j+ b
  15. #define SPIx_NSS_GPIO                    GPIOG
    3 |: U! t  E! R
  16. #define SPIx_NSS_PIN                    GPIO_PIN_10' I9 @# y5 R8 F4 n9 i! s
  17. #define SPIx_NSS_AF                    GPIO_AF5_SPI1! k; I' A3 W( M- `" k

  18. 5 @' `/ I; l8 Q) @- P( K
  19. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()- Z: |4 e' o0 Z1 {3 L0 p
  20. #define SPIx_SCK_GPIO                    GPIOB
    " v) a- q% K7 j/ A
  21. #define SPIx_SCK_PIN                    GPIO_PIN_3! L8 H+ t' r( S9 F  k& b
  22. #define SPIx_SCK_AF                    GPIO_AF5_SPI1
    ! T1 U0 j: g& G9 J
  23. * {$ W( ?' o. }( ^
  24. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()3 D/ B3 f# G" ?; ?: [
  25. #define SPIx_MISO_GPIO                    GPIOB
    0 |: [3 _1 X# O
  26. #define SPIx_MISO_PIN                     GPIO_PIN_4' h* Y5 O) ^4 Q+ Y
  27. #define SPIx_MISO_AF                    GPIO_AF5_SPI19 f2 u! S! _- Q4 i8 [. p

  28. & r. F! s% `" u9 K. J! N8 p8 q2 {
  29. #define SPIx_MOSI_CLK_ENABLE()          __HAL_RCC_GPIOB_CLK_ENABLE()
    2 \. A$ {. j1 @9 U: N; Z( N4 t
  30. #define SPIx_MOSI_GPIO                  GPIOB
    6 {8 a' S1 I6 H5 J" v" ~6 ?+ l3 G: [
  31. #define SPIx_MOSI_PIN                   GPIO_PIN_5
    3 X' e* e# g, R
  32. #define SPIx_MOSI_AF                  GPIO_AF5_SPI1
    ; _) _5 C, I& `
  33. / O% ]* c. d3 U; v" j
  34. #define SPIx_TX_DMA_STREAM              DMA2_Stream37 i; m6 e/ f. y; L4 @  X+ ~
  35. #define SPIx_RX_DMA_STREAM              DMA2_Stream2( u* }+ x; S0 R5 s; E6 G9 @( Q

  36. - P) v2 V: B( U
  37. #define SPIx_TX_DMA_REQUEST             DMA_REQUEST_SPI1_TX5 f, k- A/ }& \1 n, S! W, ~+ d
  38. #define SPIx_RX_DMA_REQUEST             DMA_REQUEST_SPI1_RX
    9 x2 v' S; T5 }" z: p" A

  39. : L$ i' o" k% e' w' `& f* D9 K
  40. #define SPIx_DMA_TX_IRQn                DMA2_Stream3_IRQn
    , p2 S$ n# d) N& j! q# y
  41. #define SPIx_DMA_RX_IRQn                DMA2_Stream2_IRQn
    ) V& h6 \6 U6 D- N

  42. ' {+ |9 t4 Y2 ~
  43. #define SPIx_DMA_TX_IRQHandler          DMA2_Stream3_IRQHandler$ h3 h/ F- V+ ?& |
  44. #define SPIx_DMA_RX_IRQHandler          DMA2_Stream2_IRQHandler  x! A/ B* y: R" u& s9 u  h9 p0 a
  45. 8 }0 ?% G1 I: V. Y" v" \
  46. #define SPIx_IRQn                       SPI1_IRQn- p3 u. Y4 E" H
  47. #define SPIx_IRQHandler                 SPI1_IRQHandler
    % f' d' t2 w4 H0 a* ^( ?1 @
复制代码

8 F1 r% C: n' C( L$ Q7 ?; `2 l2 c) n' t' O6 c/ [; J' I, F9 U3 K
  第3步:如果使用DMA方式的话,请不要使用TCM RAM,因为通用DMA1和DMA2不支持。并为了防止DMA和CPU同时访问DMA缓冲造成的数据一致性问题,将这块空间关闭Cache处理,比如使用的SRAM4:) C3 A4 y, v  ]9 S4 q  k
  1. /* 配置SRAM4的MPU属性为Non-cacheable */
    0 t% j& H0 g1 z" {# s
  2. MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    6 U; p+ _/ F) c3 {1 z& Z
  3. MPU_InitStruct.BaseAddress      = 0x38000000;
    3 A7 q5 x  w' f  Q( |1 t
  4. MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;
    6 R' x! o& w+ v2 M
  5. MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;1 @! N. N2 i# g0 `/ c
  6. MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
    ) |; C3 q8 I' e! t, o# Z1 @& M
  7. MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    ; h6 B9 V5 m1 M- i
  8. MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;& ?1 C6 h/ s) x$ r8 N
  9. MPU_InitStruct.Number           = MPU_REGION_NUMBER2;* q. j# J0 x2 |
  10. MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    $ a9 _- w" u5 |( Y0 [' V
  11. MPU_InitStruct.SubRegionDisable = 0x00;
      H8 i7 u( X7 w& Z
  12. MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;3 R5 b' v0 g0 r  g# x8 ~

  13.   ^2 H# i+ D3 V( _8 C9 m
  14. HAL_MPU_ConfigRegion(&MPU_InitStruct);
复制代码
第4步:初始化SPI。" ^5 u/ `- k7 ^
  1. /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */
    : G, q! ]0 G4 r+ Y# G- M* C0 v
  2. bsp_InitDAC8501();    /* 初始化配置DAC8501 */
复制代码
, Y: ~; o7 U# r1 U" p
  第5步:DAC8501驱动主要用到HAL库的SPI驱动文件,简单省事些可以添加所有HAL库C源文件进来。; r. A& o2 F! r
  第6步:应用方法看本章节配套例子即可
+ z2 [9 D5 j; G- u7 ]/ t) j75.12          实验例程设计框架3 l) l- U( r, T, a; h" ]7 E  Y; k, ~
通过程序设计框架,让大家先对配套例程有一个全面的认识,然后再理解细节,本次实验例程的设计框架如下:
5 O% O' ]5 @: [* a4 [! _
% |. f: ]/ \* b7 z4 k  r9 [
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

  ~! x" ^3 {$ h) r/ c% @) w4 D+ ?% I2 U2 |+ ~: d
  第1阶段,上电启动阶段:
9 J/ m! Y3 {( k" [' @' K  q0 F4 |6 {
这部分在第14章进行了详细说明。
- W( ^  P. E& {( r$ P: b: f  第2阶段,进入main函数:
9 h9 S# G8 e: H
" W% {1 j1 K: w  x) U0 ?) g  第1部分,硬件初始化,主要是MPU,Cache,HAL库,系统时钟,滴答定时器和LED。
7 m( \4 v2 |, L6 P' p  第2部分,应用程序设计部分,实现DAC8501的简易信号发生器功能。。
4 \4 f" b; y5 s/ ?& K9 |75.13          实验例程说明(MDK)
0 a/ I  G2 ], `5 I! j注:本章是配套了两个例子的,这里我们以SPI DMA方式进行说明。
1 N4 V) i0 a" m2 p- H) n& b8 Y& `4 E6 x* G$ }( r/ ?6 Y9 i
配套例子:4 z1 a7 q5 @- [; K, _# i% }

! w9 V" S# e' @" q3 D9 r* cV7-054_DAC8501简易信号发生器(单通道SPI DMA方式,16bit分辨率, 0-5V输出)
: c0 k; ^. H+ b. g* V4 I
3 V; _* P9 |( aV7-055_DAC8501简易信号发生器(双通道SPI查询方式,16bit分辨率, 0-5V输出)2 J7 {0 {& Y4 t" ?; Q  t
1 o/ x- T. {9 T* S
实验目的:* N3 R. o; F5 m+ R' T7 j

8 N/ U; O$ F. k9 O2 h% N学习DAC8501的SPI DMA驱动方式实现。! s. V0 D: i8 E0 I
实验内容:
- }9 R9 D* U. R4 }9 V% i6 U) ~- A! e/ F2 ^; c
DAC8501模块上带了两片8501,每片是单通道DAC,片上输出缓冲运放,轨到轨输出,16bit分辨率,支持30MHz的SPI时钟速度。1 l6 x* k# s2 P; |$ s
DAC8501本身仅支持一路输出,而模块上是带了两片DAC8501,其中只有一路的片选可以支持SPI NSS复用, 所以只有一路支持SPI DMA。) g% n6 L/ @1 }6 v1 V
DAC8501供电电压2.7-5.5V,模拟输出带宽350KHz。
7 v/ F6 `0 y' s; q/ a9 f实验操作:
0 L1 Z4 ?' }, [, p* Z! t
' V" d: Q, [' W( p/ e/ L启动一个自动重装软件定时器,每100ms翻转一次LED2。
' [. x3 Y+ z" i- T( s; w( sK1键按下,通道1输出方波。# g( L9 ~# b0 `1 [: ~1 r
K2键按下,通道1输出正弦波。
5 A! u3 W- Q7 R! q, ?- hK3键按下,通道1输出直流。4 g+ }# f, ?+ _/ w' z9 N
上电后串口打印的信息:  Z9 _" x' v8 v2 h0 f6 t

. T; E1 \4 q: m, k' n, |波特率 115200,数据位 8,奇偶校验位无,停止位 1。
  T- H* X7 @) J
& p7 G$ P. ?- |0 c
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
% k! P( v' n- j8 ~8 R
) R' ]+ z6 Z7 `- u$ ?, F
波形效果:1 K/ w& c5 G0 F" K- x( ]

5 ^* L" q- m" K5 j: @, q- s
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
1 s: U  {' G$ e; b8 J' L
4 L% D, [5 h! F, o. {+ y5 @
模块插入位置:1 }; F+ @' R- c2 ^. x5 s

3 C7 o- [2 B. [# R  B+ l
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
# x# F- M( @8 C' n1 n- M& b: _
8 z8 Y7 H6 _/ D3 }
程序设计:
7 j- M3 f( Y' e+ V" K5 P) ]2 J; C7 m' R, @- x" i- {9 Y
  系统栈大小分配:
6 M; x3 i" V7 C: |' F5 h. F5 p% D. z# Y
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
; @$ M* M& r$ g/ w- Y# B

, r2 H0 t* [! f, R4 C  RAM空间用的DTCM:2 p- m+ s/ T2 `) F6 N* v

$ o& d9 |! S/ c" \; n% \2 N" d2 ~
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

7 X! I& x- Z, u, s; l
% @7 o4 Y1 S! a  硬件外设初始化  g3 v% A8 j1 e
8 o% C1 K- Q  w4 w; {# ^! ?
硬件外设的初始化是在 bsp.c 文件实现:
7 d7 x' S! f" `2 o% y' b
' @7 r1 [8 f; ^
  1. /*7 a6 G- P7 o& ?4 D; |
  2. *********************************************************************************************************
      j1 }) `1 \5 A) L$ b) L5 b8 [
  3. *    函 数 名: bsp_Init0 m0 d! e6 I1 p8 U2 M
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次2 J: v( T1 B+ t. w- {
  5. *    形    参:无( J0 |; g' r' @* ]: \
  6. *    返 回 值: 无+ E# I9 q. U5 ~4 F5 X
  7. *********************************************************************************************************
    + {4 y4 `7 u# Y
  8. */
    . v! W2 l4 ]+ W+ ]- g7 S
  9. void bsp_Init(void)
    ) T$ [6 x$ q$ f
  10. {3 I% A% C+ L: u4 H9 h0 c
  11.     /* 配置MPU */5 W# r- _2 L9 l2 @, V1 W: v
  12.     MPU_Config();2 Y3 p, b- E( f3 b8 V( s

  13. ! n8 n: F& M+ s& S8 G8 v
  14.     /* 使能L1 Cache */
    7 B& \3 }! v& X" X; G6 k
  15.     CPU_CACHE_Enable();
    ! N! E7 L5 R8 U# L7 F

  16. ' o& C& A" G: L8 V  w% H3 l, @
  17.     /*
    & A$ J1 }: H( A, i& R
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:  a  `# b( l  n5 F6 i* t
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。: \* e9 }/ P* w* l% R+ r
  20.        - 设置NVIV优先级分组为4。/ P: F% |5 w1 G  a& a$ d; ^, r2 B
  21.      */
    1 c/ v! x$ @4 A- r/ }5 ~
  22.     HAL_Init();$ [, H; U4 `6 w

  23. ) q4 Y3 ~+ p( L& {# \
  24.     /*
    ) c/ ^2 V# B- A% P
  25.        配置系统时钟到400MHz; _( }4 ~9 N, a% m. r
  26.        - 切换使用HSE。3 e' v3 l; A! ?; @0 _( ?
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。+ H! W, i- V3 Z. z0 k! v
  28.     */
    . W, x$ c9 K9 s
  29.     SystemClock_Config();
    7 J5 t% k* V0 o/ v" c
  30. - i) |$ h) J5 K' p- {5 h
  31.     /* ' o* f1 I% x0 U
  32.        Event Recorder:! c0 m2 G" z& \: @* [) [1 h/ _& M
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
    ( k! l, \2 S# z" w3 O
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章) y& j8 k) Z# o& [. K9 K# S5 }
  35.     */   
    7 V9 j* |4 k; M  d" @3 e
  36. #if Enable_EventRecorder == 1  
    6 m4 e+ H- ]) y- t8 ~) Z' g" X, B% K
  37.     /* 初始化EventRecorder并开启 */
    ( [. q" M5 j- q6 s2 Y0 t
  38.     EventRecorderInitialize(EventRecordAll, 1U);+ P# u9 }9 Y2 W1 J6 @
  39.     EventRecorderStart();
    ! b2 J) V1 r( c% \% s7 b- [
  40. #endif
    3 S# x6 N3 \# z
  41. 7 N! j4 S* j7 d# F1 }9 @
  42. bsp_InitDWT();      /* 初始化DWT时钟周期计数器 */       6 ^, l9 |( R; N: l% Z
  43.     bsp_InitKey();         /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
    : A4 Z3 o' \8 o. ~% ?0 l% _1 F3 L
  44.     bsp_InitTimer();       /* 初始化滴答定时器 */# ^. n* X* M- _6 ~* _
  45.     bsp_InitLPUart();     /* 初始化串口 */
    0 c4 F  ^8 o  x& K/ K
  46.     bsp_InitExtIO();     /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */    * ~9 ~, b+ F& r, t  Z. @
  47.     bsp_InitLed();         /* 初始化LED */   
    - u1 E3 s: y" e) X2 }# W
  48. bsp_InitExtSDRAM(); /* 初始化SDRAM */
    3 U% n6 n2 t. C- p- ]/ c& \

  49. 6 h8 l) X. `' L4 h, r6 n0 ^
  50.     /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */   
    ' `. }& o* Q6 F% a  e0 v
  51.     bsp_InitDAC8501();  /* 初始化配置DAC8501 */
    " z( i2 _+ P' u
  52. }
复制代码
9 J% A$ n) I- d" K1 t8 x# ?

- r8 I, Z: E  \! F" A7 m& g
2 V4 I" ^) v/ h  MPU配置和Cache配置:
5 f+ d7 p, r9 z8 ^  |- [1 S' d8 N( _# }; _; U% z$ Z0 |, X- _
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区以及SRAM4
6 a0 r  ?1 `! f4 o1 x$ N6 M0 B" C: e" V3 P; N1 E
  1. /*
    6 l, |1 \. n& K* l' ^, K# |  G
  2. *********************************************************************************************************
    ( i, V7 c% n9 ?4 l; ], w7 U
  3. *    函 数 名: MPU_Config
    3 e& x7 J' i; E  E
  4. *    功能说明: 配置MPU
    % j* e+ Y% X! ?
  5. *    形    参: 无
    ) w/ E( [% T  c
  6. *    返 回 值: 无1 }$ l! y: v- _  _7 `$ I
  7. *********************************************************************************************************
    # b3 \3 |5 h" e
  8. *// {) ~, K( o/ |: N3 S6 r7 g3 r# K8 r
  9. static void MPU_Config( void )! l& [, Z* E" a, [9 M
  10. {
    4 E/ k  |$ z" w2 [! Z$ c  m3 h: J
  11.     MPU_Region_InitTypeDef MPU_InitStruct;
    9 E2 D" r- w& w: a

  12. ! r/ A$ h0 |# h! t/ P. z/ S
  13.     /* 禁止 MPU */& v' Z* a5 A$ J8 p7 N- |6 `/ ^% O
  14.     HAL_MPU_Disable();" b6 e5 R- C+ c2 g+ A, N
  15. * W7 O  {1 N+ z6 I; d! M* \
  16.     /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */" v4 p; m1 j7 i( j7 `  A# ~
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    % y% c8 Q* G# b1 ?
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;9 j* A( r9 m1 C) l9 l" z
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    8 d! _1 ^& X4 E% k
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    3 @9 n0 v0 u/ I0 ~9 t9 \
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    9 ]% X. k6 b. P9 Y
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;( J" K+ s) ]  |
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;$ W" j- p/ @+ }3 H- d
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    " I6 {6 s, x; a3 O& j
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;% n5 b/ T& M: j  P* y; L
  26.     MPU_InitStruct.SubRegionDisable = 0x00;; {% b& L! v% \3 E
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;/ }# l  g8 b( Z0 R# O. e% m2 \& J
  28. : {' x* j( g; T
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    : v. t9 s6 T' r0 F2 W
  30. * Y5 ?1 T9 ?/ ~$ N( Z

  31. / U5 F! x& `9 j4 C3 b: R: m
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
    6 [; n( G8 |7 d$ F. L, H/ R* x+ |1 L1 T
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    " D6 o2 T# s4 B4 V9 T6 {1 M$ x- Q
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;
    . }' ^7 F  x( O& B! Z
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;   
    1 P6 w% L! |  Q
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;% T% r( T' W) x2 g8 l6 d! Q
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    + {4 {: P5 @  u1 e' P4 l
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;   
    # C1 Q' }' {0 X6 H" C/ F. `* e
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;/ ^/ s( M' o/ V+ ]. @
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;1 h3 q- e+ m# N( Q
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    & _) @7 K8 ?: `" I. `9 O( ^
  42.     MPU_InitStruct.SubRegionDisable = 0x00;
    7 S/ Z1 O# q- H2 X* w: K& [
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;3 D% V" ^( V4 F+ D, ]
  44. ) A4 W& b+ t9 h/ w$ l2 d
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);8 @4 i: c8 N7 @' ]. m

  46. ; n% p# q/ N6 h/ e' R, K. o
  47.     /* 配置SRAM4的MPU属性为Non-cacheable */0 \, d% E$ D  |0 E9 p$ n+ \, A  V
  48.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;1 v1 l7 S0 l0 q: N# y
  49.     MPU_InitStruct.BaseAddress      = 0x38000000;. {  m1 p/ ?& s# M+ }+ J# f* R
  50.     MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;: h6 _# g4 Z6 _' _
  51.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    6 s+ P" p* l1 ~0 c
  52.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;8 f. v2 _. U6 `+ w% m( o
  53.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    7 G5 [2 a# S% T6 q$ Y8 b: a0 w
  54.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;* s! M9 |/ [6 G' j1 T% P: a
  55.     MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
    , |6 ]2 E# z  I7 }, o  g
  56.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    , ?4 a; S' v1 S
  57.     MPU_InitStruct.SubRegionDisable = 0x00;
    ' d* d- F' ^" O, T! y
  58.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    , y. B3 ]) ^3 f+ d/ _" R- ^
  59. . b; |2 @, j9 ~3 X" X- y
  60.     HAL_MPU_ConfigRegion(&MPU_InitStruct);) F: j1 |% m3 q4 j- ?- }3 C

  61. / s  b2 E; p+ `9 E! ~
  62.     /*使能 MPU */. b2 L- y, o7 m2 s. l4 G0 B, q
  63.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
    7 n8 F* w( O( {7 p- P
  64. }
    / a; O0 E* A" z; q( ~7 S1 H
  65. 5 M( X, K$ o; J. r' d/ _! b1 \# y( B5 F
  66. /*% u" k! Z2 Z! Z7 x, b% ]) _- b! U( f  j
  67. *********************************************************************************************************1 v9 G5 Y3 l, d
  68. *    函 数 名: CPU_CACHE_Enable
    9 K7 _6 i* D; C& S' x7 C
  69. *    功能说明: 使能L1 Cache
    " W; E/ e7 H# _, U+ h
  70. *    形    参: 无0 w* m) {7 O/ R, N
  71. *    返 回 值: 无- }2 t) Y* r9 }; d/ R+ U& Q3 [
  72. *********************************************************************************************************$ x8 t/ ~( D" P9 p, A
  73. */# g. Y' g3 L' h. X( r0 L/ }
  74. static void CPU_CACHE_Enable(void)
    9 `6 V2 O: l2 U3 T0 @( ]
  75. {" B; H4 w) F7 ]( S2 h  l
  76.     /* 使能 I-Cache */! \0 k5 ]9 y$ _. n1 c( f  b! Y
  77.     SCB_EnableICache();  ]; `3 K* ~5 o. J* T

  78. ) x9 I# ?( I% s) b2 e! n+ `
  79.     /* 使能 D-Cache */
    # S: ^) b+ `' |% M" @; i, \& u
  80.     SCB_EnableDCache();. ^% r% n( v: _7 T
  81. }+ O1 ^4 ~7 s6 m
复制代码
. Q$ S# h' y6 [/ t( ]

/ U$ ]0 G" L* r8 C6 t/ c  每10ms调用一次按键处理:
* q8 ~9 p) ]/ x1 F
1 ~3 J2 ?6 K8 O# G$ o. p! Y7 ?按键处理是在滴答定时器中断里面实现,每10ms执行一次检测。
8 q; ]- u$ Y' n/ e  c( |& I/ B, I2 Q
  1. /*
    ) o" z  z! u6 U' q7 R7 ]
  2. *********************************************************************************************************
    . U! a& }4 b6 }( e( C9 I
  3. *    函 数 名: bsp_RunPer10ms
    ! h5 p" \3 P# `4 d
  4. *    功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求
    / O0 Z6 ^- |, r) s; B0 f) |
  5. *              不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。
    1 f" v( q/ u5 S+ n* H/ J
  6. *    形    参: 无
    2 L& H( R! x( s; S% X4 _, H  e1 {
  7. *    返 回 值: 无. u7 o5 M& d& A9 c# s
  8. *********************************************************************************************************
    0 a) Q( j" G; t
  9. */! p" ]  F7 x, {, V; @7 \! w. N! B
  10. void bsp_RunPer10ms(void)& l" `9 Z$ t) U: R4 m: j
  11. {1 U, N0 G2 U  Z6 Q4 G
  12.     bsp_KeyScan10ms();
    ; a+ G# D% R/ B3 ?9 y" y: W6 c
  13. }" z1 \; k" M3 d( i( e2 y4 c
复制代码
$ ?' n+ I8 V+ J1 J/ T; R; s" m" N- h2 b
0 y; H2 ?. ^. H1 D
  主功能:
% k+ w; j4 W3 d7 y0 ?7 k8 B
& w. V/ |/ m. K$ O1 ?主程序实现如下操作:$ m3 M/ Y6 {! J+ _$ k( U6 J6 Q2 c
0 B. ?+ i* f6 \1 A: p! T  n
  启动一个自动重装软件定时器,每100ms翻转一次LED2。$ ]2 e5 O8 U* ?& z1 W  P* L* a
  K1键按下,通道1输出方波。
1 t: z) |- [3 f  K2键按下,通道1输出正弦波。. l. U5 n" K* G( J/ i+ `
  K3键按下,通道1输出直流。
* \7 i! i$ n" y* o
  1. /*
    ) a& w. w2 g: }
  2. *********************************************************************************************************) H8 t+ r9 X. f; j8 E
  3. *    函 数 名: main
    % k" C. d' r5 A
  4. *    功能说明: c程序入口
    ) W/ ?% G, c- y! v
  5. *    形    参: 无
    7 [" Y- i$ p  h$ }: I% c9 L
  6. *    返 回 值: 错误代码(无需处理)
    7 d- n$ T; F6 A( ?
  7. *********************************************************************************************************& W9 A+ \: B; M% [
  8. */
    6 a1 c) ^& A  Y8 ?  O1 n
  9. int main(void), Z- E, h& _* W" h# P8 ^, d, G
  10. {
    ; \6 Y, s' v: \# q( Z
  11.     bsp_Init();        /* 硬件初始化 */% b( j. _6 B, S

  12.   g$ F+ a, ?- a9 }0 {) W$ L5 `
  13.     PrintfLogo();    /* 打印例程名称和版本等信息 */
    : k. D; \8 K$ v/ H8 \
  14. & s3 A; t; X# L+ G
  15.     DemoSpiDac();   /* SPI DAC测试 */0 j1 q0 L. V6 v
  16. }
    $ d1 Q* J7 X3 ~6 ^. ?

  17. & B+ |% m& Z1 R9 u
  18. /*
    - g2 U9 o, Y# I$ `$ @2 ^2 C
  19. *********************************************************************************************************1 M* h- L7 D+ p# Y  \
  20. *    函 数 名: DemoSpiDac
    0 ~# c! i% [! s( `9 h
  21. *    功能说明: DAC8501测试
    $ q& p, w6 |$ i
  22. *    形    参: 无* c0 \" i" h# g8 H" t: Z
  23. *    返 回 值: 无
    1 ?! M. ^! X8 ]1 g
  24. *********************************************************************************************************5 y9 [6 D% Q3 N4 r: u1 l
  25. */
    ( M. y+ |& ?  t
  26. void DemoSpiDac(void)1 f- Q( e4 t* u" C& X0 j
  27. {
    & v$ w, o; u. D
  28.     uint8_t i=0;5 i. Q. X2 Y7 _" F( F
  29.     uint8_t ucKeyCode;    /* 按键代码 */3 w4 }( v. h; E  {4 B' j8 F
  30. * O  x. G+ o8 E* w! B
  31.     sfDispMenu();        /* 打印命令提示 */
    1 A5 M5 _! g/ k

  32. 0 B* H* z, S* s: P9 y
  33.     bsp_StartAutoTimer(0, 200);    /* 启动1个100ms的自动重装的定时器 */3 P0 U- O4 V9 A* ^/ l2 N
  34. " t+ |' I5 N. [3 w0 p3 t9 l- A
  35.   V3 N& D3 F8 h" _% A& _. B9 b
  36.     /* 生成方波数据 */  z5 O8 s) ~2 N6 S5 @
  37.     MakeSinTable(ch1buf, 100, 0, 65535);
      @( B8 C: n% M8 M" L  p, `
  38.   @0 y) B: N4 C  [: Z& M
  39.     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);
    * r7 l+ z. k' [% i. _

  40. $ E$ P$ ^4 e; @5 P# M5 j
  41.     while(1)
    2 F+ s! y* K+ K7 [! T, P: s
  42.     {
    : w5 P4 s* E5 B; C9 S) N& u
  43.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */6 ~+ P/ _6 N4 I" U1 ]

  44. 9 m+ s& j4 [9 n9 r% a
  45.         /* 判断定时器超时时间 */0 C& ?. H5 R9 x: H" D, @) A7 A8 e' {
  46.         if (bsp_CheckTimer(0))   
    6 j  E; g+ C' W: m, M
  47.         {
    3 n- _' Z! N' I# h: P" h" \# }+ H
  48.             /* 每隔100ms 进来一次 */  
    . a" n. i$ Y" l! V* D+ x
  49.             bsp_LedToggle(2);& g1 U- \- P: H4 u8 t+ s, t
  50.         }& G6 o7 P+ }: H3 R/ R6 x! t

  51. , k6 u; d* L& ^: v
  52.         /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */  v8 P+ j+ i* T% X- C" T8 W
  53.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */" o% U# B) p( ?3 R: R1 n3 r, a
  54.         if (ucKeyCode != KEY_NONE); O* i$ q" ?% D1 k: w- n
  55.         {
    " k( w5 d5 Z1 J/ [8 Z1 U$ X# Z
  56.             switch (ucKeyCode)3 f' Z0 V0 X/ A( w* R5 `2 h
  57.             {4 w7 j3 g% X1 r4 P8 c! i" ^
  58.                 case KEY_DOWN_K1:            /* K1键按下,通道1输出方波 */
    * [9 R$ n5 J$ n: h4 j6 ^' D# O
  59.                     /* 生成方波数据 */
    9 |8 F# S; |9 W0 n
  60.                     for(i =0; i< 50; i++)
    0 _. r3 b1 @, U
  61.                     {
    $ T3 T$ D1 ]% X9 n5 Y( w) ]" `
  62. <span style="font-style: italic;"><span style="font-style: normal;">                        ch1buf = 0;
    ! o! E! K% ?7 I) _' ]
  63.                     }. i" x1 O4 E# T! y4 g0 d

  64. $ I/ r/ ?! p! q( R# a
  65.                     for(i =50; i< 100; i++)
    0 L8 c1 k! R, n) O( P
  66.                     {
    $ ]/ V7 K9 X: w, u
  67.                         ch1buf</span><span style="font-style: normal;"> = 65535;, V( [! ]) a/ p9 h- K) J
  68.                     }
    - j5 b9 |: f% i9 d- }/ |# Q

  69. 6 M8 D! B8 e' u$ _2 z/ k
  70.                     /* 触发速度1MHz */' V9 d8 q) F% ?: U
  71.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);
    * a/ W+ y2 X  K2 C8 b+ Y5 U
  72.                     break;
    9 ?: e+ w# U$ Q

  73. % x! w: F) [% F& E
  74.                 case KEY_DOWN_K2:            /* K2键按下,通道1输出正弦波 */& o5 S/ c& Z6 C# K: \# {2 }  f1 K7 R6 B  T3 ~
  75.                     /* 生成正弦波数据 */   
    - T  Q8 \  t1 w% ^, @
  76.                     MakeSinTable(ch1buf, 100, 0, 65535);
    , n9 ]  G( W- X/ ?. e

  77. : B: B# p' U& `7 T. Q( l' k) A
  78.                     /* 触发速度1MHz */
    0 R0 F8 ~! a+ N6 w5 C
  79.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);9 w) i! K& B9 {( P; S9 p
  80.                     break;
    0 e2 F1 V% I$ I# d9 b  w
  81. ' Q5 v% F, h! ~+ _( R
  82.                 case KEY_DOWN_K3:            /* K3键按下,通道1输出直流 */9 D4 b% Q% w" B8 w2 g

  83. : Y$ j  U/ J3 @$ E
  84.                     /* 生成方波数据 */
    3 K+ ^; V8 X' o5 q
  85.                     for(i =0; i< 100; i++)
    ' a6 M( d; g: l* F, ?* x
  86.                     {
    & s3 r  V4 \  z& }6 i5 c& B& u
  87.                         ch1buf</span><span style="font-style: normal;"> = 65535;: L' a2 g+ e8 ]; B
  88.                     }
    $ K5 i. n# a) l5 v* r& R

  89. 5 t( [; Z9 n% I: g" W* k/ b* g* m
  90.                     /* 触发速度1MHz */( F9 V, V8 v9 v7 @$ ]  s
  91.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000);
    0 `; D  b; ~" I; _; g% w
  92.                     break;
    1 b* b0 I" @" K  D( E# M8 Q( P0 h
  93. / F, l( N% I  D! j1 i9 G( y
  94.                 default:3 @$ p2 s% j" ]$ g4 l1 z& q
  95.                     /* 其它的键值不处理 */
    9 {' Q7 O/ q3 F
  96.                     break;' |7 Q" }7 |0 M: c: \3 W
  97.             }
    # [9 N6 D5 D3 D* y# ?
  98.         }9 b3 E& {7 W4 |4 W2 \& }& |
  99.     }
    2 m/ C6 X! H4 S0 I+ A+ \- P
  100. }</span></span>
复制代码

* }1 t1 e& A9 y
2 E2 g3 X' I6 ^1 V7 `6 `- t7 ~' d7 c! v
75.14          实验例程说明(IAR)
4 d/ F* u6 j$ X, y注:本章是配套了两个例子的,这里我们以SPI DMA方式进行说明。2 U% z# }: x. D' B! l: r2 r; `
; \; |4 N, g, ^; U" m& ?7 S! F. N& s
配套例子:. c* X/ P; r# z  q% J
4 }: G2 j& Y5 ~9 n
V7-054_DAC8501简易信号发生器(单通道SPI DMA方式,16bit分辨率, 0-5V输出)
9 h0 y3 w* I: ~* A5 K0 ~
# B! i; P: v) z! EV7-055_DAC8501简易信号发生器(双通道SPI查询方式,16bit分辨率, 0-5V输出)" d; f5 m# E! ~* d' @: y6 I7 I

; a+ B0 g2 X9 S9 I6 S* C5 k/ K1 {+ U实验目的:9 I/ \/ E5 e! S; Q
. z1 Y- K1 p4 G5 G/ Z! p$ x. m9 e
学习DAC8501的SPI DMA驱动方式实现。; W* e# i2 c! D5 W# o
实验内容:
+ H  p4 M0 N% _7 {# L# t) X7 x5 f  e8 o0 V6 ?& u
DAC8501模块上带了两片8501,每片是单通道DAC,片上输出缓冲运放,轨到轨输出,16bit分辨率,支持30MHz的SPI时钟速度。6 N7 R: M) a" ~
DAC8501本身仅支持一路输出,而模块上是带了两片DAC8501,其中只有一路的片选可以支持SPI NSS复用, 所以只有一路支持SPI DMA。# c* D8 k7 b% W* s4 a7 J
DAC8501供电电压2.7-5.5V,模拟输出带宽350KHz。, H1 C# Y1 }' H: M8 T4 m
实验操作:
, L, k3 D/ H  M  I
' t; |" j; I/ t; s& U( q# x0 m启动一个自动重装软件定时器,每100ms翻转一次LED2。/ w1 {# r; N& I6 I! c9 R, \5 s
K1键按下,通道1输出方波。
' r! C) m, s5 K' [4 z- Z$ `K2键按下,通道1输出正弦波。, T9 E& E* f: p- N/ j5 v
K3键按下,通道1输出直流。
4 Q; Y; K. E( i/ w8 }; `; j上电后串口打印的信息:
; ?2 O+ e( G2 [
2 |' O) c& Y* C* m9 y波特率 115200,数据位 8,奇偶校验位无,停止位 1。) A* ^" Q! W: F0 P& b. {

" V. o' n6 y7 \3 E. H( D  n
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

9 Y. @1 {8 u$ F+ q; O! p& o; I( Y$ g& f
波形效果:
+ G) R7 M5 _7 e" P
& |: S$ p2 Z/ t  @

( e/ u  f8 w3 }7 w
% b& }2 W+ p! D# W1 o( `$ S) p8 Q模块插入位置:8 i3 [& Y2 k8 s# D
9 o* _8 E9 ^! I& \8 ~

- u& C$ Q2 v1 V# {+ }1 n9 c! |& E, q# L/ y+ i0 v- U
程序设计:
& q0 Z( j; o8 M7 n" Y- o
5 u! y. g$ b7 p  系统栈大小分配:% P0 ^4 o, d7 p2 L' m

3 q$ h3 f3 S# @. i, I5 \' o
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

) |; r/ i6 I1 b0 O/ @6 k8 _, P- ?3 @* t$ S, @( a$ O4 M
  RAM空间用的DTCM:
- x' ?+ t6 ?/ F' S0 |. ?
! C, m' s* q  S9 N
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
1 m8 w% [1 j0 u0 g6 _2 Z
! ?* ]+ U  H8 i, u9 r. C' o
  硬件外设初始化) n3 i, l/ d% C- Z1 R) _$ g

# i: M/ P- e) G5 C; n- Z硬件外设的初始化是在 bsp.c 文件实现:
5 o% a2 |6 v1 r: h  @5 w0 M4 N1 b
$ G/ \# i3 e3 V. o! ?  I
  1. /*
    % Q7 f% ~2 G. b/ v. Q/ r& p7 @
  2. *********************************************************************************************************
    8 \( M$ W8 w) @* O% H* `
  3. *    函 数 名: bsp_Init9 J* J+ n3 s7 _/ v
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
    7 L3 k1 n  u4 G( t
  5. *    形    参:无
    , X: j9 E) F- A! M# w
  6. *    返 回 值: 无+ Q( W6 O, t- s$ B9 j# |* q
  7. *********************************************************************************************************
    4 P) }% M+ a  M* {6 L, T
  8. */3 Z# K" d7 }/ H/ g( M8 @
  9. void bsp_Init(void); X: u& r2 l/ l8 b9 P
  10. {- l1 n/ w( O6 z; l) s! E; Q
  11.     /* 配置MPU */$ N! Q* C3 k7 V! N# h  a( A
  12.     MPU_Config();
    / w' n3 M2 Y+ d6 [8 T  e" \1 m1 [: {

  13. ) L. k7 L% N! a' f4 v
  14.     /* 使能L1 Cache */
    " m! w+ B9 S4 I; E2 ~6 _. f
  15.     CPU_CACHE_Enable();
    ' S$ F' Z) G' A* W. f5 n

  16. 8 C5 t; y9 v9 t1 @' M, K
  17.     /* ; n3 ^6 R/ j# h
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:0 h2 p+ z: B& y2 b" o( m  K7 D
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。! ?+ l5 J- d+ V: K/ {( C
  20.        - 设置NVIV优先级分组为4。4 ~% L! q8 [" U
  21.      */+ B3 e+ x% V. }- A0 P) t$ [
  22.     HAL_Init();- r) `+ L$ `) {/ t
  23. + a6 Z1 q. J5 D- r
  24.     /*
    9 X2 k# I; q  H7 ]9 t
  25.        配置系统时钟到400MHz
    : s" ?) L; h- d3 ?! c  F
  26.        - 切换使用HSE。
    - S8 Y' y+ i* K; T( Q: G1 Y3 s
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。0 A! X2 N8 Q, J8 x
  28.     */
    ) D3 H' [0 |6 |' T; M% t
  29.     SystemClock_Config();
    & {; [: n( H& I& ?  r
  30. 8 @# x; Z) H! B# K. M
  31.     /*
    % b* Z# M0 S2 a7 }, R$ p
  32.        Event Recorder:! S2 W. W# x( c. M, Z5 r
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
    6 e* H! a7 _5 S+ C; _
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章
    : S, M1 F9 P( H. \) K) X
  35.     */   
    * W8 O: ~5 r) _& k+ Q
  36. #if Enable_EventRecorder == 1  
    % Q) ]6 t1 k1 i, g2 c5 y8 Q. P' z
  37.     /* 初始化EventRecorder并开启 */
    ' @& f+ z' L# T. S; y& n1 _) V
  38.     EventRecorderInitialize(EventRecordAll, 1U);8 q# y- {; d8 t2 R$ z% Q1 v
  39.     EventRecorderStart();  _2 F6 x# |* J: @) A7 L
  40. #endif; `, {' _  G5 @- k: ]* M2 _

  41. 7 [+ s- z+ S: G: x: g. a5 ]
  42. bsp_InitDWT();      /* 初始化DWT时钟周期计数器 */       4 V' B+ S5 Z" P: [' Q
  43.     bsp_InitKey();         /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
    4 t8 V. B  [. I4 o7 P
  44.     bsp_InitTimer();       /* 初始化滴答定时器 */
    3 X; f4 q9 I! S) `& s! M: H" r) P
  45.     bsp_InitLPUart();     /* 初始化串口 */
      _# e+ j$ v, A# F
  46.     bsp_InitExtIO();     /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */   
    : h' A; C5 K' I
  47.     bsp_InitLed();         /* 初始化LED */    7 U6 a3 U# ?4 n3 V0 ]6 ^% U
  48. bsp_InitExtSDRAM(); /* 初始化SDRAM */
    8 c6 y% ~! t: v1 m/ Q! M
  49. # ~, N3 Z% o8 r  X( {  @3 k; A
  50.     /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */   
    ' @$ u# S$ [8 Q  r
  51.     bsp_InitDAC8562();    /* 初始化配置DAC8562/8563 */
    ) t2 ?+ ?4 j# f/ c8 b+ a* P' L
  52. }
    " B+ S# l+ n+ H6 Y
复制代码

/ a! A' g3 W. y3 |, {7 B7 N/ _/ S& o# |  L7 X
  MPU配置和Cache配置:
/ L# H2 T7 d, R3 A8 F7 }; q( s+ D& M8 Z
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区以及SRAM4
1 s, x% y/ s* h0 R' p* o7 Y* y4 m& o0 K- o+ t
  1. /*. N6 O9 `2 O7 t% y; Y3 T
  2. *********************************************************************************************************
    7 D1 s7 P2 u3 U; P  F  e
  3. *    函 数 名: MPU_Config2 c/ b: e/ y. d8 m: i
  4. *    功能说明: 配置MPU, n! m& t, z6 r
  5. *    形    参: 无
    / B4 U  I3 c: |8 A8 O. P7 T
  6. *    返 回 值: 无
    0 M, I3 c6 c9 G
  7. *********************************************************************************************************
    + R$ Y$ N6 L" p4 V' G( B5 i" ]1 v
  8. */
    7 \: h$ m- E' u2 ~; J0 t
  9. static void MPU_Config( void )- n( v4 n* x8 t9 Q" |; T
  10. {
    5 i# `+ n  W9 [( T( E. T2 R
  11.     MPU_Region_InitTypeDef MPU_InitStruct;  N( I" ^( e0 R; p4 j
  12. 2 E) W# j! g# t' O2 f# z+ z4 X; Q, D
  13.     /* 禁止 MPU */" `, r0 q1 ^4 w& W
  14.     HAL_MPU_Disable();% Z; ~( x) e) X6 ~; \: I6 J9 O
  15. ) E6 O6 \3 g% N  U
  16.     /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
    + y* a( r5 @: M& f9 h
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    : p2 f' r! j) o( e4 `% P
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;
    2 Z: r) q/ j4 H# q
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;8 K# B# N; E& m& z
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    7 C  K0 Z+ m: t. d7 t5 T
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;: X- t, ]% |* Y( s+ W
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    7 \1 N1 B1 Y8 a
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    ; f3 {5 P  Z6 C' |( o
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    1 E6 S' C2 m  p8 ?, X4 x
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;/ i% e( T  s: j  i! _- u' C
  26.     MPU_InitStruct.SubRegionDisable = 0x00;
    # v& N7 [" g% V% L* c/ @( }4 T
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;1 j4 v# {- `( h* F
  28. . E+ I6 k% f- D* ?
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    1 z5 n% x) d: U5 N6 Q

  30. 5 Z7 R8 F) ]' v$ V( M

  31. 2 L  o/ U7 s$ r5 H( A* y: o
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */& J* H  R/ l2 p/ F
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    2 `* N# [/ S0 W- c! I( ?( s5 I7 l
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;5 T/ Q0 G; d, R7 ?# C' P) M
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;   
    1 H3 q& T" A7 C
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;' x/ n2 D* ?( q4 P7 c7 U8 M* F7 Y
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;! b( U9 O7 w. u( e5 T" O* Y1 m. o& O
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    + V! H3 j6 Q- h+ W, M+ J7 i3 ]! @
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    % y2 V" f3 J% _; i3 H$ X% q
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;, T8 F6 }2 V9 C
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;1 R, V) j( Y& @# w: B# l' }
  42.     MPU_InitStruct.SubRegionDisable = 0x00;
    8 }8 c- W  N" \2 Q/ R" a
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;) F: L6 M% n  c

  44. ; X4 D/ }3 A! v
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    9 ], n/ a0 @6 ^* Y6 v
  46. & l  e1 D7 s$ G3 B( I; s3 ^. R
  47.     /* 配置SRAM4的MPU属性为Non-cacheable */
    6 z' j6 w# y2 S, S8 Y( q
  48.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;. \& a: h  V( r5 N
  49.     MPU_InitStruct.BaseAddress      = 0x38000000;$ H& ^' B  }' X( b
  50.     MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;
    : M& o; p3 n! @* j% R% l
  51.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;% ?8 {. }' f: ?- Y( G+ q
  52.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
    ( H6 u- G5 O" y8 x* {! ?3 {: Y, h2 \
  53.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;! b* y7 T) U4 `
  54.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;5 B1 i4 i  _0 o! w
  55.     MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
    # z( A" _0 L  t5 A3 \! t
  56.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    9 _$ C& s; I$ ^; n1 D8 D% c
  57.     MPU_InitStruct.SubRegionDisable = 0x00;
    + \! G) `6 n3 j' z1 Y9 a
  58.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;2 P4 o( @- B& C4 P

  59. % u) I  l3 g8 G% z
  60.     HAL_MPU_ConfigRegion(&MPU_InitStruct);5 h& s" |+ w6 o. N
  61. / H- ]% ^# x8 V, x, H8 a& U
  62.     /*使能 MPU */8 q1 r4 @+ M+ p/ B, e
  63.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
    9 i% T, L. S3 t. K
  64. }
    ! o  N( X! P' C8 C% r

  65. " y7 m: [# ?, E5 L( }& I5 d
  66. /*
    + a  y# @. j$ O( K: [
  67. *********************************************************************************************************% J3 d* K' Q# v: i* Y
  68. *    函 数 名: CPU_CACHE_Enable: [- x) U8 R) O! l# X; ]7 J
  69. *    功能说明: 使能L1 Cache2 h9 P+ C4 l) [
  70. *    形    参: 无' C9 l) ?5 _% e
  71. *    返 回 值: 无
    9 _5 O# l# e9 T3 {, @5 J: o) t
  72. *********************************************************************************************************, D; v2 H8 ?+ Z& H! O& J% D5 e
  73. */
    3 j+ M$ o* J' G$ E6 q. H: Y
  74. static void CPU_CACHE_Enable(void)
    + x4 d1 x& E" Y3 e3 l
  75. {+ X% s( ~3 C6 P4 W7 K2 }
  76.     /* 使能 I-Cache */
    5 \! K7 q# F6 N) H
  77.     SCB_EnableICache();
    8 J1 L; e. P8 T

  78. 8 D- j) k: v$ t" A% M3 K# j
  79.     /* 使能 D-Cache */
    7 d/ j. v, p0 s2 ~5 ^) j
  80.     SCB_EnableDCache();9 O1 E$ R) F, m+ G0 d$ G6 ?- Y3 _
  81. }
    ( w, e  \9 Y# a. F$ [: z8 \% }
复制代码

, o( u) @' j7 `) R# h
& s0 }& T2 S! w4 c5 d& q  每10ms调用一次按键处理:1 p4 J" p& c# h1 ?, d2 a

3 G) v/ X$ E! W* Y+ l3 a6 h" T按键处理是在滴答定时器中断里面实现,每10ms执行一次检测。
/ L$ B- s9 Z# r1 e7 P+ Z& t8 O$ |# }* |" I7 n
  1. /*
    0 G0 L% }& `; a2 X
  2. ********************************************************************************************************** u9 z" h) B6 n0 w5 ~
  3. *    函 数 名: bsp_RunPer10ms
    # D$ r3 d- g9 X( I! h6 i
  4. *    功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求
    / r- M7 ]/ x9 M. c3 H- x. D
  5. *              不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。4 [: ^5 a$ P! P  A) I1 m
  6. *    形    参: 无
    ! }" `7 ?5 {0 a
  7. *    返 回 值: 无4 K( V4 E) I) J3 u: ^
  8. *********************************************************************************************************# j1 g* H" Y, ]1 m  B" i
  9. */
    ! c8 Y( g; B; a/ c! w" q$ u9 G
  10. void bsp_RunPer10ms(void)) D0 `* N$ _- x6 ], {: ?5 J  R3 f* X+ M
  11. {+ h& D) t& o* d; z
  12.     bsp_KeyScan10ms();
    ' l  k- u$ D% u  o
  13. }
复制代码

* \$ o' V9 Z1 r: t% [9 f
( s7 \0 I( q4 h: k3 t* a; L; {0 P0 T4 j1 W3 `) @/ r
  主功能:4 x# \4 C8 v' ^/ s: Z3 }1 q; ]

  T) \' v2 D2 u) w5 n8 I9 V5 p主程序实现如下操作:! }9 m, H6 M" b- B/ G

* R9 S) O8 X& M& G1 k, A4 T  启动一个自动重装软件定时器,每100ms翻转一次LED2。0 m5 D  P' H; q. w6 G, P$ X
  K1键按下,通道1输出方波。
, `9 Z7 x& u" s# l2 U. `4 z  K2键按下,通道1输出正弦波。
* N0 u/ @3 U" A4 |4 w# N  K3键按下,通道1输出直流。
; M0 f, A; B8 }, r# G4 r" F% L
  1. /*
    7 U# q* r2 y- S5 J
  2. *********************************************************************************************************
    5 f" ^  E$ k1 k8 O
  3. *    函 数 名: main
    % s' K' ^' J2 h$ k
  4. *    功能说明: c程序入口. H7 L4 n: a$ W4 h7 u
  5. *    形    参: 无
    / t" b5 b2 A* ^/ a2 ?
  6. *    返 回 值: 错误代码(无需处理)
    " |; K8 w+ p+ v, x
  7. *********************************************************************************************************
    6 r' D: x% y" ]+ `" ^- U; }
  8. */
    # r8 p  Y6 h4 b* T7 k, k
  9. int main(void)/ U3 T; U" x7 p) b: }  b7 ^
  10. {) p5 ~0 i4 `2 Z- F
  11.     bsp_Init();        /* 硬件初始化 */7 M4 }3 v7 ^' }: k- @( T. F
  12. . u; g/ F) ^- y& U  _) N
  13.     PrintfLogo();    /* 打印例程名称和版本等信息 */
    . G$ c! f6 y% Z! q8 @7 w# ]+ U

  14. + n" Z# K9 V# A  ^+ U2 Q
  15.     DemoSpiDac();   /* SPI DAC测试 */
    4 N" Y' I- C- }/ C. [
  16. }
    & U. H" \1 i$ c

  17. 9 J* h- a$ p' I, \7 T$ w9 f( k
  18. /*2 _9 p8 l6 R1 S+ U
  19. *********************************************************************************************************
    ! ?# x: ~4 I# K8 O6 t+ x7 F) P9 u
  20. *    函 数 名: DemoSpiDac) e8 Z; F4 ]& u; T" S
  21. *    功能说明: DAC8501测试
    & j$ U! X& u" s  R- e/ H
  22. *    形    参: 无/ A9 G, ]5 I( Q! E( Z
  23. *    返 回 值: 无
    + \, n0 C7 U/ |0 I; R! J
  24. *********************************************************************************************************  F  v2 \3 l) F) \- V
  25. */7 T! u- A; E. M4 t, t
  26. void DemoSpiDac(void)1 x* Y: u; \; l6 o
  27. {
    , r3 ^2 c, V0 F
  28.     uint8_t i=0;7 ]0 s7 e4 i/ I) H7 U4 I: T& Z
  29.     uint8_t ucKeyCode;    /* 按键代码 */
      l( a) M" d7 n4 e  W. R$ Q: x
  30.   ]8 v2 o; @* [
  31.     sfDispMenu();        /* 打印命令提示 */
    7 ?) c" R+ h' M3 [  ]

  32. 3 q0 `8 w: Q) ?7 C1 B; R1 n
  33.     bsp_StartAutoTimer(0, 200);    /* 启动1个100ms的自动重装的定时器 */  V$ _9 O7 v$ [2 M5 r) M! M5 }8 i

  34. 2 g5 a! [6 M* f8 @1 A: |

  35. * x, H0 m5 \" w+ @9 v  p
  36.     /* 生成方波数据 */
    , Y4 Y2 a) a/ |
  37.     MakeSinTable(ch1buf, 100, 0, 65535);  s" X- p! c- B, [

  38. ! h+ j6 t* ~# g0 H4 Z; `8 h
  39.     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);
    6 ]+ d  }3 k) W( A9 W) q- t

  40. 4 G* |* R- q  F$ E! }& n0 t2 M
  41.     while(1)$ t' y" O$ z7 ?2 \
  42.     {
    ; Z+ K5 i* z$ Z* C3 w2 ?
  43.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */# |/ f9 q4 @" C2 f6 B

  44. , G. i4 F; F1 \! }
  45.         /* 判断定时器超时时间 */" _& i/ X7 l9 v5 F9 k$ L4 l
  46.         if (bsp_CheckTimer(0))   
    ' W- [7 _$ R  }# [: G& V
  47.         {/ I# s* @% S- Q4 P" V
  48.             /* 每隔100ms 进来一次 */  # L" Z5 m* r" v0 h
  49.             bsp_LedToggle(2);+ \3 L: Z$ z# E0 `- H4 m+ j( p. u6 h0 O
  50.         }
    % P: G$ Z& `4 p$ b
  51. - `, D. o" L* W& t
  52.         /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */7 V9 m6 x0 z# S" p& r
  53.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */9 t8 U0 q. i9 B
  54.         if (ucKeyCode != KEY_NONE)
    . c- P% _3 K% M1 G
  55.         {
    + p: j, t+ `) o) S1 U  T1 F2 U
  56.             switch (ucKeyCode)
    ! S/ Z% J; [2 S) C0 H
  57.             {/ a4 r; L3 V4 \' o" e
  58.                 case KEY_DOWN_K1:            /* K1键按下,通道1输出方波 */
      Z1 [8 b( R' R+ I- S& T  B
  59.                     /* 生成方波数据 */+ \( _$ E& W+ v
  60.                     for(i =0; i< 50; i++)
    : |! c2 z6 n, ~& M
  61.                     {
    6 h1 L1 X' ~8 h  s
  62.                         ch1buf = 0;" z  Y" Z0 o# e& P# Z
  63.                     }
    + A7 H4 h! `1 w, B3 {  [( G1 a& g
  64. 9 t$ l* [8 C# I9 P- @
  65.                     for(i =50; i< 100; i++)
    4 o4 Q2 l. w. [' A  y; i, J" L
  66.                     {
    - X# W9 I# n5 G- d
  67.                         ch1buf = 65535;* i0 [; }% t9 Z
  68.                     }
    , i3 ~" @( r7 ^8 t
  69.   \4 n; y' J& o- _
  70.                     /* 触发速度1MHz */
    3 q/ I1 a4 _" o  M  R; J
  71.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);
    ! R' P7 C0 j8 x
  72.                     break;
    * E# }3 i9 S4 b6 @

  73. 7 T5 ~$ B- s7 w, S: B/ G  x
  74.                 case KEY_DOWN_K2:            /* K2键按下,通道1输出正弦波 */" i8 W' P! M  X8 N! d, y
  75.                     /* 生成正弦波数据 */    ( C* s1 ~. Z1 j; G( S
  76.                     MakeSinTable(ch1buf, 100, 0, 65535);3 K/ `1 @* y3 {# I8 A( t
  77. 2 \0 ^$ a* V) X" V
  78.                     /* 触发速度1MHz */: y" C" O1 j2 W
  79.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);8 S+ f6 {7 _! F4 j2 u) y
  80.                     break;
    % D" s* i) o5 P: [" T. v# A# p

  81. 0 J$ v0 o8 E6 R1 R
  82.                 case KEY_DOWN_K3:            /* K3键按下,通道1输出直流 */
    + K7 y% t7 _" ?! |' z

  83. 0 m% y% D( b7 n
  84.                     /* 生成方波数据 */
    - T& Q& f% o& R- i
  85.                     for(i =0; i< 100; i++)
    + o; _2 u0 F# ~6 C
  86.                     {2 O0 b: n, @1 Y2 h4 C# _& b3 q
  87.                         ch1buf = 65535;
    ' d# N9 w7 l; L7 k- i7 a2 m' F; v
  88.                     }- x/ m! Y6 O8 h9 ?

  89. & u2 G$ X6 f0 m# `" N" L& L
  90.                     /* 触发速度1MHz */$ G  M2 R% x0 p8 ]& P. @
  91.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000);, ]1 ^: ~0 o( t  G, @
  92.                     break;, X. ], Z7 T, C, W  o- i- z

  93. 1 L, O; F9 O/ b" D) M
  94.                 default:
    . [2 d" J. t' S0 T9 l4 L9 I- L# t
  95.                     /* 其它的键值不处理 */
    ' k5 ?' |  Q! d7 P3 ^7 |( Q
  96.                     break;# M# z$ L! b4 b* F" u
  97.             }
    8 P2 s' k+ {+ u( c8 ?, u
  98.         }9 e& T, i, Y# `$ B: V2 f$ W! ]
  99.     }
    , F- V. l+ i0 ]2 Y3 U/ E% T
  100. }; {$ U6 n$ O" ~9 K( C: {- a7 u( _
复制代码

) k/ z; o( T: ~3 a7 O9 P  n+ ]8 ~8 _% Q" _$ L+ f  k! @# M8 ?
75.15   总结2 I7 X$ {" ?5 f
本章节涉及到的知识点非常多,特别是SPI DMA方式驱动的实现方法,需要大家稍花点精力去研究。6 H& m4 F. J2 G5 n- \

' X% r  {+ S/ x5 }3 X% r9 F0 c! H5 c
1 ~! {/ y1 H- P: G( s
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
收藏 评论0 发布时间:2021-11-3 10:44

举报

0个回答

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版