DAC 简介
DAC 输出实验
DAC 寄存器
⚫ DAC 控制寄存器(DAC_CR)
DAC_CR 的低 16 位用于控制通道 1,高 16 位用于控制通道 2,下面介绍本实验需要设置 的一些位:
EN1 位用于 DAC 通道 1 的使能,我们需要用到 DAC 通道 1 的输出,该位必须设置为 1。
BOFF1 位用于 DAC 输出缓存控制,这里 STM32 的 DAC 输出缓存做的有些不好,如果使 能的话,虽然输出能力强一点,但是输出没法到 0,这是个很严重的问题。所以本章的三个实验 我们都不使用输出缓存,即该位设置为 1。
TEN1 位用于 DAC 通道 1 的触发使能,我们设置该位为 0,不使用触发。写入 DHR1 的值 会在 1 个 APB1 周期后传送到 DOR1,然后输出到 PA4 口上。
TSEL1[2:0]位用于选择 DAC 通道 1 的触发方式,这里我们没有用到外部触发,所以这几位 设置为 0 即可。
WAVE1[1:0]位用于控制 DAC 通道 1 的噪声/波形输出功能,我们这里没用到波形发生器, 所以默认设置为 00,不使能噪声/波形输出。
MAMP[3:0]位是屏蔽/幅值选择器,用来在噪声生成模式下选择屏蔽位,在三角波生成模式 下选择波形的幅值。本实验没有用到波形发生器,所以设置为 0 即可。
DMAEN1 位用于 DAC 通道 1 的 DMA 使能,本实验没有用到 DMA 功能,所以设置为 0。
⚫ DAC 通道 1 12 位右对齐数据保持寄存器(DAC_DHR12R1)
我们只需要通过杜邦线连接 ADC 和 DAC,就可以使得 ADC1 通道 1(PA1)和 DAC 通 道 1(PA4)连接起来。对应的硬件连接如图 33.2.2.2 所示:
代码
#include "./BSP/DAC/dac.h"
DAC_HandleTypeDef g_dac_handle;
/* DAC初始化函数 */
void dac_init(void)
{
DAC_ChannelConfTypeDef dac_ch_conf = {0};
g_dac_handle.Instance = DAC;
HAL_DAC_Init(&g_dac_handle);/* 初始化 DAC */
dac_ch_conf.DAC_Trigger = DAC_TRIGGER_NONE;/* 不使用触发功能,(自动模式) */
dac_ch_conf.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE;/* DAC1 输出缓冲关闭 */
HAL_DAC_ConfigChannel(&g_dac_handle, &dac_ch_conf, DAC_CHANNEL_1);
HAL_DAC_Start(&g_dac_handle, DAC_CHANNEL_1);/* 开启 DAC 通道 1 */
}
/* DAC MSP底层初始化函数 */
void HAL_DAC_MspInit(DAC_HandleTypeDef *hdac)
{
if(hdac->Instance == DAC)
{
GPIO_InitTypeDef gpio_init_struct = {0};
__HAL_RCC_DAC_CLK_ENABLE();/* 使能 DAC1 的时钟 */
__HAL_RCC_GPIOA_CLK_ENABLE();;/* 使能 DAC OUT1/2 的 IO 口时钟(都在 PA 口,PA4/PA5) */
gpio_init_struct.Pin = GPIO_PIN_4;
gpio_init_struct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(GPIOA, &gpio_init_struct);
}
}
/* 设置通道输出电压,(vol : 0~3300,代表 0~3.3V) */
void dac_set_voltage(uint16_t vol)
{
double temp = vol;
temp /= 1000;
temp = temp * 4096 / 3.3;
if(temp >= 4096)
{
temp = 4095;/* 如果值大于等于 4096, 则取 4095 */
}
HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, temp);
}
该函数主要调用 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 两个通道的初始化,就没有用到该函数。
该函数实际就是将电压值转换为 DAC 输入值,形参 1 设置要输出 的电压值,设置的范围:0~3300,代表 0~3.3V。
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/ADC/adc.h"
#include "./BSP/DAC/dac.h"
int main(void)
{
uint16_t adcx;
float temp;
HAL_Init(); /* 初始化 HAL 库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 传口初始化 */
led_init(); /* LED初始化 */
lcd_init(); /* LCD初始化 */
adc_init(); /* ADC初始化 */
dac_init(); /* DAC初始化 */
dac_set_voltage(2000);
lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
lcd_show_string(30, 70, 200, 16, 16, "ADC TEST", RED);
lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
lcd_show_string(30, 110, 200, 16, 16, "ADC1_CH1_VAL:", BLUE);
lcd_show_string(30, 130, 200, 16, 16, "ADC1_CH1_VOL:0.000V", BLUE); /* 先在固定位置显示小数点 */
while(1)
{
adcx = adc_get_result_average(10);/* 获取通道1的转换值,10次取平均 */
lcd_show_xnum(134, 110, adcx, 5, 16, 0, BLUE); /* 显示ADCC采样后的原始值 */
temp = (float)adcx * (3.3 / 4096);/* 获取计算后的带小数的实际电压值,比如3.1111 */
adcx = temp;/* 赋值整数部分给adcx变量,因为adcx为u16整形 */
lcd_show_xnum(134, 130, adcx, 1, 16, 0, BLUE);/* 显示电压值的整数部分,3.1111的话,这里就是显示3 */
temp -=adcx;/* 把已经显示的整数部分去掉,留下小数部分,比如3.1111-3=0.1111 */
temp *= 1000;/* 小数部分乘以1000,例如:0.1111就转换为111.1,相当于保留三位小数。 */
lcd_show_xnum(150, 130, temp, 3, 16, 0X80, BLUE); /* 显示小数部分(前面转换为了整形显示),这里显示的就是111. */
LED0_TOGGLE();
delay_ms(100);
}
}