摘要:本文带来基于LiteOS一站式开发工具LiteOS Studio,通过单步调试,来动态分析LiteOS的启动流程。

编者按:在LiteOS大揭秘系列,我们和读者们分享了《LiteOS是怎么在STM32上开始运行的》,从源码上静态分析了一遍LiteOS的启动流程。本文提供一种新的方式,即基于LiteOS一站式开发工具LiteOS Studio,通过单步调试,来动态分析LiteOS的启动流程,给开发者一个更直观的展示。

了解LiteOS系统,我们可以先从它的启动流程开始。不同的芯片和编译工具,其启动流程可能会有一些差异,本文基于码云 LiteOS开源站点 master分支12月的代码,以STM32F769IDISCOVERY(ARM Cortex M7)开发板和GCC编译工具为例,使用LiteOS Studio的单步调试,动态分析LiteOS的启动流程。

LiteOS Studio环境准备

在开始前,需要准备好LiteOS Studio环境,包含LiteOS Studio安装、新建工程、编译、烧录,掌握LiteOS Studio如何调测等等,可以参考官网文档站点https://liteos.gitee.io/liteos_studio/#/project_stm32。

  • 如何搭建LiteOS Studio开发环境 请参考搭建Windows开发环境
  • 如何新建STM32F769IDISCOVERY的LiteOS工程 请参考 新建工程
  • 如何编译,烧录、调测,请分别参考 编译配置-编译代码,烧录配置-烧录,调试器-执行调试

注意,如果开发板使用的是板载ST-LINK仿真器,需要刷为JLINK。请参考 st-link仿真器单步调测。

另外,执行单步调测,默认停止在main()函数。LiteOS操作系统的启动是从main函数开始的。而ARM Cortex-M芯片从上电到执行main函数,中间经过了Reset_Handler等函数。LiteOS系统重启、复位等都是从Reset_Handler函数开始执行的。在LiteOS Studio工程找到文件.vscodelaunch.json,把其中的postLaunchCommands属性下面的"b main"改为"b Reset_Handler"。如下图:

重新开始调测,系统会暂停在Reset_Handler函数处。如下图:

los_startup_gcc.S启动引导文件介绍

当对STM32F769IDISCOVERY开发板进行上电操作或者复位操作时,该开发板会从异常向量表中获取Reset_Handler函数的地址并执行该函数。汇编文件targetsSTM32F769IDISCOVERYlos_startup_gcc.S定义了该函数。

los_startup_gcc.S是启动引导文件,从Reset_Handler开始到执行main函数,主要工作就是准备C代码的运行环境,具体包括:

  • 设置栈指针SP,对应语句 ldr sp, =_estack
  • 初始化中断向量,对应函数LoopCopyVectorInit
  • 初始化data段,对应函数LoopCopyDataInit
  • 初始化bss段,对应函数LoopFillZerobss
  • 初始化系统时钟,跳转到函数SystemInit
  • 跳转到 C 代码函数main

代码如下:

Reset_Handler:cpsid ildr   sp, =_estack      /* set stack pointer *//* Copy the vector_ram segment initializers from flash to SRAM */movs  r1, #0b  LoopCopyVectorInitCopyVectorInit:ldr   r3, =_si_liteos_vector_dataldr   r3, [r3, r1]str   r3, [r0, r1]adds   r1, r1, #4LoopCopyVectorInit:ldr   r0, =_s_liteos_vectorldr   r3, =_e_liteos_vectoradds   r2, r0, r1cmp   r2, r3bcc   CopyVectorInit/* Copy the data segment initializers from flash to SRAM */movs  r1, #0b  LoopCopyDataInitCopyDataInit:ldr  r3, =_sidataldr  r3, [r3, r1]str  r3, [r0, r1]adds  r1, r1, #4LoopCopyDataInit:ldr  r0, =_sdataldr  r3, =_edataadds  r2, r0, r1cmp  r2, r3bcc  CopyDataInitldr  r2, =_sbssb  LoopFillZerobss
/* Zero fill the bss segment. */
FillZerobss:movs  r3, #0str  r3, [r2], #4LoopFillZerobss:ldr  r3, = _ebsscmp  r2, r3bcc  FillZerobss/* Call the clock system initialization function.*/bl  SystemInit
/* Call static constructors */
/*    bl __libc_init_array */
/* Call the application's entry point.*/bl  mainbx  lr

