stm32_DMA

news2025/6/7 15:40:25

DMA


1. 概念与基本原理

DMA,全称Direct Memory Access,即直接存储器访问。它是微控制器(MCU)、嵌入式处理器中的一个独立硬件模块,用于在无需CPU干预的情况下,在不同内存区域(包括外设寄存器和SRAM、Flash等)之间进行数据传输。

基本原理: 在没有DMA的情况下,CPU负责所有的数据传输。例如,从ADC读取数据,CPU需要逐个读取ADC寄存器并将数据拷贝到RAM;向UART发送数据,CPU需要逐个将数据从RAM拷贝到UART发送寄存器。这种方式会占用大量CPU时间,尤其是在高速数据传输或大量数据传输的场景下,会严重影响CPU处理其他任务的效率。

DMA模块的出现,就是为了解决这个问题。当DMA被配置并启动后,CPU只需告诉DMA控制器需要传输的数据源、数据目标、数据量和传输方向,DMA控制器就会接管数据传输任务。CPU可以自由地执行其他指令,只有当DMA传输完成(或发生错误)时,DMA控制器才会通过中断通知CPU。

核心思想: 解放CPU,提高系统效率和吞吐量。


2. DMA内部结构(可图示)

想象一个DMA控制器的内部结构,它通常包含以下几个关键部分:

+-----------------------------------------------------+
|                  DMA控制器                         |
+-----------------------------------------------------+
|                                                     |
|  1. DMA通道/(DMA Channel/Stream)                 |  <-- 每个通道处理一个独立的传输任务
|     +-----------------------------------------+     |
|     |                                         |     |
|     |  源地址寄存器 (Source Address Register) |     |  <-- 数据源地址 (如:外设数据寄存器或RAM地址)
|     |  目标地址寄存器 (Destination Address Register)|     |  <-- 数据目标地址 (如:RAM地址或外设数据寄存器)
|     |  数据量寄存器 (Number of Data Register) |     |  <-- 待传输的数据量
|     |  控制寄存器 (Control Register)          |     |  <-- 配置传输模式、数据宽度、增量模式等
|     +-----------------------------------------+     |
|                                                     |
|  2. 仲裁器 (Arbiter)                                |  <-- 解决多个DMA通道同时请求总线访问时的冲突
|                                                     |
|  3. 总线接口 (Bus Interface)                        |  <-- 连接到系统总线,进行实际的数据读写
|                                                     |
|  4. 中断逻辑 (Interrupt Logic)                      |  <-- 传输完成/错误时生成中断请求
|                                                     |
+-----------------------------------------------------+
        ^                ^
        |                |
        |                |
+-------+-----+  +-------+-----+
|   外设总线    |  |    内存总线   |
| (Peripherals) |  |   (SRAM/Flash)|
+---------------+  +---------------+

关键部件解释:

  • DMA通道/流: 大多数微控制器会提供多个独立的DMA通道或流(Stream),每个通道可以独立地配置和启动一个DMA传输任务。例如,一个通道可以用于ADC到RAM的传输,另一个通道用于RAM到UART的传输。

  • 源地址寄存器 (Source Address Register - SAR/PAR): 存储数据传输的起始源地址。这可以是内存地址,也可以是外设的数据寄存器地址。

  • 目标地址寄存器 (Destination Address Register - DAR/MAR): 存储数据传输的起始目标地址。同样可以是内存地址或外设的数据寄存器地址。

  • 数据量寄存器 (Number of Data Register - NDTR/CR): 存储本次DMA传输需要传输的数据单位数量。每传输一个数据单位,此寄存器会自动减1,直到减为0时传输完成。

  • 控制寄存器 (Control Register - CR/CCR):

    这是DMA配置的核心。它包含了以下重要配置:

    • 传输模式: 单次传输、循环传输。
    • 数据宽度: 每次传输的数据单位大小(字节、半字、字)。
    • 地址增量模式: 源地址和目标地址在每次传输后是否自动增加(用于连续数据块传输)。
    • 传输方向: 从外设到内存,从内存到外设,或从内存到内存。
    • 优先级: DMA通道之间的优先级。
    • 中断使能: 配置是否在传输完成、一半传输完成、传输错误时触发中断。
  • 仲裁器: 当有多个DMA通道或CPU同时需要访问总线时,仲裁器负责决定哪个请求获得总线访问权,以避免冲突。

  • 总线接口: DMA控制器通过这个接口与系统总线(如AHB/APB总线)连接,从而可以读写内存和外设寄存器。

  • 中断逻辑: DMA传输完成、一半传输完成、传输错误等事件发生时,DMA控制器会生成中断请求,通知CPU进行后续处理。


