[i=s] 本帖最后由 xiaozhou 于 2025-11-21 16:20 编辑 [/i]
【BW20-12F 】开发板 WiFi LED 状态指示项目开发教程
开发板:BW20-12F
开发环境:Ubuntu 22.04 (VMware虚拟机)
开发时间:2025年11月20日
一、项目简介
这个项目实现了一个基于 FreeRTOS 的 WiFi 连接状态指示系统。开发板上的 RGB LED
会根据 WiFi 连接状态显示不同的颜色:
- 红灯快闪:未连接 WiFi
- 黄灯慢闪:正在连接中
- 绿灯常亮:WiFi 已连接并获取到 IP
- 红灯常亮:连接断开
- 紫灯闪烁:连接错误
这个项目很适合用来学习嵌入式开发的基础知识,包括 GPIO 控制、WiFi 连接、
FreeRTOS 任务管理等内容。
二、开发环境准备
2.1 硬件准备
- BW20-12F 开发板 x1
- 数据线
- Ubuntu 虚拟机 (VMware Workstation)

2.2 软件环境
我使用的是 Ubuntu 22.04 系统,Ameba RTOS SDK 已经下载在桌面。
首先要解决一个常见问题:Python 包安装超时。因为默认使用国外的 PyPI 源,
下载速度很慢,所以我们先配置清华大学的镜像源:

命令:
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
配置完成后,进入 SDK 目录并初始化环境:

命令:
cd ~/Desktop/ameba/ameba-rtos
source ameba.sh
第一次运行会自动安装一些 Python 依赖包,耐心等待安装完成。
2.3 解决串口权限问题
在虚拟机中使用 USB 串口时,经常会遇到设备无法识别的问题。这是因为一个叫
brltty 的盲文设备服务会占用串口。我们需要停用这个服务:

命令:
sudo systemctl stop brltty-udev.service
sudo systemctl mask brltty-udev.service
sudo systemctl disable brltty.service
然后在 VMware 菜单中连接 USB 设备:
VM -> Removable Devices -> QinHeng Electronics CH340 -> Connect

命令:
ls -la /dev/ttyUSB*
正常情况下会看到 /dev/ttyUSB0 设备。
三、项目目录结构
我在 SDK 的 my_project 目录下创建了项目文件夹:
命令:
mkdir -p ~/Desktop/ameba/ameba-rtos/my_project/wifi_led_component
cd ~/Desktop/ameba/ameba-rtos/my_project/wifi_led_component
项目的目录结构如下:
wifi_led_component/
├── CMakeLists.txt # CMake 构建配置文件
├── include/ # 头文件目录(可选)
├── wifi_led.h # LED 和 WiFi 控制头文件
├── wifi_led.c # LED 和 WiFi 控制实现
└── wifi_led_example.c # 示例程序主文件
四、代码实现
4.1 CMakeLists.txt 配置文件
这个文件告诉编译系统如何构建我们的项目。它分为两部分:
- 公共部分(public):定义给其他模块使用的头文件和宏
- 私有部分(private):定义本模块内部使用的源文件
需要注意的是,源文件路径必须正确。我一开始把文件放在项目根目录,但 CMakeLists.txt
中写的是 src/wifi_led.c,导致编译时找不到文件。后来我把 CMakeLists.txt 改成了:
ameba_list_append(private_sources
wifi_led.c # 注意:不是 src/wifi_led.c
wifi_led_example.c
)
这样编译器才能找到文件。

4.2 GPIO 和头文件问题
一开始我参考网上的代码,使用了很多不存在的头文件,比如 wifi_conf.h、
osdep_service.h 等,导致编译失败。
后来我查找 SDK 源码,发现正确的头文件应该是:
- wifi_api.h:WiFi API 函数声明
- wifi_api_types.h:WiFi 相关类型定义
- wifi_api_event.h:WiFi 事件定义(虽然后来发现事件回调 API 不可用)

命令:
find ~/Desktop/ameba/ameba-rtos/component/wifi/api -name "*.h"
开发板上:
- PB17 是绿色 LED
- PB18 是红色 LED
- PB19 是蓝色 LED

4.3 WiFi 连接的关键点
在实现 WiFi 连接功能时,我遇到了几个坑:
问题1:结构体初始化错误导致崩溃
一开始我用 strncpy 复制密码到结构体,但实际上 rtw_network_info 结构体中的
password 字段是一个指针,不是数组!正确的做法是直接赋值指针:
connect_param.password = (unsigned char *)password;
问题2:忘记调用 DHCP
WiFi 连接成功后,还需要调用 LwIP_DHCP() 来获取 IP 地址,否则无法正常通信。
这是我查看官方示例代码后才发现的。

