打印
[应用相关]

STM32数字示波器+详细注释+上位机程序+硬件

[复制链接]
4175|20
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
从朋友得知ST公司在搞活动,可以申请STM32F429的探索套件,作为穷学生一枚的我不免动了心。大二上学期参加完全国大学生电子设计竞赛之后闲来无事,就自己做了一个数字示波器来作为学习STM32的实战检验,前后花费了大概1个月的时间完成了初稿,之后又断断续续的完善我的示波器,并且为它编写了上位机程序和制定了通信协议。使用了前端程控模拟电路和上层电路板,uCOS-II,uCGUI,FFT,SD,上位机等等,现在因为参加老师的项目研发也就暂时闲置了下来,之前就打算在各大论坛开源,但是一直没有时间整理设计文档,恰巧现在ST公司这个活动,所以就全部开源分享给大家吧,一起学习。下一步计划重新设计模拟电路部分使用CPLD和高速AD实现更高的采样率。
         由于程序量比较大,所以在程序中我完成了大量的注释,详细的说明了具体的实现方法,其中的示波器原理和FFT原理请自己查阅相关教材。我个人认为最值得参考的就是如何制定一个效率较高的通信协议。我查阅了很多资料最后结合实际设计了一套通信协议,这套通信协议效传输速率理论最大可达16000bps,实际采用10666bps速率传输。协议的编码、解码算法的详细说明附在附件中。随着程序量的增加,F103也渐渐感到比较吃力了,其中FFT的运算就是典型代表,也希望借此机会申请到F429带DSP指令的MCU来进一步提高示波器的性能。
         限于目前的知识水平,这套示波器是实践大于理论的产物,其中很多设计存在先天的不足,以后会继续改进,向更高性能迈进。

使用特权

评论回复
沙发
两只袜子|  楼主 | 2022-5-7 09:24 | 只看该作者
设计指标:
主控:                                        STM32F103ZET6
液晶屏:                                      4.3TFT480×272  65K彩色LCD显示屏  FSMC
AD:                                         121MHz采样率
最高实时取样率:                   1MSa/s  12Bits
取样缓冲器深度:                   5K  
垂直灵敏度:                            5V1V500mV,200mV,100mV,50mV,20mV,10mV;  
水平时基范围:2S,1S,500mS,200mS,100mS,50mS,20mS,10mS,5mS,2mS,1mS,500uS,200uS,100uS,50uS,20uS,10uS,5uS,2uS,1uS
输入阻抗:                                 1MΩ
最高输入电压:                       30Vpp
耦合方式:                                 AC/DC
触发功能:                                 实现自动、常规、单次触发方式 ,上升或下降边沿触发  
参数计算:                                 频率、周期、占空比、交流峰-峰值、平均值、光标追踪显示
RUN/STOP


使用特权

评论回复
板凳
两只袜子|  楼主 | 2022-5-7 09:26 | 只看该作者
功能:
1、波形发生器:使用STM32一路DA实现正弦,三角波,方波,白噪声输出。 任意一种波形幅值在0-3.3V任意可调、频率在一定范围任意可调、方波占空比可调。调节选项可以通过触摸屏完成设置。
2SD卡存储: SD卡波形存储输出,能够对当前屏幕截屏,以JPG格式存储在SD卡上。能够存储1S内的波形数据,可以随时调用查看。
3、数据传输:  C#编写上位机,通过串口完成对下位机的控制。(1)实现STOP/RUN功能(2)输出波形电压、时间参数(3)控制截屏(4)控制波形发生器(5)控制完成FFT6)波形的存储和显示
4、图形接口:  UCGUI
2、水平扫速:  250 ns*500ns1μs5 μs10μs50μs500 μs5ms 50ms
3、垂直电压灵敏度:10mV/div, 20mV/div, 50mV/div, 0.1V/div, 0,2V/div, 0.5V/div, 1V/div,
2V/div, 5V/div
4、被测信号的各种参数屏幕显示,包括频率、电压峰峰值等。



这是上位机的程序


测试时候的照片


测试正弦波

使用特权

评论回复
地板
两只袜子|  楼主 | 2022-5-7 09:31 | 只看该作者



PCB


模拟电路板




侧面的照片


源代码的注释




使用特权

评论回复
5
两只袜子|  楼主 | 2022-5-7 09:34 | 只看该作者


