驱动(RK3588S)第九课时:多节点驱动与函数接口

news2024/11/15 15:44:01

目录

  • 一、多节点概念
    • 1、所用到的结构体说明
    • 2、函数接口主要是read和write函数
      • 2.1、把应用层的数据拷贝给底层
      • 2.2、把应用层的数据拷贝给底层
    • 3、应用层的read和write函数
    • 4、底层的read和write函数
    • 二、ioctl控制命令接口
      • 1、概念
      • 2、函数介绍应用层和驱动层
  • 三、代码与现象
    • 1.编写LED灯的多节点驱动实现流水灯
    • 2.编写KEY按键驱动实现底层按键按下反馈给应用层
    • 3.编写beep的驱动代码可以使用ioctl控制

学习目标:
1.编写LED灯的多节点驱动实现流水灯
2.编写KEY按键驱动实现底层按键按下反馈给应用层
3.编写beep的驱动代码可以使用ioctl控制

一、多节点概念

这里所谓的多节点驱动其实指的就是一个设备对应一个设备节点。比如我现在有 4 个 led 等,你怎么做到单独的去控制每一盏灯。此时如果你想单独的去操控一个 LED 灯,那么你就需要单独给他们每一个灯去申请注册一个设备节点。多节点对于一类设备他们的主设备号必然是一样的,只不过是一类设备当中的不同的子设备。
比如有四个灯,那么他们的设备编号如下:
LED1 crwx----------- 350 0 /dev/led1
LED2 crwx----------- 350 1 /dev/led2
LED3 crwx----------- 350 2 /dev/led3
LED4 crwx----------- 350 3 /dev/led4
此时你在去使用应用层的 open 去打开单独的一个硬件设备节点,这样你就可以单独的操作这个设备。

1、所用到的结构体说明

这里内核是通过你 open 打开对应节点的设备号去区分不同的设备的,靠的就是函数的参数。
int (*open) (struct inode *, struct file *);
struct inode {
i_rdev:如果索引节点代表设备文件,则表示设备的主设备号和次设备号
};
在这里插入图片描述
struct file{
void *private_data;
}
private_data:他是一个私有数据,他主要就是给其他函数使用的
在这里插入图片描述
在这里插入图片描述

2、函数接口主要是read和write函数

函数接口里主要讲解的就是 read 和 write是 对数据进行读写操作的。内核是不允许让应用层和底层进行直接数据交互的,就是为了保护内核的安全。那么内核层和应用能不能进行数据交互呢?肯定是可以的,但是必须要使用内核提供的函数,进行数据的交互。所谓的数据交互就是应用层把数据拷贝给内核层,内核层把数据拷贝给应用层这就是所谓的数据交互。上层的写函数和读函数是不能直接给底层的写函数和读函数进行数据,若想直接交互的话,这里就需要使用以下两个函数

2.1、把应用层的数据拷贝给底层

函数功能:把应用层的数据拷贝给底层
函数原型:unsigned long copy_from_user(void *to, const void __user *from,
unsigned long n)
函数头文件:#include <linux/uaccess.h>
函数参数:to:他就是你要保存应用层数据的位置,把要接受的数据保存到哪里
from:他就是底层写函数的里的 buf
n:就是数据的大小 — from
函数返回值:成功返回 0 失败返回负数

2.2、把应用层的数据拷贝给底层

函数功能:把内核层的数据拷贝给应用层
函数原型:unsigned long copy_to_user(void __user *to, const void *from,
unsigned long n)
函数头文件:#include <linux/uaccess.h>
函数参数:to:就是底层读函数里的 buf
from: 要拷贝底层的具体数据
n:就是数据的大小 即,from大小
函数返回值:成功返回 0 失败返回负数

3、应用层的read和write函数

ssize_t read(int fd, void *buf, size_t count)
参数:fd: — 打开文件的描述符
buf: — 读取数据保存的位置
count:---- 读取数据的大小

ssize_t write(int fd, const void *buf, size_t count)
参数:fd: — 打开文件的描述符
buf: — 你要写入文件的数据的保存位置
count:---- 写入数据的大小

4、底层的read和write函数

