LCD1602多级菜单实现

键盘线程扫描按键,键值入队列,菜单处理线程取按键消息,支持多级子菜单,菜单上下滚动,当前菜单光标指示,进入子菜单,返回父菜单
源文件

/** 
* @file         display.c 
* @brief        显示处理函数. 
* @details  	菜单处理,显示信息处理,菜单回调函数处理. 
* @author       ken deng
* @date     	2019-08-16
* @version  	A001 
* @par Copyright (c):  
*       
* @par History:          
*   version: ken deng, 2019-08-16, 建立\n 
*/  
#include "system.h"
#include "display.h"
#include "daemon.h"
#include "main.h"
#include "ioc.h"
#include "menu_func.h"
#include "lcdxx.h"
#include "config.h"
#include "pm.h"
#include "main.h"

#ifdef DISPLAY_DEBUG
    #define	display_log(...) do{if(DEBUG(DEBUG_ENABLE)){DBG_LOG("[DISPLAY](%ld) ",__LINE__);DBG_LOG(__VA_ARGS__);}}while(0)
    #define display_usr(...) do{if(DEBUG(DEBUG_ENABLE)){DBG_LOG("[DISPLAY] ");DBG_USR(__VA_ARGS__);}}while(0)
    #define display_err(...) do{if(DEBUG(DEBUG_ENABLE)){DBG_LOG("[DISPLAY] ");DBG_ERR(__VA_ARGS__);}}while(0)
    #define display_dump(...) if(DEBUG(DEBUG_ENABLE)){DBG_DUMP(__VA_ARGS__);}
#else
    #define display_log(...)
    #define display_usr(...)
    #define display_err(...)
    #define display_dump(...)
#endif
	
DISPLAY_MENU_STATUS_T	menu_status;
TaskHandle_t			display_handle;
extern const DISPLAY_MENU_T admin_card_menu[];

///<菜单项中同时存在回调函数及子菜单,优先执行回调函数
const DISPLAY_MENU_T device_menu_admin[]={
	"RF"			 ,admin_card_menu,NULL,NULL,
	"USB"			 ,admin_card_menu,NULL,NULL,
	"WI-FI"			 ,admin_card_menu,NULL,NULL,
};

const DISPLAY_MENU_T device_menu_system[]={
	"RF"			 ,system_card_menu,NULL,NULL,
	"USB"			 ,system_card_menu,NULL,NULL,
	"WI-FI"			 ,system_card_menu,NULL,NULL,
};

///<管理员卡菜单
const DISPLAY_MENU_T admin_card_menu[]={
	"GENERATE KEYS  ",NULL,NULL,NULL,
	"UPL SETTINGS   ",NULL,NULL,menu_func_upl_settings,
	"UPL AUDIO - SD ",NULL,NULL,NULL,
	"RETRIEVE LOGS  ",NULL,NULL,NULL,
	"VIEW LOGS      ",NULL,NULL,menu_func_view_log,
	"SAVE LOGS TO SD",NULL,NULL,NULL,
	"EABLE CARD UNIT",NULL,NULL,menu_activate_vote_card,
	"DABLE CARD UNIT",NULL,NULL,NULL,
	"VIEW CRT TALLY ",NULL,NULL,menu_func_view_crt_tally,
	"VIEW CRT ALARM ",NULL,NULL,menu_func_view_crt_alarm,
	"DEACT CRT ALARM",NULL,NULL,menu_func_deact_crt_alarm,
	"DEACT TMP-ALARM",NULL,NULL,menu_func_deact_tmp_alarm,
	"BATTERY STATUS ",NULL,NULL,NULL,
	"RETRIEVE TALLY ",NULL,NULL,menu_func_retrieve_tally,
	"END ELECTION   ",NULL,NULL,menu_func_admin_end_election,
	"TALLY TO SD    ",NULL,NULL,NULL,
	"RESET DEVICE   ",NULL,NULL,menu_func_admin_reset_device,
	"SEL COMM MODE  ",NULL,NULL,menu_func_sel_comm_mode,
	"EXIT           ",NULL,NULL,menu_func_exit,
};

///<系统管理员卡菜单
const DISPLAY_MENU_T system_card_menu[]={
	"UPL SETTINGS   " ,NULL,NULL,NULL,
	"SCAN DEVICES   " ,NULL,NULL,NULL,
	"CNCT TO NEW EVT" ,NULL,NULL,menu_func_cnct_new_evt,
	"RETRIEVE TALLY " ,NULL,NULL,menu_func_retrieve_tally,
	"RESET DEVICE   " ,NULL,NULL,menu_func_sys_reset_device,
	"DLD NEW SETTING" ,NULL,NULL,NULL,
	"END ELECTION   ",NULL,NULL,menu_func_sys_end_election,
	"SEL COMM MODE  ",NULL,NULL,menu_func_sel_comm_mode,
	"EXIT           ",NULL,NULL,menu_func_exit,
};

///<菜单中的项数
const DISPLAY_MENU_SIZE_T		menu_size[]={
	&admin_card_menu[0],		sizeof(admin_card_menu)/sizeof(DISPLAY_MENU_T),
	&device_menu_admin[0],		sizeof(device_menu_admin)/sizeof(DISPLAY_MENU_T),
	&system_card_menu[0],		sizeof(system_card_menu)/sizeof(DISPLAY_MENU_T),
	&device_menu_system[0],		sizeof(device_menu_system)/sizeof(DISPLAY_MENU_T),
};

