[RVB2601 Creative Application Development] User Experience 04 -- Wi-Fi Networking
[Copy link]
RVB2601 is equipped with Lianshengde W800 Wi-Fi module. CH2601 is driven by AT commands through SPI interface . This article records the process of enabling Wi-Fi networking.
Figure 4-1 W800 schematic diagram
1. Analysis of YoC 's W800 driver implementation
As an IoT platform that integrates CPU architecture, chip platform, operating system, cloud service and development kit, YoC 's system is really unusually complex. Out of habit, I analyzed the driver implementation of W800 , relying on the slightly "reluctant" code tracking ability of IDE and the online documentation to draw the conclusions of this section. If there are any deficiencies, please point them out.
Let me first describe a "pitted" analysis route:
1 ) Wi-Fi initialization interface
First, refer to the YoC online document - WIFI driver porting page ( 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 ).
Figure 4-2 Online document screenshot - Wi-Fi initialization function
So I searched for " hal_wifi_init " in CDK and got the function's definition location " ../aos/v7.4.3/src/devices/wifi.c ". The code is as follows.
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;
}
The macro " WIFI_DRIVER " used in the above code is also defined in the same source file, located in line 13. The code is as follows. You can see that the macro is used to convert pointer types.
#define WIFI_DRIVER(dev) ((wifi_driver_t *)(((netdev_driver_t *)dev->drv)->link_ops))
2 ) Wi-Fi device model
YoC also uses device models for peripheral management. The W800 device model in the RVB2601 SDK is defined in " ../drv_wifi_at_w800/v7.4.3/w800_devops.c ". There are four consecutive static structure variables defined in the relevant position. Look at the code and the Wi-Fi startup finally calls the w800_init() function.
//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,
};
Unfortunately, let's look at the w800_init() function again, which is also in the w800_devops.c file. The code is as follows.
//by author. 分析了个“寂寞”,“大坑”出没儿……
int w800_init(aos_dev_t *dev)
{
return 0;
}
Based on the above analysis, I personally think that RVB2601 at least in the Wi-Fi interface driver should not implement the YoC platform WiFi driver framework interface described in the chip docking chapter , but adapt through the network manager interface. Next, take a trip from the application code analysis route in the netmgr component, this time the correct route:
1 ) Start with the application code
/*
* 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);
The application code registers the Wi-Fi device by calling the wifi_w800_register() function, which is also in the w800_devops.c file, just below the four structure variable definitions mentioned above. The wifi_w800_register() function is at the bottom of the source file, and the code is as follows.
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 is registered as a task
The above code first calls w800_module_init() to register the module, and the corresponding interface driver initialization is also done here, and then calls driver_register() to register the w800 driver model. The definition of w800_module_init() is located in " w800_api.c " of the same pack , and the code is as follows.
//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 channel model
The spi channel used by w800 is defined as the data structure " at_channel_t spi_channel ", which is defined at the bottom of " w800_at_port.c " in the same pack .
at_channel_t spi_channel = {
.init = at_spi_init,
.set_event = at_spi_set_event,
.send = at_spi_send,
.recv = at_spi_recv,
};
The member init is assigned the spi interface initialization function at_spi_init() . This function also initializes SPI0 ( PA16 - SCK , PA17 - MOSI , PA18 - MISO ) in the same file.
Figure 4-3 Screenshot of some code of at_spi_init()
2. Use of W800
According to the second analysis route in the previous section, the netmgr component is used to register and initialize the Wi-Fi module in the application and connect to the AP , as follows.
The case application is based on the " Hello World Demo " extension. The header file link needs to be added in init.c , and the event service needs to be initialized in the board_yoc_init() function to handle Wi-Fi networking events. The Wi-Fi initialization function and event callback are also defined .
#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);
}
}
In addition, in the board_cli_init() function of " cli_cmd.c " , add two console commands " ping " and " 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();
}
Figure 4-4 Output effect after Wi-Fi is enabled
|