3082|1

9

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

【Luckfox幸狐 RV1106 Linux 开发板使用】USB外接 USB摄像头 EC20 [复制链接]

 
本帖最后由 0x4C 于 2024-2-25 18:57 编辑

本篇实现USB HOST 下使用USB摄像头和4G模块

1、配置USB摄像头

    涉及修改的东西有点多,也记录一下SDK的编译流程,这里重新拉SDK编译,参考链接SDK 环境部署 | LUCKFOX WIKI

git clone https://gitee.com/LuckfoxTECH/luckfox-pico.git 

    

 

    安装交叉编译工具链:

cd {SDK_PATH}/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/
source env_install_toolchain.sh

 

选择编译目标平台:

./build.sh lunch

    我是用的板载的flash

 

    开始编译:

./build.sh 

    编译后打包固件:

./build.sh firmware

    得到基础的SDK包

    进入设备树文件路径,修改 <SDK目录>/sysdrv/source/kernel/arch/arm/boot/dts/rv1106g-luckfox-pico-pro-max.dts 将USB配置为USB HOST模式

&usbdrd_dwc3 {
    status = "okay";
    dr_mode = "host";
};

  

 

    接下来修改内核配置

cd ~/SDK目录/sysdrv/source/kernel
cp ./arch/arm/configs/luckfox_rv1106_linux_defconfig .config
make ARCH=arm menuconfig

   

    按下 ‘/’进行搜索。

 

    进入搜索栏,输入搜索内容回车

 

    显示搜索相关内容,按数字1-N进行选择。

 

    依次搜索并使能以下选项

SCSI
SCSI_UFSHCD
BLK_DEV_SD
USB_STORAGE
USB_MASS_STORAGE
USB_EHCI_HCD

    SCSI

     

    SCSI_UFSHCD

     

    BLK_DEV_SD

     

USB_STORAGE

    USB_MASS_STORAGE

     

    USB_EHCI_HCD

    退出界面前先save一下

    保存config,回到SDK目录下,并重新编译内核。

make ARCH=arm savedefconfig
cp defconfig arch/arm/configs/luckfox_rv1106_linux_defconfig
cd ~/SDK目录
./build.sh kernel

    打包固件

./build.sh firmware

    下载固件到板子上(先按住板子上的boot键再按住reset键)

    这里要注意一点,电脑的USB供电功率一般比较低,我这里使用开关电源额外提供5v电源输入,特别是后面做EC20联网的时候供电不足会直接导致板子重启,卡了我很长的时间

    USB使用一个USB扩展坞扩展

    

 

    之前的固件烧录后插入USB扩展坞:

    

 

    接下来继续修改内核配置来支持USB摄像头:

    在内核中启用UVC驱动,可通过搜索关键字"Webcam"

    在搜索结果中找到并勾选"USB Video Class (UVC)",最后保存并退出。

     

    编译内核  打包固件 下载固件(尝试用串口下载了一下固件,最大波特率下载,奇慢无比,同时有概率写入失败) 插入USB摄像头:

 

    可以看见已经生成了对应的设备节点:

 

    写个程序用V4L2抓个图吧:

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <linux/types.h>
#include <linux/videodev2.h>
#include <malloc.h>
#include <math.h>
#include <string.h>
#include <sys/mman.h>
#include <errno.h>
#include <assert.h>

#include <linux/videodev2.h>


