一、前言

本文将详细介绍Hi3861开发板如何通过GPIO模块控制LED灯亮和灭。

二、鸿蒙设备开发通用框架

通用框架2.png

#include <stdio.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"


static void* HelloWorld_Task(const char* arg)
{
(void)arg;
printf("[HelloWorld] HelloWorld_Task()\n");



while(1) 
{
    // 任务代码,例如打印一个语句:
     printf("开源项目 OpenHarmony\n是每个人的OpenHarmony\n");
    usleep(100000);
}

return NULL;




}




static void HelloWorld_Entry(void)
{
osThreadAttr_t attr = {0};//定义了一个结构体



printf("[HelloWorld] HelloWorld_Entry()\n");

attr.name = "HelloWorld_Task";//当前TASK的名字
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = 1024;//整个任务的一个栈的大小
attr.priority = osPriorityNormal;//当前任务的优先级,通常设为Normal就行
////osThreadNew会创建一个线程,会执行HelloWorld_Task任务
if (osThreadNew((osThreadFunc_t)HelloWorld_Task, NULL, &amp;attr) == NULL)
{
    printf("[HelloWorld] Falied to create LedTask!\n");
}




}




SYS_RUN(HelloWorld_Entry);

SYS_RUN(HelloWorld_Entry);

为什么我们需要在入口函数中创建任务,而不直接写While(1)呢? 参考上篇文章所阐述的启动流程,如果我们的HelloWorld_Entry入口函数里写了一个While(1)的循环,那么它永远都不会返回,进入了一个死循环,后面的流程都不会进行,将影响别的应用的初始化。所以我们只能创建一个新的任务去实现它,在我们自己的线程里,便可以自由的while(1)了。 那如果我们不创建任务,把相关代码注释掉呢:

#include <stdio.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"


static void* HelloWorld_Task(const char* arg)
{
(void)arg;
printf("[HelloWorld] HelloWorld_Task()\n");



while(1) 
{
    // logic code for task
     printf("开源项目 OpenHarmony\n是每个人的OpenHarmony\n");
    usleep(10000000);
}

return NULL;




}




static void HelloWorld_Entry(void)
{
osThreadAttr_t attr = {0};



printf("[HelloWorld] HelloWorld_Entry()\n");
while(1) 
{
    // logic code for task
    printf("开源项目 OpenHarmony\n是每个人的OpenHarmony\n");
    usleep(10000000);
}
/*
attr.name = "HelloWorld_Task";
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = 1024;
attr.priority = osPriorityNormal;

if (osThreadNew((osThreadFunc_t)HelloWorld_Task, NULL, &amp;attr) == NULL)
{
    printf("[HelloWorld] Falied to create LedTask!\n");
}*/




}




SYS_RUN(HelloWorld_Entry);

SYS_RUN(HelloWorld_Entry);

BUILD.gn如下:

static_library("hello_lib") {
    sources = [
        "HelloWorld.c"
    ]

include_dirs = [
    "//utils/native/lite/include",
    "//kernel/liteos_m/components/cmsis/2.0",
    "//base/iot_hardware/peripheral/interfaces/kits",
]




}

}

image.png 注释掉任务代码,直接再入口函数中While(1),编写BUILD.gn,加入编译依赖然后编译烧录查看监视器,我们可以看到: image.png 虽然还是照常打印出了While(1)中的目标语句,但在我保留了上篇文章测试启动流程的代码情况下,所有的“上面那行代码执行了.......”都没被打印出来,说明启动流程后面的程序没法执行,程序卡在了前面的While(1)中,是个死循环。

三、通过GPIO模块控制LED灯亮灭

Hi3861 WLAN模组核心板 image.png

1.查原理图,找到LED外设对应的GPIO引脚

image.png 原理图中,J3默认由跳帽连接,为导通状态。LED1即核心板可编程LED灯,一端通过电阻R6连接到3V3电源,一端通过J3排针和GPIO09引脚连接。因此我们可以通过GPIO09引脚输出高低电平控制LED1的亮灭。 由原理图可知,当GPIO09引脚输出低电平时,导通电源,LED1亮,输出高电平时,LED1灭。

2.编写业务逻辑代码通过GPIO点灯

2.1 创建led.c文件

在applications/sample/wifi-iot/app/目录下创建led_demo目录,在该目录下创建led.c文件,内容如下:

#include <unistd.h>
#include "stdio.h"
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "iot_gpio.h"


#define LED_TEST_GPIO 9 // for hispark_pegasus
#define LED_INTERVAL_TIME_US 300000
#define LED_TASK_STACK_SIZE 512
#define LED_TASK_PRIO 25 // 通常做demo开发时都被设置为25




void *LedTask(const char *arg)
{
//初始化GPIO
IoTGpioInit(LED_TEST_GPIO);



//设置为输出
IoTGpioSetDir(LED_TEST_GPIO, IOT_GPIO_DIR_OUT);

(void)arg;
while (1) 
{
    //输出低电平
    IoTGpioSetOutputVal(LED_TEST_GPIO, 0)
    usleep(300000);
    //输出高电平
    IoTGpioSetOutputVal(LED_TEST_GPIO, 1);
    usleep(300000);
}

return NULL;




}




