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

05 在 Linux 使用 AXI DMA

DMA简介

DMA 是一种采用硬件实现存储器与存储器之间或存储器与外设之间直接进行高速数据传输的技术,传输过程无需 CPU 参与(但是CPU需要提前配置传输规则),可以大大减轻 CPU 的负担。
DMA 存储传输的过程如下:

  1. CPU 向 DMA 控制器配置传输规则,如源地址、目的地址、长度、地址是否自增等
  2. DMA 控制器根据配置进行数据搬移,此时 CPU 资源被释放
  3. DMA 数据搬移完成后向 CPU 发送中断

AXI DMA简介

AXI Direct Memory Access(AXI DMA)是 ZYNQ PL端的一个 IP 核,它提供了 CPU 内存(AXI4 内存映射)和 PL 端 AXI4-Stream 之间高速数据传输的功能,如下是 AXI DMA 的框图:
在这里插入图片描述

  • AXI4 Memory Map Read:用于从 DDR3 中读取数据
  • AXI4 Memory Map Write:用于向 DDR3 中写入数据
  • AXI4 Stream Master(MM2S):接口用于向外设写入数据
  • AXI4-Stream Slave(S2MM):接口用于从外设读取数据
  • AXI Control stream (MM2S):是控制流接口,该接口的主要作用是 AXI DMA 对目标设备写入数据时进行节流
  • AXI Stream (S2MM):是一个状态流接口,主要作用是将目标设备的状态传输到 AXI DMA 中的用户应用程序数据字段中
  • AXI Memory Map Write/Read:接口的主要作用是在 S/G 模式下 AXI DMA 与外设进行数据的读写

ADX DMA工作模式

AXI DMA 提供 3 种模式,分别是 Direct Register 模式、Scatter/Gather 模式和 Cyclic DMA(循环 DMA)模式,其中 Direct Register 模式最常用,Scatter/Gather 模式和 Cyclic DMA 模式使用的相对较少

  1. Direct Register DMA 模式 :
    Direct Register DMA 模式也就是 Simple DMA(简单 DMA)。Direct Register 模式提供了一种配置,用于在 MM2S 和 S2MM 通道上执行简单的 DMA 传输,这种传输只需要少量的 FPGA 资源。Simple DMA(简单 DMA)允许应用程序在 DMA 和 Device 之间定义单个事务。它有两个通道:一个从 DMA 到 Device,另一个从 Device 到 DMA。这里有个地方需要大家注意下,在编写 Simple DMA(简单 DMA)代码时必须设置缓冲区地址和长度字段以启动相应通道中的传输。
  2. Scatter/Gather DMA 模式 :
    SGDMA(Scatter/Gather DMA)模式允许在单个 DMA 事务中将数据传输到多个存储区域(相当于将多个 Simple DMA 请求通过事务列表链接在一起)。SGDMA 允许应用程序在内存中定义事务列表,硬件将在应用程序没有进一步干预的情况下处理这些事务,在此期间应用程序可以继续添加更多事务以保持硬件工作,还可以通过轮询或中断来检查事务是否完成。
  3. Cyclic DMA 模式:
    Cyclic DMA(循环 DMA)模式是在 Scatter/Gather 模式下的一种独特工作方式,在 Multichannel Mode 下不可用。正常情况下的 Scatter/Gather 模式在遇到 Tail BD(最后一个事务列表项)就应该结束当前的传输,但是如果使能了 Cyclic 模式的话,在遇到 Tail BD时会忽略 completed 位,并且回到 First BD(第一个事务列表项),这一过程会一直持续直到遇到错误或者人为中止。

VIVADO 工程搭建

使用 Direct Register DMA 模式搭建一个 DMA 回环测试的 VIVADO 工程,Vivado 工程框图如下:
在这里插入图片描述

  1. 添加 ZYNQ IP 核,并进行配置,使能flash接口、AXI GP0、AXI HP0、ENET、SD、DDR、PL中断、PL时钟等,其中AXI GP0、AXI HP0、PL 中断、PL 时钟、PL 中断是为了连接 PL 端的AXI DMA IP 核
    AXI GP0、AXI HP0配置如下:
    在这里插入图片描述
    PL 时钟配置如下:
    在这里插入图片描述
    PL中断配置如下:
    在这里插入图片描述

  2. 添加 DMA IP 核
    在这里插入图片描述

  3. 配置 DMA IP 核
    在这里插入图片描述

  4. 添加 FIFO IP 核心
    在这里插入图片描述

  5. 连接DMA IP和FIFO IP,将 DMA 的 MM2S 和 S2MM 接口通过一个 AXIS DATA FIFO 构成回环。
    在这里插入图片描述

  6. 点击“Run Block Automation”和“Run Connection Automation”进行自动连线(可能会重复操作多次),在连线过程中会自动添加一些 IP 核。
    在这里插入图片描述

  7. 添加 concat IP 核和 const IP 核
    添加 concat IP:
    在这里插入图片描述
    添加 const IP:
    在这里插入图片描述

  8. 配置 concat IP 核和 const IP 核
    配置 concat IP:
    在这里插入图片描述
    配置 const IP:
    在这里插入图片描述

  9. 链接中断信号
    在这里插入图片描述

  10. 生成代码,然后编译并生成bit文件,最后导出 xsa 文件,导出时必须包含bit流
    在这里插入图片描述

利用 Petalinux 构建根文件系统和 BOOT.BIN

此部分内容参考04 搭建linux驱动开发环境中的利用 Petalinux 构建根文件系统和 BOOT.BIN部分。

获取 Linux 设备树

此部分内容04 搭建linux驱动开发环境中的获取Linux设备树文件部分
可以发现在 pl.dtsi 文件中已经生成了AXI DMA IP核的设备树节点
在这里插入图片描述

编译 Linux 内核

此部分内容04 搭建linux驱动开发环境中的编译Linux内核部分。

Linux 中使用 DMA的要点

DMA驱动框架

DMA驱动框架可分为三层:
DMA slave驱动:使用DMA设备的驱动程序,如SPI控制器驱动、UART控制器驱动等
DMA核心:提供统一的编程接口,管理DMA slave驱动和DMA控制器驱动
DMA控制器驱动:驱动DMA控制器
在Linux系统中DMA核心已经包含在系统中,DMA控制器驱动一般有芯片厂家提供,普通开发人员一般只要掌握DMA slave驱动开发即可
在这里插入图片描述

DMA 区域

