模块类型

1.主控STM32F103ZET6(F1系列的程序可以互用)

2.TFT3.5寸彩屏

(这个屏幕驱动的程序大部分函数是可以直接CV,但必须将初始化换函数更改为屏幕适配的类型)

程序进程

        作者采用先对信号的频率进行检测,因F1系列的ADC采集频率是72M的,额定分频数是6分频,而6分频采集到50KHz的信号时,无法支持绘画出信号的波形(一般一个周期需要采集20个点才能画出比较OK的波形图案),于是才用超频采样的方式,即,在程序中重新配置分频数,我们采用2分频的方式。

        我们采用定时器触发ADC采样,这样可以更简单的控制ADC采样频率,画出的波形也就跟好看了些。在ADC采样结束后就可以通过波形的斜率来判断是哪种波形。ADC采集后拿到的数据进行就可以用于绘图,通过对画线函数参数的调整就可以拿到我们想要的图像。

硬件配置

   1.选择芯片型号

TFT_eSPI 背光 控制 tft屏幕led背光屏_#define

2.配置SYS为Serial Wire以免芯片自锁

TFT_eSPI 背光 控制 tft屏幕led背光屏_TFT_eSPI 背光 控制_02

3.配置RCC为外部高速时钟

TFT_eSPI 背光 控制 tft屏幕led背光屏_CLR_03

4.配置ADC1的通道2做为检测信号的引脚,配置DMA做为数据存储,配置外部触发转换源选择高级定时器8

TFT_eSPI 背光 控制 tft屏幕led背光屏_#define_04

TFT_eSPI 背光 控制 tft屏幕led背光屏_stm32_05

5.配置定时器1和2搭配着做为信号频率的检测,。配置定时器8做为控制ADC采集速率的定时器,注意定时器2的Clock Source选择ETR2,这样PA0引脚可以做为检测信号频率的引脚了,并且定时器1需要打开中断功能。定时器8的触发事件选择为Update event

TFT_eSPI 背光 控制 tft屏幕led背光屏_stm32_06

TFT_eSPI 背光 控制 tft屏幕led背光屏_TFT_eSPI 背光 控制_07

TFT_eSPI 背光 控制 tft屏幕led背光屏_开源_08

TFT_eSPI 背光 控制 tft屏幕led背光屏_CLR_09

6.配置TFT彩屏驱动的软件SPI,这里我们选用SPI2做为MCU与TFT传输数据的通信协议

TFT_eSPI 背光 控制 tft屏幕led背光屏_CLR_10

7,到了最后一步咯,有没有认真去选配置啊!!!最后我们配置TFT彩屏的引脚,其中LED是屏幕的背光引脚,作者建议直接接3.3V即可,这样可以减少GPIO的使用。这里大家就直接配置吧,相信大家有这个能力,棒棒棒!

TFT_eSPI 背光 控制 tft屏幕led背光屏_stm32_11

8.配置时钟树

TFT_eSPI 背光 控制 tft屏幕led背光屏_TFT_eSPI 背光 控制_12

9.最后就是工程的建立啦

TFT_eSPI 背光 控制 tft屏幕led背光屏_#define_13

TFT_eSPI 背光 控制 tft屏幕led背光屏_#define_14

软件编写

这里我就强调两点,一则:就是我们需要增加一个SPI传输数据的函数,二则:就是TFT屏幕由于型号不同,对应的LCD初始化函数不同(由于作者在这里栽跟头两三次,希望大家不用忘记更改哦)。而OSC的代码,LCD的代码,GUI的代码,只要是跟着这个文章一起完成的工程,都是可以直接CV的。

LCD.H

/****************************************************************************************************
//=========================================电源接线================================================//
//     LCD模块                STM32单片机
//      VCC          接        DC5V/3.3V      //电源
//      GND          接          GND          //电源地
//=======================================液晶屏数据线接线==========================================//
//本模块默认数据总线类型为SPI总线
//     LCD模块                STM32单片机    
//    SDI(MOSI)      接          PB15         //液晶屏SPI总线数据写信号
//    SDO(MISO)      接          PB14         //液晶屏SPI总线数据读信号,如果不需要读,可以不接线
//=======================================液晶屏控制线接线==========================================//
//     LCD模块 					      STM32单片机 
//       LED         接          PB9          //液晶屏背光控制信号,如果不需要控制,接5V或3.3V
//       SCK         接          PB13         //液晶屏SPI总线时钟信号
//      DC/RS        接          PB10         //液晶屏数据/命令控制信号
//       RST         接          PB12         //液晶屏复位控制信号
//       CS          接          PB11         //液晶屏片选控制信号
**************************************************************************************************/	
 /* @attention
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, QD electronic SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
**************************************************************************************************/	
#ifndef __LCD_H
#define __LCD_H		
#include "main.h"	 
#include "stdlib.h"

//LCD重要参数集
typedef struct  
{										    
	uint16_t width;			//LCD 宽度
	uint16_t height;			//LCD 高度
	uint16_t id;				  //LCD ID
 	uint8_t  dir;			  //横屏还是竖屏控制:0,竖屏;1,横屏。	
	uint16_t	 wramcmd;		//开始写gram指令
	uint16_t  setxcmd;		//设置x坐标指令
	uint16_t  setycmd;		//设置y坐标指令	 
}_lcd_dev; 	

//LCD参数
extern _lcd_dev lcddev;	//管理LCD重要参数
/用户配置区///	 
#define USE_HORIZONTAL  	 1//定义液晶屏顺时针旋转方向 	0-0度旋转,1-90度旋转,2-180度旋转,3-270度旋转

//	  
//定义LCD的尺寸
#define LCD_W 320
#define LCD_H 480

//TFTLCD部分外要调用的函数		   
extern uint16_t  POINT_COLOR;//默认红色    
extern uint16_t  BACK_COLOR; //背景颜色.默认为白色


//-----------------LCD端口定义---------------- 
#define GPIO_TYPE  GPIOB  //GPIO组类型
#define LED      9        //背光控制引脚        PB9
#define LCD_CS   11       //片选引脚            PB11
#define LCD_RS   10       //寄存器/数据选择引脚 PB10 
#define LCD_RST  12       //复位引脚            PB12


//QDtech全系列模块采用了三极管控制背光亮灭,用户也可以接PWM调节背光亮度
#define	LCD_LED_SET  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET)
#define	LCD_LED_CLR  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET)
//如果使用官方库函数定义下列底层,速度将会下降到14帧每秒,建议采用我司推荐方法
//以下IO定义直接操作寄存器,快速IO操作,刷屏速率可以达到28帧每秒! 

//GPIO置位(拉高)
#define	LCD_CS_SET  GPIO_TYPE->BSRR=1<<LCD_CS    //片选端口  	PB11
#define	LCD_RS_SET	GPIO_TYPE->BSRR=1<<LCD_RS    //数据/命令  PB10	  
#define	LCD_RST_SET	GPIO_TYPE->BSRR=1<<LCD_RST   //复位			  PB12

//GPIO复位(拉低)							    
#define	LCD_CS_CLR  GPIO_TYPE->BRR=1<<LCD_CS     //片选端口  	PB11
#define	LCD_RS_CLR	GPIO_TYPE->BRR=1<<LCD_RS     //数据/命令  PB10	 
#define	LCD_RST_CLR	GPIO_TYPE->BRR=1<<LCD_RST    //复位			  PB12

//画笔颜色
#define WHITE       0xFFFF
#define BLACK      	0x0000	  
#define BLUE       	0x001F  
#define BRED        0XF81F
#define GRED 			 	0XFFE0
#define GBLUE			 	0X07FF
#define RED         0xF800
#define MAGENTA     0xF81F
#define GREEN       0x07E0
#define CYAN        0x7FFF
#define YELLOW      0xFFE0
#define BROWN 			0XBC40 //棕色
#define BRRED 			0XFC07 //棕红色
#define GRAY  			0X8430 //灰色
//GUI颜色

#define DARKBLUE      	 0X01CF	//深蓝色
#define LIGHTBLUE      	 0X7D7C	//浅蓝色  
#define GRAYBLUE       	 0X5458 //灰蓝色
//以上三色为PANEL的颜色 
 
#define LIGHTGREEN     	0X841F //浅绿色
#define LIGHTGRAY     0XEF5B //浅灰色(PANNEL)
#define LGRAY 			 		0XC618 //浅灰色(PANNEL),窗体背景色

#define LGRAYBLUE      	0XA651 //浅灰蓝色(中间层颜色)
#define LBBLUE          0X2B12 //浅棕蓝色(选择条目的反色)
	    															  
