100ask电子产品量产工具
程序架构
程序总体框架如下:
图中有多种设备,目前,百问网该项目只实现了main page,framebuffer,触摸屏输入,网络输入和freetype。
全部代码参考链接:https://github.com/studyalldaya/100ask_project1
1. 100ask电子产品量产工具学习笔记(2)输入部分
2. 100ask电子产品量产工具学习笔记(3)字体显示部分
3. 100ask电子产品量产工具学习笔记(4)按钮及页面抽象
4. 100ask电子产品量产工具学习笔记(5)业务部分
与韦老师不同的是,我是使用clion远程连接Ubuntu进行开发,使用cmake组织代码。
CMakeLists如下:
cmake_minimum_required(VERSION 3.10)
project(100ask_project1 C)
set(CMAKE_C_STANDARD 99)
#添加tslib库
set(INC_DIR ~/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/include)
set(LINK_DIR ~/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/lib)
include_directories(
${INC_DIR}
${INC_DIR}/freetype2
)
link_directories(${LINK_DIR})
add_executable(100ask_project1 main.c include/disp_manager.h display/framebuffer.c display/disp_manager.c include/input_manager.h input/touchscreen.c input/net_input.c input/input_manager.c include/font_manager.h font/font_freetype.c font/font_manager.c include/common.h include/UI.h UI/button.c include/page_manager.h page/page_manager.c page/main_page.c include/config.h config/config.c)
target_link_libraries(
100ask_project1
ts
pthread
freetype
m
)
显示部分
上层display_manager
通过结构体抽象出一个Display_device,然后通过结构体里面 的函数指针实现不同显示设备的执行不同函数。最底层的是设备文件,如framebuffer设备,或者在web上显示,然后上面一层是一个显示管理器,来管理下层的不同设备,在这个显示管理器中实现一个单向链表连接不同设备,后续可通过name来找到设备。并且要提供api供其它文件使用。
通过Display_buffer 来表示得到的一个framebuffer相关信息。
typedef struct Display_buffer {
int xres;
int yres;
int bpp;
char *fb_base;
} Display_buffer;
typedef struct Display_device {
char *name;
//函数指针用作多态
int (*device_init)(void);//open
int (*device_exit)(void);//close
int (*get_buffer)(Display_buffer *disp_buffer);
int (*flush_region)(Region *region, Display_buffer *buffer);
struct Display_device *next;//支持多个输出 lcd or web
} Display_device;
通过register_display函数提供给底层的设备文件,用来注册该设备到链表,底层设备文件的函数都应该是static的,通过函数指针暴露给上层代码。其中display_dev为链表的头。
void register_display(Display_device *dev)
{
dev->next = display_dev;//
display_dev = dev;//注册上来
}
register_display函数提供给底层设备文件,底层设备文件需要调用register_display函数,这里是在设备文件的framebuffer_register函数里面使用register_display函数。
所以上层api要提供一个display_init函数来调用底层的framebuffer_register函数来注册framebuffer设备。
extern void framebuffer_register(void);//声明该函数,在其它文件寻找该函数
void display_init()
{
framebuffer_register();
}
通过select_default_display函数找到对应name的设备,遍历设备链表。
int select_default_display(char *name)
{
Display_device *tmp = display_dev;
while (tmp) {
if (strcmp(name, tmp->name) == 0) {
display_default = tmp;
return 0;
}
tmp = tmp->next;
}
return -1;
}
通过init_default_display函数,调用对应设备的init函数,get_buffer函数,初始化framebuffer设备并获得framebuffer的base及相关信息。
int init_default_display(void)
{
int ret;
ret = display_default->device_init();
if (ret) {
printf("device_init err!\n");
return -1;
}
ret = display_default->get_buffer(&display_buffer);
if (ret) {
printf("get_buffer err!\n");
return -1;
}
line_width = display_buffer.xres * display_buffer.bpp / 8;
pixel_width = display_buffer.bpp / 8;
return 0;
}
底层支持framebuffer设备
如上所述,底层调用register_display向上注册该设备并接入设备链表。
void framebuffer_register(void)
{
register_display(&framebuffer_dev);
}
实现Display_device 结构体为framebuffer_dev ,name为fb。
static Display_device framebuffer_dev = {
.name = "fb",
.device_init = fb_device_init,
.device_exit = fb_device_exit,
.get_buffer = fb_get_buffer,
.flush_region = fb_flush_region,
};
fb_device_init函数就是framebuffer的应用层编程了。打开/dev/fb0设备节点,通过ioctl函数获得framebuffer的info放入全局变量var中,从而计算出屏幕的大小screen_size ,
我需要多大内存来映射framebuffer,通过mmap函数将文件映射到内存空间,然后可以通过读写内存来读写文件,应用层得到映射内存的起始base值,也就是对应屏幕左上角。
MAP_SHARED表示对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享。
static int fb_device_init(void)
{
fd_fb = open("/dev/fb0", O_RDWR);
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);
if (fb_base == (unsigned char *) -1) {
printf("can't mmap\n");
return -1;
}
return 0;
}
fb_get_buffer函数填充Display_buffer 。上层直接调用get_buffer函数就会执行对应的设备的get_buffer函数。
static int fb_get_buffer(Display_buffer *disp_buffer)
{
disp_buffer->xres = var.xres;
disp_buffer->yres = var.yres;
disp_buffer->bpp = var.bits_per_pixel;
disp_buffer->fb_base = fb_base;
return 0;
}
取消内存映射,关闭文件描述符。
static int fb_device_exit(void)
{
munmap(fb_base, screen_size);
close(fd_fb);
return 0;
}