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

STM32中DMA(直接存储器访问)详解

一、DMA基本概念

在嵌入式系统中,数据传输是非常核心的操作。传统的数据传输方式需要CPU全程参与,即CPU需要不断地读取外设数据并写入存储器,或者从存储器读取数据并发送到外设。这种方式会占用大量的CPU资源,尤其是在进行大量数据传输时,会导致CPU无法处理其他重要任务,严重影响系统的实时性和效率。

而DMA(Direct Memory Access,直接存储器访问)技术的出现,很好地解决了这一问题。DMA可以让外设和存储器之间或者存储器和存储器之间直接进行数据传输,整个过程不需要CPU的干预,从而大大减轻了CPU的负担,提高了系统的数据传输效率和整体性能。

在STM32微控制器中,DMA是一个非常重要的外设,它支持多种外设与存储器之间的数据传输,能够满足各种复杂应用场景的需求。

二、STM32 DMA结构

STM32系列微控制器根据型号的不同,其DMA控制器的数量和通道数量也有所差异。以STM32F1系列为例,通常包含2个DMA控制器(DMA1和DMA2),每个DMA控制器有7个通道,每个通道可以对应不同的外设请求。而像STM32F4系列,则可能有更多的DMA通道和更强大的功能。

(一)DMA通道

每个DMA控制器的通道都可以独立地配置为处理不同外设的DMA请求。不同的STM32型号,其通道与外设的对应关系可能会有所不同,具体可以参考相应型号的参考手册。例如,在STM32F103中,DMA1的通道1可以处理TIM1_CH1的DMA请求,通道2可以处理TIM2_CH3的DMA请求等。

每个通道都有一个仲裁器来管理DMA请求的优先级。优先级分为软件优先级和硬件优先级。软件优先级可以通过配置寄存器设置为4个等级:非常高、高、中、低;硬件优先级则是根据通道号来确定,通道号越小,优先级越高。当多个DMA请求同时到达时,仲裁器会根据优先级的高低来决定处理顺序。

(二)DMA控制器主要特性

STM32的DMA控制器具有以下主要特性:

  1. 支持外设到存储器、存储器到外设以及存储器到存储器的数据传输。
  2. 每个通道都可以独立配置传输方向、数据宽度、传输数量等参数。
  3. 支持循环传输模式,适用于需要连续传输数据的场景,如ADC连续采样。
  4. 可以通过中断或DMA传输完成标志来判断传输是否完成。
  5. 具有可编程的数据传输宽度(8位、16位、32位),方便与不同数据宽度的外设和存储器进行匹配。
    在这里插入图片描述

三、STM32 DMA工作原理

DMA的数据传输过程主要包括以下几个步骤:

(一)初始化

在进行DMA传输之前,需要对DMA通道进行初始化。具体包括:

  1. 选择DMA通道,根据需要传输数据的外设选择对应的通道。
  2. 设置外设地址和存储器地址,外设地址通常是外设的数据寄存器地址,存储器地址可以是SRAM或Flash中的地址。
  3. 配置传输方向,是从外设到存储器、存储器到外设还是存储器到存储器。
  4. 设置数据传输宽度,包括外设数据宽度和存储器数据宽度,可以是8位、16位或32位。
  5. 配置传输数量,即需要传输的数据个数。
  6. 设置通道优先级。
  7. 配置传输模式,如正常模式或循环模式。
  8. 使能DMA通道。

(二)传输过程

当DMA通道初始化完成后,当外设产生DMA请求(如USART接收到数据、ADC完成转换等)时,DMA控制器会响应请求,开始进行数据传输。在传输过程中,DMA控制器会自动从外设或存储器读取数据,并写入到对应的存储器或外设中,整个过程不需要CPU参与,CPU可以继续执行其他任务。

传输过程中,DMA控制器会不断更新外设地址、存储器地址和传输数量。当传输数量减到0时,传输完成。如果是正常模式,DMA通道会自动关闭;如果是循环模式,传输数量会自动恢复为初始值,继续进行传输。

(三)传输完成处理

当DMA传输完成后,可以通过两种方式通知CPU:

  1. 中断方式:如果使能了DMA传输完成中断,当传输完成时,会产生中断请求,CPU可以在中断服务程序中进行后续处理,如处理传输的数据、重新配置DMA等。
  2. 查询方式:CPU可以通过查询DMA通道的传输完成标志来判断传输是否完成,当标志置位时,表示传输完成。

