OLED预警系统与按键菜单交互代码实操

news2024/11/24 11:12:47

引言

OLED顾名思义就是一个屏幕, 我们让一个屏幕在特定的时间, 显示特定的画面, 就是我们所需要的, 因为这里是涉及到环境预警,所以需要加入一个应急接管页面的选项, 所以我们要把按键直接操作画面, 变成按键操作完, 我们根据优先级判断之后, 才能确定要显示的是哪个画面.

比如我们用户现在想听歌, 如果此时环境异常, 用户要操作的指令已经下发,但是在控制OLED界面的时候, 通过系统判断, 有更紧急的事情, 所以就要先处理预警系统的界面, 当处理完后, 再处理用户的普通指令, 所以, 我们控制OLED显示的画面的变量, 可以被用户的按键交互控制, 也可以被预警系统所控制.

本博客, 修改完的工程代码:

点击下载

https://wwyz.lanzoul.com/iJ6fR28p2bpe

OLED预警系统与按键交互架构图

菜单页面控制

OLED我们有能力显示字符了, 我们就要在特定时间选择特定的页面显示, 对于预警系统 和 用户交互功能, 我们进行分开判断,进行显示

image-20240830111733917

OLED页面选择与底层刷新逻辑

回去(ctrl 加鼠标左键,快速跳转)

当我们知道要显示哪个界面, 剩下的就好办了, 我们只需要把特定的页面, 分成函数, 提前写好, 我们对应进行刷新就可以了, 但是单纯对于OLED界面刷新也是有技巧的, 因为我们刷新环境信息的时候, 如果页面包含文字和数据, 文字是不变的, 数据是实时刷新的, 我们不必重复刷新文字, 只是当页面第一次显示或者切换新页面的时候, 整体刷新一次, 后面就只刷新页面更新的数据就行了.(跳转下面链接,有详细演化过程)

OLED整体刷新到结合switch刷新方式演变

image-20240830112946821

预警系统接管OLED菜单控制

回去1(ctrl 加鼠标左键,快速跳转)

回去2(ctrl 加鼠标左键,快速跳转)

预警系统界面是在环境异常的时候, 接管OLED界面的, 所以我们在控制OLED显示界面的时候, 要判断一下是否报警, 如果报警, 就不刷新用户正常情况下的界面, 而是和预警系统进行交互, 显示预警系统所显示的画面(跳转下面链接, 观看详细演示步骤)

预警系统OLED挑选模式

image-20240830113321720

用户按键交互控制界面

我们完全可以把想显示的界面, 提前的纸上画出来, 但是我们用户想要显示特定的界面, 不像预警系统那样, 我们可以直接让预警系统直接接管, 因为用户的个性化需求, 比如在不同的时间段去听歌和观看家居信息 , 可能需要复杂的交互需求, 所以通过按键交互, 来赋予对应的界面交互状态, 从而实现用户所需的菜单交互控制。

按键收集

按键类型判断

回去(ctrl 加鼠标左键,快速跳转)

既然我们想要利用按键来收集用户的指令, 所以我们就要判断按键类型, 是单击,双击还是长按, 这样就可以减少按键的使用数量, 提升用户体验

按键,单击双击,长按区分(跳转观察详细博客)

利用队列收集和处理按键

回去(ctrl 加鼠标左键,快速跳转)

我们判断完按键类型, 如果直接进行处理, 如果当前有更重要的事情来做, 比如应急处理, 相对于用户的操作, 则是可以延后的, 因为这关乎了整个系统的安全性, 所以我们利用队列先及时的收集到按键信息, 等到系统正常运行时, 我们就可以进行处理用户按键交互, 从而显示界面了.

利用队列收集单双击和长按按键(跳转观察详细博客)

利用已有队列按键, 构造菜单交互

回去(ctrl 加鼠标左键,快速跳转)

利用已有队列按键, 构造菜单交互(跳转观察详细博客)

当我们有了收集到的单双击和长按按键, 我们就可以根据这些按键类型, 来赋予对应的功能, 然后来控制菜单页面了, 比如我假设长按是, 切换智能页面和音乐页面, 单击在智能模式下,是切换环境信息界面和阈值界面。

双击, 在音乐界面, 是可以选中歌曲播放, 单击可以选择歌曲, 在播放的时候,单击又可以播放和暂停歌曲, 在不同的界面,都有不同的功能。

