STM32并口屏应用实例:点亮你的显示世界之应用篇

系列内容章节如下:
STM32并口屏应用实例:点亮你的显示世界之原理篇
STM32并口屏应用实例:点亮你的显示世界之程序篇
STM32并口屏应用实例:点亮你的显示世界之应用篇


  在嵌入式系统开发中,显示设备是与用户交互的重要界面之一。STM32作为一款功能强大的微控制器,支持多种外设接口,其中并口屏的驱动应用非常广泛,其抗干扰性比其他接口更为出色。本文将通过一个实际案例,介绍如何使用STM32驱动并口屏,并展示其原理和实现过程。

PS:本文所涉及的显示框架设计不仅仅局限于并口通信,串行接口也是适用的。本文是在显示驱动之上的应用设计。文章名称只是为了承接系列文章的统一设计风格。

  书接上文STM32并口屏应用实例:点亮你的显示世界之程序篇我们继续进行并口屏的应用程序设计。本文介绍一套适合于小屏幕的上下级菜单式切页显示的方案。同时我们在前面也有一篇关于旋转编码器应用的文章:STM32旋转编码器应用实例。我们结合旋转编码器来实现显示的切换和参数的修改。

一、程序设计思路

  总体思路是面向页面对象编程,每一个页面有唯一ID,通过ID的索引把各个界面联系起来,可以从上级页面跳到下级页面,也可从下级页面返回上级页面。

二、页面操作设计

2.1 页面对象

  我们引入面向对象编程的思想,以每一个页面作为一个对象,这个对象有如下操作:

1、进入当前页面,初始化当前页面数据,如果没有可以忽略;
2、周期刷新页面内容,这个一般是必备的;
3、退出页面,当通过按键或者旋钮开关调节切页的时候,执行退出操作,如果不需要刻意忽略。

通过以下宏和页面索引结构体来实现每一个页面的定义。

typedef enum
{
    PAGE_INIT = 0,
    PAGE_SCAN,
	PAGE_EXIT,
} PageScanType_e;

typedef struct
{
	uint8_t current;
	uint8_t exit;		//返回索引号
	uint8_t enter;		//确认索引号
	void (*current_operation)(PageScanType_e opt);			/* 当前扫描页面 */
	void (*exit_operation)(PageScanType_e opt);				/* 退出切换页面 */
	void (*enter_operation)(PageScanType_e opt);			/* 确认切换页面 */
	
} page_table;

current:当前页面的索引号
exit:按下“退出“按钮后要跳转到的页面索引号
enter:按下“确认“按钮后要跳转到的页面索引号
current_operation:当前页面的索引号要执行的显示函数,这是一个函数指针
exit_operation:退出页面的索引号要执行的显示函数,这是一个函数指针
enter_operation:确认页面的索引号要执行的显示函数,这是一个函数指针

注意:因为我们在这里结合了旋转编码器,所以不需要上下切换按键,旋转编码器数值变化时,通过页面刷新不同数据达到上下切换的效果(上下切属于同一级页面,甚至可以看作同一个页面,上下切换只是显示这个页面的各个局部内容)。

2.2 页面集合

  我们以字母代替页面显示函数名称进行说明,实际应用时,可以自己编写页面显示函数名称。

#define FUN_1_BASE   1
#define FUN_A1_BASE  2
#define FUN_A21_BASE 3
#define FUN_F21_BASE 7
#define FUN_F31_BASE 8
#define FUN_F41_BASE 9

page_table table[]=
{
	//第0层
	{ 0, 2, 0,(*fun_0) ,(*fun_0) ,(*fun_a1)},
	{ 1, 1, 1,(*fun_1) ,(*fun_1) ,NULL},		/* 告警弹窗:任意界面可弹窗,按任意键返回弹窗前的界面 */
    //第1层	
	{ 2, 0, 2,(*fun_a1),(*fun_0) ,NULL},		/* 由于不使用单独页面 enter 需要 base + offset 再当前页面函数就行修改 */

    //第2层
	{ 3, 2, 3,(*fun_a21),(*fun_a1),NULL},	
	{ 4, 2, 4,(*fun_c21),(*fun_a1),NULL},
	{ 5, 2, 5,(*fun_d21),(*fun_a1),NULL},			
	{ 6, 2, 6,(*fun_e21),(*fun_a1),NULL},				
	{ 7, 2, 7,(*fun_f21),(*fun_a1),NULL},		/* 密码验证成功才能进入下一层fun_f31,否则提示密码输入错误 */	
	
    //第3层:厂家参数设置界面(输入密码后)
	{ 8, 7, 8,(*fun_f31),(*fun_f21),NULL},
	
	//第4层
	{ 9, 8, 9,(*fun_f41),(*fun_f31),NULL},
	{10, 8,10,(*fun_f42),(*fun_f31),NULL},
	{11, 8,11,(*fun_f43),(*fun_f31),NULL},	
};

  从上面的数组可以看出,程序设计将页面分为多级:

第0层是主界面:按“确认”可进入菜单界面,按其他键无效,可以填入本界面显示函数指针;
第1层是菜单界面:按“退出”可返回主界面,按其他键无效,可填本界面显示函数指针或者填`NULL`,执行时会判断;
第2层是各个菜单下的显示界面;操作不在赘述;
第3层是厂家菜单界面,在第二层输入密码后才能跳转到;
第4层是厂家餐点下的显示界面。

  然后,还可以看到第0层还有一个告警弹窗界面,这个弹窗界面其实并不独属于某一个层级,咋任何层级,只要有告警触发时,从任意界面跳转到告警弹窗界面,所以我就将告警弹窗界面暂时罗列在第0层。因为告警跳转不符合数组中的跳转逻辑,所以我们在跳转告警界面时需要保留跳转上下文,退出告警弹窗界面时能够返回原来的界面(可以是任意层的界面)。如下,定义的宏#define FUN_1_BASE 1就是告警弹窗界面的索引。
在这里插入图片描述

2.3 页面调用

  页面调用通过维护一个页面控制结构体来完成:

typedef struct PageCtrl
{
	uint16_t 		func_index; 
	uint16_t 		last_index;
	uint16_t 		pop_up_index;		/*!< 弹窗前界面 */
   	bool            isAuth;				/*!< 菜单选项是否确认鉴权 */
	bool			isAdmin;			/*!< 菜单选项是否已经登录权限界面 */
	void (*current_operation_index)(PageScanType_e opt);			/*!< 当前周期扫描页面 */
	
} PageCtrl_t;

其中,

1、func_index为当前页面索引,页面触发切换后更新;
2、last_index上一个页面索引,页面切换完成后更新;
3、pop_up_index弹窗前的页面索引,产生告警弹窗时更新;(上一节提到的告警弹窗界面)
4、isAuth菜单选项是否确认鉴权(根据需要,如果需要秘钥才能进入的界面,可以通过该标志位设定)
5、isAdmin菜单选项是否已经登录权限界面
6、void (*current_operation_index)(PageScanType_e opt);			/*!< 当前周期扫描页面 */

  页面调用通过在while(1)循环周期中,判断页面索引是否发生改变,如果索引改变,就顺序执行:

1、退出上一个界面,执行操作函数,参数为PAGE_EXIT;(本示例不需要)
2、更新当前页面操作函数指针;
3、进入下一个界面,执行操作函数,参数为PAGE_INIT;
4、更新当前界面索引;
5、周期执行当前页面操作函数,刷新页面。
		if (sPageCtrl.func_index != sPageCtrl.last_index)
		{.....
			//(*sPageCtrl.current_operation_index)(PAGE_EXIT);		/*退出上一个页面操作函数*/
			sPageCtrl.current_operation_index = table[sPageCtrl.func_index].current_operation;
			(*sPageCtrl.current_operation_index)(PAGE_INIT);		/*进入下一个页面操作函数*/
			sPageCtrl.last_index = sPageCtrl.func_index;
		}
		(*sPageCtrl.current_operation_index)(PAGE_SCAN);			/*执行当前页面操作函数*/

2.4 页面切换逻辑

  在文章开头提到的STM32旋转编码器应用实例本实例是通过旋转编码器来更新页面显示区域,通过按键来进入和退出页面。
按键逻辑如下:

static void SetKeyFunCB(IoKeyType_e key, KeyAction_e action)
{
    if (action == KEY_ACTION_PRESS)
    {
		printf("KEY_ACTION_PRESS key = %d\n", key);
        if (key == IO_MENU)			/* 返回 */
        {
			sPageCtrl.func_index = table[sPageCtrl.func_index].exit;
        }
        else if (key == IO_ENTER)	/* 确认 */
        {
			sPageCtrl.func_index = table[sPageCtrl.func_index].enter;
        }
        else if (key == IO_EC11)
        {
            places_step++;
			if(places_step>=4)
			{
				places_step = 0;
			}
        }
		if(FUN_1_BASE == sPageCtrl.func_index)			/* 弹窗后任意按键返回弹窗前界面 */
		{
			GW_INFO("func_index = pop_up_index = %d\n", sPageCtrl.pop_up_index);
			sPageCtrl.func_index = sPageCtrl.pop_up_index;
		}
    }
}

通过IO_MENU按键返回上一级界面,更新页面索引为当前页面的table[sPageCtrl.func_index].exit;
通过IO_ENTER按键进入下一级界面,更新页面索引为当前页面的table[sPageCtrl.func_index].enter;
通过IO_EC11按键切换数值更新位,这为旋转编码器的按键,配合此按键快速更改数据;

2.5 页面示例

static void fun_a1(PageScanType_e opt)
{
	switch (opt)
	{
		case PAGE_INIT:
		{
			index = 0;
			places_step = 0;
			sPageCtrl.isSelected = false;
			Api_LoadSetHmi(index);
			break;
		}
		case PAGE_SCAN:
		{
			Api_Encoder_GetPara(&index, 0, 4, PLACE_ONES);
			Api_LoadSetHmi(index);
			table[FUN_A1_BASE].enter = FUN_A21_BASE+index;			/* 直接修改下一个页面链接 */
		}
		case PAGE_EXIT:
		{
			break;
		}
	}
}

当前页面为设置菜单页面:

  1. 首次进入该页面是初始化状态case PAGE_INIT:初始化参数值index;
  2. 周期刷新页面,调用Api_Encoder_GetPara(&index, 0, 4, PLACE_ONES);来获取当前选中的菜单,通过旋转编码器可更新index值;应用旋转获取0-4的数值,每次步进1;
    详细使用方法见STM32旋转编码器应用实例
  3. 然后调用LCD显示API接口Api_LoadSetHmi(index);显示内容根据index值来确定;
    显示内容参考:STM32并口屏应用实例:点亮你的显示世界之程序篇采用画布模式,更新画布后全屏刷新。
/****************************************************************************************************
函数名称: 	Api_LoadSetHmi       
功能描述: 	设置页面加载
输 入:   	
输 出:   	无
********************************************************************************************************/
void Api_LoadSetHmi(int index)
{
	LcdTxType_e type = LCD_CANVASBUFF;
	memset(sg_arrFrameBuffer, 0x00, sizeof(sg_arrFrameBuffer));
	Api_LoadSelectPoint(type, index, 4);
	if(index<4)
	{
		Api_display_label_16x16(type, 20, 0,   ContorlM16x16,   sizeof(ContorlM16x16)/32);
		Api_display_label_16x16(type, 20, 2, CommSetting16x16,  sizeof(CommSetting16x16)/32);
		Api_display_label_16x16(type, 20, 4,   PowerPara16x16,  sizeof(PowerPara16x16)/32);	
		Api_display_label_16x16(type, 20, 6,   DisPara16x16,   sizeof(ContorlM16x16)/32);
	}
	else
	{
		Api_display_label_16x16(type, 20, 0,   ManufacturerPara16x16,   sizeof(ManufacturerPara16x16)/32);	
	}

	Api_disp_256x64(0, 1, sg_arrFrameBuffer[0]);
}

三、效果展示

  通过上述代码,我们成功驱动了8位并口的液晶屏,实现了参数的显示。如下:

特殊符号显示
中英文字符串显示
十六进制数据显示
十进制数据显示

在这里插入图片描述

四、总结

  本文通过一个实际案例,展示了如何使用STM32驱动8位并口屏。通过硬件连接和软件编程,我们实现了屏幕的初始化、数据传输和显示功能。并口屏以其快速的数据传输能力,在嵌入式显示领域具有广泛的应用前景。希望本文能为你的项目提供参考和启发。本文提及的并口屏显示应用是针对这一系列文章所取的题名,所涉及的显示框架设计不仅仅局限于并口屏,其他串行接口也是适用的。

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Geek__1992

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值