【嵌入式八股14】RTOS

一、RT-Thread 内存管理算法

RT-Thread 通过开辟静态数组的方式来管理内存,以下是具体的定义:

#define RT_HEAP_SIZE 6*1024
/* 从内部 SRAM 申请一块静态内存来作为内存堆使用 */
static uint32_t rt_heap[RT_HEAP_SIZE];	// heap default size: 24K(1024 * 4 * 6)

RT-Thread 提供了多种内存管理算法,适用于不同的内存场景,具体如下:

算法 对应文件 适用场景 形象说明
mem 小内存 mem.c 适用于 2MB 以内的小内存设备 如同一个瓜,根据需求吃多少切多少,灵活分配小内存
slab 大内存 slab.c 适用于大内存设备,采用内存池管理方式 类似一个已经切好固定大小块的瓜,需要时直接拿对应的块,提高分配效率
memheap 多内存 memheap.c 可对多个内存设备进行合并管理 好比多个瓜,吃完一个接着拿下一个,实现不同内存区域的统一调配
  1. mem 小内存管理算法(类似 heap_4)
    • 数据结构:采用链表组织内存块,每个链表表项包含 {magic(用于判断是否被非法改写), used(标识是否被使用), next(指针域,指向下一个表项), prev(指针域,指向前一个表项)}。
    • 内存分配操作:以分配 64 Byte 内存为例,从链表表头开始遍历,寻找可用的内存空间进行分配(需要注意表头本身占用 3*4 Byte 的空间)。
    • 内存释放操作:释放内存时,更改 used 表项的状态,并检查前后相邻的内存块是否为空闲状态。若有相邻空闲块,则将它们合并为一个大的内存块,以提高内存利用率。
  2. slab 大内存管理算法(内存池):为避免频繁的内存分配和释放操作带来的性能开销,该算法提前将内存划分成固定大小的块,形成内存池。当需要使用内存时,直接从内存池中获取相应的内存块;使用完毕后,将内存块归还到内存池中,从而实现高效的内存管理。
  3. memheap 内存管理算法(类似 heap_5):此算法能够将多个不连续的内存地址进行合并拼接,实现对不同内存区域的统一管理和使用。这对于一些具有分散内存资源的嵌入式系统来说,能够有效地提高内存的使用效率和灵活性。

二、RT-Thread 链表

  1. 链表类型对比
    • 普通双向循环链表:通常针对每一个数据结构固定的节点进行操作,节点的数据类型和结构在链表创建时就已经确定,灵活性相对较低。
    • RTT 中双向循环链表:数据结构不固定,其指针域指向下一个指针域,这使得插入的元素可以为不同类型,大大提高了链表的通用性和灵活性。
  2. 链表操作函数
    • 指定节点前插入
rt_inline void rt_list_insert_before(rt_list_t *l, rt_list_t *n)
{
    l->prev->next = n;
    n->prev = l->prev;

    l->prev = n;
    n->next = l;
}
- **指定节点后插入**:
rt_inline void rt_list_insert_after(rt_list_t *l, rt_list_t *n)
{
    l->next->prev = n;
    n->next = l->next;

    l->next = n;
    n->prev = l;
}
- **删除节点**:
rt_inline void rt_list_remove(rt_list_t *n)
{
    n->next->prev = n->prev;
    n->prev->next = n->next;

    n->next = n->prev = n;
}
  1. 节点元素的访问:由于在 RTT 链表的节点中,指针域的存放位置不确定,因此需要通过一种宏定义来从指针域寻找对应的结构体元素。具体来说,就是通过 rt_list_t 成员的地址来访问节点中的其他元素。虽然不同类型节点中 rt_list_t 成员的位置各不相同,但在确定类型的节点中,rt_list_t 成员的偏移是固定的。在获取 rt_list_t 成员地址的情况下,可以通过计算 (rt_list_t 成员地址) - (rt_list_t 成员偏移)得到节点的起始地址。RT-Thread 中提供了相应的算法和宏定义 rt_container_of 来实现这一功能:
/**
 * Double List structure
 */