命令:
cat ~/Desktop/ameba/ameba-rtos/component/example/wifi/wifi_user_reconnect/example_wifi_user_reconnect.c | grep -A 30 "wifi_connect"
最终的正确实现是:
// 1. 启用 WiFi
wifi_on(RTW_MODE_STA);
// 2. 设置连接参数
struct rtw_network_info connect_param = {0};
memcpy(connect_param.ssid.val, ssid, strlen(ssid));
connect_param.ssid.len = strlen(ssid);
connect_param.password = (unsigned char *)password;
connect_param.password_len = strlen(password);
// 3. 连接 WiFi
wifi_connect(&connect_param, 1);
// 4. 获取 IP 地址
LwIP_DHCP(0, DHCP_START);
4.4 FreeRTOS 任务自动启动
为了让程序启动时自动运行,我使用了 GCC 的 attribute((constructor)) 属性:
void attribute((constructor)) auto_start(void)
{
rtos_task_t task = NULL;
rtos_task_create(&task, "WIFI_TASK", (rtos_task_t)wifi_task,
NULL, 4096, 2);
}
这个函数会在 main() 之前自动执行,创建我们的 WiFi 连接任务。
五、编译和烧录
5.1 编译项目

命令:
cd ~/Desktop/ameba/ameba-rtos/amebadplus_gcc_project
build.py -a ~/Desktop/ameba/ameba-rtos/my_project/wifi_led_component/
编译过程可能会遇到各种警告和错误,比如:
- 未使用的参数警告:在函数中添加 (void)param; 来消除
- 类型不匹配:注意 s32 类型要用 %ld 格式化输出
- 头文件找不到:检查 #include 的文件名是否正确

如果看到 "Image manipulating end" 说明编译成功了!
5.2 烧录固件

命令:
flash.py -p /dev/ttyUSB0
烧录过程大约需要几秒钟,看到 "Flash completed" 表示烧录成功。
5.3 查看串口输出
使用串口监视工具查看程序运行情况:

按下开发板的复位按钮,你会看到系统启动信息,然后是我们程序的输出:
=======================================
WiFi LED 示例程序
SSID: Mi 11
[WIFI_LED] LED初始化完成
[WIFI_LED] 组件初始化完成
[WIFI_LED] 状态: 未连接
[WIFI_LED] 连接WiFi: Mi 11
[WIFI_LED] 状态: 连接中
[WIFI_LED] 开始连接...
[WIFI_LED] WiFi连接成功,开始DHCP...
[WIFI_LED] DHCP成功!
[WIFI_LED] 状态: 已连接
[EXAMPLE] ✓ IP: 192.168.35.132

同时 LED 会从红灯快闪(未连接)变成黄灯慢闪(连接中),最后变成蓝灯常亮(可是应该是绿灯呀难道是引脚不对?可能是我引脚定义没看对)

