嵌入式开发入门设计 —— 色盲检测系统

目录

前言

设计目标

搭建环境

1、虚拟机与 windows 挂载共享文件目录

2、搭建虚拟测试环境(屏幕)

3、启动虚拟测试环境(屏幕) 

Code

1、打开(bmp)图片(open函数)

2、读取(bmp)图片(read函数)

3、显示(bmp)图片(mmap函数)

1)打开 lcd 驱动              

2)内存映射mmap【*重点】

3)释放映射内存

4)关闭文件描述符

4、触摸屏

5、主函数

效果演示

结果演示

设计总结


前言

说到色盲,大家肯定会想到各种奇奇怪怪的图片,比如下图:

 可能你看来看去,也看不出来图片中有啥;可能你第一眼看过去就知道图片中有个图形,但它是三角形呢?还是圆形呢?还是方形呢?「可先在评论区留下你的答案」,等会在文末附上参考答案。

既然是嵌入式,当然少不了虚拟机了。我用的是VMare Workstation 16,使用的ubuntu 也是16。至于为什么都是16呢,当然与它的各项性能有关。下面正式进入正题。

设计目标

  1. 搭建基本的开发环境
  2. 实现基础的图片显示、切换
  3. 模拟现实触摸屏效果
  4. 计算测试结果,得出报告

搭建环境

测试测试,当然需要看到才能测试,所以先把测试环境给搭建起来。本次设计使用的屏幕是虚拟屏幕。至于为什么是虚拟屏幕,原因【¥】你懂我懂大家都懂。

1、虚拟机与 windows 挂载共享文件目录

 

 最后可别忘了在底下点个「确定」。

2、搭建虚拟测试环境(屏幕)

        进入共享文件夹:

 进入触摸屏模拟器文件夹:

分别对 event_drv 和 mmap_drv 文件中的object文件 进行清除【对上次编译】和编译 ,以及安装驱动:

清楚指令:make clean

编译指令:make

驱动安装指令:sudo insmod  xxx.ko

3、启动虚拟测试环境(屏幕) 

就会看到如下图的lcd模拟器:

 到此环境搭建、启动完成。接下来就交给程序猿了!

Code

有图才有测试,来先上图:

上图也没那么简单,不过也没有那么容易。

1、打开(bmp)图片(open函数)

  • bmp图片是没有经过压缩的图格式
  • bmp格式(24位图):文件信息头+图片信息头+像素点颜色码
  • 注意:bmp图片需要严格使用宽度是4的倍数(字节对齐)
    • 如果不是4的倍数,bmp图片储存时宽度会自动补齐为4的倍数
    • 如果不处理这个问题,图片会倾斜

<linux 提供了两个open函数,选其一即可>

int open(const char *path, int oflag, ...);
int openat(int fd, const char *path, int oflag, ...);//选用前者

        参数1:const char *path  —— 所需要打开的文件的路径以及文件名

                        比如:/test/a.bmp (根目录下的a.bmp文件)

        参数2: oflag -—— 文件的权限 —— 分为:可读、可写、可读可写等

        返回值:

                返回 -1 则说明文件不可被创建 or 修改。

代码如下:
int bmp_fd = open(bmp_name ,  O_RDWR);
	if(bmp_fd == -1){
		printf("open error %s\n ", bmp_name);
		return -1;
	}

2、读取(bmp)图片(read函数)

<与open函数一样,read函数同样也有两个>

ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset);
ssize_t read(int fildes, void *buf, size_t nbyte);//选用后者

参数1:fildes —— 所需要读取的文件描述符

参数2:void *buf —— 储存读取到的数据(地方)

参数3: size_t nbyte —— 储存多大字节的数据

代码如下:
//创建能储存bmp颜色数据的buf
unsigned char bmp_buf[800*480*3];
//每次使用前,先对buf的内容进行清空处理
bzero(bmp_buf ,sizeof(bmp_buf));
//读取数据到buf里面
read(bmp_fd , bmp_buf ,sizeof(bmp_buf));

3、显示(bmp)图片(mmap函数)

俗话说:“眼见为实,耳听为虚。”所以我们必须来点实际的,将图片在虚拟屏幕上展示出来。

1)打开 lcd 驱动              

int lcd_fd = open("/dev/ubuntu_lcd" ,  O_RDWR);    //驱动一般都在文件夹/dev/下
     if(lcd_fd == -1)
    {
	printf("open  lcd error!\n");
	return -1;
    }

2)内存映射mmap【*重点】

void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);

参数1:void *addr —— 映射内存的起始地址,一般写NULL,由内核自动分配       
参数2:size_t length  ——  映射内存的大小       
参数3:int prot  —— 从以下参数选择一个或者多个,多个使用 位或 | 操作

  •                PROT_EXEC  可执行权限.
  •                PROT_READ  可读权限.
  •                PROT_WRITE 可写权限.
  •                PROT_NONE  内存不可以操作.

