1 2 3# 轻量系统STM32F407芯片移植案例 4 5介绍基于`STM32F407IGT6`芯片在拓维信息[Niobe407](https://gitee.com/openharmony-sig/device_board_talkweb)开发板上移植OpenHarmony LiteOS-M轻量系统,提供交通、工业领域开发板解决方案。移植架构采用`Board`与`SoC`分离方案,使用`arm gcc`工具链`Newlib C`库,实现了`lwip`、`littlefs`、`hdf`等子系统及组件的适配,开发了配套应用示例代码,支持通过Kconfig图形化配置编译选项。 6 7## 适配准备 8 9- 下载[stm32cubemx](https://www.st.com/en/development-tools/stm32cubemx.html)图形工具。 10- 准备ubuntu20.04系统环境,安装[arm-none-eabi-gcc](https://gitee.com/openharmony/device_board_talkweb/blob/master/niobe407/docs/software/%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA%E4%B8%8E%E5%9B%BA%E4%BB%B6%E7%BC%96%E8%AF%91.md#6%E5%AE%89%E8%A3%85%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%91%E5%B7%A5%E5%85%B7%E9%93%BE)交叉编译工具链。 11### 生成可用工程 12 13通过stm32cubemx工具生成`STM32F407IGT6`芯片的Makefile工程,在此给出如下配置建议: 14 15- 系统相关配置采用默认配置。 16- 时钟配置时将SYSCLK选项配置为168MHz,发挥芯片最强性能。 17- 配置USART1用作调试串口,用来打印适配过程中的调试信息。 18- 配置stm32cubemx工程选项时,将Toolchain/IDE选项选为Makefile。 19 20生成的工程目录如下: 21 22``` 23├── Core 24│ ├── Inc 25│ │ ├── main.h 26│ │ ├── stm32f4xx_hal_conf.h 27│ │ └── stm32f4xx_it.h 28│ └── Src 29│ ├── main.c --- 主函数 30│ ├── stm32f4xx_hal_msp.c --- HAL库弱函数配置文件 31│ ├── stm32f4xx_it.c --- 中断回调函数文件 32│ └── system_stm32f4xx.c --- 系统 33├── Drivers 34│ ├── CMSIS --- CMSIS接口 35│ └── STM32F4xx_HAL_Driver --- HAL库驱动 36├── Makefile --- Makefile编译 37├── STM32F407IGTx_FLASH.ld --- 链接文件 38├── startup_stm32f407xx.s --- 启动文件 39└── stm32f407_output.ioc --- stm32cubemx工程文件 40``` 41 42### 验证生成的工程 43 44将生成的工程拷贝至Ubuntu,进入工程目录下执行make命令编译,确定能够编译成功。 45 46``` 47arm-none-eabi-gcc build/main.o build/stm32f4xx_it.o build/stm32f4xx_hal_msp.o build/stm32f4xx_hal_tim.o build/stm32f4xx_hal_tim_ex.o build/stm32f4xx_hal_uart.o build/stm32f4xx_hal_rcc.o build/stm32f4xx_hal_rcc_ex.o build/stm32f4xx_hal_flash.o build/stm32f4xx_hal_flash_ex.o build/stm32f4xx_hal_flash_ramfunc.o build/stm32f4xx_hal_gpio.o build/stm32f4xx_hal_dma_ex.o build/stm32f4xx_hal_dma.o build/stm32f4xx_hal_pwr.o build/stm32f4xx_hal_pwr_ex.o build/stm32f4xx_hal_cortex.o build/stm32f4xx_hal.o build/stm32f4xx_hal_exti.o build/system_stm32f4xx.o build/startup_stm32f407xx.o -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -specs=nano.specs -TSTM32F407IGTx_FLASH.ld -lc -lm -lnosys -Wl,-Map=build/stm32f407_output.map,--cref -Wl,--gc-sections -o build/stm32f407_output.elf 48arm-none-eabi-size build/stm32f407_output.elf 49 text data bss dec hex filename 50 5000 20 1636 6656 1a00 build/stm32f407_output.elf 51arm-none-eabi-objcopy -O ihex build/stm32f407_output.elf build/stm32f407_output.hex 52arm-none-eabi-objcopy -O binary -S build/stm32f407_output.elf build/stm32f407_output.bin 53``` 54 55编译完成会生成一个.bin文件,为了确认该程序能在开发板中成功运行,需要main函数中的串口初始化之后,通过串口输出一段字符串,运行时若收到打印信息,则开发板启动成功。 56``` 57printf("hello world!!\r\n"); 58``` 59 60适配printf输出到串口,只需要重写_write函数即可,参考如下: 61 62```c 63#include <stdio.h> 64 65int _write(int fd, char *ptr, int len) 66{ 67 return HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, 0xFFFF); 68} 69``` 70重新编译代码,将其烧录至开发板中验证。 71 72## 编译构建 73### 目录规划 74 75芯片适配目录规划为: 76 77``` 78device 79├── board --- 单板厂商目录 80│ └── talkweb --- 单板厂商名字:拓维信息 81│ └── niobe407 --- 单板名:与产品名一致 82└── soc --- SoC厂商目录 83 └── st --- SoC厂商名称 84 └── stm32f4xx --- SoC Series名:stm32f4xx是一个系列,包含该系列soc相关代码 85``` 86 87产品样例目录规划为: 88 89``` 90vendor 91└── talkweb --- 开发产品样例厂商目录 92 └── niobe407 --- 产品名字:niobe407 93``` 94 95获取[OpenHarmony源码](../get-code/sourcecode-acquire.md),根据上述目录规划,创建相应文件夹。 96 97### 预编译适配 98 99预编译适配内容就是围绕`hb set`命令的适配,使工程能够通过该命令设置根目录、单板目录、产品目录、单板公司名等环境变量,为后续适配编译做准备。 100 101具体的预编译适配步骤如下: 102 1031. 在`vendor/talkweb/niobe407`目录下新增`config.json`文件,用于描述这个产品样例所使用的单板、内核等信息,描述信息可参考如下内容: 104 105``` 106{ 107 "product_name": "niobe407", --- 用于hb set进行选择时,显示的产品名称 108 "type": "mini", --- 构建系统的类型,mini/small/standard 109 "version": "3.0", --- 构建系统的版本,1.0/2.0/3.0 110 "device_company": "talkweb", --- 单板厂商名,用于编译时找到/device/board/talkweb目录 111 "board": "niobe407", --- 单板名,用于编译时找到/device/board/talkweb/niobe407目录 112 "kernel_type": "liteos_m", --- 内核类型,因为OpenHarmony支持多内核,一块单板可能适配了多个内核,所以需要指定某个内核进行编译 113 "kernel_version": "3.0.0", --- 内核版本,一块单板可能适配了多个linux内核版本,所以需要指定某个具体的内核版本进行编译 114 "subsystems": [ ] --- 选择所需要编译构建的子系统 115} 116``` 117 1182. 在`//device/board/talkweb/niobe407`目录下创建`board`目录,在创建的目录下新增一个`config.gni`文件,用于描述该产品的编译配置信息: 119 120``` 121# Kernel type, e.g. "linux", "liteos_a", "liteos_m". 122kernel_type = "liteos_m" --- 内核类型,跟config.json中kernel_type对应 123 124# Kernel version. 125kernel_version = "3.0.0" --- 内核版本,跟config.json中kernel_version对应 126``` 127 1283. 验证`hb set`配置是否正确,输入`hb set`能够显示如下信息: 129 130 ![hb set](figures/niobe407_hb_set.png) 131 1324. 通过`hb env`可以查看选择出来的预编译环境变量: 133 134 ![hb env](figures/niobe407_hb_env.png) 135 1365. hb介绍 137 138 `hb`是OpenHarmony为了方便开发者进行代码构建编译,提供的python脚本工具,其源码就在`//build/lite`仓库目录下。在执行`hb set`命令时,脚本会遍历`//vendor/<product_company>/<product_name>`目录下的`config.json`,给出可选产品编译选项。在config.json文件中,`product_name`表示产品名,`device_company`和`board`用于关联出`//device/board/<device_company>/<board>`目录,匹配该目录下的`<any_dir_name>/config.gni`文件,其中`<any_dir_name>`目录名可以是任意名称,但建议将其命名为适配内核名称(如:liteos_m、liteos_a、linux)。hb命令如果匹配到了多个`config.gni`,会将其中的`kernel_type`和`kernel_version`字段与`vendor/<device_company>`下`config.json`文件中的字段进行匹配,从而确定参与编译的`config.gni`文件。 139 140至此,预编译适配完成,但工程还不能执行`hb build`进行编译,还需要准备好后续的`LiteOS-M`内核移植。 141 142## 内核移植 143 144内核移植需要完成`LiteOS-M Kconfig`适配、`gn`的编译构建和内核启动最小适配。 145 146### Kconfig文件适配 147 1481. 在`//vendor/talkweb/niobe407`目录下创建kernel_configs目录,并创建空文件,命名为debug.config。 149 1502. 打开`//kernel/liteos_m/Kconfig`文件,可以看到在该文件通过orsource命令导入了`//device/board`和`//device/soc`下多个Kconfig文件,后续需要创建并修改这些文件: 151 152``` 153orsource "../../device/board/*/Kconfig.liteos_m.shields" 154orsource "../../device/board/$(BOARD_COMPANY)/Kconfig.liteos_m.defconfig.boards" 155orsource "../../device/board/$(BOARD_COMPANY)/Kconfig.liteos_m.boards" 156orsource "../../device/soc/*/Kconfig.liteos_m.defconfig" 157orsource "../../device/soc/*/Kconfig.liteos_m.series" 158orsource "../../device/soc/*/Kconfig.liteos_m.soc" 159``` 160 1613. 在`//device/board/talkweb`下参考如下目录结构创建相应的Kconfig文件: 162 163``` 164. 165├── Kconfig.liteos_m.boards 166├── Kconfig.liteos_m.defconfig.boards 167├── Kconfig.liteos_m.shields 168└── niobe407 169 ├── Kconfig.liteos_m.board --- 开发板配置选项 170 ├── Kconfig.liteos_m.defconfig.board --- 开发板默认配置选项 171 └── liteos_m 172 └── config.gni 173``` 174 1754. 修改`Kconfig`文件内容: 176 177 - 在`//device/board/talkweb/Kconfig.liteos_m.boards`文件中添加: 178 179 ``` 180 if SOC_STM32F407 181 orsource "niobe407/Kconfig.liteos_m.board" --- 可根据SOC定义,加载指定board目录定义 182 endif 183 ``` 184 185 - 在`//device/board/talkweb/Kconfig.liteos_m.defconfig.boards`文件中添加: 186 187 ``` 188 orsource "*/Kconfig.liteos_m.defconfig.board" 189 ``` 190 191 - 在`//device/board/talkweb/Kconfig.liteos_m.defconfig.boards`文件中添加: 192 193 ``` 194 orsource "shields/Kconfig.liteos_m.shields" 195 ``` 196 197 - 在`//device/board/talkweb/niobe407/Kconfig.liteos_m.board`文件中添加: 198 199 ``` 200 menuconfig BOARD_NIOBE407 201 bool "select board niobe407" 202 depends on SOC_STM32F407 --- niobe407使用的是stm32f407的SoC,只有SoC被选择后,niobe407的配置选项才可见、可以被选择。 203 ``` 204 205 - 在`//device/board/talkweb/niobe407/Kconfig.liteos_m.defconfig.board`中添加: 206 207 ``` 208 if BOARD_NIOBE407 209 --- 用于添加BOARD_NIOBE407默认配置 210 endif #BOARD_NIOBE407 211 ``` 212 2135. 在`//device/soc/st`下参考如下目录结构创建相应的Kconfig文件,并将`stm32cubemx`自动生成工程中的Drivers目录拷贝至`stm32f4xx/sdk`目录下: 214 215 ``` 216 . 217 ├── Kconfig.liteos_m.defconfig 218 ├── Kconfig.liteos_m.series 219 ├── Kconfig.liteos_m.soc 220 └── stm32f4xx 221 ├── Kconfig.liteos_m.defconfig.series 222 ├── Kconfig.liteos_m.defconfig.stm32f4xx 223 ├── Kconfig.liteos_m.series 224 ├── Kconfig.liteos_m.soc 225 └── sdk 226 └── Drivers 227 ├── CMSIS 228 └── STM32F4xx_HAL_Driver 229 ``` 230 2316. 修改Kconfig文件内容: 232 233 - 在`//device/soc/st/Kconfig.liteos_m.defconfig`中添加: 234 235 ``` 236 rsource "*/Kconfig.liteos_m.defconfig.series" 237 ``` 238 239 - 在`//device/soc/st/Kconfig.liteos_m.series`中添加: 240 241 ``` 242 rsource "*/Kconfig.liteos_m.series" 243 ``` 244 245 - 在`//device/soc/st/Kconfig.liteos_m.soc`中添加: 246 247 ``` 248 config SOC_COMPANY_STMICROELECTRONICS 249 bool 250 if SOC_COMPANY_STMICROELECTRONICS 251 config SOC_COMPANY 252 default "st" 253 rsource "*/Kconfig.liteos_m.soc" 254 endif # SOC_COMPANY_STMICROELECTRONICS 255 ``` 256 257 - 在`//device/soc/st/stm32f4xx/Kconfig.liteos_m.defconfig.series`中添加: 258 259 ``` 260 if SOC_SERIES_STM32F4xx 261 rsource "Kconfig.liteos_m.defconfig.stm32f4xx" 262 config SOC_SERIES 263 string 264 default "stm32f4xx" 265 endif 266 ``` 267 268 - 在`//device/soc/st/stm32f4xx/Kconfig.liteos_m.defconfig.stm32f4xx`中添加: 269 270 ``` 271 config SOC 272 string 273 default "stm32f4xx" 274 depends on SOC_STM32F4xx 275 ``` 276 277 - 在`//device/soc/st/stm32f4xx/Kconfig.liteos_m.series`中添加: 278 279 ``` 280 config SOC_SERIES_STM32F4xx 281 bool "STMicroelectronics STM32F4xx series" 282 select ARCH_ARM 283 select SOC_COMPANY_STMICROELECTRONICS 284 select CPU_CORTEX_M4 285 help 286 Enable support for STMicroelectronics STM32F4xx series 287 ``` 288 289 - 在`//device/soc/st/stm32f4xx/Kconfig.liteos_m.soc`中添加: 290 291 ``` 292 choice 293 prompt "STMicroelectronics STM32F4xx series SoC" 294 depends on SOC_SERIES_STM32F4xx 295 config SOC_STM32F407 296 bool "SoC STM32F407" 297 endchoice 298 ``` 299 3007. 在`kernel/liteos_m`目录下执行`make menuconfig`,使得能够对`SoC Series`进行选择: 301 302 ![board make menuconfig](figures/niobe407_menuconfig.png) 303 304 结果将自动保存在`$(PRODUCT_PATH)/kernel_configs/debug.config`,下次执行`make menuconfig`时会导出保存的结果。 305 306### BUILD.gn文件适配 307 308为了快速熟悉gn的编译和适配,建议先阅读 [LiteOS-M内核BUILD.gn编写指南](https://gitee.com/caoruihong/kernel_liteos_m/wikis/LiteOS-M%E5%86%85%E6%A0%B8BUILD.gn%E7%BC%96%E5%86%99%E6%8C%87%E5%8D%97)。 309 310**(注意,BUILD.gn文件中不要出现tab字符,所有tab用空格代替)** 311 3121. 在 `kernel/liteos_m/BUILD.gn` 中,可以看到,通过`deps`指定了`Board`和`SoC`的编译入口: 313 314 ``` 315 deps += [ "//device/board/$device_company" ] --- 对应//device/board/talkweb目录 316 deps += [ "//device/soc/$LOSCFG_SOC_COMPANY" ] --- 对应//device/soc/st目录 317 ``` 318 3192. 在`//device/board/talkweb/BUILD.gn`中,新增内容如下: 320 321 ``` 322 if (ohos_kernel_type == "liteos_m") { 323 import("//kernel/liteos_m/liteos.gni") 324 module_name = get_path_info(rebase_path("."), "name") 325 module_group(module_name) { 326 modules = [ "niobe407" ] 327 } 328 } 329 ``` 330 3313. 在niobe407目录下创建BUILD.gn,为了方便管理,将目录名作为模块名: 332 333 ``` 334 import("//kernel/liteos_m/liteos.gni") 335 module_name = get_path_info(rebase_path("."), "name") 336 module_group(module_name) { 337 modules = [ 338 "liteos_m", 339 ] 340 } 341 ``` 342 3434. 将stm32cubemx生成的示例工程Core目录下的文件、`startup_stm32f407xx.s`启动文件和`STM32F407IGTx_FLASH.ld`链接文件拷贝至`//device/board/talkweb/niobe407/liteos_m/`目录下,并在该目录下创建`BUILD.gn`,添加如下内容: 344 345 ``` 346 import("//kernel/liteos_m/liteos.gni") 347 module_name = get_path_info(rebase_path("."), "name") 348 kernel_module(module_name) { 349 sources = [ 350 "startup_stm32f407xx.s", 351 "Src/main.c", 352 "Src/stm32f4xx_hal_msp.c", 353 "Src/stm32f4xx_it.c", 354 "Src/system_stm32f4xx.c", 355 ] 356 include_dirs = [ 357 "Inc", 358 ] 359 } 360 361 config("public") { 362 ldflags = [ 363 "-Wl,-T" + rebase_path("STM32F407IGTx_FLASH.ld"), 364 "-Wl,-u_printf_float", 365 ] 366 libs = [ 367 "c", 368 "m", 369 "nosys", 370 ] 371 } 372 ``` 373 3745. 在make menuconfig中配置`(Top) → Compat → Choose libc implementation`,选择`newlibc`。 375 3766. 由于\_write函数会与kernel的文件操作函数重名,会导致编译失败。后续会换一种方法来适配printf函数,此处我们先将main.c文件中对_write函数的重写删除,将printf函数改用如下方式进行串口打印测试。 377 378 ``` 379 uint8_t test[]={"hello niobe407!!\r\n"}; 380 int len = strlen(test); 381 HAL_UART_Transmit(&huart1, (uint8_t *)test, len, 0xFFFF); 382 ``` 383 3847. 同理`//device/soc/st/BUILD.gn`也是一样,按照目录结构层层依赖包含,最终在`//device/soc/st/stm32f4xx/sdk/BUILD.gn`中通过`kernel_module`模板中指定需要参与编译的文件及编译参数,参考如下: 385 386 ``` 387 import("//kernel/liteos_m/liteos.gni") 388 module_name = "stm32f4xx_sdk" 389 kernel_module(module_name) { 390 asmflags = board_asmflags 391 sources = [ 392 "Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c", 393 "Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c", 394 "Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c", 395 "Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c", 396 "Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c", 397 "Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c", 398 "Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c", 399 "Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_exti.c", 400 "Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c", 401 ] 402 } 403 #指定全局头文件搜索路径 404 config("public") { 405 include_dirs = [ 406 "Drivers/STM32F4xx_HAL_Driver/Inc", 407 "Drivers/CMSIS/Device/ST/STM32F4xx/Include", 408 ] 409 } 410 ``` 411 412### config.gni文件适配 413 414在预编译阶段,在`//device/board/talkweb/niobe407/liteos_m`目录下创建了一个config.gni文件,它其实就是gn脚本的头文件,可以理解为工程构建的全局配置文件。主要配置了CPU型号、交叉编译工具链及全局编译、链接参数等重要信息: 415 416``` 417# Kernel type, e.g. "linux", "liteos_a", "liteos_m". 418kernel_type = "liteos_m" 419 420# Kernel version. 421kernel_version = "3.0.0" 422 423# Board CPU type, e.g. "cortex-a7", "riscv32". 424board_cpu = "cortex-m4" 425 426# Board arch, e.g. "armv7-a", "rv32imac". 427board_arch = "" 428 429# Toolchain name used for system compiling. 430# E.g. gcc-arm-none-eabi, arm-linux-harmonyeabi-gcc, ohos-clang, riscv32-unknown-elf. 431# Note: The default toolchain is "ohos-clang". It's not mandatory if you use the default toolchain. 432board_toolchain = "arm-none-eabi-gcc" 433 434use_board_toolchain = true 435 436# The toolchain path installed, it's not mandatory if you have added toolchain path to your ~/.bashrc. 437board_toolchain_path = "" 438 439# Compiler prefix. 440board_toolchain_prefix = "arm-none-eabi-" 441 442# Compiler type, "gcc" or "clang". 443board_toolchain_type = "gcc" 444 445#Debug compiler optimization level options 446board_opt_flags = [ 447 "-mcpu=cortex-m4", 448 "-mthumb", 449 "-mfpu=fpv4-sp-d16", 450 "-mfloat-abi=hard", 451] 452 453# Board related common compile flags. 454board_cflags = [ 455 "-Og", 456 "-Wall", 457 "-fdata-sections", 458 "-ffunction-sections", 459 "-DSTM32F407xx", 460] 461board_cflags += board_opt_flags 462 463board_asmflags = [ 464 "-Og", 465 "-Wall", 466 "-fdata-sections", 467 "-ffunction-sections", 468] 469board_asmflags += board_opt_flags 470 471board_cxx_flags = board_cflags 472 473board_ld_flags = board_opt_flags 474 475# Board related headfiles search path. 476board_include_dirs = [ "//utils/native/lite/include" ] 477 478# Board adapter dir for OHOS components. 479board_adapter_dir = "" 480``` 481 482如上所示,比较难理解的就是board_opt_flags、board_cflags、board_asmflags等几个参数配置。可以参考如下描述,从stm32cubemx生成的工程中的`Makefile`文件中提取出来: 483``` 484board_opt_flags : 编译器相关选项,一般为芯片架构、浮点类型、编译调试优化等级等选项。 485board_asmflags :汇编编译选项,与Makefile中的ASFLAGS变量对应。 486board_cflags :C代码编译选项,与Makefile中的CFLAGS变量对应。 487board_cxx_flags :C++代码编译选项,与Makefile中的CXXFLAGS变量对应。 488board_ld_flags :链接选项,与Makefile中的LDFLAGS变量对应。 489``` 490 491### 内核子系统适配 492 493在`//vendor/talkweb/niobe407/config.json`文件中添加内核子系统及相关配置,如下所示: 494 495``` 496{ 497 "product_name": "niobe407", 498 "type": "mini", 499 "version": "3.0", 500 "device_company": "talkweb", 501 "board": "niobe407", 502 "kernel_type": "liteos_m", 503 "kernel_version": "3.0.0", 504 "subsystems": [ 505 { 506 "subsystem": "kernel", 507 "components": [ 508 { 509 "component": "liteos_m" 510 } 511 ] 512 } 513 ], 514 "product_adapter_dir": "", 515 "third_party_dir": "//third_party" 516} 517``` 518 519### target_config.h文件适配 520 521在`//kernel/liteos_m/kernel/include/los_config.h`文件中,有包含一个名为target_config.h的头文件,如果没有这个头文件,则会编译出错。 522 523该头文件的作用主要是定义一些与soc芯片相关的宏定义,可以创建一个空头文件,再配合编译报错提示信息来确定需要定义哪些宏。经验证,Cortex-M4的核适配只需定义`LOSCFG_BASE_CORE_TICK_RESPONSE_MAX`宏并包含`stm32f4xx.h`头文件即可将kernel编译通过。 524 525若前期不知如何配置,可以参考虚拟机qemu示例中`//device/qemu/arm_mps2_an386/liteos_m/board/target_config.h`的配置。 526 527``` 528#ifndef _TARGET_CONFIG_H 529#define _TARGET_CONFIG_H 530 531#define LOSCFG_BASE_CORE_TICK_RESPONSE_MAX 0xFFFFFFUL 532#include "stm32f4xx.h" //包含了stm32f4平台大量的宏定义 533 534#endif 535``` 536 537其中宏定义`LOSCFG_BASE_CORE_TICK_RESPONSE_MAX`是直接参考的`//device/qemu/arm_mps2_an386/liteos_m/board/target_config.h`文件中的配置,`//device/qemu/arm_mps2_an386`是`cortex-m4`的虚拟机工程,后续适配可以直接参考,在此不做深入讲解。 538 539### 内核启动适配 540 541至此,已经可以成功将kernel子系统编译通过,并且在out目录下生成OHOS_Image.bin文件。将生成的OHOS_Image.bin文件烧录至开发板,验证板子能否正常启动运行,如果能成功打印出main函数中串口输出的正确的打印信息,则可以开始进行内核启动适配。 542 5431. 为liteos_m分配内存,适配内存分配函数 544 545 在文件`//kernel/liteos_m/kernel/src/mm/los_memory.c`中,`OsMemSystemInit`函数通过LOS_MemInit进行了内存初始化。可以看到几个比较关键的宏需要我们指定,我们将其添加到`target_config.h`中: 546 547 ``` 548 extern unsigned int __los_heap_addr_start__; 549 extern unsigned int __los_heap_addr_end__; 550 #define LOSCFG_SYS_EXTERNAL_HEAP 1 551 #define LOSCFG_SYS_HEAP_ADDR ((void *)&__los_heap_addr_start__) 552 #define LOSCFG_SYS_HEAP_SIZE (((unsigned long)&__los_heap_addr_end__) - ((unsigned long)&__los_heap_addr_start__)) 553 ``` 554 555 其中,`__los_heap_addr_start__`与`__los_heap_addr_end__`变量在`STM32F407IGTx_FLASH.ld`链接文件中被定义, 将_user_heap_stack花括号内内容修改为: 556 557 ``` 558 ._user_heap_stack : 559 { 560 . = ALIGN(0x40); 561 __los_heap_addr_start__ = .; 562 __los_heap_addr_end__ = ORIGIN(RAM) + LENGTH(RAM); 563 } >RAM 564 ``` 565 566 除此之外,我们还需要适配内存分配函数,由于内核中已经对_malloc_r等内存分配函数进行了实现,在此我们采用包装函数的方式来适配,用内核中的内存分配函数替换标准库中的内存分配函数即可,在`//device/board/talkweb/niobe407/liteos_m/config.gni`中board_ld_flags链接参数变量修改为: 567 568 ``` 569 board_ld_flags = [ 570 "-Wl,--wrap=_calloc_r", 571 "-Wl,--wrap=_malloc_r", 572 "-Wl,--wrap=_realloc_r", 573 "-Wl,--wrap=_reallocf_r", 574 "-Wl,--wrap=_free_r", 575 "-Wl,--wrap=_memalign_r", 576 "-Wl,--wrap=_malloc_usable_size_r", 577 ] 578 board_ld_flags += board_opt_flags 579 ``` 580 5812. 适配printf打印 582 583 为了方便后续调试,第一步需要先适配printf函数。而printf的函数适配可大可小,在此只做简单适配,具体实现可以参考其它各开发板源码。 584 585 在main.c同级目录下创建dprintf.c文件,文件内容如下: 586 587 ``` 588 #include <stdarg.h> 589 #include "los_interrupt.h" 590 #include <stdio.h> 591 592 extern UART_HandleTypeDef huart1; 593 594 INT32 UartPutc(INT32 ch, VOID *file) 595 { 596 char RL = '\r'; 597 if (ch =='\n') { 598 HAL_UART_Transmit(&huart1, &RL, 1, 0xFFFF); 599 } 600 return HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF); 601 } 602 603 static void dputs(char const *s, int (*pFputc)(int n, FILE *cookie), void *cookie) 604 { 605 unsigned int intSave; 606 607 intSave = LOS_IntLock(); 608 while (*s) { 609 pFputc(*s++, cookie); 610 } 611 LOS_IntRestore(intSave); 612 } 613 614 int printf(char const *fmt, ...) 615 { 616 char buf[1024] = { 0 }; 617 va_list ap; 618 va_start(ap, fmt); 619 int len = vsnprintf_s(buf, sizeof(buf), 1024 - 1, fmt, ap); 620 va_end(ap); 621 if (len > 0) { 622 dputs(buf, UartPutc, 0); 623 } else { 624 dputs("printf error!\n", UartPutc, 0); 625 } 626 return len; 627 } 628 ``` 629 630 将dprintf.c文件加入BUILD.gn编译脚本,参与编译。 631 632 ``` 633 kernel_module(module_name) { 634 sources = [ 635 "startup_stm32f407xx.s", 636 ] 637 638 sources += [ 639 "Src/main.c", 640 "Src/dprintf.c", 641 "Src/stm32f4xx_hal_msp.c", 642 "Src/stm32f4xx_it.c", 643 "Src/system_stm32f4xx.c", 644 ] 645 } 646 ``` 647 648 在串口初始化之后使用printf函数打印,测试是否适配成功。 649 6503. 调用LOS_KernelInit初始化内核,进入任务调度。 651 652 在main函数中串口初始化之后,调用`LOS_KernelInit`进行初始化,创建任务示例,进入任务调度。 653 654 ``` 655 #include "los_task.h" 656 657 UINT32 ret; 658 ret = LOS_KernelInit(); //初始化内核 659 if (ret == LOS_OK) { 660 TaskSample(); //示例任务函数,在此函数中创建线程任务 661 LOS_Start(); //开始任务调度,程序执行将阻塞在此,由内核接管调度 662 } 663 ``` 664 665 其中`TaskSample()`函数内容如下: 666 667 ``` 668 VOID TaskSampleEntry2(VOID) 669 { 670 while (1) { 671 printf("TaskSampleEntry2 running...\n"); 672 (VOID)LOS_TaskDelay(2000); /* 2000 millisecond */ 673 } 674 } 675 676 VOID TaskSampleEntry1(VOID) 677 { 678 while (1) { 679 printf("TaskSampleEntry1 running...\n"); 680 (VOID)LOS_TaskDelay(2000); /* 2000 millisecond */ 681 } 682 } 683 VOID TaskSample(VOID) 684 { 685 UINT32 uwRet; 686 UINT32 taskID1; 687 UINT32 taskID2; 688 TSK_INIT_PARAM_S stTask = {0}; 689 690 stTask.pfnTaskEntry = (TSK_ENTRY_FUNC)TaskSampleEntry1; 691 stTask.uwStackSize = 0x1000; 692 stTask.pcName = "TaskSampleEntry1"; 693 stTask.usTaskPrio = 6; /* Os task priority is 6 */ 694 uwRet = LOS_TaskCreate(&taskID1, &stTask); 695 if (uwRet != LOS_OK) { 696 printf("Task1 create failed\n"); 697 } 698 699 stTask.pfnTaskEntry = (TSK_ENTRY_FUNC)TaskSampleEntry2; 700 stTask.uwStackSize = 0x1000; 701 stTask.pcName = "TaskSampleEntry2"; 702 stTask.usTaskPrio = 7; /* Os task priority is 7 */ 703 uwRet = LOS_TaskCreate(&taskID2, &stTask); 704 if (uwRet != LOS_OK) { 705 printf("Task2 create failed\n"); 706 } 707 } 708 ``` 709 710适配完内核启动后,可以通过调试串口看到如下打印信息: 711 712 ![niobe407_boot](figures/niobe407_boot.png) 713 714后续还需要对整个基础内核进行详细适配验证。 715 716### 内核基础功能适配 717 718内核基础功能适配项包括:**[中断管理](../kernel/kernel-mini-basic-interrupt.md)**、**[任务管理](../kernel/kernel-mini-basic-task.md)**、**[内存管理](../kernel/kernel-mini-basic-memory.md)**、**[内核通信机制](../kernel/kernel-mini-basic-ipc-event.md)**、**[时间管理](../kernel/kernel-mini-basic-time.md)**、**[软件定时器](../kernel/kernel-mini-basic-soft.md)**,可以参考对应链接中的编程实例进行内核基础功能验证。在验证的过程中发现问题,针对相应问题进行具体的适配。 719 720从上一节中打印信息输出时间间隔可以看出,`LOS_TaskDelay`函数的延时时间不准确,我们可以在`target_config.h`中定义如下宏进行内核时钟适配: 721 722 ``` 723 #define OS_SYS_CLOCK 168000000 724 #define LOSCFG_BASE_CORE_TICK_PER_SECOND (1000UL) 725 ``` 726其它内核基础功能的适配方法大多也是围绕于`target_config.h`中的宏定义,需要大家配合`//kernel/liteos_m`下源码,自行尝试摸索,在此不做进一步讲解。 727 728## littlefs文件系统移植适配 729 730 `Niobe407`开发板外挂了16MB的SPI-FLASH,Niobe407基于该Flash进行了littlefs适配。 731 732内核已经对littlefs进行了适配,我们只需要开启Kconfig中的配置,然后适配Littlefs如下接口: 733 734``` 735 int32_t LittlefsRead(const struct lfs_config *cfg, lfs_block_t block, 736 lfs_off_t off, void *buffer, lfs_size_t size) 737 { 738 W25x_BufferRead(buffer, cfg->context + cfg->block_size * block + off, size); 739 return LFS_ERR_OK; 740 } 741 742 int32_t LittlefsProg(const struct lfs_config *cfg, lfs_block_t block, 743 lfs_off_t off, const void *buffer, lfs_size_t size) 744 { 745 W25x_BufferWrite((uint8_t *)buffer,cfg->context + cfg->block_size * block + off,size); 746 return LFS_ERR_OK; 747 } 748 749 int32_t LittlefsErase(const struct lfs_config *cfg, lfs_block_t block) 750 { 751 W25x_SectorErase(cfg->context + cfg->block_size * block); 752 return LFS_ERR_OK; 753 } 754 755 int32_t LittlefsSync(const struct lfs_config *cfg) 756 { 757 return LFS_ERR_OK; 758 } 759``` 760 761`W25x_BufferRead`等函数是spi-flash读写操作的接口,不同型号的spi-flash其实现也不同,Niobe407的SPI-Flash操作具体实现可参考`//device/board/talkweb/niobe407/liteos_m/drivers/spi_flash/src/w25qxx.c` 762 763由于SPI已经hdf化了,而littlefs依赖于spi驱动,为了方便对文件系统进行配置,可以将littlefs的配置加入至.hcs文件中,具体参考:`//device/board/talkweb/niobe407/liteos_m/hdf_config/hdf_littlefs.hcs`文件 764 765``` 766misc { 767 littlefs_config { 768 match_attr = "littlefs_config"; 769 mount_points = ["/talkweb"]; 770 partitions = [0x800000]; 771 block_size = [4096]; 772 block_count = [256]; 773 } 774} 775``` 776 777## 板级驱动移植 778 779驱动适配相关文件放置在`//drivers/adapter/platform`中,对应有`gpio`,`i2c`,`pwm`,`spi`,`uart`,`watchdog`,都是通过`HDF`机制加载,本章节以`pwm`为例进行说明。 780 781### PWM驱动适配 782在HDF框架中,PWM的接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDF DeviceManager的服务管理能力,但需要为每个设备单独配置设备节点。 783 784- 接口说明 785 786 ``` 787 1. pwm open初始化函数:DevHandle PwmOpen(uint32_t num); 788 参数说明: 789 num: PWM设备编号。 790 return: 获取成功返回PWM设备句柄,失败返回NULL。 791 2. pwm close去初始化函数:void PwmClose(DevHandle handle); 792 参数说明: 793 handle: pwm设备句柄。 794 return: 无。 795 3. 设置PWM设备参数:int32_t PwmSetConfig(DevHandle handle, struct PwmConfig *config); 796 参数说明: 797 handle: pwm设备句柄。 798 *config 参数指针。 799 return: 返回0表示设置成功,返回负数表示失败。 800 ``` 801 802- PWM HDF HCS配置文件解析 803 804 `device_info.hcs`文件位于`//device/board/talkweb/niobe407/liteos_m/hdf_config/device_info.hcs`,以下示例为使用TIM2、TIM3和TIM7定时器输出PWM信号: 805 806 ``` 807 device_pwm1 :: device { 808 pwm1 :: deviceNode { 809 policy = 2; 810 priority = 100; 811 moduleName = "ST_HDF_PLATFORM_PWM"; 812 serviceName = "HDF_PLATFORM_PWM_1"; 813 deviceMatchAttr = "config_pwm1"; 814 } 815 } 816 device_pwm2 :: device { 817 pwm2 :: deviceNode { 818 policy = 2; 819 priority = 100; 820 moduleName = "ST_HDF_PLATFORM_PWM"; 821 serviceName = "HDF_PLATFORM_PWM_2"; 822 deviceMatchAttr = "config_pwm2"; 823 } 824 } 825 device_pwm7 :: device { 826 pwm7 :: deviceNode { 827 policy = 2; 828 priority = 100; 829 moduleName = "ST_HDF_PLATFORM_PWM"; 830 serviceName = "HDF_PLATFORM_PWM_7"; 831 deviceMatchAttr = "config_pwm7"; 832 } 833 } 834 ``` 835 836 `hdf.hcs`文件位于`//device/board/talkweb/niobe407/liteos_m/hdf_config/hdf.hcs`,在此文件中配置TIM定时器具体信息: 837 838 ``` 839 --- 注意:tim2-tim7、tim12-tim14时钟频率为84M,TIM1、TIM8~TIM11为168M,tim6和tim7不能输出pwm。 840 --- tim1~tim5、tim8有4个channel,tim9、tim12有2个channel,tim10、tim11、tim13、tim14只有1个channel。 841 842 pwm_config { 843 pwm1_config { 844 match_attr = "config_pwm1"; 845 pwmTim = 1; --- 定时器ID tim2(0:tim1,1:tim2,...,tim6和tim7不可用) 846 pwmCh = 3; --- 对应channel数(0:ch1、1:ch2、2:ch3、3:ch4) 847 prescaler = 4199; --- 预分频数,例如tim2时钟为84M,(84M/(4199+1))=20khz,则以20khz为基准。 848 } 849 pwm2_config { 850 match_attr = "config_pwm2"; 851 pwmTim = 2; 852 pwmCh = 0; 853 prescaler = 8399; 854 } 855 pwm3_config { 856 match_attr = "config_pwm7"; 857 pwmTim = 7; 858 pwmCh = 0; 859 prescaler = 8399; 860 } 861 } 862 ``` 863 864`hdf pwm`适配代码请参考:`//drivers/adapter/platform/pwm/pwm_stm32f4xx.c` 865 866`hdf pwm`使用示例可请参考:`//device/board/talkweb/niobe407/applications/206_hdf_pwm` 867 868## 子系统适配 869 870`OpenHarmony`子系统适配一般包含两部分: 871 872- 在`config.json`中增加对应子系统和部件,这样编译系统会将该部件纳入编译目标中。 873- 针对该部件的`HAL`层接口进行硬件适配,或者可选的软件功能适配。 874 875### LWIP部件适配 876 877`LiteOS-M kernel`通过Kconfig配置可以使lwip参与编译,并可以在`kernel`组件中指定`lwip`编译适配的目录。如下: 878 879``` 880{ 881 "subsystem": "kernel", 882 "components": [ 883 { 884 "component": "liteos_m", 885 "features": [ 886 "ohos_kernel_liteos_m_lwip_path = \"//device/board/talkweb/niobe407/liteos_m/lwip_adapter\"" --- 指定适配路径 887 ] 888 } 889 ] 890} 891``` 892 893在指定的编译适配目录中,通过`#include_next "lwip/lwipopts.h"`的方式入侵修改lwip三方库中头文件配置,关于有线以太网LWIP适配部分,后续会补充详细适配步骤,在此先不做深入讲解。 894 895### 启动恢复子系统适配 896 897启动恢复子系统适配`bootstrap_lite`和`syspara_lite`两个组件。请在`//vendor/talkweb/niobe407/config.json`中新增对应的配置选项。 898 899``` 900{ 901 "subsystem": "startup", 902 "components": [ 903 { 904 "component": "bootstrap_lite", 905 "features": [] 906 }, 907 { 908 "component": "syspara_lite", 909 "features": [] 910 } 911 ] 912} 913``` 914 915适配`bootstrap_lite`部件时,需要在链接文件`//device/board/talkweb/niobe407/liteos_m/STM32F407IGTx_FLASH.ld`中手动新增如下段: 916 917``` 918__zinitcall_bsp_start = .; 919KEEP (*(.zinitcall.bsp0.init)) 920KEEP (*(.zinitcall.bsp1.init)) 921KEEP (*(.zinitcall.bsp2.init)) 922KEEP (*(.zinitcall.bsp3.init)) 923KEEP (*(.zinitcall.bsp4.init)) 924__zinitcall_bsp_end = .; 925__zinitcall_device_start = .; 926KEEP (*(.zinitcall.device0.init)) 927KEEP (*(.zinitcall.device1.init)) 928KEEP (*(.zinitcall.device2.init)) 929KEEP (*(.zinitcall.device3.init)) 930KEEP (*(.zinitcall.device4.init)) 931__zinitcall_device_end = .; 932__zinitcall_core_start = .; 933KEEP (*(.zinitcall.core0.init)) 934KEEP (*(.zinitcall.core1.init)) 935KEEP (*(.zinitcall.core2.init)) 936KEEP (*(.zinitcall.core3.init)) 937KEEP (*(.zinitcall.core4.init)) 938__zinitcall_core_end = .; 939__zinitcall_sys_service_start = .; 940KEEP (*(.zinitcall.sys.service0.init)) 941KEEP (*(.zinitcall.sys.service1.init)) 942KEEP (*(.zinitcall.sys.service2.init)) 943KEEP (*(.zinitcall.sys.service3.init)) 944KEEP (*(.zinitcall.sys.service4.init)) 945__zinitcall_sys_service_end = .; 946__zinitcall_sys_feature_start = .; 947KEEP (*(.zinitcall.sys.feature0.init)) 948KEEP (*(.zinitcall.sys.feature1.init)) 949KEEP (*(.zinitcall.sys.feature2.init)) 950KEEP (*(.zinitcall.sys.feature3.init)) 951KEEP (*(.zinitcall.sys.feature4.init)) 952__zinitcall_sys_feature_end = .; 953__zinitcall_run_start = .; 954KEEP (*(.zinitcall.run0.init)) 955KEEP (*(.zinitcall.run1.init)) 956KEEP (*(.zinitcall.run2.init)) 957KEEP (*(.zinitcall.run3.init)) 958KEEP (*(.zinitcall.run4.init)) 959__zinitcall_run_end = .; 960__zinitcall_app_service_start = .; 961KEEP (*(.zinitcall.app.service0.init)) 962KEEP (*(.zinitcall.app.service1.init)) 963KEEP (*(.zinitcall.app.service2.init)) 964KEEP (*(.zinitcall.app.service3.init)) 965KEEP (*(.zinitcall.app.service4.init)) 966__zinitcall_app_service_end = .; 967__zinitcall_app_feature_start = .; 968KEEP (*(.zinitcall.app.feature0.init)) 969KEEP (*(.zinitcall.app.feature1.init)) 970KEEP (*(.zinitcall.app.feature2.init)) 971KEEP (*(.zinitcall.app.feature3.init)) 972KEEP (*(.zinitcall.app.feature4.init)) 973__zinitcall_app_feature_end = .; 974__zinitcall_test_start = .; 975KEEP (*(.zinitcall.test0.init)) 976KEEP (*(.zinitcall.test1.init)) 977KEEP (*(.zinitcall.test2.init)) 978KEEP (*(.zinitcall.test3.init)) 979KEEP (*(.zinitcall.test4.init)) 980__zinitcall_test_end = .; 981__zinitcall_exit_start = .; 982KEEP (*(.zinitcall.exit0.init)) 983KEEP (*(.zinitcall.exit1.init)) 984KEEP (*(.zinitcall.exit2.init)) 985KEEP (*(.zinitcall.exit3.init)) 986KEEP (*(.zinitcall.exit4.init)) 987__zinitcall_exit_end = .; 988``` 989 990需要新增上述段是因为`bootstrap_init`提供的对外接口,见`//utils/native/lite/include/ohos_init.h`文件,采用的是灌段的形式,最终会保存到上述链接段中。主要的服务自动初始化宏如下表格所示: 991 992| 接口名 | 描述 | 993| ---------------------- | -------------------------------- | 994| SYS_SERVICE_INIT(func) | 标识核心系统服务的初始化启动入口 | 995| SYS_FEATURE_INIT(func) | 标识核心系统功能的初始化启动入口 | 996| APP_SERVICE_INIT(func) | 标识应用层服务的初始化启动入口 | 997| APP_FEATURE_INIT(func) | 标识应用层功能的初始化启动入口 | 998 999 1000 1001通过上面加载的组件编译出来的lib文件需要手动加入强制链接。 1002 1003如在 `//vendor/talkweb/niobe407/config.json` 中配置了`bootstrap_lite` 部件 1004 1005``` 1006 { 1007 "subsystem": "startup", 1008 "components": [ 1009 { 1010 "component": "bootstrap_lite" 1011 }, 1012 ... 1013 ] 1014 }, 1015``` 1016 1017 `bootstrap_lite`部件会编译`//base/startup/bootstrap_lite/services/source/bootstrap_service.c`,该文件中,通过`SYS_SERVICE_INIT`将`Init`函数符号灌段到`__zinitcall_sys_service_start`和`__zinitcall_sys_service_end`中,由于`Init`函数是没有显式调用它,所以需要将它强制链接到最终的镜像。如下: 1018 1019``` 1020static void Init(void) 1021{ 1022 static Bootstrap bootstrap; 1023 bootstrap.GetName = GetName; 1024 bootstrap.Initialize = Initialize; 1025 bootstrap.MessageHandle = MessageHandle; 1026 bootstrap.GetTaskConfig = GetTaskConfig; 1027 bootstrap.flag = FALSE; 1028 SAMGR_GetInstance()->RegisterService((Service *)&bootstrap); 1029} 1030SYS_SERVICE_INIT(Init); --- 通过SYS启动即SYS_INIT启动就需要强制链接生成的lib 1031``` 1032 1033 在`//base/startup/bootstrap_lite/services/source/BUILD.gn`文件中,描述了在`//out/niobe407/niobe407/libs` 生成 `libbootstrap.a`,如下: 1034 1035``` 1036static_library("bootstrap") { 1037 sources = [ 1038 "bootstrap_service.c", 1039 "system_init.c", 1040 ] 1041 ... 1042``` 1043 1044适配`syspara_lite`部件时,系统参数会最终写到文件中进行持久化保存。在轻量系统中,文件操作相关接口有`POSIX`接口与`HalFiles`接口这两套实现。 1045 1046因为对接内核的文件系统,采用`POSIX`相关的接口,所以`features`字段中需要增加`enable_ohos_startup_syspara_lite_use_posix_file_api = true`。 1047 1048如果对接`HalFiles`相关的接口实现的,则无须修改。 1049### DFX子系统适配 1050 1051进行`DFX`子系统适配需要添加`hilog_lite`和`hievent_lite`部件,直接在`config.json`文件配置即可。 1052 1053``` 1054{ 1055 "subsystem": "hiviewdfx", 1056 "components": [ 1057 { 1058 "component": "hilog_lite", 1059 "features": [] 1060 }, 1061 { 1062 "component": "hievent_lite", 1063 "features": [] 1064 } 1065 ] 1066} 1067``` 1068 1069配置完成之后,需要注册日志输出实现函数,并加入编译。 1070 1071``` 1072bool HilogProc_Impl(const HiLogContent *hilogContent, uint32_t len) 1073{ 1074 char tempOutStr[LOG_FMT_MAX_LEN]; 1075 tempOutStr[0] = 0,tempOutStr[1] = 0; 1076 if (LogContentFmt(tempOutStr, sizeof(tempOutStr), hilogContent) > 0) { 1077 printf(tempOutStr); 1078 } 1079 return true; 1080} 1081 1082HiviewRegisterHilogProc(HilogProc_Impl); 1083``` 1084 1085### 系统服务管理子系统适配 1086 1087进行系统服务管理子系统适配需要添加`samgr_lite`部件,直接在`config.json`配置即可。 1088 1089``` 1090{ 1091 "subsystem": "systemabilitymgr", 1092 "components": [ 1093 { 1094 "component": "samgr_lite", 1095 "features": [] 1096 } 1097 ] 1098} 1099``` 1100 1101在轻量系统中,`samgr_lite`配置的共享任务栈大小默认为`2048`。在适配时可以在features中,通过`config_ohos_systemabilitymgr_samgr_lite_shared_task_size`重新设置共享任务栈大小。 1102 1103``` 1104"config_ohos_systemabilitymgr_samgr_lite_shared_task_size = 4096" 1105``` 1106 1107### 安全子系统适配 1108 1109进行安全子系统适配需要添加`huks`组件,直接在`config.json`配置即可。 1110 1111``` 1112{ 1113 "subsystem": "security", 1114 "components": [ 1115 { 1116 "component": "huks", 1117 "features": [ 1118 "huks_use_lite_storage = true", 1119 "huks_use_hardware_root_key = true", 1120 "huks_config_file = \"hks_config_lite.h\"", 1121 "huks_key_store_path = \"storage\"" 1122 ] 1123 } 1124 ] 1125} 1126``` 1127 1128`huks`部件适配时,`huks_key_store_path`配置选项用于指定存放秘钥路径,`huks_config_file`为配置头文件名称。 1129 1130### 公共基础库子系统适配 1131 1132公共基础库子系统适配添加了`kv_store`、`file`、`os_dump`组件,直接在`config.json`配置即可。 1133 1134``` 1135{ 1136 "subsystem": "utils", 1137 "components": [ 1138 { 1139 "component": "file", 1140 "features": [] 1141 }, 1142 { 1143 "component": "kv_store", 1144 "features": [ 1145 "enable_ohos_utils_native_lite_kv_store_use_posix_kv_api = false" 1146 ] 1147 }, 1148 { 1149 "component": "os_dump", 1150 "features": [] 1151 } 1152 ] 1153}, 1154``` 1155 1156与适配`syspara_lite`部件类似,适配`kv_store`部件时,键值对会写到文件中。在轻量系统中,文件操作相关接口有`POSIX`接口与`HalFiles`接口这两套实现。因为对接内核的文件系统,采用`POSIX`相关的接口,所以`features`需要增加`enable_ohos_utils_native_lite_kv_store_use_posix_kv_api = true`。如果对接`HalFiles`相关的接口实现的,则无须修改。 1157 1158### HDF子系统适配 1159 1160与启动恢复子系统适配类似,我们需要在链接文件`//device/board/talkweb/niobe407/liteos_m/STM32F407IGTx_FLASH.ld`中手动新增如下段: 1161 1162``` 1163_hdf_drivers_start = .; 1164KEEP(*(.hdf.driver)) 1165_hdf_drivers_end = .; 1166``` 1167 1168然后,在kernel初始化完成后调用DeviceManagerStart函数,执行完成后,才能调用hdf接口控制外设。 1169 1170``` 1171#include "devmgr_service_start.h" --- 注意需要包含该头文件 1172 1173#ifdef LOSCFG_DRIVERS_HDF 1174 DeviceManagerStart(); 1175#endif 1176``` 1177 1178`devmgr_service_start.h`头文件所在路径为: `//drivers/framework/core/common/include/manager`,为保证编译时能找到该头文件,需要将其加入到include_dirs中: 1179 1180### XTS兼容性测评子系统适配 1181 1182#### 产品兼容性规范 1183 1184产品兼容性规范文档请参考[产品兼容性SIG介绍](https://gitee.com/openharmony-sig/compatibility/tree/master)。 1185 1186#### 添加XTS子系统 1187 1188`XTS`测试参考资料见[xts参考资料](../device-test/xts.md),进行`XTS`子系统适配需要添加`xts_acts`与`xts_tools`组件,直接在`config.json`配置即可,配置如下: 1189 1190 { 1191 "subsystem": "xts", 1192 "components": [ 1193 { 1194 "component": "xts_acts", 1195 "features": [] 1196 }, 1197 { 1198 "component": "xts_tools", 1199 "features": [] 1200 } 1201 ] 1202 } 1203 1204我们可以在xts_acts组件的features数组中指定如下属性: 1205 1206- `config_ohos_xts_acts_utils_lite_kv_store_data_path` 配置挂载文件系统根目录的名字。 1207- `enable_ohos_test_xts_acts_use_thirdparty_lwip` 表示如果使用`thirdparty/lwip`目录下的源码编译,则设置为`true`,否则设置为`false`。 1208 1209#### 编译XTS 1210 1211在配置config.json后,使用`hb build`是不会去编译xts的,只有在debug版本编译时才会参与编译,并且需要我们强制链接需要进行测试的套件静态库。 1212 1213在我们`//device/board/talkweb/liteos_m`下包含`kernel_module`的BUILD.gn 脚本中添加如下内容: 1214 1215``` 1216config("public") { 1217 if (build_xts) { 1218 lib_dirs = [ "$root_out_dir/libs" ] 1219 ldflags += [ 1220 "-Wl,--whole-archive", --- 开启whole-archive特性,可以把在其后面出现的静态库包含的函数和变量输出到动态库 1221 "-lbootstrap", 1222 "-lbroadcast", 1223 "-lhctest", 1224 1225 #公共基础库 1226 # "-lmodule_ActsUtilsFileTest", 1227 # "-lmodule_ActsKvStoreTest", 1228 1229 #DFX 1230 "-lmodule_ActsDfxFuncTest", 1231 "-lmodule_ActsHieventLiteTest", 1232 1233 #启动恢复 1234 # "-lmodule_ActsBootstrapTest", 1235 # "-lmodule_ActsParameterTest", 1236 1237 #分布式任务调度 1238 # "-lmodule_ActsSamgrTest", 1239 1240 "-Wl,--no-whole-archive", --- 关掉whole-archive这个特性 1241 ] 1242 } 1243} 1244``` 1245 1246由于Niobe407开发板内存有限,xts测试时需要分套件测试。执行如下编译命令,即可生成包含xts测试的固件。 1247 1248``` 1249hb build -f -b debug --gn-args build_xts=true 1250``` 1251 1252此外,我们还需要修改`//vendor/talkweb/niobe407/hals/utils/sys_param/hal_sys_param.c`文件,将这些字符串定义正确。 1253 1254``` 1255static const char OHOS_DEVICE_TYPE[] = {"Evaluation Board"}; 1256static const char OHOS_DISPLAY_VERSION[] = {"OpenHarmony 3.1"}; 1257static const char OHOS_MANUFACTURE[] = {"Talkweb"}; 1258static const char OHOS_BRAND[] = {"Talkweb"}; 1259static const char OHOS_MARKET_NAME[] = {"Niobe"}; 1260static const char OHOS_PRODUCT_SERIES[] = {"Niobe"}; 1261static const char OHOS_PRODUCT_MODEL[] = {"Niobe407"}; 1262static const char OHOS_SOFTWARE_MODEL[] = {"1.0.0"}; 1263static const char OHOS_HARDWARE_MODEL[] = {"2.0.0"}; 1264static const char OHOS_HARDWARE_PROFILE[] = {"RAM:192K,ROM:1M,ETH:true"}; 1265static const char OHOS_BOOTLOADER_VERSION[] = {"twboot-v2022.03"}; 1266static const char OHOS_ABI_LIST[] = {"armm4_hard_fpv4-sp-d16-liteos"}; 1267static const char OHOS_SERIAL[] = {"1234567890"}; // provided by OEM. 1268``` 1269 1270#### 验证XTS 1271 1272编译完成后,将固件烧录至开发板,xts全部跑完会有显示`xx Tests xx Failures xx Ignored`等信息,以下以公共基础库测试为例: 1273 1274``` 1275../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:590:testKvStoreClearCache002:PASS 1276../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:625:testKvStoreCacheSize001:PASS 1277../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:653:testKvStoreCacheSize002:PASS 1278../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:681:testKvStoreCacheSize003:PASS 1279../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:709:testKvStoreMaxSize001:PASS 1280../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:737:testKvStoreMaxSize002:PASS 1281../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:765:testKvStoreMaxSize003:PASS 1282../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:793:testKvStoreMaxSize004:PASS 1283+-------------------------------------------+ 1284 1285----------------------- 128632 Tests 0 Failures 0 Ignored 1287OK 1288All the test suites finished! 1289``` 1290