OpenHarmony开发者论坛

标题: 基于cortex-m4移植OpenHarmony3.0无法触发任务切换 [打印本页]

作者: W1sperZ    时间: 2024-10-9 15:28
标题: 基于cortex-m4移植OpenHarmony3.0无法触发任务切换
[md]### 【问题描述】

1. 介绍问题现象和发生的背景
   已经成功编译和烧录系统,打印正常,systick打印出来看起来也是正常在运行,但是无法触发任务切换
   current Tick0 236253
   OsGetCurrSchedTimeCycle();1 357691
   OsGetCurrSchedTimeCycle();2 598186
   上述是打印systick
2. 相关的代码(请勿使用截图)

   这是部分代码

   ```

   VOID TaskSampleEntry2(VOID)
   {
       while (1) {
           printf("TaskSampleEntry2 running...\n");
           (VOID)LOS_TaskDelay(2000); /* 2000 millisecond */
       }
   }

   VOID TaskSampleEntry1(VOID)
   {
       while (1) {
           printf("TaskSampleEntry1 running...\n");
           (VOID)LOS_TaskDelay(2000); /* 2000 millisecond */
       }
   }
   VOID TaskSample(VOID)
   {
       UINT32 uwRet;
       UINT32 taskID1;
       UINT32 taskID2;
       TSK_INIT_PARAM_S stTask = {0};

       stTask.pfnTaskEntry = (TSK_ENTRY_FUNC)TaskSampleEntry1;
       stTask.uwStackSize = 0x800;
       stTask.pcName = "TaskSampleEntry1";
       stTask.usTaskPrio = 6; /* Os task priority is 6 */
       uwRet = LOS_TaskCreate(&taskID1, &stTask);
       if (uwRet != LOS_OK) {
           printf("Task1 create failed\n");
       }

       stTask.pfnTaskEntry = (TSK_ENTRY_FUNC)TaskSampleEntry2;
       stTask.uwStackSize = 0x800;
       stTask.pcName = "TaskSampleEntry2";
       stTask.usTaskPrio = 6; /* Os task priority is 6 */
       uwRet = LOS_TaskCreate(&taskID2, &stTask);
       if (uwRet != LOS_OK) {
           printf("Task2 create failed\n");
       }
   }
   /**
    * @brief This is the ohos entry, and you could call this in your main funciton after the
    *        necessary hardware has been initialized.
    */
   void OHOS_Boot(void)
   {
       UINT32 ret;
       before_ohos_run();
       ret = LOS_KernelInit();
       if (ret == LOS_OK) {
           OHOS_SystemInit();
           TaskSample();
           LOS_Start();
       }
       return;  // and should never come here
   }
   ```