struct rt_list_node
{
    struct rt_list_node *next;                          /**< point to next node. */
    struct rt_list_node *prev;                          /**< point to prev node. */
};
typedef struct rt_list_node rt_list_t;                  /**< Type for lists. */
struct rt_thread
{
    char        name[RT_NAME_MAX];                      /**< the name of thread */
    rt_list_t   list;                                   /**< the object list */
    rt_list_t   tlist;                                  /**< the thread list */
    rt_uint8_t  current_priority;                       /**< current priority */
    rt_uint8_t  init_priority;                          /**< initialized priority */
};
typedef struct rt_thread *rt_thread_t;

#define rt_container_of(ptr, type, member) \
    ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))
//ptr: 成员首地址(指针域地址,例如 rt_thread_priority_table[highest_ready_priority].next)
//type: 结构体类型(例如 struct rt_thread)
//member: 结构体成员名称(例如 tlist)

三、RT-Thread 抢占式调度实现

假设存在两个线程,低优先级的 t2 任务在 while(1) 循环中执行耗时任务,高优先级的 t1 任务则进行抢占式打印操作,随后进入阻塞状态。调度器的具体执行顺序如下:

  1. 高优先级任务 t1 首先执行,当执行到 rt_thread_mdelay() 函数时,该函数会调用 rt_thread_sleep() 中的 rt_schedule() 函数,将 t1 任务挂起。
  2. 调度器介入,通过特定的算法寻找到当前最高优先级的任务(此时为 t2 任务),并让其运行。
  3. 在低优先级任务 t2 的时间片未到期的情况下,由于高优先级任务 t1rt_thread_mdelay() 超时,其定时计数器会发生相应的变化。
  4. 当下一个节拍周期到达时,会定时执行 rt_tick_increase() 函数,该函数会调用 rt_timer_check() 中的 timeout_func() 函数。
  5. 通过函数指针跳转到 rt_thread_timeout() 函数,在该函数中执行 rt_schedule() 函数,触发任务调度。
  6. 进入 PendSV 中断处理函数,在该函数中进行线程的上下文切换,将 CPU 的控制权交给下一个任务。

四、FreeRTOS 内存管理

  1. 内存位置:FreeRTOS 的内存位于 .bss 段,而不是传统意义上的 heap(即启动文件中所定义的堆空间大小)。当使用 pvPortMalloc 函数申请内存时,实际上是从这个系统堆(位于 .bss 段)中进行申请的。
#define configTOTAL_HEAP_SIZE                        ( ( size_t ) ( 100 * 1024 )   // 申请 100KB 内存用于 RTOS 系统堆内存
  1. 内存管理策略示例:以 heap_4.c 内存管理策略为例,在 map 文件中可以看到 FreeRTOS 使用一个静态数组作为 HEAP,该数组定义在 heap_4.c 文件中。由于这个 HEAP 来自于静态数组,所以它存在于数据段(具体为 .bss 段),而不是通常所认为的系统堆。以下是 map 文件中的相关信息示例:
.bss                  zero     0x2021'7d1c  0x1'9000  heap_4.o [35]  // 实际位于.bss 段

Entry                       Address      Size  Type      Object
-----                       -------      ----  ----      ------
ucHeap                  0x2021'7d1c  0x1'9000  Data  Lc  heap_4.o [35]  // 起始地址与大小

五、FreeRTOS 任务调度

  1. 调度依据:系统通过时钟来判断当前最高优先级的任务,并进行相应的调度,确保高优先级的任务能够优先获得 CPU 资源,从而保证系统的实时性和响应速度。
  2. 任务让出 CPU 使用权:当前任务可以主动执行 taskYIELD()portYIELD_FROM_ISR() 函数,让出 CPU 的使用权,以便调度器能够切换到其他任务进行执行。

六、FreeRTOS 创建任务

在 FreeRTOS 中创建任务时,会在堆中(实际上是位于 .bss 段的系统堆)通过 pvPortMalloc 函数分配内存给任务控制块(TCB),用于存储任务的相关信息,如任务的状态、优先级、上下文等。

七、任务堆栈

