基于STM32 DAC输出任意波形的方案

基于STM32 DAC输出任意波形的方案,包含DMA传输、定时器触发、多种波形生成、可调频率/幅度/偏置,并且支持实时更新波形数据。


一、STM32 DAC核心特性

型号 DAC通道 分辨率 缓冲 DMA 触发源
STM32F103 2 12位 支持 TIMx_TRGO
STM32F407 2 12位 支持 TIMx_TRGO
STM32G474 2 12位 支持 HRTIM
STM32H743 2 12位 支持 TIMx_TRGO

推荐配置

  • DAC触发:定时器更新事件
  • 数据传输:DMA
  • 分辨率:12位(0-4095对应0-3.3V)

二、硬件连接

STM32 DAC引脚:
PA4 → DAC_OUT1
PA5 → DAC_OUT2 (可选)

输出调理电路:
DAC_OUT → 运放电压跟随器 → 输出

必须加运放:STM32 DAC输出阻抗大(约15kΩ),不能直接驱动低阻抗负载。


三、CubeMX配置

1、DAC配置

  • Mode:Normal Mode
  • Trigger:Timer 6 Trigger Out
  • Output Buffer:Enable
  • DMA:Enable DMA1/2

2、TIM6配置

  • Prescaler:0
  • Counter Period:根据频率计算
  • Trigger Event:Update Event

3、DMA配置

  • Direction:Memory to Peripheral
  • Increment Address:Enable
  • Data Width:Half Word (16-bit)

四、DAC驱动核心代码

1、头文件定义

// waveform.h
#ifndef __WAVEFORM_H
#define __WAVEFORM_H

#include "stm32f4xx_hal.h"

// 波形类型
typedef enum {
    WAVE_SINE = 0,      // 正弦波
    WAVE_SQUARE,        // 方波
    WAVE_TRIANGLE,      // 三角波
    WAVE_SAWTOOTH,      // 锯齿波
    WAVE_ARBITRARY,     // 任意波形
    WAVE_DC             // 直流
} Waveform_Type_t;

// 波形参数结构体
typedef struct {
    Waveform_Type_t type;   // 波形类型
    float frequency;        // 频率 (Hz)
    float amplitude;        // 幅值 (V)
    float offset;          // 偏置 (V)
    float duty_cycle;      // 占空比 (0-1)
    uint16_t points;       // 一个周期点数
} Waveform_Param_t;

// 全局参数
extern Waveform_Param_t wave_param;
extern uint16_t dac_buffer[1024];  // DAC输出缓冲区
extern uint16_t wave_table[1024];  // 波形数据表

// 函数声明
void DAC_Waveform_Init(void);
void Generate_Waveform(Waveform_Param_t *param);
void Set_Waveform_Params(float freq, float amp, float offset, float duty);
void DAC_Start_Waveform(void);
void DAC_Stop_Waveform(void);
void Update_Waveform_Data(uint16_t *data, uint32_t size);

#endif

2、DAC初始化

// waveform.c
#include "waveform.h"
#include "math.h"

Waveform_Param_t wave_param = {0};
uint16_t dac_buffer[1024];
uint16_t wave_table[1024];

// DAC和定时器初始化
void DAC_Waveform_Init(void)
{
    DAC_HandleTypeDef hdac;
    TIM_HandleTypeDef htim6;
    
    // 1. 初始化DAC通道1
    hdac.Instance = DAC;
    HAL_DAC_Init(&hdac);
    
    DAC_ChannelConfTypeDef sConfig = {0};
    sConfig.DAC_Trigger = DAC_TRIGGER_T6_TRGO;  // TIM6触发
    sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
    HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1);
    
    // 2. 初始化TIM6(触发定时器)
    htim6.Instance = TIM6;
    htim6.Init.Prescaler = 0;
    htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim6.Init.Period = 168;  // 168MHz/168 = 1MHz触发频率
    htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
    HAL_TIM_Base_Init(&htim6);
    
    // 3. 启动DAC DMA
    HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, 
                     (uint32_t*)dac_buffer, 1024, DAC_ALIGN_12B_R);
    
    // 4. 启动定时器
    HAL_TIM_Base_Start(&htim6);
    
    // 5. 默认参数
    wave_param.type = WAVE_SINE;
    wave_param.frequency = 1000;  // 1kHz
    wave_param.amplitude = 1.0;   // 1V
    wave_param.offset = 1.65;     // 1.65V(中点)
    wave_param.duty_cycle = 0.5;  // 50%
    wave_param.points = 100;      // 100点/周期
    
    // 6. 生成默认波形
    Generate_Waveform(&wave_param);
}