六、遇到的问题和解决方案
6.1 编译错误:找不到源文件
错误信息:
Cannot find source file: src/wifi_led.c
解决方案:
检查 CMakeLists.txt 中的源文件路径,确保与实际文件位置一致。如果文件在
项目根目录,就不要加 src/ 前缀。
6.2 编译错误:头文件不存在
错误信息:
fatal error: wifi_conf.h: No such file or directory
解决方案:
不要使用网上搜到的代码,要查看 SDK 实际提供了哪些头文件。使用 find 命令
查找正确的头文件名。
6.3 运行时崩溃:内存访问违规
错误信息:
MemManage Fault: Memory management fault is caused by data access violation
解决方案:
检查指针是否正确初始化。在我的案例中,是因为把 password 指针当成数组来
使用,导致访问了空地址。查看官方示例代码,发现应该直接赋值指针。
6.4 WiFi 连接失败
如果 WiFi 连接失败,检查以下几点:
- SSID 和密码是否正确
- WiFi 路由器是否开启了 2.4GHz 频段(BW20-12F 只支持 2.4GHz)
- 路由器是否设置了 MAC 地址过滤
- 是否调用了 wifi_on() 启用 WiFi
6.5 串口无法识别
在虚拟机中,USB 串口经常被 brltty 服务占用,导致无法使用。按照前面的步骤
停用 brltty 服务即可。
七、代码文件完整内容
7.1 wifi_led.h
#ifndef WIFI_LED_H
#define WIFI_LED_H
#ifdef __cplusplus
extern "C" {
#endif
#include "ameba_soc.h"
#include "os_wrapper.h"
#include "wifi_api.h" // WiFi API函数
#include "wifi_api_types.h" // WiFi类型定义
#include "wifi_api_event.h" // WiFi事件
#include <stdio.h>
#include <string.h>
// LED GPIO引脚定义 - 修正颜色
#define LED_GREEN_GPIO _PB_17 // 绿色
#define LED_RED_GPIO _PB_18 // 红色
#define LED_BLUE_GPIO _PB_19 // 蓝色
// WiFi状态枚举
typedef enum {
WIFI_LED_STATE_IDLE = 0,
WIFI_LED_STATE_CONNECTING,
WIFI_LED_STATE_CONNECTED,
WIFI_LED_STATE_DISCONNECTED,
WIFI_LED_STATE_ERROR
} wifi_led_state_t;
extern wifi_led_state_t current_led_state;
void wifi_led_init(void);
void wifi_led_set_state(wifi_led_state_t state);
void wifi_led_deinit(void);
int wifi_led_connect(const char *ssid, const char *password);
// WiFi配置记得改成你自己的!!!
#define WIFI_SSID "Mi 11"
#define WIFI_PASSWORD "12345678."
#ifdef __cplusplus
}
#endif
#endif /* WIFI_LED_H */
7.2 wifi_led.c
#include "wifi_led.h"
#include "lwip_netconf.h"
#define TAG "WIFI_LED"
wifi_led_state_t current_led_state = WIFI_LED_STATE_IDLE;
static rtos_task_t led_task_handle = NULL;
static uint8_t led_task_running = 0;
static void led_gpio_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = LED_GREEN_GPIO;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_Init(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = LED_RED_GPIO;
GPIO_Init(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = LED_BLUE_GPIO;
GPIO_Init(&GPIO_InitStruct);
GPIO_WriteBit(LED_GREEN_GPIO, 0);
GPIO_WriteBit(LED_RED_GPIO, 0);
GPIO_WriteBit(LED_BLUE_GPIO, 0);
printf("\r\n[%s] LED初始化完成\r\n", TAG);
}
static void set_led_color(uint8_t red, uint8_t green, uint8_t blue)
{
GPIO_WriteBit(LED_RED_GPIO, red);
GPIO_WriteBit(LED_GREEN_GPIO, green);
GPIO_WriteBit(LED_BLUE_GPIO, blue);
}
static void led_control_task(void *param)
{
uint32_t tick = 0;
(void)param;
while (led_task_running) {
tick++;
switch (current_led_state) {
case WIFI_LED_STATE_IDLE:
set_led_color((tick % 2), 0, 0);
rtos_time_delay_ms(200);
break;
case WIFI_LED_STATE_CONNECTING:
if (tick % 2) {
set_led_color(1, 1, 0);
} else {
set_led_color(0, 0, 0);
}
rtos_time_delay_ms(500);
break;
case WIFI_LED_STATE_CONNECTED:
set_led_color(0, 1, 0);
rtos_time_delay_ms(500);
break;
case WIFI_LED_STATE_DISCONNECTED:
set_led_color(1, 0, 0);
rtos_time_delay_ms(500);
break;
case WIFI_LED_STATE_ERROR:
set_led_color((tick % 2), 0, (tick % 2));
rtos_time_delay_ms(300);
break;
default:
rtos_time_delay_ms(100);
break;
}
}
set_led_color(0, 0, 0);
rtos_task_delete(NULL);
}
void wifi_led_init(void)
{
led_gpio_init();
led_task_running = 1;
if (rtos_task_create(&led_task_handle, "led_task",
(rtos_task_t)led_control_task,
NULL, 1024, 1) != 0) {
printf("\r\n[%s] LED任务创建失败!\r\n", TAG);
led_task_running = 0;
return;
}
printf("\r\n[%s] 组件初始化完成\r\n", TAG);
}
void wifi_led_set_state(wifi_led_state_t state)
{
const char *states[] = {"未连接", "连接中", "已连接", "已断开", "错误"};
current_led_state = state;
printf("[%s] 状态: %s\r\n", TAG, states[state]);
}
// 完全基于官方示例的WiFi连接
int wifi_led_connect(const char *ssid, const char *password)
{
int ret;
struct rtw_network_info connect_param = {0};
printf("\r\n[%s] 连接WiFi: %s\r\n", TAG, ssid);
wifi_led_set_state(WIFI_LED_STATE_CONNECTING);
// 启用WiFi
ret = wifi_on(RTW_MODE_STA);
if (ret < 0) {
printf("[%s] WiFi启动失败: %d\r\n", TAG, ret);
wifi_led_set_state(WIFI_LED_STATE_ERROR);
return -1;
}
rtos_time_delay_ms(1000);
// 按照官方示例设置参数
memcpy(connect_param.ssid.val, ssid, strlen(ssid));
connect_param.ssid.len = strlen(ssid);
connect_param.password = (unsigned char *)password;
connect_param.password_len = strlen(password);
printf("[%s] 开始连接...\r\n", TAG);
// 连接WiFi (阻塞模式)
ret = wifi_connect(&connect_param, 1);
if (ret != RTK_SUCCESS) {
printf("[%s] WiFi连接失败: %d\r\n", TAG, ret);
wifi_led_set_state(WIFI_LED_STATE_ERROR);
return -1;
}
printf("[%s] WiFi连接成功,开始DHCP...\r\n", TAG);
// DHCP获取IP地址
ret = LwIP_DHCP(0, DHCP_START);
if (ret == DHCP_ADDRESS_ASSIGNED) {
printf("[%s] DHCP成功!\r\n", TAG);
wifi_led_set_state(WIFI_LED_STATE_CONNECTED);
return 0;
} else {
printf("[%s] DHCP失败: %d\r\n", TAG, ret);
wifi_disconnect();
wifi_led_set_state(WIFI_LED_STATE_ERROR);
return -1;
}
}
void wifi_led_deinit(void)
{
led_task_running = 0;
rtos_time_delay_ms(200);
set_led_color(0, 0, 0);
printf("[%s] 已反初始化\r\n", TAG);
}
7.3 wifi_led_example.c
#include "wifi_led.h"
#include "lwip_netconf.h"
#define TAG "EXAMPLE"
// WiFi连接任务
static void wifi_task(void *param)
{
(void)param;
rtos_time_delay_ms(3000);
printf("\r\n");
printf("=======================================\r\n");
printf(" WiFi LED 示例程序\r\n");
printf("=======================================\r\n");
printf("SSID: %s\r\n", WIFI_SSID);
printf("=======================================\r\n\r\n");
// 初始化LED
wifi_led_init();
wifi_led_set_state(WIFI_LED_STATE_IDLE);
rtos_time_delay_ms(2000);
// 连接WiFi
if (wifi_led_connect(WIFI_SSID, WIFI_PASSWORD) == 0) {
// 等待获取IP
printf("[%s] 等待获取IP...\r\n", TAG);
int retry = 30;
while (retry-- > 0) {
uint8_t *ip = LwIP_GetIP(0); // 修正:传入索引而不是指针
if (ip && ip[0] != 0) {
printf("\r\n[%s] ✓ IP: %d.%d.%d.%d\r\n\r\n", TAG,
ip[0], ip[1], ip[2], ip[3]);
break;
}
rtos_time_delay_ms(1000);
}
if (retry <= 0) {
printf("[%s] × 获取IP超时\r\n", TAG);
}
}
// 保持运行
while (1) {
rtos_time_delay_ms(5000);
}
}
// 自动启动
void attribute((constructor)) auto_start(void)
{
rtos_task_t task = NULL;
rtos_task_create(&task, "WIFI_TASK", (rtos_task_t)wifi_task, NULL, 4096, 2);
}
int wifi_led_example(void)
{
return 0;
}
7.4 CMakeLists.txt
##########################################################################################
/* This part defines public part of the component
/* Public part will be used as global build configures for all component
set(public_includes) #public include directories, NOTE: relative path is OK
set(public_definitions) #public definitions
set(public_libraries) #public libraries(files), NOTE: linked with whole-archive options
#----------------------------------------#
/.Component public part, user config begin
#添加公共包含目录
ameba_list_append(public_includes
include
)
添加公共宏定义(可选)
ameba_list_append(public_definitions
ENABLE_WIFI_LED_DEBUG=1
)
Component public part, user config end
#----------------------------------------#
#WARNING: Fixed section, DO NOT change!
ameba_global_include(${public_includes})
ameba_global_define(${public_definitions})
ameba_global_library(${public_libraries}) #default: whole-archived
##########################################################################################
##* This part defines private part of the component
##* Private part is used to build target of current component
#* NOTE: The build API guarantees the global build configures(mentioned above)
##* applied to the target automatically. So if any configure was already added
* to public above, it's unnecessary to add again below.
#NOTE: User defined section, add your private build configures here
#You may use if-else condition to set these predefined variable
#They are only for ameba_add_internal_library/ameba_add_external_app_library/ameba_add_external_soc_library
set(private_sources) #private source files, NOTE: relative path is OK
set(private_includes) #private include directories, NOTE: relative path is OK
set(private_definitions) #private definitions
set(private_compile_options) #private compile_options
#------------------------------#
#Component private part, user config begin
#添加源文件
ameba_list_append(private_sources
wifi_led.c
wifi_led_example.c
)
#添加私有包含目录
ameba_list_append(private_includes
include
)
#添加私有宏定义(可选)
#ameba_list_append(private_definitions
#DEBUG_WIFI_LED=1
)
#Component private part, user config end
#------------------------------#
#WARNING: Select right API based on your component's release/not-release/standalone
###NOTE: For open-source component, always build from source
ameba_add_internal_library(wifi_led_component
p_SOURCES
${private_sources}
p_INCLUDES
${private_includes}
p_DEFINITIONS
${private_definitions}
p_COMPILE_OPTIONS
${private_compile_options}
)
八、总结和心得
通过这个项目,我学到了很多嵌入式开发的知识:
-
阅读官方文档和示例代码很重要
网上的资料可能过时或不适用,最可靠的还是官方的示例代码。遇到问题时,
先去 SDK 的 example 目录找找有没有类似的实现。
-
编译错误要逐个解决,不要急
刚开始编译时各种报错,看起来很吓人。但仔细看错误信息,都会告诉你哪里
出了问题。一个个解决,最后一定能编译通过。
-
指针和结构体要特别小心
嵌入式开发中,一个小的指针错误就会导致程序崩溃。要养成初始化变量的
好习惯,使用 memset() 清零结构体。
-
查找 API 的技巧
使用 grep 和 find 命令可以快速找到函数定义和头文件位置:
查找函数定义
grep -r "wifi_connect" component/wifi --include="*.h"
查找结构体定义
grep -A 30 "struct rtw_network_info" component/wifi/api/*.h
查找示例代码
find component/example -name "*.c" -exec grep -l "wifi_connect" {} ;
-
虚拟机开发的注意事项
在虚拟机中开发要注意 USB 设备的连接和权限问题。遇到串口无法识别时,
首先检查是否在 VMware 中连接了设备,然后检查是否有 brltty 冲突。
这个项目从开始到完成花了我一个下午的时间,遇到了各种问题,但最终都解决了。
最有成就感的时刻就是看到 LED 随着 WiFi 状态变化而改变颜色!
希望这个教程能帮助到其他学习 Ameba 开发的朋友。如果有问题,欢迎交流讨论。
==============================================
九、参考资料
- Ameba D Plus 数据手册
- Ameba RTOS SDK 官方文档
- FreeRTOS 官方文档:https://www.freertos.org/
- LwIP 文档:https://www.nongnu.org/lwip/
==============================================
附录:完整的命令清单
环境准备
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
cd ~/Desktop/ameba/ameba-rtos
source ameba.sh
解决串口问题
sudo systemctl stop brltty-udev.service
sudo systemctl mask brltty-udev.service
sudo systemctl disable brltty.service
ls -la /dev/ttyUSB*
创建项目
mkdir -p ~/Desktop/ameba/ameba-rtos/my_project/wifi_led_component
cd ~/Desktop/ameba/ameba-rtos/my_project/wifi_led_component
编译和烧录
cd ~/Desktop/ameba/ameba-rtos/amebadplus_gcc_project
build.py -a ~/Desktop/ameba/ameba-rtos/my_project/wifi_led_component/
flash.py -p /dev/ttyUSB0
查找 API(调试用)
find ~/Desktop/ameba/ameba-rtos/component/wifi/api -name ".h"
grep -r "wifi_connect" ~/Desktop/ameba/ameba-rtos/component/wifi --include=".h"
grep -A 30 "struct rtw_network_info" ~/Desktop/ameba/ameba-rtos/component/wifi/api/wifi_api_types.h
END