部分SOC的DMA只能访问特定的一段内存空间,而非整个内存空间,所以在使用kmalloc()、__get_free_pages()等类似函数申请可能用于DMA缓冲区的内存时需要在申请标志中增加GFP_DMA标志,此外系统也通过了一些快捷接口,如__get_dma_pages()、dma_mem_alloc()等。
提示
大多数嵌入式SOC其DMA都可以访问整个常规内存空间。

虚拟地址、物理地址、总线地址

虚拟地址:CPU所使用的地址就是虚拟地址(站在CPU角度来看)。
物理地址:站在MMU角度来看,即DRAM空间向MMU呈现出来的地址,它可通过MMU转换为虚拟地址。
总线地址:站在设备的角度来看,即DRAM空间向设备呈现出来的地址,部分SOC带有IOMMU,还可以对总线地址进行映射后在呈现给设备。
在这里插入图片描述

DMA地址掩码

部分SOC的DMA只能访问特定的一段内存空间(如系统总线32bit地址,而DMA只能访问低24bit地址,在这种情况下,外设在发起DMA操作的时候就只能访问16M以下的系统物理内存),因此需要设置DMA mask,以指示DMA能访问的地址空间,如DMA只能访问低24位地址则需要执行dma_set_mask(dev, DMA_BIT_MASK(24))和dev.coherent_dma_mask = DMA_BIT_MASK(24)操作,dma_set_mask(dev, DMA_BIT_MASK(24))设置的是DMA流式映射的寻址范围,dev.coherent_dma_mask = DMA_BIT_MASK(24)设置一致性 DMA 缓冲区的寻址范围,或者使用int dma_set_mask_and_coherent(dev, DMA_BIT_MASK(24))一次完成这两部操作。

一致性DMA缓冲区

一致性DMA缓冲区即DMA和CPU可以一致访问的缓冲区,不需要考虑cache的影响,一般通过关闭cache实现(部分SOC的DMA也支持从通过cache访问内存),一致性缓冲区分别使用如下函数申请或释放:

//分配一致性缓冲区
void *dmam_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t gfp)
void *dma_alloc_wc(struct device *dev, size_t size, dma_addr_t *dma_addr, gfp_t gfp)
//释放一致性缓冲区
void dmam_free_coherent(struct device *dev, size_t size, void *vaddr, dma_addr_t dma_handle)
void dma_free_wc(struct device *dev, size_t size, void *cpu_addr, dma_addr_t dma_addr)

single类型流式映射

有时候需要通过DMA传输的内存并非由DMA分配,针对这种情况可以采用流式映射,若来自其他驱动的是一个连续的内存区域可以使用如下函数进行映射和取消映射:

//映射
dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size, enum dma_data_direction dir)
//取消映射
void dma_unmap_single((struct device *dev, dma_addr_t addr, size_t size, enum dma_data_direction dir)

被映射后的内存在取消映射前原则上CPU不能访问,若CPU实在需要访问需要先申请拥有权,访问完后在释放拥有权:

//申请CPU拥有权
void dma_sync_single_for_cpu(struct device *dev, dma_addr_t addr, size_t size, enum dma_data_direction dir)
//释放CPU拥有权
void dma_sync_single_for_device(struct device *dev, dma_addr_t addr, size_t size, enum dma_data_direction dir)

SG类型流式映射

若来自其他驱动的内存区域在物理上是不连续的则需要进行分段映射,可以使用如下函数进行映射和取消映射:

//映射
#define dma_map_sg(d, s, n, r) dma_map_sg_attrs(d, s, n, r, 0)
int dma_map_sg_attrs(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir, unsigned long attrs)
//取消映射
#define dma_unmap_sg(d, s, n, r) dma_unmap_sg_attrs(d, s, n, r, 0)
void dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir, unsigned long attrs)

同样对于被映射后的内存在取消映射前原则上CPU不能访问,若CPU实在需要访问需要先申请拥有权,访问完后在释放拥有权:

//申请CPU拥有权
void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,  int nelems, enum dma_data_direction dir)
//释放CPU拥有权
void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, enum dma_data_direction dir)

此外对于struct scatterlist还有如下常用宏和函数

//获取总线地址成员,可用于设置或读取总线地址
#define sg_dma_address(sg)
//获取长度成员,可用于设置或读取长度
#define sg_dma_len(sg)
//设置buf,它会获取buf的页地址和偏移,并将页地址、偏移、长度设置到struct scatterlist中
void sg_set_buf(struct scatterlist *sg, const void *buf, unsigned int buflen)
//获取buf的物理地址,这里的dma_addr_t返回的是物理地址,而非总线地址
dma_addr_t sg_phys(struct scatterlist *sg)
//获取buf的虚拟地址
void *sg_virt(struct scatterlist *sg)
//初始化SG列表,此函数不会设置buf
void sg_init_table(struct scatterlist *, unsigned int);
//初始化单个sg,并设置buf
void sg_init_one(struct scatterlist *, const void *, unsigned int);

DMA统一操作接口

DMA驱动框架提供了一套标准的DMA操作接口,常用的如下:

//申请DMA通道
struct dma_chan *dma_request_slave_channel(struct device *dev, const char *name)
struct dma_chan *dma_request_chan(struct device *dev, const char *name)
//释放DMA通道
void dma_release_channel(struct dma_chan *chan)
//配置DMA通道
int dmaengine_slave_config(struct dma_chan *chan, struct dma_slave_config *config)
//基于总线地址创建一个描述符
struct dma_async_tx_descriptor *dmaengine_prep_slave_single(struct dma_chan *chan, dma_addr_t buf, size_t len, enum dma_transfer_direction dir, unsigned long flags)
//基于struct scatterlist创建一个描述符
struct dma_async_tx_descriptor *dmaengine_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,	unsigned int sg_len, enum dma_transfer_direction dir, unsigned long flags)
//创建一个循环模式的描述符
struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction dir, unsigned long flags)
//创建一个用于设置内存值的描述符
struct dma_async_tx_descriptor *dmaengine_prep_dma_memset(struct dma_chan *chan, dma_addr_t dest, int value, size_t len, unsigned long flags)
//创建一个用于内存拷贝的描述符
struct dma_async_tx_descriptor *dmaengine_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, size_t len, unsigned long flags)
//提交描述符,在提交描述符前一般还需要设置描述符的callback和callback_param参数,以便描述符传输完后进行通知和获取处理结果,此外该函数的返回值还应采用dma_submit_error函数检查是否出差
dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc)
//启动提交的描述符,开始进行传输
void dma_async_issue_pending(struct dma_chan *chan)
//获取传输结果
enum dma_status dma_async_is_tx_complete(struct dma_chan *chan, dma_cookie_t cookie, dma_cookie_t *last, dma_cookie_t *used)
//struct dma_slave_config结构体
struct dma_slave_config {//传输的方向,DMA_MEM_TO_MEM:memory到memory的传输, DMA_MEM_TO_DEV:memory到设备的传输,DMA_DEV_TO_MEM:设备到memory的传输,DMA_DEV_TO_DEV:设备到设备的传输enum dma_transfer_direction direction;//传输方向是DMA_DEV_TO_MEM或DMA_DEV_TO_DEV时,读取数据的位置phys_addr_t src_addr;//传输方向是DMA_MEM_TO_DEV或DMA_DEV_TO_DEV时,写入数据的位置phys_addr_t dst_addr;//src地址的宽度,包括1、2、3、4、8、16、32、64(bytes)等enum dma_slave_buswidth src_addr_width;//dst地址的宽度,包括1、2、3、4、8、16、32、64(bytes)等enum dma_slave_buswidth dst_addr_width;//src最大可传输的突发长度u32 src_maxburst;//dst最大可传输的突发长度u32 dst_maxburst;//当外设是Flow Controller(流控制器)的时候,需要将该字段设置为truebool device_fc;//外部设备通过slave_id告诉dma controller自己是谁,很多dma controller不区分slave,只要给它src、dst、len等信息,它就可以进行传输,因此slave_id可以忽略,有些dma controller必须清晰地知道此次传输的对象是哪个外设,就必须要提供slave_idunsigned int slave_id;
};