void LCD_Init(void);//初始化
void LCD_DisplayOn(void);
void LCD_DisplayOff(void);
void LCD_Clear(uint16_t Color);	 //清屏
void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos);//设置光标
void LCD_DrawPoint(uint16_t x,uint16_t y);//画点
uint16_t  LCD_ReadPoint(uint16_t x,uint16_t y); //读点
void LCD_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);//画线
void LCD_DrawRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);	//画矩形	   
void LCD_SetWindows(uint16_t xStar, uint16_t yStar,uint16_t xEnd,uint16_t yEnd);//设置窗口

uint16_t LCD_RD_DATA(void);//读取LCD数据									    
void LCD_WriteReg(uint8_t LCD_Reg, uint16_t LCD_RegValue);
void LCD_WR_DATA(uint8_t data);
uint16_t LCD_ReadReg(uint8_t LCD_Reg);
void LCD_WriteRAM_Prepare(void);
void LCD_WriteRAM(uint16_t RGB_Code);
uint16_t LCD_ReadRAM(void);		   
uint16_t LCD_BGR2RGB(uint16_t c);
void LCD_SetParam(void);
void Lcd_WriteData_16Bit(uint16_t Data);
void LCD_direction(uint8_t direction );

//如果仍然觉得速度不够快,可以使用下面的宏定义,提高速度.
//注意要去掉lcd.c中void LCD_WR_DATA(uint16_t data)函数定义哦
/*
#if LCD_USE8BIT_MODEL==1//使用8位并行数据总线模式
	#define LCD_WR_DATA(data){\
	LCD_RS_SET;\
	LCD_CS_CLR;\
	DATAOUT(data);\
	LCD_WR_CLR;\
	LCD_WR_SET;\
	DATAOUT(data<<8);\
	LCD_WR_CLR;\
	LCD_WR_SET;\
	LCD_CS_SET;\
	}
	#else//使用16位并行数据总线模式
	#define LCD_WR_DATA(data){\
	LCD_RS_SET;\
	LCD_CS_CLR;\
	DATAOUT(data);\
	LCD_WR_CLR;\
	LCD_WR_SET;\
	LCD_CS_SET;\
	} 	
#endif
*/
				  		 
#endif

LCD.C

#include "lcd.h"
#include "stdlib.h"
//#include "delay.h"	 
#include "SPI.h"
void  SPIv_WriteData(uint8_t Data)//SPI数据传输函数
{
	SPI_WriteByte(&Data, 1);
}
	   
//管理LCD重要参数
//默认为竖屏
_lcd_dev lcddev;

//画笔颜色,背景颜色
uint16_t POINT_COLOR = 0x0000,BACK_COLOR = 0xFFFF;  
uint16_t DeviceCode;	 

/*****************************************************************************
 * @name       :void LCD_WR_REG(uint8_t data)
 * @date       :2018-08-09 
 * @function   :Write an 8-bit command to the LCD screen
 * @parameters :data:Command value to be written
 * @retvalue   :None
******************************************************************************/
//写一个8字节的指令到屏幕
void LCD_WR_REG(uint8_t data)
{ 
   LCD_CS_CLR;     
	 LCD_RS_CLR;	  
   SPIv_WriteData(data);
   LCD_CS_SET;	
}

/*****************************************************************************
 * @name       :void LCD_WR_DATA(uint8_t data)
 * @date       :2018-08-09 
 * @function   :Write an 8-bit data to the LCD screen
 * @parameters :data:data value to be written
 * @retvalue   :None
******************************************************************************/
//写一个8字节的数据到屏幕
void LCD_WR_DATA(uint8_t data)
{
   LCD_CS_CLR;
	 LCD_RS_SET;
   SPIv_WriteData(data);
   LCD_CS_SET;
}

/*****************************************************************************
 * @name       :void LCD_WriteReg(uint8_t LCD_Reg, uint16_t LCD_RegValue)
 * @date       :2018-08-09 
 * @function   :Write data into registers
 * @parameters :LCD_Reg:Register address
                LCD_RegValue:Data to be written
 * @retvalue   :None
******************************************************************************/
//写数据到寄存器
void LCD_WriteReg(uint8_t LCD_Reg, uint16_t LCD_RegValue)
{	
	LCD_WR_REG(LCD_Reg);  
	LCD_WR_DATA(LCD_RegValue);	    		 
}	   

/*****************************************************************************
 * @name       :void LCD_WriteRAM_Prepare(void)
 * @date       :2018-08-09 
 * @function   :Write GRAM
 * @parameters :None
 * @retvalue   :None
******************************************************************************/	 

void LCD_WriteRAM_Prepare(void)
{
	LCD_WR_REG(lcddev.wramcmd);
}	 

/*****************************************************************************
 * @name       :void Lcd_WriteData_16Bit(uint16_t Data)
 * @date       :2018-08-09 
 * @function   :Write an 16-bit command to the LCD screen
 * @parameters :Data:Data to be written
 * @retvalue   :None
******************************************************************************/	
//写一个16字节的指令到屏幕
void Lcd_WriteData_16Bit(uint16_t Data)
{	
  //18Bit	
	LCD_WR_DATA((Data>>8)&0xF8);//RED
	LCD_WR_DATA((Data>>3)&0xFC);//GREEN
	LCD_WR_DATA(Data<<3);//BLUE
}

/*****************************************************************************
 * @name       :void LCD_DrawPoint(uint16_t x,uint16_t y)
 * @date       :2018-08-09 
 * @function   :Write a pixel data at a specified location
 * @parameters :x:the x coordinate of the pixel
                y:the y coordinate of the pixel
 * @retvalue   :None
******************************************************************************/
//写一个像素的数据到X,Y的位置
void LCD_DrawPoint(uint16_t x,uint16_t y)
{
	LCD_SetCursor(x,y);//设置光标位置 
	Lcd_WriteData_16Bit(POINT_COLOR); 
}

/*****************************************************************************
 * @name       :void LCD_Clear(uint16_t Color)
 * @date       :2018-08-09 
 * @function   :Full screen filled LCD screen
 * @parameters :color:Filled color
 * @retvalue   :None
******************************************************************************/	
//清屏
void LCD_Clear(uint16_t Color)
{
  unsigned int i,m;  
	LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);   
	LCD_CS_CLR;
	LCD_RS_SET;
	for(i=0;i<lcddev.height;i++)
	{
    for(m=0;m<lcddev.width;m++)
    {	
			Lcd_WriteData_16Bit(Color);
		}
	}
	 LCD_CS_SET;
} 

/*****************************************************************************
 * @name       :void LCD_RESET(void)
 * @date       :2018-08-09 
 * @function   :Reset LCD screen
 * @parameters :None
 * @retvalue   :None
******************************************************************************/	
//重置屏幕
void LCD_RESET(void)
{
	LCD_RST_CLR;
	HAL_Delay(100);	
	LCD_RST_SET;
	HAL_Delay(50);
}

/*****************************************************************************
 * @name       :void LCD_Init(void)
 * @date       :2018-08-09 
 * @function   :Initialization LCD screen
 * @parameters :None
 * @retvalue   :None
******************************************************************************/	
//屏幕初始化
void LCD_Init(void)
{  
//	LCD_GPIOInit();//LCD GPIO初始化	
 	LCD_RESET(); //LCD 复位
//************* ILI9488初始化**********//	
	LCD_WR_REG(0XF7);
	LCD_WR_DATA(0xA9);
	LCD_WR_DATA(0x51);
	LCD_WR_DATA(0x2C);
	LCD_WR_DATA(0x82);
	LCD_WR_REG(0xC0);
	LCD_WR_DATA(0x11);
	LCD_WR_DATA(0x09);
	LCD_WR_REG(0xC1);
	LCD_WR_DATA(0x41);
	LCD_WR_REG(0XC5);
	LCD_WR_DATA(0x00);
	LCD_WR_DATA(0x0A);
	LCD_WR_DATA(0x80);
	LCD_WR_REG(0xB1);
	LCD_WR_DATA(0xB0);
	LCD_WR_DATA(0x11);
	LCD_WR_REG(0xB4);
	LCD_WR_DATA(0x02);
	LCD_WR_REG(0xB6);
	LCD_WR_DATA(0x02);
	LCD_WR_DATA(0x42);
	LCD_WR_REG(0xB7);
	LCD_WR_DATA(0xc6);
	LCD_WR_REG(0xBE);
	LCD_WR_DATA(0x00);
	LCD_WR_DATA(0x04);
	LCD_WR_REG(0xE9);
	LCD_WR_DATA(0x00);
	LCD_WR_REG(0x36);
	LCD_WR_DATA((1<<3)|(0<<7)|(1<<6)|(1<<5));
	LCD_WR_REG(0x3A);
	LCD_WR_DATA(0x66);
	LCD_WR_REG(0xE0);
	LCD_WR_DATA(0x00);
	LCD_WR_DATA(0x07);
	LCD_WR_DATA(0x10);
	LCD_WR_DATA(0x09);
	LCD_WR_DATA(0x17);
	LCD_WR_DATA(0x0B);
	LCD_WR_DATA(0x41);
	LCD_WR_DATA(0x89);
	LCD_WR_DATA(0x4B);
	LCD_WR_DATA(0x0A);
	LCD_WR_DATA(0x0C);
	LCD_WR_DATA(0x0E);
	LCD_WR_DATA(0x18);
	LCD_WR_DATA(0x1B);
	LCD_WR_DATA(0x0F);
	LCD_WR_REG(0XE1);
	LCD_WR_DATA(0x00);
	LCD_WR_DATA(0x17);
	LCD_WR_DATA(0x1A);
	LCD_WR_DATA(0x04);
	LCD_WR_DATA(0x0E);
	LCD_WR_DATA(0x06);
	LCD_WR_DATA(0x2F);
	LCD_WR_DATA(0x45);
	LCD_WR_DATA(0x43);
	LCD_WR_DATA(0x02);
	LCD_WR_DATA(0x0A);
	LCD_WR_DATA(0x09);
	LCD_WR_DATA(0x32);
	LCD_WR_DATA(0x36);
	LCD_WR_DATA(0x0F);
	LCD_WR_REG(0x11);
	HAL_Delay(120);
	LCD_WR_REG(0x29);
	
  LCD_direction(USE_HORIZONTAL);//设置LCD显示方向
	LCD_LED_SET;//点亮背光	 
	LCD_Clear(WHITE);//清全屏白色
}
 