在了解了上述的功能后, 我们要清楚的在图纸上,画出我们要显示的所有界面, 并且在特定界面, 按下什么按键, 我们应该做什么, 跳转到哪个界面, 都是我们所需要构建的, 当我们知道了, 在当前界面, 按下了什么按键, 应该修改当前界面为哪个界面的时候, 我们的按键交互就构建好了。

代码实操

有了上面, 我们构建的理论, 我们现在就开始进行代码实操, 当用到哪些方面, 我会进行引导跳转到对应部分, 大家仔细阅读即可. 我也会对对应的代码进行解释. 我会站在开发者的角度上来 进行开发, 中间也会涉及到调试, 因为我们不可能一下子就开发处理出一个菜单按键, 并且每个步骤, 比如按键收集方面, 都需要进行调试, 所以我们先按照框架图, 构建底层.

先构建所有画面

我们首先从头开始构建OLED工程, 我们把所有的界面, 先定义成函数, 定义好, 这样, 我们就可以随意的显示了, 至于什么时机显示哪些页面, 是后面状态机菜单和预警系统的事情了, 我们首先完善底层

1.构建最小工程

跳转构建最小工程

其中第10步名字:

image-20240830165932042

2.引入江协科技OLED的显示文件(其中图像和文字取模,我已经加入,所以复制我的即可)

最小例程上加OLED显示

3.然后在main.c函数内引入oled头文件, 然后初始化头文件, 我们显示一下页面

#include "stm32f10x.h"                  // Device header

//oled模块
#include "OLED.h"

int main(void)
{
	/*OLED初始化*/
	OLED_Init();
	//开机界面
	OLED_ShowImage(0,0,128,64, welcome);		
	OLED_Update();		
	while(1)
	{
	
	}
	
}

4.设置一下编译器为 version5

image-20240830170702666

5.烧录入stlink

stm32最小系统版烧录方法

6.到此步骤, 本工程链接备份

点击下载

7.可以看到我们已经可以构建出``开机界面了

image-20240830172123612 8.我们后面就是先画出所有的菜单页面, 然后进行取模了

我们先看下我们要构建的页面

img

9.大概可以分成

页面刷新的原理和数据更新逻辑

页面刷新的原理和数据更新逻辑(ctrl 加鼠标左键,快速跳转)

开机界面

void oled_start(void)
{
	if(oled.button_down == 1)
	{
		oled.button_down = 0;
		OLED_ShowImage(0,0,128,64, welcome);		
		OLED_Update();	
	}
}	

温湿度环境信息界面

//模式一1①主页面
void oled_show1(void)
{
	if(oled.button_down == 1)
	{
		oled.button_down = 0;
		OLED_Clear();
		OLED_ShowChinese(0, 0, "温度");
		
		OLED_ShowChinese(0, 24, "湿度:");
		
		OLED_ShowChinese(0, 48, "烟雾浓度:");
		
		OLED_Printf(48,0,OLED_8X16,"%2d",temp);
		OLED_Printf(48,24,OLED_8X16,"%2d",humi);
		OLED_Printf(80,48,OLED_8X16,"%2.1f",ad_value);
		OLED_ShowChinese(80,0 , "℃");
		OLED_ShowChinese(80,24 , "%");
		OLED_ShowChinese(111,48 , "%");	
		OLED_Update();	
	}
	else
	if(oled.button_down == 0)
	{
		OLED_Printf(48,0,OLED_8X16,"%2d",temp);
		OLED_Printf(48,24,OLED_8X16,"%2d",humi);
		OLED_Printf(80,48,OLED_8X16,"%2.1f",ad_value);
		OLED_Update();		
	}
}	

阈值信息界面

//模式1② 温湿度烟雾阈值显示
void oled_show2(void)
{
	if(oled.button_down == 1)
	{
		oled.button_down = 0;
		OLED_Clear();
		OLED_ShowChinese(0, 0, "温度阈值:");

		OLED_ShowChinese(0, 24, "湿度阈值:");

		OLED_ShowChinese(0, 48, "浓度阈值:");

		OLED_Printf(80,0,OLED_8X16,"%3d",temp_th);

		OLED_Printf(80,24,OLED_8X16,"%3d",humi_th);

		OLED_Printf(80,48,OLED_8X16,"%3d",mq2_th);

		OLED_ShowChinese(111,0 , "℃");
		OLED_ShowChinese(111,24 , "%");
		OLED_ShowChinese(111,48 , "%");

		OLED_Update();	
	}
	else
	if(oled.button_down == 0)
	{
		OLED_Printf(80,0,OLED_8X16,"%3d",temp_th);
		
		OLED_Printf(80,24,OLED_8X16,"%3d",humi_th);
		
		OLED_Printf(80,48,OLED_8X16,"%3d",mq2_th);

		
		OLED_Update();		
	}
	
}	