/** 
* 显示公司名称及设备型号. 
* 无. 
* @param[in]   无. 
* @param[out]  无.  
* @retval  无
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-08-16创建 
*/
void display_start_up(void){
	
	char buf[DISP_BUF_SIZE];
	snprintf(buf,DISP_BUF_SIZE,"     LAXTON     \n%s",config.electoral_commission);
	display_info_in(buf,0);
	
	snprintf(buf,DISP_BUF_SIZE,"\n%s",DEVICE_MODEL);
	display_info_in(buf,0);
}

/** 
* 切换到信息显示. 
* 无. 
* @param[in]   无. 
* @param[out]  无.  
* @retval  无
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-09-27创建 
*/
void display_exit_to_info(void){
	
	menu_status.curr_menu = NULL;
	menu_status.state = MENU_INFO;
	menu_status.screen_refresh = 1;
}

/** 
* 切换到菜单显示. 
* 无. 
* @param[in]   无. 
* @param[out]  无.  
* @retval  无
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-09-27创建 
*/
void display_exit_to_menu(void){
	
	menu_status.menu_func = NULL;
	menu_status.refresh_flag = 1;
	menu_status.state = MENU_SELECT;
}

/** 
* 切换菜单. 
* 无. 
* @param[in]   menu:菜单句柄. 
* @param[out]  无.  
* @retval  无
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-08-16创建 
*/
void display_switch_menu(const DISPLAY_MENU_T *menu){
	
	if( menu != NULL ){
		menu_status.init_flag = 1;
	}
	menu_status.menu_func = NULL;
	menu_status.curr_menu = (DISPLAY_MENU_T	*)menu;
	menu_status.state = MENU_SELECT;
}

/** 
* 获取当前菜单的显示项数. 
* 无. 
* @param[in]   menu:菜单句柄. 
* @param[out]  无.  
* @retval  返回菜单项数
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-08-16创建 
*/
uint8_t display_menu_size(DISPLAY_MENU_T *menu){
	
	for( uint8_t i=0; i<sizeof(menu_size)/sizeof(DISPLAY_MENU_SIZE_T); i++ ){
		if( menu_size[i].menu==menu ){
			return menu_size[i].num;
		}
	}
	return 0;
}

/** 
* 显示消息入队列. 
* 无. 
* @param[in]   info:显示消息,confirm:1需按键确认、0:不需按键确认. 
* @param[out]  无.  
* @retval  无
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-08-16创建 
*	   2019-10-30 ken deng:入队列不成功,申请内存空间丢失
*	   2019-11-19 ken deng:增加是否按键确认
*/
void display_info_in(char *info, uint8_t confirm)
{
	
	if( strlen(info)>DISP_BUF_SIZE )
		info[DISP_BUF_SIZE-1] = 0;
	else
		info[strlen(info)] = 0;
	
	uint8_t *buf;
	buf = malloc(DISP_BUF_SIZE);
	
	if( buf ){
		DISPLAY_MSG_T msg;
		msg.buf = buf;
		msg.confirm = confirm;
		memcpy(buf, info, DISP_BUF_SIZE);
		if( xQueueSend(menu_status.disp_queue,&msg,0) !=pdPASS ){
			free(buf);
		}
	}
}

/** 
* 只有指定菜单回调下才入队列显示消息. 
* 无. 
* @param[in]   func:菜单回调函数,info:显示消息. 
* @param[out]  无.  
* @retval  无
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-12-27创建 
*/
FUN_STATUS_T display_screen_info_in( void(*func)(uint8_t), char *info )
{
	if( is_menu_func(func) ){
		display_info_in(info,0);
		return FUN_OK;
	}
	else{
		return FUN_ERROR;
	}
}

/** 
* 显示消息出队列. 
* 无. 
* @param[in]   info:显示消息. 
* @param[out]  无.  
* @retval  1:有新显示消息,0:没有新显示消息
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-08-16创建 
*/
FUN_STATUS_T display_info_out(char *info, uint8_t *confirm)
{
	DISPLAY_MSG_T msg;
	if( xQueueReceive(menu_status.disp_queue,&msg,0)==pdPASS ){
		memcpy(info,msg.buf,DISP_BUF_SIZE);
		*confirm = msg.confirm;
		free(msg.buf);
		return FUN_OK;
	}
	return FUN_NONE;
}

/** 
* 按键消息入队列. 
* 无. 
* @param[in]   button_msg:按键消息. 
* @param[out]  无.  
* @retval  无  
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-08-16创建 
*/
void display_button_in(BUTTON_MSG_T *button_msg)
{
	xQueueSend(menu_status.button_queue,button_msg,0);
//	ioc_led_blink_cnt(button_msg->value,100,1);
}

/** 
* 按键消息出队列. 
* 无. 
* @param[in]   button_msg:按键消息. 
* @param[out]  无.  
* @retval  FUN_OK:有按键消息,
* @retval  0: 没有新消息
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-08-16创建 
*/
FUN_STATUS_T display_button_out(BUTTON_MSG_T *button_msg)
{
	if( xQueueReceive(menu_status.button_queue,button_msg,0)==pdPASS ){
		return FUN_OK;
	}
	return FUN_NONE;
}

