0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

两个月嵌入式编程DIY示波器和信号发生器

电子森林 来源:电子森林 作者:电子森林 2021-04-01 10:24 次阅读

今天随机分享4篇基于第一个平台的项目:

“寒假在家一起练”(1) - 两个月嵌入式编程DIY示波器信号发生器,玩起来就免购板费

本文为中国科技大学王赫男同学完成并分享的项目内容。。

1 项目需求

完成对板上音频信号的采集和波形显示,可以通过手机播放音乐或App产生音频信号的方式提供声音信号源,通过板上电路的放大、MCUADC的采集以后将波形显示在OLED屏幕上,可以通过板上按键的操作在两个方向(横轴 - 时间;纵轴 - 幅度)来扩展、压缩波形的显示,按键的功能可自行定义;

实现信号发生器的功能,能够产生2KHz以内的正弦波、三角波、方波三种常用波形,通过按键的操作能够实现频率可调、幅度可调,通过调整板上的R、C的值,可以最高生成200KHz的模拟信号;

能够通过Ain管脚测量外部模拟信号(0-3.3V,DC-200KHz),并能够对外部的周期性波形测量其周期和峰-峰值;

能够对采集到的信号进行FFT变换,并在屏幕上显示其基频及低次谐波(比如2、3、4、5次)的分量。

2 完成的功能及达到的性能

2.1 波形显示

显示波形时,按下L提高采样率,按下R降低采样率,采样率取值范围为1kHz、2.5kHz、5kHz、10kHz、25kHz、50kHz、100kHz、250kHz、500kHz、1MHz,通过改变采样率来实现横轴的缩放。

Y轴(幅度范围)默认为自动调整,即程序自动根据采样序列调整Y轴中心电压值和缩放范围,使波形完整显示在屏幕上。通过菜单可以改为手动模式,即手动调整Y轴中心电压值和Y轴缩放范围。

左下角显示波形参数,可以显示时间轴分度值、信号峰峰值、直流分量和频率。

正下方显示当前状态,包含输入通道、触发状态和前述的Y轴缩放方式(A:自动缩放,MO (Manual Offset):U/D按键调整Y轴中心电压值,MS (Manual Scale):U/D按键调整Y轴缩放范围。

按下OK键可以暂停波形刷新,再按可以继续刷新。

2.2 触发显示和触发菜单

程序默认为上升沿触发,触发电平为1.68V。显示波形且触发开启时,屏幕正下方显示当前触发边沿(上升沿、下降沿)和触发状态(箭头点亮为触发成功、背景点亮为触发失败)。

长按R键打开触发菜单,在触发菜单中可以开启/关闭触发,选择触发边沿,选择自动触发还是单次触发。

2.3 示波器菜单

长按OK键打开示波器菜单,示波器菜单共有4项,分别是:波形/频谱显示切换、Y轴缩放方式、波形参数切换、通道切换(麦克风与板上信号输入)。LRUD四个按键用来对上述四项功能进行切换。

2.4 频谱显示

通过菜单切换至频谱显示时,屏幕显示信号的频谱,显示频率范围为直流至采样频率的一半。同样按下L提高采样率,按下R降低采样率。左下角显示频率轴分度值。

2.5 信号输出

长按L键打开输出菜单,在输出菜单中,可以开启/关闭信号输出,增加/降低输出信号的频率(步长100Hz,上限2kHz)、峰峰值(步长0.1V,上限3.3V)和调整输出波形(正弦波、三角波、方波)。

3 实现思路

ADC对模拟输入进行采样,采样由定时器触发,采样结果由DMA搬运;

将采样得到的ADC量化值映射到屏幕坐标点上,实现波形显示;

按下按键调整采样频率,实现波形在时间轴上的扩展与压缩;

对采样序列进行FFT变换,绘制频谱;

信号参数的显示,如峰峰值、直流分量、信号频率等;

输出PWM波并通过RC低通滤波实现方波、正弦波、三角波的生成,通过按键改变PWM波的频率与占空比,从而改变输出信号的频率和幅度。

4 实现过程

4.1 程序流程图

ba846f0a-9245-11eb-8b86-12bb97331649.png

注:每个框图右下角名称为执行该功能的主要文件

4.2 ADC对数据进行采样

为了方便进行FFT计算,ADC共采集256个采样点。每次ADC转换由定时器1触发,触发频率最高为1MHz,即ADC采样率最高为1Msps。ADC的转换结果直接由DMA搬运至内存。

ADC转换开始函数(定义位置:sample.c,调用位置:main.c):

/** * @brief Start a new sample sequence. * @param[in] ADCValue Array to store incoming sample values. * @retval None */ void start_sample(uint16_t *ADCValue) { HAL_Delay(1); HAL_ADCEx_Calibration_Start(&hadc1); HAL_ADC_Start_DMA(&hadc1, (uint32_t *)ADCValue, SAMPLE_POINTS); }

256次转换结束后进入中断,置位结束标志位,进入后续的数据处理程序。

ADC转换结束中断回调函数(定义位置:adc.c):

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) { if(hadc == &hadc1) { finish_sample(); } }

