1 Star 0 Fork 0

兔兔爱喝核桃露 / Hi3861_WIFI模块

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
Hi3861wifi模块开发.md 20.60 KB
一键复制 编辑 原始数据 按行查看 历史
兔兔爱喝核桃露 提交于 2023-01-18 20:52 . 笔记更新

[TOC]

Hi3861 WIFI模块开发

1. 实现功能

  1. 可作为STA模式连接路由器;
  2. 可以设置为静态IP地址;
  3. 可以修改连接路由的SSID和PASSWORD;
  4. 可以作为STA模式与其他IP地址进行UDP通信,通信内容为十六进制数组;
  5. 可以与单片机进行串口通信,将串口数据无线传输,可以根据串口指令更改连接路由的SSID和PASSWORD;
  6. 可以定时检测WIFI连接状态,通过GPIO口控制LED灯进行显示;
  7. 可部署在安信可 Hi3861模块上,可以外部修改程序。

2. 开发过程

2.1 HI3861开发环境搭建(1227)

2.1.1 环境搭建

环境搭建按照小熊派GITEE库中介绍十分钟快速上手搭建

  1. 解压小熊派虚拟机文件,安装在一个新文件夹中;
  2. 开机密码为bearpi;
  3. ifconfig 查询虚拟机的IP地址,下一步SSH连接使用;
  4. 使用RaiDrive 连接虚拟机的硬盘,可以查询虚拟机中的文件;
  5. 虚拟机中先获取源码,放置在文件夹project中;

ssh连接后,

打开bearpi文件夹

cd /home/bearpi

创建project文件夹

mkdir project && cd project

hpm init -t default

获取代码

hpm i @bearpi/bearpi_hm_nano

等待1-3分钟(根据不同网速)

当屏幕中出现Installed.意味着代码获取完成

1

  1. RaiDrive 连接虚拟机的硬盘后,在windows下打开该文件夹中,使用VScode打开project文件夹;

2.1.2 环境使用

  1. 打开VMware,打开虚拟机,终端中查询虚拟机IP地址;
  2. 打开RaiDrive 将虚拟机文件夹映射到Windows系统,将虚拟机的IP地址添加到设置中;
  3. 打开虚拟机文件系统,找到文档位置,拖动到VsCode图标处,打开整个文件夹;
  4. 在VScode中==新建终端==,在终端中输入

ssh bearpi@192.168.3.21

​ 其中的IP地址输入虚拟机的IP,输入密码后,SSH连接到虚拟机;

  1. 在终端处对项目进行编译;
  2. 编译指令,在源码的根目录下project下执行编译指令;

python build.py BearPi-HM_Nano

  1. 编译完成后,显示build successs,打开桌面的HiBurn,将固件下载到Hi3861开发板中进行验证。

2.1.3 下载程序

  1. 打开桌面的HiBurn,选择开发板的com口;

  2. select file 选择编译好的固件,Z:\home\bearpi\project\out\BearPi-HM_Nano中的Hi3861_wifiiot_app_allinone.bin

  3. 勾选上Auto burn1

  4. 点击connect,然后开发板点击reset按键重新上电,此时开始下载程序;

1

  1. 下载完成后,点击disconnect断开连接;
  2. 打开串口调试助手,打开com口,重新reset上电,开发板开始运行新程序。

2.1.4 新建程序

Z:\home\bearpi\project\applications\BearPi\BearPi-HM_Nano\sample

在sample文件夹中新建程序,例如,新家my_app文件夹

在该文件夹中新建程序,进行编译

BUILD.gn 中修改程序

static_library("wificonnect") {
    sources = [
        "demo1_1.c",
        "bsp_usart.c",
    ]
    include_dirs = [
        "//utils/native/liteos/include",
        "//kernel/liteos_m/components/cmsis/2.0",
        "//base/iot_hardware/interfaces/kits/wifiiot_lite",
        "//foundation/communication/interfaces/kits/wifi_lite/wifiservice",
        "//vendor/hisi/hi3861/hi3861/third_party/lwip_sack/include/",
    ]
}

