|
1.DAC数模转换 DAC(digital to analog converter)即数模转换器,它可以将数字信号转换为模拟信号,它的功能与ADC相反。在常见的数字信号系统中,大部分传感器信号被转化成电压信号,而ADC把电压模拟信号转换成易于计算机存储、处理的数字编码,由计算机处理完成后,再由DAC输出电压模拟信号来驱动某些执行器件,使人类易于感知。如音频信号的采集和还原就是这样的一个过程( [+ D$ R. }* ^* q
STM32F1的DAC模块是12位数字输入,电压输出型的DAC。可以配置为8位或12位模式,也可以与DMA控制器配合使用,DAC工作在12位模式下时,数据可以设置为左对齐或右对齐。DAC模块有2个输出通道,每个通道都有单独的转换器。在双DAC模式下,2个通道可以独立地进行转换,也可以同时进行转换并同步地更新2个通道的输出。DAC可以通过引脚输入参考电压VREF+以获得更精确的转换结果。DAC结构框图如下示: ! J5 ? T/ y, y0 `
) J$ D( d# p6 A: {8 T
VDDA和VSSA是DAC模块的供电引脚,VREF+是DAC模块的参考电压,DAC_OUTx是DAC的输出通道;当参考电压为VREF+时,DAC的输出电压是线性的(0~ VREF+),12位模式下DAC输出电压计算公式如下: DACx 输出电压 = VREF+ * ( DORx / 4095 ) DAC输出是受DORx 寄存器直接控制的,但是不能直接往DORx 寄存器写入数据,而是要通过DHRx间接的传给DORx 寄存器,实现对DAC输出的控制。如果未选择硬件触发,1个APB1时钟周期后,DHRx中存储的数据将自动转移到DORx 寄存器;如果选择硬件触发,将在3个APB1时钟周期后进行转移 当DORx加载了DHRx内容时,模拟输出电压将在一端时间tSETTING后可用,具体取决于电源电压和模拟输出负载,可以从数据手册查到tSETTING的典型值为3us,最大值为4us,因此DAC的转换速度最快是250K左右 s) `0 F% H6 }+ t4 G
. S2 }" d J; G; m9 Z& C6 a
! h9 ?! E1 e( g. _1 v9 m# L9 f, D3 L$ M5 ^
j6 W# m$ q( M DAC可通过外部事件(定时器、外部中断线)触发转换,外部触发源列表如下示
& [" e! N! V0 [: v+ e
- M/ B' p8 [3 m+ s1 u+ R
# x: j$ i: A( Y- g* j4 j, q8 K6 n
: ?8 M5 n1 P8 p$ S% Y z2.硬件设计
* ?% B3 ~3 u; U) q7 f8 K; V' X1 j本实验通过D1指示灯来提示系统运行状态,K_UP用来增加DAC输入值,K_DOWN用来减少DAC输入值,输入值的改变将控制DAC_OUT1的电压输出,通过USART1将输出的电压值打印出来 4 L/ i: X8 D4 R: p8 Y
指示灯D1 USART1串口 DAC_OUT1(PA4) K_UP和K_DOWN按键 8 t2 J x. D% G3 W
- [3 h' c6 e8 U5 D& ?* t
$ p7 C" b$ e% ?3 O8 v! L1 _, e3.硬件设计
, o7 b2 K2 i: u' H3.1 STM32CubeMX设置 # m1 ] H1 X% T8 P, {4 m: k5 L! V
1 g- x5 [+ W Z: Y3 f& J5 A( P* c; A% p, F
$ m% M j. V. z! l9 ?
0 R- b. o0 f8 p9 f0 k/ _ v
> RCC设置外接HSE,时钟设置为72M > PC0设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平 > USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位 > PA0设置为GPIO输入模式、下拉模式;PE3设置为GPIO输入模式、上拉模式 > 激活DAC_OUT1,关闭输出缓冲,不使用触发功能 ; Q4 { R7 {+ [
0 A T) S& G, R, i0 Q8 R
> 输入工程名,选择路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码
' ]. I% w' X+ [1 x# e! {" M
3.2 MDK-ARM软件编程 > 在dac.c文件中可以看到DAC初始化相关函数 - void MX_DAC_Init(void){2 k. }6 F$ S, \0 q5 U( W5 {+ B
- DAC_ChannelConfTypeDef sConfig = {0};1 u% r& r" U& c- `* ]; W0 v/ M n
- /** DAC Initialization */3 N/ ^" j( w1 f
- hdac.Instance = DAC;
3 F+ q1 {) B1 s - if (HAL_DAC_Init(&hdac) != HAL_OK){
7 W/ i+ T) Z: h3 O - Error_Handler();% G2 [4 I/ ?$ |. `# y
- }
) c" Z) c, |, D. j - /** DAC channel OUT1 config */
; h! j: t. s+ y6 b- G7 ~& [* i - sConfig.DAC_Trigger = DAC_TRIGGER_NONE; //不使用触发功能' Q9 L. R( p: a) _) J
- sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE; //输出缓冲关闭
1 z ^1 P |1 T A" M) F2 j) O - if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1) != HAL_OK){' D' I8 U2 q( l% Y8 Z0 i: _3 F5 l
- Error_Handler();
0 [3 _. X4 N H/ ]+ ] - }* k% F+ s3 G/ k4 h" L
- }2 F- m1 Z9 @6 u5 g9 Z
0 H7 V5 M/ W U$ l4 n- void HAL_DAC_MspInit(DAC_HandleTypeDef* dacHandle){
$ M, A7 v' j! X9 [, e - GPIO_InitTypeDef GPIO_InitStruct = {0};( h- G V- y9 j
- if(dacHandle->Instance==DAC) {* k. {& W4 n! d. N6 S& H+ v
- /* DAC clock enable *// ^) T3 t3 V9 i, C
- __HAL_RCC_DAC_CLK_ENABLE(); //使能DAC时钟
9 n/ {* C& o! t" \ - __HAL_RCC_GPIOA_CLK_ENABLE(); % p) b: ~! i. T5 T& Q8 Z
- GPIO_InitStruct.Pin = GPIO_PIN_4;
; [. n! l4 b( o V* W( e6 s" J% ]$ | - GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; //模拟
% l/ s2 u5 m2 |9 P: ^" J3 k - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+ |( s* i6 p& U; ] - }
- }9 r4 f8 O) C; U - }
复制代码 > 添加按键驱动文件key.c 和相关头文件key.h,参考按键输入例程
# u; b1 q- U# p( B. E; @* A
> 在main.c文件下编写DAC测试代码 - int main(void){4 F$ {+ j+ j( D/ F0 R! A, r
- /* USER CODE BEGIN 1 */
0 k* n4 C& h5 i- \7 @" g - uint16_t dac_val; //读取的DAC值
s3 c, Z- Q s( c5 a9 G - float dac_vol; //转换后的电压值 U6 `# B1 r# x# s; ^
- uint8_t t = 0;
- p/ {% Q0 e/ v' i* ?! a( v - uint16_t dac_setval = 0; //DAC设置值
. o" j2 n+ b# w1 e - uint8_t key;1 T k9 M9 ~/ O) U
- /* USER CODE END 1 */1 X3 x' d. C- g/ B' x, p+ T& w
- HAL_Init();4 q) Q& r+ I5 i+ J0 `$ g* q
- SystemClock_Config();
; z* J( v* H3 Z" f2 O1 W - MX_GPIO_Init();/ a9 ^4 u2 M5 F! E
- MX_DAC_Init();* w, R+ u. A- T: S- }
- MX_USART1_UART_Init();
9 X. Q. j3 x/ ~0 @$ B - /* USER CODE BEGIN 2 */5 k+ z' `/ X' b4 z% o
- HAL_DAC_Start(&hdac,DAC_CHANNEL_1); //开启DAC通道1# y7 |$ {1 j9 D1 O5 ]% w
- HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_R,0);//置初始值为0 e, u' C$ G3 W. x6 Z
- /* USER CODE END 2 */
- X! ]( B H* [8 k; z3 K - while (1){
" V2 d1 b' ^. v% q0 c - t++;
& `8 W4 n3 N& C5 P - key = KEY_Scan(0);" T$ Z# c, G# ^1 ?: u
- if(key == KEY_UP_PRES){
) l! T3 ?1 y0 E8 e4 E$ V - if(dac_setval < 4000)
$ I6 K0 t" h0 \! \; A - dac_setval += 200;
) h% H. p5 i& y9 k - HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_R,dac_setval);
/ ], [3 r' ^. b! C - }- O/ U# t3 G" g: h4 h& p
- else if(key == KEY_DOWN_PRES){
( P% g4 h: }. N1 X- Z - if(dac_setval > 200)
" }& |: a- d! Y) i5 J$ R2 v# f - dac_setval -= 200;) ]7 t( h- c& h; \1 y% y
- else7 _ J5 X( o; ? x
- dac_setval = 0;
0 v+ G8 H6 B4 D0 D - HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_R,dac_setval);
0 O& t0 j4 @$ {' D. X - }
9 O7 ]9 T1 h7 v3 j! ^ - /按键按下了或定时时间到
) l- O6 N3 T" u( w. \% v) o; b - if(t == 10 || key == KEY_UP_PRES || key == KEY_DOWN_PRES){ /
5 f; G% Q1 V/ {8 z6 U - dac_val = HAL_DAC_GetValue(&hdac,DAC_CHANNEL_1);
0 O1 b! S2 a2 i& y - printf("DAC_OUT1 DAC value: %d\r\n",dac_val);
9 U0 I* r2 u' ^ _- f5 L. l, Q - dac_vol = dac_val * (3.3/4096);
, Z6 Y' i0 Z* R9 H% d! V1 u - printf("DAC_OUT1 VOL value: %.2fV\r\n",dac_vol);+ U/ F3 \1 d' I! N9 y3 M
- printf("\r\n");
! X* f7 m4 \$ y9 g' g - t = 0;
2 Y( d% d4 [$ H. U - }
" L- A4 n) g" L, A+ _: I8 }, f - HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);
. n7 r: J# Q, B4 q8 } - HAL_Delay(100);
: `3 Z; R& l8 p' k5 I4 M( w9 m - }# c: ^ k$ d; ?% ^* E
- }
复制代码 : Y# G2 W7 v# h* a# q5 B
4.下载验证 编译无误后下载到开发板,可用看到D1指示灯不断闪烁,同时打印出DAC通道1的DAC值和电压值,当按下K_UP按键输出电压增大,按下K_DOWN按键输出电压减小 * Q! z9 p4 m, G* }, j2 E, Z
0 h# k: |" I" E* v* N& e) U
文章出处:嵌入式工程狮 安迪西
' |. F! z! h9 l
/ }% @8 d5 | ~$ S# ~7 h
7 T! A7 x2 r7 H: ~6 a4 m6 e4 p
" E# N" V8 f% R/ y7 ~( j" x |