找回密码
 立即注册
查看: 460|回复: 1

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

[复制链接]
  • 打卡等级:以坛为家III
  • 打卡总天数:663
  • 最近打卡:2026-01-13 11:51:16
已绑定手机
已实名认证

124

主题

3327

回帖

8481

积分

版主

积分
8481
发表于 2026-1-4 16:48:58 | 显示全部楼层 |阅读模式
截图202601041811055917.jpg
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数据,需要转换为小端模式,这里提供一个简单的函数,可以进行转换:

  1. //大小端交换,传入数组地址和所需转换数量(以16bit为单位)
  2. void swap_endian_uint16_array(unsigned int *arr, unsigned int count)
  3. {
  4.     unsigned int i;
  5.     unsigned short temp;
  6.    
  7.     for(i = 0; i < count; i++)
  8.     {
  9.         temp = arr[i];
  10.         arr[i] = ((temp & 0x00FF) << 8) | ((temp & 0xFF00) >> 8);
  11.     }
  12. }
复制代码


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

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

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

截图202601041643344392.jpg

以下为在STC32G144K246核心板上验证通过的正弦波输出测试程序,上电自动在P63端口输出一个正弦波:
程序运行在120Mhz
程序采用4096点满分辨率输出,无需滤波,输出速度可达2.43KHz,无需RC滤波即可看到非常平滑的波形
想要DAC看不到阶梯感,使用较高的分辨率即可, 或加滤波/毕竟DAC输出是离散信号不是连续信号
STC32G144K246 DMA-DAC.zip (60.67 KB, 下载次数: 10)
截图202601041804183008.jpg
高分辨率下,放大后依然是看不出来阶梯感的
截图202601041806423140.jpg


