笔记目录

  • 一.设计要求
  • 二.整体思路
  • 三.硬件设计
    • 1.功能部分
    • 2.电源部分
    • 3.stm32f407核心板及3.2寸的TFT显示屏
  • 四.软件设计
    • 1.主函数的设计
    • 2.测试信号方波实现
    • 3.示波器运行界面的设计
    • 4.按键实现
    • 5.ADC模数转换
    • 6.触发方式实现
  • 附录
  • 完整代码链接:

一.设计要求

二.整体思路


硬件部分主要负责电压的缩放以及垂直灵敏度的控制,因为stm32的大部分引脚最高输入电压为3.3v,而要求的电压需要50v,需要进行电压缩放。
软件部分主要负责方波的实现,电压采集,显示,水平时基的调整,hold功能等。

三.硬件设计

用到了multisim进行仿真以及立创eda进行制作原理图和pcb板图。这里主要参考了dso138示波器的硬件部分。具体设计如下:

1.功能部分


开关1: 耦合方式。
开关2和开关3: 垂直灵敏度,开关2和开关3的乘机为示波器一小格所代表的电压值,示波器的网格大小是不变的,变得是波形。

通过采集电压值判断硬件开关1,开关2,开关3所在位置,计算出初始电压,判断耦合方式。

接线图如下所示
前端电路 单片机
3.3<----------->3.3
GMD<-------->GND
Sw1<--------->PA3
Sw2<--------->PA4
Sw3<--------->PA5
Signal<------->PA1 (进行电压采集)

电压计算公式:待测电压=signal*Sw2*sw3(sw2 sw3的值见下表)
如果PA3,PA4,PA5采集的采集的电压为3v左右则为一档,如果电压为1.5v左右则为二档,如果电压为0v左右则为三档。

一档 二档 三档
Sw1 DC模式 AC模式 GND模式
Sw2 1 10 100
Sw3 1 2 5

举个例子: 下图测量结果为1.74v,第二个开关在二档也就是*10,第三个开关在二档也就是*2,要先判断是否超量程(方法就是二档开关倍数*最终电压看是否为3.48v左右),1.74*2=3.48v也就是超量程了需要改第二个开关。

将第二个开关从二档改到三档,247mv*2<3.48v,原被测电压=247mv*2*100=49.4v

2.电源部分

电源部分的图如下,负责12v转±5v(给运算放大器供电)和3.3v(给单片机供电)。

3.stm32f407核心板及3.2寸的TFT显示屏


原理图自行百度。
这里用到的部分引脚,剩余的见cubemx:

PA0 WKUP按键
PE3 KEY1
PE4 KEY0
PA6 LED2
PA7 LED3
PC6 方波输出
PA1 信号采集

四.软件设计

1.主函数的设计

程序中的主函数主要是对我们所需要的各个外部设计元件进行初始化,绘制开机动画之后,将示波器的核心部分元件进行初始化处理,其中包括了ADC定时器。以及DMA,经过初始化完成之后启动DMA通道转换AD采样结果,当转换结果达到我们与预先的设定值之后就会产生DMA中断,在DMA中断里进行绘制波形等相应的处理,处理完成之后我们还需要进行开始DMA通道转换。DMA中断用LED灯的亮灭来分别指示程序是否正在进行。主要程序在DMA中断里。

2.测试信号方波实现

由于实际使用示波器时不一定有函数发生器来检查示波器自身是否正常工作,所以为了示波器自检的方便,设计了一种1kHz的函数信号进行输出。

采用PWM(定时器)+DWA。将定时器的周期设为1ms,设pwm的占空比为50%即可,也就是在1ms内,高电平占500us低电平占500us。经过我对几个方案的对比,最后决定采用PWM(定时器)+DWA实现完成,下面介绍一下DWA在本次设计中的优势,DMA处于总线矩阵的前级,与内核cortex-M4同级别,属于主设备(Master)。DMA用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。使用DMA的话就可以保证系统的实时性,即在不用操作系统的情况下即能进行信号采集又能输出方波。

cubemx配置定时器为PWM模式

cubemx配置定时器3的DMA

系统时钟配置

这里用到的是定时器3,它的时钟为APB1 Timer clocks也就是84MHZ。(1MHz=1000KHz=1000000Hz)

定时器周期公式:T=(arr+1)*(psc+1)/Tck=(84)*(1000)/(84*1000000)= 0.001s=1ms
其中TCK为时钟频率(单位为HZ),PSC为时钟预分频系数,arr为自动重装载值。
配置和初始化程序由cubemx软件直接生成,需要在主函数中添加的程序如下所示:

uint16_t data[40]={500,500,500,500};     //设置占空比
HAL_TIM_OC_Start_DMA(&htim3,TIM_CHANNEL_1,(uint32_t*)data,4);  //开启定时器和DMA并配置占空比。

用示波器对产生的方波进行测试,测试结果如下图所示:

3.示波器运行界面的设计