/*****************************************************************************
 * @name       :void LCD_SetWindows(uint16_t xStar, uint16_t yStar,uint16_t xEnd,uint16_t yEnd)
 * @date       :2018-08-09 
 * @function   :Setting LCD display window
 * @parameters :xStar:the bebinning x coordinate of the LCD display window
								yStar:the bebinning y coordinate of the LCD display window
								xEnd:the endning x coordinate of the LCD display window
								yEnd:the endning y coordinate of the LCD display window
 * @retvalue   :None
******************************************************************************/ 
//设置LCD显示大小x1,x2为长,y1,y2为宽
void LCD_SetWindows(uint16_t xStar, uint16_t yStar,uint16_t xEnd,uint16_t yEnd)
{	
	LCD_WR_REG(lcddev.setxcmd);	
	LCD_WR_DATA(xStar>>8);
	LCD_WR_DATA(0x00FF&xStar);		
	LCD_WR_DATA(xEnd>>8);
	LCD_WR_DATA(0x00FF&xEnd);

	LCD_WR_REG(lcddev.setycmd);	
	LCD_WR_DATA(yStar>>8);
	LCD_WR_DATA(0x00FF&yStar);		
	LCD_WR_DATA(yEnd>>8);
	LCD_WR_DATA(0x00FF&yEnd);

	LCD_WriteRAM_Prepare();	//开始写入GRAM			
}   

/*****************************************************************************
 * @name       :void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos)
 * @date       :2018-08-09 
 * @function   :Set coordinate value
 * @parameters :Xpos:the  x coordinate of the pixel
								Ypos:the  y coordinate of the pixel
 * @retvalue   :None
******************************************************************************/ 
//设置坐标值
void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos)
{	  	    			
	LCD_SetWindows(Xpos,Ypos,Xpos,Ypos);	
} 

/*****************************************************************************
 * @name       :void LCD_direction(uint8_t direction)
 * @date       :2018-08-09 
 * @function   :Setting the display direction of LCD screen
 * @parameters :direction:0-0 degree
                          1-90 degree
													2-180 degree
													3-270 degree
 * @retvalue   :None
******************************************************************************/ 
//设置LCD屏幕显示方向
void LCD_direction(uint8_t direction)
{ 
			lcddev.setxcmd=0x2A;
			lcddev.setycmd=0x2B;
			lcddev.wramcmd=0x2C;
	switch(direction){		  
		case 0:						 	 		
			lcddev.width=LCD_W;
			lcddev.height=LCD_H;		
			LCD_WriteReg(0x36,(1<<3)|(0<<6)|(0<<7));//BGR==1,MY==0,MX==0,MV==0
		break;
		case 1:
			lcddev.width=LCD_H;
			lcddev.height=LCD_W;
			LCD_WriteReg(0x36,(1<<3)|(0<<7)|(1<<6)|(1<<5));//BGR==1,MY==1,MX==0,MV==1
		break;
		case 2:						 	 		
			lcddev.width=LCD_W;
			lcddev.height=LCD_H;	
			LCD_WriteReg(0x36,(1<<3)|(1<<6)|(1<<7));//BGR==1,MY==0,MX==0,MV==0
		break;
		case 3:
			lcddev.width=LCD_H;
			lcddev.height=LCD_W;
			LCD_WriteReg(0x36,(1<<3)|(1<<7)|(1<<5));//BGR==1,MY==1,MX==0,MV==1
		break;	
		default:break;
	}		
}

GUI.H

/****************************************************************************************************
//=========================================电源接线================================================//
//     LCD模块                STM32单片机
//      VCC          接        DC5V/3.3V      //电源
//      GND          接          GND          //电源地
//=======================================液晶屏数据线接线==========================================//
//本模块默认数据总线类型为SPI总线
//     LCD模块                STM32单片机    
//    SDI(MOSI)      接          PB15         //液晶屏SPI总线数据写信号
//    SDO(MISO)      接          PB14         //液晶屏SPI总线数据读信号,如果不需要读,可以不接线
//=======================================液晶屏控制线接线==========================================//
//     LCD模块 					      STM32单片机 
//       LED         接          PB9          //液晶屏背光控制信号,如果不需要控制,接5V或3.3V
//       SCK         接          PB13         //液晶屏SPI总线时钟信号
//      DC/RS        接          PB10         //液晶屏数据/命令控制信号
//       RST         接          PB12         //液晶屏复位控制信号
//       CS          接          PB11         //液晶屏片选控制信号

**************************************************************************************************/	
 /* @attention
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, QD electronic SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
**************************************************************************************************/	
#ifndef __GUI_H__
#define __GUI_H__

void GUI_DrawPoint(uint16_t x,uint16_t y,uint16_t color);
void LCD_Fill(uint16_t sx,uint16_t sy,uint16_t ex,uint16_t ey,uint16_t color);
void LCD_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
void LCD_DrawRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
void Draw_Circle(uint16_t x0,uint16_t y0,uint16_t fc,uint8_t r);
void Draw_Triangel(uint16_t x0,uint16_t y0,uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2);
void Fill_Triangel(uint16_t x0,uint16_t y0,uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2);
void LCD_ShowChar(uint16_t x,uint16_t y,uint16_t fc, uint16_t bc, uint8_t num,uint8_t size,uint8_t mode);
void LCD_ShowNum(uint16_t x,uint16_t y,uint32_t num,uint8_t len,uint8_t size);
void LCD_Show2Num(uint16_t x,uint16_t y,uint16_t num,uint8_t len,uint8_t size,uint8_t mode);
void LCD_ShowString(uint16_t x,uint16_t y,uint8_t size,uint8_t *p,uint8_t mode);
void GUI_DrawFont16(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *s,uint8_t mode);
void GUI_DrawFont24(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *s,uint8_t mode);
void GUI_DrawFont32(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *s,uint8_t mode);
void Show_Str(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *str,uint8_t size,uint8_t mode);
void Gui_Drawbmp16(uint16_t x,uint16_t y,const unsigned char *p); //显示40*40 QQ图片
void gui_circle(int xc, int yc,uint16_t c,int r, int fill);
void Gui_StrCenter(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *str,uint8_t size,uint8_t mode);
void LCD_DrawFillRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
#endif

GUI.C

#include "lcd.h"
#include "string.h"
#include "FONT.h"//文字取模存放的位置 
//#include "delay.h"
#include "gui.h"

/*******************************************************************
 * @name       :void GUI_DrawPoint(uint16_t x,uint16_t y,uint16_t color)
 * @date       :2018-08-09 
 * @function   :draw a point in LCD screen
 * @parameters :x:the x coordinate of the point
                y:the y coordinate of the point
								color:the color value of the point
 * @retvalue   :None
********************************************************************/
//画一个点在LCD上
void GUI_DrawPoint(uint16_t x,uint16_t y,uint16_t color)
{
	LCD_SetCursor(x,y);//设置光标位置 
	Lcd_WriteData_16Bit(color); 
}

/*******************************************************************
 * @name       :void LCD_Fill(uint16_t sx,uint16_t sy,uint16_t ex,uint16_t ey,uint16_t color)
 * @date       :2018-08-09 
 * @function   :fill the specified area
 * @parameters :sx:the bebinning x coordinate of the specified area
                sy:the bebinning y coordinate of the specified area
								ex:the ending x coordinate of the specified area
								ey:the ending y coordinate of the specified area
								color:the filled color value
 * @retvalue   :None
********************************************************************/
//填充指定区域
void LCD_Fill(uint16_t sx,uint16_t sy,uint16_t ex,uint16_t ey,uint16_t color)
{  	
	uint16_t i,j;			
	uint16_t width=ex-sx+1; 		//得到填充的宽度
	uint16_t height=ey-sy+1;		//高度
	LCD_SetWindows(sx,sy,ex,ey);//设置显示窗口
	for(i=0;i<height;i++)
	{
		for(j=0;j<width;j++)
		Lcd_WriteData_16Bit(color);	//写入数据 	 
	}
	LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);//恢复窗口设置为全屏
}

