wb2项目启动流程分析

[复制链接]
查看923 | 回复5 | 2024-10-4 10:08:19 | 显示全部楼层 |阅读模式

wb2项目启动流程分析

小声疑问

没拿到wb2的时候心心念念的,拿到wb2搭建好环境准备烧录helloworld的时候虽然是正常烧录运行了,最后看到代码的时候心里就慌了,小声疑问,FreeRTOS允许这么使用了吗?

先看看wb2 SDK里面的helloworld项目的main.c代码

void main(void)
{

    printf("Hello World.\r\n");
    for (int i = 10; i >= 0; i--)
    {
        printf("Restarting in %d seconds...\r\n", i);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
    printf("Restarting now.\r\n");

    bl_sys_reset_por();
}

什么时候代码可以这么写了?

于是我又看了blink里面的代码,main.c如下

void blink_test(void *param)
{
    uint8_t value = 1;
    while (1)
    {
        bl_gpio_enable_output(GPIO_LED_PIN, 0, 0);
        printf("Turning the LED %s! \r\n", value == 1 ? "ON" : "OFF");
        bl_gpio_output_set(GPIO_LED_PIN, value);
        value = !value;
        vTaskDelay(1000);
    }
}

void main(void)
{
    xTaskCreate(blink_test, "blink", 1024, NULL, 15, NULL);
}

疑问

上面两个项目的main代码让我感到非常的疑惑,FreeRTOS什么时候可以这么调用了,正常的任务使用应该是如下的

  1. 创建任务:调用xTaskCreate创建一个任务
  2. 启动调度器:调用vTaskStartScheduler开启任务调度器

官方文档里面教程如下

// Tasks can be created before or after starting the RTOS scheduler
    xTaskCreate( vTaskCode,
                 "NAME",
                 STACK_SIZE,
                 NULL,
                 tskIDLE_PRIORITY,
                 NULL );

    // Start the real time scheduler.
    vTaskStartScheduler();

这俩项目的代码里面没有启动调度器,一眼看下去根本不会运行的样子,大家也都明白aithkner的代码问题多,但不至于最简单的helloworld都不能运行吧,先不管这些,上电烧录后按ret按键发现竟然能正常运行,这下不懂了。。。

分析问题

按道理来讲c语言程序会先执行main函数,从这俩项目来看首先执行的肯定不是main函数,如果是的话,根本不能执行创建的task,经过一番查找资料发现c语言是可以指定入口函数的!!! 创建hello.c代码如下

const char * str = "hello\n";

void printf()
{
        asm("movl $13,%%edx \n\t"
                "movl str,%%ecx \n\t"
                "movl $0,%%ebx \n\t"
                "movl $4,%%eax \n\t"
                "int $0x80 \n\t"
                :
                :"r"(str):"edx","ecx","ebx");
}

void exit()
{
        asm("movl $42,%ebx \n\t"
                "movl $1,%eax \n\t"
                "int $0x80 \n\t");
}

int mymain(int argc, char **argv)
{
        printf();
        exit();
}

可以看到和平时写的不太一样,里面没有main函数,使用gcc hello.c -o hello 编译结果如下

/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/Scrt1.o: in function `_start':
(.text+0x1b): undefined reference to `main'
collect2: error: ld returned 1 exit status

提示ld程序错误,错误原因是找到不到main函数

使用ld -e命令指定入口函数,具体编译步骤如下

gcc -c -fno-builtin hello.c

执行后会生成hello.o文件,然后执行链接命令指定入口函数

ld -static -e mymain -o hello hello.o

执行后就会生成hello可执行文件,执行./hello可以看到正常输出了hello

上面代码参考 程序一定要从main函数开始运行吗?

除了通过ld -e手动指定程序入口外,还可以通过ld链接脚本来指定入口函数

ld文件咱也不懂,详情请参考 ld链接脚本

查看默认ld链接脚本

执行命令ld --verbose可以看到如下输出

GNU ld (GNU Binutils for Ubuntu) 2.38
  Supported emulations:
   elf_x86_64
   elf32_x86_64
   elf_i386
   elf_iamcu
   elf_l1om
   elf_k1om
   i386pep
   i386pe
using internal linker script:
==================================================
/* Script for -z combreloc -z separate-code */
/* Copyright (C) 2014-2022 Free Software Foundation, Inc.
   Copying and distribution of this script, with or without modification,
   are permitted in any medium without royalty provided the copyright
   notice and this notice are preserved.  */
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64",
              "elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); SEARCH_DIR("=/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu64"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib")
// 后面太长了省略了, 有兴趣的可以自行查看

可以看出来默认链接脚本第一个执行的程序是_start函数,main函数是在_start函数中调用的,这方面咱也不懂,可以参考其它大佬的文章

wb2的入口函数位置

wb2的链接脚本位置位于D:\vscodeProject\aithinker\wb2\Ai-Thinker-WB2\components\platform\soc\bl602\bl602\evb\ld\flash_rom.ld

打开ld文件就可以看到找到如下内容

OUTPUT_ARCH( "riscv" )

ENTRY( bl602_start )

其中ENTRY制定了程序的入口地址是bl602_start函数,

