【平头哥RVB2601创意应用开发】使用体验04 -- Wi-Fi联网
[复制链接]
RVB2601板载联盛德W800 Wi-Fi模块,CH2601通过SPI接口,以AT命令方式进行驱动,本篇记录使能Wi-Fi联网的过程。
图4-1 W800原理图
1、YoC的W800驱动实现分析
作为一款集CPU架构、芯片平台、操作系统、云服务和开发套件为一体的物联网领域平台,YoC的体系还真是不一般得复杂。出于习惯,本人对W800的驱动实现做了分析,依靠IDE那略有“勉强”的代码追踪能力,配合在线文档,得出本节结论,若有不足敬请大家指出。
先叙述一条“有坑”的分析路线:
1)Wi-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))
2)Wi-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));
}
2)w800注册为任务
上述代码首先是调用了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;
}
3)spi通道模型
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()。这个函数也在同文件中,初始化了SPI0(PA16 - SCK,PA17 - MOSI,PA18 - MISO)。
图4-3 at_spi_init()部分代码截图
2、W800使用
依据上一节的第二种分析路线,利用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启动后输出效果
|