FreeRTOS_中断配置和临界段

news2024/11/18 22:47:40

目录

1. Cortex-M 中断

1.1 中断简介

1.2 中断管理简介

1.3 优先级分组定义

1.4 优先级设置

1.5 用于中断屏蔽的特殊寄存器

1.5.1 PRIMASK 和 FAULTMASK 寄存器

1.5.2 BASEPRI 寄存器

2. FreeRTOS 中断配置宏

2.1 configPRIO_BITS

2.2 configLIBRARY_LOWEST_INTERRUPT_PRIORITY

2.3 configKERNEL_INTERRUPT_PRIORITY

2.4 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY

2.5 configMAX_SYSCALL_INTERRUPT_PRIORITY

3. FreeRTOS 的开关中断

4. 临界段代码

4.1 任务级临界段代码保护

4.2 中断级临界段代码保护

5. FreeRTOS 中断测试实验

5.1 实验目的

5.2 实验设计

5.3 实验程序与分析

5.4 完整代码

5.4.1 main.c

5.4.2 Timer.c

5.4.3 Timer.h


        FreeRTOS 的中断配置需要根据所使用的 MCU 来具体配置。这需要结合 MCU 架构中有关中断的知识,本节结合 Cortex-M 的 NVIC 来讲解 STM32 平台下的 FreeRTOS 中断配置;

1. Cortex-M 中断

1.1 中断简介

        中断是微控制器一个很常见的特性,中断由硬件产生,当中断产生以后 CPU 就会中断当前的流程转而去处理中断服务,Cortex-M 内核的 MCU 提供了一个用于中断管理的嵌套向量中断控制器(NVIC)。

        Cortex-M3 和 M4 的 NVIC 最多支持 240 个 IRQ(中断请求)、1 个不可屏蔽中断(NMI)、1 个Systick(滴答定时器)定时器中断和多个系统异常。

1.2 中断管理简介

        Cortex-M 处理器有多个用于管理中断和异常的可编程寄存器,这些寄存器大多数都在 NVIC 和系统控制块(SCB)中,CMSIS 将这些寄存器定义为结构体。以 STM32F407 为例,打开core_cm4.h,有两个结构体,NVIC_Type 和 SCB_Type,如下:

typedef struct
{
  __IO uint32_t ISER[8];                 /*!< Offset: 0x000 (R/W)  Interrupt Set Enable Register           */
       uint32_t RESERVED0[24];
  __IO uint32_t ICER[8];                 /*!< Offset: 0x080 (R/W)  Interrupt Clear Enable Register         */
       uint32_t RSERVED1[24];
  __IO uint32_t ISPR[8];                 /*!< Offset: 0x100 (R/W)  Interrupt Set Pending Register          */
       uint32_t RESERVED2[24];
  __IO uint32_t ICPR[8];                 /*!< Offset: 0x180 (R/W)  Interrupt Clear Pending Register        */
       uint32_t RESERVED3[24];
  __IO uint32_t IABR[8];                 /*!< Offset: 0x200 (R/W)  Interrupt Active bit Register           */
       uint32_t RESERVED4[56];
  __IO uint8_t  IP[240];                 /*!< Offset: 0x300 (R/W)  Interrupt Priority Register (8Bit wide) */
       uint32_t RESERVED5[644];
  __O  uint32_t STIR;                    /*!< Offset: 0xE00 ( /W)  Software Trigger Interrupt Register     */
}  NVIC_Type;

typedef struct
{
  __I  uint32_t CPUID;                   /*!< Offset: 0x000 (R/ )  CPUID Base Register                                   */
  __IO uint32_t ICSR;                    /*!< Offset: 0x004 (R/W)  Interrupt Control and State Register                  */
  __IO uint32_t VTOR;                    /*!< Offset: 0x008 (R/W)  Vector Table Offset Register                          */
  __IO uint32_t AIRCR;                   /*!< Offset: 0x00C (R/W)  Application Interrupt and Reset Control Register      */
  __IO uint32_t SCR;                     /*!< Offset: 0x010 (R/W)  System Control Register                               */
  __IO uint32_t CCR;                     /*!< Offset: 0x014 (R/W)  Configuration Control Register                        */
  __IO uint8_t  SHP[12];                 /*!< Offset: 0x018 (R/W)  System Handlers Priority Registers (4-7, 8-11, 12-15) */
  __IO uint32_t SHCSR;                   /*!< Offset: 0x024 (R/W)  System Handler Control and State Register             */
  __IO uint32_t CFSR;                    /*!< Offset: 0x028 (R/W)  Configurable Fault Status Register                    */
  __IO uint32_t HFSR;                    /*!< Offset: 0x02C (R/W)  HardFault Status Register                             */
  __IO uint32_t DFSR;                    /*!< Offset: 0x030 (R/W)  Debug Fault Status Register                           */
  __IO uint32_t MMFAR;                   /*!< Offset: 0x034 (R/W)  MemManage Fault Address Register                      */
  __IO uint32_t BFAR;                    /*!< Offset: 0x038 (R/W)  BusFault Address Register                             */
  __IO uint32_t AFSR;                    /*!< Offset: 0x03C (R/W)  Auxiliary Fault Status Register                       */
  __I  uint32_t PFR[2];                  /*!< Offset: 0x040 (R/ )  Processor Feature Register                            */
  __I  uint32_t DFR;                     /*!< Offset: 0x048 (R/ )  Debug Feature Register                                */
  __I  uint32_t ADR;                     /*!< Offset: 0x04C (R/ )  Auxiliary Feature Register                            */
  __I  uint32_t MMFR[4];                 /*!< Offset: 0x050 (R/ )  Memory Model Feature Register                         */
  __I  uint32_t ISAR[5];                 /*!< Offset: 0x060 (R/ )  Instruction Set Attributes Register                   */
       uint32_t RESERVED0[5];
  __IO uint32_t CPACR;                   /*!< Offset: 0x088 (R/W)  Coprocessor Access Control Register                   */
} SCB_Type;

        NVIC 和 SCB 都位于系统控制空间(SCB)内,SCS 的地址从 0XE000E000 开始,SCB 和 NVIC 的地址也在 core_cm4.h 中有定义,如下:

#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ 
#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ 
#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address 
 
#define SCnSCB ((SCnSCB_Type*) SCS_BASE) /*!< System control Register not in SCB */ 
#define SCB ((SCB_Type *) SCB_BASE) /*!< SCB configuration struct */ 
#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct *// 

1.3 优先级分组定义

        当多个中断来临的时候处理器应该响应哪一个中断是由中断的优先级来决定的,高优先级的中断(优先级编号小)肯定是首先得到响应,而且高优先级的中断可以抢占低优先级的中断,这个就是中断嵌套Cortex-M 处理器的有些中断是具有固定的优先级的,比如复位、NMI(不可屏蔽中断)、HardFault,这些中断的优先级都是负数,优先级也是最高的

        Cortex-M 处理器有三个固定优先级和 256 个可编程的优先级,最多有 128 个抢占等级,但是实际的优先级数量是由芯片厂商来决定的。在实际设计时,绝大多数的芯片都会精简设计,以致实际上支持的优先级数会更少,如 8级、16级、32级等,比如 STM32 就只有 16 级优先级。在设计芯片的时候会裁掉表达优先级的几个低端有效位,以减少优先级数,所以不管用多少位来表达优先级,都是 MSB 对齐的;

        在图 4.1.3.1 中,Bit0~Bit4 没有实现,所以读它们总是返回零,写它们的话也会忽略写入的值。因此,对于 3 个位的情况,可以使用的优先级就是 8 个0x00(最高优先级)、0x20、0x40、0x60、0x80、0xA0、0xC0 和 0xE0                                                                                                                                                                                                                                        

注意:这个是芯片厂商来决定的!不是我们能决定的,比如说 STM32 就选择了 4 位作为优先级!   

        为了使抢占机能变得更可控,Cortex-M 处理器还把 256 个优先级按位分为高低两段:抢占优先级(分组优先级)和亚优先级(子优先级),NVIC 中有一个寄存器是 "应用程序中断及复位控制寄存器(AIRCR)";AIRCR 寄存器里面有个位段名为 “优先级组”,

        上图中 PRIGROUP 就是优先级分组,他把优先级分为两个位段:MSB 所在的位段(左边的)对应抢占优先级,LSB 所在的位段(右边的)对应亚优先级,也就是子优先级。

        STM32 的优先级分组情况,STM32 使用了 4 位,因此最多有 5 组优先级分组设置,这 5 个分组在 msic.h 中有定义:

#define NVIC_PriorityGroup_0 ((uint32_t)0x700) /*!< 0 bits for pre-emption priority 
 4 bits for subpriority */ 
#define NVIC_PriorityGroup_1 ((uint32_t)0x600) /*!< 1 bits for pre-emption priority 
 3 bits for subpriority */ 
#define NVIC_PriorityGroup_2 ((uint32_t)0x500) /*!< 2 bits for pre-emption priority 
 2 bits for subpriority */
#define NVIC_PriorityGroup_3 ((uint32_t)0x400) /*!< 3 bits for pre-emption priority 
 1 bits for subpriority */ 
#define NVIC_PriorityGroup_4 ((uint32_t)0x300) /*!< 4 bits for pre-emption priority 
 0 bits for subpriority */ 

        STM32 有 5 个分组,一定要注意!STM32 中定义的分组 0 对应的值是 7!如果我们选择分组 4 ,即 NVIC_PriorityGroup_4 的话,那 4 位优先级就都全是抢占优先级了,没有亚优先级,那么就有 0~15 共 16 个优先级。移植 FreeRTOS 的时候我们配置的就是组 4

