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

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

[复制链接]
STMCU小助手 发布时间:2021-11-3 10:44
75.1 初学者重要提示
: K' H" F/ W# B: `1、  学习本章节前,务必优先学习第72章。' [# M: o+ r  ]6 {" @* s

. h9 q  L& b4 T  r6 M: [2 g1 r2、  DAC8501模块上带了两片8501,每片是单通道DAC,带片上输出缓冲运放,轨到轨输出,16bit分辨率,支持30MHz的SPI时钟速度。
6 Z# t" \4 @& N4 y9 o% L5 _
5 N* ~$ S0 G, ^; Y! o/ C2 s9 A- u3、  本章涉及到的知识点比较多,需要大家掌握STM32H7的SPI , DMA,TIM,DMAMUX和DAC8501的一些细节用法。
3 d5 s9 Y2 y3 D. L1 V8 w9 e' [; w  C/ g6 n8 b5 z3 k2 k$ L
4、 H7的SPI + DMA驱动这类外设的灵活度,绝对可以媲美FPGA去控制:
2 W1 `; P# ~3 D% e
: e1 p+ `; l$ |4 F  H7的SPI外设比F4系列的灵活性强太多了,主要表现在两个方面:数据的传输支持了4-32bit,特别是那个NSS片选引脚,超强劲,可以做各种时间插入,灵活应对了市场上这类芯片的需求。
: n2 C  P* O* h6 Y  DMA这块相比F4系列,有了质的飞跃,支持了DMAMUX,这个DMAMUX除了带来灵活的触发源选择,还支持了各种触发事件和同步触发功能。本章配套例子的触发周期控制就是利用了DMAMUX的同步触发功能。
$ v' ^; W" A, p- T' L; P5 f' p5、  本章配套了中断和DMA两种更新方式的案例,DMA实现方式与中断更新方式完全不同,因为DMA方式要使用硬件SPI1 NSS片选引脚驱动DAC8501。而中断更新方式使用公共的总线驱动文件bsp_spi_bus.c,片选是通过通用IO方式控制,支持串行FLASH、TSC2046、VS1053、AD7705、ADS1256等SPI设备。大家在看例子的时候要注意。
/ X  V+ m1 [. ?2 |  [0 u( u, L" U( Y; I, g* ]/ C& A/ ]
6、  对于本章教程配套例子的SPI DMA方式,这里特别注意一点,定时器触发一次,就会让SPI以DMA方式传输24bit数据。& t7 v1 y8 o) {7 b" U

9 m$ J+ t8 S/ K2 D6 h( I7、  DAC8501数据手册,模块原理图和接线图都已经放到本章教程配置例子的Doc文件里。
8 m- c1 {+ P( H. ^1 x: |0 b
' M+ V4 v8 l# e- [* ^75.2 DAC结构分类和技术术语1 P1 W5 R/ U6 `
在本教程的第74章进行了详细说明。
  E" K5 L% x' }3 P
4 f2 x+ |$ d; u) i9 Y  Q75.3 DAC8501硬件设计
. r2 y  r: Y9 @3 x! y0 J1 x75.3.1 DAC8501模块规格
9 K* Y" U" c4 Y" Q, K产品规格:
* M$ C6 B. m6 u/ r% j) e1 _5 W) j& M" Y. p4 K' g5 W
1、供电电压: 2.7 - 5.5V【3.3V供电时,输出电压也可以到5V】。' K! n6 h: [, }: I: X0 K
& [3 W) w  j( @5 Z/ a) }
2、通道数: 2路  (通过2片DAC8501E实现)。0 _+ q' A. S7 y! K) b/ I

6 G3 R: t7 l2 Y5 T- Q7 M7 a/ u- @" ?3、输出电压范围 : 0 - 5V【零位 < 0.020V, 满位 > 4.970V】。% d; O. e- e' c. J& y
% h. @: F. m* ~" f' q
4、分辨率: 16位。, k# y" ?- h! H; ~4 s8 P

+ P* A' a' n7 j1 c  E5 ~3 Z5、功耗 :  小于10mA。$ H$ i: w1 {) h5 S
) v! u' f# Y  A( I5 j
6、MCU接口 :高速 SPI (30M) 支持 3.3V和5V单片机。+ L2 R" @- ~; B* S# M1 `+ `

2 G& U" g, W: Y! w7、DAC输出模拟带宽:350KHz。+ V9 k( J: A! n8 U+ f* O
6 n+ v. |8 E. P6 Y4 M4 l. w
8、DAC输出响应: 10uS 到 0.003% FSR。. I  }" v/ S& H! [3 I

0 _+ D1 m( H2 `7 ]产品特点:3 O6 V& H. o  q' |! B8 t. [
: q$ j6 j) A: u6 c% S+ B3 P2 Z
1、输出和供电电压无关;模块内带升压电路和5V基准。" M: x/ L( T: e6 L) m

: [# K7 D+ [2 O# q9 q( |2、自适应单片机的电平(2.7 - 5V 均可以)。0 Z5 ?7 J, u9 J+ U9 G  J* I& K. z1 [" v

2 T4 \5 K; ]1 }, |9 I: H3、输出电压轨到轨,最高电压可以到 4.970V 以上。
1 p% S- D6 S" m0 b& q
2 M2 O7 F# }( y# E产品效果:# D" q2 }* N9 d! f% {8 h

3 Q! _, |' n2 Y$ M0 ?2 c8 [) V
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
2 _1 o" R9 u- b* S6 m2 s: w
- |9 g& F9 C0 z5 G, P/ J; A) C
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
: p# O$ g/ g; s9 g4 X$ \2 S2 S
3 i9 s& J; Q8 i: Z  P9 n* O
75.3.2 DAC8501硬件接口
  d* o7 r5 T, b) {0 }8 N3 c$ nV7板子上DAC8501模块的插座的原理图如下:# M/ [) o6 Z- y; b: y9 e

. _1 s6 |" l9 {0 Q" u7 @# r
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
  \4 q9 n4 L1 K2 `7 A9 x/ @
% h8 U2 f; }2 h6 Y
实际对应开发的位置如下:
0 l; w6 Q, w2 g$ Q
/ w% i& W, f' ^7 O! ~& |
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

* t: z3 j; X( {2 Z' ~
9 F: M7 W7 Q* c" I" ]- Y* I* u75.4 DAC8501关键知识点整理(重要)
6 c8 I) O. o3 s5 f驱动DAC8501需要对下面这些知识点有个认识。- v/ W- V, h8 l# o  |
6 k3 W  [! x2 w% G1 `
75.4.1 DAC8501基础信息: L+ v1 C( t0 j' d( r6 g6 }7 F1 v
  单通道DAC,带片上输出缓冲运放,轨到轨输出,16bit分辨率,支持30MHz的SPI时钟速度。3 x1 z0 c0 ^% k/ p0 J7 ~$ R; V
  模拟输出带宽350KHz。
5 N& J5 X5 H* z  W5 a0 T8 r  供电范围2.7V到5.5V。: l+ t( y. a8 I2 k6 z) t0 W
  具有低功耗特性。5 J4 T" C6 a1 T7 [  V& V
  上电复位输出0V。4 V  Y7 X' T5 D( w# @
5 C8 Q: c+ C# M7 _
5 B6 V7 ?' g+ P0 x! l7 m- v* P
75.4.2 DAC8501每个引脚的作用
' e  ~" e% i0 {! ^DAC8501的封装形式:
% b3 }+ e. V. a5 Z- R9 L; {" P! B( J% w
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

) s8 C: P) A3 G9 i
& {% \# L' v0 @- @( L8 u' G  Vdd
8 P1 H/ N4 b: K供电范围2.7-5.5V。
* Z- C& E# @+ K5 k) d* W$ W: r: k/ y& T* V. C+ p, C7 l. H& V
  Vref
$ |, _: X; `. D稳压基准输入。
/ C2 w- O  b. a7 j$ H
9 E: h9 n. W5 f; d  Vfb
; Y: @' n3 }1 Y输出运放的反馈。5 S8 w* ?, v: _3 z6 s4 m
1 G8 A6 R! J8 ?/ G( g( W& G
  Vout
. r/ B) f# L9 t5 b" }模拟输出电压,输出运放具有轨到轨特性。  x  c8 t" W, }; n- o  c
, S/ S6 j2 c0 {
  SYNC (片选)
" \$ z% ~7 [8 N  L$ k) N低电平有效,当SYNC变为低电平时,它使能输入移位寄存器,并且数据采样在随后的时钟下降沿。 DAC输出在第24个时钟下降沿之后更新。 如果SYNC在第23个时钟沿之前变高,SYNC的上升沿将充当中断,并且DAC8501将忽略写序列。1 l2 a9 B! Q8 ]1 p6 f
, p2 [* b5 M, g  R8 \' P
  SCLK$ D$ `% w6 k3 g* {4 A
时钟输入端,支持30MHz。
- t5 `( x% d; l- g: B* d- i3 N6 Y
# L- O, n1 e, A1 A" E' u  Din# P* r9 v! \1 E$ d( }* o# m
串行时钟输入,每个时钟下降沿将数据写到的24bit的输入移位寄存器。
# ~- \( R* ~  F4 g+ C- C# Y2 J0 a1 B
  GND* i# ]4 g5 q0 J* o5 D3 s
接地端。
( [4 J/ M5 \" k9 w3 P0 Y
: [' O5 d4 D$ _6 a2 F6 |  c( v75.4.3 DAC8501输出电压计算公式6 j- Y1 K# f- E( P$ Q- D
DAC8501的计算公式如下:
$ I" `* V8 U9 @, R' f# X# m1 S. h
% [5 ~( B/ [! o, Q: K1 ~3 L
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
- v7 |7 G& n& Y4 S% Q; {4 I8 a" X+ U

7 E+ R6 I, Y1 X4 o' b; p  D
: {' r* b2 f2 W% }  L6 T% x配置DAC8501数据输出寄存器的数值,范围0 到2^16 – 1,即0到65535。) R6 `4 T, R# O
" }$ i4 ?8 C/ ]4 q, j9 Z
  VREF
# J3 K, p, \7 I2 m7 _) U使用外部参考电压,由VREFIN引脚的输入决定。
# B# I, G) I2 r- G( F& R( M) w/ s
( }8 [7 `: W6 E+ o( {8 \  Vout% ~- X, e1 x) i2 G# w, I+ j
输出电压。4 K; P- ?. V( m( ]2 E* G8 w& |
" G% Z3 c" ?" M; z- D& D- m! f
75.4.4 DAC8501时序图
( G' s/ @0 G" V! Q& u0 a. a4 tDAC8501的时序图如下:
2 V$ k/ t  z* C1 r5 F- p- p
$ m7 C" H5 w7 S5 ]/ A) M; G
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

  y- z6 N, x/ d( K& W. t
! p! i, J1 C! T6 T$ i. W/ n! G- C; r& I
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
: g' [' z# O+ a; W, ~  s; S
5 ?+ g' R7 m( f7 q/ B" q
这个时序里面有三个参数尤其重要,后面时序配置要用到。
$ _" n, Q" j# A0 E. q7 {6 N
3 T7 N" p5 u  B5 B$ k' d  f(1)( m, I9 @6 y& W, W) O; c7 o
供电2.7到3.6V时,最高时钟20MHz。2 e) ~0 w: Z/ W# Z( _

! h. X9 t! k7 F( H: _& ]7 l供电3.6到5.5V时,最高时钟30MHz。
/ O9 f( C; x/ Q5 h2 {* r& ^! v0 X+ p9 P$ e6 C
  t(4)
# F: }) O8 O& n4 [1 @% hSYNC低电平有效到SCLK第1个上降沿信号的时间没有最小值限制,可以为0。2 p6 R8 e% \$ |" ^

# t& a4 G+ P: z: w* X0 z  t(8)
/ |& v' }  `1 e8 k; f' {5 y每传输24bit数据后,SYNC要保持一段时间的高电平。. D3 E" o  ?6 R, B

9 r/ E! e! K: g' g+ R供电2.7到3.6V时,最小要求50ns。
+ Q  `% ]* W( o$ j  V5 w% _9 i* v9 `$ @) b$ g; V' V
供电3.6到5.5V时,最小要求33ns。
/ B- y; U( e" e5 R9 j$ h" T7 D
  |  M) E+ z6 X. d75.4.5 DAC8501寄存器配置6 {0 I. `$ h& g3 ?/ m2 ~$ m9 g+ G
DAC8501的寄存器配置是24bit格式:
/ Y0 E' n+ X- D4 z
2 v: E- ~' Z" w" d
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
6 ~9 |( V) j) b1 k! p

8 C" o* m, ~1 y# q* r& N控制DAC8501每次要传输24bit数据,高8bit控制位 + 16bit数据位。
  ]& q) a9 b! F: T9 }& K6 v
& Y1 A* @. @5 b, u, l( F7 l/ g& H% k控制位的PD1和PD0定义如下:. B, f. S7 ~, y) h, P6 E
+ l# K% r  F4 c- K( x5 S# @
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
1 z# n$ l; s' i$ W4 D, J

7 }# f; K6 `% d3 r2 x3 uPD1 PD0 决定4种工作模式* O1 }1 Z% Y1 i% J: L) M0 Y
3 K7 A8 t5 J& ]
0   0  ---> 正常工作模式
. T: E& r' f1 O' ]
; M& _) f. i+ L0 _$ z0   1  ---> 输出接1K欧到GND
# G' Q' V; R0 K" u$ {' L; o; f1 y- m) O* l, v+ ?' ]
1   0  ---> 输出100K欧到GND
4 m8 Z4 }  L: G; \
: a2 V, L) F$ l& x/ `" f& A. n1   1  ---> 输出高阻
3 H( K$ r1 w, [8 o4 P& t
% i$ [/ O6 O3 [75.5 DAC8501驱动设计(中断更新方式)
  A; G/ \7 j* i1 t0 d$ ^  X7 CDAC8501的程序驱动框架设计如下:
' w4 u1 u$ o& H1 s$ J
- B- ~% E/ u/ Z: k
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

* S0 A) F* n/ {
7 z$ X& E) t" D% {! Y$ i# U有了这个框图,程序设计就比较好理解了。+ Q4 I6 p  y* }" o& c; m3 p6 Z
  b( y+ m( U2 V' I* }! g1 A
75.5.1 第1步:SPI总线配置; o& [( T4 X2 t) k
spi总线配置通过如下两个函数实现:- O$ Y. K2 c' X8 B6 D( _

  R0 |: z6 C$ O7 }
  1. /*
    6 `/ g; j$ A% r9 G
  2. *********************************************************************************************************
    1 J" M; N, ^8 m; B8 Q
  3. *    函 数 名: bsp_InitSPIBus
    0 i2 v- m) d( h( u
  4. *    功能说明: 配置SPI总线。! S3 ]* `8 V: d8 O/ D" o! k" `! e* z
  5. *    形    参: 无
    6 \* [$ F' F/ ~8 g' O
  6. *    返 回 值: 无
    & H& J5 Y8 p/ Y: c" t/ d
  7. *********************************************************************************************************
    " ]! d4 N7 J+ E# i3 {
  8. */, S8 [1 o6 s# t; |
  9. void bsp_InitSPIBus(void)/ r( |$ U2 _; p
  10. {   
    4 J0 J6 G! l4 J, S* x. V
  11.     g_spi_busy = 0;
    % ]- }: ]- G1 `7 [9 d4 I9 F

  12. ! \$ T; i4 N7 `3 Q' P
  13.     bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_1EDGE, SPI_POLARITY_LOW);
    - l! q0 o7 q8 W0 R' [8 ]
  14. }
    ! a( J/ K) b) ]  q! O* {" t
  15. 0 G& h! j: l! R) l5 e# [, k
  16. /*  p0 J9 u! A) J2 c
  17. *********************************************************************************************************  Z& M/ m4 S) ?: j! \6 @* u$ w2 Z
  18. *    函 数 名: bsp_InitSPIParam
    4 U9 v8 G1 C# e
  19. *    功能说明: 配置SPI总线参数,时钟分频,时钟相位和时钟极性。* L) x" s# S! ]; g) u
  20. *    形    参: _BaudRatePrescaler  SPI总线时钟分频设置,支持的参数如下:
    2 k0 u) R6 m* ^) ]
  21. *                                 SPI_BAUDRATEPRESCALER_2    2分频
    2 }* I8 T+ C, y) f
  22. *                                 SPI_BAUDRATEPRESCALER_4    4分频
    7 X5 Z5 A* J- b3 A7 V- p
  23. *                                 SPI_BAUDRATEPRESCALER_8    8分频
    1 @$ c- _' S- b+ h8 m7 L4 ^
  24. *                                 SPI_BAUDRATEPRESCALER_16   16分频( n8 Q1 i2 H: h9 R5 q
  25. *                                 SPI_BAUDRATEPRESCALER_32   32分频% l- ~+ F' |  f9 j. T
  26. *                                 SPI_BAUDRATEPRESCALER_64   64分频7 n. ?4 t0 a3 O6 R6 R  M
  27. *                                 SPI_BAUDRATEPRESCALER_128  128分频
    1 S" _3 @9 K7 f2 q
  28. *                                 SPI_BAUDRATEPRESCALER_256  256分频
    1 R+ u: m7 f$ C$ a8 Q, K. n
  29. *                                                        - E( W; f7 w/ |  f
  30. *             _CLKPhase           时钟相位,支持的参数如下:
    $ ]: q8 |; v1 d5 V; [. A% x/ d
  31. *                                 SPI_PHASE_1EDGE     SCK引脚的第1个边沿捕获传输的第1个数据; N4 u' {# k) R% \
  32. *                                 SPI_PHASE_2EDGE     SCK引脚的第2个边沿捕获传输的第1个数据
    2 h5 r& G0 h- ~' [4 g2 M
  33. *                                 $ P! M2 U( J# [* g5 s9 [$ s
  34. *             _CLKPolarity        时钟极性,支持的参数如下:
    ' B; f9 j) d0 q0 L9 A2 M' q" O
  35. *                                 SPI_POLARITY_LOW    SCK引脚在空闲状态处于低电平
    ! r. s' ?1 d2 d: G5 x9 u+ {. w0 R
  36. *                                 SPI_POLARITY_HIGH   SCK引脚在空闲状态处于高电平
    7 z. k" O. f1 Q8 j$ ~/ J
  37. *
    8 b! f  w/ W0 \* R
  38. *    返 回 值: 无( }: s& Z- b) x4 i3 E
  39. *********************************************************************************************************
    ( p2 S) B& O* {- v
  40. */7 e/ M. p3 F' l
  41. void bsp_InitSPIParam(uint32_t _BaudRatePrescaler, uint32_t _CLKPhase, uint32_t _CLKPolarity)
    / r: C. B$ i! C, g* j5 B9 \
  42. {
    2 N" `5 ^% y4 \6 i2 x
  43.     /* 提高执行效率,只有在SPI硬件参数发生变化时,才执行HAL_Init */
    ) L; w' \0 v  Y! T% f0 d
  44.     if (s_BaudRatePrescaler == _BaudRatePrescaler && s_CLKPhase == _CLKPhase && s_CLKPolarity == _CLKPolarity)$ j1 t1 M2 ]' C# @- j* i3 z
  45.     {        
    1 g* x% E# w" L0 \% X; L) b
  46.         return;
    5 U, l. Y" ]" b; q5 {
  47.     }
    7 u, ?0 @: S' V& R
  48. + _( N) H& Y/ }1 W
  49.     s_BaudRatePrescaler = _BaudRatePrescaler;    - r4 N6 `; p, R6 v1 ^: L0 z' N+ ^
  50.     s_CLKPhase = _CLKPhase;
    2 U  J1 ^, {4 s
  51.     s_CLKPolarity = _CLKPolarity;) S5 k1 e6 _4 w, n8 j

  52. * S  M9 x- c/ c

  53. 4 O6 L* e) G- O! U/ K( v* v2 x
  54.     /* 设置SPI参数 */
    % T8 ?3 a% D2 q* j& \! A6 U$ P
  55.     hspi.Instance               = SPIx;                   /* 例化SPI */; n( r( W# L, s
  56.     hspi.Init.BaudRatePrescaler = _BaudRatePrescaler;     /* 设置波特率 */
    9 W! s- g  P; w6 d* l. b! b( ~3 g
  57.     hspi.Init.Direction         = SPI_DIRECTION_2LINES;   /* 全双工 */8 x- j% ^. x7 D
  58.     hspi.Init.CLKPhase          = _CLKPhase;              /* 配置时钟相位 */
    ( h$ {2 _4 a2 L5 u
  59.     hspi.Init.CLKPolarity       = _CLKPolarity;           /* 配置时钟极性 */
    8 `$ o8 @, B2 u6 I, S$ F+ E/ V
  60.     hspi.Init.DataSize          = SPI_DATASIZE_8BIT;      /* 设置数据宽度 */
    4 f+ I4 ?+ i3 C! r( ^( g! T
  61.     hspi.Init.FirstBit          = SPI_FIRSTBIT_MSB;       /* 数据传输先传高位 */) }" r/ j6 w$ ?7 B
  62.     hspi.Init.TIMode            = SPI_TIMODE_DISABLE;     /* 禁止TI模式  */
    $ q% H# h! w4 y+ u2 D7 G- B' X  A
  63.     hspi.Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE; /* 禁止CRC */" j! |) Z7 R# e
  64.     hspi.Init.CRCPolynomial     = 7;                       /* 禁止CRC后,此位无效 */! l% t# [1 V" }7 M) C' a( Q6 q
  65.     hspi.Init.CRCLength         = SPI_CRC_LENGTH_8BIT;     /* 禁止CRC后,此位无效 */
    8 ]( q3 D" K6 v- \* o. T; @
  66.     hspi.Init.NSS               = SPI_NSS_SOFT;               /* 使用软件方式管理片选引脚 *// C7 g! @2 ~: Q5 A0 Y3 }8 u
  67.     hspi.Init.FifoThreshold     = SPI_FIFO_THRESHOLD_01DATA;  /* 设置FIFO大小是一个数据项 */7 f/ }. C, b3 K0 S# ?
  68.     hspi.Init.NSSPMode          = SPI_NSS_PULSE_DISABLE;      /* 禁止脉冲输出 */
    ! Y1 y; y0 [9 N( L8 Y# z  `: q
  69.     hspi.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE; /* 禁止SPI后,SPI相关引脚保持当前状态 */  5 z! i9 X' e! `
  70.     hspi.Init.Mode             = SPI_MODE_MASTER;            /* SPI工作在主控模式 */
    - R7 L; k' P1 J: r

  71. 6 \# T' P  \! K: v. j. y! M
  72.     /* 复位配置 */
    2 B9 V, \: L+ C; m9 S
  73.     if (HAL_SPI_DeInit(&hspi) != HAL_OK)# M) G7 l3 I8 Y- c/ w; D8 q
  74.     {: x' G% w( F% d& f: ]$ Y) {$ S
  75.         Error_Handler(__FILE__, __LINE__);
    * c- `/ Q* q- t4 @+ N3 }$ W
  76.     }    3 s' {" E4 |! E: j( G
  77. ! d& T2 f* S( `2 P: m8 j; M, [
  78.     /* 初始化配置 */
    : {5 ]  |$ B/ D$ f" {
  79.     if (HAL_SPI_Init(&hspi) != HAL_OK)
    ) K' a5 j9 t* l
  80.     {2 X8 m3 `7 M, N' s) U
  81.         Error_Handler(__FILE__, __LINE__);/ S8 c' \5 L( r
  82.     }   
    8 J: C, b8 K+ |% }% a
  83. }& ]/ b3 ?, q9 l5 N; t8 S
复制代码
5 U# F6 Z1 D8 d

/ C0 ]$ I. J2 ~# U1 |关于这两个函数有以下两点要做个说明:6 \7 b; X/ {7 T' V

/ k1 A1 F% D8 j9 T' ?0 t# l  函数bsp_InitSPIBus里面的配置是个初始设置。实际驱动芯片时,会通过函数bsp_InitSPIParam做再配置。1 v( }* m& {( C
  函数bsp_InitSPIParam提供了时钟分频,时钟相位和时钟极性配置。驱动不同外设芯片时,基本上调整这三个参数就够。当SPI接口上接了多个不同类型的芯片时,通过此函数可以方便的切换配置。
3 |. {5 g# Z" R3 l0 ?75.5.2 第2步:SPI总线的查询,中断和DMA方式设置9 x( o7 n1 _8 J+ n; }
注:对于DAC8501,请使用查询方式。
4 m: s6 ~3 Z1 X2 k: L4 {7 N+ R1 S4 F4 t8 \; V
SPI驱动的查询,中断和DMA方式主要通过函数bsp_spiTransfer实现数据传输:
! t( U) B6 r) j) R! [% s$ W- \1 X) B5 q7 T- ]) \
  1. /*" }( t6 {% `! z" ?
  2. *********************************************************************************************************1 B  |% g  t! E1 K
  3. *                                 选择DMA,中断或者查询方式
    2 I' V/ ?; s" X" Z2 W
  4. *********************************************************************************************************
    ! l4 ]; g! Y; Y- S; {4 Z/ l
  5. */
    + l/ l9 b  t* c6 q* Z1 J* y6 y$ ]; a
  6. //#define USE_SPI_DMA    /* DMA方式  */" t0 V9 p8 x1 }$ U8 K5 W; v
  7. //#define USE_SPI_INT    /* 中断方式 */
    ! C6 ^4 s+ W  k( G2 |/ |
  8. #define USE_SPI_POLL   /* 查询方式 */
    0 d9 {" Y3 V' R2 n$ s8 I' Y- M

  9. ; o/ P) a. {0 {/ r  v/ l
  10. /* 查询模式 */
    + Z) Q6 z0 U' e0 r/ y
  11. #if defined (USE_SPI_POLL)
    ; e+ ~* u7 n6 I6 S: i+ S( q2 M

  12. , e5 Z9 T% c+ R/ |+ i5 q
  13. uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];  
    / f1 L- S0 b) P* @8 e! ?3 g
  14. uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];1 u$ d+ z) r' h* V- m3 Q7 J

  15. $ W* y+ o& Y8 g: q  X# y1 F5 J
  16. /* 中断模式 */
    " L+ a/ N, ]' I+ T; ^% f6 k) ?6 f% }/ F
  17. #elif defined (USE_SPI_INT)" ]$ [' K# N, u9 F: }3 n+ i' e
  18. ! E4 u7 g4 y) }
  19. uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   
    ) V* a" @3 b- E
  20. uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];
      @3 P  `" `# e( E

  21. # j0 X+ F; |* m& e
  22. /* DMA模式使用的SRAM4 */
    : K+ |9 j) \' w, p5 K. s
  23. #elif defined (USE_SPI_DMA)
    + x6 D5 }3 [9 F
  24.     #if defined ( __CC_ARM )    /* IAR *******/
    ' x! z, U. Z# m4 M( N4 O1 z
  25.         __attribute__((section (".RAM_D3"))) uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   
    8 i6 d' `4 G; j$ J( a5 i
  26.         __attribute__((section (".RAM_D3"))) uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];" W# C! A! b* G; A, e* m1 ]
  27.     #elif defined (__ICCARM__)   /* MDK ********/
    ! n! l* j0 N1 i4 `
  28.         #pragma location = ".RAM_D3"
    3 T4 H  |8 Z3 x# N0 ]! X
  29.         uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   
    ; B1 d% c/ h  s" I9 t8 Z
  30.         #pragma location = ".RAM_D3"! L7 a5 n, l8 _8 H# K" P& u( h
  31.         uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];, @: P3 I. _3 }5 v3 k% C0 E
  32.     #endif
    9 u! n& R& z) f
  33. #endif
    % M* ^1 q+ V/ T, Q' }" p

  34. 6 s: `/ d! H; }6 C/ X) J$ D
  35. /*
    6 {. f7 {, b4 S& r6 r( @
  36. *********************************************************************************************************
    2 r+ [7 k' g; s! i1 O. F6 Q
  37. *    函 数 名: bsp_spiTransfer
    & W0 z. }% ]) ]- p/ m- O
  38. *    功能说明: 启动数据传输5 ~$ m: q& `& O# f3 L6 Q
  39. *    形    参: 无* r9 t, n" @  w
  40. *    返 回 值: 无
    ! _0 X, ^; L+ f- I9 [- {
  41. *********************************************************************************************************
      y; [% }* E( S+ T6 A
  42. */
    * u( _! r* z; h8 G" }5 A. Z
  43. void bsp_spiTransfer(void)
    " q5 T4 b) u/ W/ n( i
  44. {' @6 o/ [4 l, \6 f! J, [
  45.     if (g_spiLen > SPI_BUFFER_SIZE)7 ]' Y0 a9 ?4 _3 ^& Q) F3 ?" S5 R
  46.     {
    6 Y6 P/ t+ E0 i% _
  47.         return;
    ! s; L' T5 p. z, E+ ^
  48.     }
    0 G- {9 _, @% i+ F2 |" a- D

  49. * E+ K; j+ z) L' L3 _7 E
  50.     /* DMA方式传输 */
    / G: y) b2 i4 A% b
  51. #ifdef USE_SPI_DMA) j0 B8 [5 D- }
  52.     wTransferState = TRANSFER_WAIT;- g! a6 c3 o9 n6 }$ V1 r" {' U: _) e

  53. $ X8 `  {$ u- Q! M% b" m
  54.     if(HAL_SPI_TransmitReceive_DMA(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK)    4 d6 o+ i* H# g
  55.     {
    4 m& N8 m5 C4 X/ B% y7 q& V2 W& ?
  56.         Error_Handler(__FILE__, __LINE__);% q- L4 Y/ L9 J% i$ v
  57.     }
    ( ?2 I; H( u$ b$ `7 X* ?

  58. - ^4 ?  r* I# i- D) I* X% m% X5 R
  59.     while (wTransferState == TRANSFER_WAIT)
    7 O$ k1 m. Z4 ?* Y% K- o
  60.     {7 w9 F3 K, l4 `
  61.         ;' ^* T1 e$ R, q* P( r& I
  62.     }7 o' R( b% |* [: p% d
  63. #endif" [2 w: O; n3 w+ p% }* d

  64. / q' n2 c" y4 M5 y
  65.     /* 中断方式传输 */   
    ) `; U3 S2 q% h: X5 B
  66. #ifdef USE_SPI_INT
    ( o% k' N9 _5 \! x. T
  67.     wTransferState = TRANSFER_WAIT;
    ) j$ W: n8 k& y, U" E; e1 e+ |

  68. . v+ {) y* U3 N
  69.     if(HAL_SPI_TransmitReceive_IT(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK)    4 J  l! P4 q5 B$ b6 C9 e, b' P. ?( Y
  70.     {
    # ]  W& _3 {2 U$ G9 ]6 q$ X+ R
  71.         Error_Handler(__FILE__, __LINE__);
    3 d% i% f9 Q9 p6 F7 u  R
  72.     }2 \# O* _+ S. a5 [
  73.   K' ^+ a! ]/ V3 R8 d; y0 x
  74.     while (wTransferState == TRANSFER_WAIT)
    0 q9 ^5 A9 G2 g) t3 Q
  75.     {
    - x6 ]& d" v/ V4 r
  76.         ;9 o8 J* Q$ Z% ?- V  d
  77.     }2 a1 d$ O* J& y  h; h. y
  78. #endif/ t. k( Z! R8 d$ L6 t  S

  79. # D# z, p6 Q( H; w# s; r. T
  80.     /* 查询方式传输 */    8 L0 C$ Z% e$ Y9 z
  81. #ifdef USE_SPI_POLL5 c- h# @. l" \
  82.     if(HAL_SPI_TransmitReceive(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen, 1000000) != HAL_OK)    # ~6 ?* |* _% ^" h/ w% M
  83.     {
    # V* I6 j6 ?4 O8 i" E
  84.         Error_Handler(__FILE__, __LINE__);& {2 f( d/ f$ f
  85.     }   
    + j, A# f9 k# S3 N. y
  86. #endif% k, f% M" A; q7 F3 C& x2 O: ]
  87. }4 f% v0 G/ ]4 a