DMA 从设备驱动设备树

使用DMA设备时只需要在设备树节点中增加如下属性即可:

//引用DMA节点,并传入参数指定使用哪一个通道(参数的具体值和个数依据DMA驱动决定)
dmas = <&axi_dma_0 0&axi_dma_0 1>;
//dma名称,与上面的dmas一一对应,申请dma通道时就是dma-names作为参数
dma-names ="axidma0", "axidma1";

编写AXI DMA回环测试驱动

DMA 驱动编写流程

  1. 申请DMA通道
  2. 配置DMA通道(部分专用DMA不需要进行配置)
  3. 分配DMA内存(对来自其他驱动模块的内存则进行映射操作)
  4. 创建描述符,并绑定传输完成回调函数
  5. 提交描述符
  6. 启动DMA传输
  7. 等待传输完成
  8. 检查传输结果
  9. 释放前面分配的DMA内存
  10. 释放DMA通道

设备树编写

在system-user.dtsi增的根节点中增加如下节点:

	axi_dma_test0: dma_test@0{compatible = "axi_dma_test";		/* 用于设备树与驱动进行匹配 */status = "okay";					/* 状态 */dmas = <&axi_dma_0 0				/* mm2s-channel*/&axi_dma_0 1>;				/* s2mm-channel */dma-names ="axidma0", "axidma1";	/* DMA名称,与dmas对应 */};

驱动代码编写

