基于Linux设计的倒车雷达系统

举报
DS小龙哥 发表于 2023/06/20 08:53:23 2023/06/20
【摘要】 随着社会的不断发展,人们对于汽车的安全性要求越来越高,而倒车雷达系统就是为了增强汽车驾驶者的安全性而被广泛使用。在这种情况下,我们开发了一个基于Linux设计的倒车雷达系统,该系统可以采用迅为4412主控板,运行Linux3.5内核,使用USB摄像头、TFT真彩显示屏、超声波测距模块和蜂鸣器等硬件。

一、项目背景介绍

随着社会的不断发展,人们对于汽车的安全性要求越来越高,而倒车雷达系统就是为了增强汽车驾驶者的安全性而被广泛使用。在这种情况下,我们开发了一个基于Linux设计的倒车雷达系统,该系统可以采用迅为4412主控板,运行Linux3.5内核,使用USB摄像头、TFT真彩显示屏、超声波测距模块和蜂鸣器等硬件。

image-20230524162414354

二、创新点

本项目的创新点包括:

  1. 采用开源Linux系统:采用Linux系统,具有很好的可扩展性和灵活性,可以实现更为丰富和复杂的功能。
  2. 多个模块的协同工作:本项目涉及到了摄像头模块、超声波测距模块、处理模块、告警模块和显示模块等多个模块,这些模块需要协同工作才能实现完整的功能。
  3. 实现了告警功能:本项目采用了蜂鸣器来实现告警功能,根据距离调整PWM输出给出不同级别的告警声音,能够提醒驾驶者注意障碍物。

三、使用技术介绍

  1. 迅为4412主控板:本项目采用了迅为4412主控板,该主控板具有较高的性能和稳定性,能够满足系统的运行要求。
  2. Linux操作系统:本项目采用了开源的Linux操作系统,具有很好的可扩展性和灵活性,适合进行自定义开发。
  3. V4L2协议:V4L2协议是Linux下的一个视频设备驱动程序接口,可以方便地实现USB摄像头的图像采集。
  4. 超声波测距模块:通过GPIO口与主控板相连,使用定时器产生超声波并计算与接收到回波之间的时间差,从而计算出与障碍物之间的距离。
  5. PWM输出控制:根据距离调整PWM输出给出不同级别的告警声音。
  6. TFT真彩显示屏:本项目采用了TFT真彩显示屏,能够实现高清晰度的图像显示。
  7. 系统管理模块:负责管理整个系统的启动、配置和错误处理等操作。例如,可以将系统的启动脚本写在/etc/rc.local中,通过调用shell脚本来实现系统的初始化和启动。

四、系统架构

整个系统由以下几个模块组成:

  1. 摄像头模块:负责采集车尾环境图像,并传输给处理模块。
  2. 超声波测距模块:负责与障碍物之间的距离测量,并将结果传输给处理模块。
  3. 处理模块:负责视频显示、距离信息的处理、告警功能的实现。
  4. 告警模块:负责根据距离调整PWM输出给出不同级别的告警声音,并使用蜂鸣器输出告警信息。
  5. 显示模块:负责将处理模块生成的图像显示在TFT真彩显示屏上。
  6. 系统管理模块:负责管理整个系统的启动、配置和错误处理等操作。

五、功能设计

  1. 摄像头模块 由于Linux系统具有很好的驱动支持,因此可以直接使用V4L2协议来获取USB摄像头的图像。获取图像后,需要通过DMA方式将数据传输给处理模块进行处理。
  2. 超声波测距模块 超声波测距模块可以通过GPIO口与主控板相连,使用定时器产生超声波并计算与接收到回波之间的时间差,从而计算出与障碍物之间的距离。
  3. 处理模块 处理模块负责将摄像头采集到的图像和超声波测距模块测得的距离信息整合处理,并根据距离信息来控制告警模块。
  4. 告警模块 告警模块根据处理模块传递过来的距离信息来控制PWM输出,并使用蜂鸣器输出告警信息。
  5. 显示模块 显示模块负责将处理模块生成的图像显示在TFT真彩显示屏上,并实现显示屏的刷新和管理操作。
  6. 系统管理模块 系统管理模块负责管理整个系统的启动、配置和错误处理等操作。例如,可以将系统的启动脚本写在/etc/rc.local中,通过调用shell脚本来实现系统的初始化和启动。