四、STM32 DMA配置步骤(以STM32CubeMX为例)

使用STM32CubeMX可以方便地配置DMA,具体步骤如下:

(一)选择芯片型号

打开STM32CubeMX,选择对应的STM32芯片型号。

(二)配置外设

根据需要使用的外设,在Pinout & Configuration选项卡中配置外设的相关参数,如USART的波特率、ADC的采样精度等。

(三)开启DMA功能

在配置外设时,勾选对应的DMA请求,如USART的RX DMA和TX DMA。

(四)配置DMA参数

在DMA Settings选项卡中,配置DMA的参数:

  1. Channel:选择对应的DMA通道。
  2. Direction:选择数据传输方向,如Peripheral to Memory、Memory to Peripheral等。
  3. Priority:设置通道优先级。
  4. Mode:选择传输模式,Normal或Circular。
  5. Data Width:设置外设和存储器的数据宽度。
  6. Increment Address:设置是否递增外设和存储器地址,通常存储器地址需要递增,外设地址根据外设情况设置。

(五)生成代码

配置完成后,点击Generate Code按钮,生成初始化代码。生成的代码会包含DMA的初始化函数,在main函数中可以直接调用相关函数进行数据传输。

五、STM32 DMA实例代码(基于HAL库)

(一)USART+DMA发送数据

以下是使用USART1通过DMA发送数据的示例代码:

#include "stm32f1xx_hal.h"UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_tx;void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_DMA_Init(void);int main(void)
{HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_USART1_UART_Init();MX_DMA_Init();uint8_t send_buf[] = "Hello, DMA!";uint16_t send_len = sizeof(send_buf) / sizeof(send_buf[0]) - 1;// 使用DMA发送数据HAL_UART_Transmit_DMA(&huart1, send_buf, send_len);while (1){// 主循环可以执行其他任务}
}static void MX_USART1_UART_Init(void)
{huart1.Instance = USART1;huart1.Init.BaudRate = 115200;huart1.Init.WordLength = UART_WORDLENGTH_8B;huart1.Init.StopBits = UART_STOPBITS_1;huart1.Init.Parity = UART_PARITY_NONE;huart1.Init.Mode = UART_MODE_TX_RX;huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;huart1.Init.OverSampling = UART_OVERSAMPLING_16;if (HAL_UART_Init(&huart1) != HAL_OK){Error_Handler();}
}static void MX_DMA_Init(void)
{__HAL_RCC_DMA1_CLK_ENABLE();hdma_usart1_tx.Instance = DMA1_Channel4;hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;hdma_usart1_tx.Init.Mode = DMA_NORMAL;hdma_usart1_tx.Init.Priority = DMA_PRIORITY_MEDIUM;if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK){Error_Handler();}__HAL_LINKDMA(&huart1, hdmatx, hdma_usart1_tx);HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 0, 0);HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);
}void DMA1_Channel4_IRQHandler(void)
{HAL_DMA_IRQHandler(&hdma_usart1_tx);
}void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{if (huart == &huart1){// 发送完成回调处理}
}void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK){Error_Handler();}
}static void MX_GPIO_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};/* GPIO Ports Clock Enable */__HAL_RCC_GPIOA_CLK_ENABLE();/*Configure GPIO pin : PA0 */GPIO_InitStruct.Pin = GPIO_PIN_0;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_NOPULL;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}void Error_Handler(void)
{/* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}
}#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif

在上述代码中,首先初始化了USART1和DMA1的通道4(USART1_TX对应的DMA通道),然后调用HAL_UART_Transmit_DMA函数发送数据。当发送完成后,会触发DMA中断,进入中断服务程序,最终调用HAL_UART_TxCpltCallback回调函数进行处理。

(二)ADC+DMA采集数据

以下是使用ADC1通过DMA采集数据的示例代码:

#include "stm32f1xx_hal.h"ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;uint16_t adc_buf[100]; // 存储ADC采集的数据void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);
static void MX_DMA_Init(void);int main(void)
{HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_ADC1_Init();MX_DMA_Init();// 启动ADC DMA采集HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buf, 100);while (1){// 主循环可以处理采集到的数据}
}static void MX_ADC1_Init(void)
{ADC_ChannelConfTypeDef sConfig = {0};hadc1.Instance = ADC1;hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;hadc1.Init.ContinuousConvMode = ENABLE;hadc1.Init.DiscontinuousConvMode = DISABLE;hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;hadc1.Init.NbrOfConversion = 1;if (HAL_ADC_Init(&hadc1) != HAL_OK){Error_Handler();}sConfig.Channel = ADC_CHANNEL_0;sConfig.Rank = ADC_REGULAR_RANK_1;sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES_5;if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK){Error_Handler();}
}static void MX_DMA_Init(void)
{__HAL_RCC_DMA1_CLK_ENABLE();hdma_adc1.Instance = DMA1_Channel1;hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;hdma_adc1.Init.Mode = DMA_CIRCULAR;hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;if (HAL_DMA_Init(&hdma_adc1) != HAL_OK){Error_Handler();}__HAL_LINKDMA(&hadc1, hdma, hdma_adc1);HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}void DMA1_Channel1_IRQHandler(void)
{HAL_DMA_IRQHandler(&hdma_adc1);
}void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{if (hadc == &hadc1){// ADC采集完成回调处理}
}void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK){Error_Handler();}PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK){Error_Handler();}
}static void MX_GPIO_Init(void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};__HAL_RCC_GPIOA_CLK_ENABLE();GPIO_InitStruct.Pin = GPIO_PIN_1;GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}void Error_Handler(void)
{__disable_irq();while (1){}
}#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif

在该代码中,初始化了ADC1和DMA1的通道1(ADC1对应的DMA通道),设置ADC为连续转换模式,通过DMA将采集到的数据存储到adc_buf数组中。当采集到100个数据后,会触发DMA中断,调用HAL_ADC_ConvCpltCallback回调函数。

六、STM32 DMA高级特性

(一)循环模式

循环模式(Circular Mode)是DMA的一个重要特性,当启用循环模式后,当DMA传输完成时,传输数量会自动恢复为初始值,继续进行数据传输,不需要重新配置DMA。这种模式适用于需要连续传输数据的场景,如ADC连续采样、定时器的PWM输出等。

在配置DMA时,将Mode设置为DMA_CIRCULAR即可启用循环模式。

(二)存储器到存储器传输

STM32的DMA支持存储器到存储器的数据传输,即可以将数据从一个存储器地址传输到另一个存储器地址,而不需要经过外设。这种传输方式可以用于快速复制大量数据,提高数据复制的效率。

配置存储器到存储器传输时,需要将传输方向设置为Memory to Memory,同时需要注意以下几点:

  1. 只能使用DMA1的通道进行存储器到存储器传输。
  2. 传输的源地址和目的地址都必须是存储器地址。
  3. 可以通过软件触发传输,调用HAL_DMA_Start函数即可开始传输。

以下是存储器到存储器DMA传输的示例代码:

#include "stm32f1xx_hal.h"DMA_HandleTypeDef hdma_memtomem_dma1_channel1;uint8_t src_buf[] = "Hello, Memory to Memory DMA!";
uint8_t dest_buf[30];void SystemClock_Config(void);
static void MX_DMA_Init(void);int main(void)
{HAL_Init();SystemClock_Config();MX_DMA_Init();// 启动存储器到存储器DMA传输HAL_DMA_Start(&hdma_memtomem_dma1_channel1, (uint32_t)src_buf, (uint32_t)dest_buf, sizeof(src_buf));while (1){}
}static void MX_DMA_Init(void)
{__HAL_RCC_DMA1_CLK_ENABLE();hdma_memtomem_dma1_channel1.Instance = DMA1_Channel1;hdma_memtomem_dma1_channel1.Init.Direction = DMA_MEMORY_TO_MEMORY;hdma_memtomem_dma1_channel1.Init.PeriphInc = DMA_PINC_ENABLE;hdma_memtomem_dma1_channel1.Init.MemInc = DMA_MINC_ENABLE;hdma_memtomem_dma1_channel1.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;hdma_memtomem_dma1_channel1.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;hdma_memtomem_dma1_channel1.Init.Mode = DMA_NORMAL;hdma_memtomem_dma1_channel1.Init.Priority = DMA_PRIORITY_HIGH;if (HAL_DMA_Init(&hdma_memtomem_dma1_channel1) != HAL_OK){Error_Handler();}HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}void DMA1_Channel1_IRQHandler(void)
{HAL_DMA_IRQHandler(&hdma_memtomem_dma1_channel1);
}void HAL_DMA_TransferCpltCallback(DMA_HandleTypeDef *hdma)
{if (hdma == &hdma_memtomem_dma1_channel1){// 存储器到存储器传输完成回调处理}
}void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK){Error_Handler();}
}void Error_Handler(void)
{__disable_irq();while (1){}
}#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif

(三)DMA中断

STM32的DMA支持多种中断,如传输完成中断、半传输中断、传输错误中断等。通过使能这些中断,可以及时了解DMA的传输状态,进行相应的处理。

在配置DMA时,通过HAL_DMA_Init函数中的DMA_InitTypeDef结构体的相关成员可以配置中断使能。在中断服务程序中,调用HAL_DMA_IRQHandler函数,该函数会根据中断类型调用相应的回调函数,如HAL_DMA_TransferCpltCallback(传输完成回调)、HAL_DMA_TransferHalfCpltCallback(半传输完成回调)、HAL_DMA_ErrorCallback(传输错误回调)等。

七、STM32 DMA常见问题及解决方法

(一)DMA传输数据错误

  1. 原因:
    • 外设地址或存储器地址设置错误。
    • 数据传输宽度设置不正确,与外设或存储器的数据宽度不匹配。
    • DMA通道选择错误,与外设不对应。
    • 传输数量设置错误。
  2. 解决方法:
    • 检查外设地址和存储器地址是否正确,确保地址有效。
    • 确认数据传输宽度是否与外设和存储器的数据宽度一致。
    • 查阅芯片参考手册,确认所选DMA通道是否与外设对应。
    • 检查传输数量是否正确,避免超出缓冲区大小。

(二)DMA传输不触发

  1. 原因:
    • 外设未产生DMA请求,如USART未接收到数据、ADC未启动等。
    • DMA通道未使能。
    • 中断优先级设置不当,导致DMA中断被屏蔽。
    • 外设的DMA功能未开启。
  2. 解决方法:
    • 检查外设是否正常工作,是否能产生DMA请求。
    • 确认DMA通道已使能,调用HAL_DMA_Start函数后,DMA通道会自动使能。
    • 检查中断优先级设置,确保DMA中断优先级高于其他无关中断。
    • 检查外设的配置,确保已开启DMA功能,如USART的DMA使能位是否置位。

(三)DMA传输完成后无法再次传输

  1. 原因:
    • 使用了正常模式,传输完成后DMA通道自动关闭。
    • 未重新设置传输数量。
  2. 解决方法:
    • 如果需要连续传输,将DMA模式设置为循环模式。
    • 在正常模式下,传输完成后,需要重新配置传输数量,并调用HAL_DMA_Start函数重新启动DMA传输。
http://www.dtcms.com/a/273071.html

相关文章:

  • [Meetily后端框架] AI摘要结构化 | `SummaryResponse`模型 | Pydantic库 | vs marshmallow库
  • Spring Boot 与 Docker 的完美结合:容器化你的应用
  • 时序数据库InfluxDB
  • Flink 2.0 DataStream算子全景
  • MBSE工具+架构建模:从效率提升到质量赋能
  • 智能Agent场景实战指南 Day 9:市场营销Agent构建策略
  • 粗排样本架构升级:融合LTR特征提升模型性能的技术实践
  • 车载诊断架构 --- DTC深层次参数信息(e.g. ComfirmDTCLimit unconfirmDTCLimit)
  • 第10章 语句 笔记
  • 轻松使用格式工厂中的分离器功能来分离视频和音频文件
  • 噪音到10µVRMS 以下的DC-DC:TPS62913
  • 实现一个点击输入框可以弹出的数字软键盘控件 qt 5.12
  • Java 单例类详解:从基础到高级,掌握线程安全与高效设计
  • wpf使用webview2显示网页内容(最低兼容.net framework4.5.2)
  • C Primer Plus 第6版 编程练习——第8章
  • python语言编程文件删除后的恢复方法
  • ARM环境上 openEuler扩展根盘并扩展到根分区中
  • 小架构step系列10:日志热更新
  • HTTP核心基础详解(附实战要点)
  • Jaspersoft Studio-6.4.0 TextField内容展示不全
  • [实战]调频(FM)和调幅(AM)信号生成(完整C语言实现)
  • 【养老机器人】核心技术
  • 6. Z 字形变换
  • 决策树与随机森林Python实践
  • 如何测家里是否漏电,4种方法
  • 实时连接,精准监控:风丘科技数据远程显示方案提升试验车队管理效率
  • 倍增法和ST算法 个人学习笔记代码
  • esp32在vscode中仿真调试
  • QT6 源(159)模型视图架构里的文件系统模型 QFileSystemModel 篇二:本类的源代码带注释
  • Building Bridges(搭建桥梁)