嵌入式学习(48)-大彩串口屏TFT指令解析

一、概述

在使用大彩TFT串口屏时,发现指令解析的封装不太方便,对其进行的优化。

二、应用

1、指令结构体

cpp 复制代码
#pragma pack(push)
#pragma pack(1)                           //按字节对齐
typedef struct
{
    uint8_t    cmd_head;                    //帧头
    uint8_t    cmd_type;                    //命令类型(UPDATE_CONTROL)    
    uint8_t    ctrl_msg;                    //CtrlMsgType-指示消息的类型
    uint16_t   screen_id;                   //产生消息的画面ID
    uint16_t   control_id;                  //产生消息的控件ID
    uint8_t    control_type;                //控件类型
    uint8_t    param[256];                  //可变长度参数,最多256个字节
    uint8_t  cmd_tail[4];                   //帧尾
}CTRL_MSG,*PCTRL_MSG;



volatile typedef   union
{

  CTRL_MSG S;
  uint8_t  A[sizeof(CTRL_MSG)];



}Command_Union;

#pragma pack(pop)

在之前结构体的基础上增加了一个共用体,通过不同的方式访问同一块内存地址:一种是结构体的方式一种是以数组的方式;主要是因为串口接收到的数据有一些不是按照结构体协议格式传输的如RTC和记录控件。

由于使用了#pragma pack(1),所有成员都按1字节对齐,所以不会有额外的填充字节。例如,screen_id虽然是uint16_t(通常2字节),但在默认情况下可能需要对齐到偶数地址,但现在强制1字节对齐,因此直接跟在前一字段后面。

另外,param数组的大小固定为256字节,即使实际参数可能更短,这里也预留了足够的空间。而cmd_tail作为帧尾,占用4个字节,可能是用于校验或其他用途。

最后,通过#pragma pack(pop)恢复之前的打包设置,确保后续代码不受影响。这种技巧常用于嵌入式系统编程中,需要精确控制数据布局的情况。

🔹 CTRL_MSG: 结构体类型名

🔹 PCTRL_MSG: 指向该结构体的指针类型别名

2、指令解析

cpp 复制代码
/*********************************************************
函数功能:指令解析函数
入口参数:
返    回:无。
备    注:。
作    者:Roy
时    间:2025-03-05
*********************************************************/


char  ShowStr[20] = {0};
char  AlarmString[30] = {0};

char USB_State = 0;

extern uint8_t Command[300];

char  IDStr[20] = {0};

uint16_t TempValue;

uint16_t Record_UserID_SelectNum = 0;
uint16_t Record_EndoID_SelectNum = 0;

#include"User.h"
void Command_Parser(Command_Union* msg, uint16_t size)
{
    uint8_t cmd_type = msg->S.cmd_type;                                                  //指令类型
    uint8_t ctrl_msg = msg->S.ctrl_msg;                                                  //消息的类型
    uint8_t control_type = msg->S.control_type;                                          //控件类型
    uint16_t screen_id = PTR2U16( &msg->S.screen_id);                                    //画面ID
    uint16_t control_id = PTR2U16( &msg->S.control_id);                                  //控件ID
    uint32_t value = PTR2U32(msg->S.param);                                              //数值

    switch (cmd_type)
    {
    case NOTIFY_TOUCH_PRESS:                                                        //触摸屏按下
    case NOTIFY_TOUCH_RELEASE:                                                      //触摸屏松开
        //        NotifyTouchXY(cmd_buffer[1],PTR2U16(cmd_buffer+2),PTR2U16(cmd_buffer+4));
        break;

    case NOTIFY_WRITE_FLASH_OK:                                                     //写FLASH成功
        //        NotifyWriteFlash(1);
        break;

    case NOTIFY_WRITE_FLASH_FAILD:                                                  //写FLASH失败
        //        NotifyWriteFlash(0);
        break;

    case NOTIFY_READ_FLASH_OK:                                                      //读取FLASH成功
        //        NotifyReadFlash(1,cmd_buffer+2,size-6);                                     //去除帧头帧尾
        break;

    case NOTIFY_READ_FLASH_FAILD:                                                   //读取FLASH失败
        //        NotifyReadFlash(0,0,0);
        break;

    case NOTIFY_READ_RTC:                                                           //读取RTC时间	
        NotifyReadRTC(msg->A[2], msg->A[3], msg->A[4], msg->A[5], msg->A[6], msg->A[7], msg->A[8]);	

        break;

    case NOTIFY_CONTROL:
    {
        if (ctrl_msg == MSG_GET_CURRENT_SCREEN)                                 //画面ID变化通知
        {
            NotifyScreen(screen_id);                                            //画面切换调动的函数
        }
        else
        {
            switch (control_type)
            {
            case kCtrlButton:                                                   //按钮控件
                //				printf("按钮控件\r\n");
                NotifyButton(screen_id, control_id, msg->S.param[1]);
                break;

            case kCtrlText:                                                     //文本控件
                NotifyText(screen_id, control_id, msg->S.param);
                break;

            case kCtrlProgress:                                                 //进度条控件
                //                    NotifyProgress(screen_id,control_id,value);
                break;

            case kCtrlSlider:                                                   //滑动条控件
                //                    NotifySlider(screen_id,control_id,value);
                break;

            //                case kCtrlMeter:                                                    //仪表控件
            ////                    NotifyMeter(screen_id,control_id,value);
            ////                    break;
            //                case kCtrlMenu:                                                     //菜单控件
            //                    NotifyMenu(screen_id,control_id,msg->param[0],msg->param[1]);
            //                    break;
            //                case kCtrlSelector:                                                 //选择控件
            //                    NotifySelector(screen_id,control_id,msg->param[0]);
            //                    break;
            case kCtrlRTC:   //倒计时控件
                NotifyTimer(screen_id, control_id);
                break;

            case 0X1D:
                printf("记录控件\r\n");
                NotifyRecord(ctrl_msg, screen_id, control_id, msg->A[8], msg->A[9], msg->S.param);
                break;

            default:
                break;
            }
        }

        break;
    }

    case NOTIFY_HandShake:                                                          //握手通知
        //        NOTIFYHandShake();
        break;

    default:
        break;
    }
}