int main(void)
{ 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
	delay_init(168);		//初始化延时函数
	uart_init(115200);     	//初始化串口
	LED_Init();		        //初始化LED端口
	
	//创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

1.4 优先级设置

        每个外部中断都有一个对应的优先级寄存器,每个寄存器占 8 位,因此最大宽度是 8 位,但是最小为 3 位。4 个相邻的优先级寄存器拼成一个 32 位寄存器。如前所述,根据优先级组的设置,优先级又可以分为高、低两个位段 ,分别是抢占优先级亚优先级。STM32 我们已经设置位组 4,所以就只有抢占优先级了。优先级寄存器都可以按字节访问,当然也可以按半字/字来访问;

        4 个相邻的寄存器可以拼成一个 32 位的寄存器,因此地址 0xE000_ED20 ~ 0xE000_ED23 这四个寄存器就可以拼接成一个地址为 0xE000_ED20 的 32 位寄存器。

        这一点很重要!因为 FreeRTOS 在设置 PendSV 和 SysTick 的中断优先级的时候都是直接操作的地址 0xE000_ED20。

1.5 用于中断屏蔽的特殊寄存器

        在 STM32 上移植 FreeRTOS 的时候 PRIMASK、FAULTMASK 和 BASEPRI 这三个寄存器发挥了重要的作用!

1.5.1 PRIMASK 和 FAULTMASK 寄存器

        在许多应用中,需要暂时屏蔽所有的中断,执行一些对时序要求严格的任务,这个时候就可以使用 PRIMASK 寄存器PRIMASK 用于禁止除 NMI 和 HardFalut 外的所有异常和中断,汇编编程的时候可以使用 CPS(修改处理器状态)指令修改 PRIMASK 寄存器的数值

CPSIE I; //清除 PRIMASK(使能中断) 
CPSID I; //设置 PRIMASK(禁止中断) 

        PRIMASK 寄存器还可以通过 MRS 和 MSR 指令访问,如下:

MOVS R0, #1 
MSR PRIMASK, R0 ;//将 1 写入 PRIMASK 禁止所有中断

        以及:

MOVS R0, #0 
MSR PRIMASK, R0 ;//将 0 写入 PRIMASK 以使能中断

        UCOS 中的临界区代码保护就是通过开关中断实现的(UCOSIII 也可以使用禁止任务调度的方法来实现临界区代码保护),而开关中断就是直接操作 PRIMASK 寄存器的,所以在 UCOS 中关闭中断的时候关闭了除复位、NMI 和 HardFault 以外的所有中断!

        FAULTMASK 比 PRIMASK 更狠,他可以连 HardFault 都屏蔽掉,使用方法和 PRIMASK 类似,FAULTMASK 会在退出时自动清零。

        汇编编程的时候可以利用 CPS 指令修改 FAULTMASK 的当前状态:

CPSIE F ;清除 FAULTMASK 
CPSID F ;设置 FAULTMASK

        还可以利用 MRS 和 MSR 指令访问 FAULTMASK 寄存器:

MOVS R0, #1 
MSR FAULTMASK, R0 ;将 1 写入 FAULTMASK 禁止所有中断 

        以及:

MOVS R0, #0 
MSR FAULTMASK, R0 ;将 0 写入 FAULTMASK 使能中断

1.5.2 BASEPRI 寄存器

        PRIMASK 和 FAULTMASK 寄存器过于简单粗暴,直接关闭了除复位、NMI 和 HardFault 以外的其他所有中断,但是在有些场合需要对中断屏蔽进行更加细腻的控制,比如只屏蔽优先级低于某一阈值的中断。那么这个作为阈值的优先级值存储在哪里呢?

        在 BASEPRI 寄存器中,不过如果向 BASEPRI 写 0 的话就会停止屏蔽中断。

        比如,我们要屏蔽优先级不高于 0x60 的中断,则可以使用如下汇编编程:

MOV R0, #0X60 
MSR BASEPRI, R0

        如果需要取消 BASEPRI 对中断的屏蔽,可以使用如下代码:

MOV R0, #0 
MSR BASEPRI, R0

        注意:FreeRTOS 的开关中断就是操作 BASEPRI 寄存器来实现的!它可以关闭低于某个阈值的中断,高于这个阈值的中断就不会被关闭!

2. FreeRTOS 中断配置宏

2.1 configPRIO_BITS

        此宏用来设置 MCU 使用几位优先级,STM32 使用的是 4 位,因此此宏为 4!

2.2 configLIBRARY_LOWEST_INTERRUPT_PRIORITY

        此宏是用来设置最低优先级的,STM32 优先级使用了 4 位,而且 STM32 配置时使用组 4 ,也就是 4 位都是抢占优先级。因此优先级数就是 16 个,最低优先级就是 15 。所以此宏就是 15。

2.3 configKERNEL_INTERRUPT_PRIORITY

        此宏用来设置内核中断优先级,此宏定义如下:

#define configKERNEL_INTERRUPT_PRIORITY 
( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

//宏 configLIBRARY_LOWEST_INTERRUPT_PRIORITY 左移 (8 - configPRIO_BITS)位,也就是左移 4 位。
之前我们学过,STM32 使用了 4 位作为优先级,而这 4 位是高 4 位,因此要左移 4 位才是真正的优先级。

//当然也可以不用移位,直接将宏 configLIBRARY_LOWEST_INTERRUPT_PRIORITY 定义为 0XF0!

        宏 configKERNEL_INTERRUPT_PRIORITY 用来设置 PendSV 和滴答定时器的中断优先级,port.c 中有如下定义:

#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 
16UL ) 
#define portNVIC_SYSTICK_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 
24UL ) 

        可以看出,portNVIC_PENDSV_PRI 和 portNVIC_SYSTICK_PRI 都使用了宏 configKERNEL_INTERRUPT_PRIORITY,PendSV 和 SysTick 的中断优先级设置是操作 0xE000_ED20 地址的,一次写入的是 32 位的数据,SysTick 和 PendSV 的优先级寄存器分别对应这个 32 位数据的最高 8 位和次高 8 位,所以就是一个左移 16 位,一个左移 24 位了。

portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;  //设置 PendSV 中断优先级 
portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; //设置 SysTick 中断优先级

        上述代码就是设置 PendSV 和 SysTick 优先级的,它们是直接向地址 portNVIC_SYSPRI2_REG 写入优先级数据,portNVIC_SYSPRI2_REG 是个宏,在文件 port.c 中定义,如下:

#define portNVIC_SYSPRI2_REG  ( * ( ( volatile uint32_t * ) 0xe000ed20 ) )

        可以看到宏 portNVIC_SYSPRI2_REG 就是地址 0XE000ED20!同时也可以看出在 FreeRTOS 中 PendSV 和 SysTick 的中断优先级都是最低的!

2.4 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY

        此宏用来设置 FreeRTOS 系统可管理的最大优先级,也就是 BASEPRI 寄存器说的那个阈值优先级,这个可以自由设置,这里我设置的是 5 。也就是高于 5 的优先级(优先级数小于 5 )不归 FreeRTOS 管理!

2.5 configMAX_SYSCALL_INTERRUPT_PRIORITY

        此宏是 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 左移 4 位而来的,原因和宏 configKERNEL_INTERRUPT_PRIORITY 一样。此宏设置好以后,低于此优先级的中断可以安全的调用 FreeRTOS 的 API 函数,高于此优先级的中断 FreeRTOS 是不能禁止的,中断服务函数也不能调用 FreeRTOS 的 API 函数!

        以 STM32 为例,有 16 个优先级,0 为最高优先级,15 为最低优先级,配置如下:

        configMAX_SYSCALL_INTERRUPT_PRIORITY == 5

        configKERNEL_INTERRUPT_PRIORITY == 15

        由于高于 configMAX_SYSCALL_INTERRUPT_PRIORITY 的优先级不会被 FreeRTOS 内核屏蔽,因此那些对实时性要求严格的任务就可以使用这些优先级,比如四轴飞行器的避障检测。