static_library中 填写该程序的名称

sources中填写 .c文件

include中填写 .h文件的路径

然后在外部文件夹中的BUILD.gn,修改程序,添加文件夹:程序名称,将不需要编译的程序使用# 注释掉

        #"D10_iot_cloud_oc_manhole_cover:cloud_oc_manhole_cover",
        #"D11_iot_cloud_oc_infrared:cloud_oc_infrared",
        #"D12_iot_cloud_oc_agriculture:cloud_oc_agriculture",
        #"D13_iot_cloud_oc_gps:cloud_oc_gps",

        # "my_app:myapp",
        "demo1_1:wificonnect",

上图表示,只编译demo1_1文件夹中的wificonnect程序。

2.1.5 应用到的库

使用海思的SDK进行编写

#include "hi_gpio.h"

小熊派使用的库底层也是海思的库,只是在上面封装了两层。

参考API手册进行查询和编写,小熊派的程序中查看底层进行重新编写。

1

Hi3861V100/Hi3861LV100 SDK 开发指南

Hi3861V100/Hi3861LV100 Wi-Fi 软件开发指南

查询这几个文档进行编写

2.1.6 上电顺序

在使用hi3861进行串口通信时,使用串口1,串口0用于打印log,发送数据时会自动原样发送回来,没有该功能软件的开关,因此使用串口1

在使用串口1进行数据收发时,开发板连接TTL转串口模块时,无法下载程序,也无法正常重启

这是由于

1

其中IO6为UART1_TXD ,连接TTL转USB时,会置高,因此在后续使用时,先给hi3861上电,再UART1初始化。使模组可以进入正常工作状态。


2.2 GPIO控制(1228)

代码存放位置为demo1、 demo2、demo3

2.2.1 LED闪烁

创建LED闪烁任务,50ms闪烁一次

static void LedTask(void)
{

    while (1)
    {
        WhiteLedOn();
        osDelay(50);
        WhiteLedOff();
        osDelay(50);
    }
}


