STM32CubeMX:使用DAC输出正弦波的三种方法(while,定时器中断,DMA)

1.DAC概念简介:

DAC 的工作原理是根据数字输入信号的数值,生成相应的模拟输出电压或电流。它通常接收一个二进制数字输入,该数字代表了一个特定的数值范围。DAC 通过将这个数字值转换为模拟信号的电压或电流水平来输出。(功能与ADC相反)

2.正弦波输出方式1:简单粗暴while循环输出

Cubemx配置过程:

1.选择MCU:

我选择的芯片是STM32G474VET6,这款芯片性能比较强大而且功能很全面

3.配置RCC

配置RCC(复位和时钟控制模块)HSE(高速外部时钟)为 Crystal/Ceramic Resonator (晶体振荡器或陶瓷谐振器)

3.配置时钟树

直接修改HCLK的值为最高值170MHz(HCLK指高级高性能总线时钟),一般情况下默认给主频最高频率使芯片性能拉满

4.配置SYS

5.配置DAC

在输出通道1选择模式 Connected to external pin only ,表示输出 1(OUT1)的模式被设置为仅连接到外部引脚。这意味着 OUT1 的信号只会被输出到外部引脚上,而不会连接到芯片内部的其他外设。

在配置里只更改Trigger为Software trigger(软件触发)

由于使用简单粗暴的while循环生成波形,不需要配置其他项目

6.生成代码

7.DAC生成正弦波

#define PI 3.1415926
void SineWave_Data( uint16_t num,uint16_t* S,float U)
{
    uint16_t i;
    for( i=0;i<num;i++)
    {
        S[i]=(uint16_t)((U*sin((1.0*i/(num-1))*2*PI)+U)*4095/3.3);
    }
}

正弦波代码生成:

  1. 函数参数的作用:

    • num:表示要生成的正弦波数据点的数量。这个参数决定了正弦波的分辨率,数值越大,生成的正弦波越平滑。
    • S:是一个指向 uint16_t 类型的指针,用于存储生成的正弦波数据。通过指针传递,可以在函数内部修改外部传入的数组内容,方便在主程序中使用这些数据来驱动 DAC。
    • U:用于控制正弦波的幅度。可以通过调整 U 的值来改变正弦波的最大和最小电压值
  • 循环生成正弦波数据:
    • for 循环遍历从 0 到 num - 1 的每个数据点。
    • S[i]=(uint16_t)((U*sin((1.0*i/(num - 1))*2*PI)+U)*4095/3.3); 这行代码是生成正弦波数据的核心。
      • sin((1.0*i/(num - 1))*2*PI):这部分计算了在一个周期内不同位置的正弦值。i/(num - 1) 表示当前数据点在整个周期中的位置比例,乘以 2*PI 后得到对应的弧度值,再通过 sin 函数计算出正弦值。这样可以在一个周期内均匀地生成不同的正弦值。
      • U*sin((1.0*i/(num - 1))*2*PI)+U:这里将正弦值乘以幅度 U,然后再加上 U。这样做是为了确保正弦波在幅度 U 的基础上进行正负摆动,使得正弦波的范围在 0 到 2*U 之间。
      • *4095/3.3:STM32 的 DAC 通常是 12 位的,其满量程数值为 4095。而 3.3 可能是参考电压值。将前面计算得到的正弦波数值乘以 4095/3.3 是为了将正弦波的幅度映射到 DAC 的满量程范围内,使得生成的正弦波能够正确地在 DAC 输出上显示。
    • 最后,将计算得到的正弦波数据存储到数组 S 中,供后续使用。

DAC的库函数有


HAL_StatusTypeDef HAL_DAC_Start(DAC_HandleTypeDef* hdac, uint32_t Channel);     //开启DAC输出
HAL_StatusTypeDef HAL_DAC_Stop(DAC_HandleTypeDef* hdac, uint32_t Channel);   //关闭DAC输出
HAL_StatusTypeDef HAL_DAC_Start_DMA(DAC_HandleTypeDef* hdac, uint32_t Channel, uint32_t* pData, uint32_t Length, uint32_t Alignment); //需要函数中不断开启   //开启DAC的DMA输出
HAL_StatusTypeDef HAL_DAC_Stop_DMA(DAC_HandleTypeDef* hdac, uint32_t Channel); //关闭DAC的DMA输出
HAL_StatusTypeDef HAL_DAC_SetValue(DAC_HandleTypeDef* hdac, uint32_t Channel, uint32_t Alignment, uint32_t Data);  //设置DAC输出值
uint32_t HAL_DAC_GetValue(DAC_HandleTypeDef* hdac, uint32_t Channel);  //获取DAC输出值

 在主函数里初始化正弦波数组并开启DAC,在while循环里不断更新DAC发送值实现正弦波输出

		HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_1,DAC_ALIGN_12B_R,S[i]);
		HAL_DAC_Start(&hdac1,DAC_CHANNEL_1);
	    i++;
	    if(i>=1000)
		  i=0;

(注意全局变量i以及数组s需要自己定义)

成功实现正弦波输出

虽然使用while循环输出正弦波的方法简单粗暴,但是会占用大量 CPU 资源以及使用这种简单的 while 循环方式很难达到较高的频率精度和稳定性,不推荐大家使用,但是可以拿来测试。

3.正弦波输出方式2:定时器中断输出

为了达到可调频率的目的,我们可以采用定时器中断输出DAC,设置定时器中断的频率便可以调节正弦波的频率,具体原理是:

  • 通过调整定时器的计数初值和预分频系数,可以改变定时器的中断频率,从而控制正弦波的输出频率。定时器的中断频率与正弦波的输出频率之间存在一定的比例关系,一般来说,定时器中断频率是正弦波输出频率的整数倍,该整数倍取决于一个周期内的采样点数。
  • 例如,若要输出一个频率为 500Hz 的正弦波,采样频率仍为 10kHz,则一个周期内需要 20 个采样点,此时定时器的中断频率应为 10kHz / 20 = 500Hz。通过调整定时器的参数,使其产生 500Hz 的中断频率,即可实现 500Hz 正弦波的输出。

1.Cubemx设置修改:

1.添加定时器并将其配置成向上计数模式,计数器溢出触发,并且将定时器中断使能。我将定时器的中断频率设置成了100khz,具体定时器中断频率计算公式以及使用方法可以参照网上其他教程这里不做细致讲解。

2.将DAC的触发源改为定时器溢出触发

3.重新生成代码

2.重写函数

1.写入定时器初始化函数并删除原来在while里写入的代码

2.重新定时器中断回调函数(方法与while循环相同)

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	  if (htim == &htim6)
	  {
		HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_1,DAC_ALIGN_12B_R,S[i]);
		HAL_DAC_Start(&hdac1,DAC_CHANNEL_1);
	    i++;
	    if(i>=1000)
		  i=0;
	  }
}

这样便可以生成频率可调的正弦波,最终正弦波的频率便是定时器中断频率除以一个周期的采样点个数。

其实在定时器中断里不断更新输出DAC可以看作DMA不断运输DAC值的前身,但这种方法具有很多欠缺:

一、中断响应的不确定性

  1. 中断延迟
    • 虽然定时器中断可以在一定程度上保证周期性地触发,但在实际系统中,中断可能会因为其他高优先级中断或系统繁忙而被延迟响应。
    • 这可能导致正弦波的输出出现不规律的时间间隔变化,影响波形的准确性和稳定性。
    • 例如,在一个复杂的嵌入式系统中,如果有多个高优先级中断频繁发生,定时器中断可能会被推迟执行,使得 DAC 输出正弦波的时间点不准确。
  2. 中断嵌套
    • 当系统中存在多个中断源时,可能会发生中断嵌套。如果在定时器中断服务函数执行过程中,发生了更高优先级的中断,那么定时器中断的处理将被暂停,直到高优先级中断处理完成。
    • 这会进一步增加中断响应的不确定性,影响正弦波的输出质量。
    • 例如,在一个同时进行通信和数据处理的系统中,如果通信中断的优先级高于定时器中断,那么在通信中断发生时,定时器中断可能会被延迟,导致正弦波输出出现间断或失真。

二、资源占用和效率问题

  1. CPU 资源占用
    • 定时器中断需要 CPU 不断地响应和处理中断服务函数,这会占用一定的 CPU 资源。
    • 对于资源有限的嵌入式系统来说,过多的 CPU 资源被用于处理定时器中断可能会影响其他任务的执行效率。
    • 例如,在一个需要同时进行实时数据采集和处理的系统中,如果定时器中断占用了过多的 CPU 时间,可能会导致数据采集和处理的速度变慢,影响系统的整体性能。
  2. 功耗增加
    • 频繁的中断处理会使 CPU 处于较为活跃的状态,从而增加系统的功耗。
    • 对于电池供电的嵌入式设备来说,这可能会缩短设备的续航时间。
    • 例如,在一个便携式设备中,如果使用定时器中断配置 DAC 输出正弦波,可能会因为功耗增加而导致电池寿命缩短。

三、精度和稳定性限制

  1. 定时器精度限制
    • 定时器的精度受到系统时钟频率和定时器配置的限制。在一些情况下,定时器的精度可能不足以生成高精度的正弦波。
    • 例如,如果系统时钟频率不稳定或定时器的分辨率不够高,可能会导致正弦波的频率和幅度出现误差,影响波形的质量。
  2. DAC 转换误差
    • DAC 的转换精度和速度也会影响正弦波的输出质量。如果 DAC 的转换速度跟不上定时器中断的频率,可能会导致波形失真。
    • 此外,DAC 的转换误差也会影响正弦波的幅度和相位精度。
    • 例如,在一个要求高精度音频输出的系统中,如果 DAC 的转换精度不够高,可能会导致音频信号出现噪声和失真。

四、代码复杂性和维护难度

  1. 中断服务函数的复杂性
    • 定时器中断服务函数通常需要在短时间内完成对 DAC 的更新操作,以保证正弦波的连续输出。这可能会使中断服务函数变得复杂,增加代码的维护难度。
    • 例如,如果在中断服务函数中还需要进行其他复杂的计算或数据处理,可能会导致中断处理时间过长,影响正弦波的输出稳定性。
  2. 调试困难
    • 由于定时器中断的执行是异步的,并且可能会被其他中断或任务打断,因此在调试过程中可能会比较困难。
    • 例如,在调试正弦波输出问题时,很难确定是定时器中断的问题还是其他部分的代码问题,增加了调试的时间和难度。

进而,我们引出此次的最终方法——DMA配合DAC输出正弦波

4.正弦波输出方式3:DMA传输DAC

1.DMA介绍:

一、基本概念

DMA 允许某些硬件子系统(如外设)直接与主存储器进行数据传输,而无需 CPU 的持续参与。这样可以大大提高数据传输的效率,释放 CPU 资源以执行其他任务。

二、工作原理

  1. 请求阶段

    • 当外设需要进行数据传输时,它向 DMA 控制器发出数据传输请求。这个请求可以是在特定事件发生时自动触发,例如定时器溢出、数据接收完成等。
    • DMA 控制器接收到请求后,会检查当前是否有其他正在进行的 DMA 传输。如果没有,它会向 CPU 发出总线请求,请求使用系统总线进行数据传输。
  2. 授权阶段

    • CPU 在收到 DMA 控制器的总线请求后,会根据当前系统的状态决定是否授权 DMA 控制器使用总线。如果 CPU 正在执行关键任务或者系统总线已经被其他高优先级设备占用,CPU 可能会延迟授权 DMA 控制器的请求。
    • 一旦 CPU 授权 DMA 控制器使用总线,它会暂停当前正在执行的任务,并将总线控制权交给 DMA 控制器。
  3. 数据传输阶段

    • DMA 控制器获得总线控制权后,开始进行数据传输。它会根据预先设置的传输参数,从源地址读取数据,并将其写入到目标地址。
    • 源地址和目标地址可以是内存地址、外设寄存器地址或者其他存储设备的地址。DMA 控制器可以自动递增源地址和目标地址,以便连续地进行数据传输。
    • 在数据传输过程中,DMA 控制器可以根据需要进行数据校验、错误处理等操作,以确保数据的完整性和正确性。
  4. 结束阶段

    • 当 DMA 传输完成后,DMA 控制器会向 CPU 发出中断请求,通知 CPU 数据传输已经完成。CPU 在收到中断请求后,会恢复之前被暂停的任务,并对 DMA 传输的结果进行处理。
    • DMA 控制器在发出中断请求后,会释放总线控制权,等待下一次数据传输请求。

三、主要特点

  1. 高速传输:DMA 可以实现高速的数据传输,其传输速度通常只受限于系统总线的带宽和外设的性能。相比之下,通过 CPU 进行数据传输需要大量的指令执行和寄存器操作,速度相对较慢。
  2. CPU 负载减轻:由于 DMA 传输不需要 CPU 的持续参与,因此可以大大减轻 CPU 的负载,使 CPU 能够专注于执行其他重要的任务。这对于实时性要求较高的系统来说尤为重要。
  3. 独立于 CPU:DMA 控制器是一个独立的硬件模块,它可以在不依赖 CPU 的情况下进行数据传输。这使得 DMA 传输可以与 CPU 的操作并行进行,提高了系统的整体性能。
  4. 可编程性:DMA 控制器通常具有可编程的特性,可以通过设置不同的传输参数来满足不同的应用需求。例如,可以设置传输的数据长度、源地址和目标地址、传输模式等。

四、应用场景

  1. 数据采集与处理:在数据采集系统中,DMA 可以用于将传感器采集到的数据快速传输到内存中,以便进行后续的处理和分析。这样可以避免 CPU 在数据传输过程中的等待,提高数据采集的效率。
  2. 存储设备访问:在访问存储设备(如硬盘、闪存等)时,DMA 可以用于实现高速的数据读写操作。这可以提高存储设备的访问速度,减少数据传输的延迟。
  3. 网络通信:在网络通信中,DMA 可以用于将接收到的数据快速传输到内存中,或者将需要发送的数据从内存传输到网络接口。这可以提高网络通信的效率,减少 CPU 的负载。
  4. 图形处理:在图形处理系统中,DMA 可以用于将图像数据快速传输到图形处理器(GPU)中,以便进行渲染和显示。这可以提高图形处理的速度,减少 CPU 的参与。

总之,DMA 是一种非常重要的计算机技术,它可以大大提高数据传输的效率,减轻 CPU 的负载,提高系统的整体性能。在现代计算机系统中,DMA 被广泛应用于各种领域,如数据采集、存储设备访问、网络通信、图形处理等。

2.Cubemx设置修改:

我们只需要将DAC加入dma的通道中,选择Circular(循环模式) 其他设置不变 

生成代码

2.重写代码

  HAL_TIM_Base_Start_IT(&htim6);
  SineWave_Data(1000,S,1.6);
  HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, (uint32_t *)S,1000, DAC_ALIGN_12B_R);

是不是非常简单,只需要写这三个代码便可以实现DAC输出正弦波。

使用 DAC(数模转换器)配合 DMA(直接存储器访问)输出正弦波具有以下优势:

一、提高系统性能

  1. 降低 CPU 负载

    • 在传统的方式中,CPU 需要不断地向 DAC 发送数据以输出正弦波,这会占用大量的 CPU 时间,尤其是在需要高频率输出正弦波的情况下。而使用 DMA 后,CPU 只需在开始时设置好 DMA 传输参数,然后 DMA 控制器就可以自动从内存中读取正弦波数据并传输给 DAC,无需 CPU 的持续干预。
    • 这样可以大大降低 CPU 的负载,使 CPU 能够专注于执行其他重要的任务,提高整个系统的性能。
    • 例如,在一个同时需要进行数据处理、通信和控制的嵌入式系统中,使用 DMA 可以确保 CPU 有足够的时间来处理这些任务,而不会因为频繁地向 DAC 发送数据而导致系统响应变慢。
  2. 提高数据传输效率

    • DMA 可以实现高速的数据传输,其传输速度通常只受限于系统总线的带宽和外设的性能。相比之下,通过 CPU 进行数据传输需要大量的指令执行和寄存器操作,速度相对较慢。
    • 对于输出正弦波这种需要连续、高速数据传输的应用,DMA 可以确保正弦波数据能够及时、准确地传输到 DAC,从而提高正弦波的输出质量和稳定性。
    • 例如,在音频应用中,使用 DMA 可以确保音频数据能够以高采样率传输到 DAC,从而实现高质量的音频输出。

二、增强系统稳定性

  1. 减少中断次数

    • 在没有 DMA 的情况下,CPU 通常需要通过中断来向 DAC 发送数据。每次中断都需要 CPU 进行上下文切换,这会增加系统的开销,并且可能导致系统不稳定。而使用 DMA 后,CPU 可以在一个较长的时间内不被中断,从而减少了中断次数,提高了系统的稳定性。
    • 例如,在一个对稳定性要求较高的工业控制系统中,减少中断次数可以降低系统出现故障的概率,提高系统的可靠性。
  2. 提高数据传输的准确性

    • DMA 控制器通常具有数据校验和错误处理功能,可以确保数据传输的准确性。相比之下,通过 CPU 进行数据传输时,数据的准确性可能会受到 CPU 负载、中断延迟等因素的影响。
    • 对于输出正弦波这种对数据准确性要求较高的应用,使用 DMA 可以确保正弦波数据的准确性,从而提高正弦波的输出质量。
    • 例如,在精密测量仪器中,使用 DMA 可以确保测量数据能够准确地传输到 DAC,从而实现高精度的模拟输出。

三、提高系统灵活性

  1. 易于实现动态调整

    • 使用 DMA 可以方便地实现对正弦波参数的动态调整。例如,可以通过修改内存中的正弦波数据表来改变正弦波的频率、幅度、相位等参数,而无需修改 CPU 的程序代码。
    • 这使得系统能够根据不同的应用需求快速调整正弦波的输出,提高了系统的灵活性和适应性。
    • 例如,在音频合成器中,使用 DMA 可以实现对音频参数的实时调整,从而产生各种不同的音效。
  2. 支持多种数据格式

    • DMA 可以支持多种数据格式的传输,例如 8 位、16 位、32 位等。这使得系统能够根据不同的应用需求选择合适的数据格式,提高了系统的灵活性。
    • 例如,在音频应用中,可以根据音频质量的要求选择不同的数据格式,以实现高保真的音频输出。

总之,使用 DAC 配合 DMA 输出正弦波可以提高系统性能、增强系统稳定性、提高系统灵活性,是一种非常有效的方法。

3.波形展示

生成了频率非常符合设置要求的100Hz 非常丝滑

这是我第一次写博客,如果有什么不足的地方欢迎大家指正和交流!

STM32是一款广受欢迎的微控制器系列,DAC(数模转换器)则是其内部集成的功能模块之一,用于将数字信号转换成模拟电压。要在STM32上生成正弦波形并通过DAC输出,通常需要预先准备一组代表正弦曲线的数据点,并将其存储在一个数组中。 下面是一个简单的步骤说明: ### 创建正弦表 为了创建这个表格,您可以使用公式`y = sin(x)`计算一系列角度值对应的正弦函数结果。由于大多数嵌入式系统使用的都是定点运算而非浮点运算,因此一般会乘以某个因子并取整,以便能更好地利用DAC的有效位数。例如,对于12位分辨率的DAC来说,可以将每个sin()的结果*4095(即\(2^{12}-1\))然后四舍五入到最接近的整数值。 ```c #define TABLE_SIZE 256 // 正弦波周期内的样本数目 uint16_t sineTable[TABLE_SIZE]; void initSineTable(void) { float amplitude = (float)(0xFFFF >> (16 - DAC_RESOLUTION)); // 根据DAC分辨率调整幅度 for(int i=0; i<TABLE_SIZE; ++i){ float angleInRadians = ((float)i / TABLE_SIZE * M_PI); // 角度从0变到π sineTable[i] = lrint(amplitude * (sin(angleInRadians)+1)/2); } } ``` 注意这里的代码片段假设了您的应用程序只需要半个周期;如果希望得到完整的正负周期,则需适当修改范围以及偏移量。 ### 配置与初始化DAC外设 接下来,在主程序开始前配置好硬件资源。这包括启用相应的时钟、选择通道及模式等操作。具体细节依赖于所采用的具体型号及其库版本。这里给出基于HAL库的大致框架: ```c /* 初始化全局变量 */ DAC_HandleTypeDef hdac; int main(void) { /* 系统初使化 */ HAL_Init(); /* 初始化系统时钟 */ SystemClock_Config(); /* GPIO引脚初始化设置 */ MX_GPIO_Init(); /* DAC外设初始化 */ __HAL_RCC_DAC_CLK_ENABLE(); // 开启DAC时钟 hdac.Instance=DAC; // 设置实例句柄 if(HAL_OK != HAL_DAC_Init(&hdac)){ Error_Handler(__FILE__, __LINE__); } /* 其他必要的初始化工作 */ while (1) { ... } // 主循环体内容... } // 错误处理函数定义... static void Error_Handler(char *file, int line){} ``` ### 输出正弦波 最后一步就是通过定时中断或其他机制不断更新DAC寄存器中的值以产生连续变化的输出电压。“sineTable[]”里保存的就是我们需要依次送进DAC的一串数据点了。 关于如何精确控制频率的问题,则取决于您选定的时间基准源——比如可以用TIMx定时器触发DMA传输完成事件来驱动这一过程。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值