温度报警界面

//模式1 ③ 应急模式
void oled_alarm1(void)
{
	if(oled.button_down == 1)
	{
		oled.button_down = 0;
		OLED_Clear();
		OLED_ShowChinese(0, 0, "温度:");
		
		OLED_ShowChinese(0, 24, "温度阈值:");
		
		OLED_Printf(48,0,OLED_8X16,"%2d",temp);
		
		
		OLED_Printf(80,24,OLED_8X16,"%3d",temp_th);
		
		OLED_ShowChinese(80,0 , "℃");
		OLED_ShowChinese(111,24 , "℃");
		OLED_ShowChinese(0, 48, "红灯亮,风扇转");
		
		OLED_Update();		
	}
	else 
	if(oled.button_down == 0)
	{
		OLED_Printf(48,0,OLED_8X16,"%2d",temp);
		
		
		OLED_Printf(80,24,OLED_8X16,"%3d",temp_th);
		
		
		OLED_Update();	
	}

}

湿度报警界面

void oled_alarm2(void)
{
	if(oled.button_down == 1)
	{
		oled.button_down = 0;
		OLED_Clear();
		OLED_ShowChinese(0, 0, "湿度:");
		
		OLED_ShowChinese(0, 24, "湿度阈值:");
		
		OLED_Printf(48,0,OLED_8X16,"%2d",humi);
		
		
		OLED_Printf(80,24,OLED_8X16,"%3d",humi_th);
		
		OLED_ShowChinese(80,0 , "%");
		OLED_ShowChinese(111,24 , "%");
		OLED_ShowChinese(0, 48, "蓝灯亮,风扇转");
		
		OLED_Update();	
	}
	else
	if(oled.button_down == 0)	
	{
		OLED_Printf(48,0,OLED_8X16,"%2d",humi);
		
		
		OLED_Printf(80,24,OLED_8X16,"%3d",humi_th);
		
		OLED_ShowChinese(80,0 , "%");
		OLED_ShowChinese(111,24 , "%");
		
		OLED_Update();	
	}

}

歌单

对于选中的歌曲反选, 需要通过判断我们选中的是哪首歌曲, 然后再进行反选的

void oled_musicmenu(void)
{
	if(oled.button_down == 1)
	{
		oled.button_down = 0;
		OLED_Clear();
		
		OLED_ShowChinese(16, 0, "最强王者参见");
		OLED_ShowChinese(16, 16, "我和我的祖国");

		OLED_ShowChinese(16, 32, "热爱");
		OLED_Printf(48,32,OLED_8X16,"%3d",105);
		OLED_ShowChinese(72, 32, "℃的你");

		OLED_ShowChinese(16, 48, "刚好遇见你");	
		
		if(oled.music_choice == 0)
		{

			OLED_ReverseArea(0, 0, 128, 16);
		}
		else
		if(oled.music_choice == 1)
		{
			OLED_ReverseArea(0, 16, 128, 16);
		}
		else
		if(oled.music_choice == 2)
		{
			OLED_ReverseArea(0, 32, 128, 16);
		}
		else
		if(oled.music_choice == 3)
		{
			OLED_ReverseArea(0, 48, 128, 16);
		}
		OLED_Update();
	}
	
}

歌曲播放

歌曲播放也是同理, 我们歌曲播放, 只是一个界面, 我们通过判断我们选择的哪首歌曲, 然后进行播放, 对于开始和暂停按钮, 也需要加入判断

