STM32-13-MPU

STM32-01-认识单片机
STM32-02-基础知识
STM32-03-HAL库
STM32-04-时钟树
STM32-05-SYSTEM文件夹
STM32-06-GPIO
STM32-07-外部中断
STM32-08-串口
STM32-09-IWDG和WWDG
STM32-10-定时器
STM32-11-电容触摸按键
STM32-12-OLED模块

文章目录

  • STM32-12-MPU
    • 1. 内存保护单元MPU
        • 1. MPU简介
        • 2. 内存地址映射
        • 3. MPU设置内存区域的访问权限
        • 4. MPU配置内存区域的访问属性
        • 5. 可共享 Master间数据同步
        • 6. 缓存缩略
    • 2. Cache简介
        • 1. 简介
        • 2. 读操作与写操作
        • 3. 内核基本操作
        • 4. 内核读取cache
        • 5. 内核写入cache
        • 6. 数据不一致解决办法
        • 7. Cache配置相关函数
    • 3. MPU相关寄存器
    • 4. MPU相关HAL库驱动
    • 5. MPU基本配置步骤
    • 6. 代码实现

STM32-12-MPU

1. 内存保护单元MPU

1. MPU简介
  • 内存保护单元:(memory protection unit),简称MPU.

  • MPU功能

    1. 设置不同存储区域的存储器访问权限(特权级用户级

      MPU可以根据特权级或用户级设置存储器区域的读、写、执行等权限,以限制对存储器的访问。

    2. 设置存储器(内存和外设)属性(可缓存可缓冲可共享

  • 具体功能

    • 阻止用户应用程序破坏操作系统使用的数据;
    • 阻止一个任务访问其他任务的数据区,从而隔离任务;
    • 把关键数据区域设置为只读,从根本上解决被破坏的可能;
    • 检测意外的存储访问,如堆栈溢出、数组越界等;
    • 将SRAM或RAM空间定义为不可执行,防止代码注入攻击;
    • 提高嵌入式系统的健壮性,使系统更加安全。
2. 内存地址映射

在这里插入图片描述

内核地址映射是指将物理内存地址映射到内核空间的过程,其中MPU可以通过配置保护内存区域来实现对内核地址空间的保护和管理。

在MPU中,可以配置多个内存保护区域,通常编号为Region0到Region15(针对H7芯片),每个区域都有一定的访问属性和权限。这些区域可以用来定义内核地址空间的访问规则,以确保内核的安全性和稳定性。

针对内核地址映射,需要注意以下配置:

  1. 优先级和编号: MPU中每个内存保护区域都有一个编号,通常编号范围是0到15。编号越小,优先级越低,这意味着编号为15的区域具有最高的权限和访问优先级。
  2. 访问属性和权限: 每个内存保护区域可以配置不同的访问属性和权限,包括读、写和执行权限。可以根据需要配置这些属性,以确保不同内存区域的安全性。例如,你可以设置某个区域为只读(不可写),或禁用某些区域的执行权限以防止代码注入攻击。
  3. 重叠和嵌套: 如果存在多个内存保护区域并且它们发生重叠或嵌套,MPU会按照优先级高的区域的配置规则来执行。这意味着在访问重叠或嵌套区域时,遵循优先级最高的区域的访问规则,以确保内存访问的安全性。
  4. 背景区: MPU通常还会定义一个默认的背景区,其序号通常为-1。背景区用于定义未被其他保护区域覆盖的内存空间的访问规则,这可以确保整个内存空间的完整性,即使没有显式地定义所有区域。

通过配置MPU,可以在STM32中实现不同内存区域的访问控制,确保系统的安全性和稳定性。

3. MPU设置内存区域的访问权限

MPU_REGION_NO_ACCESS:无访问(特权级&用户级都不可访问);

MPU_REGION_PRIV_RW:仅支持特权级读写访问;

MPU_REGION_PRIV_RW_URO:禁止用户级写访问(特权级可读写访问);

MPU_REGION_FULL_ACCESS:全访问(特权级&用户级都可访问);

MPU_REGION_PRIV_RO:仅支持特权级读访问;

MPU_REGION_PRIV_RO_URO:只读(特权级&用户级都不可以写)。

在配置了MPU的访问权限后,如果程序尝试访问未经授权的区域或者违反了权限配置,将触发错误异常MemManage),系统会相应地进行处理,通常是通过异常处理机制中的相关异常处理函数进行处理,例如重启系统或者输出错误信息等。

4. MPU配置内存区域的访问属性

在这里插入图片描述

在配置MPU时,可以为每个内存区域指定访问属性,以确保CPU对内存的访问方式符合要求。主要的内存类型包括:

  1. 正常内存(Normal Memory): 正常内存类型允许CPU使用缓存和优化访问。这种类型的内存通常用于RAMROM。访问顺序可以被优化,以提高性能。
  2. 设备内存(Device Memory): 这种类型的内存用于访问外设,确保访问的顺序和原子性。设备内存禁止缓存,以确保每次访问都直接与硬件通信。这种内存访问被认为是强顺序的。
  3. 强排序内存(Strongly Ordered Memory): 这种类型的内存访问顺序严格,所有内存访问按照程序顺序进行,不会被重排序。这种类型通常用于控制寄存器外设

配置步骤

初始化MPU

  • 启用MPU并设置默认内存访问权限。
  • 可以通过函数HAL_MPU_Enable()启用MPU。

配置内存区域

  • 使用MPU_Region_InitTypeDef结构体定义每个内存保护区域的属性。
  • 设置区域编号、起始地址、尺寸、访问权限、子区域禁用和其他属性。
  • 调用HAL_MPU_ConfigRegion()函数配置每个区域。

启用MPU保护

  • 在配置所有内存区域后,调用HAL_MPU_Enable()函数启用MPU保护。

代码示例

void MPU_Config(void)
{MPU_Region_InitTypeDef MPU_InitStruct;/* Disable MPU */HAL_MPU_Disable();/* Configure the MPU attributes as WT for SRAM */MPU_InitStruct.Enable = MPU_REGION_ENABLE;MPU_InitStruct.BaseAddress = 0x20000000;MPU_InitStruct.Size = MPU_REGION_SIZE_128KB;MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;MPU_InitStruct.Number = MPU_REGION_NUMBER0;MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;MPU_InitStruct.SubRegionDisable = 0x00;MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;HAL_MPU_ConfigRegion(&MPU_InitStruct);/* Enable MPU (Background region) */HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

这个例子配置了一个128KB大小的SRAM区域,允许全访问权限,并且在启用MPU时启用背景区域。

三种类型对应的情景
在这里插入图片描述

  1. Normal Memory

    应用场景:

    • 用于存储一般数据和程序代码,例如RAM和ROM。

    • 适用于需要高速访问和缓存优化的数据存储区域。

    特点:

    • 可缓存: 允许使用缓存,以提高访问速度和系统性能。

    • 访问优化: 访问顺序可以被CPU优化(乱序执行),以更高效地利用总线带宽。

    • 读写权限: 根据配置,可以设置为只读、只写或读写。

    示例:

    • 程序代码存储(Flash memory)

    • 动态数据存储(SRAM)

  2. Device Memory

    应用场景:

    • 用于与外设通信,例如GPIO、ADC、UART等外设寄存器。

    • 适用于必须确保访问顺序和数据完整性的硬件接口。

    特点:

    • 非缓存: 禁止使用缓存,以确保每次访问都直接与硬件通信。

    • 访问顺序严格: 保证访问顺序不被CPU重新排序,以确保硬件操作的原子性和一致性。

    • 特殊处理: 适用于需要特定处理的外设寄存器,如读写时有副作用的寄存器。

    示例:

    • 控制寄存器(GPIO)

    • 状态寄存器(UART状态寄存器)

  3. Strongly Ordered Memory

    应用场景:

    • 用于需要严格访问顺序的关键系统部分,例如控制寄存器和重要外设寄存器。

    • 适用于多处理器系统中的共享资源管理。

    特点:

    • 严格访问顺序: 所有内存访问严格按程序顺序执行,不允许任何形式的重新排序。

    • 数据完整性: 确保多处理器系统中访问共享资源时数据的完整性。

    • 非缓存: 通常禁止缓存,确保每次访问的准确性和一致性。

    示例:

    • 多处理器系统中的共享控制寄存器

    • 高优先级系统任务中的关键配置寄存器

5. 可共享 Master间数据同步

在这里插入图片描述

在多处理器系统或多核系统中确保数据同步一致性是非常重要的。以下是一些常见的方法:

  1. 锁机制(Locking):锁是一种最基本的同步机制,它可以确保在任何时刻只有一个线程可以访问共享资源。当一个核心想要访问共享资源时,它会请求获取锁,如果锁已被其他核心持有,则该核心会被阻塞直到锁可用。
  2. 信号量(Semaphore):信号量是一种更加灵活的同步机制,它允许多个线程同时访问共享资源,但是限制同时访问资源的线程数量。信号量维护一个计数器,当一个核心想要访问资源时,它首先尝试对信号量进行加锁,如果计数器大于零,则可以继续访问资源,并将计数器减一;否则,线程将被阻塞直到计数器变为正数。
  3. 条件变量(Condition Variables):条件变量允许线程在某些条件下等待或者被唤醒。在多处理器系统中,条件变量常与锁结合使用,以实现更复杂的同步机制。当一个核心等待某个条件时,它会释放锁并进入睡眠状态,直到其他核心改变了条件并唤醒了等待线程。
  4. 原子操作(Atomic Operations):原子操作是一种特殊的指令序列,可以在不被中断的情况下执行。在多处理器系统中,原子操作通常用于实现简单的同步操作,比如对共享变量的增加或减少。原子操作保证了这些操作的原子性,即它们要么完全执行成功,要么完全不执行,不会出现部分执行的情况。
  5. 屏障(Barrier):屏障用于确保在多个线程中的所有操作都完成之后才能继续执行下一步操作。在多处理器系统中,屏障可以用于同步多个核心的执行流,确保它们在某个点上同时到达并继续执行。
  6. 读-写锁(Read-Write Lock):读-写锁允许多个线程同时读取共享资源,但是只有一个线程可以写入共享资源。在多处理器系统中,读-写锁可以提高并发性能,因为多个线程可以同时读取资源而无需互斥。
  7. 共享内存: 共享内存是一种允许多个核心共享相同物理内存区域的机制。通过共享内存,不同核心可以直接访问同一块内存,从而实现数据的共享和同步。

这些方法通常会根据具体的应用场景和性能需求进行选择和组合,以实现高效的数据同步和一致性保证。

6. 缓存缩略
  • Write Through(写透方式): 每次写操作会同步到内存和缓存,可以确保数据一致性,但写操作的性能较低,因为需要等待数据写入到内存后才能继续执行。
  • Write Back(写回方式): 写操作首先写入到缓存,然后根据一定策略(如LRU)将数据写回到内存。写回方式可以提高写操作的性能,但可能会导致缓存和内存数据不一致的情况发生。
  • No Write Allocate(非写分配方式): 写操作直接写入到内存,不会分配缓存行。这种方式可以减少缓存污染,但会增加内存访问次数,影响性能。
  • Read Allocate(读分配方式): 当读操作发生时,如果数据不在缓存中,会将数据加载到缓存中。这种方式可以提高读操作的性能,但会增加缓存的占用。

2. Cache简介

1. 简介

M7内核芯片添加了一级缓存支持,这是个很不错的改进。一级缓存通常用于存储最频繁使用的数据和指令,以提高处理器的性能和效率。这种设计利用了局部性原理,即程序倾向于访问相邻的内存位置。D-Cache用于存储数据,而I-Cache则用于存储指令,这样可以更好地优化数据和指令的访问。这种优化可以显著提高处理器的运行速度,尤其是对于需要频繁访问内存的任务来说。

  1. 数据缓存(D-Cache)

    • 数据缓存主要用于存储程序执行期间使用的数据。当处理器需要访问内存中的某个数据时,它首先会检查数据缓存。如果数据在缓存中已经存在(即命中),处理器就可以直接从缓存中读取数据,而不必访问主内存。如果数据不在缓存中(即缓存未命中),处理器就需要从主内存中读取数据,并将其加载到缓存中,以便日后的访问。
  2. 指令缓存(I-Cache)

    • 指令缓存主要用于存储程序的指令,即处理器执行的操作序列。当处理器需要执行某个指令时,它会从指令缓存中获取指令。如果指令在缓存中已经存在,处理器就可以直接执行它,而不必从主内存中读取。如果指令不在缓存中,处理器就需要从主内存中读取指令,并将其加载到缓存中以供后续执行。
2. 读操作与写操作

在这里插入图片描述

关键点:

  • 命中(Cache Hit):当处理器需要访问的数据或指令在缓存中存在时,发生命中。
  • 未命中(Cache Miss):当处理器需要访问的数据或指令在缓存中不存在时,发生未命中,处理器需要从主内存中读取数据或指令。
  • 替换策略:当缓存已满且需要将新的数据或指令加载到缓存中时,缓存控制器会根据一定的替换策略选择要替换的缓存行,常见的替换策略包括最近最少使用(LRU)和先进先出(FIFO)等。
  • 写策略:数据缓存通常有写回(Write-Back)和写直通(Write-Through)两种写策略,写回会先在缓存中更新数据,然后在某个时机将更新的数据写回主内存,而写直通则会同时更新缓存和主内存。
  • 一致性:缓存一致性是指处理器对缓存中的数据进行更新时,要确保所有缓存中的副本都得到相应的更新,以保证数据的一致性。

通过使用数据缓存和指令缓存,处理器可以显著提高程序的执行速度,因为缓存可以提供比主内存更快的访问速度,减少了处理器访问主内存的频率,从而减少了存储系统的瓶颈。

3. 内核基本操作

在STM32系列的M7内核中,Cache支持以下基本操作:

  1. Invalidate(无效化缓存):
    • 无效化操作用于使缓存中的特定数据失效。这通常在其他系统组件(如DMA控制器)更新了内存中的数据时使用,以确保处理器从内存中获取最新数据而不是使用缓存中的旧数据。
    • 在STM32的M7内核中,可以通过无效化指令来无效化整个缓存或特定的缓存行。无效化操作不会将缓存中的数据写回到主内存,只是标记为无效。
  2. Clean(清理缓存):
    • 清理操作将缓存中的脏数据(已修改但尚未写回主内存的数据)写回到主内存,但不使缓存行无效。这有助于确保主内存中的数据是最新的,但缓存行仍然保持有效状态。
    • 该操作可以通过清理指令实现,可以针对整个缓存或特定的缓存行进行清理。
  3. Clean and Invalidate(清理并无效化缓存):
    • 这是清理和无效化的组合操作。它将缓存中的脏数据写回到主内存,然后使这些缓存行无效。此操作确保数据一致性,并在不需要保留缓存数据时使用。
    • 该操作可以通过清理并无效化指令进行,可以针对整个缓存或特定的缓存行进行。
  4. Enable/Disable(启用/禁用缓存):
    • 启用操作会打开缓存,使其开始工作并缓存数据和指令。禁用操作会关闭缓存,处理器将直接访问主内存而不经过缓存。
    • 启用和禁用缓存通常通过设置缓存控制寄存器(如CM7的控制寄存器)来实现。
4. 内核读取cache

在这里插入图片描述

Cache Hit (命中)

当 CPU 需要读取的数据位于 Cache 中,并且 Cache 中已经加载了该数据时,发生 Cache Hit。
在 Cache Hit 的情况下,CPU 可以直接从 Cache 中读取数据,而无需访问主存,这样可以大大提高读取速度。
Cache Miss (未命中):

当 CPU 需要读取的数据位于主存中,而不在 Cache 中时,发生 Cache Miss。
在 Cache Miss 的情况下,需要从主存中加载数据到 Cache,以便 CPU 可以读取。

有两种常见的处理方式:
Read Through (直接读取): CPU 直接从主存中读取数据,而不将数据加载到 Cache 中。这样做可以确保数据的一致性,但可能会降低读取速度,因为需要额外的主存访问。
Read Allocate (读取分配): CPU 将缺失的数据加载到 Cache 中,然后再从 Cache 中读取数据。这样做可以提高读取速度,因为之后的读取可以直接从 Cache 中进行,但可能会导致 Cache 中的其他数据被替换掉。

5. 内核写入cache

在这里插入图片描述

在STM32系列的M7内核中,缓存(Cache)支持多种操作方式,这些操作方式在缓存命中(Cache Hit)和未命中(Cache Miss)的情况下会有所不同。具体而言,可以根据不同的写策略来处理写操作。这些策略包括写透(Write Through)和写回(Write Back)策略,以及在缓存未命中的情况下的写分配(Write Allocate)和非写分配(No Write Allocate)策略。

Cache Hit(命中)操作

  1. Write Through(写透):

    • 操作: 在缓存命中的情况下,数据同时写入到缓存和内存。
    • 优点: 保持内存和缓存的一致性,确保内存中的数据始终是最新的。
    • 缺点: 写操作速度较慢,因为每次写操作都需要同时更新内存。
  2. Write Back(写回):

    • 操作: 在缓存命中的情况下,数据仅写入缓存,而不立即写入内存。只有当缓存行被替换或被明确要求写回时,才会将数据写入内存。
    • 优点: 提高写操作的速度,因为写操作只需更新缓存,不需要立即访问内存。
    • 缺点: 需要额外的机制来确保缓存和内存之间的一致性,这可能增加系统的复杂度。

Cache Miss(未命中)操作

  1. Write Allocate(写分配):

    • 操作: 在缓存未命中的情况下,将所需的数据块从内存加载到缓存,然后再执行写入操作。
    • 优点: 未来对同一数据块的写操作可以在缓存中直接进行,提高后续的写入速度。
    • 缺点: 可能会增加第一次写入的延迟,因为需要先从内存加载数据到缓存。
  2. No Write Allocate(非写分配):

    • 操作: 在缓存未命中的情况下,直接将数据写入内存,而不将数据块加载到缓存。
    • 优点: 避免了加载数据块到缓存的额外开销,适用于写操作频率较低的情况。
    • 缺点: 未来对同一数据块的写操作仍需直接访问内存,可能会降低后续写入操作的速度。
6. 数据不一致解决办法

解决数据不一致性问题的两种主要方法分别是设置共享属性软件进行缓存维护。下面是对这两种方法的详细分析:

  1. 设置共享属性

    • 优点

      • 简单直接: 通过将内存区域设置为共享属性,可以确保所有核心或外设都能够看到最新的数据。这个方法通过硬件机制保证数据一致性,而不需要额外的软件干预。

      • 保证一致性: 所有访问该内存区域的操作都会直接访问主存,从而确保数据的一致性。

    • 缺点

      • 性能下降: Cache相当于没有开启,因为所有的读写操作直接访问内存,无法利用Cache提升访问速度。特别是在频繁访问内存的场景下,性能可能会显著下降。
      • 浪费Cache资源: Cache的优势无法得到发挥,这在处理器设计中是一个浪费,因为Cache本来就是为了提升性能而设计的。
  2. 软件进行Cache维护

    2.1 Clean(清空

    • 优点

      • 数据一致性: 在数据被修改后,通过Clean操作将Cache中的相应数据写回内存,可以确保内存中的数据与Cache中的数据保持一致。这在需要确保数据一致性的关键操作中非常有用。

      • 保持性能: Clean操作后,Cache依然可以继续使用,之后的访问仍然可以从Cache中受益。

    • 缺点

      • 额外复杂性: 需要额外的软件操作来维护Cache,增加了系统的复杂性。

      • 性能损失: Clean操作会增加额外的开销,特别是在频繁执行的情况下,可能导致性能损失。

    2.2 Invalidate(无效化)

    • 优点

      • 数据一致性: 当内存中的数据被修改但Cache中的数据尚未更新时,通过Invalidate操作使Cache中的数据无效,以便从内存中重新加载最新的数据,从而确保数据一致性。

      • 保持性能: 在Invalidate操作之后,Cache依然可以继续使用,后续访问仍然可以从Cache中受益。

    • 缺点

      • 额外复杂性: 需要额外的软件操作来维护Cache,增加了系统的复杂性。
      • 性能损失: Invalidate操作会增加额外的开销,特别是在频繁执行的情况下,可能导致性能损失。

    总结

    设置共享属性是一种简单直接的方法,通过硬件机制确保数据一致性,但无法利用Cache的性能提升优势,适用于需要绝对数据一致性且对性能要求不高的场景。

    软件进行Cache维护则允许在使用Cache的同时,通过Clean和Invalidate操作来确保数据一致性。虽然增加了系统的复杂性和一些性能开销,但在需要高性能且仍需确保数据一致性的场景中,这是更为灵活和高效的方法。

    选择哪种方法取决于具体的应用场景和系统要求。如果数据一致性至关重要且对性能要求较低,可以选择设置共享属性。如果需要高性能且可以接受一定的复杂性,则可以选择通过软件进行Cache维护。

7. Cache配置相关函数

I-Cache 相关函数

SCB_EnableICache: 启用指令缓存(I-Cache) ;

SCB_DisableICache: 禁用指令缓存(I-Cache) ;

SCB_InvalidateICache: 使指令缓存(I-Cache)无效,将其中的所有条目标记为无效 ;

D-Cache 相关函数

SCB_EnableDCache: 启用数据缓存(D-Cache) ;

SCB_DisableDCache: 禁用数据缓存(D-Cache) ;

SCB_InvalidateDCache: 使数据缓存(D-Cache)无效,将其中的所有条目标记为无效 ;

SCB_CleanDCache: 清理数据缓存(D-Cache),将其中的已更改数据写回到主存中 ;

SCB_CleanInvalidateDCache: 清理并使数据缓存(D-Cache)无效,即执行清理和使无效两个操作 ;

SCB_InvalidateDCache_by_addr: 通过地址范围使数据缓存(D-Cache)无效 ;

SCB_CleanDCache_by_addr: 通过地址范围清理数据缓存(D-Cache) ;

SCB_CleanInvalidateDCache_by_add: 通过地址范围清理并使数据缓存(D-Cache)无效。

3. MPU相关寄存器

寄存器名称功能
MPU_TYPEMPU类型寄存器用于指明MPU的一些特征 (是否支持MPU、支持多少个Region)
MPU_CTRLMPU控制寄存器设置MPU使能
MPU_RNRMPU区域编号寄存器用于选择下一个要配置的region
MPU_RBARMPU基地址寄存器用于设置区域的起始地址
MPU_RASRMPU区域属性和容量寄存器用于设置每个区域的属性
  • MPU_TYPE 寄存器
    在这里插入图片描述

  • MPU_CTRL 寄存器
    在这里插入图片描述

  • MPU_RNR 寄存器
    在这里插入图片描述

  • MPU_RBAR 寄存器
    在这里插入图片描述

  • MPU_RASR 寄存器
    在这里插入图片描述

    • AP 相关位控制数据的访问权限
      在这里插入图片描述

    • TEX用来设置Cache策略
      在这里插入图片描述

      在STM32系列的M7内核中,缓存策略的选择对于性能优化和数据一致性维护非常重要。不同的缓存策略有不同的特性和应用场景。以下是对几种常见缓存策略的详细说明:

      1. Non-cacheable(非缓存)
      • 描述: 数据不会被缓存在缓存中,每次访问都会直接读取或写入主存。
      • 特性:
        • 每次访问主存: 每次数据访问都是直接与主存进行,不经过缓存。
        • 数据一致性: 保证数据一致性,因为所有数据访问都是直接访问主存。
        • 性能: 可能较低,因为每次访问都要经过较慢的主存。
      • 适用场景: 需要绝对数据一致性且对性能要求不高的场景。
      1. Write-through, read-allocated, no-write-allocate(写透传,读分配,不写分配)
      • 描述:
        • 写操作: 直接写入主存,并通过缓存透传到主存。
        • 读操作: 在缓存中未命中时会分配缓存。
      • 特性:
        • 写操作: 每次写操作都会直接写入主存,保证内存与缓存数据一致。
        • 读操作: 在缓存未命中时,会从主存加载数据到缓存中。
        • 性能: 写操作性能较低,因为每次写都需要访问主存;读操作性能较好,命中缓存时可以快速读取。
      • 适用场景: 需要高读性能,同时需要确保写操作数据一致性的场景。
      1. Write-back, read-allocated, no-write-allocate(写回,读分配,不写分配)
      • 描述:
        • 写操作: 先写入缓存,并标记为脏数据,如果缓存中有相应数据块。
        • 读操作: 在缓存未命中时会分配缓存。
      • 特性:
        • 写操作: 写入缓存并标记为脏数据,提高写操作的性能,不立即写入主存。
        • 读操作: 在缓存未命中时,从主存加载数据到缓存中。
        • 性能: 写操作性能高,因为多数写操作在缓存中完成;读操作性能也高,未命中时会分配缓存。
      • 适用场景: 写操作频繁且对写操作性能有较高要求的场景。
      1. Write-back, read-allocated, write-allocate(写回,读分配,写分配)
      • 描述:
        • 写操作: 先写入缓存并标记为脏数据,如果缓存中有相应数据块;如果没有,则分配缓存并写入。
        • 读操作: 在缓存未命中时会分配缓存。
      • 特性:
        • 写操作: 写入缓存并标记为脏数据,未命中时分配缓存,确保缓存命中率。
        • 读操作: 在缓存未命中时,从主存加载数据到缓存中。
        • 性能: 写操作和读操作性能都很高,因为缓存可以容纳更多的数据块,减少主存访问。
      • 适用场景: 读写操作都频繁且对性能有较高要求的场景。

      总结

      选择适当的缓存策略取决于应用的具体需求:

      • Non-cacheable: 适用于需要绝对数据一致性且性能要求不高的场景。
      • Write-through, read-allocated, no-write-allocate: 适用于需要高读性能且写操作需要确保数据一致性的场景。
      • Write-back, read-allocated, no-write-allocate: 适用于写操作频繁且对写操作性能有较高要求的场景。
      • Write-back, read-allocated, write-allocate: 适用于读写操作都频繁且对性能有较高要求的场景。

4. MPU相关HAL库驱动

驱动函数关联寄存器功能描述
HAL_MPU_Enable(…)CTRLMPU使能
HAL_MPU_Disable(…)CTRLMPU失能
HAL_MPU_ConfigRegion(…)RASR\RBAR\RNR配置MPU参数

5. MPU基本配置步骤

  1. 禁止MPU

    void HAL_MPU_Disable();
    
  2. 配置某个区域的MPU保护参数

    void HAL_MPU_ConfigRegion()
    
  3. 使能MPU

    void HAL_MPU_Enable();
    
  4. 编写MemManage中断服务函数

    void MemManage_Handler(void)
    

MPU基本配置步骤

  1. 禁用MPU

    • 在配置MPU之前,确保MPU处于禁用状态。

      MPU->CTRL &= ~MPU_CTRL_ENABLE_Msk;
      
  2. 配置MPU区域

    • MPU支持多个保护区域,每个区域可以单独配置。以下是配置MPU区域的步骤:

    • 选择区域

      MPU->RNR = region_number; // 选择要配置的区域编号
      
    • 配置区域基地址

      MPU->RBAR = base_address & MPU_RBAR_ADDR_Msk; // 设置区域基地址
      
    • 配置区域属性

      • 访问权限(AP)
      • 缓存策略
      • 共享属性
      • 子区域禁用
      MPU->RASR = (attribute << MPU_RASR_AP_Pos)   |  // 访问权限(cache_policy << MPU_RASR_C_Pos) |  // 缓存策略(shareable << MPU_RASR_S_Pos)    |  // 共享属性(subregion << MPU_RASR_SRD_Pos)  |  // 子区域禁用(size << MPU_RASR_SIZE_Pos)      |  // 区域大小MPU_RASR_ENABLE_Msk;              // 使能区域
      
  3. 使能MPU

    • 在所有区域配置完成后,启用MPU。

    • 启用背景区域(可选)

      MPU->CTRL |= MPU_CTRL_PRIVDEFENA_Msk; // 启用默认背景区域(可选)
      
    • 启用MPU

        MPU->CTRL |= MPU_CTRL_ENABLE_Msk; // 启用MPU
      
  4. 刷新指令和数据缓存

    • 在配置完成并启用MPU后,刷新指令和数据缓存以确保所有更改生效。

      __DSB();
      __ISB();
      

6. 代码实现

  • 设置某个区域的MPU保护函数

    uint8_t mpu_set_protection(uint32_t baseaddr, uint32_t size, uint32_t rnum, uint8_t de, uint8_t ap, uint8_t sen, uint8_t cen, uint8_t ben)
    {MPU_Region_InitTypeDef mpu_region_init_handle;           /* MPU初始化句柄 */HAL_MPU_Disable();                                       /* 配置MPU之前先关闭MPU,配置完成以后在使能MPU */mpu_region_init_handle.Enable = MPU_REGION_ENABLE;       /* 使能该保护区域 */mpu_region_init_handle.Number = rnum;                    /* 设置保护区域 */mpu_region_init_handle.BaseAddress = baseaddr;           /* 设置基址 */mpu_region_init_handle.Size = size;                      /* 设置保护区域大小 */mpu_region_init_handle.SubRegionDisable = 0X00;          /* 禁止子区域 */mpu_region_init_handle.TypeExtField = MPU_TEX_LEVEL0;    /* 设置类型扩展域为level0 */mpu_region_init_handle.AccessPermission = ap;            /* 设置访问权限 */mpu_region_init_handle.DisableExec = de;                 /* 是否允许指令访问 */mpu_region_init_handle.IsShareable = sen;                /* 是否允许共用 */mpu_region_init_handle.IsCacheable = cen;                /* 是否允许cache */mpu_region_init_handle.IsBufferable = ben;               /* 是否允许缓冲 */HAL_MPU_ConfigRegion(&mpu_region_init_handle);           /* 配置MPU */HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);                  /* 开启MPU */return 0;
    }
    
  • 设置需要保护的存储块函数

    void mpu_memory_protection(void)
    {/* 保护整个DTCM,共128K字节,允许指令访问,禁止共用,允许cache,允许缓冲 */mpu_set_protection(0x20000000, MPU_REGION_SIZE_128KB, MPU_REGION_NUMBER1, MPU_INSTRUCTION_ACCESS_ENABLE,MPU_REGION_FULL_ACCESS, MPU_ACCESS_NOT_SHAREABLE, MPU_ACCESS_CACHEABLE, MPU_ACCESS_BUFFERABLE);/* 保护整个AXI SRAM,共512K字节,允许指令访问,禁止共用,允许cache,允许缓冲 */mpu_set_protection(0x24000000, MPU_REGION_SIZE_512KB,MPU_REGION_NUMBER2, MPU_INSTRUCTION_ACCESS_ENABLE,MPU_REGION_FULL_ACCESS, MPU_ACCESS_NOT_SHAREABLE, MPU_ACCESS_CACHEABLE, MPU_ACCESS_BUFFERABLE);/* 保护整个SRAM1~SRAM3,共288K字节,允许指令访问,禁止共用,允许cache,允许缓冲 */mpu_set_protection(0x30000000, MPU_REGION_SIZE_512KB,MPU_REGION_NUMBER3, MPU_INSTRUCTION_ACCESS_ENABLE,MPU_REGION_FULL_ACCESS, MPU_ACCESS_NOT_SHAREABLE, MPU_ACCESS_CACHEABLE, MPU_ACCESS_BUFFERABLE);/* 保护整个SRAM4,共64K字节,允许指令访问,禁止共用,允许cache,允许缓冲 */mpu_set_protection(0x38000000, MPU_REGION_SIZE_64KB, MPU_REGION_NUMBER4, MPU_INSTRUCTION_ACCESS_ENABLE,MPU_REGION_FULL_ACCESS, MPU_ACCESS_NOT_SHAREABLE, MPU_ACCESS_CACHEABLE, MPU_ACCESS_BUFFERABLE);/* 保护MCU LCD屏所在的FMC区域,,共64M字节,允许指令访问,禁止共用,禁止cache,禁止缓冲 */mpu_set_protection(0x60000000, MPU_REGION_SIZE_64MB, MPU_REGION_NUMBER5, MPU_INSTRUCTION_ACCESS_ENABLE,MPU_REGION_FULL_ACCESS, MPU_ACCESS_NOT_SHAREABLE, MPU_ACCESS_NOT_CACHEABLE, MPU_ACCESS_NOT_BUFFERABLE);/* 保护SDRAM区域,共64M字节,允许指令访问,禁止共用,允许cache,允许缓冲 */mpu_set_protection(0XC0000000, MPU_REGION_SIZE_64MB, MPU_REGION_NUMBER6, MPU_INSTRUCTION_ACCESS_ENABLE,MPU_REGION_FULL_ACCESS, MPU_ACCESS_NOT_SHAREABLE, MPU_ACCESS_CACHEABLE, MPU_ACCESS_BUFFERABLE);/* 保护整个NAND FLASH区域,共256M字节,禁止指令访问,禁止共用,禁止cache,禁止缓冲 */mpu_set_protection(0X80000000, MPU_REGION_SIZE_256MB, MPU_REGION_NUMBER7, MPU_INSTRUCTION_ACCESS_DISABLE,MPU_REGION_FULL_ACCESS, MPU_ACCESS_NOT_SHAREABLE, MPU_ACCESS_NOT_CACHEABLE, MPU_ACCESS_NOT_BUFFERABLE);
    }
    
  • MemManage错误处理中断

    void MemManage_Handler(void)
    {LED1(0);                            /* 点亮LED1(GREEN LED) */printf("Mem Access Error!!\r\n");   /* 输出错误信息 */delay_ms(1000);printf("Soft Reseting...\r\n");     /* 提示软件重启 */delay_ms(1000);NVIC_SystemReset();                 /* 软复位 */
    }
    
  • 主函数

    #include "stdlib.h"
    #include "./SYSTEM/sys/sys.h"
    #include "./SYSTEM/usart/usart.h"
    #include "./SYSTEM/delay/delay.h"
    #include "./BSP/LED/led.h"
    #include "./BSP/KEY/key.h"
    #include "./BSP/MPU/mpu.h"#if !(__ARMCC_VERSION >= 6010050)   /* 不是AC6编译器,即使用AC5编译器时 */
    uint8_t mpudata[128] __attribute__((at(0X20002000)));  /* 定义一个数组 */
    #else
    uint8_t mpudata[128] __attribute__((section(".bss.ARM.__at_0X20002000"))); /* 定义一个数组 */
    #endifint main(void)
    {uint8_t key = 0;uint8_t t = 0; sys_cache_enable();                 /* 打开L1-Cache */HAL_Init();                         /* 初始化HAL库 */sys_stm32_clock_init(240, 2, 2, 4); /* 设置时钟, 480Mhz */delay_init(480);                    /* 延时初始化 */usart_init(115200);                 /* 串口初始化为115200 */led_init();                         /* 初始化LED */key_init();                         /* 初始化按键 */printf("\r\n\r\nMPU closed!\r\n");  /* 提示MPU关闭 */mpu_memory_protection();while (1){key = key_scan(0);if (key == WKUP_PRES)           /* 使能MPU保护数组 mpudata */{mpu_set_protection(0X20002000, MPU_REGION_SIZE_128B, MPU_REGION_NUMBER0, MPU_INSTRUCTION_ACCESS_ENABLE, MPU_REGION_PRIV_RO_URO,MPU_ACCESS_NOT_SHAREABLE, MPU_ACCESS_NOT_CACHEABLE, MPU_ACCESS_BUFFERABLE);  /* 只读,禁止共用,禁止cache,允许缓冲 */printf("MPU open!\r\n");    /* 提示MPU打开 */}else if (key == KEY0_PRES)      /* 向数组中写入数据,如果开启了MPU保护的话会进入内存访问错误! */{printf("Start Writing data...\r\n");sprintf((char *)mpudata, "MPU test array %d", t);printf("Data Write finshed!\r\n");}else if (key == KEY1_PRES)      /* 从数组中读取数据,不管有没有开启MPU保护都不会进入内存访问错误! */{printf("Array data is:%s\r\n", mpudata);}else {delay_ms(10);}t++;if ((t % 50) == 0) {LED0_TOGGLE();      /* LED0取反 */}}
    }
    

声明:资料来源(战舰STM32F103ZET6开发板资源包)

  1. Cortex-M3权威指南(中文).pdf
  2. STM32F10xxx参考手册_V10(中文版).pdf
  3. STM32F103 战舰开发指南V1.3.pdf
  4. STM32F103ZET6(中文版).pdf
  5. 战舰V4 硬件参考手册_V1.0.pdf

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

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

相关文章

交换机的三层交换技术

现有pc1与pc2不在同一个网段之下&#xff0c;通过交换机相连接。 进人交换机1&#xff0c;创建两个vlan 10和vlan 20 &#xff0c;进入串口2设置串口模式为access&#xff0c;并且设置默认vlan为10.进入串口3设置串口模式为access&#xff0c;并且设置默认vlan为20. 进入串口1…

深度解析搜索引擎广告(SEM)与社交媒体广告(SMM):NetFarmer助力企业数字化出海

在当今数字化时代&#xff0c;企业出海已经成为了一个必然趋势。然而&#xff0c;如何有效地在海外市场中推广品牌、吸引潜在客户&#xff0c;成为了众多企业面临的重要挑战。搜索引擎广告&#xff08;SEM&#xff09;和社交媒体广告&#xff08;SMM&#xff09;作为两种主要的…

如何下载b站(哔哩哔哩bilibili)的学习视频教程

方法1&#xff1a; 打开粘贴视频链接下载即可哔哩哔哩(bilibili)视频解析下载 - 保存B站视频到手机、电脑哔哩哔哩高清视频解析下载工具是一个免费的B站视频在线解析提取工具,支持提取B站APP和bilibili网站上的任何视频,提取出来的视频无水印.我们可以借助此下载器方便地将视频…

最大公约数和最小公倍数(函数)(C语言)

一、运行结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>//声明函数&#xff1b; //最大公约数&#xff1b; int greatdivisor(int x, int y);//最小公倍数&#xff1b; int leastmultiple(int x, int y);int main() {/…

618精选编程书单:学好代码是用好大模型的基础

大家好&#xff0c;我是爱编程的喵喵。双985硕士毕业&#xff0c;现担任全栈工程师一职&#xff0c;热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。…

如何选择适合自己需求的云服务器

最近明月接了一个跨境电商的代维业务&#xff0c;发现他们的云服务器很有代表性&#xff0c;今天就以此为例给大家分享一下应该如何选择适合自己需求的云服务器。像明月这样专做代维业务的可以说什么云服务器都体验过了&#xff0c;也发现大家在选择自己的云服务器的时候有很大…

加密资产私钥安全完整手册(一) ,bitget钱包为例

比特币和以太坊等加密货币的兴起开创了数字金融的新时代&#xff0c;但也带来了独特的安全挑战。这些代表现实世界价值的数字资产已成为黑客和窃贼的主要目标。为了安全地应对这种情况&#xff0c;了解私钥的基本概念至关重要。 私钥是加密货币所有权和安全性的基石。它们相当于…

VSCode小技巧,忽略不想格式化的代码行

零&#xff0e;格式化工具文档 1 . Black Ignoring sections功能 2 . autopep8 disabling-line-by-line功能&#xff1b;&#xff1b;–line-range选项 3 . Prettier prettier-ignore功能(例&#xff1a;适用于JS的// prettier-ignore&#xff0c;适用于CSS的/* prettier-igno…

HTML新春烟花盛宴

目录 写在前面 烟花盛宴 完整代码 修改文字

三步问题 | 动态规划

1.三步问题 题目连接&#xff1a;面试题 08.01. 三步问题 三步问题。有个小孩正在上楼梯&#xff0c;楼梯有n阶台阶&#xff0c;小孩一次可以上1阶、2阶或3阶。实现一种方法&#xff0c;计算小孩有多少种上楼梯的方式。结果可能很大&#xff0c;你需要对结果模1000000007。 2…

C语言 指针——指针变量的定义、初始化及解引用

目录 指针 内存如何编址&#xff1f; 如何对变量进行寻址&#xff1f; 用什么类型的变量来存放变量的地址? 如何显示变量的地址?​编辑 使用未初始化的指针会怎样&#xff1f; NULL是什么&#xff1f; 如何访问指针变量指向的存储单元中的数据&#xff1f; 指针变量的…

APP原生开发与框架开发的优劣势

电话管家APP商用也有几年时间了&#xff0c;但是客户一直都有遇到一些问题。 为什么我们的APP老是要升级&#xff1f; 为什么有些手机使用体验不好&#xff1f; 为什么有些公司的APP几天就开发出来上线了&#xff1f; 为什么有些公司的APP那么便宜&#xff1f; 今天就来从…

家政预约小程序08服务详情

目录 1 创建页面2 创建URL参数3 配置数据详情组件4 从分类页跳转到详情页5 搭建详情页总结 现在我们的小程序已经在首页和分类页展示了服务的列表信息&#xff0c;当用户点击具体的内容的时候需要打开详情页&#xff0c;本篇介绍一下详情页的开发。 1 创建页面 打开应用编辑器…

中学生学人工智能系列:如何用AI学英语

经常有读者朋友给公众号《人工智能怎么学》留言咨询如何使用人工智能学习语文、数学、英语等科目。这些都是中学教师、中学生朋友及其家长们普遍关注的问题。仅仅使用留言回复的方式&#xff0c;不可能对这些问题做出具体和透彻的解答&#xff0c;因此本公众号近期将推出中学生…

如何在phpMy管理对Joomla后台的登录密码进行重置

本周有一个客户&#xff0c;购买Hostease的虚拟主机&#xff0c;询问我们的在线客服&#xff0c;如何在phpMy管理对Joomla后台的登录密码进行重置&#xff1f;我们为用户提供相关教程&#xff0c;用户很快解决了遇到的问题。在此&#xff0c;我们分享这个操作教程&#xff0c;希…

LeetCode题练习与总结:平衡二叉树--110

一、题目描述 给定一个二叉树&#xff0c;判断它是否是平衡二叉树。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;true示例 2&#xff1a; 输入&#xff1a;root [1,2,2,3,3,null,null,4,4] 输出&#xff1a;false示例 3&#xff1a…

【Java用法】java中计算两个时间差

java中计算两个时间差 不多说&#xff0c;直接上代码&#xff0c;可自行查看示例 package org.example.calc;import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit;public class MinusTest {public static void…

97.网络游戏逆向分析与漏洞攻防-ui界面的设计-通过逆向分析确认角色信息

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果&#xff0c;代码看不懂是正常的&#xff0c;只要会抄就行&#xff0c;抄着抄着就能懂了 内容…

分享几个PHP的webshell免杀思路

前言&#xff1a;网上的免杀思路有不少&#xff0c;不过大部分是基于混淆和加密的&#xff0c;我这里分享两个基于匿名函数、变量覆盖和反序列化的webshell思路&#xff0c;思路来源于深信服EDR的RCE漏洞。 ps&#xff1a;远程获取的时候&#xff0c;其实也可以用fopen读取远程…

WAMP无法启动mysql

一种原因是原来安装过mysql,mysql默认是自启动的&#xff0c;而WAMP内置mysql会发生冲突&#xff0c;所以 解决方法&#xff1a; winR 输入 services.msc 将mysql关闭&#xff0c;并设为手动模式