IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 【STM32多级界面】-LCD结构体多级图形界面框架 -> 正文阅读

[嵌入式]【STM32多级界面】-LCD结构体多级图形界面框架

STM32多级界面搭建

在实际的项目之中,需要用单片机在LCD/OLED中搭建一个3级的图形界面。本文的工程文件我将上次传到百度网盘,链接在最后的部分。

一、方案选择

方案一:使用if条件语句写一个界面,但是这种方法在两级界面的时候还可以,在多级界面的时候代码的逻辑上的复杂度会大幅增加,不太使用。

方案二:使用嵌入式图形库lvgl等,但是这个对新手不太友好。

方案三:使用结构体变量存储界面信息,可以是使得多级界面也可以逻辑清晰。

二、实现效果

主界面
在这里插入图片描述菜单界面
在这里插入图片描述

功能界面
在这里插入图片描述

三、程序设计

3-1宏定义变量设计

·界面标志位
在头文件中首先需要定义一个结构体来存放界面标志位,包括现在界面的状态,当前运行的任务状态,以及当前任务是否处于运行状态。

·界面变量
创建三个枚举变量,Main_Interface,Menu_Interface,Task_Interface,来表示目前界面所在的位置,是主界面还是菜单界面还是任务界面。

·任务变量
创建n个任务,每个任务提供一个代号,方便识别。

#ifndef __GUI_H__
#define __GUI_H__
/*********************************************
 * 创建一个结构体
 * 存放界面标志位
*********************************************/
typedef struct
{
    uint8_t Interface_Mark;     //界面状态
    uint8_t Task_Mark;          //任务状态
    uint8_t Run_Task;           //开始运行任务
} Mark;

/*********************************************
 * 创建一个枚举
 * 存放界面变量
*********************************************/
enum
{
    Main_Interface, /****主界面*****/
    Menu_Interface, /****菜单界面***/
    Task_Interface, /****任务界面***/
};
/*********************************************
 * 创建一个枚举
 * 存放功能变量
*********************************************/
enum
{
	F_ONE,
	F_TWO,
	F_THREE,
	F_FOUR,	
	Stopping,		/****任务停止***/
};

extern  Mark Mark_Sign;

void Interface_APP(Mark *Mark_Sign);
void Interface(Mark *Mark_Sign,uint8_t keys);
void GUI_init(void);

#endif

3-2控制函数设计

主函数调用
使用定义的结构体来显示界面,即显示初始界面

显示界面函数
主函数调用,根据Mark_Sign->Interface_Mar变量来确实显示当前所在的界面

功能切换函数:主函数调用,通过扫描按键,当按下对应按键时,执行相应的函数,如确认进入函数,返回退出函数,功能切换函数。

/***************
//初始化,显示主界面
***************/
void GUI_init(void)
{	
	Interface_APP(&Mark_Sign);	
}
/***************
//mian函数中显示,主循环中调用,用于显示界面
***************/
void Interface_APP(Mark *Mark_Sign)
{
	switch (Mark_Sign->Interface_Mark)
	{
		//状态标志位 主界面
		case Main_Interface:
			Main_Interface_APP();//显示主界面
		break;

		//状态标志位 菜单界面
		case Menu_Interface:
			Menu_Interface_APP();//显示菜单界面
		break;

		//状态标志位 任务界面
		case Task_Interface:
			Function_Menu_APP(Mark_Sign);//显示功能界面
		break;
		default:
		break;
	}
}
/***************
//mian函数中显示,主循环中调用,按键扫描,当按下对应按键时,执行相应的函数
***************/
void Interface(Mark *Mark_Sign,uint8_t keys)
{
/* 1,检测当前按下的按键为确认键
 * 2,检测当前的界面
 *    (1)如果是主界面,则进入菜单界面
 *    (2)如果是菜单界面,则进入任务界面
 *     (3)如果是任务界面,则开执行被选中的任务*/   
    if(keys == KEY1_PRES)
    {
		key_Inter(Mark_Sign);
    }
	
/*1,检测当前按下的按键为返回键
 * 2,检测当前的界面
 * (1)如果是任务界面,则停止正在运行的任务,返回到菜单界面
 * (2)如果是菜单界面,则返回到主界面*/
	
    if(keys == KEY2_PRES)
    {
		key_Cancel(Mark_Sign);
    }
/*1,检测当前按下的按键为任务切换
 * (1)在菜单界面,选择切换接下来要运行的任务*/
	if(keys == KEY3_PRES)
    {
		key_Up_move(Mark_Sign);
    }
	if(keys == KEY4_PRES)
    {
		key_Down_move(Mark_Sign);
    }
}

不同界面的显示函数: 主界面,菜单界面,功能界面

static unsigned char Lcd_Disp_String[21];
/***************
//显示主界面
***************/
void Main_Interface_APP(void)
{
		LCD_DisplayStringLine(Line1 ,(unsigned char *)"Main_Interface_APP");
		LCD_DisplayStringLine(Line2 ,(unsigned char *)"ADC-LED-REF");
}


