当前位置:   article > 正文

【全志T113-S3_100ask】按键控制USB摄像头采集图像_全志t113 jpeg

全志t113 jpeg

参考文章:

(2条消息) 【全志T113-S3_100ask】5-编写按键驱动(input子系统+内核按键驱动)_全志 key gpio-keys_第四维度4的博客-CSDN博客

(2条消息) 【全志T113-S3_100ask】USB摄像头通过v4l2采集图像_crabxd的博客-CSDN博客

一、材料准备

全志T113-100ask-pro、USB摄像头

二、环境配置

1、使能内核驱动

在内核目录:~/buildroot-100ask_t113-pro/buildroot/output/build/linux-origin_master/下执行,以下命令进入菜单

  1. make menuconfig
  2. -> Device Drivers
  3. -> Input device support
  4. -> Generic input layer (needed for keyboard, mouse, ...) (按y选中)
  5. -> Keyboards (INPUT_KEYBOARD [按y选中])
  6. ->GPIO Buttons

保存退出菜单。

2、修改设备树

~/buildroot-100ask_t113-pro/buildroot/output/build/linux-origin_master/arch/arm/boot/dts目录下找到名为 sun8iw20p1-t113-100ask-t113-pro.dts 的文件,使用vim打开它。

在根节点下添加以下代码:

  1. gpio-keys {
  2. compatible = "gpio-keys";
  3. autorepeat;
  4. pinctrl-names = "default";
  5. user_key {
  6. label = "USER KEY";
  7. linux,code = <103>;
  8. gpios = <&pio PB 4 GPIO_ACTIVE_LOW>;
  9. };
  10. };

完成之后,按esc退出编辑模式,输入:wq保存退出

3、编译内核

~/buildroot-100ask_t113-pro/buildroot/ 目录下执行以下代码,编译内核

make linux-rebuild V=1

4、编译最小系统镜像

~/buildroot-100ask_t113-pro/buildroot/ 目录下执行以下两个代码,编译最小系统镜像(如果是初次编译的话,时间可能会比较长,这是正常的,你可以先看看下一部分的内容,编写代码,等编译完成再继续下一步。)

  1. make BR2_EXTERNAL="../br2t113pro ../br2lvgl " 100ask_t113-pro_sdcard_core_defconfig
  2. make V=1

编译完成之后,会在~/buildroot-100ask_t113-pro/buildroot/output/images 目录下生成一个镜像文件

将文件拷贝到windows下使用wind32diskimage烧写,等待烧写完成之后,将SD卡插到开发板上启动即可。

5、启动成功后,输入root进入

6、设备联网

由于我们后续需要将编写的应用程序下载到开发板上,所以我们需要让开发板联网

用手机或者电脑开个热点,在开发板上执行以下命令:

/etc/wlan-connect.sh wifi名称 wifi密码 1

联网成功之后,可以使用ifconfig命令查看分配到的IP

7、在开发板当前目录(/root)下,创建一个目录JPG用于存放照片

三、编写代码

在编写代码之前,让我们先捋一下:

首先我们需要驱动按键、使用按键,所以我们需要三个文件:key.c、key_drv.c、key.h;

然后,我们需要使用USB摄像头,使用还需要两个文件:camera.c、camera.h