3. DMA工作模式与传输类型(可图示)

DMA的工作模式和传输类型决定了DMA如何进行数据传输:

+-------------------------------------------------------+
|                       DMA工作模式/传输类型             |
+-------------------------------------------------------+
|                                                       |
|  1. 传输方向 (Transfer Direction)                     |
|     +-----------------------------------------+       |
|     |                                         |       |
|     |  外设到内存 (Peripheral to Memory)      |       |
|     |    - e.g., ADC数据采集到RAM            |       |
|     |                                         |       |
|     |  内存到外设 (Memory to Peripheral)      |       |
|     |    - e.g., RAM数据发送到UART           |       |
|     |                                         |       |
|     |  内存到内存 (Memory to Memory)          |       |
|     |    - e.g., 快速拷贝内存块             |       |
|     +-----------------------------------------+       |
|                                                       |
|  2. 传输模式 (Transfer Mode)                          |
|     +-----------------------------------------+       |
|     |                                         |       |
|     |  普通模式 (Normal Mode)                 |       |
|     |    - 完成一次指定数量的传输后停止      |       |
|     |                                         |       |
|     |  循环模式 (Circular Mode)               |       |
|     |    - 传输完成后自动重置数据量寄存器    |       |
|     |    - 持续循环传输,无需CPU干预           |       |
|     |    - 适用于连续数据流采集或输出         |       |
|     +-----------------------------------------+       |
|                                                       |
|  3. 地址增量模式 (Address Increment Mode)             |
|     +-----------------------------------------+       |
|     |                                         |       |
|     |  源地址增量 (Peripheral Increment / Memory Increment) |
|     |    - 传输后源地址是否递增              |       |
|     |                                         |       |
|     |  目标地址增量 (Memory Increment / Peripheral Increment) |
|     |    - 传输后目标地址是否递增            |       |
|     |                                         |       |
|     |  不增量 (No Increment)                  |       |
|     |    - 地址保持不变,用于读写固定寄存器   |       |
|     +-----------------------------------------+       |
|                                                       |
|  4. 数据宽度 (Data Width)                             |
|     +-----------------------------------------+       |
|     |                                         |       |
|     |  字节 (Byte - 8-bit)                   |       |
|     |  半字 (Half-Word - 16-bit)              |       |
|     |(Word - 32-bit)                     |       |
|     +-----------------------------------------+       |
|                                                       |
|  5. 中断类型 (Interrupt Type)                         |
|     +-----------------------------------------+       |
|     |                                         |       |
|     |  传输完成中断 (Transfer Complete - TC)  |       |
|     |  半传输完成中断 (Half Transfer - HT)    |       |
|     |  传输错误中断 (Transfer Error - TE)     |       |
|     |  直接模式错误中断 (Direct Mode Error - DME) |
|     +-----------------------------------------+       |
+-------------------------------------------------------+

详细说明:

  1. 传输方向: 定义数据从哪里来到哪里去。

  2. 传输模式:

    • 普通模式: 传输指定数量的数据后,DMA控制器自动停止并可以触发传输完成中断。
    • 循环模式: 传输指定数量的数据后,DMA控制器会自动重新加载数据量寄存器并继续传输,形成一个循环。常用于连续采集传感器数据或连续发送波形数据。
  3. 地址增量模式:

    • 源地址增量: 如果源地址是内存地址,通常会配置为增量,以读取连续的数据块。如果源地址是外设寄存器,通常配置为不增量,因为外设数据寄存器地址通常是固定的。
    • 目标地址增量: 同理。
  4. 数据宽度: 指定每次DMA传输的数据单位是8位、16位还是32位。这必须与源和目标数据的实际宽度相匹配。

  5. 中断类型:

    DMA控制器可以根据不同的事件触发中断,方便CPU进行处理,例如:

    • 传输完成中断 (TC): 当所有数据传输完毕时触发。
    • 半传输完成中断 (HT): 在循环模式下,当一半数据传输完毕时触发,常用于“乒乓操作”,即在一个缓冲器被DMA填充时,CPU可以处理另一个缓冲器的数据。
    • 传输错误中断 (TE): 在传输过程中发生错误时触发。

