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