3、提取指令

cpp 复制代码
/*********************************************************
函数功能:提取指令
入口参数:
返    回:无。
备    注:目前是提取一条指令后续可考虑实现多条指令的提取。
作    者:Roy
时    间:2026-06-28
*********************************************************/

uint8_t HMI_FindCommand(RingBuffer *R_Buffer,uint8_t*Cmd,uint8_t *length )
{

char Data;

  while(RingBuffer_GetSize(R_Buffer)>0)
    {
        //取一个数据
        RingBuffer_Pop(R_Buffer,&Data);

        if((CMD_Pos==0)&&((unsigned char)Data!=CMD_HEAD))                               //指令第一个字节必须是帧头,否则跳过
        {
            continue;
        }
//        if(CMD_Pos<buf_len)                                           //防止缓冲区溢出
        Cmd[CMD_Pos++] = Data;
        CMD_End = ((CMD_End<<8)|Data);                           //拼接最后4个字节,组成一个32位整数
				printf("%02X ",Data); // 打印接收到的数据
														
        //最后4个字节与帧尾匹配,得到完整帧
        if(CMD_End==CMD_TAIL)
        {
           *length = CMD_Pos;                                       //指令字节长度
            CMD_End = 0;                                            //重新检测帧尾巴
            CMD_Pos = 0;                                             //复位指令指针
						printf("得到完整一条指令长度;%d;",*length);
					#if(CRC16_ENABLE)
            //去掉指令头尾EE,尾FFFCFFFF共计5个字节,只计算数据部分CRC
            if(!CheckCRC16(Cmd+1,*length-5))                      //CRC校验
                return 0;

            *length -= 2;                                            //去掉CRC16(2字节)
					#endif
            return *length;
        }
    }

}

4、解析指令

cpp 复制代码
void HMI_Run_Command()
{
	static uint8_t CMD_len;
	static uint16_t Rx_Cur;	
	static uint16_t Rx_Pre;	
   
#ifdef TANK_A
	
    while ( RingBuffer_GetSize(&TankA_HMIBuffer)> 0) //如果接收缓存区有数据就判断提取指令
    {
		Rx_Pre=RingBuffer_GetSize(&TankA_HMIBuffer);
        delay_ms(20);	// 等待串口数据更新
		Rx_Cur=RingBuffer_GetSize(&TankA_HMIBuffer);
	     if (Rx_Pre&& Rx_Cur) /* 接收到了数据,且接收完成了 */
		  {	
			if (HMI_FindCommand(&TankA_HMIBuffer,Command, &CMD_len) != 0) //判断是否接收到有效指令,每次提取一条指令
			{
				printf("解析指令:");
				Command_Parser((Command_Union*)Command, CMD_len);         //指令解析
				//memset(Data,0,Data_len*sizeof(uint8_t));
				memset(Command, 0, CMD_len *sizeof(uint8_t));
				printf("\r\n");
			}
		}
		  
		else
		{
		break;
		}
    }

	
#endif	
	
	
#ifdef TANK_B
    while (RingBuffer_GetSize(&TankB_HMIBuffer) > 0) //如果接收缓存区有数据就判断提取指令
    {
        Rx_Pre=RingBuffer_GetSize(&TankB_HMIBuffer);
        delay_ms(20);	// 等待串口数据更新
		Rx_Cur=RingBuffer_GetSize(&TankB_HMIBuffer);
	     if (Rx_Pre&& Rx_Cur) /* 接收到了数据,且接收完成了 */
		  {			  
		
        if (HMI_FindCommand(&TankB_HMIBuffer,Command, &CMD_len) != 0) //判断是否接收到有效指令,每次提取一条指令
        {
            printf("解析指令:");
            Command_Parser((Command_Union*)Command, CMD_len);         //指令解析
            //memset(Data,0,Data_len*sizeof(uint8_t));
            memset(Command, 0, CMD_len *sizeof(uint8_t));
            printf("\r\n");
        }
	}
    }

	
#endif		
	

}

三、测试

测试指令解析正常

四、源码链接

大彩TFT串口屏指令解析资源-CSDN下载

相关推荐
cxyxixiy1 小时前
数智码力:数据核对不用人工比对,Python 快速完成多表格差异校验
开发语言·python·学习
懒大王95271 小时前
学习TS + ArkTS 快速入门
学习
人工智能培训1 小时前
具身智能:让人工智能拥有物理世界感知能力
大数据·人工智能·学习·算法·机器学习
叼烟扛炮2 小时前
Linux 学习笔记(二十三):深入 Ext 文件系统(下)—— 原理、缓存与软硬链接
linux·笔记·学习
stolentime2 小时前
SOS DP学习笔记
c++·笔记·学习·算法·贪心算法·动态规划·二进制
江苏世纪龙科技3 小时前
从“拆不开“到“看得见“:新能源汽车原理教学的一种解法
学习
minglie13 小时前
电枢的主题和概念
学习·1024程序员节
叼烟扛炮3 小时前
Linux 学习笔记(二十二):深入 Ext 文件系统(上)—— 磁盘、块与神秘的 inode
linux·笔记·学习
坤坤子吖4 小时前
C++继承:继承的基本用法与访问规则
开发语言·c++·笔记·学习