寒假在家一起练1 - 简易示波器
基于STM32G031的mini示波器,两路信号采集其中一路为音频信号。可实现信号波形显示以及频谱分析。
标签
嵌入式系统
显示
DDS
武维扬
更新2021-03-06
1081
//按键状态机:
if(keyState != 0xff)
        {
            switch(keyState)
            {
                case 1:
                    g_scopeCtrl.voltScaleIdx++;
                    if(g_scopeCtrl.voltScaleIdx == MAX_VOLT_SCALE)
                    {
                        g_scopeCtrl.voltScaleIdx = 0;
                    }
                    
                    if(g_scopeCtrl.voltScaleIdx == (MAX_VOLT_SCALE - 1))
                    {
                        //1.0V档显示范围为0.0V~4.0V,不需要自动量程
                        autoVoltMeasureRange = 0;
                    }
                    else
                    {
                        autoVoltMeasureRange = 1;
                    }

                   // printf("volt scale: %f V.\r\n", g_scopeCtrl.voltScale[g_scopeCtrl.voltScaleIdx]);
                    break;   
                    
                case 3:
                    if(g_scopeCtrl.runStop == SCOPE_DISPLAY_STOP)
                    {
                        break;
                    }
                    
                    if(g_scopeCtrl.timeScaleIdx == (MAX_TIME_SCALE - 1))
                        g_scopeCtrl.timeScaleIdx = 0;
                    else
                        g_scopeCtrl.timeScaleIdx++;
                    sampRateChangeFlag = 1;
                   // printf("time scale: %d us.\r\n", g_scopeCtrl.timeScale[g_scopeCtrl.timeScaleIdx]);
                    break;

                case 4:
                    if(g_scopeCtrl.runStop == SCOPE_DISPLAY_STOP)
                    {
                        break;
                    }
                    g_scopeCtrl.channelSelect = (g_scopeCtrl.channelSelect ? 0 : 1);
                    channelChangeFlag = 1;
                   // printf("channel select: %d.\r\n", g_scopeCtrl.channelSelect);
                    break;
                    
                case 2:
                    if(g_scopeCtrl.runState == SCOPE_STATE_M0_MAIN)
                        g_scopeCtrl.runState = SCOPE_STATE_M1_SPECTRUM;
                    else
                        g_scopeCtrl.runState = SCOPE_STATE_M0_MAIN;
                    break;
                    
                case 0:
                    g_scopeCtrl.runStop = (g_scopeCtrl.runStop == SCOPE_DISPLAY_RUN) ? SCOPE_DISPLAY_STOP : SCOPE_DISPLAY_RUN;
                    break;
                    
                default:
                    break;
            }
            keyState = 0xff;
        } 