**ssize_t (*read) (struct file *fp, char __user buf, size_t size, loff_t offset)
参数:fp:保存文件信息的核心结构体
buf:保存底层给应用层读取数据的保存位置 ,里面就保存内核要给用层的数据。
size:就数据的大小 — 就是 buf 的大小
offset:当前文件光标的位置
**ssize_t (write) (struct file fp, const char __user buf, size_t size,loff_toffset)
参数:fp:保存文件信息的核心结构体
buf:保存应用层给底层写人数据保存位置
size:就数据的大小 — 就是 buf 的大小
offset:当前文件光标的位置
在这里插入图片描述

二、ioctl控制命令接口

1、概念

ioctl用来做一些控制命令接口 类似:msgctl、semctl、shmctl 等(ioctl 是设备
驱动程序中对设备的 I/O 通道进行管理的函数。所谓对 I/O 通道进行管理,就
是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等)
在这里插入图片描述

2、函数介绍应用层和驱动层

函数功能:做一些控制命令的接口
函数原型:int ioctl(int fd, unsigned long request, …)
函数头文件:#include <sys/ioctl.h>
函数参数:fd:就是 open 打开的文件描述符
request:cmd 就是控制的命令
函数返回值:成功返回 0 失败返回负数
函数功能:做控制的命令
函数原型:**long (unlocked_ioctl) (struct file fp, unsigned int cmd, unsigned long arg);
函数头文件:#include <linux/fs.h>
函数参数:fp:保存文件信息结构体
cmd:就是你应用层传递过来的命令 — request
arg:暂时不用管
函数返回值:成功返回 0 失败负数

三、代码与现象

1.编写LED灯的多节点驱动实现流水灯

内核层:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
int led[2]={0};
const char *devs_name[2]={"myqxjled1","myqxjled2"};
int i;
dev_t dev[2]={0};
struct cdev mydev;
struct class *myclass=NULL;

int myled_open (struct inode *inode, struct file *fp)
{
	if(inode->i_rdev==dev[0])
	{
		gpio_set_value(led[0],1);
	}
	if(inode->i_rdev==dev[1])
	{
		gpio_set_value(led[1],1);
	}
	printk("myled open 正确打开\n");
	return 0;
}

int myled_close (struct inode *inode, struct file *fp)
{
	if(inode->i_rdev==dev[0])
	{
		gpio_set_value(led[0],0);
	}
	if(inode->i_rdev==dev[1])
	{
		gpio_set_value(led[1],0);
	}
	printk("myled close 关闭正确\n");
	return 0;
}
struct file_operations myfops={
	.open = myled_open,
	.release = myled_close,
};

int myled_probe(struct platform_device *pdev)
{
	printk("探测函数:设备端和驱动端匹配成功\n");
	//led[0] led[1]返回的是gpio编口号
	led[0]=of_get_named_gpio(pdev->dev.of_node,"leds-gpios",0);//获得设备树的属性
 	led[1]=of_get_named_gpio(pdev->dev.of_node,"leds-gpios",1);
 	gpio_request(led[0], "led1 pc5");//21  申请你要使用 gpio 口的资源
	gpio_request(led[1], "led2 pc6");//22
	gpio_direction_output(led[0],0);//配置 gpio 口的工作模式
	gpio_direction_output(led[1],0);	
	alloc_chrdev_region(dev,0,2,"led");//动态申请设备号  linux2.6或杂项类型
	dev[1]=dev[0]+1;
	cdev_init(&mydev,&myfops);//初始化核心结构体
	cdev_add(&mydev,dev[0],2);//向内核去申请 linux2.6 字符设备
	myclass=class_create(THIS_MODULE,"class_led");//创建类
	if(myclass == NULL)
	{
		printk("class_create error\n");
		printk("class_create 类创建失败\n");
		return -1;
	}
	for(i=0;i<=1;i++)
	{
		device_create(myclass,NULL,dev[i],NULL,devs_name[i]);//自动创建设备节点
	}
 	return 0;
}

int myled_remove (struct platform_device *pdev)
{
	printk("移除函数成功\n");
	device_destroy(myclass,dev[0]);//销毁设备节点  在/dev/name ---device_create
	device_destroy(myclass,dev[1]);
	class_destroy(myclass);//销毁类 --class_create
	cdev_del(&mydev);//释放申请的字符设备  --cdev_add
	unregister_chrdev_region(dev[0],2);//释放申请的设备号 ---alloc_chrdev_region
	gpio_free(led[0]);// 释放 gpio 口资源 ----gpio_request
	gpio_free(led[1]);
	return 0;
}
struct of_device_id	mydev_node={
	.compatible="xyd-led",
};