/** 
* 分屏显示长字符串消息. 
* 无. 
* @param[in]   seg:段号,info:消息字符串. 
* @param[out]  无.  
* @retval  无.
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-08-16创建 
*	   Ken Deng于2019-12-03 改为全屏拷贝
*/
void display_info_seg(uint8_t seg, char *info)
{
	uint8_t len,pos=0,line=0;;
	len = strlen(info);
	char buf[DISP_BUF_SIZE];
	memset(buf,0,DISP_BUF_SIZE);
	
	if( (seg*DSIP_TEXT_LEN + DSIP_TEXT_LEN) < len ){
		
		memcpy(buf,info+seg*DSIP_TEXT_LEN,DSIP_TEXT_LEN);
	}
	else{
		
		memcpy(buf,info+seg*DSIP_TEXT_LEN,len%DSIP_TEXT_LEN);
	}
	
	display_info_in(buf,0);
}

/** 
* 解码字符格式. 
* 将解码的数据放入相应的行显示缓冲区. 
* @param[in]   str:源字符串. 
* @param[out]  p_disp_dec_str:输出显示字符串缓冲区.  
* @retval  无
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-09-27创建 
*/
void display_decode_string(char *str, DISP_DEC_STR_T *p_disp_dec_str)
{	
	memset(p_disp_dec_str,0,sizeof(DISP_DEC_STR_T));
	//sscanf(str,"%[^\n]\n%[^\n]\n%[^\n]\n%[^\n]\n",p_disp_dec_str->str[0],p_disp_dec_str->str[1],p_disp_dec_str->str[2],p_disp_dec_str->str[3]);
	uint8_t idx=0;
	uint8_t substr_len;
	while( *str ){
		substr_len = 0;
		while( *str != '\n' && *str && substr_len<LCDXX_COLUMN_NUMBER ){
			p_disp_dec_str->str[idx][substr_len++] = *str;
			str++;
		}
		if( *str == '\n' ){
			str++;
		}
		
		p_disp_dec_str->len[idx] = substr_len;
		
		idx ++;
		
		if( idx >= LCDXX_ROW_NUMBER ) 
			break;
	}
}

/** 
* 进度条显示个数. 
* 如果中间有几个没有设置,会自动补上. 
* @param[in]   step:显示第几个. 
* @param[out]  无.  
* @retval  无
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-09-27创建 
*/
void display_process_bar(uint8_t step)
{
	if( step>LCDXX_COLUMN_NUMBER )
		return;
	while( step>menu_status.process_step ){
		lcdxx_progress_bar(4,menu_status.process_step++);
	}
}

/** 
* 判断菜单的范围. 
* 无. 
* @param[in]   curr_item_ptr:菜单当前项指针. 
* @param[out]  DISPLAY_MENU_T 菜单指针.  
* @retval  无.
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-12-04创建 
*/
DISPLAY_MENU_T * disp_menu_rang(DISPLAY_MENU_T *curr_item_ptr,int8_t value)
{
	static int32_t len = admin_card_menu+sizeof(admin_card_menu)/sizeof(DISPLAY_MENU_T);
	if( (curr_item_ptr >= admin_card_menu) && (curr_item_ptr < (admin_card_menu+sizeof(admin_card_menu)/sizeof(DISPLAY_MENU_T))) ){
		
		///<超出菜单底部
		if( (curr_item_ptr+value) >= (admin_card_menu+sizeof(admin_card_menu)/sizeof(DISPLAY_MENU_T)) ){
			return (DISPLAY_MENU_T *)((curr_item_ptr-admin_card_menu+value)%sizeof(admin_card_menu)/sizeof(DISPLAY_MENU_T)+admin_card_menu);
		}
		///<超出菜单顶部
		else if( (curr_item_ptr+value) < admin_card_menu ){
			int8_t num;
			num = (curr_item_ptr-admin_card_menu)+value+sizeof(admin_card_menu)/sizeof(DISPLAY_MENU_T);
			return (DISPLAY_MENU_T *)(admin_card_menu+num);
		}
		else{
			return curr_item_ptr+value;
		}
	}
	else if( curr_item_ptr >= system_card_menu && curr_item_ptr < (system_card_menu+sizeof(system_card_menu)/sizeof(DISPLAY_MENU_T)) ){
		
		///<超出菜单底部
		if( (curr_item_ptr+value) >= (system_card_menu+sizeof(system_card_menu)/sizeof(DISPLAY_MENU_T)) ){
			return (DISPLAY_MENU_T *)((curr_item_ptr-system_card_menu+value)%sizeof(system_card_menu)/sizeof(DISPLAY_MENU_T)+system_card_menu);
		}
		///<超出菜单顶部
		else if( (curr_item_ptr+value) < system_card_menu ){
			int8_t num;
			num = (curr_item_ptr-system_card_menu)+value+sizeof(system_card_menu)/sizeof(DISPLAY_MENU_T);
			return (DISPLAY_MENU_T *)(system_card_menu+num);
		}
		else{
			return curr_item_ptr+value;
		}
	}
}

