目录
使用小华(华大)的MCU HC32F07X实现两个通道的 0-5V 电压模拟输出。
![]()
【1】DAC原理说明:
所谓DAC,就是Digital-Analog-Converter,数字模拟转换器。在模拟电路中,电流电压变化是连续的,而数字电路处理的数据都是离散的数据,输出高电平或者低电平,比如5V单片机,引脚输出的电压要么5V要么0V。DAC做的就是输出一个“任意“的电压,当然这个”任意“是有限制的。
【2】HC32F07X的DAC外设:
HC32F07X 集成了两个 DAC 转换器,各具有一个输出通道: DAC0_OUT 和 DAC1_OUT。
DAC 主要特性如下:
■ 一个数据保持寄存器
■ 12 位模式下数据采用左对齐或右对齐
■ 同步更新功能
■ 生成噪声波
■ 生成三角波
■ 双 DAC 通道,支持单独或同时转换
■ DMA 功能(包括下溢检测)
■ 通过外部触发信号进行转换
■ 4 种参考源: AVCC 电压、 ExRef 引脚、内置 1.5v 参考电压、内置 2.5v 参考电压;
DAC框图如下:


更多详细的内容可以参考HC32F07X芯片的DATASHEET。
选用引脚 PA04(DAC0)、PA05(DAC1)进行两路电压模拟输出,参考电压为AVCC(5V),并使用DMA传输数据。
【1】系统时钟初始化:
- static void App_SysClkInit(void)
- {
- stc_sysctrl_clk_cfg_t stcCfg;
- stc_sysctrl_pll_cfg_t stcPLLCfg;
-
- Sysctrl_SetPeripheralGate(SysctrlPeripheralFlash, TRUE); ///< 使能FLASH模块的外设时钟
- Flash_WaitCycle(FlashWaitCycle1);
- Sysctrl_SetRCHTrim(SysctrlRchFreq4MHz); ///< PLL使用RCH作为时钟源,因此需要先设置RCH
-
- stcPLLCfg.enInFreq = SysctrlPllInFreq4_6MHz; ///< RCH 4MHz
- stcPLLCfg.enOutFreq = SysctrlPllOutFreq36_48MHz; ///< PLL 输出48MHz
- stcPLLCfg.enPllClkSrc = SysctrlPllRch; ///< 输入时钟源选择RCH
- stcPLLCfg.enPllMul = SysctrlPllMul12; ///< 4MHz x 12 = 48MHz
- Sysctrl_SetPLLFreq(&stcPLLCfg);
-
- ///< 选择PLL作为HCLK时钟源;
- stcCfg.enClkSrc = SysctrlClkPLL;
- ///< HCLK SYSCLK/2
- stcCfg.enHClkDiv = SysctrlHclkDiv1;
- ///< PCLK 为HCLK/8
- stcCfg.enPClkDiv = SysctrlPclkDiv1;
- ///< 系统时钟初始化
- Sysctrl_ClkInit(&stcCfg);
- }
【2】DAC初始化GPIO:
- //模拟量输出引脚定义
- #define OUT_DAC0_PORT (GpioPortA)
- #define OUT_DAC0_PIN (GpioPin4)
- #define OUT_DAC1_PORT (GpioPortA)
- #define OUT_DAC1_PIN (GpioPin5)
-
- /**************************************************************************
- * 函数名称: DAC_Init
- * 功能描述: DAC初始化GPIO
- **************************************************************************/
- void DAC_Init(void)
- {
- Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE); //使能GPIO模块的外设时钟
- Gpio_SetAnalogMode(OUT_DAC0_PORT, OUT_DAC0_PIN); //PA04作为DAC0的模拟输出
- Gpio_SetAnalogMode(OUT_DAC1_PORT, OUT_DAC1_PIN); //PA05作为DAC1的模拟输出
- }
【3】DAC初始化配置:
- /**************************************************************************
- * 函数名称: DAC_Cfg
- * 功能描述: DAC初始化配置
- **************************************************************************/
- void DAC_Cfg(void)
- {
- stc_dac_cfg_t dac_initstruct;
-
- Sysctrl_SetPeripheralGate(SysctrlPeripheralDac, TRUE); ///< 使能DAC模块的时钟
-
- dac_initstruct.boff_t = DacBoffDisable; //禁止缓冲器
- dac_initstruct.ten_t = DacTenEnable; //使能通道触发
- dac_initstruct.sref_t = DacVoltageAvcc; //参考电压选择AVCC电压
- dac_initstruct.mamp_t = DacMenp4095; //振幅选择
- dac_initstruct.tsel_t = DacSwTriger; //软件触发方式
- dac_initstruct.align = DacRightAlign; //右对齐
- Dac0_Init(&dac_initstruct); //DAC0初始化
- Dac1_Init(&dac_initstruct); //DAC1初始化
-
- Dac0_Cmd(TRUE);
- Dac0_DmaCmd(TRUE);
- Dac1_Cmd(TRUE);
- Dac1_DmaCmd(TRUE);
- }
【4】DAC直接存储器存取配置:
- uint16_t out_buff[2];
-
- /**************************************************************************
- * 函数名称: DAC_DmaCfg
- * 功能描述: DAC直接存储器存取配置
- * 其他说明: 两个DAC需用两个DMA通道
- **************************************************************************/
- void DAC_DmaCfg(void)
- {
- stc_dma_cfg_t DmaInitStruct;
-
- Sysctrl_SetPeripheralGate(SysctrlPeripheralDma, TRUE); ///< 使能DMA模块的外设时钟
-
- DmaInitStruct.enMode = DmaMskBlock; ///< 选择块传输
- DmaInitStruct.u16BlockSize = 1; ///< 块传输个数
- DmaInitStruct.u16TransferCnt = 1; ///< 块传输次数,一次传输数据大小为 块传输个数*BUFFER_SIZE
- DmaInitStruct.enTransferWidth = DmaMsk16Bit; ///< 传输数据的宽度,此处选择字(16Bit)宽度
- DmaInitStruct.enSrcAddrMode = DmaMskSrcAddrInc; ///< 源地址自增
- DmaInitStruct.enDstAddrMode = DmaMskDstAddrFix; ///< 目的地址自增
- DmaInitStruct.enDestAddrReloadCtl = DmaMskDstAddrReloadDisable; ///< 禁止重新加载传输目的地址
- DmaInitStruct.enSrcAddrReloadCtl = DmaMskSrcAddrReloadEnable; ///< 使能重新加载传输源地址
- DmaInitStruct.enSrcBcTcReloadCtl = DmaMskBcTcReloadEnable; ///< 使能重新加载BC/TC值
- DmaInitStruct.u32SrcAddress = (uint32_t)&out_buff[0]; ///< 源地址
- DmaInitStruct.u32DstAddress = 0x40002508; ///< 目标地址: DAC_DHR12R0
- DmaInitStruct.enRequestNum = DmaDAC0Trig; ///< 设置DAC0触发
- DmaInitStruct.enTransferMode = DmaMskOneTransfer; ///< 传输一次
- DmaInitStruct.enPriority = DmaMskPriorityFix; ///< 各通道固定优先级,CH0优先级 > CH1优先级
- Dma_InitChannel(DmaCh0,&DmaInitStruct); ///< 初始化dma通道0
-
- DmaInitStruct.u32SrcAddress = (uint32_t)&out_buff[1]; ///< 源地址
- DmaInitStruct.u32DstAddress = 0x40002514; ///< 目标地址: DAC_DHR12R1
- DmaInitStruct.enRequestNum = DmaDAC1Trig; ///< 设置DAC1触发
- Dma_InitChannel(DmaCh1,&DmaInitStruct); ///< 初始化dma通道1
-
- Dma_Enable(); ///< 使能DMA
- Dma_EnableChannel(DmaCh0); ///< 使能DMA通道0
- Dma_ClrStat(DmaCh0); ///< 清零:STAT[2:0]
- Dma_EnableChannel(DmaCh1); ///< 使能DMA通道1
- Dma_ClrStat(DmaCh1); ///< 清零:STAT[2:0]
- }
【5】DAC通道0/1触发接口:
- /**************************************************************************
- * 函数名称: DAC0_Trig/DAC1_Trig
- * 功能描述: DAC通道0/1触发
- **************************************************************************/
- void DAC0_Trig(uint16_t p_out)
- {
- out_buff[0] = p_out;
- Dac0_SoftwareTriggerCmd();
- }
-
- void DAC1_Trig(uint16_t p_out)
- {
- out_buff[1] = p_out;
- Dac1_SoftwareTriggerCmd();
- }
【6】主函数调用:
- int32_t main(void)
- {
- //系统时钟
- App_SysClkInit();
-
- //输出模块
- DAC_Init();
- DAC_Cfg();
- DAC_DmaCfg();
-
- while(1)
- {
- //输出
- DAC0_Trig(4095);
- DAC1_Trig(2048);
-
- delay1ms(10);
- }
- }
【6】实测结果:
示波器测量PA04(DAC0)、PA05(DAC1)的输出电压如下,根据参考电压5V计算得到DAC0 输出应该为 5V * 4095 / 4095 = 5V;DAC1 输出应该为 5V * 2048 / 4095 = 2.5V。实测符合预期:

