stm32_DMA
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) |
| +-----------------------------------------+ |
+-------------------------------------------------------+
详细说明:
-
传输方向: 定义数据从哪里来到哪里去。
-
传输模式:
- 普通模式: 传输指定数量的数据后,DMA控制器自动停止并可以触发传输完成中断。
- 循环模式: 传输指定数量的数据后,DMA控制器会自动重新加载数据量寄存器并继续传输,形成一个循环。常用于连续采集传感器数据或连续发送波形数据。
-
地址增量模式:
- 源地址增量: 如果源地址是内存地址,通常会配置为增量,以读取连续的数据块。如果源地址是外设寄存器,通常配置为不增量,因为外设数据寄存器地址通常是固定的。
- 目标地址增量: 同理。
-
数据宽度: 指定每次DMA传输的数据单位是8位、16位还是32位。这必须与源和目标数据的实际宽度相匹配。
-
中断类型:
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
+-------------------+
| 结束 |
+-------------------+
流程详解:
- 初始化DMA外设: 使能DMA控制器自身的时钟。
- 配置DMA通道/流: 这是DMA的核心配置步骤,包括选择合适的通道、传输方向、数据宽度、地址增量方式、传输模式以及中断等。
- 关联DMA与外设: 这一步至关重要。DMA传输通常是由外设发起的。例如,ADC完成一次转换后会向DMA控制器发出一个请求信号,告知DMA可以传输数据了。因此,需要在外设的配置中使能DMA请求。
- 设置DMA传输参数: 明确本次DMA传输的数据源地址、目标地址以及总共要传输的数据量。
- 启动DMA传输: 启用DMA通道/流,使其处于准备就绪状态。一旦外设发出DMA请求,DMA传输就会自动开始。
- (可选)等待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配置步骤
- 新建项目并选择芯片。
- 配置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
(或其他合适的值)
- Rank1:
- Mode:
- 配置DMA2:
- 在ADC1设置的DMA Settings中,点击
Add
。 - 选择
DMA2 Stream0
(通常ADC1连接到此Stream)。 - Mode:
Normal
或Circular
(这里我们用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
(传输完成中断)。
- 在ADC1设置的DMA Settings中,点击
- 时钟配置: 保持默认即可,确保ADC和DMA的时钟都被使能。
- 生成代码。
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
数组中。
- 这是启动DMA传输的关键函数。它告诉HAL库:
HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
:- 这是一个用户自定义的ADC转换完成回调函数。当DMA传输了指定数量的数据(
ADC_BUFFER_SIZE
)后,DMA会触发一个传输完成中断,进而由HAL库的DMA中断处理函数 (HAL_DMA_IRQHandler
) 调用这个回调函数。 - 在这个回调函数中,我们将
adc_dma_transfer_complete
标志设置为1,通知主循环有新的数据可用。
- 这是一个用户自定义的ADC转换完成回调函数。当DMA传输了指定数量的数据(
DMA2_Stream0_IRQHandler(void)
:- 这是DMA2 Stream0的中断服务函数。
- 它由Cortex-M处理器的中断向量表在DMA中断发生时调用。
HAL_DMA_IRQHandler(&hdma_adc1);
:这个函数是HAL库提供的通用DMA中断处理程序。它会检查DMA的状态标志,并根据配置调用相应的回调函数(例如本例中的HAL_ADC_ConvCpltCallback
)。
相关文章:
stm32_DMA
DMA 1. 概念与基本原理 DMA,全称Direct Memory Access,即直接存储器访问。它是微控制器(MCU)、嵌入式处理器中的一个独立硬件模块,用于在无需CPU干预的情况下,在不同内存区域(包括外设寄存器和…...

物联网数据归档之数据存储方案选择分析
在上一篇文章中《物联网数据归档方案选择分析》中凯哥分析了归档设计的两种方案,并对两种方案进行了对比。这篇文章咱们就来分析分析,归档后数据应该存储在哪里?及存储方案对比。 这里就选择常用的mysql及taos数据库来存储归档后的数据吧。 你在处理设备归档表存储方案时对…...
【自动驾驶避障开发】如何让障碍物在 RViz 中‘显形’?呈现感知数据转 Polygon 全流程
【自动驾驶避障开发】如何让障碍物在 RViz 中"显形"?呈现感知数据转 Polygon 全流程 自动驾驶系统中的障碍物可视化是开发调试过程中至关重要的一环。本文将详细介绍如何将自动驾驶感知模块检测到的障碍物数据转换为RViz可显示的Polygon(多边形)形式,实现障碍物…...

【C语言】C语言经典小游戏:贪吃蛇(上)
文章目录 一、游戏背景及其功能二、Win32 API介绍1、Win32 API2、控制台程序3、定位坐标(COORD)4、获得句柄(GetStdHandle)5、获得光标属性(GetConsoleCursorInfo)1)描述光标属性(CO…...
usbutils工具的使用帮助
作为嵌入式系统开发中的常用工具,usbutils 是一套用于管理和调试USB设备的Linux命令行工具集。以下是其核心功能和使用方法的详细说明: 1. 工具组成 核心命令: lsusb:列出所有连接的USB设备及详细信息(默认安装&#…...

vue2中使用jspdf插件实现页面自定义块pdf下载
pdf下载 实现pdf下载的环境安装jspdf插件在项目中使用 实现pdf下载的环境 项目需求案例背景,点击【pdf下载】按钮,弹出pdf下载弹窗,显示需要下载四个模块的下载进度,下载完成后,关闭弹窗即可! 项目使用的是…...

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

基于cornerstone3D的dicom影像浏览器 第二十九章 自定义菜单组件
文章目录 前言一、程序结构1. 菜单数据结构2. XMenu.vue3. XSubMenu.vue4. XSubMenuSlot.vue5. XMenuItem.vue 二、调用流程总结 前言 菜单用于组织程序功能,为用户提供导航。是用户与程序交互非常重要的接口。 开源组件库像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 论文链接:https://arxiv.org/pdf/2412.13443 GitHub链接:https://github.com/cidautai/DarkIR 创新点 DarkIR提出了…...
【学习笔记】单例类模板
【学习笔记】单例类模板 一、单例类模板 以下为一个通用的单例模式框架,这种设计允许其他类通过继承Singleton模板类来轻松实现单例模式,而无需为每个类重复编写单例实现代码。 // 命名空间(Namespace) 和 模板(Tem…...
字符串加密(华为OD)
题目描述 给你一串未加密的字符串str,通过对字符串的每一个字母进行改变来实现加密,加密方式是在每一个字母str[i]偏移特定数组元素a[i]的量,数组a前三位已经赋值:a[0]=1,a[1]=2,a[2]=4。当i>=3时,数组元素a[i]=a[i-1]+a[i-2]+a[i-3]。例如:原文 abcde 加密后 bdgkr,…...

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

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}},其中 Y i Y_i Yi表示结果变量, D i D_i Di表示因变量, X i X_i Xi表…...

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

【C】-递归
1、递归概念 递归(Recursion)是编程中一种重要的解决问题的方法,其核心思想是函数通过调用自身来解决规模更小的子问题,直到达到最小的、可以直接解决的基准情形(Base Case)。 核心:自己调用…...

飞马LiDAR500雷达数据预处理
0 引言 在使用飞马D2000无人机搭载LiDAR500进行作业完成后,需要对数据进行预处理,方便给内业人员开展点云分类等工作。在开始操作前,先了解一下使用的软硬件及整体流程。 0.1 外业测量设备 无人机:飞马D2000S激光模块ÿ…...
Kerberos面试内容整理-在 Linux/Windows 中的 Kerberos 实践
Windows 实践: 在Windows环境中,Kerberos 几乎是无形融合的。用户使用域账号登录计算机时,实际上就完成了Kerberos的AS认证并获取TGT;此后的资源访问(如共享文件夹、打印机、数据库等)都会自动使用Kerberos进行验证,而无需用户干预。Windows通过LSASS进程维护和缓存用户…...
在 Allegro PCB Editor 中取消(解除或删除)已创建的 **Module** 的操作指南
在 Allegro PCB Editor 中取消(解除或删除)已创建的 Module 有两种主要场景,操作也不同: 📌 场景一:仅想解除元件与 Module 的关联(保留元件位置和布线,但可独立编辑) …...
基于springboot的校园社团信息系统的设计与实现
其他源码获取可以看首页:代码老y 个人简介:专注于毕业设计项目定制开发:springbootvue系统,Java微信小程序,javaSSM系统等技术开发,并提供远程调试部署、代码讲解、文档指导、ppt制作等技术指导。源码获取&…...
nodejs里面的http模块介绍和使用
Node.js的http模块是构建在libuv库之上,以JavaScript接口形式暴露出来的核心模块之一,它允许开发者轻松地创建和管理HTTP服务器及客户端,进而实现网络应用的快速开发。此模块的设计理念围绕着事件驱动和非阻塞I/O模型,这些特性使N…...
mamba架构和transformer区别
Mamba 架构和 Transformer 架构存在多方面的区别,具体如下: 计算复杂度1 Transformer:自注意力机制的计算量会随着上下文长度的增加呈平方级增长,例如上下文增加 32 倍时,计算量可能增长 1000 倍,在处理长序…...

嵌入式鸿蒙开发环境搭建操作方法与实现
Linux环境搭建镜像下载链接: 链接:https://pan.baidu.com/s/1F2f8ED5V1KwLjyYzKVx2yQ 提取码:Leun vscode和Linux系统连接的详细过程1.下载Visual Studio Code...
在 Spring Boot 中使用 WebFilter:实现请求拦截、日志记录、跨域处理等通用逻辑!
💡 前言 在开发 Web 应用时,我们经常需要对所有请求进行统一处理,例如: 记录请求日志实现跨域(CORS)接口权限控制请求参数预处理防止 XSS 攻击 这些功能如果都写在每个 Controller 或 Service 里&#x…...
CSS预处理器:Sass与Less的语法和特性(含实际案例)
Sass(SCSS语法示例) 1. 变量:统一管理颜色 // 定义变量 $primary-color: #1a237e; $success-color: #4caf50; $font-size-base: 16px;// 实际应用 body {color: $primary-color;font-size: $font-size-base; }.button {background: $succes…...

QT常用控件(1)
控件是构成QT的基础元素,例如Qwidget也是一个控件,提供了一个‘空’的矩形,我们可以往里面添加内容和处理用户输入,例如:按钮(QpushButton),基础显示控件(Lableÿ…...

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

【计算机网络】非阻塞IO——select实现多路转接
🔥个人主页🔥:孤寂大仙V 🌈收录专栏🌈:计算机网络 🌹往期回顾🌹:【计算机网络】NAT、代理服务器、内网穿透、内网打洞、局域网中交换机 🔖流水不争࿰…...
Figma 中构建 Master Control Panel (MCP) 的完整设计方案
以下是在 Figma 中构建 Master Control Panel (MCP) 的完整设计方案,专为设计系统管理而优化: 一、MCP 核心功能架构 #mermaid-svg-iZAnYxyYU4BtpeaE {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#merma…...
什么是权威解析服务器?权威解析服务器哪些作用?
域名系统(DNS)是互联网的核心基础设施之一,它将易于记忆的域名转换为计算机能够识别的IP地址。DNS服务器在这一过程中扮演着至关重要的角色,它们可以分为以下几种类型: 根DNS服务器 根DNS服务器位于DNS层级结构的最顶端…...

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