/********************************************************************
版权所有：	
文件版本： V1.00
文件名称： LcdComm.c
生成日期： 2008年10月9日
作    者：
使用范围：

功    能：与面板通信处理程序
更新信息：
更新日志1：
修改者:
修改日期:
修改内容:
修改原因:
*********************************************************************/
/*------------------------------- 头文件 --------------------------------------
*/
#include "head.h"
 
/*------------------------------- 宏定义 --------------------------------------
*/

enum
{
	HMI_RECV,
	HMI_SEND,
};

//#define HMI_V2
/*------------------------------ 类型结构 -------------------------------------
*/

/*------------------------------ 全局变量 -------------------------------------
*/
UART_COMM g_tUartHMI;  	// 与面板通信的串口数据结构定义

struct hmi g_hmi[HMI_COMM_TYPE];		//面板帧结构定义

LCD_COMM_DEF g_tLcdComm; // 菜单处理数据与面板通信的结构定义
DWORD dHMIDiStatus = 0Xffffffff;
u8 *hzk_table;
static u8 hmi_update=0;
int g_hmi_type;
u8 lcd_dot_type;		// 显示点阵显示类型
int hzk_num;			// 汉字库中汉字个数
int hzk_change_type;	// 当前切换的汉字库类型
uint32_t lcd_comm_delay=0;	// 面板通讯超时

/*------------------------------ 函数声明 -------------------------------------
*/
int hzk_read_file(void);
//static void _reset_uartHMI_link(void);
static void _add_inst_frame(BYTE cmd,BYTE *para,BYTE len);  //len为参数长度
static void _hmi_call(void);
static void _put_key(BYTE key);
static void _hmi_comm_recv(BYTE *ps);
static void _hmi_comm_send(void);
void _hmi_uart_send(void);
static void _send_inst(void);
static int  _send_continue_datas(void);
static void _send_dot_datas(void);


/*------------------------------ 外部函数 -------------------------------------
外部函数供其它实体文件引用，必须仔细检查传入参数的合法性.
*/
void mainImage_Init(void)
{
	u32 len;
	u8 *buf;
	struct file * pfile;
	loff_t pos;

	g_tLcdComm.tImage.pBuf=NULL;
	// 打开文件
	pfile = rt_file_open("/app/data/mainImage.lcd",O_RDONLY ,0);
	if(IS_ERR(pfile))
	{
		//rt_printf("\r\n打开 app/data/mainImage.lcd 失败\r\n");
		return ;
	}
	// 得到文件长度
	len = rt_file_getfile_size(pfile);
	if(len <= 0)
	{
		rt_file_close(pfile,0);
		return ;
	}
	
	// 分配内存
	buf = rt_malloc(len);
	if((buf) == NULL)
	{
		rt_printf("\r\n分配mainImage.lcd内存失败\r\n");
		rt_file_close(pfile,0);
		return ; 	
	}

	// 读出内容
	pos = 0;
	if(rt_file_read(pfile,buf,len,&pos) != len)
	{
		rt_file_close(pfile,0);
		rt_free(buf);
		return ;
	}
	// 关闭文件
	rt_file_close(pfile,0);
	g_tLcdComm.tImage.pBuf=buf;

	{ // 获取主接线图的内容

		WORD num;
		int i;
		D_UNIT_DEF tUnit;
		YC_UNIT_DEF tYc;
		MAIN_IMAGE_DEF *pm=&g_tLcdComm.tImage;
		BYTE *pd=buf;//主接线图的位置
		
		pm->sYxNum=0;
		pm->sYcNum=0;
		
		pm->sImageX=pd[0]+((WORD)pd[1]<<8);pd+=2;	
		pm->sImageY=pd[0]+((WORD)pd[1]<<8);pd+=2;	
		pm->lnImageAddr=pd[0]+((DWORD)pd[1]<<8)+((DWORD)pd[2]<<16)+((DWORD)pd[3]<<24);pd+=4;
		
		num=pd[0]+((WORD)pd[1]<<8);pd+=2;	
		for(i=0;i<num;i++)  //器件初始化
		{
			tUnit.sx=pd[0]+((WORD)pd[1]<<8);pd+=2;	
			tUnit.sy=pd[0]+((WORD)pd[1]<<8);pd+=2;	
			tUnit.yx_di=pd[0]+((WORD)pd[1]<<8);pd+=2;	
			tUnit.yx_attrib=pd[0];pd+=2;
			tUnit.yx_value=0;
			pd+=4;	// 遥控点号	
			tUnit.addr_close=pd[0]+((DWORD)pd[1]<<8)+((DWORD)pd[2]<<16)+((DWORD)pd[3]<<24);pd+=4;
			tUnit.addr_open=pd[0]+((DWORD)pd[1]<<8)+((DWORD)pd[2]<<16)+((DWORD)pd[3]<<24);pd+=4;
			tUnit.addr_third=pd[0]+((DWORD)pd[1]<<8)+((DWORD)pd[2]<<16)+((DWORD)pd[3]<<24);pd+=4;
			pm->tYxUnits[pm->sYxNum++]=tUnit;
			if(pm->sYxNum>=MAX_YX_UNITS)return ;
		}
		num=pd[0]+((WORD)pd[1]<<8);pd+=2;	
		for(i=0;i<num;i++)
		{
			tYc.sx=pd[0]+((WORD)pd[1]<<8);pd+=2;	
			tYc.sy=pd[0]+((WORD)pd[1]<<8);pd+=2;	
			tYc.yc_di=pd[0]+((WORD)pd[1]<<8);pd+=2;
			{
				int j;
				BYTE *p=tYc.fmtstr;
				for(j=0;j<32;j++)
				{
					*p++=*pd++;
				}
				*p='\0';
			}
			pm->tYcUnits[pm->sYcNum++]=tYc;
			if(pm->sYcNum>=MAX_YC_UNITS)return ;
		}
	}

	m_LockIcon[0]=' ';
	m_LockIcon[1]='\0';	
}