在创建任务时,用户可以选择动态创建或静态创建任务堆栈。静态的任务栈在任务结束后无法被回收,会一直占用内存空间;而动态的任务栈则可以在任务结束后,将分配的内存空间归还给系统,提高内存的使用效率。

八、RTOS 堆栈溢出的检测

  1. 方案 1:检查栈指针是否越界
    • 检测方法:在调度时,利用任务保存的栈顶和栈大小信息,检查栈指针是否越界。即每次任务切换时,对比栈指针的位置与栈顶和栈底的位置关系,判断是否发生了堆栈溢出。
    • 优点:能够快速检测到堆栈溢出的情况,只要栈指针超出了预设的范围,就能立即发现问题。
    • 缺点:对于任务运行时发生堆栈溢出,但在切换任务前又恢复正常的情况,无法进行有效的检测。因为在任务切换时,栈指针可能已经回到了正常范围内,导致检测失败。
  2. 方案 2:检查栈末尾字节是否改变
    • 检测方法:在创建任务时,将栈末尾的 16 个字节初始化为特定的字符。每次任务切换时,判断这些特定字符是否被改写。如果被改写,则说明发生了堆栈溢出。
    • 优点:可检出几乎所有的堆栈溢出情况,因为只要堆栈发生溢出,就很可能会覆盖栈末尾的这些特定字符。
    • 缺点:检测速度相对较慢,因为每次任务切换时都需要对栈末尾的 16 个字节进行比较操作,增加了系统的开销。

九、RT-Thread PendSV 系统调用--上下文切换

  1. 省流版总结:OS 调度依赖于 systick,其优先级最低。当有 ISR(中断服务程序)时,ISR 会抢占 OS 调度先执行;在无 ISR 时,OS 调度实际由 PendSV 执行;若在调度过程中 ISR 到来,则 ISR 会插队执行,执行完毕后再继续进行调度。
  2. 不同优先级设置下的情况分析
    • 方法 1 - 无 PendSV - SysTick 最高优先级(Fault 异常):假如在产生异常时,CPU 正在响应另一个中断 ISR,且 SysTick 的优先级大于 ISR,此时 SysTick 会抢占 ISR 获取 CPU 使用权。然而,在 SysTick 中不能进行上下文切换,因为这会导致中断 ISR 被延迟,在对实时性要求较高的系统中是不可接受的。此外,由于 IRQ(中断请求)未得到及时响应,而执行了线程,会触发 Fault 异常,影响系统的稳定性和实时性能。
    • 方法 2 - 无 PendSV - SysTick 最低优先级(无法满足实时性):将 SysTick 的优先级设置为最低,然后在 SysTick 中进行上下文切换。一般情况下,OS 在调度任务时会关闭中断,进入临界区。但由于 OS 任务调度需要耗费一定时间,这就可能出现一种情况:在任务调度期间,如果新的外部 IRQ 发生,CPU 将无法快速响应处理,导致系统的实时性受到影响。
    • PendSV - SysTick 最低优先级(实际方案):将 SysTick 的优先级调低,避免了触发 Fault 的问题,但会影响外部中断 IRQ 的处理速度。为了进一步优化,引入了 PenSV。由于 PendSV 具有【缓期执行】的特点,可将 OS 调度过程拆分为 2 段:
      • 滴答定时器中断,主要进行业务调度前的判断工作,不进行任务切换。
      • 触发 PendSV,PendSV 并不会立即执行,因为其优先级最低。如果此时正好有 IRQ 请求,那么先响应 IRQ,等到所有优先级高于 PendSV 的 IRQ 都执行完毕后,再执行 PendSV 进行任务调度(PendSV 可被打断)。
    • 实际方案的缺陷
      • SysTick 的优先级最低,如果外部 IRQ 比较频繁,可能会导致 SysTick 经常被挂起,进而滞后,使得 Systick 的节拍延长,导致系统时钟不准确。
      • 由于上述原因,任务的执行调度速度可能会受到影响,无法及时响应任务的切换请求。
  3. 实际方案的具体实现流程
    • 任务 A 请求 SVC(supervisor call,系统调用)进行任务切换。
    • 内核收到请求后,挂起 PendSV 异常。
    • CPU 退出 SVC 后进入 PendSV,执行上下文切换操作。
    • PendSV 执行完毕后返回任务 B。
    • 中断发生,执行 ISR(子中断服务程序)。
    • ISR 执行过程中,心跳到达,SysTick 异常发生,抢占了 ISR。
    • PendSV 准备进行上下文切换。
    • SysTick 退出后,继续执行 ISR。
    • ISR 执行完毕,进入 PendSV 进行上下文切换。
    • 切换至任务 A。

