一:介绍
1:简历
2:简图
F1,F4,F7的DAC框架图都一样。
触发源:
宏定义补全及解释
#define DAC_TRIGGER_NONE 0x00000000UL /*!< 转换是自动的,一旦DAC1_DHRxxxx寄存器被加载,不由外部触发 */
#define DAC_TRIGGER_T2_TRGO (DAC_CR_TSEL1_2 | DAC_CR_TEN1) /*!< TIM2 TRGO选为DAC通道的外部转换触发 */
#define DAC_TRIGGER_T4_TRGO (DAC_CR_TSEL1_2 | DAC_CR_TSEL1_0 | DAC_CR_TEN1) /*!< TIM4 TRGO选为DAC通道的外部转换触发 */
#define DAC_TRIGGER_T5_TRGO (DAC_CR_TSEL1_1 | DAC_CR_TSEL1_0 | DAC_CR_TEN1) /*!< TIM5 TRGO选为DAC通道的外部转换触发(注意:这里应该是TIM5,而不是TIM3) */
#define DAC_TRIGGER_T6_TRGO (DAC_CR_TEN1) /*!< 转换由软件触发为DAC通道 */
#define DAC_TRIGGER_T7_TRGO (DAC_CR_TSEL1_1 | DAC_CR_TEN1) /*!< TIM7 TRGO选为DAC通道的外部转换触发 */
#define DAC_TRIGGER_T8_TRGO (DAC_CR_TSEL1_0 | DAC_CR_TEN1) /*!< TIM8 TRGO选为DAC通道的外部转换触发 */
#define DAC_TRIGGER_EXT_IT9 (DAC_CR_TSEL1_2 | DAC_CR_TSEL1_1 | DAC_CR_TEN1) /*!< EXTI Line9事件选为DAC通道的外部转换触发 */
#define DAC_TRIGGER_SOFTWARE (DAC_CR_TEN1) /*!< 软件触发DAC转换 */
解释
- DAC_TRIGGER_NONE:没有外部触发,转换是自动的,一旦DAC的数据寄存器被写入新的数据,就会启动转换。
- DAC_TRIGGER_T2_TRGO 至 DAC_TRIGGER_T8_TRGO:这些宏定义分别选择TIM2到TIM8的TRGO(触发输出)作为DAC的外部转换触发源。这意味着DAC的转换将由指定定时器的特定事件(如更新事件)来触发。
- DAC_TRIGGER_EXT_IT9:选择外部中断/事件线9(EXTI Line9)的触发事件作为DAC的外部转换触发源。这允许DAC的转换由外部事件(如按钮按下)触发。
- DAC_TRIGGER_SOFTWARE:软件触发DAC转换。这意味着可以通过编程方式(即写入一个特定的寄存器值)来启动DAC的转换,而不是依赖于外部事件或定时器。
请注意,对于
DAC_TRIGGER_T5_TRGO
的注释中提到的TIM3可能是一个错误,因为根据宏定义中的位操作,它实际上对应的是TIM5的TRGO(假设DAC_CR_TSEL1_1
和DAC_CR_TSEL1_0
分别对应于选择触发源的位字段)。此外,DAC_TRIGGER_T6_TRGO
实际上并不直接对应于任何定时器的TRGO,而是直接由软件触发(即DAC_CR_TEN1
位被设置),这与DAC_TRIGGER_SOFTWARE
相同,但通常DAC_TRIGGER_SOFTWARE
的命名更直接地表达了其意图。
具体的TRGO可以参考我的:
3:输入/出电压
可以参考我们写的ADC的:
4:工作原理
5:电压计算
Vref+一般为3.3V
6:配置
想要注意的寄存器:
使用DAC1右对齐输出电压的时候,实际就是对于下面寄存器的写入。
使用不同的ADC和不同的对齐方式,它对于的寄存器不同
7:HAL配置
二:案列
下面的案列全部使用stm32F407VEt6做的
A:DAC简单输出
#include "stm32f4xx.h" // Device header
#include "LED.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
DAC_HandleTypeDef g_dac_handle;
/* DAC初始化函数 */
void dac_init(void)
{
DAC_ChannelConfTypeDef dac_ch_conf;
g_dac_handle.Instance = DAC1;
HAL_DAC_Init(&g_dac_handle); /* 初始化DAC */
dac_ch_conf.DAC_Trigger = DAC_TRIGGER_NONE; /* 不使用触发功能 */
dac_ch_conf.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE; /* DAC输出缓冲关闭 */
HAL_DAC_ConfigChannel(&g_dac_handle, &dac_ch_conf, DAC_CHANNEL_1); /* 配置DAC通道1 */
HAL_DAC_Start(&g_dac_handle, DAC_CHANNEL_1); /* 开启DAC通道1 */
}
/* DAC MSP初始化函数 */
void HAL_DAC_MspInit(DAC_HandleTypeDef *hdac)
{
if (hdac->Instance == DAC1)
{
GPIO_InitTypeDef gpio_init_struct;
__HAL_RCC_DAC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
gpio_init_struct.Pin = GPIO_PIN_4;
gpio_init_struct.Mode = GPIO_MODE_ANALOG; //模拟输入
HAL_GPIO_Init(GPIOA, &gpio_init_struct);
}
}
/* 设置通道输出电压
vol:想要设置的电压,单位MV
DAC的输出电压=(DORX/4096)*Vref+ ---->Vref+一般为3.3V
DORX=DAC的输出电压/Vref*4096;
*/
void dac_set_voltage(float vol)
{
float temp = vol;
temp /= 1000;
temp = temp * 4096.0 / 3.3; //temp-->DORX寄存器的值
if (temp >= 4096)temp = 4095; /* 如果值大于等于4096, 则取4095 */
HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, temp); /* 12位右对齐数据格式设置DAC值 */
}
#include "stm32f4xx.h" // Device header
#include "sys.h"
#include "delay.h"
#include "LED.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "adc.h"
#include "DAC.h"
extern DAC_HandleTypeDef g_dac_handle;
uint16_t adc_Value;
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
Clock_HSE_Init(8,336,RCC_PLLP_DIV2,7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
Uart_Init(115200);
adc_init();
dac_init();
dac_set_voltage(2200); //设置DAC的电压为2200MV=2.2
while(1)
{
printf("DORX:%d\r\n",HAL_DAC_GetValue(&g_dac_handle,DAC_CHANNEL_1));
adc_Value=adc_get_result();
printf("电压:%f %2d\r\n",(float)adc_Value*(3.3/4096),adc_Value);
delay_ms(1000);
}
}
可以直接属于我们的万用表测量电压。
也可以使用我们的ADC测量电压,使用ADC时,注意我们的数据类型。
B:输出三角波
和上面的A实验一样,什么都没有改变,只有增加了一个 dac_triangular_wave()函数。
#include "stm32f4xx.h" // Device header
#include "LED.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "delay.h"
DAC_HandleTypeDef g_dac_handle;
/* DAC初始化函数 */
void dac_init(void)
{
DAC_ChannelConfTypeDef dac_ch_conf;
g_dac_handle.Instance = DAC1;
HAL_DAC_Init(&g_dac_handle); /* 初始化DAC */
dac_ch_conf.DAC_Trigger = DAC_TRIGGER_NONE; /* 不使用触发功能 */
dac_ch_conf.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE; /* DAC输出缓冲关闭 */
HAL_DAC_ConfigChannel(&g_dac_handle, &dac_ch_conf, DAC_CHANNEL_1); /* 配置DAC通道1 */
HAL_DAC_Start(&g_dac_handle, DAC_CHANNEL_1); /* 开启DAC通道1 */
}
/* DAC MSP初始化函数 */
void HAL_DAC_MspInit(DAC_HandleTypeDef *hdac)
{
if (hdac->Instance == DAC1)
{
GPIO_InitTypeDef gpio_init_struct;
__HAL_RCC_DAC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
gpio_init_struct.Pin = GPIO_PIN_4;
gpio_init_struct.Mode = GPIO_MODE_ANALOG; //模拟输入
HAL_GPIO_Init(GPIOA, &gpio_init_struct);
}
}
/* 设置通道输出电压
vol:想要设置的电压,单位MV
DAC的输出电压=(DORX/4096)*Vref+ ---->Vref+一般为3.3V
DORX=DAC的输出电压/Vref*4096;
*/
void dac_set_voltage(float vol)
{
float temp = vol;
temp /= 1000;
temp = temp * 4096.0 / 3.3; //temp-->DORX寄存器的值
if (temp >= 4096)temp = 4095; /* 如果值大于等于4096, 则取4095 */
HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, temp); /* 12位右对齐数据格式设置DAC值 */
}
/**
* @brief 设置DAC_OUT1输出三角波
* @note 输出频率 ≈ 1000 / (dt * samples) Khz, 不过在dt较小的时候,比如小于5us时, 由于delay_us
* 本身就不准了(调用函数,计算等都需要时间,延时很小的时候,这些时间会影响到延时), 频率会偏小.
*
* @param maxval : 最大值(0 < maxval < 4096), (maxval + 1)必须大于等于samples/2
* @param dt : 每个采样点的延时时间(单位: us)
* @param samples: 采样点的个数, samples必须小于等于(maxval + 1) * 2 , 且maxval不能等于0
* @param n : 输出波形个数,0~65535
*
* @retval 无
*/
void dac_triangular_wave(uint16_t maxval, uint16_t dt, uint16_t samples, uint16_t n)
{
uint16_t i, j;
float incval; /* 递增量 */
float Curval; /* 当前值 */
if((maxval + 1) <= samples) return ; /* 数据不合法 */
incval = (maxval + 1) / (samples / 2); /* 计算递增量 */
for(j = 0; j < n; j++)
{
Curval = 0;
HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, Curval); /* 先输出0 */
for(i = 0; i < (samples / 2); i++) /* 输出上升沿 */
{
Curval += incval; /* 新的输出值 */
HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, Curval);
delay_us(dt);
}
for(i = 0; i < (samples / 2); i++) /* 输出下降沿 */
{
Curval -= incval; /* 新的输出值 */
HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, Curval);
delay_us(dt);
}
}
}
#include "stm32f4xx.h" // Device header
#include "sys.h"
#include "delay.h"
#include "LED.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "adc.h"
#include "DAC.h"
extern DAC_HandleTypeDef g_dac_handle;
uint16_t adc_Value;
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
Clock_HSE_Init(8,336,RCC_PLLP_DIV2,7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
Uart_Init(115200);
adc_init();
dac_init();
dac_set_voltage(2200); //设置DAC的电压为2200MV=2.2
while(1)
{
dac_triangular_wave(4095, 5, 2000, 100); /* 幅值4095, 采样点间隔5us, 2000个采样点, 100个波形 */
}
}
// t++;
// key = key_scan(0); /* 按键扫描 */
// if (key == KEY0_PRES) /* 高采样率 , 约100Hz波形 */
// {
// lcd_show_string(30, 130, 200, 16, 16, "DAC Wave1 ", BLUE);
// dac_triangular_wave(4095, 5, 2000, 100); /* 幅值4095, 采样点间隔5us, 2000个采样点, 100个波形 */
// lcd_show_string(30, 130, 200, 16, 16, "DAC None ", BLUE);
// }
// else if (key == KEY1_PRES) /* 低采样率 , 约100Hz波形 */
// {
// lcd_show_string(30, 130, 200, 16, 16, "DAC Wave2 ", BLUE);
// dac_triangular_wave(4095, 500, 20, 100); /* 幅值4095, 采样点间隔500us, 20个采样点, 100个波形 */
// lcd_show_string(30, 130, 200, 16, 16, "DAC None ", BLUE);
// }
// if (t == 10) /* 定时时间到了 */
// {
// LED0_TOGGLE(); /* LED0闪烁 */
// t = 0;
// }
// delay_ms(10);
// }
C:正弦波
#include "stm32f4xx.h" // Device header
#include "LED.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "delay.h"
#include "math.h"
DAC_HandleTypeDef g_dac_handle;
DMA_HandleTypeDef g_dma_dac_handle;
uint16_t g_dac_sin_buf[4096]; /* 发送数据缓冲区 */
/* DAC初始化函数 */
void dac_init(void)
{
DAC_ChannelConfTypeDef dac_ch_conf;
g_dac_handle.Instance = DAC1;
HAL_DAC_Init(&g_dac_handle); /* 初始化DAC */
dac_ch_conf.DAC_Trigger = DAC_TRIGGER_T5_TRGO; /* TIM5触发功能 */
dac_ch_conf.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE; /* DAC输出缓冲关闭 */
HAL_DAC_ConfigChannel(&g_dac_handle, &dac_ch_conf, DAC_CHANNEL_1); /* 配置DAC通道1 */
HAL_DAC_Start(&g_dac_handle, DAC_CHANNEL_1); /* 开启DAC通道1 */
}
/* DAC MSP初始化函数 */
void HAL_DAC_MspInit(DAC_HandleTypeDef *hdac)
{
if (hdac->Instance == DAC1)
{
GPIO_InitTypeDef gpio_init_struct;
__HAL_RCC_DMA2_CLK_ENABLE();
__HAL_RCC_DAC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
gpio_init_struct.Pin = GPIO_PIN_4;
gpio_init_struct.Mode = GPIO_MODE_ANALOG; //模拟输入
HAL_GPIO_Init(GPIOA, &gpio_init_struct);
g_dma_dac_handle.Instance =DMA1_Stream5 ;
g_dma_dac_handle.Init.Direction = DMA_MEMORY_TO_PERIPH; /*内存--外设*/
g_dma_dac_handle.Init.PeriphInc = DMA_PINC_DISABLE; /*外设不自增*/
g_dma_dac_handle.Init.MemInc = DMA_MINC_ENABLE; /*内存自增*/
g_dma_dac_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; /*外设16为*/
g_dma_dac_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; /*内存16为*/
g_dma_dac_handle.Init.Mode = DMA_CIRCULAR; /*循环模式*/
g_dma_dac_handle.Init.Priority = DMA_PRIORITY_MEDIUM;
HAL_DMA_Init(&g_dma_dac_handle);
__HAL_LINKDMA(&g_dac_handle, DMA_Handle1, g_dma_dac_handle);
/*
DstAddress这是目标内存缓冲区的地址:
*/
HAL_DMA_Start(&g_dma_dac_handle, (uint32_t)g_dac_sin_buf, (uint32_t)&DAC1->DHR12R1, 0);
}
}
/* 设置通道输出电压
vol:想要设置的电压,单位MV
DAC的输出电压=(DORX/4096)*Vref+ ---->Vref+一般为3.3V
DORX=DAC的输出电压/Vref*4096;
*/
void dac_set_voltage(float vol)
{
float temp = vol;
temp /= 1000;
temp = temp * 4096.0 / 3.3; //temp-->DORX寄存器的值
if (temp >= 4096)temp = 4095; /* 如果值大于等于4096, 则取4095 */
HAL_DAC_SetValue(&g_dac_handle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, temp); /* 12位右对齐数据格式设置DAC值 */
}
/**
* @brief 产生正弦波序列函数
* @note 需保证: maxval > samples/2
* @param maxval : 最大值(0 < maxval < 2048)
* @param samples: 采样点的个数
* @retval 无
*/
void dac_creat_sin_buf(uint16_t maxval, uint16_t samples)
{
uint8_t i;
float outdata = 0; /* 存放计算后的数字量 */
float inc = (2 * 3.1415962) / samples; /* 计算相邻两个点的x轴间隔 */
if(maxval <= (samples / 2))return ; /* 数据不合法 */
for (i = 0; i < samples; i++)
{
/*
* 正弦波函数解析式:y = Asin(ωx + φ)+ b
* 计算每个点的y值,将峰值放大maxval倍,并将曲线向上偏移maxval到正数区域
* 注意:DAC无法输出负电压,所以需要将曲线向上偏移一个峰值的量,让整个曲线都落在正数区域
*/
outdata = maxval * sin(inc * i) + maxval;
if (outdata > 4095)
outdata = 4095; /* 上限限定 */
//printf("%f\r\n",outdata);
g_dac_sin_buf[i] = outdata;
}
}
// * @param ndtr : DMA通道单次传输数据量
void General_timx_int_init(uint16_t arr, uint16_t psc,uint16_t cndtr)
{
TIM_IC_InitTypeDef TIM_IC_Init={0};
TIM_MasterConfigTypeDef tim_mater_config = {0};
TIM_Handle.Instance =TIM5;
TIM_Handle.Init.Prescaler = psc;
TIM_Handle.Init.Period = arr;
TIM_Handle.Init.CounterMode=TIM_COUNTERMODE_UP;
TIM_Handle.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
TIM_Handle.Init.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_DISABLE;
HAL_TIM_IC_Init(&TIM_Handle);
__HAL_TIM_CLEAR_FLAG(&TIM_Handle,TIM_FLAG_UPDATE);
TIM_IC_Init.ICPolarity=TIM_ICPOLARITY_BOTHEDGE;
TIM_IC_Init.ICSelection=TIM_ICSELECTION_DIRECTTI;
TIM_IC_Init.ICPrescaler=TIM_ICPSC_DIV1;
TIM_IC_Init.ICFilter=0x08;
HAL_TIM_IC_ConfigChannel(&TIM_Handle,&TIM_IC_Init,TIM_CHANNEL_2);
tim_mater_config.MasterOutputTrigger = TIM_TRGO_UPDATE; //更新中断做为TRGO
tim_mater_config.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&TIM_Handle, &tim_mater_config);
//CNT==ARR溢出产生的中断
__HAL_TIM_ENABLE_IT(&TIM_Handle,TIM_IT_UPDATE);
HAL_TIM_IC_Start_IT(&TIM_Handle,TIM_CHANNEL_2);
HAL_DAC_Stop_DMA(&g_dac_handle, DAC_CHANNEL_1);
HAL_DAC_Start_DMA(&g_dac_handle, DAC_CHANNEL_1, (uint32_t *)g_dac_sin_buf, cndtr, DAC_ALIGN_12B_R);
}
#include "stm32f4xx.h" // Device header
#include "sys.h"
#include "delay.h"
#include "LED.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "adc.h"
#include "DAC.h"
#include "tim.h"
extern DAC_HandleTypeDef g_dac_handle;
uint16_t adc_Value;
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
Clock_HSE_Init(8,336,RCC_PLLP_DIV2,7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
Uart_Init(115200);
dac_init();
/*
T=((ARR+1)*(PSC+1))/主频
f=主频/((ARR+1)*(PSC+1))=84000 000 /(10*28)=300,000HZ=300KHZ
定时器的更新中断的频率也是我们正弦波的频率
*/
while(1)
{
General_timx_int_init(10-1,28-1,100);
dac_creat_sin_buf(2048, 100);
}
}