当前位置: 首页 > article >正文

STM32单片机芯片与内部41 DAC TIM触发双DAC DMA搬运同步输出正弦波

目录

一、正弦波数据表

二、双DAC同步输出-标准库工程

1、DAC配置

2、GPIO配置

3、TIM配置

4、DMA配置

三、单DAC输出-HAL库工程

 1、DAC配置

2、GPIO配置

3、TIM配置

4、DMA配置

四、用户侧

1、触发方式:

2、CPU占用与效率:

3、精度与一致性:

4、灵活性与控制:

5、资源消耗:

总结:

选择建议:


正弦波由频率、幅度、采样点构成。

一、正弦波数据表

        例如对正弦波进行32个点的采样,如果以32KHz输出点,那么就会得到1KHz的正弦波,输出幅度为12bit,因此原始数据为0~+4096。通过调节输出频率可以得到不同频率的正弦波。

最终得到的如图所示:最高最低点约接近0、4096则实际量化位数越大,信噪比越高。

        [2048, 2460, 2856, 3218, 3532, 3786, 3969, 4072,4093, 4031, 3887, 3668, 3382, 3042, 2661, 2255, 1841, 1435, 1054, 714, 428, 209, 65, 3,24, 127, 310, 564, 878, 1240, 1636, 2048]

        当然最终无论输出什么都可以,直接修改相关的数组即可,例如方波、三角波等。

二、双DAC同步输出-标准库工程

1、DAC配置

        配置两个DAC,且均使用TIM2作为触发源。

  DAC_InitTypeDef  DAC_InitStructure;
	/* 使能DAC时钟 */	
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);

  /* 配置DAC 通道1 */
  DAC_InitStructure.DAC_Trigger = DAC_Trigger_T2_TRGO;						//使用TIM2作为触发源
  DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;	//不使用波形发生器
  DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;	//不使用DAC输出缓冲
  DAC_Init(DAC_Channel_1, &DAC_InitStructure);

  /* 配置DAC 通道2 */
  DAC_Init(DAC_Channel_2, &DAC_InitStructure);

2、GPIO配置

        前文提到,DAC输出必须使用模拟输入的配置。

  GPIO_InitTypeDef GPIO_InitStructure;

  /* 使能GPIOA时钟 */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	
	
  /* DAC的GPIO配置,模拟输入 */
  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_4 | GPIO_Pin_5;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

3、TIM配置

        前文介绍TIM触发DAC,TIM必须配置为TRGO的方式才可以。

  TIM_TimeBaseInitTypeDef    TIM_TimeBaseStructure;
	
	/* 使能TIM2时钟,TIM2CLK 为72M */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	
  /* TIM2基本定时器配置 */
 // TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); 
  TIM_TimeBaseStructure.TIM_Period = (20-1);       									//定时周期 20  
  TIM_TimeBaseStructure.TIM_Prescaler = 0x0;       							//预分频,不分频 72M / (0+1) = 72M
  TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;    						//时钟分频系数
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  	//向上计数模式
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

  /* 配置TIM2触发源 */
  TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);

	/* 使能TIM2 */
  TIM_Cmd(TIM2, ENABLE);

        此时最终输出正弦波频率为72MHz/20/32=112.5KHz。在运行过程中,通过调节Period,即可实现调节正弦波的输出频率。

4、DMA配置

        此时为双DAC共用一个32bit的寄存器,外设数据地址固定,内存数据地址自增。高位放一路DAC、低位放一路DAC即可。

uint32_t DualSine12bit[POINT_NUM];

  DMA_InitTypeDef  DMA_InitStructure;
	/* 使能DMA2时钟 */
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
	/* 配置DMA2 */
  DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR12RD_ADDRESS;					//外设数据地址 寄存器 DHR12RD 的地址12位、右对齐、双通道
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&DualSine12bit ;				//内存数据地址 DualSine12bit
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;											//数据传输方向内存至外设
  DMA_InitStructure.DMA_BufferSize = POINT_NUM;														//缓存大小为POINT_NUM字节	
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;				//外设数据地址固定	
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;									//内存数据地址自增
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;	//外设数据以字为单位
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;					//内存数据以字为单位	
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;													//循环模式
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;											//高DMA通道优先级
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;														//非内存至内存模式	

  DMA_Init(DMA2_Channel4, &DMA_InitStructure);
	
  /* 使能DMA2-14通道 */
  DMA_Cmd(DMA2_Channel4, ENABLE);

三、单DAC输出-HAL库工程

        HAL库来看一下单DAC输出的配置,其实基本上就是不初始化另一个DAC,输出寄存器选择单路即可。

 1、DAC配置

        配置一个DAC,且均使用TIM2作为触发源。

  /* DAC外设时钟使能 */
  __HAL_RCC_DAC_CLK_ENABLE();

  DAC_ChannelConfTypeDef sConfig;
  /* DAC初始化 */
  DAC_InitStructure.Instance = DAC;
  HAL_DAC_Init(& DAC_InitStructure);

  /* DAC通道输出配置 */
  sConfig.DAC_Trigger = DAC_TRIGGER_T6_TRGO;
  sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE;
  HAL_DAC_ConfigChannel(& DAC_InitStructure, &sConfig, DAC_CHANNEL_2);

