IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 潘多拉 IOT 开发板学习(RT-Thread)—— 实验16 WiFi 模块实验(学习笔记) -> 正文阅读

[嵌入式]潘多拉 IOT 开发板学习(RT-Thread)—— 实验16 WiFi 模块实验(学习笔记)

本文代码参考 RT-Thread 官方 BSP

实验功能

例程源码:(main 函数)

WiFi 扫描和连接的底层代码并未开源,所以这个实验只能用来学习它的代码框架。。。

int main(void)
{
    int result = RT_EOK;
    struct rt_wlan_info info;
    struct rt_wlan_scan_result *scan_result;

    /* 等待 500 ms 以便 wifi 完成初始化 */
    rt_hw_wlan_wait_init_done(500);

    /* 扫描热点 */
    LOG_D("start to scan ap ...");
    /* 执行同步扫描 */
    scan_result = rt_wlan_scan_sync();
    if (scan_result)
    {
        LOG_D("the scan is complete, results is as follows: ");
        /* 打印扫描结果 */
        print_scan_result(scan_result);
        /* 清除扫描结果 */
        rt_wlan_scan_result_clean();
    }
    else
    {
        LOG_E("not found ap information ");
        return -1;
    }

    /* 热点连接 */
    LOG_D("start to connect ap ...");
    rt_sem_init(&net_ready, "net_ready", 0, RT_IPC_FLAG_FIFO);

    /* 注册 wlan ready 回调函数 */
    rt_wlan_register_event_handler(RT_WLAN_EVT_READY, wlan_ready_handler, RT_NULL);
    /* 注册 wlan 断开回调函数 */
    rt_wlan_register_event_handler(RT_WLAN_EVT_STA_DISCONNECTED, wlan_station_disconnect_handler, RT_NULL);
    result = rt_wlan_connect(WLAN_SSID, WLAN_PASSWORD);
    if (result == RT_EOK)
    {
        rt_memset(&info, 0, sizeof(struct rt_wlan_info));
        /* 获取当前连接热点信息 */
        rt_wlan_get_info(&info);
        LOG_D("station information:");
        print_wlan_information(&info);
        /* 等待成功获取 IP */
        result = rt_sem_take(&net_ready, NET_READY_TIME_OUT);
        if (result == RT_EOK)
        {
            LOG_D("networking ready!");
            msh_exec("ifconfig", rt_strlen("ifconfig"));
        }
        else
        {
            LOG_D("wait ip got timeout!");
        }
        /* 回收资源 */
        rt_wlan_unregister_event_handler(RT_WLAN_EVT_READY);
        rt_sem_detach(&net_ready);
    }
    else
    {
        LOG_E("The AP(%s) is connect failed!", WLAN_SSID);
    }

    rt_thread_mdelay(5000);

    LOG_D("ready to disconect from ap ...");
    rt_wlan_disconnect();

    /* 自动连接 */
    LOG_D("start to autoconnect ...");
    /* 初始化自动连接配置 */
    wlan_autoconnect_init();
    /* 使能 wlan 自动连接 */
    rt_wlan_config_autoreconnect(RT_TRUE);

    return 0;
}

代码剖析

rt_hw_wlan_wait_init_done()

这只是一个判断 WiFi 模块是否初始化完成的函数。

/**
 * wait milliseconds for wifi low level initialize complete
 * 
 * time_ms: timeout in milliseconds
 */ 
int rt_hw_wlan_wait_init_done(rt_uint32_t time_ms)
{
    rt_uint32_t time_cnt = 0;

    /* wait wifi low level initialize complete */
    while (time_cnt <= (time_ms / 100))
    {
        time_cnt++;
        rt_thread_mdelay(100);
        if (rt_hw_wlan_get_initialize_status() == 1)
        {
            break;
        }
    }

    if (time_cnt > (time_ms / 100))
    {
        return -RT_ETIMEOUT;
    }

    return RT_EOK;
}

rt_hw_wlan_get_initialize_status()

/**
 * return:1 initialize done
 *        0 not initialize
 */
int rt_hw_wlan_get_initialize_status(void)
{
    return init_flag;
}

LOG_D()

本实验中,我们可以将 LOG_D() 视为 rt_kprintf()