int mainimage_exit(void)
{
	if(g_tLcdComm.tImage.pBuf!=NULL)
	{
		rt_free(g_tLcdComm.tImage.pBuf);
	}
	return 0;
}

void lcd_DrawImage(short x,short y,BYTE *ps,unsigned char mode)	//画图标，index为索引值，与按键的属性定义一致
{
	int h,w,head;
	short width,height,num;			//点阵高
	BYTE *pd;
	DOT_DATA_BUF_DEF *pdot=&g_tLcdComm.tDot;

	width=(ps[1]<<8)+ps[0];
	height=(ps[3]<<8)+ps[2];	
	if(width>SCREENWIDTH||height>SCREENHEIGHT||(x<0||x>SCREENWIDTH)||(y<0||y>SCREENHEIGHT))
	{
		rt_printf("\r\n image error !width=%d height=%d",width,height);
		return;
	}
	head=pdot->head;
	pd=pdot->dot[head].dat;
	num=0;
	ps+=4;

	pdot->dot[head].x=x;
	pdot->dot[head].y=y;
	pdot->dot[head].w=width;
	pdot->dot[head].mode=mode;
	pdot->dot[head].ex=x;	

	for(h=0;h<height;h++)
	{
		for(w=0;w<width/8;w++)
		{


			pd[num++]=*ps++;
			if(num>=230)
			{
				bool bline;// 换行
				pdot->dot[head].num=num;
				pdot->head++;
				pdot->head&=LCD_MAX_DOT_MASK;
				if(pdot->head==pdot->tail) /*缓冲区满了*/
				{
					pdot->tail++;
					pdot->tail&=LCD_MAX_DOT_MASK;
				}
				head=pdot->head;
				pd=pdot->dot[head].dat;
				bline=((w+1)>=width/8)?true:false;

				pdot->dot[head].x=x;
				pdot->dot[head].y=(bline)?(h+1):h;
				pdot->dot[head].w=width;
				pdot->dot[head].mode=mode;
				pdot->dot[head].ex=(bline)?x:(x+(w+1)*8);
				num=0;
			}
		}

	}
	
	if(num>0)
	{
		pdot->dot[head].num=num;
		pdot->head++;
		pdot->head&=LCD_MAX_DOT_MASK;
		if(pdot->head==pdot->tail) /*缓冲区满了*/
		{
			pdot->tail++;
			pdot->tail&=LCD_MAX_DOT_MASK;
		}
	}
}

