王昱顺 发表于 2026-1-4 16:48:58

DAC-DMA 例程及注意事项@STC32G144K246


STC32G144K246, DAC-DMA 输出, 离散信号输出,足够快成连续信号 !
需要注意以下问题:
1.DMA_DAC1_AMT的单位是16bit的,例如想要DMA传输50个16位的数据(数据最大不能超过12位)
则DMA_DAC1_AMT需要填入49(DMA_DAC1_AMT填0时为传输1字节,所以填入时需要减1)


2.DAC-DMA传输时,DAC1_DIV是和DMA_DAC1_ITV共同作用的

3.传入DAC的DMA数据,需要转换为小端模式,这里提供一个简单的函数,可以进行转换:
//大小端交换,传入数组地址和所需转换数量(以16bit为单位)
void swap_endian_uint16_array(unsigned int *arr, unsigned int count)
{
    unsigned int i;
    unsigned short temp;
   
    for(i = 0; i < count; i++)
    {
      temp = arr;
      arr = ((temp & 0x00FF) << 8) | ((temp & 0xFF00) >> 8);
    }
}

4.需要使用DAC-DMA模式时,DAC1_CR中,需要将转换模式配置为连续转换模式
DMA只管更新DMA_DAT,不会主动触发DAC TG,如果配置为单次转换或者其他定时器触发,仍然需要定时器触发或者软件触发DAC TG才能看到波形
否则,DAC将不会更新输出


5.DMA_DAC1_DONE寄存器,单位仍然为8bit,即一个字节,例如DMA_DAC1_AMT填入49,则全部转换完成后,将可以在DMA_DAC1_DONE读到100

6.DAC1_DIV和DAC1_DAT为16位寄存器,可以直接填入值,不需要拆分成8位填入,寄存器是否支持16位,请看头文件定义,是否为int类型


以下为在STC32G144K246核心板上验证通过的正弦波输出测试程序,上电自动在P63端口输出一个正弦波:
程序运行在120Mhz
程序采用4096点满分辨率输出,无需滤波,输出速度可达2.43KHz,无需RC滤波即可看到非常平滑的波形
想要DAC看不到阶梯感,使用较高的分辨率即可, 或加滤波/毕竟DAC输出是离散信号不是连续信号


高分辨率下,放大后依然是看不出来阶梯感的



以下为完整程序:
#include "STC32G144K246.H"
#include "math.h"
//本例程使用CHIPID内预置参数,设置HIRC为24MHz
//使用HPLL1,提供60Mhz,80Mhz,120Mhz的设置例程
#define Fosc_60Mhz 0 //系统时钟为60Mhz
#define Fosc_80Mhz 1 //系统时钟为80Mhz
#define Fosc_120Mhz 2//系统时钟为120Mhz

#define Main_Fosc Fosc_120Mhz //设置系统时钟为120Mhz

//正弦波表,最大值4095,使用计算方式在初始化的时候生成
unsigned int xdata SinTable_Software;

void CLK_Init(void);                              //设置系统时钟,由Main_Fosc定义设置
void Io_Init(void);                                        //I/O口初始化函数,设置P32为开漏+打开内部上拉电阻模式
void DAC_Init(void);                              //DAC部分初始化函数,DAC1由OPA1的缓冲模式作为输出

//大小端交换,传入数组地址和所需转换数量(以16bit为单位)
void swap_endian_uint16_array(unsigned int *arr, unsigned int count)
{
    unsigned int i;
    unsigned short temp;
   
    for(i = 0; i < count; i++)
    {
      temp = arr;
      arr = ((temp & 0x00FF) << 8) | ((temp & 0xFF00) >> 8);
    }
}

void GenerateSinTable(unsigned int *pArray, unsigned int points)
{
    unsigned int i;
    double angle, sinValue;
   
    for(i = 0; i < points; i++)
    {
      /* 生成0到2π之间的角度 */
      angle = (2.0 * 3.14159265358979323846 * i) / points;
      
      /* 计算sin值 (-1到1) */
      sinValue = sin(angle);
      
      /* 映射到0-4095范围 */
      /* sinValue从-1到1,先+1变成0到2,除以2变成0到1,再乘4095 */
      pArray = (unsigned int)((sinValue + 1.0) * 2047.5 + 0.5);
      
      /* 限制在0-4095范围内 */
      if(pArray > 4095)
      {
            pArray = 4095;
      }
    }
}

void Delay10ms(void)      //@120MHz
{
      unsigned long edata i;

      _nop_();
      _nop_();
      i = 299998UL;
      while (i) i--;
}
int cnt = 0;
void main(void)
{
      EAXFR = 1;                                        //使能访问扩展RAM区特殊功能寄存器(XFR)
      CKCON &= ~0x07;                        //清空,设置外部数据总线等待时钟为0(最快),默认为7
      CLK_Init();                                        //设置HPLL时钟为指定频率
      GenerateSinTable(SinTable_Software,4096);
      swap_endian_uint16_array(SinTable_Software,4096);
      Io_Init();                                        //初始化I/O口,设置P32等效为原准双向口模式(开漏模式+打开内部上拉电阻)
      DAC_Init();                                        //初始化DAC
      EA = 1;                                                      //打开总中断
      
      while(1)
      {
                //用户程序
                if(P32 == 0)
                {
                        
                        DAC1_STA = 0x00;//清空状态标志位
                        DMA_DAC1_STA = 0x00;//清空中断标志位
                        DMA_DAC1_CR = 0xc0;//触发启动
                        while(~P32);
                }
      }
}