4. DMA操作流程(可图示)

DMA的操作通常遵循以下流程:

+-------------------+
|     开始          |
+-------------------+
        |
        V
+-------------------+
|  1. 初始化DMA外设 |
|    - 使能DMA时钟  |
+-------------------+
        |
        V
+-------------------+
|  2. 配置DMA通道/|
|    - 选择DMA通道/流号 |
|    - 配置传输方向   |
|    - 配置数据宽度   |
|    - 配置地址增量模式 |
|    - 配置传输模式(普通/循环)|
|    - 配置优先级     |
|    - 配置中断使能(可选)|
+-------------------+
        |
        V
+-------------------+
|  3. 关联DMA与外设   |
|    - 例如:配置ADC的DMA请求使能位 |
|    - 例如:配置UART的DMA发送/接收使能位 |
+-------------------+
        |
        V
+-------------------+
|  4. 设置DMA传输参数 |
|    - 设置源地址     |
|    - 设置目标地址   |
|    - 设置传输数据量 |
+-------------------+
        |
        V
+-------------------+
|  5. 启动DMA传输     |
|    - 使能DMA通道/|
+-------------------+
        |
        V
+-------------------+
|  6. (可选)等待DMA传输完成/处理中断 |
|    - 轮询DMA状态标志 |
|    - 或,等待DMA中断并执行中断服务函数 |
+-------------------+
        |
        V
+-------------------+
|      结束         |
+-------------------+

流程详解:

  1. 初始化DMA外设: 使能DMA控制器自身的时钟。
  2. 配置DMA通道/流: 这是DMA的核心配置步骤,包括选择合适的通道、传输方向、数据宽度、地址增量方式、传输模式以及中断等。
  3. 关联DMA与外设: 这一步至关重要。DMA传输通常是由外设发起的。例如,ADC完成一次转换后会向DMA控制器发出一个请求信号,告知DMA可以传输数据了。因此,需要在外设的配置中使能DMA请求。
  4. 设置DMA传输参数: 明确本次DMA传输的数据源地址、目标地址以及总共要传输的数据量。
  5. 启动DMA传输: 启用DMA通道/流,使其处于准备就绪状态。一旦外设发出DMA请求,DMA传输就会自动开始。
  6. (可选)等待DMA传输完成/处理中断:
    • 轮询: CPU周期性地检查DMA状态寄存器的标志位,判断传输是否完成。这种方式会占用CPU时间。
    • 中断: 更高效的方式。当DMA传输完成、一半完成或发生错误时,DMA控制器会触发中断,CPU跳转到相应的中断服务函数进行处理,然后可以继续执行其他任务。

5. 完整流程代码案例 (以STM32为例)

这里以STM32微控制器为例,使用HAL库来实现一个简单的DMA功能:通过DMA将ADC采集到的数据自动传输到SRAM中的一个数组中。

硬件连接:

  • ADC: 使用STM32F407的ADC1,连接到PA1引脚(模拟输入)。
  • DMA: ADC1通常连接到DMA2的Stream0。

开发环境: Keil MDK, STM32CubeMX (用于生成初始化代码)