3、波形生成函数(核心)

// 电压转DAC值 (0-3.3V 转 0-4095)
uint16_t Voltage_To_DAC(float voltage)
{
    if (voltage < 0) voltage = 0;
    if (voltage > 3.3f) voltage = 3.3f;
    return (uint16_t)(voltage * 4095.0f / 3.3f);
}

// 生成正弦波
void Generate_Sine_Wave(Waveform_Param_t *p)
{
    float angle, voltage;
    
    for (uint16_t i = 0; i < p->points; i++) {
        angle = 2.0f * 3.1415926f * i / p->points;
        voltage = p->amplitude * sinf(angle) + p->offset;
        wave_table[i] = Voltage_To_DAC(voltage);
    }
}

// 生成方波
void Generate_Square_Wave(Waveform_Param_t *p)
{
    uint16_t high_points = (uint16_t)(p->points * p->duty_cycle);
    
    for (uint16_t i = 0; i < p->points; i++) {
        if (i < high_points) {
            wave_table[i] = Voltage_To_DAC(p->offset + p->amplitude);
        } else {
            wave_table[i] = Voltage_To_DAC(p->offset - p->amplitude);
        }
    }
}

// 生成三角波
void Generate_Triangle_Wave(Waveform_Param_t *p)
{
    float slope, voltage;
    
    for (uint16_t i = 0; i < p->points; i++) {
        if (i < p->points / 2) {
            // 上升沿
            slope = 2.0f * p->amplitude * 2.0f / p->points;
            voltage = p->offset - p->amplitude + slope * i;
        } else {
            // 下降沿
            slope = 2.0f * p->amplitude * 2.0f / p->points;
            voltage = p->offset + p->amplitude - slope * (i - p->points/2);
        }
        wave_table[i] = Voltage_To_DAC(voltage);
    }
}

// 生成锯齿波
void Generate_Sawtooth_Wave(Waveform_Param_t *p)
{
    float slope, voltage;
    
    for (uint16_t i = 0; i < p->points; i++) {
        slope = 2.0f * p->amplitude / p->points;
        voltage = p->offset - p->amplitude + slope * i;
        wave_table[i] = Voltage_To_DAC(voltage);
    }
}

// 生成直流
void Generate_DC_Wave(Waveform_Param_t *p)
{
    uint16_t dac_value = Voltage_To_DAC(p->offset);
    
    for (uint16_t i = 0; i < p->points; i++) {
        wave_table[i] = dac_value;
    }
}

// 主波形生成函数
void Generate_Waveform(Waveform_Param_t *p)
{
    switch (p->type) {
        case WAVE_SINE:
            Generate_Sine_Wave(p);
            break;
        case WAVE_SQUARE:
            Generate_Square_Wave(p);
            break;
        case WAVE_TRIANGLE:
            Generate_Triangle_Wave(p);
            break;
        case WAVE_SAWTOOTH:
            Generate_Sawtooth_Wave(p);
            break;
        case WAVE_DC:
            Generate_DC_Wave(p);
            break;
        case WAVE_ARBITRARY:
            // 用户自定义波形,不自动生成
            break;
    }
    
    // 计算实际DMA缓冲区长度
    uint32_t buffer_length = 1024;
    
    // 填充DMA缓冲区(循环填充)
    for (uint32_t i = 0; i < buffer_length; i++) {
        dac_buffer[i] = wave_table[i % p->points];
    }
}

4、频率控制(关键)