/***************
显示菜单界面
***************/
void Menu_Interface_APP(void)
{
	LCD_DisplayStringLine(Line1 ,(unsigned char *)"Menu_Interface_APP");
}


/***************
显示功能界面
***************/
void Function_Menu_APP(Mark *Mark_Sign)
{
	LCD_DisplayStringLine(Line1 ,(unsigned char *)"Function_Menu_APP  ");
	if(Mark_Sign->Task_Mark == F_ONE )
	{
		LCD_DisplayStringLine(Line2 ,(unsigned char *)"ADC         ");
		sprintf((char*)Lcd_Disp_String, "  RES38_vol: %6.3f  ",getADC1()*(3.3/4096));	
		//3.3为总的电压值 2的12次方等于4096 用于对电压值分成一份
		LCD_DisplayStringLine(Line4, Lcd_Disp_String);

	}
}

按键控制函数: 确认 返回 切换 其中keys_number用于切换功能时候,选择对应功能的时候记录用,因为up和down函数在切换的时候,需要作出循环的效果,既可以到了上限F_FOUR之后就,继续向上时,重新回到F_ONE, 也可以在到了下限F_ONE之后就,继续向下时,重新回到F_FOUR。

uint8_t keys_number;
/***************
 *  1,检测当前按下的按键为确认键
 *  2,检测当前的界面
 *     (1)如果是主界面,则进入菜单界面
 *     (2)如果是菜单界面,则进入任务界面
 *     (3)如果是任务界面,则开执行被选中的任务
***************/
void key_Inter(Mark *Mark_Sign)
{

        //当按下菜单键时,判断当前界面
        /************判断当前界面为主界面***********************/
        if(Main_Interface == Mark_Sign->Interface_Mark)
        {
            /**************进入菜单界面*************/
            Mark_Sign->Interface_Mark = Menu_Interface;
			Mark_Sign->Task_Mark = F_ONE;
			keys_number = 0;
        }
        /************判断当前界面为菜单界面*******************/
        else if(Menu_Interface == Mark_Sign->Interface_Mark)
        {
            /***************进入任务界面************/
            Mark_Sign->Interface_Mark = Task_Interface;

            /**************进入指定的功能任务*******/
            switch(Mark_Sign->Task_Mark)
            {
				//Task_Mark;          //任务状态
				//Run_Task;           //开始运行任务

            /**************开始运行time任务*******/
            case F_ONE:
                Mark_Sign->Run_Task = F_ONE;
				
                break;
            /**************开始运行蓝牙任务*******/
            case F_TWO:
                Mark_Sign->Run_Task = F_TWO;

                break;
            /**************开始运行F_THREE任务*******/
            case F_THREE:
                Mark_Sign->Run_Task = F_THREE;

                break;
            /**************开始运行设置任务*******/
            case F_FOUR:
                Mark_Sign->Run_Task = F_FOUR;
                break;
            default:
                break;
		    }
        }
        /************判断当前界面为任务界面******************/
        else if(Task_Interface == Mark_Sign->Interface_Mark)
        {
            /*******判断当前正在运行的任务*******/
            switch(Mark_Sign->Run_Task)
            {
				//Task_Mark;          //任务状态
				//Run_Task;           //开始运行任务

            /**************开始运行time任务*******/
            case F_ONE:
				
                break;
            /**************开始运行蓝牙任务*******/
            case F_TWO:
            
                break;
            /**************开始运行F_THREE任务*******/
            case F_THREE:

                break;
            /**************开始运行设置任务*******/
            case F_FOUR:
       
                break;
            default:
                break;
            }
        }
}
/*显示推出的时候箭头和向上移动的时候的箭头*/
void Menu_cancel(uint8_t keys_number,Mark *Mark_Sign);
void Menu_input(uint8_t keys_number,Mark *Mark_Sign);
/***************
//返回键,可以返回上一界面,如果在菜单界面
返回的时候指针在指向上次的位置
 * 1,检测当前按下的按键为返回键
 * 2,检测当前的界面
 *    (1)如果是任务界面,则停止正在运行的任务,返回到菜单界面
 *    (2)如果是菜单界面,则返回到主界面
***************/
void key_Cancel(Mark *Mark_Sign)
{

        //当按下返回键时,判断当前界面
        /************判断当前界面为菜单界面*******************/
        if(Menu_Interface == Mark_Sign->Interface_Mark)
        {
            /*******退出菜单界面***进入主界面**/
            Mark_Sign->Interface_Mark = Main_Interface;

        }
        /************判断当前界面为任务界面******************/
        else if(Task_Interface == Mark_Sign->Interface_Mark)
        {
            /***退出正在运行的任务***/
//            Mark_Sign->Run_Task = Stop;
            /*******退出任务界面*****/
            Mark_Sign->Interface_Mark = Menu_Interface;
			Mark_Sign->Run_Task = Stopping;
			Menu_cancel(keys_number,Mark_Sign);
        }
}
/***************
//菜单界面,可以上下选择进入那些功能之中
、、任务界面中
time功能中,可以上下更改对应的时间与日期的值
***************/
void key_Up_move(Mark *Mark_Sign)
{
	

	if(Mark_Sign->Interface_Mark==Menu_Interface)
	{
		if(keys_number==0) keys_number = 4;
		keys_number--;
		Menu_cancel(keys_number,Mark_Sign);
	}
	else if(Mark_Sign->Run_Task == F_ONE)
	{
		
	}
}


