我使用stm32cubemx为stm32f103生成了一个RTOS-cmsis_v1项目,并定义了四个任务,如下所示:
osThreadDef(defaultTask, StartDefaultTask, osPriorityLow, 0, 128);
defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);
/* definition and creation of normal */
osThreadDef(normal, StartTask02, osPriorityNormal, 0, 128);
normalHandle = osThreadCreate(osThread(normal), NULL);
/* definition and creation of high */
osThreadDef(high, StartTask03, osPriorityAboveNormal, 0, 128);
highHandle = osThreadCreate(osThread(high), NULL);
/* definition and creation of low */
osThreadDef(low, StartTask04, osPriorityBelowNormal, 0, 128);
lowHandle = osThreadCreate(osThread(low), NULL);
二进制信号量:
osSemaphoreDef(binsem);
binsemHandle = osSemaphoreCreate(osSemaphore(binsem), 1);
以及一个每 1 秒生成一次中断的计时器。 任务函数和中断服务程序:
void StartTask02(void const * argument)
{
for (;;) {
sprintf(buffer, "normal waiting for semaphore\r\n");
printout();
if (xSemaphoreTake(binsemHandle, portMAX_DELAY)) {
sprintf(buffer, "normal begin\r\n");
printout();
sprintf(buffer, "normal end\r\n");
printout();
}
osDelay(1000);
}
}
void StartTask03(void const * argument)
{
for (;;) {
sprintf(buffer, "high begin\r\n");
printout();
sprintf(buffer, "high end\r\n");
printout();
osDelay(1000);
}
}
void StartTask04(void const * argument)
{
for (;;) {
sprintf(buffer, "low begin\r\n");
printout();
sprintf(buffer, "low end\r\n\r\n");
printout();
osDelay(1000);
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM1) {
static BaseType_t xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(binsemHandle, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken) {
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
if (htim->Instance == TIM4) {
HAL_IncTick();
}
}
问题是当我运行代码时,中断仅执行一次,并且没有任何任务正在运行。如果我注释掉
xSemaphoreGiveFromISR(binsemHandle, NULL);
所有任务(除了正常任务,因为信号量检查)都会运行,并且每秒都会激活中断。
TIM4
用作时基,TIM1
中断的默认优先级为14
。
基于 this 解决方案,我检查了 configKERNEL_INTERRUPT_PRIORITY
的值,并将其定义为 15 << 4
。
并且同样基于this解决方案,将configMAX_SYSCALL_INTERRUPT_PRIORITY
的值设置为5 << 4
。但是将 TIM1
中断的优先级更改为 15、240、255 等也没有帮助。
在任务之间传递和获取信号量没有问题。
我还测试了在
xSemaphoreGiveFromISR
函数中使用 TIM1_IRQ_Handler()
而不是回调,但没有运气。使用 xTaskNotifyGive
代替信号量也不起作用。
所以我将定时器中断优先级设置为 7 并使用
configASSERT()
检查它,没有错误。打印寄存器的值也会得到值 112,即 7<<4
。
/* Initialize interrupts */
//MX_NVIC_Init();
/* USER CODE BEGIN 2 */
NVIC_SetPriority(TIM1_IRQn, 7);
NVIC_EnableIRQ(TIM1_IRQn);
sprintf(buffer, "%u\r\n", NVIC->IP[TIM1_IRQn]);
printout();
configASSERT(NVIC->IP[TIM1_IRQn] == 112);
这里是
FreeRTOSconfig.h
内核和最大优先级值的文件定义:
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
并根据this页面,以及下图
计时器应该能够从 ISR 调用 freertos API 函数。
当注释掉ISR回调函数中的
xSemaphoreGiveFromISR()
时,所有任务和中断都起作用。但取消注释会导致崩溃,这意味着计时器中断不允许从 ISR 调用 freertos API 函数。
我在这里错过了什么??
这也是让任务通知发挥作用的方法。 在 osKernelStart() 之后调用 HAL_TIM_Base_Start_IT()。
好的!所以问题是我在 main.c
文件中启动调度程序之前
启用了计时器中断:
HAL_TIM_Base_Start_IT(&htim1);
/* Call init function for freertos objects (in freertos.c) */
MX_FREERTOS_Init();
/* Start scheduler */
osKernelStart();
因此,将 HAL_TIM_Base_Start_IT(&htim1);
函数移至
freertos.c
并将其放在AFTER 信号量定义之后,一切正常(甚至从 ISR 打印)。
osSemaphoreDef(binsem);
binsemHandle = osSemaphoreCreate(osSemaphore(binsem), 1);
HAL_TIM_Base_Start_IT(&htim1);
我在 freeRTOS 文档中发现了一些文档提到了这一点,但我没有注意它,现在我找不到它在哪里。如果我找到该文档,我会在此处添加链接。