参数4:int flags  —— 使用MAP_SHARED,用于共享映射内存
参数5:int fd ——  open函数打开的文件描述符
参数6:off_t offset —— 操作共享内存的偏移量,用于映射屏幕写0,不需要偏移

返回值:成功 —— 返回映射内存的起始地址
            失败 —— 返回MAP_FAILED (MAP_FAILED是(void *) -1地址),并写入出错码

unsigned int *mmap_fd = (unsigned int *)mmap(NULL , 800*480*4 , PROT_READ |  PROT_WRITE , MAP_SHARED  , lcd_fd , 0);
	for(y=0 ;y<480 ;y++)
	{
		for(x=0 ; x<800 ;x++)
		{         
			*(mmap_fd+x+y*800) =  bmp_buf[x*3+(479-y)*800*3]<< 0  | bmp_buf[3*x+1+(479-y)*800*3]<<8 | bmp_buf[3*x+2+(479-y)*800*3]<<16 ;  //分别对应着R B G 的位置
		}	
	}

3)释放映射内存

int munmap(void *addr, size_t length);

参数1:void *addr ——  需要释放的内存映射地址(mmap返回的地址)
参数2:size_t length ——  需要释放内存的大小

返回值:成功 —— 返回0
              失败 —— 返回-1,并写入出错码

//原码如下:
munmap(mmap_fd , 800*480*4);

4)关闭文件描述符

//原码如下:	
close(bmp_fd);
close(lcd_fd);

4、触摸屏

同样,触摸屏也是要打开的。

int ts_fd = open("/dev/ubuntu_event" ,  O_RDWR);
    if(ts_fd == -1)
    {
	printf("open ubuntu_event error !\n");
	return -1;
    }

但这里的触摸屏是根据触摸事件来判断的。但由于没有真正实感,只能通过鼠标点击。即通过捕抓x,y坐标进行判断当前事件。

#include  <linux/input.h>

struct input_event {
	struct timeval time;	// 事件发生的事件
	__u16 type;		// 事件的类型 用来区分不同的硬件设备 比如键盘/鼠标/触摸屏...
	__u16 code;		// 事件编码   比如键盘的某个按键 
	__s32 value;		// 事件的值   比如某个按键时按下还是松开 / 状态
};

/*
 * Event types
 */
#define EV_SYN            0x00     //事件的分割标志       
#define EV_KEY            0x01    //按键事件
#define EV_REL            0x02    //相对位移事件 鼠标
#define EV_ABS            0x03    //绝对位移事件 触摸屏

/*
 * Absolute axes
 */

#define ABS_X            0x00    // x轴
#define ABS_Y            0x01    // y轴

 注意:上面定义,都是linux的自带文件,路径: ../usr/include/linux/        input.h 或者 input-event-codes.h

        我们不仅仅捕抓一次的(x,y),所以需要加入一个循环,将每一次点击的(x,y)都存起来。然后根据xy的范围值进行判断,判断当前用户的操作。

struct input_event  data;	
//将触摸屏的信息读取到 data
printf("the touch screen was activited!\n");
while(1)
{	
	read( ts_fd , &data , sizeof(struct input_event));
	//判断是不是触摸屏事件
	if(data.type == 3 && data.code == 0)
	{
		//x轴的值
		*x = data.value;
		printf("x = %d\n" , *x);
	}else if(data.type == 3 && data.code == 1)
	{
		//y轴的值
		*y = data.value;
		printf("y = %d\n" , *y);
		if(x>0){
			break;
                }
	}		
}
	
//关闭触摸屏
close(ts_fd);

5、主函数

//部分原码:
int main()
{
	//初始化测试界面
	show_color();//白背景
	show_bmp("../picture/1.bmp" , 800 ,400 ,0 ,0);//题目一
	show_bmp("../picture/20.bmp" , 140 ,30 , 330 ,415);//下一题
	
	char auth_ans[3] = {'3', '3', '1'};//官方参考答案
	char usr_ans[3] = {0};//用户测试答案
	
/*
	i     //用来指示答案图片
	k     //用来指示下一道题
	j     //用来指示选择后跳转到下一道题
	b     //用来指示用户测试得到的答案
	a     //用来指示用户的测试结果比较
*/

	while(1){
		new_ts(&x,&y);
		if((280 < x && x < 330 ) && (360 < y && y <390)) //选择答案是第一个
		{	
			show_bmp(picture_an[i + 0] , 800 ,400 ,0 ,0);
			show_bmp(picture_next[j], 140 ,30 , 330 ,415);
			usr_ans[b] = '1';
			printf("==== %c\n",usr_ans[b]);
		}
		
		if((370 < x && x < 420 ) && (360 < y && y <390) )//选择答案是第二个
		{		
			show_bmp(picture_an[i + 1] , 800 ,400 ,0 ,0);
			show_bmp(picture_next[j], 140 ,30 , 330 ,415);
			usr_ans[b] = '2';
			printf("==== %c\n",usr_ans[b]);
		}
		
		if((460 < x && x < 500 ) && (360 < y && y <390)) //选择答案是第三个
		{			
			show_bmp(picture_an[i + 2], 800 ,400 ,0 ,0);
			show_bmp(picture_next[j], 140 ,30 , 330 ,415);
			usr_ans[b] = '3';
			printf("==== %c\n",usr_ans[b]);
		}
		if((330 < x && x < 470 ) && (415 < y && y < 445 ))
			{
				//切换到下一张图片测试
				if(k < 3)
				{
					show_bmp( picture_topic[k], 800 ,400 ,0 ,0);
					j++;
					show_bmp(picture_next[j], 140 ,30 , 330 ,415);
				}
					k++;
					
					i = i+3;
					j++;
					b++;
			}
				printf("k = %d\n",k);
		if(k  > 3 ){                                              //测试完成,打印结果
			show_bmp("../picture/done.bmp" , 800 ,480 ,0 ,0);
			printf("the test is done!!\n");
			break;
		}
		
	}
	return 0;
	
}