/*******************************************************************
 * @name       :void LCD_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
 * @date       :2018-08-09 
 * @function   :Draw a line between two points
 * @parameters :x1:the bebinning x coordinate of the line
                y1:the bebinning y coordinate of the line
								x2:the ending x coordinate of the line
								y2:the ending y coordinate of the line
 * @retvalue   :None
********************************************************************/
//画两点之间的一条线
void LCD_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
	uint16_t t; 
	int xerr=0,yerr=0,delta_x,delta_y,distance; 
	int incx,incy,uRow,uCol; 

	delta_x=x2-x1; //计算坐标增量 
	delta_y=y2-y1; 
	uRow=x1; 
	uCol=y1; 
	if(delta_x>0)incx=1; //设置单步方向 
	else if(delta_x==0)incx=0;//垂直线 
	else {incx=-1;delta_x=-delta_x;} 
	if(delta_y>0)incy=1; 
	else if(delta_y==0)incy=0;//水平线 
	else{incy=-1;delta_y=-delta_y;} 
	if( delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴 
	else distance=delta_y; 
	for(t=0;t<=distance+1;t++ )//画线输出 
	{  
		LCD_DrawPoint(uRow,uCol);//画点 
		xerr+=delta_x ; 
		yerr+=delta_y ; 
		if(xerr>distance) 
		{ 
			xerr-=distance; 
			uRow+=incx; 
		} 
		if(yerr>distance) 
		{ 
			yerr-=distance; 
			uCol+=incy; 
		} 
	}  
} 

/*****************************************************************************
 * @name       :void LCD_DrawRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
 * @date       :2018-08-09 
 * @function   :Draw a rectangle
 * @parameters :x1:the bebinning x coordinate of the rectangle
                y1:the bebinning y coordinate of the rectangle
								x2:the ending x coordinate of the rectangle
								y2:the ending y coordinate of the rectangle
 * @retvalue   :None
******************************************************************************/
//画一个长方形
void LCD_DrawRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
	LCD_DrawLine(x1,y1,x2,y1);
	LCD_DrawLine(x1,y1,x1,y2);
	LCD_DrawLine(x1,y2,x2,y2);
	LCD_DrawLine(x2,y1,x2,y2);
}  

/*****************************************************************************
 * @name       :void LCD_DrawFillRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
 * @date       :2018-08-09 
 * @function   :Filled a rectangle
 * @parameters :x1:the bebinning x coordinate of the filled rectangle
                y1:the bebinning y coordinate of the filled rectangle
								x2:the ending x coordinate of the filled rectangle
								y2:the ending y coordinate of the filled rectangle
 * @retvalue   :None
******************************************************************************/  
//填充矩形
void LCD_DrawFillRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
	LCD_Fill(x1,y1,x2,y2,POINT_COLOR);
}
 
/*****************************************************************************
 * @name       :void _draw_circle_8(int xc, int yc, int x, int y, uint16_t c)
 * @date       :2018-08-09 
 * @function   :8 symmetry circle drawing algorithm (internal call)
 * @parameters :xc:the x coordinate of the Circular center 
                yc:the y coordinate of the Circular center 
								x:the x coordinate relative to the Circular center 
								y:the y coordinate relative to the Circular center 
								c:the color value of the circle
 * @retvalue   :None
******************************************************************************/ 
//画圆圈
void _draw_circle_8(int xc, int yc, int x, int y, uint16_t c)
{
	GUI_DrawPoint(xc + x, yc + y, c);

	GUI_DrawPoint(xc - x, yc + y, c);

	GUI_DrawPoint(xc + x, yc - y, c);

	GUI_DrawPoint(xc - x, yc - y, c);

	GUI_DrawPoint(xc + y, yc + x, c);

	GUI_DrawPoint(xc - y, yc + x, c);

	GUI_DrawPoint(xc + y, yc - x, c);

	GUI_DrawPoint(xc - y, yc - x, c);
}

/*****************************************************************************
 * @name       :void gui_circle(int xc, int yc,uint16_t c,int r, int fill)
 * @date       :2018-08-09 
 * @function   :Draw a circle of specified size at a specified location
 * @parameters :xc:the x coordinate of the Circular center 
                yc:the y coordinate of the Circular center 
								r:Circular radius
								fill:1-filling,0-no filling
 * @retvalue   :None
******************************************************************************/  
//在指定的位置画一个指定大小的圆
void gui_circle(int xc, int yc,uint16_t c,int r, int fill)
{
	int x = 0, y = r, yi, d;

	d = 3 - 2 * r;


	if (fill) 
	{
		// 如果填充(画实心圆)
		while (x <= y) {
			for (yi = x; yi <= y; yi++)
				_draw_circle_8(xc, yc, x, yi, c);

			if (d < 0) {
				d = d + 4 * x + 6;
			} else {
				d = d + 4 * (x - y) + 10;
				y--;
			}
			x++;
		}
	} else 
	{
		// 如果不填充(画空心圆)
		while (x <= y) {
			_draw_circle_8(xc, yc, x, y, c);
			if (d < 0) {
				d = d + 4 * x + 6;
			} else {
				d = d + 4 * (x - y) + 10;
				y--;
			}
			x++;
		}
	}
}

/*****************************************************************************
 * @name       :void Draw_Triangel(uint16_t x0,uint16_t y0,uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2)
 * @date       :2018-08-09 
 * @function   :Draw a triangle at a specified position
 * @parameters :x0:the bebinning x coordinate of the triangular edge 
                y0:the bebinning y coordinate of the triangular edge 
								x1:the vertex x coordinate of the triangular
								y1:the vertex y coordinate of the triangular
								x2:the ending x coordinate of the triangular edge 
								y2:the ending y coordinate of the triangular edge 
 * @retvalue   :None
******************************************************************************/ 
//在指定位置画一个三角形
void Draw_Triangel(uint16_t x0,uint16_t y0,uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2)
{
	LCD_DrawLine(x0,y0,x1,y1);
	LCD_DrawLine(x1,y1,x2,y2);
	LCD_DrawLine(x2,y2,x0,y0);
}

static void _swap(uint16_t *a, uint16_t *b)
{
	uint16_t tmp;
  tmp = *a;
	*a = *b;
	*b = tmp;
}

/*****************************************************************************
 * @name       :void Fill_Triangel(uint16_t x0,uint16_t y0,uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2)
 * @date       :2018-08-09 
 * @function   :filling a triangle at a specified position
 * @parameters :x0:the bebinning x coordinate of the triangular edge 
                y0:the bebinning y coordinate of the triangular edge 
								x1:the vertex x coordinate of the triangular
								y1:the vertex y coordinate of the triangular
								x2:the ending x coordinate of the triangular edge 
								y2:the ending y coordinate of the triangular edge 
 * @retvalue   :None
******************************************************************************/ 
//在指定位置填充三角形
void Fill_Triangel(uint16_t x0,uint16_t y0,uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2)
{
	uint16_t a, b, y, last;
	int dx01, dy01, dx02, dy02, dx12, dy12;
	long sa = 0;
	long sb = 0;
 	if (y0 > y1) 
	{
    _swap(&y0,&y1); 
		_swap(&x0,&x1);
 	}
 	if (y1 > y2) 
	{
    _swap(&y2,&y1); 
		_swap(&x2,&x1);
 	}
  if (y0 > y1) 
	{
    _swap(&y0,&y1); 
		_swap(&x0,&x1);
  }
	if(y0 == y2) 
	{ 
		a = b = x0;
		if(x1 < a)
    {
			a = x1;
    }
    else if(x1 > b)
    {
			b = x1;
    }
    if(x2 < a)
    {
			a = x2;
    }
		else if(x2 > b)
    {
			b = x2;
    }
		LCD_Fill(a,y0,b,y0,POINT_COLOR);
    return;
	}
	dx01 = x1 - x0;
	dy01 = y1 - y0;
	dx02 = x2 - x0;
	dy02 = y2 - y0;
	dx12 = x2 - x1;
	dy12 = y2 - y1;
	
	if(y1 == y2)
	{
		last = y1; 
	}
  else
	{
		last = y1-1; 
	}
	for(y=y0; y<=last; y++) 
	{
		a = x0 + sa / dy01;
		b = x0 + sb / dy02;
		sa += dx01;
    sb += dx02;
    if(a > b)
    {
			_swap(&a,&b);
		}
		LCD_Fill(a,y,b,y,POINT_COLOR);
	}
	sa = dx12 * (y - y1);
	sb = dx02 * (y - y0);
	for(; y<=y2; y++) 
	{
		a = x1 + sa / dy12;
		b = x0 + sb / dy02;
		sa += dx12;
		sb += dx02;
		if(a > b)
		{
			_swap(&a,&b);
		}
		LCD_Fill(a,y,b,y,POINT_COLOR);
	}
}