/** 
* 显示菜单项. 
* 无. 
* @param[in]   curr_item_ptr:菜单当前项指针,max_item:菜单的最大项数,dir:正向或反向显示. 
* @param[out]  无.  
* @retval  无.
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-08-16创建 
*/
void display_menu_item(DISPLAY_MENU_T *curr_item_ptr, uint8_t cursor, uint8_t max_item, uint8_t dir)
{
	uint8_t max;
	int8_t pos,new_cursor;
	DISPLAY_MENU_T *new_item_ptr;
	if( max_item > DISP_MENU_ITEMS ){
		max = DISP_MENU_ITEMS;
	}
	else{
		max = max_item;
	}
	lcdxx_clear_screen();
	new_cursor = cursor;
	if( dir ){
		for( uint8_t i=0; i<max; i++ ){
			
			if( i%cursor )
				pos = ((max-i)%cursor)*(-1);
			else
				pos = (i);
			
			new_item_ptr = disp_menu_rang(curr_item_ptr,pos);
			lcdxx_display_string(1, new_cursor, new_item_ptr->item_str,0);
			
			new_cursor++;	
			if(new_cursor==5)
				new_cursor=1;
		}
	}
	else{
		for( uint8_t i=0; i<max; i++ ){
			
			if( i/cursor && i%cursor )
				pos = (i%cursor)*(-1);
			else
				pos = (i);
			
			new_item_ptr = disp_menu_rang(curr_item_ptr,pos);
			lcdxx_display_string(1, new_cursor, new_item_ptr->item_str,0);
			
			new_cursor++;	
			if(new_cursor==5)
				new_cursor=1;
		}
	}
}

/** 
* 显示菜单光标位置. 
* 无. 
* @param[in]   curror:光标位置. 
* @param[out]  无.  
* @retval  无.
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-08-16创建 
*/
void display_menu_curror(uint8_t curror)
{
	for( uint8_t i=1; i<=DISP_MENU_ITEMS; i++ ){
		if( i==curror )
			lcdxx_display_string(0, curror, ">",0);
		else
			lcdxx_display_string(0, i, " ",0);
	}
}

/** 
* 解释文本数据并显示在相应的行上. 
* 无. 
* @param[in]   text:文本指针,clear_screen:1清屏,0只清行. 
* @param[out]  无.  
* @retval  无.
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-10-22创建 
*/
void display_text(char *text,uint8_t clear_screen)
{
	DISP_DEC_STR_T	disp_dec_str;
	
	if( clear_screen )
		lcdxx_clear_screen();
			
	display_decode_string(text,&disp_dec_str);
	
	if( disp_dec_str.len[0]!=0 ){
		
		if( clear_screen==0 )
			lcdxx_clear_row(LCDXX_ROW1);
		lcdxx_dispaly_data(LCDXX_ROW1,0,(uint8_t*)&disp_dec_str.str[0],disp_dec_str.len[0]);
	}
	
	if( disp_dec_str.len[1]!=0 ){
		
		if( clear_screen==0 )
			lcdxx_clear_row(LCDXX_ROW2);
		lcdxx_dispaly_data(LCDXX_ROW2,0,(uint8_t*)&disp_dec_str.str[1],disp_dec_str.len[1]);
	}
	
	if( disp_dec_str.len[2]!=0 ){
		
		if( clear_screen==0 )
			lcdxx_clear_row(LCDXX_ROW3);
		lcdxx_dispaly_data(LCDXX_ROW3,0,(uint8_t*)&disp_dec_str.str[2],disp_dec_str.len[2]);
	}
	
	if( disp_dec_str.len[3]!=0 ){
		
		if( clear_screen==0 )
			lcdxx_clear_row(LCDXX_ROW4);
		lcdxx_dispaly_data(LCDXX_ROW4,0,(uint8_t*)&disp_dec_str.str[3],disp_dec_str.len[3]);
	}
}

