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