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

MiniPro STM32H750 开发指南_V1.1-DAC实验

[复制链接]
STMCU小助手 发布时间:2022-10-7 21:37
DAC实验) z( p( [% l5 b3 v$ `7 r
本章,我们将介绍STM32H750的DAC(Digital -to- analog converters,数模转换器)功能。我们通过三个实验来学习DAC,分别是DAC输出实验、DAC输出三角波实验和DAC输出正弦波实验。! ]3 S$ W- C/ V& C6 O6 s& J4 K
$ B- p. n  S* \7 `! m
34.1 DAC简介
" I3 ~6 V- x6 }' x6 j0 USTM32H750的DAC模块(数字/模拟转换模块)是12位数字输入,电压输出型的DAC。DAC可以配置为8位或12位模式,也可以与DMA控制器配合使用。DAC工作在12位模式时,数据可以设置成左对齐或右对齐。DAC模块有2个输出通道,每个通道都有单独的转换器。在双DAC模式下,2个通道可以独立地进行转换,也可以同时进行转换并同步地更新2个通道的输出。DAC可以通过引脚输入参考电压Vref+(通ADC共用)以获得更精确的转换结果。
( C5 U! x- r% S5 _) q, sSTM32H750的DAC模块主要特点有:$ [  H# G: n( w0 o
① 2个DAC转换器:每个转换器对应1个输出通道6 S! b& l  D/ N3 A
② 8位或者12位单调输出9 t: z) H' N+ ^. M* H
③ 12位模式下数据左对齐或者右对齐6 N( H  G" c  m
④ 同步更新功能
8 G7 V8 k/ o1 G; c# o⑤ 噪声波形生成; l# W. L( z  T2 P* r* o2 G5 k
⑥ 三角波形生成
% a; d8 Q; x% o⑦ 双DAC双通道同时或者分别转换
* I6 v  O$ K+ m8 D; {⑧ 每个通道都有DMA功能
: K  b" @' L% I. LDAC通道框图如图34.1.1所示:
/ o, e, y5 U; S  A) g
8 x) k/ I2 o% f9 {! } 99faab3c8dc740f0bdf1217f9fed0236.png
/ x9 i: z$ N+ ~4 A6 h
! F) ~: x) T$ D$ U: M图34.1.1 DAC通道框图  G8 E2 k! D  r0 P% ?
图中VDDA和VSSA为DAC模块模拟部分的供电,而VREF+则是DAC模块的参考电压。DAC_OUT1/2就是DAC的两个输出通道了(对应PA4或者PA5引脚)。ADC的这些输入/输出引脚信息如下表所示:2 b6 ]/ e' A3 O5 P1 P* Q' V. B
/ E6 X6 v: [; P6 P: w6 t% e/ @8 W
fe9b31bd38704b7aaf0eb29d1a610a62.png 9 C: A# ]* }/ C' H$ g- q6 ?

1 F2 q2 A  O" e( {0 P表34.1.1 DAC输入/输出引脚% p8 A# Q8 U+ N9 P3 D
除了上表列出的输入/输出引脚,DAC通道框图还有一些内部输入/输出信号,具体如下表所示:
5 l2 h2 R% z1 o% @8 H3 D
1 h' @! o: I7 b; Y! M& | 7ae9c24466b64f0ca6f182996e8e315b.png 9 U" I% u" I+ M% B+ Y( g5 n

' i) E) M2 c# h' F表34.1.2 DAC内部输入/输出信号
& A3 S' m" Z% `* p* u注意:表中的dac_pclk即DAC的时钟源,DA转换和寄存器访问都是靠这个时钟,该时钟来自APB1,通过sys_stm32_clock_init函数配置之后,为120MHz。# Z+ v( K2 b: e9 i" L4 x  [
从图34.1.1可以看出,DAC输出是受DORx(x=1/2,下同)寄存器直接控制的,但是我们不能直接往DORx寄存器写入数据,而是通过DHRx间接的传给DORx寄存器,从而实现对DAC输出的控制。! E3 F  O7 Z  t: K3 N3 d$ |
前面我们提到,STM32H750的DAC支持8/12位模式,8位模式的时候是固定的右对齐的,而12位模式又可以设置左对齐/右对齐。DAC单通道模式下的数据寄存器对齐方式,总共有3种情况,如下图所示:. U; b9 w* O# U

6 x  i7 m! a3 w) N, A& t6 b b2ccd96bab6e41149fec408d04dd5706.png 7 [) s; Y) d+ Q! j6 u
% ^& ]: j% g0 _+ t0 K
图34.1.2 DAC单通道模式下的数据寄存器对齐方式
) E; b2 I/ [+ J+ Y# B①8位数据右对齐:用户将数据写入DAC_DHR8Rx[7:0]位(实际存入DHRx[11:4]位)。
1 T: }; ~* U2 o4 m" V6 H5 ~/ U1 p5 _②12位数据左对齐:用户将数据写入DAC_DHR12Lx[15:4]位(实际存入DHRx[11:0]位)。5 |' x" |8 Q& a
③12位数据右对齐:用户将数据写入DAC_DHR12Rx[11:0]位(实际存入DHRx[11:0]位)。5 [; p) q9 o  n# a
我们本章实验中使用的都是单通道模式下的DAC通道1,采用12位右对齐格式,所以采用第③种情况。另外DAC还具有双通道转换功能。9 T& t9 m+ N6 }! q& Q& v8 S6 ]+ L3 P. B
对于 DAC 双通道(可用时),也有三种可能的方式,如下图所示:7 Y# m! |' d2 v$ o, z/ T# J  K