/** 
* 处理按键消息上菜单选择,菜单回调函数处理. 
* 无. 
* @param[in]   无. 
* @param[out]  无.  
* @retval  无.
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-08-16创建 
*/
void display_menu_select(void)
{
	static DISPLAY_MENU_T	*curr_item_ptr;
	
	uint8_t	display_msg[DISP_BUF_SIZE];
	static uint8_t confirm = 0;
	DISP_DEC_STR_T	disp_dec_str;
	
	if( menu_status.disp_time>DISP_STAY_TIME*5 ){
				
		if( FUN_OK == display_info_out(&display_msg,&confirm) ){
			
			menu_status.disp_time = 0;
			menu_status.rtc_time = 0;
			menu_status.init_flag = 1;
			
			display_text((char*)display_msg,1);
			
			//<如需按键确认显示信息
			if( confirm ){
				lcdxx_clear_row(LCDXX_ROW4);
				lcdxx_dispaly_data(LCDXX_ROW4,0,(uint8_t*)"CON|CAN KEY RTN", 15);
			}
		}
		else{

			if( menu_status.curr_menu != NULL && menu_status.menu_func==NULL ){//&& uxQueueMessagesWaiting(menu_status.disp_queue)==0 ){
					
				if( menu_status.init_flag ){
					menu_status.init_flag = 0;
					menu_status.max_item = display_menu_size(menu_status.curr_menu);
					menu_status.max_item_less = menu_status.max_item>DISP_MENU_ITEMS?DISP_MENU_ITEMS:menu_status.max_item;
					menu_status.curr_item = 0;
					menu_status.cursor = 1;

					curr_item_ptr = &menu_status.curr_menu[menu_status.curr_item];
					display_menu_item(curr_item_ptr, menu_status.cursor, menu_status.max_item,1);
					lcdxx_display_string(0, 1, ">",0);
				}
				
				if( menu_status.refresh_flag ){
					
					menu_status.refresh_flag = 0;
					lcdxx_clear_screen();
					curr_item_ptr = &menu_status.curr_menu[menu_status.curr_item];
					
					if( menu_status.curr_item == 0 ){
						display_menu_item(curr_item_ptr, menu_status.cursor, menu_status.max_item,0);
						lcdxx_display_string(0, 1, ">",0);
					}
					else if( menu_status.curr_item == menu_status.max_item-1 ){
						
						display_menu_item(curr_item_ptr, menu_status.cursor, menu_status.max_item,1);
						lcdxx_display_string(0, menu_status.cursor, ">",0);
					}
					else{
						display_menu_item(curr_item_ptr, menu_status.cursor, menu_status.max_item,0);
						lcdxx_display_string(0, menu_status.cursor, ">",0);
					}
				}
				
				
				BUTTON_MSG_T button_msg;
				if(FUN_OK == display_button_out(&button_msg)){
					
					switch( button_msg.value ){
						
						case UP_KEY_MSG:
						
							if( menu_status.cursor>1 )
								menu_status.cursor--;
						
							if( menu_status.curr_item>0 )
								menu_status.curr_item--;
							else{
								menu_status.curr_item = menu_status.max_item-1;
								menu_status.cursor = menu_status.max_item_less;
								///<小于屏幕行数菜单不刷新
								if( menu_status.max_item>DISP_MENU_ITEMS )
									menu_status.refresh_flag = 1;
							}
							
							curr_item_ptr = &menu_status.curr_menu[menu_status.curr_item];
							
							display_menu_curror(menu_status.cursor);

							if( (menu_status.curr_item <= menu_status.max_item-DISP_MENU_ITEMS) && (menu_status.cursor==1) ){

								lcdxx_display_string(1, 1, curr_item_ptr->item_str,1);
								lcdxx_display_string(1, 2, (curr_item_ptr+1)->item_str,1);
								lcdxx_display_string(1, 3, (curr_item_ptr+2)->item_str,1);
								lcdxx_display_string(1, 4, (curr_item_ptr+3)->item_str,1);
								
		//						display_menu_item(curr_item_ptr, DISP_MENU_ITEMS, 1);
							}
							
							break;
						
						case DOWN_KEY_MSG:
						
							if( menu_status.cursor<DISP_MENU_ITEMS )
								menu_status.cursor++;
						
							if( menu_status.curr_item<menu_status.max_item-1 )
								menu_status.curr_item++;
							else{
								menu_status.cursor = 1;
								menu_status.curr_item = 0;
								///<小于屏幕行数菜单不刷新
								if( menu_status.max_item>DISP_MENU_ITEMS )
									menu_status.refresh_flag = 1;
							}
							
							curr_item_ptr = &menu_status.curr_menu[menu_status.curr_item];
							DISPLAY_MENU_T	*item_ptr = curr_item_ptr;
							
							if( (menu_status.curr_item >= DISP_MENU_ITEMS) && (menu_status.cursor>=4) ){
								
								lcdxx_display_string(1, 1, (curr_item_ptr-3)->item_str,1);
								lcdxx_display_string(1, 2, (curr_item_ptr-2)->item_str,1);
								lcdxx_display_string(1, 3, (curr_item_ptr-1)->item_str,1);
								lcdxx_display_string(1, 4, curr_item_ptr->item_str,1);
								
		//						display_menu_item(curr_item_ptr, DISP_MENU_ITEMS, 0);
							}
							display_menu_curror(menu_status.cursor);
							break;
						
						case CANCEL_KEY_MSG:
							if( curr_item_ptr->parent_menu )
								display_switch_menu(curr_item_ptr->parent_menu);
							break;
						
						case ENTER_KEY_MSG:
							if( curr_item_ptr->func!=NULL ){
								menu_status.menu_func = curr_item_ptr->func;
								menu_status.state = MENU_FUNC;
								display_func_refresh();
							}
							else if( curr_item_ptr->child_menu )
								display_switch_menu(curr_item_ptr->child_menu);
							break;
						
						default:
							break;
					}
				}
			}
		}
	}
	else if( menu_status.disp_time<255 ){
		
		if( confirm ){
			BUTTON_MSG_T button_msg;
			if( (FUN_OK == display_button_out(&button_msg)) ){
				if( button_msg.value == CANCEL_KEY_MSG || button_msg.value == ENTER_KEY_MSG ){
					menu_status.disp_time = DISP_STAY_TIME*5+1;
				}
			}
		}
		else{
			menu_status.disp_time++;
		}
	}
}

