了解更多开源请访问:开源基础软件社区https://ost.51cto.com1.用户程序示例以启航开发板的gpio_led程序为例例子。为什么板子上电后LedTask()会自动运行?SYS_RUN宏在幕后是如何工作的?staticvoidLedTask(void){while(1){IoTGpioSetOutputVal(LED_TASK_GPIO2,1);睡眠(500*1000);IoTGpioSetOutputVal(LED_TASK_GPIO2,0);睡眠(500*1000);}}staticvoidLedExampleEntry(void){osThreadAttr_attr;IoTGpioInit(LED_TASK_GPIO2);IoTGpioSetDir(LED_TASK_GPIO2,IOT_GPIO_DIR_OUT);attr.name="LedTask";属性.attr_bits=0U;属性.cb_mem=NULL;属性.cb_size=0U;attr.stack_mem=NULLstack_sizer=.LED_TASK_STACK_SIZE;attr.priority=LED_TASK_PRIO;if(osThreadNew((osThreadFunc_t)LedTask1,NULL,&attr)==NULL){printf("创建LedTask失败!\n");}}SYS_RUN(LedExampleEntry);Stage(应用)启动流程在之前的文章《OpenHarmony南向设备开发搭建编译分析》中提到,qihangboard产品配置文件中的相关子系统和组件如下:vendor/isoftstone/qihang/config.json。......{"subsystem":"distributedschedule",#分布式任务调度子系统"components":[{"component":"samgr_lite","features":[]}]},{"subsystem":"startup","components":[{"component":"bootstrap_lite","features":[]},#bootstrapbootstrap{"component":"syspara_lite","features":#提供系统属性的读写接口["enable_ohos_startup_syspara_lite_use_thirdparty_mbedtls=false"]}]},...可以看到,启航板在启动子系统中使用了bootstrap_lite和syspara_lite组件。关注bootstrap_lite组件,它位于SDK的base/startup/bootstrap_lite目录中。引用该组件的readme文件是这样描述的:bootstrap启动bootstrap组件,它提供了每个服务和函数的启动入口标识。SAMGR启动时,会调用boostrap标识的入口函数,启动系统服务。samgr_lite组件是一个轻量级的硬件资源有限的Hi3861系统服务框架。代码位于foundation/distributedschedule/samgr_lite目录中。该组件功能参考如下:系统服务框架基于面向服务的架构,提供服务开发、服务子功能开发、对外接口开发、多服务协程开发的开发框架.device/soc/hisilicon/hi3861v100/sdk_liteos/app/wifiiot_app/src/app_main.chi_voidapp_main(hi_void){hi_flash_partition_table*ptable=HI_NULL;外设初始化();peripheral_init_no_sleep();hi_u32ret=hi_factory_nv_init(HI_FNV_DEFAULT_ADDR,HI_NV_DEFAULT_TOTAL_SIZE,HI_NV_DEFAULT_BLOCK_SIZE);hi_flash_partition_init();ptable=hi_get_partition_table();hi_nv_init(ptable->table[HI_FLASH_PARTITON_NORMAL_NV].addr,ptable->table[HI_FLASH_PARTITON_NORMAL_NV].size,HI_NV_DEFAULT_BLOCK_SIZE);hi_fs_init();(hi_void)hi_event_init(APP_INIT_EVENT_NUM,HI_NULL);hi_sal_init();hi_syserr_watchdog_debug(HI_FALSE);hi_syserr_record_crash_info(HI_TRUE);hi_lpc_init();hi_lpc_register_hw_handler(config_before_sleep,config_after_sleep);hi_at_init();tcpip_init(NULL,NULL);hi_wifi_init(APP_INIT_VAP_NUM,APP_INIT_USR_NUM);app_demo_task_release_mem();/*释放系统栈内存所使用的任务*/hilink_main();欧HOS_Main();}device/soc/hisilicon/hi3861v100/sdk_liteos/app/wifiiot_app/src/ohos_main.cvoidOHOS_Main(){OHOS_SystemInit();}base/startup/bootstrap_lite/services/source/system_init.c。voidOHOS_SystemInit(void){MODULE_INIT(bsp);MODULE_INIT(设备);MODULE_INIT(核心);SYS_INIT(服务);SYS_INIT(功能);MODULE_INIT(运行);SAMGR_Bootstrap();}#defineSYS_INIT(名称)\do{\SYS_CALL(名称,0);\}while(0)#defineSYS_CALL(name,step)\do{\InitCall*initcall=(InitCall*)(SYS_BEGIN(name,step));\InitCall*initend=(InitCall*)(SYS_END(名称,步骤));\for(;initcallmutex);samgr->status=TO_NEXT_STATUS(samgr->status);int16size=VECTOR_Size(&(samgr->services));int16我;for(i=0;iservices),i);VECTOR_Add(&initServices,serviceImpl);}MUTEX_Unlock(samgr->mutex);初始化所有服务(&initServices);VECTOR_Clear(&initServices);InitCompleted();}在用户应用程序组的代码中,会包包含以下说明:APP_FEATURE_INIT(MQTTDemo);SYS_RUN(LedExampleEntry);上述宏的说明引用如下:/**@briefIdentifiesanentryforinitializingandstartingapplication-layerservicebythepriority2该宏用于标识启动过程中应用层服务阶段优先级为2的调用入口。\n@param函数我表示初始化和启动应用层服务的入口函数。类型为void(*)(void).*/#defineAPP_SERVICE_INIT(func)LAYER_INITCALL_DEF(func,app_service,“app.service”)/**@brief按优先级标识初始化启动系统运行阶段的入口2.该宏用于在启动过程的系统启动阶段识别优先级为2的调用入口。\n@paramfunc表示初始化和启动系统运行阶段的入口函数。类型为void(*)(void)。#defineSYS_RUN(func)LAYER_INITCALL_DEF(func,run,“run”)*/总结以上分析,第二阶段程序启动流程如下图所示:3.第一阶段(上电)的启动过程请参考文末第二篇的详细介绍。程序加载由三个启动程序配合完成:romboot:芯片自带的开机启动程序,bootloaderbootloaderboot(device/soc/hisilicon/hi3861v100/sdk_liteos/boot/loaderboot):与HiBurn通信,下载图像闪烁。烧录EFUSE(芯片配置信息)。验证并启动闪存启动。flashboot(device/soc/hisilicon/hi3861v100/sdk_liteos/boot/flashboot):升级固件。验证并启动固件(主程序)。loaderboot/common/cmd_loop.c定义了从hiburn接收和处理的操作:constloader_cmdg_loader_cmdtable[LOADER_CMD_MAX]={{CMD_DL_IMAGE,loader_download_image},{CMD_BURN_EFUSE,loader_burn_efuse},{CMD_UL_DATA,loader_upload_data,ADuse}_{CMD_UL_DATA,loader_efload_data,REfload_upload_data}_CEFCMD_FLASH_PROTECT,loader_flash_protect},{CMD_RESET,loader_reset},{CMD_FACTORY_IMAGE,loader_download_image},{CMD_VERSION,loader_burn_version},};其中:loader_download_image是从hiburn接收升级文件烧入flash。flashboot/startup目录下有两个重要文件:riscv_init_flshboot.S汇编语言格式,RISC-V启动代码。main.c.#defineKERNEL_START_ADDR0x40D3C0boot_kernel(KERNEL_START_ADDR);global_reset();hi_voidboot_kernel(uintptr_tkaddr){__asm____volatile__("ecall");/*切换U-MODE->M-MODE*/hi_void(*entry)(hi_void)=(hi_void*)(kaddr);entry();}在最后build应用生成的map文件,可看到内存布局如下:NameOriginLengthAttributesBIN0x000000000040d3c00x0000000000200000xrROM_TEXT0x00000000003b80000x00000000000457e0xrROM_DATA00x000000000011d7c00x0000000000000020xrwROM_DATA10x000000000011d7e00x00000000000006e8xrwROM_BSS0x000000000011a9c00x0000000000002e00xrwSTACK0x00000000001185c00x0000000000002400rwCHECK_INFO0x000000000011dfc00x0000000000000040rwFLASH0x000000000040d3c00x00000000001f2c40xrwPATCH_BSS0x00000000000d80000x0000000000000400xrwRAM0x00000000000d84000x00000000000401c0xrwEXTERN_ROM_DATA1_BSS0x000000000011dec80x00000000000000f8xrw*default*0x00000000000000000xffffffffffffffff.entry.text0x000000000040d3c00x4build/libs/hi3861/release/no_mesh/liblitekernel_flash.a(los_startup.o)0x000000000040d3c0_start0x000000000040d3e0.=ALIGN(0x20)对比可以看出,KERNEL_START_ADDR与应用程序的起始地址是一样的,可以推断flashboot的最终操作是通过ecall指令调用应用程序实现RISC的切换-V处理器(Hi3861使用)从UserMode(禁止不可信代码执行特权指令)到MachineMode(最高特权模式)。4.小结本文采用逆序方式初步梳理了Hi3861芯片上电到启动并运行OpenHarmony应用的过程。还有很多内容没有涉及到,包括芯片安全引导,Flash存储分配等,引导部分的描述也比较肤浅。了解更多开源知识,请访问:开源基础软件社区https://ost.51cto.com。