接下来学一些linux的应用层编程的内容,重点学习一下LCD,Socket,音频等反面的内容。

之前学习应用编程没有进入提高阶段最近捡一下,已经把TCP/IP的一部分知识补充了。

FrameBuffer

在LCD驱动章节说过Framebuffer就是帧缓冲,意味着Framebuffer就是一块内存,里面保存着一帧图像。帧缓冲(framebuffer)是 Linux 系统中的一种显示驱动接口,它将显示设备(譬如 LCD) 进行抽象、 屏蔽了不同显示设备硬件的实现,对应用层抽象为一块显示内存(显存),它允许上层应用程序直接对显示缓冲区进行读写操作 。

LCD是没有显存的,存储在DDR上的一段空间内,显存大小=分辨率x色彩精度/8。

使用ioctl函数进行获取信息

当打开 LCD 设备文件之后,需要先获取到 LCD 屏幕的参数信息,譬如 LCD 的 X 轴分辨率、 Y 轴分辨率以及像素格式等信息,通过这些参数计算出 LCD 显示缓冲区的大小。

一般获取FBIOGET_VSCREENINFO(表示获取 FrameBuffer 设备的可变参数信息 )、FBIOPUT_VSCREENINFO( 表示设置 FrameBuffer 设备的可变参数信息 )、FBIOGET_FSCREENINFO(表示获取 FrameBuffer 设备的固定参数信息 )。

可变参数信息使用 struct fb_var_screeninfo 结 构 体 来 描 述 ,固定参数信息使用struct fb_fix_screeninfo结构体来描述 。

上 面 所 提 到 的 三 个 宏 定 义 FBIOGET_VSCREENINFO 、 FBIOPUT_VSCREENINFO 、FBIOGET_FSCREENINFO 以及 2 个数据结构 struct fb_var_screeninfo 和 struct fb_fix_screeninfo 都定义在 <linux/fb.h>头文件中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
struct fb_var_screeninfo{
__u32 xres; /* 可视区域,一行有多少个像素点, X 分辨率 */
__u32 yres; /* 可视区域,一列有多少个像素点, Y 分辨率 */
__u32 xres_virtual; /* 虚拟区域,一行有多少个像素点 */
__u32 yres_virtual; /* 虚拟区域,一列有多少个像素点 */
__u32 xoffset; /* 虚拟到可见屏幕之间的行偏移 */
__u32 yoffset; /* 虚拟到可见屏幕之间的列偏移 */
__u32 bits_per_pixel; /* 每个像素点使用多少个 bit 来描述,也就是像素深度 bpp */
__u32 grayscale; /* =0 表示彩色, =1 表示灰度, >1 表示 FOURCC 颜色 */
/* 用于描述 R、 G、 B 三种颜色分量分别用多少位来表示以及它们各自的偏移量 */
struct fb_bitfield red; /* Red 颜色分量色域偏移 */
struct fb_bitfield green; /* Green 颜色分量色域偏移 */
struct fb_bitfield blue; /* Blue 颜色分量色域偏移 */
struct fb_bitfield transp; /* 透明度分量色域偏移 */
__u32 nonstd; /* nonstd 等于 0,表示标准像素格式;不等于 0 则表示非标准像素格式 */
__u32 activate;
__u32 height; /* 用来描述 LCD 屏显示图像的高度(以毫米为单位) */
__u32 width; /* 用来描述 LCD 屏显示图像的宽度(以毫米为单位) */
__u32 accel_flags;
/* 以下这些变量表示时序参数 */
__u32 pixclock; /* pixel clock in ps (pico seconds) */
__u32 left_margin; /* time from sync to picture */
__u32 right_margin; /* time from picture to sync */
__u32 upper_margin; /* time from sync to picture */
__u32 lower_margin;
__u32 hsync_len; /* length of horizontal sync */
__u32 vsync_len; /* length of vertical sync */
__u32 sync; /* see FB_SYNC_* */
__u32 vmode; /* see FB_VMODE_* */
__u32 rotate; /* angle we rotate counter clockwise */
__u32 colorspace; /* colorspace for FOURCC-based modes */
__u32 reserved[4]; /* Reserved for future compatibility */
}
1
2
3
4
5
struct fb_bitfield {
__u32 offset; /* 偏移量 */
__u32 length; /* 长度 */
__u32 msb_right; /* != 0 : Most significant bit is right
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct fb_fix_screeninfo {
char id[16]; /* 字符串形式的标识符 */
unsigned long smem_start; /* 显存的起始地址(物理地址) */
__u32 smem_len; /* 显存的长度 */
__u32 type;
__u32 type_aux;
__u32 visual;
__u16 xpanstep;
__u16 ypanstep;
__u16 ywrapstep;
__u32 line_length; /* 一行的字节数 */
unsigned long mmio_start; /* Start of Memory Mapped I/O(physical address) */
__u32 mmio_len; /* Length of Memory Mapped I/O */
__u32 accel; /* Indicate to driver which specific chip/card we have */
__u16 capabilities;
__u16 reserved[2];
};

