75.1 初学者重要提示$ w" Y( ^- S0 z6 V2 i& M
1、 学习本章节前,务必优先学习第72章。
1 E7 P/ N* F! j4 ~: p* h
3 X& C: {4 o2 A z- d2、 DAC8501模块上带了两片8501,每片是单通道DAC,带片上输出缓冲运放,轨到轨输出,16bit分辨率,支持30MHz的SPI时钟速度。
3 L# P4 \- A$ _) K' P# X9 b( Q* g: J( O! n: q" I0 p8 s# W
3、 本章涉及到的知识点比较多,需要大家掌握STM32H7的SPI , DMA,TIM,DMAMUX和DAC8501的一些细节用法。8 Z- [: f4 _- i& x. E" S
5 S% z, z8 `$ R) t- b6 w
4、 H7的SPI + DMA驱动这类外设的灵活度,绝对可以媲美FPGA去控制:
8 @3 N2 s& H# Z" R2 h7 R' j" b4 x3 Z2 r- d
H7的SPI外设比F4系列的灵活性强太多了,主要表现在两个方面:数据的传输支持了4-32bit,特别是那个NSS片选引脚,超强劲,可以做各种时间插入,灵活应对了市场上这类芯片的需求。
+ ]! f/ b! t2 m+ M DMA这块相比F4系列,有了质的飞跃,支持了DMAMUX,这个DMAMUX除了带来灵活的触发源选择,还支持了各种触发事件和同步触发功能。本章配套例子的触发周期控制就是利用了DMAMUX的同步触发功能。
% c; g# P1 a/ u+ Q" w" d0 u; f( u5、 本章配套了中断和DMA两种更新方式的案例,DMA实现方式与中断更新方式完全不同,因为DMA方式要使用硬件SPI1 NSS片选引脚驱动DAC8501。而中断更新方式使用公共的总线驱动文件bsp_spi_bus.c,片选是通过通用IO方式控制,支持串行FLASH、TSC2046、VS1053、AD7705、ADS1256等SPI设备。大家在看例子的时候要注意。
9 G5 `" o) E8 N. y4 U1 a* W& o: S
6 Z0 p2 f9 u# {" m3 c- \6、 对于本章教程配套例子的SPI DMA方式,这里特别注意一点,定时器触发一次,就会让SPI以DMA方式传输24bit数据。- P; d+ v+ _2 n7 B9 u
( _; T# j. G# P
7、 DAC8501数据手册,模块原理图和接线图都已经放到本章教程配置例子的Doc文件里。8 \5 P5 q# [! w* t3 {
( E7 [) z# t& g3 e; `3 c
75.2 DAC结构分类和技术术语" }6 R2 q8 O* z' ? i& S
在本教程的第74章进行了详细说明。
5 r6 L8 _; b; G/ T' \. ]
$ d. u- J2 {3 z0 v) k6 k% N1 B75.3 DAC8501硬件设计0 F1 `" P$ F g# A0 S
75.3.1 DAC8501模块规格
1 ]7 U5 ]" e/ Q( ~产品规格:
5 O0 W8 _3 F' Q5 S x& r6 T' q! L5 u
1、供电电压: 2.7 - 5.5V【3.3V供电时,输出电压也可以到5V】。
9 O" ?' h" L* ^2 o# F, n4 ~ g9 G& a
7 t" F* k3 L+ M$ i6 N2、通道数: 2路 (通过2片DAC8501E实现)。
+ d$ Q3 X" i1 i0 z% I) h; Y4 e4 @3 a) b8 @8 ?
3、输出电压范围 : 0 - 5V【零位 < 0.020V, 满位 > 4.970V】。$ m0 Q8 ]( { A
8 X% O9 s& \- O/ T. o
4、分辨率: 16位。
1 w }+ q+ y8 I3 i4 O
0 H9 G* v2 q: y5 J) H6 E* i7 Q, r5、功耗 : 小于10mA。9 `" H1 S1 w7 }7 \% H. w: a
2 ]$ _: J7 [! {* u% T( X
6、MCU接口 :高速 SPI (30M) 支持 3.3V和5V单片机。' y) }! Q# e3 ?" A2 I w/ _- D
% C8 k5 h# T4 \9 |. o5 q6 R5 D7、DAC输出模拟带宽:350KHz。2 n6 J2 A# ^; F8 M! n& e# D
$ X! _/ v, d( H" q5 U1 B) G
8、DAC输出响应: 10uS 到 0.003% FSR。 | ^/ y4 E+ T' q4 L4 ]4 b0 w
5 g, Y0 }* X7 S/ p产品特点:
; Z- K. M9 T q3 N2 p2 J- j+ k
0 [7 F7 o. f! |5 x1、输出和供电电压无关;模块内带升压电路和5V基准。
- x& ^! m. R7 \8 @- L% K4 Y2 ~4 L$ l8 I9 F' O
2、自适应单片机的电平(2.7 - 5V 均可以)。4 }, W' D. |) Z* E* r4 N$ d
) v5 N- Q6 b# w6 p
3、输出电压轨到轨,最高电压可以到 4.970V 以上。
) E# }- y( D9 p7 s* i# T2 R+ }" ]; a* D# r% Q }
产品效果:, B% m$ w5 T1 t0 f8 r& T
+ R' z+ }* K9 n2 {9 u# v4 U2 V
0 _, s( w' T O/ ?( @" \
' `; n8 x7 g3 W2 d9 H# Q' E( f
6 x: c4 E9 W5 ?( u2 \$ F. i
% j0 q7 U& M) i4 N75.3.2 DAC8501硬件接口# o+ F5 Q% @1 D7 w) J) }
V7板子上DAC8501模块的插座的原理图如下:
: _7 Z" q, N! G z( w* {! R* i+ b2 J9 g" m
" y4 i% e- ?; G1 w( s! e# x, J
! ^3 \! s" G4 W. n实际对应开发的位置如下:; A N( i7 V% k ?# g
4 `: z- z& u0 a. s3 r) D
/ d% L, L; s% |. B% E
, R6 d: R, `" t5 R Z: z75.4 DAC8501关键知识点整理(重要)
9 W' @$ }2 T8 H! r8 i9 |驱动DAC8501需要对下面这些知识点有个认识。" i# C4 D3 h, _- G6 }3 b, O; z3 H% e
1 s+ g# @( j& l3 M* \
75.4.1 DAC8501基础信息8 @2 S8 H! ]" G9 L3 G
单通道DAC,带片上输出缓冲运放,轨到轨输出,16bit分辨率,支持30MHz的SPI时钟速度。
. P* [8 D! V, _5 [9 Q% \- d 模拟输出带宽350KHz。' ?. h% K' w' {, M6 c
供电范围2.7V到5.5V。
- C4 D: K/ l) y4 k 具有低功耗特性。) O6 @# _# p9 J3 c O, }1 V
上电复位输出0V。" t/ I+ ?* B f
, ~8 ?/ o3 |3 y$ m/ i# V/ \
7 n2 X* j* i U" f
75.4.2 DAC8501每个引脚的作用4 [5 z" s/ r6 d! [- J D" T
DAC8501的封装形式:/ N z; B7 t }) j" r# Z
; ^ R4 ~5 i% Q9 ?, U
& b: n; B. m) A" R/ Z. \+ x: U2 j2 a7 d
Vdd
Z1 w; G7 d( D' ~供电范围2.7-5.5V。& ?. {6 ^* E; L% u; C1 ?- u
2 z* I# m u& u
Vref
4 p+ @( u; a9 W稳压基准输入。& ~' F5 ]4 L+ C+ K
: S- t4 x& q; u
Vfb
2 A/ H& W: {8 A2 j: M5 m$ O+ K3 r输出运放的反馈。
+ I3 A( `8 ?7 p; I% v: m* a: T# p- X
Vout
8 K. y# v6 \, X模拟输出电压,输出运放具有轨到轨特性。! l% Q h% a5 |; T# I9 I, H' W* K
9 Z& d6 _+ r! a5 N$ C SYNC (片选)5 l N6 k; T9 K; {) N% @
低电平有效,当SYNC变为低电平时,它使能输入移位寄存器,并且数据采样在随后的时钟下降沿。 DAC输出在第24个时钟下降沿之后更新。 如果SYNC在第23个时钟沿之前变高,SYNC的上升沿将充当中断,并且DAC8501将忽略写序列。
# S' J& a! p2 j( Z( k
; q1 v! W4 A8 {; t2 Y5 c7 q SCLK
( f* @ T1 \7 ~1 ~ X* `% ^时钟输入端,支持30MHz。
& J9 y& t, C* d$ A* t* P5 p' t- i
Din3 \3 P5 _$ @ g, o' w0 F Z- [
串行时钟输入,每个时钟下降沿将数据写到的24bit的输入移位寄存器。. G) f7 [$ S3 z* @3 w5 }% x7 T
) L4 G+ x% m0 {0 w GND7 E6 o+ }4 g( j7 ^6 e
接地端。
8 d: j2 H& _: n4 s" M! u8 b5 M$ x8 h6 M$ {9 z: v
75.4.3 DAC8501输出电压计算公式
/ m# d" Q6 B, M9 _" W2 HDAC8501的计算公式如下:
8 @& c" g0 i0 X q- e
J" q/ ?1 j- C6 c$ l% J+ u
: N- N- p' \$ ~8 i, W! `
# }" L P5 ~+ H. ~ D
- _( M) ?1 ]! c! \7 A6 F. l7 g z配置DAC8501数据输出寄存器的数值,范围0 到2^16 – 1,即0到65535。6 ~# V/ V2 w4 h! P( c& h5 j5 ]( Z
4 f. M2 A2 v6 @: d* Y \0 a VREF
1 G3 a( V8 R) ]使用外部参考电压,由VREFIN引脚的输入决定。+ i6 C) w5 H! r0 ~
) W. F# m5 e+ ^4 K3 h& T Vout
3 R1 x7 Q) T: @; b, a输出电压。
6 e6 @( W! v4 B" Q" p+ C0 R. d- M( c8 k" A8 `* G/ k: ^0 T# p0 h p
75.4.4 DAC8501时序图
& E: C: J, ^' t3 H7 }. ~DAC8501的时序图如下:
& G X0 b6 Z& b0 H! Y# f, l
# ^) d/ q2 B* j7 h: L0 W
) s5 Q) j* c! T8 N* Y
; {6 X8 b7 M# a% j, {1 n6 L0 W% h( A7 r# q' c) L/ a' l! v
1 w# F3 c9 Z9 B( N3 y, Q0 }
这个时序里面有三个参数尤其重要,后面时序配置要用到。
) L* ^* I$ q7 ?' B: A% X
* @8 m$ l9 C6 j& E# j f(1)
C) r4 Z! b4 \, k+ Q供电2.7到3.6V时,最高时钟20MHz。
1 c2 o( Q$ h" n& x
+ s- g' e0 X' f供电3.6到5.5V时,最高时钟30MHz。$ M4 F2 @% T4 i, T# h
. e* @: @& f6 |- O7 s, s1 Z t(4)
* ~, p% a5 E& j/ c9 j9 d- P( i' {SYNC低电平有效到SCLK第1个上降沿信号的时间没有最小值限制,可以为0。
, D* N& |8 u7 W; `! |5 B1 a. ^
/ f5 b a7 Y- x& j- l7 x t(8)
6 x. u) J" N0 b3 r每传输24bit数据后,SYNC要保持一段时间的高电平。7 G2 a6 Q3 |! N
. K! G$ N% \2 a( r( [+ l供电2.7到3.6V时,最小要求50ns。
7 G, L# Y8 v [6 k1 p: |. y- d- C9 V
供电3.6到5.5V时,最小要求33ns。& @4 V8 a' P' C8 s, I {. K: W9 ?
% b! u: y* _2 \3 b& ?
75.4.5 DAC8501寄存器配置+ Y4 G8 \: m- d* _5 n% p. |
DAC8501的寄存器配置是24bit格式:, |) l- y$ `# {1 h
/ f; l. O# k7 I% S0 T, i! `
$ N( [# S5 ]9 [4 Z+ N! o
% e0 j+ _) k9 e: \- _% T9 W6 ~2 {7 ~
控制DAC8501每次要传输24bit数据,高8bit控制位 + 16bit数据位。3 \- ^- j$ F: h- q. E
; H. g/ r! r9 i% e: x$ G& H0 S6 R' l控制位的PD1和PD0定义如下:4 x3 h0 I) F+ k; t2 i8 X/ O) P. K4 ~
: S% X% |7 c/ v" I
5 L) s5 c3 T# f. \: }* B1 \; n9 ^# G/ {, L' b
PD1 PD0 决定4种工作模式 T& T( {- c* B- D6 X; }
! M5 c* r4 n! @/ F& N- z0 0 ---> 正常工作模式7 }+ A' z ^+ i, i8 g+ A2 F
6 n5 w5 I8 R( v' o/ k0 1 ---> 输出接1K欧到GND* ]5 @, v# r2 E$ {. r
" S0 `! b# H" i$ l$ Y
1 0 ---> 输出100K欧到GND
1 J" H5 R$ i! Q6 M: R# a; f" `1 F
4 U& ]9 u, L0 s9 p g2 q1 1 ---> 输出高阻4 s. B; H7 H; B. A4 z& R
2 a3 {& U0 A1 P75.5 DAC8501驱动设计(中断更新方式)
; f. l5 p L' R0 M/ J& eDAC8501的程序驱动框架设计如下:. t# C4 G- J! w% d4 ~. z
% E+ i7 o! }+ u; ]
1 ~+ L! @+ N& I7 R4 ] H2 }. V0 a# a) X4 w# s
有了这个框图,程序设计就比较好理解了。: U( H) i! k- |5 ~
6 }* e/ \, h" C" a/ @5 i75.5.1 第1步:SPI总线配置
0 H% l1 M: v$ ~$ v# M% B1 e# gspi总线配置通过如下两个函数实现:
8 v! g' Y( W; v/ Z `: U+ {2 l. }% g$ G& ]8 j$ S. G
- /*" D, P5 D! v5 L# W. F4 w4 r
- *********************************************************************************************************
0 a) ?% X4 C8 D; T! `' u; f - * 函 数 名: bsp_InitSPIBus
$ _7 E* q+ W O. W c/ ~ - * 功能说明: 配置SPI总线。
( G0 ?% P% H2 b' i - * 形 参: 无
0 ?, R: [# U" D I8 d1 o2 q) Z5 K - * 返 回 值: 无
8 ]* s1 h( h3 H - *********************************************************************************************************
9 ?" |; L4 T: N: H6 V - */3 `9 v8 }/ ?/ \9 Y2 Q$ U) m
- void bsp_InitSPIBus(void)% x7 R( i; ], S
- { . S, \& \8 n6 h" b
- g_spi_busy = 0;
' {( S/ S4 @7 R4 p( I9 d: M& c - $ z! @( j, m1 V: y2 a8 o6 K( H3 _! s
- bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_1EDGE, SPI_POLARITY_LOW);
0 S# P; Z; f+ `- s) M$ n4 N - }
: Y7 d* n J$ P - 2 b4 M9 q% }& @+ h
- /*
4 ^0 ^, ]+ u5 X2 L E - *********************************************************************************************************
c' y, R+ K4 S - * 函 数 名: bsp_InitSPIParam3 X( v+ I3 _! z. ^1 P
- * 功能说明: 配置SPI总线参数,时钟分频,时钟相位和时钟极性。
/ i0 x6 {' k. p - * 形 参: _BaudRatePrescaler SPI总线时钟分频设置,支持的参数如下:8 O8 w5 U5 D: L# W% }% t0 ~/ s
- * SPI_BAUDRATEPRESCALER_2 2分频
/ R' v" U( z2 T) q( g8 } - * SPI_BAUDRATEPRESCALER_4 4分频
1 P( o. N. x, \0 e6 [9 A - * SPI_BAUDRATEPRESCALER_8 8分频% l/ n t5 }+ S0 O* E% v- Z
- * SPI_BAUDRATEPRESCALER_16 16分频- l2 r2 y. E5 t4 ?+ V8 {3 I/ s
- * SPI_BAUDRATEPRESCALER_32 32分频% e! x4 D- A% x5 P
- * SPI_BAUDRATEPRESCALER_64 64分频
O0 i5 S3 W/ p+ a; N5 _ - * SPI_BAUDRATEPRESCALER_128 128分频
# j# ~8 ^% }5 s& v% g# r9 x - * SPI_BAUDRATEPRESCALER_256 256分频' Z3 }7 f. M- ]: i9 J
- *
& \% ? g% V" _6 n - * _CLKPhase 时钟相位,支持的参数如下:
! `7 i \/ s$ K - * SPI_PHASE_1EDGE SCK引脚的第1个边沿捕获传输的第1个数据0 D( z b1 `# B* n
- * SPI_PHASE_2EDGE SCK引脚的第2个边沿捕获传输的第1个数据2 E" z1 E) P/ G# C* i- S3 N( A. L$ K
- *
" t$ }8 U9 u! x+ o- k8 n - * _CLKPolarity 时钟极性,支持的参数如下:
9 U2 n$ Z# n5 o& b& { - * SPI_POLARITY_LOW SCK引脚在空闲状态处于低电平; L! P6 j" F8 G' k+ `! v8 _
- * SPI_POLARITY_HIGH SCK引脚在空闲状态处于高电平
5 \1 r, H' b3 ?. I% ]8 U - *
' F6 q- }" Z, l8 ?) F - * 返 回 值: 无& A; k9 |! ~9 i% V- A1 s+ Z
- *********************************************************************************************************- x$ W: g U/ A/ d. }8 \
- */
" V& k5 N! h- p# @ - void bsp_InitSPIParam(uint32_t _BaudRatePrescaler, uint32_t _CLKPhase, uint32_t _CLKPolarity)6 q, D6 Z o& c
- {
$ y0 j/ r2 @) u* ~. p - /* 提高执行效率,只有在SPI硬件参数发生变化时,才执行HAL_Init */
- p% \8 z% ^+ I1 i$ P1 Q3 y4 o - if (s_BaudRatePrescaler == _BaudRatePrescaler && s_CLKPhase == _CLKPhase && s_CLKPolarity == _CLKPolarity)- W) ^3 B: {( W7 ~' H0 A
- { . Z1 b1 B+ c& \7 k4 I6 T
- return;, t1 o& ~% j# Y( [0 R
- }
/ x ?' i/ V/ [8 b% t
( R$ V3 o' T& b" s* _- s_BaudRatePrescaler = _BaudRatePrescaler; 1 ]+ s% Z9 K! ]3 R- j G; Z5 S
- s_CLKPhase = _CLKPhase;
3 k7 ]; D% w# u& F5 b/ l8 a - s_CLKPolarity = _CLKPolarity;
8 r! @8 j& l, V2 N6 o2 E - & u, u' N- h8 q% ] i' R
- . ~2 L9 \7 k7 D
- /* 设置SPI参数 */
3 h/ ^/ l- b4 ^! e* c1 R% J - hspi.Instance = SPIx; /* 例化SPI */0 g o# A8 ]0 ?6 y3 X
- hspi.Init.BaudRatePrescaler = _BaudRatePrescaler; /* 设置波特率 */9 ?& g2 ^: u" \/ K, A1 p
- hspi.Init.Direction = SPI_DIRECTION_2LINES; /* 全双工 */" B- Z; t. Z9 |3 S" _
- hspi.Init.CLKPhase = _CLKPhase; /* 配置时钟相位 */
# |1 d& I- l$ f - hspi.Init.CLKPolarity = _CLKPolarity; /* 配置时钟极性 */
; r- v$ B J) V; Y( u - hspi.Init.DataSize = SPI_DATASIZE_8BIT; /* 设置数据宽度 */8 j' U- w$ W( l! P' \
- hspi.Init.FirstBit = SPI_FIRSTBIT_MSB; /* 数据传输先传高位 */1 g: X6 K8 }$ m* I& F e$ A
- hspi.Init.TIMode = SPI_TIMODE_DISABLE; /* 禁止TI模式 */
+ F& X3 r1 V8 N3 U, C% h - hspi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; /* 禁止CRC */
: x2 c8 `8 s! K - hspi.Init.CRCPolynomial = 7; /* 禁止CRC后,此位无效 */
* _% R E* P7 r' j$ t - hspi.Init.CRCLength = SPI_CRC_LENGTH_8BIT; /* 禁止CRC后,此位无效 */' J5 A: A8 T" j% W0 a8 C
- hspi.Init.NSS = SPI_NSS_SOFT; /* 使用软件方式管理片选引脚 */
3 x. k8 z0 l: _) E, g - hspi.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA; /* 设置FIFO大小是一个数据项 */$ f/ [: A1 Z7 x8 \" _! D! M( Y6 t
- hspi.Init.NSSPMode = SPI_NSS_PULSE_DISABLE; /* 禁止脉冲输出 */
+ i6 V8 |- ]5 {' g8 s9 X3 {8 s - hspi.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE; /* 禁止SPI后,SPI相关引脚保持当前状态 */ 7 Q7 d: N9 Q! I
- hspi.Init.Mode = SPI_MODE_MASTER; /* SPI工作在主控模式 */7 G `6 o, E0 w; `; B) B7 M
0 Q% q- S9 Q; Z* f- /* 复位配置 */' [5 g% c2 C( K: j
- if (HAL_SPI_DeInit(&hspi) != HAL_OK)
4 R. O4 x9 v8 Y7 o _. I8 p - {
- z; {, d( L: C0 E" D3 ?* ` - Error_Handler(__FILE__, __LINE__);
- M3 N K( H3 x1 H. O - }
& N0 s C( R+ h- Z% X
* [# P! r+ b% m9 Q- /* 初始化配置 */6 ^7 x, k# N: ?0 ~& }6 \
- if (HAL_SPI_Init(&hspi) != HAL_OK)
\' B. C; K' q4 ` - {7 t2 y0 C$ l" s8 l9 m0 O3 M
- Error_Handler(__FILE__, __LINE__);% j+ E- T4 n4 v* o. f+ K6 D
- }
7 n. S$ ]. s8 c" _ - }+ o, `+ b( K: Z$ U
复制代码
, @2 F& U; T" F! ?
6 a* `0 x, G) x关于这两个函数有以下两点要做个说明:
9 ~4 O/ F! j6 F/ _) @ U0 I `, h: A7 k e
函数bsp_InitSPIBus里面的配置是个初始设置。实际驱动芯片时,会通过函数bsp_InitSPIParam做再配置。
0 ?) V7 T. Z' }2 E# d) S, D 函数bsp_InitSPIParam提供了时钟分频,时钟相位和时钟极性配置。驱动不同外设芯片时,基本上调整这三个参数就够。当SPI接口上接了多个不同类型的芯片时,通过此函数可以方便的切换配置。6 B, i9 m+ X6 o! j' Y& F3 O9 h! O' m J
75.5.2 第2步:SPI总线的查询,中断和DMA方式设置/ B# A; p6 ~, K1 d% |
注:对于DAC8501,请使用查询方式。
: l' k" Z$ v0 l
: m( R( Y! l0 s. ^SPI驱动的查询,中断和DMA方式主要通过函数bsp_spiTransfer实现数据传输:
4 G! e, X3 r [. v2 i& A- P! e0 ?: N" o" m+ `- G8 ^
- /*# J( h# h! z1 B4 O0 `
- *********************************************************************************************************4 F% {* c% _3 Z" ~2 K5 U
- * 选择DMA,中断或者查询方式
3 W; p8 D1 l3 O, ?- s1 g/ Y% f8 z - *********************************************************************************************************9 Y9 f& C" D. @; n' R n1 T& _
- */- }1 e1 M, B% \1 f2 X( Q! R
- //#define USE_SPI_DMA /* DMA方式 */
+ H, d2 u$ C$ y1 @: k D' C. N - //#define USE_SPI_INT /* 中断方式 */
3 M. a1 D( i7 {# S - #define USE_SPI_POLL /* 查询方式 */
) }; \: P7 ]7 I& F' g/ V) x
. O0 e q7 D2 m# F- F( V7 i: a& r- /* 查询模式 */
- g9 N5 f$ @- F - #if defined (USE_SPI_POLL)
h1 |3 O8 i3 N1 N+ r
! [6 @: A# J$ ]- G3 ?- uint8_t g_spiTxBuf[SPI_BUFFER_SIZE]; 0 U7 i6 L8 N! F% t% I. k
- uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];
, D/ d' ~& T6 r& o3 k+ y! I - + A% H7 A" ^! J r
- /* 中断模式 */
* Z# ^* D" |5 S/ z0 L$ a' T9 ` - #elif defined (USE_SPI_INT)& A& k. L( U7 }) u7 t+ F/ }) f
9 a1 s# M& d" M N: x- uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];
0 C6 a' z: a T5 @ - uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];# k/ o% V, B- T$ D% q: ~
0 D. I1 q$ }2 @5 L, \. L5 I- /* DMA模式使用的SRAM4 *// l8 P4 y4 T: ~& l* r8 ^( f/ _
- #elif defined (USE_SPI_DMA)
" E! ~6 x- I. w' }6 [5 N- f5 w a - #if defined ( __CC_ARM ) /* IAR *******/
1 `' A; q* g( F( e' @ f/ o - __attribute__((section (".RAM_D3"))) uint8_t g_spiTxBuf[SPI_BUFFER_SIZE]; & Q: ]4 _- T( K v
- __attribute__((section (".RAM_D3"))) uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];# T: S2 ~$ ?5 K2 G$ l. q
- #elif defined (__ICCARM__) /* MDK ********/
' U( _) f0 i! f, l4 ?% `5 J - #pragma location = ".RAM_D3"
+ m$ F: v4 P9 X8 \4 q - uint8_t g_spiTxBuf[SPI_BUFFER_SIZE]; : U' f( t$ g$ X% p% i
- #pragma location = ".RAM_D3"
# I% C9 L6 M- i$ Z+ s: A - uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];! X% C4 g% F6 b* {1 c6 R
- #endif
, V v# o5 t9 o$ z: `" p) r$ B - #endif
6 l+ a2 Y3 ^4 M$ f: F
! l* h+ D) e/ }- /*$ d; V4 L5 h1 `' S4 p G5 B
- *********************************************************************************************************6 L& s" j. |7 r$ Q! I9 B4 t
- * 函 数 名: bsp_spiTransfer
# A# @: c, Y" b. G5 S: j! M - * 功能说明: 启动数据传输. ?& D5 s' Q* F2 i9 z9 g+ n
- * 形 参: 无
. z. N7 u" |9 D5 a. X( C - * 返 回 值: 无. f' Y2 N0 _$ o& R& R& L
- *********************************************************************************************************) M3 H, f3 i' ~, o: \$ F+ [
- */
3 ?8 J% t- b1 C7 S1 G - void bsp_spiTransfer(void)& g: e& ~1 K9 ^5 I. Y
- {5 D2 k0 B3 f: Z3 H7 n
- if (g_spiLen > SPI_BUFFER_SIZE)
$ B: M, P6 ^9 q - {
' e5 `3 U1 ^1 E8 l, ]' D - return;. g! I! I1 W* |" D2 Z% I( [/ D
- }# _( O. g1 f. t5 J8 r* e$ j: h
: u2 t6 W( e' f; z* }1 b" p) l- /* DMA方式传输 */
, S+ d7 s( \6 z+ k( m( [- L - #ifdef USE_SPI_DMA9 g. U: I9 T, X3 i: F
- wTransferState = TRANSFER_WAIT;6 k* Z, f o$ J1 s, j
- ' d: o) x3 G' }" {0 w# k( \# R# b
- if(HAL_SPI_TransmitReceive_DMA(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK) $ S& k$ i9 a5 v) f. Z
- {
% @1 P0 o- n {- l1 n - Error_Handler(__FILE__, __LINE__);
& n" B5 Q1 `2 l; o - }2 t# U2 c( Y% W
2 {) p' N5 j% k7 K4 B ~- while (wTransferState == TRANSFER_WAIT)" z0 D! |! j+ G% J
- {+ _9 w$ M/ @6 t# |/ O- ]4 ]5 V
- ;7 ?% i K" g& X; m7 c2 g+ g
- }
2 D. @2 \5 h. a - #endif( R2 G$ e5 j0 A H8 F$ n
' l4 N' C, x0 v8 v- B5 u, N& D- /* 中断方式传输 */
( z, x! k$ k. ~+ Z - #ifdef USE_SPI_INT9 b1 Q' ~6 {$ ?: E8 ?+ E4 g
- wTransferState = TRANSFER_WAIT;# @0 t" k' C5 q- `
1 t2 t! K) J* w) d h- if(HAL_SPI_TransmitReceive_IT(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen) != HAL_OK)
5 A+ n& ] W1 [( B8 Z \; d/ s3 g# W - {
) P5 f9 \% Y: v' k$ W - Error_Handler(__FILE__, __LINE__);
- N( b! V! U/ W6 j4 V `5 a$ ? - }
H* f' ]! X% N0 U( v& n/ ]9 | - - ?& F& c1 c+ X& l
- while (wTransferState == TRANSFER_WAIT)
. @9 C! }8 Q6 W1 l" b+ ` - {
) h1 t8 X" O9 r R- \ - ;" L, ^1 W% f+ u" y: w( P: x
- }
6 g# X9 U# }5 c - #endif
) B' A/ m, i: V4 h5 E6 d2 I6 I - 9 h/ J' R+ {3 K5 Y6 ^. l5 c! P! s
- /* 查询方式传输 */
; a$ h7 C1 \* y0 B$ E9 j( o2 K - #ifdef USE_SPI_POLL) g8 m4 f! u# H& Z& g
- if(HAL_SPI_TransmitReceive(&hspi, (uint8_t*)g_spiTxBuf, (uint8_t *)g_spiRxBuf, g_spiLen, 1000000) != HAL_OK)
8 h- B3 K( N1 ]- ] }7 d& N. k - {: K; ]8 M- w4 C0 L- t; Z% b6 V+ L
- Error_Handler(__FILE__, __LINE__);( k; U) \$ }% w3 {6 \8 e& E
- }
a1 G, X* v J - #endif6 L) O K! O3 \
- }
7 t9 k' h4 V; w0 d! [3 ~
复制代码
, L: \1 P& z$ F+ E- Q
& R: `% o- c4 |) {通过开头宏定义可以方便的切换中断,查询和DMA方式。其中查询和中断方式比较好理解,而DMA方式要特别注意两点:
6 O I6 `7 t5 q& G% z a# D
; c" U. X) @( B& {" q 通过本手册第26章的内存块超方便使用方式,将DMA缓冲定义到SRAM4上。因为本工程是用的DTCM做的主RAM空间,这个空间无法使用通用DMA1和DMA2。
2 y) ~2 }( l, [. O# @ 由于程序里面开启了数据Cache,会造成DMA和CPU访问SRAM4数据不一致的问题,特此将SRAM4空间关闭Cache。1 S9 N" z5 R) [
/* 配置SRAM4的MPU属性为Non-cacheable */% \' }# X* }3 Q
MPU_InitStruct.Enable = MPU_REGION_ENABLE;3 s9 @5 |" ?" i
MPU_InitStruct.BaseAddress = 0x38000000;
; f1 N) l/ h! p! v% B0 V MPU_InitStruct.Size = MPU_REGION_SIZE_64KB;) h# e* Y# r: W; l
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;9 d4 s# U* T1 v+ z8 F9 j
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
, R! G& r: Y5 v5 G' \* W8 Q MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;. |' }! Y% L1 c6 T4 G
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;( h' S' p7 D* y' t
MPU_InitStruct.Number = MPU_REGION_NUMBER2;$ T2 K7 {3 b' U8 D" M
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
3 W* v5 o7 Q5 a+ f3 C7 Y MPU_InitStruct.SubRegionDisable = 0x00;. J9 K! e" m m: Z; J' Y" u I5 X. z
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;( u3 p9 C7 X, a* a, l+ N
& N: M' q6 G9 F2 W, a9 H3 T HAL_MPU_ConfigRegion(&MPU_InitStruct);9 ~. Y8 k4 a+ H1 A6 n$ K" _
7 R. i* r% ]1 e+ Q' |6 p" V& [! J
2 t' j. V7 Z% k, n. ?& p75.5.3 第3步:DAC8501的时钟极性和时钟相位配置) S- `5 J/ E; z- y3 g
首先回忆下STM32H7支持的4种时序配置。- b2 J! Z. I. d) d0 S2 i* u: l
+ a1 I6 l' D: P+ Z 当CPOL = 1, CPHA = 1时
) k T& H+ }$ N3 N# p5 E0 B& r# eSCK引脚在空闲状态处于低电平,SCK引脚的第2个边沿捕获传输的第1个数据。, t$ N9 G4 D" @3 u0 A
% F+ L9 H( Z* N+ {$ ?2 ?! ~4 s9 Z
当CPOL = 0, CPHA = 1时6 `, z4 \1 T" N" u8 O }/ ~( w
SCK引脚在空闲状态处于高电平,SCK引脚的第2个边沿捕获传输的第1个数据。
2 u4 @# Y, ~) d8 C4 Z$ s! d7 j
$ O( S% A8 Y O 当CPOL = 1, CPHA = 0时0 V |" Q; S9 ~$ {! r
SCK引脚在空闲状态处于低电平,SCK引脚的第1个边沿捕获传输的第1个数据。
3 a+ u, [0 i% h- I; N+ w3 ]9 G9 u: Q* h8 w1 q9 F. u. P
当CPOL = 0 ,CPHA= 0时' U! Z" s# B9 j5 l3 S5 S6 A& ^4 B
SCK引脚在空闲状态处于高电平,SCK引脚的第1个边沿捕获传输的第1个数据。' n, \6 A0 z, R
) c# L" w- E$ {% `2 v/ M$ g: C7 y
5 D. e, U9 A$ ]) ^1 t n% W" ^) M
有了H7支持的时序配置,再来看下DAC8501的时序图:
6 o! f. q0 e4 y G4 k
, I8 ~. c+ i. K2 w. |9 y5 x e. D- E8 y9 ~, @" Y4 i1 y a% V& G0 ~, L5 f
' u* ]( p- X: T首先DAC8501是下降升沿做数据采集,所以STM32H7的可选的配置就是:
! @+ u3 i, H$ f* L! e0 }, N! j6 N& l. ]2 D! E r
CHOL = 0, CPHA = 1
1 c/ J2 y7 I% x1 X! D- N
; ^$ S4 e n8 \( kCHOL = 1, CPHA = 0
- A( S: N4 S7 t4 G
, O. s! y- j% F# W对于这两种情况的主要区别是空闲状态下SCLK时钟选择高电平还是低电平,根据上面的时序图和DAC8501的数据手册,两种情况下都可以正常运行。经过实际测试,STM32H7使用这两个配置确实都可以正常运行。程序里面默认是选择CHOL = 0, CPHA = 1。: v9 o' K; j# g r+ I6 l7 n+ g' G
" {9 R" \& j1 A2 x1 ^5 [75.5.4 第4步:单SPI接口管理多个SPI设备的切换机制6 P0 ^( e. P+ a+ H) a
单SPI接口管理多个SPI设备最麻烦的地方是不同设备的时钟分配,时钟极性和时钟相位并不相同。对此的解决解决办法是在片选阶段配置切换,比如DAC8501的片选:( D( J/ q4 D3 e7 p) S1 u' I) f
" M7 B& S# {# g4 R- /*: g- u9 ?2 A4 x" F+ k- q) Q
- *********************************************************************************************************. W- j* [$ M" M! \4 q
- * 函 数 名: DAC8501_SetCS1' k" S! ]7 L# x& | d
- * 功能说明: DAC8501 片选控制函数
o8 u U' d% h2 X! M _+ N - * 形 参: 无9 i5 }; f' H: u
- * 返 回 值: 无8 O/ C& F* b; i5 ^7 w0 D
- *********************************************************************************************************
( C3 m `6 |: X - */
4 Y0 J: {7 Q; Z7 } - void DAC8501_SetCS1(uint8_t _Level)6 K4 M- `# L/ E8 o
- {- Z1 g' L6 Y5 d0 n( J8 k+ I4 a
- if (_Level == 0)( i. S4 H f) R/ c4 N9 m
- {
# i) y: N" e+ ~" K& f - bsp_SpiBusEnter(); /* 占用SPI总线 */ 7 y0 k' |1 l- B" i, R" u
- bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW); 4 L6 X% u& P8 B
- CS1_0();8 J- r# D3 J9 I7 P, ^
- }
* J1 V5 J" S' S6 ~ - else6 }' V0 k1 }) |' A5 L
- {
' I3 ~! L7 p% c - CS1_1(); 0 v) T! X" J) b5 ]
- bsp_SpiBusExit(); /* 释放SPI总线 */6 R8 u$ _9 D% H, S/ s4 z
- } 5 N7 b2 }0 z, V; ~4 g; L
- }
: P1 d+ _7 d' y0 J8 @% R6 ` - 1 z+ R0 v$ m& G5 d& J) I( p
- /*
* Z# P; u% G! V! i( F. b' U - *********************************************************************************************************) {. E5 u# P' n# G
- * 函 数 名: DAC8501_SetCS2(0)
7 a1 g- x J: N% A7 U/ _ - * 功能说明: 设置CS2。 用于运行中SPI共享。
5 R' T- ^) O) I& e) V, G5 f9 A3 a - * 形 参: 无
+ [2 g( k/ r8 o4 `5 U+ ^- ] - 返 回 值: 无
2 R0 p% s6 b! j# l& A' Q - *********************************************************************************************************
& W: @" s( ?0 |( y& c0 i6 N - */
; y: c; a. \- `6 e& V5 K6 R( x @ - void DAC8501_SetCS2(uint8_t _level)/ f: D% W0 [0 L
- {
/ u( p& W4 n2 X: n6 P- D - if (_level == 0)
' R2 }; @6 [+ k4 T+ l. p1 b - {
/ H) @ h* j$ [( d$ m7 J - bsp_SpiBusEnter(); /* 占用SPI总线 */& L ~) K4 `/ F5 T
- bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW); ) v3 V& q+ l3 L; U, P( j
- CS2_0();
: j- E% c. _1 U% ^ - }
# Z/ L& G. j( D! b6 b& H: C - else
$ Q& M& n# \8 J/ `' ^ - {
: I9 Z( s3 U& E- q8 b( Z - CS2_1();
" y1 k x; I7 G - bsp_SpiBusExit(); /* 释放SPI总线 */
3 \6 g- \2 Z5 W8 D - }
6 R/ |# N' n5 E, y - }
复制代码
+ o% [* K# k0 k9 i. `通过这种方式就有效的解决了单SPI接口管理多设备的问题。因为给每个设备都配了一个独立的片选引脚,这样就可以为每个设备都配置这么一个片选配置。1 |3 I! d. l' I. k
7 P7 w8 K/ j8 r4 E但是频繁配置也比较繁琐,所以函数bsp_InitSPIParam里面做了特别处理。当前配置与之前配置相同的情况下无需重复配置。4 f7 H" w v, {6 r& q! @- y5 P
! j( t x6 g2 R0 ~9 g: U2 \75.5.5 第5步:DAC8501的数据更新- c& i6 L- x# I. q+ ]3 P
DAC8501的双通道数据更新通过下面的函数实现:$ @: @; J2 [6 @
/ e) E3 K5 L4 D" w5 O; I* G& M
- /*
. C5 A9 _4 t0 N0 H - *********************************************************************************************************
' T( v2 r7 l+ P( d1 C& ` - * 函 数 名: DAC8501_SetDacData
* Y) L+ ^4 H0 |/ D1 S - * 功能说明: 设置DAC数据) X& M2 j( d3 A+ R2 B" [* u' {
- * 形 参: _ch, 通道,8 F! @7 X2 `" l8 Q
- * _data : 数据
0 A' h( I7 h- g9 G ]. z - * 返 回 值: 无! X' t0 v8 ~) p" P: _$ f* c/ T
- *********************************************************************************************************( z0 g r" \1 n5 P1 J# P" S
- */5 ]- Z3 M! D% {8 R* n- A( }
- void DAC8501_SetDacData(uint8_t _ch, uint16_t _dac)
% v% N; i6 ~: a - {
" ~, i: P- [3 K5 `# L7 P - uint32_t data;, I1 ^- [* q9 j; M- r) j/ }) @6 X
. n6 o4 c$ h$ w7 p! V- /*
* l" E9 \/ S. Z# Q - DAC8501.pdf page 12 有24bit定义& F0 p: Q9 r5 n8 R4 k
- . I+ k9 y1 J! E* j
- DB24:18 = xxxxx 保留
; i; I; W# ~: v, ] - DB17: PD1( G; |$ \& O9 t K
- DB16: PD01 v* W ~* d2 Q; D
. k# D/ I/ ~( i+ X) }" H, D- DB15:0 16位数据
6 k& `: a6 N0 E% j9 I. P# r% v' |2 N
, O: {# m+ G( S- |- 其中 PD1 PD0 决定4种工作模式
; ]. _3 `1 I$ |6 e ~+ J - 0 0 ---> 正常工作模式) e3 ?/ |4 `4 H" ]* r/ `/ k2 g& `
- 0 1 ---> 输出接1K欧到GND: m1 _. Z0 X+ w) r$ V. S
- 1 0 ---> 输出100K欧到GND
. K' |+ W9 j5 a% i& ]: c7 | - 1 1 ---> 输出高阻$ e3 F [* Z% B9 b% X
- */- ^% J( i' D4 j( V* a6 n
- 8 c6 _2 x% }8 z
- data = _dac; /* PD1 PD0 = 00 正常模式 */
9 D! R' M4 y& {" |6 D& Y$ Q - 4 ` x g5 a" v! v2 o" h
- if (_ch == 0). o8 p+ I8 I5 F. W6 I* @2 Z
- {1 r: @" {% q' K& K" E
- DAC8501_SetCS1(0);
( g0 V$ q8 u" `+ V. {+ X0 g - }
5 ~; Y6 b6 X$ v. d+ q' h, e9 J" e - else2 g7 f) ]3 [* P' ~3 q
- {
0 J+ x4 C, J( v# W N8 V' o# i, M& V - DAC8501_SetCS2(0);
6 q, q+ r* t9 ?( y5 h8 e - }& k( Y& G1 d% n4 ?' n; n, Y
* L+ U! U* r& e* y: I- /* DAC8501 SCLK时钟高达30M,因此可以不延迟 */
1 N6 U1 _8 N* e% B" O: Q) e* [1 ?* K; M5 m - g_spiLen = 0;, G( W" Q0 b! j. _: N; P3 W# l7 c
- g_spiTxBuf[g_spiLen++] = (data >> 16);
3 a h9 X6 `' N7 V* G1 n - g_spiTxBuf[g_spiLen++] = (data >> 8);
7 R( F( p' X4 Z3 w - g_spiTxBuf[g_spiLen++] = (data);1 G2 r l( H; p; s8 u% y
- bsp_spiTransfer();
/ b: B5 p S: n. n, x+ G9 C - . D/ h& r2 [5 s, Z1 b# V1 @
- if (_ch == 0)
3 J% F" E1 ^( W |* O - {
, ^; `% E& M [1 }7 m; b$ t0 ~ - DAC8501_SetCS1(1);, U5 C3 Q- Z! L2 k& b- h
- }1 J2 ~* h9 u1 x- m
- else
' r) ^6 k! [% g! ]8 u! U - {0 d- e) r9 ?( j. X9 n8 }
- DAC8501_SetCS2(1);' G# W8 K9 m' a. C/ G5 a
- }3 p, F/ e6 d4 K( l! N- G
- }
复制代码 5 ^ t/ I8 Q* u0 ~! v
函数实现比较简单,每次更新发送24bit数据即可。. F$ d: C9 F1 V ^6 C h
q; e+ N! `5 \) Y75.6 DAC8501驱动设计(SPI DMA更新方式)
6 |) Z. T/ g5 Z& B; B- ]DAC8501的DMA驱动方式略复杂,跟中断更新方式完全不同,要使用硬件SPI1 NSS引脚驱动DAC8501的片选,所有专门做了一个驱动文件来实现,程序驱动框架设计如下:5 k( I1 v$ C- }! ]) q
: { j+ [( y) e4 O3 E4 Z
; R8 x* J% h2 f6 K! s( `* n- ~$ A# A( V3 i. s" H% S
有了这个框图,程序设计就比较好理解了。
1 b! O1 [& M$ S" o4 L
% P$ a7 M1 v' K3 s75.6.1 第1步:SPI总线配置
3 w2 a; Q, E/ lspi总线配置通过如下两个函数实现:4 L. N/ {) u: f% F
) l# f% _9 A0 A4 H y
- /*
8 L: Y$ @9 P& z2 Q - *********************************************************************************************************/ C% P8 o5 @9 c$ M
- * 函 数 名: bsp_InitDAC8501
}9 l$ d5 c2 |' p9 ~8 j - * 功能说明: 配置GPIO并初始化DAC8501寄存器
$ R9 O8 M, n7 Q4 g - * 形 参: 无
; G1 ]) T6 L t5 } - * 返 回 值: 无
' H0 `5 o h& k r - *********************************************************************************************************' Y/ T6 [1 p7 D l/ e2 Z
- */ S0 |& |6 Y: G) n5 \7 K
- void bsp_InitDAC8501(void)3 z3 M0 l" |$ g5 |, @4 x! h& W* d
- {
) M" T. Y! R. ~5 N- t7 f - s_SpiDmaMode = 0; - o; T0 T( z) M+ c8 Y
* z, A* f7 N9 L# R2 L- /*##-1- 配置SPI DMA ############################################################*/! v5 @6 h" I Q* V
- bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);
: m# J3 d) @: L% g - 8 j! T& C/ b% ]( w
- /*##-2- 默认输出0V ############################################################*/; s# C5 N' [2 `7 j2 z
- DAC8501_SetDacData(0, 0); /* CH1输出0 */: t$ {7 w1 R, |8 @" k
- }
) a3 x' H% C7 V& h1 f
+ U6 i. W+ M0 s; f- /*% i* j7 }0 l+ p1 K0 P& P; y
- *********************************************************************************************************
) t* s' ?8 n& `4 b - * 函 数 名: bsp_InitSPIParam
( m( v% z: D% q; K- z3 y7 ~ - * 功能说明: 配置SPI总线参数,时钟分频,时钟相位和时钟极性。
4 |: X$ {: k3 x. ]/ P - * 形 参: _BaudRatePrescaler SPI总线时钟分频设置,支持的参数如下:; d/ }; B+ I8 p9 Q; W4 Z
- * SPI_BAUDRATEPRESCALER_2 2分频
0 k3 p5 J4 g- G5 I# _ - * SPI_BAUDRATEPRESCALER_4 4分频1 H1 T k0 R7 G3 u. @
- * SPI_BAUDRATEPRESCALER_8 8分频
/ }9 M/ m6 a, D- W2 K - * SPI_BAUDRATEPRESCALER_16 16分频' P0 z6 p7 K/ n' h/ L, Z" F" z3 X
- * SPI_BAUDRATEPRESCALER_32 32分频
* @. x+ z5 j; C( S- B0 K- _ - * SPI_BAUDRATEPRESCALER_64 64分频
- q$ A9 n& ]% y) \2 e - * SPI_BAUDRATEPRESCALER_128 128分频
. [" H$ [. ~% D' n - * SPI_BAUDRATEPRESCALER_256 256分频
# V' ]. ~! p6 B2 u - *
* g8 J0 P5 H8 k3 _/ J- W, w - * _CLKPhase 时钟相位,支持的参数如下:
" ]6 G$ l% h8 Y2 K/ F; |1 ?) M - * SPI_PHASE_1EDGE SCK引脚的第1个边沿捕获传输的第1个数据
9 N6 [2 m# ?6 i: D/ t, x - * SPI_PHASE_2EDGE SCK引脚的第2个边沿捕获传输的第1个数据
7 K1 G. S3 u" S - *
$ @: G1 g3 h+ _' b7 p" Y - * _CLKPolarity 时钟极性,支持的参数如下:
; P3 o" H0 ^# X4 I7 g6 U8 O5 E - * SPI_POLARITY_LOW SCK引脚在空闲状态处于低电平
0 R* L7 A) r+ D; i2 @ - * SPI_POLARITY_HIGH SCK引脚在空闲状态处于高电平3 ?+ m2 ]" W/ }8 b6 x; E
- *' B/ f# P( v: _: [+ v, `
- * 返 回 值: 无
) ~; Q" {' H+ G$ B: a - *********************************************************************************************************0 z, D: i$ }+ i
- */
4 F' d% c! f- | - void bsp_InitSPIParam(uint32_t _BaudRatePrescaler, uint32_t _CLKPhase, uint32_t _CLKPolarity)
- ]3 B0 ~ Z W7 G8 X - {$ B6 E0 e! z7 b% ?0 s
7 [( r- B) d8 c2 v% F ]4 K' q: B2 }- /* 设置SPI参数 */( D* M' g/ t- A3 ^; P6 y7 x
- hspi.Instance = SPIx; /* 例化SPI */
0 x; E# c! c5 Z8 I$ }' P3 o - hspi.Init.BaudRatePrescaler = _BaudRatePrescaler; /* 设置波特率 */
' y4 b# ]# a' d: u$ Q2 O0 X - hspi.Init.Direction = SPI_DIRECTION_2LINES_TXONLY; /* 全双工 */
: O& M. C' ]; C" `* Q! m, t. [ - hspi.Init.CLKPhase = _CLKPhase; /* 配置时钟相位 */& K0 r! f3 K! `. g7 V
- hspi.Init.CLKPolarity = _CLKPolarity; /* 配置时钟极性 */; K4 z" f7 m& _9 c e+ w6 i: i
- hspi.Init.DataSize = SPI_DATASIZE_24BIT; /* 设置数据宽度 */8 p- \- J2 P# P1 @# b. C1 b
- hspi.Init.FirstBit = SPI_FIRSTBIT_MSB; /* 数据传输先传高位 */
) s: g' c' z: |2 \ - hspi.Init.TIMode = SPI_TIMODE_DISABLE; /* 禁止TI模式 */. w, S5 L$ ^5 c a' p1 u
- hspi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; /* 禁止CRC */$ p9 H3 }- \& o5 o. V6 F" f
- hspi.Init.CRCPolynomial = 7; /* 禁止CRC后,此位无效 */
8 b: @8 u: D$ r! n - hspi.Init.CRCLength = SPI_CRC_LENGTH_8BIT; /* 禁止CRC后,此位无效 */
) k5 g: H% O; q" M9 I1 \4 E0 Z. _ - hspi.Init.FifoThreshold = SPI_FIFO_THRESHOLD_05DATA; /* 设置FIFO大小是一个数据项 */
( q- w0 n4 x, C) q6 ^. d+ y" i2 W& o
) x7 n3 W1 o0 L# C5 ~1 i7 d& {, F- hspi.Init.NSS = SPI_NSS_HARD_OUTPUT; /* 使用软件方式管理片选引脚 */
* L+ @: e# G, X/ y! ] - hspi.Init.NSSPMode = SPI_NSS_PULSE_ENABLE; /* 使能脉冲输出 */
# C" X" S+ l, X - hspi.Init.NSSPolarity = SPI_NSS_POLARITY_LOW; /* 低电平有效 */4 {; }2 x! Y( I( B( Z: W. _) Z
- : J: ^. m$ k9 s4 \' `7 `& a0 B
- /* MSS, 插入到NSS有效边沿和第一个数据开始之间的额外延迟,单位SPI时钟周期个数 */
0 ?6 j B. m8 X* f# x8 V - hspi.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE; 4 P( J, i/ G4 ~, b1 q: ^
- . P5 r) Q$ v% {' C
- /* MIDI, 两个连续数据帧之间插入的最小时间延迟,单位SPI时钟周期个数 */5 w' g4 r8 ^+ `4 u+ G5 }
- hspi.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_02CYCLE; : G z% I2 e/ G) [- a' T
, Z/ K( u$ a# A8 Q* K0 b& O" d- @- hspi.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE; /* 禁止SPI后,SPI相关引脚保持当前状态 */
0 ]% n- j3 u0 W. M+ Q8 w, g - hspi.Init.Mode = SPI_MODE_MASTER; /* SPI工作在主控模式 */( o' y/ F/ x+ {# ]2 `$ X
- ! O) l5 R; w( _! c5 j" n/ l4 k B; R
- /* 复位配置 */
1 }% f1 F- i9 c5 ?3 H - if (HAL_SPI_DeInit(&hspi) != HAL_OK)
! M2 c ~0 B; z5 }3 p9 W - {! V' S3 q" t9 n1 S1 `4 ? T) j
- Error_Handler(__FILE__, __LINE__);0 `) k/ m' G+ m, u
- }
" a( o7 q1 \5 C4 s& l
6 L3 _- E$ R$ U1 G4 z$ z6 B- /* 初始化配置 */
. H! Y; x, q+ a5 w$ [& C) {" P - if (HAL_SPI_Init(&hspi) != HAL_OK). ~2 T9 B2 s3 Y! T
- {
& o0 U- f9 Q4 { K - Error_Handler(__FILE__, __LINE__);
! c0 R7 S& Y/ w1 W - } & v- I$ n' p3 u0 ]. H
- }
复制代码
9 c. c4 D; T% M% s& v4 @& d/ m
/ `( f7 C6 L/ y+ G/ r* h
! v! e! n" `0 T0 _, E( v这两个配置函数里面最重要的是置红的几个配置选项,这里依次为大家做个说明:
0 _# {% t, \! J' p) A, @2 L" }
' |; w! q% |: z/ n* I SPI_DIRECTION_2LINES_TXONLY
# K4 n3 {. m9 F* j1 _) i驱动DAC856X仅需要SPI写操作。. n2 N" Y1 N m5 d- |1 D
c2 l- H6 N1 g: e0 S SPI_DATASIZE_24BIT$ {# P R- y3 p
STM32H7的SPI支持4-32bit数据传输,由于DAC856X需要24bit数据,所以这里配置为24即可。
8 ]8 Y$ X& z; G9 K3 K
$ U% s+ }; {6 ?. B* v7 O SPI_FIFO_THRESHOLD_05DATA
, K& [* q1 r7 s; J/ b) a, S) p对于SPI1来说,里面的FIFO大小是16字节,那么SPI数据传输配置为24bit的话,FIFO最多可以存储5个24bit,因此这个fifo阀值要设置为5。4 ?) r2 j0 ?6 S$ e7 a! i
3 P! `* Z- q4 ^9 D
SPI_NSS_HARD_OUTPUT
* b+ F. y5 d' b我们这里要使用SPI的硬件片选引脚SPI_NSS。
+ |/ ~2 m/ W9 O* j" `1 V- P" i! _' G- g: S7 c3 O7 h7 u# z) d
SPI_MASTER_SS_IDLENESS_00CYCLE1 E, R! R, p p5 t
插入到NSS有效边沿和第一个数据开始之间的额外延迟,单位SPI时钟周期个数。+ j3 {, w6 r) P4 [ Z3 n+ z: z
: z5 Z0 i: \' S: F, U$ m
根据本章75.4.4小节里面的t(4)要求,片选有效到SCLK第1个下降沿信号的时间,最小值为0。所以这里配置为0即可,也就是无需插入时间。
' ]* D& S7 ]2 E! y
. T* n% |4 p) n9 `; o SPI_MASTER_INTERDATA_IDLENESS_10CYCLE
* c) }9 e! D) x5 }9 c两个连续数据帧之间插入的最小时间延迟,单位SPI时钟周期个数。! U6 }, f$ l, T+ ?
- C0 ]! e4 O; T+ X: k4 F& r
根据本章75.4.4小节里面的t(5)要求,每传输24bit数据后,片选要保持一段时间的高电平,DAC856X要求至少要33ns(供电3.6到5.5V时),也是说,如果我们以25MHz驱动DAC856X,这里至少要配置为1个时钟周期,推荐值为2及其以上即可,我们这里直接配置为2个时钟周期(配置为1也没问题的)。
* r. c. c: i8 w. E7 h% b# J
- H' {% t$ t' O" c+ d/ B75.6.2 第2步:TIM12周期性触发配置
7 A. O( e) N5 ~, i( }( \% Q3 }这里特别注意一点,定时器触发一次,就会让SPI以DMA方式传输24bit输出。
7 r6 b# C5 H- G2 D- }9 V/ z, O% A9 b# H% O- `
TIM12的触发配置如下:
7 Y- \, n/ o2 Q$ ]! ]7 W3 n) `; o" T9 p/ P, I& x
- /*4 S8 ]: q9 U% h; _1 k
- *********************************************************************************************************
/ ]% w) H+ D8 m' d+ ]0 Z( v1 o7 E - * 函 数 名: TIM12_Config: ^ d( Y2 h0 H" C# \
- * 功能说明: 配置TIM12,用于触发DMAMUX的请求发生器
/ s% C0 L- y0 g9 m - * 形 参: _ulFreq 触发频率,推荐范围100Hz - 1MHz
6 R9 {2 x# q/ j- l* ` - * 返 回 值: 无* R! r. P& S( ?$ h( f- R$ K
- *********************************************************************************************************
! K* Q# Y4 a+ o' k7 E9 ? - */ 0 d9 _2 _" k+ m3 h
- TIM_HandleTypeDef htim ={0};
( R; i% C6 U+ u9 u5 i( b8 Z, ^7 B - TIM_MasterConfigTypeDef sMasterConfig = {0};# \- f! o6 b: W/ W& R" q
- TIM_OC_InitTypeDef sConfig = {0};
, j& f* w' U' D- ?) n* k0 \ - void TIM12_Config(uint32_t _ulFreq)* s# b' J% E$ g/ S3 C
- {6 j% K6 ` O/ d ]" ^/ v
- uint16_t usPeriod;
4 Y, ?' a' o+ ?" I% z: | - uint16_t usPrescaler;
5 X% M# a \9 ]- x) u4 X - uint32_t uiTIMxCLK;
+ E& `0 W( W+ F: k
( N2 u Z7 C) C: D& f6 B- # q& \ S0 J0 c/ F) g
- /* 使能时钟 */ 8 P7 I2 q6 p* \3 d% h5 w' I i" g1 z
- __HAL_RCC_TIM12_CLK_ENABLE();: n$ E$ i4 b9 J. |2 } X0 p8 j$ n
$ L1 S* M+ `* i- /*-----------------------------------------------------------------------6 z; P& `9 W9 Y+ f. I
- bsp.c 文件中 void SystemClock_Config(void) 函数对时钟的配置如下:
6 @0 i, y3 v \ ?' j - 0 Q' V. k8 U5 \* H0 w$ ]4 O8 O
- System Clock source = PLL (HSE)
! t; R7 c/ l. \$ N3 T2 H - SYSCLK(Hz) = 400000000 (CPU Clock)7 L" b( o7 _- F7 t! o% Z5 V
- HCLK(Hz) = 200000000 (AXI and AHBs Clock)7 Z, b: }1 i4 P# k: r" S
- AHB Prescaler = 2 Z6 S% l# o3 B, h8 S5 \, s
- D1 APB3 Prescaler = 2 (APB3 Clock 100MHz): ?& J: B" d K% G! T3 @/ j- D
- D2 APB1 Prescaler = 2 (APB1 Clock 100MHz)! w8 V$ l M, T
- D2 APB2 Prescaler = 2 (APB2 Clock 100MHz)+ l$ T& f; x& ?/ q3 c5 T+ j9 E) o
- D3 APB4 Prescaler = 2 (APB4 Clock 100MHz)& i$ b) r& I' F& }
- G2 A* g4 y ]" N1 y# h6 L) Y- 因为APB1 prescaler != 1, 所以 APB1上的TIMxCLK = APB1 x 2 = 200MHz; 不含这个总线下的LPTIM1# z% W2 I+ w+ M& b+ O* y8 R+ I
- 因为APB2 prescaler != 1, 所以 APB2上的TIMxCLK = APB2 x 2 = 200MHz;) a! v4 C9 d6 @# ?3 `
- APB4上面的TIMxCLK没有分频,所以就是100MHz;4 Y4 _8 O/ [/ I
- + B& F! O) [. Z! | g
- APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14,LPTIM1
7 h/ o) r/ \5 l - APB2 定时器有 TIM1, TIM8 , TIM15, TIM16,TIM17
i8 h W! e% f
8 u: {& {" Z6 b# I% J3 a" a2 |- APB4 定时器有 LPTIM2,LPTIM3,LPTIM4,LPTIM59 w) d% k8 {/ Q c( M. m
- ----------------------------------------------------------------------- */
3 F! t5 f) c7 X0 o I0 ^& h: N - uiTIMxCLK = SystemCoreClock / 2;
% w& e& k' s; l& ~! ^& v - ( {. r1 ^1 \ G$ H \5 P; U. q
- if (_ulFreq < 100)- N- z% I! Y/ }
- {8 o# c+ P0 q9 `) E
- usPrescaler = 10000 - 1; /* 分频比 = 10000 */
" W w1 G3 M/ X2 n2 }- P - usPeriod = (uiTIMxCLK / 10000) / _ulFreq - 1; /* 自动重装的值 */ B1 W# i. l; j7 T% ~3 m' h2 l
- }
: Z R2 x" x: b/ |/ @! J" r - else if (_ulFreq < 3000), H# c1 I9 s1 a8 o. C! v/ N: y
- {. S6 u) u e% {7 U7 Z% Y! W
- usPrescaler = 100 - 1; /* 分频比 = 100 */
, D1 [$ m3 g% [4 B2 I5 F/ z4 H, J - usPeriod = (uiTIMxCLK / 100) / _ulFreq - 1;/* 自动重装的值 */
7 C( \1 G. c& G# v+ J# g - }5 M: t) A$ A3 Y/ w, c* Z
- else /* 大于4K的频率,无需分频 */% O- O! d$ k. \' {& I
- {
% I: o: A+ N0 M3 O+ i# P/ u - usPrescaler = 0; /* 分频比 = 1 */9 v+ Y6 k1 u2 ]; K! v
- usPeriod = uiTIMxCLK / _ulFreq - 1; /* 自动重装的值 */
- R$ B# z$ w4 X- T& ^# c5 W - }
$ x0 k( u# [" w0 _) p+ }
7 g" v5 ^/ u9 E2 @4 [ x6 G4 }- htim.Instance = TIM12;8 T' d$ b' n% F% ~6 A8 u" h
- htim.Init.Period = usPeriod;
, n; s* Y2 Q* V. v - htim.Init.Prescaler = usPrescaler;
1 k* M4 B5 r. N$ I5 S+ a - htim.Init.ClockDivision = 0;$ p) h" o/ ?1 L( e
- htim.Init.CounterMode = TIM_COUNTERMODE_UP;
0 A0 J M* T6 @, }" T+ x - htim.Init.RepetitionCounter = 0;! X. n4 `, @+ e
' s8 z( N7 j. |- if(HAL_TIM_Base_DeInit(&htim) != HAL_OK): M* A/ p/ d. I8 Z9 Q
- {9 L+ p8 z+ D" I) j5 Y1 R/ Y2 G" l1 t
- Error_Handler(__FILE__, __LINE__); ( r+ y L# f' h$ [6 Z7 m3 L
- }# l' E$ g! m7 T6 @; D
1 y# T; l/ O+ d9 B- d; v7 b- if(HAL_TIM_Base_Init(&htim) != HAL_OK)
+ s9 w' |6 n( r( Q - {, u( N" A: D% R+ j8 [4 f+ i0 P
- Error_Handler(__FILE__, __LINE__); ; a$ |, |, Y+ ^
- }- `" G5 I' E* s* C% B1 d# T+ g2 u; ^
3 {; \2 A; K: k7 T' [2 E4 z c, O- sConfig.OCMode = TIM_OCMODE_PWM1;
" Q* V3 p8 S$ Y; T- c4 B; T - sConfig.OCPolarity = TIM_OCPOLARITY_LOW;
# r& B: _& s! T2 z - sConfig.Pulse = usPeriod / 2; /* 占空比50% */$ r! ?" w6 I* O
- if(HAL_TIM_OC_ConfigChannel(&htim, &sConfig, TIM_CHANNEL_1) != HAL_OK)2 O+ H* J. ~- A
- {
* \& h0 V) w( D. t! o. E2 ^ - Error_Handler(__FILE__, __LINE__);
9 m ^( v7 Y; n% _" @9 ?9 U0 r4 }) H - }' M3 Z" P, \9 A2 G3 E5 K5 M
: u0 ~; A+ q2 L- /* 启动OC1 */
/ x7 w6 A/ g7 s- m0 x' _! G - if(HAL_TIM_OC_Start(&htim, TIM_CHANNEL_1) != HAL_OK)
; M7 J: ]. C6 z% h, z% a8 ] - {
6 F/ q3 e! v9 {6 J, { - Error_Handler(__FILE__, __LINE__);) b8 D7 w# O; z" v4 f( ]4 a U
- }
& }4 j+ N q. O4 @ u3 o
; Z9 ]. A! c P, \2 _; @( ]# U- /* TIM12的TRGO用于触发DMAMUX的请求发生器 */$ F9 v6 R3 U( ^" D1 I" z Q. |2 N
- sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1REF;9 G! _2 _7 ~1 o5 a* b2 y
- sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;5 i/ L# ]7 h/ W6 o, d
- sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
7 a* K8 Z/ g6 {9 c0 a# E
5 b% t0 |& w6 k) F7 Z r- HAL_TIMEx_MasterConfigSynchronization(&htim, &sMasterConfig);
% e+ I- H2 \! W8 H; K) q+ t* m - }) W' p4 g; q* C D- v; A
- #endif
) ]+ x+ i5 |% E" R
复制代码 ; v% N9 ^& s, R2 |9 h0 ~
) x) F) ]; u0 j' k b6 ]
这个函数支持的触发频率很宽,对于DAC856X来说,如果样本点设置为100个的话,此函数推荐的触发频率是100Hz到1MHz,具体可以支持到最高触发速度计算看本章4.7.7小节即可。
5 d- b+ Z* y" \& }' S7 i: P
" A# A3 t. G' U/ U% x75.6.3 第3步:DMAMUX同步触发SPI DMA传输1 b/ m! G n; }& |
DMA和DMAMUX的配置如下:! Z, y' e. B1 S, r$ H2 O
: B4 @2 y' z* `: {2 |
- /*
% w2 O- V" N0 h& C" L9 }; [ - *********************************************************************************************************
+ c% z( O- ~' x; p. J) {6 y - * 函 数 名: bsp_spiDamStart
# C1 e4 U3 s( M3 i# p - * 功能说明: 启动SPI DMA传输
/ }1 z; R J9 l. f# `0 R - * 形 参: _ulFreq 范围推荐100Hz-1MHz% Y5 ~' P' T& J1 A
- * 返 回 值: 无+ a3 O* ^3 O( w8 P/ X, G# }+ e; V
- *********************************************************************************************************
B/ P% \: `' n$ U - */
1 K4 D" M Z: r2 V- u - void bsp_spiDamStart(uint32_t _ulFreq)
8 ]8 T/ N; L* i, q- X* u% j9 K - { @+ b0 y. Y; N5 e) [& R
- /* 设置模式,要切换到DMA CIRCULAR模式 */ u/ [" a" h4 }2 o: u2 }9 o6 |
- s_SpiDmaMode = 1;' t. y/ z4 v9 z
" s) G- }- H- c4 r- bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_4, SPI_PHASE_2EDGE, SPI_POLARITY_LOW);$ v2 j9 r1 Z2 y {0 O( K# D
- 4 F! F3 a8 }1 k( ^( u# d! A/ P
- /* 使能DMA时钟 */8 O% o- B I# G3 {2 F# r# B
- DMAx_CLK_ENABLE();
, U% l0 n/ O( L - 0 I8 Z9 L& I/ t
- /* SPI DMA发送配置 */ 3 i) e% {* H- K+ o
- hdma_tx.Instance = SPIx_TX_DMA_STREAM; /* 例化使用的DMA数据流 */ {1 a7 ~7 V; d8 |/ E) i
- hdma_tx.Init.FIFOMode = DMA_FIFOMODE_ENABLE; /* 使能FIFO */* |( z4 n6 \# Y
- hdma_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; /* 用于设置阀值 */
% \6 f+ Q, |# `! j) r1 E5 Y - hdma_tx.Init.MemBurst = DMA_MBURST_SINGLE; /* 用于存储器突发 */
: q* h" M/ Q0 f4 Q( r - hdma_tx.Init.PeriphBurst = DMA_PBURST_SINGLE; /* 用于外设突发 */4 ^" C4 ]$ G7 n% v' H6 w
- hdma_tx.Init.Request = SPIx_TX_DMA_REQUEST; /* 请求类型 */
' |% H! \( C$ N: K4 x' G - hdma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; /* 传输方向是从存储器到外设 */ 6 v/ z. o/ u& S9 c' D3 r5 v. L8 J
- hdma_tx.Init.PeriphInc = DMA_PINC_DISABLE; /* 外设地址自增禁止 */
+ i) O2 K! C6 ?. b% Z1 I. v8 x; O - hdma_tx.Init.MemInc = DMA_MINC_ENABLE; /* 存储器地址自增使能 */
2 K% J# @ u8 Y7 H% r" L( D; R6 R# D0 _ - hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; /* 外设数据传输位宽选择字节,即8bit */
3 U" n' M, G# l7 l9 U7 T6 C - hdma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; /* 存储器数据传输位宽选择字节,即8bit */
6 D) N. B& d; M% V - hdma_tx.Init.Mode = DMA_CIRCULAR; /* 正常模式 */
x, ~5 k0 G7 w8 K' ^. E# @5 U! x - hdma_tx.Init.Priority = DMA_PRIORITY_LOW; /* 优先级低 */
! ?! d8 @2 X3 N; g
) m$ ]( F* e |3 x9 O- /* 复位DMA */
9 ?. c' z# {7 y, c( K - if(HAL_DMA_DeInit(&hdma_tx) != HAL_OK)/ D9 ^+ E. |3 a6 N+ K
- {6 W+ j. {0 h1 l% |
- Error_Handler(__FILE__, __LINE__);
) |2 B$ O) o8 k( k+ r. g - }
8 v' ^2 j& h/ j; C W
( ?2 ?, c6 U6 N5 Z6 N/ b- /* 初始化DMA */+ K$ E" J. [: q) U5 a: Q) U
- if(HAL_DMA_Init(&hdma_tx) != HAL_OK)# z2 J& q6 w: ~ X5 c0 e
- {# |0 `- |" k% n+ R J$ B. F
- Error_Handler(__FILE__, __LINE__);
0 i- s$ }( |1 X# D& ^7 @ - }
! P5 t! n# A+ i% F( _# M* j - " c" O' Q% l* a% q
- /* 关联DMA句柄到SPI */
- \+ X% R3 j, m! @1 b0 C - __HAL_LINKDMA(&hspi, hdmatx, hdma_tx); * ^9 f5 M* X: r% }. h
- % [# M5 j, ^- p* h" f% f* f6 o
- 3 ^1 H- _+ y! C" D' |
- /* 关闭DMA发送中断 */3 o+ q% n( ?6 \ P' {
- HAL_NVIC_SetPriority(SPIx_DMA_TX_IRQn, 1, 0);+ U% j! \. w" r) K" G
- HAL_NVIC_DisableIRQ(SPIx_DMA_TX_IRQn);/ {, i% B6 M; f3 B
- 5 Y# z& n, K. L! ^
- /* 关闭SPI中断 */
' C+ ^8 a1 s4 H! ^+ u0 Y - HAL_NVIC_SetPriority(SPIx_IRQn, 1, 0);
. b: T: B. i8 j2 @ - HAL_NVIC_DisableIRQ(SPIx_IRQn);
/ f, t0 V9 A/ E* G" X, L8 r - ( j' q3 Q3 T7 |* \7 N4 K! M4 J
- /* 同步触发配置 */
6 E1 e% s* \- ?# R - dmamux_syncParams.EventEnable = ENABLE; d/ U5 b( }/ Y& W- R
- dmamux_syncParams.SyncPolarity = HAL_DMAMUX_SYNC_RISING; 7 o6 E4 ?& M" v3 F3 W$ m
- dmamux_syncParams.RequestNumber = 1; 4 f0 k7 q# @5 b* w4 g2 c
- dmamux_syncParams.SyncSignalID = HAL_DMAMUX1_SYNC_TIM12_TRGO; 0 J% }7 b+ X, d5 q" g! R m8 j( p
- dmamux_syncParams.SyncEnable = ENABLE; - |0 z9 z. K- w4 \
- # E6 f8 X" V) |" N: m
- HAL_DMAEx_ConfigMuxSync(&hdma_tx, &dmamux_syncParams);! y4 |& C# O' |1 L& ?5 b
- ) R; N5 E7 |( {# ~( l; ]3 I4 c
- //LPTIM_Config(_ulFreq);- M% k6 c" H7 N1 R B# T8 |
- / F& u( z& K8 B8 j+ ]8 q
- TIM12_Config(_ulFreq);; Q/ P4 M: R4 H" ?5 V2 O
5 s! p8 ~1 h, W/ ?# J. g; V# g- /* 启动DMA传输 */; k5 L' c; ~+ Y2 i& l
- if(HAL_SPI_Transmit_DMA(&hspi, (uint8_t*)g_spiTxBuf, g_spiLen/4)!= HAL_OK)
, G# \8 x- d$ O2 {: n - {
' L8 u# e/ e( a8 w - Error_Handler(__FILE__, __LINE__);) s' C" J5 o. v' B0 V x
- }/ [* C) Q, K6 f$ R3 n
- }# U$ ]# j5 e6 N3 s" d3 J2 `) s
复制代码
$ \! B# W" r m# f7 N8 c+ ?
5 k7 Y1 n8 K# f5 ~这段程序里面最关键的就是置红的部分。作用是配置DMAMUX的同步触发功能,触发周期由TIM12控制。
- T: y2 O; L) p) |" r7 N) b2 N4 k) M: u4 `, J
75.6.4 第4步:24bit数据的DMA传输解决办法
: T/ T$ C! r7 {6 h由于通用DMA1和DMA2仅支持8bit,16bit和32bit数据传输,我们这里要传输24bit数据,解决的关键就是配置DMA为传输宽度为32bit,并将传输的数据由24bit再补一个8bit的任意值组成32bit即可,实际的传输会由SPI完成。, v! |9 j- D4 p- W+ M# e
! z! [- \8 x, Y+ C- /*: q8 P& S- j4 A% M/ _
- *********************************************************************************************************
% [6 I' X3 I- w+ w1 e1 @ - * 函 数 名: DAC8501_SetDacDataDMA
4 u& S2 t* r5 q) y+ q - * 功能说明: DAC8501数据发送,DMA方式
X6 E) w9 i: @8 Y - * 形 参: _ch 1表示通道1输出* `3 r/ `3 [% H6 w% @% m* X; H
- * _pbufch1 通道1数据缓冲地址' a( k/ f. e$ ]/ t, o+ s1 d
- * _sizech1 通道1数据大小
/ O( K; x* |+ E. w8 G# M v2 y - * _ulFreq 触发频率,推荐范围100Hz- 1MHz,注意这个参数是触发频率,并不是波形周期。
) D/ I$ j3 W/ Q) e. A - * 这里触发一次,SPI DMA传输一次24bit数据。
* m: g+ i+ k5 `5 \( Z' A1 q - * 返 回 值: 无4 R8 O3 A$ E0 J8 q1 T# @% g
- *********************************************************************************************************
1 J9 p; V# A# `! o4 i$ U% V - */! f: L( ]; P+ K' g7 x" r
- void DAC8501_SetDacDataDMA(uint8_t _ch, uint16_t *_pbufch1, uint32_t _sizech1, uint32_t _ulFreq)/ i7 }( J7 {# A$ K( i3 H3 Z4 G
- {% @/ k7 D2 p+ n& _4 W# w
- uint32_t i;
' E4 ?, S: x. e0 n7 I2 q6 A* _9 u - uint32_t _cmd;, J7 B0 V! b+ V, h! y. e! ^7 ]
- - S, C$ j8 w" @7 _/ q7 }1 F
- g_spiLen = 0;
$ Q; w& h+ d9 q! s1 l, W
0 N, H$ `$ q% U: g" ~' }- switch (_ch)
. P* ~' X+ w; ^4 g1 V K - {
# t1 ?4 Z8 \( @# U& ] - 9 T6 E) y3 a! T# c
- /*; b, k! ?9 g8 p) t" L; x
- DAC8501.pdf page 12 有24bit定义+ m9 Q1 P; Y& U5 X& F
- / B% L7 m% y9 v, I! @4 X
- DB24:18 = xxxxx 保留: K- P4 v) _/ `! }' D
- DB17: PD14 a# F% K7 r$ M5 ]$ h
- DB16: PD0- l+ f( m s/ a8 B2 c' J& ]
- ! a" `" I3 T- H( l0 Q6 P x
- DB15:0 16位数据3 z( g5 P1 k& E( Y7 `; n
- v; I3 Y( L; c
- 其中 PD1 PD0 决定4种工作模式8 W& c, |" _7 X; l: ^
- 0 0 ---> 正常工作模式
6 L/ y) C& K1 e2 w- F' L* w - 0 1 ---> 输出接1K欧到GND: x5 e5 h5 ?6 Q9 V% t5 }
- 1 0 ---> 输出100K欧到GND
; ~) d: p% ?9 p - 1 1 ---> 输出高阻2 O. j/ m4 F r- q# A
- */ |$ K8 u3 n0 ~& m
& |" N4 F3 S9 C0 c- /* 通道1数据发送 */: W; ^" D1 R" V c
- case 1:
2 T+ z: m2 l& ] - for(i = 0; i < _sizech1; i++)& l( \( D5 [' `% ?8 O8 V' e/ a
- {! `3 k3 f2 ^$ r* y" w6 ^
- /* 更新需要配置PD1和PD0,当前是选择的正常工作模式 */
& Q- p, R: b( O. D - _cmd = (0 << 16) | (_pbufch1 << 0);' _0 i p* Y0 H
- 6 A' [% q; t7 M# I+ }6 Y
- g_spiTxBuf[g_spiLen++] = (uint8_t)(_cmd);! G; f/ j; y& @- e: F% D) s4 [
- g_spiTxBuf[g_spiLen++] = (uint8_t)(_cmd >> 8);
( |! v+ K0 n' H. s! }3 E W. e - g_spiTxBuf[g_spiLen++] = (uint8_t)(_cmd >> 16);
2 _$ @0 {% s4 {- o1 M7 j$ B% { - g_spiTxBuf[g_spiLen++] = 0;
0 f N0 d; U% t- W - }
7 F2 N, \, K$ P' z9 d! f% ?& O - break;& J$ T7 D) x F7 q5 z" \
- 0 t! a3 j+ J# j! B% f C; ` T
- default:" ?5 p1 S2 K# I6 W! x3 P/ g
- break;, G' Q6 Z. d9 p. Q9 I) X
( |3 c- V2 Z# a/ e2 _6 U. c- }
0 E1 H0 E0 t6 V6 I/ S - % \+ q6 C! A5 l* k& y
- bsp_spiDamStart(_ulFreq);
, u7 H; }, J% R - }
复制代码
# n/ x1 v+ [ O( Z1 X& m75.6.5 第5步:DMA缓冲区的MPU配置
" a# U! e. P# v: C# B% A$ j因为工程是用的DTCM做的主RAM空间,这个空间无法使用通用DMA1和DMA2,通过本手册第26章的内存块超方便使用方式,将DMA缓冲定义到SRAM4上:5 Q% O& }. H- n. {7 U9 t
. ~* t1 {- y: p1 ^" ^; j
- #if defined ( __CC_ARM ) /* MDK *******/
" l. _6 t% S6 ^; g - __attribute__((section (".RAM_D3"))) uint8_t g_spiTxBuf[SPI_BUFFER_SIZE]; 0 P {3 Q" L7 C D% |5 Z* j( n5 w4 S
- __attribute__((section (".RAM_D3"))) uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];
( t `2 c: c# o. M5 T2 b5 V! b* z7 D - #elif defined (__ICCARM__) /* IAR ********/
: } @$ G9 O. n1 {& V - #pragma location = ".RAM_D3"
0 W! h3 [7 O3 o0 K% F6 W) O3 O - uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];
. M( U6 W- d% z8 ?. d& a& { - #pragma location = ".RAM_D3"
) o% u0 R+ f5 }1 j/ H - uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];
$ p- ~* g' C1 ?( U8 t+ z; X7 Q, p' p - #endif
复制代码
9 ~+ J% ?' l3 R1 h. [, f由于程序里面开启了数据Cache,会造成DMA和CPU访问SRAM4数据不一致的问题,特此将SRAM4空间关闭Cache。. h9 w$ J0 o% A# F p( j! |7 ~( a
/* 配置SRAM4的MPU属性为Non-cacheable */
# M# Q( K. V* f/ z6 h - MPU_InitStruct.Enable = MPU_REGION_ENABLE;
6 q# R# Y) Q; n/ O - MPU_InitStruct.BaseAddress = 0x38000000;5 c$ ~. |+ V: w, L
- MPU_InitStruct.Size = MPU_REGION_SIZE_64KB;
! A o1 t% U" z5 R3 o* [- F - MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;4 o+ r1 j C0 T# S/ b: z
- MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;" v2 H$ n2 [" V
- MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
0 Q( ~5 L: ]5 S! u; v8 O - MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
' j* ^) j n4 U - MPU_InitStruct.Number = MPU_REGION_NUMBER2;
: q( w' v* n: Q/ B - MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
: X$ Z: \& d; W6 C1 ?9 j$ i - MPU_InitStruct.SubRegionDisable = 0x00;
& _ R* B1 m9 S; z4 Z! E6 f( n - MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
7 T: Q, G6 z6 L5 u6 b9 C R
- c8 V1 X }3 m8 W/ y- HAL_MPU_ConfigRegion(&MPU_InitStruct)
复制代码 由于程序里面开启了数据Cache,会造成DMA和CPU访问SRAM4数据不一致的问题,特此将SRAM4空间关闭Cache。% @$ j8 r' c: s' S' Q# I. A7 u' [
* F( D7 w: e3 C& @; Q75.6.6 第6步:DAC8501的时钟极性和时钟相位配置: p4 _3 A" V. V
注:与本章74.5.3小节内容是一样的。/ `) G, R4 R r: P1 G4 P' v$ E
1 c. G" N l3 C! F2 w4 W9 a$ R首先回忆下STM32H7支持的4种时序配置。. h( m* Z& D: l
4 R" ]' Z1 V9 Y- v- g$ ]+ u+ ~, j/ d
当CPOL = 1, CPHA = 1时6 p3 f8 h$ M( ?& j, l1 K# a% p
SCK引脚在空闲状态处于低电平,SCK引脚的第2个边沿捕获传输的第1个数据。- X6 t. X/ s' `. g D- g
5 C0 b" G. ^2 {8 d: ^ 当CPOL = 0, CPHA = 1时9 M [( t/ o, a
SCK引脚在空闲状态处于高电平,SCK引脚的第2个边沿捕获传输的第1个数据。
) c3 [" `5 s, I% ]
9 |0 g% { V c& I N/ J% S 当CPOL = 1, CPHA = 0时* p9 ~( O; L% \; }# N1 n9 v) J
SCK引脚在空闲状态处于低电平,SCK引脚的第1个边沿捕获传输的第1个数据。
, i3 H. x9 h8 @: {$ y Q+ `2 ^% A4 }# f% C. L0 ?% f4 I. e$ ^
当CPOL = 0 ,CPHA= 0时, I# f5 ^$ M' ]
SCK引脚在空闲状态处于高电平,SCK引脚的第1个边沿捕获传输的第1个数据。
' e1 Y$ D3 w1 H; v( T6 ]2 r
# t6 z \2 N Y
' m3 K% H' \2 T& X* n, N7 y% y
4 v4 \7 Z/ O$ `/ {4 K有了H7支持的时序配置,再来看下DAC8501的时序图:0 y% u& X0 w9 ]+ w3 \& E
6 }; B% _. }4 q( H9 Y- |6 A
- r5 _ h( e* k9 _6 Z8 g
* Y3 L3 i2 i" b2 P$ i/ J首先DAC8501是下降升沿做数据采集,所以STM32H7的可选的配置就是:
' R0 R7 u* k: z, K; v6 g! M$ r, {( n" q4 ]* K; z' L. W! u
CHOL = 0, CPHA = 16 v( ~, \) N/ k! o) }
1 k3 T) ~, H* nCHOL = 1, CPHA = 0
& c3 m2 _ a2 H! e, f! m
* E% u5 D7 V. L3 t. @对于这两种情况的主要区别是空闲状态下SCLK时钟选择高电平还是低电平,根据上面的时序图和DAC8501的数据手册,两种情况下都可以正常运行。经过实际测试,STM32H7使用这两个配置确实都可以正常运行。程序里面默认是选择CHOL = 0, CPHA = 1。- w" ~" u+ S8 {) l- i( ?; C3 f
) v' p; L+ H7 |4 H3 [6 c( d75.6.7 第7步:DAC8501的最高更新速度计算
2 j: o r% P+ O& h. G这里特别注意一点,定时器触发一次,就会让SPI以DMA方式传输24bit数据。8 N% q1 E# _; r: B( x( e
2 R9 N0 J5 T7 ]' T8 x- ?
配置条件:2 }+ D, m: ^; S( k
9 Q& a7 _# O4 V" g9 ]
SPI时钟是25MHz,SPI数据传为24bit,每个bit需要时间40ns。
! L( {: J3 t! r6 c9 A6 F hspi.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE0 M5 U! @# E' N) v
插入到NSS有效边沿和第一个数据开始之间的额外延迟,单位SPI时钟周期个数,即40ns。
- x( }# R' Y0 s! Q: T1 Z5 Q+ b
( E1 U/ p' I/ F! D1 |) Z0 P" d hspi.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_10CYCLE
1 z% H& G& f$ L两个连续数据帧之间插入的最小时间延迟,单位SPI时钟周期个数,即40ns。: @* T% p; g+ T* Z% j
# x6 f/ m1 e9 P0 a, G% u U3 z* E0 D
根据上面的配置,传输一帧(24bit)数据需要的时间:
+ M) u# }# _- D! T; ~: f7 n4 z6 w8 s. ?7 Z2 W1 t- v# z
24bit * 20ns+ SPI_MASTER_SS_IDLENESS_00CYCLE * 20ns
6 `& B8 i, O Z9 [2 E
; I9 Z2 O3 j2 z6 L. o% l, y! m+ SPI_MASTER_INTERDATA_IDLENESS_02CYCLE * 20ns
: x" g+ s9 g' @; ]. c7 f! ~ c9 u
= 24bit * 40ns + 0 * 40ns + 2 * 40ns
( f) K" ]- j% K8 k( R
( V+ P' x) w* B7 ]= 1040ns。 y. S+ I: O1 Y4 a* K, Q
& a! Q0 K1 S; r( [) m) X$ o: J1 M1 e那么这种配置下,可以支持最高触发速度是1 / 1040ns = 0.961MHz,如果想速度再提升些,可以降低参数hspi.Init.MasterInterDataIdleness,推荐的最小值是1个时钟周期,那么可以支持的最高触发速度是1/1000ns = 1MHz。
) t( Z D9 N& Y _3 M- Y: ?: _! r* m+ f' n, N& \
认识到这些后,实际输出的波形周期也比较好算了,比如我们设置10个样本点为一个周期,那么触发速度为1MHz的时候,那么波形周期就是100KHz。
+ F. _" |7 f+ p+ K: R+ M3 S0 T# w3 C/ h8 H8 n
75.6.8 第8步:DAC值和电压值互转
5 S) l, `/ k$ q- N8 z9 s8 A; t2 uDAC8501模块的输出电压范围是0V到5V,对应的编码值范围是0到65535,为了方便大家做互转,专门做了两个函数:
2 [( A* ]7 ^8 ]' t' V+ v+ N$ I. x! z; T5 k
- /*
' C8 e4 P9 N: N7 X' b - *********************************************************************************************************- t! x+ q; p9 I! Z" C! }3 K
- * 函 数 名: DAC8501_DacToVoltage& K. K. k, o6 d) B/ J6 t
- * 功能说明: 将DAC值换算为电压值,单位0.1mV
$ F# W6 p8 s) K3 O* V' X, M( O - * 形 参: _dac 16位DAC字! P `" t* _6 `! i. G9 ^3 h
- * 返 回 值: 电压,单位0.1mV9 u' z3 U# f3 z- p9 M
- *********************************************************************************************************
+ z- g6 S$ L5 ?) Y& D - */
/ A( H7 d- _! g" @% }& z% [5 R - int32_t DAC8501_DacToVoltage(uint16_t _dac) i) c+ z$ l/ V+ l4 T U/ `, z
- {4 B1 z; l" c6 V! N9 p: X! }
- int32_t y;/ u7 T. s& _+ E" l5 ]. G" Z
L0 S7 `8 D! N4 d' ]) T) p- /* CaculTwoPoint(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x);*/
" [& Z: ~8 B- G7 @ A - y = CaculTwoPoint(X1, Y1, X2, Y2, _dac);" H7 U# B# F# Y3 D, a$ Z
- if (y < 0)
9 n y; h$ Y& N V* k' L - {1 a# W0 M8 B4 p f
- y = 0;& Y1 b) f. y9 }; |8 p) I: e
- }) Y" ^8 }6 K$ y$ b1 c
- return y;$ V, X/ w. n! H
- }
6 R- B, ]0 [0 [, Q2 O5 F, w: ` - $ i. w) j- O, G- A7 M
- /*5 Z6 n0 N: ]! U, ?: n
- *********************************************************************************************************
}- v4 k* ~9 [6 l - * 函 数 名: DAC8501_DacToVoltage
1 J- \& f, q) h" m2 ^: ~ - * 功能说明: 将DAC值换算为电压值,单位 0.1mV
. ?" a" I( R* j# r+ Z/ K& ?/ }+ }) ?2 j - * 形 参: _volt 电压,单位0.1mV n0 J$ Y2 I* H% b
- * 返 回 值: 16位DAC字
4 n, D; B6 H' [* Y7 T! N - *********************************************************************************************************
1 {" A, E B6 G, |2 g J - */: ?& ?6 `+ X9 \4 m$ m, y
- uint32_t DAC8501_VoltageToDac(int32_t _volt)
7 E( V, G+ f" j. P% q, j - {
; Z. I/ c d* I" R4 l' d - /* CaculTwoPoint(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x);*/
C% H6 h4 V& k$ A) S - return CaculTwoPoint(Y1, X1, Y2, X2, _volt);% D. G# g* G2 a( h% x
- }0 }* v/ @! M) x( D( h' i
复制代码
1 T; Z5 f. k: \! _# H: j$ N, \# E, Y; _% T# j
75.7 SPI总线板级支持包(bsp_spi_bus.c): K7 c, r R/ _7 V* x" x Y8 Q
SPI总线驱动文件bsp_spi_bus.c主要实现了如下几个API供用户调用:
1 `# E: F, {) g5 c% P2 E
/ \/ a i, X! t, \8 |, R bsp_InitSPIBus
( P$ Z) o2 D7 x: |/ e bsp_InitSPIParam
& x7 k6 w. X2 W bsp_spiTransfer
* Z1 L, [" X" ]% d75.7.1 函数bsp_InitSPIBus6 A& S! u5 M0 N) p& i( i
函数原型:
* B. u. P8 j; W1 O- }& C
% y9 T' T0 E2 Vvoid bsp_InitSPIBus(void)
8 c+ ~& K/ l7 _8 T
2 D( \( ?1 k+ S& U函数描述:
- l# D. p; |/ K0 k2 C: H. P
# h# B6 G9 N1 i2 t此函数主要用于SPI总线的初始化,在bsp.c文件调用一次即可。
7 W/ q3 W# x8 x/ \! G4 Z3 s+ T1 q% j7 L0 j" Y. ]
75.7.2 函数bsp_InitSPIParam' B; }. ]( A2 N/ G* B( r4 }0 ^9 X
函数原型:
' P5 o0 E7 N4 r5 ~1 S! B6 D
2 G! W( V2 _, R: yvoid bsp_InitSPIParam(uint32_t _BaudRatePrescaler, uint32_t _CLKPhase, uint32_t _CLKPolarity)
# |5 |3 j- G5 i: h' K# F
9 Y! T3 d3 J. v3 v5 U函数描述:
, q I ?! Z- u* v% r1 w# D. w; @( Q/ z/ d7 {. I8 D
此函数用于SPI总线的配置。6 O2 q. w9 ^7 Y4 d; t0 r; {7 G7 j1 d
3 f3 b) k, N( t
函数参数:
3 K B( w0 k, b B* n
4 ]! T7 [1 ~. K6 L p; C9 v# X 第1个参数SPI总线的分频设置,支持的参数如下:: p2 E/ n& g1 A& L" R, B( X8 o
SPI_BAUDRATEPRESCALER_2 2分频$ Q. R$ x! `6 `' |, `: p
6 P2 B7 x) S5 y- V% x5 b( H2 A
SPI_BAUDRATEPRESCALER_4 4分频
1 w1 O* f' e/ P4 Z; J9 x7 N0 U, @, S) C/ s) E# M) j2 k
SPI_BAUDRATEPRESCALER_8 8分频- O9 O6 b/ i: O8 H
8 T' z" c7 G5 ?- l$ rSPI_BAUDRATEPRESCALER_16 16分频& T' B4 o; L/ S* ]
; w+ A( c' [6 ?4 _2 V
SPI_BAUDRATEPRESCALER_32 32分频- T5 @6 a; a X) @3 R2 ?
) u2 G7 n! ]! T7 f# O
SPI_BAUDRATEPRESCALER_64 64分频
9 m' H- e9 W# N; z- k) @1 M8 r) K& [% I% d/ E
SPI_BAUDRATEPRESCALER_128 128分频
. X" l6 G5 Z# r, _$ m, j
, c/ _0 D3 D3 d* j8 I8 M& mSPI_BAUDRATEPRESCALER_256 256分频- ^. w+ X! l4 y) A6 V. [" L
) o( G5 p) ]7 R: p
第2个参数用于时钟相位配置,支持的参数如下:
7 g$ i, W" b+ ]SPI_PHASE_1EDGE SCK引脚的第1个边沿捕获传输的第1个数据
4 U( T* F% U u- V3 L! v; A! M
. o5 t( U' g+ [$ a6 ]SPI_PHASE_2EDGE SCK引脚的第2个边沿捕获传输的第1个数据3 f4 v9 T3 J( m1 V, }+ E" b: Q3 f
5 }" _9 K ?9 s% E" d! @
第3个参数是时钟极性配置,支持的参数如下:
- s% u% S1 V* x' p0 }8 pSPI_POLARITY_LOW SCK引脚在空闲状态处于低电平( n$ U0 Q3 z5 L
( c8 ?) R# r; d+ e. s
SPI_POLARITY_HIGH SCK引脚在空闲状态处于高电平! ]. b$ z0 `1 Y( P
: ~1 `" N+ @+ L' ^" N
75.7.3 函数bsp_spiTransfer2 r7 L; J( }: |& e7 g
函数原型:% q; E$ a4 f% a5 \2 q9 B! a- X
" R6 X, D2 Y, y6 Zvoid bsp_spiTransfer(void)3 Z. p9 Y# Y/ W1 l' _& i+ x( N
T" n+ j. H, ~4 @, v- a
函数描述:: ]( Y8 T3 U4 X( `, u
% q8 Z: g |, ^! _) v0 T此函数用于启动SPI数据传输,支持查询,中断和DMA方式传输。
8 m K; s+ o& H/ s
+ K4 S! J( F G4 i p5 T, |; A6 B75.8 DAC8501支持包中断方式(bsp_spi_dac8501.c) j9 K$ L; a8 J+ v2 v4 i: {
DAC8501驱动文件bsp_spi_dac8501.c主要实现了如下几个API供用户调用:
- ]8 J1 O* ]9 O% a0 ^; v: j8 E- w" J0 o
bsp_InitDAC8501
4 ^) R: ]# a6 U$ \. A DAC8501_SetCS1
6 Y1 T9 t! {2 r; b2 \$ f7 U DAC8501_SetCS2
, V" D' F! O5 J. Z DAC8501_SetDacData$ ]7 m+ ?* s. t9 P
DAC8501_DacToVoltage4 Q* h% f/ D9 C u. _
DAC8501_VoltageToDac
* ?! [6 U* |& g/ y$ T; q75.8.1 函数bsp_InitDAC8501
) K& h7 \* O, F9 L6 ^4 K; {函数原型:
1 M' r% m3 H2 E/ e( D: R; p% Q4 S1 {
void bsp_InitDAC8501(void)5 E; P* g r* O: O2 j
3 n; o* F% \1 M' }6 q( B) L3 O' }
函数描述:+ u6 V+ r. l& |
+ i6 b+ j) u& _
主要用于DAC8501的初始化,调用前务必先调用函数bsp_InitSPIBus初始化SPI外设。4 V/ z! a' n0 t8 v
4 ]- x/ v4 K2 T3 m; l1 z
75.8.2 函数DAC8501_SetCS1# j/ `: b. N2 {5 b4 ^
函数原型:
7 y2 P7 E1 j+ I5 [6 P' g
0 O8 q3 z0 V% @1 e% Svoid DAC8501_SetCS1(uint8_t _Level)
3 k0 l3 U0 Q! n" b
1 r4 g, X6 j/ d2 o函数描述:
* X( O! [$ R5 |3 M6 l. O/ D; V6 Z! h( v& y/ v
此函数用于片选DAC8501模块上的第1片8501。
% f& A8 o7 B2 A t: A
6 H, w+ d+ S3 }函数参数:
* f/ ?3 r. l! [: o n% _' i
- ?1 s4 i+ g) ~, u 第1个参数为0表示选中,为1表示取消选中。
5 F. Y% F6 g; L$ r: `, b3 q# E75.8.3 函数DAC8501_SetCS2
" j. R* ~% }0 p4 P函数原型:
2 ^4 R# B! I, @, y9 ~$ x- e# c0 ]3 z- n0 m9 |8 F' K. @/ ~
void DAC8501_SetCS2(uint8_t _Level)
' v5 `: F- s' V5 f: z1 R8 e& f7 W) J- G ^, M: l! U& O
函数描述:* e" Y% o3 _4 ]6 f
3 W3 g' U7 P' ~/ Y* {
此函数用于片选DAC8501模块上的第2片8501。
" X8 o% i9 \* }: D: y2 o! Q- h) i& n$ H# Y5 {: W: `) }
函数参数:
( ?7 a& s" w; ~$ v* k$ \2 I; ~
/ \/ Y0 S. g, }+ J2 S6 N 第1个参数为0表示选中,为1表示取消选中9 E* Q# M# m# @: M. k$ Q
75.8.4 函数DAC8501_SetDacData/ o3 W7 V5 a- z0 F0 d- ]
函数原型:4 ` S: Q4 M: m$ e% e
7 h5 v- j3 i9 n, G& }0 y% Xvoid DAC8501_SetDacData(uint8_t _ch, uint16_t _dac): p. o6 Q- i+ W( C& w
8 i. [. f$ H6 V4 J; g z函数描述:
7 e6 T; X8 I) {: k9 l! W
8 ?7 g+ P; k1 r# `; ]) \- l此函数用于设置DAC输出,并立即更新。
7 C2 D) a# u7 n6 F4 s; `
: n' D4 T3 ] w0 L1 u3 I1 |# I' W& M函数参数:9 L9 e. u9 C5 `& z! j
9 w( H7 ?6 I1 f5 d* x; N& x3 X) V; `# G8 F 第1个参数为0表示通道1,为1表示通道2。( T# g0 H3 R7 i( U' r% A1 z6 a
第2个参数是DAC数值设置,范围0到65535,0对应最小电压值,65535对应最大电压值。0 h0 \ m. L4 {5 M: a+ H5 }
75.8.5 函数DAC8501_DacToVoltage
A* W) |0 `$ A k7 b2 R函数原型:
k' t0 U* p% g4 Y2 g- r
+ k9 C+ C+ q+ Q' j+ E) pint32_t DAC8501_DacToVoltage(uint16_t _dac)
5 ?& i' u. d; X" [# u& u0 p7 o+ z
函数描述:" L" T' d7 Q9 ]9 E
- m$ G) v1 a$ z: W, W此函数用于将DAC值换算为电压值,单位0.1mV。
" W) G, `4 ~8 X6 ?0 ~( x+ s7 R2 N. _9 p7 M
函数参数:3 j/ f) s; y( Z+ X" D) W
1 |8 a5 L- |% N! ^2 h) ` C7 |
第1个参数DAC数值,范围0到65535。9 x6 t: D1 x) g* z% p2 d
返回值,返回电压值,单位0.1mV。
1 C8 b/ s% s& @9 K75.8.6 函数DAC8501_VoltageToDac1 @( C9 o# _5 _; d
函数原型:
: {2 T4 n! M- b# v2 O* @0 `+ T2 ?& V* s5 v0 `
uint32_t DAC8501_VoltageToDac(int32_t _volt)
0 ^6 X; ^- @* o5 v
3 ^* q5 t# S. H% {! g函数描述:
. C' [, s, d' d$ Y5 a
" X( ]. Q" r: R3 \9 k此函数用于将电压值转换为DAC值。
6 J4 t3 Q1 ?& Y5 |! m* d
3 Z/ o6 _" \& T8 `' O函数参数:+ |( N7 C4 W0 l) P0 G7 ~9 C
4 C. q( {0 ?6 Q7 y q6 c" A3 v* k, T 第1个参数是电压值,范围0到50000,单位0.1mV。+ Z0 g" o- c: Z/ K1 }9 t
返回值,返回DAC值。. L7 f, F# ?3 z% {: x) p/ {5 w6 R! w
75.9 DAC8501支持包DMA方式(bsp_spidma_dac8501.c)
7 S$ E$ V b% d0 _& i* \% ?% {0 \1 SDAC8501驱动文件bsp_spidma_dac8501.c涉及到的函数比较多,我们主要介绍用到的如下几个函数:2 s0 A# {4 m0 Z: T# ?" ?2 }6 @
9 j, q: Q- y8 y i5 x, J/ Z
bsp_InitDAC85011 m8 ]2 `+ D- K+ H4 S
DAC8501_SetDacDataDMA
8 {" F H" h6 K DAC8501_SetDacData: I7 @: k+ p" h( L$ b( @( a) o
75.9.1 函数bsp_InitDAC8501 w, B% l" B% F- ~
函数原型:9 \: b# C/ d3 R% G& u
2 |6 a! _1 b! ~5 B4 w
void bsp_InitDAC8501(void)
8 T. z; W. }# z
2 F/ j; R8 k. A$ |$ i; l ?& L1 p函数描述:
$ v ~$ W( }! v n3 T" y, g. K J: U2 `! X% U3 i" U6 u
主要用于DAC8501的初始化。
8 P8 l7 ?) @2 @! U6 g* |% S2 Q- z; ~6 t
75.9.2 函数DAC8501_SetDacDataDMA
' w @: F, l" A$ L, g函数原型:0 j8 z& q q% F$ h4 O* ^- \
! M' U! g$ G6 w: _void DAC8501_SetDacDataDMA(uint8_t _ch, uint16_t *_pbufch1, uint32_t _sizech1, uint32_t _ulFreq)7 h# v$ }' _5 j& A1 `8 f/ w
- M+ P3 W3 Z2 ^& v% I* K. E函数描述:
7 G( b. N! a" f, I2 J7 s h' T2 B. n" G O8 _, } W
此函数用于SPI DMA方式数据发送。
, d! }' d& L) V& }9 o9 F
( x/ z4 p8 J6 L, C+ Q( y6 c6 y' Y函数参数:! u5 Z1 T6 @* [* k+ U
9 e5 |0 e5 x. n% n 第1个参数用于选择的通道: 1表示通道1输出
1 x4 A$ J% e" L5 Q& i1 t- [% t 第2个参数表示通道1数据缓冲地址。
) Z& K$ [/ X+ v& k+ r% N 第3个参数表示通道1数据大小。
. ^1 B0 k, d" l- J0 Q8 E 第4个参数表示触发频率,推荐范围100Hz- 1MHz,注意这个参数是触发频率,并不是波形周期。这里触发一次,SPI DMA传输一次24bit数据。8 c' G9 T) m/ L0 ^4 X
75.9.3 函数DAC8501_SetDacData
8 D& ] D- j9 [( t函数原型:, \5 ` R( S' n" u
0 p m) Q% v6 ]: {1 A
void DAC8501_SetDacData(uint8_t _ch, uint16_t _dac)
$ U7 N7 d( \; |
1 Y/ [7 w# F$ V6 Y+ l% D函数描述:% J. Q7 \% r% K# A7 ]- ]
6 y+ a) m3 `" t4 w* Y- Z* O2 W此函数用于设置DAC输出,并立即更新。
. k1 J7 i( u( W% b( L+ U
9 k/ D0 o9 P5 i函数参数:
6 \" |; p& ?) n) _5 W& k1 Z9 X6 y: n0 c# x K7 ^) e+ j/ ~
第1个参数为0表示通道1,为1表示通道2(对于SPI DMA方式,仅支持通道1)。
8 j# K2 J! v/ T# f 第2个参数是DAC数值设置,范围0到65535,0对应最小电压值,65535对应最大电压值。0 `- d) z; ]* R
75.10 DAC8501驱动移植和使用(中断更新方式)6 q( S" r' m5 }4 T4 l' z
DAC8501移植步骤如下:
' V3 ]+ U* x& k& F# |
% @4 Y5 X' Y2 C& H" c2 d 第1步:复制bsp_spi_bus.c,bsp_spi_bus.h,bsp_spi_dac8501.c,bsp_spi_dac8501.h到自己的工程目录,并添加到工程里面。
' g2 O& h5 I x6 a9 ^/ D 第2步:根据使用的第几个SPI,SPI时钟,SPI引脚和DMA通道等,修改bsp_spi_bus.c文件开头的宏定义
. _6 I* A k. r# ]* h7 o, E' `- s- /*5 i. ?+ L1 ~/ B
- *********************************************************************************************************8 Q9 _0 P+ {, b" Q; V$ N( O
- * 时钟,引脚,DMA,中断等宏定义
% F5 f1 D0 }% W9 d - *********************************************************************************************************: u. b5 l2 A. d! j- e* Q
- */, m, o: d2 P: X" K: Q
- #define SPIx SPI1
2 V4 y, ]* m7 W( T* Q - #define SPIx_CLK_ENABLE() __HAL_RCC_SPI1_CLK_ENABLE()# a% H, P% y" T: {
- #define DMAx_CLK_ENABLE() __HAL_RCC_DMA2_CLK_ENABLE()' O2 A1 t5 m( T1 T8 { ~
7 }$ q% D: k' J1 }- #define SPIx_FORCE_RESET() __HAL_RCC_SPI1_FORCE_RESET()8 N" M* c3 |2 M
- #define SPIx_RELEASE_RESET() __HAL_RCC_SPI1_RELEASE_RESET()
$ Z5 j" V0 g) Q. w - 5 c$ P5 { k r7 A+ r4 ~3 M- J
- #define SPIx_SCK_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()5 Z- J. R1 L3 T Q: F5 j Y
- #define SPIx_SCK_GPIO GPIOB
4 C- J/ ?8 [. C2 a! U! ?. C c* ` - #define SPIx_SCK_PIN GPIO_PIN_3
$ s6 } z" Q \* e) m* c, Y1 O$ ^- L - #define SPIx_SCK_AF GPIO_AF5_SPI1
1 i) A* h5 |0 C( Y3 V5 d$ y, i - 7 Y, e* b: R& b& R
- #define SPIx_MISO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()+ P% f- g; y; o' `9 M8 y1 P
- #define SPIx_MISO_GPIO GPIOB
5 ^3 [5 H: d! A! {: P6 y - #define SPIx_MISO_PIN GPIO_PIN_4# | Y" o3 B: {
- #define SPIx_MISO_AF GPIO_AF5_SPI1
& z/ n X7 D. l) R7 t J, d
+ d5 g" @" j, j$ C4 S7 B- #define SPIx_MOSI_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()6 {7 m0 i6 K: F. X& e" W0 y6 m1 Z$ M
- #define SPIx_MOSI_GPIO GPIOB! g# x4 r0 o$ {
- #define SPIx_MOSI_PIN GPIO_PIN_57 |/ a2 E* c0 {7 s4 i
- #define SPIx_MOSI_AF GPIO_AF5_SPI1
4 J7 Z8 A/ s* u
7 W; H: Z7 N }& X I: J. d- #define SPIx_TX_DMA_STREAM DMA2_Stream3' A& U! W& T) o2 C
- #define SPIx_RX_DMA_STREAM DMA2_Stream2$ Q5 T0 n1 m% _7 x! N/ P
C9 s) o# f0 b, R* o4 d- #define SPIx_TX_DMA_REQUEST DMA_REQUEST_SPI1_TX* l: c- k s8 z/ }
- #define SPIx_RX_DMA_REQUEST DMA_REQUEST_SPI1_RX: f* j5 E. w1 A" {2 z g0 Y
# `4 S. Z* S+ j' T/ F6 U6 i# ?- #define SPIx_DMA_TX_IRQn DMA2_Stream3_IRQn5 C/ N M% e8 }: c+ m. I# y
- #define SPIx_DMA_RX_IRQn DMA2_Stream2_IRQn
/ n2 q8 _! e) h: l3 B' I! f - 3 m0 m7 S' a! a! _; V' @. r: {
- #define SPIx_DMA_TX_IRQHandler DMA2_Stream3_IRQHandler5 z: I2 b4 J3 L: i! |
- #define SPIx_DMA_RX_IRQHandler DMA2_Stream2_IRQHandler
, D1 z" L$ l" v4 J0 O& F
1 d9 D3 D8 p5 R% \- #define SPIx_IRQn SPI1_IRQn
( Y; u+ w+ R' H) T { - #define SPIx_IRQHandler SPI1_IRQHandler
复制代码
8 f3 J% l- Q( O! W- ?# u 第3步:根据芯片支持的时钟速度,时钟相位和时钟极性配置函数DAC8501_SetCS1和DAC8501_SetCS2。
, d2 V' [& n$ x. q1 U. N! |DAC8501_SetCS1和DAC8501_SetCS2。! A* W/ y; x+ b/ z) u. D* C
- /*
7 k9 P/ ~3 ^/ O6 y' c* S# N8 B - *********************************************************************************************************
0 ?" k* t# ]3 l! j. b" R - * 函 数 名: DAC8501_SetCS1
- T" l+ i2 x( D" M d - * 功能说明: DAC8501 片选控制函数& i5 t/ b1 }3 d& j, J
- * 形 参: 无 X1 H6 L1 {" ?# U
- * 返 回 值: 无
- P) T( ]! R6 L2 l: L8 J! `- \ - *********************************************************************************************************1 [1 }0 I& L/ V/ E0 N& N
- */
$ Y: t0 d J7 @- n0 G* J% ? - void DAC8501_SetCS1(uint8_t _Level)2 e1 M1 {. S! \( I- p' A
- {8 Z, T O) U! l0 T8 w2 }
- if (_Level == 0)
; t8 ?! Z5 ]" _5 R* ^$ N$ ~. q - {
: I7 w6 R( ?4 k0 Y# t: ~ - bsp_SpiBusEnter(); /* 占用SPI总线 */ ! h/ R) k0 B1 Q+ u
- bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW); $ @! u4 F( O& A; ]) e5 m
- CS1_0();
5 d7 l1 L7 N4 n& T2 ^ - }- a/ G6 L0 J9 U- H+ ~7 {
- else5 |: i! {. I1 q: A/ F
- {
. J# U( @6 u. ?8 p$ J( H; w' _9 ?" q - CS1_1();
/ i+ O' {: X' s' \. q/ W4 ^7 g* Z - bsp_SpiBusExit(); /* 释放SPI总线 */
: J2 w+ \ d1 Q# o3 |) D) t8 ^; R - }
8 O" h. @& h; x - }
! j) d* I* `6 s2 X) F9 _
6 M1 ]! R. c2 `9 Q' u- /*
$ P+ n! ?' h6 Z9 l6 V1 G - *********************************************************************************************************
( O) E( ~+ X# U4 C) h - * 函 数 名: DAC8501_SetCS2(0)
8 |6 {! a: _4 e2 y8 |. { - * 功能说明: 设置CS2。 用于运行中SPI共享。
. C: t, o; d9 W. n' { - * 形 参: 无
9 @/ I. Z7 I1 u - 返 回 值: 无
8 M, y0 ]! X4 z: y- s - *********************************************************************************************************4 L: l# u( h. F
- */
* e5 b/ ^% u( d# ?& Y {3 I - void DAC8501_SetCS2(uint8_t _level)
! \2 y2 {7 J3 {2 N& E9 u0 w - {# k5 w3 {( x2 u' M4 @
- if (_level == 0)
( R* r" A) Z0 H - {
2 m' G' _! E1 l1 X9 O$ B+ D) \ - bsp_SpiBusEnter(); /* 占用SPI总线 */
: h- r7 b2 w f/ [ - bsp_InitSPIParam(SPI_BAUDRATEPRESCALER_8, SPI_PHASE_2EDGE, SPI_POLARITY_LOW); ' z4 @% K4 w& h3 _
- CS2_0();$ U: G' g2 |5 F' f5 r* l1 E
- }, `8 y# V$ w0 v, s/ _2 ^' I6 T
- else* \$ U$ E7 S1 ` p2 }( n/ A
- {
9 Z# X6 v' O$ B2 ^ - CS2_1();: ]( V. U1 @, K0 s# H6 h1 u
- bsp_SpiBusExit(); /* 释放SPI总线 */
' G6 N9 \$ {' {9 w; E# Q - }/ ]# `8 |' c2 c8 V! v/ \
- }$ a, y4 R x) r, u
复制代码
* Y! E1 N# k: C- q
- i# A: j4 S. G: e 第4步:根据使用的片选引脚,修改bsp_spi_dac8562.c文件开头的宏定义。7 F& b5 @6 q7 o* Z7 C; `
- #define CS1_CLK_ENABLE() __HAL_RCC_GPIOG_CLK_ENABLE()% }- G$ x' X6 Y1 U$ U8 G/ j* g
- #define CS1_GPIO GPIOG
- k* w9 P7 s6 [ - #define CS1_PIN GPIO_PIN_10
+ n; G$ C b6 d* ~3 g
, h" {; B( J5 c9 z1 \; |- #define CS1_1() CS1_GPIO->BSRR = CS1_PIN
1 Y3 [$ {2 L0 e# { - #define CS1_0() CS1_GPIO->BSRR = ((uint32_t)CS1_PIN << 16U)1 I, ^6 g% o0 M2 c* I) T
|0 b5 D* [5 |# Z- a- /*特别注意,我们这里是用的扩展IO控制的 */
4 z" {) h' g* ^3 p, O5 r0 r; K" V - #define CS2_1() HC574_SetPin(NRF24L01_CE, 1);( P7 x, H; `: {% J5 l( s: |
- #define CS2_0() HC574_SetPin(NRF24L01_CE, 0);
复制代码 % _7 H7 i* D' [& S6 u; m4 W$ F: P
第5步:如果使用DMA方式的话,请不要使用TCM RAM,因为通用DMA1和DMA2不支持。并为了防止DMA和CPU同时访问DMA缓冲造成的数据一致性问题,将这块空间关闭Cache处理,比如使用的SRAM4:
/ b( o1 S% s j) Q+ C: P1 Q- /* 配置SRAM4的MPU属性为Non-cacheable */
3 n: {* c1 R: L6 o9 | ~ - MPU_InitStruct.Enable = MPU_REGION_ENABLE;# f5 M& I; m* ?( z
- MPU_InitStruct.BaseAddress = 0x38000000;: C( J. Z8 P* [& W) f0 z
- MPU_InitStruct.Size = MPU_REGION_SIZE_64KB;0 u. f3 q$ O# a3 e
- MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
) w9 T4 H1 t! ?' g - MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
/ X# B h/ R0 V1 @ H5 T7 y6 W - MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; _! s" U0 O# j1 y
- MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
$ b& h: q; T) u6 Z4 t1 u- o+ ?5 u - MPU_InitStruct.Number = MPU_REGION_NUMBER2;3 n, L' q9 I, @: b2 L" W: K
- MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
& ?! O& _7 l% B# a - MPU_InitStruct.SubRegionDisable = 0x00;- i5 T1 E; b4 ^8 @
- MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;# b2 ?% g$ h; T
2 P% w' U7 z9 g ]: x& x) [8 ^- HAL_MPU_ConfigRegion(&MPU_InitStruct);
复制代码
4 F+ A: k9 N0 d' z! c9 n k+ c 第6步:初始化SPI。
4 d! K9 P( ?6 p2 J- /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */
' W9 V: }& p! E0 F ` - bsp_InitSPIBus(); /* 配置SPI总线 */ ; s& y9 r1 Z/ Z( Q( Z' g$ _
- bsp_InitDAC8501(); /* 初始化配置DAC8501 */
复制代码
4 @- Y( J) n3 Y* F1 P" C+ |7 E" M 第7步:DAC8501驱动主要用到HAL库的SPI驱动文件,简单省事些可以添加所有HAL库C源文件进来。& }; v% K$ m, b
第8步:应用方法看本章节配套例子即可。 V+ z' {" ?' j' [
75.11 DAC8501驱动移植和使用(SPI DMA更新方式)
- F$ t" [2 d8 ?$ X& ^' dDAC8501移植步骤如下:7 ]: O1 q+ d& G- \" }6 s
* R( u- i1 n/ r: B4 J8 X; N/ e 第1步:复制bsp_spidma_dac8501.c,bsp_spidma_dac8501.h到自己的工程目录,并添加到工程里面。/ e+ W2 M% n* P! b
第2步:根据使用的第几个SPI,SPI时钟,SPI引脚和DMA通道等,修改bsp_spidma_dac8501.c文件开头的宏定义
) }! T" j; X& u! b5 h3 b- /*
* x# I; P0 p5 Y - *********************************************************************************************************
9 @" @* x2 k. d& o" v - * 时钟,引脚,DMA,中断等宏定义7 ?3 |# } e1 _+ V7 o: T) p: a
- *********************************************************************************************************; [2 o, M" |8 i9 B) o1 o
- */& s3 l& J* c: W! l. q# f' S7 v% |6 M
- #define SPIx SPI1
1 |1 i* v0 Q2 q* M1 |8 B - #define SPIx_CLK_ENABLE() __HAL_RCC_SPI1_CLK_ENABLE()! q" I: w$ }; X, k0 Z
- #define DMAx_CLK_ENABLE() __HAL_RCC_DMA2_CLK_ENABLE()8 D$ B; z5 d- H" R) z% s
- 2 g0 S# U, I, j) N$ O% ^
- #define SPIx_FORCE_RESET() __HAL_RCC_SPI1_FORCE_RESET()4 l& P/ R v* S2 T8 J4 O
- #define SPIx_RELEASE_RESET() __HAL_RCC_SPI1_RELEASE_RESET()8 A* q' }1 R6 {: N
- " Z' O5 S- w4 s2 `* @2 @% \/ {6 r
- /* SYNC, 也就是CS片选 */
6 x5 @0 B0 ]* {* p N* T: L - #define SPIx_NSS_CLK_ENABLE() __HAL_RCC_GPIOG_CLK_ENABLE()
* f' g4 [1 c& {# m; J# j+ b - #define SPIx_NSS_GPIO GPIOG
3 |: U! t E! R - #define SPIx_NSS_PIN GPIO_PIN_10' I9 @# y5 R8 F4 n9 i! s
- #define SPIx_NSS_AF GPIO_AF5_SPI1! k; I' A3 W( M- `" k
5 @' `/ I; l8 Q) @- P( K- #define SPIx_SCK_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()- Z: |4 e' o0 Z1 {3 L0 p
- #define SPIx_SCK_GPIO GPIOB
" v) a- q% K7 j/ A - #define SPIx_SCK_PIN GPIO_PIN_3! L8 H+ t' r( S9 F k& b
- #define SPIx_SCK_AF GPIO_AF5_SPI1
! T1 U0 j: g& G9 J - * {$ W( ?' o. }( ^
- #define SPIx_MISO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()3 D/ B3 f# G" ?; ?: [
- #define SPIx_MISO_GPIO GPIOB
0 |: [3 _1 X# O - #define SPIx_MISO_PIN GPIO_PIN_4' h* Y5 O) ^4 Q+ Y
- #define SPIx_MISO_AF GPIO_AF5_SPI19 f2 u! S! _- Q4 i8 [. p
& r. F! s% `" u9 K. J! N8 p8 q2 {- #define SPIx_MOSI_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
2 \. A$ {. j1 @9 U: N; Z( N4 t - #define SPIx_MOSI_GPIO GPIOB
6 {8 a' S1 I6 H5 J" v" ~6 ?+ l3 G: [ - #define SPIx_MOSI_PIN GPIO_PIN_5
3 X' e* e# g, R - #define SPIx_MOSI_AF GPIO_AF5_SPI1
; _) _5 C, I& ` - / O% ]* c. d3 U; v" j
- #define SPIx_TX_DMA_STREAM DMA2_Stream37 i; m6 e/ f. y; L4 @ X+ ~
- #define SPIx_RX_DMA_STREAM DMA2_Stream2( u* }+ x; S0 R5 s; E6 G9 @( Q
- P) v2 V: B( U- #define SPIx_TX_DMA_REQUEST DMA_REQUEST_SPI1_TX5 f, k- A/ }& \1 n, S! W, ~+ d
- #define SPIx_RX_DMA_REQUEST DMA_REQUEST_SPI1_RX
9 x2 v' S; T5 }" z: p" A
: L$ i' o" k% e' w' `& f* D9 K- #define SPIx_DMA_TX_IRQn DMA2_Stream3_IRQn
, p2 S$ n# d) N& j! q# y - #define SPIx_DMA_RX_IRQn DMA2_Stream2_IRQn
) V& h6 \6 U6 D- N
' {+ |9 t4 Y2 ~- #define SPIx_DMA_TX_IRQHandler DMA2_Stream3_IRQHandler$ h3 h/ F- V+ ?& |
- #define SPIx_DMA_RX_IRQHandler DMA2_Stream2_IRQHandler x! A/ B* y: R" u& s9 u h9 p0 a
- 8 }0 ?% G1 I: V. Y" v" \
- #define SPIx_IRQn SPI1_IRQn- p3 u. Y4 E" H
- #define SPIx_IRQHandler SPI1_IRQHandler
% f' d' t2 w4 H0 a* ^( ?1 @
复制代码
8 F1 r% C: n' C( L$ Q7 ?; `2 l2 c) n' t' O6 c/ [; J' I, F9 U3 K
第3步:如果使用DMA方式的话,请不要使用TCM RAM,因为通用DMA1和DMA2不支持。并为了防止DMA和CPU同时访问DMA缓冲造成的数据一致性问题,将这块空间关闭Cache处理,比如使用的SRAM4:) C3 A4 y, v ]9 S4 q k
- /* 配置SRAM4的MPU属性为Non-cacheable */
0 t% j& H0 g1 z" {# s - MPU_InitStruct.Enable = MPU_REGION_ENABLE;
6 U; p+ _/ F) c3 {1 z& Z - MPU_InitStruct.BaseAddress = 0x38000000;
3 A7 q5 x w' f Q( |1 t - MPU_InitStruct.Size = MPU_REGION_SIZE_64KB;
6 R' x! o& w+ v2 M - MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;1 @! N. N2 i# g0 `/ c
- MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
) |; C3 q8 I' e! t, o# Z1 @& M - MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
; h6 B9 V5 m1 M- i - MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;& ?1 C6 h/ s) x$ r8 N
- MPU_InitStruct.Number = MPU_REGION_NUMBER2;* q. j# J0 x2 |
- MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
$ a9 _- w" u5 |( Y0 [' V - MPU_InitStruct.SubRegionDisable = 0x00;
H8 i7 u( X7 w& Z - MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;3 R5 b' v0 g0 r g# x8 ~
^2 H# i+ D3 V( _8 C9 m- HAL_MPU_ConfigRegion(&MPU_InitStruct);
复制代码 第4步:初始化SPI。" ^5 u/ `- k7 ^
- /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */
: G, q! ]0 G4 r+ Y# G- M* C0 v - bsp_InitDAC8501(); /* 初始化配置DAC8501 */
复制代码 , Y: ~; o7 U# r1 U" p
第5步:DAC8501驱动主要用到HAL库的SPI驱动文件,简单省事些可以添加所有HAL库C源文件进来。; r. A& o2 F! r
第6步:应用方法看本章节配套例子即可
+ z2 [9 D5 j; G- u7 ]/ t) j75.12 实验例程设计框架3 l) l- U( r, T, a; h" ]7 E Y; k, ~
通过程序设计框架,让大家先对配套例程有一个全面的认识,然后再理解细节,本次实验例程的设计框架如下:
5 O% O' ]5 @: [* a4 [! _
% |. f: ]/ \* b7 z4 k r9 [
~! x" ^3 {$ h) r/ c% @) w4 D+ ?% I2 U2 |+ ~: d
第1阶段,上电启动阶段:
9 J/ m! Y3 {( k" [' @' K q0 F4 |6 {
这部分在第14章进行了详细说明。
- W( ^ P. E& {( r$ P: b: f 第2阶段,进入main函数:
9 h9 S# G8 e: H
" W% {1 j1 K: w x) U0 ?) g 第1部分,硬件初始化,主要是MPU,Cache,HAL库,系统时钟,滴答定时器和LED。
7 m( \4 v2 |, L6 P' p 第2部分,应用程序设计部分,实现DAC8501的简易信号发生器功能。。
4 \4 f" b; y5 s/ ?& K9 |75.13 实验例程说明(MDK)
0 a/ I G2 ], `5 I! j注:本章是配套了两个例子的,这里我们以SPI DMA方式进行说明。
1 N4 V) i0 a" m2 p- H) n& b8 Y& `4 E6 x* G$ }( r/ ?6 Y9 i
配套例子:4 z1 a7 q5 @- [; K, _# i% }
! w9 V" S# e' @" q3 D9 r* cV7-054_DAC8501简易信号发生器(单通道SPI DMA方式,16bit分辨率, 0-5V输出)
: c0 k; ^. H+ b. g* V4 I
3 V; _* P9 |( aV7-055_DAC8501简易信号发生器(双通道SPI查询方式,16bit分辨率, 0-5V输出)2 J7 {0 {& Y4 t" ?; Q t
1 o/ x- T. {9 T* S
实验目的:* N3 R. o; F5 m+ R' T7 j
8 N/ U; O$ F. k9 O2 h% N学习DAC8501的SPI DMA驱动方式实现。! s. V0 D: i8 E0 I
实验内容:
- }9 R9 D* U. R4 }9 V% i6 U) ~- A! e/ F2 ^; c
DAC8501模块上带了两片8501,每片是单通道DAC,片上输出缓冲运放,轨到轨输出,16bit分辨率,支持30MHz的SPI时钟速度。1 l6 x* k# s2 P; |$ s
DAC8501本身仅支持一路输出,而模块上是带了两片DAC8501,其中只有一路的片选可以支持SPI NSS复用, 所以只有一路支持SPI DMA。) g% n6 L/ @1 }6 v1 V
DAC8501供电电压2.7-5.5V,模拟输出带宽350KHz。
7 v/ F6 `0 y' s; q/ a9 f实验操作:
0 L1 Z4 ?' }, [, p* Z! t
' V" d: Q, [' W( p/ e/ L启动一个自动重装软件定时器,每100ms翻转一次LED2。
' [. x3 Y+ z" i- T( s; w( sK1键按下,通道1输出方波。# g( L9 ~# b0 `1 [: ~1 r
K2键按下,通道1输出正弦波。
5 A! u3 W- Q7 R! q, ?- hK3键按下,通道1输出直流。4 g+ }# f, ?+ _/ w' z9 N
上电后串口打印的信息: Z9 _" x' v8 v2 h0 f6 t
. T; E1 \4 q: m, k' n, |波特率 115200,数据位 8,奇偶校验位无,停止位 1。
T- H* X7 @) J
& p7 G$ P. ?- |0 c% k! P( v' n- j8 ~8 R
) R' ]+ z6 Z7 `- u$ ?, F
波形效果:1 K/ w& c5 G0 F" K- x( ]
5 ^* L" q- m" K5 j: @, q- s1 s: U {' G$ e; b8 J' L
4 L% D, [5 h! F, o. {+ y5 @
模块插入位置:1 }; F+ @' R- c2 ^. x5 s
3 C7 o- [2 B. [# R B+ l# x# F- M( @8 C' n1 n- M& b: _
8 z8 Y7 H6 _/ D3 }
程序设计:
7 j- M3 f( Y' e+ V" K5 P) ]2 J; C7 m' R, @- x" i- {9 Y
系统栈大小分配:
6 M; x3 i" V7 C: |' F5 h. F5 p% D. z# Y
; @$ M* M& r$ g/ w- Y# B
, r2 H0 t* [! f, R4 C RAM空间用的DTCM:2 p- m+ s/ T2 `) F6 N* v
$ o& d9 |! S/ c" \; n% \2 N" d2 ~
7 X! I& x- Z, u, s; l
% @7 o4 Y1 S! a 硬件外设初始化 g3 v% A8 j1 e
8 o% C1 K- Q w4 w; {# ^! ?
硬件外设的初始化是在 bsp.c 文件实现:
7 d7 x' S! f" `2 o% y' b
' @7 r1 [8 f; ^- /*7 a6 G- P7 o& ?4 D; |
- *********************************************************************************************************
j1 }) `1 \5 A) L$ b) L5 b8 [ - * 函 数 名: bsp_Init0 m0 d! e6 I1 p8 U2 M
- * 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次2 J: v( T1 B+ t. w- {
- * 形 参:无( J0 |; g' r' @* ]: \
- * 返 回 值: 无+ E# I9 q. U5 ~4 F5 X
- *********************************************************************************************************
+ {4 y4 `7 u# Y - */
. v! W2 l4 ]+ W+ ]- g7 S - void bsp_Init(void)
) T$ [6 x$ q$ f - {3 I% A% C+ L: u4 H9 h0 c
- /* 配置MPU */5 W# r- _2 L9 l2 @, V1 W: v
- MPU_Config();2 Y3 p, b- E( f3 b8 V( s
! n8 n: F& M+ s& S8 G8 v- /* 使能L1 Cache */
7 B& \3 }! v& X" X; G6 k - CPU_CACHE_Enable();
! N! E7 L5 R8 U# L7 F
' o& C& A" G: L8 V w% H3 l, @- /*
& A$ J1 }: H( A, i& R - STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟: a `# b( l n5 F6 i* t
- - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。: \* e9 }/ P* w* l% R+ r
- - 设置NVIV优先级分组为4。/ P: F% |5 w1 G a& a$ d; ^, r2 B
- */
1 c/ v! x$ @4 A- r/ }5 ~ - HAL_Init();$ [, H; U4 `6 w
) q4 Y3 ~+ p( L& {# \- /*
) c/ ^2 V# B- A% P - 配置系统时钟到400MHz; _( }4 ~9 N, a% m. r
- - 切换使用HSE。3 e' v3 l; A! ?; @0 _( ?
- - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。+ H! W, i- V3 Z. z0 k! v
- */
. W, x$ c9 K9 s - SystemClock_Config();
7 J5 t% k* V0 o/ v" c - - i) |$ h) J5 K' p- {5 h
- /* ' o* f1 I% x0 U
- Event Recorder:! c0 m2 G" z& \: @* [) [1 h/ _& M
- - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
( k! l, \2 S# z" w3 O - - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章) y& j8 k) Z# o& [. K9 K# S5 }
- */
7 V9 j* |4 k; M d" @3 e - #if Enable_EventRecorder == 1
6 m4 e+ H- ]) y- t8 ~) Z' g" X, B% K - /* 初始化EventRecorder并开启 */
( [. q" M5 j- q6 s2 Y0 t - EventRecorderInitialize(EventRecordAll, 1U);+ P# u9 }9 Y2 W1 J6 @
- EventRecorderStart();
! b2 J) V1 r( c% \% s7 b- [ - #endif
3 S# x6 N3 \# z - 7 N! j4 S* j7 d# F1 }9 @
- bsp_InitDWT(); /* 初始化DWT时钟周期计数器 */ 6 ^, l9 |( R; N: l% Z
- bsp_InitKey(); /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
: A4 Z3 o' \8 o. ~% ?0 l% _1 F3 L - bsp_InitTimer(); /* 初始化滴答定时器 */# ^. n* X* M- _6 ~* _
- bsp_InitLPUart(); /* 初始化串口 */
0 c4 F ^8 o x& K/ K - bsp_InitExtIO(); /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */ * ~9 ~, b+ F& r, t Z. @
- bsp_InitLed(); /* 初始化LED */
- u1 E3 s: y" e) X2 }# W - bsp_InitExtSDRAM(); /* 初始化SDRAM */
3 U% n6 n2 t. C- p- ]/ c& \
6 h8 l) X. `' L4 h, r6 n0 ^- /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */
' `. }& o* Q6 F% a e0 v - bsp_InitDAC8501(); /* 初始化配置DAC8501 */
" z( i2 _+ P' u - }
复制代码 9 J% A$ n) I- d" K1 t8 x# ?
- r8 I, Z: E \! F" A7 m& g
2 V4 I" ^) v/ h MPU配置和Cache配置:
5 f+ d7 p, r9 z8 ^ |- [1 S' d8 N( _# }; _; U% z$ Z0 |, X- _
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区以及SRAM4
6 a0 r ?1 `! f4 o1 x$ N6 M0 B" C: e" V3 P; N1 E
- /*
6 l, |1 \. n& K* l' ^, K# | G - *********************************************************************************************************
( i, V7 c% n9 ?4 l; ], w7 U - * 函 数 名: MPU_Config
3 e& x7 J' i; E E - * 功能说明: 配置MPU
% j* e+ Y% X! ? - * 形 参: 无
) w/ E( [% T c - * 返 回 值: 无1 }$ l! y: v- _ _7 `$ I
- *********************************************************************************************************
# b3 \3 |5 h" e - *// {) ~, K( o/ |: N3 S6 r7 g3 r# K8 r
- static void MPU_Config( void )! l& [, Z* E" a, [9 M
- {
4 E/ k |$ z" w2 [! Z$ c m3 h: J - MPU_Region_InitTypeDef MPU_InitStruct;
9 E2 D" r- w& w: a
! r/ A$ h0 |# h! t/ P. z/ S- /* 禁止 MPU */& v' Z* a5 A$ J8 p7 N- |6 `/ ^% O
- HAL_MPU_Disable();" b6 e5 R- C+ c2 g+ A, N
- * W7 O {1 N+ z6 I; d! M* \
- /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */" v4 p; m1 j7 i( j7 ` A# ~
- MPU_InitStruct.Enable = MPU_REGION_ENABLE;
% y% c8 Q* G# b1 ? - MPU_InitStruct.BaseAddress = 0x24000000;9 j* A( r9 m1 C) l9 l" z
- MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
8 d! _1 ^& X4 E% k - MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
3 @9 n0 v0 u/ I0 ~9 t9 \ - MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
9 ]% X. k6 b. P9 Y - MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;( J" K+ s) ] |
- MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;$ W" j- p/ @+ }3 H- d
- MPU_InitStruct.Number = MPU_REGION_NUMBER0;
" I6 {6 s, x; a3 O& j - MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;% n5 b/ T& M: j P* y; L
- MPU_InitStruct.SubRegionDisable = 0x00;; {% b& L! v% \3 E
- MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;/ }# l g8 b( Z0 R# O. e% m2 \& J
- : {' x* j( g; T
- HAL_MPU_ConfigRegion(&MPU_InitStruct);
: v. t9 s6 T' r0 F2 W - * Y5 ?1 T9 ?/ ~$ N( Z
/ U5 F! x& `9 j4 C3 b: R: m- /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
6 [; n( G8 |7 d$ F. L, H/ R* x+ |1 L1 T - MPU_InitStruct.Enable = MPU_REGION_ENABLE;
" D6 o2 T# s4 B4 V9 T6 {1 M$ x- Q - MPU_InitStruct.BaseAddress = 0x60000000;
. }' ^7 F x( O& B! Z - MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB;
1 P6 w% L! | Q - MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;% T% r( T' W) x2 g8 l6 d! Q
- MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
+ {4 {: P5 @ u1 e' P4 l - MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
# C1 Q' }' {0 X6 H" C/ F. `* e - MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;/ ^/ s( M' o/ V+ ]. @
- MPU_InitStruct.Number = MPU_REGION_NUMBER1;1 h3 q- e+ m# N( Q
- MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
& _) @7 K8 ?: `" I. `9 O( ^ - MPU_InitStruct.SubRegionDisable = 0x00;
7 S/ Z1 O# q- H2 X* w: K& [ - MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;3 D% V" ^( V4 F+ D, ]
- ) A4 W& b+ t9 h/ w$ l2 d
- HAL_MPU_ConfigRegion(&MPU_InitStruct);8 @4 i: c8 N7 @' ]. m
; n% p# q/ N6 h/ e' R, K. o- /* 配置SRAM4的MPU属性为Non-cacheable */0 \, d% E$ D |0 E9 p$ n+ \, A V
- MPU_InitStruct.Enable = MPU_REGION_ENABLE;1 v1 l7 S0 l0 q: N# y
- MPU_InitStruct.BaseAddress = 0x38000000;. { m1 p/ ?& s# M+ }+ J# f* R
- MPU_InitStruct.Size = MPU_REGION_SIZE_64KB;: h6 _# g4 Z6 _' _
- MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
6 s+ P" p* l1 ~0 c - MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;8 f. v2 _. U6 `+ w% m( o
- MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
7 G5 [2 a# S% T6 q$ Y8 b: a0 w - MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;* s! M9 |/ [6 G' j1 T% P: a
- MPU_InitStruct.Number = MPU_REGION_NUMBER2;
, |6 ]2 E# z I7 }, o g - MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
, ?4 a; S' v1 S - MPU_InitStruct.SubRegionDisable = 0x00;
' d* d- F' ^" O, T! y - MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
, y. B3 ]) ^3 f+ d/ _" R- ^ - . b; |2 @, j9 ~3 X" X- y
- HAL_MPU_ConfigRegion(&MPU_InitStruct);) F: j1 |% m3 q4 j- ?- }3 C
/ s b2 E; p+ `9 E! ~- /*使能 MPU */. b2 L- y, o7 m2 s. l4 G0 B, q
- HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
7 n8 F* w( O( {7 p- P - }
/ a; O0 E* A" z; q( ~7 S1 H - 5 M( X, K$ o; J. r' d/ _! b1 \# y( B5 F
- /*% u" k! Z2 Z! Z7 x, b% ]) _- b! U( f j
- *********************************************************************************************************1 v9 G5 Y3 l, d
- * 函 数 名: CPU_CACHE_Enable
9 K7 _6 i* D; C& S' x7 C - * 功能说明: 使能L1 Cache
" W; E/ e7 H# _, U+ h - * 形 参: 无0 w* m) {7 O/ R, N
- * 返 回 值: 无- }2 t) Y* r9 }; d/ R+ U& Q3 [
- *********************************************************************************************************$ x8 t/ ~( D" P9 p, A
- */# g. Y' g3 L' h. X( r0 L/ }
- static void CPU_CACHE_Enable(void)
9 `6 V2 O: l2 U3 T0 @( ] - {" B; H4 w) F7 ]( S2 h l
- /* 使能 I-Cache */! \0 k5 ]9 y$ _. n1 c( f b! Y
- SCB_EnableICache(); ]; `3 K* ~5 o. J* T
) x9 I# ?( I% s) b2 e! n+ `- /* 使能 D-Cache */
# S: ^) b+ `' |% M" @; i, \& u - SCB_EnableDCache();. ^% r% n( v: _7 T
- }+ O1 ^4 ~7 s6 m
复制代码 . Q$ S# h' y6 [/ t( ]
/ U$ ]0 G" L* r8 C6 t/ c 每10ms调用一次按键处理:
* q8 ~9 p) ]/ x1 F
1 ~3 J2 ?6 K8 O# G$ o. p! Y7 ?按键处理是在滴答定时器中断里面实现,每10ms执行一次检测。
8 q; ]- u$ Y' n/ e c( |& I/ B, I2 Q
- /*
) o" z z! u6 U' q7 R7 ] - *********************************************************************************************************
. U! a& }4 b6 }( e( C9 I - * 函 数 名: bsp_RunPer10ms
! h5 p" \3 P# `4 d - * 功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求
/ O0 Z6 ^- |, r) s; B0 f) | - * 不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。
1 f" v( q/ u5 S+ n* H/ J - * 形 参: 无
2 L& H( R! x( s; S% X4 _, H e1 { - * 返 回 值: 无. u7 o5 M& d& A9 c# s
- *********************************************************************************************************
0 a) Q( j" G; t - */! p" ] F7 x, {, V; @7 \! w. N! B
- void bsp_RunPer10ms(void)& l" `9 Z$ t) U: R4 m: j
- {1 U, N0 G2 U Z6 Q4 G
- bsp_KeyScan10ms();
; a+ G# D% R/ B3 ?9 y" y: W6 c - }" z1 \; k" M3 d( i( e2 y4 c
复制代码 $ ?' n+ I8 V+ J1 J/ T; R; s" m" N- h2 b
0 y; H2 ?. ^. H1 D
主功能:
% k+ w; j4 W3 d7 y0 ?7 k8 B
& w. V/ |/ m. K$ O1 ?主程序实现如下操作:$ m3 M/ Y6 {! J+ _$ k( U6 J6 Q2 c
0 B. ?+ i* f6 \1 A: p! T n
启动一个自动重装软件定时器,每100ms翻转一次LED2。$ ]2 e5 O8 U* ?& z1 W P* L* a
K1键按下,通道1输出方波。
1 t: z) |- [3 f K2键按下,通道1输出正弦波。. l. U5 n" K* G( J/ i+ `
K3键按下,通道1输出直流。
* \7 i! i$ n" y* o- /*
) a& w. w2 g: } - *********************************************************************************************************) H8 t+ r9 X. f; j8 E
- * 函 数 名: main
% k" C. d' r5 A - * 功能说明: c程序入口
) W/ ?% G, c- y! v - * 形 参: 无
7 [" Y- i$ p h$ }: I% c9 L - * 返 回 值: 错误代码(无需处理)
7 d- n$ T; F6 A( ? - *********************************************************************************************************& W9 A+ \: B; M% [
- */
6 a1 c) ^& A Y8 ? O1 n - int main(void), Z- E, h& _* W" h# P8 ^, d, G
- {
; \6 Y, s' v: \# q( Z - bsp_Init(); /* 硬件初始化 */% b( j. _6 B, S
g$ F+ a, ?- a9 }0 {) W$ L5 `- PrintfLogo(); /* 打印例程名称和版本等信息 */
: k. D; \8 K$ v/ H8 \ - & s3 A; t; X# L+ G
- DemoSpiDac(); /* SPI DAC测试 */0 j1 q0 L. V6 v
- }
$ d1 Q* J7 X3 ~6 ^. ?
& B+ |% m& Z1 R9 u- /*
- g2 U9 o, Y# I$ `$ @2 ^2 C - *********************************************************************************************************1 M* h- L7 D+ p# Y \
- * 函 数 名: DemoSpiDac
0 ~# c! i% [! s( `9 h - * 功能说明: DAC8501测试
$ q& p, w6 |$ i - * 形 参: 无* c0 \" i" h# g8 H" t: Z
- * 返 回 值: 无
1 ?! M. ^! X8 ]1 g - *********************************************************************************************************5 y9 [6 D% Q3 N4 r: u1 l
- */
( M. y+ |& ? t - void DemoSpiDac(void)1 f- Q( e4 t* u" C& X0 j
- {
& v$ w, o; u. D - uint8_t i=0;5 i. Q. X2 Y7 _" F( F
- uint8_t ucKeyCode; /* 按键代码 */3 w4 }( v. h; E {4 B' j8 F
- * O x. G+ o8 E* w! B
- sfDispMenu(); /* 打印命令提示 */
1 A5 M5 _! g/ k
0 B* H* z, S* s: P9 y- bsp_StartAutoTimer(0, 200); /* 启动1个100ms的自动重装的定时器 */3 P0 U- O4 V9 A* ^/ l2 N
- " t+ |' I5 N. [3 w0 p3 t9 l- A
- V3 N& D3 F8 h" _% A& _. B9 b
- /* 生成方波数据 */ z5 O8 s) ~2 N6 S5 @
- MakeSinTable(ch1buf, 100, 0, 65535);
@( B8 C: n% M8 M" L p, ` - @0 y) B: N4 C [: Z& M
- DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);
* r7 l+ z. k' [% i. _
$ E$ P$ ^4 e; @5 P# M5 j- while(1)
2 F+ s! y* K+ K7 [! T, P: s - {
: w5 P4 s* E5 B; C9 S) N& u - bsp_Idle(); /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */6 ~+ P/ _6 N4 I" U1 ]
9 m+ s& j4 [9 n9 r% a- /* 判断定时器超时时间 */0 C& ?. H5 R9 x: H" D, @) A7 A8 e' {
- if (bsp_CheckTimer(0))
6 j E; g+ C' W: m, M - {
3 n- _' Z! N' I# h: P" h" \# }+ H - /* 每隔100ms 进来一次 */
. a" n. i$ Y" l! V* D+ x - bsp_LedToggle(2);& g1 U- \- P: H4 u8 t+ s, t
- }& G6 o7 P+ }: H3 R/ R6 x! t
, k6 u; d* L& ^: v- /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */ v8 P+ j+ i* T% X- C" T8 W
- ucKeyCode = bsp_GetKey(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */" o% U# B) p( ?3 R: R1 n3 r, a
- if (ucKeyCode != KEY_NONE); O* i$ q" ?% D1 k: w- n
- {
" k( w5 d5 Z1 J/ [8 Z1 U$ X# Z - switch (ucKeyCode)3 f' Z0 V0 X/ A( w* R5 `2 h
- {4 w7 j3 g% X1 r4 P8 c! i" ^
- case KEY_DOWN_K1: /* K1键按下,通道1输出方波 */
* [9 R$ n5 J$ n: h4 j6 ^' D# O - /* 生成方波数据 */
9 |8 F# S; |9 W0 n - for(i =0; i< 50; i++)
0 _. r3 b1 @, U - {
$ T3 T$ D1 ]% X9 n5 Y( w) ]" ` - <span style="font-style: italic;"><span style="font-style: normal;"> ch1buf = 0;
! o! E! K% ?7 I) _' ] - }. i" x1 O4 E# T! y4 g0 d
$ I/ r/ ?! p! q( R# a- for(i =50; i< 100; i++)
0 L8 c1 k! R, n) O( P - {
$ ]/ V7 K9 X: w, u - ch1buf</span><span style="font-style: normal;"> = 65535;, V( [! ]) a/ p9 h- K) J
- }
- j5 b9 |: f% i9 d- }/ |# Q
6 M8 D! B8 e' u$ _2 z/ k- /* 触发速度1MHz */' V9 d8 q) F% ?: U
- DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);
* a/ W+ y2 X K2 C8 b+ Y5 U - break;
9 ?: e+ w# U$ Q
% x! w: F) [% F& E- case KEY_DOWN_K2: /* K2键按下,通道1输出正弦波 */& o5 S/ c& Z6 C# K: \# {2 } f1 K7 R6 B T3 ~
- /* 生成正弦波数据 */
- T Q8 \ t1 w% ^, @ - MakeSinTable(ch1buf, 100, 0, 65535);
, n9 ] G( W- X/ ?. e
: B: B# p' U& `7 T. Q( l' k) A- /* 触发速度1MHz */
0 R0 F8 ~! a+ N6 w5 C - DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);9 w) i! K& B9 {( P; S9 p
- break;
0 e2 F1 V% I$ I# d9 b w - ' Q5 v% F, h! ~+ _( R
- case KEY_DOWN_K3: /* K3键按下,通道1输出直流 */9 D4 b% Q% w" B8 w2 g
: Y$ j U/ J3 @$ E- /* 生成方波数据 */
3 K+ ^; V8 X' o5 q - for(i =0; i< 100; i++)
' a6 M( d; g: l* F, ?* x - {
& s3 r V4 \ z& }6 i5 c& B& u - ch1buf</span><span style="font-style: normal;"> = 65535;: L' a2 g+ e8 ]; B
- }
$ K5 i. n# a) l5 v* r& R
5 t( [; Z9 n% I: g" W* k/ b* g* m- /* 触发速度1MHz */( F9 V, V8 v9 v7 @$ ] s
- DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000);
0 `; D b; ~" I; _; g% w - break;
1 b* b0 I" @" K D( E# M8 Q( P0 h - / F, l( N% I D! j1 i9 G( y
- default:3 @$ p2 s% j" ]$ g4 l1 z& q
- /* 其它的键值不处理 */
9 {' Q7 O/ q3 F - break;' |7 Q" }7 |0 M: c: \3 W
- }
# [9 N6 D5 D3 D* y# ? - }9 b3 E& {7 W4 |4 W2 \& }& |
- }
2 m/ C6 X! H4 S0 I+ A+ \- P - }</span></span>
复制代码
* }1 t1 e& A9 y
2 E2 g3 X' I6 ^1 V7 `6 `- t7 ~' d7 c! v
75.14 实验例程说明(IAR)
4 d/ F* u6 j$ X, y注:本章是配套了两个例子的,这里我们以SPI DMA方式进行说明。2 U% z# }: x. D' B! l: r2 r; `
; \; |4 N, g, ^; U" m& ?7 S! F. N& s
配套例子:. c* X/ P; r# z q% J
4 }: G2 j& Y5 ~9 n
V7-054_DAC8501简易信号发生器(单通道SPI DMA方式,16bit分辨率, 0-5V输出)
9 h0 y3 w* I: ~* A5 K0 ~
# B! i; P: v) z! EV7-055_DAC8501简易信号发生器(双通道SPI查询方式,16bit分辨率, 0-5V输出)" d; f5 m# E! ~* d' @: y6 I7 I
; a+ B0 g2 X9 S9 I6 S* C5 k/ K1 {+ U实验目的:9 I/ \/ E5 e! S; Q
. z1 Y- K1 p4 G5 G/ Z! p$ x. m9 e
学习DAC8501的SPI DMA驱动方式实现。; W* e# i2 c! D5 W# o
实验内容:
+ H p4 M0 N% _7 {# L# t) X7 x5 f e8 o0 V6 ?& u
DAC8501模块上带了两片8501,每片是单通道DAC,片上输出缓冲运放,轨到轨输出,16bit分辨率,支持30MHz的SPI时钟速度。6 N7 R: M) a" ~
DAC8501本身仅支持一路输出,而模块上是带了两片DAC8501,其中只有一路的片选可以支持SPI NSS复用, 所以只有一路支持SPI DMA。# c* D8 k7 b% W* s4 a7 J
DAC8501供电电压2.7-5.5V,模拟输出带宽350KHz。, H1 C# Y1 }' H: M8 T4 m
实验操作:
, L, k3 D/ H M I
' t; |" j; I/ t; s& U( q# x0 m启动一个自动重装软件定时器,每100ms翻转一次LED2。/ w1 {# r; N& I6 I! c9 R, \5 s
K1键按下,通道1输出方波。
' r! C) m, s5 K' [4 z- Z$ `K2键按下,通道1输出正弦波。, T9 E& E* f: p- N/ j5 v
K3键按下,通道1输出直流。
4 Q; Y; K. E( i/ w8 }; `; j上电后串口打印的信息:
; ?2 O+ e( G2 [
2 |' O) c& Y* C* m9 y波特率 115200,数据位 8,奇偶校验位无,停止位 1。) A* ^" Q! W: F0 P& b. {
" V. o' n6 y7 \3 E. H( D n
9 Y. @1 {8 u$ F+ q; O! p& o; I( Y$ g& f
波形效果:
+ G) R7 M5 _7 e" P
& |: S$ p2 Z/ t @
( e/ u f8 w3 }7 w
% b& }2 W+ p! D# W1 o( `$ S) p8 Q模块插入位置:8 i3 [& Y2 k8 s# D
9 o* _8 E9 ^! I& \8 ~
- u& C$ Q2 v1 V# {+ }1 n9 c! |& E, q# L/ y+ i0 v- U
程序设计:
& q0 Z( j; o8 M7 n" Y- o
5 u! y. g$ b7 p 系统栈大小分配:% P0 ^4 o, d7 p2 L' m
3 q$ h3 f3 S# @. i, I5 \' o
) |; r/ i6 I1 b0 O/ @6 k8 _, P- ?3 @* t$ S, @( a$ O4 M
RAM空间用的DTCM:
- x' ?+ t6 ?/ F' S0 |. ?
! C, m' s* q S9 N1 m8 w% [1 j0 u0 g6 _2 Z
! ?* ]+ U H8 i, u9 r. C' o
硬件外设初始化) n3 i, l/ d% C- Z1 R) _$ g
# i: M/ P- e) G5 C; n- Z硬件外设的初始化是在 bsp.c 文件实现:
5 o% a2 |6 v1 r: h @5 w0 M4 N1 b
$ G/ \# i3 e3 V. o! ? I- /*
% Q7 f% ~2 G. b/ v. Q/ r& p7 @ - *********************************************************************************************************
8 \( M$ W8 w) @* O% H* ` - * 函 数 名: bsp_Init9 J* J+ n3 s7 _/ v
- * 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
7 L3 k1 n u4 G( t - * 形 参:无
, X: j9 E) F- A! M# w - * 返 回 值: 无+ Q( W6 O, t- s$ B9 j# |* q
- *********************************************************************************************************
4 P) }% M+ a M* {6 L, T - */3 Z# K" d7 }/ H/ g( M8 @
- void bsp_Init(void); X: u& r2 l/ l8 b9 P
- {- l1 n/ w( O6 z; l) s! E; Q
- /* 配置MPU */$ N! Q* C3 k7 V! N# h a( A
- MPU_Config();
/ w' n3 M2 Y+ d6 [8 T e" \1 m1 [: {
) L. k7 L% N! a' f4 v- /* 使能L1 Cache */
" m! w+ B9 S4 I; E2 ~6 _. f - CPU_CACHE_Enable();
' S$ F' Z) G' A* W. f5 n
8 C5 t; y9 v9 t1 @' M, K- /* ; n3 ^6 R/ j# h
- STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:0 h2 p+ z: B& y2 b" o( m K7 D
- - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。! ?+ l5 J- d+ V: K/ {( C
- - 设置NVIV优先级分组为4。4 ~% L! q8 [" U
- */+ B3 e+ x% V. }- A0 P) t$ [
- HAL_Init();- r) `+ L$ `) {/ t
- + a6 Z1 q. J5 D- r
- /*
9 X2 k# I; q H7 ]9 t - 配置系统时钟到400MHz
: s" ?) L; h- d3 ?! c F - - 切换使用HSE。
- S8 Y' y+ i* K; T( Q: G1 Y3 s - - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。0 A! X2 N8 Q, J8 x
- */
) D3 H' [0 |6 |' T; M% t - SystemClock_Config();
& {; [: n( H& I& ? r - 8 @# x; Z) H! B# K. M
- /*
% b* Z# M0 S2 a7 }, R$ p - Event Recorder:! S2 W. W# x( c. M, Z5 r
- - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
6 e* H! a7 _5 S+ C; _ - - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章
: S, M1 F9 P( H. \) K) X - */
* W8 O: ~5 r) _& k+ Q - #if Enable_EventRecorder == 1
% Q) ]6 t1 k1 i, g2 c5 y8 Q. P' z - /* 初始化EventRecorder并开启 */
' @& f+ z' L# T. S; y& n1 _) V - EventRecorderInitialize(EventRecordAll, 1U);8 q# y- {; d8 t2 R$ z% Q1 v
- EventRecorderStart(); _2 F6 x# |* J: @) A7 L
- #endif; `, {' _ G5 @- k: ]* M2 _
7 [+ s- z+ S: G: x: g. a5 ]- bsp_InitDWT(); /* 初始化DWT时钟周期计数器 */ 4 V' B+ S5 Z" P: [' Q
- bsp_InitKey(); /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
4 t8 V. B [. I4 o7 P - bsp_InitTimer(); /* 初始化滴答定时器 */
3 X; f4 q9 I! S) `& s! M: H" r) P - bsp_InitLPUart(); /* 初始化串口 */
_# e+ j$ v, A# F - bsp_InitExtIO(); /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */
: h' A; C5 K' I - bsp_InitLed(); /* 初始化LED */ 7 U6 a3 U# ?4 n3 V0 ]6 ^% U
- bsp_InitExtSDRAM(); /* 初始化SDRAM */
8 c6 y% ~! t: v1 m/ Q! M - # ~, N3 Z% o8 r X( { @3 k; A
- /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */
' @$ u# S$ [8 Q r - bsp_InitDAC8562(); /* 初始化配置DAC8562/8563 */
) t2 ?+ ?4 j# f/ c8 b+ a* P' L - }
" B+ S# l+ n+ H6 Y
复制代码
/ a! A' g3 W. y3 |, {7 B7 N/ _/ S& o# | L7 X
MPU配置和Cache配置:
/ L# H2 T7 d, R3 A8 F7 }; q( s+ D& M8 Z
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区以及SRAM4
1 s, x% y/ s* h0 R' p* o7 Y* y4 m& o0 K- o+ t
- /*. N6 O9 `2 O7 t% y; Y3 T
- *********************************************************************************************************
7 D1 s7 P2 u3 U; P F e - * 函 数 名: MPU_Config2 c/ b: e/ y. d8 m: i
- * 功能说明: 配置MPU, n! m& t, z6 r
- * 形 参: 无
/ B4 U I3 c: |8 A8 O. P7 T - * 返 回 值: 无
0 M, I3 c6 c9 G - *********************************************************************************************************
+ R$ Y$ N6 L" p4 V' G( B5 i" ]1 v - */
7 \: h$ m- E' u2 ~; J0 t - static void MPU_Config( void )- n( v4 n* x8 t9 Q" |; T
- {
5 i# `+ n W9 [( T( E. T2 R - MPU_Region_InitTypeDef MPU_InitStruct; N( I" ^( e0 R; p4 j
- 2 E) W# j! g# t' O2 f# z+ z4 X; Q, D
- /* 禁止 MPU */" `, r0 q1 ^4 w& W
- HAL_MPU_Disable();% Z; ~( x) e) X6 ~; \: I6 J9 O
- ) E6 O6 \3 g% N U
- /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
+ y* a( r5 @: M& f9 h - MPU_InitStruct.Enable = MPU_REGION_ENABLE;
: p2 f' r! j) o( e4 `% P - MPU_InitStruct.BaseAddress = 0x24000000;
2 Z: r) q/ j4 H# q - MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;8 K# B# N; E& m& z
- MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
7 C K0 Z+ m: t. d7 t5 T - MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;: X- t, ]% |* Y( s+ W
- MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
7 \1 N1 B1 Y8 a - MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
; f3 {5 P Z6 C' |( o - MPU_InitStruct.Number = MPU_REGION_NUMBER0;
1 E6 S' C2 m p8 ?, X4 x - MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;/ i% e( T s: j i! _- u' C
- MPU_InitStruct.SubRegionDisable = 0x00;
# v& N7 [" g% V% L* c/ @( }4 T - MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;1 j4 v# {- `( h* F
- . E+ I6 k% f- D* ?
- HAL_MPU_ConfigRegion(&MPU_InitStruct);
1 z5 n% x) d: U5 N6 Q
5 Z7 R8 F) ]' v$ V( M
2 L o/ U7 s$ r5 H( A* y: o- /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */& J* H R/ l2 p/ F
- MPU_InitStruct.Enable = MPU_REGION_ENABLE;
2 `* N# [/ S0 W- c! I( ?( s5 I7 l - MPU_InitStruct.BaseAddress = 0x60000000;5 T/ Q0 G; d, R7 ?# C' P) M
- MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB;
1 H3 q& T" A7 C - MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;' x/ n2 D* ?( q4 P7 c7 U8 M* F7 Y
- MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;! b( U9 O7 w. u( e5 T" O* Y1 m. o& O
- MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; + V! H3 j6 Q- h+ W, M+ J7 i3 ]! @
- MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
% y2 V" f3 J% _; i3 H$ X% q - MPU_InitStruct.Number = MPU_REGION_NUMBER1;, T8 F6 }2 V9 C
- MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;1 R, V) j( Y& @# w: B# l' }
- MPU_InitStruct.SubRegionDisable = 0x00;
8 }8 c- W N" \2 Q/ R" a - MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;) F: L6 M% n c
; X4 D/ }3 A! v- HAL_MPU_ConfigRegion(&MPU_InitStruct);
9 ], n/ a0 @6 ^* Y6 v - & l e1 D7 s$ G3 B( I; s3 ^. R
- /* 配置SRAM4的MPU属性为Non-cacheable */
6 z' j6 w# y2 S, S8 Y( q - MPU_InitStruct.Enable = MPU_REGION_ENABLE;. \& a: h V( r5 N
- MPU_InitStruct.BaseAddress = 0x38000000;$ H& ^' B }' X( b
- MPU_InitStruct.Size = MPU_REGION_SIZE_64KB;
: M& o; p3 n! @* j% R% l - MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;% ?8 {. }' f: ?- Y( G+ q
- MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
( H6 u- G5 O" y8 x* {! ?3 {: Y, h2 \ - MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;! b* y7 T) U4 `
- MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;5 B1 i4 i _0 o! w
- MPU_InitStruct.Number = MPU_REGION_NUMBER2;
# z( A" _0 L t5 A3 \! t - MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
9 _$ C& s; I$ ^; n1 D8 D% c - MPU_InitStruct.SubRegionDisable = 0x00;
+ \! G) `6 n3 j' z1 Y9 a - MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;2 P4 o( @- B& C4 P
% u) I l3 g8 G% z- HAL_MPU_ConfigRegion(&MPU_InitStruct);5 h& s" |+ w6 o. N
- / H- ]% ^# x8 V, x, H8 a& U
- /*使能 MPU */8 q1 r4 @+ M+ p/ B, e
- HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
9 i% T, L. S3 t. K - }
! o N( X! P' C8 C% r
" y7 m: [# ?, E5 L( }& I5 d- /*
+ a y# @. j$ O( K: [ - *********************************************************************************************************% J3 d* K' Q# v: i* Y
- * 函 数 名: CPU_CACHE_Enable: [- x) U8 R) O! l# X; ]7 J
- * 功能说明: 使能L1 Cache2 h9 P+ C4 l) [
- * 形 参: 无' C9 l) ?5 _% e
- * 返 回 值: 无
9 _5 O# l# e9 T3 {, @5 J: o) t - *********************************************************************************************************, D; v2 H8 ?+ Z& H! O& J% D5 e
- */
3 j+ M$ o* J' G$ E6 q. H: Y - static void CPU_CACHE_Enable(void)
+ x4 d1 x& E" Y3 e3 l - {+ X% s( ~3 C6 P4 W7 K2 }
- /* 使能 I-Cache */
5 \! K7 q# F6 N) H - SCB_EnableICache();
8 J1 L; e. P8 T
8 D- j) k: v$ t" A% M3 K# j- /* 使能 D-Cache */
7 d/ j. v, p0 s2 ~5 ^) j - SCB_EnableDCache();9 O1 E$ R) F, m+ G0 d$ G6 ?- Y3 _
- }
( w, e \9 Y# a. F$ [: z8 \% }
复制代码
, o( u) @' j7 `) R# h
& s0 }& T2 S! w4 c5 d& q 每10ms调用一次按键处理:1 p4 J" p& c# h1 ?, d2 a
3 G) v/ X$ E! W* Y+ l3 a6 h" T按键处理是在滴答定时器中断里面实现,每10ms执行一次检测。
/ L$ B- s9 Z# r1 e7 P+ Z& t8 O$ |# }* |" I7 n
- /*
0 G0 L% }& `; a2 X - ********************************************************************************************************** u9 z" h) B6 n0 w5 ~
- * 函 数 名: bsp_RunPer10ms
# D$ r3 d- g9 X( I! h6 i - * 功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求
/ r- M7 ]/ x9 M. c3 H- x. D - * 不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。4 [: ^5 a$ P! P A) I1 m
- * 形 参: 无
! }" `7 ?5 {0 a - * 返 回 值: 无4 K( V4 E) I) J3 u: ^
- *********************************************************************************************************# j1 g* H" Y, ]1 m B" i
- */
! c8 Y( g; B; a/ c! w" q$ u9 G - void bsp_RunPer10ms(void)) D0 `* N$ _- x6 ], {: ?5 J R3 f* X+ M
- {+ h& D) t& o* d; z
- bsp_KeyScan10ms();
' l k- u$ D% u o - }
复制代码
* \$ o' V9 Z1 r: t% [9 f
( s7 \0 I( q4 h: k3 t* a; L; {0 P0 T4 j1 W3 `) @/ r
主功能:4 x# \4 C8 v' ^/ s: Z3 }1 q; ]
T) \' v2 D2 u) w5 n8 I9 V5 p主程序实现如下操作:! }9 m, H6 M" b- B/ G
* R9 S) O8 X& M& G1 k, A4 T 启动一个自动重装软件定时器,每100ms翻转一次LED2。0 m5 D P' H; q. w6 G, P$ X
K1键按下,通道1输出方波。
, `9 Z7 x& u" s# l2 U. `4 z K2键按下,通道1输出正弦波。
* N0 u/ @3 U" A4 |4 w# N K3键按下,通道1输出直流。
; M0 f, A; B8 }, r# G4 r" F% L- /*
7 U# q* r2 y- S5 J - *********************************************************************************************************
5 f" ^ E$ k1 k8 O - * 函 数 名: main
% s' K' ^' J2 h$ k - * 功能说明: c程序入口. H7 L4 n: a$ W4 h7 u
- * 形 参: 无
/ t" b5 b2 A* ^/ a2 ? - * 返 回 值: 错误代码(无需处理)
" |; K8 w+ p+ v, x - *********************************************************************************************************
6 r' D: x% y" ]+ `" ^- U; } - */
# r8 p Y6 h4 b* T7 k, k - int main(void)/ U3 T; U" x7 p) b: } b7 ^
- {) p5 ~0 i4 `2 Z- F
- bsp_Init(); /* 硬件初始化 */7 M4 }3 v7 ^' }: k- @( T. F
- . u; g/ F) ^- y& U _) N
- PrintfLogo(); /* 打印例程名称和版本等信息 */
. G$ c! f6 y% Z! q8 @7 w# ]+ U
+ n" Z# K9 V# A ^+ U2 Q- DemoSpiDac(); /* SPI DAC测试 */
4 N" Y' I- C- }/ C. [ - }
& U. H" \1 i$ c
9 J* h- a$ p' I, \7 T$ w9 f( k- /*2 _9 p8 l6 R1 S+ U
- *********************************************************************************************************
! ?# x: ~4 I# K8 O6 t+ x7 F) P9 u - * 函 数 名: DemoSpiDac) e8 Z; F4 ]& u; T" S
- * 功能说明: DAC8501测试
& j$ U! X& u" s R- e/ H - * 形 参: 无/ A9 G, ]5 I( Q! E( Z
- * 返 回 值: 无
+ \, n0 C7 U/ |0 I; R! J - ********************************************************************************************************* F v2 \3 l) F) \- V
- */7 T! u- A; E. M4 t, t
- void DemoSpiDac(void)1 x* Y: u; \; l6 o
- {
, r3 ^2 c, V0 F - uint8_t i=0;7 ]0 s7 e4 i/ I) H7 U4 I: T& Z
- uint8_t ucKeyCode; /* 按键代码 */
l( a) M" d7 n4 e W. R$ Q: x - ]8 v2 o; @* [
- sfDispMenu(); /* 打印命令提示 */
7 ?) c" R+ h' M3 [ ]
3 q0 `8 w: Q) ?7 C1 B; R1 n- bsp_StartAutoTimer(0, 200); /* 启动1个100ms的自动重装的定时器 */ V$ _9 O7 v$ [2 M5 r) M! M5 }8 i
2 g5 a! [6 M* f8 @1 A: |
* x, H0 m5 \" w+ @9 v p- /* 生成方波数据 */
, Y4 Y2 a) a/ | - MakeSinTable(ch1buf, 100, 0, 65535); s" X- p! c- B, [
! h+ j6 t* ~# g0 H4 Z; `8 h- DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);
6 ]+ d }3 k) W( A9 W) q- t
4 G* |* R- q F$ E! }& n0 t2 M- while(1)$ t' y" O$ z7 ?2 \
- {
; Z+ K5 i* z$ Z* C3 w2 ? - bsp_Idle(); /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */# |/ f9 q4 @" C2 f6 B
, G. i4 F; F1 \! }- /* 判断定时器超时时间 */" _& i/ X7 l9 v5 F9 k$ L4 l
- if (bsp_CheckTimer(0))
' W- [7 _$ R }# [: G& V - {/ I# s* @% S- Q4 P" V
- /* 每隔100ms 进来一次 */ # L" Z5 m* r" v0 h
- bsp_LedToggle(2);+ \3 L: Z$ z# E0 `- H4 m+ j( p. u6 h0 O
- }
% P: G$ Z& `4 p$ b - - `, D. o" L* W& t
- /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */7 V9 m6 x0 z# S" p& r
- ucKeyCode = bsp_GetKey(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */9 t8 U0 q. i9 B
- if (ucKeyCode != KEY_NONE)
. c- P% _3 K% M1 G - {
+ p: j, t+ `) o) S1 U T1 F2 U - switch (ucKeyCode)
! S/ Z% J; [2 S) C0 H - {/ a4 r; L3 V4 \' o" e
- case KEY_DOWN_K1: /* K1键按下,通道1输出方波 */
Z1 [8 b( R' R+ I- S& T B - /* 生成方波数据 */+ \( _$ E& W+ v
- for(i =0; i< 50; i++)
: |! c2 z6 n, ~& M - {
6 h1 L1 X' ~8 h s - ch1buf = 0;" z Y" Z0 o# e& P# Z
- }
+ A7 H4 h! `1 w, B3 { [( G1 a& g - 9 t$ l* [8 C# I9 P- @
- for(i =50; i< 100; i++)
4 o4 Q2 l. w. [' A y; i, J" L - {
- X# W9 I# n5 G- d - ch1buf = 65535;* i0 [; }% t9 Z
- }
, i3 ~" @( r7 ^8 t - \4 n; y' J& o- _
- /* 触发速度1MHz */
3 q/ I1 a4 _" o M R; J - DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);
! R' P7 C0 j8 x - break;
* E# }3 i9 S4 b6 @
7 T5 ~$ B- s7 w, S: B/ G x- case KEY_DOWN_K2: /* K2键按下,通道1输出正弦波 */" i8 W' P! M X8 N! d, y
- /* 生成正弦波数据 */ ( C* s1 ~. Z1 j; G( S
- MakeSinTable(ch1buf, 100, 0, 65535);3 K/ `1 @* y3 {# I8 A( t
- 2 \0 ^$ a* V) X" V
- /* 触发速度1MHz */: y" C" O1 j2 W
- DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000000);8 S+ f6 {7 _! F4 j2 u) y
- break;
% D" s* i) o5 P: [" T. v# A# p
0 J$ v0 o8 E6 R1 R- case KEY_DOWN_K3: /* K3键按下,通道1输出直流 */
+ K7 y% t7 _" ?! |' z
0 m% y% D( b7 n- /* 生成方波数据 */
- T& Q& f% o& R- i - for(i =0; i< 100; i++)
+ o; _2 u0 F# ~6 C - {2 O0 b: n, @1 Y2 h4 C# _& b3 q
- ch1buf = 65535;
' d# N9 w7 l; L7 k- i7 a2 m' F; v - }- x/ m! Y6 O8 h9 ?
& u2 G$ X6 f0 m# `" N" L& L- /* 触发速度1MHz */$ G M2 R% x0 p8 ]& P. @
- DAC8501_SetDacDataDMA(1, ch1buf, sizeof(ch1buf)/sizeof(uint16_t), 1000);, ]1 ^: ~0 o( t G, @
- break;, X. ], Z7 T, C, W o- i- z
1 L, O; F9 O/ b" D) M- default:
. [2 d" J. t' S0 T9 l4 L9 I- L# t - /* 其它的键值不处理 */
' k5 ?' | Q! d7 P3 ^7 |( Q - break;# M# z$ L! b4 b* F" u
- }
8 P2 s' k+ {+ u( c8 ?, u - }9 e& T, i, Y# `$ B: V2 f$ W! ]
- }
, F- V. l+ i0 ]2 Y3 U/ E% T - }; {$ U6 n$ O" ~9 K( C: {- a7 u( _
复制代码
) k/ z; o( T: ~3 a7 O9 P n+ ]8 ~8 _% Q" _$ L+ f k! @# M8 ?
75.15 总结2 I7 X$ {" ?5 f
本章节涉及到的知识点非常多,特别是SPI DMA方式驱动的实现方法,需要大家稍花点精力去研究。6 H& m4 F. J2 G5 n- \
' X% r {+ S/ x5 }3 X% r9 F0 c! H5 c
1 ~! {/ y1 H- P: G( s |