/*entry  加入,登记 */
static void LedEntry(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 = 4096;
    attr.priority = osPriorityNormal;

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

控制LED灯的GPIO口初始化设置

/* LED灯 GPIO初始化 */
void LED_INIT(void)
{
    
    //设置GPIO_2引脚为输出模式
    hi_gpio_init();
    /* 设置IO复用功能 */
    hi_io_set_func(HI_IO_NAME_GPIO_12 , HI_IO_FUNC_GPIO_12_GPIO);
    hi_io_set_func(HI_IO_NAME_GPIO_2 , HI_IO_FUNC_GPIO_2_GPIO);

    /* 设置IO为输出模式 */
    hi_gpio_set_dir(HI_GPIO_IDX_12, HI_GPIO_DIR_OUT);
    hi_gpio_set_dir(HI_GPIO_IDX_2, HI_GPIO_DIR_OUT);
}

查询SDK手册进行编写,先进行GPIO初始化,然后选择GPIO口的功能,然后设置IO的输出/输出模式。

控制LED灯亮灭,使用hi_gpio_set_ouput_val(HI_GPIO_IDX_12, HI_GPIO_VALUE1) ;

/* 绿灯亮 */
void GreenLedOn(void)
{
    hi_gpio_set_ouput_val(HI_GPIO_IDX_12,  HI_GPIO_VALUE1) ;
    printf("GreenLedOn ! \r\n");
}

/* 绿灯灭 */
void GreenLedOff(void)
{
    hi_gpio_set_ouput_val(HI_GPIO_IDX_12,  HI_GPIO_VALUE0) ;
    printf("GreenLedOff ! \r\n");
}

任务创建后,上电后,LED灯开始闪烁。

2.2.2 串口控制LED灯

定义串口收发BUF,由于存放数据。

/* 串口1收发buf */
uint8_t sendBuf[UART_BUFF_SIZE]={0};
uint8_t *sendBuf_ptr = sendBuf;

uint8_t recvBuf[UART_BUFF_SIZE]={0};
uint8_t *recvBuf_ptr = recvBuf;

初始化串口,使用串口1进行数据收发,

初始化完成后,在while循环中处理收到的数据,

使用switch case 处理command指令

收到指令后,并且回复串口指令, Usart_SendArray(sendBuf,10);

/* 串口1 任务 */
static void UART_Task(void)
{

    uint32_t ret;
    uint32_t command;


    /* 串口设置 */
    WifiIotUartAttribute uart_attr = 
    {
        .baudRate = 115200,
        .dataBits = 8,
        .stopBits = 1,
        .parity = 0,
    };

    /* 初始化串口 */
    ret = UartInit(WIFI_IOT_UART_IDX_1, &uart_attr, NULL);
    if (ret != WIFI_IOT_SUCCESS)
    {
        printf("Failed to init uart! Err code = %d\n", ret);
        return;
    }

    printf("UART Test Start\n");
    while (1)
    {
        /* 通过串口1接收数据 */
        UartRead(WIFI_IOT_UART_IDX_1, recvBuf_ptr, UART_BUFF_SIZE);

        //usleep(5000);
        /* 串口指令控制LED灯亮灭 */
        command = recvBuf[1];
	    command = (command <<8)  +  recvBuf[2];

        switch(command)
        {
            case LEDON:
                GreenLedOn();

                sendBuf[0] = 0x11;
                Usart_SendArray(sendBuf,10);
                break;
            
            case LEDOFF:
                GreenLedOff();
                break;

            default:
                break;

        }        
    }
}

2.2.3 按键外部中断控制LED灯

GPIO外部中断控制,按照下面流程初始化设置

设置完成后,在主函数中调用KEY_INIT

void KEY_INIT(void)
{

    /* 初始化F1按键,设置为下降沿触发中断 */
    /* 设置按键的GPIO复用功能为GPIO */
    hi_io_set_func(HI_GPIO_IDX_7,HI_IO_FUNC_GPIO_7_GPIO);
    
    /* 设置按键GPIO为输入模式 */
    hi_gpio_set_dir(HI_GPIO_IDX_7, HI_GPIO_DIR_IN);
    
    /* 设置上拉输入 */
    hi_io_set_pull(HI_GPIO_IDX_7,HI_IO_PULL_UP); 
    
    /* 设置终端回调函数 */
    //hi_gpio_register_isr_function
    /* 中断回调函数前加(gpio_isr_callback)类型转换 */
    hi_gpio_register_isr_function(HI_GPIO_IDX_7,HI_INT_TYPE_EDGE,HI_GPIO_EDGE_FALL_LEVEL_LOW,  (gpio_isr_callback)KEY_Pressed,  NULL ) ;
    //GpioRegisterIsrFunc(HI_GPIO_IDX_7,HI_INT_TYPE_EDGE,HI_GPIO_EDGE_FALL_LEVEL_LOW, KEY_Pressed, NULL) ;
}

注意,在使用hi库的hi_gpio_register_isr_function中的中断回调函数前,应加(gpio_isr_callback)进函数类型转换,否则会出错。

中断回调函数

static volatile hi_gpio_value led_value = HI_GPIO_VALUE0 ;

/* GPIO 中断回调函数 */
void KEY_Pressed(char *arg)
{
    
    (void) arg;
    led_value = !led_value;
    hi_gpio_set_ouput_val(HI_GPIO_IDX_12,  led_value);


}

按下按键时,led灯的电平引脚进行翻转。


2.3.4 开发板无法按reset重启的问题

1

当使用串口调试助手时,RTS勾选上时,开发板按reset按键无法进行重启

原因是:

安信可的开发板上RTS连接着PWRON引脚,导致无法被拉低,无法重启。

1

将串口调试助手中的RTS取消勾选,即可按键重启了。

2.3 STA模式连接路由器(1229)

2.3.1 WIFI术语介绍

SSID 服务集标识符(Service Set IDendifier),用于标识不同的网络,即网络的名称

AP 接入点(Access Point),是允许其他无线设备连接的设备。

STA 工作站(Station) ,WIFI设备。

WPA WIFI访问包含,是一种保护无线网络访问安全的技术,目前有WPA,WPA2,WPA3三种标准

Frequency 频段,无线网络是使用无线电波进行通信的。IEEE 802.11协议中定义了不同的频段,如2.4GHz,3.6GHz,4.9GHz,5.8GHz。

Channel 信道。每个频段又被分为若干信道

Band 频带,目前HarmonyOS定义的频带有2G和5G两种

2.3.2 连接WIFI

  1. 用小熊派wifi_connect.c 中的函数进行连接
static void UDPClientTask(void)
{   
    WifiConnect("653","123456789");
}




static void UDPClientDemo(void)
{
    osThreadAttr_t attr;

    attr.name = "UDPClientTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 10240;
    attr.priority = osPriorityNormal;

    if (osThreadNew((osThreadFunc_t)UDPClientTask, NULL, &attr) == NULL)
    {
        printf("[UDPClientDemo] Falied to create UDPClientTask!\n");
    }
}

也需要创建任务中执行WifiConnect("653","123456789");

  1. 设置自动重连
WiFiInit();
EnableWifi();

WiFiInit();函数中进行wifi事件event的注册

EnableWifi();函数进行wifi的使能,其中

会进行以下操作


/* 设置wifi模块为STA模式 */
hiRet = hi_wifi_sta_start(ifName, &len);

/* 设置wifi模块断线自动重新连接 */
hiRet = hi_wifi_sta_set_reconnect_policy(WIFI_RECONN_POLICY_ENABLE, WIFI_RECONN_POLICY_TIMEOUT,
        WIFI_RECONN_POLICY_PERIOD, WIFI_RECONN_POLICY_MAX_TRY_COUNT);

因此不需要在外部重复设置自动重连。

==注意:==

如果需要自动重连能够正常执行,应该保证WIFI连接步骤中,DHCP功能的执行,

因此

  • 先启动DHCP

  • 设置模块IP地址

  • 等待DHCP设置完成,输出连接信息

    /* 启动DHCP */
    if (g_lwip_netif)
    {
        dhcp_start(g_lwip_netif);
        printf("begain to dhcp");
    }
    
    /* 设置静态IP地址 */
    hi_sta_set_addr(g_lwip_netif);

    /* 等待DHCP */
    for(;;)
    {
        if(dhcp_is_bound(g_lwip_netif) == ERR_OK)
        {
            printf("<-- DHCP state:OK -->\r\n");

            //打印获取到的IP信息
            netifapi_netif_common(g_lwip_netif, dhcp_clients_info_show, NULL);
            break;
        }

        printf("<-- DHCP state:Inprogress -->\r\n");
        osDelay(100);
    }

串口输出模块连接状态log

[11:50:30.081]收←◆<-- DHCP state:Inprogress -->

[11:50:31.078]收←◆<-- DHCP state:OK -->
server :
	server_id : 192.168.1.1
	mask : 255.255.255.0, 1
	gw : 192.168.1.1
	T0 : 86400
	T1 : 43200
	T2 : 75600
clients <1> :
	mac_idx mac             addr            state   lease   tries   rto     
	0       94c9601e1a28    192.168.1.110   10      0       1       3    

设置连接WIFI后的IP地址的函数hi_sta_set_addr(struct netif *pst_lwip_netif)

static void hi_sta_set_addr(struct netif *pst_lwip_netif)
{   

    //struct netif *pst_lwip_netif;
    ip4_addr_t st_gw;
    ip4_addr_t st_ipaddr;
    ip4_addr_t st_netmask;
    printf("%s %d \r\n", __FILE__, __LINE__);
    if (pst_lwip_netif == NULL) {
        printf("hisi_reset_addr::Null param of netdev\r\n");
        return;
    }

    IP4_ADDR(&st_gw, 192, 168, 1, 1);
    IP4_ADDR(&st_ipaddr, 192, 168, 1, 6);
    IP4_ADDR(&st_netmask, 255, 255, 255, 0);

    netifapi_netif_set_addr(pst_lwip_netif, &st_ipaddr, &st_netmask, &st_gw);
}

设置完成后,上电后连接WIFI后,将自动连接WIFI,连接成功后,设置模块自身的IP地址。

  1. 执行ping指令,WIFI连接成功。
C:\Users\10512>ping 192.168.1.6

正在 Ping 192.168.1.6 具有 32 字节的数据:
来自 192.168.1.6 的回复: 字节=32 时间=16ms TTL=255
来自 192.168.1.6 的回复: 字节=32 时间=1ms TTL=255
来自 192.168.1.6 的回复: 字节=32 时间=2ms TTL=255
来自 192.168.1.6 的回复: 字节=32 时间=2ms TTL=255

192.168.1.6 的 Ping 统计信息:
    数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失)
