树莓派3B+驱动开发(2)- LED驱动(传统模式)
github主页:https://github.com/snqx-lqh
本项目github地址:https://github.com/snqx-lqh/RaspberryPiDriver
本项目硬件地址:https://oshwhub.com/from_zero/shu-mei-pai-kuo-zhan-ban
欢迎交流
笔记说明
如我在驱动开发总览中说的那样,一般的驱动开发模式就是有3种。
传统设备驱动模式:将所有的资源分配放进一个文件中。
PlatformDevice/PlatformDriver模式:资源放进PlatformDevice,实现驱动放进PlatformDriver文件。
设备树/PlatformDriver模式:资源放进设备树,实现驱动放进PlatformDriver文件。设备树一种是提前编译内核的时候就编译好,还有一种方式是添加设备树插件。
这一节主要说明传统的模式,就是将资源全部放进一个文件中处理。
主要参考的文章:
正点原子《I.MX6U 嵌入式 Linux 驱动开发指南 V1.81》
韦东山《01_嵌入式Linux应用开发完全手册V5.1_IMX6ULL_Pro开发板》
本节源码路径02_Firmware/02_led_drv_cdev
开发流程
1、初始化一个设备操作函数结构体 struct file_operations
,并注册内部函数。该结构体就是这个模块文件读写的相关操作
2、编写入口函数,在这个里面注册设备,并将前面定义的file_operations
结构体传入注册函数。自动分配节点需要创建类后创建设备节点。
3、编写出口函数,将设备注销。删除类和设备节点。
4、在入口函数完善IO引脚初始化,出口函数完善引脚相关处理。
5、在设备操作函数中完善各个操作函数的实现逻辑。
需要的头文件
先说明这部分代码需要包含的头文件。
#include <linux/init.h> //用于定义模块初始化和清理相关的宏以及函数原型等内容
#include <linux/module.h> //包含了众多用于构建内核模块的基本定义、宏和函数原型
#include <asm/io.h> //用于处理输入 / 输出操作,尤其是和硬件底层的内存映射 I/O 相关的操作
#include <linux/string.h> //提供了一系列字符串处理相关的函数
#include <linux/fs.h> //定义了文件系统操作的各种结构体、函数原型等内容
#include <linux/uaccess.h> //用于处理用户空间(User Space)和内核空间(Kernel Space)之间的数据拷贝操作
#include <linux/cdev.h> //定义了字符设备相关的结构体
设备操作函数结构体
我们需要定义一个struct file_operations
结构体,以供我们后面操作这个模块,并且需要注册这个结构体中的操作函数。
// 通过文件读取,得到当前LED的状态
ssize_t led_drv_read(struct file* filp, char __user* buf, size_t len, loff_t* off)
{return 0;
}// 通过向文件写入LED状态,控制LED灯
ssize_t led_drv_write(struct file* filp, const char __user* buf, size_t len, loff_t* off)
{return 0;
}const struct file_operations led_fops = {.owner = THIS_MODULE,.read = led_drv_read,.write = led_drv_write,
};
编写入口函数
入口函数就是我们加载这个模块的时候会调用的函数,这里我们将注册一个字符型设备,并且动态的为其分配设备号,其实还有静态方法,指定设备号分配,可以看正点原子教程,这里不做说明,因为感觉没有动态好用。
static dev_t led_dev_num = 0; // 设备编号
static struct cdev led_cdev; // 字符设备结构体
static struct class *class = NULL; //类
static struct device *device = NULL; //设备static int __init led_drv_cdev_init(void)
{// 将该模块注册为一个字符设备,并动态分配设备号if (alloc_chrdev_region(&led_dev_num, 0, 1, "led_drv")) {printk(KERN_ERR"failed to register kernel module!\n");return -1;}printk(KERN_INFO"led_drv device major & minor is [%d:%d]\n", MAJOR(led_dev_num), MINOR(led_dev_num));//初始化并添加一个cdevcdev_init(&led_cdev, &led_fops);cdev_add(&led_cdev, led_dev_num, 1);//创建类class = class_create("led_drv");if (IS_ERR(class)) {return PTR_ERR(class);}//创建设备device = device_create(class, NULL, led_dev_num, NULL, "led_drv");if (IS_ERR(device)) {return PTR_ERR(device);}return 0;
}module_init(led_drv_cdev_init);
为什么要使用创建类和设备,因为不使用的话就需要自己加载模块后创建,使用以下指令创建设备节点文件:
mknod /dev/led_drv c 200 0
“c”表示这是个字符设备,“200”是设备的主设备号,“0”是设备的次设备号。主设备号不一定是200,我是动态生成的,需要在内核加载信息中去查看,因为我在代码中动态申请设备号后写了个打印,使用如下指令:
demsg | tail
但是这样没有创建类和设备方便,所以就使用创建类和设备了。
编写出口函数
出口函数就是,我们在注销设备的时候会执行的函数,一般我们需要注销我们前面定义的字符设备。
static void __exit led_drv_cdev_exit(void)
{// 释放字符设备cdev_del(&led_cdev);unregister_chrdev_region(led_dev_num, 1);//删除类和设备device_destroy(class, led_dev_num);class_destroy(class);
}module_exit(led_drv_cdev_exit);
出口&入口函数完善引脚初始化
我们把框架搭好后,就可以开始初始化引脚相关的寄存器了,其实操作寄存器的方式和单片机感觉很像,但是内核不能直接操作寄存器物理地址,我们需要先把寄存器相关地址,映射到虚拟地址,然后进行操作。
首先就是虚拟地址的映射,具体操作相关如下,映射后我们才能使用相关的寄存器。
// 寄存器地址
#define BCM2837_GPIO_FSEL0_BASE 0x3F200000 // GPIO功能选择寄存器0
#define BCM2837_GPIO_FSEL1_BASE 0x3F200004 // GPIO功能选择寄存器1
#define BCM2837_GPIO_FSEL2_BASE 0x3F200008 // GPIO功能选择寄存器2
#define BCM2837_GPIO_SET0_BASE 0x3F20001C // GPIO置位寄存器0
#define BCM2837_GPIO_CLR0_BASE 0x3F200028 // GPIO清零寄存器0
#define BCM2837_GPIO_LEV0_BASE 0x3F200034 // GPIO清零寄存器0 // 寄存器对应的虚拟ioremap后的地址,会在后面初始化
static void __iomem *BCM2837_GPIO_FSEL0 = NULL;
static void __iomem *BCM2837_GPIO_FSEL1 = NULL;
static void __iomem *BCM2837_GPIO_FSEL2 = NULL;
static void __iomem *BCM2837_GPIO_SET0 = NULL;
static void __iomem *BCM2837_GPIO_CLR0 = NULL;
static void __iomem *BCM2837_GPIO_LEV0 = NULL;static int __init led_drv_cdev_init(void)
{// 将树莓派引脚控制相关的寄存器进行虚拟地址映射,使得可以控制对应寄存器BCM2837_GPIO_FSEL0 = ioremap(BCM2837_GPIO_FSEL0_BASE, 0x04);BCM2837_GPIO_FSEL1 = ioremap(BCM2837_GPIO_FSEL1_BASE, 0x04);BCM2837_GPIO_FSEL2 = ioremap(BCM2837_GPIO_FSEL2_BASE, 0x04);BCM2837_GPIO_SET0 = ioremap(BCM2837_GPIO_SET0_BASE , 0x04);BCM2837_GPIO_CLR0 = ioremap(BCM2837_GPIO_CLR0_BASE , 0x04);BCM2837_GPIO_LEV0 = ioremap(BCM2837_GPIO_LEV0_BASE , 0x04);// 注册字符设备、创建类和设备// ************** //return 0;
}
我们可以使用映射后的寄存器写一些对寄存器的读写操作来控制引脚电平的读写。
// 想要控制的LED灯,BCM引脚定义,这是设计的扩展板上的定义
#define LED2 17
#define LED3 27
#define LED4 22
#define LED5 23#define LED_OUTPUT 1
#define LED_INPUT 0/*** @brief 设置对应引脚的高低电平* @param pin 需要设置的引脚* @param level 1是高电平 0是低电平*/
void gpio_set_level(int pin, int level)
{// 通过想要的电平判断现在想要控制的寄存器void* reg = (level ? BCM2837_GPIO_SET0 : BCM2837_GPIO_CLR0);iowrite32(1 << pin, reg);
}/*** @brief 获得引脚的电平* @param pin 需要获得的引脚* @return 1是高电平 0是低电平*/
static int gpio_get_level(int pin)
{int ret = 0;int pin_level = 0;// 读取引脚电平寄存器pin_level = ioread32( BCM2837_GPIO_LEV0 );// 将想要读取的引脚的状态,提取出来ret = (1 << pin );ret = ret & pin_level;if(ret){return 1;}else{return 0;}
}/*** @brief 设置GPIO的寄存器,配置GPIO是输入还是输出* @param pin 需要设置的引脚* @param mode 1是输出模式 0是输入模式*/
static void gpio_set_mode(int pin, int mode)
{void *reg = NULL;int val = 0;int pin_ctl = 0;// 通过pin号来确定要控制的寄存器if(pin < 10){reg = BCM2837_GPIO_FSEL0;}else if(pin < 20){reg = BCM2837_GPIO_FSEL1;}else if(pin < 30){reg = BCM2837_GPIO_FSEL2;}// 比如我要控制11号脚,就是要控制BCM2837_GPIO_FSEL1_BASE的1号位置的3个位。pin_ctl = pin % 10;// 将对应的gpio的功能选择位全部写0val = ~(7 << (pin_ctl * 3));val &= ioread32(reg);// 控制设置对应的gpio的功能选择位写 000 还是 001val |= (mode << (pin_ctl * 3));iowrite32(val, reg);
}
然后在初始化的时候我们就可以初始化我们的引脚
static int __init led_drv_cdev_init(void)
{// 寄存器进行虚拟地址映射// ************** //// 控制引脚输入输出状态gpio_set_mode(LED2, LED_OUTPUT);gpio_set_mode(LED3, LED_OUTPUT);gpio_set_mode(LED4, LED_OUTPUT);gpio_set_mode(LED5, LED_OUTPUT);// 设置引脚电平gpio_set_level(LED2, 0);gpio_set_level(LED3, 0);gpio_set_level(LED4, 0);gpio_set_level(LED5, 0);// 注册字符设备、创建类和设备// ************** //return 0;
}
在出口函数完善相关引脚处理,主要是释放内存映射。
static void __exit led_drv_cdev_exit(void)
{// 设置电平为高gpio_set_level(LED2, 1);gpio_set_level(LED3, 1);gpio_set_level(LED4, 1);gpio_set_level(LED5, 1);// 取消gpio物理内存映射iounmap(BCM2837_GPIO_FSEL0);iounmap(BCM2837_GPIO_FSEL1);iounmap(BCM2837_GPIO_FSEL2);iounmap(BCM2837_GPIO_SET0);iounmap(BCM2837_GPIO_CLR0);iounmap(BCM2837_GPIO_LEV0);// 释放字符设备、删除类和设备// ************** //
}
设备操作函数完善逻辑
我们之前不是写了write和read的操作函数吗,现在就需要完善这些操作函数,使得用户文件在调用这个文件的时候可以操作gpio进行控制。
先解释读函数,这里比较重要的操作就是copy_to_user
,他会把第二个参数里面的值复制到第一个参数中去,这里的buf
就是我们用户层想读的值,len
是用户写的长度。
#define MIN(a, b) (a < b ? a : b)// 通过文件读取,得到当前LED的状态
ssize_t led_drv_read(struct file* filp, char __user* buf, size_t len, loff_t* off)
{int ret = 0;int char_len = 0;char led_state[4];printk("%s %s line %d\r\n", __FILE__, __FUNCTION__, __LINE__);led_state[0] = gpio_get_level(LED2);led_state[1] = gpio_get_level(LED3);led_state[2] = gpio_get_level(LED4);led_state[3] = gpio_get_level(LED5);char_len = sizeof(led_state);int real_len = MIN(len,char_len);ret = copy_to_user(buf, led_state, real_len);return ret < 0 ? ret : real_len;
}
然后是写函数,其实就和读差不多了,主要是copy_from_user
,能够把用户传进的值进行复制处理。
// 通过向文件写入LED状态,控制LED灯
ssize_t led_drv_write(struct file* filp, const char __user* buf, size_t len, loff_t* off)
{int ret = 0;char led_state[4];int char_len = 0;printk("%s %s line %d\r\n", __FILE__, __FUNCTION__, __LINE__);char_len = sizeof(led_state);int real_len = MIN(len,char_len);ret = copy_from_user(led_state, buf, real_len);switch (led_state[0]){case 0:gpio_set_level(LED2, led_state[1]);break; case 1:gpio_set_level(LED3, led_state[1]);break;case 2:gpio_set_level(LED4, led_state[1]);break;case 3:gpio_set_level(LED5, led_state[1]);break;default:break;}return 0;
}
到这里,这个驱动文件就编写完成了,后面就是编译加载了。
编写应用层函数
驱动编写完成后,我们需要有一个应用层函数来调用这个驱动,写出以下示例。主要功能就是打开驱动,写和读驱动的内容。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>/** sudo ./led_drv_cdev_app -w 0 0* sudo ./led_drv_cdev_app -r*/int main(int argc, char **argv)
{int fd;char writeBuff[3];char readBuff[5];int len;if (argc < 2){printf("Usage: %s -w <string>\n", argv[0]);printf(" %s -r\n", argv[0]);return -1;}fd = open("/dev/led_drv", O_RDWR);if (fd == -1){printf("can not open file /dev/led_drv\n");return -1;}if ((0 == strcmp(argv[1], "-w")) && (argc == 4)){writeBuff[0] = (char)atoi(argv[2]);writeBuff[1] = (char)atoi(argv[3]);writeBuff[2] = '\0';printf("write : %d, %d\n", writeBuff[0], writeBuff[1]);write(fd, writeBuff, 3);}else if((0 == strcmp(argv[1], "-r")) && (argc == 2)){len = read(fd, readBuff, 5);readBuff[4] = '\0';printf("pin read : 0x%x, 0x%x, 0x%x, 0x%x\n", readBuff[0], readBuff[1], readBuff[2], readBuff[3]);}else{printf("APP Failed\n");}close(fd);return 0;
}
编译加载
makefile主要参考正点原子和韦东山的写法。这个Makefile会把驱动模块和应用程序一起编译。
# 模块驱动,必须以obj-m=xxx形式编写
obj-m = led_drv_cdev.oKDIR = /home/linux-rpi-6.6.y/
CROSS = ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
CROSS_COMPILE = arm-linux-gnueabihf-all:$(MAKE) -C $(KDIR) M=$(PWD) $(CROSS) modules$(CROSS_COMPILE)gcc -o led_drv_cdev_app led_drv_cdev_app.c.PHONY: clean
clean:$(MAKE) -C $(KDIR) M=`pwd` $(CROSS) clean
makefile编译完成后,直接make就行
make
将make生成的文件,如我上文写了脚本,通过scp传到树莓派上。具体的内容参数,自己修改。
#!/bin/shsendfile="led_drv_cdev.ko led_drv_cdev_app"
pi_user=pi
pi_ip=192.168.2.149
pi_dir=/home/pi/RpiDriver/02_led_drv_cdevscp ${sendfile} ${pi_user}@${pi_ip}:${pi_dir}
在树莓派上,我们需要加载并使用这个模块。
sudo insmod led_drv_cdev.kosudo ./led_drv_cdev_app -w 0 1
sudo ./led_drv_cdev_app -rsudo rmmod led_drv_cdev
可以使用dmesg查看运行过程中的变化
dmesg | tail
相关文章:
树莓派3B+驱动开发(2)- LED驱动(传统模式)
github主页:https://github.com/snqx-lqh 本项目github地址:https://github.com/snqx-lqh/RaspberryPiDriver 本项目硬件地址:https://oshwhub.com/from_zero/shu-mei-pai-kuo-zhan-ban 欢迎交流 笔记说明 如我在驱动开发总览中说的那样&…...
超详细搭建PhpStorm+PhpStudy开发环境
刚开始接触PHP开发,搭建开发环境是第一步,网上下载PhpStorm和PhpStudy软件,怎样安装和激活就不详细说了,我们重点来看一看怎样搭配这两个开发环境。 前提:现在假设你已经安装完PhpStorm和PhpStudy软件。 我的PhpStor…...
分析比对vuex和store模式
在 Vue 中,Vuex 和 store 模式 是两个不同的概念,它们紧密相关,主要用于管理应用的状态。下面我会详细介绍这两个概念,并通过例子帮助你更好地理解。 1. Vuex 是什么? Vuex 是 Vue.js 的一个状态管理库,用…...
C# 网络编程--基础核心内容
在现今软件开发中,网络编程是非常重要的一部分,本文简要介绍下网络编程的概念和实践。 C#网络编程的主要内容包括以下几个方面: : 上图引用大佬的图,大家也关注一下,有技术有品质,有国有家,情…...
【C++游戏程序】easyX图形库还原游戏《贪吃蛇大作战》(三)
承接上一篇文章:【C游戏程序】easyX图形库还原游戏《贪吃蛇大作战》(二),我们这次来补充一些游戏细节,以及增加吃食物加长角色长度等设定玩法,也是本游戏的最后一篇文章。 一.玩家边界检测 首先是用来检测…...
uni-app H5端使用注意事项 【跨端开发系列】
🔗 uniapp 跨端开发系列文章:🎀🎀🎀 uni-app 组成和跨端原理 【跨端开发系列】 uni-app 各端差异注意事项 【跨端开发系列】uni-app 离线本地存储方案 【跨端开发系列】uni-app UI库、框架、组件选型指南 【跨端开…...
SpringBoot中的@Configuration注解
在Spring Boot中,Configuration注解扮演着非常重要的角色,它是Spring框架中用于定义配置类的一个核心注解。以下是Configuration注解的主要作用: 定义配置类: 使用Configuration注解的类表示这是一个配置类,Spring容器…...
十二、路由、生命周期函数
router路由 页面路由指的是在应用程序中实现不同页面之间的跳转,以及数据传递。通过 Router 模块就可以实现这个功能 2.1创建页面 之前是创建的文件,使用路由的时候需要创建页面,步骤略有不同 方法 1:直接右键新建Page(常用)方法 2:单独添加页面并配置2.1.1直接右键新建…...
【蓝桥杯每日一题】X 进制减法
X 进制减法 2024-12-6 蓝桥杯每日一题 X 进制减法 贪心 进制转换 题目大意 进制规定了数字在数位上逢几进一。 XX 进制是一种很神奇的进制, 因为其每一数位的进制并不固定!例如说某 种 XX 进制数, 最低数位为二进制, 第二数位为十进制, 第三数位为八进制, 则 XX 进制…...
《蓝桥杯比赛规划》
大家好啊!我是NiJiMingCheng 我的博客:NiJiMingCheng 这节课我们来分享蓝桥杯比赛规划,好的规划会给我们的学习带来良好的收益,废话少说接下来就让我们进入学习规划吧,加油哦!!! 一、…...
C++算法练习day70——53.最大子序和
题目来源:. - 力扣(LeetCode) 题目思路分析 题目:寻找最大子数组和(也称为最大子序和)。 给定一个整数数组 nums,找到一个具有最大和的连续子数组(子数组最少包含一个元素&#x…...
import是如何“占领满屏“
import是如何“占领满屏“的? 《拒绝使用模块重导(Re-export)》 模块重导是一种通用的技术。在腾讯、字节、阿里等各大厂的组件库中都有大量使用。 如:字节的arco-design组件库中的组件:github.com/arco-design… …...
ceph /etc/ceph-csi-config/config.json: no such file or directory
环境 rook-ceph 部署的 ceph。 问题 kubectl describe pod dragonfly-redis-master-0Warning FailedMount 7m59s (x20 over 46m) kubelet MountVolume.MountDevice failed for volume "pvc-c63e159a-c940-4001-bf0d-e6141634cc55" : rpc error: cod…...
C语言——验证“哥德巴赫猜想”
问题描述: 验证"哥德巴赫猜想" 任何一个大于2的偶数都可以表示为两个质数之和。例如,4可以表示为22,6可以表示为33,8可以表示为35等 //验证"哥德巴赫猜想" //任何一个大于2的偶数都可以表示为两个质数之和…...
Flourish笔记:柱状图(Column chart (grouped))
文章目录 样式设定Chart Type:图表类型Controls & Filters:展示方式Colors:颜色bars:柱子的调整labels:柱子数字标注X axis:横坐标标签Y axis:纵坐标标签Plot BackgroundNumber FormatingLe…...
深度学习案例:DenseNet + SE-Net
本文为为🔗365天深度学习训练营内部文章 原作者:K同学啊 一 回顾DenseNet算法 DenseNet(Densely Connected Convolutional Networks)是一种深度卷积神经网络架构,提出的核心思想是通过在每一层与前面所有层进行直接连接…...
excel文件合并,每个excel名称插入excel列
import pandas as pd import os # 设置文件夹路径 folder_path rC:\test # 替换为您的下载文件夹路径 output_file os.path.join(folder_path, BOM材料.xlsx) # 创建一个空的 DataFrame 用于存储合并的数据 combined_data pd.DataFrame() # 遍历文件夹中的所有文件 for …...
Linux 如何设置特殊权限?
简介 通过使用 setuid、setgid 、sticky,它们是 Linux 中的特殊权限,可以对文件和目录的访问和执行方式提供额外的控制。 命令八进制数字功能setuid4当执行文件时,它以文件所有者的权限运行,而不是执行它的用户的权限运行。setg…...
零基础如何使用ChatGPT快速学习Python
引言 AI编程时代来临,没有编程基础可以快速上车享受时代的红利吗?答案是肯定的。本文旨在介绍零基础如何利用ChatGPT快速学习Python编程语言,开启AI编程之路。解决的问题包括:传统学习方式效率低、缺乏互动性以及学习资源质量参差…...
【开源】一款基于SpringBoot 的全开源充电桩平台
一、下载项目文件 下载源码项目文件口令:动作璆璜量子屏多好/~d1b8356ox2~:/复制口令后,进入夸克网盘app即可保存(如果复制到夸克app没有跳转资源,可以复制粘贴口令到夸克app的搜索框也可以打开(不用点搜索按钮&#…...
AI - RAG中的状态化管理聊天记录
AI - RAG中的状态化管理聊天记录 大家好,今天我们来聊聊LangChain和LLM中一个重要的话题——状态化管理聊天记录。在使用大语言模型(LLM)的时候,聊天记录(History)和状态(State)管理是非常关键的。那我们先…...
JAVA安全—SpringBoot框架MyBatis注入Thymeleaf模板注入
前言 之前我们讲了JAVA的一些组件安全,比如Log4j,fastjson。今天讲一下框架安全,就是这个也是比较常见的SpringBoot框架。 SpringBoot框架 Spring Boot是由Pivotal团队提供的一套开源框架,可以简化spring应用的创建及部署。它提…...
【STM32系列】提升ADC采样精度的方法
资料地址 兆易创新GigaDevice-资料下载兆易创新GD32 MCU ADC简介 ADC转换包括采样、保持、量化、编码四个步骤。的采样电容上,即在采样开关 SW 关闭的过程中,外部输入信号通过外部的输入电阻 RAIN 和以及 ADC 采样电阻 RADC 对采样电容 CADC 充电。采样…...
前端面试如何出彩
1、原型链和作用域链说不太清,主要表现在寄生组合继承和extends继承的区别和new做了什么。2、推荐我的两篇文章:若川:面试官问:能否模拟实现JS的new操作符、若川:面试官问:JS的继承 3、数组构造函数上有哪些…...
Linux 切换用户的两种方法
sudo -su user1 与 su - user1 都可以让当前用户切换到 user1 的身份执行命令或进入该用户的交互式 Shell。但它们在权限认证方式、环境变量继承和 Shell 初始化过程等方面存在一些差异。 权限认证方式 su - user1 su 是 “switch user” 的缩写,默认情况下需要你输…...
Spring Boot 3 中Bean的配置和实例化详解
一、引言 在Java企业级开发领域,Spring Boot凭借其简洁、快速、高效的特点,迅速成为了众多开发者的首选框架。Spring Boot通过自动配置、起步依赖等特性,极大地简化了Spring应用的搭建和开发过程。而在Spring Boot的众多核心特性中ÿ…...
Vue实现留言板(实现增删改查)注意:自己引入Vue.js哦
代码: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><scri…...
IDEA创建Spring Boot项目配置阿里云Spring Initializr Server URL【详细教程-轻松学会】
1.首先打开idea选择新建项目 2.选择Spring Boot框架(就是选择Spring Initializr这个) 3.点击中间界面Server URL后面的三个点更换为阿里云的Server URL Idea中默认的Server URL地址:https://start.spring.io/ 修改为阿里云Server URL地址:https://star…...
读取电视剧MP4视频的每一帧,检测出现的每一个人脸并保存
检测效果还不错,就是追踪有点难做 import cv2 import mediapipe as mp import os from collections import defaultdict# pip install msvc-runtime# 初始化OpenCV的MultiTracker # multi_tracker = cv2.MultiTracker_create() # multi_tracker = cv2.legacy.MultiTracker_cre…...
HTML前端开发-- Iconfont 矢量图库使用简介
一、SVG 简介及基础语法 1. SVG 简介 SVG(Scalable Vector Graphics)是一种基于 XML 的矢量图形格式,用于在网页上显示二维图形。SVG 图形可以无限缩放而不会失真,非常适合用于图标、图表和复杂图形。SVG 文件是文本文件&#x…...
企业网站类型有哪些/网站测试报告
文章目录1 基本算法1.1 原地切分1.2 边界1.3 随机性1.4 终止循环1.5 切分元素重复1.6 终止递归2 备注1 基本算法 快速排序是一种分治的排序算法。它将一个数组分成2个子数组,将两部分独立排序。听起来和归并排序很像,那么有什么不一样呢?…...
浙江省城乡建设厅官网/独立站seo建站系统
很多朋友可能都会纳闷,我们的手机明明都是国产的,可为什么手机里的文件夹都是英文的呢?有时候想要删除一些无用文件夹释放一些内存,但是又因为看不懂无从下手。不要着急,今天小编就给大家科普一波。一、为何都是英文名…...
今日头条网站什么语言做的/百度客服怎么联系
邮件发送类 来源网上 稍作调整。。。出处忘了 /** * 命名空间: EmailSend * 类 名: EmailSend * * 作者 变更内容 变更日期 * ───────────────────────────────── * XXX 初版 2018-10-31 0…...
都兰县建设局交通局网站/品牌推广方案案例
01背包: 有N件物品和一个容量为V的背包。(每件物品只有一件)第i件物品的费用是c[i],价值是v[i],求解将哪些物品装入背包使总价值最大。 转移方程:f[i][v]max{f[i-1][v],f[i-1][v-c[i]]w[i]},可以优化只用一维数组. 代…...
常州市新北区城乡建设局网站/公司策划推广
2018年已经过去,各行各业都在盘点这一年的成绩,随着国产手机品牌的崛起,曾经不可一世的苹果,也逐渐没落,如今我们熟悉的很多黑科技都出自于国产自主手机品牌。不得不承认的是,近年来国产手机完成了化茧成蝶…...
公安局松江分局网站/线上营销方式主要有哪些
目录Linux操作系统1.Linux操作命令2.在Linux中find和grep的区别?3.绝对路径用什么符号表示?4.当前目录、上层目录用什么表示?5.主目录用什么表示?6.怎么查看进程信息?7.保存文件并退出vi 编辑?8.怎么查看当前用户id&a…...