首页 > 其他分享 >FreeRTOS简单内核实现4 临界段

FreeRTOS简单内核实现4 临界段

时间:2024-06-16 13:43:42浏览次数:23  
标签:__ FreeRTOS 中断 BASEPRI void 临界 CRITICAL 内核

@

目录


0、思考与回答

0.1、思考一

为什么需要临界段?

有时候我们需要部分代码一旦这开始执行,则不允许任何中断打断,这段代码称为临界段

0.2、思考二

如何实现临界段?

  1. 关中断
  2. 执行临界区代码
  3. 开中断

0.3、思考三

对于 Cotex-M4 内核的处理器如何方便的控制其中断开关?

使用 BASEPRI 寄存器,当该寄存器中的值不为 0 时,处理器将不会处理优先级值大于或等于 BASEPRI 的任何异常,该寄存器相关信息可以在 Cortex-M4 Devices Generic User Guide 手册中找到,具体如下图所示

值得注意的是 STM32 的 BASEPRI 寄存器做了一些修改,只使用了其高 4 位,低 4 位的数据没有使用,所以对于 STM32 在使用 BASEPRI 寄存器对中断进行屏蔽时,需要考虑到写入的高 4 位数据才是正确的数据,感兴趣的可以阅读 为何修改BASEPRI寄存器无效? 这篇文章

1、关中断

1.1、带返回值

Keil 版本

/* portMacro.h */
#define portSET_INTERRUPT_MASK_FROM_ISR()       ulPortRaiseBASEPRI()
// 带返回值关中断,将当前中断状态作为返回值返回
static __inline uint32_t ulPortRaiseBASEPRI(void)
{
	uint32_t ulReturn,ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
	__asm 
	{
		mrs ulReturn,basepri        // 保存中断时 BASEPRI 寄存器的值
		msr basepri,ulNewBASEPRI    // 屏蔽 优先级值 大于等于 11 的中断
		dsb
		isb
	}
	return ulReturn;
}

CLion 版本

static __inline uint32_t ulPortRaiseBASEPRI(void)  
{  
    uint32_t ulReturn,ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;  
    __asm volatile  
    (  
        "mrs ulReturn,basepri        \n"  
        "msr basepri,ulNewBASEPRI    \n"  
        "dsb                         \n"  
        "isb                         \n"  
    );  
    return ulReturn;  
}

1.2、不带返回值

Keil 版本

/* portMacro.h */
#define portDISABLE_INTERRUPTS()                vPortRaiseBASEPRI()
// 不带返回值关中断
static __inline void vPortRaiseBASEPRI(void)
{
	uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
	__asm 
	{
		msr basepri,ulNewBASEPRI
		dsb
		isb
	}
}

CLion 版本

static __inline void vPortRaiseBASEPRI(void)  
{  
    uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;  
    __asm volatile  
    (  
        "msr basepri, %0    \n"
        "isb                \n"
        :
        : "r" (ulNewBASEPRI)
        : "memory"
    );  
}

2、开中断

Keil 版本

/* portMacro.h */
// 设置 BASEPRI 为 0 开所有中断
#define portENABLE_INTERRUPTS()                 vPortSetBASEPRI(0)
// 设置 BASEPRI 为进入中断时的值则恢复原来的中断状态
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)    vPortSetBASEPRI(x)
// 开中断
static __inline void vPortSetBASEPRI(uint32_t ulBASEPRI)
{
	__asm volatile
	{
		msr basepri,ulBASEPRI       
	}
}

CLion 版本

static __inline void vPortSetBASEPRI(uint32_t ulBASEPRI)  
{
    __asm volatile  
    (
        "msr basepri, %0    \n"
        : "r" (ulBASEPRI)
        : "memory"
    );
}

3、临界段

/* task.h */
#define taskENTER_CRITICAL()           portENTER_CRITICAL()
#define taskEXIT_CRITICAL()            portEXIT_CRITICAL()

#define taskENTER_CRITICAL_FROM_ISR()  portSET_INTERRUPT_MASK_FROM_ISR()
#define taskEXIT_CRITICAL_FROM_ISR(x)  portCLEAR_INTERRUPT_MASK_FROM_ISR(x)
/* portMacro.h */
extern void vPortEnterCritical(void);
extern void vPortExitCritical(void);
#define portENTER_CRITICAL()         vPortEnterCritical()
#define portEXIT_CRITICAL()          vPortExitCritical()
/* port.c */
// 中断嵌套计数器
static UBaseType_t uxCriticalNesting = 0xAAAAAAAA;
// 进入临界区
void vPortEnterCritical(void)
{
	portDISABLE_INTERRUPTS();
	uxCriticalNesting++;
	if(uxCriticalNesting==1)
	{
		// configASSERT((portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK) == 0);
	}
}
// 退出临界区
void vPortExitCritical(void)
{
	// configASSERT(uxCriticalNesting);
	uxCriticalNesting--;
	if(uxCriticalNesting == 0)
	{
		portENABLE_INTERRUPTS();
	}
}

