1887|4

155

帖子

1

TA的资源

一粒金砂(高级)

楼主
 

【平头哥RVB2601创意应用开发】使用体验04 -- Wi-Fi联网 [复制链接]

 

      RVB2601板载联盛德W800 Wi-Fi模块,CH2601通过SPI接口,以AT命令方式进行驱动,本篇记录使能Wi-Fi联网的过程。

 

4-1 W800原理图

 

1YoCW800驱动实现分析

作为一款集CPU架构、芯片平台、操作系统、云服务和开发套件为一体的物联网领域平台,YoC的体系还真是不一般得复杂。出于习惯,本人对W800的驱动实现做了分析,依靠IDE那略有“勉强”的代码追踪能力,配合在线文档,得出本节结论,若有不足敬请大家指出。

先叙述一条“有坑”的分析路线:

1Wi-Fi初始化接口

首先,参考YoC在线文档——WIFI驱动移植页(https://yoc.docs.t-head.cn/yocbook/Chapter8-%E8%8A%AF%E7%89%87%E5%AF%B9%E6%8E%A5/WIFI%E9%A9%B1%E5%8A%A8%E7%A7%BB%E6%A4%8D.html)。

 

4-2 在线文档截图——Wi-Fi初始化函数

 

于是CDK中搜索“hal_wifi_init”,得到函数的定义位置“../aos/v7.4.3/src/devices/wifi.c”,代码如下。

 

int hal_wifi_init(aos_dev_t *dev)
{
    int ret;

    WIFI_VALID(dev);

    device_lock(dev);
    ret = WIFI_DRIVER(dev)->init(dev); //by author. WIFI_DRIVER是参数宏
    device_unlock(dev);

    return ret;
}

 

上述代码使用的宏WIFI_DRIVER”也定义在同一源文件,位于第13行,代码如下。可以看到宏就是进行指针类型转换。

 

#define WIFI_DRIVER(dev)  ((wifi_driver_t *)(((netdev_driver_t *)dev->drv)->link_ops))

 

2Wi-Fi设备模型

YoC中也是利用设备模型进行外设管理,RVB2601 SDK中的W800设备模型定义在“../drv_wifi_at_w800/v7.4.3/w800_devops.c”。相关位置有连续四个静态结构体变量定义,看代码Wi-Fi启动最终调用w800_init()函数。

 

//by author. 网络操作驱动模型
static net_ops_t w800_net_driver = {
    .set_mac_addr   = w800_set_mac_addr,
    .start_dhcp     = NULL,/*w800_start_dhcp*/
    .stop_dhcp      = NULL,/*w800_stop_dhcp*/
    .set_ipaddr     = w800_set_ipaddr,
    .get_ipaddr     = w800_get_ipaddr,
    .get_mac_addr   = w800_get_mac_addr,
    .set_dns_server = w800_set_dns_server,
    .get_dns_server = w800_get_dns_server,
    .ping           = w800_ping_remote,
    .subscribe      = w800_subscribe,
};
//by author. W800驱动模型
static wifi_driver_t w800_wifi_driver = {
    .init                = w800_init,   //by author. 分析到的启动接口,但是有坑
    .deinit              = w800_deinit,
    .start               = w800_start,
    .stop                = w800_stop,
    .reset               = w800_reset,
    .set_mode            = w800_set_mode,
    .get_mode            = w800_get_mode,
    .sta_get_link_status = w800_drv_get_link_status,
    .set_smartcfg        = w800_smartconfig,
};
//by author. SAL接口模型——适配LWIP等协议栈
static sal_op_t w800_sal_driver = {
    .version                        = "1.0.0",
    .init                           = w800_wifi_module_init,
    .start                          = w800_wifi_module_conn_start,
    .send                           = w800_wifi_module_send,
    .domain_to_ip                   = w800_wifi_module_domain_to_ip,
    .close                          = w800_wifi_module_conn_close,
    .deinit                         = w800_wifi_module_deinit,
    .register_netconn_data_input_cb = w800_wifi_packet_input_cb_register,
    .register_netconn_close_cb      = w800_wifi_close_cb_register,
};
//by author. W800设备模型
static netdev_driver_t w800_driver = {
    .drv =
    {
        .name   = "wifi",
        .init   = w800_dev_init,
        .uninit = w800_dev_uninit,
        .open   = w800_dev_open,
        .close  = w800_dev_close,
    },
    .link_type = NETDEV_TYPE_WIFI,
    .net_ops  = &w800_net_driver,
    .link_ops  = &w800_wifi_driver,
};

 

可惜,我们再来看w800_init()函数,也在w800_devops.c文件,代码如下。

 

//by author. 分析了个“寂寞”,“大坑”出没儿……
int w800_init(aos_dev_t *dev)
{
    return 0;
}

 

如上分析,个人判断RVB2601至少在Wi-Fi接口驱动中应该没有实现芯片对接章节说明的YoC平台WiFi驱动框架接口,而是通过网络管理器接口进行适配。接着,再从netmgr组件中的应用代码分析路线走一趟,这回是正确的路线:

1)应用代码入手

 

	/*
	 * by AITA Mr.Fei
	 * w800 interface struct
	 */
    w800_wifi_param_t w800_param;
    w800_param.reset_pin      = PA21;
    w800_param.baud           = 1*1000000;
    w800_param.cs_pin         = PA15;
    w800_param.wakeup_pin     = PA25;
    w800_param.int_pin        = PA22;
    w800_param.channel_id     = 0;
    w800_param.buffer_size    = 4*1024;
	/*
	 * by AITA Mr.Fei
	 * from w800_devops.c(drv_wifi_at_w800 pack),
	 * include w800_module_init(but there is no task, so create a new one) & driver_register.
	 */
    wifi_w800_register(NULL, &w800_param);

    