3. FreeRTOS 的开关中断

        FreeRTOS 开关中断函数为 portENABLE_INTERRUPTS() 和 portDISABLE_INTERRUPTS()  

#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI() 
#define portENABLE_INTERRUPTS() vPortSetBASEPRI(0) 

        可以看出开关中断实际上是通过函数 vPortSetBASEPRI(0) 和 vPortRaiseBASEPRI() 来实现的

static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI ) 
{ 
 __asm 
 { 
 msr basepri, ulBASEPRI 
 } 
} 
/*-----------------------------------------------------------*/ 
 
static portFORCE_INLINE void vPortRaiseBASEPRI( void ) 
{ 
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY; 
 
 __asm 
 { 
 msr basepri, ulNewBASEPRI 
 dsb 
 isb 
 } 
}

        函数 vPortSetBASEPRI() 是向寄存器 BASEPRI 写入一个值,此值作为参数 ulBASEPRI 传递进来,portENABLE_INTERRUPTS() 是开中断,它传递个 0 给 vPortSetBASEPRI(),根据我们前面讲解 BASEPRI 寄存器可知,结果就是开中断。

        函数 vPortRaiseBASEPRI() 是向寄存器 BASEPRI 写入宏 configMAX_SYSCALL_INTERRUPT_PRIORITY,那么优先级低于 configMAX_SYSCALL_INTERRUPT_PRIORITY 的中断就会被屏蔽!

4. 临界段代码

        临界段代码也叫临界区,是指那些必须完整运行,不能被打断的代码段,比如说有的外设的初始化需要严格的时序,初始化过程中不能被打断。FreeRTOS 在进入临界段代码的时候需要关闭中断,当处理完临界段代码以后再打开中断。FreeRTOS 系统本身就有很多的临界段代码,这些代码都加了临界段代码保护,我们在写自己的用户程序的时候有些地方也需要添加临界段代码保护。

        FreeRTOS 与临界段代码保护有关的函数有 4 个:taskENTER_CRITICAL()、taskEXIT_CRITICAL()、taskENTER_CRITICAL_FROM_ISR() 和 taskEXIT_CRITICAL_FROM_ISR(),这四个函数都是宏定义,在 task.h 文件中定义。这四个函数的区别是前两个是任务级的临界段代码保护,后两个是中断级的临界段代码保护

4.1 任务级临界段代码保护

        taskENTER_CRITICAL() 和 taskEXIT_CRITICAL() 是任务级的临界代码保护,一个是进入临界段,一个是退出临界段,这两个函数是成对使用的,函数定义如下:

#define taskENTER_CRITICAL()  portENTER_CRITICAL()
#define taskEXIT_CRITICAL()   portEXIT_CRITICAL()

        而 portENTER_CRITICAL() 和 portEXIT_CRITICAL() 也是宏定义,在文件 portmacro.h 中有定义,如下:

#define portENTER_CRITICAL() vPortEnterCritical() 
#define portEXIT_CRITICAL() vPortExitCritical() 

        函数 vPortEnterCritical() 和 vPortExitCritical() 在文件 port.c 中,函数如下:

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(); 
 } 
}

        可以看出在进入函数 vPortEnterCritical() 以后会首先关闭中断,然后给变量 uxCriticalNesting 加一,uxCriticalNesting 是一个全局变量,用来记录临界段嵌套次数。函数 vPortExitCritical() 是退出临界段调用的,函数每次将 uxCriticalNesting 减一,只有当 uxCriticalNesting 为 0 的时候才会调用函数 portENABLE_INTERRUPTS() 使能中断。这样保证了在有多个临界段代码的时候不会因为某一个临界段代码的退出而打乱其他临界段的保护,只有所有的临界段代码都退出以后才会使能中断!

//任务级临界代码保护使用方法

void taskcritical_test(void)
{
    while(1)
    {
        taskENTER_CRITICAL();           (1)
        total_num = total_num + 0.01f;
        printf("total_num 的值为:%.4f\r\n",total_num);
        taskEXIT_CRITICAL();            (2)
        vTaskDelay(1000);
    }
}

(1)、进入临界区。

(2)、退出临界区。

        (1)和(2)中间的代码就是临界区代码,注意临界区代码一定要精简!因为进入临界区会关闭中断,这样会导致优先级低于 configMAX_SYSCALL_INTERRUPT_PRIORITY 的中断得不到及时的响应!

4.2 中断级临界段代码保护

        函数 taskENTER_CRITICAL_FROM_ISR() 和 taskEXIT_CRITICAL_FROM_ISR() 中断级别临界段代码保护,是用在中断服务程序中的,而且这个中断的优先级一定要低于 configMAX_SYSCALL_INTERRUPT_PRIORITY!这两个函数在文件 task.h 中有如下定义:

#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR() 
#define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )

        接着找 portSET_INTERRUPT_MASK_FROM_ISR() 和 portCLEAR_INTERRUPT_MASK_FROM_ISR(),这两个在文件 portmacro.h 中有如下定义:

#define portSET_INTERRUPT_MASK_FROM_ISR()  ulPortRaiseBASEPRI()
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)  vPortSetBASEPRI(x)

        函数 ulPortRaiseBASEPRI() 在文件 portmacro.h 中定义的,如下:

static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void ) 
{ 
uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY; 
 
 __asm 
 { 
 mrs ulReturn, basepri (1) 
 msr basepri, ulNewBASEPRI (2) 
 dsb 
 isb 
 } 
 
 return ulReturn; (3) 
}

        (1)、先读出 BASEPRI 的值,保存在 ulReturn 中。

        (2)、将 configMAX_SYSCALL_INTERRUPT_PRIORITY 写入到寄存器 BASEPRI 中。

        (3)、返回 ulReturn,退出临界区代码保护的时候要使用到此值!