/** 
* 显示队列中消息显示. 
* 无. 
* @param[in]   无. 
* @param[out]  无.  
* @retval  无.
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-08-16创建 
*/
void display_menu_info(void)
{
	uint8_t	display_msg[DISP_BUF_SIZE];
	static uint8_t confirm;
	DISP_DEC_STR_T	disp_dec_str;
	
	if( menu_status.disp_time>DISP_STAY_TIME ){
				
		if( FUN_OK == display_info_out(&display_msg,&confirm) ){
			
			menu_status.disp_time = 0;
			menu_status.rtc_time = 0;
			menu_status.screen_refresh = 1;
			
			display_text((char*)&display_msg,1);
			
			//<如需按键确认显示信息
			if( confirm ){
				lcdxx_clear_row(LCDXX_ROW4);
				lcdxx_dispaly_data(LCDXX_ROW4,0,(uint8_t*)"CON|CAN KEY RTN", 15);
			}
		}
		else{
			
			if( menu_status.curr_menu != NULL )
				menu_status.state = MENU_SELECT;
			
			if( menu_status.rtc_time++>=10 ){
				
				menu_status.rtc_time = 0;
				if( 1 ){
				
					switch( menu_status.info_screen ){
						case SCREEN_START_UP:
						{
							char disp_buf[17];
							snprintf(disp_buf,17,"%02d:%02d:%02d %02d%02d%02d ",sys_status.rtc.Hours,sys_status.rtc.Minutes,sys_status.rtc.Seconds,\
							sys_status.rtc.Day, sys_status.rtc.Month, sys_status.rtc.Year);
							lcdxx_display_string(0,LCDXX_ROW1,disp_buf,1);
							
							if( menu_status.screen_refresh ){
								menu_status.screen_refresh = 0;
								lcdxx_display_string(0,LCDXX_ROW2,config.electoral_commission,1);
								
								sprintf(disp_buf,"EBB V %s",DEVICE_MODEL);
								lcdxx_display_string(0,LCDXX_ROW3,disp_buf,1);
								
								//sprintf(disp_buf,);
								lcdxx_display_string(0,LCDXX_ROW4,"SELF TEST OK",1);
							}
							
							break;
						}
						case SCREEN_PRE_SETTING:
						{
							char disp_buf[17];
							snprintf(disp_buf,17,"%02d:%02d:%02d %02d%02d%02d ",sys_status.rtc.Hours,sys_status.rtc.Minutes,sys_status.rtc.Seconds,\
								sys_status.rtc.Day, sys_status.rtc.Month, sys_status.rtc.Year);
							lcdxx_display_string(0,LCDXX_ROW1,disp_buf,1);
							
							if( menu_status.screen_refresh ){
								menu_status.screen_refresh = 0;
								lcdxx_display_string(0,LCDXX_ROW2,"Unprogrammed",1);
								
								lcdxx_clear_row(LCDXX_ROW3);
								lcdxx_clear_row(LCDXX_ROW4);
							}
							break;
						}
						case SCREEN_PRE_ELECTION:
						{
							char disp_buf[17];
							snprintf(disp_buf,17,"%02d:%02d:%02d BAT:%d",sys_status.rtc.Hours,sys_status.rtc.Minutes,sys_status.rtc.Seconds,pm_read_bat());
							lcdxx_display_string(0,LCDXX_ROW1,disp_buf,1);
							
							if( menu_status.screen_refresh ){
								menu_status.screen_refresh = 0;
								lcdxx_display_string(0,LCDXX_ROW2,config.electoral_commission,1);
								
								lcdxx_display_string(0,LCDXX_ROW3,"  PRE-ELECTION",1);
								lcdxx_clear_row(LCDXX_ROW4);
							}
							break;
						}
						case SCREEN_DEVICE_SETTING_UPLOAD:
						{
							char disp_buf[17];
							snprintf(disp_buf,17,"%02d:%02d:%02d BAT:%d",sys_status.rtc.Hours,sys_status.rtc.Minutes,sys_status.rtc.Seconds,pm_read_bat());
							lcdxx_display_string(0,LCDXX_ROW1,disp_buf,1);
							
							if( menu_status.screen_refresh ){
								menu_status.screen_refresh = 0;
								lcdxx_display_string(0,LCDXX_ROW2,"EVD DEVICE REG",1);
								
								lcdxx_clear_row(LCDXX_ROW3);
								
								lcdxx_display_string(0,LCDXX_ROW4,"Upload Settings",1);
							}
							break;
						}
						case SCREEN_DURING_ELECTION:
						{
							char disp_buf[17];
							snprintf(disp_buf,17,"%02d:%02d:%02d %02d%02d%02d ",sys_status.rtc.Hours,sys_status.rtc.Minutes,sys_status.rtc.Seconds,\
							sys_status.rtc.Day, sys_status.rtc.Month, sys_status.rtc.Year);
							lcdxx_display_string(0,LCDXX_ROW1,disp_buf,1);
							
							sprintf(disp_buf,"Bat: %d%%",pm_read_bat());
							lcdxx_display_string(0,LCDXX_ROW2,disp_buf,1);
							
							if( menu_status.screen_refresh ){
								menu_status.screen_refresh = 0;
							
								lcdxx_display_string(0,LCDXX_ROW3,"CAU: Active",1);
							
								lcdxx_display_string(0,LCDXX_ROW4,"Election Running",1);
							}
							break;
						}
						case SCREEN_POST_ELECTION:
						{
							char disp_buf[17];
							snprintf(disp_buf,17,"%02d:%02d:%02d %02d%02d%02d ",sys_status.rtc.Hours,sys_status.rtc.Minutes,sys_status.rtc.Seconds,\
							sys_status.rtc.Day, sys_status.rtc.Month, sys_status.rtc.Year);
							lcdxx_display_string(0,LCDXX_ROW1,disp_buf,1);
							
							if( menu_status.screen_refresh ){
								menu_status.screen_refresh = 0;
								lcdxx_clear_row(LCDXX_ROW2);
								
								lcdxx_display_string(0,LCDXX_ROW3,"ELECTION CLOSED",1);
								
								lcdxx_clear_row(LCDXX_ROW4);
							}
							break;
						}
					}
				}
			}
		}
	}
	else if( menu_status.disp_time<255 ){
		
		if( confirm ){
			BUTTON_MSG_T button_msg;
			if( (FUN_OK == display_button_out(&button_msg)) ){
				if( button_msg.value == CANCEL_KEY_MSG || button_msg.value == ENTER_KEY_MSG ){
					menu_status.disp_time = DISP_STAY_TIME*5+1;
				}
			}
		}
		else{
			menu_status.disp_time++;
		}
	}
}