复制代码
& Y9 [3 F% g  _3 e( s8 N1 ?) b

* W+ S& Y5 ^6 j& N5 v( v通过开头宏定义可以方便的切换中断,查询和DMA方式。其中查询和中断方式比较好理解,而DMA方式要特别注意两点:2 {) l- M# c1 c% P

7 o5 F% l" W0 s* T% S  通过本手册第26章的内存块超方便使用方式,将DMA缓冲定义到SRAM4上。因为本工程是用的DTCM做的主RAM空间,这个空间无法使用通用DMA1和DMA2。
; x4 a0 I1 s9 K# C/ m3 `  由于程序里面开启了数据Cache,会造成DMA和CPU访问SRAM4数据不一致的问题,特此将SRAM4空间关闭Cache。
/ e) D' ^8 H4 J9 R5 z/* 配置SRAM4的MPU属性为Non-cacheable */, Z3 j0 [3 M+ W; a" Y) i
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;! }3 t: @  g! l% M, [1 r; w" c7 i  s
    MPU_InitStruct.BaseAddress      = 0x38000000;& ^/ y/ S( G; u& l' b
    MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;9 q6 o7 F" d+ [7 u; p5 y3 ^
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  z0 a6 c$ F! L2 N) K    MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;, r6 L7 Y; L* n% s+ x
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;! r5 N# w9 `: e' g
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;; _  ?, I8 U! q, A* @
    MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
- G1 z. A7 l5 V. C; Z6 P5 c7 ]    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
5 v. m* b" D; Q  N/ w5 q1 e. E" [6 r5 t0 Y    MPU_InitStruct.SubRegionDisable = 0x00;4 M" v6 ?6 e8 b9 H' P6 n" b
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
' D! o6 `+ \6 q- @# j0 P! e" ]5 P- \2 O
    HAL_MPU_ConfigRegion(&MPU_InitStruct);
. [# I$ P3 p6 K2 h7 z
0 R- f: k7 e3 L1 Z8 A9 k0 [6 A& |/ k, f
75.5.3 第3步:DAC8501的时钟极性和时钟相位配置+ ]& |9 K3 ?. K9 T1 D) p
首先回忆下STM32H7支持的4种时序配置。5 N# ~1 M" S6 {8 n! L) p
& R+ P* W; i, P0 ^! ~; i
  当CPOL = 1, CPHA = 1时+ K1 X9 b6 F) ~; n
SCK引脚在空闲状态处于低电平,SCK引脚的第2个边沿捕获传输的第1个数据。3 ~; y8 D. I7 L4 j