struct platform_driver drv={
	.probe = myled_probe,
	.remove = myled_remove,
	.driver = {
		.name = "myxyd_leds",//与设备端必须保持一致
		.of_match_table = &mydev_node,
	},
};
static int __init myled_init(void)
{	
	platform_driver_register(&drv);
	return 0;
}
static void __exit myled_exit(void)
{
	platform_driver_unregister(&drv);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");

应用层:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
	int fd = 0;
	while(1)
		{
			fd = open("/dev/myqxjled1",O_RDWR); // --- 底层的open函数
			sleep(1);
			close(fd);//底层的close
			sleep(1);
			fd=open("/dev/myqxjled2",O_RDWR); // --- 底层的open函数
			sleep(1);
			close(fd);//底层的close
			sleep(1);			
		}
	return 0;
}

编译:

obj-m += led_driver.o #最终生成模块的名字就是 led.ko   
    
KDIR:=/home/stephen/RK3588S/kernel  #他就是你现在rk3588s里内核的路径 
    
CROSS_COMPILE_FLAG=/home/stephen/RK3588S/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-
    #这是你的交叉编译器路径 --- 这里你也要替换成你自己的交叉编译工具的路径
all:
	make -C $(KDIR) M=$(PWD) modules ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE_FLAG)
	aarch64-none-linux-gnu-gcc app.c -o app
    #调用内核层 Makefile 编译目标为 modules->模块 文件在当前路径
    # 架构  ARCH=arm64 
clean:
	rm -f  *.o *.mod.o *.mod.c *.symvers *.markers *.order app  *.mod

在这里插入图片描述
灯现象:
在这里插入图片描述

2.编写KEY按键驱动实现底层按键按下反馈给应用层

内核层:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
int key[2]={0};
int beep = 0;
int key_value = 0;
int beep_value[2] = {0};
dev_t dev;
struct cdev mydev;
struct class *myclass=NULL;

int myled_open (struct inode *inode, struct file *fp)
{
	printk("myled open 正确打开\n");
	return 0;
}

int myled_close (struct inode *inode, struct file *fp)
{
	printk("myled close 关闭正确\n");
	return 0;
}
ssize_t mykey_read (struct file *fp, char __user *buf, size_t size, loff_t *offset)
{
	unsigned long ret=0;
 	if(gpio_get_value(key[0])==0)
 	{
 		key_value=1;
 		printk("按键1被按下\n");
 	}
 	else if(gpio_get_value(key[1])==0)
 	{
 	 	key_value=2;
 		printk("按键2被按下\n");
 	}
	ret=copy_to_user(buf,&key_value,4);
	if(ret<0)
	{
		printk("copy_to_user 错误\n");
		return -1;
	}
	key_value=0;
	return 0;
}
ssize_t mykey_write (struct file *fp, const char __user *buf, size_t size, loff_t *offset)
{	
	unsigned long ret=0;
	ret=copy_from_user(beep_value,buf,4);
	if(ret<0)
	{
		printk("copy_from_user 错误\n");
		return -1;
	}
	if(beep_value[0]==1)
	{
		gpio_set_value(beep,beep_value[0]);
	}
	else if(beep_value[1]==0)
	{
		gpio_set_value(beep,beep_value[1]);
	}
	return 0;
}
struct file_operations myfops={
	.open = myled_open,
	.release = myled_close,
	.read = mykey_read,
	.write = mykey_write,
};