//中断级临界代码保护使用方法如下:

void TIM3_IRQHandler(void) 
{ 
     if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断 
     { 
         status_value=taskENTER_CRITICAL_FROM_ISR();      (1) 
         total_num+=1; 
         printf("float_num 的值为: %d\r\n",total_num); 
         taskEXIT_CRITICAL_FROM_ISR(status_value);     (2) 
     } 
         TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除中断标志位 
} 

        (1)、进入临界区。

        (2)、退出临界区。

5. FreeRTOS 中断测试实验

5.1 实验目的

        在 FreeRTOS 中,优先级低于 configMAX_SYSCALL_INTERRUPT_PRIORITY 的中断会被屏蔽掉,高于的就不会;

        本节,使用两个定时器,一个优先级为 4,一个优先级为 5,两个定时器每隔 1s 通过串口输出一串字符串。然后在某个任务中关闭中断一段时间,查看两个定时器的输出情况。

5.2 实验设计

        本实验设计了两个任务 start_task() 和 interrupt_task() ,这两个任务的任务功能如下:

        start_task():创建另外一个任务。

        interrupt_task():中断测试任务,任务中会调用 FreeRTOS 的关中断函数 portDISABLE_INTERRUPTS() 来将中断关闭一段时间。

5.3 实验程序与分析

//任务设置

#define START_TASK_PRIO  1            //任务优先级
#define START_STK_SIZE  256           //任务堆栈大小    
TaskHandler_t StartTask_Handler;      //任务句柄
void start_task(void *pvParameters);  //任务函数

#define INTERRUPT_TASK_PRIO  2        //任务优先级
#define INTERRUPT_STK_SIZE  256       //任务堆栈大小
TaskHandler_t INTERRUPTTask_Handler; //任务句柄
void interrupt_task(void *p_arg);    //任务函数
// main 函数

int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); //设置系统中断优先级分组 4 
    delay_init(168); //初始化延时函数 
    uart_init(115200); //初始化串口 
    LED_Init(); //初始化 LED 端口 
    TIM3_Int_Init(10000-1,8400-1); //初始化定时器 3,定时器周期 1S 
    TIM5_Int_Init(10000-1,8400-1); //初始化定时器 5,定时器周期 1S

    //创建开始任务
    xTaskCreate((TaskFunction_t )start_task,         //任务函数
                (const char* )"start_task",          //任务名称
                (uint16_t )START_STK_SIZE,           //任务堆栈大小
                (void* )NULL,                        //传递给任务函数的参数
                (UBaseType_t )START_TASK_PRIO,       //任务优先级 
                (TaskHandle_t* )&StartTask_Handler   //任务句柄 
                );
    vTaskStartScheduler();                           //开启任务调度
}
//任务函数

//开始任务函数

void start_task(void *pvParameters) 
{ 
     taskENTER_CRITICAL();                         //进入临界区 
     //创建中断测试任务 
     xTaskCreate((TaskFunction_t )interrupt_task,  //任务函数         (1) 
     (const char* )"interrupt_task",               //任务名称 
     (uint16_t )INTERRUPT_STK_SIZE,                //任务堆栈大小 
     (void* )NULL,                                 //传递给任务函数的参数 
     (UBaseType_t )INTERRUPT_TASK_PRIO,            //任务优先级 
     (TaskHandle_t* )&INTERRUPTTask_Handler);      //任务句柄 
     vTaskDelete(StartTask_Handler);               //删除开始任务 
     taskEXIT_CRITICAL();                          //退出临界区 
}

//中断测试函数

void interrupt_task(void *pvParameters)
{
    static u32 total_num=0;
    while(1)
    {
        total_num=total_num+1;
        if(total_num==5)                                              (2)
        {
            printf("关闭中断…………\r\n");
            portDISABLE_INTERRUPTS();   //关闭中断                     (3)
            delay_xms(5000);            //延时5s                       (4)
            printf("打开中断…………\r\n");  //打开中断
            portENABLE_INTERRUPTS();                                  (5)
        }
        LED0=~LED0;
        vTaskDelay(1000);
    }
}

(1)创建一个任务来执行开关中断的动作,任务函数为 interrupt_task()。

(2)当任务 interrupt_task() 运行 5 次以后关闭中断。

(3)调用函数 portDISABLE_INTERRUPTS() 关闭中断。优先级低于 configMAX_SYSCALL_INTERRUPT_PRIORITY 的中断都会被关闭,高于的不会受任何影响。

(4)调用函数 delay_xms() 延时 5S,此函数是对 delay_us() 的简单封装,delay_xms() 会用来模拟关闭中断一段时间,此函数不会引起任务调度!

(5)调用函数 portENABLE_INTERRUPTS() 重新打开中断。

//中断初始化及处理过程

//通用定时器 3 中断初始化
//arr:自动重装值
//psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us
//Ft=定时器工作频率,单位:Mhz
//这里使用的是定时器 3!

void TIM3_Int_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef TimeBaseInitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);  //使能 TIM3 时钟

    TIM_TimeBaseInitStructure.TIM_Period = arr;   //自动重装载值
    TIM_TimeBaseInitStructure.TIM_Prescaler = psc;  //定时器分频
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;  //向上计数模式
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);  //初始化TIM3
    
    TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);   //允许定时器 3 更新中断
    TIM_Cmd(TIM3,ENABLE);   //使能定时器 3 

    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;    //定时器 3 中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x04;  //抢占优先级 4   (1)
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;   //子优先级 0
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
    NVIC_Init(&NVIC_InitStructure);
}

//通用定时器 5 中断初始化
//arr:自动重装值
//psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us
//Ft=定时器工作频率,单位:Mhz
//这里使用的是定时器 5!