示波器的屏幕采用了3.2寸的TFT显示屏,原本选用的方案是采用串口屏进行显示,串口屏的开发简单更适合做UI界面,但是串口屏的更新速率过慢无法满足示波器实时显示的要求。
TFT显示屏采用FSMC进行驱动,不采用GPIO直接驱动是因为这种方法太慢,FSMC是用来外接各种存储芯片的,所以其数据通信速度是比普通GPIO口要快得多的。TFT-LCD 驱动芯片的读写时序和SRAM的差不多,所以就可以用FSMC四块中的SRAM块来驱动LCD。SRAM有数据线和地址线,所以FSMC跟它匹配同样也有数据线和地址线,而LCD数据线跟地址线共用,通信时用RS端来区分线上是数据还是指令,RS高是数据,RS低是指令。我们在给fsmc一个地址写数据的时候,会产生一个时序,而这个时序正好跟我们lcd的读写时序一致,从而通过地址写数据巧妙的实现了lcd的读写时序。采用cubemx对FSMC进行初始化配置如图所示:
cubemx配置FSMC总线
对TFT显示屏进行读写数据时需要注意时序,由于都是微秒级延时而HAL库只提供了毫秒级延时,需要自己对微秒级延时进行编写。重新找一个定时器实现微秒级延时,这里采用系统计数器systick。CM3与CM4包含一个系统计数器SysTick,是一个24位倒计数定时器,当计数到0 时,将从RELOAD寄存器中自动重装载定时初值,只要把它在SysTick->CTRL中的使能位清除,则一直存在。
TFT显示屏对FSMC进行读写程序如下所示:

//写寄存器函数
//regval:寄存器值
void LCD_WR_REG(vu16 regval)
{regval = regval; //使用-O2优化的时候,必须插入的延时TFTLCD->LCD_REG=regval;  //写入要写的寄存器序号
}//写LCD数据
//data:要写入的值
void LCD_WR_DATA(vu16 data)
{data=data;    //使用-O2优化的时候,必须插入的延时TFTLCD->LCD_RAM=data;
}//读LCD数据
//返回值:读到的值
u16 LCD_RD_DATA(void)
{vu16 ram;  //防止被优化ram=TFTLCD->LCD_RAM;return ram;
}
//写寄存器
//LCD_Reg:寄存器地址
//LCD_RegValue:要写入的数据
void LCD_WriteReg(vu16 LCD_Reg,u16 LCD_RegValue)
{TFTLCD->LCD_REG = LCD_Reg;  //写入要写的寄存器序号TFTLCD->LCD_RAM = LCD_RegValue;//写入数据
}//读寄存器
//LCD_Reg:寄存器地址
//返回值:读到的数据
u16 LCD_ReadReg(vu16 LCD_Reg)
{LCD_WR_REG(LCD_Reg);  //写入要读取的寄存器序号delay_us(5);return LCD_RD_DATA();  //返回读到的值
}//开始写GRAM
void LCD_WriteRAM_Prepare(void)
{TFTLCD->LCD_REG=lcddev.wramcmd;
}//从ILI93xx读出的数据为GBR格式,而我们写入的时候为RGB格式。
//通过该函数转换
//c:GBR格式的颜色值
//返回值:RGB格式的颜色值
u16 LCD_BGR2RGB(u16 c)
{u16 r,g,b,rgb;b=(c>>0)&0x1f;g=(c>>5)&0x3f;r=(c>>11)&0x1f;rgb=(b<<11)+(g<<5)+(r<<0);return (rgb);
}

示波器主界面通过输出字符来显示相应的字母和数组,调整好对应坐标进行显示。波形位置显示是通过符号[-]|按照一定组合成字符串即可得到这种效果。网格是通过画点实现的,横线每隔4像素画一个点,纵向每隔30像素画一条横线。竖线每隔4像素画一个点,横向每隔32像素画一条竖线。画完点阵之后在外部画一个矩形实线框起来,横纵坐标中心位置画十字实线表示中点。主要设计程序如下所示
画网格核心部分代码如下:

 void Lcd_DrawNetwork(void)
{u16 index_y = 0;u16 index_x = 0; //画列点   for(index_x = 0;index_x <= 288;index_x += 32){for(index_y = 0;index_y < 240;index_y += 4){LCD_Fast_DrawPoint(index_x,index_y,0X534c);  }}//画行点for(index_y = 0;index_y <=240;index_y += 30){for(index_x = 0;index_x <288;index_x += 4){LCD_Fast_DrawPoint(index_x,index_y,0X534c); }}LCD_DrawLine_color (0 ,120 , 288 ,120,BROWN);//十字叉LCD_DrawLine_color (160 ,0 , 160 ,240,BROWN);
}

TFT显示屏显示如下所示:

4.按键实现

这里采用了stm32f407核心板自带的三个按键,wk_UP、KEY0、KEY1分别对应的功能是切换功能选项,增大、减小。为保证按键的实时性采用外部中断,并将中断优先级设置为最高也就是0。用cubemx对按键进行配置,根据核心板的原理图可知wk_UP设置成下拉模式,KEY0、KEY1设置为上拉模式。
cubemx配置按键

按键原理图

cubemx配置中断