4.3 采样结果的处理

得到256个采样的ADC量化值后,根据触发电平选择波形起始点,返回起始点在数组中的下标,显示从起始点开始的100个点。

波形触发部分代码(定义位置:wave.c,调用位置:app.c,其中total_points=256, GRAPH_WIDTH=101):

/** * @brief Wave trigger. * @param[in] ADCValue Array of sampled ADC values. * @param[in] total_points Total sampled points. * @retval Index of the trigger start point(》1)。 0 means trigger off or failed. */ uint16_t trigger(uint16_t *ADCValue, uint16_t total_points) { uint16_t i; uint16_t trigger_value = VOL2ADC(1.68); if (!is_trigger_on()) return 0; for (i = 1; i 《 total_points - GRAPH_WIDTH + 2; i++) { if (get_trigger_edge()) // falling edge { if (ADCValue[i-1] 》 trigger_value && ADCValue[i] 《= trigger_value) { trigger_success(); if (is_trigger_single()) pause(); return i; } } else { if (ADCValue[i-1] 《= trigger_value && ADCValue[i] 》 trigger_value) { trigger_success(); if (is_trigger_single()) pause(); return i; } } } trigger_fail(); return 0; }

取起始点后100个采样值使其显示在OLED屏幕上(一次性刷新)。为此需要将ADC量化值与OLED屏幕上的坐标进行线性映射。在自动模式(自动缩放y轴)中,程序自动找出量化值中的最大最小值,并使最大最小值也能不超出绘制范围以外,这样屏幕就可以显示完整的波形。

自动缩放y轴代码(定义位置:wave.c,调用位置:app.c):

/** * @brief Automatically find the central/max/min voltage on y-axis. * @param[in] ADCValue Array of sampled ADC values. * @note The function calculates the min/max voltage of the sampled signal, * then find a proper scale voltage and a central voltage on y-axis. * @retval None */ void auto_scale(uint16_t *ADCValue) { uint16_t a_max_value, a_min_value, a_pp_value; float exact_voltage, floor_voltage, ceil_voltage; get_max_min_pp_value(ADCValue, &a_max_value, &a_min_value, &a_pp_value); voltage_range_auto_select(ADC2VOL(a_pp_value/2)); exact_voltage = ADC2VOL(a_max_value + a_min_value) / 2; floor_voltage = (uint8_t)(ADC2VOL((a_max_value + a_min_value)*5)) / 10.0; //keep one decimal ceil_voltage = floor_voltage + 0.1; // round center_voltage volt_on_y_axis.center_voltage = ceil_voltage - exact_voltage 《 exact_voltage - floor_voltage ? ceil_voltage : floor_voltage; volt_on_y_axis.max_voltage = volt_on_y_axis.center_voltage + v_scale_list[v_scale_index]; volt_on_y_axis.min_voltage = volt_on_y_axis.center_voltage - v_scale_list[v_scale_index]; }

坐标映射代码(定义位置:wave.c,调用位置:app.c):

/** * @brief Generate y-coordinates of the wave. * @param[in] ADCValue Array of sampled ADC values. * @param[out] y Y-coordinate array of the wave. * @note The function map ADCValues to OLED y coordinates. * @retval None */ void generate_wave(uint16_t *ADCValue, uint8_t *y) { // Quantize y-axis min/max/central voltages to ADC values. int16_t a_max_value = VOL2ADC(volt_on_y_axis.max_voltage); int16_t a_min_value = VOL2ADC(volt_on_y_axis.min_voltage); uint8_t i; // Linearly map every ADC value to its coordinate. for (i = 0; i 《 GRAPH_WIDTH - 1; i++) { if (ADCValue[i] 《= a_max_value && ADCValue[i] 》= a_min_value) y[i] = (GRAPH_HEIGHT - 1) * (a_max_value - ADCValue[i]) / (a_max_value - a_min_value) + GRAPH_START_Y; else if (ADCValue[i] 》 a_max_value) y[i] = GRAPH_START_Y; else if (ADCValue[i] 《 a_min_value) y[i] = GRAPH_HEIGHT + GRAPH_START_Y - 1; } }

波形显示代码(定义位置:display.c,调用位置:app.c):

/** * @brief Display wave on OLED. * @param[in] y Y-coordinate array of the wave. * @retval None */ void display_wave(const uint8_t *y) { uint8_t x; for (x = GRAPH_START_X; x 《 GRAPH_WIDTH - 1; x++) OLED_DrawLine(x, y[x-GRAPH_START_X], x + 1, y[x-GRAPH_START_X+1], 1); OLED_DrawPoint(x, y[x-GRAPH_START_X], 1); }

在手动模式中,可以手动调节y轴的缩放范围和y轴中心电压值,但此时波形不一定会完整显示。得到采样点坐标后,使用OLED的绘制直线函数,连接屏幕上各个离散的点,就可以得到信号的波形。

当需要显示频谱时,就需要对所有的ADC的量化值进行256点FFT变换,由于FFT变换结果关于中心点对称,且屏幕x方向分辨率为128点,所以保留FFT需要为0~127的结果,进行线性映射后显示在屏幕上。

FFT的代码定义在fftutil.c中,对变换结果的处理及显示分别定义在spectrum.c和display.c中。

4.4 信号发生器

板上有一个1Kohm的电阻和10nF的电容构成的低通滤波器,截止频率为1.6KHz。若在该输出端输出频率足够的PWM信号,则输出电压大小就和PWM的占空比成正比。通过改变PWM的占空比就可以调节输出电压波形。通过实验可知,当信号的每一个周期由500个PWM脉冲组成时,信号的纹波较小。

以正弦信号为例,在程序外,在电脑中生成一个正弦信号,并在一个周期中进行500次采样,根据电压和PWM占空比的正比关系可以计算出500个PWM脉冲的占空比。将其定义为长度为500的数组写入程序。程序中使能PWM的DMA通道,这样就可以在每个PWM脉冲结束后自动将数组中的元素载入定时器输出比较寄存器,从而改变占空比。低通滤波器再将STM32产生的PWM脉冲转变为模拟信号,即可重新生成正弦波。方波和三角波同理。

开启PWM和DMA代码(定义位置:source.c,调用位置:app.c,其中SIGNAL_LENGTH=500):

/** * @brief Start signal output at Aux. * @retval None */ void start_output(void) { HAL_TIM_PWM_Start_DMA(&htim2, TIM_CHANNEL_2, (uint32_t *)output_wave_value, SIGNAL_LENGTH); }

信号的幅度调节可以直接对上述数组每个元素乘一个常数来实现;频率调节首先要调节定时器的自动重载值(ARR),改变PWM的频率。为保证幅度不变,数组中每个元素也要同比例缩放。

5 遇到的主要难题

5.1 中断与DMA

项目共有两处使用DMA,分别用于储存ADC采样结果和调整输出PWM定时器的自动重载值(ARR)。如果用中断处理数据而非DMA,则会产生以下问题:

若在ADC转换完成中断中读取转换结果,则在一次采样序列(256点)中,中断频率过于频繁,且由于中断耗时,无法得到很高的采样率,最高只能达到几十kHz。若使用DMA,则只需在整个采样序列结束后进入中断,不会对采样造成影响。

若使用PWM中断更新自动重载值,中断耗时会使PWM频率产生偏差,且会对OLED屏幕的SPI时序造成影响,导致屏幕无法正常显示。若使用DMA更新自动重载值,则不需要PWM中断,更新耗时相比于中断有很大改善。

综上所述,在频率较高或需要频繁更新数据的情况,中断会带来各种各样的问题,而DMA则可以高效完成任务。

5.2 RAM和Flash大小(FFT优化)

项目使用的FFT算法根据Adafruit ZeroFFT修改而来。该算法最高可支持4096点FFT,其旋转因子表、窗函数表和信号序列数组占用空间极大。而本项目使用的STM32G031G8只有64K的Flash和8K的RAM,资源极为有限,无法直接运行ZeroFFT。

为此需要对ZeroFFT的代码进行优化。该项目只需256点FFT,删去256点之外的部分,缩短查找表,能极大减小RAM和Flash占用。

具体的优化步骤:

将Adafruit_ZeroFFT.h中的宏定义ZERO_FFT_MAX改为512。(对应256点FFT)

删去fftutil.c中ZeroFFT函数所有其他点数的FFT代码,只保留256点FFT的代码。同样删去窗函数中256点以外的部分和窗函数查找表。

此时fftutil.c中只调用了arm_common_tables.c中armBitRevTable和twiddleCoefQ15两个查找表,删去其他所有数组。

在fftutil.c中所有调用armBitRevTable和twiddleCoefQ15查找表的代码下面添加printf,用PC运行FFT程序,打印调用的下标。

以twiddleCoefQ15数组为例,原长度为6144;对于256点FFT,只有其中384个值被调用。PC中编写一个临时程序,根据调用的下标,用printf打印一个新的长度为384的查找表替换掉原来的。另一个查找表同理。

fftutil.c中部分变量代表查找表的步进值,查找表改变后这些步进值也要改变。

此时FFT的代码应该就可以在STM32G0上运行了~

此外,由于Flash和RAM的资源有限,在FFT之外的其他很多地方也需要对空间进行优化,比如删去oled不需要的字库等。

5.3 PWM输出频率

由于电容的充放电,由PWM经过低通滤波输出的信号会有锯齿,信号幅度较低时锯齿更为明显,并会造成波形显示的不稳定。开始时输出信号一个周期内有50个PWM脉冲,即PWM的频率是信号频率的50倍,当信号幅度较低时锯齿极为明显,对输出波形造成极大干扰。将一个周期内PWM脉冲数提升至500,锯齿密度变大,同时幅度减小,对输出信号的干扰也减小。但同时储存输出信号幅度信息的查找表也变大10倍,消耗了更多的空间。

6 未来的计划建议

该项目已经成功实现了简易示波器和信号发生器的功能,并达到了预期指标。然而通过更换硬件,还有许多可以提升与扩展的地方:

板上的OLED屏幕分辨率较低,无法显示信号细节与更多信息。可以使用分辨率更高的屏幕,或将波形信息直接发送给上位机,由上位机进行显示。

主控芯片STM32G031的资源有限。可以更换更好的主控芯片,来提高采样率,采样点数等从而实现更高的性能。

可以对输入信号进行衰减,从而增大输入信号的电压范围。

增加模拟输入的通道,并添加波形的数学运算功能,如波形之间的加减。

改变输出端的RC值,扩展输出信号频率范围。

不更换硬件可以提升与扩展的地方(懒得做的部分):

自动/手动调整触发电平。

改变输入信号耦合方式(直流/交流耦合)。

对输入信号进行数字滤波。

信号源实现更高的频率分辨率。

原文标题:如何在STM32G031上实现示波器和频谱分析功能?

文章出处:【微信公众号:FPGA入门到精通】欢迎添加关注!文章转载请注明出处。

责任编辑:haq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 示波器
    +关注

    关注

    111

    文章

    5650

    浏览量

    181775
  • STM32
    +关注

    关注

    2239

    文章

    10671

    浏览量

    348755
  • 信号发生器
    +关注

    关注

    28

    文章

    1276

    浏览量

    107771

原文标题:如何在STM32G031上实现示波器和频谱分析功能?

文章出处:【微信号:xiaojiaoyafpga,微信公众号:电子森林】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    proteus信号发生器怎么设置方波

    ,用于模拟和调试电子电路。它具有丰富的虚拟仪器库,包括示波器信号发生器、函数发生器等。Proteus信号
    的头像 发表于 02-23 16:44 2494次阅读

    信号发生器是干什么用的 信号发生器示波器怎么连接

    信号发生器是一种用于产生各种电信号的设备,它在电子设备测试、电路调试、教学实验等领域得到广泛应用。信号发生器可以产生不同频率、幅度、波形和相
    的头像 发表于 01-30 10:08 1154次阅读

    信号发生器的特点 信号发生器可以产生哪些波形?

    信号发生器的特点 信号发生器可以产生哪些波形? 信号发生器是一种用于产生各种频率、振幅和波形的电
    的头像 发表于 12-21 15:05 730次阅读

    信号发生器如何发出双脉冲?

    信号发生器如何发出双脉冲? 信号发生器是一种用于产生各种信号波形的仪器。双脉冲信号是一种特殊的
    的头像 发表于 12-21 15:03 643次阅读

    信号发生器的占空比是什么 信号发生器占空比怎么设置

    信号发生器的占空比是什么 信号发生器占空比怎么设置  信号发生器的占空比是指方波波形中高电平和低
    的头像 发表于 12-21 14:02 1848次阅读

    嵌入式硬件和软件哪个好?

    设计工具、设计原理图、PCB板的能力;熟练运用单片机、DSP、PLD、FPGA等进行软硬件开发调试的能力;熟练运用仿真工具、示波器信号发生器、逻辑分析仪等调测硬件的能力;掌握常用的标准电路的设计能力
    发表于 12-05 15:17

    泰克MSO58B示波器如何检测信号发生器输出信号

    在电子设备调试和测试过程中,准确地检测信号是非常关键的。信号发生器常用于生成不同频率和幅度的信号,以供各种电子设备进行测试。在此过程中,使用泰克MSO58B
    的头像 发表于 12-01 17:49 374次阅读
    泰克MSO58B<b class='flag-5'>示波器</b>如何检测<b class='flag-5'>信号</b><b class='flag-5'>发生器</b>输出<b class='flag-5'>信号</b>?

    信号发生器的基本使用方法

    电路信号发生器
    油泼辣子
    发布于 :2023年11月30日 15:26:53

    函数信号发生器怎么使用?函数信号发生器实现方法通常有哪几种?

    发生器的使用方法和实现方法。 一、函数信号发生器的使用方法 1. 连接电源和外部设备:将函数信号发生器连接到电源,确保电源电压符合要求。根据
    的头像 发表于 11-20 16:16 2916次阅读

    嵌入式软件好还是硬件好?

    、PCB板的能力;熟练运用单片机、DSP、PLD、FPGA等进行软硬件开发调试的能力;熟练运用仿真工具、示波器信号发生器、逻辑分析仪等调测硬件的能力;掌握常用的标准电路的设计能力,如复位电路、常用滤波
    发表于 09-18 15:31

    为何示波器示数是信号发生器输出的2倍呢?

    如题,我用的是泰克TDS2022型数字存储示波器及AFG3101型任意波形/函数发生器信号发生器输出峰峰值是1V,为什么示波器示数是2V呢
    发表于 05-15 11:01