① Framebuffer应用编程
② 文字显示及图像显示
③ 输入系统应用编程
④ 网络通信
⑤ 多线程编程
显示部分有多种形式显示设备,在程序框架中可以看出可以有LCD显示,也可以有网络传输之后Web显示。
如何构造一个通用的数据结构,使得不管对于LCD显示还是Web显示,都可以很方便的修改移植呢?
C:\Users\Administrator\Desktop\LYH\myLinux\量产工具项目\myCode\disp_manager.h
#ifndef _DISP_MANAGER_H // 防止头文件被重复定义
#define _DISP_MANAGER_H
// 一块区域
typedef struct Region{
int iLeftUpX;
int iLeftUpY;
int iWidth;
int iHeigh;
}Region, *PRegion;
typedef struct DispOpr{
char *name;
int DeviceInit(); // 初始化函数
int DeviceExit(); // 退出函数,做一些清理工作
char *GetBuffer(int *pXres, int *pYres, int *pBpp); // 获得一个Buffer的数据
int FlushRegion(PRegion ptRegion, char *buffer); // 把buffer数据刷新到ptRegion区域
struct DispOpr *ptNext; // 如果有多个显示设备,可以用链表把它们串起来
}DispOpr, *PDispOpr;
#endif /* _DISP_MANAGER_H */
C:\Users\Administrator\Desktop\LYH\myLinux\量产工具项目\myCode\framebuffer.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <string.h>
#include "disp_manager.h"
static int fd_fb; // lcd节点句柄
static struct fb_var_screeninfo var; // 可变参数
static unsigned int line_width; // 显示一行需要多少字节
static unsigned int pixel_width; // 显示一个像素需要多少字节
static int screen_size;
static unsigned char *fb_base;
static int FbDeviceInit(void)
{
// 1.打开framebuffer设备
fd_fb = open("/dev/fb0", O_RDWR);
if(fd_fb<0)
{
printf("can't open /dev/fb0 \n");
return -1;
}
// 2.测试ioctl能否获取可变信息,信息存储在 var变量中
if(ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
{
printf("can't get var \n");
return -1;
}
// 3.从可变参数总提取重要信息
// 3.1 x方向1行需要多少个字节
line_width = var.xres * var.bits_per_pixel / 8;
// 3.2 1个像素需要多少个字节
pixel_width = var.bits_per_pixel / 8;
// 3.3 1幅屏幕需要多少个字节
screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
// 4.framebuffer基地址
/* 作用:将内存空间映射到用户空间,用户空间直接访问映射的空间就是访问实际的内核态的内存空间
* addr:都写为NULL
* length:映射多少字节
* prot:可读可写属性 PROT_EXEC PROT_READ PROT_WRITE PROT_NONE
* flags:MAP_SHARED写共享模式,两边同步 MAP_PRIVATE写复制模式
* fd:打开lcd的设备文件,里面有对应的 mmap函数
* offset:偏移多少
* 返回虚拟基地址:fb_base
*/
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;
}
// 5.虚拟地址映射之后,直接往该framebuffer中填充像素数据就可以显示出来了
// 5.1 清屏:0xff是黑色,0x00是白色
memset(fb_base, 0xff, screen_size);
// 前面都成功的话,返回 0
return 0;
}
static int FbDeviceExit(void)
{
// 取消mmap地址映射
munmap(fb_base, screen_size);
// 关闭framebuffer文件句柄
close(fd_fb);
return 0;
}
/* 可以返回一个LCD的framebuffer,以后上层APP可以直接操作LCD,可以不用FbFlushRegion,可以写为空
* 也可以malloc返回一块无关的buffer,要使用FbFlushRegion
*/
static char *FbGetBuffer(int *pXres, int *pYres, int *pBpp)
{
*pXres = var.xres;
*pYres = var.yres;
*pBpp = var.bits_per_pixel;
return fb_base;
}
static int FbFlushRegion(PRegion ptRegion, char *buffer)
{
return 0;
}
// 实现一个使用LCD显示的结构体,需要实现里面的函数
// 之后综合程序只需要调用这个 g_tFramebufferOpr 里面的函数
// 就可以实现用 LCD显示了,相当于再封装了一层
static DispOpr g_tFramebufferOpr = {
.name = "fb",
.DeviceInit = FbDeviceInit,
.DeviceExit = FbDeviceExit,
.GetBuffer = FbGetBuffer,
.FlushRegion = FbFlushRegion,
};
gedit ~/.bashrc
修改交叉编译工具链路径
source ~/.bashrc
使能环境变量
export ARCH=arm
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
export PATH=$PATH:/home/book/myDoc/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin
# ?= 是如果前面有定义,这里就不定义了
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 :=
export CFLAGS LDFLAGS
TOPDIR := $(shell pwd)
export TOPDIR
# 目标文件,最终在顶层目录中生成可执行文件 test
TARGET := test
# 表示要进入subdir这个子目录下去寻找文件来编进程序里,是哪些文件由subdir目录下的Makefile决定。
obj-y += display/
obj-y += unittest/
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)
需要添加下面的语句
EXTRA_CFLAGS :=
CFLAGS_file.o :=
obj-y += disp_manager.o
obj-y += framebuffer.o
要测试自己的framebuffer,需要把自带的QT GUI给去掉,这个GUI脚本文件在 rsz自启动脚本中。
关闭屏幕休眠
echo -e "\033[9;0]" > /dev/tty1
echo -e "\033[?25l" > /dev/tty1
显示结果
可以在屏幕上显示出一个字符 A
在显示设备中添加了一层,显示管理层,承上启下的作用,编写应用程序的时候,就不用苦哈哈的去打开设备节点,去mmap之后再进行逻辑操作了。应用程序就会比较清晰。
在应用程序和各种输入设备之间,需要添加一层输入管理层,可以用来给应用程序提供统一的接口函数,同时可以管理各种不同的输入设备。
#ifndef _INPUT_MANAGER_H
#define _INPUT_MANAGER_H
#define INPUT_TYPE_TOUCH 1
#define INPUT_TYOE_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, InputDevice;
#endif /* _INPUT_MANAGER_H */
#if 1 // 单元测试触摸屏
int main(int argc, char **argv)
{
InputEvent event;
int ret;
g_tTouchscreenDev.DeviceInit();
while(1)
{
// ts_read 函数 没有监测到触摸,就会阻塞
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);
}
}
}
#endif
【测试结果】是真的能得到压力值的。
直接移植 UDP1的程序,在开发板上测试就行。
处于同一个局域网的两台电脑进行通信
为什么需要添加一层输入系统呢?
// 基本思路就是:主函数线程中一直读取环形缓冲区的 InputEvent 类型数据
// 没有数据就阻塞等待,有数据就通过 InputEvent 里面的 iType类型来判断是哪个输入设备数据
// 各个输入设备(触摸屏和网络输入),都有自己的线程,会一直获取数据,获取到了就往环形缓冲区里面填
查看是否生成了线程的方法:
【测试结果】
关闭LCD屏幕自动息屏功能
echo -e "\033[9;0]" > /dev/tty1
echo -e "\033[?25l" > /dev/tty1
可以给函数指针起别名
typedef int (*ONDRAW_FUNC)(struct Button *ptButton, PDispBuff ptDispBuff);
typedef int (*ONPRESSED_FUNC)(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent);
typedef struct Button {
char *name;
Region tRegion;
ONDRAW_FUNC OnDraw;
ONPRESSED_FUNC OnPressed;
}Button, *Button;