[文章]

移植鸿蒙系统到STM32L476RG_NUCLEO开发板的一点小经验

2020-11-12 11:22:10  523 鸿蒙系统 HarmonyOS 鸿蒙OS
分享
5
本帖最后由 死龙 于 2020-11-12 14:51 编辑

  1. https://gitee.com/walker2048/hmos_iot
复制代码
目前移植STM32L476的相关代码已上传在gitee,有兴趣的同学可以自行下载研究。

编译命令为 python build.py stm32l476rg_nucleo

移植鸿蒙的建议:
一步一步来,别想一口吃成胖子,给自己定计划。  
多看源码以及编译日志,多想,多动手 。
源码既是文档,别想着百度或者google能帮你直接解决问题。

修改完代码后,完成了小部分功能的,也要及时提交git。

1、第一步肯定是创建厂商文件夹
     首先按移植LiteOS教程里的说明,使用CubeMX工具生成makefile格式的项目(包含了stm32l4xx标准hal库和ll库实现代码及makefile),并把项目文件复制到vendor/st/stm32l4xx目录里。
这就是2020-11-06日  dbbaf5f  这个提交所包含的内容
然后在该目录执行命令  make > build.log,这样一是测试代码是否能正常编译,二是可以把stm官方提供的makefile实际执行指令信息存储到build.log文件里,方便以后修改gn系统的编译配置时做参考用。

2、第二步,配置编译环境及组件。
     根据以往阅读makefile和嵌入式开发经验,应该先确定编译工具链。不同硬件架构,需要的编译工具链并不一样,哪怕是一个最简单的helloworld,也没办法实现同一个bin文件,能在不同架构的硬件上直接运行。目前鸿蒙2.0配置好的两套编译工具(主要是gcc),并不能完成stm32的编译工作。
     打开build/lite/toolchain/目录,复制gcc.gni文件的内容到arm_none_eabi_gcc.gni,并将第14行的ohos_kernel_type (内核类型) 修改成liteos_m,并将15行的ohos_build_compiler_prefix 设置为正确的gcc工具前缀arm-none-eabi。其他内容暂时没动,然后根据其他开发板的设置,又复制粘贴了一遍各种配置,例如
  1. build/lite/config/boards/stm32l476rg_nucleo.gni
复制代码
等等配置先抄一遍hi3861的,期间各种尝试使用编译命令python build.py stm32l476rg_nucleo,直到不再提示找不到stm32l476rg_nucleo目标板,进入下一个确认工具链环节为止。
这一环节中,比较重要的应该是build/lite/product/stm32l476rg_nucleo.json文件,该文件定义了目标板名称,编译工具链,内核等等重要信息。

当编译命令提示arm-none-eabi-gcc不是OHOS的编译器时,我也楞了一会儿。翻了build目录下各种配置也找不到对应配置时,我就放弃找配置了。直接在VScode中全局搜索包含not OHOS compiler字段的文件,最终在build/lite/config.py的124行和158行找到了对应判断语句,并增加了arm-none-eabi-gcc的判断语句。

随后测试编译时,又发现编译脚本会针对ohos_kernel_type 进行各种优化和设置。没办法,就只能搜索ohos_kernel_type == "liteos_riscv",并将对应脚本一一修改
。涉及到的文件也很多,详细请看gitee上的变更记录。

