0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Linux驱动开发_倒车影像项目介绍

DS小龙哥-嵌入式技术 2022-09-17 15:47 次阅读

【摘要】 介绍Linux下倒车影像的项目,完成摄像头图像读取、超声波驱动编写、超声波距离读取。

倒车影像项目

模拟: 汽车中控台---倒车影像。

组成部分:

1.​LCD屏: 实时显示摄像头采集的数据。

2.​摄像头: 放在车尾,采集图像传输给LCD屏进行显示。

3.​倒车雷达: 超声波测距--->测量车尾距离障碍物的距离。

4.​蜂鸣器: 根据倒车雷达测量的距离,控制频率。

poYBAGMlOu-AOBVRAAAL-jj6-Tw222.png

1.1 超声波测距模块

声波测距: 已知声音在空气中传播的速度。

pYYBAGMlOu-AFmuxAAAmOEe-tck976.pngpoYBAGMlOu-AJBBaAAJM_tHZFyQ036.pngpYYBAGMlOvCAKLPrAAdGSZnRjh0757.pngpoYBAGMlOvGAEFCHAAE1wfYJhgU050.png

​硬件接线:

poYBAGMlOvWAAQJpAA3s6Fdq0JA662.png

ECHO------->GPX1_0 (开发板第9个IO口): 中断引脚----->检测回波----输入

TRIG ------->GPB_7 (开发板第8个IO口): 输出触发信号

1.2 PWM方波控制蜂鸣器

​PWM方波:

pYYBAGMlOvWAaetGAAANcrhJPCc509.pngpYYBAGMlOviAP6BdAAwht2kb2FQ071.pngpoYBAGMlOvqAIdZzAAe3isRutmE616.png

​内核自带的PWM方波驱动

pYYBAGMlOvuAVshAAACvomxi1OI945.png

1.3 UVC免驱摄像头编程框架: V4L2

编程的框架: v4l2--->全称: video4linux2

V4L2 : 针对UVC免驱USB设备设计框架。专用于USB摄像头的数据采集。

免驱 : 驱动已经成为标准,属于内核自带源码的一部分。

V4L2框架本身注册的也是字符设备,设备节点: /dev/videoX

V4L2 框架: 提供ioctl接口,提供了有很多命令,可以通过这些命令对摄像头做配置。

比如: 输出的图像尺寸,输出图像格式(RGB、YUV、JPG),申请采集数据的缓冲区。

poYBAGMlOvuAKskKAAEWG4P52zg183.pngpYYBAGMlOvuAWkiOAACnVBFUccw164.png

​配置摄像头采集队列步骤:

poYBAGMlOvuALkJwAACbwCG0l5Y794.png

Mmap函数映射。

超声波驱动读取距离:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

static unsigned int distance_irq; /*存放中断号*/
static u32 *GPB_DAT=NULL;
static u32 *GPB_CON=NULL;

static u32 distance_time_us=0; /*表示距离的时间*/

/*
工作队列处理函数: 
*/
static void distance_work_func(struct work_struct *work)
{
	u32 time1,time2;
	time1=ktime_to_us(ktime_get()); /*获取当前时间,再转换为 us 单位*/

	/*等待高电平时间结束*/
	while(gpio_get_value(EXYNOS4_GPX1(0))){}
	
	time2=ktime_to_us(ktime_get()); /*获取当前时间,再转换为 us 单位*/

	distance_time_us=time2-time1;
	//printk("us=%d\n",time2-time1);   /*us/58=厘米*/
}

/*静态方式初始化工作队列*/
static DECLARE_WORK(distance_work,distance_work_func);

/*
中断处理函数: 用于检测超声波测距的回波
*/
static irqreturn_t distance_handler(int irq, void *dev)
{
	/*调度工作队列*/
	schedule_work(&distance_work);
	return IRQ_HANDLED;
}

static void distance_function(unsigned long data);
/*静态方式定义内核定时器*/
static DEFINE_TIMER(distance_timer,distance_function,0,0);

/*内核定时器超时处理函数: 触发超声波发送方波*/
static void distance_function(unsigned long data)
{
	static u8 state=0;
	state=!state;
	
	/*更改GPIO口电平*/
	if(state)
	{
		*GPB_DAT|=1<<7;
	}
	else
	{
		*GPB_DAT&=~(1<<7);
	}
	
	/*修改定时器的超时时间*/
	mod_timer(&distance_timer,jiffies+msecs_to_jiffies(100));
}

