K210 FreeRTOS SDK启动分析

一、目的

最近在开始使用K210 FreeRTOS SDK进行应用开发,但是在使用过程发现程序的行为和预期不一致,怀疑这个官方提供的FreeRTOS SDK适配的不是很完整,故本着学习的目的跟着代码分析一下启动过程。

二、必备知识

对freertos有一些基本了解,了解滴答时钟(tick)、任务、任务优先级、空闲任务等基本概念;知道如何配置freertos一些选项,通过修改FreeRTOSConfig.h进行配置。

diff --git a/lib/freertos/conf/FreeRTOSConfig.h b/lib/freertos/conf/FreeRTOSConfig.h
index dac0af6..14fd619 100755
--- a/lib/freertos/conf/FreeRTOSConfig.h
+++ b/lib/freertos/conf/FreeRTOSConfig.h
@@ -59,14 +59,14 @@
 /* clock */
 #define configCPU_CLOCK_HZ                                     uxPortGetCPUClock()
 #define configTICK_CLOCK_HZ                                    ( configCPU_CLOCK_HZ / 50 )
-#define configTICK_RATE_HZ                                     ( ( TickType_t ) 100 )
+#define configTICK_RATE_HZ                                     ( ( TickType_t ) 1000 )
 
 /* multithreading */
 #define configUSE_NEWLIB_REENTRANT                             1
 
 #define configUSE_PREEMPTION                                   1
 #define configUSE_PORT_OPTIMISED_TASK_SELECTION        0
-#define configMAX_PRIORITIES                                   ( 5 )
+#define configMAX_PRIORITIES                                   ( 16 )
 #define configMAX_TASK_NAME_LEN                                        ( 16 )
 #define configUSE_TRACE_FACILITY                               1
 #define configUSE_16_BIT_TICKS                                 0
@@ -108,9 +108,9 @@ enum
 #define configMAX_CO_ROUTINE_PRIORITIES                        ( 2 )
 
 /* Software timer definitions. */
-#define configUSE_TIMERS                                               0
-#define configTIMER_TASK_PRIORITY                              ( 0 )
-#define configTIMER_QUEUE_LENGTH                               2
+#define configUSE_TIMERS                                               1
+#define configTIMER_TASK_PRIORITY                              ( configMAX_PRIORITIES - 1 )
+#define configTIMER_QUEUE_LENGTH                               8
 #define configTIMER_TASK_STACK_DEPTH                   ( configMINIMAL_STACK_SIZE )

 其中configTICK_RATE_HZ设置tick频率,此处修改为1000Hz,即1ms;configMAX_PRIORITIES设置系统支持的最多优先级,值越大,优先级越高,创建任务时可以配置的最大优先级为configMAX_PRIORITIES -1;configUSE_TIMERS配置系统软件定时器,即rtos实现的软件定时器,一般情况下其优先级需要设置为最大,即configMAX_PRIORITIES -1;

三、启动分析

根据教程,我们是从src/hello_world/main.c程序开始熟悉sdk的调用。

#include <stdio.h>                                                              
                                                                                
int main() {                                                                    
    printf("Hello K210!!!\n");                                                  
    while (1);                                                                  
}   

此处有个疑问,谁调用了main?rtos是否已经启动了?

针对以上疑问,我们做了这样一个尝试。

#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"

