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

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

[复制链接]
STMCU小助手 发布时间:2022-10-7 21:37
DAC实验6 \* }8 N! o9 ^( G. h+ N- G  l. T
本章,我们将介绍STM32H750的DAC(Digital -to- analog converters,数模转换器)功能。我们通过三个实验来学习DAC,分别是DAC输出实验、DAC输出三角波实验和DAC输出正弦波实验。
2 j1 J) }. q$ \: b% R% C( ~( F7 U8 W5 K+ n$ Y2 {
34.1 DAC简介
- e  Z7 r3 P/ {4 T, `* ?9 K- iSTM32H750的DAC模块(数字/模拟转换模块)是12位数字输入,电压输出型的DAC。DAC可以配置为8位或12位模式,也可以与DMA控制器配合使用。DAC工作在12位模式时,数据可以设置成左对齐或右对齐。DAC模块有2个输出通道,每个通道都有单独的转换器。在双DAC模式下,2个通道可以独立地进行转换,也可以同时进行转换并同步地更新2个通道的输出。DAC可以通过引脚输入参考电压Vref+(通ADC共用)以获得更精确的转换结果。
8 `' \1 Y) H! [3 DSTM32H750的DAC模块主要特点有:( {0 s' \! o; z, j* z5 ]& X4 x/ q
① 2个DAC转换器:每个转换器对应1个输出通道
- G, f9 @- O3 n$ F6 h. A② 8位或者12位单调输出
2 A5 ^% `4 d' b; y: U③ 12位模式下数据左对齐或者右对齐
/ J; v. K3 ^# r④ 同步更新功能+ x- V8 H  S- \* X) k) f% d6 G
⑤ 噪声波形生成5 @3 G6 h& ^/ }) j, X. Y
⑥ 三角波形生成
8 O! B( i/ Q0 D; \! @$ b9 o, E& X6 _. I3 A⑦ 双DAC双通道同时或者分别转换
1 j' F6 P6 N( }, m⑧ 每个通道都有DMA功能' O& q9 k/ e7 S
DAC通道框图如图34.1.1所示:- L' A" ]% x# _: L0 I6 E! z

+ |5 y' I. r! U, [. ?1 X4 ^ 99faab3c8dc740f0bdf1217f9fed0236.png
) m* j; S: W( o# X
0 J- a) L2 ]6 v' `- V+ i图34.1.1 DAC通道框图& r9 j- {( c# ?) g6 Q
图中VDDA和VSSA为DAC模块模拟部分的供电,而VREF+则是DAC模块的参考电压。DAC_OUT1/2就是DAC的两个输出通道了(对应PA4或者PA5引脚)。ADC的这些输入/输出引脚信息如下表所示:
  L% \- H) J$ K& d1 }2 J( x( d) ^! |4 B4 D8 z$ z
fe9b31bd38704b7aaf0eb29d1a610a62.png
3 v7 X, T! h- J) T; _# v# H6 a5 H
: c0 F7 y1 W2 r7 h" i表34.1.1 DAC输入/输出引脚) _8 X. v- N6 h0 U" `8 h( x0 K) S
除了上表列出的输入/输出引脚,DAC通道框图还有一些内部输入/输出信号,具体如下表所示:( w+ L) F% V9 U; t( S/ E( Z9 }

/ d) e& i$ G' d, {" l 7ae9c24466b64f0ca6f182996e8e315b.png 1 B3 n, G2 G% A5 }3 v9 p

" D+ H4 d! i, C. K+ `, ]3 a表34.1.2 DAC内部输入/输出信号
$ F+ U; J6 a/ X) T& u; S/ h' y注意:表中的dac_pclk即DAC的时钟源,DA转换和寄存器访问都是靠这个时钟,该时钟来自APB1,通过sys_stm32_clock_init函数配置之后,为120MHz。
9 P9 K# ?' ?* B- ~& b7 C  w从图34.1.1可以看出,DAC输出是受DORx(x=1/2,下同)寄存器直接控制的,但是我们不能直接往DORx寄存器写入数据,而是通过DHRx间接的传给DORx寄存器,从而实现对DAC输出的控制。
/ x* C2 }. r. X3 R3 e" p# Q( I4 c前面我们提到,STM32H750的DAC支持8/12位模式,8位模式的时候是固定的右对齐的,而12位模式又可以设置左对齐/右对齐。DAC单通道模式下的数据寄存器对齐方式,总共有3种情况,如下图所示:
" v6 ^& j$ C/ c, Z! `$ ^0 {- w6 E0 B
b2ccd96bab6e41149fec408d04dd5706.png ! n' P0 F0 V( T# e: V