/** 
* 菜单回调函数处理. 
* 无. 
* @param[in]   无. 
* @param[out]  无.  
* @retval  无.
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-08-16创建 
*/
void display_menu_func(void)
{
	uint8_t	display_msg[DISP_BUF_SIZE];
	static uint8_t confirm;
	DISP_DEC_STR_T	disp_dec_str;
	
	if( menu_status.disp_time>DISP_MENU_TIME ){
		if( menu_status.menu_func!=NULL && menu_status.info_complete==1 ){
				
			BUTTON_MSG_T button_msg;
			if( FUN_OK != display_button_out(&button_msg) )
				button_msg.value = 0xff;
			menu_status.menu_func(button_msg.value);
		}
	}
	
	if( menu_status.disp_time>DISP_MENU_TIME ){
				
		if( FUN_OK == display_info_out(&display_msg,&confirm) ){
			
			menu_status.disp_time = 0;
			
			menu_status.info_complete = 0;
			
			display_text((char*)&display_msg,0);

			//<如需按键确认显示信息
			if( confirm ){
				lcdxx_clear_row(LCDXX_ROW4);
				lcdxx_dispaly_data(LCDXX_ROW4,0,(uint8_t*)"CON|CAN KEY RTN", 15);
			}
		}
		else{
			menu_status.info_complete = 1;
		}
	}
	else if( menu_status.disp_time<255 ){
		
		if( confirm ){
			BUTTON_MSG_T button_msg;
			if( (FUN_OK == display_button_out(&button_msg)) ){
				if( button_msg.value == CANCEL_KEY_MSG || button_msg.value == ENTER_KEY_MSG ){
					menu_status.disp_time = DISP_STAY_TIME+1;
					display_func_refresh();
				}
			}
		}
		else{
			menu_status.disp_time++;
		}
	}
}

/**
* 显示主线程. 
* 处理菜单操作,消息显示,菜单回调函数. 
* @param[in]   pvParameters:线程参数. 
* @param[out]  无.  
* @retval  无  
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-08-16创建 
*/
void display_task( void *pvParameters )
{	
	wait_for_startup();
	
	TickType_t ticks = xTaskGetTickCount();
	uint8_t		refresh_time=100;
	while(1){
		
		//if( urg_info_display() ){
		if( 1 ){
			
			switch( menu_status.state ){
				case MENU_SELECT:
					display_menu_select();
					refresh_time = 10;
					break;
				case MENU_FUNC:
					display_menu_func();
					refresh_time=10;
					break;
				case MENU_INFO:
					display_menu_info();
					refresh_time = 50;
					break;
			}
		}
		vTaskDelay(refresh_time);
	}
}

/** 
* 显示初始化. 
* 液晶接口,液晶初始化,显示队列,按键队列初始化. 
* @param[in]   无. 
* @param[out]  无.  
* @retval  0  成功 
* @retval  1   错误  
* @par 标识符 
*      保留 
* @par 其它 
*      无 
* @par 修改日志 
*      ken deng于2019-08-16创建 
*/
FUN_STATUS_T display_init(void)
{	
	lcdxx_init();
//	HAL_Delay(1000);
	
	memset(&menu_status,0,sizeof(DISPLAY_MENU_STATUS_T));
	menu_status.disp_queue = xQueueCreate(DISP_QUEUE_MAX,sizeof(DISPLAY_MSG_T));
	if( menu_status.disp_queue ==0 ){
		
		display_log("can not create display queue!!!\r\n");
		return FUN_ERROR;
	}
	menu_status.button_queue = xQueueCreate(KEY_QUEUE_MAX,sizeof(BUTTON_MSG_T));
	if( menu_status.button_queue ==0 ){
		
		display_log("can not create key queue!!!\r\n");
		return FUN_ERROR;
	}
	menu_status.disp_time = 255;
	if( xTaskCreate( display_task, "DISPLAY", DISPLAY_STACK_SIZE, NULL, DISPLAY_TASK_PRIORITY, &display_handle ) != pdPASS ){
		return FUN_ERROR;
	}
	daemon_from_value = display_handle;
	
	display_exit_to_info();
	
//	static DISP_DEC_STR_T	disp_dec_str;
//	display_decode_string("hello world\n1234567890abcdefghijklmnopqrstuvwxyz12345678901234567890",&disp_dec_str);
	
	return FUN_OK;
}