smem_start 表示显存的起始地址,这是一个物理地址,当然在应用层无法直接使用; smem_len 表示显存的长度,这个长度并一定等于 LCD 实际的显存大小。 line_length 表示屏幕的一行像素点有多少个字节,通常可以使用 line_length * yres 来得到屏幕显示缓冲区的大小。

编写程序获取LCD屏幕信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
int main(int argc, char *argv[])
{
struct fb_fix_screeninfo fb_fix;
struct fb_var_screeninfo fb_var;
int fd;
printf("start\n");
if((fd = open("/dev/fb0", O_WRONLY)) < 0)
{
printf("open error\n");
perror("open error");
exit(-1);
}else{
printf("open success");
}
if(ioctl(fd, FBIOGET_VSCREENINFO, &fb_var) < 0)
{
printf("ioctl error\n");
perror("ioctl error");
exit(-1);
}else{
printf("ioctl success");
}
if(ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix) < 0)
{
printf("ioctl error\n");
perror("ioctl perror");
exit(-1);
}else{
printf("ioctl success");
}
printf("分辨率:%d * %d\n"
"像素深度bpp: %d\n"
"一行的字节数:%d\n"
"像素格式:%<%d %d> G<%d %d> B<%d %d>\n",
fb_var.xres, fb_var.yres, fb_var.bits_per_pixel,
fb_fix.line_length,
fb_var.red.offset, fb_var.red.length,
fb_var.green.offset, fb_var.green.length,
fb_var.blue.offset, fb_var.blue.length);
close(fd);
exit(0);
}

第一个数字表示偏移量,第二个参数为长度, 从打印的结果可知, 16bit 颜色值中高 5 位表示 R 颜色通道、中间 6 位表示 G 颜色通道、低 5 位表示 B 颜色通道, 所以这是一个 RGB565 格式的显示设备。