/*---------------------------------------------------------
                  ADC配置驱动程序
        说明: 示波器AD采样配置,经过初步测试,AD采样速率为1.286M
              正弦波最大不失真测量频率300kHz
                  三角波最大不失真测量频率150kHz
                  矩形波最大不失真测量频率100kHz
        时间: 2013年11月19日
---------------------------------------------------------*/
#include "stm32f10x.h"
#include "adc.h"
#include "oscilloscope.h"
#include "ucos_ii.h"
#include "tft_api.h"
/*-----------------------------------------
                            声明变量
------------------------------------------*/
extern WaveType WaveInfo;
extern WaveMode WaveCtrl;
volatile u16 ADCConvertedValue[SAMPDEPTH];//AD转换缓冲区,占用RAM 0.8KB

/*-----------------------------------------
                            ADC1端口初始化
------------------------------------------*/
void ADC1_GPIO_Init(void)
{
        GPIO_InitTypeDef IO_Init;
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOC,ENABLE);
        //C1模拟输入
        IO_Init.GPIO_Pin = GPIO_Pin_1;
        IO_Init.GPIO_Mode = GPIO_Mode_AIN;
        GPIO_Init(GPIOC,&IO_Init);
}

/* #define ADC1_DR_Address ((unsigned int)0x40012400+0x4c) */
/* unsigned short int ADC1_DMA_Value; */
/*--------------------------------------------------
函数说明:配置ADC1完成AD采集,是本示波器最核心的部分.
配置方案:ADC1由TIM3提供的触发事件进行触发AD转换,控
                   制TIM3的速率就可以完成采样率的改变.同时采
                  用DMA完成数据传输,注入通道1完成内部温度传
                  感器AD采集.
--------------------------------------------------*/
void ADC1_Mode_Config(void)
{
        //配置DMA
        DMA_InitTypeDef DMA_csh;
        ADC_InitTypeDef ADC_csh;
        TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
        NVIC_InitTypeDef NVIC_InitStructure;

        DMA_DeInit(DMA1_Channel1);                                             //DMA复位,通道1
        DMA_csh.DMA_PeripheralBaseAddr = ADC1_DR_Address;  //ADC1地址
        DMA_csh.DMA_MemoryBaseAddr = (unsigned int)ADCConvertedValue;  //内存地址
        DMA_csh.DMA_DIR = DMA_DIR_PeripheralSRC;  
        DMA_csh.DMA_BufferSize = SAMPDEPTH;                                            //缓冲大小为采样深度
        DMA_csh.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址固定
        DMA_csh.DMA_MemoryInc = DMA_MemoryInc_Enable;                         //内存地址自增
        DMA_csh.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;  
        DMA_csh.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
        DMA_csh.DMA_Mode = DMA_Mode_Circular;                                          //循环传输
        DMA_csh.DMA_Priority = DMA_Priority_High;                                  //DMA优先级高
        DMA_csh.DMA_M2M = DMA_M2M_Disable;
        DMA_Init(DMA1_Channel1,&DMA_csh);                                                  //写入DMA1配置参数
        DMA_Cmd(DMA1_Channel1,ENABLE);                                                           //使能DMA1通道1
        DMA_ITConfig(DMA1_Channel1,DMA_IT_TC,ENABLE);                        //使能DMA CH1中断

        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);

        //配置TIM3工作在18MHz,为AD提供触发
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
        TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
        TIM_TimeBaseStructure.TIM_Period = 1;         
        TIM_TimeBaseStructure.TIM_Prescaler = 99;         //工作在18M,每格最大值时,不至于溢出      
        TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;   
        TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
        TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);
        TIM_SelectOutputTrigger(TIM3,TIM_TRGOSource_Update); //使用TIM3事件更新作为ADC触发

        //配置ADC
        ADC_csh.ADC_Mode = ADC_Mode_Independent;                  //独立ADC模式
    ADC_csh.ADC_ScanConvMode = DISABLE;                           //关闭扫描模式
        ADC_csh.ADC_ContinuousConvMode = DISABLE;                  //连续AD转换开启
        ADC_csh.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO;  //由TIM3提供的触发事件进行触发AD转换
        ADC_csh.ADC_DataAlign = ADC_DataAlign_Right;         //数据右对齐
        ADC_csh.ADC_NbrOfChannel = 1;                                          //要转换的通道数目1
        ADC_Init(ADC1,&ADC_csh);                                                   //写入ADC1配置参数
        ADC_RegularChannelConfig(ADC1,ADC_Channel_11,1,ADC_SampleTime_1Cycles5);//采样速率1M
        ADC_DMACmd(ADC1,ENABLE);                                                 //使能ADC1 DMA
        ADC_ExternalTrigConvCmd(ADC1,ENABLE);                        //打开ADC1外部触发

        ADC_InjectedChannelConfig(ADC1,ADC_Channel_16,1,ADC_SampleTime_239Cycles5);         //配置ADC1通道16为注入通道1
        ADC_ExternalTrigInjectedConvConfig(ADC1,ADC_ExternalTrigInjecConv_None);         //软件触发注入通道的转换
        ADC_TempSensorVrefintCmd(ENABLE);                                                                                         //使能温度传感器

        ADC_Cmd(ADC1,ENABLE);                                                         //使能ADC1
        ADC_ResetCalibration(ADC1);                                                  //复位校准寄存器
        while(ADC_GetResetCalibrationStatus(ADC1));     //等待校准寄存器复位完成
        ADC_StartCalibration(ADC1);                                                  //开始校准
        while(ADC_GetCalibrationStatus(ADC1));                         //等待校准完成
        TIM_Cmd(TIM3,ENABLE);
}
/*-----------------------------------------
函数说明:通过注入通道1转换内部温度传感器
                   AD值,读取10次有效数据取平均返回
------------------------------------------*/
u16 GetTempSensor(void)   
{
        u16 temp=0,i,k=0;
        for(i=0; i<10; )
        {
                ADC_SoftwareStartInjectedConvCmd(ADC1,ENABLE);        //软件触发注入通道的转换
                k = (0x6EE - ADC_GetInjectedConversionValue(ADC1,ADC_InjectedChannel_1)) / 0x05 + 25;
                if(k>0 && k<100)
                {
                        temp += k;
                        i++;
                }
        }
        temp /= 10;
        return temp;            
}
/*-----------------------------------------
函数说明:ADC1初始化
------------------------------------------*/
void ADC1_Init(void)
{
        ADC1_GPIO_Init();
        ADC1_Mode_Config();
}
/*-----------------------------------------
函数说明:ADC传输DMA通道1中断服务程序
                   DMA把AD值传输到缓冲区完成后关闭定
                  时器3(作为触发AD转换的定时器)同时
                  置更新完成标志位为1,开定时器3在应
                  用中开启.
------------------------------------------*/
void DMA1_Channel1_IRQHandler()
{
      OSIntNesting++;  
      DMA_ClearFlag(DMA1_FLAG_TC1);        //清除DMA传输完成中断
      TIM_Cmd(TIM3,DISABLE);                //关闭TIM3
      WaveCtrl.UpdatTrue = 1;                //已经完成一次波形FIFO,可以批量读出数据
      OSIntExit();
}
/*-----------------------------------------
函数说明:擦除AD缓冲区
------------------------------------------*/
void Earse_AD_FIFO(void)
{
        uint32_t i;
         for(i=0; i<sampdepth; i++)
        {
                ADCConvertedValue[i] = 0;
        }        
}
复制代码



