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

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

[复制链接]
STMCU小助手 发布时间:2021-11-3 10:44
75.1 初学者重要提示0 @! S- T# L4 x1 N1 C0 m
1、  学习本章节前,务必优先学习第72章。
' R% s$ l9 \6 C, ]" M6 Z: W
( Y0 O0 o+ t7 `. Q' R' P- e2、  DAC8501模块上带了两片8501,每片是单通道DAC,带片上输出缓冲运放,轨到轨输出,16bit分辨率,支持30MHz的SPI时钟速度。2 L7 a( o# t; B% D. B* }) K
0 k: X* o, T5 h2 ?) g" O
3、  本章涉及到的知识点比较多,需要大家掌握STM32H7的SPI , DMA,TIM,DMAMUX和DAC8501的一些细节用法。8 }) H8 {4 I( R1 q: P$ q

* b+ h/ {, o5 O5 ~4、 H7的SPI + DMA驱动这类外设的灵活度,绝对可以媲美FPGA去控制:  m" Q" `! \4 C% r, Z. Z, `8 b. D
) b* @' w' g4 r/ ^$ W0 d
  H7的SPI外设比F4系列的灵活性强太多了,主要表现在两个方面:数据的传输支持了4-32bit,特别是那个NSS片选引脚,超强劲,可以做各种时间插入,灵活应对了市场上这类芯片的需求。
- x$ @1 u2 K9 p- K. X. n  DMA这块相比F4系列,有了质的飞跃,支持了DMAMUX,这个DMAMUX除了带来灵活的触发源选择,还支持了各种触发事件和同步触发功能。本章配套例子的触发周期控制就是利用了DMAMUX的同步触发功能。6 o9 F0 u+ K$ \2 g7 i8 ^' ^
5、  本章配套了中断和DMA两种更新方式的案例,DMA实现方式与中断更新方式完全不同,因为DMA方式要使用硬件SPI1 NSS片选引脚驱动DAC8501。而中断更新方式使用公共的总线驱动文件bsp_spi_bus.c,片选是通过通用IO方式控制,支持串行FLASH、TSC2046、VS1053、AD7705、ADS1256等SPI设备。大家在看例子的时候要注意。7 [2 W! x7 j9 U# O+ X& ~& u0 Q
& ?5 Z! o+ B* y% o: S
6、  对于本章教程配套例子的SPI DMA方式,这里特别注意一点,定时器触发一次,就会让SPI以DMA方式传输24bit数据。
. w, E5 o+ {+ w! [
9 Y3 U! ~8 a1 G- b8 n. R7、  DAC8501数据手册,模块原理图和接线图都已经放到本章教程配置例子的Doc文件里。
9 L7 O4 U6 G7 }5 u
2 \% @; i  t, Y  B75.2 DAC结构分类和技术术语, t  D/ h1 {! A4 Y: v
在本教程的第74章进行了详细说明。
5 A( g4 B  J/ V: M& q* [6 D! X/ D% u+ b$ R7 ?$ J& n8 v
75.3 DAC8501硬件设计* m9 O7 Z) V3 G$ p* G# _
75.3.1 DAC8501模块规格
: U0 O. B( d, \8 E# c8 H/ [产品规格:& E% v$ L4 D' p& _, r, }4 P$ i8 }1 Z

0 R6 O9 I$ \. \% P5 V% L. l2 M7 A1、供电电压: 2.7 - 5.5V【3.3V供电时,输出电压也可以到5V】。  i$ z/ t- F3 \% Q2 N5 E; _' o

+ C: M; A6 I% t2 Y) h5 r, n2、通道数: 2路  (通过2片DAC8501E实现)。
/ I. a  f) U5 \5 f# o  M& \/ m8 D$ g9 Q; T8 i
3、输出电压范围 : 0 - 5V【零位 < 0.020V, 满位 > 4.970V】。0 Y8 A  h3 A* x8 B4 y  V7 _

& Y0 ^+ I& r7 t: c; }9 Z; j4、分辨率: 16位。
. _1 m; X# z5 t( x( o& R# d/ h* Q! l8 {9 I/ X7 X: [
5、功耗 :  小于10mA。
. y4 _0 ?% X9 u  t5 j: f1 j. ^3 U
: j! K0 V2 F4 d2 F, t' A6、MCU接口 :高速 SPI (30M) 支持 3.3V和5V单片机。
. }' {: I9 A1 Z6 H8 e7 N* \) f% E2 G; G6 v( V4 K! W: }
7、DAC输出模拟带宽:350KHz。) Y: ~/ I1 n6 {% `2 z6 B
; @5 V3 b; p/ F& w, `
8、DAC输出响应: 10uS 到 0.003% FSR。
/ N4 z3 l: v; t- H$ O5 n- I' f" a* Y; s! s$ E
产品特点:
& u# t5 V5 S# _( r3 \8 h6 E. a$ r6 K1 Y3 D6 m
1、输出和供电电压无关;模块内带升压电路和5V基准。
2 b" m, A0 N$ ?0 ~, K3 Y) x" y
# `: r, I  G  o) i" k2、自适应单片机的电平(2.7 - 5V 均可以)。
2 I- G/ F: f$ p1 K
2 o$ n& _6 a* M3 N, Q7 w2 X3、输出电压轨到轨,最高电压可以到 4.970V 以上。
8 d7 S, m' ?" m6 L7 M- o# r3 J' J  t6 @7 G
产品效果:0 N: l" ~' j! G( D

5 P) D5 Z8 A& s: }8 J0 o, m6 G* D
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
4 N5 Q! V) L! p

! |4 }( n5 ~8 _6 [4 T. z' d
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

- F$ P( T! z! {% k7 Y+ G
% r3 l6 }5 @0 n  j: ?75.3.2 DAC8501硬件接口) K, x2 m# R5 a3 m9 k
V7板子上DAC8501模块的插座的原理图如下:
( e4 l! [8 v, w" P. ?" }4 v2 w8 o. C
- T; x0 s* U& C& b& H
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
' R" R% z# b. n

' M% t0 C, d! ?实际对应开发的位置如下:
% U2 m% q1 d7 `  b0 }* V* ^6 o, X" F& J. Y/ `$ |
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
* i% c. P" k0 F% m

" ^% _* G) k& P7 D75.4 DAC8501关键知识点整理(重要)8 {' }: J% T6 H1 n8 u# S
驱动DAC8501需要对下面这些知识点有个认识。* ?7 s) d3 ^/ g) B

' E2 z+ G; a( M/ H* }75.4.1 DAC8501基础信息
6 @9 E8 ~1 q7 x' q7 p* r  单通道DAC,带片上输出缓冲运放,轨到轨输出,16bit分辨率,支持30MHz的SPI时钟速度。
1 C6 s( j3 o4 Y) M3 z1 u  模拟输出带宽350KHz。- I# C. _& O$ Y! t4 ~
  供电范围2.7V到5.5V。: V% h  J- s: H
  具有低功耗特性。0 W4 U' \2 F0 B$ I+ ^4 c
  上电复位输出0V。
; a6 p2 ~/ x* J0 u
7 M+ N* L5 d, C: T$ s
* [5 O3 O" r3 ~" f75.4.2 DAC8501每个引脚的作用
4 w( ?2 S$ s+ c4 g% }( fDAC8501的封装形式:, u* i8 k1 i  A
2 ?! r, w. D5 k% X/ @- q
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
# E: X! s+ n- Y$ m- @* w  n
5 s4 C) m  F: a3 `
  Vdd
( t* f, }: V/ K9 W& D供电范围2.7-5.5V。
, f7 u" [$ `1 Z# R0 A  g" W. o
; r  y" s/ Y6 C  Vref
8 _% {, ?2 D. |6 P  j+ `稳压基准输入。
, t  w  z& j2 \# N- a
+ {+ ?! p8 C( E- G4 u  Vfb; p3 I  j# r- [* P
输出运放的反馈。
9 [! T$ a1 O. \' B9 k) l2 j4 F0 R. U
  Vout2 t8 S- z0 g5 j. R
模拟输出电压,输出运放具有轨到轨特性。
) `& p% ]" V5 c9 _
- |/ g  D7 H- T( @+ c+ J9 z; t  SYNC (片选)& B0 E4 ]0 q3 K3 k' `3 I7 W+ u- `
低电平有效,当SYNC变为低电平时,它使能输入移位寄存器,并且数据采样在随后的时钟下降沿。 DAC输出在第24个时钟下降沿之后更新。 如果SYNC在第23个时钟沿之前变高,SYNC的上升沿将充当中断,并且DAC8501将忽略写序列。! W8 I; X# S" \0 a3 p; h
. u% S/ r& @% G& g9 J; m
  SCLK
$ F* [+ u9 j3 d" x+ |  i7 }7 i8 o时钟输入端,支持30MHz。. G$ e2 x# b' U( [* ^! l; b" |" g; L$ S
5 s1 i- ~* W/ j+ h8 F* \
  Din: G- k0 g6 n& U8 a6 h1 {* |4 O
串行时钟输入,每个时钟下降沿将数据写到的24bit的输入移位寄存器。
. z- Q: Z8 k& ]. {: t- I, q
  d- S. d, m3 q  I  GND
* N. }7 U3 N, w8 m$ _% H! J7 ~4 C接地端。3 z0 D9 w  m/ O0 Z: J( Q' ]
4 D. I( }( F! m5 Q: V
75.4.3 DAC8501输出电压计算公式5 Q5 J+ d, M7 n, w- _6 ^$ s
DAC8501的计算公式如下:
  u( @" ]9 G9 w5 g. W' F* o$ C5 a5 S+ `4 \3 r$ L4 S4 |
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
1 B) K' f5 m* \9 Y- @( ~- J: ^1 e
3 n' D! s" h0 d6 S/ s0 T
  D
: L, k' D! [/ p8 W# s  f; x配置DAC8501数据输出寄存器的数值,范围0 到2^16 – 1,即0到65535。5 D$ }* [( p% E. g! a' i, j
- m: {* C: c6 G9 I" A: F% i4 D
  VREF
" ~) L  F5 r+ Z9 t* R% U$ I使用外部参考电压,由VREFIN引脚的输入决定。
% b! e1 A+ q9 z. w; Y  N1 w* V
( d; k7 {( J* E4 g  Vout, I$ v6 f% A6 E  \- P/ ]$ B
输出电压。
# j" c2 {( ~7 v+ c
1 U. T1 o( M" o/ ^" n! D% Z75.4.4 DAC8501时序图. S' n" @3 n8 T" u; p6 I6 P0 L
DAC8501的时序图如下:) n6 n! X1 }# f' A2 d4 e* ]

' O0 V0 j) l$ q, D" G
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
, ?" v( V# r; F; g
1 ~+ z$ R7 {6 a
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

( T/ R! S1 E; s8 j* ~; {4 {3 @5 L; s7 `. v, ?% T
这个时序里面有三个参数尤其重要,后面时序配置要用到。' I# F* B$ l% Z9 J

0 G5 I, e; x7 h$ Q  f(1)5 F6 a# P+ t/ i" s* x" I( W) }. n4 s
供电2.7到3.6V时,最高时钟20MHz。! e' w, f+ u5 A4 k2 w6 Z! R+ l4 S
! W+ F6 V; b) f( B: v% k
供电3.6到5.5V时,最高时钟30MHz。
- [, x. k" q0 G
6 _2 [: G6 s; t  v" ^* a/ {- j$ ]8 V  t(4)! n3 k" i1 J7 r
SYNC低电平有效到SCLK第1个上降沿信号的时间没有最小值限制,可以为0。* S0 r7 O6 F; Q- c7 c
& d2 q" j1 |6 k$ u& ~+ \
  t(8)
: C5 j5 O4 O$ e/ d每传输24bit数据后,SYNC要保持一段时间的高电平。
; ]1 e8 V8 V: y8 O
' v' v/ G7 O$ L供电2.7到3.6V时,最小要求50ns。
* ^+ R; i. k% x! F" g1 N& k
; V3 z3 ~* c0 p( V7 q供电3.6到5.5V时,最小要求33ns。
6 a, s0 P. k5 p8 ?
1 [7 ?, Y; {5 k/ O4 c( N75.4.5 DAC8501寄存器配置0 H; ]) u/ R( }- }
DAC8501的寄存器配置是24bit格式:
6 k7 h" |! e( M- F# Z2 [( r8 y3 |% A7 ^7 v
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

+ A+ k' S: H* E: L) v: w6 g# H& U# J
控制DAC8501每次要传输24bit数据,高8bit控制位 + 16bit数据位。
1 j, f) G/ M& \
2 H9 U) W) i2 k7 _3 I" Y' F控制位的PD1和PD0定义如下:
' ?+ f( _1 a; C0 f% ]
" m1 z! Z  n: E0 q- M4 A5 h) D! b
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
- G! X+ |' `/ O2 E. g

8 ]( a/ J1 G9 u4 G4 A  o3 QPD1 PD0 决定4种工作模式
& E, m+ ]( M2 ?6 F" J0 S" d' ]. a# d
$ g" E7 p6 ]' b/ S0   0  ---> 正常工作模式8 O/ `) @0 x1 w

! h9 C3 M" z4 d5 p0   1  ---> 输出接1K欧到GND) E4 v- [; t) D% y! E

6 o6 s% S0 i' G4 Z4 X; E1 `9 t5 D( A* Z1   0  ---> 输出100K欧到GND1 X3 l' ^6 h' x- A0 {, D2 [% w

; c2 L' V) e3 f1   1  ---> 输出高阻/ g9 q+ {# z: N( {: w( i' E( j. b
! ?6 _! }$ R8 f& ]
75.5 DAC8501驱动设计(中断更新方式)7 W* ~: [  D2 e/ C" l$ f$ B
DAC8501的程序驱动框架设计如下:  {$ O$ V3 v9 }- x# O' u: }
: c' S5 ]) u4 c: X0 u
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
/ S  P. b2 @. a+ ?$ G) P
8 R; y* ^% K6 D9 G' K$ i  D
有了这个框图,程序设计就比较好理解了。! [2 b* E) u+ }- j4 c# W- A

7 O. ]% a  n- C! ], j75.5.1 第1步:SPI总线配置
% P1 g: D+ Q" S# T- A5 l/ W, t& qspi总线配置通过如下两个函数实现:
2 d5 V/ o" y" R8 i$ b9 G7 |: j1 P5 ?- B$ J$ R. ^8 ~, d& B, [& \4 W* g; W! t
  1. /*
    8 T1 \' g9 U2 t
  2. *********************************************************************************************************
    ' t( M* a5 j$ J
  3. *    函 数 名: bsp_InitSPIBus' g9 ~' H, p9 z+ ?4 u) H
  4. *    功能说明: 配置SPI总线。2 Y9 @, i9 V6 G$ \7 x
  5. *    形    参: 无
    9 r4 p$ O  f! q! U% l' u
  6. *    返 回 值: 无
    : P, r! E: ?' z7 w: `
  7. *********************************************************************************************************
    9 \& s* Q5 ~' H& I0 f1 S5 _
  8. */, Z1 `& s$ G* n7 z( |- A1 i
  9. void bsp_InitSPIBus(void)8 e8 i! \( H7 h4 A! A
  10. {    : I5 }' L* w8 a
  11.     g_spi_busy = 0;/ e8 q* R- p- J4 y; \

  12. 1 k2 T3 g, u# g% f2 v
  13.     bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_1EDGE, SPI_POLARITY_LOW);
    $ l" `! Y6 M4 |, A  Y9 G
  14. }2 ?1 q, U( t0 _" a( x' h8 f, p' V

  15. 4 K9 Q) b. l% W- ^9 s9 E
  16. /*
    ; p+ T2 f8 I( E8 |; i& k
  17. *********************************************************************************************************
    6 `5 a+ Z% D& T2 A
  18. *    函 数 名: bsp_InitSPIParam- n) @$ t6 T$ M/ O  j! P
  19. *    功能说明: 配置SPI总线参数,时钟分频,时钟相位和时钟极性。) [9 ~+ W7 U$ I9 e0 ]) d
  20. *    形    参: _BaudRatePrescaler  SPI总线时钟分频设置,支持的参数如下:
    5 g3 x( L3 `& @0 P" X5 z; n
  21. *                                 SPI_BAUDRATEPRESCALER_2    2分频
    ( m8 c$ J6 t( S5 D2 y
  22. *                                 SPI_BAUDRATEPRESCALER_4    4分频, R, o+ A' k1 a1 W9 C! z
  23. *                                 SPI_BAUDRATEPRESCALER_8    8分频) {1 U+ M1 h+ {
  24. *                                 SPI_BAUDRATEPRESCALER_16   16分频' U  c  t+ b( y5 ?; n9 G9 r4 U
  25. *                                 SPI_BAUDRATEPRESCALER_32   32分频& j6 E/ v$ K) j  n8 X) b1 f
  26. *                                 SPI_BAUDRATEPRESCALER_64   64分频
    7 e0 Z+ L" D1 k: B2 R
  27. *                                 SPI_BAUDRATEPRESCALER_128  128分频
    ) q9 N, j: R3 t( o9 V! N
  28. *                                 SPI_BAUDRATEPRESCALER_256  256分频1 g2 Y2 z2 a% e/ M/ T3 a+ K  H
  29. *                                                        2 T: R- {- v  j# O
  30. *             _CLKPhase           时钟相位,支持的参数如下:5 |) g9 P' U2 D; h; j% t0 F
  31. *                                 SPI_PHASE_1EDGE     SCK引脚的第1个边沿捕获传输的第1个数据
      Q1 O7 c( i! H1 L: [; |+ F
  32. *                                 SPI_PHASE_2EDGE     SCK引脚的第2个边沿捕获传输的第1个数据1 m7 a& v; ^5 N: F  F& J* u7 |
  33. *                                 ( Y1 K8 Z3 O  W/ x7 t! y1 o
  34. *             _CLKPolarity        时钟极性,支持的参数如下:. m. I2 C5 P9 t9 t' G% {- B* H" i
  35. *                                 SPI_POLARITY_LOW    SCK引脚在空闲状态处于低电平
    ' O: W2 w( ~' I+ j/ u2 }
  36. *                                 SPI_POLARITY_HIGH   SCK引脚在空闲状态处于高电平
    0 Z9 e( }$ s+ N! v4 K0 R. a
  37. *
    ( T1 ~; _/ K, i$ z$ k
  38. *    返 回 值: 无+ Z+ M% T" U  `8 a* z$ O8 g
  39. *********************************************************************************************************) o: M% S: O; q4 D, E# R0 W
  40. */8 I/ s3 n. l  {) u7 ]. F& k: O
  41. void bsp_InitSPIParam(uint32_t _BaudRatePrescaler, uint32_t _CLKPhase, uint32_t _CLKPolarity)* D" ?9 n7 p" f+ }# g. \
  42. {# A' f( o- L0 i% O$ O5 c) c* X
  43.     /* 提高执行效率,只有在SPI硬件参数发生变化时,才执行HAL_Init */  |/ d4 ?3 b4 W7 M: L+ [- H
  44.     if (s_BaudRatePrescaler == _BaudRatePrescaler && s_CLKPhase == _CLKPhase && s_CLKPolarity == _CLKPolarity)8 |" F* v8 f1 p* f- d' S% @
  45.     {        " b2 |( r8 R9 K! b9 R
  46.         return;
    1 A4 B$ `, B0 G8 z- `6 Q
  47.     }  a8 z( i+ L3 U; ]% f, s
  48. / s( @: }. [" }& Y& J/ R. d* D
  49.     s_BaudRatePrescaler = _BaudRatePrescaler;    ! Y$ o4 r3 M/ l% q/ A  h
  50.     s_CLKPhase = _CLKPhase;
    ' o1 K. }) X9 d" W
  51.     s_CLKPolarity = _CLKPolarity;
    + @+ v/ ?' I$ u8 Q: u

  52. : a: U( l/ H: E& t1 E  V

  53. 6 H' w8 e! r: N4 k4 j! s# D) Z
  54.     /* 设置SPI参数 */
    ) B5 p6 a2 ?* y, j
  55.     hspi.Instance               = SPIx;                   /* 例化SPI *// o& [( u1 A6 R8 o1 y% J) R
  56.     hspi.Init.BaudRatePrescaler = _BaudRatePrescaler;     /* 设置波特率 */3 w3 P* h( T. z
  57.     hspi.Init.Direction         = SPI_DIRECTION_2LINES;   /* 全双工 */
    8 u' ~3 m& Y1 V" U
  58.     hspi.Init.CLKPhase          = _CLKPhase;              /* 配置时钟相位 */, N7 Y& E7 C: \+ P7 k% X& H
  59.     hspi.Init.CLKPolarity       = _CLKPolarity;           /* 配置时钟极性 */
    $ T7 S, ]) d2 H8 ~( `; e1 k, I9 S
  60.     hspi.Init.DataSize          = SPI_DATASIZE_8BIT;      /* 设置数据宽度 */
    5 Y' J; a1 F( K. c
  61.     hspi.Init.FirstBit          = SPI_FIRSTBIT_MSB;       /* 数据传输先传高位 */  @2 V* n% ~2 p, C6 x/ f
  62.     hspi.Init.TIMode            = SPI_TIMODE_DISABLE;     /* 禁止TI模式  */
    ! F$ i, b6 [! G* H
  63.     hspi.Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE; /* 禁止CRC */* Y* w- M4 g; l3 Q* T5 B! C
  64.     hspi.Init.CRCPolynomial     = 7;                       /* 禁止CRC后,此位无效 */
    & A# K8 D% J. u, T
  65.     hspi.Init.CRCLength         = SPI_CRC_LENGTH_8BIT;     /* 禁止CRC后,此位无效 */; \! p" M% Y2 U$ H8 C  U* }
  66.     hspi.Init.NSS               = SPI_NSS_SOFT;               /* 使用软件方式管理片选引脚 */
    8 _( J( P0 Z: j8 P
  67.     hspi.Init.FifoThreshold     = SPI_FIFO_THRESHOLD_01DATA;  /* 设置FIFO大小是一个数据项 */" `' i+ E6 W. c5 V5 G+ n6 J, z
  68.     hspi.Init.NSSPMode          = SPI_NSS_PULSE_DISABLE;      /* 禁止脉冲输出 */
    5 }3 D" j% N$ Y; Q' ^' G$ J  Z
  69.     hspi.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE; /* 禁止SPI后,SPI相关引脚保持当前状态 */  
    ) X$ k: }3 E: y, w
  70.     hspi.Init.Mode             = SPI_MODE_MASTER;            /* SPI工作在主控模式 */1 R) O/ K, h. D8 ~9 ]4 }
  71. 9 e. T& }2 Q: H
  72.     /* 复位配置 */
    4 D" H& P9 \8 Y0 U  j1 ~
  73.     if (HAL_SPI_DeInit(&hspi) != HAL_OK)# {) ?+ \% S0 H& L+ O) Y1 q8 S# d& E
  74.     {( C6 y& s( p# Z3 i' k" x3 N7 ^
  75.         Error_Handler(__FILE__, __LINE__);
    8 Y* d, h& u8 q1 H
  76.     }    6 L3 ~7 Q+ q1 v
  77. 9 R. {6 b4 z1 C7 H/ I( Y
  78.     /* 初始化配置 */$ B9 o* e" ]4 M: C" o7 V) E
  79.     if (HAL_SPI_Init(&hspi) != HAL_OK)% R# m+ p2 b* g% U6 D9 |8 ^2 {/ u
  80.     {7 ^) y; P# F% z, p
  81.         Error_Handler(__FILE__, __LINE__);) g2 o1 Z% _! ~9 Z
  82.     }    7 n4 ^  R+ `0 m# \3 {0 A2 x
  83. }
    & K& @& J/ c5 Z  J5 D
复制代码
* K/ P1 z$ h: C6 T. l$ \1 G

) q& W8 h2 ]: B. O8 `3 H$ l关于这两个函数有以下两点要做个说明:
6 \, \) o! H. M8 A2 T2 {4 Q4 _1 K8 O" R6 [# P! C
  函数bsp_InitSPIBus里面的配置是个初始设置。实际驱动芯片时,会通过函数bsp_InitSPIParam做再配置。3 o  M7 L5 x' D4 V$ c" \! N
  函数bsp_InitSPIParam提供了时钟分频,时钟相位和时钟极性配置。驱动不同外设芯片时,基本上调整这三个参数就够。当SPI接口上接了多个不同类型的芯片时,通过此函数可以方便的切换配置。. e  Y( j2 e  H& c
75.5.2 第2步:SPI总线的查询,中断和DMA方式设置$ [, h) ~- X0 u" a* y- l; H7 z
注:对于DAC8501,请使用查询方式。8 g6 v/ f) B& f: B% l2 H

! P8 u- d' O' K+ ISPI驱动的查询,中断和DMA方式主要通过函数bsp_spiTransfer实现数据传输:
* e) c' K2 R9 Y" D' k) @( z9 X6 ^# r
  1. /*
    6 A0 }0 ~/ w$ l( }  L7 Z0 R; a
  2. *********************************************************************************************************
    ' F4 [  F! g6 M# Y4 [  q
  3. *                                 选择DMA,中断或者查询方式
    3 s- ^5 B, T% j
  4. *********************************************************************************************************! d) M$ U, c! Z! F: A
  5. *// `8 T4 [2 U: X0 L7 q
  6. //#define USE_SPI_DMA    /* DMA方式  */5 T5 E3 ^( i4 X. [' t5 X
  7. //#define USE_SPI_INT    /* 中断方式 */
    ) B$ q2 L: R! M( _$ o+ U+ S
  8. #define USE_SPI_POLL   /* 查询方式 */
    + h7 d3 u5 e# F6 {

  9. # r/ B7 `* X2 N& R# k' L
  10. /* 查询模式 */  Y( A. ~* M/ r2 D6 U
  11. #if defined (USE_SPI_POLL)  Z1 w  @4 b9 U: I0 V/ c% l
  12. . l) p$ S$ n  t# S
  13. uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];  ; Y$ j: h$ o. u& H3 N; Q. M
  14. uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];& q+ r( K7 M! s& O, t) E% R& q) t

  15. * e; K8 b: V% P) V9 J
  16. /* 中断模式 */  K# ^; g$ g1 S: Z
  17. #elif defined (USE_SPI_INT)& k$ m% _# {' _9 a

  18. 9 u' J$ E8 b& Y
  19. uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   ! z: y8 {6 e8 ]+ u5 U+ n, w+ \
  20. uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];* [9 y' Y8 k9 d3 e) D
  21. $ z) B# B' x% A9 u. {
  22. /* DMA模式使用的SRAM4 */2 |' N2 s; U1 I3 G# F5 g6 c
  23. #elif defined (USE_SPI_DMA)
    $ K4 F! y! B3 v0 M
  24.     #if defined ( __CC_ARM )    /* IAR *******/
      h+ A( I. e9 {/ M. c- ^6 z
  25.         __attribute__((section (".RAM_D3"))) uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   
    * V0 n. L9 ~0 W/ p; u
  26.         __attribute__((section (".RAM_D3"))) uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];
    3 m" r- x9 W+ y4 p
  27.     #elif defined (__ICCARM__)   /* MDK ********/
    6 i' |9 e- L% S8 J, ?3 D" D& y, K  i
  28.         #pragma location = ".RAM_D3". T- ^  y$ P% D8 s; n, k
  29.         uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   - J+ \# O) S- H: V
  30.         #pragma location = ".RAM_D3"7 k! b# L0 F3 K# @" V
  31.         uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];* x$ s4 U! V9 L
  32.     #endif
    ! m& x% u+ V) {$ }4 O$ v9 q
  33. #endif; O7 ?$ T. g4 ?" S  |  l5 g. E+ A

  34. ' e+ j) F, @  K; M' P
  35. /*
    2 Z% Y" p9 F1 J0 j( D; G
  36. *********************************************************************************************************2 k  T# @8 y3 I$ r- J3 z" A
  37. *    函 数 名: bsp_spiTransfer
    ( s# V9 H' e! ^# z! g0 ^5 U' _3 o
  38. *    功能说明: 启动数据传输
    2 x8 d- T5 s8 L
  39. *    形    参: 无
    9 P% H' D  }* `( Q( }
  40. *    返 回 值: 无
    ! `' g" e" Z0 }4 J; P% ~, x
  41. *********************************************************************************************************
    * |( [# q, X/ l2 }7 |% o
  42. */* n# @9 _9 ?$ e3 Q5 a" a9 z
  43. void bsp_spiTransfer(void)
    " K- o& X6 ~/ F. j; B4 M! h! i
  44. {
    1 G  |0 X/ C4 ~8 ]+ y9 a1 P+ m* t
  45.     if (g_spiLen > SPI_BUFFER_SIZE)
    , e4 G& A8 _+ \# d, h, J
  46.     {) V0 l$ b5 w/ e* z; }
  47.         return;
    / ~+ \+ b6 j# B- t: o
  48.     }# H9 y1 N! M7 [
  49. - u. ^- F. N+ r9 u* S! u% Q! _
  50.     /* DMA方式传输 */9 M$ K& |+ o# c9 Q. x% k
  51. #ifdef USE_SPI_DMA$ K6 J: W$ G# e4 V/ Z
  52.     wTransferState = TRANSFER_WAIT;
    . L) ]9 q$ n6 n; h. r2 l7 {7 D

  53. - w2 X! ]) c; k  q; C
  54.     if(HAL_SPI_TransmitReceive_DMA(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK)   
    7 m8 ?6 E8 b+ H" y# H. Q
  55.     {
    ' V: B8 i3 m* \7 W
  56.         Error_Handler(__FILE__, __LINE__);
    , I/ K# ~1 b) f" R4 K) h; T. X! V% B
  57.     }
    4 F. E, j0 T, |3 M- B
  58. & a( Q0 j7 O- V# j0 c
  59.     while (wTransferState == TRANSFER_WAIT)1 y; }0 m9 Q3 a& [' W! q5 R, k/ M8 _4 {
  60.     {
    % K5 I. H3 [4 R+ u
  61.         ;
    7 ]/ @8 A! w; u7 L; c" a
  62.     }6 H! S3 ~4 G  B: [4 ?; p
  63. #endif5 L2 e# T% O* A* E- G
  64. ( q+ h" N! N' ]+ F; \" F& b, i
  65.     /* 中断方式传输 */   
    & z% D% D! J' j4 D5 }; {
  66. #ifdef USE_SPI_INT* a  n; k* J: b- B- X" j
  67.     wTransferState = TRANSFER_WAIT;
    , {& w% }% z( g# O, h/ X

  68. 3 `3 E9 L2 ^$ @* u4 J! z& {" Y; p
  69.     if(HAL_SPI_TransmitReceive_IT(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK)    + j" ]# Z0 u- B4 w/ j7 d
  70.     {% S- L) P) I6 P/ c: h
  71.         Error_Handler(__FILE__, __LINE__);
      c) A/ l) X# P- R
  72.     }' k* `) A  r% z) c$ n

  73. - ^7 p! S4 `! k" S
  74.     while (wTransferState == TRANSFER_WAIT)# F; `1 \; ^, V
  75.     {$ J1 i3 \, p" g# r
  76.         ;
    7 S1 ]# A; ?; M- J8 V$ \
  77.     }
    . c+ r& k# g6 f% V6 U
  78. #endif/ k% F8 ]1 \6 M; {" m1 [
  79. % L. V1 T  m1 Y8 y$ z+ D
  80.     /* 查询方式传输 */    - m4 _9 b) O; ?: _$ A  @8 R
  81. #ifdef USE_SPI_POLL
    " G6 h; {% |3 Y. E
  82.     if(HAL_SPI_TransmitReceive(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen, 1000000) != HAL_OK)   
    1 n. ^5 i0 a( W1 K% O* ]
  83.     {8 t  h( q& d- S: ?% ~: i" C
  84.         Error_Handler(__FILE__, __LINE__);7 i! A3 r# v* b) A1 l
  85.     }   
    / D' N6 w' f5 ^6 \2 |+ F
  86. #endif/ c1 {0 y- g4 z$ `; e3 ~, U
  87. }
    6 H5 |) m4 v) e2 t/ o" f