Data段存放的是已经初始化的全局变量,需要从Flash中获取这些数据到RAM中。而bss段存放的是没有初始化的全局变量,因此Flash中并没有bss段的变量值,所以启动引导文件只是对RAM中的.bss段进行清零操作。

los_startup_gcc.S启动引导文件中使用的_estack 、_si_liteos_vector_data、_s_liteos_vector、_e_liteos_vector、_sidata、_sdata 、_edata、_sbss、_ebss,这些符号都定义在targetsSTM32F769IDISCOVERYliteos.ld链接脚本中。

链接脚本根据应用需要,设置堆栈大小和栈地址,并控制每个段的存放位置。对于中断向量和data段,既要放到Flash中,也需要放到RAM中,并通过链接脚本的AT关键字把Flash的地址设定为load地址。

注:链接脚本中的相关代码可以访问https://gitee.com/LiteOS/LiteOS/blob/master/targets/STM32F769IDISCOVERY/liteos.ld查看。

los_startup_gcc.S启动引导文件中除了定义Reset_Handler函数,还定义了其他中断异常处理函数Default_Handler,并为Default_Handler的每个异常处理程序提供弱别名。所谓弱别名,即具有相同名称的任何函数都将覆盖此处的函数。这样做可以防止用户使能了中断却没有设置中断处理程序时造成的崩溃。Default_Handler函数只是进入一个无限循环以保留系统状态供调试器检查。

los_startup_gcc.S启动引导文件动态运行

现在我们来单步调测运行los_startup_gcc.S,启动调测后,系统会暂停在Reset_Handler函数的第一行代码cpsid i,此语句用来关中断,执行前后,观察寄存器primask值的变化,会发现由0变为1。继续执行语句" ldr sp, =_estack",同样观察寄存器,寄存器sp的值变化了。如下图:

继续运行单步调测,观察如何调用LoopCopyVectorInit和CopyVectorInit,实现把中断向量从Flash复制到RAM的。在调测过程中,寄存器的数值可能是10进制进行展示的,如果想查看其他进制展示的数值,可以在调测界面的监视器窗口输入$寄存器名称+进制代码来切换进制查看,如$r0,x来查看r0寄存器的16进制。详细的进制代码如下:

进制切换如图所示:

由于循环次数较多,如果想跨过中断向量的复制,继续下面的代码,可以设置断点,然后F5继续调测到断点处。如下图,我们在118行设置了断点,继续执行会完成向量表的复制,去执行数据段data的初始化。

以此类推,通过Studio边调测、边分析启动过程的后续代码。当执行到语句“bl main”,再按F11跳入继续执行时,就会跳转到C代码的main函数。下文继续分析main函数。

main函数介绍

LiteOS的main函数定义在targetsSTM32F769IDISCOVERYSrcmain.c。main函数主要负责LiteOS的初始化工作。代码如下:

INT32 main(VOID)
{HardwareInit();PRINT_RELEASE("n********Hello Huawei LiteOS********n""nLiteOS Kernel Version : %sn""build data : %s %snn""**********************************n",HW_LITEOS_KERNEL_VERSION_STRING, __DATE__, __TIME__);UINT32 ret = OsMain();if (ret != LOS_OK) {return LOS_NOK;}OsStart();return 0;
}

硬件初始化函数HardwareInit()和主要芯片相关,这里不做详细介绍。下面介绍LiteOS内核的初始化,代码如下:

LITE_OS_SEC_TEXT_INIT UINT32 OsMain(VOID)
{UINT32 ret;#ifdef LOSCFG_EXC_INTERACTIONret = OsMemExcInteractionInit((UINTPTR)&__bss_end);if (ret != LOS_OK) {return ret;}
#endif/* 初始化动态内存池 */ret = OsMemSystemInit((UINTPTR)&__bss_end + g_excInteractMemSize);if (ret != LOS_OK) {return ret;}/** 配置最大支持的任务个数、信号量个数、互斥锁个数、* 队列个数以及软件定时器个数,设置g_sysClock和* g_tickPerSecond全局变量*/OsRegister();#ifdef LOSCFG_SHELL_LKOsLkLoggerInit(NULL);
#endif#ifdef LOSCFG_SHELL_DMESGret = OsDmesgInit();if (ret != LOS_OK) {return ret;}
#endif
/** 初始化硬中断,此后LiteOS就会接管系统的中断,* 使用中断前需要先注册中断并使能*/OsHwiInit();/** 设置中断向量的中断处理函数,包括* Hard Fault硬件故障中断、* Non Maskable Interrupt不可屏蔽中断(NMI)、* Memory Management内存管理中断、* Bus Fault 总线故障中断、* Usage Fault使用故障中断、* SVCall利用SVC指令调用系统服务的中断*/ArchExcInit();ret = OsTickInit(GET_SYS_CLOCK(), LOSCFG_BASE_CORE_TICK_PER_SECOND);if (ret != LOS_OK) {return ret;}#ifdef LOSCFG_PLATFORM_UART_WITHOUT_VFSuart_init();
#ifdef LOSCFG_SHELLextern int uart_hwiCreate(void); /* HuaWeiChange */uart_hwiCreate();
#endif /* LOSCFG_SHELL */
#endif /* LOSCFG_PLATFORM_UART_WITHOUT_VFS *//** 初始化任务链表包括任务的排序链表,* 初始化优先级消息队列链表(用于管理不同优先级任务)*/ret = OsTaskInit();if (ret != LOS_OK) {PRINT_ERR("OsTaskInit errorn");return ret;}#ifdef LOSCFG_KERNEL_TRACEret = LOS_TraceInit(NULL, LOS_TRACE_BUFFER_SIZE);if (ret != LOS_OK) {PRINT_ERR("LOS_TraceInit errorn");return ret;}
#endif
/** 初始化任务监视器*/
#ifdef LOSCFG_BASE_CORE_TSK_MONITOROsTaskMonInit();
#endif
/** OsIpcInit包括初始化消息队列链表、互斥锁链表和信号量链表*/ret = OsIpcInit();if (ret != LOS_OK) {return ret;}/** CPUP should be inited before first task creation which depends on the semaphore* when LOSCFG_KERNEL_SMP_TASK_SYNC is enabled. So don't change this init sequence* if not necessary. The sequence should be like this:* 1. OsIpcInit* 2. OsCpupInit -> has first task creation* 3. other inits have task creation*/
#ifdef LOSCFG_KERNEL_CPUPret = OsCpupInit();if (ret != LOS_OK) {PRINT_ERR("OsCpupInit errorn");return ret;}
#endif
/** OsSwtmrInit对软件定时器和其在percpu上的排序链表进行初始化,* 并初始化定期器处理函数的内存池,同时还会创建软件定时器* 的消息队列和定时器任务*/
#ifdef LOSCFG_BASE_CORE_SWTMRret = OsSwtmrInit();if (ret != LOS_OK) {return ret;}
#endif#ifdef LOSCFG_KERNEL_SMP(VOID)OsMpInit();
#endif#ifdef LOSCFG_KERNEL_DYNLOADret = OsDynloadInit();if (ret != LOS_OK) {return ret;}
#endif#if defined(LOSCFG_HW_RANDOM_ENABLE) || defined (LOSCFG_DRIVERS_RANDOM)random_alg_context.ra_init_alg(NULL);run_harvester_iterate(NULL);
#endif
/* 创建空闲任务 */ret = OsIdleTaskCreate();if (ret != LOS_OK) {return ret;}#ifdef LOSCFG_KERNEL_RUNSTOPret = OsWowWriteFlashTaskCreate();if (ret != LOS_OK) {return ret;}
#endif#ifdef LOSCFG_DRIVERS_BASEret = OsDriverBaseInit();if (ret != LOS_OK) {return ret;}
#ifdef LOSCFG_COMPAT_LINUX(VOID)do_initCalls(LEVEL_ARCH);
#endif
#endif#ifdef LOSCFG_KERNEL_PERFret = LOS_PerfInit(NULL, LOS_PERF_BUFFER_SIZE);if (ret != LOS_OK) {return ret;}
#endif
/** LOSCFG_PLATFORM_OSAPPINIT宏默认已经在.config、menuconfig.h中定义。* OsAppInit创建了一个名为“app_Task”的任务,该任务处理函数为* app_init,任务优先级为10;* OsTestInit创建了一个名为“IT_TST_IN”的任务,该任务处理函数为* TestTaskEntry,任务优先级为25。该函数暂时没有开源。*/
#ifdef LOSCFG_PLATFORM_OSAPPINITret = osAppInit();
#else /* LOSCFG_TEST */ret = OsTestInit();
#endifif (ret != LOS_OK) {return ret;}return LOS_OK;
}