void TIM5_Int_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef TimeBaseInitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);  //使能 TIM5 时钟

    TIM_TimeBaseInitStructure.TIM_Period = arr;   //自动重装载值
    TIM_TimeBaseInitStructure.TIM_Prescaler = psc;  //定时器分频
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;  //向上计数模式
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStructure);  //初始化TIM5
    
    TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE);   //允许定时器 5 更新中断
    TIM_Cmd(TIM5,ENABLE);   //使能定时器 5 

    NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;    //定时器 5 中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x05;  //抢占优先级 5   (2)
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;   //子优先级 0
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
    NVIC_Init(&NVIC_InitStructure);
}

//定时器 3 中断服务函数
void TIM3_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM3,TIM_IT_Update) == SET) //溢出中断
    {
        printf("TIM3 输出…………\r\n");         (3)
    }
    TIM_ClearITPendingBit(TIM3,TIM_IT_Updata);  //清除中断标志位
}

//定时器 5 中断服务函数
void TIM5_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM5,TIM_IT_Update) == SET) //溢出中断
    {
        printf("TIM5 输出…………\r\n");         (4)
    }
    TIM_ClearITPendingBit(TIM5,TIM_IT_Updata);  //清除中断标志位
}

(1)设置定时器 3 的抢占优先级为 4 ,高于 configMAX_SYSCALL_INTERRUPT_PRIORITY,因此在调用函数 portDISABLE_INTERRUPTS() 关闭中断的时候定时器 3 是不会受影响的。

(2)设置定时器 5 的抢占优先级为 5 ,等于 configMAX_SYSCALL_INTERRUPT_PRIORITY,因此在调用函数 portDISABLE_INTERRUPTS() 关闭中断的时候定时器 5 肯定会被关闭的。

(3)和(4)定时器 3 和定时器 5 串口输出信息。

5.4 完整代码

        试验现象:本实验设置两个任务,一个是开始任务,一个是中断任务,中断程序中设置 定时器 3 和 定时器 5 ,定时器 3 的抢占优先级为 4 ,定时器 5 的抢占优先级为 5 ;开始任务执行以后,执行开始任务函数,开始任务执行完成以后,删除开始任务,进入中断测试函数;

//中断测试任务函数
void interrupt_task(void *pvParameters)
{
    static u32 total_num=0;
    while(1)
    {
        total_num=total_num+1;
        if(total_num==5)
        {
            printf("关闭中断…………\r\n");
            portDISABLE_INTERRUPTS();        //关闭中断
            delay_xms(5000);                 //延时5S
            printf("打开中断…………\r\n");       //打开中断
            portENABLE_INTERRUPTS();
        }
        LED0=~LED0;
        vTaskDelay(1000);
    }
}

//以上是中断测试函数的程序,首先开发板上 LED0 每隔一秒闪烁一次,提示程序正在运行;
//设置一个 static 修饰的静态变量 total_num,保证每一次进入中断测试函数以后,total_mum 的值
//都保留上一次函数结束时的值,而不是每一次进入中断测试函数以后 total_num 的值都为0
//第一次进入 interrupt_task 中断测试函数以后,因为 TIM3 和TIM5 定时器是每1秒输出一次;
//所以串口会首先打印5次 TIM3输出、TIM5输出;此时 total_num 的值会递增至5,进入 if 判断语句,
//串口会打印关闭中断,此时会调用宏 portDISABLE_INTERRUPTS(); ,FreeRTOS设置的中断优先级为 4
//TIM3 中断优先级为4,TIM5 中断优先级为5,所以关闭中断的时候,TIM3 不会受影响,TIM5 会受影响;
//此时串口只会打印 TIM3输出,延时5S,也就是打印5次;
//此时串口会打印打开中断,打开中断以后,串口会接着输出 TIM3输出、TIM5输出;

 

5.4.1 main.c

#include "stm32f4xx.h"         
#include "delay.h"
#include "usart.h"
#include "LED.h"
#include "Timer.h"
#include "FreeRTOS.h"
#include "task.h"


//任务优先级
#define START_TASK_PRIO            1
//任务堆栈大小 
#define START_STK_SIZE            256
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define INTERRUPT_TASK_PRIO        2
//任务堆栈大小 
#define INTERRUPT_STK_SIZE        256
//任务句柄
TaskHandle_t INTERRUPTTask_Handler;
//任务函数
void interrupt_task(void *p_arg);

int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); //设置系统中断优先级分组4
    delay_init(168);
    uart_init(115200);
    LED_Init();
    MyTIM3_Init(10000-1,8400-1);  //初始化定时器3,定时器周期1S
    MyTIM5_Init(10000-1,8400-1);  //初始化定时器5,定时器周期1S
    
    //创建开始任务
    xTaskCreate((TaskFunction_t)start_task,          //任务函数
                (const char*   )"start_task",        //任务名称
                (uint16_t      )START_STK_SIZE,      //任务堆栈大小
                (void*         )NULL,                //传递给任务函数的参数
                (UBaseType_t   )START_TASK_PRIO,     //任务优先级
                (TaskHandle_t* )&StartTask_Handler); //任务句柄
    
    vTaskStartScheduler();     //开始任务调度            
}

//开始任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();     //进入临界区
    //创建中断测试任务
    xTaskCreate((TaskFunction_t)interrupt_task,      //任务函数
                (const char*   )"interrupt_task",    //任务名称
                (uint16_t      )INTERRUPT_STK_SIZE,  //任务堆栈大小
                (void*         )NULL,                //传递给任务函数的参数
                (UBaseType_t   )INTERRUPT_TASK_PRIO, //任务优先级
                (TaskHandle_t* )&INTERRUPTTask_Handler); //任务句柄
    vTaskDelete(StartTask_Handler);         //删除开始任务
    taskEXIT_CRITICAL();     //退出临界区
}