int myled_probe(struct platform_device *pdev)
{
	printk("探测函数:设备端和驱动端匹配成功\n");
	//led[0] led[1]返回的是gpio编口号
	key[0]=of_get_named_gpio(pdev->dev.of_node,"devices-gpios",2);//获得设备树的属性
 	key[1]=of_get_named_gpio(pdev->dev.of_node,"devices-gpios",3);
 	beep = of_get_named_gpio(pdev->dev.of_node,"devices-gpios",4);
 	gpio_request(key[0], "key1 pa7");//21  申请你要使用 gpio 口的资源
	gpio_request(key[1], "key2 pb1");//22
	gpio_request(beep,  "beep pa4");//36
	gpio_direction_input(key[0]);//配置 gpio 口的工作模式
	gpio_direction_input(key[1]);	
	gpio_direction_output(beep,0);//高电平叫
	alloc_chrdev_region(&dev,0,1,"led");//动态申请设备号  linux2.6或杂项类型
	cdev_init(&mydev,&myfops);//初始化核心结构体
	cdev_add(&mydev,dev,1);//向内核去申请 linux2.6 字符设备
	myclass=class_create(THIS_MODULE,"class_led");//创建类
	if(myclass == NULL)
	{
		printk("class_create error\n");
		printk("class_create 类创建失败\n");
		return -1;
	}
	device_create(myclass,NULL,dev,NULL,"mykey");//自动创建设备节点
 	return 0;
}

int myled_remove (struct platform_device *pdev)
{
	printk("移除函数成功\n");
	device_destroy(myclass,dev);//销毁设备节点  在/dev/name ---device_create
	class_destroy(myclass);//销毁类 --class_create
	cdev_del(&mydev);//释放申请的字符设备  --cdev_add
	unregister_chrdev_region(dev,1);//释放申请的设备号 ---alloc_chrdev_region
	gpio_free(key[0]);// 释放 gpio 口资源 ----gpio_request
	gpio_free(key[1]);
	gpio_free(beep);
	return 0;
}
struct of_device_id	mydev_node={
	.compatible="xyd-device",
};

struct platform_driver drv={
	.probe = myled_probe,
	.remove = myled_remove,
	.driver = {
		.name = "xyd-device",//与设备端必须保持一致
		.of_match_table = &mydev_node,
	},
};
static int __init myled_init(void)
{	
	platform_driver_register(&drv);
	return 0;
}
static void __exit myled_exit(void)
{
	platform_driver_unregister(&drv);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");

应用层:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
	int fd = 0;
	int beep_value[2]={1,0};
	int key_value=0;
	fd = open("/dev/mykey",O_RDWR); // --- 底层的open函数
	while(1)
		{
			write(fd,&beep_value[0],4);
			usleep(500000);
			write(fd,&beep_value[1],4);
			usleep(500000);
			read(fd,&key_value,4);
			if(key_value==1)
			{
				printf("第%d个按键按下\n",key_value);
			}
			else if(key_value==2)
			{
				printf("第%d个按键按下\n",key_value);
			}
			usleep(500000);
		}
	close(fd);//底层的close	
	return 0;
}

编译:

obj-m += led_driver.o #最终生成模块的名字就是 led.ko   
    
KDIR:=/home/stephen/RK3588S/kernel  #他就是你现在rk3588s里内核的路径 
    
CROSS_COMPILE_FLAG=/home/stephen/RK3588S/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-
    #这是你的交叉编译器路径 --- 这里你也要替换成你自己的交叉编译工具的路径
all:
	make -C $(KDIR) M=$(PWD) modules ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE_FLAG)
	aarch64-none-linux-gnu-gcc app.c -o app
    #调用内核层 Makefile 编译目标为 modules->模块 文件在当前路径
    # 架构  ARCH=arm64 
clean:
	rm -f  *.o *.mod.o *.mod.c *.symvers *.markers *.order app  *.mod

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.编写beep的驱动代码可以使用ioctl控制

驱动层:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
int key[2]={0};
int beep = 0;
int key_value = 0;
int beep_value[2] = {0};
dev_t dev;
struct cdev mydev;
struct class *myclass=NULL;

int myled_open (struct inode *inode, struct file *fp)
{
	printk("myled open 正确打开\n");
	return 0;
}