/*****************************************************************************
 * @name       :void LCD_ShowChar(uint16_t x,uint16_t y,uint16_t fc, uint16_t bc, uint8_t num,uint8_t size,uint8_t mode)
 * @date       :2018-08-09 
 * @function   :Display a single English character
 * @parameters :x:the bebinning x coordinate of the Character display position
                y:the bebinning y coordinate of the Character display position
								fc:the color value of display character
								bc:the background color of display character
								num:the ascii code of display character(0~94)
								size:the size of display character
								mode:0-no overlying,1-overlying
 * @retvalue   :None
******************************************************************************/ 
//显示一个英文字符
void LCD_ShowChar(uint16_t x,uint16_t y,uint16_t fc, uint16_t bc, uint8_t num,uint8_t size,uint8_t mode)
{  
    uint8_t temp;
    uint8_t pos,t;
	uint16_t colortemp=POINT_COLOR;      
		   
	num=num-' ';//得到偏移后的值
	LCD_SetWindows(x,y,x+size/2-1,y+size-1);//设置单个文字显示窗口
	if(!mode) //非叠加方式
	{		
		for(pos=0;pos<size;pos++)
		{
			if(size==12)temp=asc2_1206[num][pos];//调用1206字体
			else temp=asc2_1608[num][pos];		 //调用1608字体
			for(t=0;t<size/2;t++)
		    {                 
		        if(temp&0x01)Lcd_WriteData_16Bit(fc); 
				else Lcd_WriteData_16Bit(bc); 
				temp>>=1; 
				
		    }
			
		}	
	}else//叠加方式
	{
		for(pos=0;pos<size;pos++)
		{
			if(size==12)temp=asc2_1206[num][pos];//调用1206字体
			else temp=asc2_1608[num][pos];		 //调用1608字体
			for(t=0;t<size/2;t++)
		    {   
				POINT_COLOR=fc;              
		        if(temp&0x01)LCD_DrawPoint(x+t,y+pos);//画一个点    
		        temp>>=1; 
		    }
		}
	}
	POINT_COLOR=colortemp;	
	LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);//恢复窗口为全屏    	   	 	  
}

/*****************************************************************************
 * @name       :void LCD_ShowString(uint16_t x,uint16_t y,uint8_t size,uint8_t *p,uint8_t mode)
 * @date       :2018-08-09 
 * @function   :Display English string
 * @parameters :x:the bebinning x coordinate of the English string
                y:the bebinning y coordinate of the English string
								p:the start address of the English string
								size:the size of display character
								mode:0-no overlying,1-overlying
 * @retvalue   :None
******************************************************************************/  
//显示字符串
void LCD_ShowString(uint16_t x,uint16_t y,uint8_t size,uint8_t *p,uint8_t mode)
{         
    while((*p<='~')&&(*p>=' '))//判断是不是非法字符!
    {   
		if(x>(lcddev.width-1)||y>(lcddev.height-1)) 
		return;     
        LCD_ShowChar(x,y,POINT_COLOR,BACK_COLOR,*p,size,mode);
        x+=size/2;
        p++;
    }  
} 

/*****************************************************************************
 * @name       :uint32_t mypow(uint8_t m,uint8_t n)
 * @date       :2018-08-09 
 * @function   :get the nth power of m (internal call)
 * @parameters :m:the multiplier
                n:the power
 * @retvalue   :the nth power of m
******************************************************************************/ 
uint32_t mypow(uint8_t m,uint8_t n)
{
	uint32_t result=1;	 
	while(n--)result*=m;    
	return result;
}

/*****************************************************************************
 * @name       :void LCD_ShowNum(uint16_t x,uint16_t y,uint32_t num,uint8_t len,uint8_t size)
 * @date       :2018-08-09 
 * @function   :Display number
 * @parameters :x:the bebinning x coordinate of the number
                y:the bebinning y coordinate of the number
								num:the number(0~4294967295)
								len:the length of the display number
								size:the size of display number
 * @retvalue   :None
******************************************************************************/  
//显示数字
void LCD_ShowNum(uint16_t x,uint16_t y,uint32_t num,uint8_t len,uint8_t size)
{         	
	uint8_t t,temp;
	uint8_t enshow=0;						   
	for(t=0;t<len;t++)
	{
		temp=(num/mypow(10,len-t-1))%10;
		if(enshow==0&&t<(len-1))
		{
			if(temp==0)
			{
				LCD_ShowChar(x+(size/2)*t,y,POINT_COLOR,BACK_COLOR,' ',size,0);
				continue;
			}else enshow=1; 
		 	 
		}
	 	LCD_ShowChar(x+(size/2)*t,y,POINT_COLOR,BACK_COLOR,temp+'0',size,0); 
	}
} 

/*****************************************************************************
 * @name       :void GUI_DrawFont16(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *s,uint8_t mode)
 * @date       :2018-08-09 
 * @function   :Display a single 16x16 Chinese character
 * @parameters :x:the bebinning x coordinate of the Chinese character
                y:the bebinning y coordinate of the Chinese character
								fc:the color value of Chinese character
								bc:the background color of Chinese character
								s:the start address of the Chinese character
								mode:0-no overlying,1-overlying
 * @retvalue   :None
******************************************************************************/
//显示一个16*16大小的中文
void GUI_DrawFont16(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *s,uint8_t mode)
{
	uint8_t i,j;
	uint16_t k;
	uint16_t HZnum;
	uint16_t x0=x;
	HZnum=sizeof(tfont16)/sizeof(typFNT_GB16);	//自动统计汉字数目
	
			
	for (k=0;k<HZnum;k++) 
	{
	  if ((tfont16[k].Index[0]==*(s))&&(tfont16[k].Index[1]==*(s+1)))
	  { 	LCD_SetWindows(x,y,x+16-1,y+16-1);
		    for(i=0;i<16*2;i++)
		    {
				for(j=0;j<8;j++)
		    	{	
					if(!mode) //非叠加方式
					{
						if(tfont16[k].Msk[i]&(0x80>>j))	Lcd_WriteData_16Bit(fc);
						else Lcd_WriteData_16Bit(bc);
					}
					else
					{
						POINT_COLOR=fc;
						if(tfont16[k].Msk[i]&(0x80>>j))	LCD_DrawPoint(x,y);//画一个点
						x++;
						if((x-x0)==16)
						{
							x=x0;
							y++;
							break;
						}
					}

				}
				
			}
			
			
		}				  	
		continue;  //查找到对应点阵字库立即退出,防止多个汉字重复取模带来影响
	}

	LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);//恢复窗口为全屏  
} 

/*****************************************************************************
 * @name       :void GUI_DrawFont24(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *s,uint8_t mode)
 * @date       :2018-08-09 
 * @function   :Display a single 24x24 Chinese character
 * @parameters :x:the bebinning x coordinate of the Chinese character
                y:the bebinning y coordinate of the Chinese character
								fc:the color value of Chinese character
								bc:the background color of Chinese character
								s:the start address of the Chinese character
								mode:0-no overlying,1-overlying
 * @retvalue   :None
******************************************************************************/ 
//显示一个24*24大小的中文
void GUI_DrawFont24(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *s,uint8_t mode)
{
	uint8_t i,j;
	uint16_t k;
	uint16_t HZnum;
	uint16_t x0=x;
	HZnum=sizeof(tfont24)/sizeof(typFNT_GB24);	//自动统计汉字数目
		
			for (k=0;k<HZnum;k++) 
			{
			  if ((tfont24[k].Index[0]==*(s))&&(tfont24[k].Index[1]==*(s+1)))
			  { 	LCD_SetWindows(x,y,x+24-1,y+24-1);
				    for(i=0;i<24*3;i++)
				    {
							for(j=0;j<8;j++)
							{
								if(!mode) //非叠加方式
								{
									if(tfont24[k].Msk[i]&(0x80>>j))	Lcd_WriteData_16Bit(fc);
									else Lcd_WriteData_16Bit(bc);
								}
							else
							{
								POINT_COLOR=fc;
								if(tfont24[k].Msk[i]&(0x80>>j))	LCD_DrawPoint(x,y);//画一个点
								x++;
								if((x-x0)==24)
								{
									x=x0;
									y++;
									break;
								}
							}
						}
					}
					
					
				}				  	
				continue;  //查找到对应点阵字库立即退出,防止多个汉字重复取模带来影响
			}

	LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);//恢复窗口为全屏  
}

