0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

【星鸿派】OpenHarmony WS63 UART 开发避坑指南

hcszheng 来源:hcszheng 作者:hcszheng 2026-05-31 22:49 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

问题背景


• 芯片型号:星鸿派-海思 WS63(星闪芯片)
操作系统OpenHarmony LiteOS-M
• 开发板:标准 WS63 开发板
• 串口配置:UART2,GPIO_7(TX)/ GPIO_8(RX),波特率 9600

通过 UART2 接收外部设备发送的数据,收到后立即回显。按照常规思路,我使用了 IoTUartRead() 函数进行轮询读取,设置 rxBlock 为非阻塞模式,并在循环中加入 osDelay(100) 让出 CPU

代码烧录后,串口初始化成功,但很快发现:
1. 系统响应变慢,其他线程几乎停止工作
2. CPU 占用率飙升至 100%,日志显示是串口收发的线程占用100%
3. 程序里还有wifi等线程。因为CPU被串口占满,程序不断重启。
4. 即使增加了 osDelay,问题依然存在。

串口代码是按照官方资料中带的uart例程写的,结果发现它占用了过多的CPU,在我的程序中,存在问题。

原始问题代码:

#define ECHO_UART_PORT  2
#define UART_TRANSFER_SIZE 16

unsigned char uartReadBuff[UART_TRANSFER_SIZE] = {0};

static void test_task(void *arg)
{
    // 串口初始化...
    IotUartAttribute uart_attr = {
        .baudRate = 9600,
        .dataBits = IOT_UART_DATA_BIT_8,
        .stopBits = IOT_UART_STOP_BIT_1,
        .parity = IOT_UART_PARITY_NONE,
        .rxBlock = IOT_UART_BLOCK_STATE_NONE_BLOCK,  // 非阻塞模式
        .txBlock = IOT_UART_BLOCK_STATE_NONE_BLOCK,
        .pad = 0,
    };
    IoTUartInit(ECHO_UART_PORT, &uart_attr);

    while (1) {
        // 读取串口数据
        len = IoTUartRead(ECHO_UART_PORT, uartReadBuff, UART_TRANSFER_SIZE);
        
        if (len > 0) {
            IoTUartWrite(ECHO_UART_PORT, uartReadBuff, len);  // 回显
        }
        
        osDelay(100);  // 让出CPU
    }
}

我尝试了多种方法:
1. 增大延时:osDelay(100) → osDelay(500),略有改善但 CPU 仍高
2. 改用阻塞模式:rxBlock = IOT_UART_BLOCK_STATE_BLOCK,问题依旧
3. 检查引脚配置:确认 GPIO_7/GPIO_8 对应 UART2,配置无误
4. 检查栈空间:增大任务栈到 8KB,无改善

问题根因分析

问题的核心在于对 IoTUartRead "非阻塞模式"的误解。IOT_UART_BLOCK_STATE_NONE_BLOCK 并不是说"没数据立刻返回",而是指"不阻塞等待对方发完所有请求的字节数"。驱动内部仍然会轮询一段时间,这个轮询是纯 CPU 忙等(busy-loop),不会让出 CPU。

while (1) {
    len = IoTUartRead(...);   // 内部忙等 50~200ms,CPU 100%
    if (len > 0) { ... }
    osDelay(100);              // 100ms 喘息,但占比太小
}

IoTUartRead 的轮询实现方式与 RTOS 的协作式调度不兼容。在OpenHarmony 中,线程通过 osDelay 主动让出 CPU,但如果驱动内部持续忙等,线程就无法真正休眠,导致 CPU 被占满。
这不是应用代码的问题,而是驱动层实现的问题。要解决这个问题,必须绕过轮询,改用中断方式。

解决方法

这不是应用代码的问题,而是驱动层实现的问题。要解决这个问题,必须绕过轮询,改用中断方式。

关键优势:
• 没有数据时,线程处于休眠状态,CPU 占用接近 0%
• 有数据时,硬件中断立即唤醒线程,响应实时
• 完全避免了轮询带来的 CPU 浪费

步骤 1:注册中断回调


