问题背景
• 芯片型号:星鸿派-海思 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卡选购要点与避坑指南揭秘
别让连接器毁了你的设计:三大常用连接器选型陷阱与避坑指南
京东商品评论 API 开发指南(避坑 + 实战)
进线电抗器选型避坑指南|企业必看干货
冬季灌封胶不干?环氧聚氨酯低温固化五大避坑指南 |铬锐特实业
【课程升级】鸿蒙星闪WS63开发板新增《LVGL应用开发指南》课程,带屏开发让你的毕设项目更出彩!
MES系统避坑指南
小红书笔记详情 API 实战指南:从开发对接、场景落地到收益挖掘(附避坑技巧)
【项目实战】基于WS63的鸿蒙星闪红外遥控车(循迹、超声波避障、远程控制、星闪/红外遥控)有教程代码
【星鸿派】OpenHarmony WS63 UART 开发避坑指南
评论