// 设置波形频率
void Set_Waveform_Frequency(float freq_hz)
{
    // 计算定时器重载值
    // 公式:f_trigger = f_sys / (PSC+1) / (ARR+1)
    // 波形频率 = f_trigger / points
    
    uint32_t sys_clk = 168000000;  // 168MHz
    uint16_t points = wave_param.points;
    float f_trigger = freq_hz * points;
    
    // 自动计算分频和重载值
    uint32_t psc = 0;
    uint32_t arr = (uint32_t)(sys_clk / f_trigger) - 1;
    
    if (arr > 65535) {
        psc = arr / 65536;
        arr = 65535;
    }
    
    // 更新TIM6
    __HAL_TIM_SET_PRESCALER(&htim6, psc);
    __HAL_TIM_SET_AUTORELOAD(&htim6, arr);
    
    // 重新启动定时器
    __HAL_TIM_SET_COUNTER(&htim6, 0);
    HAL_TIM_Base_Start(&htim6);
    
    wave_param.frequency = freq_hz;
}

5、DMA双缓冲模式(进阶)

// 使用DMA双缓冲,避免波形更新时的毛刺
void DAC_DMA_DoubleBuffer_Init(void)
{
    // 配置DMA为双缓冲模式
    HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1,
                     (uint32_t*)dac_buffer1, 512, DAC_ALIGN_12B_R);
    
    HAL_DACEx_DualStartDMA(&hdac,
                          (uint32_t*)dac_buffer1, (uint32_t*)dac_buffer2,
                          512, DAC_ALIGN_12B_R);
}

// DMA传输完成中断回调
void HAL_DAC_ConvCpltCallbackCh1(DAC_HandleTypeDef *hdac)
{
    // 缓冲区0传输完成,更新缓冲区0
    Update_Waveform_Buffer(dac_buffer1, 512);
}

void HAL_DAC_ConvHalfCpltCallbackCh1(DAC_HandleTypeDef *hdac)
{
    // 缓冲区1传输完成一半,更新缓冲区1
    Update_Waveform_Buffer(dac_buffer2, 512);
}

6、任意波形数据加载

// 从外部加载任意波形数据
void Load_Arbitrary_Waveform(float *voltage_data, uint16_t size)
{
    if (size > 1024) size = 1024;
    
    wave_param.type = WAVE_ARBITRARY;
    wave_param.points = size;
    
    for (int i = 0; i < size; i++) {
        wave_table[i] = Voltage_To_DAC(voltage_data[i]);
    }
    
    // 更新DMA缓冲区
    for (int i = 0; i < 1024; i++) {
        dac_buffer[i] = wave_table[i % size];
    }
}

// 从数组加载
void Load_Waveform_From_Array(uint16_t *dac_data, uint16_t size)
{
    if (size > 1024) size = 1024;
    
    memcpy(wave_table, dac_data, size * sizeof(uint16_t));
    wave_param.type = WAVE_ARBITRARY;
    wave_param.points = size;
}

7、主函数调用示例

// main.c
#include "waveform.h"

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    
    // 初始化DAC波形发生器
    DAC_Waveform_Init();
    
    // 示例1:生成1kHz正弦波,幅值1V,偏置1.65V
    wave_param.type = WAVE_SINE;
    wave_param.frequency = 1000;
    wave_param.amplitude = 1.0f;
    wave_param.offset = 1.65f;
    wave_param.points = 100;
    
    Generate_Waveform(&wave_param);
    Set_Waveform_Frequency(1000);
    
    // 示例2:生成10kHz方波,占空比30%
    wave_param.type = WAVE_SQUARE;
    wave_param.frequency = 10000;
    wave_param.duty_cycle = 0.3f;
    
    Generate_Waveform(&wave_param);
    Set_Waveform_Frequency(10000);
    
    // 示例3:加载自定义波形
    float custom_wave[50];
    for (int i = 0; i < 50; i++) {
        custom_wave[i] = 1.65f + 1.0f * sinf(2*3.1416*i/50);
    }
    Load_Arbitrary_Waveform(custom_wave, 50);
    Set_Waveform_Frequency(2000);
    
    while (1) {
        // 可以动态修改波形参数
        // 例如通过串口命令修改
        if (new_frequency_received) {
            Set_Waveform_Frequency(new_freq);
        }
        
        if (new_amplitude_received) {
            wave_param.amplitude = new_amp;
            Generate_Waveform(&wave_param);
        }
        
        HAL_Delay(10);
    }
}