! I* ~, H6 ?) @1 p+ N  s" F图34.1.2 DAC单通道模式下的数据寄存器对齐方式
) J" r& }9 P: [/ \; t①8位数据右对齐:用户将数据写入DAC_DHR8Rx[7:0]位(实际存入DHRx[11:4]位)。
" a7 u2 \# o' M+ }& b②12位数据左对齐:用户将数据写入DAC_DHR12Lx[15:4]位(实际存入DHRx[11:0]位)。
- j, I% H' I  p: q- U6 s1 v③12位数据右对齐:用户将数据写入DAC_DHR12Rx[11:0]位(实际存入DHRx[11:0]位)。
0 ?9 k3 X- G: ^8 V我们本章实验中使用的都是单通道模式下的DAC通道1,采用12位右对齐格式,所以采用第③种情况。另外DAC还具有双通道转换功能。
* I( a1 `- b( n' C, b- k" N对于 DAC 双通道(可用时),也有三种可能的方式,如下图所示:
2 t, C, O- F% E# [
" C4 `+ m+ k5 L) J" a8 q9 N9 b 855fb53f1f314e2b8fd9cf7bed680013.png 3 u5 d) N  _/ [: B& @# l- P( U; d
: T/ S3 T7 S% x& e' \+ H* I
图34.1.3 DAC双通道模式下的数据寄存器对齐方式
# a/ T5 p4 N7 n①8位数据右对齐:用户将DAC通道1的数据写入DAC_DHR8RD[7:0]位(实际存入DHR1 [11:4]位),将DAC通道2的数据写入DAC_DHR8RD[15:8]位(实际存入DHR2 [11:4]位)。
; T% j- M8 G9 B% G; K/ j②12位数据左对齐:用户将DAC通道1的数据写入DAC_DHR12LD [15:4]位(实际存入DHR1[11:0]位),将DAC通道2的数据写入DAC_DHR12LD [31:20]位(实际存入DHR2[11:0]位)。
' P( ^; @' P% Y8 x3 ?③12位数据右对齐:用户将DAC通道1的数据写入DAC_DHR12RD [11:0]位(实际存入DHR1[11:0]位),将DAC通道2的数据写入DAC_DHR12RD [27:16]位(实际存入DHR2[11:0]位)。7 N# N  x& s# e  D" ^: J+ v
DAC可以通过软件或者硬件触发转换,通过配置TENx 控制位来决定。
( Z+ E, ~! n' W; c! _8 q" C如果没有选中硬件触发(寄存器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左右。
2 s! @7 @& t; U不使用硬件触发(TEN=0),其转换的时间框图如图34.1.4所示:
4 K+ p/ P& b% e# A
, Q  E! O: Q' \3 M: }0 ~ 003738441a0b472cbf97752a24750915.png
! f. u7 H8 K8 X2 ^9 q
9 f4 {8 R0 _# E9 Z6 f# ^图34.1.4 TEN=0时DAC模块转换时间框图! ?" g( ?2 L( i# n
当DAC的参考电压为VREF+的时候,DAC的输出电压是线性的从0~VREF+,12位模式下DAC输出电压与VREF +以及DORx的计算公式如下:; a6 v8 h5 s2 x( A% {9 n! \# q5 Q
DACx输出电压 = VREF *(DORx/4096)8 h' G+ k8 d% X, G+ n/ L
如果使用硬件触发(TEN=1),可通过外部事件(定时计数器、外部中断线)触发DAC转换。由TSELx[3:0]控制位来决定选择16个触发事件中的一个来触发转换。这16个触发事件如下表所示:
7 L' b+ |' d& n6 e- A
, e$ E' s6 g- J* q- Z% o' x 428f1282feea482d98b916e5b589325b.png
, C7 V0 n& u# c
& ~' Z4 }/ a  |' Q, T7 Q表34.1.3 DAC触发选择% @0 I. K7 T+ Y
原表见《STM32H7xx参考手册_V3(中文版).pdf》第923页表 208。- O. @8 y/ v( a9 K
每个DAC通道都有DMA功能,两个DMA通道分别用于处理两个DAC通道的DMA 请求。如果DMAENx 位置1时,如果发生外部触发(而不是软件触发),就会产生一个DMA 请求,然后DAC_DHRx寄存器的数据被转移到DAC_NORx寄存器。
2 }5 ^' {& J/ J& h1 ^# R( U34.2 DAC输出实验
6 {8 W; p3 o, m7 p, a1 }# _本实验我们来学习DAC输出实验。: A2 i' [) A8 x9 \- o% ?$ [+ d
34.2.1 DAC寄存器
  `- x! R* B+ l5 F8 i+ j下面,我们介绍要实现DAC的通道1输出,需要用到的一些DAC寄存器。+ [6 A0 m# U# P2 M# c5 m
DACx控制寄存器(DACx_CR)(x=1或2)
2 Z, U& J( G2 NDACx控制寄存器描述如图34.2.1.1所示:
& c- g& x+ m! y3 v" j9 Y: a
0 D+ J0 E: X- s" W, L4 Z3 ? ff1c2b05d4514497867ad3b06aec9fef.png * D0 g' I+ |# P4 H% N
; j0 O( j, O5 V6 Z  S
图34.2.1.1 DACx_CR寄存器
8 B* v! {: o6 a# W) b5 eDACx_CR寄存器的低16位用于控制通道1,高16位用于控制通道2,下面介绍本实验需要设置的一些位:
$ H: I. [, [3 Q& B  p1 A! C& AEN1位用于使能/禁止DAC通道1,本实验用到ADC1通道1,所以该位EN1置1。
5 N% z' c4 q) |5 B' f) e" gTEN1位用于DAC通道1的触发使能,本实验不使用硬件触发,所以该位置0。写入DHR1的值会在1个APB1周期后传送到DOR1,然后输出到PA4口上。
7 p2 D- V. \5 f9 a+ RTSEL[3:0]位用于选择DAC通道1的触发方式,本实验使用软件触发,所以该位域置0。具体的设置关系详见表34.1.3 DAC触发选择。
. g# Q0 P+ y/ u" k- [WAVE[1:0]位用于控制DAC通道1的噪声/波形输出功能,默认设置为00,不使能噪声/波形输出。
' E, i  @' N& @& l9 F( ^8 HDMAEN1位用于控制DAC通道1的DMA使能,本实验不使能,设置该位为0即可。
$ L) h/ ?# L1 }CEN1位用于控制DAC通道1的输出缓冲校准使能,本实验不使用校准功能(默认有一个出厂校准值,我们使用默认的校准值即可),设置该位为0即可。( g# Q. L! O0 `' E) p; M/ {7 `( ]
DACx模式控制寄存器(DACx_ MCR)0 e  v' w' B7 I! a( |) k
DACx模式控制寄存器描述如图34.2.1.2所示:
; ^& U1 E6 h- `; L$ |( d8 _
* r! S8 G, O  \8 o f160baeb5bd340358d80bb53b78c6de5.png
  D$ L6 b( u4 D: v5 }+ d
+ B) s6 W. \0 G5 V图34.2.1.2 DACx_ MCR寄存器
( J9 \  @& u( Y. P" z4 U  B该寄存器我们只关心MODE1[2:0],这三个位用于设置DAC通道1的工作模式,本实验使用普通模式,且使用输出缓冲,设置MODE1[2:0]=0即可。MODE2[2:0] 设置通道2的工作模式,本实验没用到。. w( U& a4 r1 W7 ?
DACx通道1 12位右对齐数据保持寄存器(DACx_ DHR12R1)
& e) p1 R$ W# ~" H& E5 J4 R4 ?8 vDACx通道1 12位右对齐数据保持寄存器描述如图34.2.1.3所示:: W: c1 z3 h* }$ \* S  T
6 S9 |. s- {& i0 R
487905f6a54c4d89a5db029c5aa93986.png % m/ P: T1 k: ^1 j- Q) v! z

' t6 N* g: Y/ h6 L5 F  C图34.2.1.3 DACx_ DHR12R1寄存器
& m  e: f$ j1 J2 w  U0 h该寄存器用来设置DAC输出,通过写入12位数据到该寄存器,就可以在DAC输出通道1(PA4)得到我们所要的结果。
7 s8 w1 M2 s  ], T; B( ?/ h34.2.2 硬件设计) N4 V8 n" v9 @: [0 b4 o
1.例程功能
! T& T: g' E- R' x5 H7 o: ?使用KEY1/KEY_UP两个按键,控制STM32内部DAC的通道1输出电压大小,然后通过ADC1的通道19采集DAC输出的电压,在LCD模块上面显示ADC采集到的电压值以及DAC的设定输出电压值等信息。也可以通过usmart调用dac_set_voltage函数,来直接设置DAC输出电压。LED0闪烁,提示程序运行。3 S, h  [) ?5 ~5 N* X8 J
2.硬件资源
4 N$ i( H2 \7 o9 [1)RGB灯
7 ~" t. v2 D0 m: w* T; o0 _3 H) I# dRED : LED0 - PB4/ i3 [+ }4 N( y  Q
2)串口1(PA9/PA10连接在板载USB转串口芯片CH340上面)8 M4 P, l4 W6 @, w1 t5 z8 [
3)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)) S( p' x9 D8 ~+ i! g
4)独立按键 :KEY1 - PA15、WK_UP - PA05 f3 u9 X! b9 g1 U! z: e
5)ADC1 :通道19 - PA57 }5 P& |- O! `2 `- ]6 O/ A
6)DAC1 :通道1 - PA4$ I; r. n( q6 S3 ~/ C
3.原理图3 @( c4 _( r+ @2 J1 K/ U8 @" Q7 a
我们来看看原理图上ADC1通道19(PA5)和DAC1通道1(PA4)引出来的引脚,如下图所示:; O7 c+ S$ D) y5 W+ @

; L) i9 j" U' U  w" x5 d, _) q 5f078194794a44b08226343a4426136e.png 2 j9 d: C# Y7 v4 O
* |5 Q' g$ V/ `: m  D7 m( g
图34.2.2.1 ADC和DAC在开发板上的连接关系原理图( d/ r6 I9 W% T3 O, y& i( u1 ]$ L
P3是多功能端口,我们只需要通过跳线帽连接P3的ADC和DAC,就可以使得ADC1通道19(PA5)和DAC1通道1(PA4)连接起来。对应的硬件连接如图34.2.2.2所示:
& C9 P! I7 b9 ^6 A5 b9 q- n# Z  _( w; K
a36e13ae7547431398eb8d0fe1317908.png ; D9 M3 B" F) f: q7 S- f
6 D. O% K8 g6 W5 q- C. H
图34.2.2.2 硬件连接示意图
6 `, G2 F! d) H+ b  z( i, Y4 a! l$ i1 j; V5 b2 v$ l; S2 z) v9 r
34.2.3 程序设计: Q4 P8 |1 A7 L' D
34.2.3.1 DAC的HAL库驱动% J- J% Z+ q9 v; W  B1 ?5 p) _4 h
DAC在HAL库中的驱动代码在stm32h7xx_hal_dac.c和stm32h7xx_hal_dac_ex.c文件(及其头文件)中。
- C2 u# Y% L+ w2 g/ `5 \2 g1.HAL_DAC_Init函数
6 n, k4 b4 T% C$ i- X  G- @DAC的初始化函数,其声明如下:9 h9 d) p* m9 U# I6 R1 U; k8 B! G
HAL_StatusTypeDef HAL_DAC_Init(DAC_HandleTypeDef *hdac);6 t8 ~1 {% E. H# `' m$ g/ [" h% M6 u
函数描述:' D% j% \; U; T# C, g- N
用于初始化DAC。- f* n0 l  ]7 r# k* W  F$ [% g' y
函数形参:# |* _4 r. A! Q) q. L" V' V! r
形参1是DAC_HandleTypeDef结构体类型指针变量,其定义如下:
& S5 ]; c( p. z* z- h9 \# b0 z6 V3 Y
  1. typedef struct
    ' `8 E) L+ `6 t8 R$ V1 ^6 |1 W
  2. {3 ?6 V2 y) P: d3 G1 h) r  A
  3.   DAC_TypeDef                    *Instance;              /* DAC寄存器基地址 */7 H  M; m. B) x8 f2 o5 Q
  4.   __IO HAL_DAC_StateTypeDef   State;                   /* DAC 工作状态 */. n& g9 Y6 t' h$ P$ Q$ t) B; e7 u, S
  5.   HAL_LockTypeDef               Lock;                    /* DAC锁定对象 */2 l+ [8 Q" \; ^- ^6 s& x" h( ^9 {8 S
  6.   DMA_HandleTypeDef             *DMA_Handle1;          /* 通道1的DMA处理句柄指针 *// g4 X* J5 F6 m& s( G( Q; M+ Y; Z
  7.   DMA_HandleTypeDef             *DMA_Handle2;          /* 通道2的DMA处理句柄指针 */
    ) s# `1 S: Z3 e, p9 y
  8.   __IO uint32_t                  ErrorCode;             /* DAC错误代码 */$ T9 A5 N# W& h1 p/ A0 z# E
  9. } DAC_HandleTypeDef;
复制代码
* s- r' l, ]+ x4 I* _
从该结构体看到该函数并没有设置任何DAC相关寄存器,即没有对DAC进行任何配置,它只是HAL库提供用来在软件上初始化DAC,为后面HAL库操作DAC做好准备。9 H( ]. z; F# n! @; [& S
函数返回值:5 ^1 D* u9 V5 g# _/ a: {! y. H
HAL_StatusTypeDef枚举类型的值。( u1 w3 r7 ?7 z! B  a/ [2 Z. C0 A2 h
注意事项:
; E6 x1 [, ^. ]$ ZDAC的MSP初始化函数HAL_DAC_MspInit,该函数声明如下:( C+ o7 |6 |2 A% s
void HAL_DAC_MspInit(DAC_HandleTypeDef* hdac);
1 v( M3 m+ n, i- d7 a$ X! ]1 {% L9 f2. HAL_DAC_ConfigChannel函数
* @1 ~. H& P0 a( o% zDAC 的通道参数初始化函数,其声明如下:
5 u. q- F! d) u: K0 G$ cHAL_StatusTypeDef HAL_DAC_ConfigChannel(DAC_HandleTypeDef *hdac,9 a/ F) c: q; `: g1 p1 U
DAC_ChannelConfTypeDef *sConfig, uint32_t Channel);
- X4 ]  K# h" J函数描述:
+ K% [9 E, i& q( j: Z* N该函数用来配置DAC通道的触发类型以及输出缓冲。! ]% C' g5 i6 q& w  W) Z- [
函数形参:" x' x$ r& X7 P' P, \, \
形参1是DAC_HandleTypeDef结构体类型指针变量。) d* n! A3 p) P5 `  F/ A
形参2是DAC_ChannelConfTypeDef结构体类型指针变量,其定义如下:3 ~) Y- z0 l# V# Y* R
- v& Z: U& J( ^7 u# v
  1. typedef struct
    1 {( b! Q: m& Z% ^
  2. {
    ! z4 O' H+ v% X6 Z8 X2 K
  3.   uint32_t DAC_SampleAndHold;          /* 设置是否使能低功耗模式,即采样和保持模式 */
    . @$ ~0 [3 F7 L. ^- C
  4.   uint32_t DAC_Trigger;                 /* DAC触发源的选择 */$ m% s( x  b% H" q. o+ U1 R
  5.   uint32_t DAC_OutputBuffer;           /* 启用或者禁用DAC通道输出缓冲区 */
    8 I5 p' v0 ^+ L. Q1 N: |. N
  6.   uint32_t DAC_ConnectOnChipPeripheral; /* 指定DAC输出是否连接到片上外设 */( ^+ M; _: S% A. h3 ?- _
  7.   uint32_t DAC_UserTrimming;           /* 设置DAC的校准方式,采用出厂模式还是用户模式 */
    , w) }- }& P8 s2 R
  8.   uint32_t DAC_TrimmingValue;          /* 设置用户校准模式的偏移值 */: ^  {9 r* G) g( f) e6 z
  9.   DAC_SampleAndHoldConfTypeDef  DAC_SampleAndHoldConfig; /* 设置采样保持具体参数 */3 |7 P, o) v- q" N2 I3 L/ }
  10. } DAC_ChannelConfTypeDef;
复制代码

; y  C, A. Q, v形参3用于选择要配置的通道,可选择DAC_CHANNEL_1或者DAC_CHANNEL_2。* z+ Y- d  @3 j
函数返回值:
% x7 Q1 o+ }+ g: v5 o4 hHAL_StatusTypeDef枚举类型的值。
; E) F+ V: K  m8 D3. HAL_DAC_Start函数
! |  e& }# |$ E使能启动DAC转换通道函数,其声明如下:
2 K4 S; O/ r8 W3 s2 n7 O0 SHAL_StatusTypeDef HAL_DAC_Start(DAC_HandleTypeDef *hdac, uint32_t Channel);
1 s8 c9 @+ F! h2 }函数描述:6 d" Y9 z/ X* Q5 a* ^8 h
使能启动DAC转换通道。
- I$ g& i  v( S函数形参:
% ]4 q8 t# F( I6 V形参1是DAC_HandleTypeDef结构体类型指针变量。
4 f7 c5 ]5 k5 m形参2用于选择要启动的通道,可选择DAC_CHANNEL_1或者DAC_CHANNEL_2。3 H1 j9 g. l& H2 e% T
函数返回值:/ p  A  O* S( K& J8 S
HAL_StatusTypeDef枚举类型的值。  |  Z3 V- x$ s& v! c/ p1 Z
4. HAL_DAC_SetValue函数
3 @$ E/ \# S. ?- ~5 u9 T' IDAC的通道输出值函数,其声明如下:2 P0 l( y, o4 G8 j1 N
HAL_StatusTypeDef HAL_DAC_SetValue(DAC_HandleTypeDef *hdac, uint32_t Channel,! Q( {) e+ q) D* p! j  E
uint32_t Alignment, uint32_t Data);
  i1 f' _7 b1 e" n1 f函数描述:
5 |  W! }" W% g配置DAC的通道输出值。; B1 C) e# I0 k$ _
函数形参:8 o9 y- F, F$ \
形参1是DAC_HandleTypeDef结构体类型指针变量。
: N6 G* d1 }; y* m4 u形参2用于选择要输出的通道,可选择DAC_CHANNEL_1或者DAC_CHANNEL_2。/ k- [, H  P$ v( u7 W  }+ m8 u
形参3用于指定数据对齐方式。9 q3 N6 e0 l6 B4 i( E& ^
形参4设置要加载到选定数据保存寄存器中的数据。" V) B- B, o7 Q1 i# w( F
函数返回值:
1 F! R% H3 S: `+ P1 r, W0 oHAL_StatusTypeDef枚举类型的值。1 p( a5 e$ K# h7 c# F
5. HAL_DAC_GetValue函数, i# m# H6 K3 [4 G! g
DAC读取通道输出值函数,其声明如下:
9 A& z; e2 \! W' J( j2 K* v5 U3 guint32_t HAL_DAC_GetValue(DAC_HandleTypeDef hdac, uint32_t Channel);2 n% V: e6 t3 S5 n3 U% y
函数描述:& k- H, Q! j1 p8 {" d2 X# a
获取所选DAC通道的最后一个数据输出值。# G1 Y& F( F5 Z( b. |. }1 \
函数形参:
1 O4 `0 }5 W- s" x) }  U形参1是DAC_HandleTypeDef结构体类型指针变量。4 f3 d2 z5 V. E2 a
形参2用于选择要读取的通道,可选择DAC_CHANNEL_1或者DAC_CHANNEL_2。' s- E$ L* v4 @8 i) n+ |8 H
函数返回值:
# B! c. h1 p# v& H) f% |. b获取到的输出值。1 i" z8 \$ A5 ]4 n1 M3 O
DAC输出配置步骤: Q# Q" W. b0 N3 X* R+ e
1)开启DACx和DAC通道对应的IO时钟,并配置该IO为模拟功能
0 D/ W3 o. h/ p; x5 z* a, K% g7 I首先开启DACx的时钟,然后配置GPIO为模拟模式。本实验我们默认用到DAC1通道1,对应IO是PA4,它们的时钟开启方法如下:  k! P: q, V2 f2 A) s
__HAL_RCC_DAC12_CLK_ENABLE (); / 使能DAC1时钟 /. f- c0 c7 u& [% m; s6 h
__HAL_RCC_GPIOA_CLK_ENABLE(); / 开启GPIOA时钟 */1 D. l3 ]6 X4 `2 c  H8 w3 ~% Z# @. o
2)初始化DACx- R6 ^" j# r7 F8 y; {% _' c+ k
通过HAL_DAC_Init函数来设置需要初始化的DAC。该函数并没有设置任何DAC相关寄存器,也就是说没有对DAC进行任何配置,它只是HAL库提供用来在软件上初始化DAC。3 N3 J! t& a' z" [* m
注意:该函数会调用HAL_DAC_MspInit函数来存放DAC和对应通道的IO时钟使能和初始化IO等代码。
3 r" |' ~; j) g. d7 B0 J+ Z/ V3)配置DAC通道并启动DA转换器
# c& J2 E. \4 [) h0 t* G' q在HAL库中,通过HAL_DAC_ConfigChannel函数来设置配置DAC的通道,根据需求设置触发类型以及输出缓冲。' P! q- Z, _3 S% z/ ~8 {$ |
配置好DAC通道之后,通过HAL_DAC_Start函数启动DA转换器。
, h# f2 z, A# t  T3 ~4)设置DAC的输出值4 U* F/ ^% X$ T/ h
通过HAL_DAC_SetValue函数设置DAC的输出值。+ s' |4 _! ^% v6 r* I8 g
34.2.3.2 程序流程图
+ k2 d$ C! S. C2 ]0 V
0 ~2 I' w, K$ e) }/ [0 C+ n 14324d67839147a3997a9b59e727e980.png
9 r% j$ P, T# Y: {" ^" N, F: {0 @5 Y5 A' X; o5 {+ B" s9 u7 [" d
图34.2.3.2.1 DAC输出实验程序流程图
2 Q+ X# l1 D& H% {7 X2 i" I34.2.3.3 程序解析7 d3 j$ _' M! G, S8 z! F4 ^4 T- v
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。DAC驱动源码包括两个文件:dac.c和dac.h。3 d3 u/ T8 _- O) T+ B7 N8 m) w1 t) x
dac.h文件只有一些声明,下面直接开始介绍dac.c的程序,首先是DAC初始化函数。5 D2 T2 o6 {7 H8 `" ~
+ f. W. M  ^) V% i* G
  1. /**
    / B: y, q+ U  r3 Z
  2. * @brief       DAC初始化函数
    , Z, Q6 t, ]0 o; Z# `3 |: `
  3. *   @note      本函数支持DAC1_OUT1/2通道初始化3 V8 c/ {. \5 J7 e; a! b, M
  4. *       DAC的输入时钟来自APB1, 时钟频率=120Mhz=8.3ns4 v  `- R  f" H# u
  5. *       DAC在输出buffer关闭的时候, 输出建立时间: tSETTLING = 2us (H750数据手册有写)
    * M! d) ~1 s2 e$ l# P
  6. *       因此DAC输出的最高速度约为:500Khz, 以10个点为一个周期, 最大能输出50Khz左右的波形- n9 Y5 n& ?7 f. o# D$ \8 O
  7. *
    : ^  y5 ?8 z5 F/ S% ?& a5 b6 B% w
  8. * @param     outx: 要初始化的通道. 1,通道1; 2,通道21 z9 j1 p: p; V  _2 G* j
  9. * @retval    无
    : _8 B3 N3 T, ~; e
  10. */0 @, F% ~$ k7 F2 e
  11. void dac_init(uint8_t outx)5 R# M1 }6 A3 }% ~, v$ }4 k+ }8 f: y& t
  12. {
    / ~6 g. `1 C8 o2 r
  13.     DAC_ChannelConfTypeDef dac_ch_conf;     /* DAC通道配置结构体 */
    : G: c2 n, P7 E" u) Z
  14.     GPIO_InitTypeDef gpio_init_struct;
    ' y/ {0 t7 B1 ?3 W$ z/ h
  15.     __HAL_RCC_DAC12_CLK_ENABLE();/* 使能DAC12时钟,本芯片只有DAC1 */4 l8 ~4 O: K8 N% V
  16.     __HAL_RCC_GPIOA_CLK_ENABLE();/* 使能DAC OUT1/2的IO口时钟(都在PA口,PA4/PA5) */
    5 M, W$ T' g  [- S) w

  17. # _4 U0 _! e, r; B' u: E
  18. /* STM32单片机, 总是PA4=DAC1_OUT1, PA5=DAC1_OUT2 */
    ) z- p+ q6 `& q3 g5 x6 o
  19.     gpio_init_struct.Pin = (outx==1)? GPIO_PIN_4 : GPIO_PIN_5;  & g. P, `7 F& }& S2 H
  20.     gpio_init_struct.Mode = GPIO_MODE_ANALOG;               /* 模拟 */
    7 H5 r8 t7 o) T0 P* h+ D# O; I: Q
  21.     HAL_GPIO_Init(GPIOA, &gpio_init_struct);
    ' J( ^, g' Z" X2 D* q9 l1 {; t

  22. 0 i& ]5 R) ^6 s
  23.     g_dac_handle.Instance = DAC1;                             /* DAC1寄存器基地址 */, U0 K! ]* S# R& d6 {  s5 r% V
  24.     HAL_DAC_Init(&g_dac_handle);                              /* 初始化DAC */
    / g7 y1 ^2 R% W9 s! }' t8 ~
  25. ( q+ V: k; U" L* e. A/ ]$ h
  26.     dac_ch_conf.DAC_Trigger = DAC_TRIGGER_NONE;               /* 不使用触发功能 */
    ( {% m( A- }* d: e7 d0 g
  27.     dac_ch_conf.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE;/* DAC1输出缓冲关闭 */0 b4 x' b% c1 i2 V4 K) j8 L1 v: e
  28.     switch(outx)
    ( Q  @* e1 Q# J5 _
  29.     {
    , C; T  I2 ?# t' k: M) a
  30.         case 1 :1 y' C9 ?4 u. y0 r8 L1 F! x
  31. /* DAC通道1配置 */, J& _; J% A9 c2 V
  32.             HAL_DAC_ConfigChannel(&g_dac_handle, &dac_ch_conf, DAC_CHANNEL_1);
    8 h( A0 D( m0 v4 q0 h0 o9 i
  33.             HAL_DAC_Start(&g_dac_handle, DAC_CHANNEL_1);    /* 开启DAC通道2 */9 t0 B" |4 d* @; ^
  34.             break;) V) A, l* e- T, p! O* J
  35.         case 2 :; r3 b6 P8 f2 [9 @. W' x4 m% G
  36. /* DAC通道1配置 */
    : O9 b/ ~' r& c6 k
  37.             HAL_DAC_ConfigChannel(&g_dac_handle, &dac_ch_conf, DAC_CHANNEL_2);
    1 g3 U9 q/ R! r. ^4 I  a: R
  38.             HAL_DAC_Start(&g_dac_handle, DAC_CHANNEL_2);    /* 开启DAC通道2 */8 ?" {: x; P8 F& ^& I8 q' K: b' R$ [
  39.             break;/ H- }( e$ Y: u# o3 Z8 u. ]
  40.         default : break;
    4 K& O8 b5 {. h2 I' l
  41.     }
    ! d1 I4 x: p+ r9 h
  42. }
复制代码
8 r3 g, ]# f7 n* E( r' s0 B# b
该函数主要调用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两个通道的初始化,就没有用到该函数。* L" P3 E1 r2 v) `: x
下面是设置DAC通道1/2输出电压函数,其定义如下:) a4 n- j( P/ B! y, `) _  E
4 C1 }( [7 {1 t0 [" d
  1. /**$ \  f9 V8 _3 S4 P8 C7 [* c6 C3 a" p
  2. * @brief       设置通道1/2输出电压) i' T/ G. m/ h& Y& g/ Q
  3. * @param       outx: 1,通道1; 2,通道2
    + |3 d9 w- Q* Z" i4 U  {. l
  4. * @param       vol : 0~3300,代表0~3.3V3 q+ _1 Z& v& R0 d9 X. r
  5. * @retval      无% x) t* x6 s$ R, J3 b0 {
  6. */
    * w# y' P5 }% B7 t
  7. void dac_set_voltage(uint8_t outx, uint16_t vol)
    ! w  }6 Q! t" m5 c  h( r
  8. {
    7 ~; o5 V: R0 z& q- e4 E  _* Q
  9.     double temp = vol;
    5 u) ?& Y8 f/ z) m. g! H* M. Y
  10.     temp /= 1000;7 D. ]: s" Z/ {5 W* g% G5 Y* q# W5 m
  11.     temp = temp * 4096 / 3.3;
    . B$ t( K1 x, y# V' O- |
  12.     if (temp >= 4096)temp = 4095;   /* 如果值大于等于4096, 则取4095 */
    " A1 }% c7 Z* t8 A; X6 F9 V
  13.     if (outx == 1)   /* 通道1 */
      z+ W& b  U* T
  14. {7 E( i8 [( l8 ?5 _# q+ _
  15. /* 12位右对齐数据格式设置DAC值 */
      e8 z/ p# W) \4 s2 c/ m0 l7 f
  16.         HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, temp);
    7 Y( v1 P. X% ^7 e% x
  17.     }: |' q+ w5 H" Z$ f- x
  18.     else             /* 通道2 */0 f- S2 G0 [# t8 x
  19. {
    # D2 D/ }! v/ V' x5 P, L' ^. X. A. ?
  20. /* 12位右对齐数据格式设置DAC值 */
    - ^: h) W) a  c# @2 V: M" g
  21.         HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_2, DAC_ALIGN_12B_R, temp); ( n4 F+ ]2 M' {2 @( d
  22.     }
    / M) _0 T; M( G( X  \3 K2 ]
  23. }
复制代码
8 W- Z6 l2 k; R8 X& \. j9 V! G
该函数实际就是将电压值转换为DAC输入值,形参1用于设置通道,形参2设置要输出的电压值,设置的范围:03300,代表03.3V。; t6 z- O) B+ D; P- t
最后在main函数里面编写如下代码:
6 Z( M: a$ |$ S! X2 J$ b
9 X: D% \) V7 }& `! Q5 ^1 n
  1. int main(void)
    " A! m2 J! i1 h9 O4 h
  2. {9 E. J0 Y' i3 M
  3.     uint16_t adcx;$ O# s$ g3 S& v4 u0 }: W& k# y
  4.     float temp;$ `* r5 i, B; W: j) M( k
  5.     uint8_t t = 0;' Q5 K( I/ y7 h
  6.     uint16_t dacval = 0;
    % P' [9 t, v% b: g
  7.     uint8_t key;
    ( O; l8 E% @" t+ A& i
  8. & @* r0 W* r4 g2 w8 a3 n, u0 E
  9.     sys_cache_enable();                            /* 打开L1-Cache */
    1 \! h2 l* y. @; p! g
  10.     HAL_Init();                                      /* 初始化HAL库 */
    8 N' a' G- C7 |0 h0 x- K! I9 M# Y0 W
  11.     sys_stm32_clock_init(240, 2, 2, 4);         /* 设置时钟, 480Mhz */
    ' S) l5 n$ F. D, \2 d3 d
  12.     delay_init(480);                                /* 延时初始化 */
    & ?1 i2 a) L% }9 k7 C
  13.     usart_init(115200);                            /* 串口初始化为115200 */
    * [; e, d- R2 H
  14.     usmart_dev.init(240);                          /* 初始化USMART */2 o. a6 t- a  L) C
  15.     mpu_memory_protection();                      /* 保护相关存储区域 */+ c3 x# O5 l) M
  16.     led_init();                                         /* 初始化LED */  C1 d0 y4 [. a: [" C) t: H/ v' @( V& N
  17.     lcd_init();                                         /* 初始化LCD */
    + x2 [5 z/ [9 s2 W, p- n* y
  18.     key_init();                                         /* 初始化按键 */: T/ j1 y  `$ L6 v' y- Y
  19.     adc_init();                                         /* 初始化ADC */5 R, [- ?$ D' _
  20.     dac_init(1);                                                /* 初始化DAC1_OUT1通道 */- t) y* H8 U7 J. K9 V: n7 \
  21.     lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);( N1 V( j" Z  Q+ G; a1 y2 d
  22.     lcd_show_string(30, 70, 200, 16, 16, "DAC TEST", RED);- F# `. T' ^: h2 r
  23.     lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    % |1 j1 x& L& K+ L1 \
  24.     lcd_show_string(30, 110, 200, 16, 16, "WK_UP:+  KEY1:-", RED);1 r& b/ [; b# M* J$ f2 @- N2 W5 d

  25. + f" k0 f) L2 {$ T5 N/ Z
  26.     lcd_show_string(30, 150, 200, 16, 16, "DAC VAL:", BLUE);$ \' }  O. h9 C) X. P% k* H
  27.     lcd_show_string(30, 170, 200, 16, 16, "DAC VOL:0.000V", BLUE);& h: W" V5 v5 Q1 U1 ~. m
  28.     lcd_show_string(30, 190, 200, 16, 16, "ADC VOL:0.000V", BLUE);
    ( @! O2 B% Y; T
  29. /* 初始值为0 */
    5 x6 c, L7 G2 I4 e
  30.     HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 0);
    * d: m% ]6 ?, D# }8 R
  31.     while (1)4 p; z& M1 T* K) x3 o* u9 b
  32.     {
    2 }3 `8 a1 j8 ~3 ]( d7 T3 B
  33.         t++;7 |  g7 \/ y& e6 \
  34.         key = key_scan(0);          /* 按键扫描 */4 y0 S+ R7 ~3 E6 _- W& F
  35.         if (key == WKUP_PRES)
    / u4 C0 t9 }3 G; V
  36.         {
    5 o3 l/ G7 u8 H9 _7 h( i
  37.             if (dacval < 4000)dacval += 200;
    5 ^7 Y4 O& E$ n9 j. l# `
  38.             HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, $ g0 T5 `1 l0 r* O: w6 B: s: u6 J
  39. dacval);/* 输出增大200 */* b5 p( C$ L9 ^# j
  40.         }# F4 \# g0 J8 n1 R7 T, z
  41.         else if (key == KEY1_PRES)
    4 q, D  Z# |* S$ _7 I  Z
  42.         {
    5 g& [( J0 k! N6 E8 Z
  43.             if (dacval > 200)dacval -= 200;  [: J! z5 ^2 i8 g
  44.             else dacval = 0;# i' N5 E) E$ S' B- ^3 Q% [/ r3 H
  45.             HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 2 k$ f  {) F" j0 m9 v
  46. dacval);/* 输出减少200 */) l4 V, p7 C5 o/ L! J1 _3 O% ~& B
  47.         }  q5 s9 e9 d% w) @7 {' K
  48. /* WKUP/KEY1按下了,或者定时时间到了 */
    ( O4 V% i- R; K& K4 r$ d$ U
  49.         if (t == 10 || key == KEY1_PRES || key == WKUP_PRES)   
    / \: I- ?, `- n$ f
  50.         {
    8 C! `7 t  O8 k) m. _
  51. /* 读取前面设置DAC1_OUT1的值 */
    1 [- T3 X7 S% ^% a
  52.             adcx = HAL_DAC_GetValue(&g_dac_handle, DAC_CHANNEL_1);
    5 I% o, a4 B4 a7 Z1 _% i! M
  53.             lcd_show_xnum(94, 150, adcx, 4, 16, 0, BLUE);           /* 显示DAC寄存器值 */
    / w' J7 A/ q4 b1 z. o# K) ]
  54.             temp = (float)adcx * (3.3 / 4096);                        /* 得到DAC电压值 */
    3 w5 r+ o& r* ?0 L+ w
  55.             adcx = temp;
    4 h5 N" D0 j9 [# N, y& e* h
  56.             lcd_show_xnum(94, 170, temp, 1, 16, 0, BLUE);          /* 显示电压值整数部分 */9 g$ W/ [0 M$ |0 o* w# w, D
  57.             temp -= adcx;6 \$ m' \' O' \- W) e. A
  58.             temp *= 1000;$ I5 r. D# b3 ]. ^
  59.             lcd_show_xnum(110, 170, temp, 3, 16, 0X80, BLUE);/*显示电压值的小数部分*/% c. o- w8 s; s- a4 O& F
  60.             /* 得到ADC通道19的转换结果 */
    6 E3 _) a) h4 o
  61.             adcx=adc_get_result_average(ADC_ADCX_CHY, 10); 8 c$ z4 |3 g$ s  ^- i
  62.             temp = (float)adcx * (3.3 / 65536); /* 得到ADC电压值(adc是16bit的) */
    ) H% f- t9 x( c2 }) p
  63.             adcx = temp;
    8 b1 {5 Z0 V* j3 b
  64.             lcd_show_xnum(94, 190, temp, 1, 16, 0, BLUE); /* 显示电压值整数部分 */6 h5 g% s+ V) E9 p
  65.             temp -= adcx;
    % ]  a5 Q. w$ P1 e3 s; Q
  66.             temp *= 1000;
    # x: ?3 O2 }4 w9 {0 ^( ?. l
  67.             lcd_show_xnum(110, 190, temp, 3, 16, 0X80, BLUE);/*显示电压值的小数部分*// M: x4 g. o$ `  \3 q7 J6 Q4 @$ t
  68.             LED0_TOGGLE();  /* LED0闪烁 */: A1 _  X4 P7 D+ |" S$ h. y) P8 }
  69.             t = 0;
    ) U0 d- O% N# n! i( m6 I
  70.         }5 _3 E! Q/ u) I$ W. l( @7 W
  71.         delay_ms(10);
    7 R8 m  a2 v" [) Q* W, y3 Q
  72.     }
    $ D( L  Y% o- c! D; B) g
  73. }
复制代码

: M! V: m  G  M此部分代码,我们通过KEY_UP(WKUP按键)和KEY1(也就是上下键)来实现对DAC输出的幅值控制。按下KEY_UP增加,按KEY1减小。同时在LCD上面显示DHR12R1寄存器的值、DAC设置输出电压以及ADC采集到的DAC输出电压。
- v8 H! [( C$ e; Z2 {: I% B: I5 I! N" w  @" E& [
34.2.4 下载验证$ ]) d7 y5 `: L! g$ Q& h
下载代码后,可以看到LED0不停的闪烁,提示程序已经在运行了。LCD显示如下图所示:
' Z/ d- ]( @9 `2 m4 e# \$ V$ ?) J* M7 x
cfd3207df7954a18807d247b29080386.png / n5 c; L8 O5 i) T% }- v8 c

/ l2 f+ s. Y( C: L/ ~  z图34.2.4.1 DAC输出实验测试图6 Q* f% C) }7 _6 t
验证试验前记得先通过跳线帽连接P3的ADC和DAC,然后我们可以通过按WK_UP按键,增加DAC输出的电压,这时ADC采集到的电压也会增大,通过按KEY1减小DAC输出的电压,这时ADC采集到的电压也会减小。
$ F0 y0 @/ _- {除此之外,我们还可以通过usmart调用dac_set_voltage函数,来直接设置DAC输出电压。+ [; U! s$ q5 c8 s4 w4 \) g* D
4 l, y% `: H  h8 ?  y4 e6 Q1 f
34.3 DAC输出三角波实验/ V+ \# Z. H+ b2 H7 u& i% V
本实验我们来学习使用如何让DAC输出三角波,DAC初始化部分还是用DAC输出实验的,所以做本实验的前提是先学习DAC输出实验。$ h- J  _: ]% H
34.3.1 DAC寄存器  ~+ X0 _  [# M& L# O9 j  M, o
本实验用到的寄存器在DAC输出实验都有介绍。
1 u' \2 }# j: T: m/ m34.3.2 硬件设计- M  w/ J; p) R! Y2 E
1.例程功能
: t; b: R4 W7 ?: m4 ~使用DAC输出三角波,通过KEY0/KEY1两个按键,控制DAC1的通道1输出两种三角波,需要通过示波器接PA4进行观察。也可以通过usmart调用dac_triangular_wave函数,来控制输出哪种三角波。LED0闪烁,提示程序运行。
" M" R' @0 T& `4 h2.硬件资源1 w* ?* p+ R1 j  F( S/ l$ x2 z: k
1)RGB灯- ~/ \! I0 ^' e7 [: d8 Q6 G
RED : LED0 - PB4  x7 h6 @1 l: }! M/ q
2)串口1(PA9/PA10连接在板载USB转串口芯片CH340上面)2 S9 s7 W# ?" p- U* y( U9 }/ x
3)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)# m9 o2 D6 s1 l
4)独立按键 :KEY0 - PA1、KEY1 - PA15
8 h' q' z/ J3 y2 X5)DAC1 :通道1 - PA4
, b' {9 ?; S. \6 _/ c3.原理图
# u8 Y3 A* S. |& c% j1 M' `我们只需要把示波器的探头接到DAC1通道1(PA4)引脚,就可以在示波器上显示DAC输出的波形。PA4在P3多功能端口的DAC标志排针已经引出,硬件连接如图34.3.2.1所示:
& I; G1 V; M8 [9 U3 {* g! _% ?/ j在这里插入图片描述
0 X& S  e# z  X, h6 R
" w9 R' J) \3 O3 c4 q9 M: ^ 3b67bcbe769d401e97ad85b334751550.png
+ ~7 E- ?! J: t8 p: ]/ b1 |+ r. J; B5 w3 Q
图34.3.2.1 硬件连接示意图
( _1 _+ R+ S' c- z% U, w34.3.3 程序设计) N) N" \1 \; a9 a6 N: h
本实验用到的DAC的HAL库API函数前面都介绍过,具体调用情况请看程序解析部分。下面介绍DAC输出三角波的配置步骤。
% U" n; U* u/ i6 L( `DAC输出三角波配置步骤9 _: R. Y! m" n& k% n
1)开启DACx和DAC通道对应的IO时钟,并配置该IO为模拟功能
* A( A$ n. [) b首先开启DACx的时钟,然后配置GPIO为模拟模式。本实验我们默认用到DAC1通道1,对应IO是PA4,它们的时钟开启方法如下:
5 F  j' ]5 d. L1 Y0 y7 ?% y__HAL_RCC_DAC12_CLK_ENABLE (); /* 使能DAC1时钟 /
1 {6 \/ m) }- t& K# n: l0 `__HAL_RCC_GPIOA_CLK_ENABLE(); / 开启GPIOA时钟 */
, W1 @" |3 p5 d8 v3 ]1 G$ @2)初始化DACx/ A' O. b' e1 g
通过HAL_DAC_Init函数来设置需要初始化的DAC。该函数并没有设置任何DAC相关寄存器,也就是说没有对DAC进行任何配置,它只是HAL库提供用来在软件上初始化DAC。
% J4 a- C  b3 Q5 p, ~8 B$ [! u3 ]注意:该函数会调用HAL_DAC_MspInit函数来存放DAC和对应通道的IO时钟使能和初始化IO等代码。* }) C/ j/ O: l, Z$ f8 o
3)配置DAC通道并启动DA转换器
5 I' z- Y; X, L, ^: `8 m9 G; [1 t# p在HAL库中,通过HAL_DAC_ConfigChannel函数来设置配置DAC的通道,根据需求设置触发类型以及输出缓冲。5 J3 i4 `% k6 Z* Z! J
配置好DAC通道之后,通过HAL_DAC_Start函数启动DA转换器。
3 H9 y. Z: l* O  R9 |4)设置DAC的输出值6 x0 k: v' N" J' I. S: Z) [
通过HAL_DAC_SetValue函数设置DAC的输出值。这里我们根据三角波的特性,创建了dac_triangular_wave函数用于控制输出三角波。
9 Y; ^8 _( ~# Q" Y- {- }) {" g+ e34.3.3.1 程序流程图
! c% Q7 [! g6 V* v$ e1 G: z7 D4 d
. [! o9 H' W/ i1 q6 ?; c fe57b0b459184445aa4f8b2b4fd76beb.png . I0 `( \; h$ d+ ?, `* u4 B
5 f# M7 [* I  ?- [! ]3 G* n
图34.3.3.1.1 DAC输出三角波实验程序流程图4 d) d% e5 S6 m$ k. j3 g
34.3.3.2 程序解析" R0 Z4 _5 Y3 i, i
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。DAC驱动源码包括两个文件:dac.c和dac.h。
! R  f5 d" M) Qdac.h文件只有一些声明,下面直接开始介绍dac.c的程序,本实验的DAC初始化我们还是用到dac_init函数,就添加了一个设置DAC_OUT1输出三角波函数,其定义如下:
- S* b0 H- U; `- l$ F! y7 ?1 `+ b8 a3 Z
  1. /*** W0 d3 c' o2 v, u' E- I
  2. * @brief     设置DAC_OUT1输出三角波! e( L+ R: U; g
  3. *   @note     输出频率 ≈ 1000 / (dt * samples) Khz, 不过在dt较小的时候,比如. s7 a! R5 @- E
  4. 小于5us时, 由于delay_us本身就不准了(调用函数,计算等都需要时间,延时
    + r- {' f- v- [# l
  5. 很小的时候,这些时间会影响到延时), 频率会偏小.
    . g: O5 U2 B8 V; \
  6. * + D- x7 d+ f$ R$ U' k7 ?
  7. * @param     maxval : 最大值(0 < maxval < 4096), (maxval + 1)必须大于等于samples/2
    ; l0 n6 E* T" T
  8. * @param     dt     : 每个采样点的延时时间(单位: us), i" i0 b( F" C9 v* x9 ~, \( n8 X
  9. * @param     samples: 采样点的个数, samples必须小于等于(maxval + 1) * 2 , , t$ n+ ?3 \" i* A
  10. 且maxval不能等于0; K5 H: j$ U3 s+ ~9 d% r
  11. * @param     n      : 输出波形个数,0~65535
    # T* h3 L/ Q2 P1 {! s6 V/ \: D
  12. *' X# m8 [9 \* u' g3 n# V$ Q
  13. * @retval   无
    9 J9 e# T6 F+ |* l
  14. */0 M( n# R' F4 a7 g1 \
  15. void dac_triangular_wave(uint16_t maxval,
    ; t. S$ J1 v' |5 b; Y
  16. uint16_t dt, uint16_t samples, uint16_t n)
    9 T+ L5 Q1 U' E- S2 z
  17. {
    . j% ~1 L& O& o1 p8 a
  18.     uint16_t i, j;
    2 ]4 ^. p0 N2 i4 Z' n$ k! Q# S
  19.     float incval;           /* 递增量 */  C" y8 Q9 ?% C2 n0 H) M6 Q* Q2 \
  20.     float Curval;           /* 当前值 */0 u% _9 A0 @+ S/ K6 G

  21. # [5 ?, r1 p4 ]& q7 d
  22.     if((maxval + 1) <= samples)return ;             /* 数据不合法 */% _% ~) Z4 _- g5 C2 a
  23.     incval = (maxval + 1) / (samples / 2);         /* 计算递增量 */
    % e! m: w! A* a9 G
  24.     for(j = 0; j < n; j++)* M6 z+ t5 R2 d0 U1 z* a
  25. { + Z( z1 c8 K9 K, \& m' Z& T, D
  26. /* 先输出0 */! a8 c2 f# V- O) j; K9 T; D
  27.         HAL_DAC_SetValue(&g_dac_handle,DAC_CHANNEL_1,DAC_ALIGN_12B_R,Curval);   
    ; U( L, p; x4 ]1 b
  28.         for(i = 0; i < (samples / 2); i++)          /* 输出上升沿 */
    ' J- h% `! g  G0 t
  29.         {   @$ S8 n4 A1 }( F! j8 S; @1 @. C
  30.             Curval  +=  incval;                         /* 新的输出值 */
    2 H- t2 q% j/ V
  31. /* 用寄存器操作波形会更稳定 */1 |4 j& C6 G  K
  32.             HAL_DAC_SetValue(&g_dac_handle,DAC_CHANNEL_1,DAC_ALIGN_12B_R,Curval);  
    / X: e4 p) k+ B, T3 A% p
  33.             delay_us(dt);/ ]3 F( A2 Q5 X4 F9 X3 P# `8 j
  34.         }
    $ Z/ W: q2 ^9 H2 L; ?) _  ~% a% e( P
  35.         for(i = 0; i < (samples / 2); i++)          /* 输出下降沿 */: V% t" r7 r) U
  36.         {6 Y" F8 E6 {7 T+ d; L
  37.             Curval  -=  incval;                        /* 新的输出值 */3 R8 g* r# X) S; H2 Y% O
  38. /* 用寄存器操作波形会更稳定 */9 q0 Q7 L  k; r6 H* t. d
  39.             HAL_DAC_SetValue(&g_dac_handle,DAC_CHANNEL_1,DAC_ALIGN_12B_R,Curval);  
    ' `  n) h- G7 x- ?0 I8 @+ g( h+ |& o
  40.             delay_us(dt);
    & a, I" X; }# K6 Y! N
  41.         }
    0 `# ^! R3 t3 @6 a  E
  42.     }  O- l: O3 O7 B+ n  T8 T/ k
  43. }
复制代码
" Q; [& R7 i9 G: r0 p$ @
该函数用于设置DAC通道1输出三角波,输出频率 ≈ 1000 / (dt * samples) Khz,形参意义在源码已经有详细注释。该函数中,我们使用HAL_DAC_SetValue函数来设置DAC的输出值,这样得到的三角波在示波器上可以看到。如果有跳动现象(不平稳),是正常的,因为调用函数,计算等都需要时间,这样就会导致输出的波形是不太稳定的。越高性能的MCU,得到的波形会越稳定。而且用HAL库函数操作效率没有直接操作寄存器高,所以可以像寄存器版本实验一样,直接操作DHR12R1寄存器,得到的波形会相对稳定些。
( R7 i# E5 n- c由于使用HAL库的函数,CPU花费的时间会更长(因为指令变多了),在时间精度要求比较高的应用,就不适合用HAL库函数来操作了,这一点希望大家明白。所以学STM32不是说只要会HAL库就可以了,对寄存器也是需要有一定的理解,最好是熟悉。这里用HAL库操作只是为了演示怎么使用HAL库的相关函数。
8 I' b& f3 J) |7 f1 n最后在main.c里面编写如下代码:; g% J/ K+ T" [) x
6 A4 V6 s1 }5 `8 w3 ?
  1. int main(void)
    % b" [" }% T2 a3 i  B& q
  2. {
    $ {' c) t- i. @0 T9 ^
  3.     uint8_t t = 0;
    4 o! S2 M' x  k0 ?  k# U
  4.     uint8_t key;
    8 F5 P* Q& v: \+ Z2 V1 `2 G- j  q
  5.     sys_cache_enable();                    /* 打开L1-Cache */
    1 c% ^/ `# L7 T$ y+ U, g2 A+ [
  6.     HAL_Init();                                     /* 初始化HAL库 */: w, ~+ L9 H% l: M6 a3 d
  7.     sys_stm32_clock_init(240, 2, 2, 4);        /* 设置时钟, 480Mhz */* P/ p+ W5 ?: ~( F" p8 d" y
  8.     delay_init(480);                            /* 延时初始化 */
    # h) M; I' l$ E7 b% \/ Q
  9.     usart_init(115200);                         /* 串口初始化为115200 */( t$ ]# X0 T8 M' X
  10.     usmart_dev.init(240);                       /* 初始化USMART */
    $ [4 L( |( B! x9 B+ g! N" s
  11.     mpu_memory_protection();                   /* 保护相关存储区域 */
    0 a8 f% ^) M! _/ j% h
  12.     led_init();                                 /* 初始化LED */
    9 m/ w' o' x; U+ x& O5 n) e
  13.     lcd_init();                                 /* 初始化LCD *// J# l- I6 Y4 w2 D
  14.     key_init();                                 /* 初始化按键 */
    " x5 r! a+ \6 f: H: Y5 ~( a; q
  15.     dac_init(1);                                /* 初始化DAC1_OUT1通道 */6 t! l6 L& L6 U" {+ @
  16.     lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    , Y/ s- Y* o0 `) B% h# e3 j
  17.     lcd_show_string(30, 70, 200, 16, 16, "DAC Triangular WAVE TEST", RED);
    8 }$ b( K/ C* r" N* Y9 T
  18.     lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    + e0 M/ J3 b% ?, K) d' f* e
  19.     lcd_show_string(30, 110, 200, 16, 16, "KEY0:Wave1  KEY1:Wave2", RED);! M8 w3 T9 i* f# [
  20.     lcd_show_string(30, 130, 200, 16, 16, "DAC None", BLUE); /* 提示无输出 */
    7 A5 z1 Z3 ]& ^- h0 Y2 x6 k3 N
  21.     while (1)
    5 y9 C4 h6 ?$ Q- r5 j1 p: P
  22.     {6 _/ G3 c; K( P! X9 X
  23.         t++;
    % u* |1 G& l0 w) q. g) M" p
  24.         key = key_scan(0);           /* 按键扫描 */; J1 A8 C* U" n% A" G
  25.         if (key == KEY0_PRES)       /* 高采样率 , 约0.1Khz波形 */
    ) Z- I$ m7 c- }+ f
  26.         {
    . \$ C1 x: O+ C0 m3 Y; A
  27.             lcd_show_string(30, 130, 200, 16, 16, "DAC Wave1 ", BLUE);
    + V  h  Z. C/ p
  28. /* 幅值4095, 采样点间隔5us, 2000个采样点, 100个波形 */' B1 {, o% [! `9 \' j& n
  29.             dac_triangular_wave(4095, 5, 2000, 100);   
    - O# O. w8 V6 o8 W4 h
  30.             lcd_show_string(30, 130, 200, 16, 16, "DAC None  ", BLUE); ! t/ b' a& l! j% P9 i
  31.         }
    1 ~' g/ E) k% ], n! `
  32.         else if (key == KEY1_PRES)  /* 低采样率 , 约0.1Khz波形 */# X4 _" t3 e6 O; P' [: p
  33.         {% {1 i0 @6 d& a/ q8 k: {
  34.             lcd_show_string(30, 130, 200, 16, 16, "DAC Wave2 ", BLUE);
    4 S; K3 F# ]) `7 V( y: N4 d
  35. /* 幅值4095, 采样点间隔500us, 20个采样点, 100个波形 */
    2 m4 X& H* `$ ?' r/ [
  36.             dac_triangular_wave(4095, 500, 20, 100);   
    7 f- N& T$ i" L8 E5 g
  37.             lcd_show_string(30, 130, 200, 16, 16, "DAC None  ", BLUE);
    1 |  `- u* g! e! s  n% N+ C
  38.         }- ~8 S6 M2 o, O3 ~& F8 j7 t! _- j
  39.         if (t == 10 )       /* 定时时间到了 */
    , B5 d  P) B+ J# v0 ~9 x
  40.         {
    - |! T$ X3 H$ K8 r! o
  41.             LED0_TOGGLE();  /* LED0闪烁 */( j& e" S) X# j  O3 c) i# A
  42.             t = 0;3 z! i! b; ]1 O
  43.         }3 _- y5 s/ R, y7 o8 }4 v
  44.         delay_ms(10);5 ^4 R/ b. O# [+ z) ^* V2 d
  45.     }
    # I1 x: |1 _' \8 K8 N2 e, t+ w
  46. }
复制代码
, v% V5 U7 ~2 r' t9 O4 Q8 V7 `' }  t) I
该部分代码功能是,按下KEY0后,DAC输出三角波1,按下KEY1后,DAC输出三角波2,将dac_triangular_wave的形参代入公式:输出频率 ≈ 1000 / (dt * samples) KHz,得到三角波1和三角波2的频率都是0.1KHz。
4 [2 k$ J' ^. j34.3.4 下载验证
& @  z# H1 t1 U7 g1 V, S下载代码后,可以看到LED0不停的闪烁,提示程序已经在运行了。LCD显示如图34.3.4.1所示:9 j/ q- [# {% B
2 U7 g0 Z3 r! d  I" O
a3e4f9f3a8004796b8d9921e308bc155.png
. z8 s# G* E7 Z( S8 L; ^( k$ v5 t4 W- W
图34.3.4.1 DAC输出三角波实验测试图
# g' p2 C& t' [& c1 @没有按下任何按键之前,LCD屏显示DAC None,当按下KEY0后,DAC输出三角波1,LCD屏显示DAC Wave1 ,三角波1输出完成后LCD屏继续显示DAC None,当按下KEY1后,DAC输出三角波2,LCD屏显示DAC Wave2,三角波2输出完成后LCD屏继续显示DAC None。
# J1 |' P* e) ]$ d4 j( _其中三角波1和三角波2在示波器的显示情况如下图所示:. R# J) B. ^* E
% Z( t' _) X6 O$ |# Y/ q
5d5e0bc4781a44b6b52fa6c99506b469.png
+ H" a- d1 L, ^$ ?# p) L1 z( D' X/ p1 U
图34.3.4.2 DAC输出的三角波1- m1 K8 W7 _' V! i8 j' K$ l; ?! W
' d& P( N2 v4 A/ u% W5 ~
2b76bd7f927c4545997eb1acea804902.png 5 Q8 }* d7 B* `3 m- R
  ?* m; `+ H# P" ^: b
图34.3.4.3 DAC输出的三角波2& @, a$ E/ V) ^1 o0 Z
由上面两副测试图可以知道,三角波1的频率是91.2Hz,三角波2的频率是99.9Hz,基本都接近我们算出来的结果0.1KHz。三角波1频率的误差较大,在介绍dac_triangular_wave函数时也说了原因,加上三角波1的采样率比较高,所以误差就会比较大。
: B+ [* [0 ?0 W3 M! ?7 e* D. k0 p, T1 C8 `

0 D% |4 l) \- P9 a9 u: l, B* X
6 v5 K: p+ C; `) A: z1 }3 `4 a34.4 DAC输出正弦波实验
$ G1 g/ G+ ^+ W1 v( w/ y( E本实验我们来学习使用如何让DAC输出正弦波。实验将用定时器7来触发DAC进行转换输出正弦波,以DMA传输数据的方式。
2 n; [" o8 O$ F, C( x1 @34.4.1 DAC寄存器
; O# D& r9 v8 z; _本实验用到的寄存器在前面的实验都有介绍。
4 i3 h  ]& t& e" p4 J8 z! Q7 r34.4.2 硬件设计
9 Q/ ?  z  M$ i& q  D! Z1.例程功能) R- L# v& n2 m2 L4 q8 @
使用DAC输出正弦波,通过KEY0/KEY1两个按键,控制DAC1的通道1输出两种正弦波,需要通过示波器接PA4进行观察。TFTLCD显示DAC转换值、电压值和ADC的电压值。LED0闪烁,提示程序运行。' o0 p9 {2 I* H8 w
2.硬件资源: t3 ^2 K: J& J7 T
1)RGB灯
& \; k* i0 E, Q/ i) JRED : LED0 - PB4
5 r* A; b9 d- I, T# d2)串口1(PA9/PA10连接在板载USB转串口芯片CH340上面)8 A2 q& \9 W) |7 y9 O( Q7 o4 X) i
3)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)$ Y+ C6 Z7 M4 j6 I- F5 Y
4)独立按键 :KEY0 - PA1、KEY1 - PA15
" w2 S3 O, `- _* d5)ADC1 :通道19 - PA5
2 p8 [2 i: Y! U8 }5 ?6)DAC1 :通道1 - PA4
3 X' a+ v+ e) s0 [: o6 @# z7)DMA(DMA2 数据流6 DMA请求源67)
+ T' d" C( ^2 @; r0 T; E8)定时器7' s; A, u  n, E
3.原理图
& j1 i7 {( ]7 q5 h我们只需要把示波器的探头接到DAC1通道1(PA4)引脚,就可以在示波器上显示DAC输出的波形。PA4在P3多功能端口的DAC标志排针已经引出,硬件连接如图34.4.2.1所示:: ~5 p$ t. e9 ]/ Z, [
" W9 Z& ?& n# X, j- b' h9 E
图34.4.2.1 硬件连接示意图# `* R+ V% z. ~1 F% V  I5 c# J9 Y
34.4.3 程序设计
# \% }/ L7 T# n  N# R  r- N7 I34.4.3.1 DAC的HAL库驱动1 y/ ]7 S# x; E* A5 l1 Q9 Y3 `; @
本实验用到的HAL库API函数前面大都介绍过,下面将介绍本实验用到且没有介绍过的。, i' \/ L; C6 a8 y5 S

( r$ y; I4 ?& V- K% o4 \ 5f25e502509840ce8e5936fb218d911f.png
1 `. K% w+ d# p6 O6 v0 K% Y2 N
" c7 ?7 }5 p( l% GHAL_DAC_Start_DMA函数
2 ]  I2 K+ ~3 F% e6 N' T9 o启动DAC使用DMA方式传输函数,其声明如下:3 Z% r1 D% x, v
HAL_StatusTypeDef HAL_DAC_Start_DMA(DAC_HandleTypeDef *hdac, uint32_t Channel,8 `- @: _" W: q9 k+ m
uint32_t *pData, uint32_t Length, uint32_t Alignment);
" v  P* ~2 v! g+ S) d6 Q$ }函数描述:
2 K& m7 c" T6 ~' L: C0 P用于启动DAC使用DMA的方式。
2 l) I$ ~" b) C. h: I函数形参:8 j8 D" j& }9 ?
形参1是DAC_HandleTypeDef结构体类型指针变量。* {1 U  V$ }, f
形参2用于选择要启动的通道,可选择DAC_CHANNEL_1或者DAC_CHANNEL_2。
( i) Q) S* X5 O0 i  h* e1 B形参3是使用DAC输出数据缓冲区的指针。
2 T0 n1 I6 R3 Z4 Y形参4是DAC输出数据的长度。
. g* G% p" w& e+ ?! w# I) t形参5是指定DAC通道的数据对齐方式,有:DAC_ALIGN_8B_R(8位右对齐)、DAC_ALIGN_12B_L(12位左对齐)和DAC_ALIGN_12B_R(12位右对齐)三种方式。
7 |1 ~# O1 t5 b: U. p0 V- [函数返回值:& i* t+ k0 U, X* Q
HAL_StatusTypeDef枚举类型的值。; \1 M% G7 o7 K! B& }7 L% f' T
HAL_DAC_Stop_DMA函数
. Q1 m& A7 Z4 V* Y* T- Y; C9 F2 Z停止DAC的DMA方式函数,其声明如下:5 P+ N" p. ]+ \4 K, [
HAL_StatusTypeDef HAL_DAC_Stop_DMA(DAC_HandleTypeDef *hdac, uint32_t Channel);, a& V% e% z0 e; o' j' i
函数描述:
$ K7 g  E8 _  d  g% G用于停止DAC的DMA方式。  e& r( V" k* n# s
函数形参:
' e$ V4 N3 |% a, x: R' S形参1是DAC_HandleTypeDef结构体类型指针变量。
( H' U' u  O, L2 J9 c0 `0 @0 @形参2用于选择要启动的通道,可选择DAC_CHANNEL_1或者DAC_CHANNEL_2。
  |/ q( e) H+ M函数返回值:
' b* X1 Q* P. D" p* w3 WHAL_StatusTypeDef枚举类型的值。# |' X9 M1 u' U2 z+ D8 j9 w
HAL_TIMEx_MasterConfigSynchronization函数
, q/ o% W! k# l" N配置主模式下的定时器触发输出选择函数,其声明如下:
8 u3 }; d. _6 Y7 ]3 JHAL_StatusTypeDef HAL_TIMEx_MasterConfigSynchronization(
- s0 d7 g3 ^+ H+ oTIM_HandleTypeDef *htim, TIM_MasterConfigTypeDef sMasterConfig);' M! {# t& S2 V
函数描述:2 z4 S" e  p4 e; I. u9 Z3 q7 Y
用于配置主模式下的定时器触发输出选择。- F$ s- G; \2 F' _7 ]0 d, h. y
函数形参:
% [6 w6 v3 R& O' W2 w) P5 f' E形参1是TIM_HandleTypeDef结构体类型指针变量。
( J0 w: @/ ^+ @! V6 S" F2 V  V形参2是TIM_MasterConfigTypeDef结构体类型指针变量,用于配置定时器工作在主/从模式,以及触发输出(TRGO和TRGO2)的选择。4 Z' _5 A2 h4 e7 V* q
函数返回值:
# Z5 z6 f1 n/ I3 U( n: m3 j- Z# hHAL_StatusTypeDef枚举类型的值。
* D/ \' P( \0 Z* x2 YDAC输出正弦波配置步骤
9 \; m, O2 N- @0 T3 [8 J1)开启DACx和DAC通道对应的IO时钟,并配置该IO为模拟功能: r4 N8 v; ]" h8 h- m3 A# z( T
首先开启DACx的时钟,然后配置GPIO为模拟模式。本实验我们默认用到DAC1通道1,对应IO是PA4,它们的时钟开启方法如下:0 J+ \0 ]! [' U& i% f( z1 O( H1 j
__HAL_RCC_DAC12_CLK_ENABLE (); / 使能DAC1时钟 /. G& f1 F' ]) L" |
__HAL_RCC_GPIOA_CLK_ENABLE(); / 开启GPIOA时钟 */, _; }  V5 H4 u/ G! [1 J1 r8 ^
2)初始化DACx) i# a( B$ g+ D
通过HAL_DAC_Init函数来设置需要初始化的DAC。该函数并没有设置任何DAC相关寄存器,也就是说没有对DAC进行任何配置,它只是HAL库提供用来在软件上初始化DAC。& m( g4 T* n: n$ K; Z" Q
注意:该函数会调用HAL_DAC_MspInit函数来存放DAC和对应通道的IO时钟使能和初始化IO等代码。5 p& ^1 p( T1 V" }2 ?* i
3)配置DAC通道$ ^6 G) M0 V( g! \2 l
在HAL库中,通过HAL_DAC_ConfigChannel函数来设置配置DAC的通道,根据需求设置触发类型以及输出缓冲等。. i, @, C  e# f/ H. h0 W: L
4)配置DMA并关联DAC
% b' T/ Y+ G: {1 [( r通过HAL_DMA_Init函数初始化DMA,包括配置通道,外设地址,存储器地址,传输数据量等。
0 A5 t7 u* T& B; \HAL库为了处理各类外设的DMA请求,在调用相关函数之前,需要调用一个宏定义标识符,来连接DMA和外设句柄。这个宏定义为__HAL_LINKDMA。
& `1 V0 M0 i" D$ Z. L! Q8 \5)配置定时器控制触发DAC
, K4 N, L4 H) h通过HAL_TIM_Base_Init函数设置定时器溢出频率。$ g$ G, ^* ~7 o2 J
通过HAL_TIMEx_MasterConfigSynchronization函数配置定时器溢出事件用于触发。
, i* o, h/ s3 K, _  `# ]' f通过HAL_TIM_Base_Start函数启动计数。/ G! h+ G1 X* N+ Z+ x0 A
6)启动DAC转换并以DMA方式传输数据; w+ b) c, s, L+ A
通过HAL_DAC_Stop_DMA函数先停止之前的DMA传输以及DAC输出。2 }( N4 T; z5 e/ h0 W6 _! g, k1 E, h' B
再通过HAL_DAC_Start_DMA函数启动DMA传输以及DAC输出。
* }* ]+ B. r; @" @9 t: @5 p34.4.3.2 程序流程图
2 d3 E0 a- L+ J+ y1 V' V) Y+ k) Z$ @9 w% `8 F: R
70603a5a005f41beb09b5cc996c0f2fd.png
. j3 \4 T) ~: L* F+ Y7 w; T  p
9 s3 K  _$ k) l( C2 `1 `( a图34.4.3.2.1 DAC输出正弦波实验程序流程图2 G2 R8 ^/ U! d) M
34.4.3.3 程序解析
; k! l% g+ a4 v9 x这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。DAC驱动源码包括两个文件:dac.c和dac.h。
9 o8 K: N9 M% g7 [dac.h文件只有一些声明,下面直接开始介绍dac.c的程序,本实验的DAC以及DMA的初始化,我们用到dac_dma_wave_init函数,其定义如下:7 Q( e" |+ {3 F; q* r% C4 K7 V

4 |( u0 l. D0 \. S$ V* ], B# u: m
  1. /**
    ; W7 k6 m2 G$ c* w, `
  2. * @brief  DAC DMA输出正弦波初始化函数9 O* S5 c5 w8 F0 a# E
  3. * @note DAC的输入时钟来自APB1, 时钟频率=120Mhz=8.3ns" A* i) D! x- l9 E# O; x
  4. *        DAC在输出buffer关闭的时候, 输出建立时间: tSETTLING = 2us (H750数据手册有写): ]; _* ~! ^8 T( C- V8 v$ W
  5. *        因此DAC输出的最高速度约为:500Khz,以10个点为一个周期, 最大能输出50Khz左右的波形* K" @- h" W; {! T- F0 S# q
  6. *# X6 g2 ~6 m) E7 j4 Z; R. w
  7. * @retval      无+ M- S. M* x1 u$ }8 v2 a2 ?  L/ Y
  8. */
    : {8 n0 G' R8 M8 z
  9. void dac_dma_wave_init(void)
    + I' V- y+ r8 D$ J
  10. {
    * R( i& A7 Q! `, ]; F! P+ l
  11.     DAC_ChannelConfTypeDef dac_ch_conf={0};
    ' D6 f- d0 z$ ~0 \% T
  12. ; q- D: r# @& Z7 S$ t
  13.     GPIO_InitTypeDef gpio_init_struct;- O+ s+ z. L( o' B
  14. ' ~- G- ~5 D$ q6 _
  15.     __HAL_RCC_GPIOA_CLK_ENABLE();               /* DAC通道引脚端口时钟使能 */( r8 P( f8 M9 w; O* F) c' ]
  16.     __HAL_RCC_DAC12_CLK_ENABLE();               /* DAC外设时钟使能 */- d$ n# m8 `8 g+ C+ p
  17.     __HAL_RCC_DMA2_CLK_ENABLE();                /* DMA时钟使能 */
    ) G& i3 D, g& s' e$ y6 ?2 S
  18. / q3 G# O0 u) P3 o
  19.     gpio_init_struct.Pin = GPIO_PIN_4;                     /* PA4 */
    9 ?# P! V6 D$ m' [& N2 C
  20.     gpio_init_struct.Mode = GPIO_MODE_ANALOG;             /* 模拟 */7 G& f% X" \! M4 ~3 Q  u& @
  21.     HAL_GPIO_Init(GPIOA, &gpio_init_struct);              /* 初始化DAC引脚 */- J! I( U7 H( i' G$ x$ I
  22. $ f6 ?9 b0 A/ ~( h8 ?! A! h
  23.     g_dma_dac_handle.Instance = DMA2_Stream6;             /* 使用的DAM2 Stream6 */9 D& n0 t5 E# }* V4 l) |* T
  24. g_dma_dac_handle.Init.Request = DMA_REQUEST_DAC1_CH1;  /* DAC触发DMA传输 */& V* g! X* i1 {' b6 h
  25. /* 存储器到外设模式 */4 y  V# D$ D0 n. }) L* ~' A4 t- Q. P
  26.     g_dma_dac_handle.Init.Direction = DMA_MEMORY_TO_PERIPH;               
    , S+ @/ [& F5 T7 z' \% t. o" V. v7 i
  27.     g_dma_dac_handle.Init.PeriphInc = DMA_PINC_DISABLE;    /* 外设地址禁止自增 */( e* ?' t) V5 t# t0 k$ d) y5 V
  28. g_dma_dac_handle.Init.MemInc = DMA_MINC_ENABLE;         /* 存储器地址自增 */
    3 u  y& J8 z" `* \. U: R3 h: W2 I
  29. /* 外设数据长度:16位 */
    5 B& U/ V0 f+ S; m
  30. g_dma_dac_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;   
    8 I$ W1 g% \, G6 |# W2 \
  31. /* 存储器数据长度:16位 */
    * k* G+ `: P% y/ ^+ U
  32.     g_dma_dac_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;     - g2 a$ |1 I: _! B
  33.     g_dma_dac_handle.Init.Mode = DMA_CIRCULAR;               /* 循环模式 */
    * C: s- @4 L( Z8 ]
  34.     g_dma_dac_handle.Init.Priority = DMA_PRIORITY_MEDIUM;  /* 中等优先级 */2 [2 J# X) B# L# K! Y! x
  35.     g_dma_dac_handle.Init.FIFOMode = DMA_FIFOMODE_DISABLE; /* 不使用FIFO */
    / j5 u: U3 l8 U( p7 ?
  36.     HAL_DMA_Init(&g_dma_dac_handle);                            /* 初始化DMA */
    - O! o- ?4 K6 j  v% |: o' V# r
  37. 7 e9 a! j& Q5 \* n- c
  38. /* DMA句柄与DAC句柄关联 */
    ' ]$ e4 X+ F' d; z3 ?3 L% P% l+ K
  39.     __HAL_LINKDMA(&g_dac_dma_handle, DMA_Handle1, g_dma_dac_handle);       : `; G# m: T. f' u% H
  40. 6 W+ A+ B9 L3 S. q6 j4 i- ]! K
  41.     g_dac_dma_handle.Instance = DAC1;                          /* 选择哪个DAC */" H6 X: _( Y% I. {# N5 H: R9 M$ d
  42.     HAL_DAC_Init(&g_dac_dma_handle);                           /* DAC初始化 *// h8 s0 i5 E- I8 ~3 K

  43. ' L( `& g$ y. R  V0 u" u: R. A
  44.     /* 关闭采样保持模式,这个模式主要用于低功耗 */
    , y5 e2 E" i  y
  45.     dac_ch_conf.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;            ! E2 t) ~- r' U$ ?* d
  46.     dac_ch_conf.DAC_Trigger = DAC_TRIGGER_T7_TRGO;           /* 采用定时器7触发 */9 m$ V/ M  j5 I( S" r
  47. dac_ch_conf.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;/* 使能输出缓冲 */7 d3 G1 c4 @$ _) h, O& d8 D! B
  48. /* 不将DAC连接到片上外设 */
    $ S, x  w4 b, r7 B- }7 c& v3 e
  49. dac_ch_conf.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_DISABLE;     
    " S3 m/ u" n/ L& ?
  50. dac_ch_conf.DAC_UserTrimming = DAC_TRIMMING_FACTORY;  /* 使用出厂校准 */; s) [1 Z& X6 Z' V* v& W9 u' U
  51. /* DAC通道输出配置 */
    - X* Y/ C  c1 _
  52.     HAL_DAC_ConfigChannel(&g_dac_dma_handle, &dac_ch_conf, DAC_CHANNEL_1);  
    0 r6 j" Y4 k" |! C+ F4 p7 {
  53. }
复制代码

3 E' n( {: v& h7 F! |6 k( l该函数用于初始化DAC用DMA的方式输出正弦波。本函数用到的API函数起前面都介绍过,请结合前面介绍过的相关内容来理解源码。这里值得注意的是我们是采用定时器7触发DAC进行转换输出的。
& z6 Y& n5 u& ?) g9 V  A! o下面介绍DAC DMA使能波形输出函数,其定义如下:- U& A. l4 n! }. {, f

% }: ~& o2 ^* A- `" p4 g1 `
  1. /**% p! t* v/ C* r
  2. * @brief      DAC DMA使能波形输出
    : c- n; C; h( V& U8 u. s
  3. *   @note     TIM7的输入时钟频率(f)来自APB1, f = 120M * 2 = 240Mhz.
    2 z; ?! i0 u% L
  4. *              DAC触发频率 ftrgo = f / ((psc + 1) * (arr + 1))7 o3 a6 K" ?( T' c7 _# ]) }
  5. *              波形频率 = ftrgo / ndtr; , P! D9 _0 p$ |5 ]
  6. * @param       ndtr        : DMA通道单次传输数据量
    2 V' W1 ]: o! D
  7. * @param       arr         : TIM7的自动重装载值
    % W% P# N7 g' E; X
  8. * @param       psc         : TIM7的分频系数$ n' h3 c* H( l- n% Q, n
  9. * @retval      无
    " y1 ^6 t; v4 F5 B( P8 }/ N
  10. */. h2 h: N: D. D( F
  11. void dac_dma_wave_enable(uint16_t ndtr, uint16_t arr, uint16_t psc)  U& U, C; F: m' T$ {
  12. {  R+ r+ R/ g; I% @( N; ]
  13.     TIM_HandleTypeDef tim7_handle={0};: g3 N0 i8 C' \% i1 p+ c
  14. TIM_MasterConfigTypeDef master_config={0};: u% E! E' y* L  t0 q" H* c

  15.   @7 }9 t6 z' `4 G2 q
  16.     __HAL_RCC_TIM7_CLK_ENABLE();                                              /* TIM7时钟使能 */
    / G) G0 K8 e& i* }, X

  17. / h1 i3 a9 x+ U
  18.     tim7_handle.Instance = TIM7;                                    /* 选择定时器7 */: P8 u% I  Q- k; G) t- X9 g& O/ Y/ B
  19.     tim7_handle.Init.Prescaler = psc;                                 /* 分频系数 */
    7 h  e- S" U, l: u6 e+ f" F) p7 ^
  20.     tim7_handle.Init.CounterMode = TIM_COUNTERMODE_UP;             /* 递增计数 */
    ) F5 X0 J3 F9 S" c4 n. f3 P
  21. tim7_handle.Init.Period = arr;                                     /* 重装载值 */
    9 @. N: w" {; S1 I5 x
  22. /* 自动重装 */
    ) F4 s1 Q' s, q
  23.     tim7_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
    7 Z2 P# {. E, X) n# w9 d2 C- c
  24.     HAL_TIM_Base_Init(&tim7_handle);                            /* 初始化定时器7 */. X1 l+ Q, W8 l, o
  25.     master_config.MasterOutputTrigger = TIM_TRGO_UPDATE;
    8 q% C3 s# @. \& A# @
  26. master_config.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    7 w+ h, G2 ^; E; v8 U' r5 n
  27. /* 配置TIM7 TRGO */
    # G* {3 z! ?0 S. l/ v
  28.     HAL_TIMEx_MasterConfigSynchronization(&tim7_handle, &master_config);
    ! x+ y  K) @5 j. S: l3 j
  29.     HAL_TIM_Base_Start(&tim7_handle);                       /* 使能定时器7 */
    ; _* _% z: G+ u) z
  30.     HAL_DAC_Stop_DMA(&g_dac_dma_handle, DAC_CHANNEL_1); /* 先停止之前的传输 */
    & ~- s" V" o6 ?9 d) B; w
  31. HAL_DAC_Start_DMA(&g_dac_dma_handle, DAC_CHANNEL_1,
    0 b5 u2 W% {3 O' Y5 a
  32. (uint32_t *)g_dac_sin_buf, ndtr, DAC_ALIGN_12B_R);
    ) g2 t5 S1 i1 S" t# X
  33. }& K8 m% n8 Z9 M$ z7 l  B
