在使用zynq中的freertos时,程序中存在中断引发vtaskdelay卡死,但是没有报错,该如何处理?
环境:vivado2017;正点原子的xc7z010clg400-1
我的中断是从原子哥教程里的axi按键中断中复制来的
这是中断配置,这个函数的调用位置在一个优先级最低的task_start里,因为不这样的话中断无法触发
具体问题:
这个任务是每隔1s改变led的状态,在无中断的另一个application中能正确切换,但是在这个程序中输出情况如下:
进去了就出不来了,在task.c里添加了一系列xilprinf后能发现最后一条能成功输出的prinf位于1354行处(即在这一行的if{后的prinf能输出,而1356行的不行
/*ID define*/
#define AXI_GPIO_ID XPAR_AXI_GPIO_0_DEVICE_ID //PL端 AXI GPIO器件 ID
#define GPIO_INT_ID XPAR_FABRIC_GPIO_0_VEC_ID //PL端 AXI GPIO中断 ID
#define SCUGIC_ID XPAR_SCUGIC_0_DEVICE_ID //中断控制器 ID
#define GPIOPS_ID XPAR_XGPIOPS_0_DEVICE_ID
/*time define*/
#define DELAY_1_SECOND 1000UL
#define DELAY_001_SECOND 10UL
#define DELAY_05_SECOND 500UL
/*landk define*/
#define MIO_LED0 7 //PS_LED0 连接到 MIO7 8
#define MIO_LED1 8 //PS_LED1 连接到 MIO8 9
#define MIO_KEY0 12 //PS_KEY0 连接到 MIO7
/*axi define*/
#define KEY_CHANNEL 1 //PL按键使用AXI GPIO通道1
#define KEY_MASK XGPIO_IR_CH1_MASK //通道1的位定义
/*led define*/
#define LIANG 0x1
#define AN 0x0
/*task*/
#define SIZE (unsigned short) 1000
static void task_start( void *pvParameters );
static void task_led_t( void *pvParameters );
static void task_led_k( void *pvParameters );
/*intr*/
void axi_gpio_handler(void *CallbackRef); //中断服务函数
/*intr*/
static TaskHandle_t Htask_start;
static TaskHandle_t Htask_led_t;
static TaskHandle_t Htask_led_k;
//static SemaphoreHandle_t Hsem=NULL;
XScuGic scugic_inst; //中断控制器 驱动实例
XScuGic_Config * scugic_cfg_ptr; //中断控制器 配置信息
XGpioPs gpiops_inst; //PS端 GPIO 驱动实例
XGpioPs_Config * gpiops_cfg_ptr; //PS端 GPIO 配置信息
XGpio axi_gpio_inst; //PL端 AXI GPIO 驱动实例
u32 flag;
int flag_task;
int flag_sus=0;
const TickType_t x1second = pdMS_TO_TICKS( DELAY_1_SECOND );
const TickType_t x001second = pdMS_TO_TICKS( DELAY_001_SECOND );
const TickType_t x05second = pdMS_TO_TICKS( DELAY_05_SECOND );
void chushihua(){
gpiops_cfg_ptr = XGpioPs_LookupConfig(GPIOPS_ID);
XGpioPs_CfgInitialize(&gpiops_inst, gpiops_cfg_ptr, gpiops_cfg_ptr->BaseAddr);
XGpioPs_SetDirectionPin(&gpiops_inst, MIO_LED0, 1);
XGpioPs_SetDirectionPin(&gpiops_inst, MIO_LED1, 1);
XGpioPs_SetOutputEnablePin(&gpiops_inst, MIO_LED0, 1);
XGpioPs_SetOutputEnablePin(&gpiops_inst, MIO_LED1, 1);
XGpioPs_SetDirectionPin(&gpiops_inst, MIO_KEY0, 0);
}
int main( void ){
xil_printf( "Hello from Freertos example mainintm\r\n" );
//vSemaphoreCreateBinary(Hsem);
xTaskCreate( task_start,
( const char * ) "start",
SIZE,
NULL,
tskIDLE_PRIORITY+3,
&Htask_start );
vTaskStartScheduler();
for( ;; );
}
void axi_intrcsh(void){
XGpio_Initialize(&axi_gpio_inst, AXI_GPIO_ID);
XGpio_SetDataDirection(&axi_gpio_inst, KEY_CHANNEL, 1); //设置AXI GPIO通道1为输入
XGpio_InterruptEnable(&axi_gpio_inst, KEY_MASK); //使能通道1中断
XGpio_InterruptGlobalEnable(&axi_gpio_inst); //使能AXI GPIO全局中断
scugic_cfg_ptr = XScuGic_LookupConfig(SCUGIC_ID);
XScuGic_CfgInitialize(&scugic_inst, scugic_cfg_ptr, scugic_cfg_ptr->CpuBaseAddress);
XScuGic_SetPriorityTriggerType(&scugic_inst, GPIO_INT_ID, 0x04, 0x1);
//关联中断ID和中断处理函数
XScuGic_Connect(&scugic_inst, GPIO_INT_ID, axi_gpio_handler, &axi_gpio_inst);
//使能AXI GPIO中断
XScuGic_Enable(&scugic_inst, GPIO_INT_ID);
//设置并打开中断异常处理功能
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler, &scugic_inst);
Xil_ExceptionEnable();
}
static void task_start( void *pvParameters )
{
//初始化中断控制器驱动
chushihua();
axi_intrcsh();
portENABLE_INTERRUPTS();
taskENTER_CRITICAL();
xTaskCreate( task_led_t,
( const char * ) "time05",
SIZE,
NULL,
tskIDLE_PRIORITY+2,
&Htask_led_t );
xTaskCreate( task_led_k,
( const char * ) "key",
SIZE,
NULL,
tskIDLE_PRIORITY+1,
&Htask_led_k );
flag_task=1;
taskEXIT_CRITICAL();
vTaskDelete(NULL);
for (;;) {
}
}
static void task_led_t( void *pvParameters ){
for( ;; ){
xil_printf( "tskb\r\n" );
//sleep(1);
vTaskDelay( x1second );
xil_printf( "tska\r\n" );
if ( flag_sus==1 ){
xil_printf( "gus\r\n" );
flag_sus=0;
flag_task=0;
vTaskSuspend(Htask_led_t);
}
XGpioPs_WritePin(&gpiops_inst, MIO_LED1,
XGpioPs_ReadPin(&gpiops_inst, MIO_KEY0));
}
}
static void task_led_k( void *pvParameters ){
for( ;; ){
XGpioPs_WritePin(&gpiops_inst, MIO_LED0,
~XGpioPs_ReadPin(&gpiops_inst, MIO_KEY0));
}
}
void axi_gpio_handler(void *CallbackRef){
int key_value = 1;
XGpio *GpioPtr = (XGpio *)CallbackRef;
print("Interrupt Detected!\n");
XGpio_InterruptDisable(GpioPtr, KEY_MASK); //关闭AXI GPIO中断使能
key_value = XGpio_DiscreteRead(GpioPtr, KEY_CHANNEL); //读取按键数据
if(!key_value){ //判断按键按下
if(flag_task==1){
xil_printf( "gu\r\n" );
flag_sus=1;
}
else{
if(xTaskResumeFromISR(Htask_led_t)== pdTRUE){
portYIELD_FROM_ISR(pdTRUE);
flag_task=1;
};
xil_printf( "fu\r\n" );
}
}
sleep(1); //延时1s,按键消抖
XGpio_InterruptClear(GpioPtr, KEY_MASK); //清除中断
XGpio_InterruptEnable(GpioPtr, KEY_MASK); //使能AXI GPIO中断
}
该回答引用GPTᴼᴾᴱᴺᴬᴵ
在 FreeRTOS 中,如果在中断处理程序(ISR)中使用了 vTaskDelay(),那么就必须使用 FreeRTOS API 中专门为 ISR 提供的延迟函数 vTaskDelayFromISR()。因为 vTaskDelay() 函数会让当前任务进入 Blocked(阻塞)状态,而 ISR 不能阻塞,否则会导致系统死锁。
·
在你的代码中,我没有看到你在中断处理函数 axi_gpio_handler() 中使用 vTaskDelay(),因此猜测你是在中断处理函数中调用了一个任务中的 vTaskDelay() 函数,从而导致任务阻塞。
·
解决这个问题的方法是将 vTaskDelay() 函数替换为专门为 ISR 提供的 vTaskDelayFromISR() 函数,这个函数可以在中断处理函数中使用。
·
另外,在中断处理函数中,建议使用 xHigherPriorityTaskWoken 参数来通知 FreeRTOS 内核是否需要立即进行任务调度。当 xHigherPriorityTaskWoken 参数设置为非零值时,表示有更高优先级的任务需要立即执行,此时 FreeRTOS 内核会在退出中断处理函数后立即进行任务切换。
·
例如,你的中断处理函数可以修改为如下代码:
void axi_gpio_handler(void *CallbackRef)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// 处理中断事件
// 通知任务立即执行
vTaskNotifyGiveFromISR(task_handle, &xHigherPriorityTaskWoken);
// 如果有更高优先级的任务需要立即执行,立即进行任务调度
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
在任务中,使用 ulTaskNotifyTake() 函数等待中断处理函数发出的通知。这个函数可以等待通知,并返回通知的值,可以根据通知的值来判断中断处理函数处理的结果。例如,如果中断处理函数是用来处理按键的,可以根据通知的值来判断按下了哪个按键。
例如,你的任务可以修改为如下代码:
void task_led_t(void *pvParameters)
{
uint32_t notification_value;
for (;;)
{
// 等待中断处理函数通知
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
// 处理中断事件,例如修改 LED 状态
// 延迟 1 秒钟
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
另外,建议在任务中使用 pdMS_TO_TICKS() 函数将毫秒转换为 FreeRTOS 内核使用的时钟节拍数。这个函数可以保证在不同的时钟频率下都可以正确地计算出时钟节拍数。
在使用FreeRTOS时,程序中存在中断引发vTaskDelay卡死的情况,但没有报错,这可能是因为中断处理函数中使用了vTaskDelay函数,而vTaskDelay函数不能在中断处理函数中使用。这是因为在中断处理函数中,任务调度器被挂起,不能切换任务。如果在中断处理函数中使用vTaskDelay函数,则当前任务会被阻塞,无法切换到其他任务,从而导致整个系统停止响应。
要解决这个问题,可以采用以下方法:
将vTaskDelay函数移出中断处理函数:将中断处理函数中使用vTaskDelay函数的代码移出中断处理函数,放到一个普通的任务中执行。这样可以保证vTaskDelay函数在任务中被正确执行。
使用信号量:在中断处理函数中,可以使用信号量来通知任务执行某些操作。中断处理函数可以释放信号量,任务则等待信号量并执行相应操作。
另外,你提到在任务中添加了一系列xil_printf输出,但最后一条输出的位置在1354行,之后就无法输出。这可能是因为xil_printf函数使用了缓冲区,当缓冲区满时会停止输出。可以尝试将xil_printf的缓冲区大小调大,或者在输出完成后使用fflush函数刷新缓冲区,以确保输出能够正常完成。
总之,解决这个问题的关键是确保中断处理函数中不使用vTaskDelay函数,以及在任务中输出信息时注意缓冲区大小和刷新。
根据你提供的信息,可能存在以下几个可能导致问题的原因:
1.中断优先级设置不正确
如果中断优先级设置得太高,就有可能在执行中断服务程序期间,一直阻止任务执行。建议将中断优先级设置得低一些,以确保任务能够在中断结束后恢复执行。
2.vTaskDelay() 函数的调用位置不正确
在使用 FreeRTOS 中的 vTaskDelay() 函数时,需要确保该函数是在任务中调用的。如果该函数被调用时中断处于活动状态,就会出现阻塞问题。因此,需要在合适的地方调用该函数,以确保任务能够正确地休眠。
3.中断服务程序没有正确地清除中断标志
在使用中断时,需要确保中断服务程序正确地清除中断标志,否则会导致中断一直被触发。可以在中断服务程序中使用 XIntc_Acknowledge() 函数清除中断标志。
4.硬件问题
如果以上几种可能的原因都被排除了,还需要考虑硬件问题。可以检查硬件连接是否正确,是否有短路等问题。
综上所述,针对你的问题,建议你检查以上几种可能的原因,并尝试对问题进行逐一排除,以确定问题的具体原因。如果问题仍然存在,可以参考 FreeRTOS 的官方文档、论坛等资源进行更深入的学习和研究。
还有其他一些可能导致中断引起的vTaskDelay卡死的原因:
1.中断优先级问题:当中断函数的优先级高于FreeRTOS任务的优先级时,中断服务例程可能会打断正在运行的任务并在该任务挂起期间运行,这可能导致任务一直挂起并在任务中调用vTaskDelay()时卡死。
2.中断处理程序的实现:在实现中断处理程序时,必须正确地设置中断的优先级、清除中断标志位和重新使能中断。如果有任何错误,它可能会导致中断无法正常工作并卡死任务。
3.资源竞争问题:如果中断和任务之间存在资源共享或竞争,可能会导致中断在访问共享资源时阻塞任务,并在调用vTaskDelay()时卡死。
4.栈溢出问题:当任务的堆栈空间不足时,它可能会导致任务在调用vTaskDelay()时卡死。
针对这些可能导致问题的原因,你可以尝试以下的解决方法:
1.检查中断和任务的优先级设置,并确保中断服务程序不会打断正在运行的任务。
2.检查中断处理程序的实现,确保中断处理程序能够正确地清除标志位、重新使能中断和适当地访问共享资源。
3.检查任务和中断之间的资源共享,确保使用适当的同步机制来避免竞争。
4.检查任务的堆栈大小,并确保它们足够大,以避免栈溢出问题。
此外,你还可以通过使用调试器或日志记录来进一步诊断问题,以确定中断服务程序中的问题所在。
最后,如果你需要更详细的帮助,建议你查看FreeRTOS官方文档和示例代码,或者咨询专业的嵌入式系统开发人员和技术支持人员。
FreeRTOS官方文档和示例代码可以在官网上下载:
1.FreeRTOS官网:https://www.freertos.org/
2.FreeRTOS官方文档:https://www.freertos.org/Documentation/RTOS_book.html
3.FreeRTOS示例代码:https://www.freertos.org/RTOS_ports.html
在官网上,你可以找到最新的FreeRTOS版本、文档和示例代码,并了解FreeRTOS的详细信息和使用方法。在下载示例代码时,你可以选择适合你的硬件平台和编译器的版本,并查看示例代码中的注释和说明,了解如何使用FreeRTOS进行开发。
同时,也可以通过其他途径获取FreeRTOS相关的文档和示例代码,例如在GitHub上搜索FreeRTOS相关的项目,或在论坛、社区中寻求帮助和资源分享。
问题出在这里,freertos的任务调度器vTaskStartScheduler()调用时,会进行中断和定时器的初始化(在portZynq7000.c的115行),使用的是xInterruptController,所以在后面的中断设置部分的代码中,不应再次初始化,而是只要进行connect和enable,并在一开始extern XScuGic xInterruptController
将原代码
scugic_cfg_ptr = XScuGic_LookupConfig(SCUGIC_ID);
XScuGic_CfgInitialize(&scugic_inst, scugic_cfg_ptr, scugic_cfg_ptr->CpuBaseAddress);
XScuGic_SetPriorityTriggerType(&scugic_inst, GPIO_INT_ID, 0x04, 0x1);
//关联中断ID和中断处理函数
XScuGic_Connect(&scugic_inst, GPIO_INT_ID, axi_gpio_handler, &axi_gpio_inst);
//使能AXI GPIO中断
XScuGic_Enable(&scugic_inst, GPIO_INT_ID);
//设置并打开中断异常处理功能
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler, &scugic_inst);
Xil_ExceptionEnable();
改为
XScuGic_SetPriorityTriggerType(&xInterruptController, GPIO_INT_ID, 0xA0,0x01);
//关联中断ID和中断处理函数
XScuGic_Connect(&xInterruptController, GPIO_INT_ID, axi_gpio_handler, &axi_gpio_inst);
//使能AXI GPIO中断
XScuGic_Enable(&xInterruptController, GPIO_INT_ID);
即可