ws63系列是2.4GHz Wi-Fi 6 星闪多模解决方案,其中ws63E支持2.4GHz的雷达人体活动检测功能,适用于大小家电、电工照明及对人体出没检测有需求的常电类物联网智能场景。
该fbb_ws63代码包从统一开发平台FBB(Family Big Box,统一开发框架,统一API)构建而来,在该平台上开发的应用很容易被移植到其他星闪解决方案上,有效降低开发者门槛,缩短开发周期,支持开发者快速开发星闪产品。
软件文档在线化链接:https://docs.hisilicon.com/repos/fbb_ws63/zh-CN/master/
概述
WS63V100的SDK使用CMake作为构建工具,建议使用CMake进行第三方库的移植和组件开发,从而保证编译的完整性和连贯性。本文档详细介绍WS63平台的CMake构建系统,帮助开发者快速掌握CMakeLists.txt的编写方法。目标芯片是海思WS63 RISC-V架构芯片,支持华为LiteOS和openharmony-liteos-m。
文档地址:https://gitcode.com/HiSpark/fbb_ws63/blob/master/README.md

一、CMake构建系统架构
1.1 构建系统组成
WS63平台的CMake构建系统主要由以下部分组成:
1.2 构建流程
1. 执行 build.py menuconfig ws63-liteos-app
2. 在配置界面选择需要编译的组件和功能
3. 执行 build.py ws63-liteos-app 开始编译
4. CMake解析CMakeLists.txt生成Makefile
5. 编译器执行编译生成固件
二、CMakeLists.txt基本语法
2.1 核心变量说明
COMPONENT_NAME
设置组件名称,用于标识组件。
cmake
set(COMPONENT_NAME "xiaohong")
SOURCES
指定需要编译的源文件列表。
cmake
set(SOURCES "${SOURCES}"
"${CMAKE_CURRENT_SOURCE_DIR}/main.c"
"${CMAKE_CURRENT_SOURCE_DIR}/task_entry.c"
"${CMAKE_CURRENT_SOURCE_DIR}/settings.c"
PARENT_SCOPE)
注意 :使用 PARENT_SCOPE 将变量传递给父作用域,这是WS63 CMake框架的重要特性。
PUBLIC_HEADER
指定公共头文件路径,这些路径会被添加到编译器的头文件搜索路径中,供其他组件引用。
cmake
set(PUBLIC_HEADER "${PUBLIC_HEADER}"
"${CMAKE_CURRENT_SOURCE_DIR}/src"
"${CMAKE_CURRENT_SOURCE_DIR}/src/boards"
"${CMAKE_CURRENT_SOURCE_DIR}/src/display"
PARENT_SCOPE)
PRIVATE_HEADER
指定私有头文件路径,仅当前组件内部使用。
cmake
set(PRIVATE_HEADER
"${CMAKE_CURRENT_SOURCE_DIR}/inc"
)
COMPONENT_CCFLAGS
设置组件级别的C编译器标志,用于控制编译选项和警告处理。
cmake
set(COMPONENT_CCFLAGS
-Wno-error=sign-compare
-Wno-sign-compare
-Wno-error=unused-parameter
-Wno-unused-parameter
)
PUBLIC_DEFINES 和 PRIVATE_DEFINES
设置公共和私有宏定义。
cmake
set(PUBLIC_DEFINES
"CONFIG_LVGL_ENABLE=1"
)
set(PRIVATE_DEFINES
"DEBUG_MODE=1"
)
2.2 条件编译
使用 if() 语句实现条件编译,根据配置变量决定是否编译某些文件。
cmake
if(DEFINED CONFIG_XIAOHONG_BOARD_NAME AND "${CONFIG_XIAOHONG_BOARD_NAME}" STREQUAL "xiaohong_ws63_v1")
set(SOURCES "${SOURCES}"
"${CMAKE_CURRENT_SOURCE_DIR}/boards/xiaohong_ws63_v1/board_config.c"
"${CMAKE_CURRENT_SOURCE_DIR}/boards/xiaohong_ws63_v1/key_config.c"
PARENT_SCOPE)
endif()
if(DEFINED CONFIG_XIAOHONG_LVGL_ENABLE)
set(SOURCES "${SOURCES}"
"${CMAKE_CURRENT_SOURCE_DIR}/display/disp_driver.c"
"${CMAKE_CURRENT_SOURCE_DIR}/display/lvgl_task.c"
PARENT_SCOPE)
endif()
2.3 目录操作
add_subdirectory_if_exist
添加子目录到构建系统,如果目录存在。
cmake
add_subdirectory_if_exist(src)
add_subdirectory_if_exist(boards)
install_sdk
安装SDK文件,用于将头文件等资源复制到输出目录。
cmake
install_sdk("${CMAKE_CURRENT_SOURCE_DIR}/xiaohong" "*")
build_component
构建组件,在组件的CMakeLists.txt末尾调用。
cmake
build_component()
三、添加新组件到构建系统
3.1 添加示例组件
假设要添加一个新的示例组件 my_sample,步骤如下:
步骤1:创建目录结构
samples/
└── my_sample/
├── CMakeLists.txt
├── inc/
│ └── my_sample.h
└── src/
└── my_sample.c
步骤2:编写组件CMakeLists.txt
samples/my_sample/CMakeLists.txt
cmake
#===============================================================================
# @brief cmake file
# Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
#===============================================================================
add_subdirectory_if_exist(src)
set(SOURCES "${SOURCES}" PARENT_SCOPE)
set(PUBLIC_HEADER "${PUBLIC_HEADER}" "${CMAKE_CURRENT_SOURCE_DIR}/inc" PARENT_SCOPE)
samples/my_sample/src/CMakeLists.txt
cmake
#===============================================================================
# @brief cmake file
# Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
#===============================================================================
set(SOURCES "${SOURCES}"
"${CMAKE_CURRENT_SOURCE_DIR}/my_sample.c"
PARENT_SCOPE)
步骤3:在父级CMakeLists.txt中添加条件编译
samples/CMakeLists.txt
cmake
if(DEFINED CONFIG_ENABLE_MY_SAMPLE)
add_subdirectory_if_exist(my_sample)
endif()
set(SOURCES "${SOURCES}" PARENT_SCOPE)
set(PUBLIC_HEADER "${PUBLIC_HEADER}" PARENT_SCOPE)
# ... 其他配置
install_sdk("${CMAKE_CURRENT_SOURCE_DIR}/my_sample" "*")
build_component()
步骤4:在config.py中添加配置选项
在 build/config/target_config/ws63/config.py 中添加:
python
def ws63_liteos_app(target):
# ... 其他配置
target.add_option('CONFIG_ENABLE_MY_SAMPLE', bool, False, 'Enable my sample')
# 在ram_component中添加组件名
if target.get_option('CONFIG_ENABLE_MY_SAMPLE').value:
target.ram_component.append('my_sample')
3.2 使用app_run宏注册应用入口
在应用的源文件中使用 app_run() 宏来注册应用入口函数。
c
#include "app_init.h"
static void my_app_entry(void)
{
// 应用初始化代码
printf("My application started\r\n");
}
app_run(my_app_entry);
四、移植第三方库
4.1 移植步骤
以移植cJSON库为例:
步骤1:放置第三方库
将第三方组件放置于 opensource 目录下:
opensource/
└── cjson/
├── CMakeLists.txt
├── cJSON.h
└── cJSON.c
步骤2:修改opensource/CMakeLists.txt
在 opensource/CMakeLists.txt 中添加:
cmake
add_subdirectory_if_exist(cjson)
步骤3:编写第三方库的CMakeLists.txt
opensource/cjson/CMakeLists.txt
cmake
#===============================================================================
# @brief cmake file
# Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
#===============================================================================
set(COMPONENT_NAME "cjson")
set(SOURCES "${SOURCES}"
"${CMAKE_CURRENT_SOURCE_DIR}/cJSON.c"
PARENT_SCOPE)
set(PRIVATE_HEADER
"${CMAKE_CURRENT_SOURCE_DIR}"
)
set(COMPONENT_CCFLAGS
-Wno-error=unused-parameter
-Wno-unused-parameter
)
步骤4:在config.py中启用组件
在 build/config/target_config/ws63/config.py 中添加:
python
def ws63_liteos_app(target):
# ... 其他配置
target.add_option('CONFIG_ENABLE_CJSON', bool, False, 'Enable cjson library')
if target.get_option('CONFIG_ENABLE_CJSON').value:
target.ram_component.append('cjson')
4.2 引用第三方库头文件
在其他组件中引用第三方库的头文件时,有两种方式:
方式1:使用PUBLIC_HEADER
在第三方库的CMakeLists.txt中设置PUBLIC_HEADER:
cmake
set(PUBLIC_HEADER "${PUBLIC_HEADER}"
"${CMAKE_CURRENT_SOURCE_DIR}/cjson/cJSON.h"
PARENT_SCOPE)
方式2:直接在源文件中引用
c
#include "cjson/cJSON.h"
五、编译标志和警告处理
5.1 常用编译标志
禁用特定警告
cmake
set(COMPONENT_CCFLAGS
-Wno-error=sign-compare # 不将有符号/无符号比较的警告视为错误
-Wno-sign-compare # 完全禁用有符号/无符号比较的警告
-Wno-error=unused-parameter # 不将未使用参数的警告视为错误
-Wno-unused-parameter # 完全禁用未使用参数的警告
-Wno-error=unused-variable # 不将未使用变量的警告视为错误
-Wno-unused-but-set-variable # 禁用设置但未使用的变量的警告
-Wno-error=jump-misses-init # 不将跳转初始化的警告视为错误
-Wno-jump-misses-init # 完全禁用跳转初始化的警告
)
添加宏定义
cmake
set(PUBLIC_DEFINES
"CONFIG_LVGL_ENABLE=1"
"LCD_ST7789S_240x240"
)
5.2 警告处理策略
在嵌入式开发中,某些警告是常见且无害的,可以通过编译标志来处理:
- 未使用的参数:为了保持接口一致性,某些参数可能暂时未使用
- 有符号/无符号比较:嵌入式代码中常见,通常是有意为之
- 设置但未使用的变量:调试代码或预留接口导致
使用 -Wno-error= 可以让警告仍然显示但不导致编译失败,使用 -Wno- 可以完全禁用警告。
六、常见问题和解决方案
6.1 头文件找不到
问题:
fatal error: board_config.h: No such file or directory
原因:
- 头文件路径未添加到PUBLIC_HEADER或PRIVATE_HEADER
- 相对路径引用错误
解决方案:
方案1:在CMakeLists.txt中添加头文件路径
cmake
set(PUBLIC_HEADER "${PUBLIC_HEADER}"
"${CMAKE_CURRENT_SOURCE_DIR}/src"
"${CMAKE_CURRENT_SOURCE_DIR}/src/boards"
"${CMAKE_CURRENT_SOURCE_DIR}/src/boards/xiaohong_ws63_v1"
PARENT_SCOPE)
方案2:使用正确的相对路径
c
// 在 boards/xiaohong_ws63_v1/key_config.h 中
#include "../board_config.h" // 正确
// #include "board_config.h" // 错误,只在当前目录查找
6.2 未定义的宏
问题:
error: 'CONFIG_XIAOHONG_LVGL_ENABLE' was not declared in this scope
原因:
- 条件编译中使用了未定义的变量
- 配置选项未在config.py中定义
解决方案:
方案1:在config.py中定义配置选项
python
target.add_option('CONFIG_XIAOHONG_LVGL_ENABLE', bool, True, 'Enable LVGL support')
方案2:使用DEFINED检查
cmake
if(DEFINED CONFIG_XIAOHONG_LVGL_ENABLE)
set(SOURCES "${SOURCES}" ...)
endif()
6.3 编译警告导致失败
问题:
error: signed and unsigned type in conditional expression [-Werror=sign-compare]
error: unused parameter 'state' [-Werror=unused-parameter]
原因:
- 编译器将警告视为错误(-Werror)
- 代码中存在有符号/无符号比较或未使用参数
解决方案 :
在CMakeLists.txt中添加编译标志:
cmake
set(COMPONENT_CCFLAGS
-Wno-error=sign-compare
-Wno-sign-compare
-Wno-error=unused-parameter
-Wno-unused-parameter
)
6.4 静态函数未使用
问题:
error: 'MainTaskEntry' defined but not used [-Werror=unused-function]
原因:
- 函数定义为static但未被调用
- 使用了SYS_RUN宏但宏未定义
解决方案:
方案1:移除static关键字
c
uint32_t MainTaskEntry(void) // 移除static
{
// ...
}
方案2:添加app_run宏调用
c
#include "app_init.h"
static void xiaohong_app_entry(void)
{
MainTaskEntry();
}
app_run(xiaohong_app_entry); // 使用app_run注册
七、实际案例:xiaohong项目CMake配置
7.1 项目结构
samples/xiaohong/
├── CMakeLists.txt # xiaohong组件主构建脚本
└── xiaohong/
├── CMakeLists.txt # 子应用构建脚本
├── config.gni # GN配置(已废弃)
├── BUILD.gn # GN构建脚本(已废弃)
└── src/
├── CMakeLists.txt # 源文件构建脚本
├── main.c
├── task_entry.c
├── settings.c
├── boards/
│ ├── board_config.h
│ └── xiaohong_ws63_v1/
│ ├── board_config.c
│ ├── key_config.c
│ └── key_config.h
├── display/
├── network/
├── audio/
└── mongoose/
7.2 CMakeLists.txt配置详解
samples/xiaohong/CMakeLists.txt
cmake
#===============================================================================
# @brief cmake file
# Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
#===============================================================================
if(DEFINED CONFIG_SAMPLE_SUPPORT_XIAOHONG)
add_subdirectory_if_exist(xiaohong)
endif()
set(SOURCES "${SOURCES}" PARENT_SCOPE)
samples/xiaohong/xiaohong/CMakeLists.txt
cmake
#===============================================================================
# @brief cmake file
# Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
#===============================================================================
add_subdirectory_if_exist(src)
set(SOURCES "${SOURCES}" PARENT_SCOPE)
set(PUBLIC_HEADER "${PUBLIC_HEADER}"
"${CMAKE_CURRENT_SOURCE_DIR}/src"
"${CMAKE_CURRENT_SOURCE_DIR}/src/boards"
"${CMAKE_CURRENT_SOURCE_DIR}/src/boards/xiaohong_ws63_v1"
"${CMAKE_CURRENT_SOURCE_DIR}/src/display"
"${CMAKE_CURRENT_SOURCE_DIR}/src/display/lcd_st7789"
"${CMAKE_CURRENT_SOURCE_DIR}/src/display/lvgl"
"${CMAKE_CURRENT_SOURCE_DIR}/src/network"
"${CMAKE_CURRENT_SOURCE_DIR}/src/audio"
"${CMAKE_CURRENT_SOURCE_DIR}/src/audio/ci1302"
"${CMAKE_CURRENT_SOURCE_DIR}/src/mongoose"
PARENT_SCOPE)
samples/xiaohong/xiaohong/src/CMakeLists.txt
cmake
#===============================================================================
# @brief cmake file
# Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
#===============================================================================
set(PUBLIC_HEADER "${PUBLIC_HEADER}"
"${CMAKE_CURRENT_SOURCE_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}/boards"
"${CMAKE_CURRENT_SOURCE_DIR}/boards/xiaohong_ws63_v1"
"${CMAKE_CURRENT_SOURCE_DIR}/display"
"${CMAKE_CURRENT_SOURCE_DIR}/display/lcd_st7789"
"${CMAKE_CURRENT_SOURCE_DIR}/display/lvgl"
"${CMAKE_CURRENT_SOURCE_DIR}/network"
"${CMAKE_CURRENT_SOURCE_DIR}/audio"
"${CMAKE_CURRENT_SOURCE_DIR}/audio/ci1302"
"${CMAKE_CURRENT_SOURCE_DIR}/mongoose"
PARENT_SCOPE)
set(SOURCES "${SOURCES}"
"${CMAKE_CURRENT_SOURCE_DIR}/main.c"
"${CMAKE_CURRENT_SOURCE_DIR}/task_entry.c"
"${CMAKE_CURRENT_SOURCE_DIR}/settings.c"
PARENT_SCOPE)
if(DEFINED CONFIG_XIAOHONG_BOARD_NAME AND "${CONFIG_XIAOHONG_BOARD_NAME}" STREQUAL "xiaohong_ws63_v1")
set(SOURCES "${SOURCES}"
"${CMAKE_CURRENT_SOURCE_DIR}/boards/xiaohong_ws63_v1/board_config.c"
"${CMAKE_CURRENT_SOURCE_DIR}/boards/xiaohong_ws63_v1/key_config.c"
PARENT_SCOPE)
endif()
if(DEFINED CONFIG_XIAOHONG_LVGL_ENABLE)
set(SOURCES "${SOURCES}"
"${CMAKE_CURRENT_SOURCE_DIR}/display/disp_driver.c"
"${CMAKE_CURRENT_SOURCE_DIR}/display/lvgl_task.c"
"${CMAKE_CURRENT_SOURCE_DIR}/display/lvgl_ui_layout.c"
PARENT_SCOPE)
endif()
if(DEFINED CONFIG_XIAOHONG_LCD_PANEL AND "${CONFIG_XIAOHONG_LCD_PANEL}" STREQUAL "lcd_ST7789S_240x240")
set(SOURCES "${SOURCES}"
"${CMAKE_CURRENT_SOURCE_DIR}/display/lcd_st7789/lcd_st7789_driver.c"
PARENT_SCOPE)
endif()
set(SOURCES "${SOURCES}"
"${CMAKE_CURRENT_SOURCE_DIR}/network/net_http_server.c"
"${CMAKE_CURRENT_SOURCE_DIR}/network/net_wifi_hotspot.c"
"${CMAKE_CURRENT_SOURCE_DIR}/network/net_wifi_recfg_task.c"
"${CMAKE_CURRENT_SOURCE_DIR}/network/net_wifi_station.c"
"${CMAKE_CURRENT_SOURCE_DIR}/network/net_wifi_task.c"
PARENT_SCOPE)
set(SOURCES "${SOURCES}"
"${CMAKE_CURRENT_SOURCE_DIR}/audio/ci1302/ci1302_task.c"
PARENT_SCOPE)
set(SOURCES "${SOURCES}"
"${CMAKE_CURRENT_SOURCE_DIR}/mongoose/mongoose.c"
"${CMAKE_CURRENT_SOURCE_DIR}/mongoose/test/http_test.c"
"${CMAKE_CURRENT_SOURCE_DIR}/mongoose/test/mg_http_client.c"
"${CMAKE_CURRENT_SOURCE_DIR}/mongoose/test/mg_websocket_client.c"
PARENT_SCOPE)
set(COMPONENT_CCFLAGS
-Wno-error=sign-compare
-Wno-sign-compare
-Wno-error=unused-parameter
-Wno-unused-parameter
)
7.3 关键配置说明
-
多级CMakeLists.txt结构:
- 顶层:控制是否编译xiaohong组件
- 中层:设置头文件路径和添加子目录
- 底层:管理源文件和条件编译
-
头文件路径管理:
- 在三层CMakeLists.txt中都设置了PUBLIC_HEADER
- 确保所有源文件都能找到需要的头文件
-
条件编译:
- 根据配置变量选择不同的板级配置
- 根据配置变量选择是否编译LVGL支持
- 根据配置变量选择LCD驱动
-
编译标志:
- 处理有符号/无符号比较警告
- 处理未使用参数警告
八、最佳实践
8.1 目录组织
-
清晰的目录结构:
- 按功能模块划分目录(boards、display、network等)
- 头文件和源文件分离(inc/和src/)
-
命名规范:
- CMakeLists.txt统一命名
- 组件名称使用小写下划线分隔(my_sample)
8.2 CMakeLists.txt编写规范
- 添加版权信息:
cmake
#===============================================================================
# @brief cmake file
# Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
#===============================================================================
-
使用PARENT_SCOPE:
- 所有需要传递给父作用域的变量都要使用PARENT_SCOPE
- 这是WS63 CMake框架的重要特性
-
条件编译检查:
- 使用DEFINED检查变量是否定义
- 使用STREQUAL进行字符串比较
8.3 调试技巧
-
查看编译命令:
- 在build.py执行时添加--verbose参数查看详细编译命令
- 检查头文件路径是否正确
-
查看中间文件:
- 检查build目录下的Makefile
- 检查编译器实际使用的参数
-
逐步添加组件:
- 先添加简单组件验证CMake配置正确
- 再逐步添加复杂组件
九、踩坑记录