. U7 M$ k" x; s7 H, W' i  Y  当CPOL = 0, CPHA = 1时
" }" `- ~: b6 q+ ?7 ~1 E2 ?) r: ZSCK引脚在空闲状态处于高电平,SCK引脚的第2个边沿捕获传输的第1个数据。
% l) {: P6 u5 }/ U% M- m  ?0 V* j1 u; d- n! J3 x, N$ c
  当CPOL = 1, CPHA = 0时! Q! N6 c4 f1 e( [+ P- j
SCK引脚在空闲状态处于低电平,SCK引脚的第1个边沿捕获传输的第1个数据。# x& x' Q, Z1 ^% g+ |) S

4 I0 y+ m1 ~0 |5 a4 S- `- R( g& j  当CPOL = 0 ,CPHA= 0时
1 D" \8 q  ^7 ^- U3 r" J/ v" Y" OSCK引脚在空闲状态处于高电平,SCK引脚的第1个边沿捕获传输的第1个数据。
+ R2 E( n/ A) s" g0 W" b/ D% N8 Y. `! c# i# s
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

% t- }8 w" X# l8 g4 G
' X; K9 M% e# f$ P! \有了H7支持的时序配置,再来看下DAC8501的时序图:
* I) k1 ^% o$ k$ V, j# D5 z/ _
, q+ S3 ^+ p3 g0 m5 |
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
) O" l/ v4 x; K. {4 y7 m
) m5 e& o9 B6 @+ {( G
首先DAC8501是下降升沿做数据采集,所以STM32H7的可选的配置就是:
2 \) z2 U7 m5 _
& @7 _0 o9 M5 ~9 ~& s- {CHOL = 0,  CPHA = 1
0 ^7 @+ l, Y, h# U- I/ ?5 ]9 R
) F4 y% ~; J  c& R2 d( r4 l4 NCHOL = 1,  CPHA = 0
3 F& `+ H" w4 h% D$ {6 L
: d- G. ~# K- g3 r  J, B/ R; j4 g对于这两种情况的主要区别是空闲状态下SCLK时钟选择高电平还是低电平,根据上面的时序图和DAC8501的数据手册,两种情况下都可以正常运行。经过实际测试,STM32H7使用这两个配置确实都可以正常运行。程序里面默认是选择CHOL = 0,  CPHA = 1。5 Z, S8 N2 h. Y: B
# `2 M) s3 x/ z8 X, J" S
75.5.4 第4步:单SPI接口管理多个SPI设备的切换机制5 w& G- ]. z! L* g; C
单SPI接口管理多个SPI设备最麻烦的地方是不同设备的时钟分配,时钟极性和时钟相位并不相同。对此的解决解决办法是在片选阶段配置切换,比如DAC8501的片选:' w$ E7 i+ O0 N; |# B& _
4 b' G! H+ h0 d! m8 `% U
  1. /*3 c  ]2 K. x' y0 w. m( t! e  {
  2. *********************************************************************************************************
    ) N% x0 Z. F4 N- L3 _/ Z" A
  3. *    函 数 名: DAC8501_SetCS1; \, ~$ Z0 P  H( t
  4. *    功能说明: DAC8501 片选控制函数) w( u# V3 \' K% g/ T! N6 }
  5. *    形    参: 无) Z  r$ L. z+ C8 F
  6. *    返 回 值: 无
    0 c5 \# k3 t9 M% V! Y( @* ?
  7. *********************************************************************************************************
    - J) ]* g  `6 Y
  8. */
    % y' E" F9 b3 L+ O- j  o( \
  9. void DAC8501_SetCS1(uint8_t _Level)
    : [! t. {( x& g1 p! m
  10. {
    & T3 s7 \) I; O; m" L
  11.     if (_Level == 0)6 }7 J( x7 Q3 ?4 K# S: d
  12.     {5 y2 K; d5 o% e. d
  13.         bsp_SpiBusEnter();    /* 占用SPI总线  */    ( E- D' ]# s8 r- k4 F: Z
  14.         bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);        - D# s/ n: Y) v4 I) n. N
  15.         CS1_0();
    ! W% d7 d" L- r$ Q% ]
  16.     }( L# ]: i9 m. I: g! M
  17.     else* o" d% N: D! o# ~
  18.     {        / [" z1 H2 u, o. e; j" ~2 }8 o
  19.         CS1_1();   
    + L7 B* q- [5 y! V; d$ M
  20.         bsp_SpiBusExit();    /* 释放SPI总线 */1 [6 y$ x/ e! E0 U
  21.     }    2 R: y6 k% O8 l( T" T' E
  22. }
    1 O: ~& K8 O9 X: A) w

  23. + _( z2 {* \5 L: a2 |- g$ X4 g
  24. /*
    % K$ X/ O1 i* J1 W9 l( f) V! K6 ?
  25. ********************************************************************************************************** L9 B) g) t+ t' G/ c7 r
  26. *    函 数 名: DAC8501_SetCS2(0)) N7 q, \$ K$ Q7 ~
  27. *    功能说明: 设置CS2。 用于运行中SPI共享。8 _& `6 x3 a+ |$ Y: I
  28. *    形    参: 无3 P! ^4 s2 Q) H, f. @+ x8 `* X
  29.     返 回 值: 无& p& D5 c; j( p" C- u7 \
  30. *********************************************************************************************************
    , K. n. V6 P' o8 y) A/ L
  31. */, w* o" H! I& S4 n, y0 O
  32. void DAC8501_SetCS2(uint8_t _level)
    ) z" i" p, }/ Y- C3 d3 g$ [
  33. {
    " q6 B. }* @8 p0 Q4 b; p
  34.     if (_level == 0)9 R: ], n1 c; B" J* w
  35.     {
    % C) u: u) a0 c( |/ y5 p: X$ C
  36.         bsp_SpiBusEnter();    /* 占用SPI总线  */: v1 c4 ]. \. ?( I3 M4 Z* J$ U
  37.         bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);        
    8 ?: `2 S; V) |# g
  38.         CS2_0();
    : S# g0 ^+ j& ^
  39.     }
    - J0 i3 [: x4 B: I  b/ f
  40.     else* ]/ u, J9 |0 I# h
  41.     {
    : T# a- N/ B9 m
  42.         CS2_1();* V$ j, r: B1 I# ?" v( y0 g
  43.         bsp_SpiBusExit();    /* 释放SPI总线 *// v' Q2 @' g4 J# O* ?
  44.     }6 _# _8 q, L9 t+ |1 {1 @7 v3 l& n: G- m
  45. }
复制代码

% a- }/ g7 P$ @( L通过这种方式就有效的解决了单SPI接口管理多设备的问题。因为给每个设备都配了一个独立的片选引脚,这样就可以为每个设备都配置这么一个片选配置。* a' J' F2 ?/ k' d+ t

2 B. D7 H* {! d% [% a: X但是频繁配置也比较繁琐,所以函数bsp_InitSPIParam里面做了特别处理。当前配置与之前配置相同的情况下无需重复配置。
/ N  r- l* \+ S# g4 z" A: q' z! ?& O7 H9 o  E0 \
75.5.5 第5步:DAC8501的数据更新
4 z% ]2 f- t8 k8 t  w! q8 ?DAC8501的双通道数据更新通过下面的函数实现:& F) W! X- o$ Z6 q- m! o6 d
. I9 ]- M: \' D4 ]8 Z
  1. /*& }; V6 O; s0 x( R+ o
  2. *********************************************************************************************************3 b: S+ Z- s# U" q
  3. *    函 数 名: DAC8501_SetDacData8 j4 w; c& E6 N3 r: ^: M
  4. *    功能说明: 设置DAC数据
    * x( m( y  Z3 p: Q9 j
  5. *    形    参: _ch, 通道,9 o( j  n9 ^' D& w2 f; q9 L
  6. *             _data : 数据# y+ n: j2 F8 d" ~2 \- x
  7. *    返 回 值: 无7 `  h1 U8 c1 C) d  _
  8. *********************************************************************************************************" Q. \. |2 H6 z) v
  9. */
    8 |* R3 T" u% y9 @3 I1 g
  10. void DAC8501_SetDacData(uint8_t _ch, uint16_t _dac)
    ' `3 ]& L5 J6 n# l% R3 [7 F  L& @' T
  11. {. b- N' O6 A( T
  12.     uint32_t data;' m& G* U+ F3 g/ u- n2 ~

  13. 3 {# V7 C# `- W! {
  14.     /*" G3 f, k9 S( z# `
  15.         DAC8501.pdf page 12 有24bit定义
    / [) Z; j3 }  P

  16. $ }( [3 x9 H5 \) z
  17.         DB24:18 = xxxxx 保留
    1 [, _4 S! B9 A2 W9 r: ^
  18.         DB17: PD1
    0 `' g6 n* ?7 M# @6 ?# O
  19.         DB16: PD0
    ! z" Q: `5 D  X0 M" Z

  20.   r+ g- E% m2 c7 |7 M* Q6 [9 T
  21.         DB15:0  16位数据
    ) {$ u0 [5 P# \$ ~7 U3 W6 x" G

  22. & H7 P$ K9 o- U* S* T4 L7 R# F
  23.         其中 PD1 PD0 决定4种工作模式4 T2 V- z+ }2 p
  24.               0   0  ---> 正常工作模式2 Z- W$ J, B$ v4 a
  25.               0   1  ---> 输出接1K欧到GND9 V: D0 m* m5 X* m5 o& C0 a
  26.               1   0  ---> 输出100K欧到GND% w5 F1 E/ Q# G+ b& s& R- n2 v$ e
  27.               1   1  ---> 输出高阻* S- J9 P" H9 g) `8 h+ C
  28.     */
    : ?; R% r& L! S' P" L, _

  29. ( o% b0 K' B- y: m' C
  30.     data = _dac; /* PD1 PD0 = 00 正常模式 *// I" e7 W8 G# r$ r9 ?) j
  31. % h) |  ^* E# T8 {$ R7 c' z- Y
  32.     if (_ch == 0)3 ^% |4 c& t8 r9 R0 D
  33.     {
    ( C: Y+ H- L/ x$ R* m5 O
  34.         DAC8501_SetCS1(0);
    . o+ |6 w  n) ]3 d
  35.     }- t3 a. ]: \6 u# X  G  B. N: P
  36.     else
    1 \# v# B. P4 V! |2 |' W0 {
  37.     {
    6 R) ^6 v/ a  w- u/ z( Y
  38.         DAC8501_SetCS2(0);- ]4 s0 J, `9 X; t: s8 u4 c
  39.     }
    , |3 l7 x( _0 Y4 v

  40. . A; ]- g( s6 n5 g
  41.     /* DAC8501 SCLK时钟高达30M,因此可以不延迟 */
    ( ^" f: z! j2 S
  42.     g_spiLen = 0;
    2 N6 V5 _* A/ I8 u! d& C( \
  43.     g_spiTxBuf[g_spiLen++] = (data >> 16);
    & y; e* W- l/ I7 \" P8 k
  44.     g_spiTxBuf[g_spiLen++] = (data >> 8);
    ' y, {- l2 C/ L# A7 c4 F& ]
  45.     g_spiTxBuf[g_spiLen++] = (data);2 k+ s" s8 {2 i& m4 ]
  46.     bsp_spiTransfer();   
    , ~& C- c8 d5 [4 b+ X

  47. % K/ J* K7 ~4 k- S2 @! p  j) {2 p
  48.     if (_ch == 0). [8 I9 }& o2 {
  49.     {
    % C" w$ n5 R+ ^  t3 I/ M( W, l* X
  50.         DAC8501_SetCS1(1);- S, U! }6 E3 ]  r$ r% z$ d
  51.     }) ]- t( E! a/ T. r5 U# ?
  52.     else
    2 k* U  H3 M( f9 C8 G" ^; T
  53.     {
    ) E3 X* l9 o; w; i! i
  54.         DAC8501_SetCS2(1);
    7 `; u5 X8 z1 ]- l+ o9 o
  55.     }$ a& s+ ~% l" _+ `+ x
  56. }
复制代码
. S/ U$ G& a/ A) W
函数实现比较简单,每次更新发送24bit数据即可。
6 Y  I) n  I- i6 t# {/ E
, _4 C) f9 o; M  ]# L4 Y75.6 DAC8501驱动设计(SPI DMA更新方式)/ r  K& [- |. d! m' ]6 ]1 S, y3 r) ^
DAC8501的DMA驱动方式略复杂,跟中断更新方式完全不同,要使用硬件SPI1 NSS引脚驱动DAC8501的片选,所有专门做了一个驱动文件来实现,程序驱动框架设计如下:+ I( F  e' S: P/ M

+ }1 t$ t, r, E  X" S8 C
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
. Q( w4 ]3 a% T0 \
8 p( C1 A, B: M( F
有了这个框图,程序设计就比较好理解了。
- i' I9 F* \6 U7 W& r  D+ U; c- I3 U
75.6.1 第1步:SPI总线配置
, Q$ ]/ j; T9 p- kspi总线配置通过如下两个函数实现:
( h# e' e+ d; f4 C1 C. _3 A6 I- K
  1. /*
    * `. O1 a4 s8 _0 [
  2. *********************************************************************************************************+ w$ ~1 p4 I) i% l; V( X
  3. *    函 数 名: bsp_InitDAC85010 _  M  U, w) z7 W$ S
  4. *    功能说明: 配置GPIO并初始化DAC8501寄存器+ R5 Z% {% G0 s, l
  5. *    形    参: 无
    1 q9 [( X0 f+ o: q8 y8 G" s( t
  6. *    返 回 值: 无
    ; J- ?) y, a7 @  x
  7. *********************************************************************************************************
    1 D8 R0 B$ ?3 A( _8 |
  8. */( @$ t# v' W4 K
  9. void bsp_InitDAC8501(void)# L* ?: o" g+ M+ Q" s, |; G" }
  10. {8 z6 X& E% N% ~
  11.     s_SpiDmaMode = 0;  
    , W* d) v0 f/ ]7 ?6 z0 d) u

  12. 0 g; ]1 z2 i4 C) k/ R! x# W
  13.     /*##-1- 配置SPI DMA ############################################################*/
    5 @' u. `7 L9 W# \8 E' x
  14.     bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);6 Z) I8 i2 X6 w# a, h

  15. : j4 ~4 a% Y. i: w
  16.     /*##-2- 默认输出0V ############################################################*/- {9 i4 S6 M7 f. G/ K1 t
  17.     DAC8501_SetDacData(0, 0);    /* CH1输出0 */
    ! G! J- G# }) n3 f9 N
  18. }
    & W3 D2 h8 o. Q! D( H' {5 D, {

  19. ! l$ E, }6 s; z9 T; M
  20. /*
    8 F) u3 r1 S* c3 Z$ O* {
  21. *********************************************************************************************************8 |0 j( H/ p! N, \
  22. *    函 数 名: bsp_InitSPIParam* X7 ^8 n& u6 [& f& `5 w3 ], h7 T8 ?
  23. *    功能说明: 配置SPI总线参数,时钟分频,时钟相位和时钟极性。
    0 b0 M, ?" v0 c( g
  24. *    形    参: _BaudRatePrescaler  SPI总线时钟分频设置,支持的参数如下:" q: m; s4 e& P
  25. *                                 SPI_BAUDRATEPRESCALER_2    2分频$ [; _! j  \+ ?2 L3 A( W" R/ b
  26. *                                 SPI_BAUDRATEPRESCALER_4    4分频
    / F1 V$ {3 D0 `2 I, G
  27. *                                 SPI_BAUDRATEPRESCALER_8    8分频
    . |8 P8 u+ q+ \  R' y5 Y9 H4 C
  28. *                                 SPI_BAUDRATEPRESCALER_16   16分频& |0 T) B1 J* y& g4 a
  29. *                                 SPI_BAUDRATEPRESCALER_32   32分频
    # ?5 ?7 F! a3 f+ P" A
  30. *                                 SPI_BAUDRATEPRESCALER_64   64分频8 U' d+ ]8 I+ B9 m" Z, y( q
  31. *                                 SPI_BAUDRATEPRESCALER_128  128分频) W! U% O; N  z: K) F: ~
  32. *                                 SPI_BAUDRATEPRESCALER_256  256分频! L' b, @7 y+ r1 q2 ^
  33. *                                                        
    + G  c, U2 s& j% b4 \- `
  34. *             _CLKPhase           时钟相位,支持的参数如下:5 i/ X. F7 T: R8 n# i
  35. *                                 SPI_PHASE_1EDGE     SCK引脚的第1个边沿捕获传输的第1个数据
    ' j+ H% s' H2 d0 s% S0 {
  36. *                                 SPI_PHASE_2EDGE     SCK引脚的第2个边沿捕获传输的第1个数据
    ; V0 Z7 E; S& q& }# A6 J
  37. *                                 
    $ }, ~4 Z+ e$ L8 I, y
  38. *             _CLKPolarity        时钟极性,支持的参数如下:% g4 k# R$ b9 o. q8 @, y  N
  39. *                                 SPI_POLARITY_LOW    SCK引脚在空闲状态处于低电平: Z! a2 {/ j* X0 G2 I4 y
  40. *                                 SPI_POLARITY_HIGH   SCK引脚在空闲状态处于高电平
    ) Z; t8 b+ a9 w8 D/ T' o& _
  41. *1 \. H  H8 |( }+ D9 ?2 ~
  42. *    返 回 值: 无5 h( w* P% s, `- c3 `4 k6 b: |
  43. *********************************************************************************************************
    " ]7 i  ?5 D* Q
  44. */
    1 A4 L/ z8 k  Z6 o4 J
  45. void bsp_InitSPIParam(uint32_t _BaudRatePrescaler, uint32_t _CLKPhase, uint32_t _CLKPolarity)
      f9 l: d1 s- V( e$ T$ W6 P
  46. {' N6 _- M! Z9 N7 G( M6 K7 r

  47. ) b5 z9 w( I: f$ f9 \  b! S; ?- [
  48.     /* 设置SPI参数 */
    % \& D" @8 {7 ~" |
  49.     hspi.Instance               = SPIx;                           /* 例化SPI */4 z/ m+ ~" P( S; N" w
  50.     hspi.Init.BaudRatePrescaler = _BaudRatePrescaler;             /* 设置波特率 */
    ! b% ~/ n% R+ t! p/ u
  51.     hspi.Init.Direction         = SPI_DIRECTION_2LINES_TXONLY;  /* 全双工 */# \* I+ c/ [. O3 u; Y
  52.     hspi.Init.CLKPhase          = _CLKPhase;                     /* 配置时钟相位 */5 K7 A4 _" {) S5 H9 M9 |) H+ _! J
  53.     hspi.Init.CLKPolarity       = _CLKPolarity;                   /* 配置时钟极性 */
    6 u. B9 e! B2 G7 H- y# G6 c7 p5 C/ _
  54.     hspi.Init.DataSize          = SPI_DATASIZE_24BIT;               /* 设置数据宽度 */
    + ~% F# R6 V( ]; o
  55.     hspi.Init.FirstBit          = SPI_FIRSTBIT_MSB;             /* 数据传输先传高位 */
    , `) f: ?! r/ A; x
  56.     hspi.Init.TIMode            = SPI_TIMODE_DISABLE;             /* 禁止TI模式  */0 b  B& {' \( k. U' e3 t! f6 ^
  57.     hspi.Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE;     /* 禁止CRC */
    " A. X& V( B- H0 A' |; V. r
  58.     hspi.Init.CRCPolynomial     = 7;                               /* 禁止CRC后,此位无效 */
    * h8 W. m+ Z- u0 ~
  59.     hspi.Init.CRCLength         = SPI_CRC_LENGTH_8BIT;             /* 禁止CRC后,此位无效 */2 F1 F) M8 u; m1 ^8 f
  60.     hspi.Init.FifoThreshold     = SPI_FIFO_THRESHOLD_05DATA;    /* 设置FIFO大小是一个数据项 */' u8 j/ d5 j' l, }% k
  61. 4 e9 R$ t( ^! U0 W2 q6 Z
  62.     hspi.Init.NSS         = SPI_NSS_HARD_OUTPUT;                 /* 使用软件方式管理片选引脚 */
    9 E  ^- ~) a  k( l
  63.     hspi.Init.NSSPMode    = SPI_NSS_PULSE_ENABLE;                /* 使能脉冲输出 */
    7 u  d* @$ M, q$ w/ W
  64.     hspi.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;               /* 低电平有效 */
    ' P4 `  |' J1 t9 {& ~& _

  65. % N% k7 _2 A. R- _* C
  66. /* MSS, 插入到NSS有效边沿和第一个数据开始之间的额外延迟,单位SPI时钟周期个数 */
    4 Q( w- V* ?( z' E; u# b
  67.     hspi.Init.MasterSSIdleness        = SPI_MASTER_SS_IDLENESS_00CYCLE;   
    ( g/ q7 r9 R/ N" Q4 V% M* U

  68. 0 V2 x. T# R+ |
  69. /* MIDI, 两个连续数据帧之间插入的最小时间延迟,单位SPI时钟周期个数 */9 v& c! \/ ~; W3 ~/ U
  70.     hspi.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_02CYCLE; ; j2 }0 L  u2 Z. a

  71.   F1 q5 w7 H& C) N
  72.     hspi.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE; /* 禁止SPI后,SPI相关引脚保持当前状态 */  8 T4 ^2 z- [# x5 G2 g4 Y+ F
  73.     hspi.Init.Mode            = SPI_MODE_MASTER;                    /* SPI工作在主控模式 */
    / }7 t' |  i2 U* N; I1 {! t
  74. % Z) t2 N. r# f' m1 z  W
  75.     /* 复位配置 */
    1 r+ n" ?# |/ k( y. w  t3 ?, J% _. |
  76.     if (HAL_SPI_DeInit(&hspi) != HAL_OK)
    2 h7 C5 [9 ~5 n( O" h
  77.     {
    9 g5 z1 ^3 z. m" U2 c* }
  78.         Error_Handler(__FILE__, __LINE__);( w! o, Q5 n1 y
  79.     }   
    ( i* ]0 ^0 |9 B$ A& W7 h

  80. 4 \8 V3 b8 Z/ S# V2 B
  81.     /* 初始化配置 */- V: R) y2 k5 G9 `- e
  82.     if (HAL_SPI_Init(&hspi) != HAL_OK)
    4 v* o* p9 E+ h/ e, e9 b" L
  83.     {/ l8 ^+ I# q! y; K; S3 }
  84.         Error_Handler(__FILE__, __LINE__);
    / [" L- Z) O. F4 ^6 f3 q& _4 t7 D
  85.     }    ; [" j' W* \$ a5 w. f
  86. }
复制代码
% e+ K! Z% f+ X9 b( N( G! G2 |
1 O9 I$ w5 a- s
. J' r9 r( n$ p! m& @5 k
这两个配置函数里面最重要的是置红的几个配置选项,这里依次为大家做个说明:& D* M* W0 E8 z. D
& T9 {% J! M8 l2 P3 A8 v
  SPI_DIRECTION_2LINES_TXONLY
) T" Z# a% {/ P- N- b$ K8 S驱动DAC856X仅需要SPI写操作。* |7 e' Y8 t# _. l$ N+ g7 [
/ S/ Z+ U0 j2 O: z* B+ T" i- l& k6 w
  SPI_DATASIZE_24BIT
  s; g1 g" T6 c; xSTM32H7的SPI支持4-32bit数据传输,由于DAC856X需要24bit数据,所以这里配置为24即可。  U; a/ H# D- |8 n6 O
) r0 l4 M" q" L# I
  SPI_FIFO_THRESHOLD_05DATA
) M7 W$ x" g- Z7 |; J. K8 A对于SPI1来说,里面的FIFO大小是16字节,那么SPI数据传输配置为24bit的话,FIFO最多可以存储5个24bit,因此这个fifo阀值要设置为5。  O9 A' F( g% d" K6 J

6 K, d8 ~4 n  ?+ i# `  SPI_NSS_HARD_OUTPUT
; ~4 s# @: b# K- X我们这里要使用SPI的硬件片选引脚SPI_NSS。0 Y  C( l) K( W: C! Q1 r  Z

- B# C% |+ l" l  ~6 h. M  SPI_MASTER_SS_IDLENESS_00CYCLE
1 R" g2 E4 A; U! I7 l: l$ ?5 c% B: t插入到NSS有效边沿和第一个数据开始之间的额外延迟,单位SPI时钟周期个数。; a7 k: f, W& ^) u  A3 i
, }' z* ~; a3 w4 @! E# S- I
根据本章75.4.4小节里面的t(4)要求,片选有效到SCLK第1个下降沿信号的时间,最小值为0。所以这里配置为0即可,也就是无需插入时间。4 ]7 T4 V3 ~; S( A
: d2 F6 ?) u( n! f0 I0 S" v
  SPI_MASTER_INTERDATA_IDLENESS_10CYCLE- ?& I" [: K5 w7 e, m
两个连续数据帧之间插入的最小时间延迟,单位SPI时钟周期个数。. {) h8 B& P3 F. F: `

% ~. n& y! z# P" O0 N$ T; i( `* q6 N) }根据本章75.4.4小节里面的t(5)要求,每传输24bit数据后,片选要保持一段时间的高电平,DAC856X要求至少要33ns(供电3.6到5.5V时),也是说,如果我们以25MHz驱动DAC856X,这里至少要配置为1个时钟周期,推荐值为2及其以上即可,我们这里直接配置为2个时钟周期(配置为1也没问题的)。+ D" d5 H% d( F, N* _& V
" V7 p- ~* x1 S
75.6.2 第2步:TIM12周期性触发配置' e9 t$ Y, M) ?) w
这里特别注意一点,定时器触发一次,就会让SPI以DMA方式传输24bit输出。- ^: f4 k% P+ U& I
- E6 I8 M% R  ^* x2 l
TIM12的触发配置如下:# s& [4 w/ Y" ~+ a  x4 _5 ]4 ^
& n, B4 m1 y! [) e4 v
  1. /*
    6 _5 |- O9 d$ X* D( i1 X% H  i
  2. *********************************************************************************************************3 E( u9 a' j4 a7 c, ?1 H1 H
  3. *    函 数 名: TIM12_Config" r- c, J1 s; Q$ g& ?0 ^
  4. *    功能说明: 配置TIM12,用于触发DMAMUX的请求发生器
    & c, E" y/ o# `! m! w
  5. *    形    参: _ulFreq  触发频率,推荐范围100Hz - 1MHz                              ( @! \  s& n8 {  @9 k% a
  6. *    返 回 值: 无
    . m; @3 F+ ~* L/ Q; \4 R6 \
  7. *********************************************************************************************************, q3 a6 J/ F7 `5 `
  8. */   1 Q( ]& }1 y: y' v, [, h& {
  9. TIM_HandleTypeDef  htim ={0};2 O% Z, d6 u4 h! N; i4 s5 K
  10. TIM_MasterConfigTypeDef sMasterConfig = {0};
    * g) r2 I. x! \/ D) i- e) p
  11. TIM_OC_InitTypeDef sConfig = {0};2 p- C8 D, {% G' L9 o( t; Y
  12. void TIM12_Config(uint32_t _ulFreq)2 t7 S$ @6 U8 H' c+ m; g8 I" x9 ?
  13. {2 x; a  Z8 h/ w# Q, ^
  14.     uint16_t usPeriod;  e2 I+ J1 G8 R# z- C! z+ U  q" f
  15.     uint16_t usPrescaler;% `+ S( @' b+ {+ i: r. D
  16.     uint32_t uiTIMxCLK;# k  h' d" k! V7 J( m& X( e

  17. " ]) e! {$ x% f4 Y% C
  18. % M* m# M3 J/ x7 W  B: k8 q
  19.       /* 使能时钟 */  , S7 e4 t& S6 V* t/ Z/ j0 ~
  20.       __HAL_RCC_TIM12_CLK_ENABLE();! h- X# U9 R3 D9 h

  21. : ], ~$ j8 f  K) Z2 G3 H1 X7 s
  22.     /*-----------------------------------------------------------------------) g9 }+ y( A, e! V) [, Q
  23.         bsp.c 文件中 void SystemClock_Config(void) 函数对时钟的配置如下:
    & B( a, X1 A  v0 Q  e5 t
  24. $ }0 o3 Q: \/ {9 [/ W( I2 {% I8 P+ T" L
  25.         System Clock source       = PLL (HSE)5 b9 z# w5 u% N+ P0 j9 R5 R" N
  26.         SYSCLK(Hz)                = 400000000 (CPU Clock)
    , r2 o$ X' V" U3 O$ g4 Y  m9 _2 F
  27.         HCLK(Hz)                  = 200000000 (AXI and AHBs Clock)
    # f" M1 g' k& c1 n
  28.         AHB Prescaler             = 2
    4 ~4 M. s( \' d3 U0 J1 p2 M
  29.         D1 APB3 Prescaler         = 2 (APB3 Clock  100MHz)
      G" r7 Q$ t, n
  30.         D2 APB1 Prescaler         = 2 (APB1 Clock  100MHz): _8 c1 q' w0 q  a3 m. A
  31.         D2 APB2 Prescaler         = 2 (APB2 Clock  100MHz)5 t- [8 \  W; t, r& d5 U, y
  32.         D3 APB4 Prescaler         = 2 (APB4 Clock  100MHz)3 E8 q! _* c/ B9 l" @. E) p5 y7 _
  33. ; S$ o; D# _+ ]! J0 [
  34.         因为APB1 prescaler != 1, 所以 APB1上的TIMxCLK = APB1 x 2 = 200MHz; 不含这个总线下的LPTIM1; G+ ], O; K# O5 ~% z* `! @& W
  35.         因为APB2 prescaler != 1, 所以 APB2上的TIMxCLK = APB2 x 2 = 200MHz;
    ! U: {2 W' P1 c. i) ~# B- T: X3 _
  36.         APB4上面的TIMxCLK没有分频,所以就是100MHz;
    * R# X; k5 q) p" ~# g( E% n

  37. ) _; j! F' \- B# w0 E
  38.         APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14,LPTIM1
    , f  M' \0 ]& h; r/ ]
  39.         APB2 定时器有 TIM1, TIM8 , TIM15, TIM16,TIM17
    . N1 @- c1 z0 p) Q
  40.   \: C5 r# D" J& c6 ^  ?+ ~' p
  41.         APB4 定时器有 LPTIM2,LPTIM3,LPTIM4,LPTIM5
    * c, x$ W  Z' L0 j8 q' _
  42.     ----------------------------------------------------------------------- */" `, ]$ F7 N$ k9 {! X
  43.     uiTIMxCLK = SystemCoreClock / 2;& O5 S% Y7 }+ ]8 C" d) `! l& r
  44. ' \# _8 a4 v* n( g
  45.     if (_ulFreq < 100)
    & u  x* ~) i# O' R% {0 H
  46.     {
    - |: |$ x, M7 q* @6 E# `, g8 t
  47.         usPrescaler = 10000 - 1;                    /* 分频比 = 10000 */
    7 N4 |9 R+ F. s  x  t" C, A
  48.         usPeriod =  (uiTIMxCLK / 10000) / _ulFreq  - 1; /* 自动重装的值 */
    $ d# ]1 x2 J# i" T. I$ S
  49.     }
    ; j6 p2 ~6 Q2 B/ Y5 J0 |
  50.     else if (_ulFreq < 3000)- \4 d" p1 ~5 X& \$ N, G( Y
  51.     {
    ) |# U" A4 a- I! j! o" r5 g
  52.         usPrescaler = 100 - 1;                    /* 分频比 = 100 */
    . N8 s$ n) y( C8 j; N- @2 F
  53.         usPeriod =  (uiTIMxCLK / 100) / _ulFreq  - 1;/* 自动重装的值 */" m& a5 L' x8 y, J; N% |7 M
  54.     }
    / t: g& z$ c1 V; k7 {
  55.     else    /* 大于4K的频率,无需分频 */- F( h& D" ^# s8 f
  56.     {" V' m' a# |2 |) b
  57.         usPrescaler = 0;                    /* 分频比 = 1 */4 V+ r# L$ u  ~( R8 \) I( S
  58.         usPeriod = uiTIMxCLK / _ulFreq - 1;    /* 自动重装的值 */) B% H8 F3 e' T9 X: Z3 v. z- Q* C
  59.     }& h* J$ ~3 W, Q2 p% t) u

  60. 0 L9 s, ?8 t! c1 w
  61.     htim.Instance = TIM12;
    ( L5 o3 k) o1 h# j# w2 {
  62.     htim.Init.Period            = usPeriod;# v. L8 h! q: p: O) x2 {
  63.     htim.Init.Prescaler         = usPrescaler;
      O2 f# h  G3 j) F( G6 O
  64.     htim.Init.ClockDivision     = 0;" H7 v% I3 {- R9 l3 l0 o: r
  65.     htim.Init.CounterMode       = TIM_COUNTERMODE_UP;. B& V0 ?0 f: ~; v* k
  66.     htim.Init.RepetitionCounter = 0;* f* _6 D" D# N

  67. 4 K- x& J& [8 I) m; I  _( D
  68.     if(HAL_TIM_Base_DeInit(&htim) != HAL_OK)' {8 F# B' h+ x1 Y2 O; k. d
  69.     {
    9 e: |. ]* a. V7 n+ f
  70.         Error_Handler(__FILE__, __LINE__);        " E2 t% h; j2 h
  71.     }
    , Q$ O" ?( a' R) U9 z, [- E: b- o
  72. # `& V$ H+ e3 X$ q/ c9 o
  73.     if(HAL_TIM_Base_Init(&htim) != HAL_OK)- d& \" u$ d  }# e% Y) t7 p' B
  74.     {
    $ s( }# R7 ]) q5 C' [# R3 J
  75.         Error_Handler(__FILE__, __LINE__);        
    ! b0 }$ |5 Z* |& x
  76.     }0 m! H& V. `- a  c& O' c
  77. " d; w0 D, {1 Y4 ?
  78.     sConfig.OCMode     = TIM_OCMODE_PWM1;
    / F2 c* ]! @9 c2 \
  79.     sConfig.OCPolarity = TIM_OCPOLARITY_LOW;
    " f! e- ~; V9 s2 Q" e% u/ \1 X8 E2 d
  80.     sConfig.Pulse = usPeriod / 2;     /* 占空比50% */
    5 o1 _& y' \; l0 w3 ^
  81.     if(HAL_TIM_OC_ConfigChannel(&htim, &sConfig, TIM_CHANNEL_1) != HAL_OK)
    3 d$ h! O& u/ R$ X# s; P4 I
  82.     {% n% W3 ^! h% [$ ], E$ x' P1 D
  83.         Error_Handler(__FILE__, __LINE__);
    ' u% S1 e7 G6 f
  84.     }! M8 \; P9 p) B6 _. l7 f
  85. ; t* u4 D8 x* H9 G3 \0 [
  86.     /* 启动OC1 */
    $ ~! b1 C+ C, K% G' f
  87.     if(HAL_TIM_OC_Start(&htim, TIM_CHANNEL_1) != HAL_OK)
    / C  @. w! p) M8 V& p/ ]
  88.     {
    2 L& N9 s- u( b9 l( k1 j5 P
  89.         Error_Handler(__FILE__, __LINE__);- f0 X! q  }& J" x
  90.     }
    6 e/ h- E  j, A1 w' x
  91. 0 Q& E- U* e4 G& Y$ I
  92.     /* TIM12的TRGO用于触发DMAMUX的请求发生器 */
    4 ]2 k3 @% F  L7 ~$ n# b8 X
  93.     sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1REF;; x. T5 ?$ s0 [$ N9 _
  94.     sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
    6 v# C; K; d0 g
  95.     sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    . _  u8 l, K& l. C. R. E+ Q& N

  96. $ G; f! F& }3 p; y
  97.     HAL_TIMEx_MasterConfigSynchronization(&htim, &sMasterConfig);* ?! C  |2 B& ~8 m" u/ z$ W
  98. }  `, c$ X$ y/ r, G1 m0 d, a7 i  `
  99. #endif
    2 _. V( x8 T; O8 C, Z+ }% f, I+ `
