当前位置: 首页 > news >正文

嵌入式Linux的AXI平台(platform)驱动教程

本文以JFMQL100的Linux系统的AXI接口的平台驱动为例,介绍嵌入式Linux的平台驱动编写、测试软件编写以及验证方式。本文的方法适用于任意嵌入式芯片Linux的物理地址映射的平台(platform)驱动的编写、测试与应用。

       本文中AXI的开始地址为0x80000000,长度为0x20008KB)的地址空间进行地址映射。Vivado中查看AXI的地址范围如下所示:

图1 AXI地址映射

        本文PL-PS的中断(IRQ_F2P[0]),提醒PS端进行AXI数据读取。该中断在进口ZYNQ7的中断号为61,在JFMQL100TAI的中断号为57。在Linux系统设备树配置上均需要中断号减32,即ZYNQ7的中断号配置为29,JFMQL100TAI的中断号配置为25

图2  PL-PS的IRQ_F2P中断使能

1 设备树修改

在设备树的根节点添加自定义的axi驱动节点,如下所示:

图3 添加自定义的axi平台驱动

2 驱动源码

1.新建axi驱动目录,如下图所示:

图4 新建custom_axi_driver目录

2.在axi驱动目录下,新建axi驱动源文件custom_axi_driver.c,驱动源文件如下所示:

图5 新建custom_axi_driver.c的源文件