8、串口命令控制

// 通过串口控制波形
void UART_Command_Handler(uint8_t *cmd)
{
    if (strncmp(cmd, "FREQ ", 5) == 0) {
        float freq = atof(cmd + 5);
        Set_Waveform_Frequency(freq);
        printf("频率设置为: %.1f Hz\r\n", freq);
    }
    else if (strncmp(cmd, "AMP ", 4) == 0) {
        wave_param.amplitude = atof(cmd + 4);
        Generate_Waveform(&wave_param);
        printf("幅值设置为: %.2f V\r\n", wave_param.amplitude);
    }
    else if (strncmp(cmd, "TYPE ", 5) == 0) {
        uint8_t type = atoi(cmd + 5);
        wave_param.type = (Waveform_Type_t)type;
        Generate_Waveform(&wave_param);
        printf("波形类型设置为: %d\r\n", type);
    }
    else if (strcmp(cmd, "STOP") == 0) {
        DAC_Stop_Waveform();
        printf("波形输出停止\r\n");
    }
    else if (strcmp(cmd, "START") == 0) {
        DAC_Start_Waveform();
        printf("波形输出启动\r\n");
    }
}

9、性能优化(高频率)

// 使用查表法提高计算速度
void Generate_Sine_Wave_Fast(Waveform_Param_t *p)
{
    // 预计算正弦表
    static float sin_table[1024];
    static uint8_t table_initialized = 0;
    
    if (!table_initialized) {
        for (int i = 0; i < 1024; i++) {
            sin_table[i] = sinf(2.0f * 3.1415926f * i / 1024.0f);
        }
        table_initialized = 1;
    }
    
    // 查表生成
    uint32_t step = 1024 / p->points;
    
    for (uint16_t i = 0; i < p->points; i++) {
        float voltage = p->amplitude * sin_table[(i * step) % 1024] + p->offset;
        wave_table[i] = Voltage_To_DAC(voltage);
    }
}

10、双通道输出(立体声)

// 双通道波形输出
void DAC_Dual_Channel_Init(void)
{
    // 配置DAC通道1和通道2
    DAC_ChannelConfTypeDef sConfig = {0};
    
    // 通道1
    sConfig.DAC_Trigger = DAC_TRIGGER_T6_TRGO;
    sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
    HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1);
    
    // 通道2
    sConfig.DAC_Trigger = DAC_TRIGGER_T6_TRGO;
    sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
    HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_2);
    
    // 双通道DMA传输
    uint32_t dac_buffer_dual[2][1024];  // 二维数组
    
    HAL_DACEx_DualStartDMA(&hdac,
                          (uint32_t*)dac_buffer_dual[0],
                          (uint32_t*)dac_buffer_dual[1],
                          1024, DAC_ALIGN_12B_R);
}

参考代码 STM32 DAC输出任意波形 www.youwenfan.com/contentcnu/69991.html

五、输出频率计算

关键公式:

实际输出频率 = 定时器触发频率 / 每个周期点数
定时器触发频率 = 系统时钟 / (PSC+1) / (ARR+1)

示例计算(STM32F407 @168MHz):

  • 定时器配置:PSC=0, ARR=167
  • 触发频率 = 168MHz / 1 / 168 = 1MHz
  • 波形点数 = 100
  • 输出频率 = 1MHz / 100 = 10kHz

六、性能参数

波形类型 最高频率 分辨率 谐波失真
正弦波 100kHz 12位 <1%
方波 1MHz 12位 边沿时间≈100ns
任意波 50kHz 12位 取决于点数

七、常见问题

问题 原因 解决
波形毛刺 DMA更新冲突 使用双缓冲
频率不准 定时器计算误差 使用自动重载
输出范围小 运放增益不够 增加后级放大
高次谐波 点数太少 增加采样点数
posted @ 2026-05-15 15:16  躲雨小伙  阅读(59)  评论(0)    收藏  举报