void oled_music(void)
{
	if(oled.button_down == 1)
	{
		oled.button_down = 0;
		OLED_Clear();
		if(oled.music_choice == 0)
		{
			OLED_ShowChinese(16, 8, "最强王者参见");
		}
		else
		if(oled.music_choice == 1)
		{
			OLED_ShowChinese(16, 8, "我和我的祖国");
		}	
		else
		if(oled.music_choice == 2)
		{
			OLED_ShowChinese(16, 8, "热爱");
			OLED_Printf(48,8,OLED_8X16,"%3d",105);
			OLED_ShowChinese(72, 8, "℃的你");
		}	
		else
		if(oled.music_choice == 3)
		{
			OLED_ShowChinese(16, 8, "刚好遇见你");
		}
		//播放,暂停
		if(oled.music_button == 0)
		{
			OLED_ShowImage(49,33,30,30,stop);
		}
		else
		if(oled.music_button == 1)
		{
			OLED_ShowImage(49,33,30,30,start);
		}
			
	}
	OLED_Update();
}

10.我们把这些页面集中起来, 然后进行选择播放

	switch(oled.now_menu)
	{
		case 0: oled_start();break;	//开机界面
		case 1: oled_show1();break;	//温湿度
		case 2: oled_show2();break;	//阈值
		case 3: oled_alarm1();break;	//温度报警
		case 4: oled_alarm2();break;	//湿度报警
//		case 5: oled_alarm3();break;	//烟雾浓度报警
		case 6: oled_musicmenu();break;	//歌单
		case 7: oled_music();break;	//歌曲播放
		default: break;
		
	}

预警系统接手界面

​ 有了这个switch选择模式,其实我们就可以手动传入页面编号, 人脑控制交互来进行显示了, 但是毕竟人脑不能一直盯着系统来看, 所以我们就要把这些交给单片机了。特别是环境信息预警,一旦检测到环境异常,我们就要把界面转换到预警界面。

预警系统接管OLED菜单控制(ctrl 加鼠标左键,快速跳转)

image-20240830182746538

1.在这里我们要定义一个预警模式变量

_Bool alarming = 0;	//警报模式是否开启

2.在预警系统, 检测到环境异常时, 修改这个变量, 我们在赋值控制对应界面的时候, 去判断一下alarming是否报警, 如果环境正常, 就按照用户交互的模式进行显示, 如果异常, 那随之绑定的就是预警系统所指定的界面.(详细介绍, 请看上面跳转的博客, 对于完整的代码, 后续我会贴出,这里可能会用到结构体, 这是后续优化后的结果, 但是变量名字都是一致的)

image-20240830183209851

用户按键交互与菜单控制

我们用户既然要进行控制, 那么首当其冲的就是按键识别, 收集到了足够多类型的按键, 我们根据按键再构建菜单, 那么, 我们就可以基于提前画好的界面,以及规定好的按键类型, 去进行跳转页面显示.

这样就构成了我们用户按键交互与菜单控制模型

按键识别

按键识别(流程思路)(ctrl 加鼠标左键,快速跳转)

1.我们现在来看下代码流程,

我们设置了PB1下降沿触发中断

image-20240830184500579

2.当触发中断的时候, 我们就把按键标志位按键置成 1

image-20240830184555226

3.然后在定时器里面启动计时, 还有对应的按键判断, 到达按键识别周期后, 我们就可以识别出按键了, 后面我们把按键存储到队列里面

image-20240830184719015

void Key_type_judgment(void)
{
	if(collectkey.button_atom == 0)//在处理函数外面,进行捕捉信息计时(默认处理函数时间很短,自动跳过处理函数)
	{
		//当 button_atom的时候,才能进行读取按键信息
		if(collectkey.key_down == 1)
		{	
			collectkey.key_down = 0;
			collectkey.button_count++;
			collectkey.count = 1;	//开始计时
		}
		if(collectkey.count > 0)
		{
			collectkey.count++;
			if(collectkey.count > 500)
			{
				collectkey.count = 0;
				if(collectkey.button_count == 1)
				{
					collectkey.button_count = 0;
					
					if(key_scan() == 0)	//短按
					{
						collectkey.button_mode = 1;
					
						//采集按键信息
						collect_button();
					}
					else if(key_scan() == 1)	//长按
					{
						collectkey.button_mode = 3; 
						
						//采集按键信息
						collect_button();
					}

				}
				else 
				if(collectkey.button_count == 2)
				{
					collectkey.button_count = 0;
					collectkey.button_mode = 2;
					
					
					//采集按键信息
					collect_button();
				}
				else
				{
					collectkey.button_count = 0;//判断完,到时间照样得button_count 清零,为后面做准备
				}
			}

		}		
	}
		
}

按键存储到队列

程序体量较少的时候, 我们本可以直接进行根据当前按下的按键进行处理的, 但是为了后续程序体量变大, 当有更紧急的事情时, 我们只能先记录下按键, 延后处理用户按键

