嵌入式Linux驱动开发之platform
关键词:rk3399 嵌入式驱动 Linux platform
前言
前面的嵌入式Linux驱动都是描述从特定的SOC与特定设备之间的直接两两通信。而Linux不是为单一某一SOC结构而设计的操作系统,它可以运行在X86、ARM等多种架构多种SOC平台上,如果驱动程序按照SOC与设备之间直接通信的方式实现的话,其效果如下所示:
从Linux源码的角度来看,源码会变得极其臃肿并且充斥的大量的重复代码,比如A SOC平台和C SOC平台都需要各自与一个B设备通信,即A SOC连接了一个B设备,C SOC也连接了一个B设备,Linux系统实现直接两两通信,Linux源码中就会出现重复的对B设备的驱动代码
举个例子,多个人到超市去采购商品,每个人采购多个品类的商品,每个品类的商品都有一个售货员,如果每个购买者按照自己的采购清单直接跟售货员沟通,那么就会出现每个购买者跟多个售货员沟通,每个售货员跟多个购买者沟通的现象,场面就比较混乱,对应到Linux系统源码而言就显得特别无序,售货员多次往返取货会浪费时间,对应到Linux系统源码而言就是代码的重复
为了避免上述提到的问题,提高Linux设备驱动的跨平台能力,Linux系统采取了驱动分离与分层的策略,如下为Linxu驱动分离后的驱动框架示意:
其中:
主机驱动:linux系统内核能够利用SOC的接口驱动,例如,Linux系统内核对RK3399的I2C接口的驱动,就是实现让RK3399的引脚按照I2C功能工作
设备驱动:连接到SOC接口的设备的驱动,例如,RK3399的I2C接口上连接了一个MPU6050,设备驱动就是MPU6050自己的驱动
统一接口的作用如同在上面的例中,在超市门口建立一个采购平台,购买者将清单给平台,平台给售货员发送提货单,这样超市就看起来井井有条,某个售货员也可以一次将多个购买者的同类商品取出,不必往返多次,避免重复
这就是Linxu系统中的总线(Bus)、驱动(driver)和设备(device)模型。如下:
总线负责传递设备信息等。当向系统注册一个驱动时,总线会在设备中查找,如果有匹配的设备,便将两者联系起来;同样,向系统注册一个设备的时候,总线会在驱动中查找,如果有匹配的驱动,便将两者联系起来
以上是为什么采用驱动分离的策略
platform
Linux驱动分离使用了总线(Bus)、驱动(driver)和设备(device)模型,对于SOC的某些外设,是没有总线一说的,因此Linux提出了platform这种虚拟总线,相应的就有驱动platform_driver和设备platform_device
platform总线
Linux系统内使用bus_type结构体表示总线,其定义在Linxu源代码的/include/linux/device.h
中,platform总线是bus_type的一个实例,定义在Linux源代码的/drivers/base/platform.c
中,如下
struct bus_tpye platform_bus_type = {.name = "platform",.dev_groups = platform_dev_groups,.match = platform_match,.uevent = platform_uevent,.pm = &platform_dev_pm_ops,
};
其中 platform_match
就是匹配函数,总线就是使用匹配函数类根据注册的设备来查找驱动的,或根据注册的驱动来查找设备,paltform_match()函数定义在文件/drivers/base/platform.c
中,如下:
static int platform_match(struct device *dev,struct device_driver *drv)
{struct platform_device *pdev = to_platform_device(dev);struct platform_driver *pdrv = to_platform_driver(drv);/*When driver_override is set,only bind to the matching driver*/if (pdev -> driver_override)return !strcmp(pdev -> driver_override,drv -> name);/* Attemp an OF style match first */if(of_driver_match_device(dev,drv))return 1;/*Then try ACPI style match */if(acpi_driver_match_device(dev,drv))return 1;/* Then try to match against the id table */if(pdrv -> id_table)return platform_match_id(pdrv -> id_table,pdev) != NULL;/* fall-back to driver name match */return(strcmp(pdev -> name,drv -> name) == 0);
}
可以看出,函数内提供了4中匹配方法:
- OF函数匹配,即设备树匹配方式,函数of_deiver_match_device()定义在Linux源代码文件
/include/linux/of_device.h
中,device_driver结构体中有个名为of_match_table
的成员变量,此成员变量保存着驱动的compatible匹配表,设备树中的每个设备节点的compatibal属性会和of_match_table表中的所有成员比较,如果有则表示设备会和此驱动匹配 - ACPI匹配
- id_table匹配,每个platform_driver结构体有一个id_table成员变量,其中保存了很多id信息,这些id信息包含paltform_driver所支持的驱动类型
- name字段匹配
对于支持设备树的Linux版本,一般设备驱动为了兼容性都支持设备树和无设备树两种匹配方式,即OF函数匹配都会存在,id_table匹配和name字段匹配只存在一种就可以了,比较nama字段的匹配比较简单,用的多
paltform总线,即platform_bus_type的主要作用就是匹配驱动和设备
platform驱动platform_driver
platform驱动,由platform_driver结构体表示,此结构体定义在Linux源代码的/include/linux/platform_device.h
中,内容如下:
struct platform_driver {int (*probe)(struct platform_device *);/** Traditionally the remove callback returned an int which however is* ignored by the driver core. This led to wrong expectations by driver* authors who thought returning an error code was a valid error* handling strategy. To convert to a callback returning void, new* drivers should implement .remove_new() until the conversion it done* that eventually makes .remove() return void.*/int (*remove)(struct platform_device *);void (*remove_new)(struct platform_device *);void (*shutdown)(struct platform_device *);int (*suspend)(struct platform_device *, pm_message_t state);int (*resume)(struct platform_device *);struct device_driver driver;const struct platform_device_id *id_table;bool prevent_deferred_probe;/** For most device drivers, no need to care about this flag as long as* all DMAs are handled through the kernel DMA API. For some special* ones, for example VFIO drivers, they know how to manage the DMA* themselves and set this flag so that the IOMMU layer will allow them* to setup and manage their own I/O address space.*/bool driver_managed_dma;
};
其中
- probe函数当设备与驱动匹配成功后会执行
- id_table就是platform总线中匹配驱动和设备的一个匹配方式,id_table是一个数组,每个元素的类型为platform_device_id,其结构体如下
struct platform_device_id{char name[PLATFORM_NAME_SIZE];kernel_ulong_t driver_data;
};
- driver 是device_driver结构体变量,该结构体定义在Linux源代码的
/include/linux/device.h
中,如下:
struct device_driver {const char *name;struct bus_type *bus;struct module *owner;const char *mod_name; /* used for built-in modules */bool suppress_bind_attrs; /* disables bind/unbind via sysfs */enum probe_type probe_type;const struct of_device_id *of_match_table;const struct acpi_device_id *acpi_match_table;int (*probe) (struct device *dev);int (*remove) (struct device *dev);void (*shutdown) (struct device *dev);int (*suspend) (struct device *dev, pm_message_t state);int (*resume) (struct device *dev);const struct attribute_group **groups;const struct dev_pm_ops *pm;struct driver_private *p;
};
其中of_match_table是采用设备树式paltform驱动使用的匹配表,是of_device_id类型,of_device_id类型结构体数据定义在Linux源代码的/include/linux/mod_devicetable.h
中,如下:
struct of_device_id {char name[32];char type[32];char compatible[128];const void *data;
};
其中compatible就是设备树中节点的compatible属性,of_match_table中的compatible属性和设备树节点的compatible相同则代表驱动和设备匹配成功
在编写platform驱动的时候,首先定义一个paltform_device结构体变量,然后实现其成员变量,重点是实现匹配方法及probe()函数,具体的驱动程序在probe()函数中编写
定义并初始化好platform_driver后,需要在驱动入口函数(参考[[01嵌入式Linux驱动开发之点亮LED(非设备树)]]的内核模块部分)中调用platform_driver_register()函数,向Linux内核注册一个platform驱动,相应的,注销platform驱动可以用platform_driver_unregister()
见下面测试代码查看platform_driver的应用
platform设备platform_device
准备好platform驱动后,还需要准备pltform设备,由platform_device结构体表示platform设备,结构体定义在Linux源码的/include/linux/platform_device.h
中,如下
struct platform_device {const char *name;int id;bool id_auto;struct device dev;u64 platform_dma_mask;struct device_dma_parameters dma_parms;u32 num_resources;struct resource *resource;const struct platform_device_id *id_entry;/** Driver name to force a match. Do not set directly, because core* frees it. Use driver_set_override() to set or clear it.*/const char *driver_override;/* MFD cell pointer */struct mfd_cell *mfd_cell;/* arch specific additions */struct pdev_archdata archdata;
};
如果Linux内核版本支持设备树,就不需要再用platform_device来描述设备了,改用设备树去描述本文这里不再梳理platform_device,见下面测试代码了解设备树描述platform设备
设备树下platform驱动实验
platform驱动分为总线、驱动和设备,总线是由linux内核提供的,在支持设备树的Linux内核版本下,可以在设备树中描述设备,因此驱动的实现,除在设备树中添加设备外,只需要实现platform_driver即可
设备树描述设备
与前面相同,控制Firefly-RK3399开发板上电LED,引脚GPIO0B_5输出高电平点亮LED,低电平熄灭LED。在设备树中添加设备节点的操作与嵌入式Linux驱动开发之从设备树到点亮LED中相同,这里不再重复。
platform_driver实现
新建.c文件,输入以下内容(本文这里命名为platform_leddriver.c):
#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/platform_device.h>/**************************************FileName:platform_leddriver.c*Fuc: RK3399,GPIO0B_5 driver**Author: PineLiu************************************/#define CHAR_CNT 1 //设备数量
#define LED_NAME "gumpplatled" //设备名称#define LEDON 1 //点亮
#define LEDOFF 0 //熄灭//重映射后的寄存器虚拟地址
static void __iomem *GPIO0_DR; //数据寄存器
static void __iomem *GPIO0_DDR; //方向寄存器
static void __iomem *GPIO0_IOMUX; //复用寄存器
static void __iomem *GPIO0_CRU; //时钟寄存器//声明设备结构体(该设备包含的属性)
struct platform_leddev{dev_t devid; //设备号struct cdev cdev; //字符设备struct class *class; //设备节点类 主动创建设备节点文件用struct device *device; //设备int major; //主设备号int minor; //次设备号struct device_node *nd; //设备节点
};//声明一个外部设备,本文这里是字符设备
struct platform_leddev platformled;//LED打开/关闭
void led_switch(u8 sta)
{u32 val = 0;if(sta == LEDON){val = readl(GPIO0_DR);val |= (1<<13);writel(val,GPIO0_DR);}else if(sta == LEDOFF){val = readl(GPIO0_DR);val &= ~(1<<13);writel(val, GPIO0_DR);}
}//===========================================================================
//以下实现设备的具体操作函数:open函数、read函数、write函数和release函数
//===========================================================================//打开设备
static int led_open(struct inode *inode, struct file *filp)
{filp -> private_data = &platformled; //设置私有数据return 0;
}//从设备读取数据
static ssize_t led_read(struct file *filp,char __user *buf,size_t cnt,loff_t *offt)
{return 0;
}//向设备写数据
//filp:设备文件,表示打开的文件描述
//buf :保存着要向设备写入的数据
//cnt :要写入的数据长度
//offt:相对于文件首地址的偏移
//
static ssize_t led_write(struct file *filp,const char __user *buf,size_t cnt,loff_t *offt)
{int retvalue;unsigned char databuf[1];unsigned char ledstat;retvalue = copy_from_user(databuf,buf,cnt);if(retvalue < 0){printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0]; //获取状态值if(ledstat == LEDON){led_switch(LEDON);}else if(ledstat == LEDOFF){led_switch(LEDOFF);}return 0;}//关闭,释放设备
//filp: 要关闭的设备文件描述
//
static int led_release(struct inode *inode,struct file *filp)
{return 0;
}//==========================================================================
//以下实现的设备具体函数与内核的对应函数的映射
//==========================================================================
//映射设备操作函数
static struct file_operations led_fops = {.owner = THIS_MODULE,.open = led_open,.read = led_read,.write = led_write,.release = led_release,
};//==========================================================================
//内核模块相关
//==========================================================================//probe函数
static int led_probe(struct platform_device *dev)
{u32 val = 0;int ret;u32 regdata[14];const char *str;struct property *proper;printk("led driver and device was matched!\r\n");//==============获取节点属性//获取节点platformled.nd = of_find_node_by_path("/gumpleddts");if(platformled.nd == NULL){printk("gumpleddts node can not found!\r\n");return -EINVAL;}else{printk("gumpleddts node has been found!\r\n");}//获取compatible属性proper = of_find_property(platformled.nd,"compatible",NULL);if(proper == NULL){printk("compatible property can not found!\r\n");}else{printk("compatibel = %s\r\n",(char *)proper -> value);}//获取status属性ret = of_property_read_string(platformled.nd,"status",&str);if(ret < 0){printk("status read failed!\r\n");}else{printk("status = %s\r\n",str);}//获取reg属性ret = of_property_read_u32_array(platformled.nd,"reg",regdata,8);if(ret < 0){printk("reg property read failed!\r\n");}else{u8 i = 0;printk("reg data:\r\n");for(i = 0;i<8;i++)printk("%#X",regdata[i]);printk("\r\n");}//===============初始化连接LED的引脚//寄存器地址映射#if 1GPIO0_CRU = ioremap(regdata[0],regdata[1]);GPIO0_IOMUX = ioremap(regdata[2],regdata[3]);GPIO0_DDR = ioremap(regdata[4],regdata[5]);GPIO0_DR = ioremap(regdata[6],regdata[7]);#elseGPIO0_CRU = of_iomap(platformled.nd,0);GPIO0_IOMUX = of_iomap(platformled.nd,1);GPIO0_DDR = of_iomap(platformled.nd,2);GPIO0_DR = of_iomap(platformled.nd,3);#endif//使能时钟
// GPIO0_CRU |= (1 << (3 + 16));
// GPIO0_CRU &= ~(1 << 3);val = readl(GPIO0_CRU);val |= (1 << (3+16));val &= ~(1 << 3);writel(val,GPIO0_CRU);//设置GPIO0B_5引脚复用功能为GPIO
// GPIO0_IOMUX |= (3 << (10+16));
// GPIO0_IOMUX &= ~(3 << 10);val = readl(GPIO0_IOMUX);val |= (3 << (10+16));val &= ~(3 << 10);writel(val,GPIO0_IOMUX);//配置GPIO0B_5引脚为输出模式
// GPIO0_DDR |= (1 << 13);val = readl(GPIO0_DDR);val |= (1 << 13);writel(val,GPIO0_DDR);//配置GPIO0B_5引脚默认关闭LED
// GPIO0_DR &= ~(1 << 13);val = readl(GPIO0_DR);val &= ~(1 << 13);writel(val,GPIO0_DR);//===========注册设备 //创建设备号if(platformled.major){ //定义了设备号platformled.devid = MKDEV(platformled.major,0);register_chrdev_region(platformled.devid,CHAR_CNT,LED_NAME);}else{ //没有定义设备号,要向系统申请设备号alloc_chrdev_region(&(platformled.devid),0,CHAR_CNT,LED_NAME);platformled.major = MAJOR(platformled.devid);platformled.minor = MINOR(platformled.devid);}printk("platformled major = %d,minor = %d\r\n",platformled.major,platformled.minor);//初始化cdevplatformled.cdev.owner = THIS_MODULE;cdev_init(&platformled.cdev,&led_fops);//向系统注册设备cdev_add(&platformled.cdev,platformled.devid,CHAR_CNT);//创建类platformled.class = class_create(THIS_MODULE,LED_NAME);if(IS_ERR(platformled.class)){return PTR_ERR(platformled.class);}//创建设备节点platformled.device = device_create(platformled.class,NULL,platformled.devid,NULL,LED_NAME);if(IS_ERR(platformled.device)){return PTR_ERR(platformled.device);}return 0;}//remove函数
static int led_remove(struct platform_device *dev)
{//取消映射iounmap(GPIO0_CRU);iounmap(GPIO0_IOMUX);iounmap(GPIO0_DDR);iounmap(GPIO0_DR);//注销设备cdev_del(&platformled.cdev);unregister_chrdev_region(platformled.devid,CHAR_CNT);device_destroy(platformled.class,platformled.devid);class_destroy(platformled.class);return 0;
}//匹配表
static const struct of_device_id led_of_match[] = {{.compatible = "gump-led"}, //与设备树内该设备节点的compatibel相同{/*Sentinel*/}
};//platform_driver
static struct platform_driver led_driver = {.driver = {.name = "gumpleddts", //设备树中节点名称.of_match_table = led_of_match,},.probe = led_probe,.remove = led_remove,
};//驱动模块加载函数
static int __init led_init(void)
{return platform_driver_register(&led_driver);
}//驱动模块卸载函数
static void __exit led_exit(void)
{platform_driver_unregister(&led_driver);
}module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gump");
代码内实现了probe函数,在probe函数内实现了设备号分配,寄存器配置等,使用platform_driver_register
实现驱动的注册,platform_driver_unregister
实现驱动的注销
注:代码内所说的设备,是SOC内相对于cpu而言的片上外设,并不是platform_device,platform_device是指连接到这个片上外设的设备
将上面代码放到ubuntu18.04内编译为ko文件,Makefile文件内容为:
KERNELDIR:=/home/gump/rk3399/kernel-develop-4.4
CURRENT_PATH:=$(shell pwd)
obj-m:=platform_leddriver.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
编译命令:(可根据自己的交叉编译工具更改)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-`
得到ko文件放到嵌入式Linux系统内,并安装内核模块:
sudo insmod -f platform_leddriver.ko
安装后在/dev
目录下会出现该设备文件夹,如图
测试
测试程序platformTest.c
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"/**************************************************FileName:platformTest.c*Func: GPIO driver module test**Author: pineliu* **********************************************/#define LEDOFF 0
#define LEDON 1int main(int argc,char *argv[])
{int fd, retvalue;char *filename;unsigned char databuf[1];if(argc != 3){printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename,O_RDWR);if (fd < 0){printf("file %s open failed!\r\n",argv[1]);return -1;}databuf[0] = atol(argv[2]);retvalue = write(fd,databuf,sizeof(databuf));if(retvalue < 0){printf("led Control Error!\r\n");close(fd);return -1;}retvalue = close(fd);if(retvalue < 0){printf("file %s close failed!\r\n",argv[1]);return -1;}return 0;
}
将测试程序进行编译:
aarch64-linux-gnu-gcc platformTest.c -o platformTest
得到可执行文件platformTest
放到嵌入式Linux系统内,并赋予可执行权限
chmod +x platformTest
测试,点亮led:
sudo ./platformTest /dev/gumpplatled 1
实验证明可行
总结
为了提高Linux系统跨平台能力的同时,简化源代码,实现驱动代码复用,因此采用了驱动分离的策略,使用驱动、总线、设备模型。
platform驱动分为总线、驱动和设备,总线是由linux内核提供的,在支持设备树的Linux内核版本下,可以在设备树中描述设备,因此驱动的实现,除在设备树中添加设备外,只需要实现platform_driver即可
相比于基础的设备树驱动,在驱动代码方面,platform设备驱动代码更加的结构化
相关文章:
嵌入式Linux驱动开发之platform
关键词:rk3399 嵌入式驱动 Linux platform 前言 前面的嵌入式Linux驱动都是描述从特定的SOC与特定设备之间的直接两两通信。而Linux不是为单一某一SOC结构而设计的操作系统,它可以运行在X86、ARM等多种架构多种SOC平台上,如果驱动程序按照S…...
【蓝桥杯】43693.日期问题
题目描述 小明正在整理一批历史文献。这些历史文献中出现了很多日期。小明知道这些日期都在 1960 年 1 月 1 日至 2059 年 12 月 31 日。令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的,有采用月/日/年的,还有采用…...
opengrok_windows_多工程的同步
多工程的目录 工程代码下载和log配置 工程代码下载 在每个工程的src目录下,下载工程代码,以下载pulseaudio的代码为例。 git clone gitgithub.com:pulseaudio/pulseaudio.git log配置文件 拷贝D:\opengrok\opengrok-…...
Redis的安装和使用--Windows系统
Redis下载地址: windows版本readis下载(GitHub): https://github.com/tporadowski/redis/releases (推荐使用) https://github.com/MicrosoftArchive/redis/releases 官网下载(无Windows版本…...
【odbc】odbc连接kerberos认证的 hive和spark thriftserver
hive odbc驱动,以下两种都可以 教程:使用 ODBC 和 PowerShell 查询 Apache HiveHive ODBC Connector 2.8.0 for Cloudera Enterprise spark thriftserver本质就是披着hiveserver的外壳的spark server 完成kerberos认证: (1)可以…...
亚博microros小车-原生ubuntu支持系列:1 键盘控制
背景:电脑配置不太行,我在ubuntu再运行vmware,里面运行亚博官方的虚拟机镜像ubuntu,系统很卡。基本上8G内存给打满了。还是想把亚博官方的代码迁移出来,之前售后就说除了官方镜像虚拟机,需要自己摸索迁移。…...
【Linux】利用‘shell脚本’快速查看服务的运行情况
一、脚本目的 为了方便查看服务的运行情况,特此写了一个shell脚本,这样就很方便获取,查看如nginx、mysql等服务的运行状态、监听端口状态、防火墙、端口开放状态等。 二、shell脚本源代码 #!/bin/bash SCRIPTPATH$(cd "$(dirname $0)…...
[2025分类时序异常检测指标R-AUC与VUS]
梳理了一下分类中常见的指标,这些指标与时序异常检测中新提出的A-RUC与VUS之间的关系 真正例(True Positive,TP): 被正确识别为正样本的数量。真负例(True Negative,TN): 被正确识别为负样本的数量。假正例(False Positive ,FP): 被错误识为正样本数量假负例(Fals…...
设计模式之结构型模式
在软件开发的世界里,设计模式是前辈们智慧的结晶,它们为我们提供了通用的解决方案来应对各种常见的软件设计问题。今天,我们深入探讨设计模式中的结构型模式,并用 Java 语言来实现它们。 什么是结构型模式 结构型模式主要关注如…...
Spring Boot中的404错误:原因、影响及处理策略
Spring Boot中的404错误:原因、影响及处理策略 在Web开发过程中,404错误是一个常见的HTTP状态码,表示“未找到”资源。在Spring Boot项目中,尽管它以其简化的配置和快速的开发速度著称,但开发者仍可能遇到404错误。本…...
网络协议如何确保数据的安全传输?
网络协议作为计算机网络通信的基石,其设计不仅旨在实现数据的有效传输,更在于确保数据在传输过程中的安全性。对于网络协议如何保障数据安全传输,是很多企业和网络IT部门的重点,本文将从多方面概述相关方法。 加密与解密机制 1. …...
Python绘制数据地图-MovingPandas
MovingPandas 是一个用于时空数据分析的 Python 库,它扩展了 Pandas 和 GeoPandas,使得处理和分析带有时间戳的地理数据变得更加方便。虽然 MovingPandas 本身不直接提供数据可视化功能,但你可以结合其他库如 matplotlib、folium 或 plotly 来…...
鸿蒙子组件根据数据,刷新item Ui的规范
鸿蒙 子组件列表中,有多个isSelect,父组件只用一个state类型的isSelect,就可以将它们关联起来,如下: isLike:item1.isPraise?!this.isLike:this.isLike, 子组件想要数据变化,UI随着更新,就得…...
使用 Flask 构建视频转 GIF 工具
使用 Flask 构建视频转 GIF 工具 在前几日的文章当中,我介绍了如何使用 Python 脚本将视频转化为 GIF 动画,为了更好、更方便的进行操作,在这篇博客中,我将介绍使用 Flask 框架创建一个简单的 Web 应用程序,该应用程序…...
基于SpringBoot+Vue的智慧动物园管理系统的设计与实现
获取源码:基于SpringBootVue智慧动物园系统设计与实现: 后台和用户前台。后台包括首页、员工管理、考勤管理、部门管理、角色管理、审核管理、动物管理、演出管理、园区管理、园区设施维修、饲养管理、行为观察管理、疫苗管理、看护管理、个人中心、票务管理、收入管…...
“深入浅出”系列之数通篇:(5)TCP的三次握手和四次挥手
TCP(传输控制协议)的三次握手和四次挥手是TCP连接建立和释放的过程。 一、TCP三次握手 TCP三次握手是为了建立可靠的连接,确保客户端和服务器之间的通信能力。具体过程如下: 第一次握手:客户端向服务器发送一个带有…...
【初阶数据结构】探索数据的多米诺链:单链表
文章目录 1.链表1.1 概念及结构1.2 分类 2.单链表接口实现2.1 单链表节点创建2.2 单链表打印2.3 单链表尾插2.4 单链表头插2.5 单链表尾删2.6 单链表头删2.7单链表查找2.8 单链表在pos位置插入x2.8.1 pos前2.8.2 pos后 2.9单链表在pos位置删除x2.9.1 pos前2.9.2 pos后 2.2.10 单…...
我的创作纪念日——我与CSDN一起走过的365天
目录 一、机缘:旅程的开始 二、收获:沿路的花朵 三、日常:不断前行中 四、成就:一点小确幸 五、憧憬:梦中的重点 一、机缘:旅程的开始 最开始开始写博客是在今年一二月份的时候,也就是上一…...
w-form-select 组件中 分析 自定义属性 和 el-select 自带属性 的对比表格
以下是该组件中 自定义属性 和 el-select 自带属性 的对比表格: 属性/功能自定义el-select 自带说明label✔️❌自定义属性,用于设置表单项的标签。prop✔️❌自定义属性,用于表单验证时的字段名。labelWidth✔️❌自定义属性,用…...
【蓝桥杯选拔赛真题63】C++奇数 第十四届蓝桥杯青少年创意编程大赛 算法思维 C++编程选拔赛真题解
目录 C++奇数 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、运行结果 五、考点分析 七、推荐资料 C++奇数 第十四届蓝桥杯青少年创意编程大赛C++选拔赛真题 一、题目要求 1、编程实现 给定两个正整数N和M(10≤N<M≤10000),请找出N到M…...
Android 存储进化:分区存储
} 在写入存储之前,需要知道设备有多少空间可用,不够的话会抛出异常。不过现在的智能设备内存也是比较大的,这部分可以参考Google 查询可用空间 2.1 内部存储空间 打开Android studio的 Device File Explorer时,可以看到应用的内部…...
NPM 与 Node.js 版本兼容问题:npm warn cli npm does not support Node.js
问题描述与处理策略 1、问题描述 npm warn cli npm v10.9.2 does not support Node.js v18.16.1. This version of npm supports the following node versions: ^18.17.0 || >20.5.0. You can find the latest version at https://nodejs.org/.# 翻译 npm warn cli npm v1…...
docker离线安装及部署各类中间件(x86系统架构)
前言:此文主要针对需要在x86内网服务器搭建系统的情况 一、docker离线安装 1、下载docker镜像 https://download.docker.com/linux/static/stable/x86_64/ 版本:docker-23.0.6.tgz 2、将docker-23.0.6.tgz 文件上传到服务器上面,这里放在…...
redis 分布式方案
文章目录 前言一、主从复制1、主从配置2、建立连接3、数据同步3.1、全量同步3.2、全量同步配置3.3、 增量同步3.4、 增量同步配置 二、redis sentinel1、主要功能2、sentinel配置3、高可用3.1、故障发现3.2、故障转移3.2.1、选举sentinel进行故障转移3.2.2、选举从节点升级成主…...
# [0114] Task01 《数学建模导论》P1 解析几何与方程模型
链接:https://www.datawhale.cn/activity/124 整理的相关代码库 GitHub 页面链接 绪论 姜启源:“数学建模就是建立数学模型解决实际问题” 本质还是解应用题,只是曾经的“小明买糖”变成了如今的“嫦娥探月”。 SEIR 模型,也…...
PCM5142集成32位384kHz PCM音频立体声114dB差分输出DAC编解码芯片
目录 PCM5142 简介PCM5142功能框图PCM5142特性 参考原理图 PCM5142 简介 PCM514x 属于单片 CMOS 集成电路系列,由立体声数模转换器 (DAC) 和采用薄型小外形尺寸 (TSSOP) 封装的附加支持电路组成。PCM514x 使用 TI 最新一代高级分段 DAC 架构产品,可实现…...
【HarmonyOS NEXT】碰一碰开发分享
关键词:鸿蒙、碰一碰、systemShare、harmonyShare、Share Kit 华为分享新推出碰一碰分享,支持用户通过手机碰一碰发起跨端分享,可实现传输图片、共享wifi等。我们只需调用系统 api 传入所需参数拉起对应分享卡片模板即可,无需对 U…...
pytest+playwright落地实战大纲
前言 很久没有更新博客,是因为在梳理制作Playwright测试框架实战相关的课程内容。现在课程已经完结,开个帖子介绍下这门课程(硬广, o(〃^▽^〃)o) 课程放在CSDN学习频道, 欢迎关注~ PyTestPl…...
HTML<center>标签
HTML5不支持。 <center>标签在HTML4中用于使文本居中对齐。 用什么来代替呢? 例子 居中对齐文本(使用 CSS): <html> <head> <style> h1 {text-align: center;} p {text-align: center;} div {text-a…...
【部署】将项目部署到云服务器
目录 1.获得服务器 2.连接到云服务器 3.配置环境 3.1.Java(运行后端所需) 3.2.MySQL数据库 3.3.Nginx(运行前端所需) 3.4. Node.js(构建前端所需) 4.打包项目 4.1.打包后端项目 4.2.打包前端项目…...
微软宣布Win11 24H2进入新阶段!设备将自动下载更新
快科技1月19日消息,微软于1月16日更新了支持文档,宣布Windows 11 24H2进入新阶段。 24H2更新于2024年10月1日发布,此前为可选升级,如今微软开始在兼容的Windows 11设备上自动下载并安装24H2版本。 微软表示:“运行Wi…...
傅里叶变换在语音识别中的关键作用
在语音识别中,傅里叶变换起着至关重要的作用,主要体现在以下几个方面: 一、时域到频域的转换 语音信号的特点 语音信号是一种时域信号,它随时间变化。例如,当我们说话时,声带的振动产生声波,这…...
面试-业务逻辑2
应用 给定2个数组a、b,若a[i] b[j],则记(i,j)为一个二元数组,求具体的二元数组及其个数。 实现 a input("请输入数组a的元素个数:") # print(a) a_list list(map(int, input("请输入数组a的元素,…...
【20】Word:小许-质量管理-论文❗
目录 题目 NO1.2.3.4.5 NO6.7 NO8 NO9 NO10.11 题目 NO1.2.3.4.5 另存为“Word.docx”文件在考生文件夹下,F12Fn是另存为的作用布局→页面设置对话框→纸张:大小A4→页边距:上下左右不连续ctrl选择除表格外的所有内容→开始→字体对…...
Python数据分析案例70——基于神经网络的时间序列预测(滞后性的效果,预测中存在的问题)
背景 这篇文章可以说是基于 现代的一些神经网络的方法去做时间序列预测的一个介绍科普,也可以说是一个各种模型对比的案例,但也会谈一谈自己做了这么久关于神经网络的时间序列预测的论文,其中一些常见的模式及它们存在的问题以及效果&#x…...
Kotlin Bytedeco OpenCV 图像图像54 透视变换 图像矫正
Kotlin Bytedeco OpenCV 图像图像54 透视变换 图像矫正 1 添加依赖2 测试代码3 测试结果 在OpenCV中,仿射变换(Affine Transformation)和透视变换(Perspective Transformation)是两种常用的图像几何变换方法。 变换方…...
PyTorch使用教程(11)-cuda的使用方法
1. 基本概念 CUDA(Compute Unified Device Architecture)是NVIDIA开发的一种并行计算平台和编程模型,专为图形处理器(GPU)设计,旨在加速科学计算、工程计算和机器学习等领域的高性能计算任务。CUDA允许开发…...
微软开源AI Agent AutoGen 详解
AutoGen是微软发布的一个用于构建AI Agent系统的开源框架,旨在简化事件驱动、分布式、可扩展和弹性Agent应用程序的创建过程。 开源地址: GitHub - microsoft/autogen: A programming framework for agentic AI 🤖 PyPi: autogen-agentchat Discord: https://aka.ms/auto…...
上位机工作感想-2024年工作总结和来年计划
随着工作年限的增增长,发现自己越来越不喜欢在博客里面写一些掺杂自己感想的东西了,或许是逐渐被工作逼得“成熟”了吧。2024年,学到了很多东西,做了很多项目,也帮别人解决了很多问题,唯独没有涨工资。来这…...
js: 区分后端返回数字是否为null、‘-’ 或正常number类型数字。
问: 这是我的代码<CountTo v-if!isNaN(Number(item.num))> <span v-else>{{item.num}}</span> 我希望不是null的时候走countTo,是null的时候直接<span>{{item.num}}</span>显示 回答: 最终结果: …...
C#中字符串方法
字符串属性:Lenght 长度比最大索引大1 string str "frerfgd"; 1.可以通过索引,获取字符串中的某一个字符,下标“0,1.......” Console.WriteLine(str[0]);//f Console.WriteLine(str[1]);//r //Console.WriteLine(s…...
flutter在使用gradle时的加速
当我使用了一些过时的插件的时候,遇到了一些问题 比如什么namespace 问题等,因为有些插件库没有更新了,或者最新版本处于测试阶段 于是我就删除这些旧插件(不符合我要求的插件) 于是根据各论坛的解决方法去做了以下的工作 1:项目中删除了这…...
SpringCloud+Vue+Python人工智能(fastAPI,机器学习,深度学习)前后端架构各功能实现思路——主目录(持续更新)
随着公司业务的增加,公司需要一个javapython人工智能相互配合架构,正常网站业务用java来做,而ai,例如电价预测等回归任务,以及大模型预测全网负荷,新能源出力等任务,使用python通过fastapi暴露接…...
【GIS操作】使用ArcGIS Pro进行海图的地理配准(附:墨卡托投影对比解析)
文章目录 一、应用场景二、墨卡托投影1、知识点2、Arcgis中的坐标系选择 三、操作步骤1、数据转换2、数据加载3、栅格投影4、地理配准 一、应用场景 地理配准是数字化之前必须进行的一项工作。扫描得到的地图数据通常不包含空间参考信息,需要通过具有较高位置精度的…...
C#,入门教程(03)——Visual Studio 2022编写彩色Hello World与动画效果
C#,入门教程(01)—— Visual Studio 2022 免费安装的详细图文与动画教程https://blog.csdn.net/beijinghorn/article/details/123350910 C#,入门教程(02)—— Visual Studio 2022开发环境搭建图文教程https://blog.csdn.net/beijinghorn/article/detail…...
使用 ChatGPT 生成和改进你的论文
文章目录 零、前言一、操作引导二、 生成段落或文章片段三、重写段落四、扩展内容五、生成大纲内容六、提高清晰度和精准度七、解决特定的写作挑战八、感受 零、前言 我是虚竹哥,目标是带十万人玩转ChatGPT。 ChatGPT 是一个非常有用的工具,可以帮助你…...
Unity2021.3.13崩溃的一种情况
如果出现如下的报错,可能是软件冲突的原因。自己的原因是使用f.lux这款软件似乎和Unity相互冲突,出现下面报错。 错误信息如上图...
通过ssh连接debian
使用方法 ssh usernameipaddress [inputpasswd]root用户默认无法由ssh连接, 可以通过修改配置 sudo vim /etc/ssh/sshd_config去掉PermitRootLogin前的‘#’,并修改为 PermitRootLogin yes 重启sshd服务 sudo systemctl restart sshd参考 https://linuxconfig.or…...
Transformer 与注意力机制原理说明与面试笔试题
文章大纲 注意力机制不同 种类的注意力机制Encoder Decoder 框架自注意力自注意力机制概述**基本原理****优点与应用****自注意力与多头注意力的关系****总结**多头注意力**多头注意力机制概述****基本原理****计算过程****多头注意力的优势****多头自注意力与多头注意力的区别…...
C语言之装甲车库车辆动态监控辅助记录系统
🌟 嗨,我是LucianaiB! 🌍 总有人间一两风,填我十万八千梦。 🚀 路漫漫其修远兮,吾将上下而求索。 C语言之装甲车库车辆动态监控辅助记录系统 目录 一、前言 1.1 (一)…...