往返行程的估计时间(以毫秒为单位):
    最短 = 1ms,最长 = 16ms,平均 = 5ms

2.3.3 程序的文件架构

1

timer 文件夹中为定时器文件,创建一个定时器,定时检测WIFI的连接状态,通过指示灯进行显示,wifi连接后,指示灯亮,wifi断开连接后,指示灯灭;

usart 文件夹中为串口文件,创建一个串口,接收和发送串口指令

​ 还包括GPIO控制指示灯的相关函数;

​ 按键中断设置

wifi 文件夹中为WIFI连接程序,调用其中的函数进行wifi连接

mymain.c 文件为主程序,其中mymain()为主函数,程序从此开始运行

BUILD.gn 编译文件,在其中将上述头文件所在文件夹和.c源文件添加到其中

static_library("demo4") {
    sources = [
        "mymain.c",
        "usart/bsp_usart.c",
        "wifi/wifi_connect.c",
        "timer/bsp_timer.c",
    ]
    include_dirs = [
        "usart",
        "wifi",
        "timer",
        "//utils/native/liteos/include",
        "//kernel/liteos_m/components/cmsis/2.0",
        "//base/iot_hardware/interfaces/kits/wifiiot_lite",
        "//foundation/communication/interfaces/kits/wifi_lite/wifiservice",
        "//vendor/hisi/hi3861/hi3861/third_party/lwip_sack/include/",
    ]
}