5.1 STM32CubeMX配置步骤
  1. 新建项目并选择芯片。
  2. 配置ADC1:
    • Mode: Independent Mode
    • Scan Conversion Mode: Disable (这里只采集一个通道)
    • Continuous Conversion Mode: Enable (连续采集)
    • Discontinuous Conversion Mode: Disable
    • DMA Continuous Request: Enable (关键!使能ADC的DMA请求)
    • Channel Settings:
      • Rank1: PA1 (Channel 1)
      • Sampling Time: 3 Cycles (或其他合适的值)
  3. 配置DMA2:
    • 在ADC1设置的DMA Settings中,点击Add
    • 选择 DMA2 Stream0 (通常ADC1连接到此Stream)。
    • Mode: NormalCircular (这里我们用Circular模式,实现连续数据采集)。
    • Direction: Peripheral to Memory (ADC是外设,RAM是内存)。
    • Data Width: Half Word (ADC是12位,所以选择16位半字传输)。
    • Increment Address: Memory (目标地址是RAM数组,需要递增)。
    • Increment Address: Peripheral (源地址是ADC数据寄存器,通常不递增,但在这里为了配置一致性,选择No Increment更合理,因为ADC数据寄存器地址固定)。
    • Priority: Low (或Default)。
    • Enable Interrupts: 勾选 DMA2 Stream0 global interrupt (传输完成中断)。
  4. 时钟配置: 保持默认即可,确保ADC和DMA的时钟都被使能。
  5. 生成代码。
5.2 Keil MDK代码实现

在STM32CubeMX生成的基础代码上,我们主要修改 main.c 文件和实现DMA中断服务函数。

/* USER CODE BEGIN Includes */
#include "main.h"
#include <stdio.h> // 用于串口打印,可选
/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc1; // ADC句柄
DMA_HandleTypeDef hdma_adc1; // DMA句柄

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
#define ADC_BUFFER_SIZE 10 // 定义ADC数据缓冲区大小
uint16_t adc_values[ADC_BUFFER_SIZE]; // 存储ADC采集数据的数组
volatile uint8_t adc_dma_transfer_complete = 0; // DMA传输完成标志
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_ADC1_Init(void);

/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/

/* USER CODE END PFP */

/* USER CODE BEGIN 0 */
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init(); // DMA初始化必须在ADC初始化之前,因为ADC初始化会用到DMA句柄
  MX_ADC1_Init();
  /* USER CODE BEGIN 2 */

  // 启动ADC的DMA传输
  // HAL_ADC_Start_DMA(hadc, pData, Length)
  // hadc: ADC句柄
  // pData: 目标数据缓冲区地址
  // Length: 传输数据量
  if (HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_values, ADC_BUFFER_SIZE) != HAL_OK)
  {
    /* Start Error */
    Error_Handler();
  }

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    if (adc_dma_transfer_complete)
    {
      // DMA传输完成,可以处理采集到的数据了
      // 注意:在循环模式下,这个标志会持续被设置,因为它每次完成一个完整的缓冲区传输就会触发
      // 这里只是简单打印,实际应用中可以进行数据处理、滤波等

      printf("ADC Values: ");
      for (int i = 0; i < ADC_BUFFER_SIZE; i++)
      {
        printf("%d ", adc_values[i]);
      }
      printf("\r\n");

      adc_dma_transfer_complete = 0; // 清除标志
    }

    HAL_Delay(500); // 主循环可以做其他事情,等待DMA中断
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  /* ... (CubeMX生成的时钟配置代码) ... */
}

/**
  * @brief ADC1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_ADC1_Init(void)
{
  /* ... (CubeMX生成的ADC1初始化代码) ... */
  // 注意,CubeMX会在这个函数中关联DMA句柄:
  // hadc1.DMA_Handle = &hdma_adc1;
  // hdma_adc1.Parent = &hadc1;
}

/**
  * @brief  DMA Initialization Function
  * @param  None
  * @retval None
  */
static void MX_DMA_Init(void)
{
  /* USER CODE BEGIN DMA_Init_First */
  /* USER CODE END DMA_Init_First */

  /* DMA controller clock enable */
  __HAL_RCC_DMA2_CLK_ENABLE(); // 这一行是CubeMX生成的,使能DMA2时钟

  /* DMA interrupt init */
  /* DMA2_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0); // 设置DMA中断优先级
  HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn); // 使能DMA中断

  /* USER CODE BEGIN DMA_Init_Last */

  /* USER CODE END DMA_Init_Last */
}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  /* ... (CubeMX生成的GPIO初始化代码) ... */
}

/* USER CODE BEGIN 4 */