/**************************************************************************
函数名称：lcdcomm_init
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：初始化面板通信
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/

void lcdcomm_init(void)
{
	memset((BYTE *)&g_tLcdComm,0,sizeof(g_tLcdComm));
	memset((BYTE *)&g_tUartHMI,0,sizeof(g_tUartHMI));
	led_init_hmi();
	mainImage_Init();  //主接线图初始化
	mmd_menu_cfg_init();
	mmi_ScreenInit();	
	g_tLcdComm.tInit.flag=true;
	g_tLcdComm.bRecv=true;
	g_tLcdComm.bCall = true;
	lcd_comm_delay = ustimer_get_origin();
	g_dw_led[0]=0;
	g_dw_led[1]=0;
	g_dw_ledsave[0]=0;  		//  灯状态
	g_dw_ledsave[1]=0;  		//  灯状态
	// 通过面板配置灯，获取灯的状态，使用脉宽==0 黄  ==1 绿 ==2 红 通道0 ==0x55(85) 六键 管理板，其他默认6键控制单元
	{
		u32 slot;
		// 检查状态板是否配置
		slot = equ_get_slot_by_type(BOARD_TYPE_LCD_16LED);
		if(slot ==0)
		{
			slot = equ_get_slot_by_type(BOARD_TYPE_LCD_24LED);
			
			if(slot==0)     //jack.liu 2020 增加26个灯灯板定义
			   slot = equ_get_slot_by_type(BOARD_TYPE_LCD_26LED);	
		}
		if(slot >0)
		{
			int i;
			u8 index;
			struct equ_config_do *ecd;
			ecd = g_equ_config_do;
			for(i=0;i<g_equ_config->do_num;i++)
			{
				if(ecd[i].slot== slot)
				{
					 index=ecd[i].index;
					if(index>=LCD_MAX_LED)continue;
					g_tLcdComm.tInit.para[index]=(BYTE)ecd[i].time_pulse;
				}
			}
		}
	}
}

/**************************************************************************
函数名称：HMI_Comm_App
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：主函数调用与面板通信任务,10ms调用1次，100ms左右调用一次刷屏命令
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
void HMI_Comm_App(void)  /*10毫秒调用一次*/
{
	#if 1
	//正在升级面板程序
	if(is_down_program())
	{
		//有数据
		if(g_tUartHMI.r_head != g_tUartHMI.r_tail)
		{
			//接收处理
			down_program_recv(g_tUartHMI.r_buf[g_tUartHMI.r_tail]);
			g_tUartHMI.r_tail = (g_tUartHMI.r_tail + 1) & (UART_FRAME_MAXCNT - 1);	
	
		} 
		else 
		{
			//接收超时处理
			down_program_recv_timeout();
		}

		//发送处理
		down_program_send();
		return;
	}

	//内置GPRS拨号时有时候占用资源比较多，所以调整为15秒比较保险 
	if ((g_tUartHMI.r_head == g_tUartHMI.r_tail)
		&& (ustimer_get_duration(lcd_comm_delay) > 5*USTIMER_SEC))// 
 	{
 		static bool b_first=false;
		g_tLcdComm.tInit.flag=true;
		g_tLcdComm.b_init_OK = false;
		if(!soe_check(EV_HMIERR))
		{
			//soe_record_ev(EV_HMIERR, 1, 0,0,0 ); 
		}
		
		if(!b_first)
		{
			int i;
			b_first = true;
	
			for(i=0;i<CFG_UART_NUM_MAX;i++)
			{
				// 如果硬件通道不存在，跳过
				if(UART_CHANNEL[i]<0)continue;
	#ifndef CFG_UART_232_DEBUG_TO_COMM	
				// 如果此串口不是系统控制台串口，协议也不是空闲，初始化串口
				if(UART_CHANNEL[i] != g_con_uart_index && tRunPara.tUartPara[i].wProtocol != PROTOCOL_IDLE)
	#else
				if(tRunPara.tUartPara[i].wProtocol != PROTOCOL_IDLE)
	#endif
				{	
					if(tRunPara.tUartPara[i].wProtocol == PROTOCOL_HMI)  // 面板规约
					{
						int ret;
						ret = uart_open(UART_CHANNEL[i],tRunPara.tUartPara[i].dBaud,tRunPara.tUartPara[i].wParity);
						if (ret < 0) {
							printf("%s: outside uart %d open failed!\n", __FUNCTION__, UART_CHANNEL[i]);
						}
						
						RS_Ena_Init(i);
						RS_Recv_Enable(i);
						g_h_boj[0].fd = UART_CHANNEL[i];
						user_conf_init(); //用户配置初始化(面板)
				#ifdef CPU_FUXI
						uart_isr_thread_create(i);
				#endif

						lcdcomm_init();
					}
					
				}
				
			}
		}

 		lcd_comm_delay = ustimer_get_origin();
	}

	if(g_tUartHMI.r_head != g_tUartHMI.r_tail)
	{
		_hmi_comm_recv(g_tUartHMI.r_buf[g_tUartHMI.r_tail]);	
		g_tUartHMI.r_tail = (g_tUartHMI.r_tail + 1) & (UART_FRAME_MAXCNT - 1);
		lcd_comm_delay = ustimer_get_origin();
		g_tLcdComm.bRecv=true;
	}

	_hmi_comm_send();	
	#endif
}

/**************************************************************************
函数名称：Add_Continue_Data
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：菜单驱动调用函数，将要显示的数据存入缓冲区中
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/

void Add_Continue_Data(BYTE x,BYTE y,BYTE *pdat,BYTE len,BYTE mode)
{
	CONTINUE_DATA_BUF_DEF *ptBuf=&g_tLcdComm.tContinue;
	CONTINUE_DATA_DEF *pt=&ptBuf->tBuf[ptBuf->head];
	BYTE cnt;
	
	pt->x=x;	
	pt->y=y;

	if(g_tScreen.lcd_type==LCD_TYPE_160160)
	{
		if(x+len>20)
		{
			len=20-x;
		}
	}
	else if(g_tScreen.lcd_type==LCD_TYPE_128128||g_tScreen.lcd_type==LCD_TYPE_12864)
	{
		if(x+len>16)
		{
			len=16-x;
		}

	}	
	else if(g_tScreen.lcd_type==LCD_TYPE_12232)
	{
		if(x+len>15)
		{
			len=15-x;
		}
	}	
	pt->attrib=len;
	if(mode==1)	
	{
		pt->attrib|=0x80;
	}	
	else if(mode==2)
	{
		pt->attrib|=0x40;
	}
	for(cnt=0;cnt<len;cnt++) // 汉字处理
	{
		pt->dat[cnt]=*pdat;
		pdat++;
	}		
	ptBuf->head++;
	ptBuf->head&=LCD_MAX_CONTINUE_MASK;
	if(ptBuf->head==ptBuf->tail) /*缓冲区满了*/
	{
		ptBuf->tail++;
		ptBuf->tail&=LCD_MAX_CONTINUE_MASK;
	}		
}


