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