嵌入式自学第三十一天
(1)framebuf帧缓冲 :linux提供的显示设备驱动的接口。
设备路径 : 设备/dev/fb0
* 分辨率:像素点是w * h。
每个像素点色深 RGB:0-255 红绿蓝各3字节(byte)即可描述色深。
访问的显存默认左上角为坐标原点。
* xres yres真实分辨率
xres_vitrual虚拟分辨率:
一般可访问显存小一些,即虚拟分辨率,真实分辨率就是打开设置查看得到的分辨率。
* 每个像素点色深RGB用三字节描述,即3*8bit,用十六进制就是0x00rrggbb,具体颜色对应16进制可以搜索w3cschool,在里面的htmlyanse查看。
* 文件解压:tar -xvf 压缩包.tar.xz
* ctrl alt f3文字终端,没有图形界面。
ctrl alt f1图形界面,伪终端即图形界面打开的终端。
ctrl alt鼠标出现
直接操作硬件运行时sudo ./out
* 简单的操作像素点,比如画直线等,可以通过简单的循环控制xy坐标就能实现,但汉字图像等需要的像素点坐标就比较复杂了。
* 文字可以通过字模工具获取要操作的像素点坐标数组,也可以通过字库进行操作,最原始的图像是位图,文件信息中前54字节是头信息,包含图像的大小等参数,剩下的就是连续了BGRBGR像素点数据,我们可以循环取三字节,再倒序就得到RGB参数了,然后输出。
(2)利用帧缓存点亮第一个点。
* 步骤:open打开设备-》ioctl获取屏幕信息-》mmap映射:获得显存 -》draw根据坐标操作像素点 -》unmap显存回收
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <math.h>
// 颜色定义
#define RGB888_FMT 32
#define RGB565_FMT 16
#define WHITE 0x00FFFFFF
#define BLACK 0x00000000
#define RED 0x00FF0000
#define GREEN 0x0000FF00
#define BLUE 0x000000FF
#define YELLOW 0x00FFFF00
#define PURPLE 0x00FF00FF
#define CYAN 0x0000FFFF
// 全局变量
struct fb_var_screeninfo vinf;
void *pmem = NULL;
// 函数声明
int init_fb(char *devname);
void draw_point(int x, int y, unsigned int col);
void draw_h_line(int x, int y, int len, unsigned int col);
void draw_v_line(int x, int y, int len, unsigned int col);
void draw_x_line(int x1, int y1, int x2, int y2, unsigned int col);
void draw_rect(int x, int y, int w, int h, unsigned int col);
void draw_circle(int x0, int y0, int r, unsigned int col);
void draw_bmp(int x, int y, char *picname, int w, int h);
void draw_word(int x, int y, unsigned char *word, int w, int h, unsigned int col);
// 初始化帧缓冲设备
int init_fb(char *devname)
{
//1. 打开显示设备
int fd = open(devname, O_RDWR);
if (-1 == fd)
{
perror("fail open fb");
return -1;
}
//2、获取显示设备参数(分辨率、位深度等)
int ret = ioctl(fd, FBIOGET_VSCREENINFO, &vinf);
if (-1 ==ret)
{
perror("fail ioctl");
return -1;
}
//3, 建立显存和用户空间的映射关系
size_t len = vinf.xres_virtual * vinf.yres_virtual * vinf.bits_per_pixel/8;
pmem = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if ((void *)-1 == pmem)
{
perror("fail mmap");
return -1;
}
return fd;
}
// 绘制单个像素点
void draw_point(int x, int y, unsigned int col)
{
if (x >= vinf.xres || y >= vinf.yres)
{
return ; // 超出屏幕范围则不绘制
}
if (vinf.bits_per_pixel == RGB888_FMT) // 32位色模式
{
unsigned int *p = pmem;
*(p + y * vinf.xres_virtual + x) = col;
}
else if (vinf.bits_per_pixel == RGB565_FMT) // 16位色模式
{
unsigned short *p = pmem;
*(p + y * vinf.xres_virtual + x) = col;
}
}
// 绘制水平线
void draw_h_line(int x, int y, int len, unsigned int col)
{
for (int i = x; i < x+len; i++)
{
draw_point(i, y, col); // 连续绘制水平方向的点
}
}
// 绘制垂直线
void draw_v_line(int x, int y, int len, unsigned int col)
{
for (int i = y; i < y+len; i++)
{
draw_point(x, i, col); // 连续绘制垂直方向的点
}
}
// 绘制斜线(使用直线方程y=kx+b)
void draw_x_line(int x1, int y1, int x2, int y2, unsigned int col)
{
if (x1 == x2) // 垂直线特殊情况处理
{
if (y2 > y1)
{
draw_v_line(x1, y1, y2-y1, col);
}
else
{
draw_v_line(x2, y2, y1-y2, col);
}
}
// 计算直线斜率和截距
double k = (double)(y2-y1)/(double)(x2-x1);
double b = y1 - k*x1;
// 根据x坐标计算y值并绘制
for (int x = (x1 > x2 ? x2 : x1); x <= (x1 > x2 ? x1 : x2); x++)
{
int y = x * k + b;
draw_point(x, y, col);
}
}
// 绘制矩形
void draw_rect(int x, int y, int w, int h, unsigned int col)
{
draw_h_line(x, y, w, col); // 上边
draw_h_line(x, y+h-1, w, col); // 下边
draw_v_line(x, y, h, col); // 左边
draw_v_line(x+w-1, y, h, col); // 右边
}
// 绘制圆形(使用三角函数参数方程)
void draw_circle(int x0, int y0, int r, unsigned int col)
{
for (double si = 0; si <= 360; si+=0.01) // 0-360度循环
{
// 计算圆周上的点坐标
int x = r * cos(2 * 3.14159/360 *si) + x0;
int y = r * sin(2 * 3.14159/360 *si) + y0;
// 绘制点及其相邻点使线条更粗
draw_point(x, y, col);
draw_point(x-1, y, col);
draw_point(x+1, y, col);
draw_point(x, y-1, col);
draw_point(x, y+1, col);
}
}
// 绘制BMP图片
void draw_bmp(int x, int y, char *picname, int w, int h)
{
int fd = open(picname, O_RDONLY);
lseek(fd, 54, SEEK_SET); // 跳过BMP文件头
unsigned char *buff = malloc(w*h*3); // 分配缓冲区
read(fd, buff, w*h*3); // 读取像素数据
// BMP是倒序存储的,所以从下往上绘制
for (int j = h-1; j >= 0; j--)
{
for (int i = 0; i < w; i++)
{
// 读取BGR分量(注意BMP存储顺序是BGR)
unsigned char b = *buff++;
unsigned char g = *buff++;
unsigned char r = *buff++;
// 根据显示设备位深处理颜色
if (vinf.bits_per_pixel == RGB888_FMT)
{
unsigned int col = (r << 16) | (g << 8) | b;
draw_point(i+x, j+y, col);
}
else if (vinf.bits_per_pixel == RGB565_FMT)
{
unsigned short col = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
draw_point(i+x, j+y, col);
}
}
}
free(buff);
close(fd);
}
// 汉字点阵数据结构
typedef struct {
unsigned char word[32]; // 16x16点阵,每个汉字32字节
} HZ_DOT;
// 绘制单个汉字(使用点阵数据)
void draw_word(int x, int y, unsigned char *word, int w, int h, unsigned int col)
{
for (int j = 0; j < h; j++) // 行循环
{
for (int i = 0; i < w; i++) // 列循环
{
unsigned char tmp = word[i+j*w]; // 获取点阵数据的一个字节
for (int k = 0; k < 8; k++) // 每个字节8位
{
if (tmp & 0x80) // 最高位为1表示需要绘制
{
draw_point(i*8+k+x, j+y, col);
}
tmp = tmp << 1; // 检查下一位
}
}
}
}
// UTF-8字符信息结构体
typedef struct {
int width;
int height;
unsigned char *dot;
} UTF8_INFO;
// 绘制UTF-8字符串
int draw_utf8_str(UTF8_INFO *info, int arg_x, int arg_y, char* zi, unsigned int col, unsigned int col1)
{
char* temp = zi;
unsigned int x = arg_x;
unsigned int y = arg_y;
while(*temp != '\0') // 遍历字符串直到结束
{
// 绘制单个UTF-8字符
int ret = draw_utf8(info, x, y, temp, col, col1);
x += info->width; // 移动到下一个字符位置
// 处理换行
if(x > vinf.xres)
{
x = 0;
y += info->height;
if(y > vinf.yres)
{
y = 0; // 超出屏幕底部则回到顶部
}
}
temp += ret; // 移动到下一个UTF-8字符
}
return 0;
}
int main(int argc, char *argv[])
{
// 初始化帧缓冲
int fd = init_fb("/dev/fb0");
if(fd < 0)
{
printf("Frame buffer initialization failed!\n");
return -1;
}
// 清屏为黑色
memset(pmem, 0, vinf.xres_virtual * vinf.yres_virtual * vinf.bits_per_pixel/8);
// 绘制测试图形
draw_rect(100, 100, 200, 150, RED);
draw_circle(300, 200, 50, BLUE);
draw_x_line(50, 50, 400, 300, GREEN);
// 关闭设备
munmap(pmem, vinf.xres_virtual * vinf.yres_virtual * vinf.bits_per_pixel/8);
close(fd);
return 0;
}