int myled_close (struct inode *inode, struct file *fp)
{
	printk("myled close 关闭正确\n");
	return 0;
}
ssize_t mykey_read (struct file *fp, char __user *buf, size_t size, loff_t *offset)
{
	unsigned long ret=0;
 	if(gpio_get_value(key[0])==0)
 	{
 		key_value=1;
 		printk("按键1被按下\n");
 	}
 	else if(gpio_get_value(key[1])==0)
 	{
 	 	key_value=2;
 		printk("按键2被按下\n");
 	}
	ret=copy_to_user(buf,&key_value,4);
	if(ret<0)
	{
		printk("copy_to_user 错误\n");
		return -1;
	}
	key_value=0;
	return 0;
}
ssize_t mykey_write (struct file *fp, const char __user *buf, size_t size, loff_t *offset)
{	
	unsigned long ret=0;
	ret=copy_from_user(&beep_value,buf,4);
	if(ret<0)
	{
		printk("copy_from_user 错误\n");
		return -1;
	}
	if(beep_value[0]==1)
	{
		gpio_set_value(beep,beep_value[0]);
	}
	else if(beep_value[1]==0)
	{
		gpio_set_value(beep,beep_value[1]);
	}
	return 0;
}
long mybeep_ioctl (struct file *fp, unsigned int cmd, unsigned long arg)
{
	if(cmd==1)
	{
		gpio_set_value(beep,1);
	}
	if(cmd==0)
	{
		gpio_set_value(beep,0);
	}
	return 0;
}
struct file_operations myfops={
	.open = myled_open,
	.release = myled_close,
	.read = mykey_read,
	.write = mykey_write,
	.unlocked_ioctl=mybeep_ioctl,
};

int myled_probe(struct platform_device *pdev)
{
	printk("探测函数:设备端和驱动端匹配成功\n");
	//led[0] led[1]返回的是gpio编口号
	key[0]=of_get_named_gpio(pdev->dev.of_node,"devices-gpios",2);//获得设备树的属性
 	key[1]=of_get_named_gpio(pdev->dev.of_node,"devices-gpios",3);
 	beep = of_get_named_gpio(pdev->dev.of_node,"devices-gpios",4);
 	gpio_request(key[0], "key1 pa7");//21  申请你要使用 gpio 口的资源
	gpio_request(key[1], "key2 pb1");//22
	gpio_request(beep,  "beep pa4");//36
	gpio_direction_input(key[0]);//配置 gpio 口的工作模式
	gpio_direction_input(key[1]);	
	gpio_direction_output(beep,0);//高电平叫
	alloc_chrdev_region(&dev,0,1,"led");//动态申请设备号  linux2.6或杂项类型
	cdev_init(&mydev,&myfops);//初始化核心结构体
	cdev_add(&mydev,dev,1);//向内核去申请 linux2.6 字符设备
	myclass=class_create(THIS_MODULE,"class_led");//创建类
	if(myclass == NULL)
	{
		printk("class_create error\n");
		printk("class_create 类创建失败\n");
		return -1;
	}
	device_create(myclass,NULL,dev,NULL,"mykey");//自动创建设备节点
 	return 0;
}

int myled_remove (struct platform_device *pdev)
{
	printk("移除函数成功\n");
	device_destroy(myclass,dev);//销毁设备节点  在/dev/name ---device_create
	class_destroy(myclass);//销毁类 --class_create
	cdev_del(&mydev);//释放申请的字符设备  --cdev_add
	unregister_chrdev_region(dev,1);//释放申请的设备号 ---alloc_chrdev_region
	gpio_free(key[0]);// 释放 gpio 口资源 ----gpio_request
	gpio_free(key[1]);
	gpio_free(beep);
	return 0;
}
struct of_device_id	mydev_node={
	.compatible="xyd-device",
};

struct platform_driver drv={
	.probe = myled_probe,
	.remove = myled_remove,
	.driver = {
		.name = "xyd-device",//与设备端必须保持一致
		.of_match_table = &mydev_node,
	},
};
static int __init myled_init(void)
{	
	platform_driver_register(&drv);
	return 0;
}
static void __exit myled_exit(void)
{
	platform_driver_unregister(&drv);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");

应用层:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
int main(int argc,char *argv[])
{
	int fd = 0;
	fd = open("/dev/mykey",O_RDWR); // --- 底层的open函数
	while(1)
		{
			ioctl(fd,1);
			usleep(500000);
			ioctl(fd,1);
			usleep(500000);
		}
	close(fd);//底层的close	
	return 0;
}

编译:

obj-m += led_driver.o #最终生成模块的名字就是 led.ko   
    
KDIR:=/home/stephen/RK3588S/kernel  #他就是你现在rk3588s里内核的路径 
    
CROSS_COMPILE_FLAG=/home/stephen/RK3588S/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-
    #这是你的交叉编译器路径 --- 这里你也要替换成你自己的交叉编译工具的路径
all:
	make -C $(KDIR) M=$(PWD) modules ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE_FLAG)
	aarch64-none-linux-gnu-gcc app.c -o app
    #调用内核层 Makefile 编译目标为 modules->模块 文件在当前路径
    # 架构  ARCH=arm64 
