【IMX6ULL项目】IMX6ULL上Linux系统实现产测工具框架
电子产品量产测试与烧写工具。这是一套软件,用在我们的实际生产中, 有如下特点:
1.简单易用:
把这套软件烧写在 SD 卡上,插到 IMX6ULL 板子里并启动,它就会自动测试各个模块、烧写 EMMC 系统。
工人只要按照说明接入几个模块,就可以完成整个测试、烧写过程。
测试结果一目了然:等 LCD 上所有模块的图标都变绿时,就表示测试通过。
2.软件可配置、易扩展:
通过配置文件添加测试项,可以添加不限个数的测试项。
每个测试项有自己的测试程序,测试通过后把结果发送给 GUI 即可。各个测试程序互不影响。
3. 纯 C 语言编程
工具设计的界面,它可以一边测试一边烧写:
上图中的 led、speaker 按钮,可以点击:
1.当你看到 LED 闪烁时,就点击 led 按钮,它变成绿色表示测试通过;
2. 当你从耳机里听到声音时,就点击 speaker 按钮,它变成绿色表示测试通过。
其他按钮无法点击,接上对应模块后会自动测试,测试通过时图标就会变绿。
上图中的蓝色按钮表示烧写 EMMC 的进度,烧写成功后它也会变绿。
LCD 上所有图标都变绿时,就表示测试、烧写全部完成;某项保持红色的话,就表示对应模块测试失败。
目录:
目录:
一、总设计思路:
二、显示系统:
*2.1 DisplayInit();
2.2 framebuffer();
2.3 RegisterDisplay();
&& 2.4 显示管理器框架
编辑
*2.5 SelectDefaultDisplay(char *name)
*2.6 InitDefaultDisplay(void)
2.7 DeviceInit(void);
2.8 FbGetBuffer(PDispBuff ptDispBuff)
*2.9 PutPixel(int x, int y, unsigned int dwColor)
*2.10 FlushDisplayRegion
2.11 测试代码
详细介绍:Linux基础项目开发1:量产工具——显示系统(二)_linux入门项目-CSDN博客
三、输入系统:
*3.1 InputEvent 数据本身结构体
*3.2 InputDevice 输入设备结构体
*3.3 InputDevice结构体实例(Touchscreen)
3.4 TouchscreenGetInputEvent(PInputEvent ptInputEvent)
3.5 TouchscreenDeviceInit(void)
3.6 TouchscreenDeviceExit(void)
3.9 显示屏的测试函数
*3.10 InputDevice结构体实例(net)
3.11 NetinputGetInputEvent(PInputEvent ptInputEvent)
3.12 NetinputDeviceInit(void)
3.13 NetinputDeviceExit(void)
3.14 网络测试函数
3.15 client.c
&& 3.16 输入管理器框架
* 3.17 InputInit(void)
3.18 TouchscreenRegister(void)
3.19 NetInputRegister(void)
*3.20 RegisterInputDevice(PInputDevice ptInputDev)
*3.21 IntpuDeviceInit(void)
3.22 *input_recv_thread_func(void *data)
*3.23 GetInputEvent(PT_InputEvent ptInputEvent)
3.24 环形缓冲区
3.25 测试代码
详细介绍:Linux基础项目开发1:量产工具——输入系统(三)-CSDN博客
四、 文字系统:
*4.1 FontBitMap
Region
*4.2 FontOpr
*4.3 FontOpr g_tFreetypeOpr
4.4 FreeTypeFontInit(char *aFineName)
4.5 FreeTypeSetFontSize(int iFontSize)
4.6 FreeTypeGetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)
4.7 FreetypeRegister(void)
&& 4.8 字符管理器框架
4.9 FontsRegister(void)
4.10 SelectAndInitFont(char *aFontOprName, char *aFontFileName)
4.10 SetFontSize(int iFontSize)
4.11 GetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)
*4.12 DrawFontBitMap(PFontBitMap ptFontBitMap, unsigned int dwColor)
*4.13 测试代码:
详细介绍:Linux基础项目开发1:量产工具——文字系统(四)-CSDN博客
五、UI系统:
*5.1 Button
*5.2 InitButton(PButton ptButton, char *name, PRegion ptRegion, ONDRAW_FUNC OnDraw, ONPRESSED_FUNC OnPressed)
5.3 DrawRegion(PRegion ptRegion, unsigned int dwColor)
5.4 DrawTextInRegionCentral(char *name, PRegion ptRegion, unsigned int dwColor)
* 5.5 DefaultOnDraw(struct Button *ptButton, PDispBuff ptDispBuff)
*5.6 DefaultOnPressed(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent)
5.7 测试代码:
详细介绍:Linux基础项目开发1:量产工具——UI系统(五)_linux ui界面开发-CSDN博客
六、页面系统:
*6.1 PageAction
&& 6.2 页面管理器框架
6.3 PageRegister(PPageAction ptPageAction)
6.4 PPageAction Page(char *name)
6.5 PageAction MainPage
6.6 测试代码:
详细介绍:Linux基础项目开发1:量产工具——页面系统(六)-CSDN博客
七、业务系统
7.1 业务系统流程图
7.2 主页面流程图:
&&& 7.3 抽象出配置文件结构体(ItemCfg)
7.4 ParseConfigFile(char *strFileName)
7.4 GetItemCfgCount(void)
7.5 GetItemCfgByIndex(int index)
7.6 GetItemCfgByName(char *name)
*7.7 生成界面
7.8 *MainPageRun(void *pParams)
7.9 GenerateButtons(void)
*7.10 处理输入事件:GetButtonByInputEvent(PInputEvent ptInputEvent)
7.11 isTouchPointInRegion(int iX, int iY, PRegion ptRegion)
7.12 GetButtonByName(char *name)
7.13 MainPageOnPressed(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent)
详细介绍:Linux基础项目开发1:量产工具——业务系统(七)_量产工具开发-CSDN博客
一、总设计思路:
二、显示系统:
*2.1 DisplayInit();
初始化显示系统,在disp_manager.c这个中间管理器中初始化显示器,选择是framebuffer的应用端还是Web的网络端,这里如果还想要加入其他设备都可以在这里调用这个设备的初始化函数,这里我们先拿framebuffer举例。
// 函数实现:初始化显示管理器
void DisplayInit(void)
{void FramebufferInit(void); // 假设有一个FramebufferInit函数用于初始化framebufferFramebufferInit(); // 调用该函数
}
2.2 framebuffer();
这里会在framebuffer.c注册framebuffer操作的结构体到显示管理器的链表中。
// 初始化framebuffer
void FramebufferInit(void)
{RegisterDisplay(&g_tFramebufferOpr); // 注册framebuffer操作到显示管理器
}
定义一个DispOpr结构体实例,用于描述framebuffer的操作。
static DispOpr g_tFramebufferOpr = {.name = "fb", // 操作名称.DeviceInit = FbDeviceInit, // 初始化函数指针.DeviceExit = FbDeviceExit, // 退出函数指针.GetBuffer = FbGetBuffer, // 获取缓冲区信息函数指针.FlushRegion = FbFlushRegion, // 刷新区域函数指针
};
2.3 RegisterDisplay();
将上面的显示设备framebuffer操作的结构体注册到disp_manager.c这个链表中
// 函数实现:注册一个显示设备
void RegisterDisplay(PDispOpr ptDispOpr)
{ptDispOpr->ptNext = g_DispDevs; // 新设备指针指向当前第一个设备g_DispDevs = ptDispOpr; // 更新第一个设备指针为新设备
}
&& 2.4 显示管理器框架
*2.5 SelectDefaultDisplay(char *name)
当放到这个链表里后会有很多设备,那么我们选择哪个模块呢?这里我们就需要disp_manager.c中编写一个设备选择函数SelectDefaultDisplay(char *name) 从头遍历链表找设备名字。
// 函数实现:选择一个默认的显示设备
int SelectDefaultDisplay(char *name)
{PDispOpr pTmp = g_DispDevs; // 临时指针,用于遍历设备链表while (pTmp){if (strcmp(name, pTmp->name) == 0) // 如果找到匹配的设备名{g_DispDefault = pTmp; // 设置为默认设备return 0; // 返回成功}pTmp = pTmp->ptNext; // 移动到下一个设备}return -1; // 没有找到匹配的设备
}
这里disp_manager.h定义一个结构体DispOpr,用于描述显示操作的接口。
// 定义一个结构体DispOpr,用于描述显示操作的接口
typedef struct DispOpr {char *name; // 显示操作的名称int (*DeviceInit)(void); // 指向初始化函数的指针int (*DeviceExit)(void); // 指向退出函数的指针int (*GetBuffer)(PDispBuff ptDispBuff); // 指向获取缓冲区信息函数的指针int (*FlushRegion)(PRegion ptRegion, PDispBuff ptDispBuff); // 指向刷新区域函数的指针struct DispOpr *ptNext; // 指向下一个DispOpr结构体的指针,用于链表结构
} DispOpr, *PDispOpr; // DispOpr是结构体类型,PDispOpr是指向DispOpr的指针类型
*2.6 InitDefaultDisplay(void)
选择好了显示设备需要在disp_manager.c中编写初始化代码进行设备初始化
int InitDefaultDisplay(void)
{int ret;ret = g_DispDefault->DeviceInit(); // 调用设备的初始化函数if (ret){printf("DeviceInit err\n"); // 初始化失败return -1;}ret = g_DispDefault->GetBuffer(&g_tDispBuff); // 获取缓冲区信息if (ret){printf("GetBuffer err\n"); // 获取失败return -1;}line_width = g_tDispBuff.iXres * g_tDispBuff.iBpp / 8; // 计算每行像素的宽度pixel_width = g_tDispBuff.iBpp / 8; // 计算每个像素的宽度return 0; // 初始化成功
}
在这之前我们需要先在disp_manager.h中定义一个DispBuff,用于存储显示缓冲区的信息,这样会更好的调用显示缓冲信息。
typedef struct DispBuff {int iXres; // x坐标分辨率int iYres; // y坐标分辨率int iBpp; // 每像素位数(bits per pixel)char *buff; // 缓冲区地址
} DispBuff, *PDispBuff; // DispBuff是结构体类型,PDispBuff是指向DispBuff的指针类型
2.7 DeviceInit(void);
调用framebuffer.c中的DeviceInit(void)进行初始化设备。
static int DeviceInit(void)
{fd_fb = open("/dev/fb0", O_RDWR); // 打开/dev/fb0设备if (fd_fb < 0){printf("can't open /dev/fb0\n"); // 如果打开失败,打印错误信息return -1; // 返回错误代码}if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)) // 获取屏幕信息{printf("can't get var\n"); // 如果获取失败,打印错误信息return -1; // 返回错误代码}line_width = var.xres * var.bits_per_pixel / 8; // 计算行宽度pixel_width = var.bits_per_pixel / 8; // 计算像素宽度screen_size = var.xres * var.yres * var.bits_per_pixel / 8; // 计算屏幕大小fb_base = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0); // 内存映射framebufferif (fb_base == (unsigned char *)-1){printf("can't mmap\n"); // 如果映射失败,打印错误信息return -1; // 返回错误代码}return 0; // 初始化成功,返回0
}
2.8 FbGetBuffer(PDispBuff ptDispBuff)
再调用framebuffer.c中的FbGetBuffer(PDispBuff ptDispBuff)获取设备的framebuffer缓冲区信息。
// 获取framebuffer缓冲区信息
static int FbGetBuffer(PDispBuff ptDispBuff)
{ptDispBuff->iXres = var.xres; // 设置X分辨率ptDispBuff->iYres = var.yres; // 设置Y分辨率ptDispBuff->iBpp = var.bits_per_pixel; // 设置每像素位数ptDispBuff->buff = (char *)fb_base; // 设置缓冲区地址return 0; // 返回0表示成功
}
*2.9 PutPixel(int x, int y, unsigned int dwColor)
想在这块内存上绘制一个像素,再以这个像素作为起点确定图标来绘制图片,绘制像素这个最基本的函数应该在disp_manager.c这里实现,在屏幕上画一个像素点。
// 函数实现:在屏幕上画一个像素点
int PutPixel(int x, int y, unsigned int dwColor)
{// 根据屏幕的bpp(每像素位数)计算像素在缓冲区中的位置unsigned char *pen_8 = (unsigned char *)(g_tDispBuff.buff + y * line_width + x * pixel_width);unsigned short *pen_16;unsigned int *pen_32;unsigned int red, green, blue;pen_16 = (unsigned short *)pen_8;pen_32 = (unsigned int *)pen_8;// 根据bpp选择不同的颜色表示方式switch (g_tDispBuff.iBpp){case 8: // 8bpp,直接设置颜色值{*pen_8 = dwColor;break;}case 16: // 16bpp,通常是565格式,需要转换颜色值{red = (dwColor >> 16) & 0xff;green = (dwColor >> 8) & 0xff;blue = (dwColor >> 0) & 0xff;dwColor = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);*pen_16 = dwColor;break;}case 32: // 32bpp,直接设置颜色值{*pen_32 = dwColor;break;}default: // 不支持的bpp{printf("can't support %dbpp\n", g_tDispBuff.iBpp);return -1;break;}}return 0; // 成功画点
}
*2.10 FlushDisplayRegion
我们绘制好图像以后需要刷到硬件上面,我们需要在disp_manager.c中再提供一个FlushDisplayRegion()函数。
// 函数实现:刷新屏幕上的一个区域
int FlushDisplayRegion(PRegion ptRegion, PDispBuff ptDispBuff)
{return g_DispDefault->FlushRegion(ptRegion, ptDispBuff); // 调用设备的刷新区域函数
}
2.11 测试代码
int main(int argc, char **argv)
{Region region; // 定义刷新区域的大小PDispBuff ptBuffer;DisplayInit(); // 初始化显示系统SelectDefaultDisplay("fb"); // 选择默认的显示设备,这里是帧缓冲设备InitDefaultDisplay(); // 初始化选定的显示设备lcd_put_ascii(100, 100, 'A'); // 在屏幕的(100, 100)位置显示字符'A'// 设置刷新区域的位置和大小region.iLeftUpX = 100;region.iLeftUpY = 100;region.iWidth = 8;region.iHeigh = 16;ptBuffer = GetDisplayBuffer(); // 获取显示缓冲区FlushDisplayRegion(®ion, ptBuffer); // 刷新指定区域的显示内容return 0; }
lcd_put_ascii(100, 100, 'A');
// 在指定位置显示一个ASCII字符
void lcd_put_ascii(int x, int y, unsigned char c)
{unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16]; // 获取字符的字体位图数据int i, b;unsigned char byte;for (i = 0; i < 16; i++) // 遍历字体的每一行{byte = dots[i];for (b = 7; b >= 0; b--) // 遍历每一行的每一位{if (byte & (1<<b)) // 判断位图中的点是否需要显示{// 显示白色像素PutPixel(x+7-b, y+i, 0xffffff); // 白色}else{// 显示黑色像素PutPixel(x+7-b, y+i, 0); // 黑色}}}
}
详细介绍:Linux基础项目开发1:量产工具——显示系统(二)_linux入门项目-CSDN博客
三、输入系统:
我们需要抽象出来俩个结果体,一个数据本身,一个设备本身。
*3.1 InputEvent 数据本身结构体
在input_manager.h中抽象出数据本身的结构体:
typedef struct InputEvent {// 时间戳结构体,包含事件发生的时间,精确到微秒struct timeval tTime;// 事件类型,可能表示不同的输入动作,如点击、滑动、长按等int iType;// 事件的X坐标,通常表示在屏幕或输入区域的水平位置int iX;// 事件的Y坐标,通常表示在屏幕或输入区域的垂直位置int iY;// 事件的压力值,对于触摸屏等设备,可能表示用户触摸的力度int iPressure;// 一个字符串,用于存储与事件相关的额外信息,长度限制为1024个字符char str[1024];
} InputEvent, *PInputEvent;
*3.2 InputDevice 输入设备结构体
在input_manager.h中抽象出输入设备的结构体:
typedef struct InputDevice {// 指向设备名称的字符串指针,用于标识输入设备char *name;// 指向函数的指针,该函数用于获取输入事件。它接受一个指向InputEvent结构体的指针作为参数,并返回一个整数值int (*GetInputEvent)(PInputEvent ptInputEvent);// 指向函数的指针,该函数用于初始化输入设备。它不接受任何参数,并返回一个整数值int (*DeviceInit)(void);// 指向函数的指针,该函数用于退出或释放输入设备。它不接受任何参数,并返回一个整数值int (*DeviceExit)(void);// 指向另一个InputDevice结构体的指针,用于链接多个输入设备,形成链表struct InputDevice *ptNext;
} InputDevice, *PInputDevice;
*3.3 InputDevice结构体实例(Touchscreen)
在touchscreen.c 中定义一个InputDevice结构体实例,表示触摸屏设备
// 定义一个InputDevice结构体实例,表示触摸屏设备
static InputDevice g_tTouchscreenDev ={.name = "touchscreen",.GetInputEvent = TouchscreenGetInputEvent,.DeviceInit = TouchscreenDeviceInit,.DeviceExit = TouchscreenDeviceExit,
};
下面我们需要将触摸屏设备的各个模块函数进行实现。
3.4 TouchscreenGetInputEvent(PInputEvent ptInputEvent)
在touchscreen.c 中定义静态函数,用于从触摸屏设备获取输入事件
// 静态函数,用于从触摸屏设备获取输入事件
static int TouchscreenGetInputEvent(PInputEvent ptInputEvent)
{struct ts_sample samp;int ret;// 从触摸屏设备读取一个样本ret = ts_read(g_ts, &samp, 1);// 如果读取失败,返回-1if (ret != 1)return -1;// 填充输入事件结构体ptInputEvent->iType = INPUT_TYPE_TOUCH;ptInputEvent->iX = samp.x;ptInputEvent->iY = samp.y;ptInputEvent->iPressure = samp.pressure;ptInputEvent->tTime = samp.tv;// 读取成功,返回0return 0;
}
3.5 TouchscreenDeviceInit(void)
在touchscreen.c 中定义静态函数,用于初始化触摸屏设备
static int TouchscreenDeviceInit(void)
{// 初始化触摸屏设备g_ts = ts_setup(NULL, 0);if (!g_ts){// 如果初始化失败,打印错误信息并返回-1printf("ts_setup err\n");return -1;}// 初始化成功,返回0return 0;
}
3.6 TouchscreenDeviceExit(void)
在touchscreen.c 中定义静态函数,用于退出触摸屏设备
static int TouchscreenDeviceExit(void)
{// 关闭触摸屏设备ts_close(g_ts);return 0;
}
3.9 显示屏的测试函数
#if 1// 主函数,程序的入口点
int main(int argc, char **argv)
{// 定义一个输入事件的变量InputEvent event;int ret;// 初始化触摸屏设备g_tTouchscreenDev.DeviceInit();// 无限循环,持续从触摸屏获取事件while (1){// 从触摸屏设备获取一个输入事件ret = g_tTouchscreenDev.GetInputEvent(&event);// 检查是否成功获取事件if (ret) {// 如果获取事件失败,打印错误信息并退出程序printf("GetInputEvent err!\n");return -1;}else{// 如果成功获取事件,打印事件的详细信息printf("Type : %d\n", event.iType);printf("iX : %d\n", event.iX);printf("iY : %d\n", event.iY);printf("iPressure : %d\n", event.iPressure);}}// 理论上,无限循环不会结束,这行代码不会被执行return 0;
}#endif
*3.10 InputDevice结构体实例(net)
在netiput.c 中定义一个InputDevice结构体实例,表示网络输入设备
/* 定义一个InputDevice结构体实例,表示网络输入设备 */
static InputDevice g_tNetinputDev ={.name = "netinput",.GetInputEvent = NetinputGetInputEvent,.DeviceInit = NetinputDeviceInit,.DeviceExit = NetinputDeviceExit,
};
下面我们需要将网络设备的各个模块函数进行实现。
3.11 NetinputGetInputEvent(PInputEvent ptInputEvent)
在netiput.c 中定义静态函数,用于从网络套接字获取输入事件
/* 静态函数,用于从网络套接字获取输入事件 */
static int NetinputGetInputEvent(PInputEvent ptInputEvent)
{struct sockaddr_in tSocketClientAddr; /* 客户端地址结构体 */struct int iRecvLen; /* 接收到的数据长度,这里应该是int类型 */char aRecvBuf[1000]; /* 接收缓冲区 */int iAddrLen = sizeof(struct sockaddr); /* 地址结构体长度 *//* 从套接字接收数据 */iRecvLen = recvfrom(g_iSocketServer, aRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);if (iRecvLen > 0){aRecvBuf[iRecvLen] = '\0'; /* 在接收缓冲区末尾添加字符串结束符 *//* 填充输入事件结构体 */ptInputEvent->iType = INPUT_TYPE_NET;gettimeofday(&ptInputEvent->tTime, NULL);strncpy(ptInputEvent->str, aRecvBuf, 1000);ptInputEvent->str[999] = '\0';return 0; /* 成功获取事件,返回0 */}elsereturn -1; /* 获取事件失败,返回-1 */
}
3.12 NetinputDeviceInit(void)
在netiput.c 中定义静态函数,用于初始化网络输入设备
static int NetinputDeviceInit(void)
{struct sockaddr_in tSocketServerAddr; /* 服务器地址结构体 */int iRet; /* 返回值 *//* 创建一个UDP套接字 */g_iSocketServer = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == g_iSocketServer){printf("socket error!\n");return -1; /* 创建套接字失败,返回-1 */}/* 设置服务器地址结构体 */tSocketServerAddr.sin_family = AF_INET;tSocketServerAddr.sin_port = htons(SERVER_PORT); /* 将端口号转换为网络字节序 */tSocketServerAddr.sin_addr.s_addr = INADDR_ANY; /* 监听所有本地地址 */memset(tSocketServerAddr.sin_zero, 0, 8); /* 将结构体中未使用的部分置零 *//* 绑定套接字到服务器地址 */iRet = bind(g_iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));if (-1 == iRet){printf("bind error!\n");return -1; /* 绑定失败,返回-1 */}return 0; /* 初始化成功,返回0 */
}
3.13 NetinputDeviceExit(void)
在netiput.c 中定义静态函数,用于退出网络输入设备
static int NetinputDeviceExit(void)
{close(g_iSocketServer); /* 关闭套接字 */return 0; /* 退出成功,返回0 */
}
3.14 网络测试函数
#if 1/* 主函数,程序的入口点 */
int main(int argc, char **argv)
{InputEvent event; /* 定义一个输入事件结构体变量 */int ret; /* 用于存储函数返回值 *//* 调用网络输入设备的初始化函数 */g_tNetinputDev.DeviceInit();/* 进入无限循环,不断获取并处理输入事件 */while (1){/* 从网络输入设备获取输入事件 */ret = g_tNetinputDev.GetInputEvent(&event);if (ret) {/* 如果获取事件失败,打印错误信息并退出程序 */printf("GetInputEvent err!\n");return -1;}else{/* 如果获取事件成功,打印事件的类型和内容 */printf("Type : %d\n", event.iType);printf("str : %s\n", event.str);}}return 0; /* 正常退出程序 */
}#endif
3.15 client.c
这里充当客户端,用来发送数据
#include <sys/types.h> /* 包含系统数据类型头文件 */
#include <sys/socket.h> /* 包含套接字相关函数和结构体头文件 */
#include <string.h> /* 包含字符串处理函数头文件 */
#include <sys/socket.h> /* 重复包含,可以删除 */
#include <netinet/in.h> /* 包含网络地址结构体头文件 */
#include <arpa/inet.h> /* 包含IP地址转换函数头文件 */
#include <unistd.h> /* 包含文件操作和进程控制函数头文件 */
#include <stdio.h> /* 包含标准输入输出函数头文件 *//* 定义服务器端口号 */
#define SERVER_PORT 8888/* 主函数,程序的入口点 */
int main(int argc, char **argv)
{int iSocketClient; /* 客户端套接字描述符 */struct sockaddr_in tSocketServerAddr; /* 服务器地址结构体 */int iRet; /* 用于存储函数返回值 */int iSendLen; /* 用于存储发送数据的长度 */int iAddrLen; /* 用于存储地址结构体的长度 *//* 检查命令行参数数量是否正确 */if (argc != 3){printf("Usage:\n");printf("%s <server_ip> <str>\n", argv[0]);return -1;}/* 创建一个UDP套接字 */iSocketClient = socket(AF_INET, SOCK_DGRAM, 0);/* 设置服务器地址结构体 */tSocketServerAddr.sin_family = AF_INET; /* 使用IPv4地址族 */tSocketServerAddr.sin_port = htons(SERVER_PORT); /* 设置服务器端口号,转换为网络字节序 *///tSocketServerAddr.sin_addr.s_addr = INADDR_ANY; /* 注释掉,改为使用命令行参数指定的IP地址 *//* 将命令行参数指定的IP地址转换为网络字节序,并设置到地址结构体中 */if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr)){printf("invalid server_ip\n");return -1;}/* 将地址结构体的剩余部分填充为0 */memset(tSocketServerAddr.sin_zero, 0, 8);#if 0/* 注释掉的部分是尝试进行TCP风格的连接,但由于是UDP套接字,这里实际上不会进行连接操作 */iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr)); if (-1 == iRet){printf("connect error!\n");return -1;}
#endif/* 计算地址结构体的长度 */iAddrLen = sizeof(struct sockaddr);/* 向服务器发送数据 */iSendLen = sendto(iSocketClient, argv[2], strlen(argv[2]), 0,(const struct sockaddr *)&tSocketServerAddr, iAddrLen);/* 关闭套接字 */close(iSocketClient);/* 正常退出程序 */return 0;
}
&& 3.16 输入管理器框架
* 3.17 InputInit(void)
在input_manager.c中向上提供一个InputInit(void) 函数,用于注册触摸屏设备和注册网络设备
/* 初始化输入系统 */
void InputInit(void)
{/* 注册触摸屏设备 */extern void TouchscreenRegister(void);TouchscreenRegister();/* 注册网络输入设备 */extern void NetInputRegister(void);NetInputRegister();
}
3.18 TouchscreenRegister(void)
在touchscreen.c 中注册TouchscreenRegister(void)函数,用于注册触摸屏设备到输入管理器
// 函数,用于注册触摸屏设备到输入管理器
void TouchscreenRegister(void)
{// 调用输入管理器的注册函数,将触摸屏设备注册到输入管理器中RegisterInputDevice(&g_tTouchscreenDev);
}
将以下结构体注册到输入管理器
// 定义一个InputDevice结构体实例,表示触摸屏设备
static InputDevice g_tTouchscreenDev ={.name = "touchscreen",.GetInputEvent = TouchscreenGetInputEvent,.DeviceInit = TouchscreenDeviceInit,.DeviceExit = TouchscreenDeviceExit,
};
3.19 NetInputRegister(void)
在netiput.c 中注册NetInputRegister(void)函数,用于注册网络设备到输入管理器
/* 函数,用于注册网络输入设备到输入管理器 */
void NetInputRegister(void)
{RegisterInputDevice(&g_tNetinputDev); /* 调用输入管理器的注册函数,将网络输入设备注册到输入管理器中 */
}
将以下结构体注册到输入管理器
/* 定义一个InputDevice结构体实例,表示网络输入设备 */
static InputDevice g_tNetinputDev ={.name = "netinput",.GetInputEvent = NetinputGetInputEvent,.DeviceInit = NetinputDeviceInit,.DeviceExit = NetinputDeviceExit,
};
*3.20 RegisterInputDevice(PInputDevice ptInputDev)
在input_manager.c 注册上面的输入设备,这一层要接受下一层传输上来的注册信息 ,所以要定义一个 g_InputDevs的链表头,g_InputDevs这个链表中存放设备。将所有设备放到一个链表中
/* 注册输入设备函数 */
void RegisterInputDevice(PInputDevice ptInputDev)
{ptInputDev->ptNext = g_InputDevs;g_InputDevs = ptInputDev;
}
*3.21 IntpuDeviceInit(void)
在input_manager.c中向上提供一个IntpuDeviceInit(void)对于每个设备初始化它,并且创建线程
* 初始化输入设备 */
void IntpuDeviceInit(void)
{int ret;pthread_t tid;/* 遍历所有输入设备,进行初始化和创建接收线程 */PInputDevice ptTmp = g_InputDevs;while (ptTmp){/* 初始化设备 */ret = ptTmp->DeviceInit();/* 创建接收线程 */if (!ret){ret = pthread_create(&tid, NULL, input_recv_thread_func, ptTmp);}ptTmp = ptTmp->ptNext;}
}
3.22 *input_recv_thread_func(void *data)
在input_manager.c 中创建*input_recv_thread_func(void *data)输入接收线程函数,等待数据,保存数据,唤醒线程
/* 输入接收线程函数 */
static void *input_recv_thread_func(void *data)
{PInputDevice ptInputDev = (PInputDevice)data;InputEvent tEvent;int ret;while (1){/* 从输入设备读取数据 */ret = ptInputDev->GetInputEvent(&tEvent);if (!ret){/* 保存数据到环形缓冲区 */pthread_mutex_lock(&g_tMutex);PutInputEventToBuffer(&tEvent);/* 唤醒等待数据的线程 */pthread_cond_signal(&g_tConVar);pthread_mutex_unlock(&g_tMutex);}}return NULL;
}
*3.23 GetInputEvent(PT_InputEvent ptInputEvent)
在input_manager.c 中定义一个最重要的一个函数 ,最上层的代码只要调用这个函数就可以得到这些设备的数据,得到线程数据则返回数据,无数据则休眠
/* 获取输入事件的函数 */
int GetInputEvent(PInputEvent ptInputEvent)
{InputEvent tEvent; // 临时变量,用于存储从缓冲区读取的事件int ret; // 返回值,用于表示函数执行的结果/* 加锁以保证线程安全 */pthread_mutex_lock(&g_tMutex); // 获取互斥锁,保护共享资源/* 尝试从缓冲区获取事件 */if (GetInputEventFromBuffer(&tEvent)) // 如果成功从缓冲区获取事件{*ptInputEvent = tEvent; // 将获取的事件复制到传入的指针所指向的变量pthread_mutex_unlock(&g_tMutex); // 释放互斥锁return 0; // 返回0表示成功获取事件}else // 如果缓冲区为空,没有事件可获取{/* 休眠等待新事件的到来 */pthread_cond_wait(&g_tConVar, &g_tMutex); // 在条件变量上等待,同时释放互斥锁,直到被唤醒/* 再次尝试从缓冲区获取事件 */if (GetInputEventFromBuffer(&tEvent)) // 如果被唤醒后成功获取事件{*ptInputEvent = tEvent; // 将获取的事件复制到传入的指针所指向的变量ret = 0; // 设置返回值为0,表示成功获取事件}else // 如果仍然没有事件可获取{ret = -1; // 设置返回值为-1,表示获取事件失败}pthread_mutex_unlock(&g_tMutex); // 释放互斥锁}return ret; // 返回函数执行的结果
}
3.24 环形缓冲区
在input_manager.c中编写环形缓冲区也是一个一维数组,并不是一个环形的数组,用于保存各个输入设备得到的数据
/* 定义环形缓冲区的大小 */
#define BUFFER_LEN 20/* 定义环形缓冲区的读写指针 */
static int g_iRead = 0; // 读指针,指向缓冲区中下一个将被读取的元素位置
static int g_iWrite = 0; // 写指针,指向缓冲区中下一个将被写入的元素位置/* 定义环形缓冲区存储的数据类型,这里假设为InputEvent */
static InputEvent g_atInputEvents[BUFFER_LEN]; // 环形缓冲区,用于存储InputEvent类型的数据/* 检查环形缓冲区是否已满 */
static int isInputBufferFull(void)
{// 如果写指针的下一个位置是读指针,则缓冲区已满return (g_iRead == ((g_iWrite + 1) % BUFFER_LEN));
}/* 检查环形缓冲区是否为空 */
static int isInputBufferEmpty(void)
{// 如果读指针和写指针相同,则缓冲区为空return (g_iRead == g_iWrite);
}/* 将输入事件放入环形缓冲区 */
static void PutInputEventToBuffer(PInputEvent ptInputEvent)
{// 如果缓冲区未满,则将输入事件写入缓冲区,并更新写指针if (!isInputBufferFull()){g_atInputEvents[g_iWrite] = *ptInputEvent;g_iWrite = (g_iWrite + 1) % BUFFER_LEN;}
}/* 从环形缓冲区获取输入事件 */
static int GetInputEventFromBuffer(PInputEvent ptInputEvent)
{// 如果缓冲区不为空,则从缓冲区读取输入事件,并更新读指针if (!isInputBufferEmpty()){*ptInputEvent = g_atInputEvents[g_iRead];g_iRead = (g_iRead + 1) % BUFFER_LEN;return 1; // 表示成功获取事件}else{return 0; // 表示缓冲区为空,无法获取事件}
}
要想访问环形缓冲区,需要加个互斥锁
/* 互斥锁和条件变量,用于线程同步 */
static pthread_mutex_t g_tMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t g_tConVar = PTHREAD_COND_INITIALIZER;
3.25 测试代码
#include <sys/mman.h> // 内存管理声明
#include <sys/types.h> // 基本系统数据类型
#include <sys/stat.h> // 文件状态定义
#include <unistd.h> // 提供通用的文件、目录、程序及进程操作的函数
#include <linux/fb.h> // 帧缓冲设备的定义
#include <fcntl.h> // 文件控制选项定义
#include <stdio.h> // 标准输入输出定义
#include <string.h> // 字符串操作函数定义
#include <sys/ioctl.h> // IO控制设备的函数定义#include <input_manager.h> // 自定义输入管理的头文件int main(int argc, char **argv)
{int ret; // 用于函数返回值InputEvent event; // 定义一个输入事件的结构体变量InputInit(); // 初始化输入系统IntpuDeviceInit(); // 初始化输入设备,注意这里应该是 InputDeviceInitwhile (1) // 主循环{// 打印当前文件名、函数名和行号printf("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);ret = GetInputEvent(&event); // 从输入设备获取一个事件// 再次打印,并显示GetInputEvent函数的返回值printf("%s %s %d, ret = %d\n", __FILE__, __FUNCTION__, __LINE__, ret);if (ret) { // 如果返回值不是0,表示获取事件时出错printf("GetInputEvent err!\n");return -1; // 出错返回-1}else // 如果成功获取事件{// 打印事件类型printf("%s %s %d, event.iType = %d\n", __FILE__, __FUNCTION__, __LINE__, event.iType );if (event.iType == INPUT_TYPE_TOUCH) // 如果是触摸屏事件{// 打印触摸屏事件的详细信息printf("Type : %d\n", event.iType);printf("iX : %d\n", event.iX);printf("iY : %d\n", event.iY);printf("iPressure : %d\n", event.iPressure);}else if (event.iType == INPUT_TYPE_NET) // 如果是网络事件{// 打印网络事件的详细信息printf("Type : %d\n", event.iType);printf("str : %s\n", event.str);}}}return 0; // 正常退出程序
}
详细介绍:Linux基础项目开发1:量产工具——输入系统(三)-CSDN博客
四、 文字系统:
*4.1 FontBitMap
在font_manager.h描述一个文字的位图, 定义字体位图结构体,用于存储字体的位图信息
// 定义字体位图结构体,用于存储字体的位图信息
typedef struct FontBitMap {int iLeftUpX; // 字体位图左上角的X坐标int iLeftUpY; // 字体位图左上角的Y坐标int iWidth; // 字体位图的宽度int iRows; // 字体位图的行数int iCurOriginX; // 当前字符的原点X坐标int iCurOriginY; // 当前字符的原点Y坐标int iNextOriginX; // 下一个字符的原点X坐标int iNextOriginY; // 下一个字符的原点Y坐标unsigned char *pucBuffer; // 指向字体位图数据的指针
} FontBitMap, *PFontBitMap; // FontBitMap是结构体类型,PFontBitMap是指向该结构体的指针类型
Region
因为显示系统和文字系统都需要用到这个区域结构体,所以我们直接把它拿出来定义成一个公共的头文件common.h
// 定义矩形区域结构体
typedef struct Region {int iLeftUpX; // 区域左上角的X坐标int iLeftUpY; // 区域左上角的Y坐标int iWidth; // 区域的宽度int iHeigh; // 区域的高度
}Region, *PRegion; // 定义矩形区域及其指针类型
*4.2 FontOpr
在font_manager.h定义字体操作结构体,用于管理不同字体的操作函数
// 定义字体操作结构体,用于管理不同字体的操作函数
typedef struct FontOpr {char *name; // 字体操作的名称int (*FontInit)(char *aFineName); // 初始化字体的函数指针int (*SetFontSize)(int iFontSize); // 设置字体大小的函数指针int (*GetFontBitMap)(unsigned int dwCode, PFontBitMap ptFontBitMap); // 获取字体位图的函数指针struct FontOpr *ptNext; // 指向下一个字体操作结构体的指针,用于链表管理
} FontOpr, *PFontOpr; // FontOpr是结构体类型,PFontOpr是指向该结构体的指针类型
*4.3 FontOpr g_tFreetypeOpr
在freetype.c中定义一个字体操作结构体,包含字体名字、字体初始化、设置字体大小、获取字体位图等函数指针
// 定义一个字体操作结构体,包含字体初始化、设置字体大小、获取字体位图等函数指针
static FontOpr g_tFreetypeOpr = {.name = "freetype",.FontInit = FreeTypeFontInit,.SetFontSize = FreeTypeSetFontSize,.GetFontBitMap = FreeTypeGetFontBitMap,
};
4.4 FreeTypeFontInit(char *aFineName)
在freetype.c中初始化FreeType字体库
// 初始化FreeType字体库
static int FreeTypeFontInit(char *aFineName)
{FT_Library library;int error;// 初始化FreeType库error = FT_Init_FreeType( &library ); if (error){printf("FT_Init_FreeType err\n");return -1;}// 从指定文件创建字体面error = FT_New_Face(library, aFineName, 0, &g_tFace ); if (error){printf("FT_New_Face err\n");return -1;}// 设置字体大小FT_Set_Pixel_Sizes(g_tFace, g_iDefaultFontSize, 0);return 0;
}
4.5 FreeTypeSetFontSize(int iFontSize)
在freetype.c中设置字体大小
// 设置字体大小
static int FreeTypeSetFontSize(int iFontSize)
{// 设置字体大小FT_Set_Pixel_Sizes(g_tFace, iFontSize, 0);return 0;
}
4.6 FreeTypeGetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)
在freetype.c中获取字体位图
// 获取字体位图
static int FreeTypeGetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)
{int error;FT_Vector pen;FT_Glyph glyph;// 设置当前光标位置,单位为1/64像素pen.x = ptFontBitMap->iCurOriginX * 64; pen.y = ptFontBitMap->iCurOriginY * 64; // 设置变换矩阵FT_Set_Transform(g_tFace, 0, &pen);// 加载字符位图error = FT_Load_Char(g_tFace, dwCode, FT_LOAD_RENDER);if (error){printf("FT_Load_Char error\n");return -1;}// 获取位图缓冲区ptFontBitMap->pucBuffer = slot->bitmap.buffer;// 设置字体位图的区域信息ptFontBitMap->tRegion.iLeftUpX = slot->bitmap_left;ptFontBitMap->tRegion.iLeftUpY = ptFontBitMap->iCurOriginY*2 - slot->bitmap_top;ptFontBitMap->tRegion.iWidth = slot->bitmap.width;ptFontBitMap->tRegion.iHeigh = slot->bitmap.rows;ptFontBitMap->iNextOriginX = ptFontBitMap->iCurOriginX + slot->advance.x / 64;ptFontBitMap->iNextOriginY = ptFontBitMap->iCurOriginY;return 0;
}
4.7 FreetypeRegister(void)
在freetype.c中注册FreeType字体操作结构体
// 注册FreeType字体操作结构体
void FreetypeRegister(void)
{RegisterFont(&g_tFreetypeOpr);
}
&& 4.8 字符管理器框架
我们可能要用到的字体有多种,那么怎么选择用哪个字符呢,所以我们要编写一个程序管理多种字符。
4.9 FontsRegister(void)
在font_manager.c注册字体操作结构体,这里示例了注册FreeType字体
// 注册字体操作结构体,这里示例了注册FreeType字体
void FontsRegister(void)
{extern void FreetypeRegister(void);FreetypeRegister();
}
4.10 SelectAndInitFont(char *aFontOprName, char *aFontFileName)
在font_manager.c选择并初始化一个字体操作结构体
// 选择并初始化一个字体操作结构体
int SelectAndInitFont(char *aFontOprName, char *aFontFileName)
{PFontOpr ptTmp = g_ptFonts;while (ptTmp){// 比较字体操作结构体的名称,找到匹配的结构体if (strcmp(ptTmp->name, aFontOprName) == 0)break;ptTmp = ptTmp->ptNext;}// 如果没有找到匹配的结构体,返回错误if (!ptTmp)return -1;// 设置默认的字体操作结构体g_ptDefaulFontOpr = ptTmp;// 调用结构体的初始化函数,初始化字体return ptTmp->FontInit(aFontFileName);
}
4.10 SetFontSize(int iFontSize)
在font_manager.c设置当前默认字体的字体大小
// 设置当前默认字体的字体大小
int SetFontSize(int iFontSize)
{return g_ptDefaulFontOpr->SetFontSize(iFontSize);
}
4.11 GetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)
在font_manager.c获取当前默认字体的字符位图
// 获取当前默认字体的字符位图
int GetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap)
{return g_ptDefaulFontOpr->GetFontBitMap(dwCode, ptFontBitMap);
}
*4.12 DrawFontBitMap(PFontBitMap ptFontBitMap, unsigned int dwColor)
在LCD上显示出来,在disp_manager.c中还要 实现一个绘制函数:DrawFontBitMap
void DrawFontBitMap(PFontBitMap ptFontBitMap, unsigned int dwColor)
{// 定义并初始化各种变量int i, j, p, q;// 获取绘制区域的左上角X坐标int x = ptFontBitMap->tRegion.iLeftUpX;// 获取绘制区域的左上角Y坐标int y = ptFontBitMap->tRegion.iLeftUpY;// 计算绘制区域的右下角X坐标int x_max = x + ptFontBitMap->tRegion.iWidth;// 计算绘制区域的右下角Y坐标int y_max = y + ptFontBitMap->tRegion.iHeigh;// 获取区域宽度int width = ptFontBitMap->tRegion.iWidth;// 获取位图缓冲区unsigned char *buffer = ptFontBitMap->pucBuffer;// 遍历绘制区域中的每个像素for ( j = y, q = 0; j < y_max; j++, q++ ){for ( i = x, p = 0; i < x_max; i++, p++ ){// 如果像素位置超出屏幕范围,忽略该像素if ( i < 0 || j < 0 ||i >= g_tDispBuff.iXres || j >= g_tDispBuff.iYres )continue;// 如果位图缓冲区中该像素点有数据(即需要绘制的点),则用指定颜色在屏幕上绘制该像素if (buffer[q * width + p])PutPixel(i, j, dwColor);}}
}
*4.13 测试代码:
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <font_manager.h>
#include <disp_manager.h>#define FONTDATAMAX 4096// 8x16 字体数据
static const unsigned char fontdata_8x16[FONTDATAMAX] = {// 省略了点阵数据
};// 在LCD指定位置上显示一个8*16的字符
void lcd_put_ascii(int x, int y, unsigned char c)
{unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];int i, b;unsigned char byte;for (i = 0; i < 16; i++){byte = dots[i];for (b = 7; b >= 0; b--){if (byte & (1<<b)){// 显示像素点PutPixel(x+7-b, y+i, 0xffffff); // 白色}else{// 不显示像素点PutPixel(x+7-b, y+i, 0); // 黑色}}}
}int main(int argc, char **argv)
{PDispBuff ptBuffer;int error;FontBitMap tFontBitMap;char *str= "Hello Linux";int i = 0;int lcd_x;int lcd_y;int font_size;// 检查输入参数个数if (argc != 5){printf("Usage: %s <font_file> <lcd_x> <lcd_y> <font_size>\n", argv[0]);return -1;}lcd_x = strtol(argv[2], NULL, 0);lcd_y = strtol(argv[3], NULL, 0);font_size = strtol(argv[4], NULL, 0);// 初始化显示设备DisplayInit();SelectDefaultDisplay("fb");InitDefaultDisplay();ptBuffer = GetDisplayBuffer();// 注册并初始化字体FontsRegister();error = SelectAndInitFont("freetype", argv[1]);if (error){printf("SelectAndInitFont err\n");return -1;}// 设置字体大小SetFontSize(font_size);// 循环显示每个字符while (str[i]){// 获取字符位图tFontBitMap.iCurOriginX = lcd_x;tFontBitMap.iCurOriginY = lcd_y;error = GetFontBitMap(str[i], &tFontBitMap);if (error){printf("SelectAndInitFont err\n");return -1;}// 绘制字符到缓冲区DrawFontBitMap(&tFontBitMap,0xff0000); // 红色// 刷新显示区域FlushDisplayRegion(&tFontBitMap.tRegion, ptBuffer);// 更新下一个字符的位置lcd_x = tFontBitMap.iNextOriginX;lcd_y = tFontBitMap.iNextOriginY;i++;}return 0;
}
详细介绍:Linux基础项目开发1:量产工具——文字系统(四)-CSDN博客
五、UI系统:
所谓UI,就是User Interface(用户界面),有图像界面(GUI)等,我们的UI系统,就是构造各类GUI元素,比如按钮(目前只实现按钮)
*5.1 Button
在ui.h定义Button结构体,包含按钮的名称、状态、区域、绘制函数和按下处理函数
// 定义两个函数指针类型,用于按钮的绘制和按下事件处理
typedef int (*ONDRAW_FUNC)(struct Button *ptButton, PDispBuff ptDispBuff); // 绘制函数指针类型
typedef int (*ONPRESSED_FUNC)(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent); // 按下处理函数指针类型// 定义Button结构体,包含按钮的名称、状态、区域、绘制函数和按下处理函数
typedef struct Button {char *name; // 按钮名称int status; // 按钮状态Region tRegion; // 按钮区域ONDRAW_FUNC OnDraw; // 绘制函数指针ONPRESSED_FUNC OnPressed; // 按下处理函数指针
}Button, *PButton; // Button是结构体类型,PButton是指向Button的指针类型
*5.2 InitButton(PButton ptButton, char *name, PRegion ptRegion, ONDRAW_FUNC OnDraw, ONPRESSED_FUNC OnPressed)
在button.c初始化按钮的函数
// 初始化按钮的函数
void InitButton(PButton ptButton, char *name, PRegion ptRegion, ONDRAW_FUNC OnDraw, ONPRESSED_FUNC OnPressed)
{ptButton->status = 0; // 初始化状态为0,即未按下ptButton->name = name; // 设置按钮名称ptButton->tRegion = *ptRegion; // 设置按钮区域ptButton->OnDraw = OnDraw ? OnDraw : DefaultOnDraw; // 设置绘制函数,如果未提供,则使用默认绘制函数ptButton->OnPressed = OnPressed ? OnPressed : DefaultOnPressed; // 设置按下处理函数,如果未提供,则使用默认处理函数
}
5.3 DrawRegion(PRegion ptRegion, unsigned int dwColor)
在disp_manager.c中将ptRegion这一部分区域绘制成dwColor的颜色
// 函数DrawRegion用于绘制一个指定颜色和区域的矩形
void DrawRegion(PRegion ptRegion, unsigned int dwColor)
{// 获取区域的左上角坐标int x = ptRegion->iLeftUpX;int y = ptRegion->iLeftUpY;// 获取区域的宽度和高度int width = ptRegion->iWidth;int heigh = ptRegion->iHeigh;// 使用两个嵌套的循环来遍历区域内的每一个像素int i, j;for (j = y; j < y + heigh; j++){for (i = x; i < x + width; i++){// 对于每个像素,调用PutPixel函数来设置像素的颜色PutPixel(i, j, dwColor);}}
}
5.4 DrawTextInRegionCentral(char *name, PRegion ptRegion, unsigned int dwColor)
在disp_manager.c中实现函数DrawTextInRegionCentral用于在指定区域内居中显示文本
// 函数DrawTextInRegionCentral用于在指定区域内居中显示文本
void DrawTextInRegionCentral(char *name, PRegion ptRegion, unsigned int dwColor)
{// 计算文本字符串的长度int n = strlen(name);// 计算每个字符的宽度,以便文本能够居中显示int iFontSize = ptRegion->iWidth / n / 2;FontBitMap tFontBitMap; // 字体位图结构体int iOriginX, iOriginY; // 字符绘制的起始坐标int i = 0; // 用于遍历字符串的索引int error; // 用于存储错误代码// 如果计算出的字体大小超过了区域的高度,则将字体大小设置为区域的高度if (iFontSize > ptRegion->iHeigh)iFontSize = ptRegion->iHeigh;// 计算文本在区域内的起始X坐标,确保文本居中iOriginX = (ptRegion->iWidth - n * iFontSize)/2 + ptRegion->iLeftUpX;// 计算文本在区域内的起始Y坐标,确保文本居中iOriginY = (ptRegion->iHeigh - iFontSize)/2 + iFontSize + ptRegion->iLeftUpY;// 设置字体大小SetFontSize(iFontSize);// 遍历文本字符串中的每个字符while (name[i]){// 获取当前字符的字体位图tFontBitMap.iCurOriginX = iOriginX;tFontBitMap.iCurOriginY = iOriginY;error = GetFontBitMap(name[i], &tFontBitMap);if (error){printf("SelectAndInitFont err\n");return;}// 在缓冲区上绘制当前字符的字体位图DrawFontBitMap(&tFontBitMap, dwColor);// 更新下一个字符的起始坐标iOriginX = tFontBitMap.iNextOriginX;iOriginY = tFontBitMap.iNextOriginY;i++;}
}
* 5.5 DefaultOnDraw(struct Button *ptButton, PDispBuff ptDispBuff)
在button.c默认的绘制按钮的函数
// 默认的绘制按钮的函数
static int DefaultOnDraw(struct Button *ptButton, PDispBuff ptDispBuff)
{/* 绘制按钮的底色 */DrawRegion(&ptButton->tRegion, BUTTON_DEFAULT_COLOR);/* 在按钮的中央绘制文本 */DrawTextInRegionCentral(ptButton->name, &ptButton->tRegion, BUTTON_TEXT_COLOR);/* 将绘制的区域刷新到显示设备上,比如LCD或网页 */FlushDisplayRegion(&ptButton->tRegion, ptDispBuff);return 0; // 返回0表示成功
}
*5.6 DefaultOnPressed(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent)
在button.c默认的按钮按下处理函数
// 默认的按钮按下处理函数
static int DefaultOnPressed(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent)
{unsigned int dwColor = BUTTON_DEFAULT_COLOR; // 默认底色ptButton->status = !ptButton->status; // 切换按钮状态if (ptButton->status)dwColor = BUTTON_PRESSED_COLOR; // 如果按钮被按下,改变底色/* 绘制按钮的底色 */DrawRegion(&ptButton->tRegion, dwColor);/* 在按钮的中央绘制文本 */DrawTextInRegionCentral(ptButton->name, &ptButton->tRegion, BUTTON_TEXT_COLOR);/* 将绘制的区域刷新到显示设备上 */FlushDisplayRegion(&ptButton->tRegion, ptDispBuff);
}
以上在ui.h中定义按钮的默认颜色、按下时的颜色和文本颜色
#define BUTTON_DEFAULT_COLOR 0xff0000 // 默认颜色,通常是红色
#define BUTTON_PRESSED_COLOR 0x00ff00 // 按下时的颜色,通常是绿色
#define BUTTON_TEXT_COLOR 0x000000 // 文本颜色,通常是黑色
5.7 测试代码:
// 包含必要的系统头文件
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <stdlib.h>// 包含自定义的显示管理器和字体管理器头文件
#include <disp_manager.h>
#include <font_manager.h>
#include <ui.h> // 假设这是一个包含GUI相关定义的头文件// 主函数
int main(int argc, char **argv)
{// 定义显示缓冲区和错误代码的指针,以及按钮和区域的结构体PDispBuff ptBuffer;int error;Button tButton;Region tRegion;// 检查命令行参数是否正确if (argc != 2){printf("Usage: %s <font_size>\n", argv[0]);return -1;}// 初始化显示DisplayInit();// 选择默认的显示操作接口,这里假设"fb"是一个帧缓冲设备SelectDefaultDisplay("fb");// 初始化默认的显示操作接口InitDefaultDisplay();// 获取显示缓冲区ptBuffer = GetDisplayBuffer();// 注册字体FontsRegister();// 选择并初始化字体,这里使用的是"freetype"字体,字体大小由命令行参数指定error = SelectAndInitFont("freetype", argv[1]);if (error){printf("SelectAndInitFont err\n");return -1;}// 初始化区域的坐标和大小tRegion.iLeftUpX = 200;tRegion.iLeftUpY = 200;tRegion.iWidth = 300;tRegion.iHeigh = 100;// 初始化按钮,设置按钮的文本和区域,以及回调函数InitButton(&tButton, "test", &tRegion, NULL, NULL);// 绘制按钮tButton.OnDraw(&tButton, ptBuffer);// 进入主循环,当按钮被按下时执行相应的操作while (1){tButton.OnPressed(&tButton, ptBuffer, NULL);// 暂停2秒sleep(2);}// 程序正常结束return 0;
}
详细介绍:Linux基础项目开发1:量产工具——UI系统(五)_linux ui界面开发-CSDN博客
六、页面系统:
*6.1 PageAction
定义一个结构体类型 PageAction,用于表示页面动作
// 定义一个结构体类型 PageAction,用于表示页面动作
typedef struct PageAction {// 页面动作的名称,是一个字符串指针char *name;// 指向执行页面动作的函数指针,该函数接受一个void指针参数void (*Run)(void *pParams);// 指向下一个PageAction结构体的指针,用于链表结构struct PageAction *ptNext;
} PageAction, *PPageAction;
&& 6.2 页面管理器框架
页面管理器用来管理页面,只需要实现2个函数:
1. PagesRegister : 把多个页面注册进链表
2. Page(name) :取出某个页面
6.3 PageRegister(PPageAction ptPageAction)
在page_manager.c 中实现页面动作注册函数,将新的页面动作添加到链表的头部
// 声明一个静态的全局变量,用于存储页面动作链表的头指针
static PPageAction g_ptPages = NULL;// 实现页面动作注册函数,将新的页面动作添加到链表的头部
void PageRegister(PPageAction ptPageAction)
{// 将新动作的下一个指针指向当前链表的头ptPageAction->ptNext = g_ptPages;// 更新链表头指针,使其指向新添加的动作g_ptPages = ptPageAction;
}
6.4 PPageAction Page(char *name)
在page_manager.c 中实现根据名称查找页面动作的函数
// 实现根据名称查找页面动作的函数
PPageAction Page(char *name)
{// 从链表头开始遍历PPageAction ptTmp = g_ptPages;// 遍历链表直到找到匹配的名称或者遍历完整个链表while (ptTmp){// 使用strcmp函数比较名称是否相等if (strcmp(name, ptTmp->name) == 0)// 如果找到匹配的名称,返回对应的页面动作指针return ptTmp;// 移动到下一个页面动作ptTmp = ptTmp->ptNext;}// 如果没有找到匹配的名称,返回NULLreturn NULL;
}
6.5 PageAction MainPage
在main_page.c中创建一个PageAction MainPage,定义一个静态的PageAction结构体变量,表示主页面动作
// 包含页面管理器头文件,这个文件中声明了页面动作相关的结构体和函数
#include <page_manager.h>// 包含标准输入输出头文件,提供了printf等函数
#include <stdio.h>// 定义一个静态函数,用于执行主页面动作
static void MainPageRun(void *pParams)
{// 使用printf打印文件名、函数名和当前行号,用于调试信息printf("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
}// 定义一个静态的PageAction结构体变量,表示主页面动作
static PageAction g_tMainPage = {// 设置页面动作的名称为"main".name = "main",// 设置执行页面动作的函数为MainPageRun.Run = MainPageRun,
};// 定义一个函数,用于注册主页面动作
void MainPageRegister(void)
{// 调用页面管理器的注册函数,将主页面动作添加到页面动作链表中PageRegister(&g_tMainPage);
}
6.6 测试代码:
// 包含内存映射相关的头文件
#include <sys/mman.h>// 包含系统类型定义的头文件
#include <sys/types.h>// 包含文件状态相关的头文件
#include <sys/stat.h>// 包含Unix标准函数定义的头文件
#include <unistd.h>// 包含Linux帧缓冲设备相关的头文件
#include <linux/fb.h>// 包含文件控制选项相关的头文件
#include <fcntl.h>// 包含标准输入输出函数定义的头文件
#include <stdio.h>// 包含字符串处理函数定义的头文件
#include <string.h>// 包含输入输出控制函数定义的头文件
#include <sys/ioctl.h>// 包含标准库函数定义的头文件
#include <stdlib.h>// 包含页面管理器头文件,这个文件中声明了页面动作相关的结构体和函数
#include <page_manager.h>// 程序入口点,接受命令行参数
int main(int argc, char **argv)
{// 注册所有页面动作,这通常会在程序启动时执行PagesRegister();// 查找名为"main"的页面动作,并执行其Run函数// 这里传递了一个NULL参数,表示没有额外的参数传递给Run函数Page("main")->Run(NULL);// 程序正常退出,返回0return 0;
}
详细介绍:Linux基础项目开发1:量产工具——页面系统(六)-CSDN博客
七、业务系统
7.1 业务系统流程图
在main.c中实现业务系统
// 包含内存管理、系统类型、文件状态、Unix标准函数、Linux帧缓冲设备、文件控制、标准输入输出、字符串处理、输入输出控制和标准库函数相关的头文件
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <stdlib.h>// 包含显示管理、字体管理、输入管理和页面管理相关的头文件
#include <disp_manager.h>
#include <font_manager.h>
#include <input_manager.h>
#include <page_manager.h>// 程序的主函数,接受命令行参数
int main(int argc, char **argv)
{// 声明一个显示缓冲区指针和错误码变量PDispBuff ptBuffer;int error;// 检查命令行参数的数量,如果不等于2(程序名和字体文件名),则打印使用说明并返回错误码if (argc != 2){printf("Usage: %s <font_file>\n", argv[0]);return -1;}// 初始化显示系统DisplayInit();// 选择默认的显示设备,这里是帧缓冲设备SelectDefaultDisplay("fb");// 初始化默认的显示设备InitDefaultDisplay();// 初始化输入系统,包括输入设备的初始化InputInit();IntpuDeviceInit();// 注册并初始化字体系统FontsRegister();error = SelectAndInitFont("freetype", argv[1]);// 如果字体选择和初始化失败,打印错误信息并返回错误码if (error){printf("SelectAndInitFont err\n");return -1;}// 注册页面系统PagesRegister();// 运行主页面Page("main")->Run(NULL);// 程序正常退出,返回0return 0;
}
程序开始运行,进入
main
函数,首先检查命令行参数的数量。如果参数数量不等于2(一个是程序自身的路径,另一个是字体文件的路径),则打印使用说明并返回错误码,程序结束。如果命令行参数数量正确,程序会进行显示系统的初始化(
DisplayInit
)。这可能包括分配内存、设置显示分辨率等操作。然后,程序会选择默认的显示设备(
SelectDefaultDisplay
),在这个例子中是帧缓冲设备(frame buffer),并初始化该显示设备(InitDefaultDisplay
)。帧缓冲设备是一种可以直接在内存中操作像素来绘制图像的设备。接下来,程序会初始化输入系统(
InputInit
和IntpuDeviceInit
)。输入设备的初始化。然后,程序会注册并初始化字体系统(
FontsRegister
和SelectAndInitFont
)。字体文件的路径从命令行参数中获取。如果字体选择和初始化失败,程序会打印错误信息并返回错误码,结束运行。接着,程序会注册页面系统(
PagesRegister
)。页面系统通常用于管理GUI的各个页面(如主页、菜单页等)。最后,程序会运行主页面(
Page("main")->Run(NULL)
)。这通常意味着显示主页面并等待用户的输入。
7.2 主页面流程图:
在main_page.c中实现主页面流程
&&& 7.3 抽象出配置文件结构体(ItemCfg)
在config.h中定义一个结构体,用于表示配置文件中的一个条目
// 定义一个结构体,用于表示配置文件中的一个条目
typedef struct ItemCfg {int index; // 条目的索引char name[100]; // 条目的名称,最大长度为100个字符int bCanBeTouched; // 条目是否可以被触摸(例如,在GUI中)char command[100]; // 条目对应的命令,最大长度为100个字符
} ItemCfg, *PItemCfg; // ItemCfg是结构体类型,PItemCfg是指向该结构体的指针类型
7.4 ParseConfigFile(char *strFileName)
在config.h中定义一个函数,用于解析配置文件
// 定义一个静态数组,用于存储配置文件中的条目,最大数量由ITEMCFG_MAX_NUM定义
static ItemCfg g_tItemCfgs[ITEMCFG_MAX_NUM];// 定义一个静态变量,用于记录当前配置文件中条目的数量
static int g_iItemCfgCount = 0;// 定义一个函数,用于解析配置文件
int ParseConfigFile(char *strFileName)
{FILE *fp; // 文件指针,用于打开和读取文件char buf[100]; // 缓冲区,用于存储从文件读取的一行数据char *p = buf; // 指向缓冲区的指针,用于处理读取的数据/* 1. 打开配置文件 */fp = fopen(CFG_FILE, "r");if (!fp){printf("can not open cfg file %s\n", CFG_FILE);return -1; // 打开文件失败,返回-1}while (fgets(buf, 100, fp)) // 循环读取文件的每一行{/* 2.1 读取每一行数据 */buf[99] = '\0'; // 确保缓冲区末尾有一个字符串结束符/* 2.2 跳过开头的空格或制表符 */p = buf;while (*p == ' ' || *p =='\t')p++;/* 2.3 忽略注释行(以#开头的行) */if (*p == '#')continue;/* 2.4 处理有效行数据 */g_tItemCfgs[g_iItemCfgCount].command[0] = '\0'; // 初始化命令字段为空字符串g_tItemCfgs[g_iItemCfgCount].index = g_iItemCfgCount; // 设置索引为当前条目计数器值sscanf(p, "%s %d %s", g_tItemCfgs[g_iItemCfgCount].name, &g_tItemCfgs[g_iItemCfgCount].bCanBeTouched, \g_tItemCfgs[g_iItemCfgCount].command); // 从缓冲区读取数据到结构体字段g_iItemCfgCount++; // 增加条目计数器}return 0; // 解析成功,返回0
}
7.4 GetItemCfgCount(void)
在config.h中定义一个函数,用于获取配置文件中条目的数量
// 定义一个函数,用于获取配置文件中条目的数量
int GetItemCfgCount(void)
{return g_iItemCfgCount; // 返回条目计数器的值
}
7.5 GetItemCfgByIndex(int index)
在config.h中定义一个函数,用于根据索引获取配置文件中的条目
// 定义一个函数,用于根据索引获取配置文件中的条目
PItemCfg GetItemCfgByIndex(int index)
{if (index < g_iItemCfgCount) // 检查索引是否在有效范围内return &g_tItemCfgs[index]; // 返回指向对应条目的指针elsereturn NULL; // 索引无效,返回NULL
}
7.6 GetItemCfgByName(char *name)
在config.h中定义一个函数,用于根据名称获取配置文件中的条目
// 定义一个函数,用于根据名称获取配置文件中的条目
PItemCfg GetItemCfgByName(char *name)
{int i;for (i = 0; i < g_iItemCfgCount; i++) // 遍历所有条目{if (strcmp(name, g_tItemCfgs[i].name) == 0) // 比较名称是否匹配return &g_tItemCfgs[i]; // 返回指向匹配条目的指针}return NULL; // 没有找到匹配的条目,返回NULL
}
*7.7 生成界面
想显示这样的界面
通过配置文件可以知道有哪些按钮,知道每个按钮的名字
只剩最后一项了:怎么确定每个按钮的位置、大小?需要计算!
7.8 *MainPageRun(void *pParams)
在main_page.c中定义了主页面的运行函数 MainPageRun
,它负责读取配置文件、生成按钮和界面,并进入一个无限循环来监听和处理
static void MainPageRun(void *pParams)
{int error; // 用于存储函数执行过程中的错误代码InputEvent tInputEvent; // 用于存储输入事件的结构体PButton ptButton; // 指向按钮对象的指针PDispBuff ptDispBuff = GetDisplayBuffer(); // 获取显示缓冲区的指针/* 读取配置文件 */error = ParseConfigFile(); // 解析配置文件,将错误代码存储在error变量中if (error) // 如果解析配置文件出错return; // 直接返回,不继续执行/* 根据配置文件生成按钮、界面 */GenerateButtons(); // 根据配置文件中的信息生成按钮和界面while (1) // 无限循环,用于持续监听和处理输入事件{/* 读取输入事件 */error = GetInputEvent(&tInputEvent); // 获取用户的输入事件,将错误代码存储在error变量中if (error) // 如果获取输入事件出错continue; // 跳过本次循环,继续下一次循环/* 根据输入事件找到按钮 */ptButton = GetButtonByInputEvent(&tInputEvent); // 根据输入事件找到对应的按钮对象if (!ptButton) // 如果没有找到对应的按钮continue; // 跳过本次循环,继续下一次循环/* 调用按钮的OnPressed函数 */ptButton->OnPressed(ptButton, ptDispBuff, &tInputEvent); // 调用找到的按钮的OnPressed函数,处理按钮按下事件}
}
7.9 GenerateButtons(void)
在main_page.c中根据屏幕的分辨率和按钮总数计算每个按钮的大小和位置,并居中显示这些按钮。按钮的宽度和高度根据屏幕分辨率和黄金分割比(0.618)进行计算以确定其尺寸,同时保留了一定的间隔(X_GAP 和 Y_GAP)。代码中利用两层嵌套循环计算并设置每个按钮的位置和区域,并对每个按钮对象进行初始化和绘制。
static void GenerateButtons(void)
{int width, height; // 用于存储单个按钮的宽度和高度int n_per_line; // 用于存储每行能放置的按钮数量int row, rows; // row用于记录当前行号,rows用于记录总行数int col; // 用于记录当前列号int n; // 用于存储按钮总数PDispBuff pDispBuff; // 指向显示缓冲区的指针int xres, yres; // 用于存储显示区域的水平和垂直分辨率int start_x, start_y; // 用于存储按钮区域开始的x,y坐标int pre_start_x, pre_start_y; // 用于记录前一个按钮的起始x,y坐标PButton pButton; // 用于指向当前操作的按钮对象int i = 0; // 用于在循环中迭代按钮对象数组/* 算出单个按钮的width/height */g_tButtonCnt = n = GetItemCfgCount(); // 从配置获取按钮总数pDispBuff = GetDisplayBuffer(); // 获取显示缓冲区对象xres = pDispBuff->iXres; // 获取屏幕的水平分辨率yres = pDispBuff->iYres; // 获取屏幕的垂直分辨率width = sqrt(1.0 / 0.618 * xres * yres / n); // 根据页面比例和按钮总数计算单个按钮的宽度n_per_line = xres / width + 1; // 计算每行可以放置的按钮数目width = xres / n_per_line; // 调整按钮宽度,以适应每行的按钮数目height = 0.618 * width; // 按照黄金比例计算按钮的高度/* 居中显示: 计算每个按钮的region */start_x = (xres - width * n_per_line) / 2; // 计算按钮起始x坐标,使其居中显示rows = n / n_per_line; // 计算总行数if (rows * n_per_line < n) // 如果按钮总数无法被整除,则行数+1rows++;start_y = (yres - rows * height) / 2; // 计算按钮起始y坐标,使其居中显示/* 计算每个按钮的region */for (row = 0; (row < rows) && (i < n); row++) // 遍历所有行{pre_start_y = start_y + row * height; // 计算当前行的y坐标pre_start_x = start_x - width; // 设置初始x坐标值for (col = 0; (col < n_per_line) && (i < n); col++) // 遍历每行的按钮{pButton = &g_tButtons[i]; // 获取当前按钮对象的引用pButton->tRegion.iLeftUpX = pre_start_x + width; // 设置按钮的左上角x坐标pButton->tRegion.iLeftUpY = pre_start_y; // 设置按钮的左上角y坐标pButton->tRegion.iWidth = width - X_GAP; // 设置按钮的宽度,并减去预设的间隔pButton->tRegion.iHeigh = height - Y_GAP; // 设置按钮的高度,并减去预设的间隔pre_start_x = pButton->tRegion.iLeftUpX; // 更新x坐标以供下一个按钮使用/* InitButton */InitButton(pButton, GetItemCfgByIndex(i)->name, NULL, NULL, MainPageOnPressed); // 初始化按钮,设置按钮的名称和事件处理函数i++; // 按钮索引递增,移动到下一个按钮对象}}/* OnDraw */for (i = 0; i < n; i++) // 遍历所有按钮对象g_tButtons[i].OnDraw(&g_tButtons[i], pDispBuff); // 调用按钮的OnDraw方法绘制按钮
}
*7.10 处理输入事件:GetButtonByInputEvent(PInputEvent ptInputEvent)
在main_page.c中定义了一个名为 GetButtonByInputEvent
的函数,其目的是根据提供的输入事件来寻找对应的按钮对象。输入事件 PInputEvent
包含事件类型 iType
以及与特定事件类型相关的数据(例如触摸事件的坐标或网络事件的字符串数据)。
static PButton GetButtonByInputEvent(PInputEvent ptInputEvent)
{int i;char name[100]; // 用于存储从网络事件中解析出的按钮名称if (ptInputEvent->iType == INPUT_TYPE_TOUCH) // 检查输入事件是否为触摸类型{for (i = 0; i < g_tButtonCnt; i++) // 遍历所有按钮{// 检查触摸点是否在当前遍历按钮的区域内if (isTouchPointInRegion(ptInputEvent->iX, ptInputEvent->iY, &g_tButtons[i].tRegion))return &g_tButtons[i]; // 如果是,返回对应的按钮对象}}else if (ptInputEvent->iType == INPUT_TYPE_NET) // 检查输入事件是否为网络事件{// 解析网络事件中的字符串,提取按钮名称sscanf(ptInputEvent->str, "%s", name);return GetButtonByName(name); // 根据按钮名称查找并返回对应的按钮对象}else{return NULL; // 如果事件类型既不是触摸也不是网络,返回NULL}return NULL; // 如果没有找到匹配的按钮,返回NULL
}
7.11 isTouchPointInRegion(int iX, int iY, PRegion ptRegion)
在main_page.c中定义了一个名为 isTouchPointInRegion
的静态函数,用于检查给定的触摸点坐标 (iX, iY)
是否位于指定的区域 ptRegion
内。区域由其左上角坐标 (iLeftUpX, iLeftUpY)
和宽度 iWidth
以及高度 iHeigh
定义。
- 函数首先检查触摸点的X坐标是否在区域的左边界和右边界之间。如果触摸点的X坐标小于区域的左上角X坐标,或者大于或等于左上角X坐标加上区域的宽度(即右下角X坐标),则触摸点不在区域内,函数返回0。
- 接着,函数检查触摸点的Y坐标是否在区域的上边界和下边界之间。如果触摸点的Y坐标小于区域的左上角Y坐标,或者大于或等于左上角Y坐标加上区域的高度(即右下角Y坐标),则触摸点不在区域内,函数返回0。
- 如果触摸点的X和Y坐标都通过了上述检查,即触摸点完全位于区域内,函数返回1,表示触摸点在区域内。
static int isTouchPointInRegion(int iX, int iY, PRegion ptRegion)
{// 检查触摸点的X坐标是否在区域的左上角X坐标和右下角X坐标之间if (iX < ptRegion->iLeftUpX || iX >= ptRegion->iLeftUpX + ptRegion->iWidth)return 0; // 如果不在,返回0,表示触摸点不在区域内// 检查触摸点的Y坐标是否在区域的左上角Y坐标和右下角Y坐标之间if (iY < ptRegion->iLeftUpY || iY >= ptRegion->iLeftUpY + ptRegion->iHeigh)return 0; // 如果不在,返回0,表示触摸点不在区域内return 1; // 如果触摸点的X和Y坐标都在区域内,返回1,表示触摸点在区域内
}
7.12 GetButtonByName(char *name)
在main_page.c中定义了一个名为 GetButtonByName
的静态函数,其目的是根据提供的名称来查找并返回对应的按钮对象。函数接受一个字符串指针 name
作为参数,该参数代表要查找的按钮的名称。
- 函数通过一个循环遍历全局数组
g_tButtons
中的所有按钮,数组的大小由g_tButtonCnt
决定。- 在循环中,使用
strcmp
函数来比较传入
static PButton GetButtonByName(char *name)
{int i;// 遍历所有按钮for (i = 0; i < g_tButtonCnt; i++){// 使用strcmp函数比较传入的名称与当前按钮的名称是否相同if (strcmp(name, g_tButtons[i].name) == 0)return &g_tButtons[i]; // 如果名称匹配,返回当前按钮的指针}return NULL; // 如果遍历完所有按钮都没有找到匹配的名称,返回NULL
}
7.13 MainPageOnPressed(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent)
在main_page.c中定义了一个名为 MainPageOnPressed
的静态函数,用于处理按钮的按下事件。函数接受三个参数:一个指向按钮结构体的指针 ptButton
,一个指向显示缓冲区的指针 ptDispBuff
,以及一个指向输入事件结构体的指针 ptInputEvent
。
- 函数首先初始化按钮的颜色为默认颜色,并准备用于存储名称和状态的缓冲区。
- 然后,根据输入事件的类型(触摸屏事件或网络事件),执行不同的逻辑。
- 对于触摸屏事件,函数检查按钮是否可以被点击,如果可以,则切换按钮的状态并相应地修改按钮的颜色。
- 对于网络事件,函数从输入事件中解析出名称和状态,并根据状态的不同设置按钮的颜色。如果状态以数字开头,则将按钮名称更新为该数字,并设置为百分比颜色。
- 如果事件类型不是触摸屏或网络事件,或者状态不符合任何条件,函数返回-1。
- 最后,函数绘制按钮的底色,在按钮区域中央绘制文字,并将更新后的按钮区域刷新到显示缓冲区,然后返回0表示成功处理事件。
static int MainPageOnPressed(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent)
{unsigned int dwColor = BUTTON_DEFAULT_COLOR; // 初始化按钮颜色为默认颜色char name[100]; // 用于存储名称的缓冲区char status[100]; // 用于存储状态的缓冲区char *strButton; // 指向按钮名称的指针strButton = ptButton->name; // 获取按钮的名称/* 1. 处理触摸屏事件 */if (ptInputEvent->iType == INPUT_TYPE_TOUCH){/* 1.1 检查按钮是否可以被点击 */if (GetItemCfgByName(ptButton->name)->bCanBeTouched == 0)return -1; // 如果按钮不可点击,返回-1/* 1.2 修改按钮状态和颜色 */ptButton->status = !ptButton->status; // 切换按钮状态if (ptButton->status)dwColor = BUTTON_PRESSED_COLOR; // 如果按钮被按下,设置为按下时的颜色}else if (ptInputEvent->iType == INPUT_TYPE_NET){/* 2. 处理网络事件 *//* 根据传入的字符串修改颜色 */sscanf(ptInputEvent->str, "%s %s", name, status); // 从输入事件中解析名称和状态if (strcmp(status, "ok") == 0)dwColor = BUTTON_PRESSED_COLOR; // 如果状态为"ok",设置为按下时的颜色else if (strcmp(status, "err") == 0)dwColor = BUTTON_DEFAULT_COLOR; // 如果状态为"err",设置为默认颜色else if (status[0] >= '0' && status[0] <= '9'){dwColor = BUTTON_PERCENT_COLOR; // 如果状态以数字开头,设置为百分比颜色strButton = status; // 更新按钮名称}elsereturn -1; // 如果状态不符合任何条件,返回-1}else{return -1; // 如果事件类型不是触摸屏或网络事件,返回-1}/* 绘制按钮底色 */DrawRegion(&ptButton->tRegion, dwColor); // 根据当前颜色绘制按钮区域/* 在按钮区域中央绘制文字 */DrawTextInRegionCentral(strButton, &ptButton->tRegion, BUTTON_TEXT_COLOR); // 在按钮中央绘制文字,颜色为按钮文字颜色/* 刷新显示到LCD或Web界面 */FlushDisplayRegion(&ptButton->tRegion, ptDispBuff); // 刷新按钮区域到显示缓冲区return 0; // 返回0表示成功处理事件
}
详细介绍:Linux基础项目开发1:量产工具——业务系统(七)_量产工具开发-CSDN博客
相关文章:

【IMX6ULL项目】IMX6ULL上Linux系统实现产测工具框架
电子产品量产测试与烧写工具。这是一套软件,用在我们的实际生产中, 有如下特点: 1.简单易用: 把这套软件烧写在 SD 卡上,插到 IMX6ULL 板子里并启动,它就会自动测试各个模块、烧写 EMMC 系统。 工人只要按…...

【Linux基础】Vim保姆级一键配置教程(手把手教你把Vim打造成高效率C++开发环境)
目录 一、前言 二、安装Vim 三、原始Vim编译器的缺陷分析 四、Vim配置 🥝预备知识----.vimrc 隐藏文件 🍋手动配置 Vim --- (不推荐) 🍇自动化一键配置 Vim --- (强烈推荐) ✨功能演示 五、共勉 一、前言 Vim作为…...

Gartner发布准备应对勒索软件攻击指南:勒索软件攻击的三个阶段及其防御生命周期
攻击者改变了策略,在某些情况下转向勒索软件。安全和风险管理领导者必须通过提高检测和预防能力来为勒索软件攻击做好准备,同时还要改进其事后应对策略。 主要发现 勒索软件(无加密的数据盗窃攻击)是攻击者越来越多地使用的策略。…...

IB 公式解析
IB损失 自我感悟 根据对决策边界的影响程度来分配权重。影响程度越大,分配到的权重越小;影响程度越小,分配到的权重越大。 最后其实就是平衡因子和交叉熵损失的输出的乘积 公式 3.2. Influence Function 影响函数允许我们在移除样本时估…...

开发辅助工具的缩写
开发辅助工具的缩写有很多,这些工具通常是为了提高软件开发效率、代码质量和团队协作效率而设计的。以下是一些常见的开发辅助工具及其缩写: IDE - Integrated Development Environment(集成开发环境) VCS - Version Control Sys…...

linux程序分析命令(一)
linux程序分析命令(一) **ldd:**用于打印共享库依赖。这个命令会显示出一个可执行文件所依赖的所有共享库(动态链接库),这对于解决运行时库依赖问题非常有用。**nm:**用于列出对象文件的符号表。这个命令可以显示出定…...

MYSQL数据库-SQL语句
数据库相关概念 名称全称简称数据库存储数据的仓库,数据是有组织的进行存储DataBase(DB)数据库管理系统操纵和管理数据库的大型软件DataBase Management System(DBMS)SQL操作关系型数据库的编程语言,定义了一套操作关系型数据库统一标准Structured Quer…...

MyBatis认识
一、定义 MyBatis是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java O…...

【WEEK11】 【DAY6】Employee Management System Part 7【English Version】
2024.5.11 Saturday Continued from 【WEEK11】 【DAY5】Employee Management System Part 6【English Version】 Contents 10.8. Delete and 404 Handling10.8.1. Modify list.html10.8.2. Modify EmployeeController.java10.8.3. Restart10.8.4. 404 Page Handling10.8.4.1. …...

【52】Camunda8-Zeebe核心引擎-Clustering与流程生命周期
Clustering集群 Zeebe本质是作为一个brokers集群运行,形成一个点对点网络。在这个网络中,所有brokers的功能与服务都相同,没有单点故障。 Gossip协议 Zeebe实现了gossip协议,并借此知悉哪些broker当前是集群的一部分。 集群使用…...

从零开始的软件测试学习之旅(八)jmeter线程组参数化及函数学习
jmeter线程组参数化及函数学习 Jmeter基础基本使用流程组件与元件 线程组线程的执行方式Jmeter组件执行顺序 常见属性设置查看结果数的作用域举例 Jmeter参数化实现方式1.用户定义参数2.用户参数3.函数4.csv数据文件设置 每日复习 Jmeter基础 基本使用流程 启动项目案例 启动…...

图文并茂:解析Spring Boot Controller返回图片的三种方式
欢迎来到我的博客,代码的世界里,每一行都是一个故事 图文并茂:解析Spring Boot Controller返回图片的三种方式 前言使用Base64编码返回图片使用byte数组返回图片使用Resource对象返回图片图片格式转换与性能对比 前言 在互联网的世界里&…...

问题处理记录 | 表输出报错 Packet for query is too large (5,214,153 > 4,194,304).
这个错误是由于MySQL服务器接收到的查询数据包太大而引起的。具体来说,错误消息中提到的数据包大小为5,214,153字节,而MySQL服务器默认只接受最大为4,194,304字节的数据包。 要解决这个问题,你可以尝试通过修改MySQL服务器的配置来增大max_a…...

数据结构_栈和队列(Stack Queue)
✨✨所属专栏:数据结构✨✨ ✨✨作者主页:嶔某✨✨ 栈: 代码:function/数据结构_栈/stack.c 钦某/c-language-learning - 码云 - 开源中国 (gitee.com)https://gitee.com/wang-qin928/c-language-learning/blob/master/function/…...

基于docker 的elasticsearch冷热分离及生命周期管理
文章目录 冷热集群架构的应用场景冷热集群架构的优势冷热集群架构实战搭建集群 索引生命周期管理认识索引生命周期索引生命周期管理的历史演变索引生命周期管理的基础知识Rollover:滚动索引 冷热集群架构的应用场景 某客户的线上业务场景如下:系统每天增…...

pikachu靶场(xss通关教程)
(注:若复制注入代码攻击无效,请手动输入注入语句,在英文输入法下) 反射型xss(get型) 1.打开网站 发现有个框,然后我们在框中输入一个“1”进行测试, 可以看到提交的数据在url处有显示…...

实验0.0 Visual Studio 2022安装指南
Visual Studio 2022 是一个功能强大的开发工具,对于计算机专业的学生来说,它不仅可以帮助你完成学业项目,还能为你将来的职业生涯打下坚实的基础。通过学习和使用 Visual Studio,你将能够更高效地开发软件,并在编程领域…...

数据结构之----线性表
线性表分为 顺序存储结构 和 链式存储结构 线性表的顺序存储结构: 线性表的顺序存储结构,指的是用一段地址连续的存储单元依次存储线性表的数据元素。 1,顺序表的结构: #define MAXSIZE 20 typedef int El…...

thinkphp5.1 模型auto
在ThinkPHP5.1中,模型的自动完成功能可以通过在模型类中定义auto属性来实现。这个属性是一个数组,包含了需要自动填充的字段和对应的处理规则。 以下是一个简单的例子,展示了如何在ThinkPHP5.1的模型中使用自动完成功能: <?…...

企业微信创建应用(一)
登录到企业微信后台管理(https://work.weixin.qq.com/)进入自建应用(应用管理-应用-创建应用) 3.查看参数AgentId和 Secret 4.企业微信查看效果...

Cosmo Bunny Girl
可爱的宇宙兔女郎的3D模型。用额外的骨骼装配到Humanoid上,Apple混合了形状。完全模块化,包括不带衣服的身体。 技术细节 内置,包括URP和HDRP PDF。还包括关于如何启用URP和HDRP的说明。 LOD 0:面:40076,tris 76694,verts 44783 装配了Humanoid。添加到Humanoid中的其他…...

初始化linux数据盘(3TB)分区-格式化-挂载目录
场景说明:某云给我们服务器加载了一块3TB的硬盘扩容(没有直接扩,原因是原来的盘做的是mbr(什么年代了,谁干的)的分区,最大识别2TB) 确认磁盘 输入命令lsblk 查看数据盘信息 &#…...

NFS网络文件系统的应用
1.配置2台服务器要求如下: a)服务器1: 主机名:user-server.timinglee.org ip地址: 172.25.254.100 [rootserver100 桌面]# hostnamectl hostname user-server.timinglee.org [rootserver100 桌面]# ifconfig eth0: fl…...

AttributeError: module ‘PIL.Image‘ has no attribute ‘ANTIALIAS‘
问题描述 修改图片大小的时候,代码报错:AttributeError: module PIL.Image has no attribute ANTIALIAS 解决方案 在pillow的10.0.0版本中,ANTIALIAS方法被删除了。 方法1:修改版本(不推荐) pip instal…...

进程的共享主存通信实验
进程的共享主存通信 【预备知识】 共享存储区为进程提供了直接通过主存进行通信的有效手段,不像消息缓冲机制那样需要系统提供缓冲,也不像pipe机制那样需要事先建立一个特殊文件,而是由通信双方直接访问某些共享虚拟储存空间。在Linux中&…...

深度缓冲技术在AI去衣中的神奇作用
引言: 随着人工智能技术的飞速发展,其在图形处理和视觉领域的应用日益增多。AI去衣技术便是其中一个颇具争议但又技术上引人入胜的话题。今天,我们将深入探讨一项关键技术——深度缓冲(Depth Buffering),它…...

能效?性能?一个关于Windows下使用openssl speed进行速度测试的诡异问题
问题描述 最近的某个软件用到了openssl,所以就想着测试一下速度。我的电脑是惠普的,CPU是AMD Ryzen 7 PRO 6850HS,系统是Win11。我使用openssl自带的speed测试加密/解密的速度,命令大致如下: openssl speed -evp aes…...

block性能考虑和线程安全
性能考虑 频繁地创建和销毁大量的 block 可能会对性能造成影响,特别是当这些 block 被拷贝到堆上时。同时,block 捕获大量数据时也会增加内存使用。 在讨论性能考虑时,主要关注的是 block 的创建、拷贝到堆上以及捕获变量的成本。以下是针对…...

没有公网ip,如何实现外网访问内网?
目前拨号上网是最广泛的上网方式,这种方式优点是价格便宜,缺点是没有固定公网ip,每次重新您拨号ip地址都会变。如果有一台服务器,需要实现外网访问,在没有固定公网ip的环境下,该如何实现呢?使用…...

Python中如何将小数转化为百分数进行输出
小数转化为百分数 Python中如何将小数转化为百分数进行输出基本概念使用字符串格式化1. 使用字符串格式化操作符 %2. 使用str.format()方法3. 使用f-string(格式化字符串字面量) **重点内容**:**无论是通过使用%格式化操作符、str.format()方…...