4、应用

普通场合

// 进入临界区直接屏蔽优先级号大于 11 的中断
taskENTER_CRITICAL();
// 退出时直接设置 BASEPRI 寄存器的值为 0 
taskEXIT_CRITICAL();

中断场合

uint32_t ulReturn;
// 进入临界区前保存 BASEPRI 寄存器的值
ulReturn = taskENTER_CRITICAL_FROM_ISR();
// 退出临界区时恢复 BASEPRI 寄存器的值
taskEXIT_CRITICAL_FROM_ISR(ulReturn);

标签:__,FreeRTOS,中断,BASEPRI,void,临界,CRITICAL,内核
From: https://www.cnblogs.com/lc-guo/p/18250545

相关文章

  • 内核参数kernel.shmall和kernel.shmmax
    在Linux系统中,内核参数kernel.shmall和kernel.shmmax与共享内存(SystemV共享内存)有关,它们分别定义了系统可以分配的共享内存段的最大页数和单个共享内存段的最大字节数。以下是一些关于这些参数的推荐设置:kernel.shmall:这个参数控制可以使用的共享内存的总页数。Linux共享内存......
  • 泰山派学习10--内核驱动模块
    1、编写内核驱动模块hello.c2、编写makefile3、编译makemodule4、拷贝到开发板adbpush./hello.ko/home/zbl/drv5、修改文件执行权限chmod777hello.ko6、加载内核驱动sudoinsmodhello.ko7、查看下加载是否成功lsmod8、卸载内核驱动sudormmodhello.ko /**......
  • FreeRTOS简单内核实现3 任务管理
    0、思考与回答0.1、思考一对于Cotex-M4内核的MCU在发生异常/中断时,哪些寄存器会自动入栈,哪些需要手动入栈?会自动入栈的寄存器如下R0-R3:通用寄存器R12:通用寄存器LR(LinkRegister):链接寄存器,保存返回地址PC(ProgramCounter):程序计数器,保存当前执行指令的地址xPSR(Pro......
  • gdb catchsyscall的内核支持
    intro通常使用gdb调试器,希望知道某个系统调用的发生时机,直接在该系统调用打断点即可。这里有一个假设就是这里使用的glibc库的实现,但是go生成的可执行文件就是一个单独的、静态链接文件,在go生成文件中,gdb的时候并没有可以打断点监测系统调用的方法。我想在go中大概率有对特定系......
  • FreeRTOS简单内核实现2 双向链表
    FreeRTOSKernelV10.3.1FreeRTOS的list.c/list.h文件中有3个数据结构、2个初始化函数、2个插入函数、1个移除函数和一些宏函数,链表是FreeRTOS中的重要数据结构,下述“1、数据结构”和“2、操作链表”两个小节内容主要对其原理进行讲解1、数据结构1.1、xLIST_IT......
  • FreeRTOS 简单内核实现1 前言
    0、写在前面为深入理解RTOS内核工作机制,笔者制作了名为“FreeRTOS内核简单实现”的项目专栏,目标为自己动手从0到1编程一个简单的RTOS内核,从而实现任务并行工作的效果,主要实现了以下功能静态创建任务临界段保护支持任务多优先级任务阻塞延时时间片轮询注意:本......
  • 用Ubuntu24编译打包6.9.4内核(仅供参考)
    目录环境介绍前期安装下载内核源代码并编译打包并更新内核重启无法进入系统问题注意事项环境介绍Ubuntu24/4U/12G/120G/NAT172.16.186.148/24rambo@test1:~$uname-aLinuxtest1.lab.example.com6.8.0-35-generic#35-UbuntuSMPPREEMPT_DYNAMICMonMay2015:51:52UT......
  • debian12升级内核
    参考检查当前内核版本uname-r升级系统包sudoaptupdate查找最新的Debian12内核sudoapt-cachesearchlinux-image升级内核sudoaptinstall<linux_image_version>比如:sudoaptinstalllinux-image-6.5.0-0.deb12.4-amd6验证安装rambo@test1:~$dpkg--list|......
  • FreeRTOS学习笔记-基于stm32(14)内存管理
    一、FreeRTOS内存管理简介        FreeRTOS有两种方法来创建任务,队列,信号量等,一种动态一种静态。静态方法需要手动定义任务堆栈。使用动态内存管理的时候FreeRTOS内核在创建任务、队列、信号量的时候会动态的申请RAM。    我们在移植FreeRTOS时可以看到......
  • 《Linux内核完全注释》学习笔记:2.7 Linux内核源代码的目录结构
    由于Linux内核是一种单内核模式的系统,因此内核中所有程序几乎都有紧密的联系,它们之间的调用关系非常密切。所以在阅读一个源代码文件时往往需要参阅其他相关的文件。因此有必要在开始阅读内核源代码之前,先熟悉一下源代码文件的目录结构。这里我们首先列出Linux内核完整的源代......