六、摄像头图像显示应用代码

 #include <stdio.h>
 #include <sys/types.h>          /* See NOTES */
 #include <sys/socket.h>
 #include <unistd.h>
 #include <strings.h>
 #include <string.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <stdio.h>
 #include <pthread.h>
 #include <linux/fb.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <dirent.h>
 #include <stdlib.h>
 #include <fcntl.h>
 #include <poll.h>
 #include <linux/videodev2.h>
 #include <sys/time.h>
 #include <assert.h>
 #include <sys/select.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h> 
 #include <linux/fb.h>
 ​
 /* 图片的象素数据 */
 typedef struct PixelDatas {
     int iWidth;      /* 宽度: 一行有多少个象素 */
     int iHeight;     /* 高度: 一列有多少个象素 */
     int iBpp;        /* 一个象素用多少位来表示 */
     int iLineBytes;  /* 一行数据有多少字节 */
     int iTotalBytes; /* 所有字节数 */ 
     unsigned char *VideoBuf; //存放一帧摄像头的数据
     //指向了存放摄像头数据的空间地址
 }T_PixelDatas;
 ​
 T_PixelDatas Pixedata; //存放实际的图像数据
 ​
 /*
         USB摄像头相关参数定义
 */
 struct v4l2_buffer tV4l2Buf;
 int iFd;
 int ListNum;
 unsigned char* pucVideBuf[4];  // 视频BUFF空间地址
 void camera_pthread(void);
 ​
 //LCD屏相关的参数
 unsigned char *lcd_mem=NULL; /*LCD的内存地址*/
 struct fb_fix_screeninfo finfo; /*固定形参*/
 struct fb_var_screeninfo vinfo; /*可变形参*/
 void LCD_Init(void);
 void show_pixel(int x,int y,int color);
 ​
 ​
 int main(int argc ,char *argv[])
 {
     if(argc!=2)
     {
         printf("./app /dev/videoX\n");
         return -1;
     }
     
     LCD_Init(); //LCD屏初始化
     
     camera_init(argv[1]);  //摄像头设备初始化
     
     //开始采集摄像头数据,并实时显示在LCD屏幕上
     camera_pthread();
     return 0;
 }
 ​
 ​
 ​
 //LCD屏初始化
 void LCD_Init(void)
 {
     /*1.打开设备文件*/
     int fd=open("/dev/fb0",O_RDWR);
     if(fd<0)
     {
         printf("/dev/fb0设备文件打开失败!\n");
         return;
     }
     
     /*2. 读取LCD屏的参数*/
     ioctl(fd,FBIOGET_FSCREENINFO,&finfo);//固定参数
     printf("映射的长度:%d\n",finfo.smem_len);
     
     ioctl(fd,FBIOGET_VSCREENINFO,&vinfo);//可变参数,32位
     printf("分辨率:%d*%d,%d\n",vinfo.xres,vinfo.yres,vinfo.bits_per_pixel);
 ​
     /*3. 映射LCD的地址到进程空间*/
     lcd_mem=mmap(NULL,finfo.smem_len,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0);
     if(lcd_mem==NULL)
     {
         printf("lcd_mem映射失败!\n");
         return;
     }
     memset(lcd_mem,0xFFFFFFFF,finfo.smem_len);
 }
 ​
 /*画点*/
 void show_pixel(int x,int y,int color)
 {
     unsigned long *show32 = NULL;
     /* 定位到 LCD 屏上的位置*/
     show32 =(unsigned long *)(lcd_mem + y*vinfo.xres*vinfo.bits_per_pixel/8 + x*vinfo.bits_per_pixel/8);
     *show32 =color; /*向指向的 LCD 地址赋数据*/
 }
 ​
 ​
 //显示摄像头的数据
 void Show_VideoData(int w,int h,unsigned char *rgb)
 {
     int i,j;
     int x0,x=0,y=0;
     int color; //颜色 值
     unsigned char r,g,b;
     x0=x;
     for(i=0;i<h;i++)
     {
         for(j=0;j<w;j++)
         {
             b=rgb[0];
             g=rgb[1];
             r=rgb[2];
             color=r<<16|g<<8|b;       //合成颜色
             show_pixel(x0++,y,color); //显示一个像素点
             rgb+=3;
         }
         y++;
         x0=x;
     }
 }
 ​
 ​
 //摄像头设备的初始化
 int camera_init(char *video)
 {
     int i=0;
     int cnt=0;
     //定义摄像头驱动的BUF的功能捕获视频
     int iType = V4L2_BUF_TYPE_VIDEO_CAPTURE;
     
     /* 1、打开视频设备 */
     iFd = open(video,O_RDWR);
     if(iFd < 0)
     {
         printf("摄像头设备打开失败!\n");
         return 0;
     }
     struct v4l2_format  tV4l2Fmt;
     
     /* 2、 VIDIOC_S_FMT 设置摄像头使用哪种格式 */
     memset(&tV4l2Fmt, 0, sizeof(struct v4l2_format));
     tV4l2Fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕获
 ​
     //设置摄像头输出的图像格式
     tV4l2Fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV;
 ​
     /*设置输出的尺寸*/
     tV4l2Fmt.fmt.pix.width       = 1000;
     tV4l2Fmt.fmt.pix.height      = 1000;
     tV4l2Fmt.fmt.pix.field       = V4L2_FIELD_ANY;
     
     //VIDIOC_S_FMT 设置摄像头的输出参数
     ioctl(iFd, VIDIOC_S_FMT, &tV4l2Fmt); 
 ​
     //打印摄像头实际的输出参数
     printf("Support Format:%d\n",tV4l2Fmt.fmt.pix.pixelformat);
     printf("Support width:%d\n",tV4l2Fmt.fmt.pix.width);
     printf("Support height:%d\n",tV4l2Fmt.fmt.pix.height);
 ​
     /* 3、VIDIOC_REQBUFS  申请buffer */
     /* 初始化Pixedata结构体,为转化做准备 */
     Pixedata.iBpp =24;                              
     //高度 和宽度的赋值
     Pixedata.iHeight = tV4l2Fmt.fmt.pix.height;
     Pixedata.iWidth = tV4l2Fmt.fmt.pix.width;
 ​
     //一行所需要的字节数
     Pixedata.iLineBytes = Pixedata.iWidth*Pixedata.iBpp/8;
     //一帧图像的字节数
     Pixedata.iTotalBytes = Pixedata.iLineBytes * Pixedata.iHeight;
     
     Pixedata.VideoBuf=malloc(Pixedata.iTotalBytes); //申请存放图片数据空间
 ​
     //v412请求命令
     struct v4l2_requestbuffers tV4l2ReqBuffs;
     
     memset(&tV4l2ReqBuffs, 0, sizeof(struct v4l2_requestbuffers));
 ​
     /* 分配4个buffer:实际上由VIDIOC_REQBUFS获取到的信息来决定 */
     tV4l2ReqBuffs.count   = 4; /*在内核空间里开辟4个空间*/
     
     /*支持视频捕获功能*/
     tV4l2ReqBuffs.type    = V4L2_BUF_TYPE_VIDEO_CAPTURE;
     
     /* 表示申请的缓冲是支持MMAP(内存映射) */
     tV4l2ReqBuffs.memory  = V4L2_MEMORY_MMAP;
     
     /* 为分配buffer做准备 */
     ioctl(iFd, VIDIOC_REQBUFS, &tV4l2ReqBuffs);
 ​
     printf("tV4l2ReqBuffs.count=%d\n",tV4l2ReqBuffs.count);
     
     for (i = 0; i < tV4l2ReqBuffs.count; i++) 
     {
         memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
         tV4l2Buf.index = i; // 0  1 2 3
         tV4l2Buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;  /*支持视频捕获*/
         tV4l2Buf.memory = V4L2_MEMORY_MMAP;  /*支持内存映射*/
 ​
         /* 6、VIDIOC_QUERYBUF 确定每一个buffer的信息 并且 mmap */
         ioctl(iFd, VIDIOC_QUERYBUF, &tV4l2Buf);
         //映射空间地址
         pucVideBuf[i] = mmap(         /*返回用户空间的地址*/
                   0,                  /*表示系统自己制定地址*/
                   tV4l2Buf.length,   /*映射的长度*/
                   PROT_READ,          /*空间数据只读*/
                   MAP_SHARED,         /*空间支持共享*/
                   iFd,                /*将要映射的文件描述符*/
                   tV4l2Buf.m.offset  /*从哪个位置开始映射,表示起始位置*/
                   );
         printf("mmap %d addr:%p\n",i,pucVideBuf[i]);
     }
 ​
     /* 4、VIDIOC_QBUF  放入队列*/
     for (i = 0; i <tV4l2ReqBuffs.count; i++) 
     {
         memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
         tV4l2Buf.index = i;
         tV4l2Buf.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;
         tV4l2Buf.memory = V4L2_MEMORY_MMAP;
         ioctl(iFd, VIDIOC_QBUF, &tV4l2Buf); //放入队列   queue
     }
     
     /*5、启动摄像头开始读数据*/
     ioctl(iFd,VIDIOC_STREAMON, &iType);
     return 0;
 }
 ​
 ​
 void yuv_to_rgb(unsigned char *yuv_buffer,unsigned char *rgb_buffer,int iWidth,int iHeight)
 {
     int x;
     int z=0;
     unsigned char *ptr = rgb_buffer;
     unsigned char *yuyv= yuv_buffer;
     
     for (x = 0; x < iWidth*iHeight; x++) 
     {
       int r, g, b;
       int y, u, v;
 ​
       if (!z)
         y = yuyv[0] << 8;
       else
         y = yuyv[2] << 8;
       u = yuyv[1] - 128;
       v = yuyv[3] - 128;
 ​
       r = (y + (359 * v)) >> 8;
       g = (y - (88 * u) - (183 * v)) >> 8;
       b = (y + (454 * u)) >> 8;
 ​
       *(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b);
       *(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g);
       *(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r);
       if(z++)
       {
         z = 0;
         yuyv += 4;
       }
     }
 }
 ​
 ​
 /*采集摄像头数据*/
 void camera_pthread(void)
 {
     int error;
     int cnt=0;
     int i=0;
     int ListNum;
     
     /* 8.1、等待是否有数据 */
     fd_set readfds;
 ​
     while(1)
     {
         FD_ZERO(&readfds);
         FD_SET(iFd,&readfds);
         select(iFd+1,&readfds,NULL,NULL,NULL);  /*检测文件描述符是否发生了读写事件*/
       
         memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
         tV4l2Buf.type    = V4L2_BUF_TYPE_VIDEO_CAPTURE; //类型
         tV4l2Buf.memory  = V4L2_MEMORY_MMAP; //存储空间类型
 ​
         /* 9、VIDIOC_DQBUF    从队列中取出 */
         error = ioctl(iFd, VIDIOC_DQBUF, &tV4l2Buf); //取出一帧数据
         ListNum = tV4l2Buf.index; //索引编号   0
         
         //将YUV转换为RGB
         yuv_to_rgb(pucVideBuf[ListNum],Pixedata.VideoBuf,Pixedata.iWidth,Pixedata.iHeight);
         
         //在LCD屏显示图像
         Show_VideoData(Pixedata.iWidth,Pixedata.iHeight,Pixedata.VideoBuf);
         
         memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
         tV4l2Buf.index  = ListNum;
         tV4l2Buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
         tV4l2Buf.memory = V4L2_MEMORY_MMAP;
         error = ioctl(iFd, VIDIOC_QBUF, &tV4l2Buf);  /*将缓冲区再次放入队列*/
     }
 }
 ​