应用代码中注册Wi-Fi设备,调用了wifi_w800_register()函数,它也在w800_devops.c文件,就位于上面提到的四个结构体变量定义之下。wifi_w800_register()函数位于源文件的最底部,代码如下。

 

void wifi_w800_register(utask_t *task, w800_wifi_param_t *param)
{
    int ret = w800_module_init(task, param);

    if (ret < 0) {
        LOGE(TAG, "driver init error");
        return;
    }

    //run w800_dev_init to create wifi_dev_t and bind this driver
    ret = driver_register(&w800_driver.drv, NULL, 0);

    if (ret < 0) {
        LOGE(TAG, "device register error");
    }

    memcpy(&w800_param, param, sizeof(w800_wifi_param_t));
}

 

2w800注册为任务

上述代码首先是调用了w800_module_init()进行模块注册,相应的接口驱动初始化也是这里做的,接着又调用driver_register()进行w800驱动模型注册。w800_module_init()定义位于同pack的“w800_api.c”,代码如下。

 

//by author. w800使用的spi通道数据结构
extern at_channel_t spi_channel;
int w800_module_init(utask_t *task, w800_wifi_param_t *param)
{
    if (w800_module_inited) {
        return 0;
    }
//by author. 创建w800任务
    if (task == NULL) {
        task = utask_new("w800", 7 * 1024, QUEUE_MSG_COUNT, AOS_DEFAULT_APP_PRI + 4);
    }
    if (task == NULL) {
        return -1;
    }

//by author. 初始化SPI的软复位管脚
    if (param->reset_pin) {
        csi_gpio_pin_init(&g_reset_pin, param->reset_pin);
        csi_gpio_pin_mode(&g_reset_pin,GPIO_MODE_PULLUP);
        csi_gpio_pin_dir(&g_reset_pin,GPIO_DIRECTION_OUTPUT);
        csi_gpio_pin_write(&g_reset_pin, GPIO_PIN_LOW);
        aos_msleep(200);
        csi_gpio_pin_write(&g_reset_pin, GPIO_PIN_HIGH);
        aos_msleep(1000);
        LOGD(TAG, "hard reset");
    }
//by author. 启动spi通道,并绑定为系统任务
    g_atparser_uservice_t = atparser_channel_init(task, NULL, param, &spi_channel);
    aos_mutex_new(&g_cmd_mutex);
//by author. 貌似注册微服务,用于w800的事件绑定,没有做追踪不确定
    atparser_debug_control(g_atparser_uservice_t, 1);
    atparser_oob_create(g_atparser_uservice_t, "2,CLOSED", _closed2_handler, NULL);
    atparser_oob_create(g_atparser_uservice_t, "1,CLOSED", _closed1_handler, NULL);
    atparser_oob_create(g_atparser_uservice_t, "0,CLOSED", _closed0_handler, NULL);

    atparser_oob_create(g_atparser_uservice_t, "+EVENT=NET,LINK_UP", _gotip_handler, NULL);
    atparser_oob_create(g_atparser_uservice_t, "+EVENT=NET,LINK_DOWN", _disconnect_handler, NULL);
    atparser_oob_create(g_atparser_uservice_t, "+EVENT=RECV,", _recv_data_handler, NULL);
    atparser_oob_create(g_atparser_uservice_t, "+EVENT=DISCONNECT,0", _closed0_handler, NULL);
    atparser_oob_create(g_atparser_uservice_t, "+EVENT=DISCONNECT,1", _closed1_handler, NULL);
    atparser_oob_create(g_atparser_uservice_t, "+EVENT=DISCONNECT,2", _closed2_handler, NULL);

    w800_module_inited = 1;
    return 0;
}

 