bl602_start函数

全局搜索后可以找到bl602_start函数位于 D:\vscodeProject\aithinker\wb2\Ai-Thinker-WB2\components\platform\soc\bl602\bl602\evb\src\boot\gcc\start.S文件中,该文件是汇编代码编写的,内容如下

.section .init
 .globl bl602_start
 .type bl602_start,@function

bl602_start:
 .cfi_startproc
 .cfi_undefined ra
.option push
.option norelax
    /*disable IRQ*/
 li t0, MSTATUS_MIE
 csrc mstatus, t0

 la gp, __global_pointer$
.option pop
 la sp, _sp_main
/* 中间省略*/
 /* argc = argv = 0 */
 li a0, 0
 li a1, 0
 call bfl_main

终于看到能看懂的了的老朋友:bfl_main,call是汇编中调用子程序的汇编指令,bfl_main显然就是bl602_start函数第一个执行的函数了,全局搜索后可以找到bfl_main函数位于D:\vscodeProject\aithinker\wb2\Ai-Thinker-WB2\components\platform\soc\bl602\bl602\bfl_main.c中

bfl_main函数中启动FreeRTOS调度器

打开后可以看到bfl_main函数如下

void bfl_main()
{
    TaskHandle_t aos_loop_proc_task;

    bl_sys_early_init();

#ifdef SYS_REBOOT_LOG_DISENABLE
    /*Init UART In the first place*/
    log_port_reset();
#endif


    hosal_uart_init_only_tx(&uart_stdio);
    puts("Starting bl602 now....\r\n");

    _dump_boot_info();

#ifndef BL602_MATTER_SUPPORT
    vPortDefineHeapRegions(xHeapRegions);
#endif

    system_early_init();

    puts("[OS] Starting aos_loop_proc task...\r\n");
    xTaskCreate(aos_loop_proc, (char*)"event_loop", 1024, NULL, 15, &aos_loop_proc_task);

    puts("[OS] Starting OS Scheduler...\r\n");
    vTaskStartScheduler();
}

可以看到里面进行了各种初始化,还有熟悉的Starting bl602 now....以及调用了vTaskStartScheduler启动了FreeRTOS的任务调度器!到这里就明白了为什么自己写的项目中不需要启动调度器了!

main函数调用

前面bfl_main函数同样也创建了一个aos_loop_proc任务,在aos_loop_proc任务代码中同样创建了app_main_enter任务

xTaskCreate(app_main_entry,
            (char*)"main",
            SYS_APP_TASK_STACK_SIZE / sizeof(StackType_t),
            NULL,
            SYS_APP_TASK_PRIORITY,
            NULL);

static void app_main_entry(void *pvParameters)
{
    extern int main();
    main();
    vTaskDelete(NULL);
}

可以看到app_main_entry任务调只调用main函数后就被delete了

至此就大致理清了wb2项目中关于FreeRTOS代码的疑惑

修改wb2的串口波特率

通过前面的分析可以知道wb2的初始化操作在bfl_main.c中,串口初始化相关代码如下

#ifdef CUSTOM_LOG_IO
HOSAL_UART_DEV_DECL(uart_stdio, 0, CUSTOM_LOG_TX_IO, CUSTOM_LOG_RX_IO, CUSTOM_LOG_RX_BAUD);
#else
HOSAL_UART_DEV_DECL(uart_stdio, 0, 16, 7, 115200);

hosal_uart_init_only_tx(&uart_stdio);

可以直接修改115200或者是在项目使用CUSTOM_LOG_IO来自定义串口信息

例如:

#define CUSTOM_LOG_IO
#define CUSTOM_LOG_TX_IO 16
#define CUSTOM_LOG_RX_IO 7
#define CUSTOM_LOG_RX_BAUD 115200

个人水平有限难免会有失误或理解不到位的地方,如有欢迎批评指正

回复

使用道具 举报

bzhou830 | 2024-10-4 20:08:26 | 显示全部楼层
分析的很好,学习了
选择去发光,而不是被照亮
回复 支持 反对

使用道具 举报

沈夜 | 2024-10-4 23:14:36 | 显示全部楼层
#define CUSTOM_LOG_IO
#define CUSTOM_LOG_TX_IO 16
#define CUSTOM_LOG_RX_IO 7
#define CUSTOM_LOG_RX_BAUD 115200
这种在man.c 定义有效果吗
回复 支持 反对

使用道具 举报

lovzx | 2024-10-5 13:47:22 | 显示全部楼层
沈夜 发表于 2024-10-4 23:14
#define CUSTOM_LOG_IO
#define CUSTOM_LOG_TX_IO 16
#define CUSTOM_LOG_RX_IO 7

应该是可以的,可以试一下,另外在makefile中也可以的
回复 支持 反对

使用道具 举报

沈夜 | 2024-10-6 04:27:05 | 显示全部楼层
lovzx 发表于 2024-10-5 13:47
应该是可以的,可以试一下,另外在makefile中也可以的

谢谢大佬
回复 支持 反对

使用道具 举报

WildboarG | 2024-10-8 08:46:33 | 显示全部楼层
牛啊牛啊
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则