复制代码
( S' F7 I8 ?$ i- G
3 a$ o0 o! U4 ~$ v: s
这个函数支持的触发频率很宽,对于DAC856X来说,如果样本点设置为100个的话,此函数推荐的触发频率是100Hz到1MHz,具体可以支持到最高触发速度计算看本章4.7.7小节即可。% N. Q5 l& w+ m. F1 x

2 p( t5 s7 o, p; [9 q75.6.3 第3步:DMAMUX同步触发SPI DMA传输
8 I5 \' h: ^& E# a6 {DMA和DMAMUX的配置如下:
" Q9 }! F4 @4 O; C& r0 N+ Z# [, z- x
  1. /*
    ; p( {1 V; L; Q( K
  2. *********************************************************************************************************
    : G7 V$ R0 V# i; y3 F& ]
  3. *    函 数 名: bsp_spiDamStart
    / r; s& b# e5 v- D( c
  4. *    功能说明: 启动SPI DMA传输( @# w$ C/ P9 Z3 @" d4 a6 g
  5. *    形    参: _ulFreq 范围推荐100Hz-1MHz5 B9 f, m- b: j7 {8 k
  6. *    返 回 值: 无5 L8 e6 @" t0 V% ^, y6 {
  7. *********************************************************************************************************
    4 r4 m7 U; z3 k2 E8 t2 K
  8. */. S5 T0 M! E3 v
  9. void bsp_spiDamStart(uint32_t _ulFreq). |+ Q5 @) v# ~3 k
  10. {- S9 `. ^( N3 L8 l$ `& K
  11.     /* 设置模式,要切换到DMA CIRCULAR模式 */+ u. c; H2 G+ x* J6 O6 `
  12.     s_SpiDmaMode = 1;7 _1 M& }4 t+ V3 C  m' ?/ A1 Z

  13. 5 j7 |% d# f% E0 k4 e8 [8 g& j; n
  14.     bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_4, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);) ~% m  i- e3 J" j! C
  15. 2 L; _+ |) r6 [6 a
  16.     /* 使能DMA时钟 */
    6 ~, K! ?, l6 J; ?/ B/ v' ]
  17.     DMAx_CLK_ENABLE();      ' A9 L1 I  l4 C  j. Z& O) y+ W
  18. / J7 n5 U0 e5 K6 T
  19.     /* SPI DMA发送配置 */        2 o- o6 k: y! p( ]( |6 q
  20.     hdma_tx.Instance                 = SPIx_TX_DMA_STREAM;      /* 例化使用的DMA数据流 */' p7 ]5 x, S1 D3 k
  21.     hdma_tx.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;     /* 使能FIFO */4 v* J& W+ h% F0 V* O7 ]" Y% p5 d
  22.     hdma_tx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL; /* 用于设置阀值 */
    ) V7 y9 s' [1 Y" e% j' F/ C
  23.     hdma_tx.Init.MemBurst            = DMA_MBURST_SINGLE;        /* 用于存储器突发 */8 @1 {% w6 l; C
  24.     hdma_tx.Init.PeriphBurst         = DMA_PBURST_SINGLE;        /* 用于外设突发 */
    " K6 g. Y) Y, h1 t
  25.     hdma_tx.Init.Request             = SPIx_TX_DMA_REQUEST;     /* 请求类型 */  
    8 l. T. ^* ]& R7 M) ?) R
  26.     hdma_tx.Init.Direction           = DMA_MEMORY_TO_PERIPH;    /* 传输方向是从存储器到外设 */  
    ; l! `( O+ j, ~# D8 ~
  27.     hdma_tx.Init.PeriphInc           = DMA_PINC_DISABLE;        /* 外设地址自增禁止 */ . a0 V: F- T, c, I! m
  28.     hdma_tx.Init.MemInc              = DMA_MINC_ENABLE;         /* 存储器地址自增使能 */  + H; X9 ^, B* o# s8 a
  29.     hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;     /* 外设数据传输位宽选择字节,即8bit */ 9 s! ~7 F- b9 a
  30.     hdma_tx.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;     /* 存储器数据传输位宽选择字节,即8bit */    2 Z4 |7 Y) B0 D
  31.     hdma_tx.Init.Mode                = DMA_CIRCULAR;            /* 正常模式 */4 T/ w; [% w0 D" ]
  32.     hdma_tx.Init.Priority            = DMA_PRIORITY_LOW;        /* 优先级低 */, r$ [7 A! P& H- U% B3 `2 P: Y/ ~

  33. ! @2 y! Q1 g" [; t% g, L
  34.      /* 复位DMA */. n3 k% y" d4 m0 Q8 v: P
  35.     if(HAL_DMA_DeInit(&hdma_tx) != HAL_OK)/ o9 M& U% e! Z# e% m3 n
  36.     {/ I6 R. k/ z/ d6 p& G. G5 l3 }! T
  37.         Error_Handler(__FILE__, __LINE__);     ! i# Y5 e0 f  T. x2 s& K& Z, v  \
  38.     }
    5 c9 v' ?9 z* S7 d% T
  39. - S: N* ^$ `3 S& {$ t8 e5 s
  40.      /* 初始化DMA */
    * P* n8 Y& ~7 T
  41.     if(HAL_DMA_Init(&hdma_tx) != HAL_OK)
    2 n' t+ p- q3 s2 r
  42.     {
    4 ^2 U0 T% g& m/ v/ q
  43.         Error_Handler(__FILE__, __LINE__);     7 B( v8 h2 C, p
  44.     }
    + B0 b8 X1 W  W$ T  L- s* g

  45. % }. N6 @& M) M, [' b+ g: i2 d
  46.     /* 关联DMA句柄到SPI */  x% x9 s+ n/ J" ?
  47.     __HAL_LINKDMA(&hspi, hdmatx, hdma_tx);    : Q( K: n, I) m  J2 _7 a

  48. - ^5 r2 y' R' c4 I1 C

  49. 1 D( B4 f; s+ }8 X& v0 b
  50.     /* 关闭DMA发送中断 */
    " X) V6 z+ }% F4 P! K* W
  51.     HAL_NVIC_SetPriority(SPIx_DMA_TX_IRQn, 1, 0);
    $ u% B+ f9 l2 i; `$ D- [. u
  52.     HAL_NVIC_DisableIRQ(SPIx_DMA_TX_IRQn);
    9 E9 F: }; T$ n% |. `
  53. 7 `* w. V! x( R& d
  54.     /* 关闭SPI中断 */
    6 b; \& s: _9 _
  55.     HAL_NVIC_SetPriority(SPIx_IRQn, 1, 0);
    : ~3 N& W0 ~0 _# [
  56.     HAL_NVIC_DisableIRQ(SPIx_IRQn);
    3 i2 r5 q' y$ g1 y
  57. ' Z* Y3 q5 Z* k4 ~
  58.     /* 同步触发配置 */
    ( r) ~& O6 }+ @- X
  59.     dmamux_syncParams.EventEnable   = ENABLE;                             
    0 v3 l# R- |- X( H" ]: O) a8 `
  60.     dmamux_syncParams.SyncPolarity  = HAL_DMAMUX_SYNC_RISING;          5 T1 @  N5 l' l9 E6 f3 Q5 B
  61.     dmamux_syncParams.RequestNumber = 1;                  
      C. g4 @. ^% v- k0 M- O
  62.     dmamux_syncParams.SyncSignalID  = HAL_DMAMUX1_SYNC_TIM12_TRGO;
      N4 j0 h2 t6 f8 O
  63.     dmamux_syncParams.SyncEnable    = ENABLE;   
    ! [  T4 {: L2 t$ y% p" x& E. o
  64. ( p8 i( t6 P: H3 a) w4 `, B
  65.     HAL_DMAEx_ConfigMuxSync(&hdma_tx, &dmamux_syncParams);6 y' d' x& O7 a" u5 z/ s
  66. . L- V6 d- U8 ]
  67.     //LPTIM_Config(_ulFreq);
    * P, P. J6 I5 F& J. a

  68. 4 Z( X2 j+ }9 N+ ~
  69.     TIM12_Config(_ulFreq);
    4 z/ \- H% v5 B
  70. 9 d; z# M2 z  l- v2 h# x9 g* o8 T
  71.     /* 启动DMA传输 */
    ) O/ x! @9 @, b8 Y, r- l" |/ J: b. [
  72.     if(HAL_SPI_Transmit_DMA(&hspi, (uint8_t*)g_spiTxBuf, g_spiLen/4)!= HAL_OK)   
    . q% m* E8 J" h% h3 D; i
  73.     {
    4 k& b8 f, _( W" m" p+ L* h# D
  74.         Error_Handler(__FILE__, __LINE__);  t# [" h% `! ~1 X7 Y% X
  75.     }9 T4 I2 B" m+ l$ m7 W; Q
  76. }, X! y$ f9 D' {- t! b3 P
复制代码

1 O0 O9 z0 f* {5 i3 l
) {* ?0 v: R6 T4 H: G这段程序里面最关键的就是置红的部分。作用是配置DMAMUX的同步触发功能,触发周期由TIM12控制。% a7 z% I! h. H( f6 J# l
$ ]. e1 B; b& b
75.6.4 第4步:24bit数据的DMA传输解决办法" c# G5 x; V) I- l9 H
由于通用DMA1和DMA2仅支持8bit,16bit和32bit数据传输,我们这里要传输24bit数据,解决的关键就是配置DMA为传输宽度为32bit,并将传输的数据由24bit再补一个8bit的任意值组成32bit即可,实际的传输会由SPI完成。1 X0 }" Y9 u, N$ w* U: Z2 V  f- l

* }% q/ C# Z5 n  z
  1. /*
    2 {$ L5 w: ~1 R) S3 i) H+ M9 r
  2. *********************************************************************************************************8 a7 ?' y1 B! m  p+ D; m4 s: ]. E
  3. *    函 数 名: DAC8501_SetDacDataDMA  N2 v$ T4 K6 u
  4. *    功能说明: DAC8501数据发送,DMA方式! B( ~1 t+ D( O  w2 ~* A
  5. *    形    参: _ch         1表示通道1输出  \1 ?' f5 w  E; v) a7 g" I9 w# c
  6. *             _pbufch1    通道1数据缓冲地址
    5 ~" L7 k* q8 w. X  m2 b, O
  7. *             _sizech1    通道1数据大小& S2 y( m' x6 ]" F# F  K" t1 j
  8. *             _ulFreq     触发频率,推荐范围100Hz- 1MHz,注意这个参数是触发频率,并不是波形周期。
    ' t7 U1 B6 o. o# D( @  H
  9. *                         这里触发一次,SPI DMA传输一次24bit数据。$ s+ |5 W  T% ~8 W
  10. *    返 回 值: 无+ L5 K6 l0 H/ [4 |0 u9 C5 F) {8 A$ A& Q
  11. *********************************************************************************************************
    $ P# k* |# U2 ]! D, G
  12. */
    ' H8 p' N# G4 z6 g
  13. void DAC8501_SetDacDataDMA(uint8_t _ch, uint16_t *_pbufch1, uint32_t _sizech1, uint32_t _ulFreq)
    * _5 W2 H2 U2 F8 O7 a
  14. {
    ' q1 {8 c0 a  h* I! `
  15.     uint32_t i;6 k6 X1 l8 P+ g6 f, B* h
  16.     uint32_t _cmd;6 w6 z7 I) |0 W, f( P

  17. $ r& _. j9 Q! P. G$ M2 ~
  18.     g_spiLen = 0;* E- R$ v0 _5 G/ D5 c5 F) x
  19. ; r( m" r9 B7 `$ n- r" W% v
  20.     switch (_ch); h% X% u6 D/ c* V. L+ I
  21.     {
    ' f3 Q; W, S2 `5 t. m+ T/ V

  22. * c# _4 s0 z0 X7 e+ x3 }  d) U( q
  23.         /*
    ( G" ^' S  g: @* d; a- x, F8 d  u& [
  24.             DAC8501.pdf page 12 有24bit定义  u1 z/ q# u+ o( `/ i+ T" A! o

  25. 5 }4 w' W+ E7 z& T( H- p
  26.             DB24:18 = xxxxx 保留
    ( U" d2 O1 a, {  H; Y2 B
  27.             DB17: PD1* ^! e# ^. e: l6 H
  28.             DB16: PD0+ T$ ~, j9 V: e& z9 d- c( k

  29. 4 U3 h/ Z0 J; s$ c5 b3 A5 f# b
  30.             DB15:0  16位数据# |3 U3 H2 t/ V2 z0 t" Q  S) [2 F

  31. ; T! R' ]  x8 y) z( M  {3 P
  32.             其中 PD1 PD0 决定4种工作模式
    ) E7 C0 f, }( g% ?( v( K+ U& |
  33.                   0   0  ---> 正常工作模式
    ) }/ [; |& h! g% D& ^
  34.                   0   1  ---> 输出接1K欧到GND5 E& W* s1 ~& _0 A& W4 Z1 {% G1 q. B
  35.                   1   0  ---> 输出100K欧到GND- p! y" N" D% H) w( f$ s( S
  36.                   1   1  ---> 输出高阻
    ; b% j$ ]) w% b
  37.         */( C$ o( P, h* M$ d' d  o+ E" Y

  38. . Z& B1 z; w+ D% Z' l/ L) s
  39.         /* 通道1数据发送 */6 ~$ g" @+ p  R% ^
  40.         case 1:
    & w4 W; R+ w8 H/ D7 j
  41.             for(i = 0; i < _sizech1; i++)
      P! w! o0 R6 J0 C+ b% X
  42.             {
    2 p7 L) A; v  i
  43.                 /* 更新需要配置PD1和PD0,当前是选择的正常工作模式 */6 W2 l& g- }# L: Y' o+ x9 o/ H
  44.                 _cmd = (0 << 16) | (_pbufch1 << 0);
    2 w) Q; y% g/ s3 V* w
  45. 0 F- `+ x$ Y7 o  a
  46.                 g_spiTxBuf[g_spiLen++] = (uint8_t)(_cmd);1 |5 }$ L' G, R7 d/ e; j
  47.                 g_spiTxBuf[g_spiLen++] = (uint8_t)(_cmd >> 8);
    . x/ f; @+ P- L' }9 u* o4 p
  48.                 g_spiTxBuf[g_spiLen++] = (uint8_t)(_cmd >> 16);
    - J) x* d* D' I$ f; |
  49.                 g_spiTxBuf[g_spiLen++] = 0;% w( I) L! _+ h  A9 T7 W2 L1 X7 f
  50.             }0 V) m3 M; K3 J! D# {/ @
  51.             break;
      D4 U; y6 Z- L

  52. % L4 N3 c& M+ \% [: ?. }$ Z
  53.         default:% w5 W6 N! ^7 U# O) [$ O/ n
  54.             break;: E. {( p. Q) h) P0 W8 S6 f& g# m& q
  55. ! c3 e- z4 O1 X0 T9 q
  56.     }
    1 c; I8 S. o* I, F8 \
  57. 5 O% S( k5 n9 }% b) }: a# R0 ~1 e
  58.     bsp_spiDamStart(_ulFreq);
    ) y9 t1 B. u6 t( K/ y. G6 ^
  59. }
复制代码
' g5 _) ^: J* U% `1 ?5 {9 V
75.6.5 第5步:DMA缓冲区的MPU配置
. a4 b: A) {# H3 F. k8 p0 h因为工程是用的DTCM做的主RAM空间,这个空间无法使用通用DMA1和DMA2,通过本手册第26章的内存块超方便使用方式,将DMA缓冲定义到SRAM4上:) `; k# e2 B' H, s: a, k1 w
; C7 Q. E/ K% m6 h
  1. #if defined ( __CC_ARM )    /* MDK *******/
    $ j& d3 |9 ~' i5 A+ G
  2.     __attribute__((section (".RAM_D3"))) uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   
    ; O- _1 \% O# f5 N5 J! ^
  3.     __attribute__((section (".RAM_D3"))) uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];5 a" j$ y& A- Z0 ^3 A
  4. #elif defined (__ICCARM__)  /* IAR ********/
    : y9 j4 x- H! c8 L5 k5 ?
  5.     #pragma location = ".RAM_D3"
    8 x4 ?/ }& X1 L+ p9 j& o8 T
  6.     uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   4 V! P6 t- \) j! [$ Q
  7.     #pragma location = ".RAM_D3"8 S8 a$ v- g1 x/ ^
  8.     uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];
    ) N8 ]4 ^# L# a% t
  9. #endif
复制代码
5 U/ g- w+ q: F7 [) `$ l
由于程序里面开启了数据Cache,会造成DMA和CPU访问SRAM4数据不一致的问题,特此将SRAM4空间关闭Cache。
. F+ h7 S+ \1 U    /* 配置SRAM4的MPU属性为Non-cacheable *// a& Q% y4 @' Q* E+ Y* q4 c
  1.   MPU_InitStruct.Enable           = MPU_REGION_ENABLE;, `1 ?. Y9 a$ {  J' M5 i: l
  2.     MPU_InitStruct.BaseAddress      = 0x38000000;  P, G& r9 l# F
  3.     MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;+ p# z2 b. N- t! o% h( \. g! k- y
  4.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    5 [. v9 e+ t% T) N: c
  5.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
    ) K8 l7 w# q$ e& Z
  6.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;1 j6 U0 g+ V9 f
  7.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;1 d3 I0 h0 x* i+ @4 P
  8.     MPU_InitStruct.Number           = MPU_REGION_NUMBER2;; _: _' r* n2 K7 X7 s$ w5 h5 T& D8 o: q
  9.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    9 d  Y' M4 D$ K0 k' _! G! B1 X
  10.     MPU_InitStruct.SubRegionDisable = 0x00;
      U& d' C, h7 ~. _  g/ \" y
  11.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;3 O6 W0 {9 l0 w$ Y3 M+ _
  12. , k8 j& @4 b. ^+ |" ~
  13.     HAL_MPU_ConfigRegion(&MPU_InitStruct)
复制代码
由于程序里面开启了数据Cache,会造成DMA和CPU访问SRAM4数据不一致的问题,特此将SRAM4空间关闭Cache。
  q/ ~8 O9 ^* _. ?
& h# g; V( Z7 C8 d: m75.6.6 第6步:DAC8501的时钟极性和时钟相位配置
4 d$ R: q: ~- H7 z: N9 c) N注:与本章74.5.3小节内容是一样的。
5 f5 u" c1 O  J5 t! e4 O+ E( N* z1 i' L8 o4 S% V# B9 Q  P
首先回忆下STM32H7支持的4种时序配置。8 K) K. E+ K% e1 d+ C1 [7 B+ b+ I

# d" U- h" O8 J" n  当CPOL = 1, CPHA = 1时- i& D# A, W( f, F  o4 C
SCK引脚在空闲状态处于低电平,SCK引脚的第2个边沿捕获传输的第1个数据。
+ _0 }6 x- N" b# A/ s/ ~+ H: O/ s( A& N3 M$ k7 T
  当CPOL = 0, CPHA = 1时
5 \3 }% G! l8 A( r# m, uSCK引脚在空闲状态处于高电平,SCK引脚的第2个边沿捕获传输的第1个数据。
3 t$ S8 @4 T5 B) o/ M. W8 T
5 t( v7 c" F$ p6 W9 @+ p  当CPOL = 1, CPHA = 0时
; @# `$ l5 V# l8 jSCK引脚在空闲状态处于低电平,SCK引脚的第1个边沿捕获传输的第1个数据。( ?6 a% u/ {# R5 [0 t
& I6 R' I$ V; o7 }, m; Y) C7 n
  当CPOL = 0 ,CPHA= 0时
- J5 L4 i! r) j  C! x  s) q/ F3 _2 oSCK引脚在空闲状态处于高电平,SCK引脚的第1个边沿捕获传输的第1个数据。- A( m4 Q: v, B6 R: j

( l" Q7 H3 ~6 ]6 k2 c
# b) m" a: E& H6 _  [

7 p; S4 O3 K! v- n有了H7支持的时序配置,再来看下DAC8501的时序图:: F3 n2 y+ p4 y. I# {
* B9 g" J' L% P# \# {- u0 H' [& q
' g0 ?) G" T) j8 k* m1 t) Y
2 T) }! k2 T2 O3 O* O3 ~
首先DAC8501是下降升沿做数据采集,所以STM32H7的可选的配置就是:0 S/ h! o  f' p6 Q& m
8 t  @2 A  \4 Y0 J7 ?8 |
CHOL = 0,  CPHA = 1
7 `" T* }* }9 p+ |. \$ b3 x  h3 Y+ f" f6 @# l. N0 y5 P  b
CHOL = 1,  CPHA = 0) O& m5 ]* |% Y" ]( \
: N2 g! p6 g- O$ W) f/ ?
对于这两种情况的主要区别是空闲状态下SCLK时钟选择高电平还是低电平,根据上面的时序图和DAC8501的数据手册,两种情况下都可以正常运行。经过实际测试,STM32H7使用这两个配置确实都可以正常运行。程序里面默认是选择CHOL = 0,  CPHA = 1。2 M* S- p% B  y: h: n- c

" h/ m6 v# P( n% x3 L: @75.6.7 第7步:DAC8501的最高更新速度计算
; \6 f7 j& |" K这里特别注意一点,定时器触发一次,就会让SPI以DMA方式传输24bit数据。
! ?* L$ o1 c& J5 D9 [
7 f% S9 S$ j3 A3 W$ ~6 I8 Q$ X配置条件:
4 O' _. _3 N7 A- t+ l
  Z# r. D& x  P' d! p+ s* {, G  SPI时钟是25MHz,SPI数据传为24bit,每个bit需要时间40ns。
+ l9 V% b% I% n* F5 ^2 p& Y  hspi.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE. h! C/ v' D/ i1 Y( l) N% l
插入到NSS有效边沿和第一个数据开始之间的额外延迟,单位SPI时钟周期个数,即40ns。& h' T) _9 C8 I

7 F$ ?- Z8 y* a  S# w$ R" S& {  hspi.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_10CYCLE  T6 q+ j- q) f! n
两个连续数据帧之间插入的最小时间延迟,单位SPI时钟周期个数,即40ns。( P2 H) ^/ S4 r1 O) A1 H. U9 Q
5 o! P9 b) X5 e, J0 z8 v
根据上面的配置,传输一帧(24bit)数据需要的时间:
4 k; i! W% |/ E/ x$ U3 I! P9 P* ]! z# e4 Z; p. P
24bit * 20ns+ SPI_MASTER_SS_IDLENESS_00CYCLE * 20ns  _( x; T0 R" H& H( g
: V, {0 H. H2 L5 L: i
+ SPI_MASTER_INTERDATA_IDLENESS_02CYCLE * 20ns% {8 ]+ B) \; s' T
8 i. F$ ~. d' j1 C2 E2 i3 a
= 24bit * 40ns + 0 * 40ns + 2 * 40ns% ~+ Q" d. H, e/ E( Q) f

3 A. [; \$ V* r7 z* i= 1040ns。
' V8 I/ J  Y4 z7 m/ `5 l" z% A3 Q+ ~; l; F, s  ?
那么这种配置下,可以支持最高触发速度是1 / 1040ns = 0.961MHz,如果想速度再提升些,可以降低参数hspi.Init.MasterInterDataIdleness,推荐的最小值是1个时钟周期,那么可以支持的最高触发速度是1/1000ns = 1MHz。
4 v* T8 y) Y( s+ t7 O* [- s4 `" a
$ R2 t3 p3 F0 |* W- t9 J/ b认识到这些后,实际输出的波形周期也比较好算了,比如我们设置10个样本点为一个周期,那么触发速度为1MHz的时候,那么波形周期就是100KHz。
! a; l, q) _, G; a& F$ H& @: }
- A- R) x9 b& D* t75.6.8 第8步:DAC值和电压值互转4 b, [, x8 y9 W
DAC8501模块的输出电压范围是0V到5V,对应的编码值范围是0到65535,为了方便大家做互转,专门做了两个函数:5 K- O! ]: q) u& q+ [. J
  p% l  X6 z( e/ }, c
  1. /*
    - N/ I+ Y* d2 {0 a; g
  2. *********************************************************************************************************
    9 {" b, M$ P+ r" v7 ?+ C
  3. *    函 数 名: DAC8501_DacToVoltage0 Q2 @4 h- ~/ [  o
  4. *    功能说明: 将DAC值换算为电压值,单位0.1mV0 m+ I( X) R: Y
  5. *    形    参: _dac  16位DAC字
    * x: a3 ]- `7 }- K  l' f
  6. *    返 回 值: 电压,单位0.1mV
      s% \" Z% y! X
  7. *********************************************************************************************************
    ' C/ _% B1 e  S; r! X$ r+ i
  8. */. @" r5 Q' m3 m5 Y# ^3 G; {1 Y
  9. int32_t DAC8501_DacToVoltage(uint16_t _dac)) ~) O4 ?1 s- y
  10. {6 ?9 O. I- a0 e, a1 B6 q7 R
  11.     int32_t y;0 |; c' w6 [* J7 m& H2 Z$ M

  12. - X7 x& J+ \7 r" |  X2 R
  13.     /* CaculTwoPoint(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x);*/6 {( Y1 M2 ~4 K0 D& }
  14.     y =  CaculTwoPoint(X1, Y1, X2, Y2, _dac);5 j" N0 z: B: z) Q: N6 J
  15.     if (y < 0)  `% v+ T) ]  p) U2 J+ N  S+ C' v& p6 W
  16.     {* v- A# ?. c% \- m: {& O6 ?
  17.         y = 0;
    ' o0 M- @$ P8 j
  18.     }
    : D- a0 y) {2 s  s3 y9 X' e
  19.     return y;( J. [8 r# l! e  A& G
  20. }: [0 x& v( n/ c, u! f
  21. 0 B  l. V. X; @$ i
  22. /*
    % H1 B7 c) ?( G% e7 F
  23. *********************************************************************************************************
      Z5 g- N3 l0 n1 P
  24. *    函 数 名: DAC8501_DacToVoltage! l. L. d/ E* o8 P/ K
  25. *    功能说明: 将DAC值换算为电压值,单位 0.1mV
    / I) d5 |6 \" ?9 A$ I+ c$ k$ Z
  26. *    形    参: _volt 电压,单位0.1mV
    ; J% x& s. f6 C* k' N# x6 A
  27. *    返 回 值: 16位DAC字# z& w5 c( P. w. a  m3 r6 K1 E
  28. *********************************************************************************************************$ B1 U2 J3 ^3 i8 Q+ X
  29. */# H$ N/ ]' \/ V2 z. {0 m+ T
  30. uint32_t DAC8501_VoltageToDac(int32_t _volt)
    " g; B9 E2 N" W: H& L! A
  31. {) L8 B& h5 [) ^: e4 B& |
  32.     /* CaculTwoPoint(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x);*/
    ! o/ T  n. o3 p5 l5 _( M5 H) ^
  33.     return CaculTwoPoint(Y1, X1, Y2, X2, _volt);
    ; P) a6 ^( _$ A# v) X
  34. }* m: ?1 F( l& \
复制代码

! P" ]2 X! X8 s  K+ `% t  g
# V6 S# B* E6 P  `7 x75.7 SPI总线板级支持包(bsp_spi_bus.c)( i5 K2 M- X% G1 g  K
SPI总线驱动文件bsp_spi_bus.c主要实现了如下几个API供用户调用:
% ?+ x9 l& W1 j+ I% D3 d, X5 q3 F$ F5 u) N% q1 o- H4 W/ d
  bsp_InitSPIBus0 g" |3 I5 V( `* n( u
  bsp_InitSPIParam7 j! n* O' u: _1 h3 |$ |
  bsp_spiTransfer- ]- \9 Z; O7 U& s