clean:
	rm -f  *.o *.mod.o *.mod.c *.symvers *.markers *.order app  *.mod

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2115504.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

三相直流无刷电机(BLDC)控制算法实现:BLDC有感启动算法思路分析

一枚从事路径规划算法、运动控制算法、BLDC/FOC电机控制算法、工控、物联网工程师&#xff0c;爱吃土豆。如有需要技术交流或者需要方案帮助、需求&#xff1a;以下为联系方式—V 方案1&#xff1a;通过霍尔传感器IO中断触发换相 1.1 整体执行思路 霍尔传感器U、V、W三相通…

使用 nuxi preview 命令预览 Nuxt 应用

title: 使用 nuxi preview 命令预览 Nuxt 应用 date: 2024/9/8 updated: 2024/9/8 author: cmdragon excerpt: 摘要:本文介绍了如何使用nuxi preview命令预览Nuxt.js应用,包括安装和准备环境、启动预览服务器的步骤,以及如何指定根目录和使用自定义.env文件等高级用法。通…

【H2O2|全栈】关于HTML(5)HTML基础(四)

HTML基础知识 目录 HTML基础知识 前言 准备工作 标签的具体分类&#xff08;四&#xff09; 本文中的标签在什么位置中使用&#xff1f; 表单&#xff08;一&#xff09; 表单标签 输入域标签 预告和回顾 后话 前言 本系列博客将分享HTML相关知识点。 这一期博客&…

使用 DBeaver 创建 MySQL 数据库

文章目录 创建数据库创建用户 创建数据库 1.在【数据库】上点右键&#xff0c;然后选择【新建 数据库】 2.输入 数据库名&#xff0c;点击确定 这样&#xff0c;数据库就创建好了 创建用户 1.在【用户】上点右键&#xff0c;然后选择【新建 用户】 在属性这里&#xff0c;输入…

基于yolov8的血细胞检测计数系统python源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv8的血细胞检测与计数系统是一种利用深度学习技术&#xff0c;特别是YOLOv8目标检测算法&#xff0c;实现高效、准确血细胞识别的系统。该系统能够自动识别并计数图像或视频中的血细胞&#xff0c;包括红细胞、白细胞和血小板等&#xff0c;为医疗诊断提…

揭开面纱--机器学习

一、人工智能三大概念 1.1 AI、ML、DL 1.1.1 什么是人工智能? AI&#xff1a;Artificial Intelligence 人工智能 AI is the field that studies the synthesis and analysis of computational agents that act intelligently AI is to use computers to analog and instead…

v0.dev快速开发

探索v0.dev&#xff1a;次世代开发者之利器 今之技艺日新月异&#xff0c;开发者之工具亦随之进步不辍。v0.dev者&#xff0c;新兴之开发者利器也&#xff0c;迅速引起众多开发者之瞩目。本文将引汝探究v0.dev之基本功能与优势&#xff0c;助汝速速上手&#xff0c;提升开发之…

WSL 下的 CentOS 装 Docker

WSL 下的 CentOS 装 Docker 卸载旧版本安装前的准备工作1. 安装 yum-utils2. 添加阿里云的 yum 镜像仓库3. 快速生成 Yum 缓存 安装Docker启动docker运行 hello-world卸载 Docker 引擎参考资料 卸载旧版本 sudo yum remove docker \ docker-client \ docker-client-latest \ d…

论文阅读_检索增强生成 RAG 综述

英文名称: Retrieval-Augmented Generation for Large Language Models: A Survey 中文名称: 大型语言模型的检索增强生成&#xff1a;一项调查 链接: http://arxiv.org/abs/2312.10997v5 作者: Yunfan Gaoa, Yun Xiongb, Xinyu Gaob, Kangxiang Jiab, Jinliu Panb, Yuxi Bic, …

