【嵌入式】HC32F07X DAC模拟输出 + DMA传输

一 背景说明

        使用小华(华大)的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 DAC模拟输出 + DMA传输_第1张图片

【嵌入式】HC32F07X DAC模拟输出 + DMA传输_第2张图片

        更多详细的内容可以参考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。实测符合预期:

【嵌入式】HC32F07X DAC模拟输出 + DMA传输_第3张图片

【嵌入式】HC32F07X DAC模拟输出 + DMA传输_第4张图片

你可能感兴趣的:(嵌入式,单片机,嵌入式硬件,HC32F,DAC)