/**************************************************************************
函数名称：Add_Clear_Screen
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：清屏命令
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
void Add_Clear_Screen(void)  //mmidrv.c  调用
{
	LCD_COMM_DEF *pt=&g_tLcdComm;
	pt->bClrScreen=true; //清全屏 

	// 清发送缓冲区
	pt->tContinue.head=pt->tContinue.tail=0;

	// 字从首字开始
	pt->tContinue.index=0;
	memset(pt->lcdbuf,0,sizeof(pt->lcdbuf));
	memset(pt->lcdattrib,0,sizeof(pt->lcdattrib));
}

/**************************************************************************
函数名称：add_led_test
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：清屏命令
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
void add_led_test(u8 test_no) 
{
	LCD_COMM_DEF *pt = &g_tLcdComm;

	switch (test_no)
	{
		case TEST_LED:
			pt->bTestLed = true; 
		break;

		case TEST_LCD:
			pt->bTestLcd = true; 
		break;
	}	
}

void add_rmt_pair(u8 pair)
{
	LCD_COMM_DEF *pt = &g_tLcdComm;

	switch (pair)
	{
		case TEST_LED:
			pt->bTeleMatch = true; 
		break;

		case TEST_LCD:
			pt->bTeleClrMatch = true; 
		break;
	}	
}

/**************************************************************************
函数名称：Get_Key
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：获得按键函数
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
BYTE  Get_Key(void)
{
	LCD_KEY_DEF *pt=&g_tLcdComm.tKey;
	BYTE key;

	if(pt->head==pt->tail)return NO_KEY;
	key=pt->keybuf[pt->tail];
	pt->tail++;
	pt->tail&=LCD_KEY_MASK;
	return key;
}
/******************************************************************************
函数名称：	HMI_Board_Recv
函数版本：	01.01
创建作者：	xxxxxx
创建日期：	2014-12-10
函数说明：	接收帧
参数说明：	len:帧长度
					para:接收帧内容
返回值：		无
修改记录：	
*/
void HMI_Board_Recv(int len,BYTE *para)	
{

	UART_COMM *pt=&g_tUartHMI;

	memcpy(pt->r_buf[pt->r_head], para, len);

	pt->r_head = (pt->r_head + 1) &  (UART_FRAME_MAXCNT - 1);
	
}

/******************************************************************************
函数名称：	hzk_read_file
函数版本：	01.01
创建作者：	xxxxxx
创建日期：	2013-08-08
函数说明：	读取汉字库
参数说明：
返回值：	
	0：		成功
	其它：	失败
修改记录：	
*/
int hzk_read_file(void)
{
	u32 len;
	u16 crc16;
	
	struct file * pfile;
	loff_t pos;

	// 打开文件
	pfile = rt_file_open("/app/data/hzk.bin",O_RDONLY ,0);
	if(IS_ERR(pfile))
	{
		return -1;
	}

	// 得到文件长度
	len = rt_file_getfile_size(pfile);
	if(len <= 0)
	{
		rt_file_close(pfile,0);
		return -11;
	}

	// 分配内存
	hzk_table = rt_malloc(len);
	if(hzk_table == NULL)
	{
		rt_file_close(pfile,0);
		return -2; 	
	}

	// 读出内容
	pos = 0;
	if(rt_file_read(pfile,hzk_table,len,&pos) != len)
	{
		rt_file_close(pfile,0);
		rt_free(hzk_table);
		return -3;
	}

	// 关闭文件
	rt_file_close(pfile,0);

	// 检查CRC
	crc16 = CrcStr(hzk_table,len-2);
	if(crc16 != *(u16*)(hzk_table+len-2))
	{
		rt_free(hzk_table);
		hzk_table = NULL;
		return -4;
	}

	hzk_num = (len-2) / 32;
	return 0;

}

/******************************************************************************
函数名称：	hzk_exit
函数版本：	01.01
创建作者：	xxxxxx
创建日期：	2015-04-16
函数说明：	LED配置退出
参数说明：	无
返回值：  	成功返回0.
修改记录：	
*/
int hzk_exit(void)
{
	if(hzk_table)
	{
		rt_free(hzk_table);
	}
	return 0;
}

/*------------------------------ 内部函数 -------------------------------------
内部函数以下划线‘_’开头，不需要检查参数的合法性.
*/