该报错原因是,没有搞明白PUBLIC和PRIVATE_HREADER的区别。这部分官方文档惜墨如金,没有详述。因为某些头文件需要被外部模块引用,如果都定义为PRIVATE_HREADER,则外部组件则可能报找不到头文件的错误。PRIVATE_HEADER则指定私有头文件路径,仅当前组件内部使用。

这里容易踩坑,原始的cmake写法竟然不生效。

这个地方也很容易出错,比如开启了little_fs这个模块,如果这里漏掉了,则会发现明明menuconfig开启了啊为何编译时不生效呢。
比如以增加cjson模块为例,假如已经成功将一个名为"cjson"(COMPONENT_NAME)的组件新增到框架中了,最后还应开启对该组件的编译。通过修改"build/config/target_config/ws63/config.py"中对应的target的"ram_component",将需要编译的组件加入到编译流程中。
例如:想要编译的target名称为"ws63-liteos-app",则找到"ws63-liteos-app"字典下的ram_component,在该数组中新增值"cjson",当启动"ws63-liteos-app"的编译时(使用IDE启动构建,或使用python build.py编译),CMake就会尝试编译"cjson"(SOURCES)。
参考文档:参考文档
十、总结
WS63平台的CMake构建系统提供了灵活、强大的组件化编译框架。通过合理使用CMakeLists.txt,开发者可以:
- 快速添加新组件:遵循标准目录结构和CMakeLists模板
- 移植第三方库:参考现有第三方库的配置方式
- 灵活控制编译:通过条件编译和配置选项实现功能裁剪
- 处理编译警告:使用COMPONENT_CCFLAGS控制编译行为
掌握CMakeLists.txt的编写方法,是WS63平台开发的基础技能,建议开发者多参考现有示例,逐步熟悉构建系统的使用。