第
5
章
闪烁的LED实验
【本章学习目标】
(1)动手完成闪烁的LED 灯实验,熟练掌握C语言鸿蒙OS 设备程序项目的
架构和内容。
(2)动手完成呼吸灯实验,熟练掌握C语言鸿蒙OS 设备程序项目的开发步
骤和方法。
(3)了解闪烁的LED 灯和呼吸灯程序的工作原理。
第3章的C语言鸿蒙OS 设备开发实验点亮了一个LED 灯(发光二极管), 本
章利用循环控制实现一个闪烁的LED 灯。

..5.1 
闪烁的LED 
灯

5.1 
闪烁的LED 
灯程序项目的结构和内容
1.
闪烁的LED 灯C语言鸿蒙OS 程序项目由1个C语言源程序文件
c、gg.

FLASHING.2个BUILD.n文件和1个confijson文件组成。各个文件在计
算机磁盘上的存储情况如图5-1所示。


图5-
1 
闪烁的LED 
灯C语言鸿蒙OS 
程序项目结构和内容示意图

图中外带方框的FLASHING 和SOURCE_FLASHING 是文件夹,而且
SOURCE_FLASHING 文件夹是FLASHING 
j
文件夹下面的子文件夹。在
FLASHING 文件夹下有BUILD.gn和config.son两个文件,在SOURCE_ 
FLASHING 子文件夹下也有一个BUILD.n文件和一个FLASHIN.

nijognijoc文件。其
中,cofg.sn文件内容和第3章的点亮一只LED 灯项目的cofg.sn文件内容
基本相同,只有第一行“productname后(”) 面的项目名称有区别,因为是两个不同
的项目。点亮一只LED 灯项目的(_) 项目名称为LED,闪烁的LED 灯项目的项目名
称为FLASHING 。各文件的内容分别如下所示。


1 44 鸿蒙OS智能设备开发基础(微课版) 
(1)FLASHING文件夹下的BUILD.gn文件内容。 
group("FLASHING") 
{ 
deps = [ 
"SOURCE_FLASHING:FLASHING", 
"//device/bossay/hi3861_l0/sdk_liteos:wifiiot_sdk", 
"../common/iot_wifi:iot_wifi", 
] 
}
(2)FLASHING文件夹下的config.json文件内容。 
{ 
"product_name": "FLASHING", 
从第2 行开始,剩余内容跟点亮一只LED 灯项目的config.json 文件从第2 行开始的各行内
容相同,在此不再赘述。
}
(3)SOURCE_FLASHING文件夹下的BUILD.gn文件内容。 
static_library("FLASHING") 
{ 
sources = ["FLASHING.c", ] 
include_dirs = [ 
"//utils/native/lite/include", 
"//base/iot_hardware/peripheral/interfaces/kits", 
"//device/bossay/hi3861_l0/iot_hardware_hals/include", 
"//device/bossay/hi3861_l0/sdk_liteos/include" 
] 
}
(4)SOURCE_FLASHING文件夹下的FLASHING.c文件内容。 
#include <stdio.h> 
#include "ohos_init.h" 
#include "iot_gpio.h" 
#include "iot_gpio_ex.h" 
#define LED_GPIO 9 
static void led(void* args) 
{ 
printf("led running..."); 
IoTGpioInit(LED_GPIO); 
IoTGpioSetDir(LED_GPIO,IOT_GPIO_DIR_OUT); 
IoTGpioSetFunc(LED_GPIO,IOT_GPIO_FUNC_GPIO_9_GPIO); 
int v = 1; 
while(1) 
{ 
IoTGpioSetOutputVal(LED_GPIO,v); 
v = 1-v;

第5章 闪烁的LED 实验1 45 
usleep(200*1000); 
} 
}
APP_FEATURE_INIT(led); 
5.1.2 
5.1.2 编辑、编译、烧录、运行闪烁的LED 灯程序
参照本书第4章网页编译的方法,将源程序FLASHING.c的代码复制到网页进行编
译,生成可执行目标代码;或参照第3章的方式,利用VSCode的DevEco工具建立闪烁的
LED灯程序项目,编辑程序代码、编译生成可执行目标代码。然后使用USB-Type连线连
接计算机和开发板,利用Hiburn工具烧录可执行目标代码到开发实验板,按下开发板上的
RESET复位键运行项目程序,程序运行效果如图5-2所示。
图5-2 闪烁的LED 效果图
5.1.3 
5.1.3 闪烁的LED 灯程序的工作原理
图5-3是一个32引脚的Hi3861芯片。大多数引脚有多重功能,例如,本次实验使用的
GPIO9实际上是27号引脚,可以作为通用输入输出端口。
图5-3 Hi3861引脚图
闪烁的LED灯程序代码开头使用include引用
了四个类库,stdio为标准输入输出库,包含printf函
数的定义,ohos_init包含宏APP_FEATURE_INIT 
的定义,iot_gpio 包含控制Hi3861 芯片的引脚
GPIO 驱动函数的定义,iot_gpio_ex 包含IOT_ 
GPIO_FUNC_GPIO_9_GPIO 的定义。
APP_FEATURE_INIT指出程序入口为led函
数。该函数中使用printf打印了提示信息,并对
Hi3861芯片的第27引脚(即LED_GPIO)的GPIO 
功能进行了初始化。通过IoTGpioSetDir函数设定

