当前位置: 首页 > 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;快来…...

《西安科技大学学报》

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

redis详细教程(2.List教程)

List是一种可以存储多个有序字符串的数据类型&#xff0c;其中的元素按照顺序排列&#xff08;可以重复出现&#xff09;&#xff0c;可以通过数字索引来访问列表中的元素&#xff0c;索引可以从左到右或者从右到左。 Redis 列表可以通过两种方式实现&#xff1a;压缩列表&…...

电子电气架构 --- 电气系统工程

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…...

15-4连续子串和的整除问题

问题描述 小M是一个五年级的小学生&#xff0c;今天他学习了整除的知识&#xff0c;想通过一些练习来巩固自己的理解。他写下了一个长度为 n 的正整数序列 a_0, a_1, ..., a_{n-1}&#xff0c;然后想知道有多少个连续子序列的和能够被一个给定的正整数 b 整除。你能帮小M解决这…...

Spring源码:Bean创建、Bean获取

Bean是怎么被创建&#xff0c;如何获取Bean&#xff0c;基于Spring 5.3.24版本&#xff0c;Spring Boot 可用 2.7.6 结论&#xff1a; 创建&#xff1a;非懒加载的单实例bean在容器创建的时候创建&#xff0c;通过beanFactory的doGetBean方法&#xff0c;利用反射进行创建&…...

MetaArena推出《Final Glory》:引领Web3游戏技术新风向

随着区块链技术的日益成熟&#xff0c;Web3游戏成为了游戏产业探索的新方向&#xff0c;将去中心化经济与虚拟世界结合在一起&#xff0c;形成了一个全新的生态体系。然而&#xff0c;尽管Web3游戏展示了令人兴奋的可能性&#xff0c;但其背后的技术障碍依旧严峻&#xff0c;特…...

玩转Shodan:深度挖掘特定漏洞与脆弱资产的实战技巧

内容预览 ≧∀≦ゞ Shodan进阶使用之发现并解锁隐藏的脆弱资产声明导语VNC未授权访问查询被黑的网站查询思科未授权设备查询MongoDB未授权访问搜索后台管理页面结语 Shodan进阶使用之发现并解锁隐藏的脆弱资产 声明 笔记内容参考了B站UP主泷羽sec的学习视频&#xff0c;如有侵…...

Java程序设计:spring boot(8)——API ⽂档构建⼯具 - Swagger2

目录 1 环境整合配置 2 Swagger2 常⽤注解说明 2.1 Api 2.2 ApiOperation 2.3 ApiImplicitParams 2.4 ApiResponses 2.5 ApiModel 3 用户模块注解配置 3.1 Controller 使用注解 3.2 JavaBean 使用注解 4 Swagger2 接⼝⽂档访问 由于 Spring Boot 能够快速开发、便捷…...

【Python】if选择判断结构详解:逻辑分支与条件判断

目录 &#x1f354; if选择判断结构作用 1.1 if选择判断结构的基本语法 1.2 if选择结构案例 1.3 if...else...结构 1.4 if...elif...else多条件判断结构 1.5 if嵌套结构 &#x1f354; 综合案例&#xff1a;石头剪刀布 2.1 需求分析 2.2 代码实现 2.3 随机出拳 &…...

邮件系统SSL加密传输,保护你的电子邮件免受网络威胁

在互联网的浪潮中&#xff0c;企业数字化转型的步伐不断加快。企业邮箱作为数字化应用的重要组成部分&#xff0c;已成为员工沟通、协同工作和企业管理的关键工具。但是在公共网络安全性普遍较弱的背景下&#xff0c;黑客容易侵入企业网络&#xff0c;监控流量&#xff0c;截获…...

Redis_写时复制(cow)

Redis会根据配置&#xff0c;每隔一段时间中对Redis服务中当下的数据集进行快照。配置自动生成rdb文件&#xff0c;后台使用的是bgsave方式。 save 60 1000 //关闭RDB只需要将所有的save保存策略注释掉即可Redis借助操作系统提供的写时复制技术&#xff08;Copy-On-Write, COW…...

淄博哪里有做网站的/seo工具包

回想Engineer类的数据成员&#xff0c;有眼镜、背包等。某Engineer的眼镜、背包&#xff0c;是Glass、Bag类的对象。类中的数据成员&#xff0c;其类型可以是简单类型&#xff0c;也可以是类。通过这种方式&#xff0c;将某些类组合到另外的类中&#xff0c;当作其中的一个“部…...

上海做网站公司qinmoo/手机百度账号登录个人中心

子查询的小秘密 多表查询之间使用子查询&#xff0c; 需要表之间具有联系&#xff0c; 比如让两张表进行联系&#xff0c; 需要一个共同所有的标识来联系&#xff1b; 两张表表示两个对象&#xff0c; 对象之间是独立的&#xff0c; 通过一个字段进行了联系。 &#xff08;还具…...

wordpress head.php/dw网页设计模板网站

如果你把oracle11g装在笔记本上并让服务开机启动的话&#xff0c;会明显感受到笔记本比平时启动慢几十秒&#xff0c; 差点的甚至1-2分钟&#xff0c;但是不开机启动吧&#xff0c;每次到服务里打开&#xff0c;很麻烦... 用批处理文件打开和关闭不失为一个好办法。 -------…...

如何做淘宝客个人网站/semantic scholar

Open Replicator ( http://code.google.com/p/open-replicator/ ) 开源了。Open Replicator是一个用Java编写的MySQL binlog分析程序。Open Replicator 首先连接到MySQL&#xff08;就像一个普通的MySQL Slave一样&#xff09;&#xff0c;然后接收和分析binlog&#xff0c;最终…...

网站建设前期规划论文/seo引擎优化外包公司

1.iPhone X的顶部有齐刘海&#xff0c;如果底部写一个fixed定位的底部悬浮不能到达底部&#xff0c;头部和底部都留有空隙&#xff0c;其实这是它的所谓的安全区&#xff0c;头部有44px&#xff0c;底部有34px。 2.完全充满 2.1在meta标签中声明viewport-fitcover <meta …...

wordpress 动态背景/模板之家

给出一个二维的字母板和一个单词&#xff0c;寻找字母板网格中是否存在这个单词。 单词可以由按顺序的相邻单元的字母组成&#xff0c;其中相邻单元指的是水平或者垂直方向相邻。每个单元中的字母最多只能使用一次。 样例 给出board [ “ABCE”, “SFCS”, “ADEE” ]…...