“ STM32F4xx 的 DAC 是 12 位数字转模拟模块,支持双通道独立或同步输出,可生成噪声、三角波等波形,适配多种触发方式与 DMA 传输,广泛应用于音频、波形生成等场景。本文介绍其配置与应用,助于掌握开发要点。”
01
—
stm32 DAC简介
STM32F4xx 系列中的 DAC 是 12 位数字输入、电压输出的数模转换器。它可配置为 8 位或 12 位模式,在 12 位模式下数据还能设置成左对齐或右对齐,并且可与 DMA 控制器配合使用,以实现高效的数据传输。
数量
绝大多数 STM32F4 器件提供 2 路独立 DAC 通道(如 64 引脚以下,可能只引出一通道或没有 DAC)。每个通道都有对应的引脚,如 DAC_OUT1(PA4) 和 DAC_OUT2(PA5),可实现两个独立或同步的模拟信号输出。
电气特性
输出电压范围:输出电压范围为 0 到 VREF+,VREF + 为参考电压,可通过一个输入参考电压引脚获取,公式为 DACoutput = VREF* DOR/4095,其中 DOR 为数据输出寄存器的值。
输出缓冲器:集成了两个输出缓冲器,可通过 DAC_CR 寄存器中的相应 BOFFx 位使能或禁止,用于降低输出阻抗,能在不增加外部运算放大器的情况下直接驱动外部负载。
转换速度:以 STM32F407ZGT6 为例,典型转换时间为 3us,最大为 6us,转换速度最快可达 333K 左右(受输出缓冲器 RC 负载限制)。
线性度:在 12 位模式下,线性度非常好,最大偏差通常在规定范围之内,可保证输出信号的质量,减少信号失真。
应用场景
音频信号处理:可将数字音频信号转换为模拟音频信号,用于驱动扬声器发声,如 STM32F4xx Black Pill 配合 PCM5102A USB DAC 项目,能提升耳机输出质量,为用户带来更好的音频体验。
波形生成:支持三角波生成模式和噪声生成模式,可用于生成特定波形,如在信号发生器中,通过配置 DAC 可输出连续的三角波或带有随机噪声的模拟信号。
电机速度控制:可将微控制器输出的数字控制信号转换为模拟电压信号,用于控制电机的转速,DAC 的精度和稳定性对电机控制的准确性和稳定性至关重要。
传感器信号模拟:在一些数据采集系统中,可将 ADC 采集到的数据通过 DAC 转换输出,用于模拟传感器信号,便于系统调试或模拟特定的传感器输入场景。
02
—
DAC框图
1.外部触发源选择模块
输入信号:包括软件触发信号SWTRIGx,以及定时器触发信号TIM2_TRGO、TIM4_TRGO、TIM5_TRGO、TIM6_TRGO、TIM7_TRGO、TIM8_TRGO,还有外部中断信号EXTI_9。
选择器:通过TSELx[2:0]位来选择触发源。该模块的作用是决定 DAC 转换的启动信号来源,可根据实际需求选择软件触发、定时器触发或外部中断触发等方式。
2.数据保持寄存器(DHRx)
用于存储要转换的数字数据,数据宽度为 12 位。它接收来自微控制器写入的数字信号,作为数模转换的原始数据。
3.控制逻辑模块
内部结构:包含线性反馈移位寄存器LFSRx和三角波生成模块。LFSRx可用于生成特定的数字序列,三角波生成模块则用于生成三角波信号。
控制信号输入:接收来自 DAC 控制寄存器的DMAENx(DMA 使能信号)、TENx(触发使能信号)、MAMPx[3:0](模拟输出缓冲放大器波形幅值/掩码设置信号)和WAVEx[1:0](波形生成使能及模式设置信号)。这些信号用于控制 DAC 的工作模式,如是否使用 DMA 传输数据、是否启用触发功能、设置模拟输出的增益以及选择生成的波形模式(如三角波等)。
数据输出:将处理后的 12 位数字信号输出到数据输出寄存器DORx。
4.数据输出寄存器(DORx)
存储从控制逻辑模块传来的 12 位数字信号,该信号直接传输到数模转换器进行转换。
5.数模转换器
是 DAC 的核心模块,它将DORx中的 12 位数字信号转换为模拟信号,从DAC_OUTx引脚输出。模拟信号的电压范围取决于参考电压VREF+以及数模转换器的转换特性。
6.电源及参考电压接口
V_DDA为 DAC 提供模拟电源,V_SSA为模拟地,V_REF+为参考电压输入引脚,参考电压决定了 DAC 输出模拟信号的电压范围上限,例如当V_REF+接 3.3V 时,DAC 输出的模拟电压范围理论上为 0 - 3.3V。
7.DAC 控制寄存器
用于配置 DAC 的各种参数,如上述提到的DMAENx、TENx、MAMPx[3:0]和WAVEx[1:0]等信号都由该寄存器进行设置,从而控制 DAC 的工作模式和性能。
03
—
DAC原理
CPU 或 DMA 先把 12 位数字量写入数据保持寄存器 DHRx;若触发关闭 (TENx=0),数据在 1 个 APB1 周期内自动进入数据输出寄存器 DORx,若触发开启 (TENx=1),则在所选触发源(定时器 TRGO、EXTI 或软件 SWTRIG)到来后 3 个APB1周期装载到 DORx;随后 12 位 R-2R 网络(电阻梯形数模转换电路)把 DORx 转换为模拟电压 VOUT = VREF+ × DOR/4095 并输出到 DAC_OUTx 引脚。要实现连续波形,只需用定时器产生固定频率触发,DMA 按节拍把波形表自动搬进 DHRx,即可让 DAC “自己跑”而无需 CPU 干预。
1.数据写入与触发
首先,微控制器将需要转换的 12 位数字数据写入到数据保持寄存器DHRx中。如果 TENx=0(触发关闭),写入 DHRx 后数据在一个 APB1 周期内自动进入 DORx。如果 TENx=1(触发开启),必须等待触发事件,3 个 APB1 周期后才更新 DORx。
然后,根据TSELx[2:0]位的设置选择触发源。如果选择软件触发,当设置相应的软件触发位(SWTRIGx)时,触发 DAC 转换;若选择定时器触发,当对应的定时器(如TIM2、TIM4等)产生触发信号(TRGO)时,启动转换;选择外部中断触发时,EXTI_9中断信号到来则触发转换。
2.控制逻辑处理
当触发信号到来后,控制逻辑模块根据DMAENx、TENx、MAMPx[3:0]和WAVENx[1:0]等控制信号进行相应处理。
如果启用了 DMA(DMAENx置位),则可以通过 DMA 方式从内存中自动获取要转换的数据,提高数据传输效率。
根据WAVENx[1:0]设置的波形模式,控制逻辑模块可以生成三角波等特定波形。例如,当设置为生成三角波模式时,三角波生成模块工作,结合DHRx 和MAMPx[3:0]设置,三角波生成器是基于 DHRx 的初始值,在其上下摆动,摆幅由 MAMPx[3:0] 设置,最大不超过 ±(MAMPx+1) LSB。
3.DAC 数据格式
• 分辨率:12 位(可裁剪为 8 位使用)。
• 对齐方式与寄存器对应关系:
12 位右对齐:写入 DAC_DHR12Rx[11:0];
12 位左对齐:写入 DAC_DHR12Lx[15:4];
8 位右对齐:写入 DAC_DHR8Rx[7:0](实际存到内部 DHRx[11:4]);
• 双通道同步模式还有专用的 “D” 后缀寄存器(DAC_DHR12RD 等),一次写 32 位可同时装载 CH1/CH2。
4.DAC 转换原理
• 用户只能写 DHRx,不能直接写 DORx。
• 无触发 (TENx=0):数据在 1 个 APB1 周期后自动从 DHRx → DORx → 12-bit R-2R 网络 → 模拟输出。
• 有触发 (TENx=1):必须等待所选触发源的上升沿,3 个 APB1 周期后完成 DHRx→DORx 的装载;再经过 tSETTLING(典型 3~4 µs)输出电压稳定。
5.DAC 输出电压计算
DAC 的输出电压_VOUT_与参考电压_VREF_+以及输入的数字量_N_有关,计算公式如下:
对于 12 位 DAC:VOUT=N/4095×VREF+,其中_N_是输入的 12 位数字量(范围是 0 - 4095)。例如,当_VREF+为 3.3V,输入数字量_N_为 2048 时,VOUT=2048/4095×3.3_V≈1.65_V_ 。
对于 8 位 DAC:VOUT=N/255×VREF+,其中_N_是输入的 8 位数字量(范围是 0 - 255)。
6.DAC 触发选择
STM32F4xx 的 DAC 触发源有多种选择,通过控制寄存器中的 TSELx [2:0] 位进行配置:
软件触发:将 TSELx [2:0] 配置为特定值,使能软件触发(SWTRIGx)。当通过软件设置相关位时,即可启动 DAC 转换。这种方式适用于需要手动控制转换时刻的场景,比如在特定的程序流程中进行数模转换 。
定时器触发:可选择 TIM2_TRGO、TIM4_TRGO、TIM5_TRGO、TIM6_TRGO、TIM7_TRGO、TIM8_TRGO 等定时器的触发信号。在需要周期性进行 DAC 转换的应用中,如生成周期性的模拟波形(如正弦波、三角波等),可以利用定时器产生固定频率的触发信号,来精确控制 DAC 的转换频率。
外部中断触发:使用 EXTI_9 等外部中断信号作为触发源。当外部事件发生并产生相应的中断信号时,触发 DAC 转换,适用于由外部事件驱动的数模转换场景,比如外部设备发出的控制信号触发 DAC 输出特定电压。
04
—
DAC功能
STM32F4xx 的 DAC(数模转换器)具备生成噪声、生成三角波、独立触发、同步触发等功能,以下是对这些功能及其原理的介绍:
DMA请求
当 DAC_CR.DMAENx=1 且由定时器、EXTI 等外部触发源产生触发时,DMA 把内存中的数据自动搬到 DAC_DHRx→DAC_DORx,完成一次模拟输出;双通道时可只使能一个 DMAENx,让一个 DMA 流同时驱动两路 DAC。由于没有缓冲队列,若 DMA 来不及响应新的触发(新的触发产生,但上一次 DMA 还没把数据搬完,DAC 没收到新值,于是这次触发被错过)就会置位 DMAUDRx 下溢标志并停止传输,须由软件清标志、重配 DMA/DAC 后继续。其作用是在无需 CPU 干预下,以精确时序连续输出任意波形,显著减轻 CPU 负荷,同时通过下溢中断机制保障输出可靠性。
生成噪声功能
DAC 可产生伪随机噪声(LFSR):内部 12 位线性反馈移位寄存器在每个触发事件后更新一次,其值经 MAMPx[3:0] 掩码后叠加到 DHRx 基值上,再输出到 DAC_OUTx。
生成三角波功能
DAC 可输出对称三角波:在 DHRx 基值基础上,内部计数器每触发一次 ±1 递增/递减,幅值由 MAMPx[3:0] 设定,峰-峰值 = (MAMPx+1) LSB。典型应用包括电机斜坡给定、线性扫频、低成本函数发生器等。
独立触发功能
每个 DAC 通道(CH1-PA4、CH2-PA5)拥有独立的触发单元(TENx、TSELx、DMAENx 等),允许 CH1 与 CH2 使用不同触发源、不同波形参数、不同更新时刻,完全互不干扰,适用于“多通道、多速率”输出场景。
同步触发功能
通过双通道数据寄存器 DHRxxD(DHR12RD、DHR12LD 等)一次写入 32 位数据,可同时装载 CH1 与 CH2 的 DHRx;再配置同一触发源(任意 TIMx_TRGO、EXTI9 或软件触发),即可让两个通道在同一 APB1 时钟边沿把 DHRx 复制到 DORx,实现零相位差同步输出,常用于立体声音频、同步电机驱动等。
双通道DAC功能
通过两个 DAC 通道和这三个双寄存器可以实现 11 种转换功能模式。
|
模式编号 |
模式名称 |
核心配置与原理 |
|
1 |
独立触发(不产生波形) |
两通道触发使能(TEN1=TEN2=1),触发源不同(TSEL1≠TSEL2),无波形生成(WAVEx=00),数据通过双寄存器(如 DHR12RD)加载,各自触发时更新 DORx。 |
|
2 |
独立触发(生成单个 LFSR) |
触发源不同(TSEL1≠TSEL2),两通道均使能 LFSR 波形(WAVEx=01),MAMPx 相同,触发时 LFSR 值与 DHRx 基值相加后更新 DORx。 |
|
3 |
独立触发(生成不同 LFSR) |
触发源不同,LFSR 波形使能(WAVEx=01),MAMPx 不同,两通道 LFSR 幅值独立,触发时各自更新。 |
|
4 |
独立触发(生成单个三角波) |
触发源不同,三角波使能(WAVEx=1x),MAMPx 相同,触发时三角波计数器值与 DHRx 基值相加后更新 DORx。 |
|
5 |
独立触发(生成不同三角波) |
触发源不同,三角波使能,MAMPx 不同,两通道三角波幅值独立,触发时各自更新。 |
|
6 |
同步软件启动(无触发) |
不使能触发(TEN1=TEN2=0),通过双寄存器一次性加载两通道数据,1 个 APB1 周期后同时更新 DORx,无外部触发。 |
|
7 |
同步触发(不产生波形) |
触发源相同(TSEL1=TSEL2),触发使能,无波形生成,触发时两通道同时将 DHRx 数据更新到 DORx。 |
|
8 |
同步触发(生成单个 LFSR) |
触发源相同,LFSR 波形使能,MAMPx 相同,触发时两通道 LFSR 值与 DHRx 基值相加后同步更新。 |
|
9 |
同步触发(生成不同 LFSR) |
触发源相同,LFSR 波形使能,MAMPx 不同,触发时两通道独立计算后同步更新。 |
|
10 |
同步触发(生成单个三角波) |
触发源相同,三角波使能,MAMPx 相同,触发时两通道三角波计数器值与 DHRx 基值相加后同步更新。 |
|
11 |
同步触发(生成不同三角波) |
触发源相同,三角波使能,MAMPx 不同,触发时两通道独立计算后同步更新。 |
05
—
硬件说明
DAC_OUT1(PA4)和DAC_OUT2(PA5)从开发板引出,使用示波器测量,示波器探头的地线连接到开发板DAC 电路的模拟地(AGND)。
06
—
程序设计案例
以下是双通道DAC 实现 11 种转换功能模式代码(不使用DMA)。
若只需使用其中一个 DAC 通道(如通道 1),修改方法如下:在 dac_modes.h 中删除未使用通道的模式函数声明;在 dac_modes.c 的各模式函数中,移除对未使用通道(如通道 2)的配置代码(包括 HAL_DAC_ConfigChannel、HAL_DAC_SetValue 等),仅保留目标通道(如通道 1)的配置、启动及数据设置;在 main.c 中,删除未使用通道对应的定时器初始化(如 TIM4),并注释掉主函数中对该定时器初始化的调用,同时确保目标通道的触发源定时器(如 TIM2)正常初始化;若需进一步精简,可移除全局句柄中未使用的 DAC 通道相关定义及对应 GPIO 配置。
dac_modes.h
// 声明11种模式函数void DAC_Mode1_Independent_Trigger_NoWave(void);void DAC_Mode2_Independent_Trigger_SingleLFSR(void);void DAC_Mode3_Independent_Trigger_DiffLFSR(void);void DAC_Mode4_Independent_Trigger_SingleTriangle(void);void DAC_Mode5_Independent_Trigger_DiffTriangle(void);void DAC_Mode6_Sync_SoftStart(void);void DAC_Mode7_Sync_Trigger_NoWave(void);void DAC_Mode8_Sync_Trigger_SingleLFSR(void);void DAC_Mode9_Sync_Trigger_DiffLFSR(void);void DAC_Mode10_Sync_Trigger_SingleTriangle(void);void DAC_Mode11_Sync_Trigger_DiffTriangle(void);
dac_modes.c
#include "dac_modes.h"#include "stm32f4xx_hal.h" // 外部声明:DAC和定时器句柄(需在初始化函数中定义)extern DAC_HandleTypeDef hdac;extern TIM_HandleTypeDef htim2; // 触发源1:用于通道1独立触发extern TIM_HandleTypeDef htim4; // 触发源2:用于通道2独立触发extern TIM_HandleTypeDef htim6; // 触发源3:用于同步触发 // 通道基值(12位DAC范围:0~4095)static const uint32_t ch1_val = 2048; // 通道1基值(对应中点电压)static const uint32_t ch2_val = 3000; // 通道2基值 /** * @brief 公共辅助函数:快速配置DAC通道参数 * @param hdac:DAC句柄 * @param cfg:通道配置结构体 * @param trigger:触发源(如DAC_TRIGGER_T2_TRGO) * @param wave:波形模式(DAC_WAVE_NONE/LFSR/TRIANGLE) * @param amp:振幅参数(LFSR掩码或三角波振幅) */static void DAC_ConfigChannel(DAC_HandleTypeDef *hdac, DAC_ChannelConfTypeDef *cfg, uint32_t trigger, uint32_t wave, uint32_t amp){ cfg->DAC_Trigger = trigger; // 触发源选择 cfg->DAC_WaveGeneration = wave; // 波形生成模式 cfg->DAC_LFSRUnmask_TriangleAmplitude = amp; // 振幅参数 cfg->DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE; // 使能输出缓冲(降低输出阻抗)} /** * @brief 模式1:独立触发(无波形) * @note 通道1用TIM2触发,通道2用TIM4触发(独立源),无波形生成 */void DAC_Mode1_Independent_Trigger_NoWave(void){ DAC_ChannelConfTypeDef cfg = {0}; // 配置通道1:TIM2触发,无波形 DAC_ConfigChannel(&hdac, &cfg, DAC_TRIGGER_T2_TRGO, DAC_WAVE_NONE, 0); HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_1); // 配置通道2:TIM4触发(与通道1独立),无波形 cfg.DAC_Trigger = DAC_TRIGGER_T3_TRGO; // 独立触发源 HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_2); // 设置通道输出值 HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, ch1_val); HAL_DAC_SetValue(&hdac, DAC_CHANNEL_2, DAC_ALIGN_12B_R, ch2_val); // 启动定时器和DAC HAL_TIM_Base_Start(&htim2); // 启动TIM2(触发源) HAL_TIM_Base_Start(&htim4); // 启动TIM4(触发源) HAL_DAC_Start(&hdac, DAC_CHANNEL_1); // 启动通道1 HAL_DAC_Start(&hdac, DAC_CHANNEL_2); // 启动通道2} /** * @brief 模式2:独立触发(单个LFSR) * @note 通道1用TIM2触发,通道2用TIM4触发(独立源),均生成LFSR噪声(同振幅) */void DAC_Mode2_Independent_Trigger_SingleLFSR(void){ DAC_ChannelConfTypeDef cfg = {0}; // 配置通道1:TIM2触发,LFSR噪声(振幅:12位全范围) DAC_ConfigChannel(&hdac, &cfg, DAC_TRIGGER_T2_TRGO, DAC_WAVE_LFSR, DAC_LFSRUNMASK_BITS11_0); HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_1); // 配置通道2:TIM4触发(独立源),同振幅LFSR噪声 cfg.DAC_Trigger = DAC_TRIGGER_T3_TRGO; HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_2); // 设置基值(噪声叠加于基值之上) HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, ch1_val); HAL_DAC_SetValue(&hdac, DAC_CHANNEL_2, DAC_ALIGN_12B_R, ch2_val); // 启动定时器和DAC HAL_TIM_Base_Start(&htim2); HAL_TIM_Base_Start(&htim4); HAL_DAC_Start(&hdac, DAC_CHANNEL_1); HAL_DAC_Start(&hdac, DAC_CHANNEL_2);} /** * @brief 模式3:独立触发(不同LFSR) * @note 通道1用TIM2触发,通道2用TIM4触发,LFSR噪声振幅不同 */void DAC_Mode3_Independent_Trigger_DiffLFSR(void){ DAC_ChannelConfTypeDef cfg = {0}; // 通道1:TIM2触发,LFSR噪声(振幅:低7位) DAC_ConfigChannel(&hdac, &cfg, DAC_TRIGGER_T2_TRGO, DAC_WAVE_LFSR, DAC_LFSRUNMASK_BITS6_0); HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_1); // 通道2:TIM4触发,LFSR噪声(振幅:12位全范围) cfg.DAC_Trigger = DAC_TRIGGER_T3_TRGO; cfg.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUNMASK_BITS11_0; HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_2); // 设置基值 HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, ch1_val); HAL_DAC_SetValue(&hdac, DAC_CHANNEL_2, DAC_ALIGN_12B_R, ch2_val); // 启动定时器和DAC HAL_TIM_Base_Start(&htim2); HAL_TIM_Base_Start(&htim4); HAL_DAC_Start(&hdac, DAC_CHANNEL_1); HAL_DAC_Start(&hdac, DAC_CHANNEL_2);} /** * @brief 模式4:独立触发(单个三角波) * @note 通道1用TIM2触发,通道2用TIM4触发,三角波振幅相同 */void DAC_Mode4_Independent_Trigger_SingleTriangle(void){ DAC_ChannelConfTypeDef cfg = {0}; // 通道1:TIM2触发,三角波(振幅:511 LSB) DAC_ConfigChannel(&hdac, &cfg, DAC_TRIGGER_T2_TRGO, DAC_WAVE_TRIANGLE, DAC_TRIANGLEAMPLITUDE_511); HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_1); // 通道2:TIM4触发,同振幅三角波 cfg.DAC_Trigger = DAC_TRIGGER_T3_TRGO; HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_2); // 设置基值(三角波叠加于基值) HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, ch1_val); HAL_DAC_SetValue(&hdac, DAC_CHANNEL_2, DAC_ALIGN_12B_R, ch2_val); // 启动定时器和DAC HAL_TIM_Base_Start(&htim2); HAL_TIM_Base_Start(&htim4); HAL_DAC_Start(&hdac, DAC_CHANNEL_1); HAL_DAC_Start(&hdac, DAC_CHANNEL_2);} /** * @brief 模式5:独立触发(不同三角波) * @note 通道1用TIM2触发,通道2用TIM4触发,三角波振幅不同 */void DAC_Mode5_Independent_Trigger_DiffTriangle(void){ DAC_ChannelConfTypeDef cfg = {0}; // 通道1:TIM2触发,三角波(振幅:255 LSB) DAC_ConfigChannel(&hdac, &cfg, DAC_TRIGGER_T2_TRGO, DAC_WAVE_TRIANGLE, DAC_TRIANGLEAMPLITUDE_255); HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_1); // 通道2:TIM4触发,三角波(振幅:1023 LSB) cfg.DAC_Trigger = DAC_TRIGGER_T3_TRGO; cfg.DAC_LFSRUnmask_TriangleAmplitude = DAC_TRIANGLEAMPLITUDE_1023; HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_2); // 设置基值 HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, ch1_val); HAL_DAC_SetValue(&hdac, DAC_CHANNEL_2, DAC_ALIGN_12B_R, ch2_val); // 启动定时器和DAC HAL_TIM_Base_Start(&htim2); HAL_TIM_Base_Start(&htim4); HAL_DAC_Start(&hdac, DAC_CHANNEL_1); HAL_DAC_Start(&hdac, DAC_CHANNEL_2);} /** * @brief 模式6:同步软件启动(无触发) * @note 无外部触发,双通道数据通过软件一次性写入,同步输出 */void DAC_Mode6_Sync_SoftStart(void){ DAC_ChannelConfTypeDef cfg = {0}; // 配置双通道:无触发源,无波形 DAC_ConfigChannel(&hdac, &cfg, DAC_TRIGGER_NONE, DAC_WAVE_NONE, 0); HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_1); HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_2); // 启动DAC并写入双通道数据(1个APB1周期后同步生效) HAL_DAC_Start(&hdac, DAC_CHANNEL_1); HAL_DAC_Start(&hdac, DAC_CHANNEL_2); HAL_DAC_SetDualChannelData(&hdac, DAC_ALIGN_12B_R, ch1_val, ch2_val);} /** * @brief 模式7:同步触发(无波形) * @note 双通道共用TIM6触发源,同步输出基值 */void DAC_Mode7_Sync_Trigger_NoWave(void){ DAC_ChannelConfTypeDef cfg = {0}; // 配置双通道:TIM6触发(同步源),无波形 DAC_ConfigChannel(&hdac, &cfg, DAC_TRIGGER_T6_TRGO, DAC_WAVE_NONE, 0); HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_1); HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_2); // 启动DAC和定时器(触发时同步输出) HAL_DAC_Start(&hdac, DAC_CHANNEL_1); HAL_DAC_Start(&hdac, DAC_CHANNEL_2); HAL_DACEx_DualSetValue(&hdac, DAC_ALIGN_12B_R, ch1_val, ch2_val); // 双通道数据 HAL_TIM_Base_Start(&htim6); // 启动同步触发源} /** * @brief 模式8:同步触发(单个LFSR) * @note 双通道共用TIM6触发源,同步输出同振幅LFSR噪声 */void DAC_Mode8_Sync_Trigger_SingleLFSR(void){ DAC_ChannelConfTypeDef cfg = {0}; // 配置双通道:TIM6触发,LFSR噪声(同振幅) DAC_ConfigChannel(&hdac, &cfg, DAC_TRIGGER_T6_TRGO, DAC_WAVE_LFSR, DAC_LFSRUNMASK_BITS11_0); HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_1); HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_2); // 启动DAC和定时器 HAL_DAC_Start(&hdac, DAC_CHANNEL_1); HAL_DAC_Start(&hdac, DAC_CHANNEL_2); HAL_DACEx_DualSetValue(&hdac, DAC_ALIGN_12B_R, ch1_val, ch2_val); HAL_TIM_Base_Start(&htim6);} /** * @brief 模式9:同步触发(不同LFSR) * @note 双通道共用TIM6触发源,输出不同振幅LFSR噪声 */void DAC_Mode9_Sync_Trigger_DiffLFSR(void){ DAC_ChannelConfTypeDef cfg = {0}; // 通道1:TIM6触发,LFSR噪声(振幅:低7位) DAC_ConfigChannel(&hdac, &cfg, DAC_TRIGGER_T6_TRGO, DAC_WAVE_LFSR, DAC_LFSRUNMASK_BITS6_0); HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_1); // 通道2:同触发源,LFSR噪声(振幅:12位全范围) cfg.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUNMASK_BITS11_0; HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_2); // 启动DAC和定时器 HAL_DAC_Start(&hdac, DAC_CHANNEL_1); HAL_DAC_Start(&hdac, DAC_CHANNEL_2); HAL_DACEx_DualSetValue(&hdac, DAC_ALIGN_12B_R, ch1_val, ch2_val); HAL_TIM_Base_Start(&htim6);} /** * @brief 模式10:同步触发(单个三角波) * @note 双通道共用TIM6触发源,同步输出同振幅三角波 */void DAC_Mode10_Sync_Trigger_SingleTriangle(void){ DAC_ChannelConfTypeDef cfg = {0}; // 配置双通道:TIM6触发,三角波(同振幅) DAC_ConfigChannel(&hdac, &cfg, DAC_TRIGGER_T6_TRGO, DAC_WAVE_TRIANGLE, DAC_TRIANGLEAMPLITUDE_511); HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_1); HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_2); // 启动DAC和定时器 HAL_DAC_Start(&hdac, DAC_CHANNEL_1); HAL_DAC_Start(&hdac, DAC_CHANNEL_2); HAL_DACEx_DualSetValue(&hdac, DAC_ALIGN_12B_R, ch1_val, ch2_val); HAL_TIM_Base_Start(&htim6);} /** * @brief 模式11:同步触发(不同三角波) * @note 双通道共用TIM6触发源,输出不同振幅三角波 */void DAC_Mode11_Sync_Trigger_DiffTriangle(void){ DAC_ChannelConfTypeDef cfg = {0}; // 通道1:TIM6触发,三角波(振幅:255 LSB) DAC_ConfigChannel(&hdac, &cfg, DAC_TRIGGER_T6_TRGO, DAC_WAVE_TRIANGLE, DAC_TRIANGLEAMPLITUDE_255); HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_1); // 通道2:同触发源,三角波(振幅:1023 LSB) cfg.DAC_LFSRUnmask_TriangleAmplitude = DAC_TRIANGLEAMPLITUDE_1023; HAL_DAC_ConfigChannel(&hdac, &cfg, DAC_CHANNEL_2); // 启动DAC和定时器 HAL_DAC_Start(&hdac, DAC_CHANNEL_1); HAL_DAC_Start(&hdac, DAC_CHANNEL_2); HAL_DACEx_DualSetValue(&hdac, DAC_ALIGN_12B_R, ch1_val, ch2_val); HAL_TIM_Base_Start(&htim6);}
main.c
#include "stm32f4xx_hal.h" // 全局句柄定义DAC_HandleTypeDef hdac;TIM_HandleTypeDef htim2, htim4, htim6; /** * @brief TIM2初始化(用于DAC独立触发源1) * @note 配置TRGO输出(更新事件),频率:1kHz */void MX_TIM2_Init(void){ TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; htim2.Instance = TIM2; htim2.Init.Prescaler = 8399; // 预分频:84MHz/8400=10kHz htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 9; // 周期:10kHz/10=1kHz htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_Base_Init(&htim2); sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig); // 配置TRGO输出(更新事件作为触发源) sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE; HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);} /** * @brief TIM4初始化(用于DAC独立触发源2) * @note 配置TRGO输出,频率:2kHz(与TIM2不同) */void MX_TIM4_Init(void){ TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; htim4.Instance = TIM4; htim4.Init.Prescaler = 8399; // 10kHz htim4.Init.CounterMode = TIM_COUNTERMODE_UP; htim4.Init.Period = 4; // 10kHz/5=2kHz htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_Base_Init(&htim4); sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig); // 配置TRGO输出 sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE; HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig);} /** * @brief TIM6初始化(用于DAC同步触发源) * @note 配置TRGO输出,频率:500Hz */void MX_TIM6_Init(void){ TIM_MasterConfigTypeDef sMasterConfig = {0}; htim6.Instance = TIM6; htim6.Init.Prescaler = 8399; // 10kHz htim6.Init.CounterMode = TIM_COUNTERMODE_UP; htim6.Init.Period = 19; // 10kHz/20=500Hz htim6.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_Base_Init(&htim6); // 配置TRGO输出 sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE; HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig);} /** * @brief DAC初始化(基础配置) */void MX_DAC_Init(void){ DAC_ChannelConfTypeDef sConfig = {0}; hdac.Instance = DAC; HAL_DAC_Init(&hdac); // 基础配置(具体模式在模式函数中重配置) sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE; HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1); HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_2);} /** * @brief GPIO初始化(DAC输出引脚:PA4=CH1,PA5=CH2) */void MX_GPIO_Init(void){ GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟 // 配置PA4/PA5为模拟模式(DAC专用) GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);} /** * @brief 主函数 */int main(void){ HAL_Init(); SystemClock_Config(); // 配置系统时钟为168MHz MX_GPIO_Init(); MX_DAC_Init(); MX_TIM2_Init(); MX_TIM4_Init(); MX_TIM6_Init(); // 选择测试模式(1~11) DAC_Mode1_Independent_Trigger_NoWave(); while (1) { HAL_Delay(1000); // 主循环空闲 }} /** * @brief 系统时钟配置(168MHz) */void SystemClock_Config(void){ RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 25; RCC_OscInitStruct.PLL.PLLN = 336; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // 168MHz RCC_OscInitStruct.PLL.PLLQ = 4; HAL_RCC_OscConfig(&RCC_OscInitStruct); RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; // 42MHz RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; // 84MHz HAL_RCC_ClkConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);} /** * @brief 错误处理函数 */void Error_Handler(void){ while(1); // 死循环等待复位}
0