75.7.1 函数bsp_InitSPIBus" p. ?  v  Q8 W9 r$ C3 n
函数原型:% P1 N. S; P5 [5 `
5 X& I7 q. G  X% Y. _# `/ Y; W
void bsp_InitSPIBus(void)
" `, z: H) l9 Z$ }) ?" j) ^
% h! z# k6 T; L( g1 ~5 \* @& P函数描述:
8 U1 L- w* C8 Z0 _+ _
0 B" D! v) s8 }& G此函数主要用于SPI总线的初始化,在bsp.c文件调用一次即可。+ a) e1 E3 O3 Z% l6 S1 g

$ }: v) x1 b7 ]4 f75.7.2 函数bsp_InitSPIParam
5 i% [8 i( z& r9 N函数原型:6 W; |1 h  K! L7 l6 U$ M1 a7 b
" q7 }. }2 ^: ^2 ?/ j
void bsp_InitSPIParam(uint32_t _BaudRatePrescaler, uint32_t _CLKPhase, uint32_t _CLKPolarity)4 m3 G3 Y2 O  T0 C% x  M+ \+ v' `

; ^; ?8 ^9 i/ f# _7 q$ D  W函数描述:. Q" {' B% p. l) N) H& L

4 w: Q  W0 y# |" T4 R% }- z  i& e此函数用于SPI总线的配置。
+ ?0 p9 T, ~+ ?; `' `% A
/ O# w7 W' t1 X! @, H函数参数:9 b& x9 ]4 _# J5 `
7 i$ t+ Z# G1 H( S8 G/ ~
  第1个参数SPI总线的分频设置,支持的参数如下:
6 K' x1 S6 l% N+ \" Q# SSPI_BAUDRATEPRESCALER_2    2分频
1 G. w4 w. C: U, M/ a; V) p. m
; Z7 x9 @% ~: B9 W1 W* gSPI_BAUDRATEPRESCALER_4    4分频
. m& O8 @% G3 H2 }7 P) [% @7 c7 b  _- a& k5 N4 |5 k
SPI_BAUDRATEPRESCALER_8    8分频# N" P- u# p8 f+ d
, w# p. N, f% S; m, l' l4 k9 n
SPI_BAUDRATEPRESCALER_16   16分频
  t/ Z* s3 C0 v- ^6 ?& T
8 d4 F) V( N5 ~% z" `SPI_BAUDRATEPRESCALER_32   32分频
4 |# g* \* }1 e6 ~. G. L9 X' T; U' B5 M  M" C
SPI_BAUDRATEPRESCALER_64   64分频! p" `1 u  f9 T5 r
: ?/ u+ P3 i0 Y4 ?
SPI_BAUDRATEPRESCALER_128  128分频7 o$ a1 r, B5 s- b/ T

  V2 b7 I9 G8 [) D6 i) ~5 s( cSPI_BAUDRATEPRESCALER_256  256分频  A* f; R! n' Z

* S. C& s  z/ ~8 p. l1 `" B8 C$ b+ a8 J  第2个参数用于时钟相位配置,支持的参数如下:
- T# o5 u+ j  J: h  X8 ?SPI_PHASE_1EDGE     SCK引脚的第1个边沿捕获传输的第1个数据% Y9 h1 N, h, j" ~" }
! J' y" T. V  w/ c
SPI_PHASE_2EDGE     SCK引脚的第2个边沿捕获传输的第1个数据+ D& \+ d) K4 f$ J( B
, x3 D. ]* ^# O. l% w4 W
  第3个参数是时钟极性配置,支持的参数如下:
  F# u3 j, ~/ z3 X/ \SPI_POLARITY_LOW   SCK引脚在空闲状态处于低电平) c8 Y7 J. D6 O& |$ V. k. C+ j$ k7 g

( Z% x' [1 {7 p9 ]SPI_POLARITY_HIGH   SCK引脚在空闲状态处于高电平
0 V. @. h/ c( ?% s
% z3 n6 T9 g" j, P! V% z+ B1 `3 f75.7.3 函数bsp_spiTransfer
# P4 {3 n  i0 v8 l( |6 {8 A5 Q! ^& i函数原型:
6 W3 {/ m4 @. u
4 e- {' v: T3 o' a5 C. q* I& z/ Rvoid bsp_spiTransfer(void)6 }& @  e8 _! h4 t

: W* |4 @" c' ]+ B8 q2 S0 S2 p函数描述:
  a) L, S2 _' `: M+ U( g
8 S. h, L' q7 f! j! q此函数用于启动SPI数据传输,支持查询,中断和DMA方式传输。
; ^* ^0 d8 n1 t/ o
% E/ c$ h5 ~$ A6 T75.8 DAC8501支持包中断方式(bsp_spi_dac8501.c)
+ z8 v& }, W) ?7 \DAC8501驱动文件bsp_spi_dac8501.c主要实现了如下几个API供用户调用:) w( m: k* q& y& s6 x0 h3 n

4 Q6 i& g4 R8 z$ t! J3 V" O4 p  bsp_InitDAC85015 H+ Z% W9 I8 O7 f; V1 X
  DAC8501_SetCS1; O- o3 ^: X) L4 Z  E
  DAC8501_SetCS2
