13、MDK分散加载方式管理多块内存
MDK分散加载:
默认情况下是通过MDK的option选项设置Flash和RAM大小,这种情况下所有的管理工作都是编译来处理的,
MDK自动生成的分散加载文件:H7_ProjectTest.sct
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************LR_IROM1 0x08000000 0x00020000 { ; load region size_regionER_IROM1 0x08000000 0x00020000 { ; load address = execution address*.o (RESET, +First)*(InRoot$$Sections).ANY (+RO).ANY (+XO)}RW_IRAM1 0x20000000 0x00020000 { ; RW data.ANY (+RW +ZI)}RW_IRAM2 0x24000000 0x00080000 {.ANY (+RW +ZI)}
}
看着这文件前需要先看下各RAM的地址和大小,工程使用的STM32H750VBT6
未全部写出,只写出了主要使用的部分
重新编写的MDK分散加载文件,后面示例使用的也是这个文件:stm32h750vb_user.sct
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; SRAM分散加载文件
; *************************************************************LR_IROM1 0x08000000 0x00200000 { ; load region size_region;加载域
ER_IROM1 0x08000000 0x00200000 { ; load address = execution address;执行域对应H7的Flash地址和大小
*.o (RESET, +First);将启动文件 startup_stm32h743xx.s 有个段名为 RESET 的代码段(主要存储了中断向量表),将其存放在Flash
*(InRoot$$Sections);将将 MDK 的一些库文件全部放在根域,比如__main.o, _scatter*.o, _dc*.o
.ANY (+RO);将目标文件中所有具有 RO 只读属性的数据放在这里,即 ER_IROM1
}
; RW data - 128KB DTCM
RW_IRAM1 0x20000000 0x00020000 {.ANY (+RW +ZI);将目标文件中所有具有 RW 和 ZI 数据放在这里
}
; RW data - 512KB AXI SRAM
RW_IRAM2 0x24000000 0x00080000 {*(.RAM_D1);给这个域专门配了一个名字 .RAM_D1,这样就可以通过__attribute__((section("name")))将其分配到这个RAM域
}
; RW data - 128KB SRAM1(0x30000000) + 128KB SRAM2(0x3002 0000) + 32KB SRAM3(0x30040000)
RW_IRAM3 0x30000000 0x00048000 {*(.RAM_D2)
}
; RW data - 64KB SRAM4(0x38000000)
RW_IRAM4 0x38000000 0x00010000 {*(.RAM_D3)
}
}
注意:
__HAL_RCC_D2SRAM1_CLK_ENABLE();
__HAL_RCC_D2SRAM2_CLK_ENABLE();
__HAL_RCC_D2SRAM2_CLK_ENABLE();
使用分散加载文件:
在定义变量时:
__attribute__((section(".RAM_D1"))) uint32_t AXISRAMBuf[10] = {0,0,0,0,0,0,0,0,0,0};//建议程序初始化后重新对变量进行初始化,否则该值有可能不确定
分散加载示例
main.c
/* USER CODE BEGIN Header */
/********************************************************************************* @file : main.c* @brief : Main program body******************************************************************************* @attention** <h2><center>© Copyright (c) 2023 STMicroelectronics.* All rights reserved.</center></h2>** This software component is licensed by ST under BSD 3-Clause license,* the "License"; You may not use this file except in compliance with the* License. You may obtain a copy of the License at:* opensource.org/licenses/BSD-3-Clause********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes *//* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MPU_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */__attribute__((section(".RAM_D1"))) uint32_t AXISRAMBuf[10] = {0,0,0,0,0,0,0,0,0,0};
__attribute__((section(".RAM_D1"))) uint16_t AXISRAMCount = 0;__attribute__((section(".RAM_D2"))) uint32_t D2SRAMBuf[10] = {0,0,0,0,0,0,0,0,0,0};
__attribute__((section(".RAM_D2"))) uint16_t D2SRAMCount = 0;__attribute__((section(".RAM_D3"))) uint32_t D3SRAMBuf[10] = {0,0,0,0,0,0,0,0,0,0};
__attribute__((section(".RAM_D3"))) uint16_t D3SRAMCount = 0;/* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MPU Configuration--------------------------------------------------------*/MPU_Config();/* Enable I-Cache---------------------------------------------------------*/SCB_EnableICache();/* Enable D-Cache---------------------------------------------------------*/SCB_EnableDCache();/* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init */
__HAL_RCC_D2SRAM1_CLK_ENABLE();
__HAL_RCC_D2SRAM2_CLK_ENABLE();
__HAL_RCC_D2SRAM2_CLK_ENABLE();for(uint8_t i = 0;i < 10;i++)
{AXISRAMBuf[i] = 0;D2SRAMBuf[i] = 0;D3SRAMBuf[i] = 0;
}AXISRAMCount = 0;
D2SRAMCount = 0;
D3SRAMCount = 0;/* 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_USART1_UART_Init();/* USER CODE BEGIN 2 *//* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */printf("system\n");HAL_Delay(2000);AXISRAMBuf[0]++;AXISRAMBuf[5]++;AXISRAMBuf[9]++;D2SRAMBuf[0]++;D2SRAMBuf[5]++;D2SRAMBuf[9]++;D3SRAMBuf[0]++;D3SRAMBuf[5]++;D3SRAMBuf[9]++;AXISRAMCount += 5;D2SRAMCount += 2;D3SRAMCount += 1;printf("AXISRAMBuf[0]=%d AXISRAMBuf[5]=%d AXISRAMBuf[9]=%d AXISRAMCount=%d\n",\AXISRAMBuf[0],AXISRAMBuf[5],AXISRAMBuf[9],AXISRAMCount);HAL_Delay(100);printf("D2SRAMBuf[0]=%d D2SRAMBuf[5]=%d D2SRAMBuf[9]=%d D2SRAMCount=%d\n",\D2SRAMBuf[0],D2SRAMBuf[5],D2SRAMBuf[9],D2SRAMCount);HAL_Delay(100);printf("D3SRAMBuf[0]=%d D3SRAMBuf[5]=%d D3SRAMBuf[9]=%d D3SRAMCount=%d\n",\D3SRAMBuf[0],D3SRAMBuf[5],D3SRAMBuf[9],D3SRAMCount);HAL_Delay(100);}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Supply configuration update enable*/HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);/** Configure the main internal regulator output voltage*/__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;RCC_OscInitStruct.HSIState = RCC_HSI_DIV1;RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;RCC_OscInitStruct.PLL.PLLM = 4;RCC_OscInitStruct.PLL.PLLN = 60;RCC_OscInitStruct.PLL.PLLP = 2;RCC_OscInitStruct.PLL.PLLQ = 2;RCC_OscInitStruct.PLL.PLLR = 2;RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3;RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;RCC_OscInitStruct.PLL.PLLFRACN = 0;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_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK){Error_Handler();}
}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//* MPU Configuration */void MPU_Config(void)
{MPU_Region_InitTypeDef MPU_InitStruct = {0};/* Disables the MPU */HAL_MPU_Disable();/** Initializes and configures the Region and the memory to be protected*/MPU_InitStruct.Enable = MPU_REGION_ENABLE;MPU_InitStruct.Number = MPU_REGION_NUMBER0;MPU_InitStruct.BaseAddress = 0x24000000;MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;MPU_InitStruct.SubRegionDisable = 0x0;MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;HAL_MPU_ConfigRegion(&MPU_InitStruct);/** Initializes and configures the Region and the memory to be protected*/MPU_InitStruct.Number = MPU_REGION_NUMBER1;MPU_InitStruct.BaseAddress = 0x60000000;MPU_InitStruct.Size = MPU_REGION_SIZE_256MB;MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;HAL_MPU_ConfigRegion(&MPU_InitStruct);/** Initializes and configures the Region and the memory to be protected*/MPU_InitStruct.Number = MPU_REGION_NUMBER2;MPU_InitStruct.BaseAddress = 0x30000000;MPU_InitStruct.Size = MPU_REGION_SIZE_128KB;MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;HAL_MPU_ConfigRegion(&MPU_InitStruct);/** Initializes and configures the Region and the memory to be protected*/MPU_InitStruct.Number = MPU_REGION_NUMBER3;MPU_InitStruct.BaseAddress = 0x30020000;HAL_MPU_ConfigRegion(&MPU_InitStruct);/** Initializes and configures the Region and the memory to be protected*/MPU_InitStruct.Number = MPU_REGION_NUMBER4;MPU_InitStruct.BaseAddress = 0x30040000;MPU_InitStruct.Size = MPU_REGION_SIZE_32KB;HAL_MPU_ConfigRegion(&MPU_InitStruct);/** Initializes and configures the Region and the memory to be protected*/MPU_InitStruct.Number = MPU_REGION_NUMBER5;MPU_InitStruct.BaseAddress = 0x38000000;MPU_InitStruct.Size = MPU_REGION_SIZE_64KB;HAL_MPU_ConfigRegion(&MPU_InitStruct);/* Enables the MPU */HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);}/*** @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 */__disable_irq();printf("Error_Handler\n");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,ex: 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****/
注意,一定要在使用前进行单独初始化,否则会出现不定的初始值:
/* USER CODE BEGIN Init */
__HAL_RCC_D2SRAM1_CLK_ENABLE();
__HAL_RCC_D2SRAM2_CLK_ENABLE();
__HAL_RCC_D2SRAM2_CLK_ENABLE();// for(uint8_t i = 0;i < 10;i++)//使用前不对其重新初始化
// {// AXISRAMBuf[i] = 0;
// D2SRAMBuf[i] = 0;
// D3SRAMBuf[i] = 0;
// }AXISRAMCount = 0;
D2SRAMCount = 0;
D3SRAMCount = 0;/* USER CODE END Init */
示例中的MPU配置为:
void MPU_Config(void)
void MPU_Config(void)
{MPU_Region_InitTypeDef MPU_InitStruct = {0};/* Disables the MPU */HAL_MPU_Disable();/** Initializes and configures the Region and the memory to be protected*/MPU_InitStruct.Enable = MPU_REGION_ENABLE;MPU_InitStruct.Number = MPU_REGION_NUMBER0;MPU_InitStruct.BaseAddress = 0x24000000;MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;MPU_InitStruct.SubRegionDisable = 0x0;MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;HAL_MPU_ConfigRegion(&MPU_InitStruct);/** Initializes and configures the Region and the memory to be protected*/MPU_InitStruct.Number = MPU_REGION_NUMBER1;MPU_InitStruct.BaseAddress = 0x60000000;MPU_InitStruct.Size = MPU_REGION_SIZE_256MB;MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;HAL_MPU_ConfigRegion(&MPU_InitStruct);/** Initializes and configures the Region and the memory to be protected*/MPU_InitStruct.Number = MPU_REGION_NUMBER2;MPU_InitStruct.BaseAddress = 0x30000000;MPU_InitStruct.Size = MPU_REGION_SIZE_128KB;MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;HAL_MPU_ConfigRegion(&MPU_InitStruct);/** Initializes and configures the Region and the memory to be protected*/MPU_InitStruct.Number = MPU_REGION_NUMBER3;MPU_InitStruct.BaseAddress = 0x30020000;HAL_MPU_ConfigRegion(&MPU_InitStruct);/** Initializes and configures the Region and the memory to be protected*/MPU_InitStruct.Number = MPU_REGION_NUMBER4;MPU_InitStruct.BaseAddress = 0x30040000;MPU_InitStruct.Size = MPU_REGION_SIZE_32KB;HAL_MPU_ConfigRegion(&MPU_InitStruct);/** Initializes and configures the Region and the memory to be protected*/MPU_InitStruct.Number = MPU_REGION_NUMBER5;MPU_InitStruct.BaseAddress = 0x38000000;MPU_InitStruct.Size = MPU_REGION_SIZE_64KB;HAL_MPU_ConfigRegion(&MPU_InitStruct);/* Enables the MPU */HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);}
开启Cache指令
/* MPU Configuration--------------------------------------------------------*/MPU_Config();/* Enable I-Cache---------------------------------------------------------*/SCB_EnableICache();/* Enable D-Cache---------------------------------------------------------*/SCB_EnableDCache();
对TCM,SRAM进行内存动态分配实现
此处直接引用的安富莱例程:
rtx_lib.h
/*
*********************************************************************************************************
*
* 模块名称 : 动态内存管理
* 文件名称 : rtx_memory.h
* 版 本 : V1.0
* 说 明 : 将RTX5的动态内存管理整理出来, 可以管理多个内存块
*
* 修改记录 :
* 版本号 日期 作者 说明
* V1.0 2018-04-09 Eric2013 将RTX5的动态内存管理整理出来
*
* Copyright (C), 2018-2030, 安富莱电子 www.armfly.com
*
*********************************************************************************************************
*/
/** Copyright (c) 2013-2018 Arm Limited. All rights reserved.** SPDX-License-Identifier: Apache-2.0** Licensed under the Apache License, Version 2.0 (the License); you may* not use this file except in compliance with the License.* You may obtain a copy of the License at** www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an AS IS BASIS, WITHOUT* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.** -----------------------------------------------------------------------------** Project: CMSIS-RTOS RTX* Title: RTX Library definitions** -----------------------------------------------------------------------------*/#ifndef RTX_LIB_H_
#define RTX_LIB_H_#include <string.h>
#include <stdint.h>
#include "stm32h7xx.h"// Memory Pool Header structure
typedef struct {uint32_t size; // Memory Pool sizeuint32_t used; // Used Memory
} mem_head_t;// Memory Block Header structure
typedef struct mem_block_s {struct mem_block_s *next; // Next Memory Block in listuint32_t info; // Block Info or max used Memory (in last block)
} mem_block_t;// Memory Block Info: Length = <31:2>:'00', Type = <1:0>
#define MB_INFO_LEN_MASK 0xFFFFFFFCU // Length mask
#define MB_INFO_TYPE_MASK 0x00000003U // Type mask// Memory Head Pointer
__STATIC_INLINE mem_head_t *MemHeadPtr (void *mem) {//lint -e{9079} -e{9087} "conversion from pointer to void to pointer to other type" [MISRA Note 6]return ((mem_head_t *)mem);
}// Memory Block Pointer
__STATIC_INLINE mem_block_t *MemBlockPtr (void *mem, uint32_t offset) {uint32_t addr;mem_block_t *ptr;//lint --e{923} --e{9078} "cast between pointer and unsigned int" [MISRA Note 8]addr = (uint32_t)mem + offset;ptr = (mem_block_t *)addr;return ptr;
}// ==== Library functions ====// Memory Heap Library functions
extern uint32_t osRtxMemoryInit (void *mem, uint32_t size);
extern void *osRtxMemoryAlloc(void *mem, uint32_t size, uint32_t type);
extern uint32_t osRtxMemoryFree (void *mem, void *block);#endif // RTX_LIB_H_
rtx_memory.c
/*
*********************************************************************************************************
*
* 模块名称 : 动态内存管理
* 文件名称 : rtx_memory.c
* 版 本 : V1.0
* 说 明 : 将RTX5的动态内存管理整理出来, 可以管理多个内存块
*
* 修改记录 :
* 版本号 日期 作者 说明
* V1.0 2018-04-09 Eric2013 将RTX5的动态内存管理整理出来
*
* Copyright (C), 2018-2030, 安富莱电子 www.armfly.com
*
*********************************************************************************************************
*/
/** Copyright (c) 2013-2018 Arm Limited. All rights reserved.** SPDX-License-Identifier: Apache-2.0** Licensed under the Apache License, Version 2.0 (the License); you may* not use this file except in compliance with the License.* You may obtain a copy of the License at** www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an AS IS BASIS, WITHOUT* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.** -----------------------------------------------------------------------------** Project: CMSIS-RTOS RTX* Title: Memory functions** -----------------------------------------------------------------------------*/#include "rtx_lib.h"// ==== Library functions ====/// Initialize Memory Pool with variable block size.
/// \param[in] mem pointer to memory pool.
/// \param[in] size size of a memory pool in bytes.
/// \return 1 - success, 0 - failure.
__WEAK uint32_t osRtxMemoryInit (void *mem, uint32_t size) {mem_head_t *head;mem_block_t *ptr;// Check parameters//lint -e{923} "cast from pointer to unsigned int" [MISRA Note 7]if ((mem == NULL) || (((uint32_t)mem & 7U) != 0U) || ((size & 7U) != 0U) ||(size < (sizeof(mem_head_t) + (2U*sizeof(mem_block_t))))) {//EvrRtxMemoryInit(mem, size, 0U);//lint -e{904} "Return statement before end of function" [MISRA Note 1]return 0U;}// Initialize memory pool headerhead = MemHeadPtr(mem);head->size = size;head->used = sizeof(mem_head_t) + sizeof(mem_block_t);// Initialize first and last block headerptr = MemBlockPtr(mem, sizeof(mem_head_t));ptr->next = MemBlockPtr(mem, size - sizeof(mem_block_t));ptr->next->next = NULL;ptr->next->info = sizeof(mem_head_t) + sizeof(mem_block_t);ptr->info = 0U;//EvrRtxMemoryInit(mem, size, 1U);return 1U;
}/// Allocate a memory block from a Memory Pool.
/// \param[in] mem pointer to memory pool.
/// \param[in] size size of a memory block in bytes.
/// \param[in] type memory block type: 0 - generic, 1 - control block
/// \return allocated memory block or NULL in case of no memory is available.
__WEAK void *osRtxMemoryAlloc (void *mem, uint32_t size, uint32_t type) {mem_block_t *ptr;mem_block_t *p, *p_new;uint32_t block_size;uint32_t hole_size;// Check parametersif ((mem == NULL) || (size == 0U) || ((type & ~MB_INFO_TYPE_MASK) != 0U)) {//EvrRtxMemoryAlloc(mem, size, type, NULL);//lint -e{904} "Return statement before end of function" [MISRA Note 1]return NULL;}// Add block header to sizeblock_size = size + sizeof(mem_block_t);// Make sure that block is 8-byte alignedblock_size = (block_size + 7U) & ~((uint32_t)7U);// Search for hole big enoughp = MemBlockPtr(mem, sizeof(mem_head_t));for (;;) {//lint -e{923} -e{9078} "cast from pointer to unsigned int"hole_size = (uint32_t)p->next - (uint32_t)p;hole_size -= p->info & MB_INFO_LEN_MASK;if (hole_size >= block_size) {// Hole foundbreak;}p = p->next;if (p->next == NULL) {// Failed (end of list)//EvrRtxMemoryAlloc(mem, size, type, NULL);//lint -e{904} "Return statement before end of function" [MISRA Note 1]return NULL;}}// Update used memory(MemHeadPtr(mem))->used += block_size;// Update max used memoryp_new = MemBlockPtr(mem, (MemHeadPtr(mem))->size - sizeof(mem_block_t));if (p_new->info < (MemHeadPtr(mem))->used) {p_new->info = (MemHeadPtr(mem))->used;}// Allocate blockif (p->info == 0U) {// No block allocated, set info of first elementp->info = block_size | type;ptr = MemBlockPtr(p, sizeof(mem_block_t));} else {// Insert new element into the listp_new = MemBlockPtr(p, p->info & MB_INFO_LEN_MASK);p_new->next = p->next;p_new->info = block_size | type;p->next = p_new;ptr = MemBlockPtr(p_new, sizeof(mem_block_t));}//EvrRtxMemoryAlloc(mem, size, type, ptr);return ptr;
}/// Return an allocated memory block back to a Memory Pool.
/// \param[in] mem pointer to memory pool.
/// \param[in] block memory block to be returned to the memory pool.
/// \return 1 - success, 0 - failure.
__WEAK uint32_t osRtxMemoryFree (void *mem, void *block) {const mem_block_t *ptr;mem_block_t *p, *p_prev;// Check parametersif ((mem == NULL) || (block == NULL)) {//EvrRtxMemoryFree(mem, block, 0U);//lint -e{904} "Return statement before end of function" [MISRA Note 1]return 0U;}// Memory block headerptr = MemBlockPtr(block, 0U);ptr--;// Search for block headerp_prev = NULL;p = MemBlockPtr(mem, sizeof(mem_head_t));while (p != ptr) {p_prev = p;p = p->next;if (p == NULL) {// Not found//EvrRtxMemoryFree(mem, block, 0U);//lint -e{904} "Return statement before end of function" [MISRA Note 1]return 0U;}}// Update used memory(MemHeadPtr(mem))->used -= p->info & MB_INFO_LEN_MASK;// Free blockif (p_prev == NULL) {// Release first block, only set info to 0p->info = 0U;} else {// Discard block from chained listp_prev->next = p->next;}//EvrRtxMemoryFree(mem, block, 1U);return 1U;
}
main.c
/*
*********************************************************************************************************
*
* 模块名称 : 主程序模块
* 文件名称 : main.c
* 版 本 : V1.0
* 说 明 : TCM,SRAM等五块内存的动态内存分配实现。
* 实验目的:
* 1. 学习TCM,SRAM等五块内存的动态内存分配实现。
* 实验内容:
* 1. 启动自动重装软件定时器0,每100ms翻转一次LED2。
* 实验操作:
* 1. K1键按下,从DTCM依次申请280字节,64字节和6111字节。
* 2. K1键松开,释放从DTCM申请的空间。
* 3. K2键按下,从AXI SRAM依次申请160字节,32字节和2333字节。
* 4. K2键松开,释放从AXI SRAM申请的空间。
* 5. K3键按下,从D2域SRAM依次申请200字节,96字节和4111字节。
* 6. K3键松开,释放从D2域SRAM申请的空间。
* 7. 摇杆OK键按下,从D3域SRAM依次申请300字节,128字节和5111字节。
* 8. 摇杆OK键松开,释放从D3域SRAM申请的空间。
* 注意事项:
* 1. 本实验推荐使用串口软件SecureCRT查看打印信息,波特率115200,数据位8,奇偶校验位无,停止位1。
* 2. 务必将编辑器的缩进参数和TAB设置为4来阅读本文件,要不代码显示不整齐。
*
* 修改记录 :
* 版本号 日期 作者 说明
* V1.0 2018-12-12 Eric2013 1. CMSIS软包版本 V5.4.0
* 2. HAL库版本 V1.3.0
*
* Copyright (C), 2018-2030, 安富莱电子 www.armfly.com
*
*********************************************************************************************************
*/
#include "bsp.h" /* 底层硬件驱动 *//* 定义例程名和例程发布日期 */
#define EXAMPLE_NAME "V7-TCM,SRAM等五块内存的动态内存分配实现"
#define EXAMPLE_DATE "2018-12-12"
#define DEMO_VER "1.0"static void PrintfLogo(void);
static void PrintfHelp(void);/* DTCM, 64KB */
mem_head_t *DTCMUsed;
uint64_t AppMallocDTCM[64*1024/8];#if defined ( __ICCARM__ ) /* 使用的IAR *//* D1域, AXI SRAM, 512KB */
mem_head_t *AXISRAMUsed;
#pragma location = 0x24000000
uint64_t AppMallocAXISRAM[512*1024/8];/* D2域, 128KB SRAM1(0x30000000) + 128KB SRAM2(0x30020000) + 32KB SRAM3(0x30040000) */
mem_head_t *SRAM1Used;
#pragma location = 0x30000000
uint64_t AppMallocSRAM1[288*1024/8];/* D3域, SRAM4, 64KB */
mem_head_t *SRAM4Used;
#pragma location = 0x38000000
uint64_t AppMallocSRAM4[64*1024/8];#elif defined ( __CC_ARM ) /* 使用的MDK */
/* D1域, AXI SRAM, 512KB */
mem_head_t *AXISRAMUsed;
uint64_t AppMallocAXISRAM[512*1024/8]__attribute__((at(0x24000000)));/* D2域, 128KB SRAM1(0x30000000) + 128KB SRAM2(0x30020000) + 32KB SRAM3(0x30040000) */
mem_head_t *SRAM1Used;
uint64_t AppMallocSRAM1[288*1024/8]__attribute__((at(0x30000000)));/* D3域, SRAM4, 64KB */
mem_head_t *SRAM4Used;
uint64_t AppMallocSRAM4[64*1024/8]__attribute__((at(0x38000000)));
#endif/*
*********************************************************************************************************
* 函 数 名: main
* 功能说明: c程序入口
* 形 参: 无
* 返 回 值: 错误代码(无需处理)
*********************************************************************************************************
*/
int main(void)
{uint8_t ucKeyCode; /* 按键代码 */uint32_t *DTCM_Addres0, *AXISRAM_Addres0, *SRAM1_Addres0, *SRAM4_Addres0;uint16_t *DTCM_Addres1, *AXISRAM_Addres1, *SRAM1_Addres1, *SRAM4_Addres1;uint8_t *DTCM_Addres2, *AXISRAM_Addres2, *SRAM1_Addres2, *SRAM4_Addres2;bsp_Init(); /* 硬件初始化 *//* 初始化动态内存空间 */osRtxMemoryInit(AppMallocDTCM, sizeof(AppMallocDTCM));osRtxMemoryInit(AppMallocAXISRAM, sizeof(AppMallocAXISRAM));osRtxMemoryInit(AppMallocSRAM1, sizeof(AppMallocSRAM1));osRtxMemoryInit(AppMallocSRAM4, sizeof(AppMallocSRAM4));PrintfLogo(); /* 打印例程名称和版本等信息 */PrintfHelp(); /* 打印操作提示 */bsp_StartAutoTimer(0, 100); /* 启动1个100ms的自动重装的定时器 *//* 进入主程序循环体 */while (1){bsp_Idle(); /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 *//* 判断定时器超时时间 */if (bsp_CheckTimer(0)) {/* 每隔100ms 进来一次 */ bsp_LedToggle(2);}/* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */ucKeyCode = bsp_GetKey(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */if (ucKeyCode != KEY_NONE){switch (ucKeyCode){/* 从DTCM依次申请280字节,64字节和6111字节 */case KEY_DOWN_K1: /* 从DTCM申请280字节空间,使用指针变量DTCM_Addres0操作这些空间时不要超过280字节大小 */ printf("=========================================================\r\n");DTCM_Addres0 = osRtxMemoryAlloc(AppMallocDTCM, 280, 0);DTCMUsed = MemHeadPtr(AppMallocDTCM);printf("DTCM总大小 = %d字节,申请大小 = 0280字节,当前共使用大小 = %d字节\r\n", DTCMUsed->size, DTCMUsed->used);/* 从DTCM申请64字节空间,使用指针变量DTCM_Addres1操作这些空间时不要超过64字节大小 */ DTCM_Addres1 = osRtxMemoryAlloc(AppMallocDTCM, 64, 0);DTCMUsed = MemHeadPtr(AppMallocDTCM);printf("DTCM总大小 = %d字节,申请大小 = 0064字节,当前共使用大小 = %d字节\r\n", DTCMUsed->size, DTCMUsed->used);/* 从DTCM申请6111字节空间,使用指针变量DTCM_Addres2操作这些空间时不要超过6111字节大小 */ DTCM_Addres2 = osRtxMemoryAlloc(AppMallocDTCM, 6111, 0);DTCMUsed = MemHeadPtr(AppMallocDTCM);printf("DTCM总大小 = %d字节,申请大小 = 6111字节,当前共使用大小 = %d字节\r\n", DTCMUsed->size, DTCMUsed->used);break;/* 释放从DTCM申请的空间 */case KEY_UP_K1: /* 释放从DTCM申请的280字节空间 */osRtxMemoryFree(AppMallocDTCM, DTCM_Addres0);DTCMUsed = MemHeadPtr(AppMallocDTCM);printf("释放DTCM动态内存区申请的0280字节,当前共使用大小 = %d字节\r\n", DTCMUsed->used);/* 释放从DTCM申请的64字节空间 */osRtxMemoryFree(AppMallocDTCM, DTCM_Addres1);DTCMUsed = MemHeadPtr(AppMallocDTCM);printf("释放DTCM动态内存区申请的0064字节,当前共使用大小 = %d字节\r\n", DTCMUsed->used);/* 释放从DTCM申请的6111字节空间 */osRtxMemoryFree(AppMallocDTCM, DTCM_Addres2);DTCMUsed = MemHeadPtr(AppMallocDTCM);printf("释放DTCM动态内存区申请的6111字节,当前共使用大小 = %d字节\r\n", DTCMUsed->used);break;/* 从AXI SRAM依次申请160字节,32字节和2333字节 */case KEY_DOWN_K2: /* 从AXI SRAM 申请160字节空间,使用指针变量AXISRAM_Addres0操作这些空间时不要超过160字节大小 */ printf("=========================================================\r\n"); AXISRAM_Addres0 = osRtxMemoryAlloc(AppMallocAXISRAM, 160, 0);AXISRAMUsed = MemHeadPtr(AppMallocAXISRAM);printf("AXI SRAM总大小 = %d字节,申请大小 = 0162字节,当前共使用大小 = %d字节\r\n", AXISRAMUsed->size, AXISRAMUsed->used);/* 从AXI SRAM 申请32字节空间,使用指针变量AXISRAM_Addres1操作这些空间时不要超过32字节大小 */ AXISRAM_Addres1 = osRtxMemoryAlloc(AppMallocAXISRAM, 32, 0);AXISRAMUsed = MemHeadPtr(AppMallocAXISRAM);printf("AXI SRAM总大小 = %d字节,申请大小 = 0032字节,当前共使用大小 = %d字节\r\n", AXISRAMUsed->size, AXISRAMUsed->used);/* 从AXI SRAM 申请2333字节空间,使用指针变量AXISRAM_Addres2操作这些空间时不要超过2333字节大小 */ AXISRAM_Addres2 = osRtxMemoryAlloc(AppMallocAXISRAM, 2333, 0);AXISRAMUsed = MemHeadPtr(AppMallocAXISRAM);printf("AXI SRAM总大小 = %d字节,申请大小 = 2333字节,当前共使用大小 = %d字节\r\n", AXISRAMUsed->size, AXISRAMUsed->used);break;/* 释放从AXI SRAM申请的空间 */case KEY_UP_K2: /* 释放从AXI SRAM申请的160字节空间 */osRtxMemoryFree(AppMallocAXISRAM, AXISRAM_Addres0);AXISRAMUsed = MemHeadPtr(AppMallocAXISRAM);printf("释放AXI SRAM动态内存区申请的0160字节,当前共使用大小 = %d字节\r\n", AXISRAMUsed->used);/* 释放从AXI SRAM申请的32字节空间 */osRtxMemoryFree(AppMallocAXISRAM, AXISRAM_Addres1);AXISRAMUsed = MemHeadPtr(AppMallocAXISRAM);printf("释放AXI SRAM动态内存区申请的0032字节,当前共使用大小 = %d字节\r\n", AXISRAMUsed->used);/* 释放从AXI SRAM申请的2333字节空间 */osRtxMemoryFree(AppMallocAXISRAM, AXISRAM_Addres2);AXISRAMUsed = MemHeadPtr(AppMallocAXISRAM);printf("释放AXI SRAM动态内存区申请的2333字节,当前共使用大小 = %d字节\r\n", AXISRAMUsed->used);break;/* 从D2域SRAM依次申请200字节,96字节和4111字节 */case KEY_DOWN_K3: /* 从D2域的SRAM申请200字节空间,使用指针变量SRAM1_Addres0操作这些空间时不要超过200字节大小 */ printf("=========================================================\r\n"); SRAM1_Addres0 = osRtxMemoryAlloc(AppMallocSRAM1, 200, 0);SRAM1Used = MemHeadPtr(AppMallocSRAM1);printf("D2域SRAM总大小 = %d字节,申请大小 = 0200字节,当前共使用大小 = %d字节\r\n", SRAM1Used->size, SRAM1Used->used);/* 从D2域的SRAM申请96字节空间,使用指针变量SRAM1_Addres1操作这些空间时不要超过96字节大小 */ SRAM1_Addres1 = osRtxMemoryAlloc(AppMallocSRAM1, 96, 0);SRAM1Used = MemHeadPtr(AppMallocSRAM1);printf("D2域SRAM总大小 = %d字节,申请大小 = 0096字节,当前共使用大小 = %d字节\r\n", SRAM1Used->size, SRAM1Used->used);/* 从D2域的SRAM申请4111字节空间,使用指针变量SRAM1_Addres2操作这些空间时不要超过4111字节大小 */ SRAM1_Addres2 = osRtxMemoryAlloc(AppMallocSRAM1, 4111, 0);SRAM1Used = MemHeadPtr(AppMallocSRAM1);printf("D2域SRAM总大小 = %d字节,申请大小 = 4111字节,当前共使用大小 = %d字节\r\n", SRAM1Used->size, SRAM1Used->used);break;/* 释放从D2域SRAM申请的空间 */case KEY_UP_K3: /* 释放从D2域的SRAM申请的200字节空间 */osRtxMemoryFree(AppMallocSRAM1, SRAM1_Addres0);SRAM1Used = MemHeadPtr(AppMallocSRAM1);printf("释放D2域SRAM动态内存区申请的0200字节,当前共使用大小 = %d字节\r\n", SRAM1Used->used);/* 释放从D2域的SRAM申请的96字节空间 */osRtxMemoryFree(AppMallocSRAM1, SRAM1_Addres1);SRAM1Used = MemHeadPtr(AppMallocSRAM1);printf("释放D2域SRAM动态内存区申请的0096字节,当前共使用大小 = %d字节\r\n", SRAM1Used->used);/* 释放从D2域的SRAM申请的4111字节空间 */osRtxMemoryFree(AppMallocSRAM1, SRAM1_Addres2);SRAM1Used = MemHeadPtr(AppMallocSRAM1);printf("释放D2域SRAM动态内存区申请的4111字节,当前共使用大小 = %d字节\r\n", SRAM1Used->used);break;/* 从D3域SRAM依次申请300字节,128字节和5111字节 */case JOY_DOWN_OK: /* 从D3域的SRAM申请300字节空间,使用指针变量SRAM4_Addres0操作这些空间时不要超过300字节大小 */ printf("=========================================================\r\n"); SRAM4_Addres0 = osRtxMemoryAlloc(AppMallocSRAM4, 300, 0);SRAM4Used = MemHeadPtr(AppMallocSRAM4);printf("D3域SRAM总大小 = %d字节,申请大小 = 0300字节,当前共使用大小 = %d字节\r\n", SRAM4Used->size, SRAM4Used->used);/* 从D3域的SRAM申请96字节空间,使用指针变量SRAM4_Addres1操作这些空间时不要超过96字节大小 */ SRAM4_Addres1 = osRtxMemoryAlloc(AppMallocSRAM4, 128, 0);SRAM4Used = MemHeadPtr(AppMallocSRAM4);printf("D3域SRAM总大小 = %d字节,申请大小 = 0128字节,当前共使用大小 = %d字节\r\n", SRAM4Used->size, SRAM4Used->used);/* 从D3域的SRAM申请5111字节空间,使用指针变量SRAM4_Addres2操作这些空间时不要超过5111字节大小 */ SRAM4_Addres2 = osRtxMemoryAlloc(AppMallocSRAM4, 5111, 0);SRAM4Used = MemHeadPtr(AppMallocSRAM4);printf("D3域SRAM总大小 = %d字节,申请大小 = 5111字节,当前共使用大小 = %d字节\r\n", SRAM4Used->size, SRAM4Used->used);break;/* 释放从D3域SRAM申请的空间 */case JOY_UP_OK: /* 释放从D3域的SRAM申请的300字节空间 */osRtxMemoryFree(AppMallocSRAM4, SRAM4_Addres0);SRAM4Used = MemHeadPtr(AppMallocSRAM4);printf("释放D3域SRAM动态内存区申请的0300字节,当前共使用大小 = %d字节\r\n", SRAM4Used->used);/* 释放从D3域的SRAM申请的128字节空间 */osRtxMemoryFree(AppMallocSRAM4, SRAM4_Addres1);SRAM4Used = MemHeadPtr(AppMallocSRAM4);printf("释放D3域SRAM动态内存区申请的0128字节,当前共使用大小 = %d字节\r\n", SRAM4Used->used);/* 释放从D3域的SRAM申请的5111字节空间 */osRtxMemoryFree(AppMallocSRAM4, SRAM4_Addres2);SRAM4Used = MemHeadPtr(AppMallocSRAM4);printf("释放D3域SRAM动态内存区申请的5111字节,当前共使用大小 = %d字节\r\n", SRAM4Used->used);break;default:/* 其它的键值不处理 */break;}}}
}/*
*********************************************************************************************************
* 函 数 名: PrintfHelp
* 功能说明: 打印操作提示
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void PrintfHelp(void)
{printf("操作提示:\r\n");printf("1. K1键按下,从DTCM依次申请280字节,64字节和6111字节\r\n");printf("2. K1键松开,释放从DTCM申请的空间\r\n");printf("3. K2键按下,从AXI SRAM依次申请160字节,32字节和2333字节\r\n");printf("4. K2键松开,释放从AXI SRAM申请的空间\r\n");printf("5. K3键按下,从D2域SRAM依次申请200字节,96字节和4111字节\r\n");printf("6. K3键松开,释放从D2域SRAM申请的空间\r\n");printf("7. 摇杆OK键按下,从D3域SRAM依次申请300字节,128字节和5111字节\r\n");printf("8. 摇杆OK键松开,释放从D3域SRAM申请的空间\r\n");
}/*
*********************************************************************************************************
* 函 数 名: PrintfLogo
* 功能说明: 打印例程名称和例程发布日期, 接上串口线后,打开PC机的超级终端软件可以观察结果
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void PrintfLogo(void)
{printf("*************************************************************\n\r");/* 检测CPU ID */{uint32_t CPU_Sn0, CPU_Sn1, CPU_Sn2;CPU_Sn0 = *(__IO uint32_t*)(0x1FF1E800);CPU_Sn1 = *(__IO uint32_t*)(0x1FF1E800 + 4);CPU_Sn2 = *(__IO uint32_t*)(0x1FF1E800 + 8);printf("\r\nCPU : STM32H743XIH6, BGA240, 主频: %dMHz\r\n", SystemCoreClock / 1000000);printf("UID = %08X %08X %08X\n\r", CPU_Sn2, CPU_Sn1, CPU_Sn0);}printf("\n\r");printf("*************************************************************\n\r");printf("* 例程名称 : %s\r\n", EXAMPLE_NAME); /* 打印例程名称 */printf("* 例程版本 : %s\r\n", DEMO_VER); /* 打印例程版本 */printf("* 发布日期 : %s\r\n", EXAMPLE_DATE); /* 打印例程日期 *//* 打印ST的HAL库版本 */printf("* HAL库版本 : V1.3.0 (STM32H7xx HAL Driver)\r\n");printf("* \r\n"); /* 打印一行空格 */printf("* QQ : 1295744630 \r\n");printf("* 旺旺 : armfly\r\n");printf("* Email : armfly@qq.com \r\n");printf("* 微信公众号: armfly_com \r\n");printf("* 淘宝店: armfly.taobao.com\r\n");printf("* Copyright www.armfly.com 安富莱电子\r\n");printf("*************************************************************\n\r");
}/***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/
此处建议直接参考安富莱原文
13、MDK分散加载方式管理多块内存相关推荐
- stm32h7内存分配_stm32h7“分散加载方式管理多块内存”
默认情况下,我们都是通过 MDK 的 option 选项设置 Flash 和 RAM 大小,如图1 图1 这种情况下,不方便用户将变量定义到指定的 CCM 或者 SDRAM 中.而使用attribut ...
- x210开发板的三种启动方式(三星推荐的,分散加载,uboot采用的)
1.三星推荐的启动方式 (1)将bl1放在在SRAM中运行,将bl2也在SRAM中运行,就像datasheet中描述那样. (2)bootloader必须小于96KB并大于16KB,假定bootloa ...
- STM32之MDK分析,分散加载
STM32分散加载 我们知道MDK的分散加载主要是通过.sct文件实现的,链接器根据.sct文件的配置分配各个节区地址,生成分散加载代码,因此我们通过修改该文件可以定制具体节区的存储位置. 如何打开. ...
- 【C语言常识】Keil MDK的分散加载文件.sct
https://blog.csdn.net/wuhenyouyuyouyu/article/details/71171546?ops_request_misc=%257B%2522request%25 ...
- KEIL MDK链接脚本-分散加载文件sct
在了解keil的链接脚本之前需要了解几个重要概念: RO(ReadOnly):表示程序中的指令和常量 RW(Read/Write):表示程序中已初始化的变量 ZI(Zero):表示程序中未初始化的变量 ...
- 从MDK分散加载文件学习STM32启动流程
一直在用ARM的Cortex-M系列做产品开发,也陆陆续续学习了ARM的启动流程.汇编启动文件,但是总感觉没有连贯的把全部知识串起来,不知道某些汇编语句为什么要这么写,没法按照自己的情况进行修改.今天 ...
- sct分散加载文件格式与应用
*.sct分散加载文件是根据芯片内部FLASH和SRAM存储器概况生成的配置文件,链接器根据该文件的配置分配各个节区地址,生成分散加载代码,通过修改该文件可以定制节区的具体存储位置.例如控制代码的加载 ...
- keil的sct文件_STM32 分散加载文件 .sct 解析
1.STM32 启动文件与 .sct 文件分析 1) 定义STACK段,{NOINIT,读写}:分配一段内存大小为0.5K; 2) 定义HEAP段, {NOINIT,读写}:分配一段内存大小为1K; ...
- 从零实现 Cortex-M7从驱动到应用(一)分散加载+SDRAM+SPI_FLASH
接下来我将与大家分享H750的开发,最终实现emwin的运行. 搭建环境 cubemx 由于cubemx老版本不支持H750,所以只能用新版本,注意:老版本支持工程路径为中文,新版本不支持! 需要H7 ...
最新文章
- getaddrinfo()函数详解
- Java基础知识——类装载器与反射机制
- pg 递归算法_16. 图的________优先搜索遍历算法是一种递归算法,图的________优先搜索遍历算法需要使用队列。...
- java 后端校验_如何实现Java后端数据校验?看这篇就足够!
- springboot : Failed to decode downloaded font 和 OTS parsing error
- 论文浅尝 - ICLR2020 | 用于半监督分类的图形推理学习
- python监控windows日志_Python 监控日志的简单示例
- 信息学奥赛一本通(2025:【例4.11】体操队)
- 信息学奥赛一本通(1032:大象喝水查)
- 混淆矩阵(Confusion Matrix)
- jq ui 日历控件
- 解决在RHEL/CentOS7.4以上版本无法使用AFD(Oracle ASMFD)特性
- HMI-66-【MeterDisplay for Arm Linux】液晶仪表Arm Linxu迁移
- java gnuplot,用 Gnuplot 绘制实验数据
- 飞秋只能发文件不能接收文件的解决办法
- Java以毫秒为单位返回秒表记录的流逝时间(即求一个程序段的运行时间)
- java对手机芯片有没有要求_芯片对手机到底有多重要
- opensips安装教程
- selenium +eclipse+firefox/chrome 环境全套搭配
- python断网还能用吗_python 断网