lcd屏幕的基础操作(画图形)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#define argb8888_to_rgb565(color) ({ \
unsigned int temp = (color); \
((temp & 0xF80000UL) >> 8) | \
((temp & 0xFC00UL) >> 5) | \
((temp & 0xF8UL) >> 3); \
})
static int width; //LCD 的X像素也可以说是横着的像素长度
static int height; //LCD 的Y像素也可以说是竖着的像素长度
static unsigned short *screen_base = NULL; //映射后的显存基地址
/*
画点
*/
static void lcd_draw_point(unsigned int x, unsigned int y, unsigned int color)
{
unsigned short rgb565_color = argb8888_to_rgb565(color);//得到RGB565的颜色数值
if( x >= width)
x = width -1;
if( y >= height)
y = height -1; //保证传入的坐标在屏幕中,不要超过屏幕的长宽。
/* 填充颜色 */
screen_base[y * width + x] = rgb565_color;
}
/*
画线
*/
static void lcd_draw_line(unsigned int x, unsigned int y, int dir, unsigned int length, unsigned int color)
{
unsigned short rgb565_color = argb8888_to_rgb565(color);//得到 RGB565 颜色值
unsigned int end;
unsigned long temp;
/* 对传入参数的校验 ,跟之前一样*/
if (x >= width)
x = width - 1;
if (y >= height)
y = height - 1;
temp = y * width + x;//定位到起点
if (dir) { //dir为控制线的横竖方向
//水平线
end = x + length - 1;
if (end >= width)
end = width - 1;
for ( ; x <= end; x++, temp++)
screen_base[temp] = rgb565_color;
}
else { //垂直线
end = y + length - 1;
if (end >= height)
end = height - 1;
for ( ; y <= end; y++, temp += width)
screen_base[temp] = rgb565_color;
}
}
/*画矩形*/
static void lcd_draw_rectangle(unsigned int start_x, unsigned int end_x,unsigned int start_y, unsigned int end_y,unsigned int color)
{
int x_len = end_x - start_x + 1;
int y_len = end_y - start_y - 1;
lcd_draw_line(start_x, start_y, 1, x_len, color);//
lcd_draw_line(start_x, end_y, 1, x_len, color); //下边
lcd_draw_line(start_x, start_y + 1, 0, y_len, color);//左边
lcd_draw_line(end_x, start_y + 1, 0, y_len, color);//右边
}
/*矩形填充*/
static void lcd_fill(unsigned int start_x, unsigned int end_x,unsigned int start_y, unsigned int end_y,unsigned int color)
{
unsigned short rgb565_color = argb8888_to_rgb565(color);//得到 RGB565 颜色值
unsigned long temp;
unsigned int x;
/* 对传入参数的校验 */
if (end_x >= width)
end_x = width - 1;
if (end_y >= height)
end_y = height - 1;
/* 填充颜色 */
temp = start_y * width; //定位到起点行首
for ( ; start_y <= end_y; start_y++, temp+=width) {
for (x = start_x; x <= end_x; x++)
screen_base[temp + x] = rgb565_color;
}
}
int main(int argc, char *argv[])
{
struct fb_fix_screeninfo fb_fix;
struct fb_var_screeninfo fb_var;
unsigned int screen_size;
int fd;
/* 打开 framebuffer 设备 */
if (0 > (fd = open("/dev/fb0", O_RDWR)))
{
perror("open error");
exit(EXIT_FAILURE);
}
/* 获取参数信息 */
ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);
ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);
screen_size = fb_fix.line_length * fb_var.yres;
width = fb_var.xres;
height = fb_var.yres;
/* 将显示缓冲区映射到进程地址空间 */
screen_base = mmap(NULL, screen_size, PROT_WRITE, MAP_SHARED, fd, 0);
if (MAP_FAILED == (void *)screen_base) {
perror("mmap error");
close(fd);
exit(EXIT_FAILURE);
}
/* 画正方形方块 */
int w = height * 0.25;//方块的宽度为 1/4 屏幕高度
lcd_fill(0, width-1, 0, height-1, 0x0); //清屏(屏幕显示黑色)
lcd_fill(0, w, 0, w, 0xFF0000); //红色方块
lcd_fill(width-w, width-1, 0, w, 0xFF00); //绿色方块
lcd_fill(0, w, height-w, height-1, 0xFF); //蓝色方块
lcd_fill(width-w, width-1, height-w, height-1, 0xFFFF00);//黄色方块
/* 退出 */
munmap(screen_base, screen_size); //取消映射
close(fd); //关闭文件
exit(EXIT_SUCCESS); //退出进程
}