文章解读与仿真程序复现思路——电力自动化设备EI\CSCD\北大核心《考虑燃料电池和电解槽虚拟惯量支撑的电力系统优化调度方法》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

【基础算法总结】滑动窗口

目录 一&#xff0c;滑动窗口介绍二&#xff0c;算法原理和代码实现209.长度最小的子数组3.无重复字符的最长子串1004.最大连续1的个数III1658.将x减到0的最小操作数904.水果成篮438.找到字符串中所有字母异位词30.串联所有单词的子串76.最小覆盖子串 三&#xff0c;算法总结 一…

【Python知识宝库】错误与异常处理:编写健壮的Python代码

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 前言一、错误与异常的定义1. 语法错误2. 异常 二、异常处理1. try块2. except块3. finally块 三、异常处理的最佳实践…

数字IC前端:负的建立时间和保持时间

相关阅读数字IC前端https://blog.csdn.net/weixin_45791458/category_12173698.html?spm1001.2014.3001.5482 建立时间和保持时间是触发器的两个重要的时序参数&#xff1a;建立时间(setup time)指的是在有效时钟沿前&#xff0c;数据必须到达并稳定的时间&#xff1b;保持时间…

力扣416-分割等和子集(Java详细题解)

题目链接&#xff1a;416. 分割等和子集 - 力扣&#xff08;LeetCode&#xff09; 前情提要&#xff1a; 因为本人最近都来刷dp类的题目所以该题就默认用dp方法来做。 最近刚学完01背包&#xff0c;所以现在的题解都是以01背包问题为基础再来写的。 如果大家不懂01背包的话…

zabbix6.4连接邮箱发出警告

添加告警媒介 默认接收人: 故障级别:{TRIGGER.STATUS}。 服务器:【{HOSTNAME1} 】 发生:{TRIGGER.NAME} 故障! 注:默认接收人:相当于邮件的主题 默认信息:邮件的主题 告警主机:{HOSTNAME1} 告警时间:{EVENT.DATE} {EVENT.TIME} 告警等级:{TRIGGER.SEVERITY} 告警信息:{TRIGGER.…

HTML5中canvas绘图基础详解

第7章 HTML5绘图基础 H5中新增了重要元素canvas,通过绘制任意图形&#xff0c;借助自带API&#xff0c;通过编写js可以控制各种图形&#xff0c;制作动画效果&#xff0c;对web具有划时代意义。 7.1 画布的基础知识 绘图三步骤&#xff1a; 步骤一&#xff1a;使用canvas创…

FreeRTOS学习笔记(五)任务进阶篇

文章目录 前言一、列表和列表项1.1 xList 和 xLIST_ITEM1.2 相关API函数1.3 任务就绪列表 二、任务调度器的启动过程2.1 PendSV 和 SysTick 寄存器2.2 prvStartFirstTask( )2.3 xPortStartScheduler( )2.4 vTaskStartScheduler( ) 的整体流程 三、任务切换3.1基于 SysTick 中断…

一名优秀的工程师应该学会在工作中提升自己,面试篇

xxx 进行 xxx 操作&#xff0c;为什么不行&#xff1f;有人知道吗&#xff1f; 此时&#xff0c;[黑人脸问好号.jpg]。 这里大家可以阅读下《提问的艺术》这本书&#xff0c;这是一本教你如何通过富有技巧性的提问来提高沟通效率并提升自身影响力的书。 Github 上一些开源项目…

zabbix6.4连接钉钉发出警告

zabbix6.4配置钉钉告警 注册钉钉 建一个内部群 添加自定义机器人 配置zabbix服务端 打开脚本告警的配置 # vim /etc/zabbix/zabbix_server.conf AlertScriptsPath/usr/lib/zabbix/alertscripts 准备脚本 安装一个依赖包 # dnf -y install python3-requests # vim /usr/li…

希尔排序/选择排序

前言&#xff1a; 本篇主要对常见的排序算法进行简要分析&#xff0c;代码中均以数组 arr[] { 5, 3, 9, 6, 2, 4, 7, 1, 8 } 为例&#xff0c;进行升序排列。 常见的排序算法有如下&#xff1a; 选择排序中&#xff0c;直接选择排序没有任何实际与教育意义&#xff0c;而堆排…