首页 > 技术知识 > 正文

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

倒车影像项目

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

组成部分:

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

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

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

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

Linux驱动开发_倒车影像项目介绍-linux导航好还是安卓的好

1.1 超声波测距模块

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

Linux驱动开发_倒车影像项目介绍-linux导航好还是安卓的好1Linux驱动开发_倒车影像项目介绍-linux导航好还是安卓的好2Linux驱动开发_倒车影像项目介绍-linux导航好还是安卓的好3Linux驱动开发_倒车影像项目介绍-linux导航好还是安卓的好4

​硬件接线:

Linux驱动开发_倒车影像项目介绍-linux导航好还是安卓的好5

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

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

1.2 PWM方波控制蜂鸣器

​PWM方波:

Linux驱动开发_倒车影像项目介绍-linux导航好还是安卓的好6Linux驱动开发_倒车影像项目介绍-linux导航好还是安卓的好7Linux驱动开发_倒车影像项目介绍-linux导航好还是安卓的好8

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

Linux驱动开发_倒车影像项目介绍-linux导航好还是安卓的好9

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

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

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

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

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

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

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

Linux驱动开发_倒车影像项目介绍-linux导航好还是安卓的好10Linux驱动开发_倒车影像项目介绍-linux导航好还是安卓的好11

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

Linux驱动开发_倒车影像项目介绍-linux导航好还是安卓的好12

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++)>

猜你喜欢