完成内核的初始化后,调用OsStart()开始任务调度,自此LiteOS开始正常工作。OsStart函数的代码如下:

LITE_OS_SEC_TEXT_INIT VOID OsStart(VOID)
{LosTaskCB *taskCB = NULL;/* 获取当前执行任务的CPU ID,STM32F769是单核芯片,cpuid为0 */UINT32 cpuid = ArchCurrCpuid();/** 配置Tick中断向量,其中断处理函数为OsTickHandler。* 初始化System Tick Timer及其中断,并启动此Timer。* 计数器会产生周期性中断*/ OsTickStart();LOS_SpinLock(&g_taskSpin);/* 获取最高优先级任务队列中的第一个任务,赋给taskCB */taskCB = OsGetTopTask();#ifdef LOSCFG_KERNEL_SMP/** attention: current cpu needs to be set, in case first task deletion* may fail because this flag mismatch with the real current cpu.*/taskCB->currCpu = (UINT16)cpuid;
#endif/* 设置32位的调度flag,第CPU ID位设置为1 */OS_SCHEDULER_SET(cpuid);PRINTK("cpu %u entering schedulern", cpuid);/** 调度g_runTask即taskCB任务,OsStartToRun函数* 定义在los_dispatch.S汇编文件中*/OsStartToRun(taskCB);
}

main函数动态运行

现在我们来单步调测运行main.c源代码,LiteOS Studio在调测时,可以同步展示当前运行的源代码行,及对应的反汇编文件行,如下图:

在调测过程中,变量的数值可能是10进制进行展示的,如果想查看其他进制展示的数值,可以在调测界面的监视器窗口输入变量名称名称+进制代码来切换进制查看,如memStart,x来查看变量memStart的16进制。如图:

本期分享使用LiteOS Studio查看LiteOS启动过程,同时展示了使用LiteOS Studio调测的技巧,大家可以继续边调测、边分析后续的代码,会看到LiteOS整个启动流程:从板子复位上电开始,调用汇编代码Reset_Handler进入启动引导文件,完成C代码运行环境的准备工作、最后跳转到main函数。在main函数中完成硬件初始化和LiteOS内核的初始化,并通过汇编跳转到执行第一个最高优先级的任务命令的地址上,从而开始LiteOS的运行。