146鸿蒙OS智能设备开发基础(微课版) 
第27引脚为输出方向,功能为通用输入输出。之所以要进行这些设计,是因为芯片中的引
脚是有限的,引脚多了芯片的面积增大、外部电路设计也会变得复杂。
这个27端口一共有9种功能,在使用时需要用程序将信息传输到芯片,当前想使用哪
一种功能。在iot_gpio_ex中对这些功能进行了定义。
程序的主体是一个while循环,关键变量是v,其初始值为1,使用IoTGpioSetOutputVal 
将v的值设置到如图5-4所示的GPIO09上。第一次循环v的值为1,随后v=1-v,v的
值变为0,下次循环将0输出后,v的值又翻转为1。就这样,v的值一直在1和0之间翻转, 
GPIO输出的电压也在1对应的高电平和0代表的低电平之间反复转换,GPIO09连接的灯
也会在亮和灭之间切换,形成闪烁的效果。
图5-
4 
GPIO9相关电路

函数uep为延迟函数,单位为μs,程序里为0.s。可以通过修改uep的参数观察
闪烁频率的变化。

sl2sl

..5.2 
C语言鸿蒙OS设备开发实验:呼吸灯

5.1 
呼吸灯项目的程序代码
2.
nijoc、
gn文件和1个cofg.sn文件组成。各个文件在计算机磁盘上的存储情况如图5-5所示, 
文件内容分别如下所示。

呼吸灯C语言鸿蒙OS程序项目由1个C语言源程序文件BREATHE.2个BUILD. 


图5-
5 
呼吸灯C语言鸿蒙OS程序项目结构和内容示意图