#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/version.h>
#include <linux/io.h>
#include <linux/ioctl.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/delay.h>#include <linux/string.h>
#include <linux/uaccess.h>#include <linux/errno.h>    
#include <linux/interrupt.h>
#include <linux/init.h>#include <linux/of_address.h>
#include <linux/of_dma.h>
#include <linux/of_platform.h>
#include <linux/of_irq.h>#define DEVICE_NAME "custom_axi_driver"#define AXI_BASE_ADDR_LEN       0x4000
#define AXI_BASE_ADDR           0x80000000enum IOCTL_CMD
{CMD_GET_FPGA_VERSION = 10,      //获得FPGA版本号 10CMD_GET_FPGA_TEMP ,             //获得FPGA温度   11CMD_READ_REG,                   //读axi寄存器,单个寄存器操作  12CMD_WRITE_REG                   //写axi寄存器,单个寄存器操作  13
};void __iomem *axi_base_addr;        //ioremap映射的虚拟地址/*定义一个平台*/
typedef struct axi_struct
{int major;struct class            *axi_class;     //struct device           *axi_class_dev; //设备struct cdev             axi_cdev;       //cdev 结构struct file_operations  *drv_fops;      //操作函数接口char                    axi_name[20];   //设备名称struct platform_device  *pdev;          //平台设备struct device_node      *nd;            //设备节点
}struct_axi_driver,*ptstruct_axi_driver;static dev_t dev_id; //设备编号
static int irq; //中断号static struct fasync_struct *irq_async;
//设备打开
static int custom_axi_open(struct inode *inode, struct file *file)
{printk(KERN_INFO"%s axi_open!!\n",__FUNCTION__);return 0;
}
//设备关闭
static int custom_axi_release(struct inode *inode, struct file *file)
{printk(KERN_INFO"%s axi_release!!\n",__FUNCTION__);return 0;
}
//设备写
static ssize_t custom_axi_write(struct file *flip,const char __user *buf,size_t cnt,loff_t *offt)
{unsigned int start_addr = 0;unsigned int len = 0;if(copy_from_user(&start_addr, buf, sizeof(unsigned int))){printk(KERN_ERR"%s copy_from_user is error!!\n",__FUNCTION__);return -1;}if(copy_from_user(&len, buf + sizeof(unsigned int), sizeof(unsigned int))){printk(KERN_ERR"%s copy_from_user is error!!\n",__FUNCTION__);return -1;}if(len <= AXI_BASE_ADDR_LEN){if(copy_from_user((unsigned int *)(axi_base_addr + start_addr), buf + sizeof(unsigned int)+ sizeof(unsigned int), len)){printk("%s copy_from_user is error!!\n",__FUNCTION__);return -1;}}else{printk("%s write num beyond AXI_ADDRESS_LEN!!\n",__FUNCTION__);return -1;}return len;
}
//设备读
static ssize_t custom_axi_read(struct file *flip, char __user *buf, size_t cnt, loff_t *offt)
{unsigned int start_addr = 0;unsigned int len = 0;if(copy_from_user(&start_addr, buf, sizeof(unsigned int))){printk(KERN_ERR"%s copy_from_user start_addr is error!!\n",__FUNCTION__);return -1;}if(copy_from_user(&len, buf + sizeof(unsigned int), sizeof(unsigned int))){printk(KERN_ERR"%s copy_from_user len is error!!\n",__FUNCTION__);return -1;}if(len <= AXI_BASE_ADDR_LEN){if(copy_to_user(buf + sizeof(unsigned int) + sizeof(unsigned int), (unsigned int *)(axi_base_addr + start_addr), len) != 0){printk(KERN_ERR"copy to user error!!\n");return -1;}}else{printk("%s read num beyond AXI_ADDRESS_LEN!!\n",__FUNCTION__);return -1;}return len;
}
//设备IO Control
static long custom_axi_ioctl(struct file *flip, unsigned int cmd, unsigned long arg)
{void __user *argp = (void __user *)arg;unsigned int reg_value = 0;unsigned int reg_addr = 0;switch(cmd){case CMD_GET_FPGA_VERSION://读FPGA版本号reg_value=__raw_readl(axi_base_addr);if(copy_to_user(argp,(unsigned int *)&reg_value ,sizeof(unsigned int))){printk("%s copy_to_user is error!!\n",__FUNCTION__);return -1;}break;case CMD_GET_FPGA_TEMP://读FPGA温度reg_value=__raw_readl((axi_base_addr + sizeof(unsigned int)));if(copy_to_user(argp,(unsigned int *)&reg_value ,sizeof(unsigned int))){printk("%s copy_to_user is error!!\n",__FUNCTION__);return -1;}break;case CMD_READ_REG://读axi寄存器if(copy_from_user((unsigned int *)&reg_addr,argp,sizeof(unsigned int))){printk("%s copy_from_user is error!!\n",__FUNCTION__);return -1;}reg_value=__raw_readl((axi_base_addr + reg_addr));if(copy_to_user(argp + sizeof(unsigned int),(unsigned int *)&reg_value ,sizeof(unsigned int))){printk("%s copy_to_user is error!!\n",__FUNCTION__);return -1;}break;case CMD_WRITE_REG://写axi寄存器if(copy_from_user((unsigned int *)&reg_addr,argp,sizeof(unsigned int))){printk("%s copy_from_user is error!!\n",__FUNCTION__);return -1;}if(copy_from_user((unsigned int *)&reg_value,argp+sizeof(unsigned int),sizeof(unsigned int))){printk("%s copy_from_user is error!!\n",__FUNCTION__);return -1;}__raw_writel(reg_value,(axi_base_addr + reg_addr));break;default:break;}return sizeof(unsigned int);//返回操作字节数
}
//中断
static int irq_drv_fasync (int fd, struct file *filp, int on)
{return fasync_helper (fd, filp, on, &irq_async);
}//中断触发函数
static irqreturn_t irq_interrupt(int irq, void *dev_id)
{kill_fasync (&irq_async, SIGIO, POLL_IN);return 0;
}struct file_operations axi_fops = {.owner = THIS_MODULE,.open = custom_axi_open,.release = custom_axi_release,.read = custom_axi_read,.write = custom_axi_write,.fasync = irq_drv_fasync,.unlocked_ioctl = custom_axi_ioctl
};static int custom_axi_probe(struct platform_device *pdev)
{int ret;ptstruct_axi_driver pt_axidriver = NULL;printk("axi probe start!\n");/************************************************************************ register character device    ***********************************************************************/printk("axi-reg-driver: register  device...\n");pt_axidriver = (ptstruct_axi_driver)kmalloc(sizeof(struct_axi_driver),GFP_KERNEL);if (pt_axidriver == NULL){printk(KERN_ERR"%s kmalloc is error!\n",__FUNCTION__);return -1;}axi_base_addr = ioremap(AXI_BASE_ADDR, AXI_BASE_ADDR_LEN);//映射本地地址if (!axi_base_addr) {printk(KERN_ERR"%s ioremap AXI_base is error!!\n",__FUNCTION__);goto error5;}pt_axidriver->pdev = pdev;platform_set_drvdata(pdev, pt_axidriver);pt_axidriver->drv_fops = &axi_fops;strcpy(pt_axidriver->axi_name,DEVICE_NAME);/*从系统获取主设备编号*/ret = alloc_chrdev_region(&dev_id, 0, 5, pt_axidriver->axi_name);if (ret < 0){printk(KERN_ERR"cannot alloc_chrdev_region!\n");goto error4;}pt_axidriver->major = MAJOR(dev_id);printk("MAJOR dev is successed!\n");cdev_init(&pt_axidriver->axi_cdev , &axi_fops); //初始化axi结构,fopsprintk("axi_cdev init!\n");if(cdev_add(&pt_axidriver->axi_cdev , dev_id, 5) != 0){printk(KERN_ERR"add axi error\n");goto error3;}printk("axi_cdev add ok!\n");pt_axidriver->axi_class = class_create(THIS_MODULE, pt_axidriver->axi_name);if(pt_axidriver->axi_class==NULL){printk(KERN_ERR"creat axi class error\n");goto error2;}printk("axi_cdev class init!\n");pt_axidriver->axi_class_dev = device_create(pt_axidriver->axi_class, NULL, MKDEV(pt_axidriver->major, 0), NULL, pt_axidriver->axi_name);if(pt_axidriver->axi_class_dev == NULL){printk(KERN_ERR"device_create is error!\n");goto error1;}printk("axi_cdev class dev init!\n");irq = platform_get_irq(pdev,0);if (irq <= 0){goto error0;}printk("system irq  = %d\n", irq);//申请上升沿中断触发ret = request_irq(irq,irq_interrupt,IRQF_TRIGGER_RISING | IRQF_ONESHOT,pt_axidriver->axi_name, NULL);if (ret) {printk(KERN_ERR"irq_probe irq	error!\n");goto error0;}return 0;free_irq(irq, NULL);
error0:device_destroy(pt_axidriver->axi_class,MKDEV(pt_axidriver->major, 0));
error1:class_destroy(pt_axidriver->axi_class);
error2:cdev_del(&pt_axidriver->axi_cdev);   //移除字符设备
error3:unregister_chrdev_region(dev_id, 5);
error4:iounmap(axi_base_addr);//释放地址映射
error5:kfree(pt_axidriver);return -1;
}static int  custom_axi_remove(struct platform_device *pdev)
{ptstruct_axi_driver  pt_axidriver = (ptstruct_axi_driver)platform_get_drvdata(pdev);free_irq(irq, NULL);//释放中断device_destroy(pt_axidriver->axi_class,MKDEV(pt_axidriver->major, 0));class_destroy(pt_axidriver->axi_class);cdev_del(&pt_axidriver->axi_cdev);       //移除字符设备unregister_chrdev_region(dev_id, 5);    //释放设备iounmap(axi_base_addr);                 //释放axi虚拟地址映射kfree(pt_axidriver);                    //释放驱动return 0;
}/*定义,初始化平台驱动*/
static const struct of_device_id custom_axi_of_match[] = {{.compatible = "lsl,custom_axi_driver"},{},
};
MODULE_DEVICE_TABLE(of, custom_axi_of_match);
static struct platform_driver custom_axi_platform_driver = {.driver = {.name = "custom_axi_driver",.of_match_table = custom_axi_of_match,},.probe = custom_axi_probe,.remove = custom_axi_remove,
};module_platform_driver(custom_axi_platform_driver);MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("lsl custom_axi_driver platform driver");
MODULE_AUTHOR("LSL");

3.在axi驱动目录下,新建Makefile文件,Makefile文件中指定内核源码路径。如下图所示:

图6 新建Makefile文件
图7 Makefile文件内容
图8 给Makefile文件添加可执行权限

4.在axi驱动目录下,新建编译脚本build.sh,编译脚本内容如下所示:

图9 新建编译脚本
图10 编译脚本build.sh内容
图11  编译脚本添加可执行权限

5.运行编译脚本,对驱动进行动态编译,生成驱动的ko文件custom_axi_driver.ko,编译结果如下所示:

图12 生成驱动的ko文件

 3 驱动测试程序

1.新建驱动测试程序目录app_driver_test,如下图所示:

图13 新建app_driver_test目录

2.在驱动测试目录下,新建驱动测试软件app_driver_test.c,驱动测试源文件如下图所示:

图14 新建app_driver_test.c的源文件

3.在驱动测试目录下,新建驱动测试软件app_driver_test.c,驱动测试源文件如下图所示:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include <linux/ioctl.h>
#include "app_driver_test.h"int pl_axi_fd;
static int irq_count = 0;int axi_writeReg(unsigned int reg,unsigned int value, unsigned int axi_offset_addr)
{Axi_CtrlRwStruct Axi_CtrlRwStructure;int ret;Axi_CtrlRwStructure.reg_addr=REG_ADDR(reg)+axi_offset_addr;Axi_CtrlRwStructure.reg_value=value;ret = ioctl(pl_axi_fd,CMD_WRITE_REG,&Axi_CtrlRwStructure);  if(ret<0){printf("axi_writeReg addr %xis error!\n",Axi_CtrlRwStructure.reg_addr);return -1;}return ret;
}int axi_readReg(unsigned int reg,unsigned int *value,unsigned int axi_offset_addr)
{int ret=0;Axi_CtrlRwStruct Axi_CtrlRwStructure;Axi_CtrlRwStructure.reg_addr=REG_ADDR(reg)+axi_offset_addr;ret = ioctl(pl_axi_fd,CMD_READ_REG,&Axi_CtrlRwStructure);  if(ret<0){printf("axi_readReg addr %xis error!\n",Axi_CtrlRwStructure.reg_addr);return -1;}*value=Axi_CtrlRwStructure.reg_value;return ret;
}int axi_GetFpga_Version(unsigned int *version)
{unsigned int buf;int ret;ret = ioctl(pl_axi_fd,CMD_GET_FPGA_VERSION,&buf);  if(ret<0){printf("axi_GetFpga_Version is error!\n");return -1;}*version=buf;return ret;
}int axi_GetFpga_Temp(unsigned int *temp)
{unsigned int buf;int ret;ret = ioctl(pl_axi_fd,CMD_GET_FPGA_TEMP,&buf);  if(ret<0){printf("axi_GetFpga_Temp is error!\n");return -1;}*temp=buf;return ret;
}int axi_writeData(unsigned int start_addr, unsigned int len, unsigned char *value)
{int ret;Axi_DataRwStruct Axi_DataRwStructure;if(len>MAX_DATA_BUFF_SIZE){printf("write the data len beyond Max buffer!\n");return -1;}Axi_DataRwStructure.start_addr=(start_addr/4)*4;//开始地址4字节对齐Axi_DataRwStructure.len=(len/4)*4;//长度4字节对齐memcpy(Axi_DataRwStructure.buff,value,Axi_DataRwStructure.len);ret=write(pl_axi_fd, &Axi_DataRwStructure, Axi_DataRwStructure.len+2*sizeof(unsigned int));if(ret<0){printf("axi_writeData is error!\n");return -1;}return ret;
}int axi_readData(unsigned int start_addr, unsigned int len, unsigned char *value)
{int ret;Axi_DataRwStruct Axi_DataRwStructure;if(len>MAX_DATA_BUFF_SIZE){printf("read the data len beyond Max buffer!\n");return -1;}Axi_DataRwStructure.start_addr=(start_addr/4)*4;//开始地址4字节对齐Axi_DataRwStructure.len=(len/4)*4;//长度4字节对齐ret=read(pl_axi_fd, &Axi_DataRwStructure, Axi_DataRwStructure.len+2*sizeof(unsigned int));if(ret<0){printf("axi_readData is error!\n");return -1;}memcpy(value,Axi_DataRwStructure.buff,ret);return ret;
} void pl_axi_device_init(void)
{int Oflags; pl_axi_fd = open(PL_AXI_DEV, O_RDWR);if (pl_axi_fd < 0){printf("can't open PL_AXI_DEV!\n");}fcntl(pl_axi_fd, F_SETOWN, getpid());Oflags = fcntl(pl_axi_fd, F_GETFL); fcntl(pl_axi_fd, F_SETFL, Oflags | FASYNC);}void sigio_irq_service(int signum)
{irq_count+=1;printf("recv the irq_count=%d\n",irq_count);//打印irq中断计数}  void pl_sigio_irq_init(void)
{signal(SIGIO, sigio_irq_service);//注册irq服务函数
}//main入口函数
int main(int argc, char **argv)
{unsigned int ver,temp;//定义版本号和温度的变量unsigned int reg_in=0x5aa51234;unsigned int reg_out;unsigned char data_in[12]={1,2,3,4,5,6,7,8,9,10,11,12};unsigned char data_out[12];unsigned int i,flag;pl_sigio_irq_init(); //中断使能pl_axi_device_init();//pl_axi设备打开axi_GetFpga_Version(&ver);printf("the fpga version is=0x%x\n",ver);//打印版本号axi_GetFpga_Temp(&temp);printf("the fpga temp is=0x%x\n",temp);//打印温度while(1){axi_writeReg(8,reg_in, 0);//向8号寄存器写入reg_in的值axi_readReg(8,&reg_out,0);if(reg_in==reg_out)//判断读写寄存器函数是否正确{printf("write read reg is right!\n");}else{printf("write read reg is error!\n");}axi_writeData(8,sizeof(data_in),data_in);//向8号寄存器写入连续的数据axi_readData(8,sizeof(data_out),data_out);for(i=0;i<sizeof(data_out);i++){if(data_in[i]!=data_out[i])//判断读写寄存器函数是否正确{flag=1;printf("write read data is error!\n");}}if(flag==0)printf("write read data is right!\n"); }return 0;
}

4.在axi驱动目录下,新建app_driver_test.h的头文件。头文件内容如下图所示:

图15 app_driver_test.h头文件

5.在axi驱动测试程序目录下,新建Makefile文件,Makefile指定交叉编译器环境变量。如下图所示:

图16 新建Makefile文件
图17 Makefile文件内容

6.在axi驱动目录下,新建编译脚本build.sh,编译脚本内容如下所示:

图18 新建编译脚本
图19 编译脚本build.sh内容
图20 编译脚本添加可执行权限

7.运行编译脚本,对驱动测试程序进行编译,生成驱动测试程序的可执行文件app_driver_test,编译结果如下所示:

图21 生成可执行文件

4 驱动与测试软件的验证

1.拷贝驱动ko文件和驱动测试程序可执行文件到嵌入式系统的工作目录下。

tftp -g -r custom_axi_driver.ko 192.168.0.11

tftp -g -r app_driver_test 192.168.0.11

chmod +x app_driver_test

图22 驱动和测试软件的下载

2.驱动加载

insmod custom_axi_driver.ko

图23 驱动程序加载

3.运行嵌入式程序的可执行文件。

./app_driver_test

图24 驱动程序测试,获得AXI的信息

相关文章:

嵌入式Linux的AXI平台(platform)驱动教程

本文以JFMQL100的Linux系统的AXI接口的平台驱动为例&#xff0c;介绍嵌入式Linux的平台驱动编写、测试软件编写以及验证方式。本文的方法适用于任意嵌入式芯片Linux的物理地址映射的平台&#xff08;platform&#xff09;驱动的编写、测试与应用。 本文中AXI的开始地址为0x8000…...

什么是Java策略模式?与Spring的完美结合

文章目录 什么是策略模式&#xff1f;策略模式的组成部分&#xff1a; 策略模式的示例在Spring中的妙用1. 使用Spring配置2. 在上下文中选择策略3. 动态切换策略 总结推荐阅读文章 在软件设计中&#xff0c;策略模式是一种非常常见的设计模式&#xff0c;它能够让算法的变化独立…...

[Go实战]:HTTP请求转发

前言 在Web应用开发中&#xff0c;请求转发是一项核心且常见的功能&#xff0c;用于负载均衡、服务拆分、路由重定向和业务逻辑处理。通过在Go语言中封装一个通用的HTTP请求转发方法&#xff0c;我们可以简化代码结构&#xff0c;提升可读性、可维护性和可扩展性。本文将探讨如…...

【C++单调栈 贡献法】907. 子数组的最小值之和|1975

本文涉及的基础知识点 C单调栈 LeetCode907. 子数组的最小值之和 给定一个整数数组 arr&#xff0c;找到 min(b) 的总和&#xff0c;其中 b 的范围为 arr 的每个&#xff08;连续&#xff09;子数组。 由于答案可能很大&#xff0c;因此 返回答案模 109 7 。 示例 1&#x…...

极狐GitLab 17.5 发布 20+ 与 DevSecOps 相关的功能【二】

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料&#xff1a; 极狐GitLab 官网极狐…...

Django 5 增删改查 小练习

1. 用命令创建目录和框架 django-admin startproject myapp cd myapp py manage.py startapp app md templates md static md media 2. Ai 生成代码 一、app/models.py from django.db import modelsclass Product(models.Model):name models.CharField(max_length255, verb…...

【STM32 Blue Pill编程实例】-I2C主从机通信(中断、DMA)

I2C主从机通信(中断、DMA) 文章目录 I2C主从机通信(中断、DMA)1、STM32的I2C介绍2、I2C模式3、STM32 I2C 数据包错误检查4、STM32 I2C 错误情况5、STM32 I2C中断6、STM32 I2C 主发送和接收(Tx 和 RX)6.1 I2C 轮询模式6.2 I2C 中断模式6.3 I2C DMA 模式6.4 STM32 I2C 设备…...

基于SSM+小程序的旅游社交登录管理系统(旅游4)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 ​ 本旅游社交小程序功能有管理员和用户。管理员有个人中心&#xff0c;用户管理&#xff0c;每日签到管理&#xff0c;景点推荐管理&#xff0c;景点分类管理&#xff0c;防疫查询管理&a…...

高级java每日一道面试题-2024年10月24日-JVM篇-说一下JVM有哪些垃圾回收器?

如果有遗漏,评论区告诉我进行补充 面试官: 说一下JVM有哪些垃圾回收器? 我回答: 1. Serial收集器 特点&#xff1a;Serial收集器是最古老、最稳定的收集器&#xff0c;它使用单个线程进行垃圾收集工作。在进行垃圾回收时&#xff0c;它会暂停所有用户线程&#xff0c;即St…...

Java-内部类

个人主页 学习内部类&#xff08;Inner Class&#xff09;是Java编程中一项重要且强大的特性&#xff0c;它允许你在一个类的内部定义另一个类。内部类提供了一种将逻辑上相关的类组织在一起的方式&#xff0c;增加了代码的封装性和可读性。接下来带领大家进入內部类的学习。 …...

flutter集成极光推送

一、简述 极光推送&#xff0c;英文简称 JPush&#xff0c;免费的第三方消息推送服务&#xff0c;官方也推出众多平台的SDK以及插件。 参考链接 名称地址客户端集成插件客户端集成插件 - 极光文档 二、操作步骤 2.1 添加插件 flutter项目中集成官方提供的 极光推送flutte…...

D. Skipping 【 Codeforces Round 980 (Div. 2)】

D. Skipping 思路: 注意到最佳策略是先往右跳转到某处&#xff0c;然后按顺序从右往左把没有遇到过的题目全部提交。 将从 i i i跳转到 b [ i ] b[i] b[i]视为通过边权(代价)为 a [ i ] a[i] a[i]的路径&#xff0c;而向左的路径边权都是 0 0 0&#xff1b;目的是找到到从出发…...

【golang】学习文档整理

Binding | Echo 传值时注意零值和传空的区别 需要validate require 和 设置指针配合使用 保证不同值的返回不同 不能客户端传0值被判断为空 测试时要空值零值去测试字段是否正确返回 返回错误是否符合预期...

动态规划-子序列问题——1218.最长定差子序列

1.题目解析 题目来源&#xff1a;1218.最长定差子序列——力扣 测试用例 2.算法原理 1.状态表示 本题可以看作是寻找一个等差序列&#xff0c;并且公差给出&#xff0c;这里并不是普通的使用一个dp表&#xff0c;而是将arr与dp表同时存储于一个哈希表&#xff0c;arr[i]映射dp…...

双子塔楼宇可视化系统:提升建筑管理与运营效率

利用图扑可视化技术对双子塔楼宇的各项功能进行实时监控和管理。通过数据分析优化资源配置&#xff0c;提高能源效率&#xff0c;增强楼宇安全性&#xff0c;实现智能化运营。...

32位的ARMlinux的4字节变量原子访问问题

在32位的ARM Linux内核中&#xff0c;4字节整型变量通常被认为是原子操作。 这主要是因为&#xff1a; 对齐要求&#xff1a;在ARM架构中&#xff0c;4字节整型变量通常是按4字节对齐存储的&#xff0c;这样可以确保在读取和写入时&#xff0c;CPU能够以单个指令完成操作。 …...

用哪种建站程序做谷歌SEO更容易?

做网站很容易&#xff0c;但做一个能带来流量和订单的网站就没那么简单了。尤其是在谷歌SEO优化方面&#xff0c;不同的建站程序对SEO的支持程度也不同。在这方面&#xff0c;WordPress和Shopify无疑是最佳选择。 WordPress作为一个内容管理系统&#xff08;CMS&#xff09;&am…...

IPsec简单介绍

VPN相关介绍 VPN&#xff1a;虚拟私有网络 例如&#xff1a;像这种不加密的 PPTPL2TP ------- 一般用在windows server 服务端&#xff08;但是大多数企业不用这个&#xff09; 假如总公司内部的PC1要去访问分公司内部的PC2&#xff08;一般用在公司服务器有内网的服务&#…...

颠覆级AI:10秒生成超清视频

颠覆级AI&#xff1a;10秒生成超清视频 Pyramid-Flow 是一款开源 AI 视频生成神器&#x1f4bb;&#xff0c;只需文字或图片即可极速生成高清视频&#x1f3a5;&#xff01;高效、高清、资源需求低&#xff0c;适合创作广告、教学视频等多种用途&#x1f680;&#xff0c;快来…...

《西安科技大学学报》

《西安科技大学学报》主要刊载安全科学与工程、矿业工程、建筑与土木工程、地质与环境工程、测绘工程、材料科学与工程、化学与化工、机械工程、电气工程及自动化、通信与信息工程、计算机科学与工程、矿业经济管理等专业领域内具有创新性的学术论文和科研成果。 来稿必须符合以…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

基于PHP的连锁酒店管理系统

有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...

DBLP数据库是什么?

DBLP&#xff08;Digital Bibliography & Library Project&#xff09;Computer Science Bibliography是全球著名的计算机科学出版物的开放书目数据库。DBLP所收录的期刊和会议论文质量较高&#xff0c;数据库文献更新速度很快&#xff0c;很好地反映了国际计算机科学学术研…...

CppCon 2015 学习:Time Programming Fundamentals

Civil Time 公历时间 特点&#xff1a; 共 6 个字段&#xff1a; Year&#xff08;年&#xff09;Month&#xff08;月&#xff09;Day&#xff08;日&#xff09;Hour&#xff08;小时&#xff09;Minute&#xff08;分钟&#xff09;Second&#xff08;秒&#xff09; 表示…...

Shell 解释器​​ bash 和 dash 区别

bash 和 dash 都是 Unix/Linux 系统中的 ​​Shell 解释器​​&#xff0c;但它们在功能、语法和性能上有显著区别。以下是它们的详细对比&#xff1a; ​​1. 基本区别​​ ​​特性​​​​bash (Bourne-Again SHell)​​​​dash (Debian Almquist SHell)​​​​来源​​G…...

第2课 SiC MOSFET与 Si IGBT 静态特性对比

2.1 输出特性对比 2.2 转移特性对比 2.1 输出特性对比 器件的输出特性描述了当温度和栅源电压(栅射电压)为某一具体数值时,漏极电流(集电极电流...

leetcode.多数元素

169. 多数元素 - 力扣&#xff08;LeetCode&#xff09; import java.util.HashMap;public class LeetCode169 {public int majorityElement(int[] nums) {int count nums.length;int res count/2;Scanner scanner new Scanner(System.in);HashMap<Integer,Integer> …...

Kotlin REPL初探

文章目录 1. Kotlin REPL 简介2. 在命令行中玩Kotlin REPL2.1 下载Kotlin编译器压缩包2.2 安装配置Kotlin编译器2.3 启动Kotlin交互式环境2.4 在命令行玩Kotlin REPL 3. 在IDEA里玩Kotlin REPL3.1 打开Kotlin REPL窗口3.2 在Kotlin REPL窗口玩代码 4. Kotlin REPL 的优势 1. Ko…...

JDK17 Http Request 异步处理 源码刨析

为什么可以异步&#xff1f; #调用起始源码 // 3. 发送异步请求并处理响应 CompletableFuture future client.sendAsync( request, HttpResponse.BodyHandlers.ofString() // 响应体转为字符串 ).thenApply(response -> { // 状态码检查&#xff08;非200系列抛出异常&…...