利用队列收集和处理按键(思路)(ctrl 加鼠标左键,快速跳转)

image-20240830185422679

void collect_button(void)
{
	//采集按键信息
	key.button_order[key.button_send] = collectkey.button_mode;
	if(key.button_send >= 5) // 说明5的位置已经被填了,下个该0了(++后,所指位置为空)
	{
		key.button_send = 0;
	}
	else
	{
		key.button_send++;
	}			
}	

用户根据按键信息构造交互界面

回去(ctrl 加鼠标左键,快速跳转)

我们已经收集了足够多的按键, 我们现在就充当上帝, 来构造这些菜单之间的交互.

利用已有队列按键, 构造菜单交互(ctrl 加鼠标左键,快速跳转)

构造菜单:

我们把握一个点,就是我们当前处于什么模式, 在此页面下, 对于不同的按键,单击, 双击和长按, 我希望, 把此时的模式, 跳转到另外的哪个界面。我们提前画好

img

image-20240830190552259

当我们构造完菜单后, 我们就知道, 当我们处于在哪个页面的时候, 按下哪个按键, 应该跳转到哪个状态.

1.直接上代码, 当我们队列收集到按键的时候, 就意味着队首和队尾不重合, 那么就代表有指令了

image-20240830233857829

2.我们取出队首的按键, 先区分是什么类型

img

img

利用switch来区分, 我们对按键进行了编号(短按 1, 双按 2, 长按 3)

① 短按 1

在不同的模式下, 短按是不同的功能, 所以我们要先判断当前是处于哪个模式

处于智能模式 Now_mode = 1 , 说明是环境界面和阈值界面之间切换

处于音乐菜单模式 Now_mode = 2 ,说明我们要切换选择的歌曲,

处于音乐播放界面 Now_mode = 3 , 说明我们要暂停和开始播放歌曲

case 1:
    switch(oled.Now_mode)
    {
        case 1:
            oled.Now_environment ^= 1;
            break;
        case 2:
            if(oled.music_choice == 3)
            {
                oled.music_choice = 0;
            }
            else
            {
                oled.music_choice++;
            }
            break;
        case 3:
            oled.music_button ^= 1;
            break;
    }
    break;

② 双击

image-20240830234955583

我们设置的功能是, 双击播放选择到的音乐,

在音乐菜单和播放界面, 进行切换, 所以我们就把模式在两个界面之间切换

case 2:
    switch(oled.Now_mode)
    {
        case 1:break; //没想好干啥
        case 2:
            oled.Now_mode = 3;//进入音乐播放界面
            oled.music_button = 1;//播放按钮打开
            break;
        case 3:
            oled.Now_mode = 2;//返回音乐菜单
            oled.music_button = 0;//播放按钮关闭
            break;
        default:break;					
    }	

    break;

③ 长按

image-20240830235054376

长按就是我们在智能模式和音乐模式之间切换, 如果我们在播放模式 Now_mode = 3, 我们也是切换到智能模式 Now_mode = 1

case 3:
    if(oled.Now_mode == 1)
    {
        oled.Now_mode = 2;//切换为音乐模式
        oled.music_choice = 0;//音乐界面默认第一个
        oled.music_button = 0;//播放按钮也关闭
    }
    else
    {
        oled.Now_mode = 1;//切换为智能模式
        oled.Now_environment = 0;//界面默认第一个(环境信息)
        oled.music_button = 0;//播放按钮也关闭

    }				

    break;	

4.当处理完一个按键后, 我们按键队列的队首就要向后移动一位

//然后跳到下一位,接着判断是否重合
if(key.button_deal >= 5) //队尾始终为空,向目标前进
{
    key.button_deal = 0;
}
else
{
    key.button_deal++;
}

根据处理后界面状态, 选择对应的界面

回去(ctrl 加鼠标左键,快速跳转)

我们是如何知道我们用户按下了按键呢? 是因为我们检测到了 有新的按键, 就是队首和队尾不重合, 我们同时也定义了一个变量 button_down 代表按下了按键

image-20240830235625718

当我们判断出按键按下,说明我们切换了界面 Now_mode, 我们就根据当前处于的模式,去选择对应的界面

image-20240830235839554

我们先判断 Now_mode 是 1, 2,还是3, 从而选择提前准备好的界面

image-20240831092404114

当 Now_mode = 1,