#define dbg_log_line(lvl, color_n, fmt, ...)                \
    do                                                      \
    {                                                       \
        _DBG_LOG_HDR(lvl, color_n);                         \
        rt_kprintf(fmt, ##__VA_ARGS__);                     \
        _DBG_LOG_X_END;                                     \
    }                                                       \
    while (0)

LOG_D 是 RT-Thread 内核里的一个日志打印函数,详情可见:《RT-Thread 文档中心——ulog 日志》

RT-Thread 的日志 API 包括:

在这里插入图片描述

rt_wlan_scan_sync()

RT-Thread 的 wifi 同步扫描函数,位于 wlan_mgnt.c 文件中,

struct rt_wlan_scan_result *rt_wlan_scan_sync(void)
{
    struct rt_wlan_scan_result *result;

    /* Execute synchronous scan function */
    MGNT_LOCK();
    result = rt_wlan_scan_with_info(RT_NULL);
    MGNT_UNLOCK();
    return result;
}

rt_wlan_scan_with_info()

该函数可以根据指定的 WiFi 信息进行扫描,信息放在 struct rt_wlan_info 结构体成员中,

struct rt_wlan_scan_result *rt_wlan_scan_with_info(struct rt_wlan_info *info)
{
    rt_err_t err = RT_EOK;
    struct rt_wlan_complete_des *complete;
    rt_uint32_t set = 0, recved = 0;

    if (_sta_is_null())
    {
        return RT_NULL;
    }
    RT_WLAN_LOG_D("%s is run", __FUNCTION__);
    if (info != RT_NULL && info->ssid.len > RT_WLAN_SSID_MAX_LENGTH)
    {
        RT_WLAN_LOG_E("ssid is to long!");
        return RT_NULL;
    }

    /* Create an event that needs to wait. */
    MGNT_LOCK();
    complete = rt_wlan_complete_create("scan");
    if (complete == RT_NULL)
    {
        MGNT_UNLOCK();
        return &scan_result;
    }

    /* run scan */
    err = rt_wlan_dev_scan(STA_DEVICE(), info);
    if (err != RT_EOK)
    {
        rt_wlan_complete_delete(complete);
        RT_WLAN_LOG_E("scan sync fail");
        MGNT_UNLOCK();
        return RT_NULL;
    }

    /* Initializing events that need to wait */
    set |= 0x1 << RT_WLAN_DEV_EVT_SCAN_DONE;
    /* Check whether there is a waiting event */
    rt_wlan_complete_wait(complete, set, RT_WLAN_CONNECT_WAIT_MS, &recved);
    rt_wlan_complete_delete(complete);
    /* check event */
    set = 0x1 << RT_WLAN_DEV_EVT_SCAN_DONE;
    if (!(recved & set))
    {
        RT_WLAN_LOG_E("scan wait timeout!");
        MGNT_UNLOCK();
        return &scan_result;
    }

    MGNT_UNLOCK();
    return &scan_result;
}

rt_wlan_complete_create()

struct rt_wlan_complete_des 里面封装了一个 RT-Thread 事件(通信方式),该函数的作用是创建(初始化)一个 WiFi 事件的结构体变量,比如下面代码中创建了一个名为 “scan” 的 WiFi 事件。

static struct rt_wlan_complete_des *rt_wlan_complete_create(const char *name)
{
    struct rt_wlan_complete_des *complete;
    int i;

    complete = rt_malloc(sizeof(struct rt_wlan_complete_des));
    if (complete == RT_NULL)
    {
        RT_WLAN_LOG_E("complete event create failed");
        MGNT_UNLOCK();
        return complete;
    }
    rt_event_init(&complete->complete, name, RT_IPC_FLAG_FIFO);
    complete->event_flag = 0;
    //protect
    COMPLETE_LOCK();
    for (i = 0; i < sizeof(complete_tab) / sizeof(complete_tab[0]); i++)
    {
        if (complete_tab[i] == RT_NULL)
        {
            complete->index = i;
            complete_tab[i] = complete;
            break;
        }
    }
    COMPLETE_UNLOCK();

    if (i >= sizeof(complete_tab) / sizeof(complete_tab[0]))
    {
        rt_event_detach(&complete->complete);
        rt_free(complete);
        complete = RT_NULL;
    }

    return complete;
}

rt_wlan_dev_scan()

该函数最后会调用 rt_device_control() 函数,这是一个命令控制函数,可以根据命令参数的不同而执行不同的任务。

rt_err_t rt_wlan_dev_scan(struct rt_wlan_device *device, struct rt_wlan_info *info)
{
    struct rt_scan_info scan_info = { 0 };
    struct rt_scan_info *p_scan_info = RT_NULL;
    rt_err_t result = 0;

    if (device == RT_NULL)
    {
        return -RT_EIO;
    }

    if (info != RT_NULL)
    {
        if (info->ssid.len >= RT_WLAN_SSID_MAX_LENGTH)
        {
            LOG_E("L:%d ssid is to long", __LINE__);
            return -RT_EINVAL;
        }
        rt_memcpy(&scan_info.ssid, &info->ssid, sizeof(rt_wlan_ssid_t));
        rt_memcpy(scan_info.bssid, info->bssid, RT_WLAN_BSSID_MAX_LENGTH);
        scan_info.channel_min = -1;
        scan_info.channel_max = -1;
        p_scan_info = &scan_info;
    }
    result = rt_device_control(RT_DEVICE(device), RT_WLAN_CMD_SCAN, p_scan_info);
    return result;
}

在 wlan_dev.c 中,定义了设备驱动的函数操作集合(结构体):

#ifdef RT_USING_DEVICE_OPS
    wlan->device.ops = &wlan_ops;
#else
    wlan->device.init       = _rt_wlan_dev_init;
    wlan->device.open       = RT_NULL;
    wlan->device.close      = RT_NULL;
    wlan->device.read       = RT_NULL;
    wlan->device.write      = RT_NULL;
    wlan->device.control    = _rt_wlan_dev_control;
#endif

_rt_wlan_dev_control()

该函数太大,只贴出了一部分代码,当命令为 RT_WLAN_CMD_SCAN 时,会调用 wlan 设备的底层驱动 wlan_scan(),

static rt_err_t _rt_wlan_dev_control(rt_device_t dev, int cmd, void *args)
{
    struct rt_wlan_device *wlan = (struct rt_wlan_device *)dev;
    rt_err_t err = RT_EOK;

    RT_ASSERT(dev != RT_NULL);

    WLAN_DEV_LOCK(wlan);

    switch (cmd)
    {
    case RT_WLAN_CMD_MODE:
    {
        rt_wlan_mode_t mode = *((rt_wlan_mode_t *)args);

        LOG_D("%s %d cmd[%d]:%s  run......", __FUNCTION__, __LINE__, RT_WLAN_CMD_MODE, "RT_WLAN_CMD_MODE");
        if (wlan->ops->wlan_mode)
            err = wlan->ops->wlan_mode(wlan, mode);
        break;
    }
    case RT_WLAN_CMD_SCAN:
    {
        struct rt_scan_info *scan_info = args;

        LOG_D("%s %d cmd[%d]:%s  run......", __FUNCTION__, __LINE__, RT_WLAN_CMD_SCAN, "RT_WLAN_CMD_SCAN");
        if (wlan->ops->wlan_scan)
            err = wlan->ops->wlan_scan(wlan, scan_info);
        break;
    }
    ...
    ...
    ...

wlan->ops->wlan_scan(wlan, mode) 的具体实现应该是闭源的。

rt_wlan_connect()

WiFi 连接函数,主要进行也参数检查和事件初始化(连接和断开事件)。

rt_err_t rt_wlan_connect(const char *ssid, const char *password)
{
    rt_err_t err = RT_EOK;
    int ssid_len = 0;
    struct rt_wlan_info info;
    struct rt_wlan_complete_des *complete;
    rt_uint32_t set = 0, recved = 0;

    /* sta dev Can't be NULL */
    if (_sta_is_null())
    {
        return -RT_EIO;
    }
    RT_WLAN_LOG_D("%s is run ssid:%s password:%s", __FUNCTION__, ssid, password);
    if (ssid == RT_NULL)
    {
        RT_WLAN_LOG_E("ssid is null!");
        return -RT_EINVAL;
    }
    ssid_len = rt_strlen(ssid);
    if (ssid_len > RT_WLAN_SSID_MAX_LENGTH)
    {
        RT_WLAN_LOG_E("ssid is to long! ssid:%s len:%d", ssid, ssid_len);
        return -RT_EINVAL;
    }

    if ((rt_wlan_is_connected() == RT_TRUE) &&
            (rt_strcmp((char *)&_sta_mgnt.info.ssid.val[0], ssid) == 0))
    {
        RT_WLAN_LOG_I("wifi is connect ssid:%s", ssid);
        return RT_EOK;
    }
    /* get info from cache */
    INVALID_INFO(&info);
    MGNT_LOCK();
    if (rt_wlan_find_best_by_cache(ssid, &info) != RT_TRUE)
    {
        rt_wlan_scan_sync();
        rt_wlan_find_best_by_cache(ssid, &info);
        rt_wlan_scan_result_clean();
    }

    if (info.ssid.len <= 0)
    {
        RT_WLAN_LOG_W("not find ap! ssid:%s", ssid);
        MGNT_UNLOCK();
        return -RT_ERROR;
    }

    RT_WLAN_LOG_D("find best info ssid:%s mac: %02x %02x %02x %02x %02x %02x",
                  info.ssid.val, info.bssid[0], info.bssid[1], info.bssid[2], info.bssid[3], info.bssid[4], info.bssid[5]);

    /* create event wait complete */
    complete = rt_wlan_complete_create("join");
    if (complete == RT_NULL)
    {
        MGNT_UNLOCK();
        return -RT_ENOMEM;
    }
    /* run connect adv */
    err = rt_wlan_connect_adv(&info, password);
    if (err != RT_EOK)
    {
        rt_wlan_complete_delete(complete);
        MGNT_UNLOCK();
        return err;
    }

    /* Initializing events that need to wait */
    set |= 0x1 << RT_WLAN_DEV_EVT_CONNECT;
    set |= 0x1 << RT_WLAN_DEV_EVT_CONNECT_FAIL;
    /* Check whether there is a waiting event */
    rt_wlan_complete_wait(complete, set, RT_WLAN_CONNECT_WAIT_MS, &recved);
    rt_wlan_complete_delete(complete);
    /* check event */
    set = 0x1 << RT_WLAN_DEV_EVT_CONNECT;
    if (!(recved & set))
    {
        RT_WLAN_LOG_I("wifi connect failed!");
        MGNT_UNLOCK();
        return -RT_ERROR;
    }
    MGNT_UNLOCK();
    return err;
}

rt_wlan_connect_adv()

更底层的 WiFi 连接函数,首先进行了参数检查,然后判断 WiFi 热点是否已经连接(已连接就直接退出),保存热点名和密码,最后调用真正的 WiFi 连接函数 rt_wlan_dev_connect(),

rt_err_t rt_wlan_connect_adv(struct rt_wlan_info *info, const char *password)
{
    int password_len = 0;
    rt_err_t err = RT_EOK;

    if (_sta_is_null())
    {
        return -RT_EIO;
    }
    if (info == RT_NULL)
    {
        RT_WLAN_LOG_E("info is null!");
        return -RT_EINVAL;
    }
    RT_WLAN_LOG_D("%s is run ssid:%s password:%s", __FUNCTION__, info->ssid.val, password);
    /* Parameter checking */
    if (password != RT_NULL)
    {
        password_len = rt_strlen(password);
        if (password_len > RT_WLAN_PASSWORD_MAX_LENGTH)
        {
            RT_WLAN_LOG_E("password is to long! password:%s len:%d", password, password_len);
            return -RT_EINVAL;
        }
    }
    if (info->ssid.len == 0 || info->ssid.len > RT_WLAN_SSID_MAX_LENGTH)
    {
        RT_WLAN_LOG_E("ssid is zero or to long! ssid:%s len:%d", info->ssid.val, info->ssid.len);
        return -RT_EINVAL;
    }
    /* is connect ? */
    MGNT_LOCK();
    if (rt_wlan_is_connected())
    {
        if ((_sta_mgnt.info.ssid.len == info->ssid.len) &&
                (_sta_mgnt.key.len == password_len) &&
                (rt_memcmp(&_sta_mgnt.info.ssid.val[0], &info->ssid.val[0], info->ssid.len) == 0) &&
                (rt_memcmp(&_sta_mgnt.info.bssid[0], &info->bssid[0], RT_WLAN_BSSID_MAX_LENGTH) == 0) &&
                (rt_memcmp(&_sta_mgnt.key.val[0], password, password_len) == 0))
        {
            RT_WLAN_LOG_I("wifi Already Connected");
            MGNT_UNLOCK();
            return RT_EOK;
        }

        err = rt_wlan_disconnect();
        if (err != RT_EOK)
        {
            MGNT_UNLOCK();
            return err;
        }
    }

    /* save info */
    rt_enter_critical();
    _sta_mgnt.info = *info;
    rt_memcpy(&_sta_mgnt.key.val, password, password_len);
    _sta_mgnt.key.len = password_len;
    _sta_mgnt.key.val[password_len] = '\0';
    rt_exit_critical();
    /* run wifi connect */
    _sta_mgnt.state |= RT_WLAN_STATE_CONNECTING;
    err = rt_wlan_dev_connect(_sta_mgnt.device, info, password, password_len);
    if (err != RT_EOK)
    {
        rt_enter_critical();
        rt_memset(&_sta_mgnt.info, 0, sizeof(struct rt_wlan_ssid));
        rt_memset(&_sta_mgnt.key, 0, sizeof(struct rt_wlan_key));
        rt_exit_critical();
        _sta_mgnt.state &= ~RT_WLAN_STATE_CONNECTING;
        MGNT_UNLOCK();
        return err;
    }

    MGNT_UNLOCK();
    return err;
}

rt_wlan_dev_connect()

该函数在参数检查后,使用 rt_device_control() 来实现 WiFi 的连接,这个函数上文已经介绍,只是这里的命令换成了 RT_WLAN_CMD_JOIN,

rt_err_t rt_wlan_dev_connect(struct rt_wlan_device *device, struct rt_wlan_info *info, const char *password, int password_len)
{
    rt_err_t result = RT_EOK;
    struct rt_sta_info sta_info;

    if (device == RT_NULL)
    {
        return -RT_EIO;
    }
    if (info == RT_NULL)
    {
        return -RT_ERROR;
    }

    if ((password_len > RT_WLAN_PASSWORD_MAX_LENGTH) ||
            (info->ssid.len > RT_WLAN_SSID_MAX_LENGTH))
    {
        LOG_E("L:%d password or ssid is to long", __LINE__);
        return -RT_ERROR;
    }
    rt_memset(&sta_info, 0, sizeof(struct rt_sta_info));
    rt_memcpy(&sta_info.ssid, &info->ssid, sizeof(rt_wlan_ssid_t));
    rt_memcpy(sta_info.bssid, info->bssid, RT_WLAN_BSSID_MAX_LENGTH);
    if (password != RT_NULL)
    {
        rt_memcpy(sta_info.key.val, password, password_len);
        sta_info.key.len = password_len;
    }
    sta_info.channel = info->channel;
    sta_info.security = info->security;

    result = rt_device_control(RT_DEVICE(device), RT_WLAN_CMD_JOIN, &sta_info);
    return result;
}

和 WiFi 扫描一样,WiFi 连接的底层代码未开源。

    case RT_WLAN_CMD_JOIN:
    {
        struct rt_sta_info *sta_info = args;

        LOG_D("%s %d cmd[%d]:%s  run......", __FUNCTION__, __LINE__, RT_WLAN_CMD_JOIN, "RT_WLAN_CMD_JOIN");
        if (wlan->ops->wlan_join)
            err = wlan->ops->wlan_join(wlan, sta_info);
        break;
    }

rt_thread_mdelay()

这是 RT-Thread 的毫秒级延时函数,定义如下:

rt_err_t rt_thread_mdelay(rt_int32_t ms)
{
    rt_tick_t tick;

	// 获取需要的时钟节拍
    tick = rt_tick_from_millisecond(ms);
	
	// 阻塞相应的节拍时间
    return rt_thread_sleep(tick);
}

rt_tick_from_millisecond()


/**
 * 算出 ms 对应的时钟节拍数
 * 
 *
 * @param ms the specified millisecond
 *           - Negative Number wait forever
 *           - Zero not wait
 *           - Max 0x7fffffff
 *
 * @return the calculated tick
 */
rt_tick_t rt_tick_from_millisecond(rt_int32_t ms)
{
    rt_tick_t tick;

    if (ms < 0)
    {
        tick = (rt_tick_t)RT_WAITING_FOREVER;  // -1 
    }
    else
    {
    	// 将“每秒节拍数” / 1000 * ms,算出对应的秒节拍数
        tick = RT_TICK_PER_SECOND * (ms / 1000);
		
		// 加上小于 1000ms 部分的节拍数
        tick += (RT_TICK_PER_SECOND * (ms % 1000) + 999) / 1000;
    }
    
    /* return the calculated tick */
    return tick;
}
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-07-21 21:42:18  更:2022-07-21 21:44:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 22:25:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码