前言1、显示系统
1.1、程序分层1.2、几个重要的数据结构1.3、程序分析 2、输入系统
2.1、程序分层2.2、触摸屏输入
2.2.1、几个重要的数据结构2.2.1、程序分析 2.3、网络输入2.4、输入管理
2.4.1、框架framework
程序分析 2.4.2、环形缓冲区circlebuff
什么是环形缓冲区?环形缓冲区的用法环形缓冲区的工作过程环形缓冲区工作机制程序分析 2.5、测试程序 3、文字系统
3.1、数据结构抽象3.2、实现Freetype代码3.3、文字管理3.5、单元测试 参考资料
前言韦东山项目实战之七步从零编写带GUI的应用(项目开发|论文参考|C|GUI)学习笔记
文章中大多内容来自韦东山老师的文档,还有部分个人根据自己需求补充的内容
视频教程地址: https://www.bilibili.com/video/BV1it4y1Q75z
1、显示系统【嵌入式Linux】嵌入式Linux应用开发基础知识之framebuffer应用编程和字符汉字显示
1.1、程序分层目前还没有按钮和web显示的代码
1.2、几个重要的数据结构
typedef struct DispBuff {
int iXres;
int iYres;
int iBpp;
char *buff;
}DispBuff, *PDispBuff;
typedef struct Region {
int iLeftUpX;
int iLeftUpY;
int iWidth;
int iHeigh;
}Region, *PRegion;
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, *PDispOpr;
在程序中使用了一个链表g_DispDevs来保存Display设备,LCD设备和web设备都将保存在这个链表中
而g_DispDefault指向默认的Display设备
g_tDispBuff保存Dispalybuffer使用LCD时可以理解为Frambuffer
static PDispOpr g_DispDevs = NULL; //Display设备链表 static PDispOpr g_DispDefault = NULL; //默认Display设备指针 从Display设备链表中获取Display设备地址 static DispBuff g_tDispBuff; //保存Dispalybuffer 在这里可以理解为Frambuffer1.3、程序分析
disp_test.c:使屏幕上显示字符A
注:fontdata_8x16数组见韦东山老师的程序
#include#include #include #include #include #include #include #include #include #include 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< disp_manager.h:包含一些数据结构和函数声明
#ifndef _DISP_MANAGER_H #define _DISP_MANAGER_H #ifndef NULL #define NULL (void *)0 #endif typedef struct DispBuff { int iXres; int iYres; int iBpp; char *buff; }DispBuff, *PDispBuff; typedef struct Region { int iLeftUpX; int iLeftUpY; int iWidth; int iHeigh; }Region, *PRegion; 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, *PDispOpr; void RegisterDisplay(PDispOpr ptDispOpr); void DisplayInit(void); int SelectDefaultDisplay(char *name); int InitDefaultDisplay(void); int PutPixel(int x, int y, unsigned int dwColor); int FlushDisplayRegion(PRegion ptRegion, PDispBuff ptDispBuff); PDispBuff GetDisplayBuffer(void); #endifdisp_manager.c:管理Display设备相关函数
#include#include #include static PDispOpr g_DispDevs = NULL; //Display设备链表 static PDispOpr g_DispDefault = NULL; //默认Display设备指针 从Display设备链表中获取Display设备地址 static DispBuff g_tDispBuff; //保存Dispalybuffer 在这里可以理解为Frambuffer static int line_width; static int pixel_width; int PutPixel(int x, int y, unsigned int dwColor) { 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; switch (g_tDispBuff.iBpp) { case 8: { *pen_8 = dwColor; break; } case 16: { 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: { *pen_32 = dwColor; break; } default: { printf("can't surport %dbppn", g_tDispBuff.iBpp); return -1; break; } } return 0; } void RegisterDisplay(PDispOpr ptDispOpr) { //在Display设备链表中用头插法插入节点 ptDispOpr->ptNext = g_DispDevs; //ptNext指向原来的Display设备 g_DispDevs = ptDispOpr; //g_DispDevs指向新的Dispay设备 } int SelectDefaultDisplay(char *name) { PDispOpr pTmp = g_DispDevs; while (pTmp) { //遍历g_DispDevs设备链表寻找name相同设备让g_DispDefault指向该Display设备 if (strcmp(name, pTmp->name) == 0) { g_DispDefault = pTmp; return 0; } pTmp = pTmp->ptNext; } return -1; } int InitDefaultDisplay(void) { int ret; ret = g_DispDefault->DeviceInit(); if (ret) { printf("DeviceInit errn"); return -1; } ret = g_DispDefault->GetBuffer(&g_tDispBuff); if (ret) { printf("GetBuffer errn"); return -1; } //LCD行宽 单位:byte line_width = g_tDispBuff.iXres * g_tDispBuff.iBpp/8; //像素宽度 单位:byte pixel_width = g_tDispBuff.iBpp/8; return 0; } PDispBuff GetDisplayBuffer(void) { //返回Displaybuffer地址 return &g_tDispBuff; } int FlushDisplayRegion(PRegion ptRegion, PDispBuff ptDispBuff) { //将区域信息和framebuffer信息融合 return g_DispDefault->FlushRegion(ptRegion, ptDispBuff); } void DisplayInit(void) { extern void framebufferInit(void); //初始化LCD设备 framebufferInit(); } framebuffer.c:LCD的framebuffer操作的相关函数,同时也定义了LCD设备结构体g_tframebufferOpr
#include2、输入系统#include #include #include #include #include #include #include #include #include static int fd_fb; static struct fb_var_screeninfo var; static int screen_size; static unsigned char *fb_base; static unsigned int line_width; static unsigned int pixel_width; static int FbDeviceInit(void) { fd_fb = open("/dev/fb0", O_RDWR); if (fd_fb < 0) { printf("can't open /dev/fb0n"); return -1; } if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)) { printf("can't get varn"); return -1; } line_width = var.xres * var.bits_per_pixel / 8; //行宽 单位:byte pixel_width = var.bits_per_pixel / 8; //像素宽度 单位:byte screen_size = var.xres * var.yres * var.bits_per_pixel / 8; //屏幕尺寸 单位:byte 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 mmapn"); return -1; } return 0; } static int FbDeviceExit(void) { munmap(fb_base, screen_size); close(fd_fb); return 0; } static int FbGetBuffer(PDispBuff ptDispBuff) { ptDispBuff->iXres = var.xres; ptDispBuff->iYres = var.yres; ptDispBuff->iBpp = var.bits_per_pixel; ptDispBuff->buff = (char *)fb_base; return 0; } static int FbFlushRegion(PRegion ptRegion, PDispBuff ptDispBuff) { return 0; } //初始化g_tframebufferOpr结构体 static DispOpr g_tframebufferOpr = { .name = "fb", .DeviceInit = FbDeviceInit, .DeviceExit = FbDeviceExit, .GetBuffer = FbGetBuffer, .FlushRegion = FbFlushRegion, }; void framebufferInit(void) { //注册一个Display设备 RegisterDisplay(&g_tframebufferOpr); } 【嵌入式Linux】嵌入式Linux应用开发基础知识之输入系统应用编程
2.1、程序分层
【嵌入式Linux】嵌入式Linux应用开发基础知识之网络通信
【嵌入式Linux】嵌入式Linux应用开发基础知识之多线程编程▲程序分层 2.2、触摸屏输入 2.2.1、几个重要的数据结构抽象出两个结构体,数据和设备
typedef struct InputEvent { struct timeval tTime; int iType; int iX; int iY; int iPressure; char str[1024]; }InputEvent, *PInputEvent; typedef struct InputDevice { char *name; int (*GetInputEvent)(PInputEvent ptInputEvent); int (*DeviceInit)(void); int (*DeviceExit)(void); struct InputDevice *ptNext; }InputDevice, *PInputDevice;2.2.1、程序分析input_manager.h:包含数据结构和函数声明
#ifndef _INPUT_MANAGER_H #define _INPUT_MANAGER_H #include#ifndef NULL #define NULL (void *)0 #endif #define INPUT_TYPE_TOUCH 1 #define INPUT_TYPE_NET 2 typedef struct InputEvent { struct timeval tTime; int iType; int iX; int iY; int iPressure; char str[1024]; }InputEvent, *PInputEvent; typedef struct InputDevice { char *name; int (*GetInputEvent)(PInputEvent ptInputEvent); int (*DeviceInit)(void); int (*DeviceExit)(void); struct InputDevice *ptNext; }InputDevice, *PInputDevice; void RegisterInputDevice(PInputDevice ptInputDev); void InputSystemRegister(void); void IntpuDeviceInit(void); int GetInputEvent(PInputEvent ptInputEvent); #endif touchscreen.c:监测触控屏设备事件及输出报点信息
#include2.3、网络输入#include #include static struct tsdev *g_ts; static int TouchscreenGetInputEvent(PInputEvent ptInputEvent) { struct ts_sample samp; int ret; //使用ts_lib库函数读取报点信息 ret = ts_read(g_ts, &samp, 1); if (ret != 1) return -1; //将报点信息传入ptInputEvent ptInputEvent->iType = INPUT_TYPE_TOUCH; ptInputEvent->iX = samp.x; ptInputEvent->iY = samp.y; ptInputEvent->iPressure = samp.pressure; ptInputEvent->tTime = samp.tv; return 0; } static int TouchscreenDeviceInit(void) { //使用ts_lib函数初始化触控设备 g_ts = ts_setup(NULL, 0); if (!g_ts) { printf("ts_setup errn"); return -1; } return 0; } static int TouchscreenDeviceExit(void) { //使用ts_lib函数关闭触控设备 ts_close(g_ts); return 0; } static InputDevice g_tTouchscreenDev ={ .name = "touchscreen", .GetInputEvent = TouchscreenGetInputEvent, .DeviceInit = TouchscreenDeviceInit, .DeviceExit = TouchscreenDeviceExit, }; #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 : %dn", event.iType); printf("iX : %dn", event.iX); printf("iY : %dn", event.iY); printf("iPressure : %dn", event.iPressure); } } return 0; } #endif 网络输入编程使用的数据结构和触摸屏使用的数据结构相同,这里不再赘述
在开发板上运行的是server端的程序使用udp通信,所以在创建设备的时候需要完成netinput.c:创建一个server设备使用udp通信协议,接收client信息并打印到console终端上
#include#include #include #include #include #include #include #include #include #include #define SERVER_PORT 8888 static int g_iSocketServer; static int NetinputGetInputEvent(PInputEvent ptInputEvent) { struct sockaddr_in tSocketClientAddr; int iRecvLen; char aRecvBuf[1000]; unsigned int iAddrLen = sizeof(struct sockaddr); //没有数据报到达时阻塞 iRecvLen = recvfrom(g_iSocketServer, aRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen); if (iRecvLen > 0) { aRecvBuf[iRecvLen] = ' '; //printf("Get Msg From %s : %sn", inet_ntoa(tSocketClientAddr.sin_addr), ucRecvBuf); ptInputEvent->iType = INPUT_TYPE_NET;//设置上报信息类型为网络类型 gettimeofday(&ptInputEvent->tTime, NULL);//获取默认时区的时间 strncpy(ptInputEvent->str, aRecvBuf, 1000);//拷贝缓冲区的数据到ptInputEvent ptInputEvent->str[999] = ' ';//添加结束符 return 0; } else return -1; } 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; } tSocketServerAddr.sin_family = AF_INET; tSocketServerAddr.sin_port = htons(SERVER_PORT); tSocketServerAddr.sin_addr.s_addr = INADDR_ANY; memset(tSocketServerAddr.sin_zero, 0, 8); //绑定socket文件描述符和socket address iRet = bind(g_iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr)); if (-1 == iRet) { printf("bind error!n"); return -1; } return 0; } static int NetinputDeviceExit(void) { close(g_iSocketServer); return 0; } static InputDevice g_tNetinputDev ={ .name = "touchscreen", .GetInputEvent = NetinputGetInputEvent, .DeviceInit = NetinputDeviceInit, .DeviceExit = NetinputDeviceExit, }; #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 : %dn", event.iType); printf("str : %sn", event.str); } } return 0; } #endif client.c:测试使用的client程序,使用./client
来向server发送信息 #include2.4、输入管理 2.4.1、框架framework#include #include #include #include #include #include #include #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 n", argv[0]); return -1; } iSocketClient = socket(AF_INET, SOCK_DGRAM, 0); tSocketServerAddr.sin_family = AF_INET; tSocketServerAddr.sin_port = htons(SERVER_PORT); //tSocketServerAddr.sin_addr.s_addr = INADDR_ANY; if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr)) { printf("invalid server_ipn"); return -1; } memset(tSocketServerAddr.sin_zero, 0, 8); #if 0 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; } ▲输入管理器 在输入管理器中一共向外部提供了三个函数:
void InputInit(void)
连接到驱动侧,驱动侧通过这个函数将注册输入设备结构体到设备链表中
void RegisterInputDevice(PInputDevice ptInputDev) / void InpuInit(PInputDevice ptInputDev)
连接到APP侧,APP侧可以使用这个函数初始化设备链表中所有的输入设备,并且为其创建监听线程
int GetInputEvent(PT_InputEvent ptInputEvent)
连接到APP侧,APP侧可以使用这个函数获取底层上报的事件信息此外还有一个线程函数static void *input_recv_thread_func (void *data)负责完成监听线程工作
程序分析input_manage.c:其中int GetInputEvent(PT_InputEvent ptInputEvent)和static void *input_recv_thread_func (void *data) static void *input_recv_thread_func (void *data)函数没写完
#include#include #include #include #include static PInputDevice g_InputDevs = NULL; void RegisterInputDevice(PInputDevice ptInputDev) { ptInputDev->ptNext = g_InputDevs; g_InputDevs = ptInputDev; } void InputInit(void) { extern void TouchscreenRegister(void); TouchscreenRegister(); extern void NetInputRegister(void); NetInputRegister(); } static void *input_recv_thread_func (void *data) { PInputDevice tInputDev = (PInputDevice)data; InputEvent tEvent; int ret; while (1) { ret = tInputDev->GetInputEvent(&tEvent); if (!ret) { pthread_mutex_lock(&g_tMutex); pthread_cond_wait(&g_tConVar, &g_tMutex); pthread_mutex_unlock(&g_tMutex); } } return NULL; } 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; } } int GetInputEvent(PT_InputEvent ptInputEvent) { } touchscreen.c:添加函数void TouchscreenRegister(void)
... void TouchscreenRegister(void) { RegisterInputDevice(&g_tTouchscreenDev); }netinput.c:添加函数void NetInputRegister(void)
... void NetInputRegister(void) { RegisterInputDevice(&g_tNetinputDev); }2.4.2、环形缓冲区circlebuff建立一个环形缓冲区,APP、触摸屏设备和网络设备会互斥的访问改写这个缓冲区来让APP获得设备上报的事件信息
什么是环形缓冲区?圆形缓冲区(circular buffer),也称作圆形队列(circular queue),循环缓冲区(cyclic buffer),环形缓冲区(ring buffer),是一种用于表示一个固定尺寸、头尾相连的缓冲区的数据结构,适合缓存数据流。
环形缓冲区的用法圆形缓冲区的一个有用特性是:当一个数据元素被用掉后,其余数据元素不需要移动其存储位置。相反,一个非圆形缓冲区(例如一个普通的队列)在用掉一个数据元素后,其余数据元素需要向前搬移。换句话说,圆形缓冲区适合实现先进先出缓冲区,而非圆形缓冲区适合后进先出缓冲区。
环形缓冲区的工作过程
圆形缓冲区适合于事先明确了缓冲区的最大容量的情形。扩展一个圆形缓冲区的容量,需要搬移其中的数据。因此一个缓冲区如果需要经常调整其容量,用链表实现更为合适。
写操作覆盖圆形缓冲区中未被处理的数据在某些情况下是允许的。特别是在多媒体处理时。例如,音频的生产者可以覆盖掉声卡尚未来得及处理的音频数据。一个圆形缓冲区最初为空并有预定的长度。例如,这是一个具有七个元素空间的圆形缓冲区,其中底部的单线与箭头表示“头尾相接”形成一个圆形地址空间:
▲环形缓冲区 假定1被写入缓冲区中部(对于圆形缓冲区来说,最初的写入位置在哪里是无关紧要的):
▲环形缓冲区初次写入数据 再写入2个元素,分别是2 & 3 — 被追加在1之后:
▲环形缓冲区再次写入数据 如果两个元素被处理,那么是缓冲区中最老的两个元素被移除。在本例中,1 & 2被移除,缓冲区中只剩下3:
▲环形缓冲区移除数据 如果缓冲区中有7个元素,则是满的:
▲环形缓冲区满员 如果缓冲区是满的,又要写入新的数据,一种策略是覆盖掉最老的数据。此例中,2个新数据— A & B — 写入,覆盖了3 & 4:
▲环形缓冲区覆盖写入数据 也可以采取其他策略,禁止覆盖缓冲区的数据,采取返回一个错误码或者抛出异常。
最终,如果从缓冲区中移除2个数据,不是3 & 4 而是 5 & 6 。因为 A & B 已经覆盖了3 & 4:
▲环形缓冲区覆盖后移除数据 环形缓冲区工作机制由于计算机内存是线性地址空间,因此圆形缓冲区需要特别的设计才可以从逻辑上实现。
读指针与写指针
一般的,圆形缓冲区需要4个指针:在内存中实际开始位置;在内存中实际结束位置,也可以用缓冲区长度代替;(对应BUFFER_LEN)存储在缓冲区中的有效数据的开始位置(读指针);(对应g_iRead)存储在缓冲区中的有效数据的结尾位置(写指针)。(对应g_iWrite)
区分缓冲区满或者空(满指缓冲区中充满了新的数据/未读的数据,空指缓冲区中没有新的数据)
缓冲区是满、或是空,都有可能出现读指针与写指针指向同一位置:
▲读指针与写指针指向同一位置 有多种策略用于检测缓冲区是满、或是空:
总是保持一个存储单元为空
缓冲区中总是有一个存储单元保持未使用状态。缓冲区最多存入(size - 1) 个数据。如果读写指针指向同一位置,则缓冲区为空。如果写指针位于读指针的相邻后一个位置,则缓冲区为满。这种策略的优点是简单、鲁棒;缺点是语义上实际可存数据量与缓冲区容量不一致,测试缓冲区是否满需要做取余数计算。使用数据计数
这种策略不使用显式的写指针,而是保持着缓冲区内存储的数据的计数。因此测试缓冲区是空是满非常简单;对性能影响可以忽略。缺点是读写操作都需要修改这个存储数据计数,对于多线程访问缓冲区需要并发控制。镜像指示位
缓冲区的长度如果是n,逻辑地址空间则为0至n-1;那么,规定n至2n-1为镜像逻辑地址空间。本策略规定读写指针的地址空间为0至2n-1,其中低半部分对应于常规的逻辑地址空间,高半部分对应于镜像逻辑地址空间。当指针值大于等于2n时,使其折返(wrapped)到ptr-2n。使用一位表示写指针或读指针是否进入了虚拟的镜像存储区:置位表示进入,不置位表示没进入还在基本存储区。
▲带有镜像指示位的环形缓冲区 在读写指针的值相同情况下,如果二者的指示位相同,说明缓冲区为空;如果二者的指示位不同,说明缓冲区为满。这种方法优点是测试缓冲区满/空很简单;不需要做取余数操作;读写线程可以分别设计专用算法策略,能实现精致的并发控制。 缺点是读写指针各需要额外的一位作为指示位。
如果缓冲区长度是2的幂,则本方法可以省略镜像指示位。如果读写指针的值相等,则缓冲区为空;如果读写指针相差n,则缓冲区为满,这可以用条件表达式(写指针 == (读指针 异或 缓冲区长度))来判断。读/写计数
程序分析
用两个有符号整型变量分别保存写入、读出缓冲区的数据数量。其差值就是缓冲区中尚未被处理的有效数据的数量。这种方法的优点是读线程、写线程互不干扰;缺点是需要额外两个变量。记录最后的操作
使用一位记录最后一次操作是读还是写。读写指针值相等情况下,如果最后一次操作为写入,那么缓冲区是满的;如果最后一次操作为读出,那么缓冲区是空。 这种策略的缺点是读写操作共享一个标志位,多线程时需要并发控制。input_manage.c
#include2.5、测试程序#include #include #include #include static PInputDevice g_InputDevs = NULL; static pthread_mutex_t g_tMutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t g_tConVar = PTHREAD_COND_INITIALIZER; #define BUFFER_LEN 20 static int g_iRead = 0; static int g_iWrite = 0; static InputEvent g_atInputEvents[BUFFER_LEN]; 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; } } void RegisterInputDevice(PInputDevice ptInputDev) { ptInputDev->ptNext = g_InputDevs; g_InputDevs = ptInputDev; } void InputInit(void) { extern void TouchscreenRegister(void); TouchscreenRegister(); extern void NetInp utRegister(void); NetInputRegister(); } static void *input_recv_thread_func (void *data) { PInputDevice ptInputDev = (PInputDevice)data; InputEvent tEvent; int ret; while (1) { //这里使用的是设备结构体中的GetInputEvent函数,并非下面的GetInputEvent函数 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; } 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; } } int GetInputEvent(PT_InputEvent ptInputEvent) { InputEvent tEvent; int ret; pthread_mutex_lock(&g_tMutex); if (GetInputEventFromBuffer(&tEvent)) { *ptInputEvent = tEvent; pthread_mutex_unlock(&g_tMutex); return 0; } else { pthread_cond_wait(&g_tConVar, &g_tMutex); if (GetInputEventFromBuffer(&tEvent)) { ret = 0; } else { ret = -1; } pthread_mutex_unlock(&g_tMutex); } return ret; } #include3、文字系统#include #include #include #include #include #include #include #include #include int main(int argc, char **argv) { int ret; InputEvent event; InputInit(); IntpuDeviceInit(); while (1) { printf("%s %s %dn", __FILE__, __FUNCTION__, __LINE__); ret = GetInputEvent(&event); printf("%s %s %d, ret = %dn", __FILE__, __FUNCTION__, __LINE__, ret); if (ret) { printf("GetInputEvent err!n"); return -1; } else { printf("%s %s %d, event.iType = %dn", __FILE__, __FUNCTION__, __LINE__, event.iType ); if (event.iType == INPUT_TYPE_TOUCH)//touch设备 { printf("Type : %dn", event.iType); printf("iX : %dn", event.iX); printf("iY : %dn", event.iY); printf("iPressure : %dn", event.iPressure); } else if (event.iType == INPUT_TYPE_NET)//web设备 { printf("Type : %dn", event.iType); printf("str : %sn", event.str); } } } return 0; } 【嵌入式Linux】嵌入式Linux应用开发基础知识之framebuffer应用编程和字符汉字显示
▲程序分层 3.1、数据结构抽象▲字形指标 font_manage.h:
#ifndef _FONT_MANAGER_H #define _FONT_MANAGER_H #ifndef NULL #define NULL (void *)0 #endif typedef struct FontBitMap { int iLeftUpX; int iLeftUpY; int iWidth; int iRows; int iCurOriginX; int iCurOriginY; int iNextOriginX; int iNextOriginY; unsigned char *pucBuffer; }FontBitMap, *PFontBitMap; 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; #endif3.2、实现Freetype代码freetype.c
#include3.3、文字管理#include #include #include #include #include #include #include #include #include #include #include #include #include FT_FREETYPE_H #include FT_GLYPH_H static FT_Face g_tFace; //矢量字体文字 static int g_iDefaultFontSize = 12; //默认字体大小 static int FreeTypeFontInit(char *aFineName) { FT_Library library; int error; error = FT_Init_FreeType( &library ); if (error) { printf("FT_Init_FreeType errn"); return -1; } error = FT_New_Face(library, aFineName, 0, &g_tFace ); if (error) { printf("FT_New_Face errn"); return -1; } FT_Set_Pixel_Sizes(g_tFace, g_iDefaultFontSize, 0); return 0; } static int FreeTypeSetFontSize(int iFontSize) { FT_Set_Pixel_Sizes(g_tFace, iFontSize, 0); return 0; } static int FreeTypeGetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap) { int error; FT_Vector pen; //对应origin FT_Glyph glyph; //用来保存字体文件中保存有字符的原始关键点信息 FT_GlyphSlot slot = g_tFace->glyph; //字形槽,用来保存字符处理后的结果 glyph->bitmap 中包含了中文位图信息 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 errorn"); return -1; } ptFontBitMap->pucBuffer = slot->bitmap.buffer; ptFontBitMap->iLeftUpX = slot->bitmap_left; ptFontBitMap->iLeftUpY = ptFontBitMap->iCurOriginY*2 - slot->bitmap_top; ptFontBitMap->iWidth = slot->bitmap.width; ptFontBitMap->iRows = slot->bitmap.rows; ptFontBitMap->iNextOriginX = ptFontBitMap->iCurOriginX + slot->advance.x / 64; ptFontBitMap->iNextOriginY = ptFontBitMap->iCurOriginY; return 0; } static FontOpr g_tFreetypeOpr = { .name = "freetype", .FontInit = FreeTypeFontInit, .SetFontSize = FreeTypeSetFontSize, .GetFontBitMap = FreeTypeGetFontBitMap, }; font_manage.c:管理文字库设备
#include3.5、单元测试static PFontOpr g_ptFonts = NULL; //字库设备链表头 static PFontOpr g_ptDefaulFontOpr = NULL; //默认字库设备指针 void RegisterFont(PFontOpr ptFontOpr) { ptFontOpr->ptNext = g_ptFonts; g_ptFonts = ptFontOpr; } void FontsRegister(void) { extern void FreetypeRegister(void); FreetypeRegister(); } 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); } int SetFontSize(int iFontSize) { return g_ptDefaulFontOpr->SetFontSize(iFontSize); } int GetFontBitMap(unsigned int dwCode, PFontBitMap ptFontBitMap) { return g_ptDefaulFontOpr->GetFontBitMap(dwCode, ptFontBitMap); } common.h:包含通用结构体及宏定义声明
#ifndef _COMMON_H #define _COMMON_H #ifndef NULL #define NULL (void *)0 #endif typedef struct Region { int iLeftUpX; int iLeftUpY; int iWidth; int iHeigh; }Region, *PRegion; #endif在font_manager.h中修改struct FontBitMap用Region结构体代替原来保存区域信息的变量
typedef struct FontBitMap { int iLeftUpX; int iLeftUpY; int iWidth; int iRows; int iCurOriginX; int iCurOriginY; int iNextOriginX; int iNextOriginY; unsigned char *pucBuffer; }FontBitMap, *PFontBitMap; typedef struct FontBitMap { Region tRegion; int iCurOriginX; int iCurOriginY; int iNextOriginX; int iNextOriginY; unsigned char *pucBuffer; }FontBitMap, *PFontBitMap;disp_manage.c:添加函数DrawFontBitMap,完成功能根据位图信息和颜色信息在LCD上显示信息
void DrawFontBitMap(PFontBitMap ptFontBitMap, unsigned int dwColor) { int i, j, p, q; int x = ptFontBitMap->tRegion.iLeftUpX; int y = ptFontBitMap->tRegion.iLeftUpY; int x_max = x + ptFontBitMap->tRegion.iWidth; int y_max = y + ptFontBitMap->tRegion.iHeigh; int width = ptFontBitMap->tRegion.iWidth; unsigned char *buffer = ptFontBitMap->pucBuffer; //printf("x = %d, y = %dn", x, y); 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; //image[j][i] |= bitmap->buffer[q * bitmap->width + p]; if (buffer[q * width + p]) PutPixel(i, j, dwColor); } } }font_test.c:文字系统测试程序,在LCD上使用freetype字库设备显示一行文字
#include参考资料#include #include #include #include #include #include #include #include #include #include #include int main(int argc, char **argv) { PDispBuff ptBuffer; int error; FontBitMap tFontBitMap; char *str= "Hello world!"; int i = 0; int lcd_x; int lcd_y; int font_size; if (argc != 5) { printf("Usage: %s 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 errn"); 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 errn"); return -1; } DrawFontBitMap(&tFontBitMap, 0xff0000); FlushDisplayRegion(&tFontBitMap.tRegion, ptBuffer); lcd_x = tFontBitMap.iNextOriginX; lcd_y = tFontBitMap.iNextOriginY; i++; } return 0; } 环形缓冲区
聊聊ringbuffer



