linux驱动开发-yb体育官方
【摘要】 介绍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)