/ I( g, U6 j& T 855fb53f1f314e2b8fd9cf7bed680013.png 2 o! ]/ F/ [6 Q: R( Y! r
2 z0 A) Z+ m! `  J
图34.1.3 DAC双通道模式下的数据寄存器对齐方式0 J) Z' F) x) ^$ i, h
①8位数据右对齐:用户将DAC通道1的数据写入DAC_DHR8RD[7:0]位(实际存入DHR1 [11:4]位),将DAC通道2的数据写入DAC_DHR8RD[15:8]位(实际存入DHR2 [11:4]位)。5 l' g- b2 B& I
②12位数据左对齐:用户将DAC通道1的数据写入DAC_DHR12LD [15:4]位(实际存入DHR1[11:0]位),将DAC通道2的数据写入DAC_DHR12LD [31:20]位(实际存入DHR2[11:0]位)。0 w3 `' a8 h# d$ K5 ~. y% p( N
③12位数据右对齐:用户将DAC通道1的数据写入DAC_DHR12RD [11:0]位(实际存入DHR1[11:0]位),将DAC通道2的数据写入DAC_DHR12RD [27:16]位(实际存入DHR2[11:0]位)。
8 X; |5 {* P; N0 @: [DAC可以通过软件或者硬件触发转换,通过配置TENx 控制位来决定。
# n' F6 O+ @7 W3 v+ \如果没有选中硬件触发(寄存器DAC_CR1的TENx位置0),存入寄存器DAC_DHRx的数据会在1个APB1时钟周期后自动传至寄存器DAC_DORx。如果选中硬件触发(寄存器DAC_CR1的TENx位置1),数据传输在触发发生以后3个APB1时钟周期后完成。 一旦数据从DAC_DHRx寄存器装入DAC_DORx寄存器,在经过时间tSETTLING之后,输出即有效,这段时间的长短依电源电压和模拟输出负载的不同会有所变化。我们可以从《STM32H750VBT6.pdf》数据手册查到tSETTLING的典型值为1.7us,最大是2us,所以DAC的转换速度最快是588K左右。
- z* R' K, I2 b5 {0 V不使用硬件触发(TEN=0),其转换的时间框图如图34.1.4所示:
) A" z! }) \/ i* M
1 `$ K$ h* f3 n4 A 003738441a0b472cbf97752a24750915.png % w# {* b& W# {, y9 a/ B

0 u/ U1 T  M5 j' N, B图34.1.4 TEN=0时DAC模块转换时间框图$ ~+ b& P9 L( W$ y- j% {9 p
当DAC的参考电压为VREF+的时候,DAC的输出电压是线性的从0~VREF+,12位模式下DAC输出电压与VREF +以及DORx的计算公式如下:' Q& p  F0 p3 p+ }  V
DACx输出电压 = VREF *(DORx/4096)
; M( {: w# W5 \0 b& h) S如果使用硬件触发(TEN=1),可通过外部事件(定时计数器、外部中断线)触发DAC转换。由TSELx[3:0]控制位来决定选择16个触发事件中的一个来触发转换。这16个触发事件如下表所示:0 E6 O, R4 h+ q' g. u. W
. E" K; W$ H& l" D
428f1282feea482d98b916e5b589325b.png $ u; i: ~, _( F$ ?6 w2 Z& E+ B/ s5 i

) ?, g' w: e; B, {  \, x表34.1.3 DAC触发选择
3 n2 x; f' u3 T  x1 I$ C4 |# Y原表见《STM32H7xx参考手册_V3(中文版).pdf》第923页表 208。
; o1 R. U, Z- ~# K4 d  V9 G9 m每个DAC通道都有DMA功能,两个DMA通道分别用于处理两个DAC通道的DMA 请求。如果DMAENx 位置1时,如果发生外部触发(而不是软件触发),就会产生一个DMA 请求,然后DAC_DHRx寄存器的数据被转移到DAC_NORx寄存器。
4 S7 r7 h* Y$ q! {, z34.2 DAC输出实验
( q3 B- ]% K+ g* P本实验我们来学习DAC输出实验。
6 c- |8 r2 F' M34.2.1 DAC寄存器2 c, d7 v5 g5 }9 o2 c4 h
下面,我们介绍要实现DAC的通道1输出,需要用到的一些DAC寄存器。. d' o! @8 I4 F9 E' u9 S$ S, O
DACx控制寄存器(DACx_CR)(x=1或2)+ m+ o$ X0 c9 T6 @1 D5 r$ o
DACx控制寄存器描述如图34.2.1.1所示:
1 S( x2 c& Q( }$ a" D# R& v: k& W" z: U4 H% L
ff1c2b05d4514497867ad3b06aec9fef.png
( K5 ~" o  ~$ i, b* |! y1 N4 c/ }+ A7 G" @
图34.2.1.1 DACx_CR寄存器
( N6 ~# J: ~# _+ ODACx_CR寄存器的低16位用于控制通道1,高16位用于控制通道2,下面介绍本实验需要设置的一些位:& @3 n; v+ F& @3 Y5 l& j
EN1位用于使能/禁止DAC通道1,本实验用到ADC1通道1,所以该位EN1置1。6 z9 c5 m( ]4 ^) j3 v! `' A
TEN1位用于DAC通道1的触发使能,本实验不使用硬件触发,所以该位置0。写入DHR1的值会在1个APB1周期后传送到DOR1,然后输出到PA4口上。+ F( \, `; e& B2 c
TSEL[3:0]位用于选择DAC通道1的触发方式,本实验使用软件触发,所以该位域置0。具体的设置关系详见表34.1.3 DAC触发选择。
; R( q! \) S- Y1 R+ j. A1 EWAVE[1:0]位用于控制DAC通道1的噪声/波形输出功能,默认设置为00,不使能噪声/波形输出。; _; I# R, Z  K0 r7 y
DMAEN1位用于控制DAC通道1的DMA使能,本实验不使能,设置该位为0即可。
; U* B  l/ a4 ~- |- t, P% m1 SCEN1位用于控制DAC通道1的输出缓冲校准使能,本实验不使用校准功能(默认有一个出厂校准值,我们使用默认的校准值即可),设置该位为0即可。
0 O( w1 r4 E+ z* Z# T; jDACx模式控制寄存器(DACx_ MCR)
3 [0 s* g( ^* |: c5 R# Q) p  o1 LDACx模式控制寄存器描述如图34.2.1.2所示:
+ t7 g1 k, ?% b4 J3 T
7 b7 c* [; w1 k2 g+ O) k$ f% y: ?: x f160baeb5bd340358d80bb53b78c6de5.png
$ y. o" E" }( h& i' R; }
; x+ K% Y$ L1 D  j图34.2.1.2 DACx_ MCR寄存器
, Y- P7 h/ E: s7 |' J该寄存器我们只关心MODE1[2:0],这三个位用于设置DAC通道1的工作模式,本实验使用普通模式,且使用输出缓冲,设置MODE1[2:0]=0即可。MODE2[2:0] 设置通道2的工作模式,本实验没用到。
. G% q" o9 X' @8 c5 q5 H+ n/ d, RDACx通道1 12位右对齐数据保持寄存器(DACx_ DHR12R1)) ^! R/ h' K) Y% H
DACx通道1 12位右对齐数据保持寄存器描述如图34.2.1.3所示:
' R0 M, t- ?# \& p0 v( R8 r& D% t+ q4 K  G
487905f6a54c4d89a5db029c5aa93986.png $ Z% t! z: S! X5 F# Q' w
9 U) K( J  n/ ~& V
图34.2.1.3 DACx_ DHR12R1寄存器& y& L- d& u/ w3 I4 @& ]0 X( L
该寄存器用来设置DAC输出,通过写入12位数据到该寄存器,就可以在DAC输出通道1(PA4)得到我们所要的结果。+ d4 o& c4 d" p1 f
34.2.2 硬件设计2 g; I1 _: I7 y8 _$ m
1.例程功能. Q6 s$ M8 m5 X* M" u
使用KEY1/KEY_UP两个按键,控制STM32内部DAC的通道1输出电压大小,然后通过ADC1的通道19采集DAC输出的电压,在LCD模块上面显示ADC采集到的电压值以及DAC的设定输出电压值等信息。也可以通过usmart调用dac_set_voltage函数,来直接设置DAC输出电压。LED0闪烁,提示程序运行。' F8 l, A: \2 d/ m
2.硬件资源9 b- s6 J5 ~  ~5 Q8 y+ D) b4 W
1)RGB灯
3 U) y& m1 E& ]6 H9 tRED : LED0 - PB4+ D  j! W  {% E# C- o# |
2)串口1(PA9/PA10连接在板载USB转串口芯片CH340上面). v) T* e: F  _+ S8 J# x+ z, p
3)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
: j/ t* l4 S; A) q- o1 k3 q# p+ s4)独立按键 :KEY1 - PA15、WK_UP - PA0
6 q+ D/ Q1 [3 Z: Z7 h1 x+ x5)ADC1 :通道19 - PA5
# z* `% ~$ E3 Z: X  e, U: n* b" ~6)DAC1 :通道1 - PA49 C! D% j( h+ w8 V
3.原理图
# ]! b/ t+ ^  \, P7 V8 A" @7 e  T我们来看看原理图上ADC1通道19(PA5)和DAC1通道1(PA4)引出来的引脚,如下图所示:- n. n% G8 A9 e
! P: q* v9 ~  N1 @3 P% e! r! a9 R9 `
5f078194794a44b08226343a4426136e.png
" J6 X3 }7 I4 l& m1 R$ a, E4 V1 x
* t2 |$ {% E; q) \, L0 p图34.2.2.1 ADC和DAC在开发板上的连接关系原理图
' t) T  A; I, V. b' kP3是多功能端口,我们只需要通过跳线帽连接P3的ADC和DAC,就可以使得ADC1通道19(PA5)和DAC1通道1(PA4)连接起来。对应的硬件连接如图34.2.2.2所示:3 ~, L1 t8 X$ I8 b& _5 c" a: i1 G

$ p/ B, N  J2 r0 K" Y$ o& s. X a36e13ae7547431398eb8d0fe1317908.png
: ?- ]' Y/ d% k1 r" t% O6 h' h! O' [* N/ }3 G4 T8 w
图34.2.2.2 硬件连接示意图
. s5 s# ?7 ~6 v$ f1 n+ a4 i1 Y( f6 e8 g
34.2.3 程序设计! w! w4 K; l' f2 ]/ r
34.2.3.1 DAC的HAL库驱动
+ c/ X7 [' ~; m6 H/ L0 J) C4 f! _DAC在HAL库中的驱动代码在stm32h7xx_hal_dac.c和stm32h7xx_hal_dac_ex.c文件(及其头文件)中。3 O( S" f/ C3 c! C
1.HAL_DAC_Init函数* a2 E9 J: R  k- u
DAC的初始化函数,其声明如下:
8 x: {& S; i# u4 _! b, OHAL_StatusTypeDef HAL_DAC_Init(DAC_HandleTypeDef *hdac);
. d0 `  C/ C( I1 {  q% |函数描述:
/ T( i* i8 o4 w$ R用于初始化DAC。
6 R; d( S3 c$ S( \* v" c, y函数形参:8 {4 S1 t5 Z$ p0 l
形参1是DAC_HandleTypeDef结构体类型指针变量,其定义如下:$ S9 |! b9 ^, G
  1. typedef struct
    9 S: f! W! p1 n8 V
  2. {4 O' [" |2 w) u5 d' B1 g  _0 T
  3.   DAC_TypeDef                    *Instance;              /* DAC寄存器基地址 */
    % S# M0 b' g5 d9 q
  4.   __IO HAL_DAC_StateTypeDef   State;                   /* DAC 工作状态 */& q6 z, U1 ]9 _; M/ x
  5.   HAL_LockTypeDef               Lock;                    /* DAC锁定对象 *// r: {1 X" i  Z& ^7 \) h
  6.   DMA_HandleTypeDef             *DMA_Handle1;          /* 通道1的DMA处理句柄指针 */
    3 }& Q( @3 v& M# U8 _
  7.   DMA_HandleTypeDef             *DMA_Handle2;          /* 通道2的DMA处理句柄指针 */. C( E0 D% L  p! @# B; X
  8.   __IO uint32_t                  ErrorCode;             /* DAC错误代码 */6 U9 w) y5 T! f- ?
  9. } DAC_HandleTypeDef;
复制代码

6 l3 F4 J  Z# q5 ~% U3 O从该结构体看到该函数并没有设置任何DAC相关寄存器,即没有对DAC进行任何配置,它只是HAL库提供用来在软件上初始化DAC,为后面HAL库操作DAC做好准备。$ C8 j6 O& B: C  L5 y# T
函数返回值:
1 {$ q5 |' w$ `* a+ `4 C% \HAL_StatusTypeDef枚举类型的值。; F: @: q, @3 b. J
注意事项:
8 o0 Z. B6 X# e% ~) ?( L1 q, ]DAC的MSP初始化函数HAL_DAC_MspInit,该函数声明如下:
" X: n5 L$ b! X0 I& z2 {6 b+ Nvoid HAL_DAC_MspInit(DAC_HandleTypeDef* hdac);- ?2 O8 G/ M! @, r2 u0 {; n
2. HAL_DAC_ConfigChannel函数
  P! _# I2 o& jDAC 的通道参数初始化函数,其声明如下:
, `: N# p8 T4 f) @/ y! E1 b: \, @HAL_StatusTypeDef HAL_DAC_ConfigChannel(DAC_HandleTypeDef *hdac,. h: l# \# a# [0 m; d: [! X( T  ~
DAC_ChannelConfTypeDef *sConfig, uint32_t Channel);
# u3 d8 S- L" i- P$ w函数描述:
* Y( `& M+ _$ e' q# R4 \该函数用来配置DAC通道的触发类型以及输出缓冲。
- K* s) X* O' d" O6 e# r函数形参:/ m7 e* S% Y' U" P' }- F
形参1是DAC_HandleTypeDef结构体类型指针变量。6 t. y) Q$ p+ U0 @5 J% ?
形参2是DAC_ChannelConfTypeDef结构体类型指针变量,其定义如下:9 H  `1 \  U7 H$ F
/ [( t- ^( x: i) w! y* g5 {, @  g- h) X
  1. typedef struct# k, e; }) y& k, D$ [6 R7 t: J
  2. {/ L' B& S6 h3 a5 x1 v
  3.   uint32_t DAC_SampleAndHold;          /* 设置是否使能低功耗模式,即采样和保持模式 *// d4 x3 \  N* k! l
  4.   uint32_t DAC_Trigger;                 /* DAC触发源的选择 */
      h$ H! x0 l# J- |5 ^/ a' |2 V/ W
  5.   uint32_t DAC_OutputBuffer;           /* 启用或者禁用DAC通道输出缓冲区 */
    ) Q  ~" L: y' e* ^0 g& Z
  6.   uint32_t DAC_ConnectOnChipPeripheral; /* 指定DAC输出是否连接到片上外设 */& G# f. d& ]5 _# Z6 ?
  7.   uint32_t DAC_UserTrimming;           /* 设置DAC的校准方式,采用出厂模式还是用户模式 */
    & ~, [/ N% y+ G: f4 I
  8.   uint32_t DAC_TrimmingValue;          /* 设置用户校准模式的偏移值 */$ m3 h/ H% ^: B+ F, D
  9.   DAC_SampleAndHoldConfTypeDef  DAC_SampleAndHoldConfig; /* 设置采样保持具体参数 */
    8 I% u2 J% K  A% Z+ m
  10. } DAC_ChannelConfTypeDef;
复制代码
1 K" W& j4 d! f& ]5 a# [1 j
形参3用于选择要配置的通道,可选择DAC_CHANNEL_1或者DAC_CHANNEL_2。
- i3 U4 e$ w' L$ W- C4 Q/ G' u1 ^函数返回值:9 Y' H2 N5 P0 Q- h
HAL_StatusTypeDef枚举类型的值。0 ^# \8 A( l* H" P
3. HAL_DAC_Start函数) ~# j9 X  Y. a  Y+ Q6 Z( V& d
使能启动DAC转换通道函数,其声明如下:! X* a5 n: Y1 p! {/ a; N# |, R5 M
HAL_StatusTypeDef HAL_DAC_Start(DAC_HandleTypeDef *hdac, uint32_t Channel);
% ?2 X3 {6 n: l3 P; w/ W' g2 o9 ^函数描述:
1 T2 ]) ?3 @& U) h* K0 o( `使能启动DAC转换通道。
7 e2 f! Y- |. L函数形参:
+ ~; z3 g1 y! w5 M* D形参1是DAC_HandleTypeDef结构体类型指针变量。
* Y( X9 |3 X; j# x形参2用于选择要启动的通道,可选择DAC_CHANNEL_1或者DAC_CHANNEL_2。
' D' u# v4 G. ]6 o5 l函数返回值:
0 T4 \5 y5 ^# @8 c9 Y6 HHAL_StatusTypeDef枚举类型的值。
2 u/ p' g- N/ a, y# P/ n4. HAL_DAC_SetValue函数& n9 @* d8 f3 R/ \
DAC的通道输出值函数,其声明如下:% Q9 x0 ?+ e4 L2 W# x& ?
HAL_StatusTypeDef HAL_DAC_SetValue(DAC_HandleTypeDef *hdac, uint32_t Channel,+ C7 W' S' @& Q5 E
uint32_t Alignment, uint32_t Data);7 J  S0 q) ~; P& m. c1 d' C' q' m
函数描述:
7 S4 x0 f0 Z. e- h配置DAC的通道输出值。5 e4 H# _& E+ S
函数形参:
6 B+ T" g. _7 e7 V! z% X; }5 v1 e1 x形参1是DAC_HandleTypeDef结构体类型指针变量。) M' B: S  T. \/ m1 S  @% r3 c
形参2用于选择要输出的通道,可选择DAC_CHANNEL_1或者DAC_CHANNEL_2。  m2 w/ r; g$ q  X4 A2 o9 S8 r% A
形参3用于指定数据对齐方式。
; Y& }0 P  I% e; e1 k形参4设置要加载到选定数据保存寄存器中的数据。* c) \# P& m. J5 }$ p' K4 @
函数返回值:# d" Y8 j. [' H+ \2 Y4 e2 i) c( u0 B2 b
HAL_StatusTypeDef枚举类型的值。/ n& e3 q1 z( f
5. HAL_DAC_GetValue函数% f6 G$ w. ~- C3 T0 ^0 F
DAC读取通道输出值函数,其声明如下:
/ o! q  B  D3 f" N2 @$ q2 m( guint32_t HAL_DAC_GetValue(DAC_HandleTypeDef hdac, uint32_t Channel);+ X3 V. Z) v2 }% T5 B( J+ m
函数描述:) @+ s2 `: j% x4 f/ x/ s
获取所选DAC通道的最后一个数据输出值。2 f$ S; `' B3 s6 c; x2 P  s
函数形参:5 u2 |4 F( a7 E/ k
形参1是DAC_HandleTypeDef结构体类型指针变量。5 K  w0 N) {. C6 G% w
形参2用于选择要读取的通道,可选择DAC_CHANNEL_1或者DAC_CHANNEL_2。
' E6 t, T: t; H  l* c$ g6 e函数返回值:
, b$ U& }2 J! t9 Y+ a获取到的输出值。
1 A; f0 Q' B0 \9 J  \6 K' z- DDAC输出配置步骤
) y3 |! W: t8 V1)开启DACx和DAC通道对应的IO时钟,并配置该IO为模拟功能* E4 `# f# [2 {) A6 O
首先开启DACx的时钟,然后配置GPIO为模拟模式。本实验我们默认用到DAC1通道1,对应IO是PA4,它们的时钟开启方法如下:7 S; x' P9 \6 z- ^" n
__HAL_RCC_DAC12_CLK_ENABLE (); / 使能DAC1时钟 /& v& v* I; G7 s5 F/ k9 s5 J+ u
__HAL_RCC_GPIOA_CLK_ENABLE(); / 开启GPIOA时钟 */
; m* U6 h" G6 q2)初始化DACx
  a% }# R# i8 s通过HAL_DAC_Init函数来设置需要初始化的DAC。该函数并没有设置任何DAC相关寄存器,也就是说没有对DAC进行任何配置,它只是HAL库提供用来在软件上初始化DAC。
- g  G: P! x+ ~  Y6 X注意:该函数会调用HAL_DAC_MspInit函数来存放DAC和对应通道的IO时钟使能和初始化IO等代码。
7 z% }2 m5 I' t: r' p9 m  M1 C3)配置DAC通道并启动DA转换器
6 I( s# U& a& v' ^在HAL库中,通过HAL_DAC_ConfigChannel函数来设置配置DAC的通道,根据需求设置触发类型以及输出缓冲。
. q+ T! U4 f* r/ }( G1 }. Y( d配置好DAC通道之后,通过HAL_DAC_Start函数启动DA转换器。9 E6 c2 P% Z4 O, \# i4 g
4)设置DAC的输出值& J; @- |/ g' ~/ H
通过HAL_DAC_SetValue函数设置DAC的输出值。
) P& Z; \. p/ a( Q6 D. v( d34.2.3.2 程序流程图
* q: O9 j) \0 x- q2 t
( @1 m$ g+ G5 ~; |) ^  m 14324d67839147a3997a9b59e727e980.png
# D' V  n5 L, o1 I7 c6 Z" A! L' c/ y) ~
图34.2.3.2.1 DAC输出实验程序流程图1 R" p' i8 c$ u
34.2.3.3 程序解析' o2 }% \) _& m
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。DAC驱动源码包括两个文件:dac.c和dac.h。, ?" l* i6 l9 _6 R) \  ^! h/ k. `
dac.h文件只有一些声明,下面直接开始介绍dac.c的程序,首先是DAC初始化函数。
8 W; c# k$ W5 [! F1 V" s
% `. F% w" y! I) C+ c+ Y
  1. /**3 s0 T6 Q# B/ N  W* B
  2. * @brief       DAC初始化函数1 w: f1 L- f8 c- q
  3. *   @note      本函数支持DAC1_OUT1/2通道初始化4 s# ]' ?+ F' ]7 s4 U% y2 [/ |; _* y7 g
  4. *       DAC的输入时钟来自APB1, 时钟频率=120Mhz=8.3ns
    4 z! }* l) F7 J/ U; o
  5. *       DAC在输出buffer关闭的时候, 输出建立时间: tSETTLING = 2us (H750数据手册有写)8 w, n2 ?0 g' L/ L4 v. \! ^
  6. *       因此DAC输出的最高速度约为:500Khz, 以10个点为一个周期, 最大能输出50Khz左右的波形5 Y: ^5 {% c; O* ]
  7. *
    * ]4 ^: T! O$ Q( L. v& a( I3 S7 e0 S
  8. * @param     outx: 要初始化的通道. 1,通道1; 2,通道2
    * c' ]5 Z, l/ J7 @" {2 Z0 t
  9. * @retval    无
    ! w/ [6 c' u( ]8 l$ g. F! s8 P% t; X
  10. */
    . d$ N$ x" X- x" o- Y0 `; g
  11. void dac_init(uint8_t outx)
    # Q1 X, `! T# E
  12. {
    % O  p' x/ _6 h! G# t7 V9 _
  13.     DAC_ChannelConfTypeDef dac_ch_conf;     /* DAC通道配置结构体 */: O- i% v8 Q& }0 f4 Q, i
  14.     GPIO_InitTypeDef gpio_init_struct;/ I, X: B; }) _  O; e. e+ d, n
  15.     __HAL_RCC_DAC12_CLK_ENABLE();/* 使能DAC12时钟,本芯片只有DAC1 */
    ) v; I& ]7 V0 u0 Z' `3 H4 I
  16.     __HAL_RCC_GPIOA_CLK_ENABLE();/* 使能DAC OUT1/2的IO口时钟(都在PA口,PA4/PA5) */
    5 u5 Q0 u: C/ b# s
  17. * }1 P% q1 b- J
  18. /* STM32单片机, 总是PA4=DAC1_OUT1, PA5=DAC1_OUT2 */; |/ l% f4 o: G. J% l! J
  19.     gpio_init_struct.Pin = (outx==1)? GPIO_PIN_4 : GPIO_PIN_5;  
    $ i4 w9 a% Y$ i" J
  20.     gpio_init_struct.Mode = GPIO_MODE_ANALOG;               /* 模拟 */
    * I, [! a& @+ w9 Z
  21.     HAL_GPIO_Init(GPIOA, &gpio_init_struct);: [9 K4 @, s1 V: L; k* n

  22. ( d8 |, \4 P% X! z5 B
  23.     g_dac_handle.Instance = DAC1;                             /* DAC1寄存器基地址 */- P' F, Z; e# }: a1 z+ v
  24.     HAL_DAC_Init(&g_dac_handle);                              /* 初始化DAC */
    - {, t8 L: }" O& C+ v6 [
  25. & C4 U) p. f& _  b! f8 J& B1 O
  26.     dac_ch_conf.DAC_Trigger = DAC_TRIGGER_NONE;               /* 不使用触发功能 */( B% m, T& a/ L/ c
  27.     dac_ch_conf.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE;/* DAC1输出缓冲关闭 */
    2 ~1 i3 r/ U. e/ T: x
  28.     switch(outx)
    & S7 j4 I2 ?/ e6 }6 e/ ]! _
  29.     {
    : z% U- K  ~& z. W! b1 q0 P9 N
  30.         case 1 :5 Z6 V! t  S$ Y5 ~
  31. /* DAC通道1配置 */' A8 I1 {) G' ~9 J5 R
  32.             HAL_DAC_ConfigChannel(&g_dac_handle, &dac_ch_conf, DAC_CHANNEL_1);
    ( j  R* V2 x) }# A- u( j
  33.             HAL_DAC_Start(&g_dac_handle, DAC_CHANNEL_1);    /* 开启DAC通道2 */
    ) G8 h- Q, A% s0 A) r" z6 |
  34.             break;
    ( ^) y& B+ O+ z! x8 [& r9 ?. e9 Y8 F
  35.         case 2 :
    : ?% Z! t7 C  C& [$ \# \) u
  36. /* DAC通道1配置 */
    ! Q! _# O& }2 m2 J4 M3 S3 y  f; W
  37.             HAL_DAC_ConfigChannel(&g_dac_handle, &dac_ch_conf, DAC_CHANNEL_2);
    & v& P3 j* f( b0 z: F
  38.             HAL_DAC_Start(&g_dac_handle, DAC_CHANNEL_2);    /* 开启DAC通道2 */4 ^# u. p( u( N' m4 X* ]
  39.             break;5 c4 p+ A# z' V8 A0 \  x  h7 N% S
  40.         default : break;- Z- C9 W/ d$ d* c
  41.     }1 S- j$ l% t, j9 f, j; r
  42. }
复制代码

: n- Y. M9 P6 v& M' ]3 |- e2 W该函数主要调用HAL_DAC_Init和HAL_DAC_ConfigChannel函数初始化DAC,并调用HAL_DAC_Start函数使能DAC通道。HAL_DAC_Init函数会调用HAL_DAC_MspInit回调函数,该函数用于存放DAC和对应通道的IO时钟使能和初始化IO等代码。本实验为了让dac_init函数支持DAC的OUT1/2两个通道的初始化,就没有用到该函数。* P9 x+ T6 l' U! G1 `: ^& S5 F
下面是设置DAC通道1/2输出电压函数,其定义如下:3 z1 o) g3 [1 \) Q

( v- \$ B, q' W  A5 D, {2 V. T! J
  1. /**
    $ s" p6 @( ~3 D# j
  2. * @brief       设置通道1/2输出电压# h* ^, P4 a( W8 @
  3. * @param       outx: 1,通道1; 2,通道2
    4 N% x. E0 \5 M# }" t
  4. * @param       vol : 0~3300,代表0~3.3V
    9 n* b8 M# g  J; X
  5. * @retval      无( V0 {; T, z5 H+ Q3 n
  6. */
    5 ~/ H7 t  k2 H  I+ E
  7. void dac_set_voltage(uint8_t outx, uint16_t vol)
    7 h+ n# m! u- a
  8. {$ ~3 n5 G( M3 F4 N- e1 X
  9.     double temp = vol;4 F/ ?% T) [4 h% T; n# u! A; w
  10.     temp /= 1000;
      S0 B. l4 C# q
  11.     temp = temp * 4096 / 3.3;8 f, c: G, x7 ]; @
  12.     if (temp >= 4096)temp = 4095;   /* 如果值大于等于4096, 则取4095 */
    2 T: S# a7 J$ Y
  13.     if (outx == 1)   /* 通道1 */
    6 c# f" j0 \$ t+ V1 e% x
  14. {1 O+ y9 |! I* M6 k. }, E
  15. /* 12位右对齐数据格式设置DAC值 */* I2 l& v( o( b& p; Y
  16.         HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, temp); ( [, ?( ^: r/ k0 L
  17.     }! E  ^" Y& l, r: S, d3 o
  18.     else             /* 通道2 */
    * s- ^& W7 R; O' [# `' W
  19. {
    * g0 R! a& m9 c) k- I* _, F7 C7 v& M
  20. /* 12位右对齐数据格式设置DAC值 */7 S2 ^+ L/ m1 E8 [
  21.         HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_2, DAC_ALIGN_12B_R, temp);
    " m, z4 j* L6 p
  22.     }
    7 |' p& C1 k5 ~
  23. }