/*****************************************************************************
 * @name       :void GUI_DrawFont32(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *s,uint8_t mode)
 * @date       :2018-08-09 
 * @function   :Display a single 32x32 Chinese character
 * @parameters :x:the bebinning x coordinate of the Chinese character
                y:the bebinning y coordinate of the Chinese character
								fc:the color value of Chinese character
								bc:the background color of Chinese character
								s:the start address of the Chinese character
								mode:0-no overlying,1-overlying
 * @retvalue   :None
******************************************************************************/ 
//显示一个32*32大小的中文
void GUI_DrawFont32(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *s,uint8_t mode)
{
	uint8_t i,j;
	uint16_t k;
	uint16_t HZnum;
	uint16_t x0=x;
	HZnum=sizeof(tfont32)/sizeof(typFNT_GB32);	//自动统计汉字数目
	for (k=0;k<HZnum;k++) 
			{
			  if ((tfont32[k].Index[0]==*(s))&&(tfont32[k].Index[1]==*(s+1)))
			  { 	LCD_SetWindows(x,y,x+32-1,y+32-1);
				    for(i=0;i<32*4;i++)
				    {
						for(j=0;j<8;j++)
				    	{
							if(!mode) //非叠加方式
							{
								if(tfont32[k].Msk[i]&(0x80>>j))	Lcd_WriteData_16Bit(fc);
								else Lcd_WriteData_16Bit(bc);
							}
							else
							{
								POINT_COLOR=fc;
								if(tfont32[k].Msk[i]&(0x80>>j))	LCD_DrawPoint(x,y);//画一个点
								x++;
								if((x-x0)==32)
								{
									x=x0;
									y++;
									break;
								}
							}
						}
					}
					
					
				}				  	
				continue;  //查找到对应点阵字库立即退出,防止多个汉字重复取模带来影响
			}
	
	LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);//恢复窗口为全屏  
} 

/*****************************************************************************
 * @name       :void Show_Str(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *str,uint8_t size,uint8_t mode)
 * @date       :2018-08-09 
 * @function   :Display Chinese and English strings
 * @parameters :x:the bebinning x coordinate of the Chinese and English strings
                y:the bebinning y coordinate of the Chinese and English strings
								fc:the color value of Chinese and English strings
								bc:the background color of Chinese and English strings
								str:the start address of the Chinese and English strings
								size:the size of Chinese and English strings
								mode:0-no overlying,1-overlying
 * @retvalue   :None
******************************************************************************/	
//显示中午或英文字符串
void Show_Str(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *str,uint8_t size,uint8_t mode)
{					
	uint16_t x0=x;							  	  
  	uint8_t bHz=0;     //字符或者中文 
    while(*str!=0)//数据未结束
    { 
        if(!bHz)
        {
			if(x>(lcddev.width-size/2)||y>(lcddev.height-size)) 
			return; 
	        if(*str>0x80)bHz=1;//中文 
	        else              //字符
	        {          
		        if(*str==0x0D)//换行符号
		        {         
		            y+=size;
					x=x0;
		            str++; 
		        }  
		        else
				{
					if(size>16)//字库中没有集成12X24 16X32的英文字体,用8X16代替
					{  
					LCD_ShowChar(x,y,fc,bc,*str,16,mode);
					x+=8; //字符,为全字的一半 
					}
					else
					{
					LCD_ShowChar(x,y,fc,bc,*str,size,mode);
					x+=size/2; //字符,为全字的一半 
					}
				} 
				str++; 
		        
	        }
        }else//中文 
        {   
			if(x>(lcddev.width-size)||y>(lcddev.height-size)) 
			return;  
            bHz=0;//有汉字库    
			if(size==32)
			GUI_DrawFont32(x,y,fc,bc,str,mode);	 	
			else if(size==24)
			GUI_DrawFont24(x,y,fc,bc,str,mode);	
			else
			GUI_DrawFont16(x,y,fc,bc,str,mode);
				
	        str+=2; 
	        x+=size;//下一个汉字偏移	    
        }						 
    }   
}

/*****************************************************************************
 * @name       :void Gui_StrCenter(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *str,uint8_t size,uint8_t mode)
 * @date       :2018-08-09 
 * @function   :Centered display of English and Chinese strings
 * @parameters :x:the bebinning x coordinate of the Chinese and English strings
                y:the bebinning y coordinate of the Chinese and English strings
								fc:the color value of Chinese and English strings
								bc:the background color of Chinese and English strings
								str:the start address of the Chinese and English strings
								size:the size of Chinese and English strings
								mode:0-no overlying,1-overlying
 * @retvalue   :None
******************************************************************************/ 
//英文和中文字符串居中显示
void Gui_StrCenter(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *str,uint8_t size,uint8_t mode)
{
	uint16_t len=strlen((const char *)str);
	uint16_t x1=(lcddev.width-len*8)/2;
	Show_Str(x1,y,fc,bc,str,size,mode);
} 
 
/*****************************************************************************
 * @name       :void Gui_Drawbmp16(uint16_t x,uint16_t y,const unsigned char *p)
 * @date       :2018-08-09 
 * @function   :Display a 16-bit BMP image
 * @parameters :x:the bebinning x coordinate of the BMP image
                y:the bebinning y coordinate of the BMP image
								p:the start address of image array
 * @retvalue   :None
******************************************************************************/ 
//显示40*40大小的图片
void Gui_Drawbmp16(uint16_t x,uint16_t y,const unsigned char *p) //显示40*40 QQ图片
{
  	int i; 
	unsigned char picH,picL; 
	LCD_SetWindows(x,y,x+40-1,y+40-1);//窗口设置
    for(i=0;i<40*40;i++)
	{	
	 	picL=*(p+i*2);	//数据低位在前
		picH=*(p+i*2+1);				
		Lcd_WriteData_16Bit(picH<<8|picL);  						
	}	
	LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);//恢复显示窗口为全屏	

}


#ifndef __OSC_H
#define __OSC_H



extern uint8_t oscState;
void DELAY(unsigned t);
void setAdcFrequency(uint8_t t);
void setWaveEnlarge(uint8_t inc);
void setWaveOffset(uint8_t inc);
void Scan_Keys(void);
void OSC_Init(void);
void updateWaveFrequency(void);
void OSC_ShowWave(void);
void OSC_ShowInfo(void);
void control(uint16_t con1);
void ADC1_process(void);

#endif

OSC.C

#include "stdint.h"	// uint16_t 定义
#include "gui.h"	// gui绘制
#include "lcd.h"	// 颜色定义
#include "main.h"	//  LED定义 中断引脚定义
#include "adc.h"	// 
#include "tim.h"
#include "OSC.h"
#include <stdio.h>

//用于整合频率值
uint16_t adc1[10];
unsigned int  adc_val=0;  //ADC1实际电压值
uint16_t ADC_value;          //模拟值adc1DMA

/* 全局变量 */
uint8_t oscState 		= 	0;		// 0:未就绪	1:运行中 在主函数初始化中改变

/* 波形显示相关		arr:一个格子的时间长度;enlarge调整高度;offset波形左右移动距离 */
uint16_t numF 			=	1;	 	// 第几个等级触发频率
uint32_t ARR			= 	0;		// ADC触发频率,(72M / 36)/ ARR
uint8_t enlarge			=	1;		// 波形放大倍数,默认是1的时候,屏幕刚好显示 0-4095
int8_t offset			=	0;		// 波形偏移量 用于左右移动波形

/* 被测信号的原始数据 */
uint16_t originalAdc[1024];			// ADC采集的DMA传输过来的原始数据
uint16_t numOfCollect 	=	1024;	// 采样数量
uint16_t ADC_STORE[1024];
/* 用于计算被测信号的频率 */
uint16_t adcThreshold	=	1024;	// 标记被测信号起始位置的阈值
uint16_t startPosition	=	0;		// 标记被测信号一个周期的起始位置
uint16_t endPosition	=	0;		// 标记被测信号一个周期的结束位置(也是下一个周期的开始)
 uint16_t waveFrequency	=	0;		// 被测信号的频率

/* 经过换算用于显示的数据(这里必须用有符号的,否则在限幅的时候会出问题) */
int16_t newWave[600] 	= 	{0};	// 新获取的波形数据
int16_t oldWave[600] 	= 	{0};	// 上一次显示的波形数据

/* 被测信号电压信息 */
float maxVol			=	0;		// 被测信号 最大电压
float minVol			=	0;		// 被测信号 最小电压
float difVoMaxAndMin	=	0;		// 被测信号 最大电压与最小电压的差

/* 按键监测 */
uint8_t keyNum 			=	0;		// 此时选中的按键。0:调整放大倍数; 1:调整时间长度; 2:调整左右偏移
uint8_t keyChanged		=	1;		// 按键状态,是否被按下过。如果按下过,部分内容需要重新刷新显示。

/* 增加 / 减少 */
#define INCREASE 1
#define REDUCE 0