复制代码

: [* C/ L% q/ v( M; U+ t, y- f; n, Z& a+ q8 O! |* t3 g7 f
通过开头宏定义可以方便的切换中断,查询和DMA方式。其中查询和中断方式比较好理解,而DMA方式要特别注意两点:5 X4 L# E2 G5 Y, y+ k

( _" h1 D! z' K% d4 w8 }  通过本手册第26章的内存块超方便使用方式,将DMA缓冲定义到SRAM4上。因为本工程是用的DTCM做的主RAM空间,这个空间无法使用通用DMA1和DMA2。
' g" P1 }  l% Z. G/ y  由于程序里面开启了数据Cache,会造成DMA和CPU访问SRAM4数据不一致的问题,特此将SRAM4空间关闭Cache。, D1 k2 {# K9 `+ {9 K; T9 j9 {
/* 配置SRAM4的MPU属性为Non-cacheable */9 T' S. \% h& T! ]/ a; \
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;; a- H. m: k- l5 F% ]! |
    MPU_InitStruct.BaseAddress      = 0x38000000;
% o8 @7 b. O. @) R+ G( u    MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;
; A$ {$ c6 U2 [; `6 g    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
7 @3 C, |# W4 E- E7 j& k    MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
, b' ^6 m# b4 K) a    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;
. E/ x* d9 j6 j' w5 m3 N& E- v# T    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;' t8 |0 W' C3 @) M' B( E% A, f- s% Y
    MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
, P. Y/ D$ J2 S7 \0 f& ^    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;' X/ ?: H9 Z" W. y  V
    MPU_InitStruct.SubRegionDisable = 0x00;
+ B% a+ h9 G9 q# x( l    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;: _% \' ~0 h1 j7 K" F% T" q
, g) A, B6 _( v
    HAL_MPU_ConfigRegion(&MPU_InitStruct);
* L$ Q2 p; r7 l5 g& @5 m3 k9 l) Z% g

1 e( n/ W6 i9 q& f. Y4 B# B75.5.3 第3步:DAC8501的时钟极性和时钟相位配置
. Q0 M* ^& M/ U  Q首先回忆下STM32H7支持的4种时序配置。
; K* l* c  K1 ^1 N" V9 ^% L4 C3 W5 L
  当CPOL = 1, CPHA = 1时
! _+ y& Z( r7 ~, m, X% S( ]SCK引脚在空闲状态处于低电平,SCK引脚的第2个边沿捕获传输的第1个数据。8 q) s* H/ e( ?! z" Z# V) Y1 B

: n& s2 s. _, i% o" o  当CPOL = 0, CPHA = 1时% ~6 l- H& K, e
SCK引脚在空闲状态处于高电平,SCK引脚的第2个边沿捕获传输的第1个数据。
9 u) ^% \& v& C" X9 s! v! T- L. i7 R. \# c# t1 r0 ?
  当CPOL = 1, CPHA = 0时7 I/ o' H+ H7 w
SCK引脚在空闲状态处于低电平,SCK引脚的第1个边沿捕获传输的第1个数据。. u' A  R6 d; t) }) T
0 i. C! {$ C  d5 V5 I
  当CPOL = 0 ,CPHA= 0时4 Q( H3 Y# ~0 L! Y+ ]3 A
SCK引脚在空闲状态处于高电平,SCK引脚的第1个边沿捕获传输的第1个数据。
1 {4 _+ N2 g% S5 X1 \+ @, i: y2 x# B- w$ q9 I& `
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

1 M: d/ t. v6 ]1 J0 w5 ~
' C, n) }% I1 q  i1 [. Z4 ^有了H7支持的时序配置,再来看下DAC8501的时序图:
; ^! i0 S7 h+ P" D: |4 J# }
$ J8 D+ H$ J' B) P( L" L  O
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

- Z4 V( p" e" K$ m' O/ U# e& q* n' D) T: L. l! |
首先DAC8501是下降升沿做数据采集,所以STM32H7的可选的配置就是:
# I: ^4 g& _) f2 ^" j; V8 T+ U8 |6 p' R( t
CHOL = 0,  CPHA = 1
3 Y: F6 Y6 ]$ J5 ~* n7 n( C. t# S1 T4 K% l+ s
CHOL = 1,  CPHA = 0
; d+ H7 Z' A& v& z  j" G7 d- ]2 K/ u6 ]
对于这两种情况的主要区别是空闲状态下SCLK时钟选择高电平还是低电平,根据上面的时序图和DAC8501的数据手册,两种情况下都可以正常运行。经过实际测试,STM32H7使用这两个配置确实都可以正常运行。程序里面默认是选择CHOL = 0,  CPHA = 1。8 W% i: v8 Y. n3 l7 G  N6 t
8 l/ y( M" ?% T
75.5.4 第4步:单SPI接口管理多个SPI设备的切换机制6 K6 k+ q$ K9 V  B- Z
单SPI接口管理多个SPI设备最麻烦的地方是不同设备的时钟分配,时钟极性和时钟相位并不相同。对此的解决解决办法是在片选阶段配置切换,比如DAC8501的片选:
/ ^% M; ^2 y% m, q5 L' ?5 s  E6 d* Z- S, j, T
  1. /*
    ; @, d4 R# F7 Z1 U6 a
  2. *********************************************************************************************************
    5 J4 M6 D6 \/ Q0 ]5 H: F# K
  3. *    函 数 名: DAC8501_SetCS1; ^# I* }/ a7 j" O2 x6 t
  4. *    功能说明: DAC8501 片选控制函数
    ( X5 `" e, }" j
  5. *    形    参: 无0 W8 k: q3 S& Q6 M
  6. *    返 回 值: 无3 j1 R0 ?9 A0 Z0 p5 K& w/ U
  7. *********************************************************************************************************% k7 o/ U0 q; q3 s- w
  8. */
    8 [# K- M5 T+ Q  p' V
  9. void DAC8501_SetCS1(uint8_t _Level)
    ! x. \/ o! ]% m4 ]
  10. {6 u$ m* ], w2 Q$ h; |" b% i
  11.     if (_Level == 0)8 [0 t$ |5 S& B8 k! M+ p! d& V9 w# p
  12.     {
    % m* [! x9 d7 |% n. l3 L( b$ c7 X
  13.         bsp_SpiBusEnter();    /* 占用SPI总线  */    , T3 Z% N. L* @( F: v
  14.         bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);        - T9 E+ o0 t5 b; T4 h3 W  O
  15.         CS1_0();& |/ j2 d/ z9 U1 x! ~  r+ z; U
  16.     }# l& P+ i+ W, }% T
  17.     else
    0 F% B9 Z! m3 @. h% t; Z" z9 i, H
  18.     {        
    ( `) `/ l( g" S$ g* b  e3 G8 P8 U
  19.         CS1_1();    ! `$ t* _" S( z  q# [5 G5 i
  20.         bsp_SpiBusExit();    /* 释放SPI总线 */+ g1 A6 ^$ z: h8 ]
  21.     }   
    ; W" q% `' b% I" O
  22. }
    + Z, }; t7 @/ g8 ^$ f1 q6 Q

  23. 5 w3 x6 Y6 u$ \$ L* h
  24. /*& R' T4 O1 B& z+ y8 {# m
  25. *********************************************************************************************************
    4 C- M1 q2 V8 L- K
  26. *    函 数 名: DAC8501_SetCS2(0)6 X/ Y. i$ ~! s( G# `5 R
  27. *    功能说明: 设置CS2。 用于运行中SPI共享。
    8 Y/ x$ `8 w, ~6 l, l
  28. *    形    参: 无1 H3 ^' P: {, C. A
  29.     返 回 值: 无
      |7 n6 v3 H7 X) N5 ]
  30. *********************************************************************************************************' H7 e1 z8 o) ?# M( }* D% V
  31. */
    * B6 ]- ]- v6 U$ ]$ c/ H  Q; j$ }
  32. void DAC8501_SetCS2(uint8_t _level)
    + C5 l: f8 o1 X4 s8 G. w9 s) N* u
  33. {
    * Z( u# e; @5 `8 _' Q
  34.     if (_level == 0)
    * w, t  _, a1 j- K2 M
  35.     {
    % a( h' U( T  |4 S7 W
  36.         bsp_SpiBusEnter();    /* 占用SPI总线  */1 Q1 W0 _9 A, N/ x& U
  37.         bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);        
    . f$ U# `5 N% `4 f0 R) i. _7 d
  38.         CS2_0();& u2 w" r, U6 p3 E$ v; X0 u) e3 z
  39.     }
      E; I% ]6 Q1 i! l0 [  ^/ D( B. t
  40.     else( r# |  y$ W" ^  l$ `
  41.     {) ~" ~' p4 n" i% C; ^
  42.         CS2_1();
    1 T) I: c' a- H; H! r
  43.         bsp_SpiBusExit();    /* 释放SPI总线 */
    8 I5 Y- q3 J1 t
  44.     }
    , {6 o+ a% r! r
  45. }
复制代码
3 x$ C0 u6 y, s
通过这种方式就有效的解决了单SPI接口管理多设备的问题。因为给每个设备都配了一个独立的片选引脚,这样就可以为每个设备都配置这么一个片选配置。
3 q" V9 L% a* U- a( I8 D# Z, X$ U: t6 R" P; l0 j4 h9 C
但是频繁配置也比较繁琐,所以函数bsp_InitSPIParam里面做了特别处理。当前配置与之前配置相同的情况下无需重复配置。
, O* j' B' C) @# `9 P6 ^. \: A6 q& X0 f6 M0 G( n( e
75.5.5 第5步:DAC8501的数据更新: L% A9 c+ A: V2 r# o
DAC8501的双通道数据更新通过下面的函数实现:
0 e! |( k1 d/ a+ v! B: J1 V  U5 N" x( \8 b0 W8 F* b/ V
  1. /*4 j- \9 X! B# K  B
  2. *********************************************************************************************************
    4 t* [: a* o. X. @) s
  3. *    函 数 名: DAC8501_SetDacData
    8 T) L0 v) ~6 F
  4. *    功能说明: 设置DAC数据
      s  D1 z8 P5 r. Z, m' I: n) t
  5. *    形    参: _ch, 通道,
    ( c8 \# z0 E" E' ?$ x* q
  6. *             _data : 数据; g  C1 o( }. `5 p9 ?% m. G( E8 b
  7. *    返 回 值: 无2 s  h$ R& R7 n6 {  e# v+ S  V
  8. *********************************************************************************************************
    % Y: k) f* M. Y
  9. */
    , g7 j$ g/ x4 A
  10. void DAC8501_SetDacData(uint8_t _ch, uint16_t _dac)2 O% ^+ M, X$ E0 Z$ [3 y! ]9 I
  11. {6 x) U5 ]* y# D8 R3 g
  12.     uint32_t data;
    8 ]) c  P: u- {. a
  13. % Z) n2 m& F  l. G- }
  14.     /*
    ( Y5 X! {+ q" a$ s; ?6 g+ ~- L6 k$ ?. q
  15.         DAC8501.pdf page 12 有24bit定义+ ?+ l7 K5 L7 p( Z1 a  k8 X

  16. 2 Y  w; ~7 f; N: w. Z
  17.         DB24:18 = xxxxx 保留  o9 O6 {! ]! W' p
  18.         DB17: PD19 _0 v! l' @) I
  19.         DB16: PD0) h" S9 f* S4 V, d

  20. * K) X  u1 u( C
  21.         DB15:0  16位数据
    " i0 {: [& Q6 O; D; O& K

  22. $ H, y7 M' l( ~: f% h  @
  23.         其中 PD1 PD0 决定4种工作模式
    ; Q  e0 u7 }, p! O( r
  24.               0   0  ---> 正常工作模式3 x/ U4 h2 |3 H7 h" f( I9 [' I
  25.               0   1  ---> 输出接1K欧到GND2 g: S7 m# r: G+ a
  26.               1   0  ---> 输出100K欧到GND
    ! @0 r8 ]6 Z. z9 d. z& D
  27.               1   1  ---> 输出高阻9 ?% a" X" o* b2 l: f' u
  28.     */
    : T* B# Z2 D$ {5 k; {9 ^, M
  29. # A1 \) w2 M2 v
  30.     data = _dac; /* PD1 PD0 = 00 正常模式 */# G7 `7 F0 S* a7 L( i! n7 p+ R# g

  31. * `  f1 D% j. l! i* T# x
  32.     if (_ch == 0)
    # p/ y8 k# @# z! a# Z
  33.     {
    ' z2 ]# y/ s8 F- m, x- ]
  34.         DAC8501_SetCS1(0);. ~' R/ m# G2 s3 R8 T
  35.     }2 v* k3 o4 d2 D- v+ Y3 c/ {- H
  36.     else! [* o  z( O. f& I
  37.     {; x& E: t; B" L% A
  38.         DAC8501_SetCS2(0);/ r' p9 |  i# ]" i' R9 T
  39.     }. [% h2 `1 y1 {

  40. - C/ }( G% a4 S/ p# ]
  41.     /* DAC8501 SCLK时钟高达30M,因此可以不延迟 */% l& m) _1 H' }; V, ~
  42.     g_spiLen = 0;
    ( J: t$ w% I% _! ^4 g
  43.     g_spiTxBuf[g_spiLen++] = (data >> 16);
    1 d# {0 D0 t: q) l6 e5 V
  44.     g_spiTxBuf[g_spiLen++] = (data >> 8);, b2 @2 \( A7 D" b( @% A
  45.     g_spiTxBuf[g_spiLen++] = (data);" N& ]3 l9 b1 H4 I3 M( Q2 o
  46.     bsp_spiTransfer();   
    # [0 G' u; \8 z7 w# u5 R  c& ]/ a

  47. ' c& T8 U0 i: Z3 T
  48.     if (_ch == 0)  M; f7 {; F* D7 W) Q; z
  49.     {
    . A6 `/ U7 C% ~1 C4 [
  50.         DAC8501_SetCS1(1);
    ; |1 t3 i( F) ^4 z9 Q/ G2 l
  51.     }" i1 P( e% U4 {9 G  G
  52.     else* M2 N3 y$ d+ J, p; V3 B
  53.     {% O5 }$ g3 q* n- N# P2 K/ k, K& o7 O
  54.         DAC8501_SetCS2(1);2 d1 u. A2 s6 [* y
  55.     }; U& D# G- `& ?2 l
  56. }
复制代码
4 r+ X/ u( _7 I* i, O' f
函数实现比较简单,每次更新发送24bit数据即可。* V/ O( ?; X& v0 v  j$ C

3 F: f4 K/ i9 `, r; R! M75.6 DAC8501驱动设计(SPI DMA更新方式)
4 `# O% O* Q4 p: \DAC8501的DMA驱动方式略复杂,跟中断更新方式完全不同,要使用硬件SPI1 NSS引脚驱动DAC8501的片选,所有专门做了一个驱动文件来实现,程序驱动框架设计如下:
9 i1 ?: Y1 Q- \
- H& j( y- N/ l. `
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
; K2 T: c: T3 S

8 ~; d" X& N4 U1 U  @有了这个框图,程序设计就比较好理解了。
( _& c, r- j+ t9 k$ S
9 a7 V- y% D. F) J75.6.1 第1步:SPI总线配置
; c2 M. b! D* ]7 S: S1 wspi总线配置通过如下两个函数实现:
' P3 l0 M- M) |9 S& p, y! k
8 A* |5 h- L7 @6 p9 l; s4 t* m
  1. /*$ }% Z$ z* b5 T
  2. *********************************************************************************************************
    0 C) v4 Q7 }' @& a1 t
  3. *    函 数 名: bsp_InitDAC8501
    6 k, E7 p* h, i# S1 S7 D! k  _
  4. *    功能说明: 配置GPIO并初始化DAC8501寄存器
    % f; V' y, E( J/ X
  5. *    形    参: 无2 P) d9 z6 i* P1 E9 S# K
  6. *    返 回 值: 无9 k& k6 E/ I# x9 [1 M& p
  7. *********************************************************************************************************
    " H  T$ A" H, `4 J
  8. */; Y8 ~4 i3 R$ a! x. E7 z! w1 g
  9. void bsp_InitDAC8501(void)6 F& ?1 }$ b) B' v
  10. {
    & J. q4 }8 W4 i" P$ M* v
  11.     s_SpiDmaMode = 0;  4 Y5 I! Y( S8 k2 r2 W+ R
  12. 8 ?; x; u7 e( P
  13.     /*##-1- 配置SPI DMA ############################################################*/( L! S% W* ~6 Q
  14.     bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);% {; x; U2 I% p7 k

  15. ; O0 U, y7 }, m! f2 v
  16.     /*##-2- 默认输出0V ############################################################*/
    ! N. O( d9 N1 K- e
  17.     DAC8501_SetDacData(0, 0);    /* CH1输出0 *// k3 _& ]3 l. L( d$ k2 {) |; G# S
  18. }! H1 t7 q% [9 C+ J7 |
  19. $ }' a$ [& I" |8 D( X( D' t
  20. /*
    / }  P  g; y; L4 O& q  J1 v) w7 M/ y
  21. *********************************************************************************************************9 u3 j; F6 G  F5 g1 j) A3 l
  22. *    函 数 名: bsp_InitSPIParam  q! T; k" Q8 A+ u1 r% x: R. T
  23. *    功能说明: 配置SPI总线参数,时钟分频,时钟相位和时钟极性。) K0 G) Z9 ]4 Z
  24. *    形    参: _BaudRatePrescaler  SPI总线时钟分频设置,支持的参数如下:
    , ]: r- }# Z8 F6 I3 i9 T4 n+ h/ Y
  25. *                                 SPI_BAUDRATEPRESCALER_2    2分频* _6 v4 T& J* r# g. W8 G; V) \) T
  26. *                                 SPI_BAUDRATEPRESCALER_4    4分频
    3 f8 K# j. f) B' m$ B
  27. *                                 SPI_BAUDRATEPRESCALER_8    8分频
    ' F- C5 C$ P/ k; K
  28. *                                 SPI_BAUDRATEPRESCALER_16   16分频
    , K4 ?% ^6 ?7 Y. @% \
  29. *                                 SPI_BAUDRATEPRESCALER_32   32分频4 ^6 p6 S* O: u* [& X+ Q
  30. *                                 SPI_BAUDRATEPRESCALER_64   64分频
    # U- C9 h- d% E
  31. *                                 SPI_BAUDRATEPRESCALER_128  128分频# q/ @; K) I( G! N$ k' ~
  32. *                                 SPI_BAUDRATEPRESCALER_256  256分频1 t# L6 e2 Z, [. R/ J
  33. *                                                        ! B& Y; D! _1 s) ?% i8 ]
  34. *             _CLKPhase           时钟相位,支持的参数如下:3 a6 w2 R) ~, }8 o9 y
  35. *                                 SPI_PHASE_1EDGE     SCK引脚的第1个边沿捕获传输的第1个数据
    5 }3 A/ ?; j3 M! M0 w/ D
  36. *                                 SPI_PHASE_2EDGE     SCK引脚的第2个边沿捕获传输的第1个数据
    % \' F4 @; S2 |  E
  37. *                                 & }1 X, Z$ {. G! h' i3 V% Y# s/ r
  38. *             _CLKPolarity        时钟极性,支持的参数如下:
    / [  _# u- M# B' m; W
  39. *                                 SPI_POLARITY_LOW    SCK引脚在空闲状态处于低电平
    ( d8 |" s; K3 A& m& A% B
  40. *                                 SPI_POLARITY_HIGH   SCK引脚在空闲状态处于高电平. X5 `8 j6 v' q4 G
  41. *
    2 N* }; F8 D; u- z0 x+ {! }& Q
  42. *    返 回 值: 无$ M7 x4 x( A( ~$ S6 @. K
  43. *********************************************************************************************************& _; t% v3 H7 q  e% ]6 X) F
  44. */
    3 [4 w. n; O7 w1 y4 c# l" }( N  v
  45. void bsp_InitSPIParam(uint32_t _BaudRatePrescaler, uint32_t _CLKPhase, uint32_t _CLKPolarity); j4 q6 l1 d+ P+ r$ E
  46. {" z, b/ {5 e% N. z, i

  47.   Q$ G$ @+ s* J8 a
  48.     /* 设置SPI参数 */+ u. [5 z4 {* d$ k9 r4 z# R* o& m' n
  49.     hspi.Instance               = SPIx;                           /* 例化SPI */! f; d4 \% z+ B; C9 I, h) F
  50.     hspi.Init.BaudRatePrescaler = _BaudRatePrescaler;             /* 设置波特率 */
    0 V( u/ V# S% J0 e; V- Y+ x1 ?* F# k
  51.     hspi.Init.Direction         = SPI_DIRECTION_2LINES_TXONLY;  /* 全双工 */
    3 g, F; s4 d, Z" ]- x* s. E
  52.     hspi.Init.CLKPhase          = _CLKPhase;                     /* 配置时钟相位 */' q  E9 g# Y1 a* O5 U+ r
  53.     hspi.Init.CLKPolarity       = _CLKPolarity;                   /* 配置时钟极性 */
    ( R5 i8 Z0 b; j0 ~( _2 n
  54.     hspi.Init.DataSize          = SPI_DATASIZE_24BIT;               /* 设置数据宽度 */
    ! W( `: {0 _0 R9 M& p
  55.     hspi.Init.FirstBit          = SPI_FIRSTBIT_MSB;             /* 数据传输先传高位 */
    7 \0 o$ x5 @& ^) d) y  p8 C
  56.     hspi.Init.TIMode            = SPI_TIMODE_DISABLE;             /* 禁止TI模式  */7 h+ O9 m. \! H. x( I6 _! ^  @( c
  57.     hspi.Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE;     /* 禁止CRC */9 d0 o+ @" I! m  x5 s; R
  58.     hspi.Init.CRCPolynomial     = 7;                               /* 禁止CRC后,此位无效 */0 ~: @2 e9 ~6 L' n& p5 b3 r
  59.     hspi.Init.CRCLength         = SPI_CRC_LENGTH_8BIT;             /* 禁止CRC后,此位无效 */
    * b$ h8 O# B  ~1 {  k. A
  60.     hspi.Init.FifoThreshold     = SPI_FIFO_THRESHOLD_05DATA;    /* 设置FIFO大小是一个数据项 */2 S; _& [3 v) a! o2 F

  61. 6 {1 h7 ^- v9 ?2 O& H. K4 q
  62.     hspi.Init.NSS         = SPI_NSS_HARD_OUTPUT;                 /* 使用软件方式管理片选引脚 */
    ) f5 [$ F4 m5 z
  63.     hspi.Init.NSSPMode    = SPI_NSS_PULSE_ENABLE;                /* 使能脉冲输出 */
    9 ~) V  u7 [, C8 {" r
  64.     hspi.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;               /* 低电平有效 */
    . {7 }  X/ Q% N7 S& e
  65. 9 u5 D1 n: Z6 \: Z! S& _8 @
  66. /* MSS, 插入到NSS有效边沿和第一个数据开始之间的额外延迟,单位SPI时钟周期个数 */
    + P( M# P( ^. g1 M9 x  |- F
  67.     hspi.Init.MasterSSIdleness        = SPI_MASTER_SS_IDLENESS_00CYCLE;   
    % e$ }( }2 N6 _. |

  68. 9 ~8 K' c, @4 D( P$ D! M0 A
  69. /* MIDI, 两个连续数据帧之间插入的最小时间延迟,单位SPI时钟周期个数 */- N, r) r6 H9 D/ f' ^0 z3 m
  70.     hspi.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_02CYCLE;
    4 T* F* J: ?4 v  e+ j# U
  71. $ @  M7 [- |& `4 j' q$ o; {7 z
  72.     hspi.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE; /* 禁止SPI后,SPI相关引脚保持当前状态 */  : [3 P2 E$ }7 \# U
  73.     hspi.Init.Mode            = SPI_MODE_MASTER;                    /* SPI工作在主控模式 */
    9 U* U9 z7 h" i6 p; J2 [4 a( q, R

  74. ' k( G- {1 H. _; M. k/ p/ ?5 [
  75.     /* 复位配置 */4 z+ |' i! a) r/ o) p
  76.     if (HAL_SPI_DeInit(&hspi) != HAL_OK)0 g7 E6 @& Z3 S
  77.     {
    2 w" D. i' s$ }1 l+ i
  78.         Error_Handler(__FILE__, __LINE__);
    2 ?/ E9 n6 @, d7 v( ^) `
  79.     }    % W1 B: l1 q# R. a2 X" J

  80. % _" i5 \" j5 R/ w# Q7 ]) b1 Q
  81.     /* 初始化配置 */
    , x0 v/ t2 |5 {. e
  82.     if (HAL_SPI_Init(&hspi) != HAL_OK)
      P* {, B0 e' F- |$ @4 f7 l
  83.     {
    + b/ g6 z2 _: G; i, S
  84.         Error_Handler(__FILE__, __LINE__);+ i& ^' j4 t6 p. d& s$ F1 l5 U4 \
  85.     }   
    5 `2 L' ]$ j% k" v( R. c
  86. }
复制代码
- W$ E* O: t) x; L

) h+ B- @6 |: j* |- ]6 k  _* m* M1 U0 p* x
这两个配置函数里面最重要的是置红的几个配置选项,这里依次为大家做个说明:
& u* Z1 @/ s2 j
8 g; ~) c+ I) p  I! W  SPI_DIRECTION_2LINES_TXONLY
3 W8 N7 c1 y* c( a驱动DAC856X仅需要SPI写操作。# R; t0 ?+ b" Y

; F* p, V) K" T, q$ u  SPI_DATASIZE_24BIT
) v5 s$ v2 o! d1 i" t6 }" RSTM32H7的SPI支持4-32bit数据传输,由于DAC856X需要24bit数据,所以这里配置为24即可。9 ^' Z% `. B4 m3 ]
) X; i$ O8 C! S3 }  C& W7 P2 s( I' f; ^
  SPI_FIFO_THRESHOLD_05DATA
% v4 t8 |. i7 r3 U% `5 A* n. u对于SPI1来说,里面的FIFO大小是16字节,那么SPI数据传输配置为24bit的话,FIFO最多可以存储5个24bit,因此这个fifo阀值要设置为5。, T; Q' E3 t, I2 t9 f/ ]- P' K
# A, _+ d+ V- |6 k5 v- y" m9 {3 W& z, Z
  SPI_NSS_HARD_OUTPUT
+ L; ^" }5 b! b% ^我们这里要使用SPI的硬件片选引脚SPI_NSS。9 p, x+ A" l$ \
  q& m7 R) ~$ l; T; v
  SPI_MASTER_SS_IDLENESS_00CYCLE3 \* m, p6 a# {; R8 t5 `
插入到NSS有效边沿和第一个数据开始之间的额外延迟,单位SPI时钟周期个数。: _, X4 F! S. l
' K& `( e& V5 r; W  m% t
根据本章75.4.4小节里面的t(4)要求,片选有效到SCLK第1个下降沿信号的时间,最小值为0。所以这里配置为0即可,也就是无需插入时间。
* R9 E: E. `* i1 E& U: G* z* \! f7 i4 {5 e& _) a4 k/ A' o
  SPI_MASTER_INTERDATA_IDLENESS_10CYCLE) {( Y3 D& W% d7 j: D
两个连续数据帧之间插入的最小时间延迟,单位SPI时钟周期个数。
7 L4 \. H& p% L- a! ^; I$ Q
) M( ^/ i4 s3 o( [; o) [# m根据本章75.4.4小节里面的t(5)要求,每传输24bit数据后,片选要保持一段时间的高电平,DAC856X要求至少要33ns(供电3.6到5.5V时),也是说,如果我们以25MHz驱动DAC856X,这里至少要配置为1个时钟周期,推荐值为2及其以上即可,我们这里直接配置为2个时钟周期(配置为1也没问题的)。
- Y$ }1 F! p* i! u. e2 E( p
7 K) ~6 O* M0 l- {: w/ M0 ~75.6.2 第2步:TIM12周期性触发配置! z8 {6 J6 [9 E) K. k' T  t: f  O5 b) T; I
这里特别注意一点,定时器触发一次,就会让SPI以DMA方式传输24bit输出。) T* S+ {- }9 T+ `9 z3 ?4 U
0 F; d5 i$ n2 o8 r
TIM12的触发配置如下:
2 |3 p$ }; y3 m1 P8 V- y" s/ i8 a. K5 d+ c
  1. /*! O% x! h% F8 }+ D' B) ?8 r
  2. *********************************************************************************************************4 s) R8 d/ j* g0 }* n
  3. *    函 数 名: TIM12_Config9 w$ b. z& {& e
  4. *    功能说明: 配置TIM12,用于触发DMAMUX的请求发生器' w- j0 Q, P# e8 @- z3 q* H, e
  5. *    形    参: _ulFreq  触发频率,推荐范围100Hz - 1MHz                              
    , x& L& }  m" ~5 a) C- g$ B
  6. *    返 回 值: 无' r: W8 D4 m3 @+ e) K% a1 P) ~
  7. *********************************************************************************************************
    ( b: K2 q8 o0 F4 T4 a+ H3 J
  8. */   ; s' k! _1 [3 ^, _* K
  9. TIM_HandleTypeDef  htim ={0};
    7 G4 r' J. }7 R/ p# b! C
  10. TIM_MasterConfigTypeDef sMasterConfig = {0};& y4 S! F  w# y( R( D8 I* u
  11. TIM_OC_InitTypeDef sConfig = {0};
    7 H  d0 Z) \6 w! l: p4 N3 m: H
  12. void TIM12_Config(uint32_t _ulFreq)
    & C: Z3 k6 V1 O
  13. {
    * K7 D& Q. a) Y) q$ d
  14.     uint16_t usPeriod;
    2 w% m, i. L9 ^6 P( j7 N5 l
  15.     uint16_t usPrescaler;
    % T8 B6 k9 U3 t
  16.     uint32_t uiTIMxCLK;- {3 M, I0 N8 X' |; k2 w2 A+ e

  17. 0 d* _. Q! a2 U9 @' F% d: ~. e
  18. ) C6 U1 q" i; H' ]' X& s& d2 h
  19.       /* 使能时钟 */  7 r; B8 j2 w# {5 S5 Z0 H
  20.       __HAL_RCC_TIM12_CLK_ENABLE();  H' o/ I( O& j2 ?

  21. 7 T9 P( {2 k, V' `' `
  22.     /*-----------------------------------------------------------------------4 y& P3 q0 N' P/ c6 B9 H* m
  23.         bsp.c 文件中 void SystemClock_Config(void) 函数对时钟的配置如下: 7 I! F' o! F0 w9 Y7 ]2 h" r; e$ l
  24. 1 C7 e* n. z1 K1 ^
  25.         System Clock source       = PLL (HSE)( A7 s, D8 S" \6 \9 J' f
  26.         SYSCLK(Hz)                = 400000000 (CPU Clock)
      b" ]0 `$ T" D+ t7 S6 d
  27.         HCLK(Hz)                  = 200000000 (AXI and AHBs Clock)
    8 o9 l. f% s0 l
  28.         AHB Prescaler             = 2
    5 |. U) s4 D3 \/ X; c
  29.         D1 APB3 Prescaler         = 2 (APB3 Clock  100MHz)5 |; B  q: ^. y% q
  30.         D2 APB1 Prescaler         = 2 (APB1 Clock  100MHz)
    ' G# w% P- r1 B8 N
  31.         D2 APB2 Prescaler         = 2 (APB2 Clock  100MHz)
    , U5 U# n0 I/ n  u3 |6 ]% _7 D
  32.         D3 APB4 Prescaler         = 2 (APB4 Clock  100MHz)
    ! t0 ^; A& z$ u4 o  x

  33. 9 Y  O8 ^" Z8 w) _  u2 h5 a6 j
  34.         因为APB1 prescaler != 1, 所以 APB1上的TIMxCLK = APB1 x 2 = 200MHz; 不含这个总线下的LPTIM1
    ( h9 o. `8 n6 F0 v: Q
  35.         因为APB2 prescaler != 1, 所以 APB2上的TIMxCLK = APB2 x 2 = 200MHz;
    ) z0 s+ G; n, K6 U! b* U
  36.         APB4上面的TIMxCLK没有分频,所以就是100MHz;
    1 J. V1 ?  |& T) N( \& x

  37. 3 f& z# v8 i- J" ~% J; A& w- z% A
  38.         APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14,LPTIM16 K+ ~: ]* f0 g7 Q$ L
  39.         APB2 定时器有 TIM1, TIM8 , TIM15, TIM16,TIM179 f5 u  W$ g" Q
  40. 2 }" s0 e2 u# s4 I! ^% x: r
  41.         APB4 定时器有 LPTIM2,LPTIM3,LPTIM4,LPTIM5
    ' Y0 u8 Q- m1 F! R
  42.     ----------------------------------------------------------------------- */
    $ r8 g) G, {5 E% e& v9 Y
  43.     uiTIMxCLK = SystemCoreClock / 2;, S0 Y) C" ^; \  ^

  44. 0 y9 @* t& x3 n) n* T# b$ D2 h0 p
  45.     if (_ulFreq < 100)
    - M& j/ Z* V9 k# V
  46.     {
    5 j" X7 m& |/ `' t' N
  47.         usPrescaler = 10000 - 1;                    /* 分频比 = 10000 */
    4 J  l2 Y3 o" f* u6 N2 K
  48.         usPeriod =  (uiTIMxCLK / 10000) / _ulFreq  - 1; /* 自动重装的值 */5 ?) y9 A/ x9 g9 z8 V* f0 O* M
  49.     }
    6 O' `$ d. p: q! I3 r* U
  50.     else if (_ulFreq < 3000)5 U, v5 b) a4 E( z
  51.     {, Z8 _: h, ?# ~+ D: R
  52.         usPrescaler = 100 - 1;                    /* 分频比 = 100 */
    5 \8 ~( G0 U; }- Z' o3 y3 j
  53.         usPeriod =  (uiTIMxCLK / 100) / _ulFreq  - 1;/* 自动重装的值 */
    4 D' K2 v2 F9 n7 f
  54.     }
    1 M5 p# E0 y) z9 u
  55.     else    /* 大于4K的频率,无需分频 */
    - ~+ l* P% f4 E
  56.     {" @* v% z8 i, u1 A
  57.         usPrescaler = 0;                    /* 分频比 = 1 */
    & ~' [# V5 e$ o0 A: ]( u9 z+ D) ^
  58.         usPeriod = uiTIMxCLK / _ulFreq - 1;    /* 自动重装的值 */
    5 Y! g! G( b) V
  59.     }
    $ Q9 }- G' l0 r  j1 T
  60. % w$ s. i/ u& F( v
  61.     htim.Instance = TIM12;' Y" r! N' O4 v7 N" A! s- ]/ B
  62.     htim.Init.Period            = usPeriod;
    - c/ X+ F# P; s
  63.     htim.Init.Prescaler         = usPrescaler;
    # H  r4 w* E6 D7 v( K  E
  64.     htim.Init.ClockDivision     = 0;
    ) n, s: _4 w; z* P1 j
  65.     htim.Init.CounterMode       = TIM_COUNTERMODE_UP;
    . X* p. Q6 J3 _3 g
  66.     htim.Init.RepetitionCounter = 0;
    $ [: o/ D/ }% y9 g9 q4 o5 w
  67. / w3 g4 c5 a- r/ i6 Z1 g( O
  68.     if(HAL_TIM_Base_DeInit(&htim) != HAL_OK)! q4 R# d6 O) |" J, ~7 l
  69.     {
    $ Y* A. K* G: n  V' w
  70.         Error_Handler(__FILE__, __LINE__);        ! O% i. X5 J$ x! T1 T; T, ?
  71.     }
    / g  i3 }: q; E' ?9 o' ?

  72. + @  Z* F( T% [) B9 y3 `, S
  73.     if(HAL_TIM_Base_Init(&htim) != HAL_OK)
    $ c& r% h' [, `1 x" b* F. x
  74.     {' @8 ^0 V- V/ B7 l+ f6 T& a
  75.         Error_Handler(__FILE__, __LINE__);        . O" b; D4 R; A/ u! v& V6 K
  76.     }% H/ Z0 m, z! u4 H, B( u
  77. 2 a3 }( f/ A: p7 t1 y  t
  78.     sConfig.OCMode     = TIM_OCMODE_PWM1;
    + b* D( D- \" E4 t) e# Z
  79.     sConfig.OCPolarity = TIM_OCPOLARITY_LOW;; w( J' P+ l0 V; d0 I' j/ V
  80.     sConfig.Pulse = usPeriod / 2;     /* 占空比50% */
    / B& {, D4 F+ I6 G1 h( V7 q
  81.     if(HAL_TIM_OC_ConfigChannel(&htim, &sConfig, TIM_CHANNEL_1) != HAL_OK)
    7 b7 H) b% U! {
  82.     {% U5 d3 d) u4 a6 O$ n) @4 {6 y5 x
  83.         Error_Handler(__FILE__, __LINE__);5 @. g- K; ]$ F' X, c
  84.     }" l' A* A  k" Y* X+ N+ I% K! P! J
  85. , V$ C8 X; I8 r' F) D. J1 S
  86.     /* 启动OC1 */
    ! R% x" H* L5 [
  87.     if(HAL_TIM_OC_Start(&htim, TIM_CHANNEL_1) != HAL_OK); w9 y- m/ Y2 L; W+ J. E" X
  88.     {8 Z* q/ a- t; q7 k0 `% s# A
  89.         Error_Handler(__FILE__, __LINE__);
    3 Q( Z" U1 ?  B$ n8 @& Z. {8 M0 M
  90.     }% M! [5 w% j- M" @0 B% E/ M7 S" l" x
  91. : f0 J/ H6 z3 ~( h' O
  92.     /* TIM12的TRGO用于触发DMAMUX的请求发生器 */
    # ^9 W& o' r' c& Q, D. T$ R$ z+ o
  93.     sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1REF;  b3 y2 ?' n  ?
  94.     sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;$ {/ c8 a7 D' G. z
  95.     sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    ' U9 a2 [$ m, ?  J, i

  96. , I3 i7 p5 w, a- z) [
  97.     HAL_TIMEx_MasterConfigSynchronization(&htim, &sMasterConfig);) Y5 q. k0 G1 Z, ?# @& I( y/ @
  98. }
    6 n) Y- m- u9 Q6 I, ~3 O4 C8 e( c
  99. #endif: `# z* t% t* @' _" S
复制代码

& r1 T5 K2 \' F( Z( p( A! Z" U# e3 F. S  R' u$ `1 k$ C: C2 q
这个函数支持的触发频率很宽,对于DAC856X来说,如果样本点设置为100个的话,此函数推荐的触发频率是100Hz到1MHz,具体可以支持到最高触发速度计算看本章4.7.7小节即可。  m. F0 d8 m% B4 I0 u
4 |6 o2 g3 K( {' U. F1 f0 U, U
75.6.3 第3步:DMAMUX同步触发SPI DMA传输
5 h3 d+ @/ C: S( ]3 [DMA和DMAMUX的配置如下:  _7 j3 ~1 s2 J% T* ?/ c% M
; E4 ^% M! W+ N2 i4 T; Z: }
  1. /*
    $ R, q. q, ~3 C# k
  2. *********************************************************************************************************5 I  p- B. Z/ N" o+ |( t
  3. *    函 数 名: bsp_spiDamStart/ e! h: j- T* l) t: y  B
  4. *    功能说明: 启动SPI DMA传输, Q/ a( K* o, R
  5. *    形    参: _ulFreq 范围推荐100Hz-1MHz
    , ^- b8 c! u9 V
  6. *    返 回 值: 无
    ; h$ h8 ~; I- O. c  F- A: k! j' A! R
  7. *********************************************************************************************************/ D* o1 B$ J1 y- u
  8. */3 ^" n" ]$ v' ^2 o1 L" [
  9. void bsp_spiDamStart(uint32_t _ulFreq)
    ' h6 m* j5 ]+ O6 W& [
  10. {
    + o6 A% e. r$ n, x3 z/ S
  11.     /* 设置模式,要切换到DMA CIRCULAR模式 */
    & d. M6 H2 U, q( }
  12.     s_SpiDmaMode = 1;
    $ o: o: r4 Q3 H* b% H

  13. , q3 j) z7 P$ Z+ d
  14.     bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_4, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);
    0 @9 x+ i4 y- P) P% O
  15. : F6 G: m: a2 w7 Z0 M" R
  16.     /* 使能DMA时钟 */# o# V4 n9 `; B" p4 M* p* R7 R% h
  17.     DMAx_CLK_ENABLE();      : R1 S& ^% P8 d0 R1 P# v& W2 @

  18. ( j# a, t" \# ^# j" I- I
  19.     /* SPI DMA发送配置 */        6 k, U( ?" ?, p2 ~
  20.     hdma_tx.Instance                 = SPIx_TX_DMA_STREAM;      /* 例化使用的DMA数据流 */3 G* B$ t/ s, I; O  v/ V( i
  21.     hdma_tx.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;     /* 使能FIFO */
    2 x, b' i; T2 ^8 T" M/ G
  22.     hdma_tx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL; /* 用于设置阀值 */' C; H% T- {9 _* p/ c
  23.     hdma_tx.Init.MemBurst            = DMA_MBURST_SINGLE;        /* 用于存储器突发 */
    $ b# W5 O# M, F0 T9 w2 o+ h
  24.     hdma_tx.Init.PeriphBurst         = DMA_PBURST_SINGLE;        /* 用于外设突发 */
    $ Q$ z1 u! c' r7 H
  25.     hdma_tx.Init.Request             = SPIx_TX_DMA_REQUEST;     /* 请求类型 */  
    4 |2 g$ W* i8 D, D* K, [1 t2 m/ d7 g/ d
  26.     hdma_tx.Init.Direction           = DMA_MEMORY_TO_PERIPH;    /* 传输方向是从存储器到外设 */  
    , k' E' B/ G9 g
  27.     hdma_tx.Init.PeriphInc           = DMA_PINC_DISABLE;        /* 外设地址自增禁止 */ 9 j4 R* ~& Y# E3 Q
  28.     hdma_tx.Init.MemInc              = DMA_MINC_ENABLE;         /* 存储器地址自增使能 */  
    : _, O  e5 a& l; u
  29.     hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;     /* 外设数据传输位宽选择字节,即8bit */
    1 `$ I: ]2 c( H6 b5 w
  30.     hdma_tx.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;     /* 存储器数据传输位宽选择字节,即8bit */   
    0 }. X. I9 a8 g7 d( E$ ]
  31.     hdma_tx.Init.Mode                = DMA_CIRCULAR;            /* 正常模式 */
    3 G; v5 d- f& w3 r7 q& h" S
  32.     hdma_tx.Init.Priority            = DMA_PRIORITY_LOW;        /* 优先级低 */
    $ t( O  i; P6 H$ W# R/ |& r

  33. " Q3 z* h# P( g3 c1 s* M
  34.      /* 复位DMA */
    2 H6 [! t# [. S4 p( {" |- A
  35.     if(HAL_DMA_DeInit(&hdma_tx) != HAL_OK)0 e, E% b$ z* A) F' }( w. `+ ^
  36.     {/ Q- S. `  g$ {
  37.         Error_Handler(__FILE__, __LINE__);     6 W3 C' I) A4 k/ t) o/ \
  38.     }* [+ Z1 j+ v7 T* ]5 e( x* P
  39. 0 J4 B* G: E+ K0 G/ u! U6 F
  40.      /* 初始化DMA */8 M+ H- p' V- p
  41.     if(HAL_DMA_Init(&hdma_tx) != HAL_OK)! L8 i7 Z5 E3 f9 C
  42.     {
    # C9 F1 R' d9 [
  43.         Error_Handler(__FILE__, __LINE__);     # W6 A- \* y6 `8 u8 E0 X
  44.     }) F# ]6 x+ H! N

  45. ! s; M8 d* z: Z1 \5 f
  46.     /* 关联DMA句柄到SPI */, u  i( T7 V. X9 E1 l
  47.     __HAL_LINKDMA(&hspi, hdmatx, hdma_tx);    6 N6 v4 a3 C! S! g8 E' M8 _+ p
  48. ' T2 V+ X2 j" ^8 y. S
  49. 9 x5 l1 O" H9 i7 P
  50.     /* 关闭DMA发送中断 */
    ; G& J; Q! V7 c# @) C+ O$ A; ]
  51.     HAL_NVIC_SetPriority(SPIx_DMA_TX_IRQn, 1, 0);: R! p+ D/ S& L. O+ K$ a9 x$ k1 u
  52.     HAL_NVIC_DisableIRQ(SPIx_DMA_TX_IRQn);4 D" E, L, a7 \) n# v+ E& Z
  53. , \5 m  T. A6 h: G
  54.     /* 关闭SPI中断 */! s8 W# t( }- W, M% b1 n8 {
  55.     HAL_NVIC_SetPriority(SPIx_IRQn, 1, 0);4 ]( z0 W: W( o2 P
  56.     HAL_NVIC_DisableIRQ(SPIx_IRQn);
    4 ^2 s2 r# b$ v- Y# @& ~/ g

  57. , V% ]6 @* u) Q6 x
  58.     /* 同步触发配置 */
    " P$ s6 e  X. ^. s$ \9 C% Q
  59.     dmamux_syncParams.EventEnable   = ENABLE;                             ! b  c  _0 S3 z# A1 [1 z
  60.     dmamux_syncParams.SyncPolarity  = HAL_DMAMUX_SYNC_RISING;         
    0 _+ H" R1 A& [4 u
  61.     dmamux_syncParams.RequestNumber = 1;                  
    . g6 j# X  U$ W5 p) y  G' y
  62.     dmamux_syncParams.SyncSignalID  = HAL_DMAMUX1_SYNC_TIM12_TRGO; * a* K: y' ]! h, J2 J. y9 w2 T
  63.     dmamux_syncParams.SyncEnable    = ENABLE;   
    - _4 w9 F' m" z7 p
  64. # Z6 P; ]7 S& J. Q) N4 D' J" M' y
  65.     HAL_DMAEx_ConfigMuxSync(&hdma_tx, &dmamux_syncParams);2 H$ Q8 e; y; ~0 |( z9 }! k
  66. $ {7 ^- W8 g& S, z
  67.     //LPTIM_Config(_ulFreq);. A& S* B9 |; g4 z$ \2 g
  68. 9 b- T( t  Z+ j1 f8 i6 m, b. i
  69.     TIM12_Config(_ulFreq);
    - X# [8 H5 n! p. ^& ~& f

  70. # K6 K* d2 ?/ [: ^3 q' G! T
  71.     /* 启动DMA传输 */
    8 `7 G. W" a/ L- P- E, _- K
  72.     if(HAL_SPI_Transmit_DMA(&hspi, (uint8_t*)g_spiTxBuf, g_spiLen/4)!= HAL_OK)   
    3 a7 P2 i9 d3 R' r) ^. b4 b# r
  73.     {1 J  f- |( t, q0 x# ]7 o% _8 _8 m
  74.         Error_Handler(__FILE__, __LINE__);
    ( M3 J6 K- q" l
  75.     }
    + x9 O6 N) P3 U. o
  76. }$ U8 a8 _$ w3 V& n" k
复制代码

" r( |6 U. o5 @- e2 A8 N7 ^1 G$ }% u  \8 {
这段程序里面最关键的就是置红的部分。作用是配置DMAMUX的同步触发功能,触发周期由TIM12控制。
$ E7 V% a; @! x* Z& U0 o- X3 n9 K, E
0 m7 U+ e0 D0 y5 }9 m' ?) ]75.6.4 第4步:24bit数据的DMA传输解决办法
) i' [/ K# b. i) K" p" n由于通用DMA1和DMA2仅支持8bit,16bit和32bit数据传输,我们这里要传输24bit数据,解决的关键就是配置DMA为传输宽度为32bit,并将传输的数据由24bit再补一个8bit的任意值组成32bit即可,实际的传输会由SPI完成。
' W% \( y3 Q0 F6 k$ u; s! C/ F$ \5 X7 Z, V" }6 y2 C
  1. /*
    0 n" s& e: G9 Q. h3 H8 a. H0 Z
  2. *********************************************************************************************************
    3 v8 L7 i% J& T' I* E5 c0 f  [! c) d
  3. *    函 数 名: DAC8501_SetDacDataDMA
    ( p3 M2 X" |( @; K2 v, P
  4. *    功能说明: DAC8501数据发送,DMA方式
    5 M& o! _5 J' l7 Y8 ]: p
  5. *    形    参: _ch         1表示通道1输出
    2 F. R* ]1 D3 n. }# z
  6. *             _pbufch1    通道1数据缓冲地址2 ]+ t5 `( v1 U/ O( {( q1 c7 N
  7. *             _sizech1    通道1数据大小5 w0 O" d( M& n0 U" e
  8. *             _ulFreq     触发频率,推荐范围100Hz- 1MHz,注意这个参数是触发频率,并不是波形周期。
    , G7 l6 ?) Z; t4 t- f" H7 ^/ R
  9. *                         这里触发一次,SPI DMA传输一次24bit数据。
    4 V' z6 {. |) P9 _- F
  10. *    返 回 值: 无
    * I' i# P2 K. x% R5 i7 \1 Q
  11. *********************************************************************************************************; I6 A( V. n* E, X+ k$ n
  12. */
    4 ^: ]' X; F5 ^, B$ L# D9 b  c! T
  13. void DAC8501_SetDacDataDMA(uint8_t _ch, uint16_t *_pbufch1, uint32_t _sizech1, uint32_t _ulFreq)6 a5 s9 @2 B6 z/ T
  14. {
    # I) n* k9 y  T" H" \0 w, x
  15.     uint32_t i;
    5 {1 A' S" W6 e4 L7 y: |$ e
  16.     uint32_t _cmd;
      w9 Q$ f% R, F- Y8 q8 n$ i0 |
  17. % W+ P4 ^8 E+ @# ~& R. F
  18.     g_spiLen = 0;
    * H- @4 Q, u1 e7 X, u9 l8 }

  19.   |7 t7 R; L) V) H2 @
  20.     switch (_ch)
    ( o* H. a5 w: I3 i  X* }7 c
  21.     {
    + N3 [5 x' T! Z- N
  22. 4 [; S( E6 Y( Q
  23.         /*5 F. ]" N1 V, Y# ]
  24.             DAC8501.pdf page 12 有24bit定义: B: D1 q9 p2 H  i% {5 m1 O

  25. 6 P9 n" q5 }3 g6 Q9 w1 @; K) ~
  26.             DB24:18 = xxxxx 保留/ B6 @2 }* l: t6 {! ?
  27.             DB17: PD1
    6 A1 _/ b; O( J' |( [
  28.             DB16: PD0
    0 V5 n8 m0 J2 X
  29. % v! o9 y/ ?3 F4 Y8 {, x1 _+ b! h
  30.             DB15:0  16位数据3 o) a- X7 Z2 j( B7 H
  31. ( c# k% ?9 L3 t( K' a
  32.             其中 PD1 PD0 决定4种工作模式
    / b9 A) J- v2 h1 K
  33.                   0   0  ---> 正常工作模式' T$ [# j/ F# O  z
  34.                   0   1  ---> 输出接1K欧到GND' o) h- W; v  s0 M% L& F
  35.                   1   0  ---> 输出100K欧到GND
    . r& N5 `" L5 s! n9 @% Y7 l
  36.                   1   1  ---> 输出高阻
    ; H- ]9 g0 o, g
  37.         */
    7 M8 L" o2 w, ]+ l' t$ p

  38. 0 b& B+ r$ N3 \+ \, j' m
  39.         /* 通道1数据发送 */
      N% D2 W5 w; f2 A- t; q+ t! g- A
  40.         case 1:! Z7 l" J% T) i! L
  41.             for(i = 0; i < _sizech1; i++)9 j1 E( w) M% |( L9 B
  42.             {
    - s: s! u9 l! O# A
  43.                 /* 更新需要配置PD1和PD0,当前是选择的正常工作模式 */7 `' ^' H" \0 f
  44.                 _cmd = (0 << 16) | (_pbufch1 << 0);
    4 z( L! P- i3 w1 ~* `7 w  m
  45. 1 ~3 @2 i; @' E
  46.                 g_spiTxBuf[g_spiLen++] = (uint8_t)(_cmd);
    : k! A2 `( C. S2 g7 v: U; J9 ]6 A
  47.                 g_spiTxBuf[g_spiLen++] = (uint8_t)(_cmd >> 8);
    % v7 y/ X7 F- p* `& _  H
  48.                 g_spiTxBuf[g_spiLen++] = (uint8_t)(_cmd >> 16);
      X) x& U1 I- [1 Q2 L( v
  49.                 g_spiTxBuf[g_spiLen++] = 0;2 F7 a( |3 p2 E" z( ]
  50.             }
    - H+ L3 F# J. V  U! i
  51.             break;3 o$ ?# {4 k6 a# V7 X7 w+ U. s$ e
  52.   E- k+ a/ V' V
  53.         default:
    * h  A2 M  c; R* H. G3 A
  54.             break;
    ! v" Y3 p8 O+ W3 g% p
  55. 0 _. T( U( W( U2 A! w
  56.     }% W* `7 \) P7 K
  57. ; T5 w! r: K. I! j
  58.     bsp_spiDamStart(_ulFreq);
    . |8 d: N; d! Q
  59. }
复制代码
# m. e, G& w/ d
75.6.5 第5步:DMA缓冲区的MPU配置4 n& E- s$ U+ Y$ O; Y: A# G3 {
因为工程是用的DTCM做的主RAM空间,这个空间无法使用通用DMA1和DMA2,通过本手册第26章的内存块超方便使用方式,将DMA缓冲定义到SRAM4上:
9 B, D0 x+ f9 H7 d! Z9 P3 A& Q6 i" K% d
  1. #if defined ( __CC_ARM )    /* MDK *******/
    9 l7 {5 J- B+ d
  2.     __attribute__((section (".RAM_D3"))) uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   / _" N9 h) t" e
  3.     __attribute__((section (".RAM_D3"))) uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];% X9 r  A9 T2 D
  4. #elif defined (__ICCARM__)  /* IAR ********/
    ( X9 e; N# b* t! }
  5.     #pragma location = ".RAM_D3"
    2 g7 ^4 v  W, D! N6 S$ ~7 T- f
  6.     uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];   5 w; ], t; K6 S
  7.     #pragma location = ".RAM_D3"
    ; k$ d$ j  E1 o9 Z9 G  c4 F1 e
  8.     uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];7 J7 ^( Z: G& U3 h; Y
  9. #endif
复制代码

8 m0 B. h$ f( k! ]  S由于程序里面开启了数据Cache,会造成DMA和CPU访问SRAM4数据不一致的问题,特此将SRAM4空间关闭Cache。5 E# ]! A8 ?4 K: q+ n
    /* 配置SRAM4的MPU属性为Non-cacheable */  X) G& t8 L& n7 v6 Y8 K) F
  1.   MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    * O: Q4 g7 ~3 b# J- E" m8 S$ N1 c
  2.     MPU_InitStruct.BaseAddress      = 0x38000000;
      k; h1 @1 a$ X: Q/ y
  3.     MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;
    # x1 T, R3 r" x# I  u% {" k" k
  4.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    0 U( d6 O& R% t, J
  5.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
    . m" u. s. H2 Q# j
  6.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;5 N. n" |' V7 w1 j4 X
  7.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;/ p0 Y4 ?8 n) V' @$ r
  8.     MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
    ; d8 \9 M  [! |5 y
  9.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;8 K% d, F& I, g; O( J3 Z- z/ x
  10.     MPU_InitStruct.SubRegionDisable = 0x00;
    ( G; z/ o' x) o
  11.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;$ }: _! J3 M4 p' m; Q

  12. + l. {) F, p# _* y; N
  13.     HAL_MPU_ConfigRegion(&MPU_InitStruct)
复制代码
由于程序里面开启了数据Cache,会造成DMA和CPU访问SRAM4数据不一致的问题,特此将SRAM4空间关闭Cache。
9 s4 I, D" ^% O7 k/ l
8 @4 Q; x) z- V3 r8 ^2 M75.6.6 第6步:DAC8501的时钟极性和时钟相位配置- {4 D- I# l- Z6 `0 h1 u& K
注:与本章74.5.3小节内容是一样的。& f0 g8 C8 N6 ?3 t2 x9 K4 k
  v$ F5 |6 q: y; z+ Y
首先回忆下STM32H7支持的4种时序配置。( e1 Y: M( Y+ R! L# r

. f0 K# n# v8 ~, ^7 x$ z" \1 I  当CPOL = 1, CPHA = 1时
+ |$ C. N. u: c1 ~( hSCK引脚在空闲状态处于低电平,SCK引脚的第2个边沿捕获传输的第1个数据。
: w1 ]3 Z0 H- `! X5 J7 N+ T
- [2 J! O3 ]; Q( x. i: q  当CPOL = 0, CPHA = 1时
) z( b3 d; B6 `/ w9 ?SCK引脚在空闲状态处于高电平,SCK引脚的第2个边沿捕获传输的第1个数据。( O# l7 J4 \$ e% N2 T' }
6 x9 E5 v, t: A1 m# r% c) ?
  当CPOL = 1, CPHA = 0时
% Z, k  b& U# [* C4 i! `SCK引脚在空闲状态处于低电平,SCK引脚的第1个边沿捕获传输的第1个数据。+ Y, J: G+ ^- y5 ^/ [, A1 P4 M
1 v1 V' \7 q; ~* Q) l" O: T. ~
  当CPOL = 0 ,CPHA= 0时+ p- C' [+ M0 r0 _9 y6 |2 H+ O9 |
SCK引脚在空闲状态处于高电平,SCK引脚的第1个边沿捕获传输的第1个数据。" j. [4 u/ h- s( n
- m8 q8 \8 g1 {& f1 h0 F% g9 S

9 H, n5 E1 D+ T$ L* A, B1 P/ L6 l% ]. B% E' h% s4 C
有了H7支持的时序配置,再来看下DAC8501的时序图:+ g4 c" X7 t. K8 V! h

5 A( x- C) R( w+ q
. R; s" G6 q5 k/ ^, z, C+ l5 U7 N% o1 }
/ L/ w) i' d- {6 y4 M
首先DAC8501是下降升沿做数据采集,所以STM32H7的可选的配置就是:( |8 p& b& s7 ?9 _3 g8 p
$ Y1 F) U9 R' m7 \8 m3 ^4 E
CHOL = 0,  CPHA = 1
5 Y& U) M, Y7 P! ]  _* x* B% O# G" G" \0 j6 T
CHOL = 1,  CPHA = 0
/ T: W, C$ b, I: `/ V* e6 D3 b  ^; f. |7 Z+ E$ G
对于这两种情况的主要区别是空闲状态下SCLK时钟选择高电平还是低电平,根据上面的时序图和DAC8501的数据手册,两种情况下都可以正常运行。经过实际测试,STM32H7使用这两个配置确实都可以正常运行。程序里面默认是选择CHOL = 0,  CPHA = 1。
) r. U8 v- U  Q/ @9 t( e" t8 s; b& d* y* Q7 |7 G1 B
75.6.7 第7步:DAC8501的最高更新速度计算5 O- E$ M, |0 G/ T( x% i/ L2 r
这里特别注意一点,定时器触发一次,就会让SPI以DMA方式传输24bit数据。
6 m4 ~* p7 {! ?. N+ r7 z0 s
! j; Y" t6 L& S( i* [0 I$ ~# N配置条件:- j4 ]# Z  B5 M+ v, f9 |% z

% @' a4 F7 [& G+ D# @# T3 b( U& R  SPI时钟是25MHz,SPI数据传为24bit,每个bit需要时间40ns。
+ |% _  B7 d/ [% Q, K  hspi.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE. i7 I% z. X4 C2 h4 l
插入到NSS有效边沿和第一个数据开始之间的额外延迟,单位SPI时钟周期个数,即40ns。
% K0 i: m( E* R4 z: T) K; X: Q7 t/ t! H  Y% W& o) j; B0 j
  hspi.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_10CYCLE+ a- E) E* h8 Y5 c. J9 O, [) Y
两个连续数据帧之间插入的最小时间延迟,单位SPI时钟周期个数,即40ns。1 P5 q, S# s; ^' U. o1 c; ]

, ?1 r! b6 F3 d根据上面的配置,传输一帧(24bit)数据需要的时间:
) v; L/ w  R4 R& h& P1 M; e( o4 ~2 N8 }2 N, v8 @  ^
24bit * 20ns+ SPI_MASTER_SS_IDLENESS_00CYCLE * 20ns
# [) X, n( L. z5 j& I0 f8 ?- V9 ~$ H) N9 q" o& ]
+ SPI_MASTER_INTERDATA_IDLENESS_02CYCLE * 20ns
4 J& s; e( D: d: [
9 E& H, ~3 q/ n. a9 l! q& X" Z" C= 24bit * 40ns + 0 * 40ns + 2 * 40ns
. K* F- T! A8 Z3 n0 |$ m* |
2 R! g9 z' X0 h7 m' k= 1040ns。. g9 S9 j1 r; h# i% ^/ U" [5 ]4 L

& N( J! s$ I& s5 G& j/ V: c那么这种配置下,可以支持最高触发速度是1 / 1040ns = 0.961MHz,如果想速度再提升些,可以降低参数hspi.Init.MasterInterDataIdleness,推荐的最小值是1个时钟周期,那么可以支持的最高触发速度是1/1000ns = 1MHz。! w1 y; G( I6 T! l* g
6 ]4 h; I* U0 e' ]' ?4 T
认识到这些后,实际输出的波形周期也比较好算了,比如我们设置10个样本点为一个周期,那么触发速度为1MHz的时候,那么波形周期就是100KHz。7 `1 H, T9 q& r1 Q4 X& e
3 `( D. B' _3 A
75.6.8 第8步:DAC值和电压值互转
! p$ f: l( Y, p4 LDAC8501模块的输出电压范围是0V到5V,对应的编码值范围是0到65535,为了方便大家做互转,专门做了两个函数:
4 Z( |% U, [: e" h  ?+ X7 D2 s5 e0 P. f, I+ D& K  S' _
  1. /*
    8 t, B) |6 t$ O% C6 t, L( ^5 J+ S
  2. *********************************************************************************************************) n( g& a/ e" |  e9 X# y
  3. *    函 数 名: DAC8501_DacToVoltage
    6 B) j4 S. t; z9 y7 W
  4. *    功能说明: 将DAC值换算为电压值,单位0.1mV
    - l2 E+ y& H: k8 X0 E8 R' ~
  5. *    形    参: _dac  16位DAC字3 Q* b9 I7 h/ b2 T7 K0 v1 a. u
  6. *    返 回 值: 电压,单位0.1mV; U9 ^0 ~. Y/ R' S6 t' v
  7. *********************************************************************************************************
    " E* |' u( p: o" K: k0 |. S
  8. */
    8 D9 o) @  D; V4 }1 c4 H1 t
  9. int32_t DAC8501_DacToVoltage(uint16_t _dac)
    - c; x6 r' P0 r! k8 B6 V! v
  10. {3 Y) q. I, c6 ]
  11.     int32_t y;
    - ^7 h0 q8 g! H, F
  12. 8 Z, `; W$ \" r4 P$ a  d" a
  13.     /* CaculTwoPoint(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x);*/
    * G: o( `( C+ T0 K- I! X
  14.     y =  CaculTwoPoint(X1, Y1, X2, Y2, _dac);9 o9 P6 I) m' v- R! _  v# _+ Q
  15.     if (y < 0)" E7 y5 s# V) o; |1 J, R
  16.     {. o% E; Y" u8 H& l
  17.         y = 0;* [9 J' y9 r. s" e# x& M
  18.     }" i: ~0 N$ T( e* _/ @8 K# n
  19.     return y;
    ! O- z! S2 m3 T: N. f* i0 Q% K
  20. }
    - H" J1 q6 R! z
  21. * b  ?4 ~' m0 y0 N: f
  22. /*6 u8 t& p- K: _( R
  23. *********************************************************************************************************
    7 k0 K8 \% M& F* g
  24. *    函 数 名: DAC8501_DacToVoltage
    ) t" A8 m- {6 u5 b, |7 Q- e
  25. *    功能说明: 将DAC值换算为电压值,单位 0.1mV
    9 I" h# P8 r+ S7 N
  26. *    形    参: _volt 电压,单位0.1mV* M( j* H- h' g7 g  a( S5 I
  27. *    返 回 值: 16位DAC字
    2 N9 }. ^" y6 F. h- R3 w
  28. *********************************************************************************************************
    * D9 T9 w* V$ }# c( t
  29. */; t$ }; n3 V2 v* x: j5 ~
  30. uint32_t DAC8501_VoltageToDac(int32_t _volt)" z+ r7 u( S) k6 R# _, {
  31. {
    ) N& t/ a/ N4 s% X" W5 [
  32.     /* CaculTwoPoint(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x);*/
    * E& J* V* F$ y4 o' H) j4 ]- |
  33.     return CaculTwoPoint(Y1, X1, Y2, X2, _volt);
    " o, Y- e& u& n! f& T
  34. }$ Y9 U: W6 F  ^$ k0 M9 |+ Q7 b. {! p
复制代码

: v$ p' r' N5 V3 u% v3 Z4 g, o! @( h, V( ^7 _
75.7 SPI总线板级支持包(bsp_spi_bus.c)7 t6 w+ g( Y1 m; i" c& g
SPI总线驱动文件bsp_spi_bus.c主要实现了如下几个API供用户调用:. C0 {+ D. K: p3 A7 ^

) i, `! J7 Z( W% ?6 @  bsp_InitSPIBus1 M2 D1 {3 ]: _  h4 M: Y
  bsp_InitSPIParam% l. A$ O% V, T1 ?  B+ D* c" a  ~
  bsp_spiTransfer
' l  ~& F  g, z  j75.7.1 函数bsp_InitSPIBus. Y( i* C  i  f# d
函数原型:. M3 O# ]- W: {! S( |/ e4 Y$ q, u

% O3 {  j6 I* }8 Fvoid bsp_InitSPIBus(void)
8 v4 i$ y2 ^# A% ?1 L' K! _: n9 m" i& G9 e& {/ U
函数描述:" c+ ~- I& q# Y8 t" c$ M' T
. p" U! w  X, t; L" R
此函数主要用于SPI总线的初始化,在bsp.c文件调用一次即可。, L- o  Q  F/ U9 F" w

* D. F# X" `1 p- Z0 w75.7.2 函数bsp_InitSPIParam
4 W7 L1 ?6 i& B( |: W8 _函数原型:
3 q. B- T" p8 I' l6 ~/ O; v6 Y/ H0 R: a/ M2 ]5 ^; q
void bsp_InitSPIParam(uint32_t _BaudRatePrescaler, uint32_t _CLKPhase, uint32_t _CLKPolarity)
5 ?5 q: z& Q( i8 g; t% i, Y; N6 V" I% x
函数描述:
: F( k% D0 l! W" _  u9 \/ t2 A( L3 w& K& f0 u* X/ l. K
此函数用于SPI总线的配置。
' `8 R/ L) @+ D, S5 R4 d  f( b- Q% P2 ^& o
函数参数:
+ Q: i+ M' n' H  B! E, U+ u
% m6 K, j& l$ U  第1个参数SPI总线的分频设置,支持的参数如下:
1 L) T- K7 T& A8 V: V; |" \( k( z  ?" zSPI_BAUDRATEPRESCALER_2    2分频) N/ A3 C+ U+ `
) z6 x# r, y! f5 ?5 `: M. {% t
SPI_BAUDRATEPRESCALER_4    4分频
5 v7 I* r+ c4 S. z+ `& r) S
8 l# Y1 e5 E& jSPI_BAUDRATEPRESCALER_8    8分频, r8 H  Y, w* S( W0 o
$ @, k- [( r7 h3 k* A. z
SPI_BAUDRATEPRESCALER_16   16分频2 R6 Q3 u& H2 T. p! l+ t0 C$ e

" H& R& [2 l" v( Y" r8 U- TSPI_BAUDRATEPRESCALER_32   32分频% H6 d" F+ f' l" S2 Q7 C0 P: ?% T
, R% @) x" W$ P" a4 z
SPI_BAUDRATEPRESCALER_64   64分频
7 z( ^+ f3 U$ }" P4 T% m; k* ~! @" R, }) q! U& _
SPI_BAUDRATEPRESCALER_128  128分频
  j0 O9 ^& s/ i: ?% D; P. s. r/ r
' ]- L- L6 E$ z* WSPI_BAUDRATEPRESCALER_256  256分频3 p( h- A3 d4 v+ U  ?

0 o/ g$ q" X) {5 q  第2个参数用于时钟相位配置,支持的参数如下:& V" I# j' C" L
SPI_PHASE_1EDGE     SCK引脚的第1个边沿捕获传输的第1个数据2 K: B6 X) @4 u- R! V$ I
/ m% f5 }* T& u
SPI_PHASE_2EDGE     SCK引脚的第2个边沿捕获传输的第1个数据
; e3 q0 D  T8 ~, Z7 I; |$ f4 u
+ {$ H% B0 U! l0 X, V8 W6 O( V  第3个参数是时钟极性配置,支持的参数如下:
  g! k( n: h$ LSPI_POLARITY_LOW   SCK引脚在空闲状态处于低电平
' ]" e# g8 y  l3 h1 x+ s3 t& c% i5 }% }3 d5 U2 U. m- T8 e
SPI_POLARITY_HIGH   SCK引脚在空闲状态处于高电平
) B; |+ u' }3 S- r/ j0 T& n
3 e  [0 U* D% N( C$ u75.7.3 函数bsp_spiTransfer6 B$ Y# f6 P' t. |$ X- m
函数原型:* \2 y- ~: K8 s7 A* r% r% E
( W0 V4 s, e. H3 Z
void bsp_spiTransfer(void)4 L5 T- `' J  v1 V0 E
/ u5 b3 f' U( w/ _2 a- K+ d( Y
函数描述:
& [& P& l( |9 ?" X2 w7 J
) b- b9 F9 _8 z) T7 C此函数用于启动SPI数据传输,支持查询,中断和DMA方式传输。9 g, G4 J0 t. W( W# `+ B
, ]& @! p; ]0 ^1 q" J. A6 R
75.8 DAC8501支持包中断方式(bsp_spi_dac8501.c)% y( d! d8 I9 h) y+ e/ P" B/ m- q
DAC8501驱动文件bsp_spi_dac8501.c主要实现了如下几个API供用户调用:% ^+ t2 k) u. N/ c$ {7 X% L; ?
7 G* \$ O/ `* s7 ~
  bsp_InitDAC85013 B# U) I# |/ i
  DAC8501_SetCS1% E/ M) l& p5 M* U" ?! c
  DAC8501_SetCS2, J# y  k2 K9 x
  DAC8501_SetDacData
1 A9 e5 n+ w) N8 z# a" d  DAC8501_DacToVoltage* n; b* Q2 A& ]2 m9 t# R: i  f$ z
  DAC8501_VoltageToDac
- w/ O8 p1 D. y5 B5 E& C75.8.1 函数bsp_InitDAC8501
8 |# V# i! m. E' o0 X* f  D! a函数原型:
. W+ ~! m7 |3 l: H7 J2 @- I( D
void bsp_InitDAC8501(void): P: M, U9 W$ [. Z+ t* [- H& j2 f
( h: d5 |; T* Y
函数描述:
+ W1 a5 z1 n" f  k9 J  ^! t" D& Y0 @& F; L$ a' o
主要用于DAC8501的初始化,调用前务必先调用函数bsp_InitSPIBus初始化SPI外设。! {; b! W1 Q* H* r  H4 Q5 ?/ S

6 K. m# t! O5 Y2 l, m75.8.2 函数DAC8501_SetCS1
- n) u, h/ p& k* V" f! v4 g函数原型:
. T1 R% \, M) C! R# O
- c. @8 a' D7 jvoid DAC8501_SetCS1(uint8_t _Level)
6 b) B+ N/ L9 t# O) h" W: i  q: g) |
函数描述:
/ {: T. Z; ~" o- q$ @0 z  k1 e( l
( ~- ^0 [: M  d' L( R  j" ?此函数用于片选DAC8501模块上的第1片8501。+ J- n1 D/ s* z: {# m

# K# p2 x# g% f9 @9 Q5 R- `函数参数:
% W3 H  @5 M% h8 K* T6 }* m/ p8 G# A, C" c) Q' c: s
  第1个参数为0表示选中,为1表示取消选中。
( H' X+ s  H5 U! Q75.8.3 函数DAC8501_SetCS21 [# X, p/ z( `) K0 G- p
函数原型:9 I' l; }+ M. n  o! B( m

9 V) m. \4 k" r) P  j# ^7 s% f$ \void DAC8501_SetCS2(uint8_t _Level)
9 [0 e, ~. g5 B/ b: }% `
8 I) G- W4 |7 w3 `7 y% e函数描述:, d; s, U" K, `- v
8 n$ j2 I) T( p, p
此函数用于片选DAC8501模块上的第2片8501。" }% {. G" \. Z
6 y% p2 @4 z* V: i( S% l# O
函数参数:
. x$ S, ]* b! x6 ^9 f" Q+ v, x8 |8 q$ o" l. l
  第1个参数为0表示选中,为1表示取消选中
+ G9 U+ o! \+ b; S75.8.4 函数DAC8501_SetDacData1 O- H: y2 Q% |& e
函数原型:% w9 m& |% |; A  ?5 \5 M
" F6 A( {6 d) }# N  {
void DAC8501_SetDacData(uint8_t _ch, uint16_t _dac)$ L; T. M, n% d. a  m( o$ E
6 X9 b- N. f% D, _) f0 S+ X
函数描述:3 e/ }1 z) A6 T6 O% r7 Q  e! P

' p& [- `6 M" ]! c此函数用于设置DAC输出,并立即更新。
  U* k, x. u% C* X$ k7 I
! u+ d( V& F9 G+ _/ E7 F5 a8 Z函数参数:
9 |1 r! ^. c. G
& H  }9 [7 \( O4 N  第1个参数为0表示通道1,为1表示通道2。9 n+ X* u  D6 S; S9 T" N  Q3 ~
  第2个参数是DAC数值设置,范围0到65535,0对应最小电压值,65535对应最大电压值。1 C# }5 ^% s5 S
75.8.5 函数DAC8501_DacToVoltage
4 Q0 \! Q# O8 A' ^! I0 o; c; |函数原型:9 |7 T! z, w6 Z% G6 k+ B
$ d) [! B. a# D$ d2 w0 s+ y
int32_t DAC8501_DacToVoltage(uint16_t _dac)$ c7 N# c7 v/ Y( |  }

# q7 l$ Z$ @  n: r# r# b6 O, [: b$ Z函数描述:( Y8 K6 Y' H/ [4 ]) s( |
6 l# ?, p, @2 Y# g
此函数用于将DAC值换算为电压值,单位0.1mV。
6 I; p8 C/ g; `9 O/ \
7 d9 }% H' w  [& q" Y$ l函数参数:4 z5 e5 O9 a5 U6 S

9 Q* E; l7 r  e0 l7 P8 [0 ~0 g7 m  第1个参数DAC数值,范围0到65535。
; l/ Z( d' |' J, ~9 h7 c  返回值,返回电压值,单位0.1mV。
! [- s, x% o$ c/ m5 v! d75.8.6 函数DAC8501_VoltageToDac
6 E, A5 T- }1 b/ s9 c& x" t& u6 d! R函数原型:2 l; o) J5 h$ Y6 f; J, r

# P5 N' K8 g, |  Duint32_t DAC8501_VoltageToDac(int32_t _volt)8 K% e) `( h& Q+ F0 @
8 A% ]0 n/ N# Y" I8 [  `* m6 n
函数描述:0 F4 Y" x; S* T3 ^" ]
* A) `0 b  D( j' c# H
此函数用于将电压值转换为DAC值。& h; o2 m) H7 h
2 h+ D; u' r( s3 X2 N! l! m
函数参数:
$ d+ A! X* o; b( {; M2 Q7 b
. S! t0 Y0 O! a; c0 w8 B  第1个参数是电压值,范围0到50000,单位0.1mV。4 m  U  b( D) U0 Z
  返回值,返回DAC值。
4 D! v+ ]- \) s) {, z9 k75.9 DAC8501支持包DMA方式(bsp_spidma_dac8501.c)5 U& n' y( |7 N1 B- g! L1 T
DAC8501驱动文件bsp_spidma_dac8501.c涉及到的函数比较多,我们主要介绍用到的如下几个函数:
( ~" e! x: C% ~. ]" F% m% l  m: _9 h! E* T" v# U) B# q- W: q
  bsp_InitDAC8501
5 g6 \# g8 {$ g: W% j( y  DAC8501_SetDacDataDMA0 j* T  v$ d1 H
  DAC8501_SetDacData' G" Y9 {( M4 F3 C* Q  I
75.9.1 函数bsp_InitDAC8501
9 K  l8 ?4 Q9 V! I& k; {函数原型:
1 x9 V' s- c! I+ g* f2 T9 ^* G4 f8 B
void bsp_InitDAC8501(void)5 x$ b) f: H  Z2 _$ ^
& u9 K3 u" s6 V' s" B' A" K
函数描述:
/ s; l4 g9 H+ u* R8 V2 c- d
) G$ f4 a: y8 W) a* M* C9 E主要用于DAC8501的初始化。5 z+ G. i4 B  e6 x8 N/ ?+ V
, ?8 z4 l6 L2 `3 \5 [, H4 x
75.9.2 函数DAC8501_SetDacDataDMA# g' ~7 |6 F) q# s  C' d/ O
函数原型:
( m; N1 D+ d3 p6 s, \% g
3 ~* {% G- S! \void DAC8501_SetDacDataDMA(uint8_t _ch, uint16_t *_pbufch1, uint32_t _sizech1, uint32_t _ulFreq)
/ o, n  }* I5 _1 R" V; I7 E0 i1 ^$ Y" [  ?# J1 ~0 Z1 w, L
函数描述:
, k$ f5 e  v. J' n  d! u$ v4 |1 w& U
此函数用于SPI DMA方式数据发送。2 J$ b* E0 h( O7 b; Z, X5 q
2 `: m/ U! r* D7 w
函数参数:
- e1 T2 h; T+ B" A' C; g8 ~0 G3 C6 c( F8 f$ U5 K9 G& o/ {4 v
  第1个参数用于选择的通道: 1表示通道1输出+ V( b, v( r- o4 P7 {# Y6 L  J
  第2个参数表示通道1数据缓冲地址。
$ _  a" p/ I! }+ x. E. P7 Q  第3个参数表示通道1数据大小。
! f2 X" ]8 G* Q9 R6 f( [4 j- o1 N  第4个参数表示触发频率,推荐范围100Hz- 1MHz,注意这个参数是触发频率,并不是波形周期。这里触发一次,SPI DMA传输一次24bit数据。  i; e) O- F0 `* h, W* F0 {
75.9.3 函数DAC8501_SetDacData, P1 [* C! i9 b' r- }6 E
函数原型:: g- H+ j9 T; ]0 X2 N- e" V9 F2 G# ^) c
& w  j- Z3 e, \1 L! h
void DAC8501_SetDacData(uint8_t _ch, uint16_t _dac)
' U4 c0 N( l7 l' [( {
! N  O! w. p5 e, \0 m函数描述:3 d0 U/ i, O% P' ^9 R' P3 p
: e2 v% _3 V  ^( K1 J, r* \
此函数用于设置DAC输出,并立即更新。, z" R8 K: s; h  `- g8 ^
/ x% D. V4 H! Q* O
函数参数:
. w5 E3 b6 g0 Q2 j
2 x$ _, l7 b9 h  b1 i" [8 r  第1个参数为0表示通道1,为1表示通道2(对于SPI DMA方式,仅支持通道1)。
' {8 n/ H9 I  q" P( P% Z  第2个参数是DAC数值设置,范围0到65535,0对应最小电压值,65535对应最大电压值。
. S) |' c5 d' {& M+ z) a75.10          DAC8501驱动移植和使用(中断更新方式)% {6 J1 `8 G5 E2 Q  C! d
DAC8501移植步骤如下:
$ q! o2 z& f; ~; p& `3 ?) m$ T& D/ Q0 [
  第1步:复制bsp_spi_bus.c,bsp_spi_bus.h,bsp_spi_dac8501.c,bsp_spi_dac8501.h到自己的工程目录,并添加到工程里面。( f' R4 U* ^% l: z% U% u
  第2步:根据使用的第几个SPI,SPI时钟,SPI引脚和DMA通道等,修改bsp_spi_bus.c文件开头的宏定义
9 @$ x2 }( G) x) q9 J* W) l, _7 ~7 ]
  1. /*
    4 m6 Z# R2 X+ ?* t! x: \
  2. *********************************************************************************************************$ {8 N0 k( e9 d
  3. *                                时钟,引脚,DMA,中断等宏定义
    ( ^, ]# u! t0 B; y
  4. *********************************************************************************************************
    3 N4 ?0 O4 t$ }9 ?
  5. *// q) D8 s+ L4 W9 ^4 A4 X3 `
  6. #define SPIx                            SPI1
    3 C, C8 i, w' F8 R, c
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE(). W# t# ?+ d" L: a, t6 _. a  I
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()7 W& n) J, O$ U. _5 B

  9. $ |' r; J& h2 r+ p/ b
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()
    + E& u: O$ B6 J! R8 ~
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()
    . n( p7 y3 i' q( G- z
  12. & }  S. x# @7 Y$ k
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()+ O  I7 ]7 b. m5 o5 g
  14. #define SPIx_SCK_GPIO                    GPIOB
    % x1 J# ], G- E2 Z6 J2 C
  15. #define SPIx_SCK_PIN                    GPIO_PIN_3
    " p& O( E8 g9 E
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1
    ( V: j5 L8 X: z1 |- a# O
  17. # f( \* F. G" G. p1 u
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()# |2 t2 f; |  B* h: r3 k
  19. #define SPIx_MISO_GPIO                    GPIOB, M6 f1 N; g8 }" c2 n
  20. #define SPIx_MISO_PIN                     GPIO_PIN_4
    ! B# M0 V. _3 d
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI1
    , q2 t0 ~2 P/ {9 }

  22. " B+ R5 o2 H, Y8 P) K+ q/ J. ^/ [9 ?
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()( h2 ^4 o: A4 R- N
  24. #define SPIx_MOSI_GPIO                    GPIOB% p( ^1 {) k  o' ~! {
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_5
    , |$ F- k8 g) d; _% F( U
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI18 p; E( f' G( @/ z

  27. 7 ?+ [3 @) x& @
  28. #define SPIx_TX_DMA_STREAM               DMA2_Stream3) s' M/ w* Z/ b+ n, o* k1 L+ J9 r
  29. #define SPIx_RX_DMA_STREAM               DMA2_Stream2
    4 B3 V1 D- P; ?( T, z- n6 o  |& n

  30. 8 s# b* @7 e. _* Q
  31. #define SPIx_TX_DMA_REQUEST              DMA_REQUEST_SPI1_TX4 c6 n* Z* C7 c' ]7 \
  32. #define SPIx_RX_DMA_REQUEST              DMA_REQUEST_SPI1_RX
    / a% `+ [3 B0 P3 D0 O

  33. 8 ~( l' }( L) @9 V- a8 W
  34. #define SPIx_DMA_TX_IRQn                 DMA2_Stream3_IRQn
    8 e4 f; o% a. _) J* h( p! D
  35. #define SPIx_DMA_RX_IRQn                 DMA2_Stream2_IRQn2 c. F$ r  x9 ^: |& z; X

  36. ) A; Q: I- W' L, r
  37. #define SPIx_DMA_TX_IRQHandler           DMA2_Stream3_IRQHandler+ @' t3 I, @: X& E, i8 \% D
  38. #define SPIx_DMA_RX_IRQHandler           DMA2_Stream2_IRQHandler5 w  Z+ j2 X' Y6 D

  39. # ?. z2 ?  P! z% R; p2 v% b" _& Y; i
  40. #define SPIx_IRQn                        SPI1_IRQn
    + |2 j; ]9 b6 k9 B: G$ Z# m
  41. #define SPIx_IRQHandler                  SPI1_IRQHandler
复制代码
% c9 m& u9 o1 G
  第3步:根据芯片支持的时钟速度,时钟相位和时钟极性配置函数DAC8501_SetCS1和DAC8501_SetCS2。" n  {& _8 l( h* h# B
DAC8501_SetCS1和DAC8501_SetCS2。
. ?; c2 `5 [) {( f' F0 C, A
  1. /*/ l( \; o+ a4 J3 F+ F% Y
  2. *********************************************************************************************************4 @4 ^' u6 ^! A1 [: T3 s+ `
  3. *    函 数 名: DAC8501_SetCS1) s( u" u! Z0 D1 u. H2 Z4 z: h: l
  4. *    功能说明: DAC8501 片选控制函数! U: j' G3 i* |
  5. *    形    参: 无; Y  |, L/ b6 r5 K
  6. *    返 回 值: 无; c$ D6 S. u0 a! g& N! s
  7. *********************************************************************************************************
    $ t  s0 H$ e3 d
  8. */8 M0 l1 T- o  {3 @) U& H! e
  9. void DAC8501_SetCS1(uint8_t _Level)
    0 {) f) T$ m9 k
  10. {' Z$ P. W6 H+ L% X& }+ {% T& Y
  11.     if (_Level == 0)& b! Z% ?, a) k( u; g
  12.     {
    ! z0 @/ M/ ^6 C: D- \# v
  13.         bsp_SpiBusEnter();    /* 占用SPI总线  */    2 ?9 @9 h" h; M4 c' B' [$ l) g: M* _
  14.         bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);        2 m9 e5 Q4 h: p! `/ l
  15.         CS1_0();
    . l2 X0 Q2 Y* j* q! w
  16.     }6 T2 d2 A. ~7 D4 k4 y7 `
  17.     else& [3 T8 |$ F8 j' t$ s% y3 h) i
  18.     {        
    2 F9 V+ O, H, Z$ j' Z3 s  m
  19.         CS1_1();   
    6 u' m  }  w; I2 \7 [. O
  20.         bsp_SpiBusExit();    /* 释放SPI总线 */
    1 ~5 d& M1 d! Z  ]
  21.     }   
      v: d0 v& W& ~4 H+ x
  22. }. H! F. ^& S9 G

  23. & C  v0 ?! }2 G9 T- q
  24. /*2 P/ Y. G6 s% f3 x
  25. *********************************************************************************************************
    " {7 i& w9 E& k/ w, h. C. H! n! s2 l
  26. *    函 数 名: DAC8501_SetCS2(0)
    8 [% u1 K) W5 D; N0 |
  27. *    功能说明: 设置CS2。 用于运行中SPI共享。% i( S% y) O! X5 w2 j
  28. *    形    参: 无
    ( ?- v3 _" j3 d; F) ]6 G
  29.     返 回 值: 无
    4 l7 |. Z* o9 O8 Y
  30. *********************************************************************************************************! C, p" h( T- c9 P4 X! k5 |
  31. */! H3 A' W4 J! q- n. C6 O
  32. void DAC8501_SetCS2(uint8_t _level)
    1 O8 {' S5 c, h/ [1 |
  33. {
    $ {/ h8 v4 g8 i! I
  34.     if (_level == 0)
    ! s3 G, v  Y) N, _* i+ |; t
  35.     {* `1 A% n. K9 ^# F9 `# D8 g
  36.         bsp_SpiBusEnter();    /* 占用SPI总线  */+ Z; m. [" J* |. }+ C2 v% E
  37.         bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);        
    4 E8 g9 y$ g0 o' E9 x5 K' C3 _
  38.         CS2_0();# t0 Y5 ?! f/ v2 a6 d# ~" B3 B; B
  39.     }
    8 ~2 c5 ?1 T7 b, F
  40.     else
    ) r( B# r7 n4 O7 y+ b5 f
  41.     {8 r# p- b- S. z, f4 Z" u& u" `
  42.         CS2_1();
    ! f: l- @0 I7 T. {" D
  43.         bsp_SpiBusExit();    /* 释放SPI总线 */! G6 s( Q8 m) B: T
  44.     }3 g  R9 y6 C* i: b# O
  45. }$ H8 v0 i5 s& ]& G7 H
复制代码

3 r8 k2 }( Y5 B1 V& n$ k- p. X# q, U' z: Q3 f
  第4步:根据使用的片选引脚,修改bsp_spi_dac8562.c文件开头的宏定义。
, F; k  K3 |( s0 y: e0 `! k3 D& Y
  1. #define CS1_CLK_ENABLE()     __HAL_RCC_GPIOG_CLK_ENABLE()2 Z) }1 P( r' Q+ N! f. ~: o
  2. #define CS1_GPIO            GPIOG, p+ ]6 A9 ?& Y0 S' W3 c2 O
  3. #define CS1_PIN            GPIO_PIN_10( U  D4 T; ^9 h! i- _' Q

  4. ; q# Q4 W# x% o+ _% ^+ [. \/ s
  5. #define CS1_1()            CS1_GPIO->BSRR = CS1_PIN8 i1 v$ s$ \: D1 T! a" q& U8 }
  6. #define CS1_0()            CS1_GPIO->BSRR = ((uint32_t)CS1_PIN << 16U)
    ( C2 S) @) o% R9 s+ s0 L" ^

  7. * @8 i9 |+ o) p' y
  8. /*特别注意,我们这里是用的扩展IO控制的 */   
    2 ]) |# i2 M# _# ~+ C3 l
  9. #define CS2_1()            HC574_SetPin(NRF24L01_CE, 1);
    $ e5 F1 O' f; b: E3 l8 e
  10. #define CS2_0()            HC574_SetPin(NRF24L01_CE, 0);
复制代码
+ O0 M1 v7 t4 P6 q: I& E
  第5步:如果使用DMA方式的话,请不要使用TCM RAM,因为通用DMA1和DMA2不支持。并为了防止DMA和CPU同时访问DMA缓冲造成的数据一致性问题,将这块空间关闭Cache处理,比如使用的SRAM4:
% r$ b1 N. T* ?0 e9 a- e
  1. /* 配置SRAM4的MPU属性为Non-cacheable */0 a5 j$ Y. {5 M
  2. MPU_InitStruct.Enable           = MPU_REGION_ENABLE;1 v1 M3 C. B  t3 H! `
  3. MPU_InitStruct.BaseAddress      = 0x38000000;
    0 @( N( p. }5 E9 k
  4. MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;
    0 X  a5 n6 ~6 u  V
  5. MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    ( @3 I7 g+ ~' ~* K; x0 M; a( B
  6. MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;' a  @0 ~: c; D: O# u+ b# n6 p
  7. MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;. g# O9 l& B$ y( h- U& l
  8. MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;! s4 L2 Q! V/ G4 _$ i
  9. MPU_InitStruct.Number           = MPU_REGION_NUMBER2;* o* W7 z: ?6 n# @, E
  10. MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;+ g; n& T3 Q; s1 B' I' v# _
  11. MPU_InitStruct.SubRegionDisable = 0x00;
    , O) K/ x- d- C2 C0 t/ T- v
  12. MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    , w, D0 V; _' m! \4 {; e
  13. 2 F/ n0 r$ Z1 m# `5 O
  14. HAL_MPU_ConfigRegion(&MPU_InitStruct);
复制代码
+ \- A0 M2 o- c' f0 B& ~& i
  第6步:初始化SPI。
! h( I+ h. G. ?* P& M- {
  1. /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */
    ' f/ V% o2 j' R1 d
  2. bsp_InitSPIBus();    /* 配置SPI总线 */        
    ) z: y: E+ }5 D$ K
  3. bsp_InitDAC8501(); /* 初始化配置DAC8501 */
复制代码

& f- A) n, x8 L# t  第7步:DAC8501驱动主要用到HAL库的SPI驱动文件,简单省事些可以添加所有HAL库C源文件进来。$ _+ p$ z- _$ X# r  r6 Q7 J
  第8步:应用方法看本章节配套例子即可。8 O: {' ^/ T$ t. ~
75.11          DAC8501驱动移植和使用(SPI DMA更新方式)* N/ u* l; R1 _$ t
DAC8501移植步骤如下:
8 U3 l+ I: F  |$ B" Y  B- E0 i5 c$ s1 U# B( ?3 ^( S
  第1步:复制bsp_spidma_dac8501.c,bsp_spidma_dac8501.h到自己的工程目录,并添加到工程里面。
, x; }, N& c9 T) S% G  第2步:根据使用的第几个SPI,SPI时钟,SPI引脚和DMA通道等,修改bsp_spidma_dac8501.c文件开头的宏定义
- g4 _+ v8 g, C3 y
  1. /*
    * c( ~* m$ X! e+ N2 u+ G) U
  2. *********************************************************************************************************
    . w6 \- t+ |  M3 A
  3. *                                时钟,引脚,DMA,中断等宏定义# r# A* X$ H5 o/ W+ l2 m5 B
  4. *********************************************************************************************************$ v+ h+ M9 E- c- j
  5. */
    7 N8 y: F* z4 `- C  O
  6. #define SPIx                            SPI1
    5 E. t3 a/ |( p; l& d7 o6 C7 K
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()9 r! O9 |6 J9 S7 F- B
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()
    / {( ]- `$ ]8 c* q4 u# a& Z3 [

  9. # f, ~. f: U9 v4 o3 f: i8 ?
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()! R: o, G4 H$ S! f: F
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()1 P* V! v( q6 K! Q' k

  12. + ~! t8 m6 t: L8 p! G( D5 G% {
  13. /* SYNC, 也就是CS片选 */    ' R  J( [( P' {; O6 f
  14. #define SPIx_NSS_CLK_ENABLE()             __HAL_RCC_GPIOG_CLK_ENABLE()
    & v3 H& B" c+ J8 n' w/ E
  15. #define SPIx_NSS_GPIO                    GPIOG+ M' E. x. G4 A' u' L1 O
  16. #define SPIx_NSS_PIN                    GPIO_PIN_10
    + F  Q) c* T( i3 K
  17. #define SPIx_NSS_AF                    GPIO_AF5_SPI1
    " M# e' y/ J) Z& [+ g4 P0 O0 I+ z
  18. % d! G. c! g0 ^
  19. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()  {$ _7 Q, }- M7 P& y5 b/ w
  20. #define SPIx_SCK_GPIO                    GPIOB
    4 z1 T( Q/ ~* m" A" \) I
  21. #define SPIx_SCK_PIN                    GPIO_PIN_3
    4 Y7 v! ]4 n8 P, g
  22. #define SPIx_SCK_AF                    GPIO_AF5_SPI1  a6 j8 v' o" K' V
  23.   l3 j; V3 ^1 S: t- d8 b
  24. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()- D2 v- a6 e. y/ r
  25. #define SPIx_MISO_GPIO                    GPIOB/ f4 Y! t& p: t+ z4 C( r
  26. #define SPIx_MISO_PIN                     GPIO_PIN_4
    ; }, A2 s) Y8 o; d4 t( f8 @
  27. #define SPIx_MISO_AF                    GPIO_AF5_SPI1
    ( U; N4 |  M9 R  I$ |2 S9 H
  28. # Z# P0 k7 W0 m6 q1 [
  29. #define SPIx_MOSI_CLK_ENABLE()          __HAL_RCC_GPIOB_CLK_ENABLE()4 t8 ?$ a& s% l. j6 v* ]6 s0 f
  30. #define SPIx_MOSI_GPIO                  GPIOB
    $ R4 |* c0 [% s# @
  31. #define SPIx_MOSI_PIN                   GPIO_PIN_5. S1 `; v& ]2 t# v' t  I
  32. #define SPIx_MOSI_AF                  GPIO_AF5_SPI16 n# `2 s( _  f: T
  33. 4 v/ J- l; T0 ~3 X' V; B
  34. #define SPIx_TX_DMA_STREAM              DMA2_Stream35 P" I* p+ r3 [3 M# b( I
  35. #define SPIx_RX_DMA_STREAM              DMA2_Stream28 V2 m. V) N- J1 i0 o8 `; V/ t

  36. & e9 ?& o% F5 O8 C2 |
  37. #define SPIx_TX_DMA_REQUEST             DMA_REQUEST_SPI1_TX
    7 i5 x% g& ?' c  j9 X$ b
  38. #define SPIx_RX_DMA_REQUEST             DMA_REQUEST_SPI1_RX" p/ W5 @4 _9 S, h1 p
  39. . ]4 G; Z$ Z- V: k  y
  40. #define SPIx_DMA_TX_IRQn                DMA2_Stream3_IRQn
    2 j% S8 K' j! E
  41. #define SPIx_DMA_RX_IRQn                DMA2_Stream2_IRQn
    4 S4 I8 B+ W2 P1 M

  42. * u- V6 a+ P0 z, s% a$ `6 d
  43. #define SPIx_DMA_TX_IRQHandler          DMA2_Stream3_IRQHandler* ?$ k; @- \0 J' l
  44. #define SPIx_DMA_RX_IRQHandler          DMA2_Stream2_IRQHandler
    # E) G: j6 X0 ?' f& X

  45. 7 l" o. l& [; J6 {( f
  46. #define SPIx_IRQn                       SPI1_IRQn
    / y! {  p7 w& m
  47. #define SPIx_IRQHandler                 SPI1_IRQHandler+ Y3 o  Y* l0 p" R9 t( Q
复制代码

2 h4 p* A3 `/ M: O5 m& c, o0 k
! _, \" A' N1 m6 t  第3步:如果使用DMA方式的话,请不要使用TCM RAM,因为通用DMA1和DMA2不支持。并为了防止DMA和CPU同时访问DMA缓冲造成的数据一致性问题,将这块空间关闭Cache处理,比如使用的SRAM4:
1 |* e) L5 G6 P3 z! |/ a, g
  1. /* 配置SRAM4的MPU属性为Non-cacheable */
    2 Q# y2 j+ q& W# T. d/ J. V2 E
  2. MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    # X4 D3 k; r8 }. P. w
  3. MPU_InitStruct.BaseAddress      = 0x38000000;, b; d( `/ N5 j& W7 I$ _2 s  m
  4. MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;
    # g6 E4 [" v0 ^$ {3 V1 H
  5. MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;1 _) g) U6 H; H8 R# ^$ {
  6. MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;
    3 x8 D- B0 x, I. p" i3 y; `) J
  7. MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;7 J# G# o, Z/ l6 j3 n+ b' [
  8. MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;0 J! z# m- R- c  @# F
  9. MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
    " c& G' a5 l2 x* i% @2 c% C
  10. MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;3 `8 b+ a$ d% f3 {# ?- K0 p/ ?
  11. MPU_InitStruct.SubRegionDisable = 0x00;
    6 E5 u. L! |4 R' \& A' Y- R
  12. MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;) u. L# |4 E% b3 x8 t% M

  13. $ N/ \! w% X0 Q; c! k9 T- B
  14. HAL_MPU_ConfigRegion(&MPU_InitStruct);
复制代码
第4步:初始化SPI。+ _3 U; J) T1 f7 R. c2 `  y7 m
  1. /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */
    & _2 s4 o4 @1 ~3 ?. T/ S5 I2 F) ^
  2. bsp_InitDAC8501();    /* 初始化配置DAC8501 */
复制代码
1 M3 l3 h% B- [  ~% i# R0 E/ k+ b
  第5步:DAC8501驱动主要用到HAL库的SPI驱动文件,简单省事些可以添加所有HAL库C源文件进来。
* M( b& r; f, \  第6步:应用方法看本章节配套例子即可7 p% k  L0 I7 T; {; P! x
75.12          实验例程设计框架
! w+ ~* m4 b7 U' d6 a& z3 o1 Q( E4 h& q通过程序设计框架,让大家先对配套例程有一个全面的认识,然后再理解细节,本次实验例程的设计框架如下:' T6 P6 x9 F6 A$ D

  E& X  r# r( w  |2 x) @
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
$ n: _  P& G* V  a6 b% O+ Y! w

: ]  \' e3 L1 @3 D6 n1 W2 E  第1阶段,上电启动阶段:
5 y& t" [8 R9 G+ I% e
, S8 ?3 n7 y  H: x3 z' N这部分在第14章进行了详细说明。
, l: K) y) g3 e8 c  第2阶段,进入main函数:
9 Q+ {2 p" s- y6 r8 ?' f; @; h* |4 K. b  i5 w8 M$ p0 h) V& g
  第1部分,硬件初始化,主要是MPU,Cache,HAL库,系统时钟,滴答定时器和LED。9 n9 m1 P& l; R' R/ d7 Q/ C
  第2部分,应用程序设计部分,实现DAC8501的简易信号发生器功能。。0 h1 t6 A; Q8 o6 E- o
75.13          实验例程说明(MDK)
! C' b- g( H2 p注:本章是配套了两个例子的,这里我们以SPI DMA方式进行说明。
; d4 `& i6 I& e. r9 i- _; ^) H2 y4 N; E  i1 |4 ]% ]$ i# q
配套例子:
# B& Y/ {, c$ @( N: r
& I3 {* L' T7 q" T7 wV7-054_DAC8501简易信号发生器(单通道SPI DMA方式,16bit分辨率, 0-5V输出)
+ Q, J9 D2 d4 v3 j( |/ {  a. h; w! o/ W! R  y
V7-055_DAC8501简易信号发生器(双通道SPI查询方式,16bit分辨率, 0-5V输出)/ K+ {  n# E1 _5 h

) o* U- t' z3 @! N实验目的:
7 x! _  z# D) g7 c" x1 M
# W5 @" Q7 c! E5 s4 I学习DAC8501的SPI DMA驱动方式实现。  @. m' [4 b7 r* `
实验内容:6 U, N8 |+ r8 z5 ?6 G# j$ n

. b1 }1 S% n1 UDAC8501模块上带了两片8501,每片是单通道DAC,片上输出缓冲运放,轨到轨输出,16bit分辨率,支持30MHz的SPI时钟速度。
, k" L* f) V! K9 R3 s3 v' V# j8 ^DAC8501本身仅支持一路输出,而模块上是带了两片DAC8501,其中只有一路的片选可以支持SPI NSS复用, 所以只有一路支持SPI DMA。9 w0 _# d: k5 X. Y8 b- T# s9 M! V2 j
DAC8501供电电压2.7-5.5V,模拟输出带宽350KHz。& b% M5 o- s* ~" o
实验操作:8 h$ c5 G* y$ x  k

" e: V. x, b9 [启动一个自动重装软件定时器,每100ms翻转一次LED2。1 H$ o, M4 \5 M" _
K1键按下,通道1输出方波。! S! u2 \, y* a% J
K2键按下,通道1输出正弦波。
9 Z* ]4 ^, d8 O, N. T8 p, W2 L* vK3键按下,通道1输出直流。
- \* f5 u- q4 J9 @上电后串口打印的信息:2 e! i5 d; s; s8 N4 v

# V# l7 i2 D- v8 F, S; D波特率 115200,数据位 8,奇偶校验位无,停止位 1。
. d# o, N- I2 P) a7 ]/ B* l- h1 W/ \/ d% u) O+ W. Y) w
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

( G* O* z& t. A6 J9 H: w0 O- A" w5 j" W8 ^: v
波形效果:5 d- O( }/ n) N8 e0 G/ A% U
; F( O. k( d; q& a8 K* l. r
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
& r& I! l/ I+ `* r7 C
. ?) }; g* X. s) U/ ?
模块插入位置:3 v7 Y2 s* @  n
$ ]" j/ O  L$ w. s! I
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

* g( }$ i9 k& B" E& d
4 G6 U8 t# B7 x9 s# g# Z5 A6 p+ @程序设计:
  @2 q$ a! z+ b. b2 P, v, D; @% T, g1 T: i4 ^6 z$ M) p, x* ~3 p8 J9 W
  系统栈大小分配:2 U8 Z$ ?" Z6 y
; L& S( O4 E5 J* E# l
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
+ ^8 r& O5 D! Y# H2 X2 r; C
  I' K! |9 G: n# X; g
  RAM空间用的DTCM:/ z) ?+ ~6 J$ x
. M: g# t: Y  \% V4 U
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png

4 {( J* r) d4 ]+ b; Y+ K5 Z+ {$ _: m
  硬件外设初始化- J3 q0 R! q$ N) X7 r
  A+ z8 X9 {+ j. d# H
硬件外设的初始化是在 bsp.c 文件实现:. r& k* X9 J. X$ D9 i7 _
9 f/ \1 i9 F- H! w1 D. z( K3 R
  1. /*
    2 p! x, F) A. _( c8 L! n9 n2 p
  2. *********************************************************************************************************
    + t/ P( N, h7 Q1 q1 ^. t
  3. *    函 数 名: bsp_Init
    3 J( F7 f  m$ j7 L0 S& U
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次! R4 o! ?; ?, L# B4 C5 S
  5. *    形    参:无+ c# t' T) b' h' m0 f. I0 E- \
  6. *    返 回 值: 无
    - e' E! z' S) a' k. ~' o4 U* e
  7. *********************************************************************************************************
    - m) O. V0 A( Z) {
  8. */
    + \# Z1 f6 ]+ a8 l
  9. void bsp_Init(void)
    3 P, @- N) j! J% A8 \7 i: z5 }
  10. {' t/ ^* B  D, u$ X! ?
  11.     /* 配置MPU */  N3 C1 `( x3 W& `
  12.     MPU_Config();& `7 n9 f; m' g
  13. 1 H3 i' n+ v4 L; K% v) r
  14.     /* 使能L1 Cache */
    ' |: A9 X( w. N5 \- z' p
  15.     CPU_CACHE_Enable();0 `! d" Y% O1 Q$ @6 l) p/ \# Z
  16. + X4 B, G! {' C# c
  17.     /* ' b& f5 R- S0 x9 n7 R* P( U
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
    1 {* Y! e' o- S0 s& o+ B
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。( e1 D6 Y/ K. v* L% g) ]- n
  20.        - 设置NVIV优先级分组为4。0 t' V+ B5 _8 W( q8 m. G7 ~
  21.      */" V) N2 X9 E( M8 j/ R
  22.     HAL_Init();
      O" F8 r! {" ^" U
  23. 4 Z) l7 x0 j( `" ~6 G
  24.     /*
    8 R. w0 q0 S& j5 u
  25.        配置系统时钟到400MHz
    $ ]& \8 z/ X  x6 c+ h3 |. E1 y
  26.        - 切换使用HSE。. j% ^4 d2 G4 B# ?( G
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    & ]- s8 v  [* |+ z6 R
  28.     */0 ?& _+ C- |, A/ S9 W- J
  29.     SystemClock_Config();
    " v3 g' S0 j' |. M7 n
  30. , J/ Z- d* B, ?/ G! t8 h4 K. S4 ]
  31.     /*
    ! i5 i! u3 H! {; E& z% K
  32.        Event Recorder:& Z+ d: Y5 S  w5 o+ ~; v
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
    ! X+ K6 H. p) u/ U
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章
    # {: z$ S/ u3 I# }$ x+ N
  35.     */   
    - s* Z& I2 J0 F
  36. #if Enable_EventRecorder == 1  
    8 x; M4 S  a7 X9 K7 K. m
  37.     /* 初始化EventRecorder并开启 */
    / W! R" ]; G( u; m" |. C, m* T8 S2 a
  38.     EventRecorderInitialize(EventRecordAll, 1U);
    3 E1 I7 ^0 e6 g$ e" |; V0 ?
  39.     EventRecorderStart();( K$ ]& `8 O+ w  }+ E1 ^
  40. #endif
    1 P1 |% H! C5 ?3 T1 N# u
  41. 7 r% ]9 ~4 A- O7 ]$ F2 I) T# r
  42. bsp_InitDWT();      /* 初始化DWT时钟周期计数器 */      
    # i0 c' j5 m" U* w! K8 M* `( P- r0 l
  43.     bsp_InitKey();         /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */% c$ _: G  h. f' R
  44.     bsp_InitTimer();       /* 初始化滴答定时器 */
    / Y; r+ F( K4 @9 E
  45.     bsp_InitLPUart();     /* 初始化串口 */2 y" x6 t& o  O( ?1 m  Y6 T& X
  46.     bsp_InitExtIO();     /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */    ! Q/ S) k# H2 S4 T
  47.     bsp_InitLed();         /* 初始化LED */   
    # [8 O% A& g% j% \! C
  48. bsp_InitExtSDRAM(); /* 初始化SDRAM */( h$ ^- @+ F, @0 Z% F
  49. 1 S! X8 @& ^2 C$ |! h8 `
  50.     /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */    ! p3 a( [1 c- s- O' V
  51.     bsp_InitDAC8501();  /* 初始化配置DAC8501 */
    ; q6 f$ \  H  \! j$ P
  52. }
复制代码
* L$ @& Q% b& [! l1 U1 Q
4 k) R. ^/ c- V; ?2 C2 u5 q  Y
4 N; W! C1 L+ S- n
  MPU配置和Cache配置:7 S7 G$ n3 H, L' d1 K! B
4 [! A6 c1 v& X* x) ~2 |
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区以及SRAM4) Z/ `3 b9 A( n( F$ A4 P4 `2 o

  \. k& o) d: T* v( l
  1. /*" ?; _" t7 {; F9 s4 C5 C; f6 @" |
  2. *********************************************************************************************************1 ?2 I6 z' |0 O) h) T# _. a8 j
  3. *    函 数 名: MPU_Config' q3 N6 f# G+ k  M9 c1 h
  4. *    功能说明: 配置MPU3 Y3 Q0 a4 g3 J
  5. *    形    参: 无
    2 ?0 y- W3 `( y- y& e  [* R
  6. *    返 回 值: 无
    : I4 @2 n8 ?( L8 o! }# e
  7. *********************************************************************************************************
    * Y3 C8 X+ r; x) G/ g; D
  8. */
    0 d, K7 w" c9 q, Z; f
  9. static void MPU_Config( void )5 j9 r' m: m; [) a2 B) i
  10. {2 c! N) M4 k) T: [8 Z
  11.     MPU_Region_InitTypeDef MPU_InitStruct;3 O+ H7 X2 E' }

  12. 6 j- j) t+ R/ n
  13.     /* 禁止 MPU */
    2 {  C9 H, t' O1 F; q
  14.     HAL_MPU_Disable();
    2 h! G4 d4 S9 {

  15. 1 A* S1 r8 l! J5 c% Z. ]( L
  16.     /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
    % w4 r9 M+ y+ t9 T
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    * f( \$ b! H& W2 w
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;3 L6 n  R' W% B. f& \! I- r" F, Y
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;$ J5 X0 |7 @! o2 }" u
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    0 a8 t1 @  V7 S# L: l7 l% d
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    0 s$ X/ `1 [3 e4 O
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;. c' j/ B6 p+ y: E, o
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;! I0 F/ y0 k% l+ i: D
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    + w; Y) v' t5 m9 M/ o
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    % h* }2 h- n! A& r+ w, F0 }
  26.     MPU_InitStruct.SubRegionDisable = 0x00;
    $ e$ B$ p! V& k. f/ a2 N
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;3 X$ }) A. M$ R; q9 R4 [
  28. 2 V9 f. R; D! P5 x4 y
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);7 d* a9 W9 I2 z$ s2 ?1 n8 p7 `

  30. 3 y" A4 Q3 T# }1 o' t# d! d

  31. ( ]) T8 x6 L* a
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */8 T1 b/ b+ s% e7 ~% i$ I) y
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;% P" v" Y' s' f  O! v
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;
    5 H: Z. a8 c2 H3 u8 @  d
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    . I6 G; g$ P# ?1 U
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    * v1 v% L6 S6 _) N
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    ' c4 n3 _& i  D0 x3 @0 r) e
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    ; P7 i% a0 @) I* T4 F
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;; t9 k1 x  r& f9 S0 Q( l4 I
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    # g; q! i+ ~: K: f1 x' D* i. u
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    0 j6 b+ c; ]# y) D5 A) f
  42.     MPU_InitStruct.SubRegionDisable = 0x00;
    8 X. I$ W8 m. w: }6 m, I- F
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;, f) K4 G. L% M( n7 K* f

  44. 6 D2 E9 m' v0 i6 n/ ?* {( A1 {
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);* G1 @# q/ `! r7 D/ t
  46. # t8 I* v; m2 p: w2 U
  47.     /* 配置SRAM4的MPU属性为Non-cacheable *// H5 f+ d8 g) y0 E4 ^% s
  48.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;1 W0 }- N5 H% v, K; p
  49.     MPU_InitStruct.BaseAddress      = 0x38000000;% L1 x6 d' J' R! W4 H
  50.     MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;! K: o+ m$ P! N3 u) Z
  51.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;1 M1 u3 U. m1 R+ G% a( X( E0 R$ x
  52.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;% {/ l. y' j7 y
  53.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;% I3 T; Y' I4 W, n
  54.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    8 J! @) a% U) r! V
  55.     MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
    $ }; M/ ^- [  d
  56.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    9 B5 m! {2 \' W& m
  57.     MPU_InitStruct.SubRegionDisable = 0x00;
    ; I4 q- O6 o) }4 I& T" ^5 i
  58.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    6 C% O/ m( \7 F9 `6 x

  59. 8 X5 h$ J: q0 y8 }: B
  60.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    2 s: h, p1 h" E

  61. % k& m, B8 r( I) i( Q$ x/ W! b5 T
  62.     /*使能 MPU */
    & L9 c) ?! _& N
  63.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);- l9 J9 t4 z) [3 j4 }( h+ j3 e% x
  64. }0 w# s3 {! C( B3 B- o9 Z
  65. - s- v- }* Q% \& D: e
  66. /*
    / A) |; U0 J6 v8 v
  67. *********************************************************************************************************
    0 [9 T! |% n0 p
  68. *    函 数 名: CPU_CACHE_Enable
    " S* Y: N6 @0 p" u$ U6 z
  69. *    功能说明: 使能L1 Cache
    ! `1 l( T* S$ ?0 k* f9 E9 W0 ]
  70. *    形    参: 无
    & m* ]3 j; J( G' v+ I
  71. *    返 回 值: 无% r; ]! ~: m3 ~3 s" f
  72. *********************************************************************************************************
    ! V0 e$ {4 j. ^, u
  73. */
    ! g: y6 N# z9 c) f% U$ ]7 J  L& S
  74. static void CPU_CACHE_Enable(void)- h& }$ G* c: {/ C" c- P
  75. {
    : M$ q# Z' \  o  o- _
  76.     /* 使能 I-Cache */
      U4 _; H* s& H. U: g
  77.     SCB_EnableICache();, n% x/ t0 T" M

  78. ! G: d9 }; H6 e8 A) o, G# l% o
  79.     /* 使能 D-Cache */0 Q3 R" @" U6 n: Q
  80.     SCB_EnableDCache();+ T( ~; D; {% ]
  81. }
    ' E0 s& F1 H3 s9 Q) k% H
复制代码
3 b  P  r% @& ?

) s. x  Y# n* v2 L. z8 j  每10ms调用一次按键处理:9 i9 y4 v4 X; T$ B

' T7 z+ ~1 C3 X* `按键处理是在滴答定时器中断里面实现,每10ms执行一次检测。
8 Y  \  Y$ D. [" h* n8 s% O: T3 M9 r6 Y4 X' O
  1. /*( Y, T: _. J( p9 B, N
  2. *********************************************************************************************************$ p6 w" A% R  [1 l1 f( I
  3. *    函 数 名: bsp_RunPer10ms
    2 H- X( A# U& u3 m
  4. *    功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求) R$ k# |, ~. A# N9 i* l& [
  5. *              不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。
    ) c1 o" ]) D4 ~- q
  6. *    形    参: 无
    0 }5 n: O2 L  r! O% @
  7. *    返 回 值: 无
    . m4 J& n( F4 o% l
  8. *********************************************************************************************************
    , ~0 x. \) V9 g7 g/ V3 `0 u
  9. */
    * H2 J5 \  X! S# a
  10. void bsp_RunPer10ms(void)
    / @. k+ a3 `: X# a% u( A8 _+ v
  11. {: x0 [0 h' d; J8 M! O
  12.     bsp_KeyScan10ms();( G+ ^: [0 F# F4 \! ^+ e; m
  13. }
    2 H. d( W: e7 V- m- R
复制代码

9 M1 m5 H- {1 b5 D9 D3 X; M' v9 Q
) r" V9 o; o: u  主功能:
& a% g2 Y% k& \2 |' G( v& _5 }3 A5 q6 j9 w1 }; X
主程序实现如下操作:
% v* G' h5 [" G9 o5 i5 a" \
; s% P. }3 v7 I* F  启动一个自动重装软件定时器,每100ms翻转一次LED2。
$ @9 ~6 s0 x% K+ c# J% t  K1键按下,通道1输出方波。
- m* A  u4 L: A* P* Q: D8 x  K2键按下,通道1输出正弦波。
% p0 p& J, F, L9 i  K3键按下,通道1输出直流。3 m: n9 \" R4 [# {+ n
  1. /*& E# q8 V! o0 e* C& W
  2. *********************************************************************************************************
    1 B( M) `% @$ Q
  3. *    函 数 名: main
    , [- j) F) c% h/ x4 J  b
  4. *    功能说明: c程序入口) V9 {8 N5 b: E, c
  5. *    形    参: 无
    5 L1 g" l6 I% J$ {; J2 B3 o8 s6 U
  6. *    返 回 值: 错误代码(无需处理), @9 T8 J1 R0 R$ j) [4 m; t2 P
  7. *********************************************************************************************************7 }: M: `3 A1 o/ V0 Q: X
  8. */
    5 W% x5 s: p+ G$ o* b
  9. int main(void)4 x0 M6 o1 H% L* q+ h3 M# \& M
  10. {
    8 U' Y0 s& Y# o8 `9 e2 w4 i2 s0 C' w" ^
  11.     bsp_Init();        /* 硬件初始化 */  A& b! h; {& L0 R$ c4 T6 n+ i
  12. 0 {+ C& m) I  u9 A2 V& @7 }7 h
  13.     PrintfLogo();    /* 打印例程名称和版本等信息 */( i; n" }( B7 c0 f$ I
  14. 9 d) W1 @: _0 f  }) W
  15.     DemoSpiDac();   /* SPI DAC测试 */" Z% K9 z. e1 k) [1 }8 O6 ]$ C0 j
  16. }2 z( S2 F: m. _4 y6 r3 r

  17. 8 L* ~4 ^! x3 V( H( b  ]4 X6 X
  18. /*1 y3 ]& m+ x; z9 P# T) u7 F* e
  19. *********************************************************************************************************/ e- `/ @7 S0 k2 C# D) l
  20. *    函 数 名: DemoSpiDac
    1 {3 t6 V( _' M* F2 _9 g# z0 K
  21. *    功能说明: DAC8501测试
    ( ]) Q  `( n/ l6 ?9 y
  22. *    形    参: 无: F- i& C- M1 P- U# e. h; P
  23. *    返 回 值: 无6 M' X: Z2 q8 ^( G
  24. *********************************************************************************************************
    / X! l5 i+ p" C
  25. */& J9 [1 L8 h' c" o6 i( |
  26. void DemoSpiDac(void)
    / ^- O; f& `& x' m& s4 W
  27. {
    ) V( a# a. G) o% \2 b  |) X: V
  28.     uint8_t i=0;3 b, g* i! W5 b# O+ m
  29.     uint8_t ucKeyCode;    /* 按键代码 */# T1 v0 u( D2 ~: [- m, b
  30. 7 Z; p( p  p+ }& h7 o+ f# N2 G" Y% z
  31.     sfDispMenu();        /* 打印命令提示 */
    . u% I, Z4 S* F% {1 c, j

  32. ' D& a$ Q; A! B" V  y, p, k3 A
  33.     bsp_StartAutoTimer(0, 200);    /* 启动1个100ms的自动重装的定时器 */
    ) y( L  c2 H6 Y! ^

  34. 3 ?' J8 U! J. J6 j& U3 F' U) b
  35. ! I, {& H, F! f4 e8 o) C2 z! {5 A
  36.     /* 生成方波数据 */
    + C- i5 S# N  Q  B
  37.     MakeSinTable(ch1buf, 100, 0, 65535);! D! [& Q: z5 J3 G! [5 i
  38. 5 o4 v% W* C7 [. J- x
  39.     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);
    5 v; s; w9 k" \' ]
  40. 4 _6 i7 [* Y6 z5 o' ?1 K6 `2 A
  41.     while(1), n) I: K6 v" q' d
  42.     {8 _+ `9 T1 {0 _$ Z
  43.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */6 L# I( r  l4 I' j1 i- F' F' s0 T0 [
  44. 2 F2 C" B! ^- n( c$ L! L) d3 `
  45.         /* 判断定时器超时时间 */+ X/ T& r% a! [
  46.         if (bsp_CheckTimer(0))    / p7 Y7 {1 J1 |7 k$ H3 X
  47.         {
    / }& R7 V- ~1 G
  48.             /* 每隔100ms 进来一次 */  
    ( E. b% h1 F- Q2 V& f
  49.             bsp_LedToggle(2);6 ?, N* C1 C9 m* k# p. I
  50.         }3 ]' r9 O, E, w: A& ^' j% B! L

  51. 6 B# o- ?1 f! Y# J1 X5 m, U
  52.         /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */) f$ I6 v; [+ D  G- O
  53.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
    # d+ Q* G3 Q* Y9 O+ X3 x- h( C: j
  54.         if (ucKeyCode != KEY_NONE)
    5 O) ^+ O% a+ o8 a$ Y
  55.         {
    % B8 Y* f: ]# l) n6 m
  56.             switch (ucKeyCode)& }" O. M9 t# C' [2 f) Z7 F
  57.             {
    1 \: M, n! ^8 N4 ^# v# E
  58.                 case KEY_DOWN_K1:            /* K1键按下,通道1输出方波 */! Z+ R- c3 z% q. v% l! _% i4 w( D# G) k5 i
  59.                     /* 生成方波数据 */6 p% I( P+ T& S' k
  60.                     for(i =0; i< 50; i++)
    " E  @/ Z' S* R2 g8 i
  61.                     {, q+ O: O6 I" L
  62. <span style="font-style: italic;"><span style="font-style: normal;">                        ch1buf = 0;! G, ~# x7 g: R; v, O
  63.                     }2 A% s- D% }: s' ~) N6 a6 k( }

  64. 4 Y* `4 @: @$ `
  65.                     for(i =50; i< 100; i++)
    : k% g5 t9 M- m1 ~( U
  66.                     {
    * r5 ]6 ?' K1 i& o  O/ P* |
  67.                         ch1buf</span><span style="font-style: normal;"> = 65535;" h: i3 T# q- J/ v4 f  ^
  68.                     }$ y; Q  P2 V( v/ F
  69. # t  `5 Q: c5 a, Z' m7 b5 c  j7 q
  70.                     /* 触发速度1MHz */' P% K- k/ |( [9 T
  71.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);% @' f9 H8 Y* Y+ u/ @+ _
  72.                     break;
    * H. q9 T& @' \/ _1 G* J8 ]

  73. 3 v$ _' h& y% P. `" c. l& ~+ Y
  74.                 case KEY_DOWN_K2:            /* K2键按下,通道1输出正弦波 */
    ! e4 o6 \' _6 M/ s  l" n0 Y
  75.                     /* 生成正弦波数据 */   
    . U; G) j# K( F  ^
  76.                     MakeSinTable(ch1buf, 100, 0, 65535);' A9 p8 d* |3 n) J

  77. 1 v; ?! C( B" }5 k6 V$ y2 y, |
  78.                     /* 触发速度1MHz */
      t) s5 }4 j5 \3 b( j
  79.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);
    4 P* D! D' v9 O/ k9 q
  80.                     break;  q, |, c8 ^1 J% ?% t' O

  81. 8 u( s- S. ^  N$ E0 f! L* L
  82.                 case KEY_DOWN_K3:            /* K3键按下,通道1输出直流 */
    ' s6 Z2 B( y! C2 E8 s

  83. , {8 h: H$ u/ [* m2 S
  84.                     /* 生成方波数据 */
    9 v  x* p% G" R8 b5 Z3 O
  85.                     for(i =0; i< 100; i++)
    ' d! w7 s; Z% j; E
  86.                     {
    ! t0 n+ `3 ~: K. ]1 h% \
  87.                         ch1buf</span><span style="font-style: normal;"> = 65535;: D2 U3 j% C+ w. u4 J
  88.                     }( v" P8 B- R* R6 Z7 S
  89. 8 Y6 [5 S0 H1 z2 S9 r9 O5 f8 f7 x
  90.                     /* 触发速度1MHz */
    ' J9 E* j0 T) j0 r  Y) X  H3 Z0 m0 L  V: k
  91.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000);- f  I$ j- m) T7 X6 K! \# `
  92.                     break;
    ) S/ Y5 H4 @5 K: f; X! [
  93. $ Z1 f% e0 _! R8 c- |2 j
  94.                 default:
    5 D3 ]( m' X9 ?+ m! S8 M2 }
  95.                     /* 其它的键值不处理 */
    # n4 m- J0 M$ i  I
  96.                     break;
    . p# \5 H/ c, t- v
  97.             }
      t, z# w& \1 ?7 }8 H) R8 r
  98.         }
    ' ]! ?& m3 F! Z  }
  99.     }
    $ P) p: F+ \: z+ _" w* I
  100. }</span></span>
复制代码
: j; S  c8 @' [0 g, p  a

! H. x7 B- g7 F. w/ C  v: F) w2 Q. O$ A& t" k% a7 I: n# P
75.14          实验例程说明(IAR)
& {  N+ W3 O( O$ e注:本章是配套了两个例子的,这里我们以SPI DMA方式进行说明。
$ j9 u1 C9 y9 {$ ]% e* Q, A9 d
7 f5 I: `4 B" Y+ n! Z" O' {9 h配套例子:* W/ @- l3 S% W/ d& w

. B+ X; G% S6 ?1 t; KV7-054_DAC8501简易信号发生器(单通道SPI DMA方式,16bit分辨率, 0-5V输出)+ _. H! a6 M4 O
8 Q5 W% a& {% W. p2 f6 I
V7-055_DAC8501简易信号发生器(双通道SPI查询方式,16bit分辨率, 0-5V输出); D2 [3 j$ t7 D+ ?

; ~6 [* h0 U& `9 _+ v( i+ E实验目的:- m  G5 c0 ~3 `

% a/ i7 Z+ ^, `2 e, F; z, I& a4 S. W& I% v学习DAC8501的SPI DMA驱动方式实现。
5 O; I) _% B+ i$ g3 A/ _实验内容:& ]: g. Y% B  ~; w9 P5 ]

& o8 Q' [* v( M3 ^$ U- J. x' N6 ]DAC8501模块上带了两片8501,每片是单通道DAC,片上输出缓冲运放,轨到轨输出,16bit分辨率,支持30MHz的SPI时钟速度。
- G& P% X, M6 lDAC8501本身仅支持一路输出,而模块上是带了两片DAC8501,其中只有一路的片选可以支持SPI NSS复用, 所以只有一路支持SPI DMA。9 n" W+ z: }( X: ]! J7 {' ~# x
DAC8501供电电压2.7-5.5V,模拟输出带宽350KHz。* m4 ~. H2 z- h7 [) r  _$ Y
实验操作:% i* x) w( L- L

. f% P& z  h' D, e  E启动一个自动重装软件定时器,每100ms翻转一次LED2。
0 s$ q9 {- a- v) q: E- i3 CK1键按下,通道1输出方波。, \8 R# n/ I# L! W- M+ P
K2键按下,通道1输出正弦波。) s  U* z, [! o6 j8 d
K3键按下,通道1输出直流。$ F) e4 |' S+ ], ^
上电后串口打印的信息:/ H/ }- B* U! i( A$ u. Z

1 ~; O8 y. |7 I& @5 y/ C波特率 115200,数据位 8,奇偶校验位无,停止位 1。2 D( F& B7 V" u# [8 L
& u" ~$ X5 q2 }$ D% d% x! P
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
+ v9 r+ X/ H  Q- v/ q* K

% [% s4 U0 j7 U0 T4 x7 e波形效果:+ V1 T1 Y% K0 ?; g/ E

/ u$ B6 Q+ p# c' _- a0 D
9 Y+ R8 J. w4 C' A- k
& p+ r; ^" C7 ?& r3 A; _
模块插入位置:  n) Q* Y7 i6 ^( m- k! m: ]

8 S) V9 E9 @7 C6 `( f% U
6 ?* a& }" K8 k; W
7 z" F( Y9 I! s, \* M( @
程序设计:
( o3 y$ h; v. X. i" H  Y6 F6 S% V
  系统栈大小分配:0 [4 K0 Y  X1 a# {8 T
2 Y0 \* S4 Y- N4 t6 Y6 h0 `
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
3 ?0 c$ u. K% X2 [& ]# d6 O5 Z. R
9 j& N' H- I% q* v: a
  RAM空间用的DTCM:1 t' v. s  V: y6 p0 ~/ R; `; w8 @6 C/ D
/ z+ m4 G3 @" ^) I1 L' F  K  d
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
7 y: Z9 j# S- `

& Y, g  K# s" r8 @$ j8 H5 \9 U  硬件外设初始化$ e' y- `3 _" J/ A
; k. ?( c" c# c7 ~1 a0 g, T
硬件外设的初始化是在 bsp.c 文件实现:
9 D: I3 P# o- D
* ?& Q, O- E: J/ l$ F, |, Z$ C5 ?8 V% e
  1. /*$ Y+ w- g: f4 s) H& O. @$ Q
  2. *********************************************************************************************************
    & ^+ k" v) k# h% W
  3. *    函 数 名: bsp_Init" p" u- ~9 ~8 _# s/ H( [
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次# P* y' v% H8 ^( Q+ h1 a6 K
  5. *    形    参:无
    " q2 C0 L5 w; c1 y0 X& u
  6. *    返 回 值: 无+ s$ L9 A7 q0 h# r
  7. *********************************************************************************************************
    6 I" P/ P# [- f3 W
  8. */& y& n. x  t; z$ Q2 `6 _- a' [
  9. void bsp_Init(void)
    % P) l1 K$ X) N3 d
  10. {; U( S2 U1 a6 v- V
  11.     /* 配置MPU */7 ]5 G4 h7 ]2 R! h* p/ [7 L; t
  12.     MPU_Config();( `" b( E/ b# f) X6 h3 b# j3 Q

  13. 4 ]3 ^( Q+ q( r0 o
  14.     /* 使能L1 Cache */) S; y+ y9 E# V& E  s
  15.     CPU_CACHE_Enable();
    - M2 Z) ~, y: b5 ^3 u# b
  16. " T) p+ K" H! O' W2 j# @$ ^4 U
  17.     /*
    ) T: F' @: D9 {4 M& _( a
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
    7 H5 A4 i7 ?9 D: J) j8 `7 s7 X( X
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。! @2 `& R" c9 o6 y
  20.        - 设置NVIV优先级分组为4。$ e; ~1 b/ H( h& r/ }; G
  21.      */
      w- p+ F% |8 v9 X3 |
  22.     HAL_Init();
    $ i) h* r- C2 `4 d6 m
  23. # ^8 `! g% y1 [7 X! b! f
  24.     /*
    4 v/ u4 r3 ?9 t: g  X
  25.        配置系统时钟到400MHz) E4 p8 i$ Z. u3 r% O: p( J
  26.        - 切换使用HSE。
    0 e3 k9 @8 c+ P* a7 c/ @- I
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。4 w. o6 F7 ~% e* ^* ?
  28.     */
    7 _- C. o  o0 K, x; p
  29.     SystemClock_Config();5 ^1 U) A2 e+ R: ]2 T0 B
  30. * W& \  `% U. Y0 s: ~( F4 z8 R
  31.     /* : c, h1 s) C( C( s
  32.        Event Recorder:
    " q, ~  e; L9 C' I
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
    3 U  C3 _% w! p0 Y6 _
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章' ~4 j2 q7 w! r+ L- o; }- c
  35.     */   
    & @7 w8 V! B" l4 w% \+ z
  36. #if Enable_EventRecorder == 1  
    5 }4 N' Y7 p9 @
  37.     /* 初始化EventRecorder并开启 */
    " |/ n/ u2 e; {
  38.     EventRecorderInitialize(EventRecordAll, 1U);1 b% G0 w2 R' O1 a' d2 u" D! w
  39.     EventRecorderStart();1 o6 ~+ Y1 I: D, d, a( y; S! @
  40. #endif
    6 Y. I9 {5 `# o+ m

  41. 6 v5 c& v3 p# d; X- |
  42. bsp_InitDWT();      /* 初始化DWT时钟周期计数器 */      
    7 h# f* w$ v, x$ k# o% c4 A7 k
  43.     bsp_InitKey();         /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
    % N5 c0 w7 A! p# |( q
  44.     bsp_InitTimer();       /* 初始化滴答定时器 */
    5 \' P6 A- v& j+ I7 L) e
  45.     bsp_InitLPUart();     /* 初始化串口 */
    , z+ V7 x# P$ {, P# G" X8 d; y: _
  46.     bsp_InitExtIO();     /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */   
    ; y2 E( {! Z) R
  47.     bsp_InitLed();         /* 初始化LED */    ! f1 `8 [) t1 R2 v' A) [) y8 H3 L
  48. bsp_InitExtSDRAM(); /* 初始化SDRAM */
    4 m3 L: S' o0 C& V, b

  49. , p' w. _# V8 k
  50.     /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */   
    5 O" t& ~2 Q0 c4 b$ O! R3 e
  51.     bsp_InitDAC8562();    /* 初始化配置DAC8562/8563 */
    & ]- q1 K% [3 [/ G8 d! @6 A
  52. }
    8 G0 ^; E; Q: a& z- k+ H( V0 H; O
复制代码

( w6 ~- r  l5 D: c) F6 C* F# Q& E6 s. d6 o8 A! T3 j1 r3 x
  MPU配置和Cache配置:
, Q0 k7 }9 M" s2 T5 m: K' v- O1 }( Z3 c7 J
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区以及SRAM4; W. s( N9 `4 V: j- t' b

7 p- K* W! ]8 C4 u$ ~- Q
  1. /*
    0 i9 m0 N$ H% X9 T8 o
  2. *********************************************************************************************************) I) ^; T- v' n
  3. *    函 数 名: MPU_Config
    " S$ }! l  f- @% w
  4. *    功能说明: 配置MPU; Q( ?- G5 @1 h. c+ Z
  5. *    形    参: 无
    , o' V- y0 `6 N6 [
  6. *    返 回 值: 无
    8 h' H2 Z: W8 l  r/ T. D
  7. *********************************************************************************************************
    0 ^4 ^# x0 l7 f' o
  8. */2 G: s, c4 ~5 _' S% G- h6 c$ O- e
  9. static void MPU_Config( void )
    - |. z9 {1 N  a; p# i
  10. {# [" C) l7 R. w' ~0 x
  11.     MPU_Region_InitTypeDef MPU_InitStruct;
    ' E- [4 R9 @5 p$ a# k. U+ j
  12. . o) _4 s: `, [) u  ^/ T2 z  S
  13.     /* 禁止 MPU */
    8 x9 \, j0 I; N) t* _# t' F( T: x
  14.     HAL_MPU_Disable();% ^& D9 b& H, }0 R0 X

  15. / P) I6 P; m; C% v2 S
  16.     /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */3 [  i4 B  C6 n3 G
  17.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;$ I1 n. i' ~7 c8 G' B) Z
  18.     MPU_InitStruct.BaseAddress      = 0x24000000;" i5 x% R- d: ?
  19.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;" d9 n$ G- A) Z4 u3 Y0 p$ P
  20.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    1 x7 W# U8 r$ z  K
  21.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;0 A1 \5 H) o  T1 e4 y
  22.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;) A$ _6 d& F, f5 Y0 l+ m4 |1 n  w* |
  23.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;' y( x: L% }' f2 \  s7 @% u
  24.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    $ g: n1 k, y2 d1 w
  25.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
      Z/ L1 W) X8 @6 t$ n
  26.     MPU_InitStruct.SubRegionDisable = 0x00;5 d* k4 s; u. f' L3 J) s  r$ {0 \6 q
  27.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    8 w/ a  q! Q* q/ Q- ~
  28. 3 k8 X* G1 e- H7 T: `( R
  29.     HAL_MPU_ConfigRegion(&MPU_InitStruct);9 T+ L5 X1 W3 p  m8 V

  30. 4 P6 A# X, ~  l+ q: M
  31. ; I6 I, ]. D, T; k7 F8 a" a2 v
  32.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */1 ^) |$ z2 h1 E2 ]/ E0 P
  33.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;0 e/ ]' b1 Q5 ^$ e& N9 K* y& }
  34.     MPU_InitStruct.BaseAddress      = 0x60000000;
    $ M2 C) x1 _1 S* W7 e# c9 `
  35.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    # G% ~0 C1 a, O3 ]$ U& _0 T5 r
  36.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;* s) x" a8 Y/ O, L: \) P
  37.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;9 I2 Q2 L' n/ `7 i7 O% [
  38.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    1 e9 f! }! W, E. {# M  q1 p
  39.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    3 R% _2 {9 W" [8 V& y5 Y# E% g
  40.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;! ^9 w: S% l+ t2 V+ N0 [6 V$ p
  41.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;' I$ a4 G* B/ G. ^, Z
  42.     MPU_InitStruct.SubRegionDisable = 0x00;
    & H& `% h  v/ L( X/ [/ D
  43.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;1 A. z% f9 L( ?, {2 @
  44. 8 ?2 k+ ^) K( K9 t, K8 b5 \9 V4 I
  45.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    4 ?- o# j, h% J/ B& ~
  46. , y1 m+ s/ \% X* C
  47.     /* 配置SRAM4的MPU属性为Non-cacheable */
    4 N, s+ T" R8 s$ \) Z9 |  w
  48.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;8 q% C8 B5 n% @- j$ ]
  49.     MPU_InitStruct.BaseAddress      = 0x38000000;% l$ n$ u: ^( D$ ~* P
  50.     MPU_InitStruct.Size             = MPU_REGION_SIZE_64KB;' t, w7 I7 i7 t; R
  51.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    * t" X7 G) M. _1 v: y* H. y7 W& B
  52.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;# p, b, b0 @: f+ m/ w
  53.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;! `/ R- ^) _* ]) `" V1 W# y
  54.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    3 ~" C) F6 `$ {% [
  55.     MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
    8 i+ E' x! |  x$ ^- ?, T
  56.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    ( V- A% ?/ b& v* J8 u7 j( N' b( E- \, Q
  57.     MPU_InitStruct.SubRegionDisable = 0x00;
    3 H, l/ c: Z0 K- B7 ?
  58.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;5 Y0 D, j- b8 ^( j5 Q

  59. 8 I+ L7 U0 I& H' r
  60.     HAL_MPU_ConfigRegion(&MPU_InitStruct);
    , I! y! e. [* }+ w; r! x
  61. % C# ^) T2 `( _# F# [: ?) q
  62.     /*使能 MPU */9 R$ m3 c' v' v9 c* E2 F4 w
  63.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);" `! {& T/ k  V
  64. }8 N' {( K! H; y" t
  65. 0 O; L9 D, E' i: ]7 q' E
  66. /*6 w$ X9 o+ T0 A3 f8 ^" f
  67. *********************************************************************************************************
    8 E) P& D5 w% x* @  O
  68. *    函 数 名: CPU_CACHE_Enable
    . m2 v& f+ }' v
  69. *    功能说明: 使能L1 Cache
    / W8 v! i) o* C! q% V/ F! ~
  70. *    形    参: 无. [9 X3 J: ]4 ^/ ^3 i9 b3 {
  71. *    返 回 值: 无) F9 |1 J0 l$ B3 I3 H
  72. *********************************************************************************************************
    : y3 W: F' l- \6 M9 H! V
  73. */
    " P( b. b; r- a5 W" j
  74. static void CPU_CACHE_Enable(void)
    9 O! G8 _+ K' n- k; n1 I. ?
  75. {+ q9 c% v$ F" [
  76.     /* 使能 I-Cache */
    6 ?; F- |# [( C; h& v2 }# w  h
  77.     SCB_EnableICache();
      D+ m% ^5 D2 O! n

  78. / J. x3 ]9 G, O& ^
  79.     /* 使能 D-Cache */" ]+ Y$ A( j, e+ y1 W
  80.     SCB_EnableDCache();' z* i1 M. r; t, c
  81. }
    . {& g- v0 W: v5 v) F
复制代码
5 @% Q2 r' l' j9 D+ W0 y: [
' B7 I* i- B% k
  每10ms调用一次按键处理:4 D( {% ]  v* T! B( p

1 }( `8 ?5 p! E) M+ ?1 G按键处理是在滴答定时器中断里面实现,每10ms执行一次检测。
/ C6 w( X) f( p+ w+ V$ n
$ H) t, V6 ?& p* b4 ^4 a- h
  1. /*
    , i; p* N( o) C: V1 a" h
  2. *********************************************************************************************************2 Y+ Y4 C8 X: G( L& i! }! \
  3. *    函 数 名: bsp_RunPer10ms- ]- n3 H7 F4 y  w8 p
  4. *    功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求
    : r5 |3 N2 l7 J+ [
  5. *              不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。' w' h5 b4 ]! k! e7 w4 C
  6. *    形    参: 无
    ) R- m+ _3 ]1 y/ t. n( F" A
  7. *    返 回 值: 无" K8 g. M$ g& N5 F3 b) g( E
  8. *********************************************************************************************************
    ! _9 C4 U, {* X- \0 j# q3 E
  9. */2 k% H+ W( y+ f$ {0 [2 @+ S" Q
  10. void bsp_RunPer10ms(void)0 @# T& Z" w5 O8 m" B7 J
  11. {
    5 H) _& N2 O5 B6 P/ Z, X
  12.     bsp_KeyScan10ms();! z& N5 H0 G4 z' K/ W7 p
  13. }
复制代码
, |( @9 V& ]' f. Q/ X) b% b

* i6 F* @1 \7 i- x/ d
$ [( D" N3 T; S6 a( u+ I3 s9 p$ b  主功能:5 c" N2 S9 A0 ]& a( ~0 t! L  u

1 X1 E+ b$ g  ?( [/ W, f3 l主程序实现如下操作:* A/ p0 A7 w. A) f/ }' K

) s: Z* E8 h, E3 p% Q$ i( E8 [( e) z  启动一个自动重装软件定时器,每100ms翻转一次LED2。; Z; [0 h% N) T& {% f
  K1键按下,通道1输出方波。
, D% {6 ~6 C0 k7 p  K2键按下,通道1输出正弦波。
0 b4 y4 x2 L. U$ d# H$ Y4 `  K3键按下,通道1输出直流。* O! V, B$ v* u5 k: z: b, O
  1. /*' z; {3 m# l0 z/ j* f/ S2 F
  2. *********************************************************************************************************
    ! ^! c  ^: q/ ]0 `7 y
  3. *    函 数 名: main
    + r' d. {% x7 T( i& Y$ x! Z- p& P
  4. *    功能说明: c程序入口5 Q( d* q/ C( J5 k7 Y
  5. *    形    参: 无
    0 [! F5 }/ ]9 V3 r2 z
  6. *    返 回 值: 错误代码(无需处理)" E, T* J4 U& k
  7. *********************************************************************************************************1 q3 Y! F1 {+ r3 g/ F5 o7 l
  8. */" p( }- I. O6 P
  9. int main(void)
    : T* H" Z) V/ o! }3 f8 a8 m
  10. {8 R, q- w  V( T
  11.     bsp_Init();        /* 硬件初始化 */8 a  d: i" a4 J; J* ?/ {+ {

  12. $ [4 p! Q7 x, I" \9 a" u4 D
  13.     PrintfLogo();    /* 打印例程名称和版本等信息 */
    4 x) s' _! \+ M" U
  14. , X; }9 h4 q( K" S2 S' M1 J
  15.     DemoSpiDac();   /* SPI DAC测试 */
    " n/ V) S3 V2 c9 w
  16. }* m, B9 Z6 d6 ~8 L4 L

  17. 0 g9 G) }: m& p* d! r, _
  18. /*
    % j* r+ V' R9 v+ M/ j- b' R
  19. *********************************************************************************************************
    - s1 S5 B0 x. Z3 D- b+ y/ n
  20. *    函 数 名: DemoSpiDac  {6 z7 D' ~% U8 O
  21. *    功能说明: DAC8501测试
    ' I/ @; T5 @5 r5 [0 E
  22. *    形    参: 无
    5 p3 D5 `! K- N; W, ]: ]4 O
  23. *    返 回 值: 无
    9 D+ s9 o5 k1 t1 e" N$ S8 k1 D: @
  24. *********************************************************************************************************
    6 X+ s0 T/ `  K( J
  25. */% X1 }7 f: x) N, j
  26. void DemoSpiDac(void)+ `0 a) F3 J" Q& U! y* r  d/ ^
  27. {
    8 K7 ~* t- _0 l2 n9 k
  28.     uint8_t i=0;4 m( u) F& T1 a* w- W3 A
  29.     uint8_t ucKeyCode;    /* 按键代码 */9 C3 \  T6 c  s5 g7 l" g# T

  30. - h8 G: v4 \& a3 b8 w
  31.     sfDispMenu();        /* 打印命令提示 */
    5 D6 ^/ G& W3 t& a) x
  32. & r! D6 d8 M0 O
  33.     bsp_StartAutoTimer(0, 200);    /* 启动1个100ms的自动重装的定时器 */
    1 y2 x; P3 p* x* ?- N" j0 ^

  34. % v+ y0 k+ R4 g2 a
  35. 5 l; G& U, y$ D: |
  36.     /* 生成方波数据 */5 l- H# f6 z6 U0 ^' C$ o9 g: E
  37.     MakeSinTable(ch1buf, 100, 0, 65535);, {9 R# r7 A( i: Y0 J" d8 h

  38. 8 l9 ]+ w  y: w8 m7 V
  39.     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);$ ~# i5 S9 F/ s# q% Q, V! s0 Y
  40. ! p  V/ }" v6 h. L8 [) h. L
  41.     while(1)/ `3 ]3 f- F; @4 x# H) p2 {
  42.     {/ E  [9 K- r1 t5 t7 G# b
  43.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
    2 W0 v! _" J3 O' u% x4 M- q
  44. $ x0 u  O  j+ x( o% |1 J7 k5 F
  45.         /* 判断定时器超时时间 */1 L% N2 y) |/ ?9 E3 l
  46.         if (bsp_CheckTimer(0))   
      n+ x1 D: z( e6 f
  47.         {
    5 B1 g& j; M# n+ {4 i! b
  48.             /* 每隔100ms 进来一次 */  
    - c. D+ z7 a7 f0 O* g- s' V) ^# g5 E+ n6 }
  49.             bsp_LedToggle(2);
    , j  h) W7 }2 L" o: s" Z
  50.         }5 ?1 o' `( ]. t( {8 z
  51. + s0 `% h. b; j) m5 H
  52.         /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
    : E1 D. B0 d! a# m# z
  53.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
    " o2 z4 _+ h9 r5 R8 ~. b
  54.         if (ucKeyCode != KEY_NONE)/ {/ a3 d7 k0 a3 F; f/ j0 p
  55.         {
      g: j3 R* J- H+ g( g* N) J
  56.             switch (ucKeyCode)
    5 C$ Y' a+ H" t3 ~* `9 Y3 h5 X
  57.             {7 y4 f2 l0 t1 ^' p$ U
  58.                 case KEY_DOWN_K1:            /* K1键按下,通道1输出方波 */
    : N& J9 `" p3 O5 [+ ^
  59.                     /* 生成方波数据 */
    . `0 r; E" _6 ^3 m( S  Q1 ^; z
  60.                     for(i =0; i< 50; i++)
    5 x) {# ]' c9 G. x! g" N6 S% @
  61.                     {' z  ?) c! W) ?& t0 Q
  62.                         ch1buf = 0;3 Q! m# `0 R" P1 ~: U' a( Z8 x& \3 |
  63.                     }" h3 C* U- g) l4 O  Z! N) u
  64. 1 Y; Y( o! K- e! }
  65.                     for(i =50; i< 100; i++)
    3 P0 z4 o2 k$ Z$ ?
  66.                     {
    ' h7 d' j9 p& G  \
  67.                         ch1buf = 65535;$ k( N- H( j  ~  n$ `
  68.                     }
    $ Z+ f4 M, |2 T+ J- q
  69. 5 R9 p6 F( l: ^  F. ~
  70.                     /* 触发速度1MHz */0 W7 R$ n" t2 I. c6 q' Q$ |$ z
  71.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);" h6 l9 y7 C# G% S
  72.                     break;
      I1 A) O) u# T% y* ?
  73. 8 q# d  l/ [/ q5 l
  74.                 case KEY_DOWN_K2:            /* K2键按下,通道1输出正弦波 */7 W2 B6 u1 Q, y- _* S
  75.                     /* 生成正弦波数据 */    % t) t& h+ b( i
  76.                     MakeSinTable(ch1buf, 100, 0, 65535);
    9 o# Y) ^  e* B. z. E% q

  77. ; ~. r3 y5 y+ t; k* j3 w0 i
  78.                     /* 触发速度1MHz */4 x. F# \4 b$ z+ l, S
  79.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);$ J: C7 c% o# F3 x0 r& y+ g- a
  80.                     break;- G+ j8 K  N. d# g, z' N
  81. ; X+ ~2 e0 [; s/ N# f
  82.                 case KEY_DOWN_K3:            /* K3键按下,通道1输出直流 */
    / I1 v( A  y- @& v. o8 h) o

  83. 8 |, R* @: O4 g( ^( u3 F! a. X" ^
  84.                     /* 生成方波数据 */
    - r0 m! n+ w. l6 d
  85.                     for(i =0; i< 100; i++)$ B) y: f) S  A  J" T
  86.                     {
    ( x% {1 A) F. X; L0 l6 L
  87.                         ch1buf = 65535;3 ^2 Y. y8 z$ j
  88.                     }' A  f0 G% N5 O0 T% P! a1 j' U
  89.   |0 H6 l+ j' e0 {9 G: I7 U: A1 C2 b
  90.                     /* 触发速度1MHz */, E! B4 U* n. N
  91.                     DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000);
    # x7 ]# R% E- D( z! B" I
  92.                     break;2 }3 b# n% e; X0 ~

  93. 5 G) X) [, S+ v6 H
  94.                 default:$ D8 g. U- K/ j- K, f, p: H$ I; o1 w
  95.                     /* 其它的键值不处理 */
    . T5 G, U. i% z& o" S7 W* o* N2 T3 f4 l
  96.                     break;2 F' G: ~9 B0 B# X4 e0 L2 w& M! _
  97.             }
    : L, o' p) F9 f7 H" ^0 |
  98.         }
    ) }! X3 v6 u) A7 W* G! x( ^: t
  99.     }
    4 j: k1 g  F6 I& R. P/ W' H' R
  100. }  ~% K6 i# S! H! e% W
复制代码
( Y) Z: k. y. _+ X
( S9 W- v% v, ]) f7 m) d, T
75.15   总结' L  H0 [7 H1 w  c' J
本章节涉及到的知识点非常多,特别是SPI DMA方式驱动的实现方法,需要大家稍花点精力去研究。
& x$ J: \' g4 Z& o% k
+ v5 I: B6 r9 i* T2 C9 H
+ |) g* H- a1 U, v
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDUvMTM3OTEwNy0yMDIw.png
收藏 评论0 发布时间:2021-11-3 10:44

举报

0个回答

所属标签

相似分享

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