最终各组件的配置判断语句没问题了,能顺利进入到编译状态,出现类似以下信息了

  1. === start build ===

  2. Done. Made 39 targets from 41 files in 648ms
  3. ninja: Entering directory `/mnt/out/stm32l476rg_nucleo'
  4. [1/112] cross compiler obj/applications/sample/wifi-iot/app/demolink/helloworld.o
  5. [2/112] AR libs/libdemolink.a
复制代码
也就是说能出现[1/112]之类的,恭喜你,编译配置已经完成了80%了。期间还删除了大量容易出现问题的组件,例如wifi功能等等一堆组件。


3、调整头文件配置

    为了减少以后找文件找目录头疼,我在源码目录新建了一个include文件夹,并将疑似应该从厂商目录中提取出来的头文件放在该目录的hal目录下,并将难以解决的头文件错误组件去掉,不编译对应组件。
     最终编译命令都顺利通过了,只差最后一步生成elf和bin文件了。

4、根据原厂makefile修改和调整编译细节

     重头戏是此文件build/lite/toolchain/arm_none_eabi_gcc.gni
     查看原厂makefile的build.log文件,可以看出编译过程为
     .c文件=>.o文件,然后.S文件=>.o文件,然后将所有的.o文件以及
STM32L476RGTx_FLASH.ld文件一起链接成elf文件。最后再由elf文件生成bin和hex。
     多次尝试修改后,最终调整为以下内容
  1. template("gcc_toolchain") {
  2.     toolchain(target_name) {
  3.         assert(defined(invoker.cc), "gcc toolchain must specify a \"cc\" value")
  4.         assert(defined(invoker.cxx), "gcc toolchain must specify a \"cxx\" value")
  5.         assert(defined(invoker.ld), "gcc toolchain must specify a \"ld\" value")
  6.         assert(defined(invoker.ar), "gcc toolchain must specify a \"ar\" value")
  7.         assert(defined(invoker.as), "clang toolchain must specify a \"as\" value")
  8.         assert(defined(invoker.cp), "clang toolchain must specify a \"cp\" value")
  9.         
  10.         ar = invoker.ar
  11.         as = invoker.as
  12.         cc = invoker.cc
  13.         cxx = invoker.cxx
  14.         ld = invoker.ld
  15.         cp = invoker.cp

  16.         need_strip = false
  17.         if(defined(invoker.strip)) {
  18.             strip = invoker.strip
  19.             need_strip = true
  20.         }

  21.         if (defined(invoker.extra_ldflags) && invoker.extra_ldflags != "") {
  22.           extra_ldflags = ""
  23.         } else {
  24.           extra_ldflags = ""
  25.         }

  26.         tool("cc") {
  27.             command = "$cc -c {{cflags}} {{defines}} {{include_dirs}} {{cflags_c}} " +
  28.                      # "-MMD -MP -MF‘{{source_out_dir}}/{{source_name_part}}.d’ " +
  29.                      # "-Wa,-a,-ad,-alms={{source_out_dir}}/{{source_name_part}}.lst " +
  30.                       "{{source}} -o {{output}}"
  31.             depsformat = "gcc"
  32.             description = "cross compiler {{output}}"
  33.             outputs = [
  34.                 "{{source_out_dir}}/{{source_name_part}}.o",
  35.             ]
  36.         }
  37.         tool("cxx") {
  38.             depfile = "{{output}}.d"
  39.             command = "$cxx -c {{cflags}} {{defines}} {{include_dirs}} {{cflags_c}} " +
  40.                      # "-MMD -MP -MF‘{{source_out_dir}}/{{source_name_part}}.d’ " +
  41.                      # "-Wa,-a,-ad,-alms={{source_out_dir}}/{{source_name_part}}.lst " +
  42.                       "{{source}} -o {{output}}"
  43.             depsformat = "gcc"
  44.             description = "CXX {{output}}"
  45.             outputs = [
  46.                 "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
  47.             ]
  48.         }
  49.         tool("asm") {
  50.             depfile = "{{output}}.d"
  51.             command = "$as -c {{cflags}} {{defines}} {{include_dirs}} {{asmflags}} {{source}} {{cflags_c}} " +
  52.                       "-o {{output}}"
  53.             depsformat = "gcc"
  54.             description = "cross compiler {{output}}"
  55.             outputs = [
  56.                 "{{source_out_dir}}/{{source_name_part}}.o"
  57.             ]
  58.         }
  59.         tool("alink") {
  60.             outfile = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
  61.             rspfile = "{{output}}.rsp"
  62.             rspfile_content = "{{inputs}}"
  63.             command = "$ar cr {{output}} @\"$rspfile\""

  64.             description = "AR {{output}}"
  65.             outputs = [
  66.                 outfile
  67.             ]

  68.             default_output_dir = "{{root_out_dir}}/libs"
  69.             default_output_extension = ".a"
  70.             output_prefix = "lib"
  71.         }
  72.         tool("link") {
  73.             outfile = "{{output_dir}}/bin/{{target_output_name}}.elf"
  74.             rspfile = "$outfile.rsp"
  75.             command = "$ld {{inputs}} {{ldflags}} $extra_ldflags -specs=nano.specs " +
  76.             # set ld file patch in vendor path
  77.                       "-lc -lm -lnosys {{libs}} -Wl,-Map={{target_output_name}}.map,--cref " +
  78.                       "-Wl,--gc-sections -o $outfile "
  79.             if(need_strip) {
  80.                 command += "&& $cp -O binary -S $outfile {{output_dir}}/bin/{{target_output_name}}.bin"
  81.             }

  82.             description = "LINK $outfile"
  83.             default_output_dir = "{{root_out_dir}}"
  84.             rspfile_content = "{{inputs}}"
  85.             outputs = [
  86.                 outfile
  87.             ]
  88.         }
  89.         tool("stamp") {
  90.             if (host_os == "win") {
  91.                 command = "cmd /c type nul > \"{{output}}\""
  92.             } else {
  93.                 command = "/usr/bin/touch {{output}}"
  94.             }
  95.             description = "STAMP {{output}}"
  96.         }
  97.         tool("copy") {
  98.             command = "$cp -O binary -S {{source}} {{output}}.bin && echo $strip"
  99.             description = "COPY {{source}} {{output}}"
  100.         }
  101.     }
复制代码

同时在stm32l4xx/Src/BUILD.gn文件中添加ldflags,实现ld文件在厂商文件内设置。

  1.     ldflags = [
  2.       "-T",
  3.       "../../vendor/st/stm32l4xx/STM32L476RGTx_FLASH.ld"
  4.     ]
复制代码

最终,顺利生成了elf文件,bin文件以及hex文件。
其实gn配置相对来说,命令行的提示,以及配置的可读性都是相当不错的。还是建议大家多动手,多看,多想。




死龙 2020-11-12 14:58:11
说白了,就是不断的尝试编译命令,将实际执行命令与原厂执行的命令做比较,然后修改gn配置文件。直到能产生正确的结果为止,期间需要付出大量的时间去尝试。另外编译命令的执行目录就是out/目标板目录,本次案例中是out/stm32l476rg_nucleo目录,所有文件的相对路径可以参照此路径执行。同时cflags,ldflags这些参数中不能包含空格,否则最终参数会在空格前加\,影响编译结果。
1 回复

举报

死龙 2020-11-12 15:03:58
个人理解鸿蒙设置的这一套GN系统的执行顺序就是:设置并检验目标板=》设置并检验编译器=》检查组件依赖=》开始编译动作=》完成编译结果=》检查是否需要rootfs=》完成所有动作。大家也可以按照这个步骤尝试。
回复

举报

dianzi 2020-11-12 18:13:25
感谢李哥分享
1 回复

举报

评论

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

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
发文章