七、超声波测距驱动代码

 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/miscdevice.h>
 #include <linux/fs.h>
 #include <linux/uaccess.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/workqueue.h>
 #include <linux/timer.h>
 #include <linux/input.h>
 ​
 #include <linux/gpio.h>
 #include <mach/gpio.h>
 #include <plat/gpio-cfg.h>
 ​
 #include <linux/io.h>
 ​
 static int chaoshengbo_irq=0;
 ​
 #define GPB_CON  0x11400040
 #define GPB_DAT  0x11400044
 ​
 static u32 *gpb_con=NULL;
 static u32 *gpb_dat=NULL;
 ​
 /*
 硬件连接:
 输出脚: GPX1_0 ---ECHO
 输入脚: GPB_7 
 */
 static void tiny4412_work_func(struct work_struct *work)
 {   
     printk("GPB=%d\n",*gpb_dat&1<<7);
 ​
     ktime_t my_time1,my_time2;
     unsigned int i,j;
     unsigned int time_cnt=0;
 ​
     my_time1=ktime_get();  //获取当前时间
     i=ktime_to_us(my_time1); //转 us
 ​
     while(gpio_get_value(EXYNOS4_GPX1(0))){} //高电平卡住
 ​
     
     my_time2=ktime_get(); //获取当前时间
     j=ktime_to_us(my_time2); //转 us
 ​
     printk("us:%d\n",j-i);
 }
 ​
 static DECLARE_WORK(caoshengbotiny4412_work,tiny4412_work_func);
 ​
 /*
 中断处理函数
 */
 irqreturn_t irq_handler_chaoshengbo(int irq, void *dev)
 {
     schedule_work(&caoshengbotiny4412_work); /*正常情况下:  是在中断服务函数里面*/
     return IRQ_HANDLED; /*表示中断已经处理过了*/
 }
 ​
 static void timer_function(unsigned long data);
 static DEFINE_TIMER(timer_caoshengbo, timer_function,0,0);
 ​
 static void timer_function(unsigned long data)
 {
     static u8 state;
     state=!state;
     
     if(state)*gpb_dat|=1<<7;
     else *gpb_dat&=~(1<<7);
     
 ​
     mod_timer(&timer_caoshengbo,jiffies+msecs_to_jiffies(500)); 
 }
 ​
 static int __init tiny4412_chaoshengbo_init(void)
 {
     int i,err;
     /*获取中断号*/
     chaoshengbo_irq=gpio_to_irq(EXYNOS4_GPX1(0));
     printk("中断号:%d\n",chaoshengbo_irq);
     
     /*外部中断注册*/
     err=request_irq(chaoshengbo_irq,irq_handler_chaoshengbo,IRQ_TYPE_EDGE_RISING,"tiny4412_chaoshengbo",NULL);
     if(err!=0)printk("中断注册失败!\n");
 ​
     /*映射内存*/
     gpb_con=ioremap(GPB_CON,4);
     gpb_dat=ioremap(GPB_DAT,4);
 ​
     /*配置模式*/
     *gpb_con&=~(0xF<<4*7); //输出模式   
     *gpb_con|=0x1<<4*7;
     
     mod_timer(&timer_caoshengbo,jiffies+msecs_to_jiffies(100));         
     return 0;
 }
 ​
 static void __exit tiny4412_chaoshengbo_exit(void)
 {
     free_irq(chaoshengbo_irq,NULL);
     iounmap(gpb_con);
     iounmap(gpb_dat);
     del_timer(&timer_caoshengbo);
 }
 ​
 module_init(tiny4412_chaoshengbo_init);  /*指定驱动的入口函数*/
 module_exit(tiny4412_chaoshengbo_exit);  /*指定驱动的出口函数*/
 MODULE_LICENSE("GPL");      /*指定驱动许可证*/
 ​
 ​
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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