/**************************************************************************
函数名称：ResetUartHMILink
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：复归接收数据结构
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
#if 0
static void _reset_uartHMI_link(void)
{
	g_tUartHMI.cTypeCounter=0;
	g_tUartHMI.cRecvCnt=0;
}
#endif




/**************************************************************************
函数名称：Add_Inst_Frame
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：将发给面板的命令放入待发送指令缓冲区中
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/

static void _add_inst_frame(BYTE cmd,BYTE *para,BYTE len)  //len为参数长度
{
	BYTE i;
	LCD_INST_BUF_DEF *ptBuf=&g_tLcdComm.tInstBuf;
	INST_DEF *pt=&ptBuf->tInst[ptBuf->head];
	BYTE srcbuf[UART_FRAME_LEN];
	BYTE *pd=srcbuf;
	int offset=0;

//	if(pRunSet->bHMI_OLD)
//	{	
//		offset = 2;
//		*pd++=0;// 控制域
//		*pd++=0xCC;  // HMI地址为CC  状态板为55
//	}


	*pd++=cmd;

	for(i=0;i<len;i++)
	{
		*pd++=para[i];
	}


	//组包
	(void)protocol_packet(srcbuf, (int)len+5+offset,  MAIN_BOARD, HMI_BOARD, pt->para);	

	ptBuf->head++;
	ptBuf->head&=LCD_MAX_INST_MASK;
	if(ptBuf->head==ptBuf->tail) //缓冲区满了
	{
		ptBuf->tail++;
		ptBuf->tail&=LCD_MAX_INST_MASK;
	}	

}

void HMI_add_inst_frame(BYTE cmd,BYTE *para,BYTE len)  
{
	_add_inst_frame(cmd,para, len) ;
}



#if 0  // 转换汉字库点阵，stc3100是正序纵显 ,(标准字库是正序横显)
static void _get_hz_dot(char *hzdot,const char *hzk)   
{
	int i;

	for(i=0;i<32;i++)  // 160*160液晶为负序横显
	{
		hzdot[i]=hzk[i];
	}	

	for(m=0;m<8;m++)
	{	
		BYTE dat0=0;
		BYTE dat1=0;
		BYTE dat2=0;
		BYTE dat3=0;		
		for(i=0;i<8;i++)
		{
			dat0|=(((hzk[i*2]>>m)&0x01)<<i);
			dat1|=(((hzk[i*2+1]>>m)&0x01)<<i);
			dat2|=(((hzk[i*2+16]>>m)&0x01)<<i);
			dat3|=(((hzk[i*2+17]>>m)&0x01)<<i);
		}
		hzdot[m]=dat0;
		hzdot[m+8]=dat1;
		hzdot[m+16]=dat2;
		hzdot[m+24]=dat3;
	}
}
#endif
/**************************************************************************
函数名称：HMI_Call
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：主程序召唤面板数据命令
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/

static void _hmi_call(void)
{
	LCD_INST_BUF_DEF *ptBuf=&g_tLcdComm.tInstBuf;
	INST_DEF *pt;

	//缓存区没有数据,发查询命令
	{
		_add_inst_frame((BYTE)HMI_CMD_POLL,  (BYTE*)0, 0);
		
		pt=&ptBuf->tInst[ptBuf->tail];
		//
		Add_Uart_Frame(HMI_BOARD,  (int)pt->para[1] + 6,  pt->para);
		ptBuf->tail = (ptBuf->tail+1) & LCD_MAX_INST_MASK;		
		
	}
}

#ifdef GD_TEST_2021  // 处理复归按键
void lcd_FG_key(BYTE cmd,BYTE key)
{
	static DWORD dTKeyRst=0;	
	static DWORD dTNoKey=0;
	static BYTE oldkey=NO_KEY;
	key&=KE_KEY_MASK;

	if(cmd==DRV_CMD_KEY&&key==HA_KEY_RST)
	{
		if(dTCounter-dTKeyRst>pRunSet->resReuseFuncFilterTime*T_1s)  //复归按键满足防抖要求
		{
			if(key != oldkey)
			{
				oldkey=key;
				// ResetHzLed(0);
				// SignalReset(0,true);
				if(pRunSet->resReuseFuncSelect == 1) //解锁
				{
					g_run_stu.rmtjs=true;
					rt_printf("装置解锁\r\n");
				}
				else if(pRunSet->resReuseFuncSelect == 2)  //复位
				{
					rt_printf("装置复位\r\n");
					watchdog_reset_cpu(2);	//装置复位
				}
			}
			dTKeyRst=dTCounter;
		}
		dTNoKey=dTCounter;
	}
	else
	{
		if(dTCounter-dTNoKey>T_500ms)
		{
			dTKeyRst=dTCounter;
			oldkey=NO_KEY;
		}
	}
}
#endif

/**************************************************************************
函数名称：Put_Key
函数版本：1.00 
作者：   
创建日期：2008.9.1     

函数功能说明：将通信来的按键值放入缓冲中
输入参数：
输出参数：
返回值：
更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因： 
***************************************************************************/
static void _put_key(BYTE lnkey)
{
	LCD_KEY_DEF *pt=&g_tLcdComm.tKey;
	BYTE key=lnkey;
	if(g_tScreen.lcd_type==LCD_TYPE_6KEY) //老版本按键，ESC和ENTER键互换
	{
		key&=KE_KEY_MASK;
		if(key==HA_KEY_ENTER)
		{
			key=HA_KEY_ESC;
		}
		if(key==HA_KEY_ESC)
		{
			key=HA_KEY_ENTER;
		}
		key|=(lnkey&(~KE_KEY_MASK));
	}	
	pt->keybuf[pt->head]=key;
	pt->head++;
	pt->head&=LCD_KEY_MASK;
	if(pt->head==pt->tail) //缓冲区满了
	{
		pt->tail++;
		pt->tail&=LCD_KEY_MASK;
	}

	g_tLcdComm.tLight.light=LIGHT_ON;
	g_tLcdComm.tLight.flag=true;
	g_tScreen.lnLightTime=dTCounter;	
	
}

