linux驱动开发-星辰平台

ds小龙哥 发表于 2022/05/16 22:29:27 2022/05/16
【摘要】 介绍linux下倒车影像的项目,完成摄像头图像读取、超声波驱动编写、超声波距离读取。

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

组成部分:

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

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

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

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


1.1 超声波测距模块

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



​ 硬件接线:


echo------->gpx1_0 (开发板第9个io口): 中断引脚----->检测回波----输入

trig ------->gpb_7 (开发板第8个io口): 输出触发信号。


1.2 pwm方波控制蜂鸣器


​ pwm方波:



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


1.3 uvc免驱摄像头编程框架: v4l2

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

v4l2 : 针对uvc免驱usb设备设计框架。专用于usb摄像头的数据采集

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

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

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

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



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

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;
}



【星辰平台的版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区),文章链接,文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。