38. DAC 您所在的位置:网站首页 正弦函数是什么样的 38. DAC

38. DAC

2024-07-12 05:20| 来源: 网络整理| 查看: 265

38.4.2.2. 代码分析¶

生成正弦波数据表

要输出正弦波,实质是要控制DAC以v=sin(t)的正弦函数关系输出电压,其中v为电压输出,t为时间。

而由于模拟信号连续而数字信号是离散的,所以使用DAC产生正弦波时,只能按一定时间间隔输出正弦曲线上的点, 在该时间段内输出相同的电压值,若缩短时间间隔,提高单个周期内的输出点数,可以得到逼近连续正弦波的图形, 见图 DAC按点输出正弦波数据 ,若在外部电路加上适当的电容滤波,可得到更完美的图形。

由于正弦曲线是周期函数,所以只需要得到单个周期内的数据后按周期重复即可,而单个周期内取样输出的点数又是有限的, 所以为了得到呈v=sin(t)函数关系电压值的数据通常不会实时计算获取,而是预先计算好函数单个周期内的电压数据表,并且转化成以DAC寄存器表示的值。

如sin函数值的范围为[-1: +1],而STM32的DAC输出电压范围为[0~3.3]V,按12位DAC分辨率表示的方法, 可写入寄存器的最大值为212 = 4096,即范围为[0:4096]。所以,实际输出时,会进行如下处理:

1) 抬升sin函数的输出为正值:v = sin(t)+1 , 此时,v的输出范围为[0:2];

2) 扩展输出至DAC的全电压范围: v = 3.3*(sin(t)+1)/2 , 此时,v的输出范围为[0:3.3],正是DAC的电压输出范围,扩展至全电压范围可以充分利用DAC的分辨率;

3) 把电压值以DAC寄存器的形式表示:Reg_val = 212/3.3 * v = 211*(sin(t)+1), 此时,存储到DAC寄存器的值范围为[0:4096];

4) 实践证明,在sin(t)的单个周期内,取32个点进行电压输出已经能较好地还原正弦波形, 所以在t∈[0:2π]区间内等间距根据上述Reg_val公式运算得到32个寄存器值,即可得到正弦波表;

5) 控制DAC输出时,每隔一段相同的时间从上述正弦波表中取出一个新数据进行输出, 即可输出正弦波。改变间隔时间的单位长度,可以改变正弦波曲线的周期。

为方便起见,我们使用了Python和Matlab脚本制作正弦波表,脚本的代码存储在本工程的目录下,感兴趣可以打开文件查看, 以下列出Python脚本代码,见 代码清单:DAC-2 。

