赞
踩
参考文章:
(2条消息) 【全志T113-S3_100ask】5-编写按键驱动(input子系统+内核按键驱动)_全志 key gpio-keys_第四维度4的博客-CSDN博客
(2条消息) 【全志T113-S3_100ask】USB摄像头通过v4l2采集图像_crabxd的博客-CSDN博客
全志T113-100ask-pro、USB摄像头
在内核目录:~/buildroot-100ask_t113-pro/buildroot/output/build/linux-origin_master/下执行,以下命令进入菜单
- make menuconfig
- -> Device Drivers
- -> Input device support
- -> Generic input layer (needed for keyboard, mouse, ...) (按y选中)
- -> Keyboards (INPUT_KEYBOARD [按y选中])
- ->GPIO Buttons
保存退出菜单。
在~/buildroot-100ask_t113-pro/buildroot/output/build/linux-origin_master/arch/arm/boot/dts目录下找到名为 sun8iw20p1-t113-100ask-t113-pro.dts 的文件,使用vim打开它。
在根节点下添加以下代码:
- gpio-keys {
- compatible = "gpio-keys";
- autorepeat;
-
- pinctrl-names = "default";
- user_key {
- label = "USER KEY";
- linux,code = <103>;
- gpios = <&pio PB 4 GPIO_ACTIVE_LOW>;
- };
- };
完成之后,按esc退出编辑模式,输入:wq保存退出
在 ~/buildroot-100ask_t113-pro/buildroot/ 目录下执行以下代码,编译内核
make linux-rebuild V=1
在 ~/buildroot-100ask_t113-pro/buildroot/ 目录下执行以下两个代码,编译最小系统镜像(如果是初次编译的话,时间可能会比较长,这是正常的,你可以先看看下一部分的内容,编写代码,等编译完成再继续下一步。)
- make BR2_EXTERNAL="../br2t113pro ../br2lvgl " 100ask_t113-pro_sdcard_core_defconfig
- make V=1
编译完成之后,会在~/buildroot-100ask_t113-pro/buildroot/output/images 目录下生成一个镜像文件
将文件拷贝到windows下使用wind32diskimage烧写,等待烧写完成之后,将SD卡插到开发板上启动即可。
由于我们后续需要将编写的应用程序下载到开发板上,所以我们需要让开发板联网
用手机或者电脑开个热点,在开发板上执行以下命令:
/etc/wlan-connect.sh wifi名称 wifi密码 1
联网成功之后,可以使用ifconfig命令查看分配到的IP
在编写代码之前,让我们先捋一下:
首先我们需要驱动按键、使用按键,所以我们需要三个文件:key.c、key_drv.c、key.h;
然后,我们需要使用USB摄像头,使用还需要两个文件:camera.c、camera.h
最后,我们需要用一个 main.c 来初始化、测试。
- #ifndef __KEY_H
- #define __KEY_H
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <linux/input.h>
-
- int key(void);
-
- #endif
#include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/init.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/gpio.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_gpio.h> #include <linux/semaphore.h> #include <asm/mach/map.h> #include <asm/uaccess.h> #include <asm/io.h> #define KEY_CNT 1 /* 设备号个数 */ #define KEY_NAME "key" /* 名字 */ /* 定义按键值 */ #define KEY0VALUE 0XF0 /* 按键值 */ #define INVAKEY 0X00 /* 无效的按键值 */ /* key设备结构体 */ struct key_dev{ dev_t devid; /* 设备号 */ struct cdev cdev; /* cdev */ struct class *class; /* 类 */ struct device *device; /* 设备 */ int major; /* 主设备号 */ int minor; /* 次设备号 */ struct device_node *nd; /* 设备节点 */ int key_gpio; /* key所使用的GPIO编号 */ atomic_t keyvalue; /* 按键值 */ }; struct key_dev keydev; /* key设备 */ /* * @description : 初始化按键IO,open函数打开驱动的时候 * 初始化按键所使用的GPIO引脚。 * @param : 无 * @return : 无 */ static int keyio_init(void) { keydev.nd = of_find_node_by_path("/key"); if (keydev.nd== NULL) { return -EINVAL; } keydev.key_gpio = of_get_named_gpio(keydev.nd ,"key-gpio", 0); if (keydev.key_gpio < 0) { printk("can't get key0\r\n"); return -EINVAL; } printk("key_gpio=%d\r\n", keydev.key_gpio); /* 初始化key所使用的IO */ gpio_request(keydev.key_gpio, "key0"); /* 请求IO */ gpio_direction_input(keydev.key_gpio); /* 设置为输入 */ return 0; } /* * @description : 打开设备 * @param - inode : 传递给驱动的inode * @param - filp : 设备文件,file结构体有个叫做private_data的成员变量 * 一般在open的时候将private_data指向设备结构体。 * @return : 0 成功;其他 失败 */ static int key_open(struct inode *inode, struct file *filp) { int ret = 0; filp->private_data = &keydev; /* 设置私有数据 */ ret = keyio_init(); /* 初始化按键IO */ if (ret < 0) { return ret; } return 0; } /* * @description : 从设备读取数据 * @param - filp : 要打开的设备文件(文件描述符) * @param - buf : 返回给用户空间的数据缓冲区 * @param - cnt : 要读取的数据长度 * @param - offt : 相对于文件首地址的偏移 * @return : 读取的字节数,如果为负值,表示读取失败 */ static ssize_t key_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) { int ret = 0; int value; struct key_dev *dev = filp->private_data; if (gpio_get_value(dev->key_gpio) == 0) { /* key0按下 */ while(!gpio_get_value(dev->key_gpio)); /* 等待按键释放 */ atomic_set(&dev->keyvalue, KEY0VALUE); } else { atomic_set(&dev->keyvalue, INVAKEY); /* 无效的按键值 */ } value = atomic_read(&dev->keyvalue); ret = copy_to_user(buf, &value, sizeof(value)); return ret; } /* * @description : 向设备写数据 * @param - filp : 设备文件,表示打开的文件描述符 * @param - buf : 要写给设备写入的数据 * @param - cnt : 要写入的数据长度 * @param - offt : 相对于文件首地址的偏移 * @return : 写入的字节数,如果为负值,表示写入失败 */ static ssize_t key_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) { return 0; } /* * @description : 关闭/释放设备 * @param - filp : 要关闭的设备文件(文件描述符) * @return : 0 成功;其他 失败 */ static int key_release(struct inode *inode, struct file *filp) { return 0; } /* 设备操作函数 */ static struct file_operations key_fops = { .owner = THIS_MODULE, .open = key_open, .read = key_read, .write = key_write, .release = key_release, }; /* * @description : 驱动入口函数 * @param : 无 * @return : 无 */ static int __init mykey_init(void) { /* 初始化原子变量 */ atomic_set(&keydev.keyvalue, INVAKEY); /* 注册字符设备驱动 */ /* 1、创建设备号 */ if (keydev.major) { /* 定义了设备号 */ keydev.devid = MKDEV(keydev.major, 0); register_chrdev_region(keydev.devid, KEY_CNT, KEY_NAME); } else { /* 没有定义设备号 */ alloc_chrdev_region(&keydev.devid, 0, KEY_CNT, KEY_NAME); /* 申请设备号 */ keydev.major = MAJOR(keydev.devid); /* 获取分配号的主设备号 */ keydev.minor = MINOR(keydev.devid); /* 获取分配号的次设备号 */ } /* 2、初始化cdev */ keydev.cdev.owner = THIS_MODULE; cdev_init(&keydev.cdev, &key_fops); /* 3、添加一个cdev */ cdev_add(&keydev.cdev, keydev.devid, KEY_CNT); /* 4、创建类 */ keydev.class = class_create(THIS_MODULE, KEY_NAME); if (IS_ERR(keydev.class)) { return PTR_ERR(keydev.class); } /* 5、创建设备 */ keydev.device = device_create(keydev.class, NULL, keydev.devid, NULL, KEY_NAME); if (IS_ERR(keydev.device)) { return PTR_ERR(keydev.device); } return 0; } /* * @description : 驱动出口函数 * @param : 无 * @return : 无 */ static void __exit mykey_exit(void) { /* 注销字符设备驱动 */ gpio_free(keydev.key_gpio); cdev_del(&keydev.cdev);/* 删除cdev */ unregister_chrdev_region(keydev.devid, KEY_CNT); /* 注销设备号 */ device_destroy(keydev.class, keydev.devid); class_destroy(keydev.class); } module_init(mykey_init); module_exit(mykey_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("z");
#include "key.h" int key(void) { struct input_event in_ev = {0}; int fd = -1; int value = -1; /* 打开文件 */ if (0 > (fd = open("/dev/input/event5", O_RDONLY))) { perror("open error"); exit(-1); } /* 循环读取数据 */ if (sizeof(struct input_event) != read(fd, &in_ev, sizeof(struct input_event))) { perror("read error"); exit(-1); } if (EV_KEY == in_ev.type) { //按键事件 switch (in_ev.value) { case 0: printf("code<%d>: 检测到按键松开\n", in_ev.code); return 0; break; case 1: printf("code<%d>: 检测到按键按下\n", in_ev.code); return 1; break; case 2: //printf("code<%d>: 长按\n", in_ev.code); return 2; break; } } return 0; }
#ifndef __CAMERA_H #define __CAMERA_H #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <linux/videodev2.h> #include <string.h> #include <sys/mman.h> int take_Photo(char *name); int camera_Init(void); void close_camera(void); #endif
struct v4l2_format vfmt; vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头采集 //设置宽高(宽高有要求,不是说可以随便设置,宽和高的倍数固定) vfmt.fmt.pix.width = 1280; vfmt.fmt.pix.height = 720; //设置采集格式 vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; ret = ioctl(fd,VIDIOC_S_FMT,&vfmt); if(ret < 0) { perror("格式设置错误!"); } if(vfmt.fmt.pix.width == 1280 && vfmt.fmt.pix.height == 720 && vfmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) { printf("采集格式设置成功!\n\n"); }else { printf("设置失败!\n"); return -1; } return 0; } int take_Photo(char *name) { //申请内核缓冲区队列 struct v4l2_requestbuffers reqbuffer; reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//设置采集格式 //申请4个缓冲区 reqbuffer.count = 4; //指定映射方式 reqbuffer.memory = V4L2_MEMORY_MMAP; ret = ioctl(fd,VIDIOC_REQBUFS,&reqbuffer); if(ret < 0) { perror("申请队列空间失败!"); } //将内核的缓冲区队列映射到用户空间 struct v4l2_buffer mapbuffer; mapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//指定type for(int i = 0; i < 4; i++)//查询出缓冲区 { mapbuffer.index = i; ret = ioctl(fd,VIDIOC_QUERYBUF,&mapbuffer);//从内核空间中查询一个空间做映射 if(ret < 0) { perror("查询内核空间失败"); } mptr[i] =(unsigned char *)mmap(NULL,mapbuffer.length,PROT_READ | PROT_WRITE,MAP_SHARED,fd,mapbuffer.m.offset); size[i]=mapbuffer.length; //使用完毕,“放回去” ret = ioctl(fd,VIDIOC_QBUF,&mapbuffer); if(ret < 0) { perror("放回失败!"); } } //开始采集 type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = ioctl(fd, VIDIOC_STREAMON, &type); if(ret < 0) { perror("开启失败"); } //从队列中提取一帧数据 struct v4l2_buffer readbuffer; readbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = ioctl(fd, VIDIOC_DQBUF, &readbuffer); if(ret < 0) { perror("提取数据失败"); } FILE *file=fopen(name, "w+"); fwrite(mptr[readbuffer.index], readbuffer.length, 1, file);//写数据 fclose(file);//关闭文件 //通知内核已经使用完毕 ret = ioctl(fd, VIDIOC_QBUF, &readbuffer); if(ret < 0) { perror("放回队列失败"); } //停止采集 ret = ioctl(fd, VIDIOC_STREAMOFF, &type); //释放映射 for(int i=0; i<4; i++) { munmap(mptr[i], size[i]); } return 0; } void close_camera(void) { //关闭设备 close(fd); }
#include "camera.h" #include "key.h" int main(int argc, char* argv[]) { int key_val,num; // key_val:用于存放按键状态;num:用于存放用户输入的图像采集数量 int i = 0; // 用于记录当前拍照次数 char name[20] = {0}; // 用于存放照片文件名 printf("\n/***********图像采集************/\r\n\n"); printf("/*******按下按键采集图像********/\r\n\n"); printf("请输入要采集图像的数量:"); scanf("%d",&num); camera_Init(); // 初始化摄像头 printf("/*******摄像头初始化成功********/\r\n\n"); while(1) { key_val = key(); // 获取按键状态 if(key_val == 1) // 如果按键被按下,则执行拍照 { i++; sprintf(name,"./JPG/%d.jpg",i); // 照片的位置:./JPG/ 照片名字:i.jpg printf("进行第%d次拍摄\r\n\n",i); take_Photo(name); // 拍照 printf("/***********拍照成功************/\r\n"); } if(i>=num) // 如果到达采集的数量,跳出循环 break; } printf("/********正在关闭摄像头*********/\r\n\n"); close_camera(); // 关闭摄像头 }
因为这里涉及的源文件比较多,使用我们是Makefile来进行编译链接源文件。
- KERN_DIR = /home/usr/buildroot-100ask_t113-pro/buildroot/output/build/linux-origin_master
- #这里的路径需要根据自己的情况进行修改
-
-
- all:
- make -C $(KERN_DIR) M=`pwd` modules
- $(CROSS_COMPILE)gcc -o camera_App main.c camera.c key.c
-
- clean:
- make -C $(KERN_DIR) M=`pwd` modules clean
- rm -rf modules.order
- rm -f camera_App
-
- obj-m += key_drv.o
到这里我们的代码就基本编写完成了。
在存放代码的目录下,执行make命令,即可编译;
编译成功后会生成一个mainApp的可执行文件,我们将该文件通过TFTP发送下载到我们的开发板上
打开MobaXterm(没有的话,下载一个),选择 servers
打开TFTP server,点击TFTP后的设置,选择你存放可执行文件(camera_App)的文件夹
开启TFTP服务
在开发板上使用以下命令下载
tftp -gr camera_App 主机的IP
下载成功,可以在开发板上看到camera_App,执行以下命令:
chmod 777 camera_App./camera_App
成功执行
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。