# p( y" T* q% Y) C  DAC8501_SetDacData
  _$ Q* w( Y4 `9 ?, P4 p* M: u% Q  DAC8501_DacToVoltage  i$ M  E$ u/ ?6 J" n
  DAC8501_VoltageToDac& h& v; _8 w2 P( r/ p
75.8.1 函数bsp_InitDAC8501
- O7 U' i1 C( G" C! P5 b3 |& d函数原型:
' u8 |: S! M, l# N, u
4 q1 [& Q( ^8 M% _2 kvoid bsp_InitDAC8501(void)
; f$ u: w9 i" h7 J
9 I8 [# n" ], I$ k函数描述:* l% ]; X7 a/ H2 S( J( N2 f
+ V* w$ c& {7 H2 ]2 {
主要用于DAC8501的初始化,调用前务必先调用函数bsp_InitSPIBus初始化SPI外设。: m. z% w) m* y5 r6 k- V

$ Z; _! u, B, O3 j9 \75.8.2 函数DAC8501_SetCS1
4 T- B/ Y1 Y" A9 e/ |" O6 A( K! |函数原型:; x: t# n% v& A

% z9 L- K7 v5 f7 {7 P8 dvoid DAC8501_SetCS1(uint8_t _Level); l5 q' w5 b6 j. k/ g8 M) `9 {- N

4 _, ]  f4 A1 i# M3 ~) e) Y8 [: F8 n函数描述:
! q+ X4 P6 m' Z. c2 Y" x0 e0 ?% `) T# ~. [; f
此函数用于片选DAC8501模块上的第1片8501。6 X) c9 i3 C" s0 Q+ L. B  ?
/ ?4 u* f0 j, V8 A/ h' ]
函数参数:6 [7 m6 b: F: Z7 D2 [" W! r

  y; R* H$ M% S/ O! Z* V8 v  第1个参数为0表示选中,为1表示取消选中。
0 H; Q8 r9 ~) o: b/ c! W( A' n1 Y75.8.3 函数DAC8501_SetCS2' ^, Z' L. W9 X- f5 y
函数原型:8 G+ W- Y1 l8 U/ z  ?( y

3 `* q/ I8 i: c" l6 ~1 t# S4 z( Y" Zvoid DAC8501_SetCS2(uint8_t _Level)7 k9 \7 k3 t& e. z3 Z: V9 C2 u
% h6 @1 x; S. @" V
函数描述:
! c& A% P) N8 ^+ @& u! y9 G/ {. C( j# o" ?0 e5 q" L+ Z) Q9 C& c
此函数用于片选DAC8501模块上的第2片8501。$ B% @% }# W1 j% L; ^; H
4 }% L% r% [5 U' M
函数参数:
, a% X; x' ^( E4 W4 k. R
, I8 e5 I; h+ r% _$ S% _& W  第1个参数为0表示选中,为1表示取消选中6 t$ [( |. y# U5 Q6 f! f7 l
75.8.4 函数DAC8501_SetDacData) c- q# ^- a( J2 r/ W) g
函数原型:4 Y! V8 ^- b1 M. b7 e# T8 T+ q

& m  J+ o+ d2 y$ }, uvoid DAC8501_SetDacData(uint8_t _ch, uint16_t _dac)
4 E+ f3 c6 e6 E6 \* M
# [3 r2 D9 S( P" l% ?+ l2 B函数描述:, c% g5 G$ V3 G1 ?8 F  }
# U. P9 F0 g5 H3 D* Y
此函数用于设置DAC输出,并立即更新。+ ?3 L: D7 Q3 {: F
  ^+ r2 i, z; y8 [2 s& }
函数参数:1 f2 P. P' R7 W
& M6 J/ q6 i: m; R( H3 e  ^
  第1个参数为0表示通道1,为1表示通道2。
) T# I( X1 _) H6 x# x7 t  第2个参数是DAC数值设置,范围0到65535,0对应最小电压值,65535对应最大电压值。
7 R2 D- Z. o& `# c* I7 Q" C: r' @75.8.5 函数DAC8501_DacToVoltage# e7 B$ B+ Q8 S( H
函数原型:
( B8 Y8 ]$ K# Z3 e
: ]# o3 R1 W7 o9 i) `5 U# gint32_t DAC8501_DacToVoltage(uint16_t _dac). h. H# C1 d+ {2 i  k

$ l( w( x& q* I# d函数描述:! s8 v+ I& b- w4 f
6 a3 f% P' R+ r  u+ b& Z3 S
此函数用于将DAC值换算为电压值,单位0.1mV。
8 ]7 j- ^" J, S  p8 U$ U, C3 P1 e* z
函数参数:
/ W8 Y7 w0 y& H5 i! r% [; y6 Y5 W* b" t3 n7 `) Q+ b: o4 j5 r" {
  第1个参数DAC数值,范围0到65535。6 g- U) @4 s3 u: _& U1 g- S7 a
  返回值,返回电压值,单位0.1mV。1 e7 u) s2 i! x" f
75.8.6 函数DAC8501_VoltageToDac
- u, I3 o3 \  a- R6 L& W函数原型:: A7 a$ V9 ~2 S2 g+ E- Q
0 v( ?* f8 @+ A: n! Z  C
uint32_t DAC8501_VoltageToDac(int32_t _volt)$ `+ ~, ?* |  F
" J1 E* }9 P* u
函数描述:- j! J1 V6 c8 ~% o% z: Z

8 R$ a5 D( Z2 O4 }1 V此函数用于将电压值转换为DAC值。
% _' s1 p, v' s# W8 B6 t
0 n/ m; W6 T! i# F3 `& K函数参数:
7 P7 X* i" z- l" v  G9 u: j) k7 }
  第1个参数是电压值,范围0到50000,单位0.1mV。
; a* e# g; m" U* O* V$ f  返回值,返回DAC值。
* q( `' N  t& w. H' }( z1 C75.9 DAC8501支持包DMA方式(bsp_spidma_dac8501.c)6 d6 }* _" y0 x) j
DAC8501驱动文件bsp_spidma_dac8501.c涉及到的函数比较多,我们主要介绍用到的如下几个函数:& b1 G$ K' H, W5 _) Q  L
, Q( h# T! }! I; v
  bsp_InitDAC8501" k" X2 A! y- b9 j
  DAC8501_SetDacDataDMA
& d+ @$ c# [2 L. ~4 }' ~/ M( t  DAC8501_SetDacData
, s7 s& ?+ Z; B( y75.9.1 函数bsp_InitDAC8501
! G* F  E$ `6 w) B2 l* m1 N函数原型:
" d0 p* \+ l4 n6 l" o6 @: D( S" C  [! x+ K, T  a9 q
void bsp_InitDAC8501(void)
# W1 e# i" Q2 k+ R8 ^2 O, \& ^! v. U
函数描述:# J: L: V  [2 ^  b

; D5 P! @* z! {' P主要用于DAC8501的初始化。9 W* Z# N- A) w0 `; w9 N
) \+ S/ b+ g7 n; D5 V
75.9.2 函数DAC8501_SetDacDataDMA
, d0 n. B& |* N* F: q函数原型:5 v# e/ }1 G  k) ]- z. ?2 c3 v
# D6 X% Z- j! b" Q0 T
void DAC8501_SetDacDataDMA(uint8_t _ch, uint16_t *_pbufch1, uint32_t _sizech1, uint32_t _ulFreq)* x" F1 n8 S- v( ~( p- H
) p. E8 R, W4 b4 f2 d
函数描述:
; P  ~! w, V  b- H8 A) x0 G( U& _* Y4 k8 }/ Q& S
此函数用于SPI DMA方式数据发送。% k. J6 d  B9 t# e$ Z1 M; o% \
4 n5 |' {7 r0 k! J' j$ k' k7 t
函数参数:2 ?: T7 I% o( y* b" D7 v  {$ _. t

8 F! r$ ?# F. {) q7 @( N9 C  第1个参数用于选择的通道: 1表示通道1输出6 Q9 r' _% R) J  q! D6 R# P2 A* O
  第2个参数表示通道1数据缓冲地址。
; u% B6 M- j- A7 m  第3个参数表示通道1数据大小。
9 c. F1 e# r. L  第4个参数表示触发频率,推荐范围100Hz- 1MHz,注意这个参数是触发频率,并不是波形周期。这里触发一次,SPI DMA传输一次24bit数据。
0 R& B4 O) j* f7 ^  j  t, ~% O75.9.3 函数DAC8501_SetDacData
. G3 ^0 e4 m9 {; Z- f1 Z' n函数原型:
4 y6 P3 r9 o9 b( ~& ?2 t0 s
2 t6 B/ H. R. B! C9 Cvoid DAC8501_SetDacData(uint8_t _ch, uint16_t _dac)3 ~) Q9 d! k( T7 W9 [! P5 Y# B( D5 g
7 ^' w, P+ i$ K0 J4 X
函数描述:
3 T6 i# Q4 r. R
: O+ q: }) ?' W此函数用于设置DAC输出,并立即更新。2 j* g3 F, @: B8 R1 K8 s

1 H- B: i1 F+ V; H8 R函数参数:
# u+ m0 ?& t( x% z. d
) t/ h" P" w6 Y3 P+ W4 v! e  第1个参数为0表示通道1,为1表示通道2(对于SPI DMA方式,仅支持通道1)。
8 c* ~! J% z- `9 I5 Q  第2个参数是DAC数值设置,范围0到65535,0对应最小电压值,65535对应最大电压值。
9 n( e: g* L, e/ Y3 T" E75.10          DAC8501驱动移植和使用(中断更新方式)
$ C5 G) h* s. x# m9 _, g! wDAC8501移植步骤如下:1 D7 S; X; M  N
5 l' F3 b* X9 {* a" }
  第1步:复制bsp_spi_bus.c,bsp_spi_bus.h,bsp_spi_dac8501.c,bsp_spi_dac8501.h到自己的工程目录,并添加到工程里面。
& W2 h% D) Z/ B$ O  第2步:根据使用的第几个SPI,SPI时钟,SPI引脚和DMA通道等,修改bsp_spi_bus.c文件开头的宏定义
% g8 H" b$ p2 K4 S
  1. /*
    3 D0 n: I% I5 c" r
  2. *********************************************************************************************************
    & Z1 C1 Q" v5 ?0 R) a1 C
  3. *                                时钟,引脚,DMA,中断等宏定义
    $ @3 V% q% G  v/ M: V, I7 g
  4. *********************************************************************************************************
    $ A9 e9 {/ Z- ^/ s
  5. */3 E6 J5 G" [2 `' ?0 {+ f0 @
  6. #define SPIx                            SPI1: ^, o3 }( `1 N6 p, B
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()
    - `6 h) s3 K0 A  r; I
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()
    " i5 }4 h% B& M$ l* Q, u, H

  9. " B4 V- |' ^0 Z: M9 k; M
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()
      x: j* \+ R8 j  f$ U+ H& X) o& z2 y
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()
    ; X, S: T( w* q) V) X
  12.   c# T0 f: p! ~
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    , J) u1 Y/ v  z, j. a% o) c( ]- M
  14. #define SPIx_SCK_GPIO                    GPIOB
    0 L) \; x) H8 a2 w6 k  E0 P* ^
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3
    " v( E9 n- Y2 R0 c! S. Z0 `+ A9 V% ?
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1
    # A8 M+ ~+ \0 @$ u$ `5 d" W6 i
  17. + X) a9 ~- Z9 ~, X7 c0 v
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    * F1 `1 s. M  {/ ~5 L/ I! Z
  19. #define SPIx_MISO_GPIO                    GPIOB5 W9 ~; J4 M+ u+ U
  20. #define SPIx_MISO_PIN                     GPIO_PIN_4
    8 @$ u; Y+ y! z8 A5 Z; ^
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI1, [: E  O$ U' H; }2 ~( `" W
  22. : \0 }) P/ @. @) K. t
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()/ H  s# F- y0 h& _( b$ T
  24. #define SPIx_MOSI_GPIO                    GPIOB
    8 e9 W& Z7 n  Q! J2 H3 n, T; t& Q
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_5
    + ]4 m0 i$ H7 i
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1
    / w9 [6 z+ z, S
  27. : a# U$ i6 f: d; s& `: T+ w5 @1 p! H  @
  28. #define SPIx_TX_DMA_STREAM               DMA2_Stream3
    . I7 }/ B0 S1 o3 j9 X  e8 u
  29. #define SPIx_RX_DMA_STREAM               DMA2_Stream2* V! t7 e3 s' o
  30. 5 M4 N$ |( v6 |9 {2 A1 v7 j& Q
  31. #define SPIx_TX_DMA_REQUEST              DMA_REQUEST_SPI1_TX2 p7 U( a: J) [8 N" s0 {8 f3 h
  32. #define SPIx_RX_DMA_REQUEST              DMA_REQUEST_SPI1_RX# {$ u' E4 w& h2 }8 u9 j; g
  33. 4 a% ^* `. S6 o5 ^4 @
  34. #define SPIx_DMA_TX_IRQn                 DMA2_Stream3_IRQn
    / B1 J* S! n# l& P5 h
  35. #define SPIx_DMA_RX_IRQn                 DMA2_Stream2_IRQn; h) t# }- H( I8 l6 p

  36. 5 p6 ~+ e0 I! c+ V, z2 K9 E2 L
  37. #define SPIx_DMA_TX_IRQHandler           DMA2_Stream3_IRQHandler/ K. _, I+ L; p
  38. #define SPIx_DMA_RX_IRQHandler           DMA2_Stream2_IRQHandler
    : J# g. U4 N: M; I- ]
  39. * Q! D5 ]. m( i$ w7 @1 _% p5 v
  40. #define SPIx_IRQn                        SPI1_IRQn6 O/ R7 F0 L8 s, L
  41. #define SPIx_IRQHandler                  SPI1_IRQHandler
复制代码
' M' G; J6 ~9 t/ y. _
  第3步:根据芯片支持的时钟速度,时钟相位和时钟极性配置函数DAC8501_SetCS1和DAC8501_SetCS2。
. ~& M2 i8 ?5 e2 }. uDAC8501_SetCS1和DAC8501_SetCS2。
2 o" `& e# C# `* \! U8 s
  1. /*& G" h; s, M$ f( L$ y
  2. *********************************************************************************************************9 d# Q1 J6 L5 \/ V# N0 P
  3. *    函 数 名: DAC8501_SetCS1$ [6 ]8 j) t% [7 T) e
  4. *    功能说明: DAC8501 片选控制函数
    : f/ f' F* O0 _& n* j0 G# K
  5. *    形    参: 无3 a8 J! C6 Q$ z. U2 _
  6. *    返 回 值: 无
    ( k6 s+ f6 k( a6 }6 k: W
  7. *********************************************************************************************************
    . P! J  D! Y. h  B# d; |' G8 I  B
  8. */7 g) Q3 F6 P$ t6 N; c2 v  {" K
  9. void DAC8501_SetCS1(uint8_t _Level)0 L+ ], ?& y1 [' i& W4 H: j# W: n
  10. {
    1 b' E  E: Z' }+ m
  11.     if (_Level == 0)8 F: y% s8 ]+ i. B$ B% p& z: Q
  12.     {
    * H( P/ @' a! Z5 r: T
  13.         bsp_SpiBusEnter();    /* 占用SPI总线  */   
      N0 [7 l# k8 e! C8 d
  14.         bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);        $ x2 i' I. Z( U( N, J0 l) @
  15.         CS1_0();
    5 h8 B8 n0 H- i$ M, I
  16.     }
    * u- C8 p$ t  U3 q+ |# q
  17.     else& a1 |. w+ H: m7 E
  18.     {        ; T- Z5 u, f4 h7 T; o
  19.         CS1_1();    , A2 f$ x+ B: ~, j& s
  20.         bsp_SpiBusExit();    /* 释放SPI总线 */- Y# @3 C9 [/ j( M2 p7 _' P
  21.     }    8 `8 l5 u2 Q+ p8 A5 y, u
  22. }
    , w3 q0 H8 {# `# Z
  23. # o) q3 a( r: b; J, Y0 z# X
  24. /*! I, B' o- b& P- P9 i8 @+ T
  25. *********************************************************************************************************
      A1 {& C2 C/ s4 N. `
  26. *    函 数 名: DAC8501_SetCS2(0)! u0 e' T0 y2 n7 t. c
  27. *    功能说明: 设置CS2。 用于运行中SPI共享。: A/ Q+ I4 p9 u7 T9 k! n
  28. *    形    参: 无
    , r- E4 n5 x# {5 Y$ I0 r
  29.     返 回 值: 无
    ( ]2 h! O0 p  @0 t4 s& @
  30. *********************************************************************************************************, [5 l" O2 x6 k: U) j! b
  31. */9 G6 n+ V7 n3 p+ _2 y7 \
  32. void DAC8501_SetCS2(uint8_t _level)* F( z, q  K% q2 N( q( u+ B
  33. {
      D/ I1 Q( L( K* n
  34.     if (_level == 0)
    " u7 @  o8 ^% e' _
  35.     {+ O2 K0 c5 ?% M. T
  36.         bsp_SpiBusEnter();    /* 占用SPI总线  */' W0 e: K" i3 {- B+ \5 E: e
  37.         bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);        3 M' c( t+ X9 z0 @
  38.         CS2_0();* X$ x0 M2 ]( z
  39.     }3 E* d, o: a9 H; H; i" r
  40.     else
    $ g8 V( o- M; b* C: S
  41.     {( n8 l: p3 _0 ]- V  y* Q& s# @
  42.         CS2_1();" Q; Y. {. Y1 ]$ ]6 |) X
  43.         bsp_SpiBusExit();    /* 释放SPI总线 */0 H1 U  R4 J6 ?# k: C/ r
  44.     }
    4 E. N$ U5 G( _0 Y. h. S3 I
  45. }" B- @5 M; q2 w" o5 J: E/ C
复制代码

& j( h! U) \3 _, I  |5 }$ d, O( n4 u' F! Z( K* x5 K, _
  第4步:根据使用的片选引脚,修改bsp_spi_dac8562.c文件开头的宏定义。6 ^0 L; f* D, M4 l
  1. #define CS1_CLK_ENABLE()     __HAL_RCC_GPIOG_CLK_ENABLE()
    / A4 H4 c- Z! l& T* X
  2. #define CS1_GPIO            GPIOG
    9 u( L; Z$ g5 D$ {6 a
  3. #define CS1_PIN            GPIO_PIN_10
    $ I5 c. l  l* Q# [; j, O" [

  4. ; P# Q3 H; {0 l
  5. #define CS1_1()            CS1_GPIO->BSRR = CS1_PIN8 E! a/ J# Q0 j# Y4 X$ D7 i- M. a
  6. #define CS1_0()            CS1_GPIO->BSRR = ((uint32_t)CS1_PIN << 16U)$ I: S7 J1 |* ^2 B( Y7 Y$ q% R

  7. # y- ?' k0 \  H: v5 \
  8. /*特别注意,我们这里是用的扩展IO控制的 */   
    4 H7 n; u1 F& l% Y% D0 ?( f0 b
  9. #define CS2_1()            HC574_SetPin(NRF24L01_CE, 1);7 e3 W- `% t8 i0 V! E
  10. #define CS2_0()            HC574_SetPin(NRF24L01_CE, 0);
复制代码
9 ~" V8 ]! _; g: K9 H" @! `
  第5步:如果使用DMA方式的话,请不要使用TCM RAM,因为通用DMA1和DMA2不支持。并为了防止DMA和CPU同时访问DMA缓冲造成的数据一致性问题,将这块空间关闭Cache处理,比如使用的SRAM4:/ ~) D$ d- B2 u/ }
  1. /* 配置SRAM4的MPU属性为Non-cacheable */
    # y( T4 |2 a3 |' ]  l
  2. MPU_InitStruct.Enable           = MPU_REGION_ENABLE;3 Z, @' n, Z* M; J5 C
  3. MPU_InitStruct.BaseAddress      = 0x38000000;6 e% O% h3 [- Q4 I* g
  4. MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;; ~( _% B  @+ p
  5. MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;1 s! _" @. m1 K1 x( g
  6. MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
    5 Y% M/ C* {4 t( f7 F- D. `9 V
  7. MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    9 C9 S4 k6 N: h4 G4 v  c
  8. MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    3 Z* N$ D, i6 }, q
  9. MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
    9 R- V3 o5 G% Z- U! j6 `& _6 _
  10. MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;$ ^+ j/ G- p5 m; g
  11. MPU_InitStruct.SubRegionDisable = 0x00;2 T. ^; Y' E  M3 q4 S
  12. MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;; n2 F$ u4 P2 Z' y* H. L

  13. ' f( P: U+ K3 D6 x3 ~0 D  i
  14. HAL_MPU_ConfigRegion(&MPU_InitStruct);
复制代码
  R; `# r2 k- t+ U2 W1 A
  第6步:初始化SPI。& p  l. @1 Q3 c$ x& b) E
  1. /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */$ {$ R4 j+ p! ~1 N+ i2 O4 }
  2. bsp_InitSPIBus();    /* 配置SPI总线 */        
    9 L5 G8 C7 p& l' `$ V
  3. bsp_InitDAC8501(); /* 初始化配置DAC8501 */
复制代码
) i% T  j. y. f9 T9 W  Q, A$ }. \
  第7步:DAC8501驱动主要用到HAL库的SPI驱动文件,简单省事些可以添加所有HAL库C源文件进来。
5 l. B; H$ Y$ B+ W) i( X* y7 B  第8步:应用方法看本章节配套例子即可。$ Y8 T! k9 [: [: C4 S; @
75.11          DAC8501驱动移植和使用(SPI DMA更新方式)
  X/ I. L9 T3 T' d) _  oDAC8501移植步骤如下:  J6 }6 ~: }; E2 B4 G- L. l

1 F( ~/ S5 E5 [- N% {  第1步:复制bsp_spidma_dac8501.c,bsp_spidma_dac8501.h到自己的工程目录,并添加到工程里面。6 t* A: V* n' P4 _0 T7 n, v3 ?5 H
  第2步:根据使用的第几个SPI,SPI时钟,SPI引脚和DMA通道等,修改bsp_spidma_dac8501.c文件开头的宏定义
2 \! `* L; @3 c' ~8 `' i5 k
  1. /*1 c+ z6 w" T7 {% s  S% U, I
  2. *********************************************************************************************************
    ' ?5 m; f7 Y1 n3 c* U
  3. *                                时钟,引脚,DMA,中断等宏定义
    $ [' l5 e2 @0 F+ q# O% q
  4. *********************************************************************************************************' A+ S. B+ R: d$ L* _
  5. */* @. P- C0 [4 e) b, C
  6. #define SPIx                            SPI13 E/ O2 Q; y  E5 q
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE(), }. b: `, t# Z. y7 [6 M4 |1 ?
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()
    , J$ p( t5 E6 P8 m0 Z4 j9 X. X

  9. # m" }, f0 k: s& k! N6 @
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()
    5 E! F: S5 x: ?" S" O
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()
    ) z$ _/ y  Q/ Q
  12. $ @% z5 c/ v6 ~( f5 O
  13. /* SYNC, 也就是CS片选 */    4 h& p5 m* \- Q1 Y, }
  14. #define SPIx_NSS_CLK_ENABLE()             __HAL_RCC_GPIOG_CLK_ENABLE()! g& l8 Q( N  y. s; H8 s2 F
  15. #define SPIx_NSS_GPIO                    GPIOG
    : @& k; _. @) ]! x. h8 I
  16. #define SPIx_NSS_PIN                    GPIO_PIN_10- L$ O6 o, {) i" o; U! w* ]4 ]5 h
  17. #define SPIx_NSS_AF                    GPIO_AF5_SPI10 S( K; r& G& @& I/ j
  18. 1 a0 k! r8 g3 ?  i$ Z
  19. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    # b( X/ R9 ^+ q! r: X; }; @
  20. #define SPIx_SCK_GPIO                    GPIOB
    ( d8 X, c: Z3 N' @: U
  21. #define SPIx_SCK_PIN                    GPIO_PIN_3" H( x9 y# t- V( g6 t# m
  22. #define SPIx_SCK_AF                    GPIO_AF5_SPI1
    - C0 c! \, ^) k2 f

  23. 1 E0 ]1 D; e$ b- L" c) b& _# _. Q
  24. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()0 e8 S( j- S( g
  25. #define SPIx_MISO_GPIO                    GPIOB
    ) J+ g- t. u1 y5 @6 Z+ r! _
  26. #define SPIx_MISO_PIN                     GPIO_PIN_4  D6 w, a9 Q9 t, R: b* m2 G
  27. #define SPIx_MISO_AF                    GPIO_AF5_SPI1* `$ T$ J5 r# u# ~( C2 B4 C( q1 s" M

  28. 8 b4 I1 S! @2 I3 k
  29. #define SPIx_MOSI_CLK_ENABLE()          __HAL_RCC_GPIOB_CLK_ENABLE()
    : j+ R* B- ]2 K0 L# z" l
  30. #define SPIx_MOSI_GPIO                  GPIOB
    # h" }: t5 y8 C! Y# ~* F, x
  31. #define SPIx_MOSI_PIN                   GPIO_PIN_52 @$ D, ]0 v1 v9 {
  32. #define SPIx_MOSI_AF                  GPIO_AF5_SPI1
    ; X4 m& s/ ]. T& q. Z

  33. . o, m  k* ~/ o; N9 [- w
  34. #define SPIx_TX_DMA_STREAM              DMA2_Stream37 M# ~/ V$ d+ z6 J7 g: z+ |
  35. #define SPIx_RX_DMA_STREAM              DMA2_Stream2; B: W' a0 ^( h! h  Q$ Q- \4 m

  36. ' E; s% I4 d& V: Y7 K2 [
  37. #define SPIx_TX_DMA_REQUEST             DMA_REQUEST_SPI1_TX, w# o& h+ e' w# g9 a7 v5 U
  38. #define SPIx_RX_DMA_REQUEST             DMA_REQUEST_SPI1_RX
    3 u( c; b6 A. a$ C6 N# G
  39. ( L: T8 K7 @7 h9 z; D
  40. #define SPIx_DMA_TX_IRQn                DMA2_Stream3_IRQn5 P1 o6 ?6 a# ?
  41. #define SPIx_DMA_RX_IRQn                DMA2_Stream2_IRQn
    3 [: ]" R2 c$ |1 r# @

  42. ' ?% a  Q& k( L) p/ Q
  43. #define SPIx_DMA_TX_IRQHandler          DMA2_Stream3_IRQHandler; G0 K! x) z) C( Y' m
  44. #define SPIx_DMA_RX_IRQHandler          DMA2_Stream2_IRQHandler! X, \# S9 U8 B' F, O& y

  45. 1 y2 Q9 U9 b/ x; [7 s/ f. J
  46. #define SPIx_IRQn                       SPI1_IRQn
    $ L1 f2 j7 |$ b5 H3 d% {+ |0 m( q
  47. #define SPIx_IRQHandler                 SPI1_IRQHandler
    ; f# p6 l. B" M! D2 p2 w8 ?
复制代码

$ Q% m4 A7 I7 J# i, W
2 w) H- d5 h0 `, {. D  第3步:如果使用DMA方式的话,请不要使用TCM RAM,因为通用DMA1和DMA2不支持。并为了防止DMA和CPU同时访问DMA缓冲造成的数据一致性问题,将这块空间关闭Cache处理,比如使用的SRAM4:
2 Q5 M) v+ P- p! I
  1. /* 配置SRAM4的MPU属性为Non-cacheable */
    ' a- k" {5 E1 R1 B  j
  2. MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    8 r  k$ c- f/ y: E, E8 w$ Y
  3. MPU_InitStruct.BaseAddress      = 0x38000000;( X' w9 t2 I+ f% V
  4. MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;4 C! K1 R0 _  x" Z/ j% A& U
  5. MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;. i, N% W% u! c* ~
  6. MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;2 E$ Q4 C( s3 C  s* B9 n3 p
  7. MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    + u' G# D' ~: f  w
  8. MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    : g2 T, S+ Z/ F4 E
  9. MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
    7 q% o) W7 N/ P8 D, Q
  10. MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    $ J$ ]" V! V, Z, w' N: m4 f
  11. MPU_InitStruct.SubRegionDisable = 0x00;
    3 v" n# a" H' @; F
  12. MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    + G# g2 Y3 R% S8 Z

  13. ! |& X& B& }: r/ v3 G6 p
  14. HAL_MPU_ConfigRegion(&MPU_InitStruct);
复制代码
第4步:初始化SPI。
1 k+ E# _1 a3 C# p$ t
  1. /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */1 ~1 H3 L0 ^8 @3 E
  2. bsp_InitDAC8501();    /* 初始化配置DAC8501 */
复制代码

8 M9 N' }( s1 k8 u5 Z  第5步:DAC8501驱动主要用到HAL库的SPI驱动文件,简单省事些可以添加所有HAL库C源文件进来。
- a( }/ N4 q# G! x  第6步:应用方法看本章节配套例子即可7 s# |3 H& ?: x0 v, U  a
75.12          实验例程设计框架
0 N& r4 c; Y# F9 X3 ^- e; s通过程序设计框架,让大家先对配套例程有一个全面的认识,然后再理解细节,本次实验例程的设计框架如下:& u  t( K# B, }4 H9 A; g
  R; Z5 h1 S" `' A! R
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
( z. J2 W* {+ t( W
0 G: j$ L) P9 s: D) @
  第1阶段,上电启动阶段:
) s1 ]7 V. ^0 ~* F/ L
/ I0 N) Y/ }# A4 |! C2 O8 k. q这部分在第14章进行了详细说明。
9 z5 F  Q5 _! F6 R6 h% V  第2阶段,进入main函数:+ x; g% v# ^* r! [) Q

( u9 `6 e# ?/ S  ?) g7 W  第1部分,硬件初始化,主要是MPU,Cache,HAL库,系统时钟,滴答定时器和LED。
, K- C+ L1 F" t$ R  第2部分,应用程序设计部分,实现DAC8501的简易信号发生器功能。。& D2 n, V# [7 w( m
75.13          实验例程说明(MDK)0 ]  y  k) G$ A% d0 Y. b
注:本章是配套了两个例子的,这里我们以SPI DMA方式进行说明。
0 Y  F$ f' o  X3 l6 M. h/ T8 D- i9 E( F9 n# @2 @* D  ^3 d  _8 a
配套例子:1 ^& ]( Z! A4 ]8 s' ~3 ~  @- y

3 g0 _/ ?7 q$ {  U6 w& c- N4 Q' qV7-054_DAC8501简易信号发生器(单通道SPI DMA方式,16bit分辨率, 0-5V输出)* d# _$ N4 n# E$ u
5 r* [4 n6 e2 R0 p1 S
V7-055_DAC8501简易信号发生器(双通道SPI查询方式,16bit分辨率, 0-5V输出)
9 c# d! ]7 u0 h' I! h
/ x( O/ O1 B1 X# R/ V! b0 x实验目的:
$ S% U9 k: v. |& y) V7 w2 Q. `! |# e+ a( |# @( x
学习DAC8501的SPI DMA驱动方式实现。
) Q! ~$ [3 }' B) t3 N3 L4 j- T实验内容:
5 B" |/ F8 s. [# R/ i, u* c  F- T0 c
DAC8501模块上带了两片8501,每片是单通道DAC,片上输出缓冲运放,轨到轨输出,16bit分辨率,支持30MHz的SPI时钟速度。
; n9 A2 j) u" C7 X# zDAC8501本身仅支持一路输出,而模块上是带了两片DAC8501,其中只有一路的片选可以支持SPI NSS复用, 所以只有一路支持SPI DMA。4 P; g4 c9 n2 V
DAC8501供电电压2.7-5.5V,模拟输出带宽350KHz。
3 `& F9 ?" v9 I7 b" f9 g实验操作:
. k2 Z& X! F! }/ \7 T
; T% y  ~6 p: }% Y, ^: e3 {9 @启动一个自动重装软件定时器,每100ms翻转一次LED2。" F+ o5 P5 J1 e& W
K1键按下,通道1输出方波。
6 x- a, Z, |/ n  i1 \; \) fK2键按下,通道1输出正弦波。
0 k) h5 K, w- [6 VK3键按下,通道1输出直流。
% j( b" a6 Q) o* X% n% v9 K上电后串口打印的信息:
& [  M$ C/ ~# p: O" D, t- \' M, c4 I/ z/ o2 e; U) f! A
波特率 115200,数据位 8,奇偶校验位无,停止位 1。+ G; j6 D  f0 p# F
% s* i6 h. q, b6 G  |
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

& c$ Q3 }& @/ o0 n, a+ G0 ?- ~
9 x  z6 C  f$ ]/ f( _8 u9 g( ^波形效果:- w7 H3 s. r8 }. |7 t0 t( T; S; Y0 A

8 m0 F& Y" X2 ~- C0 j0 C
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

1 y5 o; @- u7 n, O3 w) w2 f% q6 x" ?0 a5 |" y& {7 L
模块插入位置:% _, n- `& o* ^' x
" f" x3 w" j# V5 Z5 o
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
. f$ d- m* q4 N0 W' y% P* l" a& {

3 P* a9 ?) M8 `2 Q. m( ^5 f程序设计:5 K) E$ N8 J2 k. u

6 `5 s0 f2 r; d. V6 p; b  系统栈大小分配:
5 z5 ~1 A: a6 h2 M' S/ m+ v: ~% y( C+ n) U
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
! [7 e# j( F5 s1 B

' i8 B8 d# u# S+ ^. u+ }( G3 ]  RAM空间用的DTCM:
; P" u: d5 Y2 W8 W* I1 K& M$ w. R# S7 g7 b9 k$ A- I
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

/ d! b& S  B) T4 Z1 d
$ Q; U2 K8 i! q) u+ D' o4 C  硬件外设初始化
% O  c% b( g: e! p& w9 _( q. _
+ f: G7 C& l  D9 ^# W. d硬件外设的初始化是在 bsp.c 文件实现:
$ r9 g$ z4 Z: v9 J1 i9 b  l/ k+ E3 N
  1. /*/ T  `! o5 Q6 k
  2. ********************************************************************************************************** v  C- X- t9 e
  3. *    函 数 名: bsp_Init
    + m( i2 y/ K2 f4 ~1 n6 c) n
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次. n+ d4 B1 f. r! j
  5. *    形    参:无1 }& v$ n/ P: t( _- |2 J
  6. *    返 回 值: 无
    ; l) f5 `# X( m+ ?- L+ U- {
  7. *********************************************************************************************************2 k+ Y* q! m9 ?5 A8 {; P
  8. */
    ) ?9 a2 z+ ]" ^8 `# E
  9. void bsp_Init(void)7 h0 z- [( ~: R) m
  10. {
    9 n5 i8 j4 Y/ A7 e* p
  11.     /* 配置MPU */8 r/ a* {- F3 ~
  12.     MPU_Config();
    ( j4 w! U9 K0 J
  13. ) J2 N" {. \% m2 l' ]* G6 w
  14.     /* 使能L1 Cache */
    # l6 p- b* E- w4 n2 ]$ x
  15.     CPU_CACHE_Enable();
    % y6 z4 o/ K% C4 w6 a

  16. : A# v" `1 X( `* Y
  17.     /* & U$ k+ f- D3 Q( ]2 M: s5 n& _; C
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:* [4 A8 t- a8 K
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
    $ j: Q% T6 w8 Y- D
  20.        - 设置NVIV优先级分组为4。: G* i! ?  C9 k8 _: G" B  H# |  v" m
  21.      */# U% ^2 z4 M0 y7 V/ j
  22.     HAL_Init();" [, ]3 E5 ^0 \
  23. - g% X! f* `& ~/ I" S
  24.     /*
    ' v8 \, L( N; z6 m" J
  25.        配置系统时钟到400MHz
    - n3 A# I- v: {
  26.        - 切换使用HSE。- n! V: w5 a1 |1 `: M" e
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。4 q" |4 I/ h' d5 R
  28.     */" {0 Y+ Q! c3 E/ I$ g
  29.     SystemClock_Config();1 }' |" G" A2 `4 ^1 i9 y8 D; e4 o1 e
  30. , w& l: V5 g. a' S. T
  31.     /* 4 t6 n5 q; |1 a9 M! w  s
  32.        Event Recorder:) c& l8 j% u1 ]: D
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。- A0 C, X6 P" f
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章
    : d) \' }$ ?) ?4 c, v  ]/ v
  35.     */   
    " \6 g2 k' L0 z# X4 Z' c
  36. #if Enable_EventRecorder == 1  ) h( k3 ]6 W6 q& M
  37.     /* 初始化EventRecorder并开启 */: T- O8 F! x) ~) i& m/ W8 c; x
  38.     EventRecorderInitialize(EventRecordAll, 1U);5 c' Y, v1 H: k& ~4 W- P
  39.     EventRecorderStart();
    - O/ h! j: s" g* C
  40. #endif( m, ]5 m. o" X. S
  41. " I6 X+ N9 K+ t: i
  42. bsp_InitDWT();      /* 初始化DWT时钟周期计数器 */      
      w3 Z% x2 S/ x. @  z8 }) v
  43.     bsp_InitKey();         /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
    2 d. e$ l4 h" T0 [' s
  44.     bsp_InitTimer();       /* 初始化滴答定时器 */, A1 O4 m: U/ f
  45.     bsp_InitLPUart();     /* 初始化串口 */
    1 r# z" U1 l1 j* R2 \5 j
  46.     bsp_InitExtIO();     /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */    . {* M! q' L4 W* w
  47.     bsp_InitLed();         /* 初始化LED */   
    0 O1 M, }& ?) K
  48. bsp_InitExtSDRAM(); /* 初始化SDRAM */
    4 j! |' N( _' n# @2 P  W8 x/ ?& P  P

  49.   k+ h! Y/ Z* D! `9 o0 w' Z
  50.     /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */   
    " q4 [0 u: P: B3 A( O4 Y7 v
  51.     bsp_InitDAC8501();  /* 初始化配置DAC8501 */
    : }4 f3 J4 W( B8 W) j6 H* v0 s1 T
  52. }
复制代码

; F" E1 h3 k  V) x6 r$ u% |- ^" p0 R
/ U$ v& y* r1 r. @% W& Y2 ]& G6 p
  MPU配置和Cache配置:; n8 t& \1 t7 p7 D# f9 [+ s
) ]7 q6 ^8 n" n* e
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区以及SRAM40 S' y( i- |+ g( U) Y& A1 ?
% U. p/ w7 ]0 ]) ]6 H8 r
  1. /*/ s3 i& S' }2 ?2 |) N& m
  2. *********************************************************************************************************
    + c5 _( R4 ^* W3 ^0 h* a8 u% S
  3. *    函 数 名: MPU_Config- \- ~7 j% r/ r
  4. *    功能说明: 配置MPU/ A/ k# o% m( I* t  r; `& I
  5. *    形    参: 无6 \# s3 e. `% @4 Y; q; R1 l+ I
  6. *    返 回 值: 无+ D' W7 A+ y8 Y) {9 C% D" G3 H
  7. *********************************************************************************************************% t+ P& }# e9 S8 R1 p
  8. */
      d  ^' A! C* n; c- {" G7 G
  9. static void MPU_Config( void )
    $ q1 s- g* t0 e8 d# ^5 `
  10. {# \# E) d$ H# t. n4 [+ q
  11.     MPU_Region_InitTypeDef MPU_InitStruct;
      y0 d" V) z  U; Z6 b

  12. 5 W1 t& [  }) o+ |3 l. `- i3 d) g
  13.     /* 禁止 MPU */: l- @: N$ {9 L
  14.     HAL_MPU_Disable();
    3 Z" x  l7 m/ w$ C6 x4 c  z* j( e# [# u
  15. / r' O* o1 H0 c# Y- b4 Q
  16.     /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
    4 ~/ x/ _/ X3 L/ I
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;! s+ T. e) ^, n9 N1 }6 h. ^. f  X% H, ]
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;
    % s4 w* k# c) y: c; E
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;' f7 M5 ^* D+ C8 `! P. S. R
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    - T& B. U" P! u6 r
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;8 V: h6 `0 S: v& O% e% I
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;; b& c9 S# E4 F5 B
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    ( }, O1 l  x/ f
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;, l+ R4 I( {* y+ I# T
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;! C4 h" i8 H# B* r8 _- g# s/ @7 u+ g
  26.     MPU_InitStruct.SubRegionDisable = 0x00;+ _! h. W* F3 V% ]( w( F9 H
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;# s' r8 x& L$ t( [: Y. Y1 b# B8 G

  28. $ N: v. `! A2 _$ f, w; Y/ |
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    : J+ o& [% R9 O: V& @2 |1 O" k

  30. % q  V* I9 ~. t( |' E, k# C* A1 G- }
  31. ' k5 k$ F7 V( s# V/ P
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */5 p/ Q" N+ W( H" m2 D; v$ n2 l" U
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    ! [) U3 L9 {' b! G% l
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;
    9 u0 L  v1 W- i4 @2 D3 _6 o# q8 w
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;   
    + ^' W' Y; [& M: O
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    9 f; ^) m% [$ p. v' V% `; N/ d
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    . e+ o  W- e0 ~7 f0 T( U+ m
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    5 L# r& e4 v# ?7 i( w  P. r; z8 g; O) U
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;$ _$ e  O5 o( k6 S$ k0 _
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;" {. @! }8 `: H. L
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    " G2 H1 m/ e, j
  42.     MPU_InitStruct.SubRegionDisable = 0x00;
    6 L* Q* _' t5 T1 Z9 v: g
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    ; @# _. C3 N8 K- n' H, L$ G
  44. + v- O  o8 E$ t$ \2 _
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);! R: R% F# X) p3 V
  46. * o7 o' c9 v) s
  47.     /* 配置SRAM4的MPU属性为Non-cacheable */2 n+ B! G' x7 v2 W' r+ R
  48.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    - C  ]9 o. h1 t! @# @& Q
  49.     MPU_InitStruct.BaseAddress      = 0x38000000;) }' L, w( ~# F$ @9 `# A4 Z
  50.     MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;- A" _2 s: x0 i  u! I) ~; S; ^
  51.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    0 Z5 ~! K& |$ A1 w2 k
  52.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;, F. ^: X! _! V
  53.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    - w3 O, @! Y8 t/ ?
  54.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;  z& z4 n: s: n, @: W. c8 U$ m
  55.     MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
    : E4 V( ^0 t5 q$ t& k! b
  56.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;3 T# J( \3 w3 G' I9 `0 r; A
  57.     MPU_InitStruct.SubRegionDisable = 0x00;2 V( i7 Z! U  @7 V+ J5 y1 Q  i
  58.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;8 `3 i3 V- S1 m) o( J# @

  59. / q1 {& l( m" ]
  60.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    9 ^9 ?7 q- y$ G6 B9 f+ |
  61. 4 s9 p; t. {, D, s4 A* ]# Y. X
  62.     /*使能 MPU */
    4 i' i4 p* W3 u" O) z( t
  63.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);- ?+ {2 b. s/ f
  64. }. I/ L1 v1 Z- j
  65. ) y8 ]5 o' X2 O, i- p* v6 i% _
  66. /*
    6 D+ k$ d( [, v: {
  67. *********************************************************************************************************( w; m* w. S4 c+ r/ D
  68. *    函 数 名: CPU_CACHE_Enable' d/ {. }! ~; \
  69. *    功能说明: 使能L1 Cache& T+ q8 t3 g$ h+ y7 \" j
  70. *    形    参: 无
    9 O8 A# p# j' r; i7 T" o; f" w
  71. *    返 回 值: 无
      v; M: j# R, ?( R! U3 v* b# _
  72. *********************************************************************************************************9 V' O2 @" M/ y; H; f
  73. */
    & K5 @4 D7 t+ `0 f( x
  74. static void CPU_CACHE_Enable(void)
    3 N2 q3 u& Z7 n) |5 u0 F  L
  75. {
    + F- ?  N8 a' m2 A! S4 A
  76.     /* 使能 I-Cache */7 Z; N& Z2 l4 q1 F; e' s
  77.     SCB_EnableICache();& T( [% S; M; _
  78. . @6 l7 ~& m4 e- F* D% h/ v+ v( c
  79.     /* 使能 D-Cache */: V3 N( I6 s5 ^
  80.     SCB_EnableDCache();
    ) _* {! t; B1 d$ {
  81. }
    0 r5 C1 v0 R4 g9 a" c
复制代码
+ x1 ]' ^) Y6 [: Q" F
$ ~, b+ N0 n" M( o* r" Q- [+ Z
  每10ms调用一次按键处理:" v0 d. n4 j2 N: k3 n5 b

$ E! E5 g/ q" g: [" t按键处理是在滴答定时器中断里面实现,每10ms执行一次检测。
8 l  ^6 D5 N9 |/ {/ X; ^; z0 _' B) Z# W* D
  1. /*
    3 j& P! |0 d0 y' r
  2. *********************************************************************************************************3 k  N2 ~0 {) U) L, A9 l& U5 F
  3. *    函 数 名: bsp_RunPer10ms
    . s: J$ W# o6 u6 x" x* M2 q
  4. *    功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求2 r8 `5 i1 E$ m; D  I8 k, N# r
  5. *              不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。: _9 @! b1 E+ Q" }3 _4 ^, W
  6. *    形    参: 无" P- _! t$ Q; z
  7. *    返 回 值: 无" ]9 r# p  @0 J# c# Z( z/ C
  8. *********************************************************************************************************
    - K' i4 u, x8 v* A
  9. */, y9 d1 o1 f( _: i! z6 J! k$ U6 y7 V8 r
  10. void bsp_RunPer10ms(void)
    9 z: K6 x$ {: W2 f. w3 N0 p
  11. {2 u) T. X! M/ D+ C9 c6 z2 M9 E
  12.     bsp_KeyScan10ms();9 K* x% ^% L, \
  13. }4 q, B. T; x& y- v/ R4 q6 X
复制代码

5 z! B0 H8 `& e9 Q, u! a  f7 S$ E# h! K3 M+ K8 R' R
  主功能:2 [1 `( @& Z. w7 r' Q' u

1 X% \2 |2 R4 m8 t, h主程序实现如下操作:
2 o" O7 h6 i+ n) B
& m( t! p, O5 h1 C  启动一个自动重装软件定时器,每100ms翻转一次LED2。0 h2 J& [9 ~( N1 p) Y& k% L: i
  K1键按下,通道1输出方波。
! G% k4 r% S9 a/ Y( b3 b6 H  K2键按下,通道1输出正弦波。
1 R+ |$ u( `" D" b: h: B! }( T) S  K3键按下,通道1输出直流。2 y& g; c6 e6 u9 x' ~* W! L! R4 c
  1. /*
    7 q9 M( X0 r/ ~& W
  2. *********************************************************************************************************
    ; C* W9 b+ p' F  ]' e+ f+ q
  3. *    函 数 名: main9 ]/ }7 B3 }9 y: N
  4. *    功能说明: c程序入口$ B# Y$ z- G2 J, i  _; o
  5. *    形    参: 无
    : G/ e9 J4 m: e4 u
  6. *    返 回 值: 错误代码(无需处理)* b9 O' D% X! r. f9 o6 c" M! e
  7. *********************************************************************************************************
    5 t" k- J2 O& z) _% B2 w. }
  8. */1 {3 g: J- m1 ^
  9. int main(void)
    ) Z! f' E9 Q$ U: l
  10. {
    " @" G; @$ y8 `  S8 U6 w& N
  11.     bsp_Init();        /* 硬件初始化 */
    ; r# W$ C3 |3 |( ?

  12. # l, q' S) I% \
  13.     PrintfLogo();    /* 打印例程名称和版本等信息 */4 V# K# \7 B4 U4 f% I& _/ o

  14. . ?- f  ?; M, J8 y
  15.     DemoSpiDac();   /* SPI DAC测试 */
    " H7 V, ?3 X! R, \( I. R
  16. }$ P" Z$ Z" K; r+ V8 E- i

  17. ( l! n% P: v; X' e
  18. /*0 B. P4 h  G( }! i. U. y# Q
  19. *********************************************************************************************************
    , Q( R) y( [. W1 ?4 s
  20. *    函 数 名: DemoSpiDac8 o2 Q1 ?- t5 S" J( ]
  21. *    功能说明: DAC8501测试
    . {+ ?" R) X6 D1 W7 @. ?
  22. *    形    参: 无5 c" B: e" I- G7 M, `, @
  23. *    返 回 值: 无
    % v% X6 F2 W9 o* ]/ u! g
  24. *********************************************************************************************************
    ) p0 c+ }- Q8 B6 }- c7 i. r$ `
  25. */6 S$ g4 r( C2 Y/ {! P2 r8 [* y) O
  26. void DemoSpiDac(void)% o& _% B: L* ]- X2 q5 N
  27. {
    9 h0 J. f0 i" w- i
  28.     uint8_t i=0;  C+ Y6 o" m+ a, x& w# _
  29.     uint8_t ucKeyCode;    /* 按键代码 */
    5 U( x8 `3 Z: O- |; B- K0 m+ ~

  30. / y8 ^" N( Z6 q
  31.     sfDispMenu();        /* 打印命令提示 */& N5 m! j8 U( m. X, x7 w" n
  32. 0 e% x; @! y! c0 d9 V
  33.     bsp_StartAutoTimer(0, 200);    /* 启动1个100ms的自动重装的定时器 */
    7 ^% H2 o7 d/ D: D, v. J

  34. , l6 q% |* B4 ~1 B; J' w
  35. ) x. F( y' |7 ]- l, H
  36.     /* 生成方波数据 */  H3 M+ N) R' M9 F( n( D) T
  37.     MakeSinTable(ch1buf, 100, 0, 65535);
      ?  M( B9 ?- C
  38. 8 B8 Y, F, r% d3 e0 h6 e
  39.     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);
    2 c6 z" ?* b% P/ o
  40. 1 f' j& j0 j) E
  41.     while(1)
    : `6 [# L  @3 @: c
  42.     {+ |( ]6 _& [- I0 \4 T6 z( h; o
  43.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */8 ]' U8 h3 L2 s6 z
  44. 2 @! w6 V! R9 z/ k& ~) W1 C! a1 x
  45.         /* 判断定时器超时时间 */
    0 w* v- b& e' B0 K4 d
  46.         if (bsp_CheckTimer(0))    ; r1 p  f" F) {! l) J5 u: `- ^
  47.         {
    ) \! k* C. f! n
  48.             /* 每隔100ms 进来一次 */  
    8 p! g$ c; k/ e( t: T
  49.             bsp_LedToggle(2);
    " \" h0 s- G3 x, }8 z2 w3 x
  50.         }# h8 r& S, A$ ?& b
  51. 5 ~; {4 q0 M3 W7 J
  52.         /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
    + b6 D! v# l5 f1 S5 |* _
  53.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */) ?  ^4 K9 s2 Q, `% {
  54.         if (ucKeyCode != KEY_NONE)
    / V9 l/ k3 v7 Q) S3 V6 x+ }
  55.         {
    # e7 u7 D" \$ S  w1 D- y5 ^
  56.             switch (ucKeyCode): N6 M. a. A' q9 t% e6 k
  57.             {
    ) T6 T0 s& \3 |) q# b
  58.                 case KEY_DOWN_K1:            /* K1键按下,通道1输出方波 */
    2 }1 q9 g7 {3 @9 u8 F& ^
  59.                     /* 生成方波数据 */8 N9 Z3 C, `# \( g, q3 `+ A
  60.                     for(i =0; i< 50; i++)
    & v; ~+ e# n2 ^/ p
  61.                     {% ]8 K5 ?/ }6 X+ P. i
  62. <span style="font-style: italic;"><span style="font-style: normal;">                        ch1buf = 0;
    ' m9 z2 J( v9 _( U( Q/ t+ ^
  63.                     }
    " E5 P! K% K6 T+ V

  64. ' I' U3 v  u* D$ i% l
  65.                     for(i =50; i< 100; i++)
    7 e/ z0 J$ u  {0 G3 J$ K7 O
  66.                     {
    2 P$ H( T4 S$ R( B0 j6 ]% k
  67.                         ch1buf</span><span style="font-style: normal;"> = 65535;; u- B! F6 ]5 I' D/ P1 M+ X) g6 g
  68.                     }2 L5 T  ]& s, J. {# p8 p. |$ e
  69. # D% }* K: G, M5 Z" S
  70.                     /* 触发速度1MHz */. O4 G0 f0 _2 O1 N
  71.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);+ J$ e3 k: o9 `1 F9 r
  72.                     break;1 f  ^* Y$ n" G5 o" g( z( C
  73. & y4 Y6 a& W% p3 g( F# u
  74.                 case KEY_DOWN_K2:            /* K2键按下,通道1输出正弦波 */
    5 ~) S& T1 a; V1 _, x1 C, Q& Z
  75.                     /* 生成正弦波数据 */   
    8 L1 H3 Q8 |4 q4 [
  76.                     MakeSinTable(ch1buf, 100, 0, 65535);7 c, ]  F! ~7 s/ A% I8 E- W- [

  77. & J  p+ J( @- {; E0 w
  78.                     /* 触发速度1MHz */
    0 w6 V6 l* R9 ?6 q  |
  79.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);
    $ d1 W( f& A& W7 T5 ?9 L6 ^3 ?
  80.                     break;: e2 D& |% u. w6 R* I0 d
  81. 2 e9 Q) r- I: m* P4 y/ A; w' f
  82.                 case KEY_DOWN_K3:            /* K3键按下,通道1输出直流 */3 H' R$ c; e0 C2 A& M4 O, g; Y

  83. 9 X4 ]0 G8 R9 @0 I
  84.                     /* 生成方波数据 */6 U+ m1 B; I. T* G. a
  85.                     for(i =0; i< 100; i++)  X4 U! b4 F) J' m1 O
  86.                     {
    ' l  ]$ k: U& A6 W
  87.                         ch1buf</span><span style="font-style: normal;"> = 65535;2 f2 B- k! U: b. Q
  88.                     }5 j3 Z6 S; t0 y9 D( S" D" v
  89. ' i# m' B8 w7 ]  ~5 h
  90.                     /* 触发速度1MHz */* F. v" ?& Q9 l4 [! E- i5 U# ~" C
  91.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000);
    " p) a5 r! }0 q0 e6 P6 R
  92.                     break;
    6 F4 s8 W5 }/ e* S
  93. 2 O6 y9 @  Z% p  `
  94.                 default:
    1 C7 l' f5 o( j/ ^, f; _
  95.                     /* 其它的键值不处理 */
    6 n8 k& l( Y1 D; [6 [& u
  96.                     break;
    , T9 @9 @0 N4 ]4 u. c
  97.             }$ D- y; S; C3 h& t% s
  98.         }. [, Q  f4 i; m2 A: A- W6 n
  99.     }
    2 k8 J# @3 N9 g) L7 ~$ ^: A, r
  100. }</span></span>
复制代码

) D; p! d/ n, }  c4 J
  G, k" U8 m$ O: x4 E* {8 |  ]8 I
75.14          实验例程说明(IAR)
% t! `0 l8 J; V2 E6 x& |1 l/ c& N注:本章是配套了两个例子的,这里我们以SPI DMA方式进行说明。  M5 H6 A4 M, ?  \

6 b3 }$ }, [6 P( m配套例子:- w- Y7 S1 G3 @* a2 t( `
6 X% y7 F0 ?. z' J
V7-054_DAC8501简易信号发生器(单通道SPI DMA方式,16bit分辨率, 0-5V输出)
8 L& j! r! ~5 D8 ~7 H' f' G$ v7 O' i) m8 ~5 B& m. `0 v
V7-055_DAC8501简易信号发生器(双通道SPI查询方式,16bit分辨率, 0-5V输出)
4 m( h" ]$ o1 t2 e1 }1 z; R9 P
9 v  `. J1 s% c" s4 c& \5 ~/ z; B% g实验目的:
( V1 j3 `- v. i! f& _' `1 v8 c2 k4 ]* h+ z6 Y% s, \
学习DAC8501的SPI DMA驱动方式实现。
$ E  e2 @; A+ q* y; I实验内容:
; `5 I$ p9 O$ r( p" [( c& h
; k0 a4 Q' f$ i3 x; C, YDAC8501模块上带了两片8501,每片是单通道DAC,片上输出缓冲运放,轨到轨输出,16bit分辨率,支持30MHz的SPI时钟速度。
2 E# u4 x) C( o1 }# IDAC8501本身仅支持一路输出,而模块上是带了两片DAC8501,其中只有一路的片选可以支持SPI NSS复用, 所以只有一路支持SPI DMA。
' Q$ @/ b: |9 R, m+ V' IDAC8501供电电压2.7-5.5V,模拟输出带宽350KHz。
) w" m4 C& r: {; `% W0 w实验操作:+ D4 a/ V  d( j8 r+ u/ v
% l4 c) t  t' @6 r
启动一个自动重装软件定时器,每100ms翻转一次LED2。
$ i4 {5 L! k6 h5 s5 {/ }6 O3 {K1键按下,通道1输出方波。
! E7 H/ [- v, j, {+ e  TK2键按下,通道1输出正弦波。
3 s1 R7 F) \: F* O, E7 W6 u/ GK3键按下,通道1输出直流。- x  X1 l3 Z& A7 m
上电后串口打印的信息:
8 Y: N2 E* x+ r" U' P* ~9 v1 {5 ]5 }( ?' T& _
波特率 115200,数据位 8,奇偶校验位无,停止位 1。, V. Q9 H/ K* E' U* L; Q2 P
" z6 j5 X$ x% C- Z
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
4 S7 g. [# Q7 Y
; J" d5 `1 S) z* c6 E
波形效果:
( r3 v8 k1 e3 R% |' E# _: @$ l( a7 J: K, E1 t; v
! [  Q/ d# t# `; u0 {+ Y' g. C

" x, H+ s# d6 X3 o# i8 ~模块插入位置:
) ?1 n+ I& u2 W' Z0 _5 v8 }
) w/ ~- P8 {+ T) P
3 [5 R: T! y2 n; _% c' }

" {3 s8 ~% ~7 C" U# ?, e- `程序设计:
+ ?$ }  A' L) M) H" s( K9 {+ ?& Q# t6 o# E
  系统栈大小分配:9 P. v# N* Y# Q& m5 x( A0 D# \
% I, S  t' u1 i" e
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

' ?' N2 W# E+ y- g: d, _6 i9 _' h, M/ A6 P; O: l- q
  RAM空间用的DTCM:
& Y+ s$ F8 A: \) J2 V. z$ o) b- M# w/ d( q: i
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
5 T4 _; o& w: M/ d; G
- N9 y# o( l$ V  x' Y0 f6 Q
  硬件外设初始化2 [" M* K2 [& Q) \

$ A3 w* e0 ]1 g  q8 G硬件外设的初始化是在 bsp.c 文件实现:* o& a+ i4 R' I$ P0 z; a# b, @' h

8 G* u3 N+ C& d  z2 j/ `( `
  1. /*: w) H! D- H; |, g
  2. *********************************************************************************************************
    ) S6 i8 v: g3 D5 I
  3. *    函 数 名: bsp_Init9 u; @$ H; I: S+ L  I
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次+ p# `6 a% h  r* w& V# B
  5. *    形    参:无
    # A! x5 }" u( B- y
  6. *    返 回 值: 无8 h. @" S5 k" j2 R" |1 X
  7. *********************************************************************************************************
    4 a6 V) j# ]' z4 ~
  8. */6 j* G" |1 Z6 J! R
  9. void bsp_Init(void)
    ! ~# h# m! M7 j# E9 `
  10. {
    : M/ {1 W0 v6 [* g! I
  11.     /* 配置MPU */
    * ]% s  V: J4 }' d' A4 l
  12.     MPU_Config();0 a2 i2 p; V. c/ c' e

  13. 1 L) A; z- ]1 n
  14.     /* 使能L1 Cache */1 Y4 X( n3 ~8 m& M/ H6 O4 q
  15.     CPU_CACHE_Enable();
    0 e4 p: b4 K0 y% j7 ?9 i

  16. ) Q6 x4 T* f9 x% N' w6 U  }
  17.     /* . A) S) l& M( M4 [3 ~8 r  A
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:5 E; S- D4 A9 ]9 ~
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。" r4 p9 @8 p1 j) i
  20.        - 设置NVIV优先级分组为4。
    9 f( Z! d9 l( ]( A, C: I$ G
  21.      */
    4 I, |4 a! Z  E" c+ g0 q( z
  22.     HAL_Init();% ]! t* S5 C( ]# ?9 Q9 ?
  23. & |9 ?% f9 O( G+ p* M; O
  24.     /* 4 h$ M0 u; s/ i7 A' O: g( }2 H& o5 m
  25.        配置系统时钟到400MHz
    9 \9 F3 j0 V9 A7 W* u/ _+ m0 Z
  26.        - 切换使用HSE。
    + S7 v/ _7 f( y3 O9 Y
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    # u! s5 ?9 f9 e6 y
  28.     */
    - e% I$ X. `" H: m% v
  29.     SystemClock_Config();
    4 I9 [' ?) N5 V, U
  30. 3 i0 I" s& x. Q7 f
  31.     /*
    0 \9 u6 D" G! j' g  d! H* M
  32.        Event Recorder:' q+ ^8 N$ F! y
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
    ) [. L. [; Y' B
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章; G9 [. }, @# b& m
  35.     */    - r1 h# L+ E- h2 C/ K
  36. #if Enable_EventRecorder == 1  
    9 j, L& P. B4 l) m' V9 ]4 k
  37.     /* 初始化EventRecorder并开启 */
    ' H7 k/ o0 V6 B9 g) e
  38.     EventRecorderInitialize(EventRecordAll, 1U);
    8 P! f+ u# c( O
  39.     EventRecorderStart();1 U( z6 }5 v9 _
  40. #endif! c5 {/ g7 t! M$ L, i, l% ~! g5 X5 S5 s

  41. 9 q; o7 X2 c5 F  y0 F3 s# C
  42. bsp_InitDWT();      /* 初始化DWT时钟周期计数器 */      
    # f0 [5 t0 |4 n/ k4 H2 Q! I. Q. h
  43.     bsp_InitKey();         /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */6 P, T- A- x) Z; k" M1 b0 n9 @
  44.     bsp_InitTimer();       /* 初始化滴答定时器 */  P$ [( f" f' C3 r& y) _
  45.     bsp_InitLPUart();     /* 初始化串口 */
    2 F" A7 y6 s7 L0 n# Q& |0 z
  46.     bsp_InitExtIO();     /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */   
    ( n$ a" b* w$ H0 s: _% Y
  47.     bsp_InitLed();         /* 初始化LED */    9 t' n/ {) x) Q( A
  48. bsp_InitExtSDRAM(); /* 初始化SDRAM */
    2 O0 h' J4 H; W3 {: l& {) \' z
  49. ! H5 ?! I" l: h# v
  50.     /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */    $ M/ e# s! D5 g% U, m
  51.     bsp_InitDAC8562();    /* 初始化配置DAC8562/8563 */
    / @8 P+ Y/ Y  ~/ o  W
  52. }  `7 H/ B( G, [  W; D+ p9 ?
复制代码
. s, p1 ~! }0 o9 P
+ Z6 p0 |. r+ X1 G
  MPU配置和Cache配置:
5 a/ E+ \1 V/ {) `4 F% a$ C2 m  s* k1 G
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区以及SRAM4
, W  ?4 F$ H6 ^  M4 |8 @! o, k  Z7 q. t7 I# r
  1. /*
    & L& t0 S. n3 g0 b/ p* S/ x
  2. *********************************************************************************************************; K6 ^1 ~/ b8 }- B5 H
  3. *    函 数 名: MPU_Config
    9 r- F5 p9 w" h' q, O) [1 s
  4. *    功能说明: 配置MPU! u& |& j. R; a' }
  5. *    形    参: 无( Z' h5 X: q' ~
  6. *    返 回 值: 无) E. c5 l+ T0 ]  Z7 |
  7. *********************************************************************************************************
    9 D; s3 @- `3 \9 v0 t2 H
  8. */! b. G$ Y8 ^- N3 A/ [1 E
  9. static void MPU_Config( void )2 K8 g- g% d  e# l6 k6 W
  10. {5 o+ V& P5 q6 F6 v# F5 O- T
  11.     MPU_Region_InitTypeDef MPU_InitStruct;2 C* |  h( N! A4 _" ^3 \- r
  12. 9 O0 }' ^' [* p9 w; A
  13.     /* 禁止 MPU */
    3 F. ]! L. `) I4 I) w. W
  14.     HAL_MPU_Disable();
    2 {0 p0 t0 U6 a/ Y
  15. " E1 ^8 O/ f0 p( A$ b$ |, v: t
  16.     /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */' O! U# {$ Z7 a$ H# M, `7 x
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    ( i5 G8 J7 n% S. V) {; R
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;
    + `6 d) G1 B( E  ~1 N+ v# u
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;1 _+ j3 z. r5 w8 Q
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    * c& l2 D0 x% W9 W9 j: u
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    . S2 U4 v) Y) W% a0 B' e
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;5 w. v1 [8 F5 I1 r8 p* A7 w9 r
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;2 T- |' W9 x, e# x! H& k
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    1 @4 t  O9 K8 |! F* e
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    : n& u' p/ i( r
  26.     MPU_InitStruct.SubRegionDisable = 0x00;* @8 H4 u  i2 P3 s0 e4 B7 b6 j6 o
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;  |$ q3 E7 ^, g' C
  28. ) `# x' y! {3 q# p
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);' J$ z3 _4 t- v

  30. 1 D' G( d& O1 [0 {- P6 W( E% Z
  31. 7 K7 M$ \% Z! l- g' D
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */# X" h& g7 |3 ~+ y; W
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;) [& m% S( j# G8 U) @( V
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;
    # ~6 Z! f* v& k% h' L; g4 L
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    $ Y9 |7 Q4 `: u1 t
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    ; V' \" `# J0 h- ~
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    ( E, C2 D% S) g/ U) G; ?8 l) e+ J$ Q* ]
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;   
    7 a$ o2 I6 u" Y2 S$ r' p$ J- R
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;7 U- V* X7 i. b( s) B
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;, r$ b1 [% d; L0 m+ R4 u
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    + A/ a1 |( q- i* o3 t0 h
  42.     MPU_InitStruct.SubRegionDisable = 0x00;; ~; b0 [' e3 _7 S
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    0 D+ t- b7 y0 ]/ k/ H  X" _

  44. 5 k7 C5 s' |5 Q7 L
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    $ q7 `) u+ }3 \6 z$ p: W

  46. * z- C! e3 T8 u/ m
  47.     /* 配置SRAM4的MPU属性为Non-cacheable */+ K& u: b6 u) k( R) Q3 s! k5 l2 Y
  48.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    - D2 j% q. e0 W* f
  49.     MPU_InitStruct.BaseAddress      = 0x38000000;
    * s* N: b8 e& u9 a
  50.     MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;
    " e6 F( r" v  H" x/ x$ w- ~
  51.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    . d2 }8 P6 t$ R9 E& P' ?' n( W
  52.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
    . A" x3 W4 z  u) ^7 h
  53.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
    # f# D$ g& v, l% A; y
  54.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;8 G- a. U& ]3 [! \
  55.     MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
    1 s& R+ K2 V* T
  56.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;1 l* k) W8 {* u* P
  57.     MPU_InitStruct.SubRegionDisable = 0x00;! A6 x3 x' g5 R& I8 F+ K7 h+ h' I
  58.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    + l# F: R) s) ]" X1 W

  59. + l6 V( j% y4 ~) Q  X( P. j
  60.     HAL_MPU_ConfigRegion(&MPU_InitStruct);* L$ M, I5 q, E' X* E( f
  61. 1 Z3 w- d) y% W  M' Q' f  X
  62.     /*使能 MPU */4 r; D4 x3 d! u0 b# L, r
  63.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
    4 W! M4 `- x0 F, r7 D; }% B
  64. }* V! O! v1 ^* V  N
  65. 5 ?) z6 I# }. N8 i- q+ A4 |
  66. /*
    * Y% w( Q1 Q% V% C
  67. *********************************************************************************************************: v; t2 W' p8 A6 K, `
  68. *    函 数 名: CPU_CACHE_Enable6 Z8 `" A6 t! G4 H) L
  69. *    功能说明: 使能L1 Cache
    # H2 z6 x; z1 a: z, ]
  70. *    形    参: 无
    " E) |$ B4 C. S
  71. *    返 回 值: 无
    0 |0 E9 R3 K1 c% c
  72. *********************************************************************************************************; \5 N% ]5 v4 g) ]+ x4 u1 Z7 D
  73. */
    6 P3 h. q/ h# Z
  74. static void CPU_CACHE_Enable(void)
    1 Z: G* p' i1 }: v7 P& ]
  75. {1 w& F/ A% e' b5 i* r3 Z. P
  76.     /* 使能 I-Cache */
    9 I# J. j( e2 R# K; T* Q' Z
  77.     SCB_EnableICache();  Q' l: Z2 Q+ r& x$ i
  78. $ f" u; m# v+ U1 b3 }5 h% i
  79.     /* 使能 D-Cache */$ j; O1 p1 M! D0 ?* S6 O
  80.     SCB_EnableDCache();
    / ^' K/ Y( T! W
  81. }
    ) g' f  q' L# Y4 i  ~* ~5 N
复制代码

3 t8 ^- x3 X* y/ J3 I& ?8 Q) K  s+ a9 Y6 p
  每10ms调用一次按键处理:2 F1 x& B3 H8 m$ Q& d2 d1 o' ~0 d5 B$ T/ Z

: o! \2 G/ Y  j5 d/ W' N8 S按键处理是在滴答定时器中断里面实现,每10ms执行一次检测。1 ^9 j. |4 T1 f- k
4 [( ^4 n) L2 \; m9 H
  1. /*5 ^5 e* A; A! q4 V
  2. *********************************************************************************************************
    1 a2 E7 B3 @. X5 ^7 |! n% u
  3. *    函 数 名: bsp_RunPer10ms
    " l; m* P% s' t* N7 [. X( Q
  4. *    功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求* `3 {5 _/ X! b- y% K
  5. *              不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。
    : V" p1 B& ]3 Y- |, l/ q/ [
  6. *    形    参: 无/ y& o, r7 \7 Y8 S- R4 G
  7. *    返 回 值: 无* A9 n. G4 O" r" F9 w$ r) J9 F
  8. *********************************************************************************************************( o5 _: a3 K; }& z4 m
  9. */
    & [, |$ V) i* _: O5 B* [
  10. void bsp_RunPer10ms(void)  N3 Z5 B; n; f3 g4 i9 x, G
  11. {
    7 R7 r$ ]& A. z3 I
  12.     bsp_KeyScan10ms();! w* s. L- n5 @/ L
  13. }
复制代码
+ h# N7 e4 K  }: D
1 l6 m5 k- ^4 ]/ p

& e. ^; S9 X1 ~$ o  主功能:9 o; U+ ]4 {8 m2 W/ R

* }8 J  M; \* O3 C/ x主程序实现如下操作:* w7 q& ?5 A( F( M( [; p& A' ]$ ^" G
+ S8 b( q1 s& b  z% H& Q
  启动一个自动重装软件定时器,每100ms翻转一次LED2。
3 _4 Z8 w% X# ^7 z1 \  |% b( |8 a  K1键按下,通道1输出方波。
+ b& {* q8 O! P- d2 H; v$ f. X& B! v  K2键按下,通道1输出正弦波。& _. `) ~: Q2 @$ v' @% p) X3 u! h
  K3键按下,通道1输出直流。6 G  G% @! J3 G# l0 G
  1. /*
    " v- J) @% f! j! ^
  2. *********************************************************************************************************
    * H4 x4 f, Q8 i9 q
  3. *    函 数 名: main
    + P+ b1 f1 ~, i1 [9 Y  e2 A
  4. *    功能说明: c程序入口& M3 R4 I* f7 }, C2 l
  5. *    形    参: 无
    9 x* [2 @0 G" k7 C" X% j
  6. *    返 回 值: 错误代码(无需处理)
    % K* S8 L* g# ]5 I
  7. *********************************************************************************************************" Y8 L! i4 ^3 J  l2 n( ^. f
  8. */
    9 ]! ?$ N3 @  H$ Q* J! @+ @
  9. int main(void)
    . d* M' w, S( g0 P+ z
  10. {
    7 `4 q5 r* i+ ]- ^/ N2 X
  11.     bsp_Init();        /* 硬件初始化 */
    2 q9 z5 s4 V* X! J* b! _$ l5 }- {
  12. $ t: n% G( f* t5 D  G) d+ g
  13.     PrintfLogo();    /* 打印例程名称和版本等信息 */
    $ ^$ V' w4 ~( f7 }* G9 `2 Z9 `

  14. : E0 f; T% }2 a
  15.     DemoSpiDac();   /* SPI DAC测试 */
    / Q/ Z/ T5 E! C; O
  16. }4 |; Q$ h- b0 N6 {$ U' O
  17. 8 n5 y. f; D: W' l9 q; t$ s! _
  18. /*' M/ W1 A% \) k- Q. O* @9 }
  19. *********************************************************************************************************3 W. F$ t* F- T9 J/ y
  20. *    函 数 名: DemoSpiDac
    ) u$ Z2 [0 ~( L2 e( }
  21. *    功能说明: DAC8501测试
    6 I- n( G) r) \3 v
  22. *    形    参: 无
    $ ~$ Z! c6 p5 r7 d
  23. *    返 回 值: 无% O* F' M- ?) h& H) t) i3 n* \- Y
  24. *********************************************************************************************************
    8 z9 a4 n6 V; z: Y' P/ J" @+ ~
  25. *// W8 v/ p: d! O( N
  26. void DemoSpiDac(void)+ |( R1 W4 [6 x' S2 o2 R; G* k
  27. {: |! U" _) ^0 Q$ b. b0 u
  28.     uint8_t i=0;
    7 f' K5 w8 ?& y4 c+ ~
  29.     uint8_t ucKeyCode;    /* 按键代码 */
    # y- R* R$ y: C9 o5 g

  30. 1 u& D5 ]$ s1 S
  31.     sfDispMenu();        /* 打印命令提示 */% c+ @1 n" ^4 T# m  S
  32. ' _* k$ {; N. }4 ~  r
  33.     bsp_StartAutoTimer(0, 200);    /* 启动1个100ms的自动重装的定时器 */
    # I: |, S/ |1 w* ?4 y- V2 i( q

  34. 1 [6 q6 A1 F# m# v  r6 b

  35. + |5 ~  q2 s7 _/ s) B2 s3 ~
  36.     /* 生成方波数据 */
    ; X0 T# M: O# _3 Y: A2 Y2 q
  37.     MakeSinTable(ch1buf, 100, 0, 65535);
    - m* t$ x: y' {1 D3 [5 g

  38.   W: T6 H$ m4 c8 R' B3 s+ X
  39.     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);
    ; |( \' \0 R( I* O, v1 B. S+ `; M) _
  40. ' U+ M0 }- |2 o5 B
  41.     while(1)# v+ b- c5 u5 c- c& H0 F
  42.     {
    * Z: p$ Q6 E7 N. {0 m4 U% f( S
  43.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */' y% P* z" T$ i  P# o1 |5 G9 i
  44. 9 P+ S8 I) Y3 ^5 J* K& n1 b" H1 L$ A
  45.         /* 判断定时器超时时间 */
    0 B: I6 f8 Y" t$ p' Y- A* z
  46.         if (bsp_CheckTimer(0))   
    - C# a2 Z  q. |, v$ E
  47.         {; p$ J$ P; I% p7 V- \8 X& m
  48.             /* 每隔100ms 进来一次 */  * J& }: w7 \; j7 _
  49.             bsp_LedToggle(2);5 y' B8 v9 Y) U5 b, P) r( m3 x) b! B
  50.         }% o% O. Z# l- G* ?: r6 q9 J4 _& x% U
  51. % w6 b( w3 c, E" U$ q2 H+ I
  52.         /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */5 R) C. v* G( f, B4 F3 U7 T6 N
  53.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
    8 {6 n% Y* }, z
  54.         if (ucKeyCode != KEY_NONE)
    & u2 l( t4 {- i3 z# ?% j& F
  55.         {) H# w3 x7 ?+ J
  56.             switch (ucKeyCode): T0 J2 {( t# ~. A
  57.             {
    7 W% f4 |/ t* u, W" i3 U
  58.                 case KEY_DOWN_K1:            /* K1键按下,通道1输出方波 */
    ) @0 H: y) X) X
  59.                     /* 生成方波数据 */
    2 T+ z1 F* M" V$ z. d' _
  60.                     for(i =0; i< 50; i++). }% W8 ~/ d- g* \
  61.                     {
    : e' l9 G- ?# \' R+ [
  62.                         ch1buf = 0;* I. B& E3 G4 B- P  U- `
  63.                     }
    $ c) W8 O  J+ d- V
  64. 8 z3 K+ @9 u3 W# Y4 y. b* [- y6 ~5 q
  65.                     for(i =50; i< 100; i++)6 m& G/ Q& T& R7 g! A( p8 q! b
  66.                     {) q0 ]! [/ k2 m. f
  67.                         ch1buf = 65535;# K- ^) B- I4 }! _1 F
  68.                     }  ~' R% t' D; W4 B7 d! X
  69. ( w9 H  o( o& o4 b) W
  70.                     /* 触发速度1MHz */
    * g: ~& q) `, `  e+ e
  71.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);
    % Y' D5 I# B3 `3 k
  72.                     break;- l. {) b7 W; w; A: U+ D! M

  73. . g) D9 m, {- [% c$ g
  74.                 case KEY_DOWN_K2:            /* K2键按下,通道1输出正弦波 */' D# J; V% A8 D% y6 h) J# }: ]1 ?
  75.                     /* 生成正弦波数据 */    4 s" I* ?  g7 \- k; r6 g
  76.                     MakeSinTable(ch1buf, 100, 0, 65535);
    9 D! B8 ~4 N; E$ f% c; I
  77. 3 g5 r+ ?0 d' A3 b3 H. y
  78.                     /* 触发速度1MHz */
    ; t3 t9 w5 _" R( E$ {4 r& W
  79.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);
    / @, s. s* M2 ]% t! u: F
  80.                     break;5 Q" K' e/ w1 t2 J) {3 ]& P
  81. / V7 m2 f/ g: W1 `
  82.                 case KEY_DOWN_K3:            /* K3键按下,通道1输出直流 */* `" l% Y$ G/ R2 |4 R

  83. 2 ^7 S9 f1 H8 E* @/ i# g
  84.                     /* 生成方波数据 */& l; t! e. I* q$ C+ u9 K4 X. H
  85.                     for(i =0; i< 100; i++)
    5 z% R! K3 B1 A
  86.                     {: C( N/ p! i; {
  87.                         ch1buf = 65535;) ]' C, W5 G. ^3 K
  88.                     }
    ' d4 h+ I: J& a9 A' t
  89. ) b- A+ l+ e4 n) S! o0 }
  90.                     /* 触发速度1MHz */) t, H4 V( @6 t1 n
  91.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000);+ H  r6 u/ n+ i: F; t
  92.                     break;
    ( {$ ^7 \- @: u: }2 S

  93. $ q1 D& k2 x8 P: o
  94.                 default:
    2 f7 U% o  X; V+ t9 ^+ t6 N
  95.                     /* 其它的键值不处理 */
    2 v6 z3 U- V% L6 j' _4 n8 j6 y
  96.                     break;' e+ P' _7 c/ [$ M  ?( q
  97.             }) M- M0 V7 ], a! [% w" I+ F, t
  98.         }; z  s- @0 f/ _" B, ~
  99.     }
    # a& A/ ^2 a. d; L
  100. }
    2 ]) [4 K& i( _6 W2 j0 b- B
复制代码

8 K, F) \( b+ A" }8 T1 O) }/ y4 }7 f! x% V
75.15   总结
4 X/ e( t; o  r6 S+ }- Y* f# E本章节涉及到的知识点非常多,特别是SPI DMA方式驱动的实现方法,需要大家稍花点精力去研究。" {) f( W; Z2 S2 M( v7 K
" S- ?# Z5 }: ~
. s. i* v* Q* Q6 \. w% w/ g& y
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
赞 收藏 评论0 发布时间:2021-11-3 10:44

举报

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