S32K在FreeRTOS下的外设中断设置

文章讨论了在开发过程中遇到的外设中断优先级高于操作系统内核中断优先级导致的错误。当调用如LPSPI_DRV_MasterTransferBlocking的阻塞传输接口并在中断服务函数中尝试调用OSIF_SemaPost等内核接口时,由于中断抢占问题,系统可能出现错误。解决方案是设置外设中断优先级低于操作系统内核中断优先级,例如通过INT_SYS_SetPriority函数在S32K平台中调整中断优先级,确保正确抢占并避免冲突。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如果在开发过程中遇到如下报错,你就可以继续往下看:

configASSERT( ucCurrentPriority >= ucMaxSysCallPriority );

描述

在调用外设blocking数据传输接口时(比如LPSPI_DRV_MasterTransferBlocking),需要在外设中断产生时调用操作系统的信号量接口来传递传输状态,这里面其实存在一个中断优先级的问题。

在不设置该外设的中断优先级情况下,系统默认将其外设的中断优先级设置为0x00,可使用INT_SYS_GetPriority 接口进行查看。

此时,假如FreeRTOS配置中(FreeRTOSConfig.h),你的配置为:

configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY = 0x01
configPRIO_BITS = 4
configMAX_SYSCALL_INTERRUPT_PRIORITY = 0x01 << (8-configPRIO_BITS)

那么,就表示操作系统内核中断优先级为0x10

处于以上情况时,就会出现一种现象:当外设中断产生,在中断服务函数中去调用操作系统内核接口(如OSIF_SemaPost)就会出现系统内核无法抢占当前中断的情况,从而导致系统出错。因为按照数字越小,优先级越高的规则,外设中断优先级(0x00) 高于 操作系统内核中断优先级 (0x10),低优先级无法抢占高优先级

解决方法

在初始化外设驱动之前,设置该外设的中断优先级,使得该外设中断优先级低于(数字大于)操作系统内核中断优先级。

在S32K中,使用接口 INT_SYS_SetPriority(IRQn_Type irqNumber, uint8_t priority) 来设置外设的中断优先级,设置值与配置FEATURE_NVIC_PRIO_BITS有关。

比如SDK\platform\devices\S32K148\include\S32K148_features.h FEATURE_NVIC_PRIO_BITS = 4,则设置:

INT_SYS_SetPriority(LPSPI2_IRQn, 0x02);

表示设置SPI2的中断优先级为:(0x02  << FEATURE_NVIC_PRIO_BITS) = (0x02 << 4) = 0x20。

此时SPI2的外设中断优先级(0x20) 低于 操作系统内核中断优先级 (0x10),中断抢占问题得以解决,调用操作系统内核接口(如OSIF_SemaPost)成功,外设blocking数据传输接口得以正常使用。

推荐默认将外设中断优先级设置为支持的最低优先级,然后按需地逐步增加其优先级。

### S32K 微控制器上的 FreeRTOS 中断处理实现 在 S32K SDK 中,通过集成 FreeRTOS 实现了对 MCU 外设资源的有效管理和应用程序的任务调度功能[^1]。对于中断处理机制而言,FreeRTOS 提供了一套完善的 API 和配置选项来支持高效的中断响应和服务。 #### 配置优先级分组 为了确保系统能够正确区分不同级别的中断请求,需要合理设置 NVIC (Nested Vectored Interrupt Controller) 的优先级分组。通常情况下,建议将所有 ISRs 设置为相同的抢占优先级,并利用子优先级来进行进一步细分: ```c // 在 FreeRTOSConfig.h 文件中定义如下宏 #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15UL #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5UL ``` 这些参数决定了哪些硬件中断可以在执行 RTOS 函数调用时不被阻塞。 #### 创建互斥量保护临界区 当多个任务共享某些全局变量或访问同一片内存区域时,则可能引发竞态条件问题。为了避免这种情况发生,可以创建一个二值信号量作为互斥锁用于同步操作: ```c SemaphoreHandle_t xMutex; void vCreateMutex(void){ // 初始化互斥型信号量对象 xMutex = xSemaphoreCreateMutex(); } ``` 每当进入 ISR 或者其他上下文中涉及到对该资源的操作前都应先获取该互斥量;而在完成相应工作之后再释放它以便后续使用者继续获得权限。 #### 使用队列传递事件通知 除了直接修改标志位之外,还可以借助消息队列的方式向目标线程发送特定类型的命令或者状态更新信息。这有助于简化逻辑结构同时也提高了代码可读性和维护便利程度: ```c QueueHandle_t xQueue; BaseType_t xHigherPriorityTaskWoken = pdFALSE; void prvSetupHardware( void ) { /* Create the queue used by the interrupt to communicate with the task. */ xQueue = xQueueCreate( QUEUE_LENGTH, sizeof( uint8_t ) ); } void vAnISRHandlerFunction( void ) { BaseType_t xResult; do { // 向指定队列添加新元素 xResult = xQueueSendFromISR( xQueue, &ucMessageToSend, &xHigherPriorityTaskWoken ); if(xResult != pdPASS) { // 如果失败则尝试重新入队... } } while(/* some condition */); portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); } ``` 上述方法不仅适用于普通任务间通信场景下也能很好地适应于异步 I/O 控制流程之中。 #### 解决常见问题 - **死循环等待**:如果某个 ISR 占用了过多 CPU 时间而未能及时返回给调度器的话将会导致整个系统的崩溃。因此务必保持函数体尽可能简洁紧凑。 - **堆栈溢出风险**:由于嵌入式环境中可用 RAM 资源有限所以要特别注意防止因频繁分配临时缓冲区而导致意外终止的情况出现。 - **重入冲突隐患**:考虑到部分库函数内部可能存在静态局部变量等原因故而不宜随意从多处位置并发调用它们以免造成不可预见的结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值