十、SVC 中断

  1. SVC 和 PendSV 的作用:SVC(系统服务调用)和 PendSV(可悬挂系统调用)在操作系统之上的软件开发中有着重要的应用。SVC 主要用于产生系统函数的调用请求。例如,在操作系统中,为了保证系统的安全性和稳定性,不让用户程序直接访问硬件,而是通过提供一些系统服务函数来实现对硬件的间接访问。用户程序使用 SVC 发出对系统服务函数的呼叫请求,当用户程序想要控制特定的硬件时,就会产生一个 SVC 异常,然后操作系统提供的 SVC 异常服务例程得到执行,它再调用相关的操作系统函数,完成用户程序请求的服务。
  2. SVC 异常的触发和处理:系统调用会处理异常,实现用户与内核之间的交互。当用户想要执行一些与内核相关的功能时,必须通过 SVC 异常,使内核进入异常模式,从而调用执行内核的源码。一旦触发 SVC 异常,会立即执行 SVC 异常代码。
  3. SVC 在 FreeRTOS 中的应用:在 FreeRTOS 中,任务调度器触发了 SVC 中断来启动第一个任务,之后的工作主要依靠 PendSVSysTick 中断触发来实现。SVC 是系统服务调用,由 SVC 指令触发调用,在 FreeRTOS 中用于在任务调度中开启第一个任务,触发指令为 svc 0SVC 中断本质上是软中断,为用户提供了一个访问硬件的接口;而 PendSV 中断相对 SVC 来说,可以被延迟执行,主要用于任务切换,确保系统在合适的时机进行任务的切换,提高系统的性能和稳定性。

十一、FreeRTOS 中 _FROM_ISR

  1. 作用:在 FreeRTOS 中,带有 _FROM_ISR 后缀的 API 是专门用于在中断中调用的。这些 API 会禁用调度器,以避免在中断处理期间发生任务调度,从而保证对临界区资源的快速访问,防止出现资源竞争等问题。同时,它们不包含延时等阻塞操作,确保中断处理能够尽快完成,符合中断处理快进快出的原则,保证系统的实时性和稳定性。
  2. 与 RT-Thread 的对比:RT-Thread 中没有完全类似的 API 设计。在 RT-Thread 中,对于中断处理相关的操作,没有专门以这种形式来区分的 API,仅有延时参数选项等其他方式来控制中断处理过程中的一些行为。

十二、RT-Thread 同步互斥与通信

RT-Thread 提供了多种内核对象用于实现任务之间的同步、互斥与通信,它们各自具有不同的特点和适用场景,具体如下:

内核对象 生产者 消费者 数据/状态 说明
Semaphore(信号量) 所有任务 所有任务 数量范围为 0 到 n 主要用于维护一定数量的资源,通过信号量的计数来控制对资源的访问,当信号量的值大于 0 时,任务可以获取信号量并访问资源,否则任务将等待信号量的释放
Mutex(互斥锁) 任务 A 上锁 只能由任务 A 开锁 取值为 bit 0、1 用于保护单一的互斥资源,确保同一时间只有一个任务能够访问该资源,避免资源竞争和数据不一致问题
Event(事件) 所有任务 所有任务 多个 bit 用于在任务之间传递事件,通过设置和清除事件标志位来实现任务的同步,当某个任务等待的事件发生时,该任务将被唤醒继续执行
Mail box(邮箱) 所有任务 所有任务 固定大小为 4 Byte,通常传递指针 用于在任务之间传递指针,通过邮箱可以快速地将一个指向数据的指针传递给其他任务,而不需要进行数据的复制,提高了通信效率
Message queue(消息队列) 所有任务 所有任务 若干数据(通常以结构体形式) 可以承载一定数量的数据,用于在任务之间传递复杂的数据结构,任务可以向消息队列中发送消息,也可以从消息队列中接收消息,实现数据的共享和传递
Signal(信号) 无特定 无特定 无特定 类似于软中断,主要用于唤醒等待的任务,当某个信号发生时,相应的任务将被唤醒并执行相关操作