效果演示

初始界面

选择界面

 下一题

 

测试结束

结果演示

正常者

 色弱者

 色盲者

设计总结

本设计为很基础的嵌入式开发设计,所用的素材基本都是就地取材,如read、open等函数。设计中唯一较难理解的应该就是内存映射的那一部分了。因为一个像素点包含4个字节,分别对应A、R、B、G。一个字节对应者8位比特。所以需要将图片提取出来的像素值给它安排到对应的位置上比如G是最后提取的,即左移0位即可;而R是首先提取的,所以要将其左移16位。同时数据的先后问题,所以会出现显示图片倒置的现象,只需将其纵坐标的值对称交换即可。实现同样的效果的方法还有很多,比如HTML5,因为HTML5设计出来的色盲检测系统可能会更贴切实际感官。同时设计效果还可以多样化,比如给用户提示哪一道错误、增加返回上一道题目的功能等。enmm,大脑继续开发!

注意:本设计所有资源(lcd+源码)已上传,需要的自取

本设计中所用的资源,部分来源于网络,若侵权,联系删除。

写在最后,本文不代表任何人观点,若有不足之处,欢迎指正~

  • 12
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
### 回答1: 色盲是一种常见的视觉缺陷,影响了很多人的日常生活。为了帮助人们及时了解自己是否色盲,我们可以使用C语言来实现一个简单的色盲检测系统。 首先,我们需要收集一些色盲测试片,这些片上会有一些特定的案和颜色组合,可以帮助识别色盲。在程序中,我们可以将这些片存储为二维数组,每个像素点代表一个颜色。 接下来,我们需要实现一个功能,让用户输入自己看到的颜色,并进行判断是否为色盲。我们可以使用C语言中的控制语句和条件语句来实现该功能。通过遍历片的像素点,将用户输入的颜色与片中的颜色进行对比,判断用户是否识别正确。 为了提高判断的准确性,我们还可以引入一些色盲检测算法,比如色弱模拟算法。该算法可以将正常人识别的颜色转换为色盲人可以辨别的颜色,从而帮助色盲人更好地认识自己的视觉情况。 最后,我们可以根据用户的回答,输出一个结果,告诉用户是否为色盲。这个结果可以用文字描述,也可以用形化界面显示,更加直观地展示给用户。 通过以上的步骤,我们可以实现一个简单的色盲检测系统。用户可以通过该系统来了解自己是否色盲,并及时采取相应的措施,提高生活质量。当然,为了更加准确地进行色盲检测,我们还可以引入更多的色盲测试方法和算法,众多的研究和验证,以提供更准确的结果。 ### 回答2: 要实现色盲检测系统,可以使用C语言结合像处理技术来实现。 首先,需要使用C语言读取和处理像。可以使用C语言中的像处理库,如OpenCV,来读取像文件,并对像进行处理。通过读取每个像素的RGB值,可以获取像的颜色信息。 接下来,要实现色盲检测,可以使用颜色转换算法来将RGB颜色空间转换为其他颜色空间,如Lab颜色空间或HSV颜色空间。这些颜色空间将颜色信息分成不同的分量,使得检测色盲更加容易。 在转换为其他颜色空间后,可以通过计算颜色分量之间的差异来判断是否存在色盲。例如,对于红绿色盲,可以通过比较红色和绿色分量的值来判断。如果两者相差较小,就可能存在色盲。 最后,可以根据色盲检测的结果,给出相应的提示或建议,以帮助色盲者更好地区分颜色。可以使用C语言的控制语句和输出函数,如if语句和printf函数,根据检测结果输出相应的信息。 总之,要实现色盲检测系统,需要用C语言读取和处理像,通过颜色转换算法转换颜色空间,并通过计算颜色分量之间的差异来判断是否存在色盲。最后,根据检测结果输出相应的提示或建议。这样就可以用C语言实现一个简单的色盲检测系统

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值