2、GPIO配置

        前文提到,DAC输出必须使用模拟输入的配置。

  GPIO_InitTypeDef GPIO_InitStruct;
  
  /* DAC通道引脚端口时钟使能 */
  __HAL_RCC_GPIOA_CLK_ENABLE();
  
  /* DAC通道引脚配置 */
  GPIO_InitStruct.Pin = GPIO_PIN_5;
  GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

3、TIM配置

        前文介绍TIM触发DAC,TIM必须配置为TRGO的方式才可以。

  TIM_MasterConfigTypeDef sMasterConfig;
  
   __HAL_RCC_TIM6_CLK_ENABLE();
  /* 初始化定时器,用于触发DAC更新 */
  TIM_Time.Instance = TIM6;
  TIM_Time.Init.Prescaler = 71;
  TIM_Time.Init.CounterMode = TIM_COUNTERMODE_UP;
  TIM_Time.Init.Period = 1000;
  HAL_TIM_Base_Init(&TIM_Time);
  
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  HAL_TIMEx_MasterConfigSynchronization(&TIM_Time, &sMasterConfig);

        此时最终输出正弦波频率为72MHz/72/1000/32=31.25Hz。在运行过程中,通过调节Period,即可实现调节正弦波的输出频率。

4、DMA配置

        此时为单DAC用一个32bit的寄存器,外设数据地址固定,内存数据地址自增。高位无数据、低位放DAC即可,因此可以配置为半字16bit即可。