当处于智能模式下,我们再判断 Now_environment 是 0 还是 1

0: 处于环境信息界面 (oled.now_moding = 1;)

1: 阈值信息界面 (oled.now_moding = 2;)

当 Now_mode = 2,

当前处于音乐菜单模式, 我让当前用户界面oled.now_moding = 6;(本页面, 我们是歌单,因为单击也会反选歌曲,我们不可能为每个歌曲都添加一个页面, 而是通过判断选择的是哪首歌曲music_choice, 而得出是否反选对应的区域)

当 Now_mode = 3,

当前处于音乐播放模式, 并且根据我们画的页面菜单得出, 我们只能从歌单模式转换过来,所以我们已经有了对应选择的歌曲, 存放在music_choice里面(因为有四首歌曲, 我们不能为每首歌曲都加一个页面, 所以根据music_choice的数值, 通过if判断要显示哪些歌名就可以了)

image-20240831092559805 image-20240831092710641

界面已经确定, 开始刷新显示

经过上面 按键类型判断-> 队列收集按键-> 构造菜单页面 ->根据队列按键,挑选对应状态 -> 根据对应模式, 选择对应界面

我们已经能够确定, 我们要刷新是我们定义好的哪个界面, 我们 根据对应标号, 刷新底层就可以了

image-20240831093651420

在最终接管控制的并不是 now_moding, 而是 now_menu, 这是因为, 我们这是预警系统, 如果有警报, 我们要以应急页面为主, 所以加了预警标志, 当正常情况下,预警没触发, 我们就按照用户的now_moding , 当预警触发, 我们就按照预警系统, 警报解除后, 通过判断, 仍会按照用户界面刷新

image-20240831094236982

预警系统接手界面(详细介绍)(ctrl 加鼠标左键,快速跳转)

image-20240830113321720

工程下载与演示

由于涉及到的步骤较多, 并且变量容易眼花缭乱, 所以我把按键收集构成了结构体, 还有oled处理也变成了结构体, 这样就方便进行归纳和移植. 下面我把相关的变量给大家整理一下, 并且带领大家熟悉一下, 上面的步骤, 走个流程, 为后续移植做出保障.

按键类型判断变量

功能代码 按键识别(ctrl 加鼠标左键,快速跳转)

//按键收集结构体重构
struct CollectKey
{
	_Bool key_down;//按键是否按下
	uint8_t button_count;	//按键在一个判断周期内的计数次数
	uint16_t count;	//定时器计数毫秒数(按键按下时长)
	uint8_t button_mode;	//按键模式:无-0 , 单击- 1, 双击 - 2, 长按 - 3
	_Bool button_atom; //当前正在处理,不可捕捉新按键信息
	
};
extern struct CollectKey collectkey; // 声明 collectkey 为外部变量

image-20240831102439706

按键收集队列

功能代码:按键收集队列(ctrl 加鼠标左键,快速跳转)

struct Key_storage
{
	//接受按键信息的容器
	u8 button_order[6];
	//控制输入按键信息的变量
	u8 button_send;
	//控制处理按键信息的变量
	u8 button_deal;	
};	

extern struct Key_storage key;	

image-20240831102923347

我们加入了结构体, 就代表着这些变量就是为这一个功能区域服务的, 我们就不用担心后面变量乱飞了.

OLED菜单交互

功能代码:OLED菜单交互(ctrl 加鼠标左键,快速跳转)

//按键收集结构体重构
struct OLED_Choose
{
	//控制按键是否按下(按键模式下, 按下按键则代表着要刷新页面)
	uint8_t button_down;	//按下 1 , 没按下 0 

	//oled界面
	//********************重构变量********************
	//****************模式切换************
	uint8_t Now_mode;	//模式选择:1-智能模式, 2-音乐模式 , 3-播放模式
	uint8_t Now_environment;	// 0- 环境信息界面, 1 阈值信息界面
	uint8_t music_choice;	// 0-最强王者参见,1-我和我的祖国,2-热爱105℃的你, 3刚好遇见你
	uint8_t music_button;	// 0-暂停, 1-播放

	//*****************菜单控制******
	u8 now_moding;	//处理得出的结果的moing(没有警报的情况下, 是直接赋值给now_menu)
	u8 now_menu;	//当前的oled菜单

};

extern struct OLED_Choose oled;