static int distance_open(struct inode *inode, struct file *file)
{
	return 0;
}

#define GET_US_TIME 0x45612
static long distance_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long argv)
{
	u32 *us_data=(u32*)argv;
	int err;
	u32 time_us=distance_time_us;
	switch(cmd)
	{
		case GET_US_TIME:
			err=copy_to_user(us_data,&time_us,4);
			if(err!=0)printk("拷贝失败!\n");
			break;
	}
	return 0;
}

static int distance_release(struct inode *inode, struct file *file)
{
	return 0;
}

/*定义文件操作集合*/
static struct file_operations distance_fops=
{
	.open=distance_open,
	.unlocked_ioctl=distance_unlocked_ioctl,
	.release=distance_release
};
/*定义杂项设备结构体*/
static struct miscdevice distance_misc=
{
	.minor=MISC_DYNAMIC_MINOR,
	.name="tiny4412_distance",
	.fops=&distance_fops
};

static int __init tiny4412_distance_dev_init(void) 
{
	int err;
	/*1. 映射GPIO口地址*/
	GPB_DAT=ioremap(0x11400044,4);
	GPB_CON=ioremap(0x11400040,4);

	*GPB_CON&=~(0xF<<4*7);
	*GPB_CON|=0x1<<4*7; /*配置输出模式*/
	
	/*2. 根据GPIO口编号,获取中断号*/
	distance_irq=gpio_to_irq(EXYNOS4_GPX1(0));
	
	/*3. 注册中断*/
	err=request_irq(distance_irq,distance_handler,IRQ_TYPE_EDGE_RISING,"distance_device",NULL);
	if(err!=0)printk("中断注册失败!\n");
	else printk("中断:超声波测距驱动安装成功!\n");

	/*4. 修改定时器超时时间*/
	mod_timer(&distance_timer,jiffies+msecs_to_jiffies(100));

	/*杂项设备注册*/
	misc_register(&distance_misc);
	return 0;
}

static void __exit tiny4412_distance_dev_exit(void) 
{
	/*5. 注销中断*/
	free_irq(distance_irq,NULL);

	/*6. 停止定时器*/
	del_timer(&distance_timer);
	
	/*7. 取消IO映射*/
	iounmap(GPB_DAT);
	iounmap(GPB_CON);

	/*注销杂项设备*/
	misc_deregister(&distance_misc);
	
	printk("中断:超声波测距驱动卸载成功!\n");
}