十三、RT-Thread 消息队列、邮箱、信号量区别

  1. 全局变量通信的局限性:使用全局变量进行通信虽然可以承载通信的内容,但存在明显的缺陷。因为全局变量无法主动告知接收方数据的到达,接收方需要不断地轮询全局变量来检查数据是否更新,这会占用大量的 CPU 资源,降低系统的效率。
  2. 信号量的特点:信号量主要的作用是告知接收方信息已经到达,但它并不携带具体的数据内容。例如,当一个任务完成了某个操作后,可以释放一个信号量来通知其他任务,但其他任务并不知道具体发生了什么,还需要通过其他方式获取详细信息。
  3. 消息队列的优势:消息队列不仅能够承载信息内容,还能有效地告知接收方信息的到达。任务可以将数据以结构体等形式发送到消息队列中,接收方任务可以从消息队列中接收消息,从而获取具体的数据内容,实现了数据的可靠传递和共享。
  4. 邮箱的特点:邮箱主要用于 4 Byte 的通信,它通过传递指针而不是使用 memcpy() 函数来复制数据,因此开销较小。这种方式适用于需要快速传递少量数据(如指针)的场景,能够提高通信的效率。

十四、RTOS 优先级的分配原则

在 RTOS 中,任务优先级的分配需要综合考虑多个因素,以确保系统的高效运行和实时性要求,具体原则如下:

  1. 任务对响应的敏感性:对于那些对响应时间要求极高的任务,如串口接收中断等任务,由于它们需要及时处理外部输入的数据,否则可能会导致数据丢失或系统错误,因此应将其优先级设置为最高。
  2. 执行时长:考虑到 RTOS 通常采用抢占式调度策略,如果高优先级任务执行时间过长,可能会导致低优先级任务长时间得不到调度,从而出现饥饿现象。因此,对于像电机 PID 计算以及控制这类需要固定控制周期的任务,虽然它们对实时性也有较高要求,但为了避免影响其他任务的执行,其优先级应设置得较高,但不是最高。
  3. 任务的重要性和实时性需求:看门狗任务用于监控系统的运行状态,确保系统的稳定性;按键处理任务用于响应用户的输入操作。这两类任务的实时性要求相对中等,因此它们的优先级可设置为中等。
  4. APP 层任务的优先级:最低优先级通常分配给 APP 层的心跳和信息显示任务等。这些任务对实时性要求较低,即使稍微延迟执行也不会对系统的核心功能产生严重影响。

十五、FreeRTOS 优先级

在 FreeRTOS 中,优先级的设定规则与一些其他 RTOS 不同,其高优先级对应的数字较大。这意味着在任务调度时,数字较大的优先级任务会优先于数字较小的优先级任务得到执行。

十六、优先级反转

  1. 定义:当使用信号量进行任务同步和互斥时,可能会出现优先级反转的情况。具体表现为高优先级任务被低优先级任务阻塞,导致高优先级任务迟迟得不到调度。然而,其他中等优先级的任务却能够抢到 CPU 资源。从现象上看,就好像是中优先级的任务比高优先级任务具有更高的优先权。
  2. 示例:假设有三个任务,高优先级任务 H、中等优先级任务 M 和低优先级任务 L。低优先级任务 L 获得了某个信号量,正在访问共享资源。此时高优先级任务 H 也需要访问该共享资源,由于信号量被占用,高优先级任务 H 被阻塞。而在这个过程中,中等优先级任务 M 可以正常运行,抢占了 CPU 资源,使得高优先级任务 H 无法及时执行,从而出现了优先级反转的现象。