为方便看清功能选项选到了哪一个功能,在选择的功能下面画一条绿色的直线并清除上一次画的横线。当按下一次按键时会让按键对应的按键标志位(key0,key1,key2)加一,如果按下wk_UP切换功能键会通过取余判断现在所选的功能是哪一个并对KEY0、KEY1按键的标志位进行清零。按键中断程序如下:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{if(GPIO_Pin==GPIO_PIN_0){delay_us(10);     //防抖key0++;if(key0%6==1){LCD_DrawLine_color(0,238,25,238,BLACK);      //清除上一次画线LCD_DrawLine_color(295,30,320,30,GREEN);           //画线}else if(key0%6==2){LCD_DrawLine_color(295,30,320,30,BLACK);LCD_DrawLine_color(295,70,320,70,GREEN);}else if(key0%6==3){LCD_DrawLine_color(295,70,320,70,BLACK);LCD_DrawLine_color(295,110,320,110,GREEN);}else if(key0%6==4){LCD_DrawLine_color(295,110,320,110,BLACK);LCD_DrawLine_color(295,150,320,150,GREEN);}else if(key0%6==5){LCD_DrawLine_color(295,150,320,150,BLACK);LCD_DrawLine_color(295,190,320,190,GREEN);}else if(key0%6==0){LCD_DrawLine_color(295,190,320,190,BLACK);LCD_DrawLine_color(0,238,25,238,GREEN);}key1=0;key2=0;}else if(GPIO_Pin==GPIO_PIN_3){delay_us(10);if(key0%6==0)time_flag=0;          //时基调整标志位key2++;}else if(GPIO_Pin==GPIO_PIN_4){delay_us(10);if(key0%6==0)time_flag=0;               key1++;}
}

5.ADC模数转换

ADC采样是指将连续变量的模拟信号转换为离散的数字信号的器件。典型的模拟数字转换器将模拟信号转换为表示一定比例电压值的数字信号。由于要保证采样的实时性采用DMA对ADC采集后的数据进行存储,将DMA存储量设为1024字节。用定时器2来改变采样间隔从而改变时基。当DMA中存储的数据达到1024字节时会触发DMA中断,判断此时示波器所处的状态是运行还是停止,如果是停止则会直接跳出终端不在TFT显示屏上显示,如果是运行会关闭定时器2,也就是停止采样,之后读取DMA中的数据并显示到TFT显示屏上,由于画点的速度比画线快所以采用画点的方法。显示完成后再打开定时器2开启下一次采样。

cubemxADC配置(ADC+DMA+TIMER2)

ADC的DMA中断的程序如下所示:


void DMA2_Stream0_IRQHandler(void)
{if(mode1==1){if(HAL_DMA_GetState(&hdma_adc1)){HAL_TIM_Base_Stop(&htim2);     //关闭定时器2adc_line();                     //画点HAL_TIM_Base_Start(&htim2);     //开启定时器2}}HAL_DMA_IRQHandler(&hdma_adc1);
}

HOLD功能实现代码如下所示:
代码思想:如果按键key_up选择了hold功能并且按下了key1键,就会判断现在所处状态是运行还是停止,如果是运行就改为停止状态,DMA中断就不会在执行显示任务,波形也就不会更新。如果是停止状态就改为运行状态,DMA中断执行显示任务。

if(key0%6==1&&key1==1)
{if(mode1==1){BACK_COLOR=GRAYBLUE;LCD_ShowString_COLOR(295,15,200,12,12,"stop",RED);mode1=0;}else{BACK_COLOR=GRAYBLUE;LCD_ShowString_COLOR(295,15,200,12,12," run",GREEN);Lcd_DrawNetwork();mode1=1;}key1=0;
}

画点部分程序如下所示(adc_line()中部分程序):
代码思想:清除上一次显示的点,如果清除和显示的速率足够高也就是刷新率足够高人眼就看不出来屏幕一闪一闪的,之后对DMA中存储的数据进行坐标转换,屏幕的分辨率为240*320而ADC采集的点的范围为0-4095,所以转换公式为ADC_Value[t] =120-((ADC_ConvertedValue[i] * 3.3 / 4095)*30)+mode5


for(n = 160;n<1024;n++)
{adc_line_clean();        //清除上次画的点for(i=n-160;i<n+128;i++){ADC_Value[t] =120-((ADC_ConvertedValue[i] * 3.3 / 4095)*30)+mode5;    //坐标转换LCD_Fast_DrawPoint(t++,ADC_Value[t],POINT_COLOR);        //画点}     }

波形清除程序如下所示:
程序的思想:将上次画完的点的纵坐标保存在ADC_Value数组中,重新绘制一遍,将颜色改成背景色也就是黑色,如果上次的点出现在十字位置则改成棕黄色。

void adc_line_clean()
{for(i=0;i<288;i++){if(i==160||ADC_Value[i]==120){LCD_Fast_DrawPoint(i,ADC_Value[i],BROWN);}elseLCD_Fast_DrawPoint(i,ADC_Value[i],BLACK);}
}

通过改变定时器的采样间隔从而改变时基(水平时基范围),程序在adc_line()程序中,也就是DMA中断中,部分程序如下:

     else if(key0%6==0)    //如果按键key_up选择了时基调准功能{   if(time_flag==0){time_i=key1-key2+6;     //key1 增加时基 key2  减小时基if(time_i==1){timebase=0.64;TIM2->ARR = timebase - 1;timeshow=10;}else if(time_i==2){timebase=1.28;TIM2->ARR = timebase - 1;timeshow=20;}else if(time_i==3){timebase=3.2;TIM2->ARR = timebase - 1;timeshow=50;}else if(time_i==4){timebase=6.4;TIM2->ARR = timebase - 1;timeshow=100;}else if(time_i==5){timebase=12.8;TIM2->ARR = timebase - 1;timeshow=200;}else if(time_i==6){timebase=32;TIM2->ARR = timebase - 1;timeshow=500;}else if(time_i==7){timebase=64;TIM2->ARR = timebase - 1;timeshow=1000;}else if(time_i==8){timebase=128;TIM2->ARR = timebase - 1;timeshow=2000;}else if(time_i==9){timebase=320;TIM2->ARR = timebase - 1;timeshow=5000;}else if(time_i==10){timebase=640;TIM2->ARR = timebase - 1;timeshow=10000;}else if(time_i==11){timebase=1280;TIM2->ARR = timebase - 1;timeshow=20000;}else if(time_i==12){timebase=3200;TIM2->ARR = timebase - 1;timeshow=50000;}else if(time_i==13){timebase=6400;TIM2->ARR = timebase - 1;timeshow=100000;}else if(time_i==14){timebase=12800;TIM2->ARR = timebase - 1;timeshow=200000;}else if(time_i==15){timebase=32000;TIM2->ARR = timebase - 1;timeshow=500000;}else if(time_i==16){timebase=64000;TIM2->ARR = timebase - 1;timeshow=1000000;}else if(time_i==17){timebase=128000;TIM2->ARR = timebase - 1;timeshow=2000000;}else if(time_i==18){timebase=320000;TIM2->ARR = timebase - 1;timeshow=5000000;}else if(time_i==19){timebase=640000;TIM2->ARR = timebase - 1;timeshow=10000000;}else if(time_i==20){timebase=1280000;TIM2->ARR = timebase - 1;timeshow=20000000;}else if(time_i==21){timebase=3200000;TIM2->ARR = timebase - 1;timeshow=50000000;}if(time_i>21)time_i=21;if(time_i<i)time_i=1;time_flag=1;}}

效果图如下所示

6.触发方式实现

本次设计目标要求我们实现自动、常规和单次触发方式,方便捕捉瞬间波形可用上升或下降边沿触发,下面解释各个触发方式的特点以及其相互之间的不同点自动触发:在这种模式下,当触发没有发生时,示波器的扫描系统会根据设定的扫描速率自动进行扫描;而当有触发发生时,扫描系统会尽量按信号的频率进行扫描。
普通触发:在这种模式下示波器只有当触发条件满足了才进行扫描,如果没有触发,就不进行扫描。普通触发程序见附录。
程序思想:需要预先设定一个触发值,触发值如图绿色线所示,因为要显示出发之前的波形也就是负延时,选取DMA中200个点之后的点开始判断,当触发值比采集到的电压值小时触发成功,将这个触发成功的点显示在中间位置然后显示这个点之前的160个点和这个点之后的128个点。
达到触发条件

未达到触发条件

上升沿与下降沿触发程序的思想:需要预先设定一个触发值,选取DMA中200个点之后的点开始判断,如果采集到的前一个电压值比触发值高而后一个点比触发值低则为下降沿触发,将后一个点显示在中间位置然后显示这个点之前的160个点和这个点之后的128个点。反之如果采集到的前一个电压值比触发值低而后一个点比触发值高则为上升沿触发。
上升沿与下降沿触效果如图所示:

上升沿触发

下降沿触发

单次触发:只触发一次,出发成功后不再触发。
实现代码与普通触发类似,当触发一次后便不再显示。效果如下所示:

自动触发:不论触发条件是否满足,示波器都会产生扫描,都可以在屏幕上可以看到有变化的扫描线。代码思想:从DMA中存储的的第一个数值开始显示,显示288个点,然后把第五个点放在屏幕上的最左边开始显示剩下的点以此类推,直到显示完1024个点,形成一个点流有一种流动效果。

附录

main.c

/* USER CODE BEGIN Header */
/********************************************************************************* @file           : main.c* @brief          : Main program body******************************************************************************* @attention** <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.* All rights reserved.</center></h2>** This software component is licensed by ST under BSD 3-Clause license,* the "License"; You may not use this file except in compliance with the* License. You may obtain a copy of the License at:*                        opensource.org/licenses/BSD-3-Clause********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "dma.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
#include "fsmc.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "string.h"
#include "fsmc.h"
#include "arm_math.h"                  //添加头文件
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
#define BYTE0(dwTemp)       ( *( (char *)(&dwTemp)    ) )
#define BYTE1(dwTemp)       ( *( (char *)(&dwTemp) + 1) )
#define BYTE2(dwTemp)       ( *( (char *)(&dwTemp) + 2) )
#define BYTE3(dwTemp)       ( *( (char *)(&dwTemp) + 3) )#define  FFT_LENGTH        1024        //FFT长度,默认是1024点FFT
#define  SAMPLE_FREQ       1024        //采样频率uint8_t testdatatosend[50];
void TestSendData(uint8_t *dataToSend , uint8_t length);
void Test_Send_User(uint16_t data1, uint16_t data2, uint16_t data3,uint16_t data4,uint16_t data5,uint16_t data6,uint16_t data7,uint16_t data8,uint16_t data9,uint16_t data10)   ;void ENDSend(uint8_t k);void adc_line(void);
void adc_line_clean(void);float Get_vpp(u16 *buf);     //获取峰峰值
/* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
uint8_t aRxBuffer; //接收中断缓冲//usart1发送和接收数组
uint8_t usart1_txbuf[256];
uint8_t usart1_rxbuf[512];
uint8_t usart1_rxone[1];
uint8_t usart1_rxcounter;uint16_t adcBuf[2]={0,0};    //ad的值
float adcvcc[2];
int i;int j;uint16_t data[40]={500,500,500,500};__IO uint16_t ADC_ConvertedValue[1024];
__IO float ADC_Volt;float ADC_Value[1024] ;u32 max_data=2048; //触发电平
vu32 temp=0; //频率输出
float vpp=0;
int freq=0;
int timeshow=500;
u8  Vpp_buff[20] = {0};//sprintf数据输出
u8  freq_buff[20] = {0};//sprintf数据输出
u8  time_buff[20] = {0};//sprintf数据输出
int time_flag=0;
int once=0;int mode1=1;     //0 stop   1 run
int mode2=1;     //0 down    1 up
int mode3=115;   //触发电平
int mode4=2;    //0 auto   1once  2pt
int mode5=0;   //垂直位移
int mode6=0;    //0 DC 1 AC 2 GND float timebase=32;
int time_i=6;     //时基计数extern int key0,key1,key2;
/* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END 0 *//*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 */u8 x=0;u8 lcd_id[12];              //存放LCD ID字符串/* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_DMA_Init();MX_USART1_UART_Init();MX_USART2_UART_Init();MX_ADC1_Init();MX_TIM3_Init();MX_FSMC_Init();MX_TIM6_Init();MX_TIM2_Init();/* USER CODE BEGIN 2 */HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1); HAL_GPIO_WritePin(GPIOA,GPIO_PIN_6,GPIO_PIN_SET);TFTLCD_Init(); LCD_Clear(BLACK);Lcd_DrawNetwork();BACK_COLOR=BLACK;LCD_ShowString_COLOR(0,226,120,12,12,"Time:",WHITE);LCD_ShowString_COLOR(80,226,120,12,12,"V= 1v",WHITE);LCD_ShowString_COLOR(120,226,120,12,12,"Freq:",WHITE);LCD_ShowString(200,226,120,12,12,"Vpp:");BACK_COLOR=GRAYBLUE;LCD_ShowString_COLOR(295,15,200,12,12," run",GREEN);LCD_ShowString_COLOR(295,55,200,12,12,"rise",BLACK);LCD_ShowString_COLOR(295,95,200,12,12,"trig",BLACK);LCD_ShowString_COLOR(295,135,200,12,12," nor",BLACK);LCD_ShowString_COLOR(295,175,200,12,12,"move",BLACK);LCD_ShowString_COLOR(295,215,200,12,12,"DC",BLACK);BACK_COLOR=GREEN;LCD_ShowString_COLOR(289,mode3-5,200,12,12,"<",BLACK);LCD_DrawLine_color(0,mode3,288,mode3,GREEN);LCD_DrawLine_color(0,mode5+120,288,mode5+120,LIGHTBLUE);LCD_DrawLine_color(295,30,320,30,GREEN);//printf("ok\r\n");//HAL_TIM_Base_Start(&htim2);//打开定时器
//  HAL_TIM_Base_Start_IT(&htim2);//打开定时器中断HAL_TIM_OC_Start_DMA(&htim3,TIM_CHANNEL_1,(uint32_t*)data,4);HAL_TIM_Base_Start(&htim2);HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&ADC_ConvertedValue, 1024);sprintf((char*)lcd_id,"LCD ID:%04X",lcddev.id);//将LCD ID打印到lcd_id数组。int i,j=0;    /* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */
//      //匿名上位机+HMI串口屏
//      for(j=0;j<20;j++)
//      {printf("add 2,0,255");
//      ENDSend(0xff);
//      Test_Send_User(1,2,3,4,5,6,7,8,9,10);
//    HAL_Delay(50);
//      }
//      for(j=0;j<20;j++)
//      {printf("add 2,0,100");
//      ENDSend(0xff);
//      Test_Send_User(1,2,3,4,5,6,7,8,9,10);
//    HAL_Delay(50);
//      }// //adc测试代码
//  for(i=0;i<2;i++)
//  {//      HAL_ADC_Start(&hadc1);
//    HAL_ADC_PollForConversion(&hadc1,0xffff);
//    adcBuf[i]=HAL_ADC_GetValue(&hadc1);
//      adcvcc[i]=adcBuf[i]*3.3f/4096;
//    printf("------ch:%d--%.2f-------\r\n",i,adcvcc[i]);
//   }
//   HAL_ADC_Stop(&hadc1);
//   HAL_Delay(1);vpp=Get_vpp(ADC_ConvertedValue);BACK_COLOR=BLACK;sprintf((char*)Vpp_buff,"Vpp:%0.3fV",vpp);LCD_ShowString_COLOR(200,226,120,12,12,Vpp_buff,WHITE);sprintf((char*)freq_buff,"Freq:%6dHz",freq);LCD_ShowString_COLOR(120,226,120,12,12,freq_buff,WHITE);sprintf((char*)time_buff,"Time:%5dus/div",timeshow);LCD_ShowString_COLOR(0,226,120,12,12,time_buff,WHITE);if(key0%6==1&&key1==1){if(mode1==1){BACK_COLOR=GRAYBLUE;LCD_ShowString_COLOR(295,15,200,12,12,"stop",RED);mode1=0;}else{BACK_COLOR=GRAYBLUE;LCD_ShowString_COLOR(295,15,200,12,12," run",GREEN);Lcd_DrawNetwork();mode1=1;}key1=0;}}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Configure the main internal regulator output voltage*/__HAL_RCC_PWR_CLK_ENABLE();__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLM = 4;RCC_OscInitStruct.PLL.PLLN = 168;RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;RCC_OscInitStruct.PLL.PLLQ = 4;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK){Error_Handler();}
}/* USER CODE BEGIN 4 */
void TestSendData(uint8_t *dataToSend , uint8_t length)
{HAL_UART_Transmit(&huart2, (uint8_t *)&testdatatosend, length, 0xffff);
}void Test_Send_User(uint16_t data1, uint16_t data2, uint16_t data3,uint16_t data4,uint16_t data5,uint16_t data6,uint16_t data7,uint16_t data8,uint16_t data9,uint16_t data10)
{uint8_t _cnt=0;testdatatosend[_cnt++]=0xAA;testdatatosend[_cnt++]=0x05;testdatatosend[_cnt++]=0xAF;testdatatosend[_cnt++]=0xF1;testdatatosend[_cnt++]=0;testdatatosend[_cnt++]=BYTE1(data1);testdatatosend[_cnt++]=BYTE0(data1);testdatatosend[_cnt++]=BYTE1(data2);testdatatosend[_cnt++]=BYTE0(data2);testdatatosend[_cnt++]=BYTE1(data3);testdatatosend[_cnt++]=BYTE0(data3);testdatatosend[_cnt++]=BYTE1(data4);testdatatosend[_cnt++]=BYTE0(data4);testdatatosend[_cnt++]=BYTE1(data5);testdatatosend[_cnt++]=BYTE0(data5);testdatatosend[_cnt++]=BYTE1(data6);testdatatosend[_cnt++]=BYTE0(data6);testdatatosend[_cnt++]=BYTE1(data7);testdatatosend[_cnt++]=BYTE0(data7);testdatatosend[_cnt++]=BYTE1(data8);testdatatosend[_cnt++]=BYTE0(data8);testdatatosend[_cnt++]=BYTE1(data9);testdatatosend[_cnt++]=BYTE0(data9);testdatatosend[_cnt++]=BYTE1(data10);testdatatosend[_cnt++]=BYTE0(data10);testdatatosend[4] = _cnt-5;uint8_t sum = 0;    for(uint8_t i=0;i<_cnt;i++)sum += testdatatosend[i];testdatatosend[_cnt++]=sum;  TestSendData(testdatatosend, _cnt);
}void ENDSend(uint8_t k)           //字节发送函数
{   uint8_t i;for(i=0;i<3;i++){if(k!=0){  HAL_UART_Transmit(&huart1,&k,1,1000);    //发送一个字节   HAL_Delay(10);while((__HAL_UART_GET_FLAG(&huart1,UART_FLAG_TXE)==RESET)){};   //等待发送结束HAL_Delay(10);} }
} void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(huart->Instance == USART1) // 判断是由哪个串口触发的中断{//将接收到的数据放入接收usart1接收数组usart1_rxbuf[usart1_rxcounter] = usart1_rxone[0];usart1_rxcounter++; //接收数量+1if(usart1_rxone[0]==0x30){HAL_GPIO_WritePin(GPIOA,GPIO_PIN_6,GPIO_PIN_SET);}if(usart1_rxone[0]==0x31){HAL_GPIO_WritePin(GPIOA,GPIO_PIN_6,GPIO_PIN_RESET);}//重新使能串口1接收中断HAL_UART_Receive_IT(&huart1,usart1_rxone,1);      }
}void adc_line()
{   static u16 Ypos1 = 0,Ypos2 = 0;static vu32 min_data=0;//buf[1];static vu32 n=0,con_t=0,con_t1=0;static    u16 i = 0,y=0,t=0;POINT_COLOR = RED;for(n = 0;n<1024;n++){if(ADC_ConvertedValue[n] > max_data&&ADC_ConvertedValue[n+3] < max_data){con_t=n;break;}            } for(n = con_t+3;n<1024;n++){if(ADC_ConvertedValue[n] > max_data&&ADC_ConvertedValue[n+3] < max_data){con_t1=n;break;}          }temp=con_t1- con_t;freq=(temp*timebase)*0.5;if(key0%6==2&&key1==1){if(mode2==1){BACK_COLOR=GRAYBLUE;LCD_ShowString_COLOR(295,55,200,12,12,"down",BLACK);mode2=0;}else{BACK_COLOR=GRAYBLUE;LCD_ShowString_COLOR(295,55,200,12,12,"rise",BLACK);mode2=1;}key1=0;}else if(key0%6==4&&key1==1){if(mode4==0){BACK_COLOR=GRAYBLUE;LCD_ShowString_COLOR(295,135,200,12,12,"once",BLACK);mode4=1;}else if(mode4==1){BACK_COLOR=GRAYBLUE;LCD_ShowString_COLOR(295,135,200,12,12," nor",BLACK);mode4=2;}else{BACK_COLOR=GRAYBLUE;LCD_ShowString_COLOR(295,135,200,12,12,"auto",BLACK);mode4=0;}key1=0;}else if(key0%6==0){   if(time_flag==0){time_i=key1-key2+6;if(time_i==1){timebase=0.64;TIM2->ARR = timebase - 1;timeshow=10;}else if(time_i==2){timebase=1.28;TIM2->ARR = timebase - 1;timeshow=20;}else if(time_i==3){timebase=3.2;TIM2->ARR = timebase - 1;timeshow=50;}else if(time_i==4){timebase=6.4;TIM2->ARR = timebase - 1;timeshow=100;}else if(time_i==5){timebase=12.8;TIM2->ARR = timebase - 1;timeshow=200;}else if(time_i==6){timebase=32;TIM2->ARR = timebase - 1;timeshow=500;}else if(time_i==7){timebase=64;TIM2->ARR = timebase - 1;timeshow=1000;}else if(time_i==8){timebase=128;TIM2->ARR = timebase - 1;timeshow=2000;}else if(time_i==9){timebase=320;TIM2->ARR = timebase - 1;timeshow=5000;}else if(time_i==10){timebase=640;TIM2->ARR = timebase - 1;timeshow=10000;}else if(time_i==11){timebase=1280;TIM2->ARR = timebase - 1;timeshow=20000;}else if(time_i==12){timebase=3200;TIM2->ARR = timebase - 1;timeshow=50000;}else if(time_i==13){timebase=6400;TIM2->ARR = timebase - 1;timeshow=100000;}else if(time_i==14){timebase=12800;TIM2->ARR = timebase - 1;timeshow=200000;}else if(time_i==15){timebase=32000;TIM2->ARR = timebase - 1;timeshow=500000;}else if(time_i==16){timebase=64000;TIM2->ARR = timebase - 1;timeshow=1000000;}else if(time_i==17){timebase=128000;TIM2->ARR = timebase - 1;timeshow=2000000;}else if(time_i==18){timebase=320000;TIM2->ARR = timebase - 1;timeshow=5000000;}else if(time_i==19){timebase=640000;TIM2->ARR = timebase - 1;timeshow=10000000;}else if(time_i==20){timebase=1280000;TIM2->ARR = timebase - 1;timeshow=20000000;}else if(time_i==21){timebase=3200000;TIM2->ARR = timebase - 1;timeshow=50000000;}if(time_i>21)time_i=21;if(time_i<i)time_i=1;time_flag=1;}}else if(key0%6==3){if(key1==1){adc_line_clean();Lcd_DrawNetwork();LCD_ShowString_COLOR(289,mode3-5,200,12,12," ",BLACK);LCD_DrawLine_color(0,mode3,288,mode3,BLACK);mode3=mode3+5;BACK_COLOR=GREEN;LCD_ShowString_COLOR(289,mode3-5,200,12,12,"<",BLACK);LCD_DrawLine_color(0,mode3,288,mode3,GREEN);key1=0;}if(key2==1){adc_line_clean();Lcd_DrawNetwork();LCD_ShowString_COLOR(289,mode3-5,200,12,12," ",BLACK);LCD_DrawLine_color(0,mode3,288,mode3,BLACK);mode3=mode3-5;BACK_COLOR=GREEN;LCD_ShowString_COLOR(289,mode3-5,200,12,12,"<",BLACK);LCD_DrawLine_color(0,mode3,288,mode3,GREEN);key2=0;}}else if(key0%6==5){if(key1==1){adc_line_clean();Lcd_DrawNetwork();LCD_DrawLine_color(0,mode5+120,288,mode5+120,BLACK);mode5=mode5+5;LCD_DrawLine_color(0,mode5+120,288,mode5+120,LIGHTBLUE);key1=0;}if(key2==1){adc_line_clean();Lcd_DrawNetwork();LCD_DrawLine_color(0,mode5+120,288,mode5+120,BLACK);mode5=mode5-5;LCD_DrawLine_color(0,mode5+120,288,mode5+120,LIGHTBLUE);key2=0;}}if(mode4==0){once=0;for(y=0;y<750;){for(i=0+y;i<288+y;i++){if(t==160||ADC_Value[i]==120){LCD_Fast_DrawPoint(t++,ADC_Value[i],BROWN);}elseLCD_Fast_DrawPoint(t++,ADC_Value[i],BLACK);}t=0;i=i-1;y=y+10;for(i=0+y;i<288+y;i++){if(mode1==1){ADC_Value[i] =120-((ADC_ConvertedValue[i] * 3.3 / 4095)*30+mode5);LCD_Fast_DrawPoint(t++,ADC_Value[i],POINT_COLOR);}}t=0; }}if(mode4==2&&mode2==1){   once=0;for(n = 160;n<1024;n++){if((120-(ADC_ConvertedValue[n]* 3.3 / 4095)*30)>mode3&&(120-(ADC_ConvertedValue[n+1]* 3.3 / 4095)*30)<mode3)      //down{t=0;adc_line_clean();for(i=n-160;i<n+128;i++){ADC_Value[t] =120-((ADC_ConvertedValue[i] * 3.3 / 4095)*30)+mode5;LCD_Fast_DrawPoint(t++,ADC_Value[t],POINT_COLOR);}break;}              }}if(mode4==2&&mode2==0){   once=0;for(n = 160;n<1024;n++) {if((120-(ADC_ConvertedValue[n]* 3.3 / 4095)*30)<mode3&&(120-(ADC_ConvertedValue[n+1]* 3.3 / 4095)*30)>mode3)      //up{t=0;adc_line_clean();for(i=n-160;i<n+128;i++){ADC_Value[t] =120-((ADC_ConvertedValue[i] * 3.3 / 4095)*30)+mode5;LCD_Fast_DrawPoint(t++,ADC_Value[t],POINT_COLOR);}break;}}}if(once==0&&mode4==1&&mode2==0){   for(n = 160;n<1024;n++){if(ADC_ConvertedValue[n]>mode3&&ADC_ConvertedValue[n+1]<mode3)      //down{t=0;adc_line_clean();for(i=n-160;i<n+128;i++){ADC_Value[t] =120-((ADC_ConvertedValue[i] * 3.3 / 4095)*30)+mode5;LCD_Fast_DrawPoint(t++,ADC_Value[t],POINT_COLOR);}once=1;break;}              }}if(once==0&&mode4==1&&mode2==1){   for(n = 160;n<1024;n++) {if(ADC_ConvertedValue[n]<mode3&&ADC_ConvertedValue[n+1]>mode3)      //up{t=0;adc_line_clean();for(i=n-160;i<n+128;i++){ADC_Value[t] =120-((ADC_ConvertedValue[i] * 3.3 / 4095)*30)+mode5;LCD_Fast_DrawPoint(t++,ADC_Value[t],POINT_COLOR);}once=1;break;}                 }}
}void adc_line_clean()
{for(i=0;i<288;i++){if(i==160||ADC_Value[i]==120){LCD_Fast_DrawPoint(i,ADC_Value[i],BROWN);}elseLCD_Fast_DrawPoint(i,ADC_Value[i],BLACK);}
}float Get_vpp(u16 *buf)       //获取峰峰值
{u32 max_data=buf[0];u32 min_data=buf[0];//buf[1];u32 n=0;float Vpp=0;for(n = 1;n<1024;n++){if(buf[n] > max_data){max_data = buf[n];}if(buf[n] < min_data){min_data = buf[n];}            }   Vpp = (float)(max_data - min_data);Vpp = Vpp*(3.3/4096);return Vpp;
}/* USER CODE END 4 *//*** @brief  This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */
}#ifdef  USE_FULL_ASSERT
/*** @brief  Reports the name of the source file and the source line number*         where the assert_param error has occurred.* @param  file: pointer to the source file name* @param  line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT *//************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

完整代码链接:

https://download.csdn.net/download/qq_44181970/19151369

基于stm32f407的示波器相关推荐

  1. 【有图有真像】基于STM32F407小型示波器

    点击打开链接 源码连接 : https://pan.baidu.com/s/1FfVj31fKGaFFA61AMfiNJA 先晒晒照片吧:(纯手工打造哦~ 手机拍摄,效果一般般) emwin演示 前硬 ...

  2. 基于stm32f407的示波器+FFT频谱分析

    1 设计思路 2 DMA传输ADC采样值 使用DMA直接将ADC->DR中的数据传输到ADC数据缓存区,节省cpu资源,高速AD采集,代码如下: DMA_InitStructure.DMA_Pe ...

  3. EC20模组使用MQTT库对接EMQX,基于STM32F407

    一.说明 本lib库基于STM32F407编译,其他的cortexM4内核也支持,采用串口和EC20模组通信. 库包括两个文件:ec20_mqtt.h和ec20_mqtt.lib.使用时添加lib文件 ...

  4. stm32 U盘升级 bootloader程序 基于stm32f407 将升级包下载到U盘中,插入到设备中,完成对主程序的升级

    stm32 U盘升级 bootloader程序 基于stm32f407 将升级包下载到U盘中,插入到设备中,完成对主程序的升级,无需上位机操作. 清单: u盘升级的bootloader源码. YID: ...

  5. 基于STM32F407的人脸追踪

    整体概述 本项目采用两个舵机构成的二自由度的电动云台作为执行机构,控制摄像头在水平和垂直方向的运动.舵机带动摄像头进行二维平面的运动的同时,摄像头进行实时人脸检测,一旦检测到人脸,则进行人脸跟踪. 摄 ...

  6. ​基于STM32F407的五子棋游戏设计​

    本博客为资源:基于STM32F407的五子棋游戏设计内的说明文档. 目录 一.设计目标 三.设计方案 1.游戏模式 2.游戏过程 3.游戏设计 四.硬件配置 1.TFT-LCD液晶屏模块 (1)工作原 ...

  7. 简单实现基于 STM32F407+ESP8266+RFID 的物联网小项目

    BusChargeSystem-IOT 项目介绍 基于 STM32F407+ESP8266+RFID 的模拟公交车刷卡收费系统(物联网) STM32 通过 RFID 识别获取卡片数据并通过串口发送到 ...

  8. 基于Stm32f407 的贪吃蛇小游戏【正点原子-探索者开发板】

    基于单片机stm32f407的单机小游戏----贪吃蛇小游戏 1.介绍 这是我花一个星期完成的一个简单地单机贪吃蛇小游戏项目,芯片是stm32f407,项目是基于正点原子-探索者开发板完成的,有需要的 ...

  9. 基于STM32F407+RFID的模拟公交车刷卡收费系统小项目

    BusChargeSystem 项目介绍 基于STM32F407+RFID的模拟公交车刷卡收费系统 开源仓库地址: github:https://github.com/DaXiongRen/BusCh ...

最新文章

  1. 为何艾伦·图灵想让AI智能体故意犯错
  2. Eclipse真机测试注意事项
  3. c语言实参和形参占用存储单元_必须知道的C语言知识细节:函数形参和实参的区别...
  4. 在香蕉派的树莓派系统上配置 Syncthing 自启动(暨 Linux 软件自启服务配置)
  5. StreamWriter打开文件报错:”不支持给定路径的格式。“
  6. Application Constants
  7. 硅谷渐患“大城市”病,世界创新中心或将外移
  8. C++函数free和delete如何操作指针?
  9. harmonyos2.0怎么更新,harmonyOS 2.0什么时候更新 harmonyOS 2.0手机开发者Beta版更新内容[多图]...
  10. linux定位so快捷方式_5分钟带你了解Linux常用命令全称
  11. 【优化算法】自私羊群优化算法(SHO)【含Matlab源码 1569期】
  12. 使用PyCharm官方中文语言包汉化PyCharm
  13. Web测试-Web界面易用性测试
  14. python 豆瓣电影top250_「豆瓣电影top250」豆瓣电影TOP250抓取 - seo实验室
  15. EnableQ问卷属性控制项
  16. win7系统查看硬盘序列号步骤
  17. 个人空间岁末大回报活动12月16日获奖名单
  18. 跨越信息沟通的障碍,构建企业高效应用平台
  19. 【SDX62】ERROR: Error executing a python function in exec_python_func() autogenerated:
  20. linux 第十一天 linuxprobe

热门文章

  1. 前端工资涨不上去?可能是你没掌握构建工具:关于 Webpack、Babel、esbuild、Vite、Rollup、Parcel、SWC......的那些事
  2. 频域判断波形_股票价格频域特性
  3. 【BIM入门实战】Revit创建地形的几种方法及优缺点
  4. 无论多大年纪,请保留一份童真和幻想
  5. APICLOUD实现沉浸式导航栏在Android和ios上的兼容
  6. 肌酐研究之Biovision肌酐合成试剂盒
  7. 罗德里格旋转公式推导(自制)
  8. c#文件名去掉后缀_C#如何从文件路径中分离出文件名以及文件扩展名
  9. Python+OpenCV实现图像处理OCR手写数字识别原理
  10. 2023最新最新ChatGPT超全面从基础到实战视频教程/有兴趣自己学