3. 运行结果、错误截图
   这是运行结果

   ![image.png](https://forums-obs.openharmony.c ... a8lxdeugoxmy4s4.png "image.png")
4. 我尝试过的解决方法和结果
5. 我想要达到的结果

   能正常进行线程切换

### 【运行环境】

硬件:

corex-m4
ROM版本:OpenHarmony 3.0
DevEvoStudio版本:
SDK版本:OpenHarmony 3.0
[/md]
作者: W1sperZ    时间: 2024-10-16 11:50
标题: 基于cortex-m4移植OpenHarmony3.0无法触发任务切换
[md]我尝试检查向量表,其中代码如下

```
#define ISR_VECTOR_START_ADDRESS 0x00000000  // 中断向量表的起始地址
#define NUM_INTERRUPTS 16                    // 向量表中的中断数目 (可以根据需求调整)
// 函数指针类型,用于表示中断处理函数
typedef void (*ISRHandler)(void);

void PrintVectorTable(void)
{
    ISRHandler *vectorTable = (ISRHandler *)ISR_VECTOR_START_ADDRESS;
  
    // 遍历并打印中断向量表中每个中断处理函数的地址
    for (int i = 0; i < NUM_INTERRUPTS; i++) {
        printf("Interrupt %04d handler address: %p\n", i, (void *)vectorTable);
    }
}
```

打印结果如下

```
Interrupt 0000 handler address: 20002C0C
Interrupt 0001 handler address: 00000941
Interrupt 0002 handler address: 00000AC9
Interrupt 0003 handler address: 00000AD5
Interrupt 0004 handler address: 00000B8B
Interrupt 0005 handler address: 00000B67
Interrupt 0006 handler address: 00000BAF
Interrupt 0007 handler address: 00000000
Interrupt 0008 handler address: 00000000
Interrupt 0009 handler address: 00000000
Interrupt 0010 handler address: 00000000
Interrupt 0011 handler address: 0000098F
Interrupt 0012 handler address: 0000098F
Interrupt 0013 handler address: 00000000
Interrupt 0014 handler address: 00000A2F
Interrupt 0015 handler address: 00001DC5
```

打印出的结果和 `Interrupt 0014 handler address: 00000A2F`和map文件中的 `HalPendSV`的地址是一直的 `0x0000000000000a2e                HalPendSV`,由于 ARM Cortex-M 处理器使用的是 **Thumb 指令集**,所以函数地址的最低有效位(LSB,最低位)通常用于标识这是一个 Thumb 函数。也就是说,当地址为 `0x0000012F` 时,处理器会认为这是 `0x0000012E` 地址上的 Thumb 函数,并忽略最低位的 `1`,仍然能够正确执行 `HalPendSV`。所以第一点 `PendSV`中断处理函数是正确的
[/md]
作者: W1sperZ    时间: 2024-10-16 11:55
标题: 基于cortex-m4移植OpenHarmony3.0无法触发任务切换
[md]其次我又验证了调度,首先在创建完成线程之后打印出系统中所有的线程

```
TID  Priority   Status StackSize WaterLine StackPoint TopOfStack EventMask  SemID  TaskEntry name
---  -------- -------- --------- --------- ---------- ---------- --------- ------ ---------- ----
   0       31     Ready     0x400      0xcc 0x20004614 0x200042e0         0 0xffff    0x393d IdleCore000                  
   1        6     Ready     0x800      0xcc 0x20004e1c 0x200046e8         0 0xffff    0x6921 TaskSampleEntry1              
   2        6     Ready     0x800      0xcc 0x20005624 0x20004ef0         0 0xffff    0x68f5 TaskSampleEntry2
```

在线程 `TaskSampleEntry1`中获得了下一个要执行的线程的id,并且打印出来

```
TaskSampleEntry1 running...  currTime 12201708 next task 2
```

再结合内核调试信息

```
VOID LOS_Schedule(VOID)
{
    PRINTK("Entry function %s   ", __FUNCTION__);
    if (OsCheckKernelRunning()) {
        PRINTK("schedule\r\n");
        ArchTaskSchedule();
        PRINTK("end schedule\r\n");
    }
}
```

以及结果,可以证明完全走完所有调度流程

```
TaskSampleEntry1 running...  currTime 12583571 next task 2
Entry function LOS_Schedule   schedule
end schedule
```
[/md]
作者: W1sperZ    时间: 2024-10-16 11:59
标题: 基于cortex-m4移植OpenHarmony3.0无法触发任务切换
[md]再之后,我怀疑是中断设置不正确,又添加了如下的调试代码

```
#define OS_NVIC_PENDSVSET    0x10000000     // 设置PendSV的位
#define OS_NVIC_INT_CTRL     0xE000ED04     // 中断控制状态寄存器
#define OS_NVIC_PENDSV_PRI   0xF0000000     // PendSV优先级掩码
#define OS_NVIC_SYSPRI2      0xE000ED20     // PendSV优先级寄存器

// 获取PendSV优先级并确保其为最低优先级
void CheckPendSVPriority(void) {
    uint32_t regVal = *((volatile uint32_t *)OS_NVIC_SYSPRI2);
    uint8_t pendSVPriority = (regVal >> 16) & 0xFF;
  
    if (pendSVPriority != 0xF0) {
        // 如果优先级不是最低,设置为最低优先级 (0xF0)
        regVal &= ~OS_NVIC_PENDSV_PRI;      // 清除优先级位
        regVal |= (0xF0 << 16);             // 设置最低优先级
        *((volatile uint32_t *)OS_NVIC_SYSPRI2) = regVal;
        printf("PendSV priority was incorrect 0x%x, fixed to 0xF0\n", pendSVPriority);
    } else {
        printf("PendSV priority is correct (0xF0)\n");
    }
}

// 检查PendSV是否触发
uint8_t CheckPendSVStatus(void) {
    uint32_t regVal = *((volatile uint32_t *)OS_NVIC_INT_CTRL);
    if (regVal & OS_NVIC_PENDSVSET) {
        printf("PendSV is set\n");
        return 1;   // PendSV已触发
    } else {
        printf("PendSV is not set\n");
        return 0;   // PendSV未触发
    }
}

// 检查PRIMASK是否禁用了全局中断
uint8_t CheckInterruptStatus(void) {
    uint32_t primask;
    __asm volatile ("MRS %0, PRIMASK" : "=r" (primask));
    if (primask == 0) {
        printf("Interrupts are enabled\n");
        return 1;  // 中断已启用
    } else {
        printf("Interrupts are disabled\n");
        return 0;  // 中断被禁用
    }
}


// 函数返回 1 表示中断被禁用,返回 0 表示中断启用
uint32_t CheckPrimaskStatus(void) {
    uint32_t primask;
    __asm volatile ("MRS %0, PRIMASK" : "=r" (primask));  // 将 PRIMASK 的值存储到 primask 变量中
    return primask;
}

// 主检查函数,检查PendSV设置、优先级和中断状态
void CheckSystemStatus(void) {

    printf("CheckSystemStatus\r\n");
  
    // 检查并修正 PendSV 优先级
    CheckPendSVPriority();

    //检查 PendSV 中断是否触发
    if (!CheckPendSVStatus()) {
        // 如果未触发,尝试手动触发 PendSV
        printf("Manually triggering PendSV...\n");
        *((volatile uint32_t *)OS_NVIC_INT_CTRL) = OS_NVIC_PENDSVSET;
    }

    // 检查 PRIMASK 状态确认全局中断是否启用
    if (CheckPrimaskStatus()) {
        // 如果中断被禁用,启用中断
        __asm volatile ("CPSIE i");  // 使能中断
        printf("Interrupts were disabled, now enabled\n");
    } else {
        // 中断已经启用
        printf("Interrupts are enabled\n");
    }
}
```

其结果如下所示

```
CheckSystemStatus
PendSV priority was incorrect 0x0, fixed to 0xF0
PendSV is not set
Manually triggering PendSV...
Interrupts were disabled, now enabled
```

函数执行的位置在 `LOS_KernelIni`之后

```
void OHOS_Boot(void)
{
    UINT32 ret;
    before_ohos_run();
    ret = LOS_KernelInit();
    if (ret == LOS_OK) {
        TaskSample();
        PrintVectorTable();
        OsGetAllTskInfo();
        CheckSystemStatus();
        LOS_Start();
    }
    return;  // and should never come here
}
```

```
int main(void)
{
    int i = 0;
    hal_clock_init(hal_clk_150M);
    uart3_init();
    systick_init();
    OHOS_Boot();

    while(1);
    return 0;
}
```
[/md]
作者: fengyunrenwu    时间: 2024-10-22 14:11
判断一下TaskDelay的返回值,看taskDelay是否生效,打印print("TaskSampleEntry2 running...\n");和printf(TaskSampleEntry1 running...\n);时带上tick值,看看TaskDelay是否生效。
作者: W1sperZ    时间: 2024-10-25 10:36
回复 fengyunrenwu: task delay 并未生效,后来使用int cnt = 0xFFFFFF; while(cnt--);来做延时的,然后打印的tick是在变动的

VOID TaskSampleEntry1(VOID)
{
    while (1) {
        int cnt = 0xFFFFFF;
        while(cnt--);
        UINT64 currTime = LOS_SysCycleGet();
        printf("TaskSampleEntry1 running...  currTime %lld next task %d\r\n", currTime,LOS_NewTaskIDGet());
        // CheckSystemStatus();
        (VOID)LOS_TaskDelay(2000); /* 2000 millisecond */
        // LOS_Schedule();
        // LOS_TaskYield();
    }
}
打印信息如下所示
TaskSampleEntry1 running...  currTime 6609156 next task 2
Entry function LOS_Schedule   schedule
end schedule
TaskSampleEntry1 running...  currTime 6623871 next task 2
Entry function LOS_Schedule   schedule
end schedule
TaskSampleEntry1 running...  currTime 6638586 next task 2
Entry function LOS_Schedule   schedule
end schedule
TaskSampleEntry1 running...  currTime 6653301 next task 2
Entry function LOS_Schedule   schedule
end schedule
TaskSampleEntry1 running...  currTime 6668016 next task 2
Entry function LOS_Schedule   schedule
end schedule





欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/) Powered by Discuz! X3.5