/***************
//菜单界面,可以上下选择进入那些功能之中
//任务界面中
//time功能中,可以上下更改对应的时间与日期的值
***************/
void key_Down_move(Mark *Mark_Sign)
{
	
	if(Mark_Sign->Interface_Mark==Menu_Interface)
	{
		if(keys_number==4) keys_number=0;
		keys_number++;
		Menu_input(keys_number,Mark_Sign);
	}
	else if(Mark_Sign->Run_Task == F_ONE)
	{
		
	}
}

切换时的效果: 不管是LCD 还是OLED在切换界面的时候,如果不及时清理的话,都会保留上一次的印记,因此在任务变更,界面改变的时候都要及时清理。

==而且可以在任务界面的时候可以写一个光标来表示,即将运行的任务是什么。==这样便于观看。

/*显示推出的时候箭头和向上移动的时候的箭头*/
void Menu_cancel(uint8_t keys_number,Mark *Mark_Sign)
{
		if(keys_number==3)
		{
			Mark_Sign->Task_Mark = F_FOUR;
			LCD_DisplayStringLine(Line2 ,(unsigned char *)"TEST       ");

		}
		else if(keys_number==2)
		{
			Mark_Sign->Task_Mark = F_THREE;
			LCD_DisplayStringLine(Line2 ,(unsigned char *)"FRE        ");
		}
		else if(keys_number==1)
		{
			Mark_Sign->Task_Mark = F_TWO;
			LCD_DisplayStringLine(Line2 ,(unsigned char *)"LED        ");
		}
		else if(keys_number<=0)
		{
			Mark_Sign->Task_Mark = F_ONE;
			LCD_DisplayStringLine(Line2 ,(unsigned char *)"ADC        ");
			keys_number=0;
		}
}

/*前进移动的时候的箭头*/
void Menu_input(uint8_t keys_number,Mark *Mark_Sign)
{
		if(keys_number==1)
		{
			Mark_Sign->Task_Mark = F_TWO;
			LCD_DisplayStringLine(Line2 ,(unsigned char *)"LED        ");
		}
		else if(keys_number==2)
		{
			Mark_Sign->Task_Mark = F_THREE;
			LCD_DisplayStringLine(Line2 ,(unsigned char *)"FRE        ");
		}
		else if(keys_number==3)
		{
			Mark_Sign->Task_Mark = F_FOUR;
			LCD_DisplayStringLine(Line2 ,(unsigned char *)"AAA        ");
		}
		else if(keys_number>=4)
		{
			Mark_Sign->Task_Mark = F_ONE; 
			LCD_DisplayStringLine(Line2 ,(unsigned char *)"ADC        ");
		}
}

3-3主函数调用设计

主函数调用:
在主函数先定义一个结构体Mark Mark_Sign = {Main_Interface,0,0};来表示初始界面为主界面。之后调用gui_init函数来显示初始界面。

在while循环中不断读取按键值和显示界面函数来实现显示界面的动态更新。

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "usart.h"
#include "gpio.h"
#include "lcd.h"
#include "stdio.h"
#include "gui.h"

/* Private define ------------------------------------------------------------*/
Mark Mark_Sign = {Main_Interface,0,0};

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
unsigned char Lcd_Disp_String[21];
/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* Configure the system clock */
  HAL_Init();
  SystemClock_Config();
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_ADC1_Init();
  MX_ADC2_Init();
  /* USER CODE BEGIN 2 */
	LCD_Init();
	LCD_Clear(White);
	LCD_SetBackColor(White);
	LCD_SetTextColor(Black);
	LCD_Clear(White);
	LCD_DisplayStringLine(Line1 ,(unsigned char *)"   hello	dma.   ");
	
	GUI_init();/

  /* USER CODE END 2 */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	Interface_APP(&Mark_Sign);
	Interface(&Mark_Sign, KEY_Scan(0));
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

四、总结

这是一个用结构体写的一个基础多级界面的框架,如果你使用的界面大于或是小时3级的话,这时候就需要对结构的中的界面枚举变量进行更改。根据整个设计你所喜欢的界面叭。总体来说来时不错的

本文的工程文件我将上次传到百度网盘。链接: https://pan.baidu.com/s/1QNpKRsyHVr-UyCGsyvkLBw .
提取码:vsh2
在这里插入图片描述

此文本工程参考了链接: 基于STM32之OLED菜单界面框架搭建,特此感谢。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-05-14 10:06:25  更:2022-05-14 10:06:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 12:06:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码