LCD对于jpeg图片的显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <jpeglib.h>
typedef struct bgr888_color {
unsigned char red;
unsigned char green;
unsigned char blue;
} __attribute__ ((packed)) bgr888_t;
static int width; //LCD X 分辨率
static int height; //LCD Y 分辨率
static unsigned short *screen_base = NULL; //映射后的显存基地址
static unsigned long line_length; //LCD 一行的长度(字节为单位)
static unsigned int bpp; //像素深度 bpp
static int show_jpeg_image(const char *path)
{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE *jpeg_file = NULL;
bgr888_t *jpeg_line_buf = NULL; //行缓冲区:用于存储从 jpeg 文件中解压出来的一行图像数据
unsigned short *fb_line_buf = NULL; //行缓冲区:用于存储写入到 LCD 显存的一行数据
unsigned int min_h, min_w;
unsigned int valid_bytes;
int i;
//绑定默认错误处理函数
cinfo.err = jpeg_std_error(&jerr);
//打开.jpeg/.jpg 图像文件
jpeg_file = fopen(path, "r"); //只读方式打开
if (NULL == jpeg_file) {
perror("fopen error");
return -1;
}
//创建 JPEG 解码对象
jpeg_create_decompress(&cinfo);
//指定图像文件
jpeg_stdio_src(&cinfo, jpeg_file);
//读取图像信息
jpeg_read_header(&cinfo, TRUE);
printf("jpeg 图像大小: %d*%d\n", cinfo.image_width, cinfo.image_height);
//设置解码参数
cinfo.out_color_space = JCS_RGB;//默认就是 JCS_RGB
//cinfo.scale_num = 1;/* JPEG 当前仅支持 1/1、 1/2、 1/4、 和 1/8 这几种缩小比例。 默认是 1/1,也就是保持原图大小。*/
//cinfo.scale_denom = 2;
//开始解码图像
jpeg_start_decompress(&cinfo);
//为缓冲区分配内存空间
jpeg_line_buf = malloc(cinfo.output_components * cinfo.output_width);
fb_line_buf = malloc(line_length);
//判断图像和 LCD 屏那个的分辨率更低
if (cinfo.output_width > width)
min_w = width;
else
min_w = cinfo.output_width;
if (cinfo.output_height > height)
min_h = height;
else
min_h = cinfo.output_height;
//读取数据
valid_bytes = min_w * bpp / 8;//一行的有效字节数 表示真正写入到 LCD 显存的一行数据的大小
while (cinfo.output_scanline < min_h) {
jpeg_read_scanlines(&cinfo, (unsigned char **)&jpeg_line_buf, 1);//每次读取一行数据,只能支持一次只读 1 行
//将读取到的 BGR888 数据转为 RGB565
for (i = 0; i < min_w; i++)
fb_line_buf[i] = ((jpeg_line_buf[i].red & 0xF8) << 8) |((jpeg_line_buf[i].green & 0xFC) << 3) |((jpeg_line_buf[i].blue & 0xF8) >> 3);
memcpy(screen_base, fb_line_buf, valid_bytes);
screen_base += width;//+width 定位到 LCD 下一行显存地址的起点
}
//解码完成
jpeg_finish_decompress(&cinfo); //完成解码

jpeg_destroy_decompress(&cinfo);//销毁 JPEG 解码对象、释放资源
//关闭文件、释放内存
fclose(jpeg_file);
free(fb_line_buf);
free(jpeg_line_buf);
return 0;
}
int main(int argc, char *argv[])
{
struct fb_fix_screeninfo fb_fix;
struct fb_var_screeninfo fb_var;
unsigned int screen_size;
int fd;
/* 传参校验 */
if (2 != argc) {
fprintf(stderr, "usage: %s <jpeg_file>\n", argv[0]);
exit(-1);
}
/* 打开 framebuffer 设备 */
if (0 > (fd = open("/dev/fb0", O_RDWR))) {
perror("open error");
exit(EXIT_FAILURE);
}
/* 获取参数信息 */
ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);
ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);
line_length = fb_fix.line_length;
bpp = fb_var.bits_per_pixel;
screen_size = line_length * fb_var.yres;
width = fb_var.xres;
height = fb_var.yres;
/* 将显示缓冲区映射到进程地址空间 */
screen_base = mmap(NULL, screen_size, PROT_WRITE, MAP_SHARED, fd, 0);
if (MAP_FAILED == (void *)screen_base) {
perror("mmap error");
close(fd);
exit(EXIT_FAILURE);
}
/* 显示 BMP 图片 */
memset(screen_base, 0xFF, screen_size);
show_jpeg_image(argv[1]);
/* 退出 */
munmap(screen_base, screen_size); //取消映射
close(fd); //关闭文件
exit(EXIT_SUCCESS); //退出进程
}