//数据处理以及波形显示
if(DMA_Conver_Flag)
        {
           // printf("\n\r into DMA_Conver_Flag\n\r");  
            if(g_scopeCtrl.runState == SCOPE_STATE_M0_MAIN)
            {
                uint8_t yPos, yPosLast;
                int16_t autoVMRoffset = 0;
                int16_t autoVMRoffsetIndex = 0;
                static int16_t lastIndex = 0;
                
                OLED_ShowGrid(0);

                vMax = 0x00000000;
                vMin = 0x7FFFFFFF;
                for(i = 0; i < 128; i++)
                {
                    ADC_TransBuf[i] = ((ADC_ConvertedValue[i] & 0x0000ffff));

    // printf("\n\r ADC_TransBuf[i] %d\n\r",  ADC_TransBuf[i]);
                    if(ADC_TransBuf[i] > vMax)
                    {
                        vMax = ADC_TransBuf[i];
                    }

                    if(ADC_TransBuf[i] < vMin)
                    {
                        vMin = ADC_TransBuf[i];
                    }
                    
              // printf("\n\r vmin %d vmax %d \n\r", vMin, vMax);      
                }

                //自动量程
                if(autoVoltMeasureRange)
                {
                    autoVMRoffsetIndex = vMin / g_scopeCtrl.voltRawValuePerGrad[g_scopeCtrl.voltScaleIdx];

                    //消除量程抖动
                    if((autoVMRoffsetIndex < lastIndex)&&(lastIndex - autoVMRoffsetIndex == 1))
                    {
                        autoVMRoffsetIndex = lastIndex;
                    }
                    
                    autoVMRoffset = autoVMRoffsetIndex *  g_scopeCtrl.voltRawValuePerGrad[g_scopeCtrl.voltScaleIdx];
                }
                else
                {
                    autoVMRoffsetIndex = 0;
                    autoVMRoffset = 0;
                }

                SCOPE_UIShowVoltGrad(autoVMRoffsetIndex);

                //if((trigStartPos > 6)&&(trigStartPos < 16))
                {
                    trigStartPos = 8;
                    trigEndPos = trigStartPos + 108;
                    
                    for(i = 0, ii = trigStartPos; ii < trigEndPos; i++, ii++)
                    {
                        
                        tempVal = (ADC_TransBuf[ii] - autoVMRoffset) / g_scopeCtrl.voltRawValuePerPixel[g_scopeCtrl.voltScaleIdx];

                        if(tempVal > 48)
                        {
                            tempVal = 48;
                        }

                        //画波形
                        {
                            yPos = 48 - tempVal;

                            //tempVal为48时(即yPos为0)不显示,表示实际值超出显示范围
                            if(yPos > 0)
                            {
                                ssd1306_DrawPixel(i, yPos, White);
                            }

                            //y轴插值,使波形看起来是连续的
                            if(i == 0)
                            {
                                yPosLast = yPos;
                            }
                            else
                            {
                                if(yPos > (yPosLast + 1))
                                {
                                    for(j = yPosLast + 1; j < yPos; j++)
                                    {
                                        ssd1306_DrawPixel(i, j, White);
                                    }
                                }
                                else if(yPosLast > (yPos + 1))
                                {
                                    for(j = yPosLast - 1; j > yPos; j--)
                                    {
                                        ssd1306_DrawPixel(i, j, White);
                                    }
                                }
                                yPosLast = yPos;
                            }
                        }
                        
                    }

                    ssd1306_UpdateScreen();
                }
            }

1 项目需求

  • 完成对板上音频信号的采集和波形显示,可以通过手机播放音乐或App产生音频信号的方式提供声音信号源,通过板上电路的放大、MCU中ADC的采集以后将波形显示在OLED屏幕上,可以通过板上按键的操作在两个方向(横轴 - 时间;纵轴 - 幅度)来扩展、压缩波形的显示,按键的功能可自行定义;
  • 实现信号发生器的功能,能够产生2KHz以内的正弦波、三角波、方波三种常用波形,通过按键的操作能够实现频率可调、幅度可调,通过调整板上的R、C的值,可以最高生成200KHz的模拟信号;
  • 能够通过Ain管脚测量外部模拟信号(0-3.3V,DC-200KHz),并能够对外部的周期性波形测量其周期和峰-峰值;
  • 能够对采集到的信号进行FFT变换,并在屏幕上显示其基频及低次谐波(比如2、3、4、5次)的分量。

      项目主要参考工程师JACK在LPC824上的实现,不得不说Jack的代码写得条理清晰、干净整洁。我从中学到了很多,在这里再次感谢Jack!

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

  1. 按键控制部分

      L键控制电压幅度来控制波形纵向伸缩一共有"0.1v", "0.4v", "0.8v", "1.0v"四个量程

      R键控制时间分度值共有"10us", "20us", "50us", "100u", "200u", "500u", "1.0m", "2.0m", "2.5m"九个挡位。

      U键控制信号采集通道的切换Ain/音频

      D键控制频域波形和时域波形显示的切换

      OK键控制波行显示的stop/run状态切换

  1. 波形显示

      时域波形:下方显示波形参数,可以显示电压量程、时间轴分度值、通道选择、信号状态,右侧显示电压幅度具体数值。幅度范围可自动调整。使波形完整显示在屏幕上。正下方显示当前状态,按下OK键可以暂停波形刷新,再按可以继续刷新。

      频域波形:左侧显示波形频谱,右侧显示采样率以及采样点数等信息

  1. 未实现功能

      由于时间原因未能实现信号发生器功能。不过我已经有了大致思路,方波只需要在cubemx中配置一下即可。正弦和三角波可以使用DAC来实现。但是看方波信号的识别效果还不错,推测正弦信号应该没问题。等开学后到学校使用信号发生器再试试啊。

  1. 实现过程:

      1 首先配置屏幕,主要是由于底板的设计无法使用硬件spi需要自己手动模拟。之后直接调用屏幕库函数即可。

      2 设计按键功能以及状态机,以此来通过按键实现状态跳转。

      3 用cubemx配置ADC+DMA双通道信号采集。将采集到的数字信号与电压值想关联之后在与频幕像素相关联将采样点打印在屏幕上。在世用插值函数将点连起来以实信号连续而非离散的点。

      4 引入FFT库使用fft库函数计算快速傅里叶变换从而对采集到的信号进行傅丽耶变换。在按上面提到的方法将信号转换后打印在屏幕上。

  1. 遇到的主要难题

         1 在我第一次用cubemx生成ADC+DMA代码后发现代码会在初始化阶段卡死。后来经过长时间的排查发现是因为代码不断的进入dma半中断并且卡在里面。我直接改了库禁止half中断。。。不过后面这个问题又莫名其妙的消失了,可能是cubemx的问题吧。

         2 可能是由于音频信号噪声滤除的不好,频域分析的结果不理想混叠严重。

         3 未能实现信号发生器功能。

  1. 未来的计划

        1.开学后到实验室试一下正弦等其他信号的识别情况,测试下最大识别频率。

         2.计划更换到STMF4系列单片机重新做一遍,看一下效果能有多大提升。有机会的话想自己设计下pcb重新做一下,再把信号发生器功能加上。

 

 

附件下载
MiniOSC.hex
团队介绍
西安电子科技大学/微电子学院
团队成员
王方
电子爱好者
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号