复制代码
5 j& N: w& V9 a. f/ E7 Q
该函数用于使能波形输出,利用定时器7的更新事件来触发DAC转换输出。使能定时器7的时钟后,调用HAL_TIMEx_MasterConfigSynchronization函数配置TIM7选择更新事件作为触发输出 (TRGO),然后调用HAL_DAC_Stop_DMA函数停止DAC转换以及DMA传输,最后再调用HAL_DAC_Start_DMA函数重新配置并启动DAC和DMA。; U1 c" w1 p$ s+ T2 K. g
最后在main.c里面编写如下代码:
( c9 \8 b# i  @+ m, @5 M( r3 I7 M' B; B8 L4 G4 O0 E
  1. uint16_t g_dac_sin_buf[4096];    /* 发送数据缓冲区 */
    8 T% y, k( ^# @% g8 F# ~9 Z

  2. : c8 I4 O! K* A4 S
  3. /**" |' K2 `; @. @2 \1 Q( y2 G
  4. * @brief       产生正弦波序列  b: G' N. P0 S
  5. *   @note      需保证: maxval > samples/2
    1 m2 `8 D% D# P/ w
  6. *
    - I9 D( Q" E2 s* J8 [" G
  7. * @param       maxval : 最大值(0 < maxval < 2048); V$ ?9 w% @+ H2 j, M
  8. * @param       samples: 采样点的个数
    3 x, i( A$ u  d4 H" X/ Y# S! l0 r
  9. *9 J' Q8 L  K; Z8 z+ z* x8 d3 ]2 h
  10. * @retval      无3 D# u$ I* i) D8 n% S6 ~
  11. */
    ; u: _4 }: D, @* b
  12. void dac_creat_sin_buf(uint16_t maxval, uint16_t samples)
      X8 V3 }6 `# ]
  13. {
    # r6 c+ ?5 P2 H" K( Y2 X
  14.     uint8_t i;
    6 S  c, x$ p0 \( P, M% x0 }
  15.     float inc = (2 * 3.1415962) / samples; /* 计算增量(一个周期DAC_SIN_BUF个点)*/  d5 k% q% o- \5 X9 G
  16.     float outdata = 0;/ S/ v5 f. ?  C* u' o
  17.     for (i = 0; i < samples; i++)
    & c( p, Y2 P0 P* n) X1 m
  18. {! }4 T9 i0 K0 l: F% Y
  19. /* 计算以dots个点为周期的每个点的值,放大maxval倍,并偏移到正数区域 */
    , E  |5 r6 r* C1 r. Z! M
  20.         outdata = maxval * (1 + sin(inc * i));  
    8 i. s  B  e4 f8 B
  21.         if (outdata > 4095) outdata = 4095;     /* 上限限定 */
    ! l! h/ [4 H( t6 S4 u( S: T
  22.         //printf("%f\r\n",outdata);$ U# E! P; x$ ]# X2 B! Z
  23.         g_dac_sin_buf<i> = outdata;0 G$ ]( w; w9 I* i2 B; r4 n0 ~
  24.     }
    : q* I$ W+ R$ |3 V3 X6 p
  25. }' O/ d; u8 T9 j3 c3 F' [% o
  26. 4 S: o* }2 q  j4 k7 o
  27. /**
      O+ _' M! n& P1 [* z
  28. * @brief       通过USMART设置正弦波输出参数,方便修改输出频率., x( {0 [+ ~0 s% p% A3 _
  29. * @param       arr : TIM7的自动重装载值( N" T4 C, J( a& a! \( F+ m/ T
  30. * @param       psc : TIM7的分频系数- y8 ?% V, \' R1 s. W5 ^
  31. * @retval      无
    * l& Y# W) J% c* u
  32. */
    + T# \0 B' Z- K
  33. void dac_dma_sin_set(uint16_t arr, uint16_t psc)" e3 a4 n, r, {1 q
  34. {
    * z5 W( E! [2 g# r' A
  35.     dac_dma_wave_enable(100, arr, psc);% T7 z0 j) U) E* L; [' w
  36. }
    * ^+ L3 p+ u9 p1 i

  37. ; a0 P, I& `$ V$ w* v
  38. int main(void). ^  P+ d* p- a+ H% d
  39. {
    3 l7 x6 |9 K- G3 {
  40.     uint16_t adcx;, X  e  }4 c! v
  41.     float temp;
    " L$ a( r* E1 X! S7 e; [- h
  42.     uint8_t t = 0;5 ~& Z) v7 T0 y& I
  43.     uint8_t key;+ L1 g. q  ~6 k) z; V! _
  44.     sys_cache_enable();                            /* 打开L1-Cache */
    : j4 x+ g( P; r" d& P/ K
  45.     HAL_Init();                                      /* 初始化HAL库 */0 ?+ d- x# O2 ?+ `
  46.     sys_stm32_clock_init(240, 2, 2, 4);         /* 设置时钟, 480Mhz */' g3 e, {  K6 q
  47.     delay_init(480);                                /* 延时初始化 */
    7 \6 }2 t% `5 ?/ J$ Y$ I* \
  48.     usart_init(115200);                            /* 串口初始化为115200 */
    % @: }  D& w% I: _- Z
  49.     usmart_dev.init(240);                          /* 初始化USMART */
    / Y9 s, x9 `7 y+ h& j. t
  50.     mpu_memory_protection();                      /* 保护相关存储区域 */! o& t2 j8 @. w( U9 }. u
  51.     led_init();                                             /* 初始化LED */! R: P6 y/ ~6 t! J, X" O7 f
  52.     lcd_init();                                             /* 初始化LCD */  R; Z" `- _6 U- Q( y
  53.     key_init();                                             /* 初始化按键 */2 D2 S& m" B1 m2 D" i) ^. k7 Y4 \
  54.     adc_init();                                             /* 初始化ADC */
    & E- W4 V0 `& s7 H- N- `
  55.     dac_dma_wave_init();                                  /* 初始化DAC通道1 DMA波形输出 */" A$ v0 M! P' x$ w, j* x
  56.     lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    . ?7 i* w1 h2 S/ c- \" u
  57.     lcd_show_string(30, 70, 200, 16, 16, "DAC DMA Sine WAVE TEST", RED);  m$ r: j) ?3 s& G- R7 ~5 h. ^
  58.     lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);1 ^3 h. |* G7 ~3 Q
  59.     lcd_show_string(30, 110, 200, 16, 16, "KEY0:5Khz  KEY1:50Khz", RED);. |# |$ V% i1 e  [, N0 n! S
  60.     lcd_show_string(30, 130, 200, 16, 16, "DAC VAL:", BLUE);3 c6 M4 r5 A% A, ?
  61.     lcd_show_string(30, 150, 200, 16, 16, "DAC VOL:0.000V", BLUE);
    - J, d5 V% w" K
  62.     lcd_show_string(30, 170, 200, 16, 16, "ADC VOL:0.000V", BLUE);9 h+ @1 N0 y% ?. O- @2 ?( X
  63.     dac_creat_sin_buf(2048, 100);6 {+ c( x0 Z: v) m: Z6 D* ^% ?; W
  64.     dac_dma_wave_enable(100, 100 - 1, 24 - 1);2 d. o( `% j8 @% m* U$ ~% a7 t
  65.     while (1)
    5 p1 ]* ]1 h- W8 v" H( V
  66.     {4 |1 g* P( Z' C/ l! p( X
  67.         t++;5 p8 \& {; H& V7 W7 W. {% u" T
  68.         key = key_scan(0);  /* 按键扫描 */
    $ a' C( ^- v3 q9 _5 J& j1 N
  69.         if (key == KEY0_PRES)                             /* 高采样率 , 约5Khz波形 */
    , Y( ^+ N. i3 _$ h8 V) e% E  @
  70.         {
    2 S; p  C' i7 `3 ?
  71.             dac_creat_sin_buf(2048, 100);               /* 产生正弦波函序列 */
    8 R/ ^, [$ v& Q+ r% R
  72. /* 500Khz触发频率, 100个点, 得到最高5KHz的正弦波. */' m* v0 h8 u1 U% a% ]! |1 H
  73.             dac_dma_wave_enable(100, 20 - 1, 24 - 1);           
    1 z  e7 v0 `* y1 F- m: e
  74.         }: N2 f8 g  H2 g0 P$ `: M
  75.         else if (key == KEY1_PRES)                       /* 低采样率 , 约50Khz波形 */
    ( x: T0 Z0 ?" Q# z& o' H
  76.         {/ {& r# |, |2 `# B& L$ D
  77.             dac_creat_sin_buf(2048, 10);                /* 产生正弦波函序列 */
    ' j% O( [& p) x2 \* D4 e3 v  Q
  78. /* 500Khz触发频率, 10个点, 可以得到最高50KHz的正弦波. */
      l4 u; X. Q* v
  79.             dac_dma_wave_enable(10, 20 - 1, 24 - 1);            2 b& h! R3 J$ W! _# r
  80.         }- i2 }  W- Y. m! @1 T  N2 J6 V
  81.         adcx = DAC1->DHR12R1;                             /* 获取DAC1_OUT1的输出状态 */
    " K5 m: J; M" b/ {$ P/ r% ]
  82.         lcd_show_xnum(94, 130, adcx, 4, 16, 0, BLUE);/* 显示DAC寄存器值 */9 s: `5 @: Q& j5 ?& l8 ~- V
  83.         temp = (float)adcx * (3.3 / 4096);             /* 得到DAC电压值 */, W: I6 K$ O" _2 P; H$ l
  84.         adcx = temp;( c- g# p# |$ K0 G1 @+ d
  85.         lcd_show_xnum(94, 150, temp, 1, 16, 0, BLUE);/* 显示电压值整数部分 */" \  N& g  o) @5 G( Y: r! V
  86.         temp -= adcx;
    ) G8 e* l0 @/ ?" J# E" M
  87.         temp *= 1000;" [# C) I9 L; r+ Y. [" @
  88.         lcd_show_xnum(110, 150, temp, 3, 16, 0X80, BLUE);  /* 显示电压值的小数部分 */0 s- `5 ~$ q* D& k! r% g
  89.         adcx = adc_get_result_average(ADC_ADCX_CHY,20);/*得到ADC通道19的转换结果*/" K. h+ a/ k' k: ^' d/ k
  90.         temp = (float)adcx * (3.3 / 65536);      /* 得到ADC电压值(adc是16bit的) */4 l/ S. o" u1 W# W5 v
  91.         adcx = temp;. j4 `* x+ n! B
  92.         lcd_show_xnum(94, 170, temp, 1, 16, 0, BLUE);   /* 显示电压值整数部分 */& H3 m" _, F# P+ ?: l
  93.         temp -= adcx;1 r, h# ~% O1 [9 C
  94.         temp *= 1000;
    ( M) @( z! i& u
  95.         lcd_show_xnum(110, 170, temp, 3, 16, 0X80, BLUE);/* 显示电压值的小数部分 */
    ( ?* N8 w1 Y( @8 Y
  96.         if (t == 10)        /* 定时时间到了 */! I- y( z) ?1 \
  97.         {
    , K8 z1 P$ z, h0 f
  98.             LED0_TOGGLE();  /* LED0闪烁 */
    % s3 w% z: k$ h" ?) b6 }
  99.             t = 0;6 F. l* o, A1 q$ d
  100.         }$ l& _; W* G; t3 q9 A
  101.         delay_ms(5);
    7 X" g6 C8 z, d' g# F3 O7 s. L- K0 K
  102.     }% }3 q- v0 }( K* ^  \
  103. }. i/ t( C9 n9 `" r
  104. </i>
复制代码
9 {+ c3 l1 J8 Z
adc_init函数初始化ADC1,用于测量DAC通道1的电压值。
1 A6 \1 T1 Q9 m: h4 kdac_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的正弦波?
. @1 t6 l' }/ c( ]4 E1 L& m5 I% ^定时器7的溢出频率为100KHz,不记得怎么计算的朋友,请回顾基本定时器的相关内容,这里直接把公式列出:
) q! h) j! ^5 N& ATout= ((arr+1)(psc+1))/Tclk
; r/ k. w7 c% s9 f. U看到dac_dma_wave_enable(100, 100 - 1, 24 - 1);这个语句,第二个形参是自动重装载值,第三个形参是分频系数,那么代入公式,可得:
  W  ~$ ?: q# xTout= ((arr+1)(psc+1))/Tclk= ((99+1)*(23+1))/ 240MHZ= 0.00001s. [! q, b% d$ D. F" I3 F8 e
得到定时器的更新事件周期是0.00001秒,即更新事件频率为100KHz,也就得到DAC输出触发频率为100KHz。4 ^( e! x7 ~" k( n/ e
再结合总一个正弦波共有100个采样点,就可以得到正弦波的频率为100KHz/100 = 1KHz。! v4 j8 z$ Q- ]1 Z5 Y: N9 ?5 i
知道了正弦波的频率怎么来的,下面代码中,按下按键KEY0,得到5KHz的正弦波,按下按键KEY1,得到50KHz的正弦波,计算方法都一样的。
$ g7 L( ~3 q" Z7 ~dac_dma_sin_set函数可以通过USMART设置正弦波输出参数,方便修改输出频率。
+ }0 s$ c- v9 t( G! S  p34.4.4 下载验证
8 R7 D( ~& [* m" K下载代码后,可以看到LED0不停的闪烁,提示程序已经在运行了。LCD显示如图34.4.4.1所示:
9 y# m7 V" A# M1 D9 x) A, i9 A6 ?% Z+ u
6ecb324163ab49fba23267222746b741.png ) l1 M( ^, O, A

. i. b1 ^, }  x* U图34.4.4.1 DAC输出正弦波实验测试图
' D, G$ H! s& s9 U) h7 ~- g6 s0 B  L2 I上图是将跳线帽连接多功能端口P3的ADC和DAC两个排针,可以看到ADC VOL的值随着DAC的输出变化而变化,即ADC采集到的值是不停变化的。由于变化太快了,这样看不出采集到值形成什么波形,下面我们借用示波器来进行观察,首先将探头接到DAC的排针上。
7 W! \$ K8 t% a9 [7 H没有按下任何按键之前,默认是输出1KHz(100个采样点)的正弦波,如下图所示:) v9 N+ _# l9 w) B6 U/ }6 X" h
) X* ]3 ]1 K. j  e  G
b1cbd00ba2834db393cc496688ac5d56.png . q6 R. _# x5 t6 t# `! `

" N0 b/ J0 ]8 J图34.3.4.2 默认DAC输出的的正弦波$ H5 s: S" \- ~% w  X- F, X) T
当按下KEY0后,DAC输出5KHz(100个采样点)的正弦波,如下图所示:
  `% j7 ]; a2 k1 n1 n  K9 h$ u$ N- ~% x7 A7 d" C
df5214fceb534e30b03382eec300a27a.png
4 g8 H$ d1 D9 W  e: ]$ h& D; P" y7 B5 O! w7 A8 `: j5 e7 }
图34.3.4.3 按下KEY0,DAC输出的的正弦波
& H5 Y* H% d) Q# w/ c当按下KEY1后,DAC输出30KHz(10个采样点)的正弦波,如下图所示:
% L3 }. J. z0 \( R! L1 m8 R( p. v" J; P; N" A
edaab4ccd31145088df0e85b26f4bdd9.png
, \  V, E4 Q6 L- M9 V6 W) ?  f/ u# S
图34.3.4.4 按下KEY1,DAC输出的的正弦波
) j# _& n% w- ^4 h————————————————0 L9 P( y6 ~  V% F
版权声明:正点原子
: {* j% i* }5 r
( |3 G% E7 [2 Y* K
+ u4 n( m, r0 i5 t' Z( B9 C3 W/ [0 R4 X2 a
dfc1b296e0c24920b94b9228487074db.png
赞 收藏 评论0 发布时间:2022-10-7 21:37

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版