/*------------------------------------------------------------------------------------
                                                                   STM32示波器
硬件平台:
                         主控器: STM32F103ZET6 64K RAM 512K ROM
                        屏幕器: SSD1963
                        分辨率: 480x272 16位色
                   触摸屏: TSC2046
                        模拟电路: OP  - TL084
                                          OP  - u741
                                          SW  - CD4051
                                          CMP - LM311
                                          PWR - LM7805
                                              - LM7905
                                                  - MC34063
                                                  - AMS1117-3.3
                                          DRI - ULN2003
                        继电器:信号继电器
                    电源:   DC +12V

软件平台:
                         开发环境: RealView MDK-ARM uVision4.10
                        C编译器 : ARMCC
                        ASM编译器:ARMASM
                        连接器:   ARMLINK
                        实时内核: uC/OS-II 2.90实时操作系统
                        GUI内核 : uC/GUI   3.90图形用户接口
                        底层驱动: 各个外设驱动程序

        ROM Size = Code + RO-data +RW-data
        RAM Size = RW-data + ZI-data
        Program Size: Code=56024 RO-data=8272 RW-data=256 ZI-data=29912         

时间: 2013年11月9日       BoX编写于大二上学期

版本: V1.0 - 2013/11/9
       V1.1 - 2014/2/8
           V3.0 - 2014/2/19                                
-------------------------------------------------------------------------------------*/   
#include "stm32f10x.h"
#include "ucos_ii.h"
#include "app.h"
#include "GUI.h"

#include "usart1.h"
#include "pincfg.h"
#include "delay.h"
#include "tft_api.h"
#include "dac.h"
#include "adc.h"
#include "iwdg.h"
#include "timer.h"
#include "EXTI.H"
#include "oscilloscope.h"
#include "indkey.h"
#include "tsc2046.h"

#include "task_rtc.h"
#include "task_gui.h"
#include "task_sd.h"


/*-----------------------------------------
                                声明起始任务栈
------------------------------------------*/
OS_STK  Task_Start_Stk[TASK_START_STK_SIZE];