2.3.4 定时检测WIFI连接状态

在timer文件中创建一个timer定时器,每3s检测一次wifi的连接状态

==注意==

不要在定时器的回调函数中检测状态,否则会报警告

可以在回调函数中设置标志位,在外部函数中进行检测

/* 定时器定时时间到标志 */
uint8_t timerOverFlag = 0;

/***** 定时器1 回调函数 *****/
void Timer1_Callback(void *arg)
{
    (void)arg;
    timerOverFlag = 1;   
}

在外部函数中检测,当wifi连接正常时,红灯亮,当wifi断开连接时,红灯灭。

/* WIFI连接 任务*/
static void UDPClientTask(void)
{  

    hi_wifi_status mywifi_status; 
    
    WifiConnect("653","123456789");
    while (1)
    {

        if(hi_wifi_sta_get_connect_info(&mywifi_status)== HISI_OK &&timerOverFlag == 1 )
        {
            
            timerOverFlag = 0;
            if(mywifi_status.status == HI_WIFI_DISCONNECTED)
            {
                RedLedOff(); 
            }
            else
            {   
                RedLedOn();
            }    
        } 
    }    
}

2.4 UDP通信(1230)

功能实现: demo5

2.4.1 UDP收发数据

  1. 创建socket,用于发送数据;