3spi通道模型

w800使用的spi通道,被定义为数据结构“at_channel_t spi_channel”,它定义在同pack的“w800_at_port.c”的最底部。

 

at_channel_t spi_channel = {
    .init       = at_spi_init,
    .set_event  = at_spi_set_event,
    .send       = at_spi_send,
    .recv       = at_spi_recv,
};

 

其中成员init,赋值为spi接口初始化函数at_spi_init()。这个函数也在同文件中,初始化了SPI0PA16 - SCKPA17 - MOSIPA18 - MISO)。

 

4-3 at_spi_init()部分代码截图

 

2W800使用

依据上一节的第二种分析路线,利用netmgr组件在应用程序中进行Wi-Fi模块注册即初始化,并连接AP,具体如下。

案例应用基于Hello World Demo”扩展而来,init.c中需要增加头文件链接,并在board_yoc_init()函数中需要初始化事件服务以处理Wi-Fi联网事件,另定义Wi-Fi初始化函数及事件回调。

 

#include <yoc/netmgr.h>
#include <devices/devicelist.h>
#include <devices/w800.h>
#include <uservice/eventid.h>

void board_yoc_init()
{
    board_init();
	event_service_init(NULL); //initialize event service for network events
    console_init(CONSOLE_UART_IDX, 115200, 128);
    ulog_init();
    aos_set_log_level(AOS_LL_DEBUG);    
    LOGI(TAG, "Build:%s,%s\n",__DATE__, __TIME__);
	
    /* load partition & init kv function */
	aita_InitKV();
	
	/* set cycle timer for polling job */
	aita_InitTimer();
	
	/* initialize wifi network */
	aita_InitNetwork();

    board_cli_init();
}

