基于stm32f407的示波器


一.设计要求

在这里插入图片描述

二.整体思路

在这里插入图片描述
硬件部分主要负责电压的缩放以及垂直灵敏度的控制,因为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);     //关闭定时器2
		adc_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);
		}
		else
		LCD_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++;	//接收数量+1
		if(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);
					}
					else
						LCD_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);
		}
		else
		LCD_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

版权声明:本文为CSDN博主「Wyd_(ง •̀_•́)ง」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_44181970/article/details/117361614

生成海报
点赞 0

Wyd_(ง •̀_•́)ง

我还没有学会写个人说明!

暂无评论

相关推荐

基于STM32的室内环境监测系统

设计简介: 本设计是基于单片机的室内环境监测器,主要实现以下功能: 可实现LCD1602显示环境数据实时监测一氧化碳、甲烷、烟雾值空气质量大于各自限值报警,并通过TTS报警 标签&#x

实验一 stm32F407VETx点亮流水灯

二、设计指标 使电路板上的8个LED轮流点亮,并按键控制点亮速度。 三、操作 1、CubeMX操作 1.1依据开发板LED引脚设置CubeMX中8个LED的引脚为GPIO_Output模式, 2、按键设置

KEIL5安装C51依赖,解决KEIL5无法创建C51工程

KEIL5安装C51依赖,解决KEIL5无法创建C51工程 相信很多STM32初学者在把自己的keil4升级成keil5之后,再想创建89C51的工程却发现找不到89C51的芯片包了。今天给大家分享一下解决办法。