b
承接实训第三天:zigbee无线传感网实训---LCD显示bmp图片及一些简单的c语言编程功能( On the third day)
一、修改实训第三天中练习2中的bug(在原码的基础上增加:lseek(bmp, 54, SEEK_SET);即可)
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char **argv)
{
int lcd = open("/dev/fb0", O_RDWR);
if(lcd == -1)
{
perror("打开LCD失败");
exit(0);
}
// 2, 映射一块恰当大小的内存
char *p = mmap(NULL, // 不指定映射内存的位置,让系统自动选取
800*480*4, // 映射内存的大小
PROT_READ | PROT_WRITE, // 以读、写模式操作映射内存
MAP_SHARED, // 其他线程、进程可以共享访问这块内存
lcd, // LCD文件描述符
0 // 映射内存与LCD的偏移量,0代表完全重合
);
// 3, 准备数据
if(argc < 2)
{
printf("参数不对!\n");
close(lcd);
munmap(p, 800*480*4);
exit(0);
}
int bmp = open(argv[1], O_RDONLY);
if(bmp == -1)
{
perror("打开图片失败!");
close(lcd);
munmap(p, 800*480*4);
exit(0);
}
char bmp_buf[800*480*3];
lseek(bmp, 54, SEEK_SET);//bug修复处
int ret = read(bmp, bmp_buf, sizeof(bmp_buf));
if(ret <= 0)
{
perror("读取bmp数据失败!");
close(lcd);
close(bmp);
munmap(p, 800*480*4);
exit(0);
}
int lcd_buf[800*480];
// 4, 铺满整个映射内存
for(int i=0; i<800*480; i++)
{
lcd_buf[i]=bmp_buf[i*3] | bmp_buf[i*3+1]<<8 | bmp_buf[i*3+2]<<16;
}
for(int y=0; y<480; y++)
{
for(int x=0; x<800; x++)
{
memcpy(p+4*x+800*4*y, &lcd_buf[x+(479-y)*800], 4);
}
}
// 5, 释放资源
munmap(p, 800*480*4);
close(lcd);
return 0;
}
编译过程同实训第二天。
效果图:
二、常见的图片格式:
bmp jpg jpeg bng ....
除了bmp位图其他格式的图片全都是进过特定的图像压缩算法压缩过的若要在开发板显示其他格式的图片需要移植解码库 jpg及jpeg解码库移植
复制 jpegsrc.v9c.tar.gz到ubuntu的家目录
cp jpegsrc.v9c.tar.gz ~/
解压
cd ~
mkdir jpeg
tar zxvf jpegsrc.v9c.tar.gz -C ./jpeg
编译:
切换路径到/home/gec/jpeg/jpeg-9c/
cd jpeg/jpeg-9c/
配置:
./configure --prefix=/home/gec/jpeg/ --host=arm-none-linux-gnueabi
prefix//指定安装路径 host//指定编译器
编译:
make
安装:
make install
切换路径到/home/gec/jpeg
查看是否安装成功:
bin include lib share(存在这些内容则表示安装成功)
压缩库文件“lib”
tar zcvf jpeglib.tar.gz lib
拷贝到共享文件夹
cp jpeglib.tar.gz /mnt/hgfs/** //**表示自己创建的文件夹名
-----------------------------------------------------------
在LCD开发板中
将压缩包传输到开发板
tftp -g 192.168.1.** -r jpeglib.tar.gz //192.168.1.**表示本机IP
解压到你开发板的家目录下
cd ~
mkdir jpeg
tar zxvf jpeglib.tar.gz -C ~/jpeg
-------------------------------------------------------------
编译显示jpg图片代码
arm-none-linux-gnueabi-gcc show_jpg.c -o show_jpg -I/home/gec/jpeg/include -L/home/gec/jpeg/lib -ljpeg
-I --->指定头文件的路径
-L --->指明库文件所在路径
-l --->指定要连接的库名
动态库的命名libxxxx.so.**** ---> xxxx表示库的名字
将可执行程序下载到开发板
tftp -g 192.168.1.181 -r show_jpg
chomd 777 show_jpg //修改权限
tftp -g 192.168.1.181 -r 1.jpg
./show_jpg 1.jpg
出错:
./show_jpg: error while loading shared libraries: libjpeg.so.9: cannot open shared object file: No such file or directory
在环境变量中指定库文件的路径
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/chongjiao/lib
”/chongjiao/lib“ 下载到开发板的lib的路径
一次的
想要永久保留将export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:“/chongjiao/lib”
写入/etc/profile即可
vi /etc/profile
写到最后一句export ***的下一行即可
练习:
在LCD屏上显示JPG图片:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "jpeglib.h"
struct imginfo
{
int width;
int height;
int pixel_size;
};
char *readjpg(char const *filename, int size)
{
char *jpg_buffer = calloc(1, size);
// 不断读取JPG信息,放入jpg_buffer
int fd = open(filename, O_RDONLY);
int n = 0;
while(size > 0)
{
n = read(fd, jpg_buffer+n, size);
size -= n;
}
close(fd);
return jpg_buffer;
}
int main(int argc, char const *argv[]) // ./showjpg xxx.jpg
{
struct stat info;
bzero(&info, sizeof(info));
stat(argv[1], &info);
char *jpg_buffer = readjpg(argv[1], info.st_size);
// 声明解压缩结构体,以及错误管理结构体
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
// 使用缺省的出错处理来初始化解压缩结构体
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
// 配置该cinfo,使其从jpg_buffer中读取jpg_size个字节
// 这些数据必须是完整的JPEG数据
jpeg_mem_src(&cinfo, jpg_buffer, info.st_size);
// 读取JPEG文件的头,并判断其格式是否合法
int ret = jpeg_read_header(&cinfo, true);
if(ret != 1)
{
fprintf(stderr, "[%d]: jpeg_read_header failed: "
"%s\n", __LINE__, strerror(errno));
exit(1);
}
// 开始解压
jpeg_start_decompress(&cinfo);
// 将图片的宽、高、深度信息保存起来
struct imginfo imageinfo;
imageinfo.width = cinfo.output_width;
imageinfo.height = cinfo.output_height;
imageinfo.pixel_size = cinfo.output_components;
int row_stride = imageinfo.width * imageinfo.pixel_size;
// 根据图片的尺寸大小,分配一块相应的内存bmp_buffer
// 用来存放从jpg_buffer解压出来的图像数据
unsigned long rgb_size;
unsigned char *rgb_buffer;
rgb_size = imageinfo.width * imageinfo.height * imageinfo.pixel_size;
rgb_buffer = (unsigned char *)calloc(1, rgb_size);
// 循环地将图片的每一行读出并解压到bmp_buffer中
int line = 0;
while(cinfo.output_scanline < cinfo.output_height)
{
unsigned char *buffer_array[1];
buffer_array[0] = rgb_buffer +
(cinfo.output_scanline) * row_stride;
jpeg_read_scanlines(&cinfo, buffer_array, 1);
}
// 解压完了,将jpeg相关的资源释放掉
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
free(jpg_buffer);
// 将rgb_buffer中的数据,妥善地放到lcd上去显示
int lcd = open("/dev/fb0", O_RDWR);
// 获取LCD的屏幕参数信息
struct fb_var_screeninfo vinfo;
bzero(&vinfo, sizeof(vinfo));
ioctl(lcd, FBIOGET_VSCREENINFO, &vinfo);
int lcd_w = vinfo.xres;
int lcd_h = vinfo.yres;
int lcd_bpp = vinfo.bits_per_pixel;
char *p = mmap(NULL, lcd_w * lcd_h * lcd_bpp/8, PROT_READ|PROT_WRITE,
MAP_SHARED, lcd, 0);
int w = imageinfo.width;
int h = imageinfo.height;
for(int j=0; j<480 && j
相关编译操作看上面笔记.
结果图:
struct input_event {
struct timeval time; //时间戳
__u16 type; //事件类型
__u16 code; //事件代码(代号)
__s32 value; //事件的值
};
解释:
type:3 //事件类型EV_ABS (触摸屏事件)
code:0 //事件代码ABS_X (X轴坐标)
value:343 //值
type:3
code:1 //事件代码ABS_Y (Y轴坐标)
value:343
type:3
code:24 //事件代码ABS_PRESSURE(压力值)
value:200
type:1 //事件类型EV_KEY (按键事件)
code:330 //事件代码BTN_TOUCH (按钮)
value:1 //值按下
type:0 code:0 value:0 //同步
type:3 code:24 value:0
type:1 code:330 value:0
type:0 code:0 value:0
查找字串:strstr(str1, str2);
作用:判断str2是否是str1的子串
返回值:是返回子串首次出现的位置
否返NULL
---------------------------------------------------------------
练习:
1、获取触摸屏坐标(touch.c)
#include
#include
#include
#include
#include
#include
#include
#include
int main(void)
{
//打开触摸屏
int ts = open("/dev/input/event0", O_RDONLY);
if(ts == -1)
{
perror("打开触摸屏失败:");
exit(0);
}
struct input_event myevent;
bzero(&myevent, sizeof(myevent));
while(1)
{
read(ts, &myevent, sizeof(myevent));
if(myevent.type == EV_ABS)
{
if(myevent.code == ABS_X)
{
printf("X:%d ",myevent.value);
}
if(myevent.code == ABS_Y)
{
printf("Y:%d\n",myevent.value);
}
}
}
close(ts);
return 0;
}
编译操作:
虚拟机:
arm-none-linux-gnueabi -gcc touch.c -o touch
lcd开发板:
tftp -g 192.168.1.176 -r touch
chomd 777 touch
./touch
效果图:
存在bug。
修复bug:
#include
#include
#include
#include
#include
#include
#include
#include
#include
void get_xy(int *x, int *y)
{
//打开触摸屏
int ts = open("/dev/input/event0", O_RDONLY);
if(ts == -1)
{
perror("打开触摸屏失败:");
exit(0);
}
struct input_event myevent;
bzero(&myevent, sizeof(myevent));
bool x_flag = false;
bool y_flag = true;
while(1)
{
read(ts, &myevent, sizeof(myevent));
if(myevent.type == EV_ABS)
{
if(myevent.code==ABS_X && x_flag==false && y_flag == true)
{
*x = myevent.value;
x_flag = true;
y_flag = false;
}
if(myevent.code==ABS_Y && x_flag == true && y_flag == false)
{
*y = myevent.value;
x_flag = false;
y_flag = true;
}
if(myevent.code==ABS_PRESSURE && myevent.value==0)
break;
}
if(myevent.type==EV_KEY && myevent.code==BTN_TOUCH && myevent.value==0)
break;
}
//printf("X:%d Y:%d\n", *x, *y);
close(ts);
return ;
}
int main(void)
{
int x,y;
while(1)
{
get_xy(&x, &y);
printf("X:%d Y:%d\n", x,y);
}
return 0;
}
效果:
2、整合代码完成lcd触摸屏图片浏览功能:要求可浏览jpg、bmp图片(相册)
bmp.c
#include
#include "bmp.h"
/*初始化LCD屏*/
char *init_lcd(int lcd,struct fb_var_screeninfo *vinfo)
{
//获取LCD屏硬件参数
ioctl(lcd, FBIOGET_VSCREENINFO, vinfo);
//LCD屏像素点的位深
int bpp = vinfo->bits_per_pixel;
//LCD屏可显示的像素点个素(字节)
int screensize = vinfo->xres * vinfo->yres * bpp/8;
//给LCD申请显存
char *fbmem = mmap(NULL, screensize, PROT_READ|PROT_WRITE,MAP_SHARED, lcd, 0);
if(fbmem == MAP_FAILED)
{
perror("映射显存失败");
exit(0);
}
return fbmem;
}
/*获取bmp图片的rgb数据*/
char * load_bmp(const char *bmpfile, struct image_info *minfo)
{
//打开bmp图片
int fd = open(bmpfile, O_RDONLY);
if(fd == -1)
{
fprintf(stderr, "opening \"%s\" failed: %s\n",
bmpfile, strerror(errno));
exit(0);
}
// 获得文件大小,并分配内存
struct stat fileinfo;
fstat(fd, &fileinfo);
int rgb_size = fileinfo.st_size;
char *rgb_buf = calloc(1, rgb_size);
// 读取BMP内容到内存中
struct bitmap_header header;
struct bitmap_info info;
struct rgb_quad quad;
//读取文件头
read(fd, &header, sizeof(header));
//读取信息头
read(fd, &info, sizeof(info));
if(info.compression != 0)
{
read(fd, &quad, sizeof(quad));
fprintf(stderr, "read quad! \n");
}
//读取rgb数据
read(fd, rgb_buf, rgb_size);
//将bmp图片的宽度、高度和图片的色彩深度信息保存下来
minfo->width = info.width;
minfo->height= info.height;
minfo->pixel_size = info.bit_count/8;
close(fd);
return rgb_buf;
}
/*显示bmp图片*/
void display(char *bmpfile, //图片路径
struct fb_var_screeninfo *vinfo,//lcd硬件参数
int xoffset, //x轴的偏移量(相对于坐标原点)
int yoffset //y轴的偏移量
)
{
//打开LCD屏
int lcd = open("/dev/fb0", O_RDWR);
//初始化LCD屏
char *FB = init_lcd(lcd,vinfo);
//初始化保存图片信息的结构体
struct image_info *minfo = calloc(1, sizeof(struct image_info));
//获取图片的rgb数据
char *rgb_buf = load_bmp(bmpfile, minfo);
char *tmp = rgb_buf;
//清除屏幕(全屏显示白色)
int x, y, k = 0x00FFFFFFFF;
for(x=0; xyres; x++)
{
for(y=0; yxres; y++)
{
memcpy(FB+(4*y)+((vinfo->xres)*4*x), &k, 4);
}
}
// 从最后一行开始显示BMP图像
int pad = ((4-( minfo->width * minfo->pixel_size ) % 4)) % 4; // 0-3
rgb_buf += (minfo->width * minfo->pixel_size + pad) * (minfo->height-1);
//偏移映射区域(相对于坐标原点(0,0))
FB += (yoffset * vinfo->xres + xoffset) * 4;
//每一行显示的最大值
int lcd_w = vinfo->xres - xoffset;
//没列显示的最大值
int lcd_h = vinfo->yres - yoffset;
//开始显示
for(x=0; xheight; x++)
{
for(y=0; ywidth; y++)
{
unsigned long lcd_offset = (vinfo->xres*x + y) * 4;
memcpy(FB + lcd_offset + vinfo->red.offset/8, rgb_buf + 2, 1);
memcpy(FB + lcd_offset + vinfo->green.offset/8, rgb_buf + 1, 1);
memcpy(FB + lcd_offset + vinfo->blue.offset/8, rgb_buf + 0, 1);
rgb_buf += minfo->pixel_size;
}
rgb_buf += pad;
rgb_buf -= (minfo->width * minfo->pixel_size + pad) * 2;
}
/* 取消内存映射 */
munmap(FB, (vinfo->xres * vinfo->yres * vinfo->bits_per_pixel/8));
close(lcd);
free(tmp);
}
bmp.h
//
// Copyright(C), 2013-2017, GEC Tech. Co., Ltd.
//
// 文件: piano/inc/bmp.h
// 日期: 2017-9
// 描述: 处理BMP格式图像数据头文件
//
// 作者: Vincent Lin (林世霖) 微信公众号:秘籍酷
//
// 技术微店: http://weidian.com/?userid=260920190
// 技术交流: 260492823(QQ群)
//
#ifndef __BMP_H_
#define __BMP_H_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LCD "/dev/fb0"
struct bitmap_header
{
int16_t type;
int32_t size; // 图像文件大小
int16_t reserved1;
int16_t reserved2;
int32_t offbits; // bmp图像数据偏移量
}__attribute__((packed));
struct bitmap_info
{
int32_t size; // 本结构大小
int32_t width;
int32_t height;
int16_t planes; // 总为零
int16_t bit_count; // 色深
int32_t compression;
int32_t size_img; // bmp数据大小,必须是4的整数倍
int32_t X_pel;
int32_t Y_pel;
int32_t clrused;
int32_t clrImportant;
}__attribute__((packed));
struct rgb_quad
{
int8_t blue;
int8_t green;
int8_t red;
int8_t reserved;
}__attribute__((packed));
struct image_info
{
int width;
int height;
int pixel_size;
};
void display(char *bmpfile, struct fb_var_screeninfo *vinfo, int xoffset, int yoffset);
void draw_piano(char *FB, struct fb_var_screeninfo *vinfo, char *backgound);
char *load_bmp (const char *bmpfile, struct image_info *imginfo);
#endif
jpg.h
#ifndef __JPG_H_
#define __JPG_H_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "jpeglib.h"
struct imginfo
{
int width;
int height;
int pixel_size;
};
char *readjpg(char const *filename, int size);
int show_jpg(char *jpgpath);
#endif
show_jpg.c
#include "jpg.h"
char *readjpg(char const *filename, int size)
{
char *jpg_buffer = calloc(1, size);
// 不断读取JPG信息,放入jpg_buffer
int fd = open(filename, O_RDONLY);
int n = 0;
while(size > 0)
{
n = read(fd, jpg_buffer+n, size);
size -= n;
}
close(fd);
return jpg_buffer;
}
int show_jpg(char *jpgpath) // ./showjpg xxx.jpg
{
//获取文件属性
struct stat info;
bzero(&info, sizeof(info));
stat(jpgpath, &info);
//读取JPEG文件
char *jpg_buffer = readjpg(jpgpath, info.st_size);
// 声明解压缩结构体,以及错误管理结构体
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
// 使用缺省的出错处理来初始化解压缩结构体
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
// 配置该cinfo,使其从jpg_buffer中读取jpg_size个字节
// 这些数据必须是完整的JPEG数据
jpeg_mem_src(&cinfo, jpg_buffer, info.st_size);
// 读取JPEG文件的头,并判断其格式是否合法
int ret = jpeg_read_header(&cinfo, true);
if(ret != 1)
{
fprintf(stderr, "[%d]: jpeg_read_header failed: "
"%s\n", __LINE__, strerror(errno));
exit(1);
}
// 开始解压
jpeg_start_decompress(&cinfo);
// 将图片的宽、高、深度信息保存起来
struct imginfo imageinfo;
imageinfo.width = cinfo.output_width;
imageinfo.height = cinfo.output_height;
imageinfo.pixel_size = cinfo.output_components;
int row_stride = imageinfo.width * imageinfo.pixel_size;
// 根据图片的尺寸大小,分配一块相应的内存bmp_buffer
// 用来存放从jpg_buffer解压出来的图像数据
unsigned long rgb_size;
unsigned char *rgb_buffer;
rgb_size = imageinfo.width * imageinfo.height * imageinfo.pixel_size;
rgb_buffer = (unsigned char *)calloc(1, rgb_size);
// 循环地将图片的每一行读出并解压到bmp_buffer中
int line = 0;
while(cinfo.output_scanline < cinfo.output_height)
{
unsigned char *buffer_array[1];
buffer_array[0] = rgb_buffer +
(cinfo.output_scanline) * row_stride;
jpeg_read_scanlines(&cinfo, buffer_array, 1);
}
// 解压完了,将jpeg相关的资源释放掉
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
free(jpg_buffer);
// 将rgb_buffer中的数据,妥善地放到lcd上去显示
int lcd = open("/dev/fb0", O_RDWR);
// 获取LCD的屏幕参数信息
struct fb_var_screeninfo vinfo;
bzero(&vinfo, sizeof(vinfo));
ioctl(lcd, FBIOGET_VSCREENINFO, &vinfo);
int lcd_w = vinfo.xres;
int lcd_h = vinfo.yres;
int lcd_bpp = vinfo.bits_per_pixel;
char *p = mmap(NULL, lcd_w * lcd_h * lcd_bpp/8, PROT_READ|PROT_WRITE,
MAP_SHARED, lcd, 0);
int w = imageinfo.width;
int h = imageinfo.height;
for(int j=0; j<480 && j
touch.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "bmp.h"
#include "jpg.h"
/*初始化LCD屏*/
char *init_lcd(struct fb_var_screeninfo *vinfo)
{
//打开LCD屏
int lcd = open("/dev/fb0", O_RDWR);
//获取LCD屏硬件参数
ioctl(lcd, FBIOGET_VSCREENINFO, vinfo);
//LCD屏像素点的位深
int bpp = vinfo->bits_per_pixel;
//LCD屏可显示的像素点个素(字节)
int screensize = vinfo->xres * vinfo->yres * bpp/8;
//给LCD申请显存
char *fbmem = mmap(NULL, screensize, PROT_READ|PROT_WRITE,MAP_SHARED, lcd, 0);
if(fbmem == MAP_FAILED)
{
perror("映射显存失败");
exit(0);
}
return fbmem;
}
/*判断参数是否正确*/
void usage(int argc, char **argv)
{
if(argc != 4)
{
printf("参数不对\n");
exit(0);
}
}
int init_touch(void)
{
//打开触摸屏
int ts = open("/dev/input/event0", O_RDONLY);
if(ts == -1)
{
perror("打开触摸屏失败:");
exit(0);
}
return ts;
}
void get_xy(int ts, int *x, int *y)
{
struct input_event myevent;
bzero(&myevent, sizeof(myevent));
bool x_flag = false;
bool y_flag = true;
while(1)
{
read(ts, &myevent, sizeof(myevent));
if(myevent.type == EV_ABS)
{
if(myevent.code==ABS_X && x_flag==false && y_flag == true)
{
*x = myevent.value;
x_flag = true;
y_flag = false;
}
if(myevent.code==ABS_Y && x_flag == true && y_flag == false)
{
*y = myevent.value;
x_flag = false;
y_flag = true;
}
if(myevent.code==ABS_PRESSURE && myevent.value==0)
break;
}
if(myevent.type==EV_KEY && myevent.code==BTN_TOUCH && myevent.value==0)
break;
}
return ;
}
int main(int argc,char **argv)
// int main()
{
int x,y;
struct fb_var_screeninfo my_vinfo;
int i = 0;
// printf("size=%d",argc);
// //将图片路径存起来
// char *img_pathname[argc];
// for(int i=0;i0 && x<100 && y>160 && y<320)
{
//重置坐标,为下一次获取做准备
x = 0;
y = 0;
//计数
i--;
if(i<0)
i=4;
//显示
if(strstr(img_pathname[i],".bmp"))
{
display(img_pathname[i], //图片路径
p, //映射内存的地址
&my_vinfo, //lcd硬件参数
0, //x轴的偏移量(相对于坐标原点)
0 //y轴的偏移量
);
}
if(strstr(img_pathname[i],".jpg") || strstr(img_pathname[i],".jpeg"))
{
show_jpg(img_pathname[i]);
}
}
if(x>700 && x<800 && y>160 && y<320)
{
x = 0;
y = 0;
i++;
if(i>4)
i=0;
if(strstr(img_pathname[i],".bmp"))
{
display(img_pathname[i], //图片路径
p, //映射内存的地址
&my_vinfo, //lcd硬件参数
0, //x轴的偏移量(相对于坐标原点)
0 //y轴的偏移量
);
}
if(strstr(img_pathname[i],".jpg") || strstr(img_pathname[i],".jpeg"))
{
show_jpg(img_pathname[i]);
}
}
}
//关闭触摸屏
close(ts);
return 0;
}
编译过程:
虚拟机:
arm-none-linux-gnueabi-gcc *.c -o main -I/home/gec/jpeg/include -L/home/gec/jpeg/lib -ljpeg
打开tftp软件找到要执行软件的目录
LCD开发板:
tftp -g 192.168.1.181 -r main
chomd 777 main //修改权限
./main
注:实训第五天将实现LCD开发板连接摄像头实时显示:zigbee无线传感网实训---实现LCD开发板连接摄像头实时显示(The Fifth day)