模块类型
1.主控STM32F103ZET6(F1系列的程序可以互用)
2.TFT3.5寸彩屏
(这个屏幕驱动的程序大部分函数是可以直接CV,但必须将初始化换函数更改为屏幕适配的类型)
程序进程
作者采用先对信号的频率进行检测,因F1系列的ADC采集频率是72M的,额定分频数是6分频,而6分频采集到50KHz的信号时,无法支持绘画出信号的波形(一般一个周期需要采集20个点才能画出比较OK的波形图案),于是才用超频采样的方式,即,在程序中重新配置分频数,我们采用2分频的方式。
我们采用定时器触发ADC采样,这样可以更简单的控制ADC采样频率,画出的波形也就跟好看了些。在ADC采样结束后就可以通过波形的斜率来判断是哪种波形。ADC采集后拿到的数据进行就可以用于绘图,通过对画线函数参数的调整就可以拿到我们想要的图像。
硬件配置
1.选择芯片型号
2.配置SYS为Serial Wire以免芯片自锁
3.配置RCC为外部高速时钟
4.配置ADC1的通道2做为检测信号的引脚,配置DMA做为数据存储,配置外部触发转换源选择高级定时器8
5.配置定时器1和2搭配着做为信号频率的检测,。配置定时器8做为控制ADC采集速率的定时器,注意定时器2的Clock Source选择ETR2,这样PA0引脚可以做为检测信号频率的引脚了,并且定时器1需要打开中断功能。定时器8的触发事件选择为Update event
6.配置TFT彩屏驱动的软件SPI,这里我们选用SPI2做为MCU与TFT传输数据的通信协议
7,到了最后一步咯,有没有认真去选配置啊!!!最后我们配置TFT彩屏的引脚,其中LED是屏幕的背光引脚,作者建议直接接3.3V即可,这样可以减少GPIO的使用。这里大家就直接配置吧,相信大家有这个能力,棒棒棒!
8.配置时钟树
9.最后就是工程的建立啦
软件编写
这里我就强调两点,一则:就是我们需要增加一个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的采集速率,以致我们能够画出比较好的波形。我们同时需要在工程里进行超分频设置,即,配置为二分频。
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
30K
10K
评论区留下QQ邮箱,作者会发程序源码的哦!(强烈建议大家能够按着步骤自己完成)
同时欢迎大家指出这篇文章的不足之处,作者也是第一次写文章,有很多提升空间。。。
之后作者会更新一篇关于加法器的文章,运用加法器就可以实现电压的抬升,这样就能绘画出完整波形了。