// DMA传输完成中断回调函数
// 这个函数会在HAL_DMA_IRQHandler中被调用
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hadc); // 避免编译器警告

  /* NOTE : This function Should not be modified, when the callback is needed,
            the HAL_ADC_ConvCpltCallback could be implemented in the user file.
   */
  adc_dma_transfer_complete = 1; // 设置DMA传输完成标志
}

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  while(1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  * where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{ 
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

中断服务函数 (在 stm32f4xx_it.c 中)

CubeMX会自动生成DMA中断服务函数的框架,你需要在 DMA2_Stream0_IRQHandler 中调用HAL库的DMA处理函数。

/* USER CODE BEGIN Includes */
/* USER CODE END Includes */

/* External variables --------------------------------------------------------*/
extern DMA_HandleTypeDef hdma_adc1; // 声明在main.c中定义的DMA句柄
/* USER CODE BEGIN EV */
/* USER CODE END EV */

/******************************************************************************/
/* Cortex-M4 Processor Interruption and Exception Handlers          */ 
/******************************************************************************/
/**
  * @brief This function handles DMA2 Stream0 global interrupt.
  */
void DMA2_Stream0_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Stream0_IRQn 0 */

  /* USER CODE END DMA2_Stream0_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_adc1); // 调用HAL库的DMA中断处理函数
  /* USER CODE BEGIN DMA2_Stream0_IRQn 1 */

  /* USER CODE END DMA2_Stream0_IRQn 1 */
}

/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
5.3 代码解释
  • ADC_HandleTypeDef hadc1; DMA_HandleTypeDef hdma_adc1;: 定义了ADC和DMA的HAL库句柄,用于操作对应的外设。
  • uint16_t adc_values[ADC_BUFFER_SIZE];: 定义了一个数组,用于存储DMA从ADC传输过来的12位(实际存储在16位uint16_t中)数据。
  • volatile uint8_t adc_dma_transfer_complete = 0;: 一个标志位,用于在中断中通知主循环DMA传输完成。volatile关键字很重要,因为它告诉编译器这个变量的值可能在程序执行流程之外(例如中断)被改变。
  • MX_DMA_Init();: STM32CubeMX生成的DMA初始化函数,负责使能DMA时钟,配置DMA通道/流的各项参数(方向、数据宽度、地址增量、模式、优先级),并使能中断。
  • MX_ADC1_Init();: STM32CubeMX生成的ADC初始化函数,其中会配置ADC的通道、采样时间,并且最重要的是,会关联DMA句柄到ADC句柄 (hadc1.DMA_Handle = &hdma_adc1;)。
  • if (HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_values, ADC_BUFFER_SIZE) != HAL_OK):
    • 这是启动DMA传输的关键函数。它告诉HAL库:
      • &hadc1: 使用哪个ADC(以及关联的DMA通道)。
      • (uint32_t*)adc_values: DMA的目标地址,即数据将传输到adc_values数组。注意这里需要强制类型转换为uint32_t*,因为HAL库的设计中,pData参数是uint32_t*,但实际传输的是uint16_t
      • ADC_BUFFER_SIZE: 传输的数据量。
    • 一旦此函数被调用,并且ADC配置为连续转换模式和DMA请求使能,每当ADC完成一次转换,它就会自动触发DMA将数据从ADC数据寄存器 (ADC1->DR) 传输到adc_values数组中。
  • HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc):
    • 这是一个用户自定义的ADC转换完成回调函数。当DMA传输了指定数量的数据(ADC_BUFFER_SIZE)后,DMA会触发一个传输完成中断,进而由HAL库的DMA中断处理函数 (HAL_DMA_IRQHandler) 调用这个回调函数。
    • 在这个回调函数中,我们将adc_dma_transfer_complete标志设置为1,通知主循环有新的数据可用。
  • DMA2_Stream0_IRQHandler(void):
    • 这是DMA2 Stream0的中断服务函数。
    • 它由Cortex-M处理器的中断向量表在DMA中断发生时调用。
    • HAL_DMA_IRQHandler(&hdma_adc1);:这个函数是HAL库提供的通用DMA中断处理程序。它会检查DMA的状态标志,并根据配置调用相应的回调函数(例如本例中的HAL_ADC_ConvCpltCallback)。

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

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

相关文章

物联网数据归档之数据存储方案选择分析