int main(int argc, char** argv)
{
	printf("********************************start***************************************\n");
	
    int cameraFd;/* 定义一个设备描述符 */
    cameraFd = open("/dev/video21", O_RDWR);
    if(cameraFd < 0){
        perror("video设备打开失败\n");
        return -1;
    }
    else{
        printf("video设备打开成功\n");
    }
	printf("********************************1***************************************\n");
	struct v4l2_capability vcap;
	ioctl(cameraFd, VIDIOC_QUERYCAP, &vcap);
    if (!(V4L2_CAP_VIDEO_CAPTURE & vcap.capabilities)) {
        perror("Error: No capture video device!\n");
        return -1;
    }
	printf("********************************2***************************************\n");
	struct v4l2_fmtdesc fmtdesc;
    fmtdesc.index = 0;
    fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    
    printf("摄像头支持所有格式如下:\n");
    while(ioctl(cameraFd,VIDIOC_ENUM_FMT,&fmtdesc) == 0){
        printf("v4l2_format%d:%s\n",fmtdesc.index,fmtdesc.description);
        fmtdesc.index++;
    }
	printf("********************************3***************************************\n");
	struct v4l2_frmsizeenum frmsize;
	frmsize.index = 0;
	frmsize.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

	// printf("MJPEG格式支持所有分辨率如下:\n");
	// frmsize.pixel_format = V4L2_PIX_FMT_MJPEG;
	// while(ioctl(cameraFd,VIDIOC_ENUM_FRAMESIZES,&frmsize) == 0){
	// 	printf("frame_size<%d*%d>\n",frmsize.discrete.width,frmsize.discrete.height);
	// 	frmsize.index++;
	// }
	printf("YUYV422格式支持所有分辨率如下:\n");
	frmsize.pixel_format = V4L2_PIX_FMT_MJPEG;
	while(ioctl(cameraFd,VIDIOC_ENUM_FRAMESIZES,&frmsize) == 0){
		printf("frame_size<%d*%d>\n",frmsize.discrete.width,frmsize.discrete.height);
		frmsize.index++;
	}
	printf("********************************4***************************************\n");
	// struct v4l2_frmivalenum frmival;
    // frmival.index = 0;
    // frmival.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    // frmival.pixel_format = V4L2_PIX_FMT_MJPEG;
    // frmival.width = 640;
    // frmival.height = 480;
    // while(ioctl(cameraFd,VIDIOC_ENUM_FRAMEINTERVALS,&frmival) == 0){
    //     printf("frame_interval under frame_size <%d*%d> support %dfps\n",frmival.width,frmival.height,frmival.discrete.denominator / frmival.discrete.numerator);
    //     frmival.index++;
    // }
	struct v4l2_frmivalenum frmival;
    frmival.index = 0;
    frmival.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    frmival.pixel_format = V4L2_PIX_FMT_MJPEG;
    frmival.width = 2592;
    frmival.height = 1944;
    while(ioctl(cameraFd,VIDIOC_ENUM_FRAMEINTERVALS,&frmival) == 0){
        printf("frame_interval under frame_size <%d*%d> support %dfps\n",frmival.width,frmival.height,frmival.discrete.denominator / frmival.discrete.numerator);
        frmival.index++;
    }
	printf("********************************5***************************************\n");

	// struct v4l2_format vfmt;
    // vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    // vfmt.fmt.pix.width = 640;
    // vfmt.fmt.pix.height = 480;
    // vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
    // if(ioctl(cameraFd,VIDIOC_S_FMT,&vfmt) < 0){
    //     perror("设置格式失败\n");
    //     return -1;
    // }
    // // 检查设置参数是否生效
    // if(ioctl(cameraFd,VIDIOC_G_FMT,&vfmt) < 0){
    //     perror("获取设置格式失败\n");
    //     return -1;
    // }
    // else if(vfmt.fmt.pix.width == 640 && vfmt.fmt.pix.height == 480 && vfmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG){
    //     printf("设置格式生效,实际分辨率大小<%d * %d>,图像格式:Motion-JPEG\n",vfmt.fmt.pix.width,vfmt.fmt.pix.height);
    // }
    // else{
    //     printf("设置格式未生效\n");
    // }

	struct v4l2_format vfmt;
    vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    vfmt.fmt.pix.width = 2592;
    vfmt.fmt.pix.height = 1944;
    vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
    if(ioctl(cameraFd,VIDIOC_S_FMT,&vfmt) < 0){
        perror("设置格式失败\n");
        return -1;
    }
    // 检查设置参数是否生效
    if(ioctl(cameraFd,VIDIOC_G_FMT,&vfmt) < 0){
        perror("获取设置格式失败\n");
        return -1;
    }
    else if(vfmt.fmt.pix.width == 2592 && vfmt.fmt.pix.height == 1944 && vfmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG){
        printf("设置格式生效,实际分辨率大小<%d * %d>,图像格式:Motion-JPEG\n",vfmt.fmt.pix.width,vfmt.fmt.pix.height);
    }
    else{
        printf("设置格式未生效\n");
    }

	printf("********************************6***************************************\n");
	struct v4l2_requestbuffers reqbuf;
    reqbuf.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuf.count = 3;   //3个帧缓冲
    reqbuf.memory = V4L2_MEMORY_MMAP;
    if(ioctl(cameraFd,VIDIOC_REQBUFS,&reqbuf) < 0){
        perror("申请缓冲区失败\n");
        return -1;
    }
	printf("********************************7***************************************\n");
	    // 将帧缓冲映射到进程地址空间
    void *frm_base[3];  //映射后的用户空间的首地址
    unsigned int frm_size[3];

    struct v4l2_buffer buf;
    buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    // 将每一帧对应的缓冲区的起始地址保存在frm_base数组中,读取采集数据时,只需直接读取映射区即可
    for(buf.index=0;buf.index<3;buf.index++){
        ioctl(cameraFd, VIDIOC_QUERYBUF, &buf);
        frm_base[buf.index] = mmap(NULL,buf.length,PROT_READ | PROT_WRITE,MAP_SHARED,cameraFd,buf.m.offset);
        frm_size[buf.index] = buf.length;

        if(frm_base[buf.index] == MAP_FAILED){
            perror("mmap failed\n");
            return -1;
        }

        // 入队操作
        if(ioctl(cameraFd,VIDIOC_QBUF,&buf) < 0){
            perror("入队失败\n");
            return -1;
        }
    }
	printf("********************************8***************************************\n");
	enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(cameraFd, VIDIOC_STREAMON, &type) < 0){
        perror("开始采集失败\n");
        return -1;
    }
	printf("********************************9***************************************\n");
	struct v4l2_buffer  readbuffer;
    readbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    readbuffer.memory = V4L2_MEMORY_MMAP;
    if(ioctl(cameraFd, VIDIOC_DQBUF, &readbuffer) < 0){
        perror("读取帧失败\n");
    }

    // 保存这一帧,格式为jpg
    FILE *file = fopen("v4l2_cap.jpg","w+");
    fwrite(frm_base[readbuffer.index],buf.length,1,file);
    fclose(file);
	printf("********************************10***************************************\n");
	// 停止采集
    if (ioctl(cameraFd, VIDIOC_STREAMOFF, &type) < 0){
        perror("停止采集失败\n");
        return -1;
    }

    // 释放映射
    for(int i=0;i<3;i++){
        munmap(frm_base[i],frm_size[i]);
    }
    
	close(cameraFd);

    return 0;
}

    编译方法:

 

 

 

