一 背景说明
使用小华(华大)的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。实测符合预期: