STM32数模转换器的说明
2023-08-16 来源:elecfans
使用STM32 micros的 模拟数字转换器(ADC) 后,接下来要处理的明显内部硬件模块是 数字到模拟转换器(DAC) 。顾名思义,该模块仅具有ADC的补充功能。它将数字二进制值转换为模拟电压输出。 DAC模块具有多种用途,包括音频生成,波形生成等。通常在大多数8位微控制器中,该模块不可用,并且 脉冲宽度调制(PWM)可以稍微满足其需求。 》 块。部分原因是由于它们的硬件资源和运行速度相对较低。所有STM32单片机都具有PWM模块,但大容量STM32也具有DAC模块。 STM32 DAC模块不是很复杂,并且在工作原理方面与ADC模块相似。下面的简化框图显示了STM32 DAC模块的主要组件。
再次请注意低密度STM32微处理器没有任何内置DAC。对于这个问题,我使用了Waveshare的带有STM32F103ZET6 micro的Port103Z(http://www.wvshare.com/product/Port103Z.htm)开发板。该STM32 micro具有两个带独立转换器的DAC通道,并包含大量其他硬件资源。像这样的大多数高端微控制器至少带有两个DAC通道。立体声音频生成需要两个DAC通道-一个非常常见的基于DAC的应用程序。开发板经过精心设计,可将DAC输出连接至LED,以直接可视化DAC输出。
STM32 DAC模块的主要功能
STM32 micros的DAC模块既可以与8-位或12位分辨率。后者的精度更高,而前者的性能要比后者更高。
每个通道都有自己的转换器,并且不依赖于其他转换器。在STM32F103ZET6中,有两个带有两个独立转换器的DAC通道输出。
与其他微控制器的DAC不同,STM32微控制器的DAC具有两个附加功能,它们是伪随机的噪声和三角波形生成功能。这些功能有助于信号处理和定制信号生成。
为进一步提高精度并进一步控制输出电压,DAC模块可与基准电压源相连,通常在2.4V之间到3.3V DC。
可以使用左右两端的合理输入。
DA转换可以触发事件。此功能有很多应用。
就像其他任何硬件一样,DAC也具有DMA功能。
DAC模块集成了可用于降低输出阻抗并直接驱动外部负载的输出缓冲器,而无需添加外部运算放大器。缓冲可确保原始信号不会因负载效应而衰减或失真。
可能具有独立或双重操作模式。
功能描述
STM32 micros的DAC可以通过多种方式进行操作。如前所述,我们可以在独立和双重模式下使用它。除此之外,我们还可以通过外部事件触发这些DAC。如果不使用触发选项,则在一个时钟周期后更新DAC输出。如果使用任何触发选项,则在3 – 4个时钟周期后更新输出。 DAC模块的输出可以独立或同时更新。 DAC的最大分辨率为12位。 DAC输出的电压公式如下:
其中n是位数。
除非使用触发功能,否则操作DAC非常简单。我们将必须打开所需的输出通道并仅写入数字数据值。但是,如果相反,则会出现很多可能性。 STM32F10x参考手册在双模式操作部分中陈述了所有这些可能性。尽管我不会详细介绍所有这些操作,但是这些操作很简单。
输出可以在内部进行缓冲,从而无需外部运算放大器缓冲器,从而减少了外部组件数量和成本。成本。我强烈建议使用内部缓冲器,以确保并保持精度,同时避免输出负载问题。
STM32 DAC模块随附的另外两个玩具是白噪声和三角形波形发生器。首先,这些功能可能会让您感到惊讶,甚至可能使您觉得它们是不必要的。但是,当我们需要为各种应用(包括音乐,音调等)生成任意波形或在信号中注入噪声以进行调试时,这些东西会派上用场。从字面上看,我们可以模拟现实生活中嘈杂的电子电路环境。三角波形发生器可用于将可变幅度三角波形分量添加到给定信号。此处有更多详细信息:http://www.st.com/web/en/resource/technical/document/application_note/CD00259245.pdf。
在STM32F103ZET6中,DAC输出1和2引脚位于GPIOA PA4 和 PA5 。尽管这些引脚在与DAC模块一起使用时用作输出,但ST建议将它们设置为模拟输入,以避免寄生消耗–这是要考虑的重要点。
寄存器
在STM32 micros中,我们只需要处理三个寄存器-一个用于配置,一个用于选择软件触发,另一个用于设置DAC输出。 。我们没有其他寄存器要处理。
DAC控制寄存器(DAC_CR) 是我们唯一需要的寄存器配置DAC。配置该寄存器使我们能够启用/禁用DAC通道,设置输出类型,启用DMA支持以及执行其他操作。 STM32 micro的两个DAC都单独使用此寄存器进行配置。
下一组寄存器是数据寄存器。在这些地方,我们将编写数字数据,这些数据最终将转换为缩放的模拟输出。 DAC数据寄存器可以接受三种整数格式的数据-8位,12位右对齐和12位左对齐。这些都有专用的寄存器。检出 DAC数据保存寄存器(DAC_DHRx) 。对于双模式DAC检出 DAC_DHRD 寄存器。然后,将 DAC_DHRx 寄存器中的数据加载一个或几个周期后,取决于是否使用任何触发器,将其加载到 DAC_DORx 寄存器中或不。 DAC_DORx 寄存器不能直接写入。
最后有一个寄存器称为 DAC软件触发寄存器(DAC_SWTRIGR) ,用于设置软件触发。
就像以前一样,我们不再需要我已经将我的SPL版本与代码示例集成在一起,因此可以处理寄存器。我仍然建议读者对它们有所了解。
硬件连接
下图概述了我们将在代码示例中使用的GPIO引脚。
代码示例
请注意,在STM32 micro的其他内部模块上,我们需要先启用DAC模块的外设时钟,然后通过设置正确的寄存器来配置所需的操作,最后启用所需的硬件。这是我们应始终保持的顺序。在设置DAC本身之前,应按照建议启用和配置所需的GPIO端口引脚。检查一下我在所有示例中使用的时钟配置。
DAC Basic Demo
这是STM32 DAC的最原始演示。不使用触发。 DAC引脚输出参考偏移的正弦和余弦波形。由于在我的示例中DAC的参考源与电源(VDD)的参考源相同-3.3V,因此输出移位1.65V,以覆盖整个正弦变化。 MikroC的C Math库用于三角函数。与DAC输出引脚相连的LED逐渐淡入并发光,表明它们两端的电压变化平稳。
#include “DAC.h” #include “GPIO.h” #define radian_per_degree 0.0174532925 void setup(); void GPIO_setup(); void DAC_setup(); void main() { signed int temp1 = 0; signed int temp2 = 0; unsigned int degree = 0; setup(); while(1) { for(degree = 0; degree 《 360; degree++) { temp1 = (2047 * cos(radian_per_degree * degree)); temp1 = (2048 - temp1); DAC_DHR12R1 = ((unsigned int)temp1); temp2 = (2047 * sin(radian_per_degree * degree)); temp2 = (2048 - temp2); DAC_DHR12R2 = ((unsigned int)temp2); delay_ms(6); } }; } void setup() { GPIO_setup(); DAC_setup(); } void GPIO_setup() { enable_GPIOA(true); pin_configure_low(GPIOA_CRL, 4, analog_input); pin_configure_low(GPIOA_CRL, 5, analog_input); } void DAC_setup() { enable_DAC(true); DAC_reset(); set_DAC1_buffer(enable); set_DAC2_buffer(enable); enable_DAC1_channel(true); enable_DAC2_channel(true); }
视频链接:https://www.youtube.com/watch?v=JW6e-oHBCPQ。
三角波形生成
STM32 DAC模块的一个很酷的独特功能就是它能够生成对称的三角波形。该波形可以叠加在其他慢速波形或稳定的DC信号上。不需要任何波表或方程来生成三角波。内部计数器负责生成波形。我们可以根据需要设置三角波的幅度或峰值。为了使用三角波形,我们需要在 DAC_CR 寄存器中设置波形类型和触发选项。三角波的频率取决于两个因素:波幅和触发频率。在软件触发的情况下,我们需要考虑代码内部的延迟。这些延迟包括延迟函数调用,代码执行时间和其他因素。到目前为止,我注意到以下公式似乎给出了输出波形的大致时间段:
2 是由于DAC计数从最小或0开始到最大幅度计数,然后以与上升相同的方式达到最小计数这一事实。因此,根据上面的公式,我们可以推断出三角形波形的频率。
例如,让我们考虑一种情况,其中我们将使用软件触发作为这样每60µs触发一次,最大DAC计数幅度为2047。在这种情况下,三角波的时间段为
#include “DAC.h” #include “GPIO.h” void setup(); void DAC_setup(); void GPIO_setup(); void main() { bit time; unsigned long peak = Triangle_Amplitude_equal_to_4095; setup(); while(1) { set_DAC1_software_trigger(enable); set_DAC2_software_trigger(enable); if(get_input(GPIOF_IDR, 14) == 0) { while(get_input(GPIOF_IDR, 14) == 0); peak++; if(peak 》 Triangle_Amplitude_equal_to_4095) { peak = Triangle_Amplitude_equal_to_1; } enable_DAC1_channel(false); enable_DAC2_channel(false); set_DAC1_triangle_waveform_amplitude(peak); set_DAC2_triangle_waveform_amplitude(peak); enable_DAC1_channel(true); enable_DAC2_channel(true); } if(get_input(GPIOF_IDR, 13) == 0) { while(get_input(GPIOF_IDR, 13) == 0); time = ~time; } switch(time) { case 1: { delay_us(600); break; } default: { delay_us(60); break; } } }; } void setup() { GPIO_setup(); DAC_setup(); } void DAC_setup() { enable_DAC(true); DAC_reset(); set_DAC1_buffer(enable); enable_DAC1_trigger(true); select_DAC1_trigger_source(Software_trigger); select_DAC1_wave_type(triangle_wave_generation_enabled); set_DAC1_triangle_waveform_amplitude(Triangle_Amplitude_equal_to_4095); enable_DAC1_channel(true); set_DAC2_buffer(enable); enable_DAC2_trigger(true); select_DAC2_trigger_source(Software_trigger); select_DAC2_wave_type(triangle_wave_generation_enabled); set_DAC2_triangle_waveform_amplitude(Triangle_Amplitude_equal_to_4095); enable_DAC2_channel(true); } void GPIO_setup() { enable_GPIOA(true); enable_GPIOF(true); pin_configure_low(GPIOA_CRL, 4, analog_input); pin_configure_low(GPIOA_CRL, 5, analog_input); pin_configure_high(GPIOF_CRH, 13, digital_input); pin_configure_high(GPIOF_CRH, 14, digital_input); pull_up_enable(GPIOF_ODR, 13); pull_up_enable(GPIOF_ODR, 14); }
在本演示中,我们可以使用连接到GPIO PF14引脚的按钮来改变从两个DAC输出通道输出的三角波的幅度。连接到GPIO PF13引脚的按钮可用于选择时间段。
视频链接:https://www.youtube.com/watch?v=eX3zRjr8bQo。
产生白噪声
白噪声生成模块的设置与三角波形发生器的设置类似。除了波形以外,唯一不同的是产生白噪声的算法。这不在我的解释范围内。我们只需要知道我们可以使用该模块生成可变幅度的伪随机噪声。为了更好地理解,请查看https://en.wikipedia.org/wiki/Linear_feedback_shift_register。我们所需要做的就是根据需要屏蔽LFSR。
#include “DAC.h” #include “GPIO.h” void setup(); void DAC_setup(); void GPIO_setup(); void main() { setup(); while(1) { set_DAC2_software_trigger(enable); delay_us(10); }; } void setup() { GPIO_setup(); DAC_setup(); } void DAC_setup() { enable_DAC(true); DAC_reset(); set_DAC2_buffer(enable); enable_DAC2_trigger(enable); select_DAC2_trigger_source(Software_trigger); select_DAC2_wave_type(noise_wave_generation_enabled); set_DAC2_LFSR_mask(0x3FF); enable_DAC2_channel(true); } void GPIO_setup() { enable_GPIOA(true); pin_configure_low(GPIOA_CRL, 5, analog_input); }
视频链接:https://www.youtube.com/watch?v=ylxTQXLa-D4。
8位DAC与12位DAC
此示例旨在显示分辨率,速度和精度及其影响。 DAC1配置为12位DAC,而DAC2配置为8位DAC。我试图证明,对于相同的输出,12位分辨率将更精确,但比不那么精确的8位分辨率要慢。您会注意到,在相同的时间间隔和相同的输出下,8位输出会在12位输出之前饱和。如果您注意到快照,您会发现这两个输出都有不同的斜率,这就是精度和速度发挥作用的地方。
#include “DAC.h” #include “GPIO.h” void setup(); void GPIO_setup(); void DAC_setup(); void main() { signed int temp = 0; setup(); while(1) { for(temp = 0; temp 《 4096; temp++) { DAC_DHR12R1 = temp; if((temp 》 0) && (temp 《 256)) { DAC_DHR8R2 = temp; } delay_us(90); } for(temp = 4095; temp 》 -1; temp--) { DAC_DHR12R1 = temp; if((temp 》 0) && (temp 《 256)) { DAC_DHR8R2 = temp; } delay_us(90); } }; } void setup() { GPIO_setup(); DAC_setup(); } void GPIO_setup() { enable_GPIOA(true); pin_configure_low(GPIOA_CRL, 4, analog_input); pin_configure_low(GPIOA_CRL, 5, analog_input); } void DAC_setup() { enable_DAC(true); DAC_reset(); set_DAC1_buffer(enable); set_DAC2_buffer(enable); enable_DAC1_channel(true); enable_DAC2_channel(true); }
视频链接:https://www.youtube.com/watch?v=jBfzgFGs7JM。
双模DAC
双模DAC为多种可能性开辟了道路。 STM32F10x参考手册讨论了在双模式下操作DAC的各种方法。我的感觉使它变得更加简单。我们只需要分别处理两个DAC通道。如果两个DAC具有相同的触发源,则可以说它们同时或同步更新,否则它们是独立的。两个DAC可以输出相同或不同的波形,具体取决于它们的配置方式。在本示例中,我将一个DAC配置为输出叠加在可变DC分量上的三角波,而将另一个DAC配置为在由ADC调用时输出白噪声信号。 GPIO PA9 引脚上的外部中断。
#include “DAC.h” #include “GPIO.h” #include “AFIO.h” #include “Ex_Int.h” void setup(); void GPIO_setup(); void AFIO_setup(); void DAC_setup(); void ext_int() iv IVT_INT_EXTI9_5 ics ICS_AUTO { if(read_pending_reg(9) != 0) { pending_clr(9); bit_clr(GPIOA_ODR, 6); delay_ms(200); bit_set(GPIOA_ODR, 6); } } void main() { signed int value = 0; setup(); while(1) { if(get_input(GPIOF_IDR, 13) == 0) { delay_ms(10); value++; } if(get_input(GPIOF_IDR, 14) == 0) { delay_ms(10); value--; } if(value 》 2047) { value = 2047; } if(value 《 0) { value = 0; } set_DAC1_software_trigger(enable); DAC_DHR12RD = (((unsigned int)value) | 0x00FF0000); }; } void setup() { GPIO_setup(); AFIO_setup(); DAC_setup(); } void GPIO_setup() { enable_GPIOA(true); pin_configure_low(GPIOA_CRL, 4, analog_input); pin_configure_low(GPIOA_CRL, 5, analog_input); pin_configure_low(GPIOA_CRL, 6, (output_mode_low_speed | GPIO_PP_output)); enable_GPIOF(true); pin_configure_high(GPIOF_CRH, 13, digital_input); pin_configure_high(GPIOF_CRH, 14, digital_input); pull_up_enable(GPIOF_ODR, 13); pull_up_enable(GPIOF_ODR, 14); } void AFIO_setup() { AFIO_enable(true); pin_configure_high(GPIOA_CRH, 9, digital_input); pull_up_enable(GPIOA_ODR, 9); bit_set(GPIOA_ODR, 6); set_EXTI8_11(9, PA_pin); falling_edge_selector(9); interrupt_mask(9); NVIC_IntEnable(IVT_INT_EXTI9_5); EnableInterrupts(); } void DAC_setup() { enable_DAC(true); DAC_reset(); set_DAC1_buffer(enable); enable_DAC1_trigger(true); select_DAC1_trigger_source(Software_trigger); select_DAC1_wave_type(triangle_wave_generation_enabled); set_DAC1_triangle_waveform_amplitude(Triangle_Amplitude_equal_to_511); enable_DAC1_channel(true); set_DAC2_buffer(enable); enable_DAC2_trigger(true); select_DAC2_trigger_source(EXTI9_event); select_DAC2_wave_type(noise_wave_generation_enabled); set_DAC2_LFSR_mask(0x3AA); enable_DAC2_channel(true); DAC_DHR12RD = 0x00FF0000; }
视频链接:https://www.youtube.com/watch?v = 2rQQXBaYLMk。
MikroC DAC库示例
考虑到许多基于ARM的微型计算机都内置了DAC模块,因此Mikroelektronika还将易于使用的DAC库与MikroC编译器集成在一起。该库支持带DAC模块的STM32 micro中通常具有的大多数功能,这就是代码保护程序的原因。以下是配置和使用DAC所需的一些功能。
DACx_Init
DACx_Advanced_Chy_Output
DACx_Chy_Output
DAC1_Dual_Output
DACx_Deinit
函数在其编译器的手册和帮助文档中进行了详细说明。
在我的示例中,我还使用了MikroC的GPIO库–用于配置GPIO引脚的另一个有用的库。在最后一个示例中,我演示了DAC的原始用法。我使用了两个按钮来交替增加和减少连接到DAC输出的两个LED的亮度。
void setup(); void main() { signed int value = 2047; setup(); while(1) { if(GPIOF_IDRbits.IDR13 == 0) { value++; } if(GPIOF_IDRbits.IDR14 == 0) { value--; } if(value 》= 4095) { value = 4095; } if(value 《= 0) { value = 0; } DAC1_Ch1_Output(value); DAC1_Ch2_Output((4095 - value)); delay_ms(1); }; } void setup() { GPIO_Clk_Enable(&GPIOA_BASE); GPIO_Clk_Enable(&GPIOF_BASE); GPIO_Config(&GPIOA_BASE, (_GPIO_PINMASK_4 | _GPIO_PINMASK_5), _GPIO_CFG_MODE_ANALOG); GPIO_Config(&GPIOF_BASE, (_GPIO_PINMASK_13 | _GPIO_PINMASK_14), (_GPIO_CFG_MODE_INPUT | _GPIO_CFG_PULL_UP)); DAC1_Init(_DAC_DUAL_MODE_ENABLE); }