头文件

#ifndef __DISPLAY_H__
#define __DISPLAY_H__

#include "stdint.h"
#include "stdbool.h"
#include "system.h"
#include "macro_def.h"

#define		DISP_QUEUE_MAX		10
#define		KEY_QUEUE_MAX		10
#define 	DISP_BUF_SIZE		68
#define		DISP_STAY_TIME		20
#define 	DISP_MENU_TIME		5
#define		DISP_MENU_ITEMS		4
#define 	DSIP_TEXT_LEN		64
#define 	DISP_LINE_LEN		16

enum{
	ENTER_KEY_MSG=0,
	CANCEL_KEY_MSG,
	DOWN_KEY_MSG,
	UP_KEY_MSG,
};

typedef enum{
	LOGO_UI,
	VOTE_UI,
	SETTING_UI,
}UI_T;

typedef struct {
	char		*item_str;
	void const  *parent_menu;
	void const  *child_menu;
	void		(*func)(uint8_t);
}DISPLAY_MENU_T;

typedef struct {
	const DISPLAY_MENU_T	*menu;
	uint8_t			num;
}DISPLAY_MENU_SIZE_T;

typedef struct {
	//char		disp_buf[DISP_BUF_SIZE];
	uint8_t		confirm;
	void*		buf;
}DISPLAY_MSG_T;

typedef struct {
	uint8_t 	value;
	uint8_t		event;
}BUTTON_MSG_T;

typedef enum {
	MENU_INFO,
	MENU_SELECT,
	MENU_FUNC,
}MENU_STATE_T;

typedef enum{
	SCREEN_INIT=0,
	SCREEN_START_UP,
	SCREEN_PRE_SETTING,
	SCREEN_PRE_ELECTION,
	SCREEN_DEVICE_SETTING_UPLOAD,
	SCREEN_DURING_ELECTION,
	SCREEN_POST_ELECTION,
}DISP_SCREEN_T;

typedef struct{
	char		str[4][16];
	uint8_t		len[4];
}DISP_DEC_STR_T;

typedef struct {
	
	DISPLAY_MENU_T	*curr_menu;
	
	void			(*menu_func)(uint8_t);
	
	uint8_t			curr_item;
	uint8_t			cursor;
	uint8_t			max_item;
	uint8_t			max_item_less;
	uint8_t			init_flag;
	uint8_t			refresh_flag;
	uint8_t			disp_time;
	uint8_t			rtc_time;
	
	uint8_t			process_step;
	uint8_t			process_row;
	
	uint8_t			req_admin_card2;
	uint8_t			admin_card2_present;
	
	uint8_t			info_complete;				///<信息显示完成
	
	uint8_t			func_fitst;
	
	MENU_STATE_T	state;
	
	DISP_SCREEN_T	info_screen;
	uint8_t			screen_refresh;
	
	QueueHandle_t	*disp_queue;
	QueueHandle_t	*button_queue;
	
}DISPLAY_MENU_STATUS_T;

#define is_req_admin_card2()	(menu_status.req_admin_card2==1)			///<请求第二个管理员卡
#define set_req_admin_card2()	(menu_status.req_admin_card2=1)				///<设置请求第二个管量员卡
#define clr_req_admin_card2()	(menu_status.req_admin_card2=0)				///<清空请求第二个管量员卡

#define set_admin_card2_present()	(menu_status.admin_card2_present=1)		///<设置第二张管理员卡插入
#define clr_admin_card2_present()	(menu_status.admin_card2_present=0)		///<删除第二张管理员卡插入
#define is_admin_card2_present()	(menu_status.admin_card2_present==1)	///<判断第二张管理员卡插入

#define is_menu_state(sta)			(menu_status.state==sta)

#define switch_screen(sce)			{menu_status.info_screen=sce;menu_status.screen_refresh=1;}

#define display_func_refresh()		(menu_status.func_fitst = 1)

#define is_menu_func(func)			(menu_status.menu_func==func)

extern const DISPLAY_MENU_T admin_card_menu[];
extern const DISPLAY_MENU_T maintenance_card_menu[];
extern const DISPLAY_MENU_T system_card_menu[];
extern DISPLAY_MENU_STATUS_T	menu_status;

extern void display_switch_menu(const DISPLAY_MENU_T *item);
extern FUN_STATUS_T display_init(void);
extern void display_info_in(char *info, uint8_t confirm);
extern FUN_STATUS_T display_screen_info_in( void(*func)(uint8_t), char *info );
extern void display_log_version(void);
extern void display_exit_to_menu(void);
extern void display_exit_to_info(void);
extern void display_button_in(BUTTON_MSG_T *button_msg);
extern FUN_STATUS_T display_button_out(BUTTON_MSG_T *button_msg);
extern void display_info_seg(uint8_t seg, char *info);
extern void display_card_pull_out(void);
extern void display_process_bar(uint8_t step);
extern void display_text(char *text,uint8_t clear_screen);
#endif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

纵向深耕

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

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

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

打赏作者

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

抵扣说明:

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

余额充值