以下为完整程序:
  1. #include "STC32G144K246.H"
  2. #include "math.h"
  3. //本例程使用CHIPID内预置参数,设置HIRC为24MHz
  4. //使用HPLL1,提供60Mhz,80Mhz,120Mhz的设置例程
  5. #define Fosc_60Mhz 0 //系统时钟为60Mhz
  6. #define Fosc_80Mhz 1 //系统时钟为80Mhz
  7. #define Fosc_120Mhz 2//系统时钟为120Mhz
  8. #define Main_Fosc Fosc_120Mhz //设置系统时钟为120Mhz
  9. //正弦波表,最大值4095,使用计算方式在初始化的时候生成
  10. unsigned int xdata SinTable_Software[4096];
  11. void CLK_Init(void);                                //设置系统时钟,由Main_Fosc定义设置
  12. void Io_Init(void);                                        //I/O口初始化函数,设置P32为开漏+打开内部上拉电阻模式
  13. void DAC_Init(void);                                //DAC部分初始化函数,DAC1由OPA1的缓冲模式作为输出
  14. //大小端交换,传入数组地址和所需转换数量(以16bit为单位)
  15. void swap_endian_uint16_array(unsigned int *arr, unsigned int count)
  16. {
  17.     unsigned int i;
  18.     unsigned short temp;
  19.    
  20.     for(i = 0; i < count; i++)
  21.     {
  22.         temp = arr[i];
  23.         arr[i] = ((temp & 0x00FF) << 8) | ((temp & 0xFF00) >> 8);
  24.     }
  25. }
  26. void GenerateSinTable(unsigned int *pArray, unsigned int points)
  27. {
  28.     unsigned int i;
  29.     double angle, sinValue;
  30.    
  31.     for(i = 0; i < points; i++)
  32.     {
  33.         /* 生成0到2π之间的角度 */
  34.         angle = (2.0 * 3.14159265358979323846 * i) / points;
  35.         
  36.         /* 计算sin值 (-1到1) */
  37.         sinValue = sin(angle);
  38.         
  39.         /* 映射到0-4095范围 */
  40.         /* sinValue从-1到1,先+1变成0到2,除以2变成0到1,再乘4095 */
  41.         pArray[i] = (unsigned int)((sinValue + 1.0) * 2047.5 + 0.5);
  42.         
  43.         /* 限制在0-4095范围内 */
  44.         if(pArray[i] > 4095)
  45.         {
  46.             pArray[i] = 4095;
  47.         }
  48.     }
  49. }
  50. void Delay10ms(void)        //@120MHz
  51. {
  52.         unsigned long edata i;
  53.         _nop_();
  54.         _nop_();
  55.         i = 299998UL;
  56.         while (i) i--;
  57. }
  58. int cnt = 0;
  59. void main(void)
  60. {
  61.         EAXFR = 1;                                        //使能访问扩展RAM区特殊功能寄存器(XFR)
  62.         CKCON &= ~0x07;                        //清空[2:0],设置外部数据总线等待时钟为0(最快),默认为7
  63.         CLK_Init();                                        //设置HPLL时钟为指定频率
  64.         GenerateSinTable(SinTable_Software,4096);
  65.         swap_endian_uint16_array(SinTable_Software,4096);
  66.         Io_Init();                                        //初始化I/O口,设置P32等效为原准双向口模式(开漏模式+打开内部上拉电阻)
  67.         DAC_Init();                                        //初始化DAC
  68.         EA = 1;                                                        //打开总中断
  69.         
  70.         while(1)
  71.         {
  72.                 //用户程序
  73.                 if(P32 == 0)
  74.                 {
  75.                         
  76.                         DAC1_STA = 0x00;//清空状态标志位
  77.                         DMA_DAC1_STA = 0x00;//清空中断标志位
  78.                         DMA_DAC1_CR = 0xc0;//触发启动
  79.                         while(~P32);
  80.                 }
  81.         }
  82. }
  83. void Io_Init(void)
  84. {
  85.         P3M0 |= 0x04; P3M1 |= 0x04; //P32设置为开漏输出
  86.         P3PU |= 0x04;                                                         //打开P32的上拉电阻
  87.         //I/O口设置为开漏输出+打开上拉电阻==原准双向口模式,写1可读外部电平
  88.         P3M0 |= 0x20; P3M1 &= ~0x20;//设置P35为推挽输出模式,用于观察定时器0的T0CLKO输出
  89.         P0M0 = 0x00; P0M1 = 0xff;
  90. }
  91. void CLK_Init(void)
  92. {
  93.         #if Main_Fosc == Fosc_120Mhz
  94.         WTST = 4;CLKDIV = 2;                 //设置系统时钟=480MHz/2/2=120MHz,(因为CLKSEL选择时,已经将HPLL/2了)
  95.         #elif Main_Fosc == Fosc_80Mhz
  96.         WTST = 3;CLKDIV = 3;         //设置系统时钟=480MHz/2/3=80MHz
  97.         #elif Main_Fosc == Fosc_60Mhz
  98.         WTST = 2;CLKDIV = 4;         //设置系统时钟=480MHz/2/4=60MHz
  99.         #endif
  100.         //以下为超过60MHz时,系统时钟使用HPLL方式提供
  101.         VRTRIM = CHIPID22;                //载入27MHz频段的VRTRIM值
  102.         IRTRIM = CHIPID12;                //指定当前HIRC为24MHz,此时会覆盖掉ISP设置的时钟频率
  103.         IRCBAND &= ~0x03;                        //清空频段选择
  104.         IRCBAND |= 0x01;                        //选择27Mhz频段
  105.         HPLLCR &= ~0x10;            //选择HPLL输入时钟源为HIRC
  106.         HPLLPDIV = 4;                                        //24MHz/4=6MHz,需要保证输入HPLL的时钟在6MHz附近
  107.         HPLLCR |= 0x0e;             //HPLL=6MHz*80=480MHz
  108.         HPLLCR |= 0x80;             //使能HPLL
  109.         Delay10ms();
  110.         CLKSEL &= ~0x03;                        //BASE_CLK选择为HIRC,用以提供给HPLL
  111.         CLKSEL &= ~0x0c;                        //清空主时钟源选择
  112.         CLKSEL |= 1<<2;                                //设置主时钟源为内部 HPLL1 输出/2
  113. }
  114. //配置DAC为DMA输出,并且使用OPA1缓冲模式作为输出
  115. void DAC_Init(void)
  116. {
  117.         //OPA配置输出
  118.         PGA1_CR1 = 0x73;//缓冲模式,DACIO正极输入,P63输出
  119.         PGA1_CR2 = 0x04;//允许PGA1输出
  120.         
  121.         //DAC配置
  122.         DAC1_DIV = 0;//设置DAC分频系数,防止超过2Mhz推荐最大速度
  123.         //DAC1_CR = 0x81;//使能DAC功能,DAC使用DMA模式,单次触发,无法启动DMA
  124.         DAC1_CR = 0xb3;//使能DAC功能,DAC使用DMA模式,连续触发,打开DAC1中断
  125.         //DAC1_CR = 0x41;//使能DAC功能,连续触发,用于测试无DMA情况下是否正常输出,测试结果:正常
  126.         DAC1_STA = 0x00;//清空状态标志位
  127.         
  128.         //DMA配置
  129.         DMA_ARB_CFG = (0<<5);//切换到xdata部分
  130. //        DMA_DAC1_CFG = 0x00;//关闭DMA_DAC中断
  131.         DMA_DAC1_CFG = 0x8f;//使能DMA_DAC中断
  132.         DMA_DAC1_STA = 0x00;//清空中断标志位
  133.         DMA_DAC1_AMTH = (unsigned char)((4095-1)>>8);
  134.         DMA_DAC1_AMT = (unsigned char)(4095-1);//单位是16bit
  135.         DMA_DAC1_TXAH = (unsigned char)(((unsigned int)&SinTable_Software)>>8);
  136.         DMA_DAC1_TXAL = (unsigned char)((unsigned int)&SinTable_Software);
  137.         DMA_DAC1_ITVH = 0x00;//长延时,DAC输出速度主要由此提供
  138.         DMA_DAC1_CR = 0xc0;//触发启动
  139. }
  140. void dac1_isr(void) interrupt DAC1_VECTOR
  141. {
  142.         DAC1_STA = 0x00;//清空状态标志位
  143. }
  144. void DMA_DAC1_Interrupt(void) interrupt DMA_DAC_VECTOR
  145. {
  146.   DMA_DAC1_STA = 0;P35 = ~P35;DMA_DAC1_CR = 0xc0;//触发启动
  147. }
复制代码








回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:416
  • 最近打卡:2026-01-13 08:55:21
已绑定手机

98

主题

4001

回帖

8657

积分

荣誉版主

无情的代码机器

积分
8657
发表于 2026-1-4 17:55:00 | 显示全部楼层
定时17触发源+DAC_DMA,200us步进阶梯波:


截图202601041750589017.jpg

DAC_DMA_Test.zip (1.14 MB, 下载次数: 7)
三天不学习,赶不上刘少奇~
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-1-14 04:35 , Processed in 0.110853 second(s), 52 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表