//netmgr_hdl_t actually is void *
netmgr_hdl_t app_netmgr_hdl; //net manager handler for w800
//network event callback function
static void network_event(uint32_t event_id, const void *param, void *context) {
    switch(event_id) {
		case EVENT_NETMGR_GOT_IP: {
			LOGD(TAG, "EVENT_NETMGR_GOT_IP\n");
			break;
		}
		case EVENT_NETMGR_NET_DISCON: {
			LOGD(TAG, "EVENT_NETMGR_NET_DISCON\n");
			netmgr_reset(app_netmgr_hdl, 30);
			break;
		}	
	}
}
//network init: open w800, 
void aita_InitNetwork(void) {
	/*
	 * by AITA Mr.Fei
	 * w800 interface struct
	 */
    w800_wifi_param_t w800_param;
    w800_param.reset_pin      = PA21;
    w800_param.baud           = 1*1000000;
    w800_param.cs_pin         = PA15;
    w800_param.wakeup_pin     = PA25;
    w800_param.int_pin        = PA22;
    w800_param.channel_id     = 0;
    w800_param.buffer_size    = 4*1024;
	/*
	 * by AITA Mr.Fei
	 * from w800_devops.c(drv_wifi_at_w800 pack),
	 * include w800_module_init(but there is no task, so create a new one) & driver_register.
	 */
    wifi_w800_register(NULL, &w800_param);
	/*
	 * by AITA Mr.Fei
	 * register uservice for wifi, 
	 * init netmgr_uservice struct then return the pointer
	 */
    app_netmgr_hdl = netmgr_dev_wifi_init();
    if(app_netmgr_hdl) {
		//init uservice for wifi
        utask_t *task = utask_new("netmgr", 2 * 1024, QUEUE_MSG_COUNT, AOS_DEFAULT_APP_PRI);
        netmgr_service_init(task);
		//join AP, WIFI_SSID & WIFI_PSW both are Macro
        netmgr_config_wifi(app_netmgr_hdl, WIFI_SSID, 10, WIFI_PSW, 10);
		//init w800 AT parser
		w800_module_init(task, &w800_param);
		//register input event callback for w800 module
//		w800_packet_input_cb_register(&w800_in);
		//start uservice
        netmgr_start(app_netmgr_hdl);
		//subscribe events which is managed by netmgr uservice
        event_subscribe(EVENT_NETMGR_GOT_IP, network_event, NULL);
        event_subscribe(EVENT_NETMGR_NET_DISCON, network_event, NULL);
    }
}

 

另外,在cli_cmd.c”的board_cli_init()函数中,增加两个控制台命令“ping”和“ifconfig”。

 

void board_cli_init()
{
    aos_cli_init();

    extern void cli_reg_cmd_ps(void);
    cli_reg_cmd_ps();

    extern void cli_reg_cmd_free(void);
    cli_reg_cmd_free();
	
	extern void cli_reg_cmd_ping(void);
	cli_reg_cmd_ping();
	
	extern void cli_reg_cmd_ifconfig(void);
    cli_reg_cmd_ifconfig();
}

 

4-4 Wi-Fi启动后输出效果

最新回复

对了你的那个aita_InitTimer可以贴出来学习一下吗?   详情 回复 发表于 2022-4-9 20:47
点赞 关注
 
 

回复
举报

6960

帖子

11

TA的资源

版主

沙发
 
楼主的分享很受用,感谢,拜读你的文章后,我去看了一下w800_api收获挺多,谢谢!期待你更精彩的分享。

点评

谢谢关注  详情 回复 发表于 2022-4-9 20:43
 
 
 

回复

155

帖子

1

TA的资源

一粒金砂(高级)

板凳
 
lugl4313820 发表于 2022-4-9 18:50 楼主的分享很受用,感谢,拜读你的文章后,我去看了一下w800_api收获挺多,谢谢!期待你更精彩的分享。

谢谢关注

点评

对了你的那个aita_InitTimer可以贴出来学习一下吗?  详情 回复 发表于 2022-4-9 20:47
 
 
 

回复

6960

帖子

11

TA的资源

版主

4
 

对了你的那个aita_InitTimer可以贴出来学习一下吗?

 
 
 

回复

155

帖子

1

TA的资源

一粒金砂(高级)

5
 
lugl4313820 发表于 2022-4-9 20:47 对了你的那个aita_InitTimer可以贴出来学习一下吗?

https://bbs.eeworld.com.cn/thread-1198907-1-1.html

 

暂时就做到这个程度,我的第03篇

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/9 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表