static void task_0(void *args) {
    while (1) {
        printf("task 0 is polling\n");
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
    vTaskDelete(NULL);
}

static void task_1(void *args) {
    while (1) {
        printf("task 1 is polling\n");
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
    vTaskDelete(NULL);
}

int main() {
    printf("Hello K210!!!\n");
    xTaskCreateAtProcessor(0, task_0, "task0", 1024, NULL, 5, NULL);
    xTaskCreateAtProcessor(0, task_1, "task1", 1024, NULL, 5, NULL);
    while (1) {
        printf("main is polling\n");
        vTaskDelay(2000 / portTICK_PERIOD_MS);
    }
}

我们在core 0上面创建了两个任务,并且在mian函数里面也有循环打印。通过编译烧写验证后,确认rtos已经正常工作。

我们知道freertos是通过vTaskStartScheduler接口调用开启多任务调度的,我们跟踪源码lib/freertos/os_entry.c

/* Copyright 2018 Canaan Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "FreeRTOS.h"
#include "core_sync.h"
#include "kernel/device_priv.h"
#include "task.h"
#include <clint.h>
#include <encoding.h>
#include <fpioa.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    int (*user_main)(int, char **);
    int ret;
} main_thunk_param_t;

extern void __libc_init_array(void);
extern void __libc_fini_array(void);

static StaticTask_t s_idle_task[portNUM_PROCESSORS];
static StackType_t s_idle_task_stack[portNUM_PROCESSORS][configMINIMAL_STACK_SIZE];
static StaticTask_t s_timer_task[portNUM_PROCESSORS];
static StackType_t s_timer_task_stack[portNUM_PROCESSORS][configMINIMAL_STACK_SIZE];

void start_scheduler(int core_id);

int __attribute__((weak)) configure_fpioa()
{
    return 0;
}

static void main_thunk(void *p)
{
    /* Register finalization function */
    atexit(__libc_fini_array);
    /* Init libc array for C++ */
    __libc_init_array();

    install_hal();
    install_drivers();
    configure_fpioa();

    main_thunk_param_t *param = (main_thunk_param_t *)p;
    param->ret = param->user_main(0, 0);
}

static void os_entry_core1()
{
    clear_csr(mie, MIP_MTIP);
    clint_ipi_enable();
    set_csr(mstatus, MSTATUS_MIE);

    vTaskStartScheduler();
}

int os_entry(int (*user_main)(int, char **))
{
    clear_csr(mie, MIP_MTIP);
    clint_ipi_enable();
    set_csr(mstatus, MSTATUS_MIE);

    TaskHandle_t mainTask;
    main_thunk_param_t param = {};
    param.user_main = user_main;

    if (xTaskCreate(main_thunk, "Core 0 Main", configMAIN_TASK_STACK_SIZE, &param, configMAIN_TASK_PRIORITY, &mainTask) != pdPASS)
    {
        return -1;
    }

    core_sync_awaken((uintptr_t)os_entry_core1);
    vTaskStartScheduler();
    return param.ret;
}

void vApplicationIdleHook(void)
{
}

void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize)
{
    UBaseType_t uxPsrId = uxPortGetProcessorId();
    /* Pass out a pointer to the StaticTask_t structure in which the Idle task's
    state will be stored. */
    *ppxIdleTaskTCBBuffer = &s_idle_task[uxPsrId];

    /* Pass out the array that will be used as the Idle task's stack. */
    *ppxIdleTaskStackBuffer = s_idle_task_stack[uxPsrId];

    /* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer.
    Note that, as the array is necessarily of type StackType_t,
    configMINIMAL_STACK_SIZE is specified in words, not bytes. */
    *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}

void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize)
{
    UBaseType_t uxPsrId = uxPortGetProcessorId();
    /* Pass out a pointer to the StaticTask_t structure in which the Idle task's
    state will be stored. */
    *ppxTimerTaskTCBBuffer = &s_timer_task[uxPsrId];

    /* Pass out the array that will be used as the Idle task's stack. */
    *ppxTimerTaskStackBuffer = s_timer_task_stack[uxPsrId];

    /* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer.
    Note that, as the array is necessarily of type StackType_t,
    configMINIMAL_STACK_SIZE is specified in words, not bytes. */
    *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}

void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
{
    configASSERT(!"Stackoverflow !");
}

我们看到os_entry这个函数里面调用了vTaskStartScheduler,并且在调用之前,创建了一个main_thunk任务并且同时调用core_sync_awaken((uintptr_t)os_entry_core1);这个os_entry_core1内部又调用了vTaskStartScheduler,开启了core 1的多任务调度。main_thunk任务内部执行了一些系统初始化操作(hal/drivers/fpioa),然后执行user_main函数,这个函数就是os_entry函数的入参,我们继续跟踪,发现lib/bsp/entry_user.c这个里面的_init_bsp调用了os_entry(main),此处的main即src/hello_world/main.c里面main函数,另外通过汇编代码跟踪,我们发现lib/bsp/crt.S这个里面调用了_init_bsp。

至此我们完成了整个系统rtos的启动过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值