//中断测试任务函数
void interrupt_task(void *pvParameters)
{
    static u32 total_num=0;
    while(1)
    {
        total_num=total_num+1;
        if(total_num==5)
        {
            printf("关闭中断…………\r\n");
            portDISABLE_INTERRUPTS();        //关闭中断
            delay_xms(5000);                 //延时5S
            printf("打开中断…………\r\n");   //打开中断
            portENABLE_INTERRUPTS();
        }
        LED0=~LED0;
        vTaskDelay(1000);
    }
}


5.4.2 Timer.c

#include "sys.h"
#include "LED.h"
#include "Timer.h"
#include "usart.h"

//通用定时器 3 设置
//AutomaticArray:自动重装载值
//PresclarSendCount:时钟预分频数
//定时器溢出时间计算:Tout=((AutomaticArray+1)*(PresclarSendCount+1))/Ft us
//Ft:定时器工作频率,单位 MHz
void MyTIM3_Init(u16 AutomaticArray,u16 PresclarSendCount)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);  //使能TIM3时钟
    
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;  
    TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
    TIM_TimeBaseInitStructure.TIM_Period=AutomaticArray;  //自动重装载值
    TIM_TimeBaseInitStructure.TIM_Prescaler=PresclarSendCount;  //时钟预分频值
    TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
    
    TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);  //TIM3更新中断使能
    TIM_Cmd(TIM3,ENABLE);  //使能定时器3
    
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn; //定时器3中断
    NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x04; //抢占优先级4
    NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x00;  //子优先级0
    NVIC_Init(&NVIC_InitStructure);
}

//通用定时器 5 设置
//AutomaticArray:自动重装载值
//PresclarSendCount:时钟预分频数
//定时器溢出时间计算:Tout=((AutomaticArray+1)*(PresclarSendCount+1))/Ft us
//Ft:定时器工作频率,单位 MHz
void MyTIM5_Init(u16 AutomaticArray,u16 PresclarSendCount)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);  //使能TIM5时钟
    
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;  
    TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
    TIM_TimeBaseInitStructure.TIM_Period=AutomaticArray;  //自动重装载值
    TIM_TimeBaseInitStructure.TIM_Prescaler=PresclarSendCount;  //时钟预分频值
    TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStructure);
    
    TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE);  //TIM5更新中断使能
    TIM_Cmd(TIM5,ENABLE);  //使能定时器5
    
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel=TIM5_IRQn; //定时器5中断
    NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x05; //抢占优先级5
    NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x00;  //子优先级0
    NVIC_Init(&NVIC_InitStructure);
}
//定时器3中断服务函数
void TIM3_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET)  //TIM3 溢出中断
    {
        printf("TIM3输出……\r\n");
    }
    TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除中断标志位
}
//定时器5中断服务函数
void TIM5_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM5,TIM_IT_Update)==SET)  //TIM5 溢出中断
    {
        printf("TIM5输出……\r\n");
    }
    TIM_ClearITPendingBit(TIM5,TIM_IT_Update); //清除中断标志位
}


5.4.3 Timer.h

#ifndef _TIMER__H_
#define _TIMER__H_
#include "sys.h"



void MyTIM3_Init(u16 AutomaticArray,u16 PresclarSendCount);
void MyTIM5_Init(u16 AutomaticArray,u16 PresclarSendCount);

#endif

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

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

相关文章

IOC(控制反转)

目录 理解IOC 理解IOC容器 Spring IOC IOC优点 什么是依赖注入DI DI是如何实现的呢&#xff1f; 总结 : IOC实现的基本过程 容器如何放入Bean对象? IOC实现的基本过程 IoC容器在Spring的实现 理解IOC IoC 是 Inversion of Control 的简写&#xff0c;译为“控制反…

容器部署 redis 哨兵集群 【问题笔记】

目录 1.哨兵模式 &#xff08;sentinel&#xff09; 启动时报错&#xff1a;Cant resolve instance hostnames 2.哨兵模式下 master 节点崩掉后无法切换从节点为master节点 1.哨兵模式 &#xff08;sentinel&#xff09; 启动时报错&#xff1a;Cant resolve instance hostna…

Axure教程—单色折线图(中继器)

本文将教大家如何用AXURE中的中继器制作单色折线图 一、效果介绍 如图&#xff1a; 预览地址&#xff1a;https://d84rfm.axshare.com 下载地址&#xff1a;https://download.csdn.net/download/weixin_43516258/87814278 二、功能介绍 简单填写中继器内容即可动态显示值样…

Call for Papers丨第十一届全国社会媒体处理大会,AI Open设独立专刊投稿渠道

全国社会媒体处理大会&#xff08;SMP&#xff09;专注于以社会媒体处理为主题的科学研究&#xff0c;为传播社会媒体处理最新的学术研究与技术成果提供广泛的交流平台&#xff0c;旨在构建社会媒体处理领域的产学研生态圈&#xff0c;成为中国乃至世界社会媒体处理的风向标。 …

2023年了PWA开发模式在国内还有发展空间吗?

随着国内外技术及生态的迅猛发展&#xff0c;应用程序的开发模式也在不断演进。在过去&#xff0c;就我及身边同事的经验而言&#xff0c;会常常需要在不同的平台&#xff08;如Android、iOS&#xff09;编写不同的代码&#xff0c;导致开发成本和维护困难的问题。 然而&#…

WBS项目分解的7大基本原则

制定和分解WBS&#xff0c;需要遵循的基本原则&#xff1a; 1、唯一性 每一项工作任务在WBS中是唯一的。 WBS项目分解的7大基本原则 2、负责制 每一项任务都需要明确责任人&#xff0c;一人负责&#xff0c;其他人参与。 3、可测量性 每一项任务都应该是可以量化和测量的&#…

5月份招了个00后测试堪称卷王之王,让人崩溃...