最后,我们需要用一个 main.c 来初始化、测试。

  1. key.h

  1. #ifndef __KEY_H
  2. #define __KEY_H
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. #include <fcntl.h>
  8. #include <unistd.h>
  9. #include <linux/input.h>
  10. int key(void);
  11. #endif
  1. key_drv.c

  1. #include <linux/types.h>
  2. #include <linux/kernel.h>
  3. #include <linux/delay.h>
  4. #include <linux/ide.h>
  5. #include <linux/init.h>
  6. #include <linux/module.h>
  7. #include <linux/errno.h>
  8. #include <linux/gpio.h>
  9. #include <linux/cdev.h>
  10. #include <linux/device.h>
  11. #include <linux/of.h>
  12. #include <linux/of_address.h>
  13. #include <linux/of_gpio.h>
  14. #include <linux/semaphore.h>
  15. #include <asm/mach/map.h>
  16. #include <asm/uaccess.h>
  17. #include <asm/io.h>
  18. #define KEY_CNT 1 /* 设备号个数 */
  19. #define KEY_NAME "key" /* 名字 */
  20. /* 定义按键值 */
  21. #define KEY0VALUE 0XF0 /* 按键值 */
  22. #define INVAKEY 0X00 /* 无效的按键值 */
  23. /* key设备结构体 */
  24. struct key_dev{
  25. dev_t devid; /* 设备号 */
  26. struct cdev cdev; /* cdev */
  27. struct class *class; /* 类 */
  28. struct device *device; /* 设备 */
  29. int major; /* 主设备号 */
  30. int minor; /* 次设备号 */
  31. struct device_node *nd; /* 设备节点 */
  32. int key_gpio; /* key所使用的GPIO编号 */
  33. atomic_t keyvalue; /* 按键值 */
  34. };
  35. struct key_dev keydev; /* key设备 */
  36. /*
  37. * @description : 初始化按键IO,open函数打开驱动的时候
  38. * 初始化按键所使用的GPIO引脚。
  39. * @param : 无
  40. * @return : 无
  41. */
  42. static int keyio_init(void)
  43. {
  44. keydev.nd = of_find_node_by_path("/key");
  45. if (keydev.nd== NULL) {
  46. return -EINVAL;
  47. }
  48. keydev.key_gpio = of_get_named_gpio(keydev.nd ,"key-gpio", 0);
  49. if (keydev.key_gpio < 0) {
  50. printk("can't get key0\r\n");
  51. return -EINVAL;
  52. }
  53. printk("key_gpio=%d\r\n", keydev.key_gpio);
  54. /* 初始化key所使用的IO */
  55. gpio_request(keydev.key_gpio, "key0"); /* 请求IO */
  56. gpio_direction_input(keydev.key_gpio); /* 设置为输入 */
  57. return 0;
  58. }
  59. /*
  60. * @description : 打开设备
  61. * @param - inode : 传递给驱动的inode
  62. * @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
  63. * 一般在open的时候将private_data指向设备结构体。
  64. * @return : 0 成功;其他 失败
  65. */
  66. static int key_open(struct inode *inode, struct file *filp)
  67. {
  68. int ret = 0;
  69. filp->private_data = &keydev; /* 设置私有数据 */
  70. ret = keyio_init(); /* 初始化按键IO */
  71. if (ret < 0) {
  72. return ret;
  73. }
  74. return 0;
  75. }
  76. /*
  77. * @description : 从设备读取数据
  78. * @param - filp : 要打开的设备文件(文件描述符)
  79. * @param - buf : 返回给用户空间的数据缓冲区
  80. * @param - cnt : 要读取的数据长度
  81. * @param - offt : 相对于文件首地址的偏移
  82. * @return : 读取的字节数,如果为负值,表示读取失败
  83. */
  84. static ssize_t key_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
  85. {
  86. int ret = 0;
  87. int value;
  88. struct key_dev *dev = filp->private_data;
  89. if (gpio_get_value(dev->key_gpio) == 0) { /* key0按下 */
  90. while(!gpio_get_value(dev->key_gpio)); /* 等待按键释放 */
  91. atomic_set(&dev->keyvalue, KEY0VALUE);
  92. } else {
  93. atomic_set(&dev->keyvalue, INVAKEY); /* 无效的按键值 */
  94. }
  95. value = atomic_read(&dev->keyvalue);
  96. ret = copy_to_user(buf, &value, sizeof(value));
  97. return ret;
  98. }
  99. /*
  100. * @description : 向设备写数据
  101. * @param - filp : 设备文件,表示打开的文件描述符
  102. * @param - buf : 要写给设备写入的数据
  103. * @param - cnt : 要写入的数据长度
  104. * @param - offt : 相对于文件首地址的偏移
  105. * @return : 写入的字节数,如果为负值,表示写入失败
  106. */
  107. static ssize_t key_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
  108. {
  109. return 0;
  110. }
  111. /*
  112. * @description : 关闭/释放设备
  113. * @param - filp : 要关闭的设备文件(文件描述符)
  114. * @return : 0 成功;其他 失败
  115. */
  116. static int key_release(struct inode *inode, struct file *filp)
  117. {
  118. return 0;
  119. }
  120. /* 设备操作函数 */
  121. static struct file_operations key_fops = {
  122. .owner = THIS_MODULE,
  123. .open = key_open,
  124. .read = key_read,
  125. .write = key_write,
  126. .release = key_release,
  127. };
  128. /*
  129. * @description : 驱动入口函数
  130. * @param : 无
  131. * @return : 无
  132. */
  133. static int __init mykey_init(void)
  134. {
  135. /* 初始化原子变量 */
  136. atomic_set(&keydev.keyvalue, INVAKEY);
  137. /* 注册字符设备驱动 */
  138. /* 1、创建设备号 */
  139. if (keydev.major) { /* 定义了设备号 */
  140. keydev.devid = MKDEV(keydev.major, 0);
  141. register_chrdev_region(keydev.devid, KEY_CNT, KEY_NAME);
  142. } else { /* 没有定义设备号 */
  143. alloc_chrdev_region(&keydev.devid, 0, KEY_CNT, KEY_NAME); /* 申请设备号 */
  144. keydev.major = MAJOR(keydev.devid); /* 获取分配号的主设备号 */
  145. keydev.minor = MINOR(keydev.devid); /* 获取分配号的次设备号 */
  146. }
  147. /* 2、初始化cdev */
  148. keydev.cdev.owner = THIS_MODULE;
  149. cdev_init(&keydev.cdev, &key_fops);
  150. /* 3、添加一个cdev */
  151. cdev_add(&keydev.cdev, keydev.devid, KEY_CNT);
  152. /* 4、创建类 */
  153. keydev.class = class_create(THIS_MODULE, KEY_NAME);
  154. if (IS_ERR(keydev.class)) {
  155. return PTR_ERR(keydev.class);
  156. }
  157. /* 5、创建设备 */
  158. keydev.device = device_create(keydev.class, NULL, keydev.devid, NULL, KEY_NAME);
  159. if (IS_ERR(keydev.device)) {
  160. return PTR_ERR(keydev.device);
  161. }
  162. return 0;
  163. }
  164. /*
  165. * @description : 驱动出口函数
  166. * @param : 无
  167. * @return : 无
  168. */
  169. static void __exit mykey_exit(void)
  170. {
  171. /* 注销字符设备驱动 */
  172. gpio_free(keydev.key_gpio);
  173. cdev_del(&keydev.cdev);/* 删除cdev */
  174. unregister_chrdev_region(keydev.devid, KEY_CNT); /* 注销设备号 */
  175. device_destroy(keydev.class, keydev.devid);
  176. class_destroy(keydev.class);
  177. }
  178. module_init(mykey_init);
  179. module_exit(mykey_exit);
  180. MODULE_LICENSE("GPL");
  181. MODULE_AUTHOR("z");
  1. key.c

  1. #include "key.h"
  2. int key(void)
  3. {
  4. struct input_event in_ev = {0};
  5. int fd = -1;
  6. int value = -1;
  7. /* 打开文件 */
  8. if (0 > (fd = open("/dev/input/event5", O_RDONLY))) {
  9. perror("open error");
  10. exit(-1);
  11. }
  12. /* 循环读取数据 */
  13. if (sizeof(struct input_event) !=
  14. read(fd, &in_ev, sizeof(struct input_event))) {
  15. perror("read error");
  16. exit(-1);
  17. }
  18. if (EV_KEY == in_ev.type) { //按键事件
  19. switch (in_ev.value) {
  20. case 0:
  21. printf("code<%d>: 检测到按键松开\n", in_ev.code);
  22. return 0;
  23. break;
  24. case 1:
  25. printf("code<%d>: 检测到按键按下\n", in_ev.code);
  26. return 1;
  27. break;
  28. case 2:
  29. //printf("code<%d>: 长按\n", in_ev.code);
  30. return 2;
  31. break;
  32. }
  33. }
  34. return 0;
  35. }
  1. camera.h

  1. #ifndef __CAMERA_H
  2. #define __CAMERA_H
  3. #include <stdio.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <fcntl.h>
  7. #include <stdlib.h>
  8. #include <unistd.h>
  9. #include <sys/ioctl.h>
  10. #include <linux/videodev2.h>
  11. #include <string.h>
  12. #include <sys/mman.h>
  13. int take_Photo(char *name);
  14. int camera_Init(void);
  15. void close_camera(void);
  16. #endif
  1. camera.c

  1. struct v4l2_format vfmt;
  2. vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头采集
  3. //设置宽高(宽高有要求,不是说可以随便设置,宽和高的倍数固定)
  4. vfmt.fmt.pix.width = 1280;
  5. vfmt.fmt.pix.height = 720;
  6. //设置采集格式
  7. vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
  8. ret = ioctl(fd,VIDIOC_S_FMT,&vfmt);
  9. if(ret < 0)
  10. {
  11. perror("格式设置错误!");
  12. }
  13. if(vfmt.fmt.pix.width == 1280 && vfmt.fmt.pix.height == 720 && vfmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)
  14. {
  15. printf("采集格式设置成功!\n\n");
  16. }else
  17. {
  18. printf("设置失败!\n");
  19. return -1;
  20. }
  21. return 0;
  22. }
  23. int take_Photo(char *name)
  24. {
  25. //申请内核缓冲区队列
  26. struct v4l2_requestbuffers reqbuffer;
  27. reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//设置采集格式
  28. //申请4个缓冲区
  29. reqbuffer.count = 4;
  30. //指定映射方式
  31. reqbuffer.memory = V4L2_MEMORY_MMAP;
  32. ret = ioctl(fd,VIDIOC_REQBUFS,&reqbuffer);
  33. if(ret < 0)
  34. {
  35. perror("申请队列空间失败!");
  36. }
  37. //将内核的缓冲区队列映射到用户空间
  38. struct v4l2_buffer mapbuffer;
  39. mapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//指定type
  40. for(int i = 0; i < 4; i++)//查询出缓冲区
  41. {
  42. mapbuffer.index = i;
  43. ret = ioctl(fd,VIDIOC_QUERYBUF,&mapbuffer);//从内核空间中查询一个空间做映射
  44. if(ret < 0)
  45. {
  46. perror("查询内核空间失败");
  47. }
  48. mptr[i] =(unsigned char *)mmap(NULL,mapbuffer.length,PROT_READ | PROT_WRITE,MAP_SHARED,fd,mapbuffer.m.offset);
  49. size[i]=mapbuffer.length;
  50. //使用完毕,“放回去”
  51. ret = ioctl(fd,VIDIOC_QBUF,&mapbuffer);
  52. if(ret < 0)
  53. {
  54. perror("放回失败!");
  55. }
  56. }
  57. //开始采集
  58. type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  59. ret = ioctl(fd, VIDIOC_STREAMON, &type);
  60. if(ret < 0)
  61. {
  62. perror("开启失败");
  63. }
  64. //从队列中提取一帧数据
  65. struct v4l2_buffer readbuffer;
  66. readbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  67. ret = ioctl(fd, VIDIOC_DQBUF, &readbuffer);
  68. if(ret < 0)
  69. {
  70. perror("提取数据失败");
  71. }
  72. FILE *file=fopen(name, "w+");
  73. fwrite(mptr[readbuffer.index], readbuffer.length, 1, file);//写数据
  74. fclose(file);//关闭文件
  75. //通知内核已经使用完毕
  76. ret = ioctl(fd, VIDIOC_QBUF, &readbuffer);
  77. if(ret < 0)
  78. {
  79. perror("放回队列失败");
  80. }
  81. //停止采集
  82. ret = ioctl(fd, VIDIOC_STREAMOFF, &type);
  83. //释放映射
  84. for(int i=0; i<4; i++)
  85. {
  86. munmap(mptr[i], size[i]);
  87. }
  88. return 0;
  89. }
  90. void close_camera(void)
  91. {
  92. //关闭设备
  93. close(fd);
  94. }
  1. main.c

  1. #include "camera.h"
  2. #include "key.h"
  3. int main(int argc, char* argv[])
  4. {
  5. int key_val,num; // key_val:用于存放按键状态;num:用于存放用户输入的图像采集数量
  6. int i = 0; // 用于记录当前拍照次数
  7. char name[20] = {0}; // 用于存放照片文件名
  8. printf("\n/***********图像采集************/\r\n\n");
  9. printf("/*******按下按键采集图像********/\r\n\n");
  10. printf("请输入要采集图像的数量:");
  11. scanf("%d",&num);
  12. camera_Init(); // 初始化摄像头
  13. printf("/*******摄像头初始化成功********/\r\n\n");
  14. while(1)
  15. {
  16. key_val = key(); // 获取按键状态
  17. if(key_val == 1) // 如果按键被按下,则执行拍照
  18. {
  19. i++;
  20. sprintf(name,"./JPG/%d.jpg",i); // 照片的位置:./JPG/ 照片名字:i.jpg
  21. printf("进行第%d次拍摄\r\n\n",i);
  22. take_Photo(name); // 拍照
  23. printf("/***********拍照成功************/\r\n");
  24. }
  25. if(i>=num) // 如果到达采集的数量,跳出循环
  26. break;
  27. }
  28. printf("/********正在关闭摄像头*********/\r\n\n");
  29. close_camera(); // 关闭摄像头
  30. }

7.编写Makefile文件

因为这里涉及的源文件比较多,使用我们是Makefile来进行编译链接源文件。

  1. KERN_DIR = /home/usr/buildroot-100ask_t113-pro/buildroot/output/build/linux-origin_master
  2. #这里的路径需要根据自己的情况进行修改
  3. all:
  4. make -C $(KERN_DIR) M=`pwd` modules
  5. $(CROSS_COMPILE)gcc -o camera_App main.c camera.c key.c
  6. clean:
  7. make -C $(KERN_DIR) M=`pwd` modules clean
  8. rm -rf modules.order
  9. rm -f camera_App
  10. 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

成功执行

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/762259
推荐阅读
相关标签
  

闽ICP备14008679号