一、鸿蒙 6.0 单片机开发概述
1.1 什么是 OpenHarmony 轻量系统
OpenHarmony 6.0 LTS(Long Term Support)是华为面向全场景智能终端设备推出的开源操作系统。作为国产操作系统的标杆之作,鸿蒙系统在嵌入式领域展现出独特的技术魅力。与传统 RTOS 相比,鸿蒙轻量系统不仅具备实时响应能力,更原生支持分布式架构,为物联网设备赋予了互联互通的基因。
轻量系统(LiteOS-M 内核)专为 MCU 类处理器设计,支持 ARM Cortex-M、RISC-V 等架构。系统最小内存需求仅 128KB,却能提供完整的设备管理能力。对于单片机开发者而言,这意味着可以在资源受限的芯片上运行功能完备的操作系统,告别裸机开发的种种局限。
轻量系统的典型应用场景:
1.2 鸿蒙 6.0 开发架构解析
如上图所示,鸿蒙单片机开发采用经典的五层架构设计:
应用层 承载具体业务逻辑,开发者使用 C 语言编写控制程序。鸿蒙提供了标准化的设备能力接口,屏蔽了底层硬件差异,使得同一套代码可以在不同开发板上运行。
框架层 封装了分布式能力框架、设备管理框架等核心组件。开发者通过统一的 API 调用硬件功能,无需关心 GPIO 寄存器的具体地址配置。
系统服务层 提供安全服务、通信服务、数据管理等基础能力。这些服务经过华为多年技术积累,稳定性得到充分验证。
硬件抽象层(HAL) 是开发的核心区域。GPIO、I2C、SPI、UART、ADC 等外设驱动都在这一层实现。鸿蒙 6.0 引入的 HDF(HarmonyOS Driver Foundation)驱动框架,将驱动开发从"手写寄存器"带入"配置驱动"时代。
硬件层 包括 Hi3861、STM32F4 等主控芯片,以及各类传感器、执行器模块。
1.3 主流开发板选型指南
入门首选推荐 BearPi-HM Nano——板载高度集成的 Hi3861 WiFi SoC 芯片,配套完善的 E53 扩展接口生态,支持直接连接 OLED 屏幕、温湿度传感器、LED 等外设。最关键的是,这块开发板的资料最为丰富,社区活跃度高,遇到问题容易找到解决方案。
二、开发环境搭建
2.1 工具链概述
鸿蒙单片机开发需要配置两套环境:
Linux 编译服务器(或虚拟机):承担代码编译任务
Windows 工作台:负责代码编辑、烧录、调试
2.2 Linux 环境配置详解
# 1. 安装Python及依赖sudo apt-get updatesudo apt-get install python3.7 python3-setuptools python3-pip -ysudo pip3 install setuptools kconfiglib pycryptodome ecdsa six
# 2. 安装编译工具链sudo apt-get install scons build-essential git -y
# 3. 下载gcc_riscv32交叉编译器cd ~ && wget https://download.qcloud.com/wireless/openharmony/compiler/gcc_riscv32-linux-7.3.0.tar.xztar -xvf gcc_riscv32-linux-7.3.0.tar.xzexport PATH=~/gcc_riscv32/bin:$PATH
# 4. 验证工具链riscv32-unknown-elf-gcc -v# 应显示 gcc version 7.3.0
复制代码
实战心得:国内开发者经常遇到 gcc_riscv32 下载失败的问题。可以使用百度网盘镜像(搜索"鸿蒙 gcc_riscv32 网盘"),或从华为官方镜像站点获取。部分企业用户反映,使用代理或 VPN 下载速度会显著提升。
2.3 DevEco Device Tool 配置
DevEco Device Tool 是华为官方提供的 VS Code 插件,将编译、烧录、调试集成到统一界面:
从华为开发者联盟官网下载.deb 安装包
VS Code 中依次点击"扩展" → "从 VSIX 安装"
重启 VS Code,左侧工具栏出现鸿蒙图标即表示安装成功
常见问题排查:
如果 VS Code 无法识别设备,检查 USB 线是否为数据线(部分充电线不支持数据传输)
烧录时提示"设备未找到",需要安装 CH340 驱动
编译报错"command not found",检查 PATH 环境变量是否配置正确
三、项目创建与代码结构
3.1 源码下载与工程初始化
# 使用repo工具同步源码(需先配置git)mkdir ~/harmony && cd ~/harmonyrepo init -u https://gitee.com/openharmony/manifest.git -b master --no-repo-verifyrepo sync -crepo forall -c 'git lfs pull'
# 进入源码目录cd ~/harmony
复制代码
3.2 目录结构详解
device/ # 芯片与板级适配├── board/ # 厂商开发板代码│ └── bearpi/ # 小熊派适配│ └── hm_nano/├── soc/ # SoC芯片适配│ └── hisilicon/│ └── hi3861/vendor/ # 产品配置applications/ # 应用程序 └── sample/ # 示例代码 └── wifi_iot/ ├── app/BUILD.gn # 应用构建配置 └── src/ # 源代码目录
复制代码
关键文件说明:
BUILD.gn:GN 构建脚本,定义模块依赖和编译规则
config.json:产品配置文件,定义外设列表
hi3861_adapter.h:硬件适配层头文件
3.3 新建第一个工程
在applications/sample/wifi_iot/app目录下创建新项目:
mkdir my_first_projectcd my_first_project
复制代码
创建hello_world.c:
#include <stdio.h>#include "ohos_init.h"#include "kernel/os/os.h"
static void HelloTask(void *arg){ (void)arg; printf("Hello, HarmonyOS!\r\n"); int count = 0; while (1) { printf("Count: %d\r\n", count++); osDelay(100); // 延时100个Tick }}
APP_FEATURE_INIT(HelloTask); // 模块初始化宏
复制代码
创建对应的BUILD.gn:
import("//build/lite/gn_rust.gni")import("//build/lite/ohos.gni")
ohos_executable("my_hello") { sources = [ "hello_world.c" ] include_dirs = [ "//utils/native/lite/include", "//kernel/liteos_m/components/libs/libc/include", ] deps = [ "//kernel/liteos_m:liteos_m", "//third_party/FreeBSD/libc:libc", ]}
复制代码
四、核心代码实战
4.1 GPIO 控制 LED 闪烁
LED 控制是单片机开发的"Hello World"。通过 GPIO 输出高低电平,实现 LED 的亮灭控制。
#include <stdio.h>#include <unistd.h>#include "ohos_gpio.h"#include "ohos_errno.h"#include "hi_io.h"#include "hi_gpio.h"
/* 定义LED连接的GPIO引脚 - BearPi-HM Nano默认GPIO09 */#define LED_GPIO_IDX 9
/** * GPIO初始化配置 */static void GpioLedInit(void){ // 1. 设置GPIO为输出模式 IoTGpioInit(LED_GPIO_IDX); IoTGpioSetDir(LED_GPIO_IDX, IOT_GPIO_DIR_OUT); // 2. 初始化默认关闭LED(高电平关闭,低电平点亮) IoTGpioSetOutputVal(LED_GPIO_IDX, IOT_GPIO_VALUE1);}
/** * LED控制任务 * 每500ms切换一次LED状态 */static void LedBlinkTask(void *arg){ (void)arg; GpioLedInit(); printf("[LED] Blink task started on GPIO%d\r\n", LED_GPIO_IDX); while (1) { // 点亮LED IoTGpioSetOutputVal(LED_GPIO_IDX, IOT_GPIO_VALUE0); printf("[LED] ON\r\n"); usleep(500000); // 延时500ms // 关闭LED IoTGpioSetOutputVal(LED_GPIO_IDX, IOT_GPIO_VALUE1); printf("[LED] OFF\r\n"); usleep(500000); }}
// 模块入口注册APP_FEATURE_INIT(LedBlinkTask);
复制代码
实战心得:
BearPi-HM Nano 的 LED 是共阳极设计,因此 GPIO 输出低电平时点亮
使用usleep()延时精度优于osDelay(),适合毫秒级精确控制
调试时可在串口输出状态信息,便于观察程序运行流程
4.2 ADC 按键检测
ADC(模数转换器)可以仅用少量 GPIO 实现多个按键检测。通过分压电阻网络,不同按键按下时产生不同的电压值。
#include <stdio.h>#include "ohos_adc.h"#include "hi_gpio.h"#include "hi_adc.h"
/* ADC通道定义 - 按键连接在GPIO11/ADC通道 */#define ADC_CHANNEL 1#define ADC_VREF_MV 1800 // 参考电压1.8V
/* 按键阈值定义(根据实际电路调整) */#define KEY_THRESHOLD_1 400 // 按键1按下电压约0.4V#define KEY_THRESHOLD_2 1000 // 按键2按下电压约1.0V#define KEY_THRESHOLD_3 1600 // 按键3按下电压约1.6V
/** * 读取ADC原始值 */static unsigned int ReadAdcValue(unsigned int channel){ unsigned int value = 0; // 配置ADC:采样10次取平均 if (HiAdcRead(channel, &value, HI_ADC_EQU_MODEL_4, HI_ADC_CUR_BAUDRATE) != 0) { printf("[ADC] Read error!\r\n"); return 0; } return value;}
/** * 电压值转换为mV */static unsigned int GetVoltageMv(unsigned int adcValue){ // ADC分辨率为12位,满量程1800mV return (adcValue * ADC_VREF_MV) / 4095;}
/** * 按键检测任务 */static void AdcKeyTask(void *arg){ (void)arg; unsigned int lastKey = 0; printf("[ADC] Key detection task started\r\n"); while (1) { unsigned int adcValue = ReadAdcValue(ADC_CHANNEL); unsigned int voltage = GetVoltageMv(adcValue); unsigned int keyPressed = 0; if (voltage < KEY_THRESHOLD_1) { keyPressed = 1; } else if (voltage < KEY_THRESHOLD_2) { keyPressed = 2; } else if (voltage < KEY_THRESHOLD_3) { keyPressed = 3; } // 消抖处理:状态变化时才触发 if (keyPressed != lastKey) { if (keyPressed > 0) { printf("[ADC] Key%d pressed, voltage=%dmV, raw=%d\r\n", keyPressed, voltage, adcValue); } lastKey = keyPressed; } usleep(50000); // 50ms采样周期 }}
APP_FEATURE_INIT(AdcKeyTask);
复制代码
实战心得:
ADC 按键的关键是精确测量分压电阻值,计算各按键的特征电压
建议在电路设计时,各按键电压间隔大于 200mV,便于软件区分
消抖处理必不可少,否则一次按键可能被识别为多次
4.3 I2C 读取温湿度传感器
I2C 是单片机开发中最常用的传感器接口协议。以 SHT30 温湿度传感器为例:
#include <stdio.h>#include "ohos_i2c.h"#include "hi_i2c.h"
/* I2C配置 */#define I2C_IDX 0#define SHT30_ADDR 0x44 // SHT30传感器7位地址#define I2C_BAUDRATE 400000 // 400KHz高速模式
/* SHT30命令 */#define CMD_MEASURE 0x2C06 // 高精度测量命令
/** * I2C初始化 */static void Sht30I2cInit(void){ IoTI2cInit(I2C_IDX, I2C_BAUDRATE); printf("[I2C] Initialized I2C%d at %dHz\r\n", I2C_IDX, I2C_BAUDRATE);}
/** * 向SHT30写入命令 */static int Sht30WriteCommand(unsigned short cmd){ unsigned char data[2] = { (cmd >> 8) & 0xFF, cmd & 0xFF }; return IoTI2cWrite(I2C_IDX, SHT30_ADDR << 1, data, 2);}
/** * 从SHT30读取数据 */static int Sht30ReadData(unsigned char *buffer, int len){ return IoTI2cRead(I2C_IDX, SHT30_ADDR << 1, buffer, len);}
/** * 解析温度值 */static float ParseTemperature(unsigned char msb, unsigned char lsb){ // 温度计算公式:T = -45 + 175 * (raw / 65535) unsigned int raw = ((unsigned int)msb << 8) | lsb; return -45.0f + 175.0f * ((float)raw / 65535.0f);}
/** * 解析湿度值 */static float ParseHumidity(unsigned char msb, unsigned char lsb){ // 湿度计算公式:RH = 100 * (raw / 65535) unsigned int raw = ((unsigned int)msb << 8) | lsb; return 100.0f * ((float)raw / 65535.0f);}
/** * 温湿度读取任务 */static void Sht30Task(void *arg){ (void)arg; unsigned char recvData[6]; Sht30I2cInit(); printf("[SHT30] Temperature/Humidity sensor task started\r\n"); while (1) { // 发送测量命令 if (Sht30WriteCommand(CMD_MEASURE) == 0) { usleep(20000); // 等待测量完成 // 读取6字节数据 if (Sht30ReadData(recvData, 6) == 0) { float temp = ParseTemperature(recvData[0], recvData[1]); float humi = ParseHumidity(recvData[3], recvData[4]); printf("[SHT30] Temperature: %.2f°C, Humidity: %.2f%%\r\n", temp, humi); } else { printf("[SHT30] Read failed\r\n"); } } else { printf("[SHT30] Write command failed\r\n"); } sleep(2); // 每2秒读取一次 }}
APP_FEATURE_INIT(Sht30Task);
复制代码
4.4 UART 串口通信
UART 用于开发板与 PC 或其他设备的数据交互:
#include <stdio.h>#include "ohos_uart.h"#include "hi_uart.h"
/* UART配置 - 使用UART0,波特率115200 */#define UART_IDX 0#define UART_BAUDRATE 115200
/** * UART初始化配置 */static void UartInit(void){ // 配置UART参数 UartAttribute attr = { .baudRate = UART_BAUDRATE, .dataBits = 8, .stopBits = 1, .parity = 0, // 无校验 }; IoTUartInit(UART_IDX); IoTUartSetAttribute(UART_IDX, &attr); printf("[UART] Initialized UART%d at %d baud\r\n", UART_IDX, UART_BAUDRATE);}
/** * UART发送字符串 */static void UartSendString(const char *str){ unsigned int len = 0; while (str[len] != '\0') len++; IoTUartWrite(UART_IDX, (unsigned char *)str, len);}
/** * UART接收任务 */static void UartTask(void *arg){ (void)arg; unsigned char recvBuffer[256]; UartInit(); // 发送欢迎信息 UartSendString("\r\n=== HarmonyOS UART Demo ===\r\n"); UartSendString("Please send commands:\r\n"); while (1) { int len = IoTUartRead(UART_IDX, recvBuffer, sizeof(recvBuffer)); if (len > 0) { // 收到数据,原样回显 recvBuffer[len] = '\0'; printf("[UART] Received: %s\r\n", recvBuffer); // 回传确认 char response[64]; snprintf(response, sizeof(response), "[UART] Echo: %s\r\n", recvBuffer); UartSendString(response); } usleep(10000); // 10ms轮询间隔 }}
APP_FEATURE_INIT(UartTask);
复制代码
五、烧录与调试
5.1 编译固件
# 设置工具链环境变量export PATH=~/gcc_riscv32/bin:$PATHexport PATH=~/ninja:$PATH
# 进入源码目录cd ~/harmony
# 选择开发板hb set# 交互式选择:Select board → bearpi_hm_nano
# 编译hb build -f
复制代码
编译成功后,固件位于out/bearpi_hm_nano/目录:
5.2 烧录流程
硬件连接:使用 USB Type-C 线连接开发板与 PC
进入烧录模式:按住复位键,松开后立即点击 HiBurn 的"开始烧录"按钮
选择固件:在 HiBurn 中选择编译生成的.bin文件
烧录进度:观察进度条,等待烧录完成
验证运行:打开串口工具(115200-8-N-1),查看系统启动日志
常见烧录问题:
5.3 串口调试技巧
使用 MobaXterm 创建串口会话,连接到开发板:
# 查看串口输出[OHOS] boot succeed...[APP] Hello HarmonyOS![LED] Blink task started on GPIO9
复制代码
调试技巧:
六、进阶学习路径
6.1 能力提升路线
结语
鸿蒙 6.0 单片机开发为嵌入式工程师打开了新的大门。借助完善的驱动框架和分布式能力,开发者可以专注于业务逻辑实现,而不必深陷寄存器配置的泥潭。从 LED 闪烁到传感器采集,从 WiFi 连接到云端通信,每一步都是成长的印记。
愿每一位开发者都能在鸿蒙生态中找到属于自己的舞台,为国产操作系统生态贡献力量。
评论