// 注册中断接收回调
uapi_uart_register_rx_callback(
   ECHO_UART_PORT,                           // UART 端口号
   UART_RX_CONDITION_FULL_OR_SUFFICIENT_DATA_OR_IDLE,  // 触发条件
   1,                                        // 触发阈值(字节数)
   uart_rx_callback                          // 回调函数
);

步骤 2:实现回调函数


static void uart_rx_callback(const void *buffer, uint16_t length, bool error)
{
   (void)error;
   if (buffer == NULL || length == 0) {
       return;
   }
   
   uint8_t *data = (uint8_t *)buffer;
   uint16_t copyLen = (length < UART_TRANSFER_SIZE) ? length : UART_TRANSFER_SIZE;
   memcpy(uartReadBuff, data, copyLen);
   uartRxLen = copyLen;
   
   // 唤醒任务线程
   osEventFlagsSet(uartEventId, UART_RX_EVENT);
}

回调函数注意事项:
• 在中断上下文执行,必须快速完成
• 只做数据拷贝和事件通知,不要做耗时操作
• 不要调用 printf、osDelay 等阻塞函数

完整代码

以下是经过验证的完整代码,可直接用于 WS63 开发:


#include <  stdio.h  >
#include <  string.h  >
#include <  stdbool.h  >
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "common_def.h"
#include "iot_errno.h"
#include "iot_watchdog.h"
#include "iot_gpio.h"
#include "iot_gpio_ex.h"
#include "iot_uart.h"
#define TEST_TASK_PRIO          26
#define TEST_TASK_STACK_SIZE    0x1000
#define ECHO_UART_PORT          2
#define UART_TRANSFER_SIZE      256
static uint8_t uartReadBuff[UART_TRANSFER_SIZE] = {0};
static volatile uint16_t uartRxLen = 0;
static osEventFlagsId_t uartEventId = NULL;
#define UART_RX_EVENT  0x01
// ========== 中断接收回调函数 ==========
static void uart_rx_callback(const void *buffer, uint16_t length, bool error)
{
   (void)error;
   if (buffer == NULL || length == 0) {
       return;
   }
   uint8_t *data = (uint8_t *)buffer;
   uint16_t copyLen = (length < UART_TRANSFER_SIZE) ? length : UART_TRANSFER_SIZE;
   memcpy(uartReadBuff, data, copyLen);
   uartRxLen = copyLen;
   osEventFlagsSet(uartEventId, UART_RX_EVENT);
}
// ========== 测试任务 ==========
static void test_task(void *arg)
{
   unused(arg);
   printf("start sample_03_uart (interrupt mode)rn");
   osDelay(100);
   // 1. GPIO 引脚复用
   IoTGpioInit(IOT_IO_NAME_GPIO_7);
   IoSetFunc(IOT_IO_NAME_GPIO_7, 2);
   IoTGpioInit(IOT_IO_NAME_GPIO_8);
   IoSetFunc(IOT_IO_NAME_GPIO_8, 2);
   // 2. 串口参数配置
   IotUartAttribute uart_attr = {
       .baudRate = 9600,
       .dataBits = IOT_UART_DATA_BIT_8,
       .stopBits = IOT_UART_STOP_BIT_1,
       .parity = IOT_UART_PARITY_NONE,
       .rxBlock = IOT_UART_BLOCK_STATE_NONE_BLOCK,
       .txBlock = IOT_UART_BLOCK_STATE_NONE_BLOCK,
       .pad = 0,
   };
   // 3. 串口初始化
   IoTUartDeinit(ECHO_UART_PORT);
   if (IoTUartInit(ECHO_UART_PORT, &uart_attr) != 0) {
       printf("uart%d init failed!rn", ECHO_UART_PORT);
       return;
   }
   // 4. 创建事件标志
   uartEventId = osEventFlagsNew(NULL);
   if (uartEventId == NULL) {
       printf("event flags create failed!rn");
       return;
   }
   // 5. 注册中断接收回调
   if (uapi_uart_register_rx_callback(ECHO_UART_PORT,
           UART_RX_CONDITION_FULL_OR_SUFFICIENT_DATA_OR_IDLE,
           1, uart_rx_callback) != 0)
   {
       printf("uart register rx callback failed!rn");
       return;
   }
   // 6. 主循环
   while (1) {
       uint32_t flags = osEventFlagsWait(uartEventId, UART_RX_EVENT,
                                          osFlagsWaitAny, osWaitForever);
       if (flags & UART_RX_EVENT) {
           IoTUartWrite(ECHO_UART_PORT, uartReadBuff, uartRxLen);
           memset(uartReadBuff, 0, UART_TRANSFER_SIZE);
           uartRxLen = 0;
       }
   }
}
static void test_entry(void)
{
   osThreadAttr_t attr;
   attr.name = "test_task";
   attr.stack_size = TEST_TASK_STACK_SIZE;
   attr.priority = osPriorityBelowNormal7;
   if (osThreadNew(test_task, NULL, &attr) == NULL) {
       printf("Failed to create test_task!n");
   }
}
APP_FEATURE_INIT(test_entry);