DMA_HandleTypeDef DMA_InitStructure;
  /* DMA控制器初始化 */  
  DMA_InitStructure.Instance = DMA2_Channel4;
  DMA_InitStructure.Init.Direction = DMA_MEMORY_TO_PERIPH;
  DMA_InitStructure.Init.PeriphInc = DMA_PINC_DISABLE;
  DMA_InitStructure.Init.MemInc = DMA_MINC_ENABLE;
  DMA_InitStructure.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
  DMA_InitStructure.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
  DMA_InitStructure.Init.Mode = DMA_CIRCULAR;
  DMA_InitStructure.Init.Priority = DMA_PRIORITY_HIGH;
  HAL_DMA_Init(&DMA_InitStructure);
  __HAL_LINKDMA( DAC_InitStructure,DMA_Handle2,DMA_InitStructure); 
  /* DMA中断配置 */
  HAL_NVIC_SetPriority(DMA2_Channel4_5_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(DMA2_Channel4_5_IRQn);
  /* 启动DACx DMA功能 */
  HAL_DAC_Start_DMA(& DAC_InitStructure,DACx_CHANNEL,(uint32_t *) Sine12bit,32,DAC_ALIGN_12B_R);

四、用户侧

        相比较直接用TIM触发双路DAC DMA搬运输出正弦波,和在TIM中断输出两个DAC的数据值,有什么区别呢。

1、触发方式

  • TIM触发DAC DMA:TIM产生定时器中断或事件,触发DMA(直接存储器访问),DMA从存储器中搬运数据到DAC。这种方式由硬件自动控制,比较适合高效的数据传输,特别是在需要连续和高精度的信号输出时。
  • TIM中断手动输出DAC:TIM中断触发时,CPU在中断服务程序(ISR)中手动更新DAC的值。这种方式依赖于软件中断的处理和CPU执行,适用于相对较简单、较低频率的应用。

2、CPU占用与效率

  • TIM触发DAC DMA
    • 高效:使用DMA搬运数据,完全由硬件负责数据的传输,不需要CPU干预。CPU只需在必要时进行初始化和配置,DMA的工作完全不占用CPU资源。
    • 低延迟:由于数据传输由DMA直接完成,减少了CPU的负担,因此延迟较低,且能高效地输出高频信号。
    • 适合高频应用:对于需要较高频率的正弦波输出,使用DMA可以提供更高的稳定性和精度。
  • TIM中断手动输出DAC
    • CPU占用高:每次TIM中断发生时,CPU必须进入中断服务程序,执行DAC数据更新操作。如果采样频率较高,会导致CPU负担加重,可能影响其他任务的执行。
    • 相对低效:由于每个中断都需要CPU参与,尤其是在高频信号输出时,处理速度和效率相对较低。
    • 适合低频应用:适合输出频率较低或不要求高精度的场景。

3、精度与一致性

  • TIM触发DAC DMA
    • 更高的精度和一致性:DMA的传输是由硬件控制的,精度和同步性通常较高,能够保证正弦波的输出不受CPU的干扰。
    • 稳定性高:DMA传输数据时不会受到CPU调度的影响,能够实现更加稳定的输出,尤其是在较高的输出频率时。
  • TIM中断手动输出DAC
    • 中断延迟可能影响精度:如果中断处理不及时,或者系统其他任务占用较多CPU资源,可能导致DAC输出的波形不如DMA方式稳定。尤其在频率较高时,可能会出现微小的波形误差。
    • CPU的处理中断调度可能有延迟:当CPU处理其他任务时,中断的响应时间可能会有所不同,导致输出信号的时序不如DMA稳定。

4、灵活性与控制

  • TIM触发DAC DMA
    • 灵活性较低:一旦设置好DMA和定时器,整个过程几乎不需要CPU干预,适用于简单的连续信号输出。如果要更改信号类型或频率,需要重新配置DMA。
    • 适合周期性信号:非常适合周期性、稳定的信号输出,比如正弦波、三角波等。
  • TIM中断手动输出DAC
    • 灵活性较高:可以在中断中灵活地修改输出值,改变正弦波的频率、振幅、相位等,适合需要动态调整的应用。
    • 适合更复杂的信号:如果输出的信号需要进行动态调整或者涉及到比较复杂的控制,手动更新DAC可能会更加灵活。

5、资源消耗

  • TIM触发DAC DMA
    • 资源消耗低:DMA和定时器触发是硬件控制的,CPU几乎不参与数据传输和波形生成,节省了CPU资源。
    • 对系统影响小:由于DMA工作在硬件层面,其他系统任务不会被影响,尤其在多任务操作系统(RTOS)中,能够确保较高的实时性和稳定性。
  • TIM中断手动输出DAC
    • CPU资源消耗高:每次TIM中断都需要CPU参与,因此系统负载较高,特别是在高频率时,可能影响到其他任务的处理。
    • 中断优先级和响应影响:如果系统中断处理比较复杂或其他中断优先级较高,可能会影响TIM中断的及时响应,导致输出的信号不够精确。

总结:

特性TIM触发DAC DMATIM中断手动输出DAC
CPU 占用低(DMA 完全由硬件控制)高(每个中断都需要 CPU 处理)
精度与稳定性高,数据传输由硬件控制较低,依赖中断处理延迟
灵活性低,主要用于稳定周期性信号输出高,可以动态调整输出波形
实现复杂度较低,配置定时器和 DMA 后基本无需干预较高,需要手动在中断服务程序中控制输出
适用场景高频、稳定、连续的正弦波输出低频、动态可变的信号输出

选择建议

  • 如果你的目标是生成高精度、高频、稳定的正弦波信号,并且不需要频繁调整信号特性,使用 TIM触发DAC DMA 是更好的选择。
  • 如果你需要在信号输出时动态调整参数或对信号进行复杂的控制,或者正弦波频率较低,可以考虑使用 TIM中断手动输出DAC

http://www.kler.cn/a/451968.html

相关文章:

  • 【ES6复习笔记】模板字符串(3)
  • Python知识图谱框架
  • Ruby+Selenium教程
  • langchain使用FewShotPromptTemplate出现KeyError的解决方案
  • TGRS | 可变形傅里叶卷积用于遥感道路分割
  • 游戏引擎学习第58天
  • Matrix-Breakout 2 Morpheus
  • 使用vcpkg安装opencv>=4.9后#include<opencv2/opencv.hpp>#include<opencv2/core.hpp>无效
  • C语言-09内存管理
  • MR-GDINO: Efficient Open-World Continual Object Detection
  • vue中做一个最多输入一位小数且可以为负数的输入框(包含最前面最后面为小数点及多个-符号与前导零校验)
  • PaginationInnerInterceptor,spring中pojo
  • WebRTC搭建与应用(五)-Coturn踩坑记
  • 游戏APP如何设计混合变现,最大化变现收益?
  • Unity 重写GridLayoutGroup使居中对齐
  • HarmonyOS NEXT 实战之元服务:静态案例效果---最近播放音乐
  • imx6ull qt多页面控制系统(正点原子imx系列驱动开发)
  • ASN.1 轻松入门2
  • HarmonyOS NEXT 实战之元服务:静态案例效果(二)
  • 131、sqlserver中使用mybatis中的Page进行分页查询时,SQL成功执行(控制台已打印),Page的Records没值bug1.代码复现:
  • NUCLEO-F446RE测试板验证DS100示波器功能
  • 【视觉惯性SLAM:编译及编译工具】
  • 2024.8 设计可解释的 ML 系统以增强对医疗保健的信任:对提出的负责任的临床医生-AI 协作框架的系统评价
  • wordpress调用指定ID分类下浏览最多的内容
  • 印度软件业的发展能给中国软件行业什么样的启示和借鉴
  • C语言-基因序列转换独热码(one-hot code)