因为用户不是一直在按按键, 所以我们也只是在用户按下按键的时候, 刷新选择界面, 所以我们要定义一个变量 button_down, 在队列中有按键的时候, 才刷新界面, 令 button_down = 1

image-20240831163820132

image-20240830233857829

当我们按下按键的时候, 我们button_down = 1的时候, 我们就根据构造的菜单和界面, 通过判断当前按下的是什么按键, 在当前界面是什么功能, 从而跳转到哪个模式, 进行修改界面状态

image-20240831164252043

image-20240830190552259

根据菜单,刷新界面

我们在上一步已经根据按键类型和当前处于的界面,进行了页面跳转, 那么我们现在就需要根据当前的菜单编号状态, 来确定 接下来用户要刷新的是哪个界面了

选择界面(ctrl 加鼠标左键,快速跳转)

因为中间涉及到一个预警界面, 所以我们用户定义的界面, 不能直接传入刷新函数进行刷新, 就多定义了一个变量 now_moding , (方便预警系统插手)

控制最终刷新的界面的变量是 now_menu

image-20240831165455742

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2096716.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【无标题】书生大模型实战营闯关记录----第十二关:茴香豆:企业级知识库问答工具;Web版茴香豆使用教程;茴香豆本地化标准版部署搭建教程;知识库构建问答

0 茴香豆介绍 茴香豆 是由书生浦语团队开发的一款开源、专门针对国内企业级使用场景设计并优化的知识问答工具。在基础 RAG 课程中我们了解到,RAG 可以有效的帮助提高 LLM 知识检索的相关性、实时性,同时避免 LLM 训练带来的巨大成本。在实际的生产和生活…

Yolov5 AI学习笔记

Yolov5 AI学习笔记 环境准备 需要Python的开发环境&#xff0c;安装Anaconda。 Anaconda的一些命令&#xff1a; # 创建虚拟环境 conda create -n yolo_cpu python3.9 # 查看虚拟环境 conda env list # 激活虚拟环境 conda activate <env_name>Yolov5上手 下载源码 …

上下文的弹性

“上下文的弹性”是指在自然语言处理中&#xff0c;模型对输入文本的上下文信息的理解和利用能力。它描述了模型在处理文本时能够根据上下文信息来调整其输出的程度。具有弹性上下文的模型可以更好地处理自然语言中的语义和语境&#xff0c;从而提供更准确和有用的回答。这种弹…

这才是老板喜欢的产品经理简历

速创猫今天给大家分享的是应届毕业生产品经理简历优化案例&#xff0c;希望对大家求职有帮助。速创猫总结了以下七条简历制作干货&#xff0c;希望对大家有帮助&#xff1a; 明确目标岗位&#xff1a;在简历的开头&#xff0c;明确指出你申请的职位&#xff0c;让招聘者一眼就能…

聚焦AI4SE软件工程领域,基于Multi Agent System多智能体系统开发的最新成果,实现软件开发领域的PUGC!

可能有很多小伙伴不了解AI4SE是什么&#xff0c;其实从字意上就不难看出一定是和AI相关。 AI4SE&#xff08;Artificial Intelligence for Software Engineering&#xff09;是指将人工智能技术应用于软件工程领域。 其核心目标是通过自动化和智能化技术降低软件开发的复杂性…

MySQL 官方高可用方案 InnoDB Cluster

文章目录 前言1. 方案构成2. 使用要求和限制3. 集群部署3.1 环境说明3.2 软件下载3.2 MySQL Server 安装3.3 MySQL Shell 安装3.4 检测实例是否符合资格3.5 创建集群3.6 向集群中添加节点3.7 配置 Router3.8 测试验证 4. 集群运维4.1 Router 服务管理4.2 MySQL 服务管理4.3 集群…

结束Linux特定端口上的进程,超级实用!

在使用 Linux 时&#xff0c;您可能会遇到特定端口被进程占用的情况。了解如何终止在特定端口上运行的进程可能非常有用&#xff0c;特别是对于排除故障和有效地管理系统而言。 本文中&#xff0c;我们将完成查找端口号、识别使用该端口的进程以及安全停止该进程。 详细步骤 …

【最全深度学习介绍】基本概念、类型、应用、优缺点、与机器学习区别是什么?

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发…

PostgreSQL技术内幕6:PostgreSQL索引技术