module_init(tiny4412_distance_dev_init);
module_exit(tiny4412_distance_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("tiny4412 wbyq");

摄像头代码,读取摄像头画面:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "framebuffer.h"

#define PWM_DEVICE "/dev/pwm"  /*PWM方波设备文件*/
#define DISTANCE_DEVICE "/dev/tiny4412_distance" /*超声波测距设备文件*/
#define UVC_VIDEO_DEVICE "/dev/video15"  /*UVC摄像头设备节点*/

#define GET_US_TIME 0x45612     /*获取超声波测量的距离: ioctl命令*/
#define PWM_IOCTL_SET_FREQ		1 /*控制PWM方波频率: ioctl命令*/
#define PWM_IOCTL_STOP			0 /*停止PWM方波输出: ioctl命令*/

int distance_fd;  /*超声波设备的文件描述符*/
int pwm_fd;       /*PWM方波设备的文件描述符*/
int uvc_video_fd; /*UVC摄像头设备文件描述符*/
int Image_Width;  /*图像的宽度*/
int Image_Height; /*图像的高度*/
unsigned char *video_memaddr_buffer[4]; /*存放摄像头映射到进程空间的缓冲区地址*/

/*
函数功能: 用户终止了进程调用
*/
void exit_sighandler(int sig)
{
	//停止PWM波形输出,关闭蜂鸣器
	ioctl(pwm_fd,PWM_IOCTL_STOP,0);
	close(pwm_fd);
	close(distance_fd);
	exit(1);
}

/*
函数功能: 读取超声波数据的线程
*/
void *distance_Getpthread_func(void *dev)
{
	/*1. 打开PWM方波驱动*/
	pwm_fd=open(PWM_DEVICE,O_RDWR);
	if(pwm_fd<0) //0 1 2
	{
		printf("%s 设备文件打开失败\n",PWM_DEVICE);
		/*退出线程*/
		pthread_exit(NULL); 
	}

	/*2. 打开超声波测距设备*/
	distance_fd=open(DISTANCE_DEVICE,O_RDWR);
	if(distance_fd<0) //0 1 2
	{
		printf("%s 设备文件打开失败\n",DISTANCE_DEVICE);
		/*退出线程*/
		pthread_exit(NULL); 
	}

	/*3. 循环读取超声波测量的距离*/
	struct pollfd fds;
	fds.fd=distance_fd;
	fds.events=POLLIN;
	int data;
	while(1)
	{
		poll(&fds,1,-1);
		ioctl(distance_fd,GET_US_TIME,&data);
		printf("距离(cm):%0.2f\n",data/58.0);
		data=data/58;
		if(data>200) /*200厘米: 安全区域*/
		{
			//停止PWM波形输出,关闭蜂鸣器
			ioctl(pwm_fd,PWM_IOCTL_STOP,0);
		}
		else if(data>100) /*100厘米: 警告区域*/
		{
			printf("警告区域!\n");
			ioctl(pwm_fd,PWM_IOCTL_SET_FREQ,2);
		}
		else /*小于<100厘米: 危险区域*/
		{
			printf(" 危险区域!\n");
			ioctl(pwm_fd,PWM_IOCTL_SET_FREQ,10);
		}
		
		//ioctl(pwm_fd,PWM_IOCTL_SET_FREQ,pwm_data);
		/*倒车影像: 测距有3个档位*/
	}
}
/*
函数功能: UVC摄像头初始化
返回值: 0表示成功
*/
int UVCvideoInit(void)
{
	/*1. 打开摄像头设备*/
	uvc_video_fd=open(UVC_VIDEO_DEVICE,O_RDWR);
	if(uvc_video_fd<0)
	{
		printf("%s 摄像头设备打开失败!\n",UVC_VIDEO_DEVICE);
		return -1;
	}
	
	/*2. 设置摄像头的属性*/
	struct v4l2_format format;
	memset(&format,0,sizeof(struct v4l2_format));
	format.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; /*表示视频捕获设备*/
	format.fmt.pix.width=800;  /*预设的宽度*/
	format.fmt.pix.height=480; /*预设的高度*/
	format.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV; /*预设的格式*/
	format.fmt.pix.field=V4L2_FIELD_ANY; /*系统自动设置: 帧属性*/
	if(ioctl(uvc_video_fd,VIDIOC_S_FMT,&format)) /*设置摄像头的属性*/
	{
		printf("摄像头格式设置失败!\n");
		return -2;
	}
	
	Image_Width=format.fmt.pix.width;
	Image_Height=format.fmt.pix.height;
		
	printf("摄像头实际输出的图像尺寸:x=%d,y=%d\n",format.fmt.pix.width,format.fmt.pix.height);
	if(format.fmt.pix.pixelformat==V4L2_PIX_FMT_YUYV)
	{
		printf("当前摄像头支持YUV格式图像输出!\n");
	}
	else
	{
		printf("当前摄像头不支持YUV格式图像输出!\n");
		return -3;
	}

	/*3. 请求缓冲区: 申请摄像头数据采集的缓冲区*/
	struct v4l2_requestbuffers req_buff;
	memset(&req_buff,0,sizeof(struct v4l2_requestbuffers));
	req_buff.count=4; /*预设要申请4个缓冲区*/
	req_buff.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; /*视频捕获设备*/
	req_buff.memory=V4L2_MEMORY_MMAP; /*支持mmap内存映射*/
	if(ioctl(uvc_video_fd,VIDIOC_REQBUFS,&req_buff)) /*申请缓冲区*/
	{
		printf("申请摄像头数据采集的缓冲区失败!\n");
		return -4;
	}
	printf("摄像头缓冲区申请的数量: %d\n",req_buff.count);

	/*4. 获取缓冲区的详细信息: 地址,编号*/
	struct v4l2_buffer buff_info;
	memset(&buff_info,0,sizeof(struct v4l2_buffer));
	int i;
	for(i=0;i> 8;
		g = (y - (88 * u) - (183 * v)) >> 8;
		b = (y + (454 * u)) >> 8;

		*(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r);
		*(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g);
		*(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b);
			
		if(z++)
		{
			z = 0;
			yuyv += 4;
		}
	}
}

int main(int argc,char **argv)
{
	int data;
	
	/*1. 注册将要捕获的信号*/
	signal(SIGINT,exit_sighandler);

	/*2. 创建线程: 采集超声波测量的距离*/
	pthread_t threadID;
	pthread_create(&threadID,NULL,distance_Getpthread_func,NULL);
	pthread_detach(threadID); //设置分离属性

	/*3. 初始化摄像头*/
	UVCvideoInit();

	/*4. 初始化LCD屏*/
	framebuffer_Device_init();
	
	/*5. 循环采集摄像头的数据*/
	struct pollfd fds;
	fds.fd=uvc_video_fd;
	fds.events=POLLIN;

	struct v4l2_buffer buff_info;
	memset(&buff_info,0,sizeof(struct v4l2_buffer));
	int index=0; /*表示当前缓冲区的编号*/
	unsigned char *rgb_buffer=NULL;

	/*申请空间:存放转换之后的RGB数据*/
	rgb_buffer=malloc(Image_Width*Image_Height*3);
	if(rgb_buffer==NULL)
	{
		printf("RGB转换的缓冲区申请失败!\n");
		exit(0);
	}
	
	while(1)
	{
		/*1. 等待摄像头采集数据*/
		poll(&fds,1,-1); 

		/*2. 取出一帧数据: 从采集队列里面取出一个缓冲区*/
		buff_info.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;   /*视频捕获设备*/
		ioctl(uvc_video_fd,VIDIOC_DQBUF,&buff_info); /*从采集队列取出缓冲区*/
		index=buff_info.index;
		//printf("采集数据的缓冲区的编号:%d\n",index);

		/*3. 处理数据: YUV转RGB\显示到LCD屏*/
		//video_memaddr_buffer[index]; /*当前存放数据的缓冲区地址*/

		/*3.1 将YUV数据转为RGB格式*/
		yuv_to_rgb(video_memaddr_buffer[index],rgb_buffer,Image_Width,Image_Height);

		/*3.2 将RGB数据实时刷新到LCD屏幕上*/
		framebuffer_DisplayImages((800-Image_Width)/2,0,Image_Width,Image_Height,rgb_buffer);
		
		/*4. 将缓冲区再次放入采集队列*/
		buff_info.memory=V4L2_MEMORY_MMAP; 	/*支持mmap内存映射*/
		buff_info.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; /*视频捕获设备*/
		buff_info.index=index; /*缓冲区的节点编号*/
		ioctl(uvc_video_fd,VIDIOC_QBUF,&buff_info); /*根据节点编号将缓冲区放入队列*/
	}
	return 0;
};i++)>
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    11229

    浏览量

    208927
  • 摄像头
    +关注

    关注

    59

    文章

    4810

    浏览量

    95445
  • 倒车影像
    +关注

    关注

    1

    文章

    42

    浏览量

    5719