复制代码

3 U. T, ^" L0 U! F该函数实际就是将电压值转换为DAC输入值,形参1用于设置通道,形参2设置要输出的电压值,设置的范围:03300,代表03.3V。
% Q  b8 j; p8 T" a1 h$ E% P  \最后在main函数里面编写如下代码:
2 u- ]. J& _. h- B& H/ p9 [
  m5 q  B- e5 K. H* _
  1. int main(void)$ q, l: k( B, p6 u
  2. {
    $ g, {: J; ~6 }% e
  3.     uint16_t adcx;. `/ P' R0 |: r- \1 u& o, |6 M
  4.     float temp;: N9 q) p1 g0 K6 G# X, H
  5.     uint8_t t = 0;
    ) n" \7 ^  Y3 S: Y" d
  6.     uint16_t dacval = 0;0 e/ I/ g3 O" {
  7.     uint8_t key;( g+ v" h+ A) @9 D# M

  8. 6 l7 \2 z3 p: e
  9.     sys_cache_enable();                            /* 打开L1-Cache */& x+ V$ u. ^, E4 b& A% r
  10.     HAL_Init();                                      /* 初始化HAL库 */4 C- `7 A' c3 T/ J; }5 m* i9 n
  11.     sys_stm32_clock_init(240, 2, 2, 4);         /* 设置时钟, 480Mhz */# P0 Y4 s6 K, ^8 k  o5 i0 Z
  12.     delay_init(480);                                /* 延时初始化 */
    1 {9 b& R+ o- Z5 ^
  13.     usart_init(115200);                            /* 串口初始化为115200 */2 [% \  A6 |1 i( G
  14.     usmart_dev.init(240);                          /* 初始化USMART */
    ; E) |' c- K4 u' ~: X; p6 Q! Q* n
  15.     mpu_memory_protection();                      /* 保护相关存储区域 */! }% X; Q: Y3 X/ J9 N
  16.     led_init();                                         /* 初始化LED */  S7 n- t- R1 M& j; _6 f% M# w
  17.     lcd_init();                                         /* 初始化LCD */2 |( z9 j% Y+ ]! Q" h
  18.     key_init();                                         /* 初始化按键 */
      ~/ o# w! `% v. g7 }$ x
  19.     adc_init();                                         /* 初始化ADC */
    $ W5 M# }: Y8 E
  20.     dac_init(1);                                                /* 初始化DAC1_OUT1通道 */
    8 @7 ^2 v1 C' ~  G- X5 x7 s* h* S) `! k# x
  21.     lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);% Z4 j" W' y" s/ p! t7 w% N
  22.     lcd_show_string(30, 70, 200, 16, 16, "DAC TEST", RED);, f% A% {+ S! @1 D
  23.     lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    1 T% A3 ?: p& G: F% v" d5 T# a9 H6 [6 b
  24.     lcd_show_string(30, 110, 200, 16, 16, "WK_UP:+  KEY1:-", RED);
    1 v( j1 U0 M9 O9 w; y$ ?& R7 n

  25. / x7 v( X6 h. V& V* t% e( z" ^
  26.     lcd_show_string(30, 150, 200, 16, 16, "DAC VAL:", BLUE);6 `; O! g% {  b( }* q/ m* ^
  27.     lcd_show_string(30, 170, 200, 16, 16, "DAC VOL:0.000V", BLUE);
    2 C; p6 y2 T/ N* T) b
  28.     lcd_show_string(30, 190, 200, 16, 16, "ADC VOL:0.000V", BLUE);
    3 ]* q8 Z+ v4 ~% d8 X
  29. /* 初始值为0 */
    $ E$ a* V$ f1 k! {# I( \
  30.     HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 0);  ~2 X2 W  c9 M/ D! Z7 Y  s
  31.     while (1). D- w! G. O+ l. j
  32.     {
    2 _* Z) X( e( o! ^8 {
  33.         t++;; \9 ^- b/ j3 g+ C9 j+ O, Q
  34.         key = key_scan(0);          /* 按键扫描 */9 K: F1 U1 S" E! Y; ?! ?
  35.         if (key == WKUP_PRES)$ o& {6 g, i  D
  36.         {2 n2 C: Y* B# c  w* ?' _
  37.             if (dacval < 4000)dacval += 200;
    1 ~* M: n/ J2 R$ T) r; G0 W
  38.             HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, $ ?! Q& I$ b9 r9 s, L
  39. dacval);/* 输出增大200 */' n" ?  m' B8 R9 X2 u8 G: c
  40.         }& x3 F* H( H$ i  ~# _" K- y; x, k
  41.         else if (key == KEY1_PRES)
    5 Y: v4 C9 t1 t4 q- q. Q
  42.         {7 u  _+ d% U9 n/ c) q
  43.             if (dacval > 200)dacval -= 200;9 ~* y& Q" g, H, U8 j" X
  44.             else dacval = 0;
    * p) ^4 @) X% \3 v& W, q; |
  45.             HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 6 ]8 Q9 F5 W! E2 `+ S% r
  46. dacval);/* 输出减少200 */1 u' }* t* W1 W" a. ^3 k9 A) n& a4 `
  47.         }
    * ^' R! D2 r/ Q
  48. /* WKUP/KEY1按下了,或者定时时间到了 */8 l' w2 `5 S2 E* ]; G- b
  49.         if (t == 10 || key == KEY1_PRES || key == WKUP_PRES)   
    1 ^0 G& X% ^4 D, S  U
  50.         {! H6 w! H2 d. H0 P) x% w( _
  51. /* 读取前面设置DAC1_OUT1的值 */" @/ z$ L4 N" `; E2 F" z
  52.             adcx = HAL_DAC_GetValue(&g_dac_handle, DAC_CHANNEL_1);2 s& f; A  Z4 r
  53.             lcd_show_xnum(94, 150, adcx, 4, 16, 0, BLUE);           /* 显示DAC寄存器值 */
    4 Z' r. `# R3 a
  54.             temp = (float)adcx * (3.3 / 4096);                        /* 得到DAC电压值 */
    ) F% B# [# _( O1 v; \
  55.             adcx = temp;, l0 I% A3 ?. \/ H; \1 c! d+ s- C
  56.             lcd_show_xnum(94, 170, temp, 1, 16, 0, BLUE);          /* 显示电压值整数部分 */' B7 d% l4 V1 m  Z2 T. a, r
  57.             temp -= adcx;' k  H2 a2 g# ~* I( |1 J5 e
  58.             temp *= 1000;
    , S' L, P; K8 }) T  {
  59.             lcd_show_xnum(110, 170, temp, 3, 16, 0X80, BLUE);/*显示电压值的小数部分*/
    . h+ x2 {4 N9 b& ~$ D* m8 n
  60.             /* 得到ADC通道19的转换结果 */2 B) |: W/ b9 j# w
  61.             adcx=adc_get_result_average(ADC_ADCX_CHY, 10); 7 Q  [7 x: R$ A0 ?8 X
  62.             temp = (float)adcx * (3.3 / 65536); /* 得到ADC电压值(adc是16bit的) */
    & H5 t! _! C6 i0 e& D; P
  63.             adcx = temp;  g& v: M8 i! v8 q
  64.             lcd_show_xnum(94, 190, temp, 1, 16, 0, BLUE); /* 显示电压值整数部分 */. z5 C# Z& S9 g: M
  65.             temp -= adcx;4 _6 t" X% x) T; r1 e+ _3 I! a+ l
  66.             temp *= 1000;- K  p& Q# w! X) L8 s
  67.             lcd_show_xnum(110, 190, temp, 3, 16, 0X80, BLUE);/*显示电压值的小数部分*/# ]+ N# B7 b( B9 P/ C
  68.             LED0_TOGGLE();  /* LED0闪烁 */
    " r9 ~- X4 y( I- ?( `$ m
  69.             t = 0;' m  W) l  s) x8 t
  70.         }
    - _+ b1 H& U. N+ n
  71.         delay_ms(10);+ u0 I# g$ n; y0 |: }+ n! _
  72.     }0 g, W# G2 l* l. X9 ]* H& ?1 O
  73. }
复制代码
/ |( l% |4 R4 l: v
此部分代码,我们通过KEY_UP(WKUP按键)和KEY1(也就是上下键)来实现对DAC输出的幅值控制。按下KEY_UP增加,按KEY1减小。同时在LCD上面显示DHR12R1寄存器的值、DAC设置输出电压以及ADC采集到的DAC输出电压。
$ o0 f# j7 }- ~+ k) ]* n" r
+ p" @, r/ J' a  P  D: }# h- A34.2.4 下载验证* M3 O  |& f& O3 a, ^! R
下载代码后,可以看到LED0不停的闪烁,提示程序已经在运行了。LCD显示如下图所示:
% I3 o: q' Y* _; w$ c2 Z. O4 ]# K' H: Y  }$ x8 }
cfd3207df7954a18807d247b29080386.png & [+ |) Y6 q$ O2 z/ I
; e3 Q. |5 a# n6 T* Z) y
图34.2.4.1 DAC输出实验测试图
) Q1 |- c  U  y$ f. ]$ p验证试验前记得先通过跳线帽连接P3的ADC和DAC,然后我们可以通过按WK_UP按键,增加DAC输出的电压,这时ADC采集到的电压也会增大,通过按KEY1减小DAC输出的电压,这时ADC采集到的电压也会减小。
( ~# D& V% M# V$ H, A: K1 m. i除此之外,我们还可以通过usmart调用dac_set_voltage函数,来直接设置DAC输出电压。2 l# D$ `- p7 I0 ]$ }
) `! y* [- A$ u* V% g9 \+ T& G
34.3 DAC输出三角波实验+ r4 G" b+ {0 S* Q" x' b5 i
本实验我们来学习使用如何让DAC输出三角波,DAC初始化部分还是用DAC输出实验的,所以做本实验的前提是先学习DAC输出实验。7 k- l' F5 `# V4 d
34.3.1 DAC寄存器3 g" B9 }; {% H# k/ D
本实验用到的寄存器在DAC输出实验都有介绍。1 j" I" t0 z" z6 H- t# W) r! G5 G
34.3.2 硬件设计5 }5 T& e3 M, W2 A# T3 d* B
1.例程功能
4 D' m* l% `0 H& t9 h使用DAC输出三角波,通过KEY0/KEY1两个按键,控制DAC1的通道1输出两种三角波,需要通过示波器接PA4进行观察。也可以通过usmart调用dac_triangular_wave函数,来控制输出哪种三角波。LED0闪烁,提示程序运行。5 h, R* W5 ^; N& J* \, I
2.硬件资源# m/ S% ?: ]: b0 C, v3 ]* `
1)RGB灯
6 ^, s( a5 z/ I: x! CRED : LED0 - PB4: f& E) J+ J! q: ]
2)串口1(PA9/PA10连接在板载USB转串口芯片CH340上面)
. n2 C2 ]0 H! q# g' g5 V2 y3)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
* ~$ `, a) ^  M0 u2 b8 K4)独立按键 :KEY0 - PA1、KEY1 - PA15& H& ]; u  B( K
5)DAC1 :通道1 - PA4
9 G- h8 C9 `  Y* J& \% F3.原理图4 s; d( N2 n& y3 Y8 G
我们只需要把示波器的探头接到DAC1通道1(PA4)引脚,就可以在示波器上显示DAC输出的波形。PA4在P3多功能端口的DAC标志排针已经引出,硬件连接如图34.3.2.1所示:
) d) {+ A3 S" e4 o在这里插入图片描述
: h$ g+ x. J" _9 q  g0 q7 `" U6 b* {1 j
3b67bcbe769d401e97ad85b334751550.png
8 L0 l/ c" Q1 p0 e; K/ k5 C) T& o" Z3 O& r0 Y9 @; o- `; M( ?3 }0 h1 K
图34.3.2.1 硬件连接示意图
7 }$ i3 m9 F) c* s34.3.3 程序设计
7 x4 y' D3 j* U. \% Z& g! E% P本实验用到的DAC的HAL库API函数前面都介绍过,具体调用情况请看程序解析部分。下面介绍DAC输出三角波的配置步骤。) ^: R3 h( T; x, E, B" N
DAC输出三角波配置步骤
7 M6 T; _0 Z  I5 f. Q/ n1)开启DACx和DAC通道对应的IO时钟,并配置该IO为模拟功能' o" j' u" ]2 v" O/ p7 q
首先开启DACx的时钟,然后配置GPIO为模拟模式。本实验我们默认用到DAC1通道1,对应IO是PA4,它们的时钟开启方法如下:- A) e4 p( `0 m8 s
__HAL_RCC_DAC12_CLK_ENABLE (); /* 使能DAC1时钟 // l% p2 Q9 A5 j* A5 g7 o1 B
__HAL_RCC_GPIOA_CLK_ENABLE(); / 开启GPIOA时钟 */
$ i1 H: V: C& M% a3 y3 j/ w4 X, D4 ]9 R2)初始化DACx
; b. _( L, U5 X# W4 x* O# D通过HAL_DAC_Init函数来设置需要初始化的DAC。该函数并没有设置任何DAC相关寄存器,也就是说没有对DAC进行任何配置,它只是HAL库提供用来在软件上初始化DAC。- ]+ r: }- X1 T8 g. S. o  w
注意:该函数会调用HAL_DAC_MspInit函数来存放DAC和对应通道的IO时钟使能和初始化IO等代码。( U; a; e6 G: C- ]% l9 Y9 U6 e! F, P
3)配置DAC通道并启动DA转换器- c7 x6 T9 R  K; f# F, D! ]6 P' ~
在HAL库中,通过HAL_DAC_ConfigChannel函数来设置配置DAC的通道,根据需求设置触发类型以及输出缓冲。2 {  \! y7 c; o' l  N
配置好DAC通道之后,通过HAL_DAC_Start函数启动DA转换器。; V* V8 x/ X/ |. L* O$ E
4)设置DAC的输出值& \! ]7 }7 G  X: W6 _$ }) N* [2 c
通过HAL_DAC_SetValue函数设置DAC的输出值。这里我们根据三角波的特性,创建了dac_triangular_wave函数用于控制输出三角波。; K# m- H3 q7 e6 i) m+ p
34.3.3.1 程序流程图
/ y" ?/ [( ?0 I2 p6 s
# P) F+ L6 s' x# i2 E fe57b0b459184445aa4f8b2b4fd76beb.png 2 ]8 j) ]7 |4 ?/ b

9 E$ {" U& d) z' N& ~' T) P9 F图34.3.3.1.1 DAC输出三角波实验程序流程图
( C( G6 @; ^6 n! C  a) a1 x+ ]34.3.3.2 程序解析# I# p/ \: u* l. K
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。DAC驱动源码包括两个文件:dac.c和dac.h。$ C* m! z2 J6 H4 F$ [0 g# Z
dac.h文件只有一些声明,下面直接开始介绍dac.c的程序,本实验的DAC初始化我们还是用到dac_init函数,就添加了一个设置DAC_OUT1输出三角波函数,其定义如下:
- H, F8 z, D" Y
( f" o3 X+ R, G
  1. /**
    7 a3 |1 O3 ~0 U4 b! s1 O
  2. * @brief     设置DAC_OUT1输出三角波" r8 b" U0 K3 T! a5 e; x
  3. *   @note     输出频率 ≈ 1000 / (dt * samples) Khz, 不过在dt较小的时候,比如( z; H5 U( y$ J0 u
  4. 小于5us时, 由于delay_us本身就不准了(调用函数,计算等都需要时间,延时
    - R- y* ~" ?! `% m5 I' G, H- Z3 B
  5. 很小的时候,这些时间会影响到延时), 频率会偏小.5 K* j* S$ ?! e
  6. *
    % w& F7 ^# z1 ]  ?
  7. * @param     maxval : 最大值(0 < maxval < 4096), (maxval + 1)必须大于等于samples/2& v4 ~; p) h, H2 I' q
  8. * @param     dt     : 每个采样点的延时时间(单位: us): Z1 X0 h3 b. t, L* x" ?/ r
  9. * @param     samples: 采样点的个数, samples必须小于等于(maxval + 1) * 2 ,
    , s; H- a" ?1 a) T: b# g1 g3 b2 H
  10. 且maxval不能等于0  v$ }* y9 U4 a7 ~, h
  11. * @param     n      : 输出波形个数,0~65535! d  S; R6 L% F/ \, V8 d
  12. *9 o; Z/ ]5 H% W
  13. * @retval   无) |( ]& x- G( a$ @* k0 d% j6 Q
  14. */
    ! Q# E# T, X7 R7 e; g3 d: U
  15. void dac_triangular_wave(uint16_t maxval, ; y+ D, w; c5 _
  16. uint16_t dt, uint16_t samples, uint16_t n)
    , ]" p! f* v9 Q6 Y. W
  17. {5 o% e/ q' m1 |6 _# {) L* P2 M
  18.     uint16_t i, j;
    ; [0 R( C# s) g6 Q0 G' w, r
  19.     float incval;           /* 递增量 */+ o. A4 [, y5 s1 R8 i* e
  20.     float Curval;           /* 当前值 */% p+ P7 [  l% ^9 k3 T3 c( p

  21. ( n! ?4 d& Y! A7 ?& o$ x
  22.     if((maxval + 1) <= samples)return ;             /* 数据不合法 */
    8 z3 W1 D: c7 {8 k0 i
  23.     incval = (maxval + 1) / (samples / 2);         /* 计算递增量 */" J4 L, t7 O( V
  24.     for(j = 0; j < n; j++)
    4 Y6 ?' {! k  D
  25. {
    7 L/ z& b  v: K) g9 s- p# M5 A! ~
  26. /* 先输出0 */6 }, {" {" D) G& c( c8 L& i" }: V
  27.         HAL_DAC_SetValue(&g_dac_handle,DAC_CHANNEL_1,DAC_ALIGN_12B_R,Curval);   
    4 b$ Z8 L# J2 O) Q
  28.         for(i = 0; i < (samples / 2); i++)          /* 输出上升沿 */
    ) a" r+ D( p, b( [
  29.         {
    0 \' Y. ], D# d. {
  30.             Curval  +=  incval;                         /* 新的输出值 */4 M7 w: b7 [( V% n% f
  31. /* 用寄存器操作波形会更稳定 */7 K' ~& ^7 l6 x/ p$ Y
  32.             HAL_DAC_SetValue(&g_dac_handle,DAC_CHANNEL_1,DAC_ALIGN_12B_R,Curval);  ! ]1 H/ n4 q. h* r
  33.             delay_us(dt);* @1 j. ^7 K" `4 _  C: b9 \6 d
  34.         }
    $ n, M$ s2 c0 }. W, w
  35.         for(i = 0; i < (samples / 2); i++)          /* 输出下降沿 */; J8 B( K1 e2 d; f3 n' A3 s6 h
  36.         {' x8 L1 q# j6 b% f# E
  37.             Curval  -=  incval;                        /* 新的输出值 */, N9 t1 h* b- ]* e* G
  38. /* 用寄存器操作波形会更稳定 */
    # |- f6 _. \! h1 F* [" k+ Q
  39.             HAL_DAC_SetValue(&g_dac_handle,DAC_CHANNEL_1,DAC_ALIGN_12B_R,Curval);  
    " I1 H& R0 P$ [$ V5 J/ X
  40.             delay_us(dt);3 H, a, P( @. h3 m& o: h
  41.         }
    4 A; ]2 Z/ X5 p% |* Q, i" o
  42.     }
    ' B2 R- H, V$ `8 H& X
  43. }
复制代码
; R. m" _% m( b9 M
该函数用于设置DAC通道1输出三角波,输出频率 ≈ 1000 / (dt * samples) Khz,形参意义在源码已经有详细注释。该函数中,我们使用HAL_DAC_SetValue函数来设置DAC的输出值,这样得到的三角波在示波器上可以看到。如果有跳动现象(不平稳),是正常的,因为调用函数,计算等都需要时间,这样就会导致输出的波形是不太稳定的。越高性能的MCU,得到的波形会越稳定。而且用HAL库函数操作效率没有直接操作寄存器高,所以可以像寄存器版本实验一样,直接操作DHR12R1寄存器,得到的波形会相对稳定些。
7 z# m/ _: x8 V, S3 k0 i由于使用HAL库的函数,CPU花费的时间会更长(因为指令变多了),在时间精度要求比较高的应用,就不适合用HAL库函数来操作了,这一点希望大家明白。所以学STM32不是说只要会HAL库就可以了,对寄存器也是需要有一定的理解,最好是熟悉。这里用HAL库操作只是为了演示怎么使用HAL库的相关函数。. d* g2 V' J4 n% q1 |
最后在main.c里面编写如下代码:/ c- ^4 n3 _; g" Y

2 i5 K/ A8 e7 k1 E
  1. int main(void)
    . y+ x0 C) x' k2 y; r5 p! O) Q
  2. {
    % h' F2 p  y9 a! _8 N" r: H
  3.     uint8_t t = 0; 1 y" v  Z- k: ?$ d8 K
  4.     uint8_t key;
    ; f6 C2 H, H( I2 |' H! [& a0 [5 Q: @
  5.     sys_cache_enable();                    /* 打开L1-Cache */
    2 Y& v% v1 T' k# I+ j! [" L
  6.     HAL_Init();                                     /* 初始化HAL库 */& l$ y) k# L( I2 k" O
  7.     sys_stm32_clock_init(240, 2, 2, 4);        /* 设置时钟, 480Mhz */
    , Z1 |% g: Z+ ]: Y. K8 p# e
  8.     delay_init(480);                            /* 延时初始化 */
    " {+ ?5 V8 [" W
  9.     usart_init(115200);                         /* 串口初始化为115200 */
    . D: \( C6 t" D5 k: P% f- G. ?
  10.     usmart_dev.init(240);                       /* 初始化USMART */
    * ~. r. Y* A+ @) {* i4 |5 Q* I
  11.     mpu_memory_protection();                   /* 保护相关存储区域 */
    8 M! D, V2 y1 i4 r
  12.     led_init();                                 /* 初始化LED */
    8 p6 t, [2 E* L
  13.     lcd_init();                                 /* 初始化LCD */& _- U( p* M8 l2 B: D  R0 {' B
  14.     key_init();                                 /* 初始化按键 */, l6 p/ r/ }0 Z0 J5 N2 B6 v
  15.     dac_init(1);                                /* 初始化DAC1_OUT1通道 */' r3 e7 Y9 b0 i$ Z/ ^1 q$ }  r
  16.     lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);3 F6 r5 i0 G# o0 y! v7 `( d
  17.     lcd_show_string(30, 70, 200, 16, 16, "DAC Triangular WAVE TEST", RED);1 q% h& _. H* g1 I
  18.     lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);& e- c4 R2 E9 U+ M! I: L5 ~
  19.     lcd_show_string(30, 110, 200, 16, 16, "KEY0:Wave1  KEY1:Wave2", RED);
    * e' U& N4 |& o! ^* X
  20.     lcd_show_string(30, 130, 200, 16, 16, "DAC None", BLUE); /* 提示无输出 */. }( @2 }$ Z% w: U
  21.     while (1); G/ o+ Y6 m+ H6 ~+ Q( Z
  22.     {3 m# n: P) g3 i
  23.         t++;
    " `4 Z3 u/ C: l+ u9 a# \
  24.         key = key_scan(0);           /* 按键扫描 */
    2 D4 h" ?, h% a+ R
  25.         if (key == KEY0_PRES)       /* 高采样率 , 约0.1Khz波形 */  ?" I" f6 p! Y4 i) Y0 z
  26.         {
    $ C3 Q% j2 O5 C/ }7 M
  27.             lcd_show_string(30, 130, 200, 16, 16, "DAC Wave1 ", BLUE); . Q. T: N- S. V
  28. /* 幅值4095, 采样点间隔5us, 2000个采样点, 100个波形 */7 P0 D  q) {, \- {  t" |
  29.             dac_triangular_wave(4095, 5, 2000, 100);   
    * \. U! r2 u% J
  30.             lcd_show_string(30, 130, 200, 16, 16, "DAC None  ", BLUE);
    1 R- x; y% [& A' @
  31.         }
    8 z5 P( \1 ?* `! M( y
  32.         else if (key == KEY1_PRES)  /* 低采样率 , 约0.1Khz波形 */
    7 n' j2 |# K  j* @0 o* n( I  f
  33.         {+ S4 r  X" ^) I5 }
  34.             lcd_show_string(30, 130, 200, 16, 16, "DAC Wave2 ", BLUE);
    / [$ B* h7 u$ m1 s; i
  35. /* 幅值4095, 采样点间隔500us, 20个采样点, 100个波形 *// _: R) k. J9 C1 M$ @4 M* M
  36.             dac_triangular_wave(4095, 500, 20, 100);    9 R9 h3 M' T6 S6 N
  37.             lcd_show_string(30, 130, 200, 16, 16, "DAC None  ", BLUE); 6 E! |; m$ d1 z8 M6 ~  Y2 S# B
  38.         }- E; e3 `# d6 ?
  39.         if (t == 10 )       /* 定时时间到了 */
    * a# s6 M' ]0 V, ]7 F1 `
  40.         {
    7 X9 r$ M1 u6 P" Y% Z! Q0 l
  41.             LED0_TOGGLE();  /* LED0闪烁 */
    5 v7 F8 \# n0 I4 y; v
  42.             t = 0;
    * _. ?/ h& t% M- O! S5 Z
  43.         }
    9 f. f, _# N7 x% q- A
  44.         delay_ms(10);
    ; D) c9 B3 v) T9 `- ^/ a
  45.     }
    4 n+ F; a3 A+ g' |9 ~7 \
  46. }
复制代码
) R+ b2 J7 T+ Q- ]. f
该部分代码功能是,按下KEY0后,DAC输出三角波1,按下KEY1后,DAC输出三角波2,将dac_triangular_wave的形参代入公式:输出频率 ≈ 1000 / (dt * samples) KHz,得到三角波1和三角波2的频率都是0.1KHz。3 I* M2 T3 W' e/ H5 X% i$ m' b
34.3.4 下载验证
7 ]  }/ v5 h' ^( A下载代码后,可以看到LED0不停的闪烁,提示程序已经在运行了。LCD显示如图34.3.4.1所示:
' ]) L+ v8 H1 `/ v" w* L2 k: ~+ p6 I) g/ ~- ~% W
a3e4f9f3a8004796b8d9921e308bc155.png . s7 Q3 f# T+ _

! _/ J+ `& e  D$ x7 q图34.3.4.1 DAC输出三角波实验测试图4 }2 ?& n8 r% Q; J! }
没有按下任何按键之前,LCD屏显示DAC None,当按下KEY0后,DAC输出三角波1,LCD屏显示DAC Wave1 ,三角波1输出完成后LCD屏继续显示DAC None,当按下KEY1后,DAC输出三角波2,LCD屏显示DAC Wave2,三角波2输出完成后LCD屏继续显示DAC None。/ x/ V" M# V: g
其中三角波1和三角波2在示波器的显示情况如下图所示:
* ]$ Z: n; p' U, T3 i: Z2 F  @
5d5e0bc4781a44b6b52fa6c99506b469.png 9 z6 t; T* s7 n

" k  Q0 {" }7 k: b# d$ U图34.3.4.2 DAC输出的三角波1
: u: D% l9 ^9 d8 Z; b1 q1 A# M7 N9 C4 \6 M1 j, W
2b76bd7f927c4545997eb1acea804902.png
% g' c9 e5 ^" c+ L. u8 @
4 s4 N6 A  D8 C图34.3.4.3 DAC输出的三角波2
! i5 U. }+ i  O( l, z( T/ M由上面两副测试图可以知道,三角波1的频率是91.2Hz,三角波2的频率是99.9Hz,基本都接近我们算出来的结果0.1KHz。三角波1频率的误差较大,在介绍dac_triangular_wave函数时也说了原因,加上三角波1的采样率比较高,所以误差就会比较大。
9 _8 o( H/ R9 V8 R" o5 e) z2 m6 G6 y& c# B+ J! N

8 B3 R- f& F6 ?8 g6 j" C+ |3 I" [
, V' R8 |0 l: d* n5 k) [& K8 v1 D34.4 DAC输出正弦波实验/ p* q/ @: _$ [; X; z' H: W! ?
本实验我们来学习使用如何让DAC输出正弦波。实验将用定时器7来触发DAC进行转换输出正弦波,以DMA传输数据的方式。
$ G" B2 y3 r. o9 O! l) W34.4.1 DAC寄存器# r# l8 T5 H; Q8 k+ V
本实验用到的寄存器在前面的实验都有介绍。
. b  j1 f$ }  i34.4.2 硬件设计$ }- A$ O9 a* g3 j9 g8 x
1.例程功能  v5 E3 ]2 v; q5 c/ x, o  J' S8 ^
使用DAC输出正弦波,通过KEY0/KEY1两个按键,控制DAC1的通道1输出两种正弦波,需要通过示波器接PA4进行观察。TFTLCD显示DAC转换值、电压值和ADC的电压值。LED0闪烁,提示程序运行。
, Z; S2 h, N. A6 V; N1 A2 V2.硬件资源/ Y: C2 o/ l$ I: l4 v
1)RGB灯
' w, a5 \6 q# i5 a. t4 \4 G) HRED : LED0 - PB46 }4 _1 |. x9 L! U6 F
2)串口1(PA9/PA10连接在板载USB转串口芯片CH340上面)
% h- l# R3 T5 b% ~/ x" d" [( k! U3)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)- Y  @. w7 E8 }$ @, E2 i/ l* L
4)独立按键 :KEY0 - PA1、KEY1 - PA15" [) O" }  ^" |1 N9 W
5)ADC1 :通道19 - PA54 l8 p6 l7 ?* o+ V6 d4 p( p
6)DAC1 :通道1 - PA4
% j, t& F- N" q$ D$ J: P. q7)DMA(DMA2 数据流6 DMA请求源67)6 ~& c( C2 ~. a6 ^3 {7 z
8)定时器7' m! |$ G( P! D( O/ C
3.原理图+ H! G$ m" {/ G0 [# H0 t
我们只需要把示波器的探头接到DAC1通道1(PA4)引脚,就可以在示波器上显示DAC输出的波形。PA4在P3多功能端口的DAC标志排针已经引出,硬件连接如图34.4.2.1所示:. B; i. n. S% {4 X- E
, P4 V/ C" b8 g; C, p1 K% R
图34.4.2.1 硬件连接示意图( n" n+ j$ e* q( P0 s# p
34.4.3 程序设计  c$ N5 Q5 e+ B) v, v, o
34.4.3.1 DAC的HAL库驱动( X# F6 h6 \; V+ }( R$ S
本实验用到的HAL库API函数前面大都介绍过,下面将介绍本实验用到且没有介绍过的。0 U5 w' }) d; I

9 h0 t( P4 c7 ?! c1 H+ `) ~5 V 5f25e502509840ce8e5936fb218d911f.png 0 ^4 q" A  @( u( }
# s  r+ a- a5 i9 H% G+ A/ Q
HAL_DAC_Start_DMA函数4 |5 v1 \: |5 ^  x+ a* Z  b
启动DAC使用DMA方式传输函数,其声明如下:
' i- t1 k$ v; d. H6 f: ~2 f0 GHAL_StatusTypeDef HAL_DAC_Start_DMA(DAC_HandleTypeDef *hdac, uint32_t Channel,
0 A- J% y% H2 Puint32_t *pData, uint32_t Length, uint32_t Alignment);! v9 E4 v/ L  F) ^
函数描述:3 {$ K- f8 s0 p0 z& w
用于启动DAC使用DMA的方式。
. x, z/ l2 g5 ?, I. Q/ ^函数形参:
( [' U1 P) n7 d6 _0 ], A7 Q形参1是DAC_HandleTypeDef结构体类型指针变量。8 B, ^/ b& ^- }
形参2用于选择要启动的通道,可选择DAC_CHANNEL_1或者DAC_CHANNEL_2。1 x/ q& c4 W. u+ R6 d$ z
形参3是使用DAC输出数据缓冲区的指针。
4 v0 p+ p& g& e: I5 B( D& B/ E, e形参4是DAC输出数据的长度。8 p+ w2 s1 y- D$ n' W# o
形参5是指定DAC通道的数据对齐方式,有:DAC_ALIGN_8B_R(8位右对齐)、DAC_ALIGN_12B_L(12位左对齐)和DAC_ALIGN_12B_R(12位右对齐)三种方式。( Y7 s: S6 D$ N5 r% O
函数返回值:' I4 T% ]6 W6 U
HAL_StatusTypeDef枚举类型的值。6 B( D: W* X4 ~8 s7 L* `& r' m7 n+ R
HAL_DAC_Stop_DMA函数6 k0 [5 v# J- g+ G
停止DAC的DMA方式函数,其声明如下:
# h) B4 l" T6 A- _HAL_StatusTypeDef HAL_DAC_Stop_DMA(DAC_HandleTypeDef *hdac, uint32_t Channel);- m+ e- a, b7 @, J+ [4 I
函数描述:
# [( l: v& B2 M用于停止DAC的DMA方式。1 ~- Y2 q2 H: r
函数形参:
; t$ D; |; b' B4 `形参1是DAC_HandleTypeDef结构体类型指针变量。
7 m  l( o( y5 ~; V7 B, u形参2用于选择要启动的通道,可选择DAC_CHANNEL_1或者DAC_CHANNEL_2。
9 y! ^+ x6 V) e- p函数返回值:( ^! L. W; y: y* ^; ]# ], i
HAL_StatusTypeDef枚举类型的值。* e. X  {7 [; F3 D( E
HAL_TIMEx_MasterConfigSynchronization函数. R  Y+ `/ X7 N( h' v# X: z7 k1 U
配置主模式下的定时器触发输出选择函数,其声明如下:/ a! T. c7 M+ i* x" c: U; n
HAL_StatusTypeDef HAL_TIMEx_MasterConfigSynchronization(
% f. i% f9 Z0 m+ lTIM_HandleTypeDef *htim, TIM_MasterConfigTypeDef sMasterConfig);
- _# ~, t1 ?/ i9 ]5 t函数描述:
# a, k9 q/ F- I+ W. z用于配置主模式下的定时器触发输出选择。
3 D) p2 ^; F3 ~# B/ s, y! G7 |函数形参:! u0 U0 f# n5 O: p8 ~1 J( m( Y$ c
形参1是TIM_HandleTypeDef结构体类型指针变量。. y" G0 v* N6 a; I, v: \
形参2是TIM_MasterConfigTypeDef结构体类型指针变量,用于配置定时器工作在主/从模式,以及触发输出(TRGO和TRGO2)的选择。
1 n& _# i4 W* c" f. m5 _% y. Y函数返回值:
5 ~. v. N# J* K; dHAL_StatusTypeDef枚举类型的值。
. u1 G! O$ h; d! v& h' H! `DAC输出正弦波配置步骤
% X& H, e% x! b- n: z" Q: |/ [1)开启DACx和DAC通道对应的IO时钟,并配置该IO为模拟功能
$ C" |9 [$ b7 {; `* [首先开启DACx的时钟,然后配置GPIO为模拟模式。本实验我们默认用到DAC1通道1,对应IO是PA4,它们的时钟开启方法如下:8 }& Z2 J6 M$ ^
__HAL_RCC_DAC12_CLK_ENABLE (); / 使能DAC1时钟 /: J$ {8 e  i+ I  ^5 Z5 z
__HAL_RCC_GPIOA_CLK_ENABLE(); / 开启GPIOA时钟 */6 \. Z2 y. Z, o, l/ N' Q
2)初始化DACx) {$ o' R5 w5 y
通过HAL_DAC_Init函数来设置需要初始化的DAC。该函数并没有设置任何DAC相关寄存器,也就是说没有对DAC进行任何配置,它只是HAL库提供用来在软件上初始化DAC。
5 b  F# v& m3 ^3 K注意:该函数会调用HAL_DAC_MspInit函数来存放DAC和对应通道的IO时钟使能和初始化IO等代码。" ?; [# m/ }1 B1 I  A
3)配置DAC通道3 J% d2 y+ w1 D: ^5 L3 H( _
在HAL库中,通过HAL_DAC_ConfigChannel函数来设置配置DAC的通道,根据需求设置触发类型以及输出缓冲等。
* p: s; `' y1 Q# s  d5 g6 M4)配置DMA并关联DAC& j% j: }1 Z+ g! f4 p0 p. C
通过HAL_DMA_Init函数初始化DMA,包括配置通道,外设地址,存储器地址,传输数据量等。
0 g( I9 T4 i* D. h. Z3 t" w4 ZHAL库为了处理各类外设的DMA请求,在调用相关函数之前,需要调用一个宏定义标识符,来连接DMA和外设句柄。这个宏定义为__HAL_LINKDMA。, F: E8 m5 A, [! W0 o- ?" e6 g
5)配置定时器控制触发DAC& S$ T8 A: J# z' y8 S! w; C
通过HAL_TIM_Base_Init函数设置定时器溢出频率。
; j2 ?3 b- [0 M4 c: c5 g通过HAL_TIMEx_MasterConfigSynchronization函数配置定时器溢出事件用于触发。$ m% j5 ?& d) V: S" S+ x  E9 b
通过HAL_TIM_Base_Start函数启动计数。% Q4 ?% q9 R3 Y: e/ u
6)启动DAC转换并以DMA方式传输数据# D  E" Q4 T1 G7 p$ [. N# p2 k
通过HAL_DAC_Stop_DMA函数先停止之前的DMA传输以及DAC输出。) t, [2 n' k$ a' K1 j% l# k
再通过HAL_DAC_Start_DMA函数启动DMA传输以及DAC输出。* A& x. ?& v; ^
34.4.3.2 程序流程图5 u) z( A. E: V

% l  A. y' L' q% `( v3 P8 p 70603a5a005f41beb09b5cc996c0f2fd.png / z2 k7 J6 b' ?3 w. \

6 H1 P' u) d6 l  u8 i& v9 h图34.4.3.2.1 DAC输出正弦波实验程序流程图1 }6 V2 A3 e1 e  Y
34.4.3.3 程序解析1 K, `: q9 j3 ], L
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。DAC驱动源码包括两个文件:dac.c和dac.h。: U  V' D8 ]( q/ a5 b
dac.h文件只有一些声明,下面直接开始介绍dac.c的程序,本实验的DAC以及DMA的初始化,我们用到dac_dma_wave_init函数,其定义如下:" h3 u$ d, f3 k2 R) v6 |1 X

. ]' i& _, A0 X  I
  1. /**! o, B1 ~2 f0 {
  2. * @brief  DAC DMA输出正弦波初始化函数9 N$ }2 i: w3 H3 V9 d! s
  3. * @note DAC的输入时钟来自APB1, 时钟频率=120Mhz=8.3ns5 T/ v8 I3 l/ R: Q1 w
  4. *        DAC在输出buffer关闭的时候, 输出建立时间: tSETTLING = 2us (H750数据手册有写)6 I1 Z, D2 L- v9 x* @
  5. *        因此DAC输出的最高速度约为:500Khz,以10个点为一个周期, 最大能输出50Khz左右的波形! X0 G  D! w3 Q  F7 t  H* ~
  6. *
    , F7 r5 \! w' m
  7. * @retval      无
    ; K8 L" U# {! c" R0 W; }
  8. */
      e: g8 K1 n2 B( i/ r
  9. void dac_dma_wave_init(void)9 @3 @( @* `! K6 H
  10. {% z" v+ d2 d2 e* [
  11.     DAC_ChannelConfTypeDef dac_ch_conf={0};
    ( F3 N5 i8 m3 A5 Y; e# z+ }
  12. : v  h5 b# G2 C  O' v
  13.     GPIO_InitTypeDef gpio_init_struct;
    1 p% b0 i  m8 Y" z8 E1 Z- g

  14. 7 {. K6 Q3 q& c+ Z
  15.     __HAL_RCC_GPIOA_CLK_ENABLE();               /* DAC通道引脚端口时钟使能 */
    # ~0 ^* t+ b0 h/ }# h
  16.     __HAL_RCC_DAC12_CLK_ENABLE();               /* DAC外设时钟使能 */
    : Y: C- j1 J, g) v2 O0 H9 R, o
  17.     __HAL_RCC_DMA2_CLK_ENABLE();                /* DMA时钟使能 */+ P7 b! j/ X) ?( `
  18. : I0 b+ F- d( s4 S* J8 R
  19.     gpio_init_struct.Pin = GPIO_PIN_4;                     /* PA4 */
    1 _* g+ x  I! {. G/ g& K
  20.     gpio_init_struct.Mode = GPIO_MODE_ANALOG;             /* 模拟 */
    7 h" H8 z+ w4 l2 v, P. W1 I2 N
  21.     HAL_GPIO_Init(GPIOA, &gpio_init_struct);              /* 初始化DAC引脚 */# h, p6 w' j9 A7 z9 h6 L( \1 U5 q; |

  22. + f5 D2 x: ~7 y7 h! P( e5 w
  23.     g_dma_dac_handle.Instance = DMA2_Stream6;             /* 使用的DAM2 Stream6 */$ C: e4 O* r: y" d
  24. g_dma_dac_handle.Init.Request = DMA_REQUEST_DAC1_CH1;  /* DAC触发DMA传输 */  S; _" n/ R/ B+ V) R  h2 D. r; \
  25. /* 存储器到外设模式 */
    9 u9 A- m2 [+ ?( |) E' F& k
  26.     g_dma_dac_handle.Init.Direction = DMA_MEMORY_TO_PERIPH;               
    9 _+ I, r! t- q: V
  27.     g_dma_dac_handle.Init.PeriphInc = DMA_PINC_DISABLE;    /* 外设地址禁止自增 */
    ' \5 }5 e% k4 O" \" h( d6 d9 Q
  28. g_dma_dac_handle.Init.MemInc = DMA_MINC_ENABLE;         /* 存储器地址自增 */! o$ b! q+ |- w6 |
  29. /* 外设数据长度:16位 */
    7 z* p- o' q- |5 Q% t
  30. g_dma_dac_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;    7 t- t% g% b  f* k% F& K
  31. /* 存储器数据长度:16位 */
    7 v8 J- F7 N  S
  32.     g_dma_dac_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;     ( F9 C5 c) n# O" O% z! p
  33.     g_dma_dac_handle.Init.Mode = DMA_CIRCULAR;               /* 循环模式 */' K, w# `7 E- ^
  34.     g_dma_dac_handle.Init.Priority = DMA_PRIORITY_MEDIUM;  /* 中等优先级 */* \/ }6 R, B  Y, e1 k' m8 A
  35.     g_dma_dac_handle.Init.FIFOMode = DMA_FIFOMODE_DISABLE; /* 不使用FIFO */
    & v) h5 j2 \6 c: o4 L4 E
  36.     HAL_DMA_Init(&g_dma_dac_handle);                            /* 初始化DMA */1 ~2 i1 D; i- R- z
  37. - ~# j3 T1 j. z# l% ]& I1 s, d  b
  38. /* DMA句柄与DAC句柄关联 */: z- z+ M4 s  G" T# E
  39.     __HAL_LINKDMA(&g_dac_dma_handle, DMA_Handle1, g_dma_dac_handle);      
    8 H3 z, v8 A2 o2 |
  40. 1 n# R! z! d1 I( X- [0 L3 X
  41.     g_dac_dma_handle.Instance = DAC1;                          /* 选择哪个DAC */
    ' M1 ~  Q# q3 Y! ^/ U! }$ s
  42.     HAL_DAC_Init(&g_dac_dma_handle);                           /* DAC初始化 */$ I- z( y+ l: N" A7 o, f  B6 f8 e. m
  43. " X  Y- L1 s2 y# ~5 T' c
  44.     /* 关闭采样保持模式,这个模式主要用于低功耗 */
    ; h/ |2 p- Z) e% m) V6 g
  45.     dac_ch_conf.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;            + _0 _8 W4 _' V2 t" v
  46.     dac_ch_conf.DAC_Trigger = DAC_TRIGGER_T7_TRGO;           /* 采用定时器7触发 */
    4 l/ ~+ w1 }, {2 L# P, Z
  47. dac_ch_conf.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;/* 使能输出缓冲 */
    . A( [- o! r$ }0 x6 u8 A, p
  48. /* 不将DAC连接到片上外设 */8 g0 |4 {$ K/ w; I% y. `1 @
  49. dac_ch_conf.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_DISABLE;     
    ! K) R5 B$ O; U9 F, n
  50. dac_ch_conf.DAC_UserTrimming = DAC_TRIMMING_FACTORY;  /* 使用出厂校准 */0 V. C; |4 m% y" d8 a$ N; @" X5 T: }
  51. /* DAC通道输出配置 */# g2 [' b- f' h/ `
  52.     HAL_DAC_ConfigChannel(&g_dac_dma_handle, &dac_ch_conf, DAC_CHANNEL_1);  
    5 t: i  H6 K8 H4 l" m( L( v
  53. }
复制代码

3 @" ]. e( v* S  ^: E该函数用于初始化DAC用DMA的方式输出正弦波。本函数用到的API函数起前面都介绍过,请结合前面介绍过的相关内容来理解源码。这里值得注意的是我们是采用定时器7触发DAC进行转换输出的。3 C+ u' N/ ~( |7 g3 I2 u
下面介绍DAC DMA使能波形输出函数,其定义如下:
3 c2 U( J. k4 C$ d7 V5 O$ E3 e6 J  a, {4 b( s* _& x) V
  1. /**! l) |- [# c# \% X: p
  2. * @brief      DAC DMA使能波形输出7 O# B4 v; F  D7 W
  3. *   @note     TIM7的输入时钟频率(f)来自APB1, f = 120M * 2 = 240Mhz.7 F9 `# I3 M" W5 P+ v# \0 n3 N' P! g
  4. *              DAC触发频率 ftrgo = f / ((psc + 1) * (arr + 1))! K  i( u2 s: [0 [- \+ K
  5. *              波形频率 = ftrgo / ndtr;
    7 }- w: w: _# T1 X" X  ^
  6. * @param       ndtr        : DMA通道单次传输数据量6 \2 i4 m, @1 _3 F4 Z
  7. * @param       arr         : TIM7的自动重装载值
    3 v9 E8 V/ E5 ^/ A$ A1 s" ~1 w+ M( Y
  8. * @param       psc         : TIM7的分频系数
    ( Y- c- U- ~5 I1 B5 x
  9. * @retval      无6 [: T; K) `: D
  10. */9 a; k8 W) K! i8 d, f: q# B  g8 r
  11. void dac_dma_wave_enable(uint16_t ndtr, uint16_t arr, uint16_t psc)
    . }. D' L, W( V3 A$ x
  12. {
    8 s0 Q  r. {, N  }; N! f4 N3 _" Y
  13.     TIM_HandleTypeDef tim7_handle={0};7 b+ o2 D; X$ C) A; l* v
  14. TIM_MasterConfigTypeDef master_config={0};6 ]/ u* b( K* G3 X( w$ j# [; {
  15. & B, {; I/ z) Q7 d, ~* s
  16.     __HAL_RCC_TIM7_CLK_ENABLE();                                              /* TIM7时钟使能 */
    8 e& p& s- d. s  A1 Y

  17. + p0 K" p3 c3 r' B$ m$ k" }
  18.     tim7_handle.Instance = TIM7;                                    /* 选择定时器7 */# s: s: }- i+ S( J% [% j! m$ G4 p& d
  19.     tim7_handle.Init.Prescaler = psc;                                 /* 分频系数 */& o% o- y# V1 E' E) }/ O
  20.     tim7_handle.Init.CounterMode = TIM_COUNTERMODE_UP;             /* 递增计数 */" a  L( n- _# F" t1 v( Z. ^2 G
  21. tim7_handle.Init.Period = arr;                                     /* 重装载值 */8 K/ X- i; {: @: B3 N, i. t
  22. /* 自动重装 */9 l, t/ v% `* Q$ C5 ~/ T
  23.     tim7_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
    . l- }  a% V/ B1 T" j6 L. s2 i, m& W
  24.     HAL_TIM_Base_Init(&tim7_handle);                            /* 初始化定时器7 */
    5 J. t2 J* ^8 M
  25.     master_config.MasterOutputTrigger = TIM_TRGO_UPDATE;
    + ^: d0 g+ w) U; B' z
  26. master_config.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    2 g7 L& B$ f$ p; Y
  27. /* 配置TIM7 TRGO *// I2 v" K: T4 c' P: f$ b
  28.     HAL_TIMEx_MasterConfigSynchronization(&tim7_handle, &master_config); 5 q( X- ?4 Y3 f. M, s, t
  29.     HAL_TIM_Base_Start(&tim7_handle);                       /* 使能定时器7 */
    . \% m7 Y# H! n  Z' a5 w+ k
  30.     HAL_DAC_Stop_DMA(&g_dac_dma_handle, DAC_CHANNEL_1); /* 先停止之前的传输 */2 V; R" V) }0 p( A6 b. g
  31. HAL_DAC_Start_DMA(&g_dac_dma_handle, DAC_CHANNEL_1,
    5 [5 m, o: D0 o# S7 X5 W
  32. (uint32_t *)g_dac_sin_buf, ndtr, DAC_ALIGN_12B_R);
    . Z" j8 J$ P( R! C2 n0 ?6 Y2 D6 h
  33. }2 `& g) Q* c. B7 B/ m9 _2 v
复制代码
6 x% Y8 b+ g1 K
该函数用于使能波形输出,利用定时器7的更新事件来触发DAC转换输出。使能定时器7的时钟后,调用HAL_TIMEx_MasterConfigSynchronization函数配置TIM7选择更新事件作为触发输出 (TRGO),然后调用HAL_DAC_Stop_DMA函数停止DAC转换以及DMA传输,最后再调用HAL_DAC_Start_DMA函数重新配置并启动DAC和DMA。
  _  ]2 I8 h0 U6 l最后在main.c里面编写如下代码:
$ C2 H$ ~4 p. H3 @* Z5 w) V; V8 V
  1. uint16_t g_dac_sin_buf[4096];    /* 发送数据缓冲区 */- H, p3 y) f( x4 n& `6 w3 q/ e

  2. % ?9 \1 q, r9 q) D8 a9 _  j. k5 s
  3. /**+ }, a" P1 k  X# }8 ]
  4. * @brief       产生正弦波序列
    6 |' [1 I$ z5 M6 J3 H+ ]) t
  5. *   @note      需保证: maxval > samples/2
    ! b  \! i, J9 B/ Z1 v/ d- z
  6. *
    . A+ M$ l% u2 g$ e! v, |( D
  7. * @param       maxval : 最大值(0 < maxval < 2048)
      Q! ?4 i) d1 S
  8. * @param       samples: 采样点的个数8 A/ c; k' Q# {* `6 ]" t2 B: d
  9. *
    5 o/ p* }% _* N5 h) m1 z
  10. * @retval      无5 @3 g: @% h; ~
  11. */
    9 @# S, D7 P6 L6 ~6 o' i: a
  12. void dac_creat_sin_buf(uint16_t maxval, uint16_t samples)7 D7 l7 b9 W( ^" o* G
  13. {3 ?" d+ |2 Y7 g) d  A3 T
  14.     uint8_t i;: B' n7 r8 J2 T5 t: X
  15.     float inc = (2 * 3.1415962) / samples; /* 计算增量(一个周期DAC_SIN_BUF个点)*/7 e4 V1 q: ^: }- _6 ~
  16.     float outdata = 0;
    ; a3 h! h3 ]9 ]+ \- _  _% Q" Z
  17.     for (i = 0; i < samples; i++)
    8 Y4 B7 d: W; T
  18. {, i4 v$ W5 a4 M7 n9 ?
  19. /* 计算以dots个点为周期的每个点的值,放大maxval倍,并偏移到正数区域 */
    . V. S4 i8 g; y& L* R- |+ h
  20.         outdata = maxval * (1 + sin(inc * i));  0 l. Y% T( M+ w* B/ I
  21.         if (outdata > 4095) outdata = 4095;     /* 上限限定 */
    : a5 c$ \7 r5 @" R; Q! l
  22.         //printf("%f\r\n",outdata);
    $ B; R" C$ d5 Y5 m9 o0 _
  23.         g_dac_sin_buf<i> = outdata;
    ( d) B% ?: ^* ^7 _% p6 E9 T, w
  24.     }
    ! D; c, p- q7 h, Q8 j; D0 v; C
  25. }( E& I# J% p8 R" y& I9 A
  26. : B6 R% O' b6 K5 e: {/ j% X! n' U
  27. /**# D! \$ @. j" m$ {( a# {$ Y
  28. * @brief       通过USMART设置正弦波输出参数,方便修改输出频率.
    1 M* P% N4 V* ], Y/ @( ?
  29. * @param       arr : TIM7的自动重装载值1 a' w4 U% V% C1 l$ x: ?  l# o
  30. * @param       psc : TIM7的分频系数
    ' }& @5 u' s# p
  31. * @retval      无
    ! I! I- w5 n3 q  U
  32. */. ?( o' R- Q- Q
  33. void dac_dma_sin_set(uint16_t arr, uint16_t psc)
    ) x9 K: }' t( p; d; I2 i
  34. {
    * i+ C% k& a- R% O8 V* N( y! ]
  35.     dac_dma_wave_enable(100, arr, psc);0 ^5 g: {9 B; V  A: H6 K
  36. }& o# h4 }2 C4 d% a! ~

  37. ) M( P5 S5 u+ i$ `) ]% k- B
  38. int main(void)8 L+ a/ o7 Q3 B- l  M
  39. {4 D$ i2 u! v) t1 ^
  40.     uint16_t adcx;- {' [1 F9 P' N5 u5 {% B( h+ J
  41.     float temp;
    8 D  D: y. M3 @
  42.     uint8_t t = 0;7 u0 @& v& I" {0 x; e! c
  43.     uint8_t key;6 r% c8 p) i9 d. s- w
  44.     sys_cache_enable();                            /* 打开L1-Cache */- C. Q, Y4 h: P1 R* z- W5 u
  45.     HAL_Init();                                      /* 初始化HAL库 */
    " M0 y0 L) b( F, Q! T
  46.     sys_stm32_clock_init(240, 2, 2, 4);         /* 设置时钟, 480Mhz */
    , @' _8 X* L4 ^7 Z5 f5 [  n
  47.     delay_init(480);                                /* 延时初始化 */
    8 w6 \0 j) L/ K4 P- L0 j4 E0 Z
  48.     usart_init(115200);                            /* 串口初始化为115200 */% G; {- X( a7 ?" h0 \
  49.     usmart_dev.init(240);                          /* 初始化USMART */
    5 Y7 H' U, \; s3 C& K- D  u
  50.     mpu_memory_protection();                      /* 保护相关存储区域 */
    7 p3 w. n6 Q7 Y. j; e
  51.     led_init();                                             /* 初始化LED */1 v5 [3 q: I: ~# n2 ], m+ K, h! k
  52.     lcd_init();                                             /* 初始化LCD */
    0 ?8 L5 L8 r4 X$ `
  53.     key_init();                                             /* 初始化按键 */
    . O2 K% c  K0 |% @$ [1 f
  54.     adc_init();                                             /* 初始化ADC */
    8 C; l% t2 N$ b6 m9 E. H6 b* O
  55.     dac_dma_wave_init();                                  /* 初始化DAC通道1 DMA波形输出 */7 ^" g9 J4 F+ E' f3 H
  56.     lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);1 e' w' _2 `4 O" h% @( l
  57.     lcd_show_string(30, 70, 200, 16, 16, "DAC DMA Sine WAVE TEST", RED);# S; z; b/ z' n
  58.     lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);& [1 x0 L: a1 g5 X
  59.     lcd_show_string(30, 110, 200, 16, 16, "KEY0:5Khz  KEY1:50Khz", RED);
    0 F6 d# A" J, r/ x# z# @; G3 q2 _
  60.     lcd_show_string(30, 130, 200, 16, 16, "DAC VAL:", BLUE);  G$ I2 s# t# [7 `
  61.     lcd_show_string(30, 150, 200, 16, 16, "DAC VOL:0.000V", BLUE);& M. t2 c4 Z' R2 V) n* k6 s- H0 b$ c3 q
  62.     lcd_show_string(30, 170, 200, 16, 16, "ADC VOL:0.000V", BLUE);
    # d& Z  S, |9 s4 F0 y5 q
  63.     dac_creat_sin_buf(2048, 100);
    : k( u; u! g* g+ b
  64.     dac_dma_wave_enable(100, 100 - 1, 24 - 1);1 a* d0 j4 Y# h
  65.     while (1)
    ) b, O8 n3 P5 }4 O. ?7 C6 ^7 i- d
  66.     {# B- j8 e! z/ g9 k% j
  67.         t++;
    # l  W  a1 c! T- \6 E3 j7 t
  68.         key = key_scan(0);  /* 按键扫描 */+ z3 \: {- `6 h, k" k5 S
  69.         if (key == KEY0_PRES)                             /* 高采样率 , 约5Khz波形 */% c3 _" a3 q; R* Y$ @' S
  70.         {3 V7 `2 h% n0 u& ?& J$ W0 `3 @
  71.             dac_creat_sin_buf(2048, 100);               /* 产生正弦波函序列 */
    4 h; s) S- a. Z$ y
  72. /* 500Khz触发频率, 100个点, 得到最高5KHz的正弦波. */4 B! C- q) }# b# d+ c. K
  73.             dac_dma_wave_enable(100, 20 - 1, 24 - 1);           
    " B% Y3 o: T3 K. n$ w
  74.         }
    ! o( J0 m" f( D; m- y
  75.         else if (key == KEY1_PRES)                       /* 低采样率 , 约50Khz波形 */
    $ k* t* O; L; ~  [) |% F, ?# @
  76.         {
    * y+ W3 x8 ~" i. k) g6 Y% z
  77.             dac_creat_sin_buf(2048, 10);                /* 产生正弦波函序列 */% E# G4 u+ H; R, K2 S7 k% y1 P! _. p
  78. /* 500Khz触发频率, 10个点, 可以得到最高50KHz的正弦波. */) A7 ]% K' ?- u+ C
  79.             dac_dma_wave_enable(10, 20 - 1, 24 - 1);            3 e0 |# t, M5 Z  s6 U
  80.         }
    & L! Y) t! ~& Z8 l, O' w, d
  81.         adcx = DAC1->DHR12R1;                             /* 获取DAC1_OUT1的输出状态 */6 C5 Q# g) b( ^, M% ?8 t5 k
  82.         lcd_show_xnum(94, 130, adcx, 4, 16, 0, BLUE);/* 显示DAC寄存器值 */
    8 L- N& g) }* d# @3 M7 Q
  83.         temp = (float)adcx * (3.3 / 4096);             /* 得到DAC电压值 */% R2 M* i* F0 d
  84.         adcx = temp;
    1 I* \, ]" e/ f/ t
  85.         lcd_show_xnum(94, 150, temp, 1, 16, 0, BLUE);/* 显示电压值整数部分 */
    9 a2 }  h0 R+ z7 {* \
  86.         temp -= adcx;
    ( Z  Q$ C9 F& b, a' s; ?; @
  87.         temp *= 1000;; Q+ }# z: o; K( K8 P& v( p! L" G
  88.         lcd_show_xnum(110, 150, temp, 3, 16, 0X80, BLUE);  /* 显示电压值的小数部分 */
    , a/ z- _$ G+ P" p9 x
  89.         adcx = adc_get_result_average(ADC_ADCX_CHY,20);/*得到ADC通道19的转换结果*/
    , z  D, y) L$ |. `
  90.         temp = (float)adcx * (3.3 / 65536);      /* 得到ADC电压值(adc是16bit的) */* r: c; b# H* z$ M/ w
  91.         adcx = temp;* a% k9 E9 W! t+ f
  92.         lcd_show_xnum(94, 170, temp, 1, 16, 0, BLUE);   /* 显示电压值整数部分 */4 @% Y+ N* B3 K1 _/ K( |% |
  93.         temp -= adcx;
    / Q% y1 U( l- S7 y4 o
  94.         temp *= 1000;1 H+ d9 Q# {/ j+ y; R7 D  H
  95.         lcd_show_xnum(110, 170, temp, 3, 16, 0X80, BLUE);/* 显示电压值的小数部分 */
    6 B. q( Y0 x( O0 I3 \
  96.         if (t == 10)        /* 定时时间到了 */
    + C" j% n; m8 x- }5 i0 B" d" u! \
  97.         {! A) Z+ r; W: s' ]; {
  98.             LED0_TOGGLE();  /* LED0闪烁 */2 f, @( f. ?+ {) j# `: I/ \' ^
  99.             t = 0;1 u7 Y  {6 `3 D* A# y$ \& R" q
  100.         }
    0 H  W0 q9 [  t& X$ Z( h
  101.         delay_ms(5);
    " I! o3 v7 h, S! A3 j/ R9 O: w, T
  102.     }
    $ U) _4 n5 K5 b& N/ }/ u& r+ d( Q7 l$ m# l
  103. }  g8 \5 d' }- c9 ~" w7 Z/ z
  104. </i>
复制代码
! Y- Q* d5 G2 q! a/ O! o7 v1 O
adc_init函数初始化ADC1,用于测量DAC通道1的电压值。
& A0 V8 F* Z# d1 L8 j5 r8 Gdac_dma_wave_init函数初始化DAC通道1,并指定DMA搬运的数据的开始地址和目标地址。dac_creat_sin_buf函数用于产生正弦波序列,并保存在g_dac_sin_buf数组中,供给DAC转换。在进入wilhe(1)循环之前,dac_dma_wave_enable函数默认配置DAC的采样点个数时100,并配置定时器7的溢出频率为100KHz。这样就可以输出1KHz的正弦波。下面给大家解释一下为什么是输出1KHz的正弦波?
7 Z. x9 V- O% H# G* F: \定时器7的溢出频率为100KHz,不记得怎么计算的朋友,请回顾基本定时器的相关内容,这里直接把公式列出:8 f- K) I+ [, H' ^* y
Tout= ((arr+1)(psc+1))/Tclk9 Q9 ?1 b! T! s+ _+ t
看到dac_dma_wave_enable(100, 100 - 1, 24 - 1);这个语句,第二个形参是自动重装载值,第三个形参是分频系数,那么代入公式,可得:
: y; z8 r6 g( g5 B1 mTout= ((arr+1)(psc+1))/Tclk= ((99+1)*(23+1))/ 240MHZ= 0.00001s
/ A4 z; k# ^5 r4 f% z得到定时器的更新事件周期是0.00001秒,即更新事件频率为100KHz,也就得到DAC输出触发频率为100KHz。
/ o7 P( B3 G  p再结合总一个正弦波共有100个采样点,就可以得到正弦波的频率为100KHz/100 = 1KHz。
: l7 v% x1 b; k$ o知道了正弦波的频率怎么来的,下面代码中,按下按键KEY0,得到5KHz的正弦波,按下按键KEY1,得到50KHz的正弦波,计算方法都一样的。
) Y# L2 J  G! S9 `& ~/ ?dac_dma_sin_set函数可以通过USMART设置正弦波输出参数,方便修改输出频率。
  v7 G: \" }3 G' c) R/ y4 W3 y- n34.4.4 下载验证
1 N# r% M: v; @* z下载代码后,可以看到LED0不停的闪烁,提示程序已经在运行了。LCD显示如图34.4.4.1所示:
4 C% g+ F- z( w( Y3 ?4 i( d( y
& `* J: W1 \& `2 u 6ecb324163ab49fba23267222746b741.png
4 T2 p" b1 D. T; A$ h, X. l4 i
  I5 P/ d4 `; y) F: N图34.4.4.1 DAC输出正弦波实验测试图
4 w, Y, S- B! M3 b' q$ J' i; s+ A上图是将跳线帽连接多功能端口P3的ADC和DAC两个排针,可以看到ADC VOL的值随着DAC的输出变化而变化,即ADC采集到的值是不停变化的。由于变化太快了,这样看不出采集到值形成什么波形,下面我们借用示波器来进行观察,首先将探头接到DAC的排针上。
0 O1 T* V' {' V' ]没有按下任何按键之前,默认是输出1KHz(100个采样点)的正弦波,如下图所示:: d* m6 Z: f: H* C( J! P

% t5 J# o2 n9 K  r) V+ z b1cbd00ba2834db393cc496688ac5d56.png
* H, z. ?. [# d4 _) Y) ]) |4 B
5 D2 G- I5 @1 N图34.3.4.2 默认DAC输出的的正弦波* L) {  {. Q, G" z: H# r+ M) G9 r
当按下KEY0后,DAC输出5KHz(100个采样点)的正弦波,如下图所示:- u: Q) O6 X/ V! B6 h  J+ U. h: s

# D; \  J$ O9 |1 v* S( w9 ~ df5214fceb534e30b03382eec300a27a.png 5 [$ H1 |' f3 Q) ~5 t7 \8 W, n+ L

* ~% D/ o4 H: u  {) U图34.3.4.3 按下KEY0,DAC输出的的正弦波% _( D  ?; a" d  p* |% q4 ~
当按下KEY1后,DAC输出30KHz(10个采样点)的正弦波,如下图所示:7 f! B- e* p  _
" D1 P  Z8 j$ n  p: u% M
edaab4ccd31145088df0e85b26f4bdd9.png 3 I& R8 L) H- b& _" Q7 Z0 I
3 ]5 _* j: I. c# r6 F
图34.3.4.4 按下KEY1,DAC输出的的正弦波
, T- m' r8 \  |) a————————————————
! g6 G3 D/ {' z3 G2 a3 _版权声明:正点原子
+ v! l  i& h3 v& ~2 S* N, l* w1 M& N' z: j2 z, Q

1 N1 b' I- A5 l6 b: K5 h6 K
9 q/ `1 m8 Q- w% k9 i' B5 l% \
dfc1b296e0c24920b94b9228487074db.png
收藏 评论0 发布时间:2022-10-7 21:37

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版