/* 创建socket */
    if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        perror("create socket failed!\r\n");
        exit(1);
    }
  1. 绑定SOCKER和地址,用于监听本地端口;
    /* 初始化模块自身监听的IP地址和端口,用于接收数据 */
    recv_addr.sin_family = AF_INET;
    recv_addr.sin_port = htons(_PROT_);
    recv_addr.sin_addr.s_addr = inet_addr("192.168.1.110");
    addr_rexv_length = sizeof(recv_addr);


    /* 监听模块自身的IP和PORT */
    if (bind(sock_fd, (struct sockaddr *)&recv_addr, sizeof(recv_addr)) == -1)
    {
        perror("bind is error\r\n");
        exit(1);
    }
  1. 发送UDP数据指令,发送一段字符串至服务器端
sendto(sock_fd, send_data, strlen(send_data), 0, (struct sockaddr *)&send_addr, addr_length);
  1. 接收服务端发过来的UDP数据,储存在recvBuf中;
recvfrom(sock_fd, recvBuf, sizeof(recvBuf), 0, (struct sockaddr *)&recv_addr, &addr_length_recv);

2.4.2 UDP十六进制通信

  1. 接收上位机UDP十六进制数组,储存在recvBuf中;
recvfrom(sock_fd, recvBuf, sizeof(recvBuf), 0, (struct sockaddr *)&recv_addr, &addr_length_recv);

网络调试助手按图中设置

1

  1. 根据数组中的十六进制数据,使用switch case 判断处理数据

当收到LEDON的数据时,绿灯亮,然后通过UDP发送ledon的字符数组

/* 接收服务端发送的字符串 */
        recvfrom(sock_fd, recvBuf, sizeof(recvBuf), 0, (struct sockaddr *)&recv_addr, &addr_rexv_length);

        /* 串口指令控制LED灯亮灭 */
        wifi_command = recvBuf[1];
	    wifi_command = (wifi_command <<8)  +  recvBuf[2];

        switch(wifi_command)
        {
            case LEDON:
                GreenLedOn();
                sendto(sock_fd, ledon, strlen(ledon), 0, (struct sockaddr *)&send_addr, addr_length);
                break;
            
            case LEDOFF:
                GreenLedOff();
                sendto(sock_fd, ledoff, strlen(ledoff), 0, (struct sockaddr *)&send_addr, addr_length);
                break;

            default:
                break;

        }

        /* 关闭这个 socket */
        /* 不关闭这个socket , 持续接收数据 */
        //closesocket(sock_fd);
  1. 接收完成后,不关闭socket,持续接收UDP数据

2.4.3 存在问题

在没有开启UDP通信之前,可以实现定时检测WIFI连接状态,断线自动重连,

目前将UDP通信功能和上述功能放置在一个任务的while中时,功能异常,后续需要进行修改。

2.5 串口配网

2.6 串口收到的数据通过UDP透传

串口1收到的串口信息原样,通过UDP透传出去。优先完成。

2.7 APP无线配网


3. 硬件测试电路板设计

为测试安信可Hi-12F模块功能,设计测试板进行测试

设计两路串口 一路用于串口通信,另一路用于下载程序

设计低功耗唤醒GPIO,WAKEUP引脚,使用此引脚唤醒模块进入工作模式。

1

4. 功能调试

4.1 测试电路板各部分功能

4.2 测试低功耗模式

4.3 与GD32进行串口功能测试

4.4 与GD32测试UDP通信

4.5 完善指令功能

5. 开发总结

1
https://gitee.com/tutusheep/hi3861--wifi-module.git
git@gitee.com:tutusheep/hi3861--wifi-module.git
tutusheep
hi3861--wifi-module
Hi3861_WIFI模块
master

搜索帮助