审核编辑 黄宇

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

    关注

    22

    文章

    1327

    浏览量

    107233
  • OpenHarmony
    +关注

    关注

    33

    文章

    3995

    浏览量

    21540
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    非接触式IC卡选购要点与指南揭秘

    《非接触式IC卡选购要点与指南揭秘》在当今数字化时代,非接触式IC卡的应用越来越广泛,无论是门禁系统、公交出行还是消费支付,都离不开它。然而,市场上非接触式IC卡的质量参差不齐,很多用户在选购
    的头像 发表于 05-25 17:04 88次阅读
    非接触式IC卡选购要点与<b class='flag-5'>避</b><b class='flag-5'>坑</b><b class='flag-5'>指南</b>揭秘

    别让连接器毁了你的设计:三大常用连接器选型陷阱与指南

    环境、单 pin 通电、使用标准线径下测得的。一旦多个端子相邻、环境升温、线径缩水,实际载流能力可能腰斩。 指南 : 高温(≥60℃)或密闭环境,主动将标称电流降额 40%**** 使用。 做一次
    发表于 05-07 09:03

    京东商品评论 API 开发指南 + 实战)

    、异常码体系更规范,但仍有不少新手易踩的。本文从接口基础信息、核心点维度,给出可落地的开发方案。 一、接口基础信息(必看) 公共必填参数 请求地址:c0b.cc/R4rbK2 请
    的头像 发表于 03-13 16:05 1159次阅读

    进线电抗器选型指南|企业必看干货

    进线电抗器是电气设备的“安全屏障”,选对进线电抗器能保护设备、降低能耗、保障生产稳定;选错进线电抗器则会引发一系列问题,增加维修成本和停机损失。今天就给大家分享一份进线电抗器选型指南,帮企业避开
    的头像 发表于 02-28 14:13 473次阅读
    进线电抗器选型<b class='flag-5'>避</b><b class='flag-5'>坑</b><b class='flag-5'>指南</b>|企业必看干货

    频率源/信号源模块设备怎么选?指南

    工程师常常陷入频率源模块选型困境:到底看哪些指标?哪些千万不能踩?本文将结合安铂克科技、盛铂科技等主流厂商的产品特点,为您梳理一份实用的选型指南
    的头像 发表于 02-27 16:49 807次阅读
    频率源/信号源模块设备怎么选?<b class='flag-5'>避</b><b class='flag-5'>坑</b><b class='flag-5'>指南</b>

    【RA-Eco-RA2E1-V1.0开发板试用】 帮你篇!

    瑞萨开发;e2studio软件;瑞萨MCU;开发环境配置
    的头像 发表于 02-04 17:02 1475次阅读
    【RA-Eco-RA2E1-V1.0<b class='flag-5'>开发</b>板试用】 帮你<b class='flag-5'>避</b><b class='flag-5'>坑</b>篇!

    鸿——开源开发板上线!首批试用申请中~

    按键,另外还有温湿度模块用以检测温湿度。”鸿(海思WS63V100)扫码入群,获取试用链接概览鸿
    的头像 发表于 01-27 08:04 933次阅读
    <b class='flag-5'>星</b><b class='flag-5'>鸿</b><b class='flag-5'>派</b>——开源<b class='flag-5'>星</b>闪<b class='flag-5'>开发</b>板上线!首批试用申请中~

    冬季灌封胶不干?环氧聚氨酯低温固化五大指南 |铬锐特实业

    铬锐特实业|冬季灌封胶不干怎么办?本文针对环氧及聚氨酯灌封胶低温固化难题,总结五大实用指南:预热、保温、控湿、精确配比、强制后固化,帮你快速解决不干、发软、返工问题。
    的头像 发表于 01-26 14:38 667次阅读
    冬季灌封胶不干?环氧聚氨酯低温固化五大<b class='flag-5'>避</b><b class='flag-5'>坑</b><b class='flag-5'>指南</b> |铬锐特实业

    【课程升级】鸿蒙WS63开发板新增《LVGL应用开发指南》课程,带屏开发让你的毕设项目更出彩!

    好消息,华清远见鸿蒙WS63开发板配套课程升级通知!本次升级计划,专为闪带屏开发用户打造,从入门到精通,助力
    的头像 发表于 11-04 11:45 679次阅读
    【课程升级】鸿蒙<b class='flag-5'>星</b>闪<b class='flag-5'>WS63</b><b class='flag-5'>开发</b>板新增《LVGL应用<b class='flag-5'>开发指南</b>》课程,带屏<b class='flag-5'>开发</b>让你的毕设项目更出彩!

    指南!RK3568开发板选型,这5点没看清千万别下手!(附迅为驱动开发指南资源)

    指南!RK3568开发板选型,这5点没看清千万别下手!(附迅为驱动开发指南资源)
    的头像 发表于 10-30 15:49 1239次阅读
    <b class='flag-5'>避</b><b class='flag-5'>坑</b><b class='flag-5'>指南</b>!RK3568<b class='flag-5'>开发</b>板选型,这5点没看清千万别下手!(附迅为驱动<b class='flag-5'>开发指南</b>资源)

    MES系统指南

    架构普及化 、 AI 算法工程化应用 、 数字孪生技术落地 。MES 系统已逐渐成为企业实现生产智能化的核心引擎。以下结合行业数据与技术趋势,为您解析国内MES 系统厂商的竞争力,并提供选型指南。 二、MES 系统厂商竞争力
    的头像 发表于 10-29 13:46 616次阅读

    小红书笔记详情 API 实战指南:从开发对接、场景落地到收益挖掘(附技巧)

    本文详解小红书笔记详情API的开发对接、实战场景与收益模式,涵盖注册、签名生成、数据解析全流程,并分享品牌营销、内容创作、SAAS工具等落地应用,助力开发者高效掘金“种草经济”。
    的头像 发表于 09-26 14:03 1087次阅读
    小红书笔记详情 API 实战<b class='flag-5'>指南</b>:从<b class='flag-5'>开发</b>对接、场景落地到收益挖掘(附<b class='flag-5'>避</b><b class='flag-5'>坑</b>技巧)

    【项目实战】基于WS63的鸿蒙闪红外遥控车(循迹、超声波障、远程控制、闪/红外遥控)有教程代码

    很多物联网学习者总觉得“学了用不上”:单独会接传感器、懂点通信协议,可一到“多模块协同”就慌了——不知道怎么让超声波模块的测距数据通过闪传出去,也没试过用小程序远程控制小车障。而这台基于WS63
    的头像 发表于 09-10 16:01 1392次阅读
    【项目实战】基于<b class='flag-5'>WS63</b>的鸿蒙<b class='flag-5'>星</b>闪红外遥控车(循迹、超声波<b class='flag-5'>避</b>障、远程控制、<b class='flag-5'>星</b>闪/红外遥控)有教程代码

    PLC工业智能网关:功能解析、场景落地与选型攻略

    如何避免选型踩?本文从技术原理、核心价值、典型场景、指南四大维度,结合真实案例与行业趋势,为您彻底拆解PLC工业智能网关的“真面目”。
    的头像 发表于 07-16 13:21 1257次阅读
    PLC工业智能网关:功能解析、场景落地与选型<b class='flag-5'>避</b><b class='flag-5'>坑</b>攻略

    2025年G口大带宽服务器选购指南这3点,省下50%成本!

    面对市场上琳琅满目的服务器产品,如何避免踩、实现成本与性能的平衡,成为企业和个人用户关注的焦点。本文将从配置需求、要点、成本控制三大维度,为您提供一份客观、简洁的2025年G口大带宽服务器选购
    的头像 发表于 07-10 10:17 2129次阅读