赞
踩
韦东山项目实战之七步从零编写带GUI的应用(项目开发|论文参考|C|GUI)学习笔记
文章中大多内容来自韦东山老师的文档,还有部分个人根据自己需求补充的内容
视频教程地址: https://www.bilibili.com/video/BV1it4y1Q75z
【嵌入式Linux】嵌入式Linux应用开发基础知识之Framebuffer应用编程和字符汉字显示
▲程序分层
目前还没有按钮和web显示的代码
/* * 显示缓冲区 * iXres iYres:屏幕的x,y轴长度 * iBpp:字符的颜色Bpp(bit per pixel)信息 * buff:framebuffer基地址 */ typedef struct DispBuff { int iXres; int iYres; int iBpp; char *buff; }DispBuff, *PDispBuff; /* * 区域信息 * iLeftUpX iLeftUpY:里面包含了绘制的起始坐标 * iWidth iHeigh:字符的长宽信息 */ typedef struct Region { int iLeftUpX; int iLeftUpY; int iWidth; int iHeigh; }Region, *PRegion; /* * Display相关函数集合结构体 用来表示一个Display设备 * DeviceInit:初始化设备 * DeviceExit:退出回收设备 * GetBuffer :获得Displaybuffer * FlushRegio:融合区 * ptNext:指向下一个DispOpr类型结构体指针即下一个Display设备 */ 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
/* 管理底层的LCD、WEB */
static PDispOpr g_DispDevs = NULL; //Display设备链表
static PDispOpr g_DispDefault = NULL; //默认Display设备指针 从Display设备链表中获取Display设备地址
static DispBuff g_tDispBuff; //保存Dispalybuffer 在这里可以理解为Frambuffer
disp_test.c
:使屏幕上显示字符A
注:fontdata_8x16
数组见韦东山老师的程序
#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 <disp_manager.h> /********************************************************************** * 函数名称: lcd_put_ascii * 功能描述: 在LCD指定位置上显示一个8*16的字符 * 输入参数: x坐标,y坐标,ascii码 * 输出参数: 无 * 返 回 值: 无 * 修改日期 版本号 修改人 修改内容 * ----------------------------------------------- * 2020/05/12 V1.0 zh(angenao) 创建 ***********************************************************************/ 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)) { /* show */ PutPixel(x+7-b, y+i, 0xffffff); /* 白 */ } else { /* hide */ PutPixel(x+7-b, y+i, 0); /* 黑 */ } } } } int main(int argc, char **argv) { Region region; PDispBuff ptBuffer; //初始化Display相关设备,LCD设备和Web设备(暂无),在Display设备链表中插入节点 DisplayInit(); //设定默认Display设备,在g_DispDevs链表中寻找name == fb的设备使g_DispDefault指向它 SelectDefaultDisplay("fb"); //初始化默认Display设备 包含设备初始化和获得Displaybuffer InitDefaultDisplay(); //向Framebuffer缓冲区中填写内容 lcd_put_ascii(100, 100, 'A'); /* * 下面的函数及内容是为web设备准备的,由于在InitDefaultDisplay时已经获得了framebuffer的基地址 * 所以这里不用FlushDisplayRegion */ //填写区域信息 region.iLeftUpX = 100; region.iLeftUpY = 100; region.iWidth = 8; region.iHeigh = 16; //获得Framebuffer地址 ptBuffer = GetDisplayBuffer(); //将区域信息和Framebuffer信息融合 FlushDisplayRegion(®ion, ptBuffer); return 0; }
disp_manager.h
:包含一些数据结构和函数声明
#ifndef _DISP_MANAGER_H #define _DISP_MANAGER_H #ifndef NULL #define NULL (void *)0 #endif /* * 显示缓冲区 * iXres iYres:屏幕的x,y轴长度 * iBpp:字符的颜色Bpp(bit per pixel)信息 * buff:framebuffer基地址 */ typedef struct DispBuff { int iXres; int iYres; int iBpp; char *buff; }DispBuff, *PDispBuff; /* * 区域信息 * iLeftUpX iLeftUpY:里面包含了绘制的起始坐标 * iWidth iHeigh:字符的长宽信息 */ typedef struct Region { int iLeftUpX; int iLeftUpY; int iWidth; int iHeigh; }Region, *PRegion; /* * Display相关函数集合结构体 用来表示一个Display设备 * DeviceInit:初始化设备 * DeviceExit:退出回收设备 * GetBuffer :获得Displaybuffer * FlushRegio:融合区 * ptNext:指向下一个DispOpr类型结构体指针即下一个Display设备 */ 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); #endif
disp_manager.c
:管理Display设备相关函数
#include <stdio.h> #include <string.h> #include <disp_manager.h> /* 管理底层的LCD、WEB */ 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: { /* 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: { *pen_32 = dwColor; break; } default: { printf("can't surport %dbpp\n", 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 err\n"); return -1; } ret = g_DispDefault->GetBuffer(&g_tDispBuff); if (ret) { printf("GetBuffer err\n"); 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
#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 <disp_manager.h> static int fd_fb; static struct fb_var_screeninfo var; /* Current var */ static int screen_size; static unsigned char *fb_base; static unsigned int line_width; static unsigned int pixel_width; /*Framebuffer初始化*/ static int FbDeviceInit(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; //行宽 单位: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 mmap\n"); return -1; } return 0; } /*Framebuffer回收*/ static int FbDeviceExit(void) { munmap(fb_base, screen_size); close(fd_fb); return 0; } /* 可以返回LCD的framebuffer, 以后上层APP可以直接操作LCD, 可以不用FbFlushRegion * 也可以malloc返回一块无关的buffer, 要使用FbFlushRegion */ 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应用开发基础知识之输入系统应用编程
【嵌入式Linux】嵌入式Linux应用开发基础知识之网络通信
【嵌入式Linux】嵌入式Linux应用开发基础知识之多线程编程
▲程序分层
抽象出两个结构体,数据和设备
/* * 输入事件结构体 * iType:输入事件类型 * iX iY:输入事件坐标 * iPressure:输入事件压力值 * str:输入事件字符串,网络编程使用 */ typedef struct InputEvent { struct timeval tTime; int iType; int iX; int iY; int iPressure; char str[1024]; }InputEvent, *PInputEvent; /* * 输入设备结构体 用来表示一个输入设备 * DeviceInit:初始化设备 * DeviceExit:退出回收设备 * GetInputEvent:接收输入事件 * ptNext:指向下一个InputDevice类型结构体指针即下一个输入设备 */ typedef struct InputDevice { char *name; int (*GetInputEvent)(PInputEvent ptInputEvent); int (*DeviceInit)(void); int (*DeviceExit)(void); struct InputDevice *ptNext; }InputDevice, *PInputDevice;
input_manager.h
:包含数据结构和函数声明
#ifndef _INPUT_MANAGER_H #define _INPUT_MANAGER_H #include <sys/time.h> #ifndef NULL #define NULL (void *)0 #endif #define INPUT_TYPE_TOUCH 1 #define INPUT_TYPE_NET 2 /* * 输入事件结构体 * iType:输入事件类型 * iX iY:输入事件坐标 * iPressure:输入事件压力值 * str:输入事件字符串,网络编程使用 */ typedef struct InputEvent { struct timeval tTime; int iType; int iX; int iY; int iPressure; char str[1024]; }InputEvent, *PInputEvent; /* * 输入设备结构体 用来表示一个输入设备 * DeviceInit:初始化设备 * DeviceExit:退出回收设备 * GetInputEvent:接收输入事件 * ptNext:指向下一个InputDevice类型结构体指针即下一个输入设备 */ 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
:监测触控屏设备事件及输出报点信息
#include <input_manager.h> #include <tslib.h> #include <stdio.h> 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 err\n"); 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 : %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
网络输入编程使用的数据结构和触摸屏使用的数据结构相同,这里不再赘述
在开发板上运行的是server端的程序使用udp通信,所以在创建设备的时候需要完成
/* socket
* bind
* sendto/recvfrom
*/
netinput.c
:创建一个server设备使用udp通信协议,接收client信息并打印到console终端上
#include <input_manager.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> #include <string.h> /* socket * bind * sendto/recvfrom */ #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] = '\0'; //printf("Get Msg From %s : %s\n", inet_ntoa(tSocketClientAddr.sin_addr), ucRecvBuf); ptInputEvent->iType = INPUT_TYPE_NET;//设置上报信息类型为网络类型 gettimeofday(&ptInputEvent->tTime, NULL);//获取默认时区的时间 strncpy(ptInputEvent->str, aRecvBuf, 1000);//拷贝缓冲区的数据到ptInputEvent ptInputEvent->str[999] = '\0';//添加结束符 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); /* host to net, short */ 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) { /*接收client信息并输出*/ 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
client.c
:测试使用的client程序,使用./client <server_ip> <str>
来向server发送信息
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> /* socket * connect * send/recv */ #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; } iSocketClient = socket(AF_INET, SOCK_DGRAM, 0); tSocketServerAddr.sin_family = AF_INET; tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short */ //tSocketServerAddr.sin_addr.s_addr = INADDR_ANY; if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr)) { printf("invalid server_ip\n"); 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 <pthread.h> #include <stdio.h> #include <unistd.h> #include <semaphore.h> #include <string.h> static PInputDevice g_InputDevs = NULL; void RegisterInputDevice(PInputDevice ptInputDev) { ptInputDev->ptNext = g_InputDevs; g_InputDevs = ptInputDev; } /* */ void InputInit(void) { /* regiseter touchscreen */ extern void TouchscreenRegister(void); TouchscreenRegister(); /* regiseter netinput */ 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; /* for each inputdevice, init, pthread_create */ PInputDevice ptTmp = g_InputDevs; while (ptTmp) { /* init device */ ret = ptTmp->DeviceInit(); /* pthread create */ 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);
}
建立一个环形缓冲区,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个指针:
区分缓冲区满或者空(满指缓冲区中充满了新的数据/未读的数据,空指缓冲区中没有新的数据)
缓冲区是满、或是空,都有可能出现读指针与写指针指向同一位置:
▲读指针与写指针指向同一位置
有多种策略用于检测缓冲区是满、或是空:
▲带有镜像指示位的环形缓冲区
在读写指针的值相同情况下,如果二者的指示位相同,说明缓冲区为空;如果二者的指示位不同,说明缓冲区为满。这种方法优点是测试缓冲区满/空很简单;不需要做取余数操作;读写线程可以分别设计专用算法策略,能实现精致的并发控制。 缺点是读写指针各需要额外的一位作为指示位。
如果缓冲区长度是2的幂,则本方法可以省略镜像指示位。如果读写指针的值相等,则缓冲区为空;如果读写指针相差n,则缓冲区为满,这可以用条件表达式(写指针 == (读指针 异或 缓冲区长度))来判断。
input_manage.c
#include <pthread.h> #include <stdio.h> #include <unistd.h> #include <semaphore.h> #include <string.h> static PInputDevice g_InputDevs = NULL; static pthread_mutex_t g_tMutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t g_tConVar = PTHREAD_COND_INITIALIZER; /* start of 实现环形buffer */ #define BUFFER_LEN 20 static int g_iRead = 0; static int g_iWrite = 0; static InputEvent g_atInputEvents[BUFFER_LEN]; /* 环形缓冲区满返回1 否则返回0 */ static int isInputBufferFull(void) { return (g_iRead == ((g_iWrite + 1) % BUFFER_LEN)); } /* 环形缓冲区空返回1 否则返回0 */ 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; } } /* 从环形缓冲区获取事件信息 成功获得信息返回1 否则返回0*/ static int GetInputEventFromBuffer(PInputEvent ptInputEvent) { if (!isInputBufferEmpty())//如果环形缓冲区不为空 { *ptInputEvent = g_atInputEvents[g_iRead]; g_iRead = (g_iRead + 1) % BUFFER_LEN; return 1; } else { return 0; } } /* end of 实现环形buffer */ void RegisterInputDevice(PInputDevice ptInputDev) { ptInputDev->ptNext = g_InputDevs; g_InputDevs = ptInputDev; } void InputInit(void) { /* regiseter touchscreen */ extern void TouchscreenRegister(void); TouchscreenRegister(); /* regiseter netinput */ 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; /* for each inputdevice, init, pthread_create */ PInputDevice ptTmp = g_InputDevs; while (ptTmp) { /* init device */ ret = ptTmp->DeviceInit(); /* pthread create */ if (!ret) { ret = pthread_create(&tid, NULL, input_recv_thread_func, ptTmp); } ptTmp= ptTmp->ptNext; } } /* 从环形缓冲区中获取事件信息 成功返回0 失败返回-1*/ 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; }
#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 <input_manager.h> int main(int argc, char **argv) { int ret; InputEvent event; /* 设备注册、设备初始化、设备线程创建 */ InputInit(); IntpuDeviceInit(); while (1) { printf("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); ret = GetInputEvent(&event); printf("%s %s %d, ret = %d\n", __FILE__, __FUNCTION__, __LINE__, ret); if (ret) { printf("GetInputEvent err!\n"); return -1; } else { printf("%s %s %d, event.iType = %d\n", __FILE__, __FUNCTION__, __LINE__, event.iType ); if (event.iType == INPUT_TYPE_TOUCH)//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)//web设备 { printf("Type : %d\n", event.iType); printf("str : %s\n", event.str); } } } return 0; }
【嵌入式Linux】嵌入式Linux应用开发基础知识之Framebuffer应用编程和字符汉字显示
▲程序分层
▲字形指标
font_manage.h
:
#ifndef _FONT_MANAGER_H #define _FONT_MANAGER_H #ifndef NULL #define NULL (void *)0 #endif /* 描述一个文字的位图 * iLeftUpX、iLeftUpY:左上角(x,y)坐标 * iWidth、iRows:宽度、行数 * iCurOriginX、iCurOriginY:当前字符的基点(x,y)坐标 * iNextOriginX、iNextOriginY:下一个字符的基点(x,y)坐标 * pucBuffer:位图指针 */ typedef struct FontBitMap { int iLeftUpX; int iLeftUpY; int iWidth; int iRows; int iCurOriginX; int iCurOriginY; int iNextOriginX; int iNextOriginY; unsigned char *pucBuffer; }FontBitMap, *PFontBitMap; /* 字库的操作结构体 name:使用freetype还是点阵 FontInit:初始化函数 SetFontSize:设置字体大小 GetFontBitMap:获得字符的位图 ptNext:下一个字库的操作结构体 */ 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; #endif
freetype.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 <math.h> #include <wchar.h> #include <sys/ioctl.h> #include <font_manager.h> #include <ft2build.h> #include FT_FREETYPE_H #include FT_GLYPH_H static FT_Face g_tFace; //矢量字体文字 static int g_iDefaultFontSize = 12; //默认字体大小 /* 初始化FreeType 创建字形结构体 */ static int FreeTypeFontInit(char *aFineName) { FT_Library library; int error; error = FT_Init_FreeType( &library ); /* initialize library */ if (error) { printf("FT_Init_FreeType err\n"); return -1; } error = FT_New_Face(library, aFineName, 0, &g_tFace ); /* create face object */ if (error) { printf("FT_New_Face err\n"); 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; } /* 根据编码值dwCode获得位图保存在ptFontBitMap指向的结构体中 */ 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; /* 单位: 1/64像素 */ pen.y = ptFontBitMap->iCurOriginY * 64; /* 单位: 1/64像素 */ /* 转换:transformation */ FT_Set_Transform(g_tFace, 0, &pen); /* 加载位图: load glyph image into the slot (erase previous one) */ error = FT_Load_Char(g_tFace, dwCode, FT_LOAD_RENDER); if (error) { printf("FT_Load_Char error\n"); return -1; } /* 将加载出来的位图数据保存到ptFontBitMap */ ptFontBitMap->pucBuffer = slot->bitmap.buffer; /* 位图左上角坐标x值 */ ptFontBitMap->iLeftUpX = slot->bitmap_left; /* * iCurOriginY是LCD坐标系 bitmap_top是笛卡尔坐标系 iLeftUpY是LCD坐标系 * iLeftUpY = iCurOriginY - delta * delta = bitmap_top - iCurOriginY //此处有疑问 * iLeftUpY = 2 * iCurOriginY - bitmap_top */ 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; } /* 初始化freetype字库的操作结构体 */ static FontOpr g_tFreetypeOpr = { .name = "freetype", .FontInit = FreeTypeFontInit, .SetFontSize = FreeTypeSetFontSize, .GetFontBitMap = FreeTypeGetFontBitMap, };
font_manage.c
:管理文字库设备
#include <font_manager.h> 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 /* * 区域信息 * iLeftUpX iLeftUpY:里面包含了绘制的起始坐标 * iWidth iHeigh:字符的长宽信息 */ 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;
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/639181
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。