一、流程代码框架
1.业务系统流程图
2.主页面流程图:
3.main.c
#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; 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); return 0; }
第22~26行:打印用法
第29~35行:初始化显示系统
第29行:初始化显示设备
第30行:选择默认的显示设备
第31行:初始化这个默认的设备
第35行:获得显示区域
第38、39行:初始化输入系统
第38行:初始化输入设备
第39行:选择默认输入设备
第43~50行:初始化文字系统
第45行:选择字库
第53行:初始化页面系统
第55行:运行业务系统的主页面
4.main_page.c
#include <page_manager.h> #include <stdio.h> static void MainPageRun(void *pParams) { /* 读取配置文件 */ /* 根据配置文件生成按钮、界面 */ while (1) { /* 读取输入事件 */ /* 根据输入事件找到按钮 */ /* 调用按钮的OnPressed函数 */ } } static PageAction g_tMainPage = { .name = "main", .Run = MainPageRun, }; void MainPageRegister(void) { PageRegister(&g_tMainPage); }
二、处理配置文件
对于我们要制作的页面,里面有很多按钮,那么我们怎么知道每个按钮里面都存放着什么呢?这时我们就需要配置文件,以后我们想要打开哪个按钮,调整测试模块时,可以直接修改配置文件即可,就不需要重新修改,编译程序就可以带来非常大的灵活性。
1.配置文件的样子
2. 怎么处理配置文件
对于配置文件的每一行,都创建一个ItemCfg结构体
并对外提供函数,比如获得某个配置项
3.config.h
#ifndef _CONFIG_H #define _CONFIG_H #include <common.h> #define ITEMCFG_MAX_NUM 30 #define CFG_FILE "/etc/test_gui/gui.conf" typedef struct ItemCfg { int index; char name[100]; int bCanBeTouched; char command[100]; }ItemCfg, *PItemCfg; int ParseConfigFile(void); int GetItemCfgCount(void); PItemCfg GetItemCfgByIndex(int index); PItemCfg GetItemCfgByName(char *name); #endif
第7行:解析配置文件,得到的结构体保存到数组里,数组的内存大小为30
第8行:提供默认的配置文件
第11行:描述配置文件的哪一项
第12行:描述配置文件的名字
第13行:描述能否被点击
第14行:状态发生变化时调用某个命令
第17行:传入配置文件的名字解析配置文件,得到ItemCfg结构体
第18行:确定有多少行,也就是确定出有多少个按钮
第19行:传入某个index,返回某个配置项
第20行:传入名字,返回某个配置项
4.config.c
#include <string.h> #include <config.h> #include <stdio.h> 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. open config file */ fp = fopen(CFG_FILE, "r"); if (!fp) { printf("can not open cfg file %s\n", CFG_FILE); return -1; } while (fgets(buf, 100, fp)) { /* 2.1 read each line */ buf[99] = '\0'; /* 2.2 吃掉开头的空格或TAB */ 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; } int GetItemCfgCount(void) { return g_iItemCfgCount; } PItemCfg GetItemCfgByIndex(int index) { if (index < g_iItemCfgCount) return &g_tItemCfgs[index]; else return NULL; } 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; }
第6行:解析配置文件,得到ItemCfg结构体保存到g_tItemCfgs[ITEMCFG_MAX_NUM]这个数组里
第7行:解析配置文件以后,记录配置文件有多少项
int ParseConfigFile(char *strFileName)
第9~45行: 进行解析配置文件
第13行:读进一行放到buf数组里
第16行:打开文件 ,只需要读(r)即
第19行:如果没有打开的话,打印不能打开这个文件,表示配置失败
第23行:fgets(buf, 100, fp)读入一行
第26行:读出每一行,最大数量是100
第29~31行:吃掉开头的空格或TAB
第34~35行:忽略注释
第38~42行:进行处理
第38行:根据command[0] 是否为 '\0' 来判断g_tItemCfgs里是否有命令,如果第0项没有command就表明command从配置文件里面得到了他的值
第44行:返回0表示成功
int GetItemCfgCount(void)
第47~50行:确定有多少行,也就是确定出有多少个按钮,直接获得g_iItemCfgCount即可
PItemCfg GetItemCfgByIndex(int index)
第52~58行:通过index获得某一项
PItemCfg GetItemCfgByName(char *name)
第60~69行:通过名字获得某一项
三、生成界面
想显示这样的界面
通过配置文件可以知道有哪些按钮,知道每个按钮的名字
只剩最后一项了:怎么确定每个按钮的位置、大小?需要计算!
1.计算每个按钮的Region
2.main_page.c
#include <ui.h> #include <config.h> #include <page_manager.h> #include <stdio.h> #include <math.h> #include <string.h> #define X_GAP 5 #define Y_GAP 5 static Button g_tButtons[ITEMCFG_MAX_NUM]; static void GenerateButtons(void) { int width, height; int n_per_line; int row, rows; int col; int n; PDispBuff pDispBuff; int xres, yres; int start_x, start_y; int pre_start_x, pre_start_y; PButton pButton; int i = 0; /* 算出单个按钮的width/height */ 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; rows = n / n_per_line; if (rows * n_per_line < n) rows++; start_y = (yres - rows*height)/2; /* 计算每个按钮的region */ for (row = 0; (row < rows) && (i < n); row++) { pre_start_y = start_y + row * height; pre_start_x = start_x - width; for (col = 0; (col < n_per_line) && (i < n); col++) { pButton = &g_tButtons[i]; pButton->tRegion.iLeftUpX = pre_start_x + width; pButton->tRegion.iLeftUpY = pre_start_y; pButton->tRegion.iWidth = width - X_GAP; pButton->tRegion.iHeigh = height - Y_GAP; pre_start_x = pButton->tRegion.iLeftUpX; /* InitButton */ InitButton(pButton, GetItemCfgByIndex(i)->name, NULL, NULL, NULL); i++; } } /* OnDraw */ for (i = 0; i < n; i++) g_tButtons[i].OnDraw(&g_tButtons[i], pDispBuff); } static void MainPageRun(void *pParams) { int error; /* 读取配置文件 */ error = ParseConfigFile(); if (error) return; /* 根据配置文件生成按钮、界面 */ GenerateButtons(); while (1) { /* 读取输入事件 */ /* 根据输入事件找到按钮 */ /* 调用按钮的OnPressed函数 */ } } static PageAction g_tMainPage = { .name = "main", .Run = MainPageRun, }; void MainPageRegister(void) { PageRegister(&g_tMainPage); }
第77~75行:读取配置文件
第80~89行:根据配置文件生成按钮、界面
static void GenerateButtons(void)
第13~69行:生成按钮
第28~36行:算出单个按钮的width/height
第28行:算出有多少个按钮
第30~32行:获得屏幕分辨率
第33行:算出按钮理想宽度
第34行:算出每一行最多显示出多少个按钮
第35行:再反算出实际按钮宽度
第36行:根据宽度算出高度
第39~44行:居中显示: 计算每个按钮的区域region
第39~44行:算出左上角的具体位置
第40行:算出有多少行
第41行:如果rows * n_per_line < n则有小数出现,需要给rows++
第46~64行:计算每个按钮的区域region
第49、50行:前一个按钮的位置
第54、55行:计算下一个按钮的位置
第56、57行:计算下一个按钮的宽度和高度,并且留5个距离的间隙
第58行:更新起始位置
第61行:初始化按钮
GetItemCfgByIndex(i)->name从配置文件中得到
void InitButton(PButton ptButton, char *name, PRegion ptRegion, ONDRAW_FUNC OnDraw, ONPRESSED_FUNC OnPressed) { ptButton->status = 0; ptButton->name = name; if (ptRegion) ptButton->tRegion = *ptRegion; ptButton->OnDraw = OnDraw ? OnDraw : DefaultOnDraw; ptButton->OnPressed = OnPressed ? OnPressed : DefaultOnPressed; }
第67~68行:显示出按钮
四、处理输入事件
我们得到的输入事件,可能来自触摸屏,也可能是其他APP发来的网络数据
对于触摸屏事件,根据iX、iY找到按钮
对于网络数据,我们限定为这样的格式:
“name ok”、“name err”、“name 70%”
根据name找到按钮
对于按钮,我们要提供自己的OnPressed函数,不适用UI系统默认的函数
main_page.c
#include <config.h> #include <stdio.h> #include <ui.h> #include <page_manager.h> #include <math.h> #include <string.h> #define X_GAP 5 #define Y_GAP 5 static Button g_tButtons[ITEMCFG_MAX_NUM]; static int g_tButtonCnt; 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.2 修改颜色 */ ptButton->status = !ptButton->status; if (ptButton->status) dwColor = BUTTON_PRESSED_COLOR; } else if (ptInputEvent->iType == INPUT_TYPE_NET) { /* 2. 对于网络事件 */ /* 根据传入的字符串修改颜色 : wifi ok, wifi err, burn 70 */ sscanf(ptInputEvent->str, "%s %s", name, status); if (strcmp(status, "ok") == 0) dwColor = BUTTON_PRESSED_COLOR; else if (strcmp(status, "err") == 0) dwColor = BUTTON_DEFAULT_COLOR; else if (status[0] >= '0' && status[0] <= '9') { dwColor = BUTTON_PERCENT_COLOR; strButton = status; } else return -1; } else { return -1; } /* 绘制底色 */ DrawRegion(&ptButton->tRegion, dwColor); /* 居中写文字 */ DrawTextInRegionCentral(strButton, &ptButton->tRegion, BUTTON_TEXT_COLOR); /* flush to lcd/web */ FlushDisplayRegion(&ptButton->tRegion, ptDispBuff); return 0; } static void GenerateButtons(void) { int width, height; int n_per_line; int row, rows; int col; int n; PDispBuff pDispBuff; int xres, yres; int start_x, start_y; int pre_start_x, pre_start_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; rows = n / n_per_line; if (rows * n_per_line < n) rows++; start_y = (yres - rows*height)/2; /* 计算每个按钮的region */ for (row = 0; (row < rows) && (i < n); row++) { pre_start_y = start_y + row * height; pre_start_x = start_x - width; for (col = 0; (col < n_per_line) && (i < n); col++) { pButton = &g_tButtons[i]; pButton->tRegion.iLeftUpX = pre_start_x + width; pButton->tRegion.iLeftUpY = pre_start_y; pButton->tRegion.iWidth = width - X_GAP; pButton->tRegion.iHeigh = height - Y_GAP; pre_start_x = pButton->tRegion.iLeftUpX; /* 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); } static int isTouchPointInRegion(int iX, int iY, PRegion ptRegion) { if (iX < ptRegion->iLeftUpX || iX >= ptRegion->iLeftUpX + ptRegion->iWidth) return 0; if (iY < ptRegion->iLeftUpY || iY >= ptRegion->iLeftUpY + ptRegion->iHeigh) return 0; return 1; } static PButton GetButtonByName(char *name) { int i; for (i = 0; i < g_tButtonCnt; i++) { if (strcmp(name, g_tButtons[i].name) == 0) return &g_tButtons[i]; } return NULL; } 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; } return NULL; } static void MainPageRun(void *pParams) { int error; InputEvent tInputEvent; PButton ptButton; PDispBuff ptDispBuff = GetDisplayBuffer(); /* 读取配置文件 */ error = ParseConfigFile(); if (error) return ; /* 根据配置文件生成按钮、界面 */ GenerateButtons(); while (1) { /* 读取输入事件 */ error = GetInputEvent(&tInputEvent); if (error) continue; /* 根据输入事件找到按钮 */ ptButton = GetButtonByInputEvent(&tInputEvent); if (!ptButton) continue; /* 调用按钮的OnPressed函数 */ ptButton->OnPressed(ptButton, ptDispBuff, &tInputEvent); } } static PageAction g_tMainPage = { .name = "main", .Run = MainPageRun, }; void MainPageRegister(void) { PageRegister(&g_tMainPage); }
第86行:计算按钮的个数
第119行:初始化按钮时候提供我们自己的处理函数 MainPageOnPressed
第195~209行:
第198~200行:读取输入事件,得到的数据保存在tInputEvent这个结构体里
第203~205行:根据输入事件找到按钮
第208行:调用按钮的OnPressed函数
static PButton GetButtonByInputEvent(PInputEvent ptInputEvent)
第155~178行:根据输入事件找到按钮
第160~167行:判断事件的类型是否为触摸屏
第164行:遍历每个按钮并且找寻触点
第168~172行:判断事件的类型是否为网络数据
第170行:将网络数据名字提取出来
第171行:根据名字找到按钮
static int isTouchPointInRegion(int iX, int iY, PRegion ptRegion)
第129~138行:寻找触点
static PButton GetButtonByName(char *name)
第141~152行:根据网络数据输入的名字找到按钮
static int MainPageOnPressed(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent)
第15~68行:之前是默认的处理函数,现在我们要提供自己自定义的处理函数
第17行:设置默认按钮颜色为红色
第25行:判断是否是触摸屏事件
第28行:判断是否能被点击
第32行:修改颜色
第36行:判断是否为网络事件
第41~52行:根据传入的字符串进行判断是否修改颜色
在ui.h里面多定义一个颜色”蓝色“
#define BUTTON_PERCENT_COLOR 0x0000ff
献上韦东山老师对以上代码流程的梳理过程
电子产品量产工具业务系统代码流程梳理
五、综合测试:
1.business目录的Makefile
EXTRA_CFLAGS := CFLAGS_file.o := obj-y += main.o
2.config目录的Makefile
EXTRA_CFLAGS := CFLAGS_file.o := obj-y += config.o
3.顶层目录的Makefile
CROSS_COMPILE ?= AS = $(CROSS_COMPILE)as LD = $(CROSS_COMPILE)ld CC = $(CROSS_COMPILE)gcc CPP = $(CC) -E AR = $(CROSS_COMPILE)ar NM = $(CROSS_COMPILE)nm STRIP = $(CROSS_COMPILE)strip OBJCOPY = $(CROSS_COMPILE)objcopy OBJDUMP = $(CROSS_COMPILE)objdump export AS LD CC CPP AR NM export STRIP OBJCOPY OBJDUMP CFLAGS := -Wall -O2 -g CFLAGS += -I $(shell pwd)/include LDFLAGS := -lts -lpthread -lfreetype -lm export CFLAGS LDFLAGS TOPDIR := $(shell pwd) export TOPDIR TARGET := test obj-y += display/ obj-y += input/ obj-y += font/ obj-y += ui/ obj-y += page/ obj-y += config/ obj-y += business/ all : start_recursive_build $(TARGET) @echo $(TARGET) has been built! start_recursive_build: make -C ./ -f $(TOPDIR)/Makefile.build $(TARGET) : built-in.o $(CC) -o $(TARGET) built-in.o $(LDFLAGS) clean: rm -f $(shell find -name "*.o") rm -f $(TARGET) distclean: rm -f $(shell find -name "*.o") rm -f $(shell find -name "*.d") rm -f $(TARGET)
obj-y += display/ obj-y += input/ obj-y += font/ obj-y += ui/ obj-y += page/ obj-y += config/ obj-y += business/
LDFLAGS := -lts -lpthread -lfreetype -lm
六、上板测试
1.ubuntu上:
book@100ask:~/28_business_test$ make book@100ask:~/28_business_test$ arm-buildroot-linux-gnueabihf-gcc -o client unittest/client.client.c
2.开发板上:
[root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt [root@100ask : /mnt/28_business_test] # mkdir /etc/test_gui [root@100ask : /mnt/28 business test] # cp etc/test_gui/gui.conf /etc/ [root@100ask : /mnt/28 business test] # ./test ./simsun.ttc & [root@100ask : /mnt/28 business test] # ./client 192.168.5.9 "net1 ok" [root@100ask : /mnt/28 business test] # ./client 192.168.5.9 "ALL ok" [root@100ask : /mnt/28 business test] # ./client 192.168.5.9 "net1 err" [root@100ask : /mnt/28 business test] # ./client 192.168.5.9 "ALL err"
3.测试结果:
当led测试没问题时,可以点击led
用客户端进行网络测试