代码清单:DAC-2 制作正弦波数据表的python脚本(工程目录下的sinWave.py文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59#! python3 #coding=utf-8 """ Python版本:3.x 外部库:matplotlib1.5.3、numpy1.11.2 运行方式: 在命令行中输入:python sinWave.py 运行结果: 命令行中会打印计算得的各点数据, 在当前目录下会生成py_dac_sinWav.c文件,包含上述数据, 并且会弹出描绘曲线的对话框。 """ import matplotlib.pyplot as plt import numpy as np import math #修改本变量可以更改点数,如16、32、64等 POINT_NUM = 32 pi = math.pi #一个周期 POINT_NUM 个点 n = np.linspace(0,2*pi,POINT_NUM) #计算POINT_NUM个点的正弦值 a = map(math.sin,n) r =[] for i in a: #调整幅值至在0~1区间 i+=1 #按3.3V电压调整幅值 i*= 3.3/2 #求取dac数值,12位dac LSB = 3.3V/2**12 ri = round(i*2**12/3.3) #检查参数 if ri >= 4095: ri = 4095 #得到dac数值序列 r.append( ri ) print(list(map(int,r))) #写入序列到文件 with open("py_dac_sinWav.c",'w',encoding= 'gb2312') as f: print(list(map(int,r)),file= f) #绘图 plt.plot(n,r,"-o") plt.show()

Python脚本的实现原理就是前面介绍的正弦波数据表的制作过程,运行后,该脚本把得到的正弦波表数据输出到目录下的py_dac_sinWav.c文件中, 见 代码清单:DAC-3 ,并且根据取样点描绘出示意图,见图 python脚本根据正弦波表描绘的曲线图 。 Matlab脚本原理相同,此处不再列出,实际上使用C语言也能制作正弦波表,只是画图不方便而已。

代码清单:DAC-3 生成的正弦波数据表¶ 1 2 3 4[2048, 2460, 2856, 3218, 3532, 3786, 3969, 4072, 4093, 4031, 3887, 3668, 3382, 3042, 2661, 2255, 1841, 1435, 1054, 714, 428, 209, 65, 3, 24, 127, 310, 564, 878, 1240, 1636, 2048]

DAC宏定义

制作好正弦波数据表后,开始使用MDK编写STM32的DAC工程,首先设置好相关的宏, 见 代码清单:DAC-4 。

代码清单:DAC-4 DAC宏定义(bsp_dac.h文件)¶ 1 2//DAC DHR12RD寄存器,12位、右对齐、双通道 #define DAC_DHR12RD_ADDRESS (DAC_BASE+0x20)

此处定义的宏DAC_DHR12RD_ ADDRESS是寄存器DHR12RD的地址,该寄存器是12位右对齐的双通道寄存器, 见图 DHR12RD寄存器说明 。在本实验中将会使用DMA把正弦波数据表的点数据赋值到该寄存器中, 往该寄存器赋值后的数据会在DAC被触发的时候搬运到2个DAC转换器,然后在这2个通道中输出以12位右对齐表示的这两个通道的电压。DAC中还有其它寄存器, 它们的功能类似,可以在《STM32中文参考手册》中了解到。

与DAC控制相关的引脚固定是PA4和PA5,就不使用宏定义了,在源代码中会直接使用引脚号操作。

DAC GPIO和模式配置

代码清单:DAC-5 DAC GPIO和模式配置¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41/** * @brief 使能DAC的时钟,初始化GPIO * @param 无 * @retval 无 */ static void DAC_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; DAC_InitTypeDef DAC_InitStructure; /* 使能GPIOA时钟 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); /* 使能DAC时钟 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE); /* DAC的GPIO配置,模拟输入 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure); /* 配置DAC 通道1 */ //使用TIM2作为触发源 DAC_InitStructure.DAC_Trigger = DAC_Trigger_T2_TRGO; //不使用波形发生器 DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; //不使用DAC输出缓冲 DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable; DAC_Init(DAC_Channel_1, &DAC_InitStructure); /*以同样的配置 初始化DAC 通道2 */ DAC_Init(DAC_Channel_2, &DAC_InitStructure); /* 使能通道1 由PA4输出 */ DAC_Cmd(DAC_Channel_1, ENABLE); /* 使能通道2 由PA5输出 */ DAC_Cmd(DAC_Channel_2, ENABLE); /* 使能DAC的DMA请求 */ DAC_DMACmd(DAC_Channel_2, ENABLE); }

在DAC_Config函数中,完成了DAC通道的GPIO的初始化和DAC模式配置 。 其中GPIO按照要求被配置为模拟输入模式(没有模拟输出模式),在该模式下才能正常输出模拟信号。

配置DAC工作模式时,则使用了DAC_InitTypeDef 类型的初始化结构体,把DAC通道1和2都配置成了使用定时器TIM2触发、 不使用波形发生器以及不使用DAC输出缓冲的模式。

初始化完GPIO和DAC模式后,还使用了DAC_Cmd、DAC_DMACmd函数使能了通道以及DMA的请求。由于本实验中对DAC1和2的操作是同步的, 所以只要把DMA与DAC通道2关联起来即可,当使用DMA设置通道2的数据值时,同时更新通道1的内容。

定时器配置及计算正弦波的频率

初始化完DAC后,需要配置触发用的定时器,设定每次触发的间隔,以达到控制正弦波周期的目的。

代码清单:DAC-6 配置定时器¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29/** * @brief 配置TIM * @param 无 * @retval 无 */ static void DAC_TIM_Config(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; /* 使能TIM2时钟,TIM2CLK 为72M */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); /* TIM2基本定时器配置 */ //定时周期 20 TIM_TimeBaseStructure.TIM_Period = (20-1); //预分频,不分频 72M / (0+1) = 72M TIM_TimeBaseStructure.TIM_Prescaler = 0x0; //时钟分频系数 TIM_TimeBaseStructure.TIM_ClockDivision = 0x0; //向上计数模式 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); /* 配置TIM2触发源 */ TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); /* 使能TIM2 */ TIM_Cmd(TIM2, ENABLE); }

因为前面的DAC配置了TIM2当触发源,所以这里将对TIM2进行配置。TIM2的定时周期被配置为20,向上计数,不分频。 即TIM2每隔20*(1/72M)秒就会触发一次DAC事件,作DAC触发源使用的定时器并不需要设置中断,当定时器计数器向上计数至指定的值时, 产生Update事件,同时触发DAC把DHRx寄存器的数据转移到DORx,从而开始进行转换。

根据定时器的配置,可推算出正弦波频率的计算方式:

按默认配置,STM32系统时钟周期为:

\(T_{\text{systick}} = 1/72000000\),

定时器TIM2的单个时钟周期:

\(T_{\text{tim}} = (TIM\_ Prescaler + 1) \times T_{\text{systick}}\),

定时器触发周期:

\(T_{\text{update}} = \ (TIM\_ Period + 1) \times T_{\text{tim}}\),

根据正弦波单个周期的点数N,求出正弦波单个周期时间为:

\(T_{\sin} = T_{\text{update}} \times N\),

对应正弦波的频率为:

\[f_{\sin} = \frac{1}{T_{\sin}} = \frac{1}{T_{\text{systick}} \times (TIM\_ Prescaler + 1) \times (TIM\_\text{Period} + 1) \times N}\]

根据上述公式,代入本工程的配置,可得本实验中的正弦波频率为112500:

\[f_{\sin} = \frac{1}{T_{\sin}} = \frac{72000000}{(0 + 1) \times (19 + 1) \times 32} = 112500\]

在实际应用中,可以根据工程里的正弦波点数和定时器配置生成特定频率的正弦波。

DMA配置

本工程的数据传输由DMA完成,其代码见 代码清单:DAC-7 。

代码清单:DAC-7 DMA配置¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74//正弦波单个周期的点数 #define POINT_NUM 32 /* 波形数据 -----------------------------------------------*/ const uint16_t Sine12bit[POINT_NUM] = { 2048 , 2460 , 2856 , 3218 , 3532 , 3786 , 3969 , 4072 , 4093 , 4031 , 3887 , 3668 , 3382 , 3042 , 2661 , 2255 , 1841 , 1435 , 1054 , 714 , 428 , 209 , 65 , 3 , 24 , 127 , 310 , 564 , 878 , 1240 , 1636 , 2048 }; uint32_t DualSine12bit[POINT_NUM]; /** * @brief DAC初始化函数 * @param 无 * @retval 无 */ void DAC_Mode_Init(void) { uint32_t Idx = 0; DAC_Config(); DAC_TIM_Config(); /* 填充正弦波形数据,双通道右对齐*/ for (Idx = 0; Idx


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有