收藏 人收藏

    评论

    相关推荐

    【毕设项目参考】嵌入式Linux 10+项目推荐,附项目文档/源码/视频

    又是一年毕设季,针对即将开始毕业设计的大学生,我们汇总了10+嵌入式linux项目,供同学们参考。这些项目覆盖了多个领域,希望可以为同学们提供丰富的灵感来源。我们鼓励同学们根据自己的兴趣和专业方向
    的头像 发表于 11-19 15:21 307次阅读
    【毕设<b class='flag-5'>项目</b>参考】嵌入式<b class='flag-5'>Linux</b> 10+<b class='flag-5'>项目</b>推荐,附<b class='flag-5'>项目</b>文档/源码/视频

    安泰功率放大器在倒车雷达传感器测试中的应用介绍

    和可靠性。本文将介绍功率放大器在倒车雷达传感器测试中的应用,强调其重要性和功能。 倒车雷达传感器工作原理 首先,让我们了解一下倒车雷达传感器的工作原理。这些传感器通常使用毫米波雷达技术
    的头像 发表于 09-06 11:34 301次阅读
    安泰功率放大器在<b class='flag-5'>倒车</b>雷达传感器测试中的应用<b class='flag-5'>介绍</b>

    北京迅为RK3568开发板嵌入式学习之Linux驱动全新更新-CAN+

    北京迅为RK3568开发板嵌入式学习之Linux驱动全新更新-CAN+
    的头像 发表于 09-04 15:29 407次阅读
    北京迅为RK3568<b class='flag-5'>开发</b>板嵌入式学习之<b class='flag-5'>Linux</b><b class='flag-5'>驱动</b>全新更新-CAN+

    嵌入式linux开发的基本步骤有哪些?

    嵌入式Linux开发是一个复杂的过程,涉及到硬件选择、操作系统移植、驱动开发、应用程序开发等多个方面。以下是嵌入式
    的头像 发表于 09-02 09:11 398次阅读

    linux系统的设备驱动一般分几类

    视频设备驱动 USB设备驱动 其他设备驱动 下面将对这些设备驱动进行详细的介绍。 字符设备驱动
    的头像 发表于 08-30 15:13 347次阅读

    linux驱动程序如何加载进内核

    Linux系统中,驱动程序是内核与硬件设备之间的桥梁。它们允许内核与硬件设备进行通信,从而实现对硬件设备的控制和管理。 驱动程序的编写 驱动程序的编写是
    的头像 发表于 08-30 15:02 381次阅读

    linux驱动程序的编译方法是什么

    Linux驱动程序的编译方法主要包括两种: 与内核一起编译 和 编译成独立的内核模块 。以下是对这两种方法的介绍: 一、与内核一起编译 与内核一起编译意味着将驱动程序的源代码直接集成到
    的头像 发表于 08-30 14:46 426次阅读

    Linux 驱动开发与应用开发,你知道多少?

    一、Linux驱动开发与应用开发的区别开发层次不同:Linux
    的头像 发表于 08-30 12:16 605次阅读
    <b class='flag-5'>Linux</b> <b class='flag-5'>驱动</b><b class='flag-5'>开发</b>与应用<b class='flag-5'>开发</b>,你知道多少?

    因为延迟低,所以倒车影像全都用模拟摄像头?

    电子发烧友网报道(文/梁浩斌)最近有博主在某社交平台上发布视频宣称,“再好的数字监控也有半秒到一秒的延迟,而模拟信号传输的监控几乎没有延迟,因此汽车倒车影像一定都是模拟摄像头。”   从原理上看
    的头像 发表于 03-08 09:07 3508次阅读

    fpga开发板与linux开发板区别

    FPGA开发板与Linux开发板是两种不同的硬件开发平台,各自具有不同的特点和应用场景。在以下的文章中,我将详细介绍FPGA
    的头像 发表于 02-01 17:09 2085次阅读

    盘点那些硬件+项目学习套件:STM32MP157 Linux开发板及入门常见问题解答

    来盘点一下,比较受欢迎几款“硬件+项目”学习套件,以及一些初学者比较关注的问题。 盘点一:STM32MP157 Linux开发板 ▋开发板标识图 ▋
    发表于 02-01 14:25

    嵌入式Linux开发的三种方式

    嵌入式Linux开发主要有三种方式:裸机开发、SDK开发驱动开发
    的头像 发表于 01-22 14:22 925次阅读

    如何构建linux开发环境和编译软件工程、应用程序

    前文介绍了如何使用官方提供的镜像文件启动开发板,本文将说明如何构建linux开发环境和编译软件工程、应用程序。
    的头像 发表于 01-03 12:31 1940次阅读
    如何构建<b class='flag-5'>linux</b><b class='flag-5'>开发</b>环境和编译软件工程、应用程序

    倒车雷达的工作原理

    倒车雷达的工作原理  倒车雷达是一种汽车安全辅助设备,可以通过使用超声波或雷达技术来帮助驾驶员在倒车时检测障碍物。它通过发射无害的超声波或雷达信号,并根据信号的反弹时间和强度来计算距离和障碍物位置
    的头像 发表于 12-20 10:56 1661次阅读

    linux驱动程序的主要流程和功能

    介绍Linux驱动程序的主要流程和功能。 一、驱动程序的加载和初始化 Linux系统在启动过程中,会自动加载已安装的设备
    的头像 发表于 12-08 14:56 2255次阅读