十七、RT-Thread 内核移植

  1. CPU 架构移植:为了使 RT-Thread 能够在不同的 CPU 架构,如 RISC-V、Cortex-M 等上运行,需要进行 CPU 架构移植。这涉及到对上下文切换、时钟配置以及中断操作等方面的适配。例如,不同架构的 CPU 在上下文切换时保存和恢复的寄存器可能不同,需要根据具体架构进行相应的调整;时钟配置也需要根据不同架构的时钟源和时钟控制寄存器进行设置,以确保系统时钟的准确性;中断操作则需要适配不同架构的中断控制器和中断处理机制。
  2. BSP 移植:对于相同架构的 CPU,但不同的外设配置,需要进行 BSP(Board Support Package,板级支持包)移植。BSP 移植主要包括对不同外设的驱动适配,确保 RT-Thread 能够正确地访问和控制这些外设。同时,还涉及到动态内存管理的适配,根据硬件平台的内存布局和需求,合理地管理内存资源,提高内存的使用效率。

十八、RT-Thread POSIX 标准

POSIX(Portable operating system interface,可移植操作系统接口)标准在 RT-Thread 中的应用,旨在保证应用程序在不同操作系统下的可移植性。通过遵循 POSIX 标准,开发者可以编写具有较高可移植性的代码,使得应用程序能够在支持 POSIX 标准的不同操作系统之间轻松迁移,减少了因操作系统差异带来的开发和维护成本,提高了软件开发的效率和可维护性。

十九、RT-Thread 单元测试

  1. 定义:RT-Thread 中的单元测试是指对软件中的最小可测试单元,如函数、方法、类、功能模块等进行检查和验证的过程。通过单元测试,可以确保每个单元的功能正确性,及时发现和修复潜在的错误,提高软件的质量和可靠性。
  2. utest 框架(unit test):RT-Thread 提供了 utest 框架来支持单元测试。该框架提供了一系列的工具和接口,方便开发者编写、运行和管理单元测试用例。开发者可以使用 utest 框架定义测试用例、设置测试环境、执行测试并查看测试结果,从而有效地进行单元测试工作。

二十、RT-Thread 崩溃调试

在 RT-Thread 中,CmBacktrace 函数在系统崩溃调试中发挥着重要作用。当系统发生崩溃时,该函数能够保存线程栈和寄存器的值。通过这些保存的信息,开发人员可以逆向分析函数的调用关系,追溯系统崩溃的原因。例如,可以查看在崩溃前执行了哪些函数,函数之间的参数传递情况等,从而快速定位问题所在,进行修复。

二十一、RTOS 中多线程看门狗

  1. 方案 1:在 RTOS 中,可以将喂狗操作放在最低优先级线程中。这样,如果高优先级线程长时间抢占 CPU 资源,导致最低优先级线程无法及时执行喂狗操作,看门狗就会超时。这种方式可以检测到高优先级任务长时间占用 CPU 而导致系统无法正常运行的情况。
  2. 方案 2:另一种方案是监控各线程的调度情况,在每个线程中放置定时任务进行喂狗操作。如果某个线程的定时任务超时未执行喂狗操作,就说明该线程可能出现了阻塞或其他异常情况。通过这种方式,可以更精确地检测到单个线程的运行状态,及时发现和处理线程级别的问题。
  3. 初始化时的操作
    • 系统复位时,从 0x00000000 处读出 MSP(主堆栈指针)的初始值,为系统的堆栈操作提供基础。
    • 在 OS 初始化时,对 PSP(进程堆栈指针)进行初始化,确保每个任务都有独立的堆栈空间,用于保存任务的上下文信息。
  4. 任务调度时的操作
    • 当进行任务切换时,首先用任务 A 的 SP(堆栈指针)执行入栈操作,将任务 A 的当前上下文信息保存到堆栈中,并保存任务 A 的 SP 值。
    • 然后设置 PSP 指向任务 B 的栈空间,用任务 B 的 SP 执行出栈操作,将任务 B 的上下文信息从堆栈中恢复出来,随后开始执行任务 B,实现任务的切换和上下文的恢复。