void led_demo_entry(void)
{
osThreadAttr_t attr;



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;//当前任务的优先级,通常做demo开发时我们设为25

//osThreadNew会创建一个线程,会执行LedTask任务
if (osThreadNew((osThreadFunc_t)LedTask, NULL, &amp;attr) == NULL) {
    printf("[LedExample] Falied to create LedTask!\n");
}




}
SYS_RUN(led_demo_entry);

} SYS_RUN(led_demo_entry);

API功能描述

  • IoTGpioInit()用于GPIO模块初始化,
  • IoTGpioSetDir用于设置GPIO引脚方向,id第一个参数用于指定引脚,dir第二个参数用于指定输入或输出。
  • IoTGpioSetOutputVal函数用于设置引脚的输出状态。函数的第一个参数用于指定引脚,第二个参数使用的枚举IOT_GPIO_VALUE0和IOT_GPIO_VALUE1对应的值分别为0和1,用于指定高电平或低电平,直接使用0或1程序也同样运行。
2.2 创建BUILD.gn文件

在applications/sample/wifi-iot/app/led_demo目录下创建BUILD.gn文件

static_library("led_lib") {
    sources = [
        "led.c"
    ]

include_dirs = [
    "//utils/native/lite/include",
    "//kernel/liteos_m/components/cmsis/2.0",
    "//base/iot_hardware/peripheral/interfaces/kits",
]




}

}

在本BUILD.gn文件中,定义了一个名为led_demo的静态库,同时指定了编译该静态库所需的源代码文件列表和包含目录列表。

2.3 在applications/sample/wifi-iot/app目录下的BUILD.gn的feartures中添加编译依赖
import("//build/lite/config/component/lite_component.gni")


lite_component("app") {
features = [
"startup",
"led_demo:led_lib"
]
}

lite_component("app") { features = [ "startup", "led_demo:led_lib" ] }

3.烧录和运行

编译烧录运行后,将会看到主板上的LED灯开始闪烁。 更多关于OpenHarmony轻量系统驱动框架的介绍,可以参照 OpenHarmony轻量系统开发【5】驱动之GPIO点灯~

四、GPIO点灯进阶之流水灯

掌握了最基础的点灯操作后,我们可以来试试流水灯玩玩。因为我手边没有红绿灯板,所以我将Hi3861 WLAN模组与Hi3861底板连接,外接了六个LED灯管构成了流水灯 my_led.c如下:

#include <stdio.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "iot_gpio.h"


#define LED_INTERVAL_TIME_US 300000
#define LED_TASK_STACK_SIZE 512
#define LED_TASK_PRIO 25 // 通常做demo开发时都被设置为25




static led[] =
{
9,11,12,10,7,8   // 六个灯管依次接9,11,12,10,7,8
};
static void *LedTask(const char *arg)
{
(void)arg;
printf("ledTask start!\r\n");



int i = 0;
while (1)
{
    int j = 0;

    while( j &lt; 6 )
    {
        IoTGpioSetOutputVal(led[j++], 0);
    }

    i = (i + 1) % 6;

    IoTGpioSetOutputVal(led[i], 1);
    
    usleep(500000);

}
return NULL;




}




static void LedExampleEntry(void)
{
osThreadAttr_t attr;//定义了一个结构体



for(int i=0; i&lt;6; i++)
{
    IoTGpioInit(led[i]);
    //设置为输出
    IoTGpioSetDir(led[i], IOT_GPIO_DIR_OUT);
}

attr.name = "LedTask";//当前TASK(任务)的名字
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;//当前任务的优先级,通常做demo开发时我们设为25

//osThreadNew会创建一个线程,会执行LedTask任务
if (osThreadNew((osThreadFunc_t)LedTask, NULL, &amp;attr) == NULL) {
    printf("[LedExample] Falied to create LedTask!\n");
}




}




SYS_RUN(LedExampleEntry);


BUILD.gn:

static_library("led_lib") {
    sources = [
        "myled.c"
    ]

include_dirs = [
    "//utils/native/lite/include",
    "//kernel/liteos_m/components/cmsis/2.0",
    "//base/iot_hardware/peripheral/interfaces/kits",
    "//device/soc/hisilicon/hi3861v100/hi3861_adapter/hals/communication/wifi_lite/wifiservice",
    "//device/soc/hisilicon/hi3861v100/hi3861_adapter/kal",
]




}

}

再在app目录下的BUILD.gn中加入编译依赖:

import("//build/lite/config/component/lite_component.gni")


lite_component("app") {
features = [
"startup",
"my_led:led_lib"
]
}

lite_component("app") { features = [ "startup", "my_led:led_lib" ] }

视频在这: tutieshi_368x640_5s.gif

五、后记

参考文档及教程 penHarmony轻量系统开发【5】驱动之GPIO点灯 三周带你上手OpenHarmony设备开发 有兴趣的小伙伴赶紧玩起来吧! 如果发现本篇文章有不对的地方,欢迎交流探讨哦! 【本文正在参加物联网有奖征文活动】,活动链接:https://ost.51cto.com/posts/14758;

想了解更多关于开源的内容,请访问:

51CTO 开源基础软件社区

https://ost.51cto.com/#bkwz