/* 临时存储需要显示内容,无实际意义 */
uint8_t tempBuf[10];

//控制显示频率等级
void control(uint16_t con1)
{
	 numF = con1;
}

/* ===================================================== */
// 描述:设置示波器每个格子显示的时间长度(其实就是 设置ADC触发频率 / ADC的重装载值)
// 参数:arr:定时器重装载值
//			LCD把屏幕 宽度 320分为了16个格子,每个格子就是20个点。
//			以 ARR = 200举例,定时器频率 72M / 36 = 2M。
//			则 每0.1ms 产生一次中断。20个点就需要 2ms。
//		 	也就是说 一个格子的时常是 2ms。
//		 	其他数值 计算同理。
// 		 实际上,如果算上采样周期的画,每次采样需要7.5个ADC时钟周期,ADC时钟周期为12Mhz,
//		 采集20个点需要的时间为:20 * 7.5 * 1000 / 12M = 0.0125ms 
//		 太小了,所以这里忽略不计。
// 返回值:
/* ===================================================== */
void setAdcFrequency(uint8_t t)
{
    switch(t)
    {
        case 1: 
            ARR = 5;
            break;

        case 2: 
            ARR = 5;
            break;

        case 3: 
            ARR = 10;
            break;

        case 4: 
            ARR = 19;
            break;

        case 5: 
            ARR = 20;
            break;

        case 6: 
            ARR = 32;
            break;

        case 7: 
            ARR = 40;
            break;

        case 8: 
            ARR = 90;
            break;

        case 9: 
            ARR = 95;
            break;
				
				case 10:
						ARR = 110;
						break;
				
				case 11:
						ARR = 130;
						break;
				
				case 12:
						ARR = 160;
						break;
				
				case 13:
						ARR = 200;
						break;
				
				case 14:
						ARR = 255;
						break;
				
				case 15:
						ARR = 300;
						break;
				
				case 16:
						ARR = 325;
						break;
				
				case 17:
						ARR = 360;
						break;
				
				case 18:
						ARR = 390;
						break;
				
				case 19:
						ARR = 440;
						break;
				
				case 20:
						ARR = 500;
				
				case 21:
						ARR = 550;
				
				case 22:
						ARR = 650;
				
				case 23:
						ARR = 770;
				
				case 24:
						ARR = 970;
				
				case 25:
						ARR = 1270;
				
				case 26:
						ARR = 1490;
				
				case 27:
						ARR = 1900;
				
				case 28:
						ARR = 2400;
				
				case 29:
						ARR = 2800;
				
				case 30:
						ARR = 3200;
				
				case 31:
						ARR = 3900;
				
				case 32:
						ARR = 5000;
				
				case 33:
						ARR = 5400;
				
				case 34:
						ARR = 6400;
				
				case 35:
						ARR = 7300;
				
				case 36:
						ARR = 9000;
				
				case 37:
						ARR = 13000;
				
				case 38:
						ARR = 20000;
				
				case 39:
						ARR = 40000;
						break;
    }
	
    TIM8->ARR = ARR - 1;
}



/* ===================================================== */
// 描述:示波器初始化
// 参数:
// 返回值:
/* ===================================================== */
void OSC_Init(void)
{
	POINT_COLOR=RED;
	// 1. adc校准
    HAL_ADCEx_Calibration_Start(&hadc1);
	// 2. 开启DMA传输
    HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&originalAdc, numOfCollect);
	// 3. 关掉传输一半中断
    __HAL_DMA_DISABLE_IT((&hadc1)->DMA_Handle, DMA_IT_HT);
	// 4. 设置ADC采样频率。并开始计时采样
    setAdcFrequency(numF);
    HAL_TIM_Base_Start(&htim8);
}

/* ===================================================== */
// 描述:DMA传输回调函数,用于绘制获取的ADC波形并输出提示信息。
// 参数:
// 返回值:
// 注意:在绘制期间,会关闭定时器,停止触发ADC。
// 		可以看出,这里的逻辑是,
//		1. 打开定时器计时,定时器开始计数,可以触发中断;
//		2. 定时器触发中断,ADC获取连续的一段电压值;(在这里频率是可以设置的)
//		3. 关闭定时器计数,ADC停止转换;
//		4. 绘制刚刚获取的ADC波形,绘制完成之后,再次打开定时器。
/* ===================================================== */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
		uint16_t i;
		uint32_t slope1[50]={0};
		uint32_t slope2[50]={0};
	// 失能定时器3
    HAL_TIM_Base_MspDeInit(&htim8);

		for(i = 0;i<1024;i++)
		{
			ADC_STORE[i] =  originalAdc[i];
		}
    if(oscState == 1)
    {

			for(i=0;i<50;i++)
			{
				slope1[i] = originalAdc[i+1]-originalAdc[i];
				slope2[i] =slope1[i+1]-slope1[i];
				if(slope2[i]<10&&slope2[i]>0)//斜率差值总是小于7
				{
					LCD_ShowString(400,0,16,"Triangular_wave",0);
				}
				else if(slope1[i]==0&&slope1[i+1]==0&&slope1[i+2]==0&&slope1[i+3]==0&&slope1[i+4]==0&&slope1[i+5]==0&&slope1[i+6]==0&&slope1[i+7]==0&&slope1[i+8]==0)//连续斜率等于0
				{
					LCD_ShowString(400,0,16,"Square_wave",0);
				}
				else 
				{
					LCD_ShowString(400,0,16,"sin_wave",0);
				}
			}
		
		updateWaveFrequency();
		OSC_ShowWave();
		OSC_ShowInfo();
		}
		else
		{
			POINT_COLOR=RED;
			LCD_ShowString(0,0,16,"stop",0);
		}
	// 使能定时器3

    HAL_TIM_Base_MspInit(&htim8);
}

/* ===================================================== */
// 描述:从一个上升沿开始计算获取的波形的频率。
//		 对于杂乱的信号,这里其实算的并不准确。
// 参数:
// 返回值:
/* ===================================================== */
void updateWaveFrequency(void)
{
    static uint16_t n = 0;

	// 寻找原始数据中第一个 adcThreshold 附近的点
	// adcThreshold 
    for(n = 100; n < numOfCollect; n++)
    {
        if(originalAdc[n] < adcThreshold && originalAdc[n + 2] > adcThreshold)
        {
            if(n > (numOfCollect - lcddev.width))
            {
                startPosition = 100;
            }
            else
            {
                startPosition = n;
            }
            break;
        }
    }
	// 寻找原始数据中第二个 adcThreshold 附近的点
    for(n = startPosition + 3; n < numOfCollect; n++)
    {
        if(originalAdc[n] < adcThreshold && originalAdc[n + 2] > adcThreshold)
        {
            endPosition = n;
            break;
        }
    }
	/*
		每采集一个点的时间间隔为:ARR / 2M s
		所以这两个点之间的时间间隔为:(endPosition - startPosition) * ARR / 2M s
		频率为:1 / ((endPosition - startPosition) * ARR / 2M)
				= 2M / ((endPosition - startPosition) * ARR)
				= adcFrequency / (endPosition - startPosition)
	*/
		for(n=0;n<5;n++)
		{
			adc1[n] = 2000000 / ((endPosition - startPosition) * ARR);
		}
//    waveFrequency = 2000000 / ((endPosition - startPosition) * ARR);
		waveFrequency = (adc1[0]+adc1[1]+adc1[2]+adc1[3]+adc1[4])/5;
}

/* ===================================================== */
// 描述:计算上峰值电压、下峰值电压、两者之间的差值
// 参数:
// 返回值:
/* ===================================================== */
void updateDifVoMaxAndMin(void)
{
    uint16_t max = 0;
    uint16_t min = 4095;
    uint16_t n = 0;

    for(n = 1; n < lcddev.width; n++)
    {
        if(newWave[n] > max)
        {
            max = newWave[n];
        }
        if(newWave[n] < min)
        {
            min = newWave[n];
        }
    }
	maxVol = (float) max * (3.3 / 4096.0);
//	maxVol =  KalmanFilter(&kfp,maxVol);
	minVol = (float) min * (3.3 / 4096.0);
//	minVol = KalmanFilter(&kfp,minVol);
  difVoMaxAndMin = maxVol - minVol;
}