vi ~/.bashrc 
export PATH=<SDK Directory>/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin:$PATH
source ~/.bashrc  
arm-rockchip830-linux-uclibcgnueabihf-gcc gpio.c -o gpio

    放到开发板,执行后可以看到效果:

    

 

      

 

2、配置EC20    

    这里的配置仅仅提供一个参考,参考的博客:Quectel EC20在嵌入式平台的适配 - 大窟窿 - 博客园 (cnblogs.com)

     进入文件夹下,路径:luckfox-pico/sysdrv/source/kernel/drivers/usb/serial

      

    打开添加如下内容,保存 

{ USB_DEVICE(0x05C6, 0x9215) }, /* Quectel EC20 */   

 

    注释掉冲突的vid以及pid设备,路径:luckfox-pico/sysdrv/source/kernel/drivers/usb/serial   

 

      

    修改路径:luckfox-pico/sysdrv/source/kernel/drivers/net/usb

添加零包处理(这个和usb 协议中的批量传输有关)For Linux Kernel Version newer than 2.6.34: 路径:luckfox-pico/sysdrv/source/kernel/drivers/usb/serial

.reset_resume   = usb_wwan_resume, 

    

修改内核配置: 

   

    

    编译内核  打包固件 下载固件  可以看见其设备节点ttyUSB0~3

 

    下载ppp2.4.7源码 https://github.com/ppp-project/ppp.git

git clone -b ppp-2.4.7 https://github.com/ppp-project/ppp.git

    交叉编译

./configure
make CC=/home/czm/luckfox-pico/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc

    编译完成后会在当前目录下生成chat/chat、 pppd/pppd、 pppdump/pppdump 和pppstats/pppstats 这四个文件,把这四个文件拷贝到文件系统中的/usr/bin目录下

    最后移植ppp拨号脚本(附件提供),放到/etc/目录下即可

    使用 pppd -v :

    

 

    移植完成后 使用 pppd call quectel-ppp & 拨号

    

 

    使用iperf测速

  

ppp.rar

464.15 KB, 下载次数: 14

编译好的PPP包

最新回复

读了一下大佬的作品,感觉配个USB视频还得花不少的时间。  详情 回复 发表于 2024-3-14 10:13

赞赏

1

查看全部赞赏

点赞(1) 关注
 
 

回复
举报

6960

帖子

11

TA的资源

版主

沙发
 
读了一下大佬的作品,感觉配个USB视频还得花不少的时间。

赞赏

1

查看全部赞赏

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/9 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表