鸿蒙OS IoT设备开发之基础 —— 外设驱动
历尽挫折,总算将鸿蒙OS设备开发环境 DevEco device tools 安装完成。因事耽搁了一段时间,如今重新上手,开始尝试 MCU 编程的最基础部分:硬件驱动。
之前买过一本《HarmonyOS IoT设备开发实战》:

本想对照书的内容逐步实施,但发现书的内容和我安装的环境(Harmony OS 3.0.3LTS)并非一一对应,而且书上的代码还要自己敲入,思考再三,决定尝试用 AI 辅助,实现硬件驱动程序的编写。书只是作为参考。
1、GPIO口
单片机的硬件编程,首先是驱动 IO 口,系统示例中已包含了一个 LED example 例程,GPIO控制比较简单,阅读程序后基本能理解,便直接编译后下载到板子上,功能正常:

通过上述过程实际感受了一下所安装的鸿蒙设备开发环境的使用细节,初步知道了集成环境(IDE)如何操作。
示例程序编写的有点复杂,但让我了解了鸿蒙 OS 中的线程概念,还算有价值,代码如下:
#include <stdio.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "iot_gpio.h"
#include "app_demo_sysinfo.h" // 20260722
#define LED_INTERVAL_TIME_US 2000000
#define LED_TASK_STACK_SIZE 512
#define LED_TASK_PRIO 25
#define LED_TEST_GPIO 9 // for hispark_pegasus
enum LedState {
LED_ON = 0,
LED_OFF,
LED_SPARK,
};
enum LedState g_ledState = LED_SPARK;
static void *LedTask(const char *arg)
{
(void)arg;
printf("Hello, OpenHarmony!\n");
while (1) {
switch (g_ledState) {
case LED_ON:
IoTGpioSetOutputVal(LED_TEST_GPIO, 1);
usleep(LED_INTERVAL_TIME_US);
break;
case LED_OFF:
IoTGpioSetOutputVal(LED_TEST_GPIO, 0);
usleep(LED_INTERVAL_TIME_US);
break;
case LED_SPARK:
IoTGpioSetOutputVal(LED_TEST_GPIO, 0);
usleep(LED_INTERVAL_TIME_US);
IoTGpioSetOutputVal(LED_TEST_GPIO, 1);
usleep(LED_INTERVAL_TIME_US);
break;
default:
usleep(LED_INTERVAL_TIME_US);
break;
}
}
return NULL;
}
static void LedExampleEntry(void)
{
osThreadAttr_t attr;
IoTGpioInit(LED_TEST_GPIO);
IoTGpioSetDir(LED_TEST_GPIO, IOT_GPIO_DIR_OUT);
attr.name = "LedTask";
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = LED_TASK_STACK_SIZE;
attr.priority = LED_TASK_PRIO;
if (osThreadNew((osThreadFunc_t)LedTask, NULL, &attr) == NULL) {
printf("[LedExample] Falied to create LedTask!\n");
}
//app_demo_heap_task(); // 20260722
}
SYS_RUN(LedExampleEntry);
2、UART
Uart 是单片机最常用的硬件之一,所以第二步就着手驱动 uart 接口。
uart0 被系统的日志输出所占用,故准备启用 uart1。
系统例程中没有关于 uart 的示例程序,便尝试用 AI 辅助,让豆包帮我编写一段 uart1 的demo 程序。
按照提示,豆包帮我完成了一段 uart1 的demo程序。因为想保留 LED 闪烁功能,故将 AI 编写的 demo 程序嵌入到原有的 LED example 中。
开始编译出错。对照头文件 iot _uart.h,发现豆包所写的函数格式有点小错误,修正后编译正确,下载后运行,日志输出提示栈溢出,估计是启用串口后,系统会开相应的收发缓冲区,增加了内存开销。
将原来 LED 任务的栈尺寸从 512 改为 1024后,运行正常。程序如下:
include <stdio.h>
#include "iot_errno.h"
#include "iot_uart.h"
#include "unistd.h"
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "iot_gpio.h"
#define LED_INTERVAL_TIME_US 1000000
#define LED_TASK_STACK_SIZE 1024
#define LED_TASK_PRIO 25
#define LED_TEST_GPIO 9 // for hispark_pegasus
// UART1 初始化
static void Uart1Test(void)
{
IotUartAttribute uart_cfg = {
.baudRate = 115200,
.dataBits = IOT_UART_DATA_BIT_8,
.stopBits = IOT_UART_STOP_BIT_1,
.parity = IOT_UART_PARITY_NONE
};
// 初始化UART1
unsigned int ret = IoTUartInit(1, &uart_cfg);
if(ret != 0 ) {
printf("Failed to initialize UART1: %d\n", ret);
}
else
{
char buf[] = "Hello UART1!\r\n";
printf("UART1 write result: %d\n", IoTUartWrite(1, (unsigned char *)buf, sizeof(buf)-1));
}
}
static void *LedTask(const char *arg)
{
(void)arg;
printf("Hello, OpenHarmony!\n");
while (1) {
IoTGpioSetOutputVal(LED_TEST_GPIO, 0);
usleep(LED_INTERVAL_TIME_US);
IoTGpioSetOutputVal(LED_TEST_GPIO, 1);
usleep(LED_INTERVAL_TIME_US);
IoTUartWrite(1, "s ", 2);
}
return NULL;
}
static void LedExampleEntry(void)
{
osThreadAttr_t attr;
Uart1Test();
IoTGpioInit(LED_TEST_GPIO);
IoTGpioSetDir(LED_TEST_GPIO, IOT_GPIO_DIR_OUT);
IoTGpioSetOutputVal(LED_TEST_GPIO, 1);
attr.name = "LedTask";
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = LED_TASK_STACK_SIZE;
attr.priority = LED_TASK_PRIO;
if (osThreadNew((osThreadFunc_t)LedTask, NULL, &attr) == NULL) {
printf("[LedExample] Falied to create LedTask!\n");
}
else
{
printf("[LedExample] LedTask created successfully!\n");
}
}
SYS_RUN(LedExampleEntry);
3、I2C
因为手头有一块 SSD1306 小屏,故选择 I2C 作为第三个驱动的外设。
还是让豆包帮我编写,豆包也确实尽责,编写了一段让我可以直接拷贝到 IDE 环境中编译运行的程序,包括:ssd1306_i2c.c、ssd1306_i2c.h 以及调用测试函数的主程序,还有 DevEco 中编译必须的 BUILD.gn。
我习惯将新的功能添加到已经实现的程序中,故仍以前面的程序为基础,略微修改了 BUILD.gn:
import("//build/lite/config/component/lite_component.gni")
static_library("uart1demo") {
sources = [
"uart1hello.c",
"ssd1306_i2c.c",
"ssd1306_i2c.h"
]
include_dirs = [
"//applications/sample/wifi_iot/app/iothardware",
"//utils/native/lite/include",
"//kernel/liteos_m/kal/cmsis",
"//base/iot_hardware/peripheral/interfaces/kits",
# 海思底层SDK路径
"//device/hisilicon/hispark_pegasus/sdk_liteos/include"
]
# Hi3861 Pegasus 正确UART依赖
# deps = [ "//vendor/hisi/hi3861/hi3861_peripheral:iot_uart"]
deps = []
}
可这段程序编译不太顺利,对 I2C 引脚的复用功能设置函数在 iot 的头文件中未找到定义,和豆包交流了并尝试了数次,总算用海思的底层完成了 I2C 驱动。
豆包还算不错,在 iot 中(BASE目录下)受阻后,先是换 Wifi_iot 的头文件,在我装的系统中也找不到,最后选择了海思提供的库才算搞定。
经历这些挫折,让我更进一步了解了所安装的鸿蒙系统,还是有收获的。海思的库在 Device 目录下,完整目录为:
/OpenHarmony303/OpenHarmony/device/hisilicon/hispark_pegasus/sdk_liteos/include
这里面的硬件相关头文件比较全:

豆包的程序基本正确,除了个别细节(如符号化常数定义)不对外,无大错,估计是系统版本更新所致。
程序运行结果如下:

不过仔细阅读代码,发现豆包所编写的代码基本和网上以及我买的那本书上一致,看来它是学习了这些内容。不过按我多年从事硬件编程的经验,这段程序还有优化的空间,主要是显示数据的刷新,豆包写的是一次 i2c 写一个字节数据,对于点阵显示屏而言,效率太低,因为写一字节数据,i2c 实际输出 2 个字节(一字节命令、一字节数据),按豆包的程序刷新整屏(128*64),分 8 页数据,共要写 1024 字节数据,如果一次一字节,实际 i2c 输出了2048 字节,在实际产品中,这将会大大降低程序效率,对于实时系统,甚至有可能降低可靠性,因为会影响程序的实时性。
正确的操作,应该是一个页面128字节一次写入,i2c 总共输出129字节。程序如下:
ssd1306_i2c.h(注意:头文件用了 hi_io.h、hi_i2c.h):
#ifndef SSD1306_I2C_H
#define SSD1306_I2C_H
#include <stdint.h>
#include "hi_io.h"
#include "hi_i2c.h"
#define SSD1306_WIDTH 128
#define SSD1306_HEIGHT 64
#define SSD1306_ADDR 0x78 // 屏幕不亮改为0x3D
#define I2C_IDX HI_I2C_IDX_0
#define I2C_BAUDRATE 100000
//8x8 ASCII字模,ASCII 32(' ') ~ 126('~')
static const uint8_t font8x8[95][8] =
{
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},// 32 空格
{0x00,0x00,0x5F,0x00,0x00,0x00,0x00,0x00},// 33 !
{0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x00},// 34 "
{0x14,0x7F,0x14,0x7F,0x14,0x00,0x00,0x00},// 35 #
{0x24,0x2A,0x7F,0x2A,0x12,0x00,0x00,0x00},// 36 $
{0x23,0x13,0x08,0x64,0x62,0x00,0x00,0x00},// 37 %
{0x36,0x49,0x55,0x22,0x50,0x00,0x00,0x00},// 38 &
{0x00,0x05,0x03,0x00,0x00,0x00,0x00,0x00},// 39 '
{0x00,0x1C,0x22,0x41,0x00,0x00,0x00,0x00},// 40 (
{0x00,0x41,0x22,0x1C,0x00,0x00,0x00,0x00},// 41 )
{0x14,0x08,0x3E,0x08,0x14,0x00,0x00,0x00},// 42 *
{0x08,0x08,0x3E,0x08,0x08,0x00,0x00,0x00},// 43 +
{0x00,0x80,0x70,0x30,0x00,0x00,0x00,0x00},// 44 ,
{0x08,0x08,0x08,0x08,0x08,0x00,0x00,0x00},// 45 -
{0x00,0x00,0x60,0x60,0x00,0x00,0x00,0x00},// 46 .
{0x20,0x10,0x08,0x04,0x02,0x00,0x00,0x00},// 47 /
{0x3E,0x51,0x49,0x45,0x3E,0x00,0x00,0x00},// 48 0
{0x00,0x42,0x7F,0x40,0x00,0x00,0x00,0x00},// 49 1
{0x42,0x61,0x51,0x49,0x46,0x00,0x00,0x00},// 50 2
{0x21,0x41,0x45,0x4B,0x31,0x00,0x00,0x00},// 51 3
{0x18,0x14,0x12,0x7F,0x10,0x00,0x00,0x00},// 52 4
{0x27,0x45,0x45,0x45,0x39,0x00,0x00,0x00},// 53 5
{0x3C,0x4A,0x49,0x49,0x30,0x00,0x00,0x00},// 54 6
{0x01,0x71,0x09,0x05,0x03,0x00,0x00,0x00},// 55 7
{0x36,0x49,0x49,0x49,0x36,0x00,0x00,0x00},// 56 8
{0x06,0x49,0x49,0x29,0x1E,0x00,0x00,0x00},// 57 9
{0x00,0x36,0x36,0x00,0x00,0x00,0x00,0x00},// 58 :
{0x00,0x56,0x36,0x00,0x00,0x00,0x00,0x00},// 59 ;
{0x08,0x14,0x22,0x41,0x00,0x00,0x00,0x00},// 60 <
{0x14,0x14,0x14,0x14,0x14,0x00,0x00,0x00},// 61 =
{0x00,0x41,0x22,0x14,0x08,0x00,0x00,0x00},// 62 >
{0x02,0x01,0x51,0x09,0x06,0x00,0x00,0x00},// 63 ?
{0x32,0x49,0x79,0x41,0x3E,0x00,0x00,0x00},// 64 @
{0x7E,0x11,0x11,0x11,0x7E,0x00,0x00,0x00},// 65 A
{0x7F,0x49,0x49,0x49,0x36,0x00,0x00,0x00},// 66 B
{0x3E,0x41,0x41,0x41,0x22,0x00,0x00,0x00},// 67 C
{0x7F,0x41,0x41,0x22,0x1C,0x00,0x00,0x00},// 68 D
{0x7F,0x49,0x49,0x49,0x41,0x00,0x00,0x00},// 69 E
{0x7F,0x09,0x09,0x09,0x01,0x00,0x00,0x00},// 70 F
{0x3E,0x41,0x49,0x49,0x7A,0x00,0x00,0x00},// 71 G
{0x7F,0x08,0x08,0x08,0x7F,0x00,0x00,0x00},// 72 H
{0x00,0x41,0x7F,0x41,0x00,0x00,0x00,0x00},// 73 I
{0x20,0x40,0x41,0x3F,0x01,0x00,0x00,0x00},// 74 J
{0x7F,0x08,0x14,0x22,0x41,0x00,0x00,0x00},// 75 K
{0x7F,0x40,0x40,0x40,0x40,0x00,0x00,0x00},// 76 L
{0x7F,0x02,0x0C,0x02,0x7F,0x00,0x00,0x00},// 77 M
{0x7F,0x04,0x08,0x10,0x7F,0x00,0x00,0x00},// 78 N
{0x3E,0x41,0x41,0x41,0x3E,0x00,0x00,0x00},// 79 O
{0x7F,0x09,0x09,0x09,0x06,0x00,0x00,0x00},// 80 P
{0x3E,0x41,0x51,0x21,0x5E,0x00,0x00,0x00},// 81 Q
{0x7F,0x09,0x19,0x29,0x46,0x00,0x00,0x00},// 82 R
{0x46,0x49,0x49,0x49,0x31,0x00,0x00,0x00},// 83 S
{0x01,0x01,0x7F,0x01,0x01,0x00,0x00,0x00},// 84 T
{0x3F,0x40,0x40,0x40,0x3F,0x00,0x00,0x00},// 85 U
{0x1F,0x20,0x40,0x20,0x1F,0x00,0x00,0x00},// 86 V
{0x3F,0x40,0x38,0x40,0x3F,0x00,0x00,0x00},// 87 W
{0x63,0x14,0x08,0x14,0x63,0x00,0x00,0x00},// 88 X
{0x07,0x08,0x70,0x08,0x07,0x00,0x00,0x00},// 89 Y
{0x61,0x51,0x49,0x45,0x43,0x00,0x00,0x00},// 90 Z
{0x00,0x7F,0x41,0x41,0x00,0x00,0x00,0x00},// 91 [
{0x02,0x04,0x08,0x10,0x20,0x00,0x00,0x00},// 92 '\'
{0x00,0x41,0x41,0x7F,0x00,0x00,0x00,0x00},// 93 ]
{0x04,0x02,0x01,0x02,0x04,0x00,0x00,0x00},// 94 ^
{0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x00},// 95 _
{0x00,0x01,0x02,0x04,0x00,0x00,0x00,0x00},// 96 `
{0x20,0x54,0x54,0x54,0x78,0x00,0x00,0x00},// 97 a
{0x7F,0x48,0x48,0x48,0x30,0x00,0x00,0x00},// 98 b
{0x38,0x44,0x44,0x44,0x28,0x00,0x00,0x00},// 99 c
{0x30,0x48,0x48,0x48,0x7F,0x00,0x00,0x00},//100 d
{0x38,0x54,0x54,0x54,0x18,0x00,0x00,0x00},//101 e
{0x08,0x7E,0x09,0x01,0x02,0x00,0x00,0x00},//102 f
{0x18,0xA4,0xA4,0xA4,0x7C,0x00,0x00,0x00},//103 g
{0x7F,0x08,0x08,0x08,0x70,0x00,0x00,0x00},//104 h
{0x00,0x00,0x7A,0x40,0x00,0x00,0x00,0x00},//105 i
{0x40,0x80,0x84,0x7A,0x00,0x00,0x00,0x00},//106 j
{0x7F,0x10,0x28,0x44,0x00,0x00,0x00,0x00},//107 k
{0x00,0x41,0x7F,0x40,0x00,0x00,0x00,0x00},//108 l
{0x78,0x04,0x18,0x04,0x78,0x00,0x00,0x00},//109 m
{0x78,0x08,0x08,0x08,0x70,0x00,0x00,0x00},//110 n
{0x38,0x44,0x44,0x44,0x38,0x00,0x00,0x00},//111 o
{0xFC,0x24,0x24,0x24,0x18,0x00,0x00,0x00},//112 p
{0x18,0x24,0x24,0x24,0xFC,0x00,0x00,0x00},//113 q
{0x78,0x08,0x08,0x08,0x08,0x00,0x00,0x00},//114 r
{0x48,0x54,0x54,0x54,0x24,0x00,0x00,0x00},//115 s
{0x04,0x3F,0x44,0x40,0x20,0x00,0x00,0x00},//116 t
{0x3C,0x40,0x40,0x20,0x7C,0x00,0x00,0x00},//117 u
{0x1C,0x20,0x40,0x20,0x1C,0x00,0x00,0x00},//118 v
{0x3C,0x40,0x30,0x40,0x3C,0x00,0x00,0x00},//119 w
{0x44,0x28,0x10,0x28,0x44,0x00,0x00,0x00},//120 x
{0x0C,0x50,0x50,0x50,0x3C,0x00,0x00,0x00},//121 y
{0x44,0x64,0x54,0x4C,0x44,0x00,0x00,0x00},//122 z
{0x00,0x08,0x36,0x41,0x00,0x00,0x00,0x00},//123 {
{0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00},//124 |
{0x00,0x41,0x36,0x08,0x00,0x00,0x00,0x00},//125 }
{0x08,0x08,0x2A,0x1C,0x08,0x00,0x00,0x00},//126 ~
};
// SSD1306 初始化命令表,128×64 I2C OLED
static const uint8_t ssd1306_init_cmds[] =
{
0xAE, // Display OFF
0xD5, 0x80, // Set clock divide
0xA8, 0x3F, // Multiplex ratio 64
0xD3, 0x00, // Display offset
0x40, // Display start line 0
0x8D, 0x14, // 开启内部电荷泵(I2C OLED必须)
0x20, 0x02, // Page寻址模式
0xA1, // Segment remap
0xC8, // COM扫描方向
0xDA, 0x12, // COM硬件配置,128‑64屏
0x81, 0xCF, // 对比度
0xD9, 0xF1, // Pre‑charge周期
0xDB, 0x30, // VCOMH
0xA4, // RAM输出
0xA6, // 正常显示(非反色)
0xAF // Display ON
};
static uint8_t g_oled_buf[SSD1306_WIDTH * SSD1306_HEIGHT / 8];
void Ssd1306_Init(void);
void Ssd1306_Clear(void);
void Ssd1306_Refresh(void);
void Ssd1306_DisplayString(uint8_t x, uint8_t y, const char *str);
#endif
ssd1306_i2c.c(注意 Ssd1306_Senddata函数)
#include "ssd1306_i2c.h"
#include <string.h>
#include <stdio.h>
// 发送单条命令
static hi_u32 Ssd1306_SendCmd(uint8_t cmd)
{
uint8_t buf[2] = {0x00, cmd};
hi_i2c_data i2c_data = {
.send_buf = buf,
.send_len = 2,
.receive_buf = NULL,
.receive_len = 0
};
return hi_i2c_write(I2C_IDX, SSD1306_ADDR, &i2c_data);
}
// 批量发送初始化命令
static hi_u32 Ssd1306_SendCmdArray(const uint8_t *cmds, uint16_t len)
{
hi_u32 ret;
for (uint16_t i = 0; i < len; i++)
{
ret = Ssd1306_SendCmd(cmds[i]);
if (ret != HI_ERR_SUCCESS)
return ret;
}
return HI_ERR_SUCCESS;
}
// 发送显示数据
static hi_u32 Ssd1306_SendData(uint8_t *dataptr, uint16_t len)
{
uint8_t buf[SSD1306_WIDTH+1];
uint16_t i;
buf[0] = 0x40; // 数据标识符
for (i = 0; i < len; i++)
{
buf[i + 1] = *dataptr;
dataptr++;
}
hi_i2c_data i2c_data = {
.send_buf = buf,
.send_len = len + 1,
.receive_buf = NULL,
.receive_len = 0
};
return hi_i2c_write(I2C_IDX, SSD1306_ADDR, &i2c_data);
}
void Ssd1306_Init(void)
{
// 1. GPIO复用配置:GPIO13=I2C0_SDA,GPIO14=I2C0_SCL
hi_io_set_func(HI_IO_NAME_GPIO_13, HI_IO_FUNC_GPIO_13_I2C0_SDA);
hi_io_set_func(HI_IO_NAME_GPIO_14, HI_IO_FUNC_GPIO_14_I2C0_SCL);
// 2. 初始化I2C0,400K波特率
hi_u32 ret = hi_i2c_init(I2C_IDX, I2C_BAUDRATE);
if (ret != HI_ERR_SUCCESS)
{
printf("I2C init fail, ret=%#X\r\n", ret);
return;
}
else
{
printf("I2C init success\r\n");
}
// 3. 下发SSD1306初始化序列
ret = Ssd1306_SendCmdArray(ssd1306_init_cmds, sizeof(ssd1306_init_cmds));
if (ret != HI_ERR_SUCCESS)
{
printf("SSD1306 cmd send fail, ret=%#X\r\n", ret);
return;
}
else
{
printf("SSD1306 cmd send success\r\n");
}
Ssd1306_Clear();
// 测试点亮左上角像素
g_oled_buf[0] = 0xFF;
Ssd1306_Refresh();
}
void Ssd1306_Clear(void)
{
memset(g_oled_buf, 0x00, sizeof(g_oled_buf));
}
void Ssd1306_Refresh(void)
{
for (uint8_t page = 0; page < 8; page++)
{
Ssd1306_SendCmd(0xB0 + page);
Ssd1306_SendCmd(0x00);
Ssd1306_SendCmd(0x10);
Ssd1306_SendData(&g_oled_buf[page * SSD1306_WIDTH], SSD1306_WIDTH);
}
}
void Ssd1306_DisplayString(uint8_t x, uint8_t y, const char *str)
{
while (*str != '\0')
{
if (x > SSD1306_WIDTH - 8)
{
x = 0;
y += 1;
}
//过滤超出可打印范围字符
if((*str <32)||(*str>126))
{
str++;
continue;
}
uint8_t idx = *str - 32;
for (uint8_t i = 0; i < 8; i++)
{
g_oled_buf[y * SSD1306_WIDTH + x + i] = font8x8[idx][i];
}
x += 8;
str++;
}
Ssd1306_Refresh();
}
在原来的主程序上增加了几句 i2c 测试函数:
#include <stdio.h>
#include "iot_errno.h"
#include "iot_uart.h"
#include "unistd.h"
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "iot_gpio.h"
#include "ssd1306_i2c.h"
#define LED_INTERVAL_TIME_US 1000000
#define LED_TASK_STACK_SIZE 1024
#define LED_TASK_PRIO 25
#define LED_TEST_GPIO 9 // for hispark_pegasus
// UART1 初始化
static void Uart1Test(void)
{
IotUartAttribute uart_cfg = {
.baudRate = 115200,
.dataBits = IOT_UART_DATA_BIT_8,
.stopBits = IOT_UART_STOP_BIT_1,
.parity = IOT_UART_PARITY_NONE
};
// 初始化UART1
unsigned int ret = IoTUartInit(1, &uart_cfg);
if(ret != 0 ) {
printf("Failed to initialize UART1: %d\n", ret);
}
else
{
char buf[] = "Hello UART1!\r\n";
printf("UART1 write result: %d\n", IoTUartWrite(1, (unsigned char *)buf, sizeof(buf)-1));
}
}
static void *LedTask(const char *arg)
{
(void)arg;
printf("Hello, OpenHarmony!\n");
while (1) {
IoTGpioSetOutputVal(LED_TEST_GPIO, 0);
usleep(LED_INTERVAL_TIME_US);
IoTGpioSetOutputVal(LED_TEST_GPIO, 1);
usleep(LED_INTERVAL_TIME_US);
IoTUartWrite(1, "s ", 2);
}
return NULL;
}
static void LedExampleEntry(void)
{
osThreadAttr_t attr;
Uart1Test();
Ssd1306_Init();
Ssd1306_DisplayString(3, 3, "Hello Hi3861!");
IoTGpioInit(LED_TEST_GPIO);
IoTGpioSetDir(LED_TEST_GPIO, IOT_GPIO_DIR_OUT);
IoTGpioSetOutputVal(LED_TEST_GPIO, 1);
attr.name = "LedTask";
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = LED_TASK_STACK_SIZE;
attr.priority = LED_TASK_PRIO;
if (osThreadNew((osThreadFunc_t)LedTask, NULL, &attr) == NULL) {
printf("[LedExample] Falied to create LedTask!\n");
}
else
{
printf("[LedExample] LedTask created successfully!\n");
}
}
SYS_RUN(LedExampleEntry);
由此可以看出,虽然 AI 可以帮你减少很多编程的工作量,但要做一个真正可靠的产品,必须能读懂 AI 帮你编写的程序, 以它为基础优化后使用,而非不加处理的照搬。尤其是硬件产品,有诸多软件实现的显性功能之外的隐性约束,如:实时性、可靠性、低功耗等。
4、开发环境使用遇到的一个问题
在上述实施过程中,经常会遇到做着做着就连不上远程(虚拟机)了,观察发现,虚拟机已经死机,只好重启。
在做调试程序时精力没放在这方面,重启就重启吧。
完成之后才想起来问一下豆包,在它的提示下确定是虚拟机内存耗尽所致。
原因是打开鸿蒙工程后,VScode 会自动扫描系统目录下的所有内容,并做相应处理,从而开了很多线程,逐渐占满了内存。我虚拟机分配了8G 都不够。总共就 16G 内存,不能再增加了!
在豆包的指导下,做了多次尝试,都没有明显效果,只好按它所建议,只打开自己的工作目录,编程完成后,用命令行编辑、烧录。或者启动工程,快速编译、烧录后退出。
因为编程时需要参考库函数的头文件,故我自己创建了一个Worksapce,打开工作目录和库的 include 目录,这样编写时比较方便。

这是不得而已的对策,不知道这个现象是否正常?有无更好的解决方法?毕竟这样等于丧失了 IDE 环境的优势。
顺带提出一个疑问:鸿蒙 OS 的设备开发环境到底需要什么样资源的 PC 才能胜任?还望高手指点!
——————————————————
更多推荐



所有评论(0)