/* ===================================================== */
// 描述:显示波形图
// 参数:
// 返回值:
/* ===================================================== */
void OSC_ShowWave(void)
{
	// 电压曲线中的点 在LCD上对应的 Y 坐标
  int16_t prePos = 0;		// 前一个点坐标
	int16_t afterPos = 0;	// 后一个点坐标
	
    static uint16_t n = 0;
	// 把原始ADC数据 放到 newWave 数组中(从我们算频率的地方截取)
    for(n = 0; n < 95; n++)
    {
        newWave[n] = originalAdc[offset + startPosition + n];
    }


	// 计算上峰值电压、下峰值电压、差值
    updateDifVoMaxAndMin();
	
    newWave[0] = prePos = ((lcddev.height - 40) - (newWave[0] * 0.0397 * enlarge));

    for(n = 1; n < (95 - 2); n++)
    {
        newWave[n] = afterPos = (lcddev.height - 40) - ((double)(newWave[n] * 0.0397 * enlarge));
		// 如果调整了放大倍数,需要限幅(20 - 220之间)
//        if(afterPos >= 220)	// 这里貌似也不用其实
//        {
//            newWave[n] = afterPos = 219;
//        }
        if(afterPos <= 20)	// 限制顶端
        {
            newWave[n] = afterPos = 21;
        }

				POINT_COLOR=WHITE;
        LCD_DrawLine(5*n, oldWave[n], 5*(n + 1), oldWave[n + 1]);	// 清除上一时刻的线
				POINT_COLOR=DARKBLUE;
        LCD_DrawLine(5*n, prePos, 5*(n + 1), afterPos);			// 画上新的线
					
//				}
		prePos = afterPos; // 更新
    }

	// 全部画完之后,把现在的存到旧的里面去,用于下次画线的时候擦除。
    for(n = 1; n < 95; n++)
    {
        oldWave[n] = newWave[n - 1];
    }		
}

/* ===================================================== */
// 描述:显示波形的信息
// 参数:
// 返回值:
/* ===================================================== */
void OSC_ShowInfo(void)
{

	/* LCD下面的信息 */
	POINT_COLOR=BLACK;
// 显示最大电压
	sprintf((char*)tempBuf, "%.2f", maxVol);
	LCD_ShowString(10,0,16,"max",0);
	LCD_ShowString(40, 0, 16, tempBuf, 0);
	// 显示最小电压
	sprintf((char*)tempBuf, "%.2f", minVol);
	LCD_ShowString(80,0,16,"min",0);
	LCD_ShowString(110, 0, 16, tempBuf, 0);
	// 显示电压差

	// 显示频率
	LCD_ShowString(160,0,16,"frequency",0);
	
}

其中有一个是对ARR进行改变的函数,大家要注意一下,根据自己需要进行改进。

MAIN.C,我们在定时器中断回调函数中进行一个计数的算法,这样我们就能拿到信号的准确频率,之后我们根据信号的频率来改变ADC的采集速率,以致我们能够画出比较好的波形。我们同时需要在工程里进行超分频设置,即,配置为二分频。

TFT_eSPI 背光 控制 tft屏幕led背光屏_CLR_15

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* 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_ADC1_Init();
  MX_TIM8_Init();
  MX_SPI2_Init();
  MX_TIM2_Init();
  MX_TIM3_Init();
  MX_TIM1_Init();
  /* USER CODE BEGIN 2 */
	//通过IO控制背光
	LCD_LED_SET;
	LCD_RST_SET;
// 1. 初始化LCD//并设置文字和图片
	LCD_Init();
	HAL_TIM_Base_Start_IT(&htim2);
	HAL_TIM_Base_Start_IT(&htim3);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
			Judeg();
			// 2. 初始化示波器(核心代码)接收信号  引脚为PA2
			OSC_Init();
			// 3. 设置运行标志位
			oscState = 1;
			
			HAL_Delay(100);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//进行频率的测量和计数
{
    if (htim == (&htim2))
    {
        count_over++;
    }
		if (htim == (&htim3))
    {
        count_i++;
    }
}
void Judeg(void)//进行频率的测量与ARR的判断
{
		__HAL_TIM_SET_COUNTER(&htim2, 0);
		__HAL_TIM_SET_COUNTER(&htim3, 0);
		count_i=0;
		count_over=0;
		HAL_Delay(990);
		count=__HAL_TIM_GET_COUNTER(&htim2)+count_over*65536;
		while(count==__HAL_TIM_GET_COUNTER(&htim2)+count_over*65536);   //等待下一个被测信号上升沿到来 
		count=count+1;
		count_all=__HAL_TIM_GET_COUNTER(&htim3)+count_i*100;
		count_ms=((float)count)*2000000/((float)(count_all-185));	 	//2M标准时钟计数 	189为标准值校准 需要调参
		if(count_ms>=50000)
			{
				control(1);
			}
			if(count_ms>=43000&&count_ms<50000)
			{
				control(2);
			}
			if(count_ms>=39000&&count_ms<43000)
			{
				control(3);
			}
			if(count_ms>=35000&&count_ms<39000)
			{
				control(4);
			}
			if(count_ms>=32500&&count_ms<35000)
			{
				control(5);
			}
			if(count_ms>=30000&&count_ms<32500)
			{
				control(6);
			}
			if(count_ms>=24500&&count_ms<30000)
			{
				control(7);
			}
			if(count_ms>=22000&&count_ms<24500)
			{
				control(8);
			}
			if(count_ms>=20000&&count_ms<22000)
			{
				control(9);
			}
			if(count_ms>=17500&&count_ms<20000)
			{
				control(10);
			}
			if(count_ms>=15000&&count_ms<17500)
			{
				control(11);
			}
			if(count_ms>=12500&&count_ms<15000)
			{
				control(12);
			}
			if(count_ms>=10000&&count_ms<12500)
			{
				control(13);
			}
			if(count_ms>=7500&&count_ms<10000)
			{
				control(14);
			}
			if(count_ms>=6500&&count_ms<7500)
			{
				control(15);
			}
			if(count_ms>=6000&&count_ms<6500)
			{
				control(16);
			}
			if(count_ms>=5500&&count_ms<6000)
			{
				control(17);
			}
			if(count_ms>=5000&&count_ms<5500)
			{
				control(18);
			}
			if(count_ms>=4500&&count_ms<5000)
			{
				control(19);
			}
			if(count_ms>=4000&&count_ms<4500)
			{
				control(20);
			}
			if(count_ms>=3500&&count_ms<4000)
			{
				control(21);
			}
			if(count_ms>=3000&&count_ms<3500)
			{
				control(22);
			}
			if(count_ms>=2500&&count_ms<3000)
			{
				control(23);
			}
			if(count_ms>=2000&&count_ms<2500)
			{
				control(24);
			}
			if(count_ms>=1500&&count_ms<2000)
			{
				control(25);
			}
			if(count_ms>=1300&&count_ms<1500)
			{
				control(26);
			}
			if(count_ms>=1000&&count_ms<1300)
			{
				control(27);
			}
			if(count_ms>=800&&count_ms<1000)
			{
				control(28);
			}
			if(count_ms>=700&&count_ms<800)
			{
				control(29);
			}
			if(count_ms>=600&&count_ms<700)
			{
				control(30);
			}
			if(count_ms>=500&&count_ms<600)
			{
				control(31);
			}
			if(count_ms>=400&&count_ms<500)
			{
				control(32);
			}
			if(count_ms>=350&&count_ms<400)
			{
				control(33);
			}
			if(count_ms>=300&&count_ms<350)
			{
				control(34);
			}
			if(count_ms>=250&&count_ms<300)
			{
				control(35);
			}
			if(count_ms>=200&&count_ms<250)
			{
				control(36);
			}
			if(count_ms>=150&&count_ms<200)
			{
				control(37);
			}
			if(count_ms>=100&&count_ms<150)
			{
				control(38);
			}
			if(count_ms>=50&&count_ms<100)
			{
				control(39);
			}
			LCD_ShowNum(240, 0, count_ms, 8, 16);
}

这个judge函数本来是要写成根据频率计算需要的ARR的函数,但经过测试,算法没能实现,需要看到这个文章的朋友,开动大脑,帮帮作者实现一下。作者是算法是ADC的ARR等于ADC的主频除以分频再除以频率再除以40(因为每一个周期我们需要有40个点去画波形)。

效果展示

因为这个程序会根据频率改变ARR的值,所以每次画出的波形大多适中。而ADC只能采集0-3.3V的数据,所以我们只能看到一半的波形。

50K

TFT_eSPI 背光 控制 tft屏幕led背光屏_开源_16

TFT_eSPI 背光 控制 tft屏幕led背光屏_stm32_17

30K

TFT_eSPI 背光 控制 tft屏幕led背光屏_stm32_18

TFT_eSPI 背光 控制 tft屏幕led背光屏_TFT_eSPI 背光 控制_19

10K

TFT_eSPI 背光 控制 tft屏幕led背光屏_TFT_eSPI 背光 控制_20

TFT_eSPI 背光 控制 tft屏幕led背光屏_CLR_21

评论区留下QQ邮箱,作者会发程序源码的哦!(强烈建议大家能够按着步骤自己完成)

同时欢迎大家指出这篇文章的不足之处,作者也是第一次写文章,有很多提升空间。。。

之后作者会更新一篇关于加法器的文章,运用加法器就可以实现电压的抬升,这样就能绘画出完整波形了。