在上一篇文章中《物联网数据归档方案选择分析》中凯哥分析了归档设计的两种方案,并对两种方案进行了对比。这篇文章咱们就来分析分析,归档后数据应该存储在哪里?及存储方案对比。 这里就选择常用的mysql及taos数据库来存储归档后的数据吧。 你在处理设备归档表存储方案时对…

【C语言】C语言经典小游戏:贪吃蛇(上)

文章目录 一、游戏背景及其功能二、Win32 API介绍1、Win32 API2、控制台程序3、定位坐标&#xff08;COORD&#xff09;4、获得句柄&#xff08;GetStdHandle&#xff09;5、获得光标属性&#xff08;GetConsoleCursorInfo&#xff09;1&#xff09;描述光标属性&#xff08;CO…

vue2中使用jspdf插件实现页面自定义块pdf下载

pdf下载 实现pdf下载的环境安装jspdf插件在项目中使用 实现pdf下载的环境 项目需求案例背景&#xff0c;点击【pdf下载】按钮&#xff0c;弹出pdf下载弹窗&#xff0c;显示需要下载四个模块的下载进度&#xff0c;下载完成后&#xff0c;关闭弹窗即可&#xff01; 项目使用的是…

如何防止服务器被用于僵尸网络(Botnet)攻击 ?

防止服务器被用于僵尸网络&#xff08;Botnet&#xff09;攻击是关键的网络安全措施之一。僵尸网络是黑客利用大量被感染的计算机、服务器或物联网设备来发起攻击的网络。以下是关于如何防止服务器被用于僵尸网络攻击的技术文章&#xff1a; 防止服务器被用于僵尸网络&#xff…

基于cornerstone3D的dicom影像浏览器 第二十九章 自定义菜单组件

文章目录 前言一、程序结构1. 菜单数据结构2. XMenu.vue3. XSubMenu.vue4. XSubMenuSlot.vue5. XMenuItem.vue 二、调用流程总结 前言 菜单用于组织程序功能&#xff0c;为用户提供导航。是用户与程序交互非常重要的接口。 开源组件库像Element Plus和Ant Design中都提供了功能…

【Block总结】DBlock,结合膨胀空间注意模块(Di-SpAM)和频域模块Gated-FFN|即插即用|CVPR2025

论文信息 标题: DarkIR: Robust Low-Light Image Restoration 作者: Daniel Feijoo, Juan C. Benito, Alvaro Garcia, Marcos Conde 论文链接&#xff1a;https://arxiv.org/pdf/2412.13443 GitHub链接&#xff1a;https://github.com/cidautai/DarkIR 创新点 DarkIR提出了…

口罩佩戴检测算法AI智能分析网关V4工厂/工业等多场景守护公共卫生安全

一、引言​ 在公共卫生安全日益受到重视的当下&#xff0c;口罩佩戴成为预防病毒传播、保障人员健康的重要措施。为了高效、精准地实现对人员口罩佩戴情况的监测&#xff0c;AI智能分析网关V4口罩检测方案应运而生。该方案依托先进的人工智能技术与强大的硬件性能&#xff0c;…

Double/Debiased Machine Learning

独立同步分布的观测数据 { W i ( Y i , D i , X i ) ∣ i ∈ { 1 , . . . , n } } \{W_i(Y_i,D_i,X_i)| i\in \{1,...,n\}\} {Wi​(Yi​,Di​,Xi​)∣i∈{1,...,n}}&#xff0c;其中 Y i Y_i Yi​表示结果变量&#xff0c; D i D_i Di​表示因变量&#xff0c; X i X_i Xi​表…

HarmonyOS Next 弹窗系列教程(4)

HarmonyOS Next 弹窗系列教程&#xff08;4&#xff09; 介绍 本章主要介绍和用户点击关联更加密切的菜单控制&#xff08;Menu&#xff09; 和 气泡提示&#xff08;Popup&#xff09; 它们出现显示弹窗出现的位置都是在用户点击屏幕的位置相关 菜单控制&#xff08;Menu&…

【C】-递归

1、递归概念 递归&#xff08;Recursion&#xff09;是编程中一种重要的解决问题的方法&#xff0c;其核心思想是函数通过调用自身来解决规模更小的子问题&#xff0c;直到达到最小的、可以直接解决的基准情形&#xff08;Base Case&#xff09;。 核心&#xff1a;自己调用…