欢迎大家分享使用LiteOS Studio调测LiteOS的心得,有任何问题、建议,都可以在开源LiteOS社区(https://gitee.com/liteos)留言,谢谢!

本文分享自华为云社区《使用LiteOS Studio图形化查看LiteOS在STM32上运行的奥秘》,原文作者:zhushy 。

点击关注,第一时间了解华为云新鲜技术~

stm32仿真不能设置断点_使用LiteOS Studio图形化查看LiteOS在STM32上运行的奥秘相关推荐

  1. 使用LiteOS Studio图形化查看LiteOS在STM32上运行的奥秘

    摘要:本文带来基于LiteOS一站式开发工具LiteOS Studio,通过单步调试,来动态分析LiteOS的启动流程. 编者按:在LiteOS大揭秘系列,我们和读者们分享了<LiteOS是怎么 ...

  2. 物联网打工人必备:LiteOS Studio图形化调测能力

    摘要:本文会给大家介绍下LiteOS Studio的调测的几个知识点,包括: 调测配置,监视变量,反汇编代码同步展示,数值进制切换,跨平台编译调测,Qemu模拟器调测,多核调测,远程设备调测等. 掌握 ...

  3. Ubuntu16.04设置扩展屏幕左右位置 及图形化方式

    Ubuntu16.04设置扩展屏幕左右位置 及图形化方式 查询当前屏幕信息 设置扩展屏幕左右位置 (补充)图形化设置方式 参考自: https://blog.csdn.net/bzfys/articl ...

  4. keil stm32标准库放在哪里_使用Keil MDK以及标准外设库创建STM32工程

    应部分网友要求,最新加入固件库以及开发环境使用入门视频教程,同时提供例程模板,个人录制,欢迎指正.下载地址:http://dl.dbank.com/c0w0ehqynd 2013.3补充在线视频教程 ...

  5. 基于LiteOS Studio零成本学习LiteOS物联网操作系统

    摘要:大家在学习物联网操作系统开发的时候,都不得不准备一套开发板和仿真器,也是一笔不小的投资.LiteOS社区现在对外开放了对Qemu模拟器的适配工程,在不使用开发板的情况下,也可以搭建LiteOS开 ...

  6. deepin允许root登录_[多图|需要root]在安卓手机上运行 UOS v20 arm64 版

    桌面效果展示 UOS v20 rc版,在小米10 pro上运行. 安装条件 手机是ARMv8芯片,运行的是64位安卓系统.(这两年出的手机基本上都符合要求.) 手机已root,并且有超过10GB的空闲 ...

  7. dev c++怎么设置断点_斑马进度计划软件可以检查计划中是否存在逻辑断点和错误逻辑关系...

    可以通过斑马进度计划软件的[计划云检查]和[国际检查]功能检查计划中是否存在逻辑断点和错误逻辑关系. 计划云检查 打分标准: ①关键线路 得分标准:(关键路径所占时间跨度/整个项目时间跨度)*20 ② ...

  8. 洋桃电子STM32物联网入门30步笔记三、CubeMX图形化编程、设置开发板上的IO口

    此文档作为对杨桃电子视频的整理,B站链接:第四集 一.开启RCC的外部时钟.包括外部高速时钟HSE和外部低速时钟LSE 时钟配置三个选项的含义: 选择禁用的话就只能使用内部时钟 旁路时钟源一般是有源晶 ...

  9. pyqt5 不报错退出_最新版本Python图形化开发环境Anaconda(Python3.7) +PyQT5+Eric6

    Anaconda是完全免费的企业级的Python发行大规模数据处理.预测分析和科学计算工具.Anacoda是Python科学技术包的合集,所以不同的包所遵循的协议不一样.PyQt5与Eric6是众所周 ...

最新文章

  1. Java中的包,类的导入,静态导入
  2. linux下find查找带有指定权限的文件(windows下编译的源代码文件)
  3. Python-练习7
  4. java treeset subset_Java中TreeSet的详细用法
  5. SAP License:SAP集成(后面为粘贴)
  6. 小量数据和海量数据分页显示存储过程
  7. IDEA 使用generator逆向工程生成pojo,mapper
  8. VK Cup 2018 Round 1: A. Primal Sport
  9. UVA 540 Team Queue
  10. 分布式存储 FastDFS-5.0.5线上搭建
  11. from pristine store, because no checksum is recorded for this file
  12. SLIC超像素分割并保存分割得到的超像素块,python代码
  13. Xcode No account for team . Add a new account in the Accounts preference pane or verify that your
  14. Python 数据相关性分析
  15. 英语语法 定冠词与专有名词
  16. 浙江大学-翁凯 C语言进阶,编程题
  17. idea默认编码设置
  18. Knight On the Chessboard
  19. 关于线程中断thread interrupt
  20. python:比较人脸识别中gallery数据集和prob数据的IP

热门文章

  1. IOError: cannot open resource
  2. 【sql绕过】Bypass waf notepad of def
  3. [cb]SceneView 获取鼠标位置
  4. Repeater使用:绑定时 结合 前台JS及后台共享方法
  5. pycharm自定义代码段
  6. webpack资源管理
  7. C# 对话框使用整理
  8. Openwrt 软件安装源
  9. 正则表达式 详解---2017-04-16
  10. (十一)C语言中内存堆和栈的区别