文章目录 0. 简介1.PG索引类型介绍2. PG创建索引说明及索引属性查看2.1 创建说明2.2 查看方式2.2.1 查看PG默认支持的索引及对应的Handler类型2.2.2 查看B树索引属性 3. 索引选择3.1 查看索引情况 4.PG中B-Tree索引原理4.1 页存储结构 5.索引代码分析5.1 不同索引结构解析5.1.1…

郑州建站网页手机版

随着移动互联网的迅猛发展&#xff0c;越来越多的企业和个人开始重视手机网页的建设。在郑州&#xff0c;这一趋势尤为明显&#xff0c;搭建手机网页已经成为提升品牌形象和吸引客户的重要方式。本文将就郑州建站网页手机版的现状、重要性及注意事项进行探讨。 首先&#xff0c…

LabVIEW中升采样和降采样

升采样 (Upsampling) 和 降采样 (Downsampling) 是信号处理中的两种常见操作&#xff0c;用于改变信号的采样率。它们在数字信号处理&#xff08;DSP&#xff09;和许多工程应用中非常重要&#xff0c;尤其是在处理不同采样率的数据流时。 升采样 (Upsampling) 升采样是增加信…

SpringBoot 引入使用消息队列RabbitMQ通信 配置连接 无路由模式

介绍 请先对Rabbitmq的用户和权限配置好在进行往下的操作 依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency>下面将演示最简单的例子不过路由 生产者 -&g…

IDEA工程连接不上Mysql数据库

在进行项目创建的时候&#xff0c;发现IDEA工程连接不上数据库。即使登陆的用户名和密码全部正确也报错。 在服务里发现Mysql80没有启动 直接利用命令提示符启动&#xff0c;也显示启动失败。 通过 netstat -ano | findstr :3306 查看数据库默认端口3306的占用情况&#…

【CSP:202009-2】风险人群筛查(Java)

题目链接 202009-2 风险人群筛查 题目描述 求解思路 本题的数据量并不大&#xff0c;直接模拟即可。x和y表示每次读取的坐标点。res1表示经过高风险场地的人数&#xff0c;res2表示在高风险场地停留的人数。s用来记录连续在高风险场地停留的点数。r1表示是否经过高风险场地&…

【学习笔记】卫星通信NTN 3GPP标准化进展分析(三)- 3GPP Release17 内容

一、引言&#xff1a; 本文来自3GPP Joern Krause, 3GPP MCC (May 14,2024) Non-Terrestrial Networks (NTN) (3gpp.org) 本文总结了NTN标准化进程以及后续的研究计划&#xff0c;是学习NTN协议的入门。 【学习笔记】卫星通信NTN 3GPP标准化进展分析&#xff08;一&#xff…

CART算法原理及Python实践

一、CART算法原理 CART&#xff08;Classification And Regression Trees&#xff09;算法是一种用于分类和回归任务的决策树学习技术。它采用贪心策略递归地划分数据集&#xff0c;以构建一棵二叉决策树。CART算法的原理可以概括为以下几个关键步骤&#xff1a; 1. 特征选择与…

如何在 CentOS 6 上安装 Nagios

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 Status: 已弃用 本文涵盖的 CentOS 版本已不再受支持。如果您目前正在运行 CentOS 6 服务器&#xff0c;我们强烈建议升级或迁移到受支持…

什么是单片机?为什么要学习单片机?

实现目标 1、熟悉单片机定义、特点、应用场景、发展历史等&#xff1b; 2、理解为什么要学习单片机&#xff1f;怎样学习单片机&#xff1f; 一、单片机是什么&#xff1f; 1、定义 单片机是集成在一块&#xff08;单&#xff09;芯片上的微型计算机。平时我们把 MCU&#x…

E. Sheep Eat Wolves

https://codeforces.com/gym/104869/problem/E 赛时队友想贪心&#xff0c;贪不了一点&#xff0c;我想了数学办法每次都送固定的发现送过去就不满足了 赛后补&#xff0c;暴力做O&#xff08;n4) 至少要几次才能把安全所有羊送到对岸去 考虑最短路,bfs,用数组存下所有状态 …

nerf论文代码理解

近年来&#xff0c;生成式AI(AGI)快速发展&#xff0c;各类生成式模型层出不群&#xff0c;但我更觉得具有物理意义的生成式AI将是未来革命性技术。因此也在抽空看看关于三维重建的知识&#xff0c;这篇文章就是记录我对nerf的理解。 一、论文理解 首先&#xff0c;需要知道ne…