飞马LiDAR500雷达数据预处理

0 引言 在使用飞马D2000无人机搭载LiDAR500进行作业完成后&#xff0c;需要对数据进行预处理&#xff0c;方便给内业人员开展点云分类等工作。在开始操作前&#xff0c;先了解一下使用的软硬件及整体流程。 0.1 外业测量设备 无人机&#xff1a;飞马D2000S激光模块&#xff…

嵌入式鸿蒙开发环境搭建操作方法与实现

Linux环境搭建镜像下载链接: 链接:https://pan.baidu.com/s/1F2f8ED5V1KwLjyYzKVx2yQ 提取码:Leun vscode和Linux系统连接的详细过程1.下载Visual Studio Code

QT常用控件(1)

控件是构成QT的基础元素&#xff0c;例如Qwidget也是一个控件&#xff0c;提供了一个‘空’的矩形&#xff0c;我们可以往里面添加内容和处理用户输入&#xff0c;例如&#xff1a;按钮&#xff08;QpushButton&#xff09;&#xff0c;基础显示控件&#xff08;Lable&#xff…

明基编程显示器终于有优惠了,程序员快来,错过等一年!

最近618的活动已经陆续开始了&#xff0c;好多人说这是买数码产品的好时候&#xff0c;作为一名资深程序员&#xff0c;我做了不少功课&#xff0c;决定给自己升级办公设备&#xff0c;入手明基 RD 系列的显示器&#xff0c;这是市面上首家专注于我们程序员痛点和需求的产品&am…

【计算机网络】非阻塞IO——select实现多路转接

&#x1f525;个人主页&#x1f525;&#xff1a;孤寂大仙V &#x1f308;收录专栏&#x1f308;&#xff1a;计算机网络 &#x1f339;往期回顾&#x1f339;&#xff1a;【计算机网络】NAT、代理服务器、内网穿透、内网打洞、局域网中交换机 &#x1f516;流水不争&#xff0…

LeetCode--23.合并k个升序链表

解题思路&#xff1a; 1.获取信息&#xff1a; 给出了多个升序链表&#xff0c;要求合并成一个升序链表&#xff0c;返回首元结点 2.分析题目&#xff1a; 外面在21题的时候&#xff0c;讲了怎样合并两个升序链表为一个升序链表&#xff0c;不了解的&#xff0c;建议去看一下21…

【推荐算法】NeuralCF:深度学习重构协同过滤的革命性突破

NeuralCF&#xff1a;深度学习重构协同过滤的革命性突破 一、算法背景知识&#xff1a;协同过滤的演进与局限1.1 协同过滤的发展历程1.2 传统矩阵分解的缺陷 二、算法理论/结构&#xff1a;NeuralCF架构设计2.1 基础NeuralCF结构2.2 双塔模型进阶结构2.3 模型实现流程对比 三、…

负载均衡相关基本概念

负载均衡在系统架构设计中至关重要&#xff0c;其核心目标是合理分配负载&#xff0c;提升系统整体性能和可靠性。本文简要介绍了负载均衡的基本概念&#xff0c;包括四层和七层负载均衡、负载均衡的使用场景和实现方式、负载均衡的常用算法以及一些配置相关知识。 1、负载均衡…

集成电路设计:从概念到实现的完整解析优雅草卓伊凡

集成电路设计&#xff1a;从概念到实现的完整解析优雅草卓伊凡 一、集成电路设计&#xff1a;芯片制造的”灵魂蓝图” 1.1 什么是集成电路设计&#xff1f; 集成电路&#xff08;IC&#xff09;设计是指通过电子设计自动化&#xff08;EDA&#xff09;工具&#xff0c;将数百…

动态规划之网格图模型(二)

文章目录 动态规划之网格图模型&#xff08;二&#xff09;LeetCode 931. 下降路径最小和思路Golang 代码 LeetCode 2684. 矩阵中移动的最大次数思路Golang 代码 LeetCode 2304. 网格中的最小路径代价思路Golang 代码 LeetCode 1289. 下降路径最小和 II思路Golang 代码 LeetCod…