二十二、用户级和特权级

在 Cortex-M 架构中,分为两个运行级别:

  1. 处理模式:当系统发生异常与中断时,CPU 进入处理模式,此时工作在特权级。在特权级下,CPU 可以访问所有的系统资源,包括一些受保护的寄存器和内存区域,以便进行异常和中断的处理。
  2. 线程模式:在其他情况下,CPU 处于线程模式。线程模式可以工作在用户级和特权级,通过配置可以选择线程模式下的运行级别。在用户级下,CPU 的操作受到一定的限制,不能访问一些敏感的系统资源,从而提高系统的安全性;而在特权级下,线程模式可以访问更多的系统资源,适用于一些需要更高权限的操作。

二十三、NVIC(嵌套向量中断控制器)

NVIC 是 Cortex-M 架构中的一个重要组件,它支持中断嵌套功能。当一个中断触发并且系统进行响应时,处理器硬件会自动将当前运行位置的上下文寄存器压入中断栈中。这些上下文寄存器包括 PSR(程序状态寄存器)、PC(程序计数器)、LR(链接寄存器)、R12、R3 - R0 寄存器等。通过保存这些寄存器的值,当处理完中断后,系统可以准确地恢复到中断前的状态,继续执行被中断的程序,保证了系统的连续性和稳定性。

二十四、M3 M4 对比

  1. FPU 浮点功能:相较于 Cortex-M3,Cortex-M4 新增了 FPU(浮点运算单元)。在 Cortex-M3 中,计算浮点数据通常需要使用软件方式,这会消耗较多的 CPU 时间和资源,计算速度相对较慢。
  2. 计算速度优势:而 Cortex-M4 的硬件浮点计算功能使得浮点运算可以直接由 FPU 进行处理,大大提高了浮点计算的速度。这对于一些对浮点计算性能要求较高的应用,如数字信号处理、科学计算等领域,具有明显的优势,能够更高效地处理相关任务。
特性 Cortex - M4 Cortex - M3 Cortex - M0
核心版本 v7ME v7M v6M
指令系统 Thumb® / Thumb - 2 Thumb® / Thumb - 2 Thumb® / Thumb - 2 subset
指令增强 单周期的16、32位MAC
单周期的双16位MAC
8、16位SIMD计算
硬件除法(2~12周期)
硬件除法(2~12周期)
单周期 (32x32)乘法
支持饱和算术运算
可选的硬件单周期 (32x32)乘法
流水线 三级 + 分支推测 三级 三级
执行效率 2.19 CoreMark/MHz,1.25 DMIPS/MHz 未提及 1.62 CoreMark/MHz,0.9 DMIPS/MHz
存储器保护 可选。8区域管理,可划分子区域和后台区 没有 没有
中断 非屏蔽中断(NMI) + 1~240个物理中断源 非屏蔽中断(NMI) + 1~240个物理中断源 非屏蔽中断(NMI) + 1~32个物理中断源
中断优先级 8~256个优先级 未提及 4级优先
唤醒中断控制器 多达240个唤醒中断 未提及 可选
睡眠模式 集成WFI和WFE指令。退出时睡眠功能,睡眠和深度睡眠信号。
使用ARM的电源管理部件,可选择保持模式。
未提及 未提及
位操作 集成位指令和位带域 未提及 未提及
调试 可选的JTAG和SWD调试接口
支持最多8个断点和4个察看点
未提及 可选的JTAG和SW调试接口
支持最多4个断点和2个察看点
跟踪(可选) 指令跟踪(ETM)、数据跟踪(DWT)和仪器跟踪(ITM)模块 未提及 未提及
#牛客激励计划#
嵌入式八股/模拟面试拷打 文章被收录于专栏

一些八股模拟拷打Point,万一有点用呢

全部评论
大佬是还在找春招吗
点赞 回复 分享
发布于 2025-03-07 22:48 广东
学废了
点赞 回复 分享
发布于 2025-03-07 21:38 陕西

相关推荐

评论
2
19
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务