/*-----------------------------------------
因为涉及到对共享资源的访问,创建互斥信号量
------------------------------------------*/
OS_EVENT *LCD_Buffer_MUTEX;
OS_EVENT *USART_Buffer_MUTEX;
OS_EVENT *SDtoRAM_Buffer_MUTEX;
/*-----------------------------------------
                                主 函 数
------------------------------------------*/   
int main(void)   
{   
         INT8U err;
     SystemInit();                 //初始化RCC时钟
         OSInit();

         LCD_Buffer_MUTEX     = OSMutexCreate(4,&err);        //创建3个共享资源互斥信号量
         USART_Buffer_MUTEX   = OSMutexCreate(4,&err);
         SDtoRAM_Buffer_MUTEX = OSMutexCreate(4,&err);

     OSTaskCreate(Task_Start,(void *)0,&Task_Start_Stk[TASK_START_STK_SIZE-1],TASK_START_PRIO); //起始任务   

         OSStart();      
}
/*-------------------------------------------------------------------------------
函数功能:初始化各种外设
说    明:因为跑操作系统,所以外设的初始化应该放在操作系统的初始任务中完成,不能
          在while(1)循环外部完成外设的初始化工作,否则操作系统可能无法启动.
                  本函数在app.c中被调用
-------------------------------------------------------------------------------*/
void BSP_Init(void)
{
        OS_CPU_SR  cpu_sr = 0u;
        OS_ENTER_CRITICAL();
        DelayInit();                 //初始化延时
        PinCfg();                         //外部器件控制配置
        FSMC_LCD_Init();         //FSMC总线配置                  
        GUI_Init();                         //初始化UCGUI界面
        TSC2046_Config();         //初始化触摸控制器
        USART1_Config();         //初始化串口
        DAC_Config();                 //初始化DAC1
        ADC1_Init();                 //初始化ADC1
        EXTI_Config();                 //初始化外部中断
        Key_EXTI_Config();         //初始化按键中断
        TIM5_Config(1999,71);  //TIM5以1MHz频率计数,每2ms溢出中断
//        TIM4_Config(59999,71); //TIM4以1MHz频率计数,每60ms溢出中断
        RTC_Init();                            //初始化RTC
//        SD_Init();
        JDQ_ACDC = 1;                  //交流耦合
        ManualGainScan(3);    //调试用
        IWDG_Init(4,625);     //喂狗时间为1000ms
        OS_EXIT_CRITICAL();         
}
复制代码





使用特权

评论回复
6
littlelida| | 2022-5-7 15:31 | 只看该作者
好牛的样子,赞

使用特权

评论回复
7
Jacquetry| | 2022-10-5 21:38 | 只看该作者
代码验证过吗?

使用特权

评论回复
8
Uriah| | 2022-10-7 12:22 | 只看该作者

跟那个施密特触发器的配置有关

使用特权

评论回复
9
Bblythe| | 2022-10-7 15:21 | 只看该作者

该函数执行完后继续后续

使用特权

评论回复
10
Pulitzer| | 2022-10-7 18:20 | 只看该作者

HSI是高速内部时钟,RC振荡器,频率为8MHz,精度不高

使用特权

评论回复
11
hudi008| | 2022-10-23 17:13 | 只看该作者
用STM32来DIY一个示波器需要多少硬件成本

使用特权

评论回复
12
pentruman| | 2022-10-23 17:28 | 只看该作者
基于STM32F103芯片及自带ADC开发的吗?   

使用特权

评论回复
13
chenci2013| | 2022-10-23 17:50 | 只看该作者
STM32单片机怎么检测0-24电压和电流

使用特权

评论回复
14
averyleigh| | 2022-10-23 18:22 | 只看该作者
用STM32做示波器,据查阅,STM32采样率最大为1M,

使用特权

评论回复
15
xiaoyaodz| | 2022-10-23 19:10 | 只看该作者
stm32怎么用示波器显示定时器

使用特权

评论回复
16
sfd123| | 2022-10-23 19:29 | 只看该作者
厉害!膜拜一下!!!

使用特权

评论回复
17
robertesth| | 2022-10-23 19:59 | 只看该作者
简易示波器,简易函数信号发生器的方案都有吗

使用特权

评论回复
18
zerorobert| | 2022-10-23 20:56 | 只看该作者
基于正点原子精英板制作的一个简易示波器,可以读取信号的频率和幅值,并可以通过按键改变采样频率和控制屏幕的更新暂停。

使用特权

评论回复
19
abotomson| | 2022-10-24 17:35 | 只看该作者
stm32采集到的电压值比实际小。

使用特权

评论回复
20
AdaMaYun| | 2022-10-24 21:55 | 只看该作者
abotomson 发表于 2022-10-24 17:35
stm32采集到的电压值比实际小。

确实是一般比实际的小一点,我也遇到过

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2055

主题

7455

帖子

10

粉丝