第5章 闪烁的LED 实验1 47 
(1)BREATHE文件夹下的BUILD.gn文件内容。 
group("BREATHE") 
{ 
deps = [ 
"SOURCE_BREATHE:BREATHE", 
"//device/bossay/hi3861_l0/sdk_liteos:wifiiot_sdk", 
"../common/iot_wifi:iot_wifi", 
] 
}
(2)BREATHE文件夹下的config.json文件内容。 
{ 
"product_name": "BREATHE", 
从第2 行开始,剩余内容跟点亮一只LED 灯项目的config.json 文件从第2 行开始的各行内
容相同,在此不再赘述。
}
(3)SOURCE_BREATHE文件夹下的BUILD.gn文件内容。 
static_library("BREATHE") 
{ 
sources = ["BREATHE.c", ] 
include_dirs = [ 
"//utils/native/lite/include", 
"//base/iot_hardware/peripheral/interfaces/kits", 
"//device/bossay/hi3861_l0/iot_hardware_hals/include", 
"//device/bossay/hi3861_l0/sdk_liteos/include" 
] 
}
(4)SOURCE_BREATHE文件夹下的BREATHE.c文件内容。 
#include <stdio.h> 
#include "ohos_init.h" 
#include "iot_gpio.h" 
#include "iot_gpio_ex.h" 
#include "iot_pwm.h" 
#define PWM_GPIO 9 
void pwm_entry() 
{ 
printf("pwm_entry called \n"); 
IoTGpioInit(PWM_GPIO); 
IoTGpioSetDir(PWM_GPIO,IOT_GPIO_DIR_OUT); 
IoTGpioSetFunc(PWM_GPIO,IOT_GPIO_FUNC_GPIO_9_PWM0_OUT); 
IoTPwmInit(0); 
int i; 
while(1) 
{ 
for(i=0;i<100;i++) 
{

1 48 鸿蒙OS智能设备开发基础(微课版) 
IoTPwmStart(0,i,40000); 
usleep(1000*10); 
} 
for(i=100;i>=0;i--) 
{ 
IoTPwmStart(0,i,40000); 
usleep(1000*10); 
} 
} 
}A
PP_FEATURE_INIT(pwm_entry); 
5.2.2 
5.2.2 编辑、编译、烧录、运行呼吸灯程序
参照本书第4章网页编译的方法,将源程序BREATHE.c的代码复制到网页进行编
译,生成可执行目标代码;或参照第3章的方式,利用VSCode的DevEco工具建立呼吸灯
程序项目,编辑程序代码、编译生成可执行目标代码。然后使用USB-Type连线连接计算机
和开发板,利用Hiburn工具烧录可执行目标代码到开发实验板,按下开发板上的RESET 
复位键运行项目程序。
5.2.3 
5.2.3 呼吸灯程序的电气工作原理
呼吸灯是像呼吸一样慢慢变亮又慢慢变暗的周期性亮度变化的灯。单片机中无法输出
这种不断变化的模拟信号,必须配合相应的电路才能实现,那么怎样控制LED 的亮度呢? 
单片机提供了一种通用的解决方案:脉冲宽度调制(PulseWidthModulation,PWM)方案, 
即使用周期性的脉冲输出代替持续稳定的电压输出。图5-6给出了使用PWM 模拟2.5V、
3.75V 和1V 电压输出的样例。该样例中5V 是基准电压,如果只有一半的时间输出高电平
(占空比50%),那么对于同一个负载来说,功率就是原本的一半,那么就可以模拟2.5V 电
压。同样的原理,如果只有20%的时间输出高电平,结果就是原本电压的1/5,即1V。如果
有75%的时间输出高电平,则为3.75V。
图5-6 PWM 波形
当然,这样输出的电压是周期性变化的,如果想保持特定电压的稳定输出,就需要将
PWM 输出的信号通过带有电容的滤波电路来实现,对于驱动一个LED 来说,显然不需要
通过这样的滤波电路。那么使用周期性变化的信号来驱动点亮LED会不会让肉眼看到闪

第5章闪烁的LED实验149 

烁呢? 这个问题等价于看电影时能不能看到闪烁一样,答案是只要闪烁得够快,肉眼就看不

到闪烁。

Hi3861芯片不仅支持6个硬件PWM输出,而且也可以通过程序代码来控制PWM的
输出,PWM的输出不过是周期性地快速拉高拉低电平而已,使用GPIO操作就可以实现, 
这种是软件PWM 。硬件PWM的好处是可以达到更高的频率,频率越高越不容易看到闪
烁,另外,只需要启动硬件PWM,程序就可以做别的事情,而不需要进入一个不断地拉高和
拉低引脚电平的循环,这样CPU空闲以后就可以做别的事情。

鸿蒙系统提供一组API(应用编程接口)函数实现引脚的PWM功能控制。首先通过
IoTGpioInit初始化引脚,随后使用IoTGpioSetDir将方向设为输出,最重要的是使用
IoTGpioSetFunc将功能设置为PWM 。前面提到支持6路硬件PWM,这意味着只有6个
引脚可以支持硬件PWM,它们是GPIO9 、GPIO10 、GPIO5 、GPIO12 、GPIO13 、GPIO14,分
别对应PWM0~PWM5,可以从Hi3861的用户指南中查到。这里使用GPIO9引脚
(PWM0)进行实验。

函数IoTPwmInit传入PWM的编号0实现了初始化,函数IoTPwmStart则实现了
PWM的启动。其中,三个参数中的第一个参数传入0表示操作的是PWM0,第二个参数i 
为占空比,最后一个参数为频率,传入了40000 。

为了实现灯慢慢变亮的效果,程序将占空比从0逐渐调整到了100,再慢慢降低至0。
第一个循环实现从0到99,第二个循环实现100到0。
外层嵌套着的while循环保证这个过程持续运行,这样一个会呼吸的灯就做好了。

..5.3 
习题

bosay实验板上呈五角星分布的5个LED灯的GPIO分别是GPIO9 、GPIO10 、
GPIO11 、GPIO12 、GPIO13 。

(1)设计程序实现流水灯功能,让实验板上呈五角星分布的LED灯循环依次点亮。
(2)设计程序实现让实验板上呈五角星分布的LED灯全部闪烁。