前段时间公司新来了个同事&#xff0c;听说大学是学的广告专业&#xff0c;因为喜欢IT行业就找了个培训班&#xff0c;后来在一家小公司干了三年&#xff0c;现在跳槽来我们公司。来了之后把现有项目的性能优化了一遍&#xff0c;服务器缩减一半&#xff0c;性能反而提升4倍&am…

非流式语音合成和流式语音合成

语音合成技术&#xff1a;https://zhuanlan.zhihu.com/p/113282101 流式语音合成技术揭秘与实践 1、非流式语音合成&#xff0c;一次性输入文字&#xff0c;一次性输出语音&#xff0c;注重语音合成系统的 整体运算速度 &#xff0c;不适合做语音交互&#xff1b;流式语音合成…

如何在 Linux Mint 21 上安装 VirtualBox 7?

VirtualBox 是一款开源的虚拟化软件&#xff0c;它可以让你在一台计算机上同时运行多个操作系统。本文将详细介绍如何在 Linux Mint 21 上安装 VirtualBox 7。以下是安装过程的详细步骤&#xff1a; 步骤一&#xff1a;下载 VirtualBox 7 首先&#xff0c;你需要下载 VirtualB…

姜春宇:数据治理五大发展趋势

4月27日在2023数据治理新实践峰会上&#xff0c;大数据技术标准推进委员会副主席姜春宇先生以《数据治理发展趋势》为主题为大家分享了数据的价值和最新发展趋势。 以下为姜春宇先生的演讲实录&#xff0c;为了方便阅读&#xff0c;小编做了一些字句修改和文本优化。 大家上午…

如何在 OpenSUSE 上安装 VirtualBox 7?

VirtualBox 是一款开源的虚拟化软件&#xff0c;允许用户在单个计算机上运行多个操作系统。本文将详细介绍如何在 OpenSUSE 上安装 VirtualBox 7。以下是安装过程的步骤&#xff1a; 步骤一&#xff1a;下载 VirtualBox 7 首先&#xff0c;我们需要下载 VirtualBox 7 的安装包…

从火灾演习中认识火灾以及火灾发生时如何确保消防设备的正常运行

安科瑞虞佳豪 火光、浓烟、热浪……5月10日&#xff0c;在宁波高新区光华路119号一座废弃厂房内&#xff0c;一场特殊的“火灾”正在发生&#xff01; 据悉&#xff0c;宁波市消防部门组织开展了一场真人真火的火灾实验。目的是为了让人们直观感受火灾的危险&#xff0c;进一…

vcruntime140_1.dll丢失怎样修复,推荐4个vcruntime140_1.dll丢失的修复方法

vcruntime140_1.dll文件是Microsoft Visual C Redistributable for Visual Studio 2015运行库的一部分&#xff0c;它是一个用于支持Visual C构建的应用程序的系统文件。这个文件包含了在运行C程序时所需要的函数和类库&#xff0c;主要负责向应用程序提供运行时环境。如果电脑…

布隆过滤器和布谷鸟过滤器

过滤器使用场景&#xff1a; 比如有如下几个需求&#xff1a; 1.原本有10亿个号码&#xff0c;现在又来了10万个号码&#xff0c;要快速准确判断这10万个号码是否在10亿个号码库中&#xff1f;   解决办法一&#xff1a;将10亿个号码存入数据库中&#xff0c;进行数据库查询&…

听说小破站新上一批“高质量”的视频,于是怀揣着“学习”的目的,我用Python将他们全部采集了下来

事情是这样的&#xff0c;昨晚室友悄咪咪的拉着我去他的电脑&#xff0c;说带我欣赏一点高雅的作品&#xff0c;于是这一坐下&#xff0c;便是一晚上… 作为一个乐于分享的博主&#xff0c;本来我是决定直接分享的&#xff0c;但是转念一想&#xff0c;授人以鱼不如授人以渔&am…

如何看待 30 岁学云计算,转行做云计算运维这件事?

作为IT培训行业的从业人员&#xff0c;30岁学云计算转行不算什么的&#xff0c;还有38岁想学云计算的呢&#xff01;最主要的是个人兴趣和意向&#xff0c;当然这个年龄阶段还会考虑的一点就是目前的收入与家庭支出的问题。不过这位38岁的“大龄”学员学习的主要目的不是说去找…

不是吧,交换机坏了你还只会这么排查?

又见面了&#xff0c;我的网工朋友 上次给你分享了交换机和路由器的对接上网配置案例&#xff0c;还记得吗&#xff1f; 今天这篇&#xff0c;和你聊聊交换机接口故障。 接口故障这件事&#xff0c;对咱们网工来说其实算是家常便饭了。 工作到现在&#xff0c;你复盘一下&a…

卷积、相关、匹配滤波、脉冲压缩以及模糊函数

文章目录 【 1. 卷积 】连续卷积离散卷积 【 2.相关 】自相关互相关 【 3.匹配滤波 】滤波器模型有色噪声 时滤波器的特性白噪声 时滤波器的特性 【 4.脉冲压缩】时域脉冲压缩频域脉冲压缩 【 5.模糊函数 】【 6.四者之间的关系 】相关和卷积之间的关系 【 7.参考文献 】 【 1.…

day10 - 使用canny算子进行人像勾勒

本期主要介绍canny算子&#xff0c;了解canny算子的流程以及各个流程的原理和实现。 ​ 完成本期内容&#xff0c;你可以&#xff1a; 了解canny算子的流程和应用 若要运行案例代码&#xff0c;你需要有&#xff1a; 操作系统&#xff1a;Ubuntu 16 以上 或者 Windows10 工…

Kubernetes(k8s)集群安装部署

一. 环境说明 名称IP系统配置主控节点192.168.136.11Rocky9.22核4G工作节点1192.168.136.12Rocky9.22核4G工作节点2192.168.136.13Rocky9.22核4G 二. 系统先决条件配置(所有节点&#xff09; 2.1 关闭防火墙 防火墙可能会导致重复的防火墙规则和破坏kube-proxy&#xff0c;…