DAC,即Digital to Analog
Convertor,是数字到模拟转换器,也称为D/A转换器。其核心部分由R-2R电阻网络(也称倒T型电阻网络)、模拟开关和运算放大器组成。它可以将二进制码或BCD码表示的数字量转换为与其成正比的模拟量输出。
DAC的工作原理主要包括数字信号采样、量化、编码和模拟信号输出几个步骤。首先,将连续变化的模拟信号在一定的时间间隔内进行离散取样,即数字信号采样。接着,对采样后的数字信号进行量化,将其转换为离散的数值。然后,通过编码将量化后的数值进行转换,以便DAC能够识别和处理。最后,DAC将这些数字信号转换为模拟信号输出。
DAC是数字系统和模拟系统之间的桥梁,具有广泛的应用领域。在音频处理中,DAC被用来将数字音频信号转换为模拟音频信号,以驱动扬声器和耳机,其性能对音频质量有着决定性的影响。在通信系统中,DAC用于将数字信号转换为模拟信号,以实现信号调制和解调。此外,在仪器仪表领域,DAC也被广泛应用于各种测量和控制设备中。
STM32中的DAC(视芯片而定例如F407ZGT6是2个DAC通道,有些芯片不支持DAC功能)支持12模式的数据输入,可以双通道同时转换。
本期我们将介绍如何使用STM32F407和CubeMX利用HAL库实现DAC的输出。(本来是想使用C8T6的,结果突然想起来C8T6)没有DAC。
CubeMX配置
在DAC通道中开启DAC,在F407ZGT6中DAC1对应PA4,DAC2对应PA5。
OutputBuffer这里设置DAC的输出缓存使能,Trigger是DAC是触发方式,这里我们选择不触发(手动写入)。
如果选择触发的话是从缓存区写入数据。
这里顺带加一路ADC采样,因为手上没有示波器,所以使用ADC来进行查看。 这里使用DMA进行采样,具体可以参考公众号之前的关于DMA+ADC采样的内容。
但是相比于之前的,需要更改一些设置,首先是DMA的设置
这里设置单词请求,而不是循环模式防止数据跑飞掉。
MX_GPIO_Init(); MX_DMA_Init(); MX_ADC1_Init(); MX_DAC_Init(); MX_USART1_UART_Init(); MX_TIM1_Init(); MX_TIM2_Init(); /* USER CODE BEGIN 2 */ HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); //用来触发adc采样 HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC HAL_DAC_Start(&hdac,DAC_CHANNEL_1); /* USER CODE END 2 */
/* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */
/* USER CODE BEGIN 3 */ if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)) { HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
HAL_Delay(20); while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)); HAL_Delay(1000); for(int i = 0;i<ADLenth;i++) { printf("A:%drn",AD_Value[i]); } } } /* USER CODE END 3 */}
我们编写一段代码。
按下按键的时候设置DAC的值,开启DMA传输,再加上ADC采样,之后输出结果。
可以看到,我们按下按键之后,ADC的值会呈阶梯状上升。
- 三角波发生器
在DAC配置中打开波形发生器,触发方式选择定时器2触发,三角波最大振幅511。
之后开启定时器2,由于ADC的采样率是1000HZ,因此根据奈奎斯特采样定律,信号的最大频率不能超过500HZ,因此我们使用100HZ的三角波。
定时器设置好时间之后,设置触发事件。
芯片手册中简单的介绍了一下如何计算三角波的频率。当触发信号发生后,内部计数器的值就会+1,所以频率可以用如下公式计算:
定时器频率/分频系数+1/Period/三角波最大值/2
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); //用来触发adc采样 HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); //用来触发DAC输出 HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC HAL_DAC_Start(&hdac,DAC_CHANNEL_1); HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_L,0);//设置直流信号
- ** 正弦波发生器 **
首先是定时器触发(方便控制频率)。但是关闭波形发生器。
添加DMA,模式选择循环模式。 还有改变一下定时器的频率!
其他几乎不做改变。
#define POINTS 256 #define MIN_VALUE 50 #define MAX_VALUE 650 #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0) #define OFFSET 50 #define M_PI 3.14159265 uint16_t SinWaveInt[POINTS]; int SinWave[POINTS]; void SinInit(void) { for (int i = 0; i < POINTS; i++) { double x = ((double)i / (POINTS - 1)) * 2 * M_PI; double sin_value = sin(x); // 计算正弦值 SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET); SinWaveInt[i] = (uint16_t)SinWave[i]; } }
计算一个正弦表。
这里的Points决定了分辨率,结合定时器触发频率决定了正弦波的信号。
测试一下正弦表,输出的是正弦信号。 我们之后将正弦信号表导入DMA中。
SinInit(); HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)SinWave, POINTS, DAC_ALIGN_12B_R);
正弦表初始化,之后启动DMA传输,导入正弦信号。
测试正弦信号成功。
- FM调制
#define POINTS 1024 #define MIN_VALUE 50 #define MAX_VALUE 650 #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0) #define OFFSET 50 #define M_PI 3.14159265 uint16_t SinWaveInt[POINTS]; int SinWave[POINTS]; void SinInit(void) { for (int i = 0; i < POINTS; i++) { double x = ((double)i / (POINTS/4 - 1)) * 2 * M_PI; double sin_val = sin(x)*sin(x/4); // 计算正弦值 SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET); SinWaveInt[i] = (uint16_t)SinWave[i]; } }
拓宽点数,加上载波,即可构成FM调制信号。