void Io_Init(void)
{
      P3M0 |= 0x04; P3M1 |= 0x04; //P32设置为开漏输出
      P3PU |= 0x04;                                                         //打开P32的上拉电阻
      //I/O口设置为开漏输出+打开上拉电阻==原准双向口模式,写1可读外部电平
      P3M0 |= 0x20; P3M1 &= ~0x20;//设置P35为推挽输出模式,用于观察定时器0的T0CLKO输出
      P0M0 = 0x00; P0M1 = 0xff;
}



void CLK_Init(void)
{
      #if Main_Fosc == Fosc_120Mhz
      WTST = 4;CLKDIV = 2;               //设置系统时钟=480MHz/2/2=120MHz,(因为CLKSEL选择时,已经将HPLL/2了)
      #elif Main_Fosc == Fosc_80Mhz
      WTST = 3;CLKDIV = 3;         //设置系统时钟=480MHz/2/3=80MHz
      #elif Main_Fosc == Fosc_60Mhz
      WTST = 2;CLKDIV = 4;         //设置系统时钟=480MHz/2/4=60MHz
      #endif
      //以下为超过60MHz时,系统时钟使用HPLL方式提供
      VRTRIM = CHIPID22;                //载入27MHz频段的VRTRIM值
      IRTRIM = CHIPID12;                //指定当前HIRC为24MHz,此时会覆盖掉ISP设置的时钟频率
      IRCBAND &= ~0x03;                        //清空频段选择
      IRCBAND |= 0x01;                        //选择27Mhz频段
      HPLLCR &= ~0x10;            //选择HPLL输入时钟源为HIRC
      HPLLPDIV = 4;                                        //24MHz/4=6MHz,需要保证输入HPLL的时钟在6MHz附近
      HPLLCR |= 0x0e;             //HPLL=6MHz*80=480MHz
      HPLLCR |= 0x80;             //使能HPLL
      Delay10ms();
      CLKSEL &= ~0x03;                        //BASE_CLK选择为HIRC,用以提供给HPLL
      CLKSEL &= ~0x0c;                        //清空主时钟源选择
      CLKSEL |= 1<<2;                              //设置主时钟源为内部 HPLL1 输出/2
}

//配置DAC为DMA输出,并且使用OPA1缓冲模式作为输出
void DAC_Init(void)
{
      //OPA配置输出
      PGA1_CR1 = 0x73;//缓冲模式,DACIO正极输入,P63输出
      PGA1_CR2 = 0x04;//允许PGA1输出
      
      //DAC配置
      DAC1_DIV = 0;//设置DAC分频系数,防止超过2Mhz推荐最大速度
      //DAC1_CR = 0x81;//使能DAC功能,DAC使用DMA模式,单次触发,无法启动DMA
      DAC1_CR = 0xb3;//使能DAC功能,DAC使用DMA模式,连续触发,打开DAC1中断
      //DAC1_CR = 0x41;//使能DAC功能,连续触发,用于测试无DMA情况下是否正常输出,测试结果:正常
      DAC1_STA = 0x00;//清空状态标志位
      
      //DMA配置
      DMA_ARB_CFG = (0<<5);//切换到xdata部分
//      DMA_DAC1_CFG = 0x00;//关闭DMA_DAC中断
      DMA_DAC1_CFG = 0x8f;//使能DMA_DAC中断
      DMA_DAC1_STA = 0x00;//清空中断标志位
      DMA_DAC1_AMTH = (unsigned char)((4095-1)>>8);
      DMA_DAC1_AMT = (unsigned char)(4095-1);//单位是16bit
      DMA_DAC1_TXAH = (unsigned char)(((unsigned int)&SinTable_Software)>>8);
      DMA_DAC1_TXAL = (unsigned char)((unsigned int)&SinTable_Software);
      DMA_DAC1_ITVH = 0x00;//长延时,DAC输出速度主要由此提供
      DMA_DAC1_CR = 0xc0;//触发启动
}

void dac1_isr(void) interrupt DAC1_VECTOR
{
      DAC1_STA = 0x00;//清空状态标志位
}

void DMA_DAC1_Interrupt(void) interrupt DMA_DAC_VECTOR
{
DMA_DAC1_STA = 0;P35 = ~P35;DMA_DAC1_CR = 0xc0;//触发启动
}







ercircle 发表于 2026-1-4 17:55:00

定时17触发源+DAC_DMA,200us步进阶梯波:





神农鼎 发表于 2026-1-21 14:23:14

STC32G144K246-QFN100/12mm*12mm-0.4mm间距,
走在克服封装困难的道路上;


STC32G144K246-QFN64/8mm*8mm-0.4mm间距,
走在克服封装困难的道路上;


STC32G144K246-LQFP64/12mm*12mm-0.4mm间距,
会下周前立即供应跟上;

草木灰06 发表于 2026-2-18 23:19:24

{:qiang:}

yinds 发表于 2026-3-17 12:11:33

学习
页: [1]
查看完整版本: DAC-DMA 例程及注意事项@STC32G144K246