前言
最近學(xué)校要求使用粵嵌的開發(fā)板實現(xiàn)電子相冊,具體的功能要有點擊特定的區(qū)域?qū)崿F(xiàn)上一張、下一張、自動播放圖片、黑屏退出應(yīng)用程序、左右滑動切換圖片相關(guān)功能。其中涉及到的知識點也比較多(文件IO、內(nèi)存映射、觸摸屏、bmp圖片格式、進(jìn)程、線程創(chuàng)建和同步、字符串操作等)。為理清思路和復(fù)習(xí)去年學(xué)的Linux C應(yīng)用編程知識,特寫下此文進(jìn)行回顧和總結(jié)。
先看看效果
粵嵌Linux GEC6818開發(fā)板實現(xiàn)電子相冊
整個工程文件和使用到的圖片在下方鏈接
門牙會稍息 / 粵嵌GEC 6818開發(fā)板實現(xiàn)簡易電子相冊和音樂播放器 · GitCode
一:內(nèi)存映射
存儲映射 I/O(memory-mapped I/O)是一種基于內(nèi)存區(qū)域的高級 I/O 操作,它能將一個文件映射到進(jìn)程地址空間中的一塊內(nèi)存區(qū)域中,當(dāng)從這段內(nèi)存中讀數(shù)據(jù)時,就相當(dāng)于讀文件中的數(shù)據(jù)(對文件進(jìn)行 read 操作),將數(shù)據(jù)寫入這段內(nèi)存時,則相當(dāng)于將數(shù)據(jù)直接寫入文件中(對文件進(jìn)行 write 操作)。用到的兩個函數(shù)是mmap和munmap,函數(shù)原型如下:
簡言之a(chǎn)ddr設(shè)置為NULL的話內(nèi)核會自動找一內(nèi)存空間,大小就是length,粵嵌的屏是800*480的,所以length就是800*480*4,4代表一個像素點由四字節(jié)構(gòu)成(ARGB),port參數(shù)設(shè)為PROT_READ、PROT_WRITE就是可讀可寫,flags描述的是映射區(qū)的屬性。
?二:BMP格式圖片
?用hexdump查看一下bmp圖片的數(shù)據(jù)(高度和寬度)
LCD屏顯示BMP格式圖片的函數(shù)
/**
*lcd屏顯示bmp格式圖片函數(shù)
*@param pathname 圖片名字
*@param x0 圖片在LCD上顯示的x起點坐標(biāo)
*@param y0 圖片在LCD上顯示的y起點坐標(biāo)
*@return 函數(shù)返回值
*/
int lcd_show_bmp(char *pathname, int x0, int y0)
{
//1、打開設(shè)備文件
int lcd = open("/dev/fb0", O_RDWR);
if(-1 == lcd)
{
printf("lcd open error!\n");
return -2;
}
//2、內(nèi)存映射
char *ptr = (char *)mmap(NULL, 800*480*4, PROT_READ|PROT_WRITE,
MAP_SHARED, lcd, 0);
if(NULL == ptr)
{
printf("mmap error!\n");
return -3;
}
//清除背景色
//bzero(ptr, 800*480*4);
//打開圖片
int bmp = open(pathname, O_RDWR);
if(-1 == bmp)
{
printf("bmp open error!\n");
return -2;
}
//讀取圖片相關(guān)信息
int bmp_size, bmp_width, bmp_height;
//大小
lseek(bmp, 2, SEEK_SET);
read(bmp, &bmp_size, sizeof(bmp_size));
//寬度和高度
lseek(bmp, 18, SEEK_SET);
read(bmp, &bmp_width, sizeof(bmp_width));
read(bmp, &bmp_height, sizeof(bmp_height));
//讀取圖片的顏色數(shù)據(jù)
lseek(bmp, 54, SEEK_SET);
char *bmp_buf = malloc(bmp_size); //申請空間
read(bmp, bmp_buf, bmp_width*bmp_height*3);
//對顏色數(shù)據(jù)進(jìn)行處理(上下顛倒以及數(shù)據(jù)混亂)
int bmp_sum = 0;
int lcd_sum = 0;
for(int y=0; y+y0<480 && y<bmp_height; y++)
{
for(int x=0; x+x0<800 && x<bmp_width; x++)
{
bmp_sum = 3*(x+((bmp_height-1-y)*bmp_width));
lcd_sum = 4*(800*(y+y0)+x+x0);
//等號的坐標(biāo)屬于lcd屏幕, 等號的右邊是bmp圖像數(shù)據(jù)
ptr[lcd_sum+0] = bmp_buf[bmp_sum+0]; //藍(lán)色數(shù)據(jù)
ptr[lcd_sum+1] = bmp_buf[bmp_sum+1]; //綠色數(shù)據(jù)
ptr[lcd_sum+2] = bmp_buf[bmp_sum+2]; //紅色數(shù)據(jù)
ptr[lcd_sum+3] = bmp_buf[bmp_sum+3]; //透明度數(shù)據(jù)
}
//usleep(1);
}
//釋放相關(guān)資源
munmap(ptr, 800*480*4);
free(bmp_buf);
//3、關(guān)閉文件
close(lcd);
close(bmp);
}
?三:input_event接收觸摸屏上報值
用hexdump命令查看觸摸屏上報值
?此實驗用到input子系統(tǒng)中的type、code(下面的圖片來源Linux內(nèi)核中的input.h文件)
獲取觸摸屏坐標(biāo)返回值函數(shù)
/**
*獲取觸摸屏坐標(biāo)返回值函數(shù)
*param NULL
*return NULL
*/
void get_touch()
{
//打開設(shè)備文件
int touch_fd = open("/dev/input/event0", O_RDONLY);
if(-1 == touch_fd)
{
printf("event0 open error!\n");
}
while(1)
{
read(touch_fd, &ts, sizeof(ts));
//獲取X、Y坐標(biāo)
if(EV_ABS == ts.type) //判斷是否為觸摸屏事件
{
if(ABS_X == ts.code)//判斷是否為x軸數(shù)據(jù)
{
ts_x = ts.value;
flag_x = 1;
}
else if(ABS_Y == ts.code) //判斷是否為y軸數(shù)據(jù)
{
ts_y = ts.value;
flag_y = 1;
}
}
if(EV_KEY == ts.type)
{
//剛觸碰的坐標(biāo)/長按時
if(ts.code == BTN_TOUCH && ts.value == 1)
{
old_x = ts_x;
first_press_flag = 1;
}
}
if(flag_x == 1 && flag_y == 1 && first_press_flag == 1)
{
flag_x = 0;
flag_y = 0;
pthread_mutex_lock(&mutex);
flag_x_y = 1;
//黑色底板才需要執(zhí)行如下操作
ts_x = ts_x*800/1024;
ts_y = ts_y*480/600;
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond);
//break;
}
if(EV_KEY == ts.type)
{
//松開
if(ts.code == BTN_TOUCH && ts.value == 0)
{
//從左到右的滑動
if(((ts_x > old_x) && (ts_x - old_x > 150) && (ts_x < 600)))
{
right_left_slide_flag = 1;
slider_right = 1;
old_x = 300;
}
//從右到左的滑動
else if(((old_x > ts_x) && (old_x - ts_x > 150) && (old_x < 600)))
{
right_left_slide_flag = 1;
slider_left = 1;
old_x = 300;
}
}
}
}
//4、關(guān)閉文件
close(touch_fd);
}
四:創(chuàng)建線程,處理觸摸屏坐標(biāo)數(shù)據(jù)
阻塞式 I/O 的優(yōu)點在于能夠提升 CPU 的處理效率,當(dāng)自身條件不滿足時,進(jìn)入阻塞狀態(tài),交出 CPU資源,將 CPU 資源讓給別人使用;而非阻塞式則是抓緊利用 CPU 資源,譬如不斷地去輪訓(xùn),這樣就會導(dǎo)致該程序占用了非常高的 CPU 使用率!我這里是想獲得觸摸點坐標(biāo)之后再做相關(guān)的操作,當(dāng)沒有按下觸摸屏的時候,相關(guān)線程就會阻塞掛起,節(jié)約資源,線程同步中使用互斥鎖和條件變量就可以實現(xiàn)。
左右滑動線程處理函數(shù):
void *right_left_slide_func(void *arg)
{
printf("enter right_left_slide_func\r\n");
while(1){
if(right_left_slide_flag == 1){
if(slider_left == 1){
touch_flag--;
if(touch_flag <= 0)
touch_flag = BMP_MAX_NUMBER;
lcd_show_bmp(bmp_path[touch_flag - 1], 0, 0);
slider_left = 0;
right_left_slide_flag == 0;
}
else if(slider_right == 1){
touch_flag++;
if(touch_flag > BMP_MAX_NUMBER)
touch_flag = 1;
lcd_show_bmp(bmp_path[touch_flag - 1], 0, 0);
slider_right = 0;
right_left_slide_flag == 0;
}
right_left_slide_flag == 0;
}
}
}
右側(cè)選項框線程處理函數(shù):
void *area_switch(void *arg)
{
printf("enter area_switch\r\n");
while(1){
pthread_mutex_lock(&mutex);
while(flag_x_y == 0)
pthread_cond_wait(&cond, &mutex);
while(flag_x_y == 1){
//上一張
if((ts_y > 0 && ts_y < 120) && (ts_x > 600))
{
touch_flag--;
if(touch_flag <= 0)
touch_flag = BMP_MAX_NUMBER;
lcd_show_bmp(bmp_path[touch_flag - 1], 0, 0);
ts_x = 0;
ts_y = 0;
}
//下一張
else if((ts_y > 120 && ts_y < 240) && (ts_x > 600))
{
touch_flag++;
if(touch_flag > BMP_MAX_NUMBER)
touch_flag = 1;
lcd_show_bmp(bmp_path[touch_flag - 1], 0, 0);
ts_x = 0;
ts_y = 0;
}
//幻燈片
else if((ts_y > 240 && ts_y < 360) && (ts_x > 600))
{
printf("click slider photo\r\n");
if(slider_flag == 0){
slider_flag = 1;
}
else{
slider_flag = 0;
}
printf("slider_flag = %d\r\n", slider_flag);
ts_x = 0;
ts_y = 0;
}
//息屏
else if((ts_y > 360 && ts_y < 480) && (ts_x > 600))
{
char command[] = "kill -9 ";
char str[10];
sprintf(str, "%d", pid);
strcat(command, str);
printf("command = %s\r\n", command);
//方式一:顯示一張黑色的圖片
lcd_show_bmp("black.bmp", 0, 0);
system(command);
}
else{
flag_x_y = 0;
break;
}
flag_x_y = 0;
}
pthread_mutex_unlock(&mutex);
}
}
?自動播放圖片線程處理函數(shù):
void *slider_func(void *arg)
{
printf("enter slider_func : %d\r\n", pthread_self());
while(1){
if(slider_flag == 1){
printf("enter slider_func\r\n");
touch_flag++;
if(touch_flag > BMP_MAX_NUMBER)
touch_flag = 1;
lcd_show_bmp(bmp_path[touch_flag - 1], 0, 0);
sleep(1);
}
}
}
五:Main函數(shù)
main函數(shù)就是一些線程、互斥鎖、條件變量的創(chuàng)建和回收文章來源:http://www.zghlxwxcb.cn/news/detail-756678.html
int main()
{
lcd_show_bmp("choice.bmp", 600, 0);
lcd_show_bmp(bmp_path[touch_flag - 1], 0, 0);
pthread_t tid, tid_area_switch, tid_right_left_slide;
int ret = 0;
pthread_create(&tid, NULL, slider_func, NULL);
pthread_create(&tid_area_switch, NULL, area_switch, NULL);
pthread_create(&tid_right_left_slide, NULL, right_left_slide_func, NULL);
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
printf("main thread = %ld\r\n", pthread_self());
pid = getpid();
printf("pid = %d\r\n", pid);
while(1){
get_touch();
}
pthread_join(tid, NULL);
pthread_join(tid_area_switch, NULL);
pthread_join(tid_right_left_slide, NULL);
pthread_cond_destroy(&cond);
exit(0);
}
六:完整代碼
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <linux/input.h>
#include <pthread.h>
#include <unistd.h>
#define BMP_MAX_NUMBER 4
char bmp_path[4][100] = {"1.1.bmp", "1.2.bmp", "1.3.bmp", "1.4.bmp"};
int slider_flag = 0;
pid_t pid;
pthread_mutex_t mutex;
pthread_cond_t cond;
//自定義函數(shù):LCD屏幕顯示bmp圖片
//pathname:需要打開的圖片路徑
//x0:存放圖片顯示的x軸起點
//y0:存放圖片顯示的y軸起點
int lcd_show_bmp(char *pathname, int x0, int y0)
{
//1、打開設(shè)備文件
int lcd = open("/dev/fb0", O_RDWR);
if(-1 == lcd)
{
printf("lcd open error!\n");
return -2;
}
//2、內(nèi)存映射
char *ptr = (char *)mmap(NULL, 800*480*4, PROT_READ|PROT_WRITE,
MAP_SHARED, lcd, 0);
if(NULL == ptr)
{
printf("mmap error!\n");
return -3;
}
//清除背景色
//bzero(ptr, 800*480*4);
//打開圖片
int bmp = open(pathname, O_RDWR);
if(-1 == bmp)
{
printf("bmp open error!\n");
return -2;
}
//讀取圖片相關(guān)信息
int bmp_size, bmp_width, bmp_height;
//大小
lseek(bmp, 2, SEEK_SET);
read(bmp, &bmp_size, sizeof(bmp_size));
//寬度和高度
lseek(bmp, 18, SEEK_SET);
read(bmp, &bmp_width, sizeof(bmp_width));
read(bmp, &bmp_height, sizeof(bmp_height));
//讀取圖片的顏色數(shù)據(jù)
lseek(bmp, 54, SEEK_SET);
char *bmp_buf = malloc(bmp_size); //申請空間
read(bmp, bmp_buf, bmp_width*bmp_height*3);
//對顏色數(shù)據(jù)進(jìn)行處理(上下顛倒以及數(shù)據(jù)混亂)
int bmp_sum = 0;
int lcd_sum = 0;
for(int y=0; y+y0<480 && y<bmp_height; y++)
{
for(int x=0; x+x0<800 && x<bmp_width; x++)
{
bmp_sum = 3*(x+((bmp_height-1-y)*bmp_width));
lcd_sum = 4*(800*(y+y0)+x+x0);
//等號的坐標(biāo)屬于lcd屏幕, 等號的右邊是bmp圖像數(shù)據(jù)
ptr[lcd_sum+0] = bmp_buf[bmp_sum+0]; //藍(lán)色數(shù)據(jù)
ptr[lcd_sum+1] = bmp_buf[bmp_sum+1]; //綠色數(shù)據(jù)
ptr[lcd_sum+2] = bmp_buf[bmp_sum+2]; //紅色數(shù)據(jù)
ptr[lcd_sum+3] = bmp_buf[bmp_sum+3]; //透明度數(shù)據(jù)
}
//usleep(1);
}
munmap(ptr, 800*480*4);
free(bmp_buf);
//3、關(guān)閉文件
close(lcd);
close(bmp);
}
int touch_flag = 1;
int ts_x, ts_y;
int old_x = 300;
int slider_left = 0;
int slider_right = 0;
int flag_x_y = 0;
int right_left_slide_flag = 0;
struct input_event ts;
int flag_x = 0, flag_y = 0, first_press_flag = 0;
//獲取觸摸屏坐標(biāo)
//自定義函數(shù):獲取觸摸屏的坐標(biāo)
void get_touch()
{
//1、打開設(shè)備文件
int touch_fd = open("/dev/input/event0", O_RDONLY);
if(-1 == touch_fd)
{
printf("event0 open error!\n");
}
while(1)
{
read(touch_fd, &ts, sizeof(ts));
//3、分析數(shù)據(jù)
if(EV_ABS == ts.type) //判斷是否為觸摸屏事件
{
if(ABS_X == ts.code)//判斷是否為x軸數(shù)據(jù)
{
ts_x = ts.value;
flag_x = 1;
}
else if(ABS_Y == ts.code) //判斷是否為y軸數(shù)據(jù)
{
ts_y = ts.value;
flag_y = 1;
}
}
if(EV_KEY == ts.type)
{
//剛觸碰的坐標(biāo)/長按時
if(ts.code == BTN_TOUCH && ts.value == 1)
{
old_x = ts_x;
first_press_flag = 1;
}
}
if(flag_x == 1 && flag_y == 1 && first_press_flag == 1)
{
flag_x = 0;
flag_y = 0;
pthread_mutex_lock(&mutex);
flag_x_y = 1;
//黑色底板才需要執(zhí)行如下操作
ts_x = ts_x*800/1024;
ts_y = ts_y*480/600;
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond);
//break;
}
if(EV_KEY == ts.type)
{
//松開
if(ts.code == BTN_TOUCH && ts.value == 0)
{
//從左到右的滑動
if(((ts_x > old_x) && (ts_x - old_x > 150) && (ts_x < 600)))
{
right_left_slide_flag = 1;
slider_right = 1;
old_x = 300;
}
//從右到左的滑動
else if(((old_x > ts_x) && (old_x - ts_x > 150) && (old_x < 600)))
{
right_left_slide_flag = 1;
slider_left = 1;
old_x = 300;
}
}
}
}
//4、關(guān)閉文件
close(touch_fd);
}
void *right_left_slide_func(void *arg)
{
printf("enter right_left_slide_func\r\n");
while(1){
if(right_left_slide_flag == 1){
if(slider_left == 1){
touch_flag--;
if(touch_flag <= 0)
touch_flag = BMP_MAX_NUMBER;
lcd_show_bmp(bmp_path[touch_flag - 1], 0, 0);
slider_left = 0;
right_left_slide_flag == 0;
}
else if(slider_right == 1){
touch_flag++;
if(touch_flag > BMP_MAX_NUMBER)
touch_flag = 1;
lcd_show_bmp(bmp_path[touch_flag - 1], 0, 0);
slider_right = 0;
right_left_slide_flag == 0;
}
right_left_slide_flag == 0;
}
}
}
void *area_switch(void *arg)
{
printf("enter area_switch\r\n");
while(1){
pthread_mutex_lock(&mutex);
while(flag_x_y == 0)
pthread_cond_wait(&cond, &mutex);
while(flag_x_y == 1){
//上一張
if((ts_y > 0 && ts_y < 120) && (ts_x > 600))
{
touch_flag--;
if(touch_flag <= 0)
touch_flag = BMP_MAX_NUMBER;
lcd_show_bmp(bmp_path[touch_flag - 1], 0, 0);
ts_x = 0;
ts_y = 0;
}
//下一張
else if((ts_y > 120 && ts_y < 240) && (ts_x > 600))
{
touch_flag++;
if(touch_flag > BMP_MAX_NUMBER)
touch_flag = 1;
lcd_show_bmp(bmp_path[touch_flag - 1], 0, 0);
ts_x = 0;
ts_y = 0;
}
//幻燈片
else if((ts_y > 240 && ts_y < 360) && (ts_x > 600))
{
printf("click slider photo\r\n");
if(slider_flag == 0){
slider_flag = 1;
}
else{
slider_flag = 0;
}
printf("slider_flag = %d\r\n", slider_flag);
ts_x = 0;
ts_y = 0;
}
//息屏
else if((ts_y > 360 && ts_y < 480) && (ts_x > 600))
{
char command[] = "kill -9 ";
char str[10];
sprintf(str, "%d", pid);
strcat(command, str);
printf("command = %s\r\n", command);
//方式一:顯示一張黑色的圖片
lcd_show_bmp("black.bmp", 0, 0);
system(command);
}
else{
flag_x_y = 0;
break;
}
flag_x_y = 0;
}
pthread_mutex_unlock(&mutex);
}
}
void *slider_func(void *arg)
{
printf("enter slider_func : %d\r\n", pthread_self());
while(1){
if(slider_flag == 1){
printf("enter slider_func\r\n");
touch_flag++;
if(touch_flag > BMP_MAX_NUMBER)
touch_flag = 1;
lcd_show_bmp(bmp_path[touch_flag - 1], 0, 0);
sleep(1);
}
}
}
int main()
{
lcd_show_bmp("choice.bmp", 600, 0);
lcd_show_bmp(bmp_path[touch_flag - 1], 0, 0);
pthread_t tid, tid_area_switch, tid_right_left_slide;
int ret = 0;
pthread_create(&tid, NULL, slider_func, NULL);
pthread_create(&tid_area_switch, NULL, area_switch, NULL);
pthread_create(&tid_right_left_slide, NULL, right_left_slide_func, NULL);
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
printf("main thread = %ld\r\n", pthread_self());
pid = getpid();
printf("pid = %d\r\n", pid);
while(1){
get_touch();
}
pthread_join(tid, NULL);
pthread_join(tid_area_switch, NULL);
pthread_join(tid_right_left_slide, NULL);
pthread_cond_destroy(&cond);
exit(0);
}
總結(jié)
以上就是本文的全部內(nèi)容, 希望能夠幫助到你。文章來源地址http://www.zghlxwxcb.cn/news/detail-756678.html
到了這里,關(guān)于粵嵌Linux GEC6818開發(fā)板實現(xiàn)電子相冊的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!