此驱动采用内核线程进行DMA回环测试,DMA相关操作均放在内核线程中实现(申请和释放dma通道除外)。

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/kthread.h>
#include <linux/idr.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>#define DMA_TEST_TASK_MAX		8#define DMA_TEST_BUFFER_COUNT	1		//一次传输测试的SG数量,vavido中未使能描述符模式,所以只能是1
#define DMA_TEST_BUFFER_SIZE	512		//每个SG缓冲区大小,不能超过vivado中用于回环的AXI DATA FIFO的大小#define DMA_TEST_COUNT			10		//测试次数struct dma_test_handle {struct task_struct *kthread;uint32_t number;struct dma_chan *mm2s_chan;struct dma_chan *s2mm_chan;uint32_t thread_done;
};static DEFINE_IDA(task_number_ida);static void mm2s_chan_callback(void *completion)
{complete((struct completion*)completion);
}static void s2mm_chan_callback(void *completion)
{complete((struct completion*)completion);
}static int dma_test_thread(void *arg)
{int loop_count;int buffer_index;int test_count;enum dma_status status;uint8_t *ptr_mm2s[DMA_TEST_BUFFER_COUNT];uint8_t *ptr_s2mm[DMA_TEST_BUFFER_COUNT];struct scatterlist mm2s_sg[DMA_TEST_BUFFER_COUNT];struct scatterlist s2mm_sg[DMA_TEST_BUFFER_COUNT];struct dma_async_tx_descriptor *mm2s_des;struct dma_async_tx_descriptor *s2mm_des;struct completion mm2s_cmp;struct completion s2mm_cmp;dma_cookie_t mm2s_cookie;dma_cookie_t s2mm_cookie;struct dma_test_handle *dma_test_handle = (struct dma_test_handle*)arg;printk("start axi_dma_test, task number = %d\r\n", dma_test_handle->number);//分配源内存,mm2smemset(ptr_mm2s, 0, sizeof(ptr_mm2s));for(loop_count = 0; loop_count < DMA_TEST_BUFFER_COUNT; loop_count++){ptr_mm2s[loop_count] = kmalloc(DMA_TEST_BUFFER_SIZE, GFP_KERNEL|GFP_DMA);if (!ptr_mm2s[loop_count])goto MM2S_ALLOC_ERROR;}//分配目的内存,s2mmmemset(ptr_s2mm, 0, sizeof(ptr_s2mm));for(loop_count = 0; loop_count < DMA_TEST_BUFFER_COUNT; loop_count++){ptr_s2mm[loop_count] = kmalloc(DMA_TEST_BUFFER_SIZE, GFP_KERNEL|GFP_DMA);if (!ptr_s2mm[loop_count])goto S2MM_ALLOC_ERROR;}//初始化完成量init_completion(&mm2s_cmp);init_completion(&s2mm_cmp);test_count = 0;while(!kthread_should_stop() && (test_count < DMA_TEST_COUNT)){//填充待发送的数据for(loop_count = 0; loop_count < DMA_TEST_BUFFER_COUNT; loop_count++){for(buffer_index = 0; buffer_index < DMA_TEST_BUFFER_SIZE; buffer_index++){ptr_mm2s[loop_count][buffer_index] = (uint8_t)(loop_count + buffer_index + test_count);}}//复位目的数据for(loop_count = 0; loop_count < DMA_TEST_BUFFER_COUNT; loop_count++){memset(ptr_s2mm[loop_count], 0, DMA_TEST_BUFFER_SIZE);}//初始化分散聚集映射描述符sg_init_table(mm2s_sg, DMA_TEST_BUFFER_COUNT);sg_init_table(s2mm_sg, DMA_TEST_BUFFER_COUNT);for(loop_count = 0; loop_count < DMA_TEST_BUFFER_COUNT; loop_count++){sg_set_buf(&mm2s_sg[loop_count], ptr_mm2s[loop_count], DMA_TEST_BUFFER_SIZE);sg_set_buf(&s2mm_sg[loop_count], ptr_s2mm[loop_count], DMA_TEST_BUFFER_SIZE);}//进行分散聚集映射if(dma_map_sg(dma_test_handle->mm2s_chan->device->dev, mm2s_sg, DMA_TEST_BUFFER_COUNT, DMA_TO_DEVICE) == 0){printk("map mm2s_sg faled\r\n");break;}if(dma_map_sg(dma_test_handle->s2mm_chan->device->dev, s2mm_sg, DMA_TEST_BUFFER_COUNT, DMA_FROM_DEVICE) == 0){dma_unmap_sg(dma_test_handle->mm2s_chan->device->dev, mm2s_sg, DMA_TEST_BUFFER_COUNT, DMA_TO_DEVICE);printk("map s2mm_sg faled\r\n");break;}//创建DMA传输描述符mm2s_des = dmaengine_prep_slave_sg(dma_test_handle->mm2s_chan, mm2s_sg, DMA_TEST_BUFFER_COUNT, DMA_MEM_TO_DEV, DMA_CTRL_ACK | DMA_PREP_INTERRUPT);if(!mm2s_des){dma_unmap_sg(dma_test_handle->s2mm_chan->device->dev, s2mm_sg, DMA_TEST_BUFFER_COUNT, DMA_FROM_DEVICE);dma_unmap_sg(dma_test_handle->mm2s_chan->device->dev, mm2s_sg, DMA_TEST_BUFFER_COUNT, DMA_TO_DEVICE);printk("create mm2s_des faled\r\n");break;}s2mm_des = dmaengine_prep_slave_sg(dma_test_handle->s2mm_chan, s2mm_sg, DMA_TEST_BUFFER_COUNT, DMA_DEV_TO_MEM, DMA_CTRL_ACK | DMA_PREP_INTERRUPT);if(!s2mm_des){dma_unmap_sg(dma_test_handle->s2mm_chan->device->dev, s2mm_sg, DMA_TEST_BUFFER_COUNT, DMA_FROM_DEVICE);dma_unmap_sg(dma_test_handle->mm2s_chan->device->dev, mm2s_sg, DMA_TEST_BUFFER_COUNT, DMA_TO_DEVICE);printk("create s2mm_des faled\r\n");break;}//绑定传输完成回调函数reinit_completion(&mm2s_cmp);mm2s_des->callback = mm2s_chan_callback;mm2s_des->callback_param = &mm2s_cmp;reinit_completion(&s2mm_cmp);s2mm_des->callback = s2mm_chan_callback;s2mm_des->callback_param = &s2mm_cmp;//提交描述符mm2s_cookie = dmaengine_submit(mm2s_des);if(dma_submit_error(mm2s_cookie)){dma_unmap_sg(dma_test_handle->s2mm_chan->device->dev, s2mm_sg, DMA_TEST_BUFFER_COUNT, DMA_FROM_DEVICE);dma_unmap_sg(dma_test_handle->mm2s_chan->device->dev, mm2s_sg, DMA_TEST_BUFFER_COUNT, DMA_TO_DEVICE);printk("submit mm2s_des faled\r\n");break;}s2mm_cookie = dmaengine_submit(s2mm_des);if(dma_submit_error(s2mm_cookie)){dma_unmap_sg(dma_test_handle->s2mm_chan->device->dev, s2mm_sg, DMA_TEST_BUFFER_COUNT, DMA_FROM_DEVICE);dma_unmap_sg(dma_test_handle->mm2s_chan->device->dev, mm2s_sg, DMA_TEST_BUFFER_COUNT, DMA_TO_DEVICE);printk("submit s2mm_des faled\r\n");break;}//启动传输dma_async_issue_pending(dma_test_handle->mm2s_chan);dma_async_issue_pending(dma_test_handle->s2mm_chan);//等待传输完成if(wait_for_completion_timeout(&mm2s_cmp, msecs_to_jiffies(300000)) == 0){dma_unmap_sg(dma_test_handle->s2mm_chan->device->dev, s2mm_sg, DMA_TEST_BUFFER_COUNT, DMA_FROM_DEVICE);dma_unmap_sg(dma_test_handle->mm2s_chan->device->dev, mm2s_sg, DMA_TEST_BUFFER_COUNT, DMA_TO_DEVICE);printk("mm2s transfer timeout\r\n");break;}if(wait_for_completion_timeout(&s2mm_cmp, msecs_to_jiffies(300000)) == 0){dma_unmap_sg(dma_test_handle->s2mm_chan->device->dev, s2mm_sg, DMA_TEST_BUFFER_COUNT, DMA_FROM_DEVICE);dma_unmap_sg(dma_test_handle->mm2s_chan->device->dev, mm2s_sg, DMA_TEST_BUFFER_COUNT, DMA_TO_DEVICE);printk("s2mm transfer timeout\r\n");break;}//获取传输结果status = dma_async_is_tx_complete(dma_test_handle->mm2s_chan, mm2s_cookie, NULL, NULL);if(status != DMA_COMPLETE){dma_unmap_sg(dma_test_handle->s2mm_chan->device->dev, s2mm_sg, DMA_TEST_BUFFER_COUNT, DMA_FROM_DEVICE);dma_unmap_sg(dma_test_handle->mm2s_chan->device->dev, mm2s_sg, DMA_TEST_BUFFER_COUNT, DMA_TO_DEVICE);printk("mm2s transfer error\r\n");break;}status = dma_async_is_tx_complete(dma_test_handle->s2mm_chan, s2mm_cookie, NULL, NULL);if(status != DMA_COMPLETE){dma_unmap_sg(dma_test_handle->s2mm_chan->device->dev, s2mm_sg, DMA_TEST_BUFFER_COUNT, DMA_FROM_DEVICE);dma_unmap_sg(dma_test_handle->mm2s_chan->device->dev, mm2s_sg, DMA_TEST_BUFFER_COUNT, DMA_TO_DEVICE);printk("s2mm transfer error\r\n");break;}//取消映射dma_unmap_sg(dma_test_handle->s2mm_chan->device->dev, s2mm_sg, DMA_TEST_BUFFER_COUNT, DMA_FROM_DEVICE);dma_unmap_sg(dma_test_handle->mm2s_chan->device->dev, mm2s_sg, DMA_TEST_BUFFER_COUNT, DMA_TO_DEVICE);//验证传输结果for(loop_count = 0; loop_count < DMA_TEST_BUFFER_COUNT; loop_count++){for(buffer_index = 0; buffer_index < DMA_TEST_BUFFER_SIZE; buffer_index++){if(ptr_mm2s[loop_count][buffer_index] != ptr_s2mm[loop_count][buffer_index]){printk("verifying failed!!!\r\n");printk("mm2s[%d][%d] = %d, s2mm[%d][%d] = %d\r\n", loop_count, buffer_index, ptr_mm2s[loop_count][buffer_index],loop_count, buffer_index, ptr_s2mm[loop_count][buffer_index]);break;}}if(buffer_index < DMA_TEST_BUFFER_SIZE)break;}if((loop_count < DMA_TEST_BUFFER_COUNT) || (buffer_index < DMA_TEST_BUFFER_SIZE))break;printk("%d.DMA test success\r\n", test_count);//测试技术递增test_count++;}//停止通道上的所有传输事件dmaengine_terminate_all(dma_test_handle->mm2s_chan);dmaengine_terminate_all(dma_test_handle->s2mm_chan);S2MM_ALLOC_ERROR:for(loop_count = 0; loop_count < DMA_TEST_BUFFER_COUNT; loop_count++){if(ptr_s2mm[loop_count])kfree(ptr_s2mm[loop_count]);}
MM2S_ALLOC_ERROR:for(loop_count = 0; loop_count < DMA_TEST_BUFFER_COUNT; loop_count++){if(ptr_mm2s[loop_count])kfree(ptr_mm2s[loop_count]);}dma_test_handle->thread_done = 1;return 0;
}//设备和驱动匹配成功执行
static int dma_test_probe(struct platform_device *pdev)
{int result;struct dma_test_handle *dma_test_handle;printk("%s probe\r\n", pdev->name);//分配设备句柄dma_test_handle = devm_kzalloc(&pdev->dev, sizeof(struct dma_test_handle), GFP_KERNEL);if(!dma_test_handle){printk("alloc memory failed\r\n");return -ENOMEM;}//复位LED设备句柄memset(dma_test_handle, 0, sizeof(struct dma_test_handle));//分配一个编号,用于标识测试任务dma_test_handle->number = ida_simple_get(&task_number_ida, 0, DMA_TEST_TASK_MAX, GFP_KERNEL);if(dma_test_handle->number < 0){printk("get number failed\r\n");return dma_test_handle->number;}//请求DMA通道dma_test_handle->mm2s_chan = dma_request_chan(&pdev->dev, "axidma0");if(IS_ERR(dma_test_handle->mm2s_chan)) {ida_simple_remove(&task_number_ida, dma_test_handle->number);result = IS_ERR(dma_test_handle->mm2s_chan);printk("xilinx_dmatest: No Tx channel\n");}dma_test_handle->s2mm_chan = dma_request_chan(&pdev->dev, "axidma1");if(IS_ERR(dma_test_handle->s2mm_chan)) {dma_release_channel(dma_test_handle->mm2s_chan);ida_simple_remove(&task_number_ida, dma_test_handle->number);result = IS_ERR(dma_test_handle->s2mm_chan);printk("xilinx_dmatest: No Rx channel\n");}//线程主动退出标志dma_test_handle->thread_done = 0;//创建内核线程dma_test_handle->kthread = kthread_create(dma_test_thread, dma_test_handle, "dma_test_thread%d", dma_test_handle->number);if(IS_ERR(dma_test_handle->kthread)){dma_release_channel(dma_test_handle->s2mm_chan);dma_release_channel(dma_test_handle->mm2s_chan);ida_simple_remove(&task_number_ida, dma_test_handle->number);printk("create dma_test_thread failed\r\n");return IS_ERR(dma_test_handle->kthread);}//启动内核线程wake_up_process(dma_test_handle->kthread);//设置平台设备的驱动私有数据pdev->dev.driver_data = (void*)dma_test_handle;return 0;
}//设备或驱动卸载时执行
static int dma_test_remove(struct platform_device *pdev)
{struct dma_test_handle *dma_test_handle;printk("%s remove\r\n", pdev->name);//提取平台设备的驱动私有数据dma_test_handle = (struct dma_test_handle*)pdev->dev.driver_data;//停止内核线程if(dma_test_handle->thread_done == 0)kthread_stop(dma_test_handle->kthread);//释放DMA通道dma_release_channel(dma_test_handle->s2mm_chan);dma_release_channel(dma_test_handle->mm2s_chan);//释放测试任务编号ida_simple_remove(&task_number_ida, dma_test_handle->number);return 0;
}//系统关机前执行
static void dma_test_shutdown(struct platform_device *pdev)
{printk("%s shutdown\r\n", pdev->name);
}//系统进入睡眠状态之前执行
static int dma_test_suspend(struct platform_device *pdev, pm_message_t state)
{printk("%s suspend\r\n", pdev->name);return 0;
}//系统从睡眠状态中唤醒系统后执行
static int dma_test_resume(struct platform_device *pdev)
{printk("%s resume\r\n", pdev->name);return 0;
}//匹配列表,用于设备树和平台驱动匹配
static const struct of_device_id dma_test_of_match[] = {{ .compatible = "axi_dma_test" },{ /* Sentinel */ }
};
//平台驱动
struct platform_driver dma_test_drv = {.driver = {.name = "axi_dma_test",				//平台驱动名称.owner = THIS_MODULE,.pm = NULL,.of_match_table = dma_test_of_match,},.probe = dma_test_probe,				//设备和驱动匹配成功执行.remove = dma_test_remove,				//设备或驱动卸载时执行.shutdown = dma_test_shutdown,			//系统关机前执行.suspend = dma_test_suspend,			//系统休眠前执行.resume = dma_test_resume,				//系统唤醒后执行
};static int __init plt_drv_init(void)
{int result;printk("%s\r\n", __FUNCTION__);//注册平台驱动result = platform_driver_register(&dma_test_drv);if(result < 0){printk("add cdev failed\r\n");return result;}return 0;
}static void __exit plt_drv_exit(void)
{printk("%s\r\n", __FUNCTION__);//注销平台驱动platform_driver_unregister(&dma_test_drv);
}module_init(plt_drv_init);
module_exit(plt_drv_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("csdn");
MODULE_DESCRIPTION("dma_test_dev");

相关文章:

05 在 Linux 使用 AXI DMA

DMA简介 DMA 是一种采用硬件实现存储器与存储器之间或存储器与外设之间直接进行高速数据传输的技术&#xff0c;传输过程无需 CPU 参与&#xff08;但是CPU需要提前配置传输规则&#xff09;&#xff0c;可以大大减轻 CPU 的负担。 DMA 存储传输的过程如下&#xff1a; CPU 向…...

android AIDL ipc binder转换

一. 概述 众所周知AIDL并不是所有的数据类型都可以传输。 可以传输的类型包括&#xff1a; 1.原生类型 2.String 3. CharSequence 4. List 5.Map 6. Binder 7. Parcelable 容器类 parcelable传输的时候会分解成成员&#xff0c;数组item的方式&#xff0c;传输完成后再进行…...

【docker】12. Docker Volume(存储卷)

什么是存储卷? 存储卷就是将宿主机的本地文件系统中存在的某个目录直接与容器内部的文件系统上的某一目录建立绑定关系。这就意味着&#xff0c;当我们在容器中的这个目录下写入数据时&#xff0c;容器会将其内容直接写入到宿主机上与此容器建立了绑定关系的目录。 在宿主机上…...

1. 机器学习基本知识(4)——机器学习测试和验证

1.6 测试和验证 了解模型对新实例的泛化能力的唯一方法是在新实例上进行实际尝试。 一种方法是将模型部署到生产环境并监控其性能。 ​ 这种方法很有效&#xff0c;但如果模型非常糟糕&#xff0c;你的用户就会抱怨&#xff0c;所以这显然不是最好的方法。 更好的选择是将数…...

生成式AI概览与详解

1. 生成式AI概览&#xff1a;什么是大模型&#xff0c;大模型应用场景&#xff08;文生文&#xff0c;多模态&#xff09; 生成式AI&#xff08;Generative AI&#xff09;是指通过机器学习模型生成新的数据或内容的人工智能技术。生成式AI可以生成文本、图像、音频、视频等多种…...

软件项目标书参考,合同拟制,开发合同制定,开发协议,标书整体技术方案,实施方案,通用套用方案,业务流程,技术架构,数据库架构全资料下载(原件)

1、终止合同协议书 2、项目合作协议 3、合同交底纪要 4、合同管理台账 软件资料清单列表部分文档清单&#xff1a;工作安排任务书&#xff0c;可行性分析报告&#xff0c;立项申请审批表&#xff0c;产品需求规格说明书&#xff0c;需求调研计划&#xff0c;用户需求调查单&…...

C++ extern “C“ 用法

extern “C” 由于c中需要支持函数重载&#xff0c;所以c和c中对同一个函数经过编译后生成的函数名是不相同的 extern “C” 的主要作用就是为了实现c代码能够调用其他 c 语言代码。 1(不常用) //告诉编译器 show() 函数按c语言的方式进行编译和链接 extern "C" voi…...

Node.js(v16.13.2版本)安装及环境配置教程

一、进入官网地址下载安装包 https://nodejs.org/zh-cn/download/ 选择对应你系统的Node.js版本&#xff0c;这里我选择的是Windows系统、64位&#xff08;v16.13.2版本&#xff09; 下载后的zip文件 二、解压文件到nodejs&#xff0c;并打开文件夹nodejs&#xff0c;复制解压…...

父组件调用子组件的方法

父组件 在父组件中调用子组件的sayHello()函数 <template><div><button click"getChild(wendu)">触发子组件方法wendu</button><button click"getChild(shidu)">触发子组件方法shidu</button><el-dialog v-model…...

运维排错系列:Excel上传失败,在剪切板有大量信息。是否保存其内容...

问题点 在导入 Excel 数据到 SAP 的时候&#xff0c;某些时刻系统会出现如下的弹窗。 上载 excel 文件时&#xff0c;您会收到错误&#xff1a;“剪贴板上有大量信息。XXX” Microsoft Office Excel 的弹出窗口显示以下信息&#xff1a; 剪贴板上存在大量信息。是否保留其内容…...

【自学】Vues基础

学习目录 Vues基础本地应用网络应用综合应用 工具的准备 我个人比较喜欢使用HTMLDROWNER&#xff0c;学习资料推荐使用VC&#xff0c;仅供选择吧 前置知识 HTMLCSSJSAJAX&#xff1a;这个是学习资料博主推荐的 个人感觉认真学好HTMLCSSJS理解vues基础很容易上手 官方网址…...

ubuntu 7z解压rar文件报错:unsupported method message

问题说明 最近项目需要支持线上上传rar格式&#xff0c;7z来解压缩入库。开发测试过程中发现使用以下命令解压报错&#xff0c; 7z x FileImportTest01.rar -p"123456" -o/home/download -y文件目录内容已列出&#xff0c;但无法解压文件!!! 仔细检查命令没有问题…...

C语言基础六:循环结构及面试上机题

Day06&#xff1a;循环结构 定义 代码的重复执行&#xff0c;就叫做循环 循环的分类 无限循环:其实就是死循环&#xff0c;程序设计中尽量避免无限循环。程序中的无限循环必须可控。有限循环:循环限定循环次数或者循环的条件。 循环的构成 循环条件循环体 当型循环的实现…...

【蓝桥杯每日一题】技能升级

技能升级 2024-12-10 蓝桥杯每日一题 技能升级 二分 题目大意 一个角色有 N 种可以增加攻击力的技能&#xff0c;对于第 i 个技能首次升级可以提升 A i A_i Ai​ 点攻击力&#xff0c;随后的每次升级增加的攻击力都会减少 B i B_i Bi​ 。升级 ⌈ A i B i ⌉ \lceil \frac{A…...

C++作业4

1、思维导图 2、Person和Stu的拷贝构造和拷贝赋值函数 #include <iostream> using namespace std; class Person {string name;int *age; public:Person():age(new int(23)){cout << "Person的无参构造" << endl;}//初始化列表 :成员属性1(值),成…...

密码学——密码学基础、散列函数与数字签名

1.密码学概述 是信息安全的基础和核心&#xff0c;是防范各种安全威胁的重要手段&#xff0c;信息安全的许多相关知识都与密码学相关。 密码学发展 密码学是一门古老而又年轻的学科 &#xff0c;几千年以前就存在&#xff0c;至今仍在发展演进。地位非常重要甚至起决定性作用…...

鹦鹉的饮食偏好:探索多彩的食物世界

鹦鹉&#xff0c;作为聪明且迷人的鸟类&#xff0c;其饮食习性一直是鸟类爱好者关注的焦点。了解鹦鹉喜欢吃什么食物&#xff0c;对于确保它们的健康与幸福至关重要。 鹦鹉的食物种类丰富多样&#xff0c;首先是各类种子与谷物。例如&#xff0c;葵花籽富含脂肪和蛋白质&#…...

AtCoder Beginner Contest 383

C - Humidifier 3 Description 一个 h w h \times w hw 的网格&#xff0c;每个格子可能是墙、空地或者城堡。 一个格子是好的&#xff0c;当且仅当从至少一个城堡出发&#xff0c;走不超过 d d d 步能到达。&#xff08;只能上下左右走&#xff0c;不能穿墙&#xff09;&…...

HALCON算子函数 Filter(过滤)(1)

Arithmetic图像算数处理 abs_diff_image&#xff1a;计算两个图像的绝对差别&#xff1b;abs_image&#xff1a;计算一个图像的绝对值&#xff08;模数&#xff09;&#xff1b;add_image&#xff1a;使两个图像相加&#xff1b;div_image&#xff1a;使两个图像相除&#xff…...

哈希知识详解

目录 一、哈希 二、哈希函数 1、直接定值法 2、除留余数法 三、哈希冲突 四、哈希冲突解决 1、闭散列(开放定值法) 闭散列代码 2、哈希桶 哈希桶的结构 查找方法 插入方法 删除方法 析构 迭代器 完整哈希桶代码 五、通过哈希桶封装 unordered_map 和 unordered_set 一、…...

分库分表基本概念讲解

一、基本概念 产生背景 在数据爆炸的年代&#xff0c;单表数据达到千万级别&#xff0c;甚至过亿的量&#xff0c;都是很常见的情景。这时候再对数据库进行操作就是非常吃力的事情了&#xff0c;select个半天都出不来数据&#xff0c;这时候业务已经难以维系。不得已&#xf…...

【DRAM存储器四十二】LPDDR5介绍--LPDDR5的bank架构还能配,为什么8B模式只支持BL32?

👉个人主页:highman110 👉作者简介:一名硬件工程师,持续学习,不断记录,保持思考,输出干货内容 参考资料:《某LPDDR5数据手册》 、《JESD209-5C》 目录 BG mode 16B mode 8B mode...

ubuntu 新建脚本shell并增加图标 双击应用实现python运行

1.使用nano创建shell脚本文件 需要在终端窗口中输入“nano”以打开文本编辑器。 nano 在创建脚本文件前&#xff0c;我们要了解脚本文件是如何运行的&#xff1a; 直接运行&#xff1a;直接在终端直接输入需要运行的脚本文件名称&#xff0c;系统或用缺省版本的shell运行脚…...

【0x0006】HCI_Disconnect命令详解

目录 一、命令概述 二、命令格式和参数说明 2.1. HCI_Disconnect 命令格式 2.2. Connection_Handle 2.3. Reason 三、返回事件说明 3.1. HCI_Command_Status 事件 3.2. HCI_Disconnection_Complete事件 3.3. HCI_LE_CIS_Established事件(针对CIS) 四、事件执行流程…...

【C语言练习(5)—回文数判断】

C语言练习&#xff08;5&#xff09; 文章目录 C语言练习&#xff08;5&#xff09;前言问题问题解析结果总结 前言 通过回文数练习&#xff0c;巩固数字取余和取商如何写代码 问题 输入一个五位数判断是否为回文数&#xff1f; 问题解析 回文数是指正读反读都一样的整数。…...

【全连接神经网络】核心步骤及其缺陷

前向传播 计算公式&#xff08;其中一种&#xff09; x1/x2&#xff1a;输入值&#xff0c;一般是神经网络上一层的输出或者输入数据本身&#xff0c;上图中表示两个节点w11 w13&#xff1a;权重&#xff0c;在神经网络中&#xff0c;权重是学习的参数&#xff0c;表示每个输入…...

【Vulkan入门】11-CreateCommandBuffer

[TOC]目录 先叨叨 先叠个甲&#xff0c;我个人都觉得本篇有点灌水的嫌疑。 到上篇为止&#xff0c;已经创建好了Pipeline。接下来就是要给Pipeline发指令进行渲染了。 从一开始就不断强调&#xff0c;发给Vulkan的指令要通过Queue发送给Device。命令不是一条一条推到Queue中的…...

Redis原理—4.核心原理摘要

大纲(9870字) 1.Redis服务器的Socket网络连接建立 2.Redis多路复用监听与文件事件模型 3.基于队列串行化的文件事件处理机制 4.完整的Redis Server网络通信流程 5.Redis串行化单线程模型为什么能高并发 6.Redis内核级请求处理流程与原理 7.Redis通信协议与内核级请求数据…...

面向对象系统的分析和设计

来源&#xff1a;《设计模式精解-GOF23种设计模式解析》 作者&#xff1a;k_eckel k_eckels mindview - 博客园 (cnblogs.com) --------- 面向对象系统的分析和设计实际上追求的就是两点&#xff1a; &#xff08;1&#xff09;高内聚 &#xff08;2&#xff09;低耦合 …...

单片机:实现交通信号灯(附带源码)

使用单片机实现交通信号灯控制系统是一个经典的嵌入式系统应用。这个项目可以帮助你理解如何通过单片机控制不同颜色的LED灯、处理时间控制、以及输入输出的基本操作。通过这个项目&#xff0c;你将掌握如何设计交通信号灯的时序控制、如何实现定时控制交通灯的切换、以及如何与…...

小白如何学习看懂CAD图纸?

首先&#xff0c;你需要了解CAD图纸的基本构成&#xff0c;包括图例、尺寸标注、比例等等。接着&#xff0c;你可以通过一些专业的书籍、在线课程或视频教程来逐步学习如何识别和理解这些元素。但建议不要学的太复杂了。 掌握基本概念&#xff1a; 坐标系&#xff1a;了解CAD…...

HarmonyOS-高级(一)

文章目录 一次开发、多端部署自由流转 &#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;HarmonyOS专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年12月09日12点19分 一次开发、多端部署 布局能力 自适应布局 拉伸能力均分能力占比能力缩放…...

Datawhale AI 冬令营(第一期)定制你的第一个专属模型-学习笔记

最近我报名参加了Datawhale组织的主题为“动手学系列&#xff0c;人人都能应用的AI”的Datawhale AI冬令营&#xff08;第一期&#xff09;。 本次学习一共12天&#xff0c;从12月10日-12月21日&#xff0c;学习会包含【跑通速通手册】&#xff0c;【学习大模型微调&数据集…...

群控系统服务端开发模式-应用开发-登录退出发送邮件

一、登录成功发送邮件 在根目录下app文件夹下controller文件夹下common文件夹下&#xff0c;修改Login.php&#xff0c;代码如下 <?php /*** 登录退出操作* User: 龙哥三年风水* Date: 2024/10/29* Time: 15:53*/ namespace app\controller\common; use app\controller\Em…...

app-2 App 应用抓包之 Postern+Charles

一、前言 本篇是基于 Postern Charles 方式对安卓应用数据包进行抓取。可以抓取到市面上大多数的app数据包。 二、环境准备 postern&#xff1a;postern下载地址 charles&#xff1a;Charles 4.5.6 中文版(便携免安装).rar 提取码&#xff1a;6d8f 三、配置及抓包测试 3.…...

cnocr配置及训练测试

cnocr配置及训练测试 1&#xff0c;相关链接2&#xff0c;已有模型调用测试&#xff08;1&#xff09;下载相关模型&#xff08;2&#xff09;Cnstd文本检测模型&#xff08;3&#xff09;模型调用解析脚本 3&#xff0c;自定义数据集训练测试&#xff08;1&#xff09;标签转换…...

【优选算法 前缀和】前缀和算法模板详解:一维前缀 & 与二维前缀和

一维前缀和 题目解析 算法原理 解法一&#xff1a;暴力解法 简单模拟&#xff0c;读完题意有 q 次询问&#xff0c;给哪两个数&#xff0c;就求哪段区间的和并且返回&#xff0c;这样的做法&#xff0c;时间复杂度为O(N*q)&#xff0c;这个时间复杂度会超时&#xf…...

【记录】用JUnit 4的@Test注解时报错java.lang.NullPointerException的原因与解决方法

项目场景&#xff1a; 在练习黑马点评的逻辑过期解决缓存击穿时&#xff0c;编写了一个预热缓存数据的单元测试 SpringBootTest public class HmDianPingApplicationTests {Resourceprivate ShopServiceImpl shopService;Testpublic void testSaveShop() throws InterruptedE…...

Transformer入门(6)Transformer编码器的前馈网络、加法和归一化模块

文章目录 7.前馈网络8.加法和归一化组件9.组合所有编码器组件构成完整编码器 7.前馈网络 编码器块中的前馈网络子层如下图所示&#xff1a; 图1.32 – 编码器块 前馈网络由两个带有ReLU激活函数的全连接层组成。全连接层&#xff08;Fully Connected Layer&#xff09;有时也…...

(七)腾讯cloudstudio+Stable-Diffusion-webui AI绘画教程-安装Stable-Diffusion-WebUI

一、说明 本文选择安装stable-diffusion-webui最新版本 cloud studio 免费版最大的问题是空间不足&#xff0c;我晚上上传时超过了硬盘大小&#xff0c;直接不能启动&#xff0c;没办法&#xff0c;删除&#xff0c;又建了一个工作空间 二、安装 1、打开终端 2、配置Git代理…...

算法基础Day7(动态规划)

文章目录 1.题目2.题目解答1.第N个泰波那契数题目及题目解析动态规划算法学习1.状态表示2.状态转移方程3.初始化4.填表顺序5.空间优化 代码提交空间优化 2.三步问题题目及题目解析算法学习代码提交 1.题目 1137. 第 N 个泰波那契数 - 力扣&#xff08;LeetCode&#xff09;面试…...

代理IP地址和端口是什么?怎么进行设置?

保护个人隐私、突破地域限制、提升网络安全性是我们不断追求的目标。IP地址与端口一种实现这些目标的重要工具。但是&#xff0c;你可能对它是什么&#xff0c;以及如何设置感到困惑。别担心&#xff0c;本文将为你揭开这些神秘的面纱&#xff0c;让你轻松掌握这项技能。 1.IP…...

一文详解TCP协议 [图文并茂, 明了易懂]

欢迎来到啊妮莫的学习小屋! 目录 什么是TCP协议 TCP协议特点✨ TCP报文格式 三次握手和四次挥手✨ 可靠性 效率性 基于字节流✨ 基于TCP的应用层协议 什么是TCP协议 TCP(传输控制协议, Transmission Control Protocol) 是一种面向连接的, 可靠的, 基于字节流的传输层通…...

js后端开发之Next.js、Nuxt.js 与 Express.js

后端js之Next.js、Nuxt.js 与 Express.js 在现代 Web 开发中&#xff0c;JavaScript 已经成为前后端通用的编程语言&#xff0c;而选择合适的后端框架则是构建高效、可扩展应用程序的关键。本文将带你深入了解三个流行的 JavaScript 后端框架&#xff1a;Next.js、Nuxt.js 和 …...

人工智能概要

目录 前言1.什么是人工智能&#xff08;Artificial Intelligence, AI&#xff09;2.人工智能发展的三次浪潮2.1 人工智能发展的第一次浪潮2.2 人工智能发展的第二次浪潮2.3 人工智能发展的第三次浪潮 3.人工智能发展的必备三要素3.1 数据3.2 算法&#xff08;algorithm&#xf…...

spring boot 3集成swagger

Spring Boot 3 集成 Swagger 的过程与之前版本相比有一些变化&#xff0c;主要是因为 springfox 库已经停止更新&#xff0c;并且不再支持新的 Spring Boot 版本。因此&#xff0c;对于 Spring Boot 3 来说&#xff0c;推荐使用 springdoc-openapi 作为集成 Swagger 的解决方案…...

【PlantUML系列】状态图(六)

一、状态图的组成部分 状态&#xff1a;对象在其生命周期内可能处于的条件或情形&#xff0c;使用 state "State Name" as Statename 表示。初始状态&#xff1a;表示对象生命周期的开始&#xff0c;使用 [*] 表示。最终状态&#xff1a;表示对象生命周期的结束&…...

前端缓存页面处理方法

当前一个前端应用新发布时&#xff0c;重新编译后&#xff0c;原来引用的资源文件名都会有变化。如果这个应用的页面在前端浏览器中有缓存&#xff0c;则会导致加载资源失败。怎样去除这种缓存&#xff0c;同时也能尽可能的保证前端访问的性能 ChatGPT said: ChatGPT 这是一个经…...

每日一题 284. 窥视迭代器

284. 窥视迭代器 想要提前知道下一个内容&#xff0c;就需要缓存 class PeekingIterator : public Iterator { public:PeekingIterator(const vector<int>& nums) : Iterator(nums) {// Initialize any member here.// **DO NOT** save a copy of nums and manipula…...

Cesium-(Primitive)-(BoxGeometry)

含实现代码 GISer世界 效果: 以下是 BoxGeometry 类的构造函数属性,以表格形式展示: 属性名类型默认值描述minimumCartesian3盒子的最小 x, y, 和 z 坐标。maximumCartesian3盒子的最大 x, y, 和 z 坐标。vertexFormatVertexFormatVertexFormat.DEFAULT要计算的顶点属性。以下…...