第 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灯全部闪烁。