STMicroelectronics 系列:STM32L4 系列_(12).STM32L4系列的内存架构
STM32L4系列的内存架构
内存架构概述
STM32L4系列微控制器具有复杂的内存架构,旨在提供高性能、低功耗和高灵活性。该系列的内存架构包括多种类型的内存,如闪存(Flash Memory)、静态随机存取内存(SRAM)、外设总线(Peripheral Bus)等。合理的内存管理对于优化系统性能和降低功耗至关重要。
内存类型
-
闪存(Flash Memory)
-
用于存储程序代码和常量数据。
-
高密度存储,具有较长的使用寿命。
-
支持多种读取模式,如单周期读取和多周期读取,以适应不同的性能需求。
-
静态随机存取内存(SRAM)
-
用于存储变量和运行时数据。
-
高速访问,适用于频繁读写操作。
-
STM32L4系列通常配置有多个SRAM区域,以提供更大的存储空间和更高的灵活性。
-
外设总线(Peripheral Bus)
-
用于连接各种外设,如GPIO、UART、SPI等。
-
提供多种总线速度和访问模式,以适应不同外设的需求。
内存映射
STM32L4系列的内存映射是其内存架构的核心,定义了各种内存和外设在地址空间中的位置。内存映射如下:
闪存映射
地址范围:0x0000 0000 – 0x0007 FFFF
通常用于存储程序代码和初始化数据。
SRAM映射
地址范围:0x2000 0000 – 0x2001 FFFF
用于存储运行时数据和变量。
外设映射
地址范围:0x4000 0000 – 0x400F FFFF
用于连接各种外设。
内存控制器
STM32L4系列微控制器通过内存控制器来管理内存的读写操作。内存控制器支持多种功能,如内存保护、DMA传输、缓存管理等。
内存保护单元(MPU)
用于保护内存区域,防止非法访问。
可以配置多个区域,每个区域可以设置访问权限和缓存属性。
直接存储器访问(DMA)
用于在内存和外设之间进行高效的数据传输。
支持多个DMA通道,每个通道可以配置不同的传输参数。
缓存管理
STM32L4系列微控制器配备了缓存管理单元,用于优化内存访问性能。缓存管理包括指令缓存和数据缓存。
指令缓存(ICache)
存储最近访问的指令,减少指令读取时间。
可以通过软件控制启用或禁用。
数据缓存(DCache)
存储最近访问的数据,减少数据读取时间。
可以通过软件控制启用或禁用。
闪存管理
闪存读取模式
STM32L4系列支持多种闪存读取模式,以适应不同的性能需求。常见的读取模式包括:
单周期读取模式
在最高性能模式下,闪存可以以单周期的速度进行读取。
适用于需要高速执行代码的场景。
多周期读取模式
为了降低功耗,可以配置闪存为多周期读取模式。
适用于对功耗有严格要求的场景。
闪存编程
闪存编程是STM32L4系列微控制器中的一个重要操作,用于更新程序代码或常量数据。闪存编程通常需要以下几个步骤:
-
解锁闪存
- 在编程前,需要解锁闪存以允许写操作。
-
擦除闪存
- 闪存编程前需要先擦除目标区域。
-
编程闪存
- 将数据写入闪存。
-
锁定闪存
- 编程完成后,需要锁定闪存以防止意外写操作。
代码示例:闪存编程
以下是一个使用STM32L4系列HAL库进行闪存编程的示例代码:
#include "stm32l4xx_hal.h"
// 定义要编程的数据
uint32_t dataToWrite = 0x12345678;
uint32_t addressToWrite = 0x0800 0000 + 0x100; // 从闪存起始地址0x0800 0000开始编程
void Flash_Write(uint32_t address, uint32_t data) {
HAL_StatusTypeDef status;
// 解锁闪存
HAL_FLASH_Unlock();
// 擦除闪存
FLASH_EraseInitTypeDef eraseInitStruct;
uint32_t pageError = 0;
eraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
eraseInitStruct.PageAddress = address;
eraseInitStruct.NbPages = 1; // 擦除一个页面
status = HAL_FLASHEx_Erase(&eraseInitStruct, &pageError);
if (status != HAL_OK) {
// 错误处理
while (1);
}
// 编程闪存
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, data);
if (status != HAL_OK) {
// 错误处理
while (1);
}
// 锁定闪存
HAL_FLASH_Lock();
}
int main(void) {
HAL_Init();
// 初始化系统时钟
SystemClock_Config();
// 写入闪存
Flash_Write(addressToWrite, dataToWrite);
while (1) {
// 主循环
}
}
闪存保护
闪存保护是STM32L4系列中的一项重要功能,用于防止非法修改闪存中的数据。通过设置闪存保护寄存器,可以实现对特定区域的保护。闪存保护可以防止恶意攻击和意外的数据写入,确保系统的稳定性和安全性。
代码示例:闪存保护
以下是一个设置闪存保护的示例代码:
#include "stm32l4xx_hal.h"
void Flash_Protect(uint32_t address, uint32_t length) {
HAL_StatusTypeDef status;
// 解锁闪存
HAL_FLASH_Unlock();
// 设置闪存保护
FLASH_Option_InitTypeDef optionInitStruct;
optionInitStruct.OptionType = OPTIONBYTE_WRP;
optionInitStruct.WRPState = FLASH_WRPSTATE_ENABLE;
optionInitStruct.Banks = FLASH_BANK_1;
optionInitStruct.WrapSector = FLASH_SECTOR_0;
optionInitStruct.WrapLength = FLASH_SIZE_128K;
status = HAL_FLASHEx_OptionByteProgram(&optionInitStruct);
if (status != HAL_OK) {
// 错误处理
while (1);
}
// 锁定闪存
HAL_FLASH_Lock();
}
int main(void) {
HAL_Init();
// 初始化系统时钟
SystemClock_Config();
// 保护闪存
Flash_Protect(0x0800 0000, 0x1000);
while (1) {
// 主循环
}
}
SRAM管理
SRAM区域配置
STM32L4系列微控制器通常配置有多个SRAM区域,每个区域可以独立配置其访问属性和缓存属性。常见的SRAM区域包括:
SRAM1
地址范围:0x2000 0000 – 0x2000 7FFF
用于存储运行时数据和变量。
SRAM2
地址范围:0x2001 0000 – 0x2001 7FFF
用于存储低功耗模式下的数据。
SRAM访问优化
为了优化SRAM的访问性能,STM32L4系列提供了多种技术,如内存对齐、DMA传输等。内存对齐可以提高数据访问速度,而DMA传输可以减少CPU的负担,提高数据传输效率。
代码示例:SRAM访问优化
以下是一个使用DMA进行SRAM访问的示例代码:
#include "stm32l4xx_hal.h"
// 定义源数据和目标数据缓冲区
uint32_t sourceData[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
uint32_t targetData[10];
// 配置DMA
void DMA_Config(void) {
__HAL_RCC_DMA1_CLK_ENABLE();
// 创建DMA句柄
DMA_HandleTypeDef hdma;
// 初始化DMA
hdma.Instance = DMA1_Channel1;
hdma.Init.Request = DMA_REQUEST_0;
hdma.Init.Direction = DMA_MEMORY_TO_MEMORY;
hdma.Init.PeriphInc = DMA_PINC_DISABLE;
hdma.Init.MemInc = DMA_MINC_ENABLE;
hdma.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma.Init.Mode = DMA_NORMAL;
hdma.Init.Priority = DMA_PRIORITY_HIGH;
hdma.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
HAL_DMA_Init(&hdma);
// 配置DMA传输
HAL_DMA_Start(&hdma, (uint32_t)sourceData, (uint32_t)targetData, 10);
}
int main(void) {
HAL_Init();
// 初始化系统时钟
SystemClock_Config();
// 配置DMA
DMA_Config();
while (1) {
// 主循环
}
}
SRAM低功耗管理
SRAM低功耗管理是STM32L4系列中的一个重要功能,用于在低功耗模式下保持SRAM中的数据。通过配置低功耗模式下的SRAM保留区域,可以在进入低功耗模式时保留关键数据,确保系统在唤醒时能够快速恢复。
代码示例:SRAM低功耗管理
以下是一个配置SRAM保留区域的示例代码:
#include "stm32l4xx_hal.h"
void LowPower_Config(void) {
// 配置低功耗模式
HAL_PWREx_EnableLowPowerRunMode();
// 配置SRAM保留区域
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWREx_Control_BOR(PWR_BOR_LEVEL_3);
HAL_PWREx_EnableVddIO2();
HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);
HAL_PWREx_EnableLowPowerStopMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI, PWR_SRAM1_RETAINED, 0);
}
int main(void) {
HAL_Init();
// 初始化系统时钟
SystemClock_Config();
// 配置低功耗模式
LowPower_Config();
while (1) {
// 主循环
}
}
外设总线管理
外设总线配置
STM32L4系列微控制器的外设总线配置非常灵活,可以适应不同的外设需求。常见的配置包括总线速度、优先级等。合理配置外设总线可以提高系统的响应速度和性能。
代码示例:外设总线配置
以下是一个配置外设总线的示例代码:
#include "stm32l4xx_hal.h"
void PeriphBus_Config(void) {
// 配置GPIO
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef gpioInitStruct;
gpioInitStruct.Pin = GPIO_PIN_5;
gpioInitStruct.Mode = GPIO_MODE_OUTPUT_PP;
gpioInitStruct.Pull = GPIO_NOPULL;
gpioInitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &gpioInitStruct);
// 配置UART
__HAL_RCC_USART1_CLK_ENABLE();
UART_HandleTypeDef huart1;
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;
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;
HAL_UART_Init(&huart1);
}
int main(void) {
HAL_Init();
// 初始化系统时钟
SystemClock_Config();
// 配置外设总线
PeriphBus_Config();
while (1) {
// 主循环
}
}
外设总线优先级
外设总线的优先级配置可以提高系统的响应速度,确保关键外设的访问优先级。通过配置外设的中断优先级和DMA通道的优先级,可以实现高效的外设管理。
代码示例:外设总线优先级配置
以下是一个配置外设总线优先级的示例代码:
#include "stm32l4xx_hal.h"
void PeriphBus_PriorityConfig(void) {
// 配置DMA通道优先级
__HAL_RCC_DMA1_CLK_ENABLE();
DMA_HandleTypeDef hdma1;
hdma1.Instance = DMA1_Channel1;
hdma1.Init.Priority = DMA_PRIORITY_HIGH;
HAL_DMA_Init(&hdma1);
// 配置UART优先级
__HAL_RCC_USART1_CLK_ENABLE();
UART_HandleTypeDef huart1;
huart1.Instance = USART1;
huart1.Init.Priority = NVIC_PRIORITY_LOW;
HAL_UART_Init(&huart1);
}
int main(void) {
HAL_Init();
// 初始化系统时钟
SystemClock_Config();
// 配置外设总线优先级
PeriphBus_PriorityConfig();
while (1) {
// 主循环
}
}
外设总线DMA传输
DMA传输可以显著提高外设总线的数据传输效率,减少CPU的负担。通过配置DMA通道和外设之间的链接,可以实现高效的数据传输。
代码示例:外设总线DMA传输
以下是一个使用DMA进行UART数据传输的示例代码:
#include "stm32l4xx_hal.h"
// 定义UART传输缓冲区
uint8_t transmitData[10] = "Hello, STM32L4!";
void UART_DMA_Config(void) {
// 配置UART
__HAL_RCC_USART1_CLK_ENABLE();
UART_HandleTypeDef huart1;
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;
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;
HAL_UART_Init(&huart1);
// 配置DMA
__HAL_RCC_DMA1_CLK_ENABLE();
DMA_HandleTypeDef hdma1;
hdma1.Instance = DMA1_Channel1;
hdma1.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma1.Init.MemInc = DMA_MINC_ENABLE;
hdma1.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma1.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma1.Init.Mode = DMA_NORMAL;
hdma1.Init.Priority = DMA_PRIORITY_HIGH;
hdma1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
HAL_DMA_Init(&hdma1);
// 链接DMA和UART
__HAL_LINKDMA(&huart1, hdmarx, hdma1);
__HAL_LINKDMA(&huart1, hdmatx, hdma1);
// 开始DMA传输
HAL_UART_Transmit_DMA(&huart1, transmitData, 10);
}
int main(void) {
HAL_Init();
// 初始化系统时钟
SystemClock_Config();
// 配置UART和DMA
UART_DMA_Config();
while (1) {
// 主循环
}
}
内存保护单元(MPU)
MPU配置
STM32L4系列的MPU配置非常灵活,可以保护多个内存区域,防止非法访问。配置MPU时需要指定区域的起始地址、大小、访问权限和缓存属性。合理的MPU配置可以防止任务之间的干扰,确保系统的稳定性和安全性。
代码示例:MPU配置
以下是一个配置MPU的示例代码:
#include "stm32l4xx_hal.h"
void MPU_Config(void) {
// 启用MPU
HAL_MPU_Enable(MPU_HARD_FAULT_NA);
// 配置MPU区域
MPU_Region_InitTypeDef MPU_InitStruct;
// 区域0:保护闪存
MPU_InitStruct.BaseAddress = 0x0800 0000;
MPU_InitStruct.RegionSize = MPU_REGION_SIZE_128KB;
MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
MPU_InitStruct.IsBufferable = MPU_BUFFERABLE_DISABLE;
MPU_InitStruct.IsCacheable = MPU_CACHEABLE_DISABLE;
MPU_InitStruct.IsShareable = MPU_UNSHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
// 区域1:保护SRAM
MPU_InitStruct.BaseAddress = 0x2000 0000;
MPU_InitStruct.RegionSize = MPU_REGION_SIZE_64KB;
MPU_InitStruct.AccessPermission = MPU_REGION_PRIV_RW;
MPU_InitStruct.IsBufferable = MPU_BUFFERABLE_DISABLE;
MPU_InitStruct.IsCacheable = MPU_CACHEABLE_ENABLE;
MPU_InitStruct.IsShareable = MPU_UNSHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER1;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
}
int main(void) {
HAL_Init();
// 初始化系统时钟
SystemClock_Config();
// 配置MPU
MPU_Config();
while (1) {
// 主循环
}
}
MPU使用场景
MPU在实际应用中非常有用,特别是在多任务操作系统和安全关键应用中。合理的MPU配置可以防止任务之间的干扰,确保系统的稳定性和安全性。以下是一些常见的MPU使用场景:
-
多任务操作系统
- 在多任务操作系统中,MPU可以防止一个任务对另一个任务的内存区域进行非法访问,确保系统的稳定性和安全性。
-
安全关键应用
- 在安全关键应用中,MPU可以保护关键数据和代码,防止恶意攻击和意外的数据修改。
缓存管理
缓存配置
STM32L4系列的缓存配置包括指令缓存(ICache)和数据缓存(DCache)。缓存可以显著提高系统的性能,但不当的缓存配置可能会导致数据不一致和其他问题。合理配置缓存可以优化内存访问速度,减少CPU的等待时间。
ICache配置
指令缓存(ICache)用于存储最近访问的指令,减少指令读取时间。可以通过软件控制启用或禁用ICache。
启用ICache
禁用ICache
代码示例:ICache配置
以下是一个启用和禁用ICache的示例代码:
#include "stm32l4xx_hal.h"
void ICache_Enable(void) {
// 启用ICache
HAL_ICACHE_Enable();
}
void ICache_Disable(void) {
// 禁用ICache
HAL_ICACHE_Disable();
}
int main(void) {
HAL_Init();
// 初始化系统时钟
SystemClock_Config();
// 启用ICache
ICache_Enable();
while (1) {
// 主循环
}
}
DCache配置
数据缓存(DCache)用于存储最近访问的数据,减少数据读取时间。同样可以通过软件控制启用或禁用DCache。
启用DCache
禁用DCache
代码示例:DCache配置
以下是一个启用和禁用DCache的示例代码:
#include "stm32l4xx_hal.h"
void DCache_Enable(void) {
// 启用DCache
HAL_DCACHE_Enable();
}
void DCache_Disable(void) {
// 禁用DCache
HAL_DCACHE_Disable();
}
int main(void) {
HAL_Init();
// 初始化系统时钟
SystemClock_Config();
// 启用DCache
DCache_Enable();
while (1) {
// 主循环
}
}
缓存管理操作
除了启用和禁用缓存外,STM32L4系列还提供了多种缓存管理操作,如刷新缓存、使缓存失效等。这些操作在特定情况下非常有用,例如在进行DMA传输时,确保缓存中的数据与内存中的数据一致,避免数据不一致问题。
代码示例:缓存管理操作
以下是一个进行缓存刷新和使缓存失效的示例代码:
#include "stm32l4xx_hal.h"
void Cache_Management(uint32_t address, uint32_t length) {
// 刷新DCache
HAL_DCACHE_Invalidate(address, length);
// 刷新ICache
HAL_ICACHE_Invalidate(address, length);
// 使DCache失效
HAL_DCACHE_Disable();
// 使ICache失效
HAL_ICACHE_Disable();
}
int main(void) {
HAL_Init();
// 初始化系统时钟
SystemClock_Config();
// 缓存管理
Cache_Management(0x2000 0000, 0x1000);
while (1) {
// 主循环
}
}
缓存使用场景
缓存在实际应用中非常有用,特别是在高性能计算和实时系统中。合理的缓存配置可以显著提高系统的响应速度和整体性能。以下是一些常见的缓存使用场景:
-
高频访问的数据
- 对于频繁访问的数据,启用DCache可以显著减少访问时间。
-
频繁执行的代码
- 对于频繁执行的代码段,启用ICache可以减少指令读取时间,提高执行效率。
-
DMA传输
- 在进行DMA传输时,确保缓存中的数据与内存中的数据一致,避免数据不一致问题。
总结
STM32L4系列微控制器的内存架构设计旨在提供高性能、低功耗和高灵活性。合理的内存管理和缓存配置对于优化系统性能和降低功耗至关重要。通过使用HAL库和配置内存保护单元(MPU)、直接存储器访问(DMA)等技术,可以实现高效的数据管理和系统保护。希望本文的内容能够帮助开发者更好地理解和利用STM32L4系列的内存架构,提升系统的性能和稳定性。
作者:kkchenkx