/**************************************************************************
函数名称：HMI_CommRecv
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：与面板通信的接收处理函数
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
static void _hmi_comm_recv(BYTE *ps)
{
	BYTE *pd=ps;
	int offset=0;

	g_tLcdComm.b_init_OK=true;
	switch(pd[4+offset]&0x1f)
	{
		case DRV_CMD_POWERON:  //上电报文
		{
			if(pd[5+offset]==1)//上电初始化报文
			{
				g_tLcdComm.tInit.flag=true;
			}
			else
			{
				g_tLcdComm.bClrScreen=true;
			}
			
		}
		break;
		case DRV_CMD_KEY:  //按键报文
		{
			_put_key(pd[5+offset]);	
			_add_inst_frame(HMI_CMD_RESPOND,(void *)0,0);
		}
		break;
		case DRV_CMD_TEMP:  //面板遥信
		{	
		}	
		break;
		case DRV_CMD_YX:   //遥信
		{
			
		}
		break;
		case DRV_CMD_SET:  //定值
		{
			
		}
		break;
		case DRV_CMD_YKQ:  //遥控器
		{
			g_rmt_op.key = pd[5];
			_add_inst_frame(HMI_CMD_RESPOND,(void *)0,0);
		}
		break;

		case DRV_CMD_DOWN:   //下载程序应答
		break;
		
		default:
		break;
	}

#ifdef GD_TEST_2021  // 处理复归按键
	lcd_FG_key((pd[4+offset]&0x1f),pd[5+offset]);
#endif	
}

/**************************************************************************
函数名称：HMI_Comm_Send
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：启动面板通信的发送，包含重发机制。
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
static void _hmi_comm_send(void)
{
	static uint32_t us_call=0;
	static uint32_t us_lcdtest=0;
	static uint32_t us_ledtest=0;
	static uint32_t us_menu=0;
	static uint32_t us_send=0;

	LCD_COMM_DEF *pt=&g_tLcdComm;

	//pt->ReSend=0;
	if(pt->tInit.flag)  //需初始化lcd
	{
		pt->tInstBuf.head=pt->tInstBuf.tail=0;
		Add_Clear_Screen();		
		pt->tLight.light=LIGHT_ON;
		_add_inst_frame(HMI_CMD_INIT_LCD,pt->tInit.para,LCD_MAX_LED);  		//初始化lcd
		_add_inst_frame(HMI_CMD_CLEAR_SCREEN,(void *)0,0);          //清屏
		_add_inst_frame(HMI_CMD_BLIGHT,&pt->tLight.light,1);          	//点亮背光		
		pt->tInit.flag=false;
		pt->tLight.flag=false;
		pt->bClrScreen=false;
		pt->bFreshLed=true;
		hmi_update = 0;
	}
	else
	{
		if(pt->bClrScreen)  //需初始化lcd
		{
			_add_inst_frame(HMI_CMD_CLEAR_SCREEN,(void *)0,0);          //清屏
			//reset_update_flag();
			pt->bClrScreen=false;
		}
		if(pt->tLight.flag)
		{
			_add_inst_frame(HMI_CMD_BLIGHT,&pt->tLight.light,1);          //背光控制
			pt->tLight.flag=false;
			
		}

		if(pt->bTestLed && (!pt->bLcdUpdate))						
		{
			_add_inst_frame(HMI_CMD_LEDRUN,(void *)0,0);          //led测试
			pt->bTestLed=false;
			pt->bLedUpdate = true;
			us_ledtest = ustimer_get_origin();
		}
		if(pt->bLedUpdate)
		{
			if (ustimer_get_duration(us_ledtest) >= 4*USTIMER_SEC)
			{
				pt->bLedUpdate = false;
			}
			lcd_comm_delay = ustimer_get_origin();
		}
		else
		{
			us_ledtest = ustimer_get_origin();
		}
		if(pt->bTestLcd && (!pt->bLedUpdate))						
		{
			_add_inst_frame(HMI_CMD_TESTLCD,(void *)0,0);          //lcd测试
			
			pt->bTestLcd=false;
			pt->bLcdUpdate = true;

			us_lcdtest = ustimer_get_origin();
		}
		if(pt->bLcdUpdate)
		{
			if (ustimer_get_duration(us_lcdtest) >= 3*USTIMER_SEC)
			{
				//reset_update_flag();
				pt->bLcdUpdate = false;
			}
			lcd_comm_delay = ustimer_get_origin();
		}
		else
		{
			us_lcdtest = ustimer_get_origin();
		}
		if(pt->bTeleMatch)						
		{
			pt->tTele.cmd = 0x10;
			_add_inst_frame(HMI_CMD_WIRELESS,&pt->tTele.cmd,1);          //遥控器配对
			pt->bTeleMatch=false;
		}
		if(pt->bTeleClrMatch)						
		{
			pt->tTele.cmd = 0x20;
			_add_inst_frame(HMI_CMD_WIRELESS,&pt->tTele.cmd,1);          //遥控器配对清除
			pt->bTeleClrMatch=false;
		}

		// 1秒钟刷新一次LED
		if (pt->b_init_OK)
		{
			static uint32_t us0_led;
			u8 led_st[8];
			int i;
			if(ustimer_delay_origin2(&us0_led,USTIMER_SEC/8))
			{

				for(i=0;i<CFG_UART_NUM_MAX*2+CFG_ETH_NUM_MAX;i++)
				{
					if(g_run_stu.led[i])  // 232 1TX
					{
						led_set_pub(PUB_LED_232T1+i, LED_QUICK);	
						g_run_stu.led[i]=0;
					}
				}				
				if(g_bCommLed)
				{
					led_set_pub(PUB_LED_COMM, LED_QUICK);	
					g_bCommLed=false;
				}
			
				led_to_hmi();
				if((g_dw_led[0]!=g_dw_ledsave[0])||(g_dw_led[1]!=g_dw_ledsave[1])||pt->bFreshLed)
				{
					led_st[0]=(BYTE)g_dw_led[0];          // 状态灯
					led_st[1]=(BYTE)(g_dw_led[0]>>8);  // 状态灯高位
					led_st[2]=(BYTE)(g_dw_led[0]>>16);  // 状态灯高位
					led_st[3]=(BYTE)(g_dw_led[0]>>24);  // 状态灯高位
					led_st[4]=(BYTE)g_dw_led[1];          // 状态灯
					led_st[5]=(BYTE)(g_dw_led[1]>>8);  // 状态灯高位
					led_st[6]=(BYTE)(g_dw_led[1]>>16);  // 状态灯高位
					led_st[7]=(BYTE)(g_dw_led[1]>>24);  // 状态灯高位
					_add_inst_frame(HMI_CMD_LED,led_st,8);          			//led点灯
					g_dw_ledsave[0]=g_dw_led[0];
					g_dw_ledsave[1]=g_dw_led[1];
					pt->bFreshLed=false;					
				}
				for(i=0;i<CFG_UART_NUM_MAX*2+CFG_ETH_NUM_MAX;i++)
				{
					led_set_pub(PUB_LED_232T1+i, LED_OFF);	
				}
				led_set_pub(PUB_LED_COMM, LED_OFF);			
				
			}
		}
	}
	if(!pt->bRecv)
	{
		if(ustimer_get_duration(us_send) >= USTIMER_SEC/2) // 发送接收超时0.5秒
		{
			if(pt->ReSend++>3) //重发3次 
			{
				pt->tInit.flag=true;
				pt->bRecv=true;
				pt->ReSend=0;
			}
			else
			{		
				us_send= ustimer_get_origin();		
				Sub_Uart_Frame(HMI_BOARD);
				pt->bRecv=false;
			}
		}
		return;
	}
	pt->ReSend=0;
	//组织报文
	if(pt->tInstBuf.head!=pt->tInstBuf.tail)  //组织报文
	{
		_send_inst();
		us_send= ustimer_get_origin();
		pt->bRecv=false;

	}
	else if(pt->tDot.head!=pt->tDot.tail)  //组织报文
	{
		_send_dot_datas();
		us_send= ustimer_get_origin();
		pt->bRecv=false;
	}	
	else if(pt->tContinue.head!=pt->tContinue.tail)  //组织报文
	{
		if(_send_continue_datas()>0)
		{
			us_send= ustimer_get_origin();
			pt->bRecv=false;
		}
	}	
	else if(ustimer_get_duration(us_call) >= 200*USTIMER_MS) //无 发送数据,召唤
	{
		_hmi_call();
		us_call = ustimer_get_origin();
 		us_send= ustimer_get_origin();
		pt->bRecv=false;
		pt->bCall = true;
	}
	else if(ustimer_get_duration(us_menu) >= 100*USTIMER_MS) // 无数据发送后再刷新菜单
	{	
		mmi_Menu_Task();
		us_menu= ustimer_get_origin();
	}
	
}

/**************************************************************************
函数名称：Send_Inst
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：主程序发送命令数据
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
static void _send_inst(void)
{
//	CONTINUE_DATA_BUF_DEF *ptBuf_con=&g_tLcdComm.tContinue; 
	LCD_INST_BUF_DEF *ptBuf=&g_tLcdComm.tInstBuf;	


	
	INST_DEF *pt=&ptBuf->tInst[ptBuf->tail];
	//
	Add_Uart_Frame(HMI_BOARD,  (int)pt->para[1] + 6,  pt->para);
	ptBuf->tail = (ptBuf->tail+1) & LCD_MAX_INST_MASK;
	
	
}

/**************************************************************************
函数名称：Send_Continue_Datas
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：主程序发送菜单数据命令
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
static int _send_continue_datas(void)
{
	int i,cnt;
	BYTE num=0; 
//	BYTE checksum=0;
	//BYTE *pd=g_tUartHMI.arrSendBuf;
	CONTINUE_DATA_BUF_DEF *ptBuf=&g_tLcdComm.tContinue; 	
	BYTE srcbuf[UART_FRAME_LEN];
	BYTE dstbuf[UART_FRAME_LEN];	
	BYTE *pd=srcbuf+2;

	//pd+=8+offset;	//报文长度+报文头+命令码+数据区个数,共8个字节,从第7个字节开始数据
	while(ptBuf->head!=ptBuf->tail)
	{
		int len;
		CONTINUE_DATA_DEF *pt=&ptBuf->tBuf[ptBuf->tail];
		WORD bufoffset;
		BYTE attrib=0;
		len=(pt->attrib&0x3f);
		bufoffset=pt->x+(WORD)pt->y/4*20;
		
		if(pt->attrib&0x80)
		{
			attrib=((pt->attrib>>6)&0x03);
		}
		if((bufoffset+len)>=LCD_BUF_BYTES)
		{
			rt_printf("\r\n LCD BUF溢出!!!\r\n");
			rt_printf("\r\n x=%d ,y=%d",pt->x,pt->y);
			bufoffset=0;
		}		
		for(cnt=0;cnt<len;cnt++)
		{
			if(g_tLcdComm.lcdattrib[bufoffset]!=attrib)break;  // lcd 缓冲区数据不相等
			if(pt->dat[cnt]>0x80)// 是汉字
			{
				if(g_tLcdComm.lcdbuf[bufoffset]!=pt->dat[cnt]||g_tLcdComm.lcdbuf[bufoffset+1]!=pt->dat[cnt+1])break;  // lcd 缓冲区数据不相等
				bufoffset++;
				cnt++;
			}
			else
			{
				if(g_tLcdComm.lcdbuf[bufoffset]!=pt->dat[cnt])break;  // lcd 缓冲区数据不相等
			}
			bufoffset++;
		}
		ptBuf->tail++;
		ptBuf->tail&=LCD_MAX_CONTINUE_MASK;
		if(cnt<len)
		{
			len-=cnt;
			*pd++=(pt->x+cnt)*8;
			*pd++=pt->y;
			*pd++=len|(pt->attrib&0xc0);	
			for(i=0;i<len;i++)
			{
				*pd++=pt->dat[cnt];
				g_tLcdComm.lcdbuf[bufoffset]=pt->dat[cnt];
				g_tLcdComm.lcdattrib[bufoffset]=attrib;
				bufoffset++;	
				cnt++;
			}
			num+=(3+len);
			if(num>200)break;
		}

	}	
	if(num>0)
	{
		pd=srcbuf;
		*pd++=HMI_CMD_WRITE_DATAS; //命令码	
		*pd++=num; //数目
		(void)protocol_packet(srcbuf, (int)(num+6),  MAIN_BOARD, HMI_BOARD, dstbuf);	

		//添加到缓存区
		Add_Uart_Frame(HMI_BOARD, (int)dstbuf[1] + 6, dstbuf);	
	}
	return num;
}

/**************************************************************************
函数名称：Send_dot_Datas
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：主程序发送菜单数据命令
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
static void _send_dot_datas(void)
{
	int i;
	DOT_DATA_BUF_DEF *ptBuf=&g_tLcdComm.tDot; 	
	BYTE srcbuf[UART_FRAME_LEN];
	BYTE dstbuf[UART_FRAME_LEN];	
	BYTE *pd=srcbuf+2;
	int num=0;;

	while(ptBuf->head!=ptBuf->tail)
	{
		DOT_DATA_DEF *pt=&ptBuf->dot[ptBuf->tail];
		ptBuf->tail++;
		ptBuf->tail&=LCD_MAX_DOT_MASK;
		
		*pd++=(BYTE)pt->x;
		*pd++=(BYTE)(pt->x>>8);
		*pd++=(BYTE)pt->y;
		*pd++=(BYTE)(pt->y>>8);

		*pd++=(BYTE)pt->w;
		*pd++=(BYTE)(pt->w>>8);

		*pd++=(BYTE)pt->ex;
		*pd++=(BYTE)(pt->ex>>8);
		*pd++=pt->mode;
		*pd++=pt->num;
		for(i=0;i<pt->num;i++)
		{
			*pd++=pt->dat[i];
		}
		num+=pt->num+10;
		if((num+ptBuf->dot[ptBuf->tail].num)>230)
		{
			break;
		}
	}
	if(num>0)
	{
		pd=srcbuf;
		*pd++=HMI_CMD_WRITE_DOTS; //命令码	
		*pd++=num; //数目
		(void)protocol_packet(srcbuf, (int)(num+6),  MAIN_BOARD, HMI_BOARD, dstbuf);	
		//添加到缓存区
		Add_Uart_Frame(HMI_BOARD, (int)dstbuf[1] + 6, dstbuf);	
	}
}


/******************************************************************************
函数名称：	_hmi_changehzk
函数版本：	01.01
创建作者：	xxxxxx
创建日期：	2015-07-16
函数说明：	根据液晶点阵类型，转换字库，原始字库为横向正序
参数说明：	type:液晶点阵类型
返回值：  	无
修改记录：	
*/
 int _hmi_changehzk(u8 type)
{
	int i, j, k, n, ch, data_idx;
	u8 hz[32], data[32];

	if(hzk_table  == NULL)
	{
		return 0;
	}
	
	switch(type)
	{
		case LCD_HN:
		{
			if (hzk_change_type == LCD_HN)
			{
				return 0;
			}

			hzk_change_type = LCD_HN;			
		}
		break;
		
		case LCD_VN:
		{
			if (hzk_change_type == LCD_VN)
			{
				return 0;
			}
			
			// 纵向取模，字节倒序
			for (k=0; k<hzk_num; k++)
			{
				memcpy(hz, &hzk_table[k*32], 32);
				//第一组8字节
				for(i=7; i >=0 ; i=i-1)
				{
					ch = 0;
					n = 0;
					for(j=0; j<16; j=j+2)
					{
						ch |= ((hz[j] >> i) & 0x01) << n ;
						n++;
					}
					data[data_idx++] = ch;  
				}
				//第二组8字节
				for(i=7; i >=0 ; i=i-1)
				{
					ch = 0;
					n = 0;
					for(j=1; j<16; j=j+2)
					{
						ch |= ((hz[j] >> i) & 0x01) << n ;
						n++;
					}
					data[data_idx++] = ch;  
				}

				//第三组8字节
				for(i=7; i >=0 ; i=i-1)
				{
					ch = 0;
					n = 0;
					for(j=16; j<32; j=j+2)
					{
						ch |= ((hz[j] >> i) & 0x01) << n ;
						n++;
					}
					data[data_idx++] = ch;  
				} 

				//第四组8字节
				for(i=7; i >=0 ; i=i-1)
				{
					ch = 0;
					n = 0;
					for(j=17; j<32; j=j+2)
					{
						ch |= ((hz[j] >> i) & 0x01) << n ;
						n++;
					}
					data[data_idx++] = ch;  
				} 

				memcpy(&hzk_table[k*32], data, 32);
				data_idx = 0;
				hzk_change_type = LCD_VN;
			}
		}
		break;
	}
	return 0;
};
/*------------------------------ 测试函数 -------------------------------------
一个实体文件必须带一个本模块的测试函数来进行单元测试，如果的确不方便在本模块中
进行单元测试，必须在此注明实际的测试位置（例如在哪个实体文件中使用哪个测试函数）.
*/


/*------------------------------ 文件结束 -------------------------------------
*/


