请选择 进入手机版 | 继续访问电脑版
论坛
登录 | 立即注册 设为首页收藏本站 切换到宽版
查看: 119|回复: 2

【ESP8266】【RTOS3.0】【IDF】【IWIP】【netconn】关于UDP发送错误

[复制链接]

1

主题

12

帖子

66

积分

注册会员

Rank: 2

积分
66
发表于 2019-1-10 19:11:02 | 显示全部楼层 |阅读模式
本帖最后由 abcrazy 于 2019-1-16 14:50 编辑

学习IWIP后,百度上说,netconn 函数比用 socket 函数更省内存,netconn可以不用拷贝数据,socket发送和接收都需要拷贝数据。所以,尝试了使用 netconn 。可是,发现接收没问题,发送却会引起重启,错误提示如下:
udp_sync: UDP sync register error, socket is -1

百度寻找没有结果,只有自己解决。


出错文件:
C:\ESP8266_RTOS_SDK-3.0\components\lwip\port\esp8266\freertos\udp_sync.c
第75行,所在函数:

/*
* @brief register a UDP API message(struct api_msg) to module
*/
void udp_sync_regitser(void *in_msg)
{
    s_cur_msg = in_msg;

    struct api_msg *msg = (struct api_msg *)in_msg;
    int s = msg->conn->socket;

    if (s < 0 || s >= UDP_SYNC_MAX) {
        ESP_LOGE(TAG, "UDP sync register error, socket is %d", s);
    } else if (s_udp_syncs.msg) {
        ESP_LOGE(TAG, "UDP sync register error, msg is %p", s_udp_syncs.msg);
    }
    s_udp_sync_num++;
    s_udp_sync
s.ret = ERR_OK;
    s_udp_sync
s.retry = 0;
    s_udp_sync
s.msg = msg;
}



于是在控制台“make menuconfig” 设置:
Component config  --->  LWIP  --->  SOCKET  --->  [ *] LWIP socket UDP sync send
改为:
Component config  --->  LWIP  --->  SOCKET  --->  [ ] LWIP socket UDP sync send

重新编译,出现新的错误:
文件
C:\ESP8266_RTOS_SDK-3.0\components\lwip\port\esp8266\netif\ethernetif.c
第211行,链接不到函数:udp_sync_trigger();

所在函数:
/*
* @brief LWIP low-level AI/O sending callback function, it is to free pbuf
*
* @param aio AI/O control block pointer
*
* @return 0 meaning successs
*/
static int low_level_send_cb(esp_aio_t *aio)
{
    struct pbuf *pbuf = aio->arg;

    pbuf_free(pbuf);
    udp_sync_trigger();
    return 0;
}



此函数调用的是:
/*
* @brief trigger a UDP sync process
*/
void udp_sync_trigger(void)
{
    if (!s_udp_sync_num)
        return ;


    tcpip_callback_with_block((tcpip_callback_fn)udp_sync_trigger_null, NULL, 0);
}


看代码意思是设置回调函数的样子,深究这个回调函数,如下:
/*
* @brief NULL function and just as sync message
*/
static void udp_sync_trigger_null(void *p)
{


}

一片空白,啥都没有,看来乐鑫官方有头没尾,既然没内容,又把函数先设置了。
直接把
udp_sync_trigger();
函数注释掉,重新编译,没有出错,UDP发送正常。




分析错误原因:
原来,乐鑫默认开启UDP同步文件 udp_sync.c ,正常调用 sockets.c 文件里面的函数时,没有错误。因为就在调用 int lwip_socket(int domain, int type, int protocol); 函数时,内部自动创建 sockets 结构体列表 和 新建 netconn 结构体,返回的是 sockets 结构体列表的索引号,而UDP同步正需要这个索引号才能正常工作。我们在外部的函数,即使得到了索引号,也无法获取获得索引号对应的 netconn 结构体指针,这个指针是发送数据所必需的,所以只能把UDP同步关掉了。


回复

使用道具 举报

1

主题

12

帖子

66

积分

注册会员

Rank: 2

积分
66
 楼主| 发表于 2019-1-10 19:29:03 | 显示全部楼层
本帖最后由 abcrazy 于 2019-1-14 22:36 编辑

netconn  发送UDP调用函数流程:格式:
源文件  所在函数   调用下一个函数
下一个函数源文件  所在函数  调用下一个函数
...
以此类推


api_lib.c                err_t netconn_sendto(struct netconn *conn, struct netbuf *buf, const ip_addr_t *addr, u16_t port)
return netconn_send(conn, buf);


api_lib.c                err_t netconn_send(struct netconn *conn, struct netbuf *buf)
err = netconn_apimsg(lwip_netconn_do_send, &API_MSG_VAR_REF(msg));


api_msg.c                void lwip_netconn_do_send(void *m)
msg->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p);


udp.c        err_t udp_send(struct udp_pcb *pcb, struct pbuf *p)
return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port);


udp.c        err_t udp_sendto(struct udp_pcb *pcb, struct pbuf *p,  const ip_addr_t *dst_ip, u16_t dst_port)
return udp_sendto_if(pcb, p, dst_ip, dst_port, netif);


udp.c        err_t udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p,  const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif)
return udp_sendto_if_src(pcb, p, dst_ip, dst_port, netif, src_ip);


udp.c        udp_sendto_if_src(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif, const ip_addr_t *src_ip)
err = ip_output_if_src(q, src_ip, dst_ip, ttl, pcb->tos, ip_proto, netif);


ip4.c        ip4_output_if_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, u8_t ttl, u8_t tos, u8_t proto, struct netif *netif)
return netif->output(netif, p, dest);


ethernetif.c        int8_t ethernetif_init(struct netif* netif)
netif->output = etharp_output;


eharp.c                err_t etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr)
return ethernet_output(netif, q, (struct eth_addr*)(netif->hwaddr), dest, ETHTYPE_IP);


ethernet.c                err_t ethernet_output(struct netif* netif, struct pbuf* p, const struct eth_addr* src, const struct eth_addr* dst, u16_t eth_type)
return netif->linkoutput(netif, p);


ethernetif.c        int8_t ethernetif_init(struct netif* netif)
netif->linkoutput = low_level_output;


ethernetif.c        int8_t low_level_output(struct netif* netif, struct pbuf* p)
err = esp_aio_sendto(&aio, NULL, 0);


esp_socket.c        int esp_aio_sendto(esp_aio_t *aio, const struct sockaddr_ll *to, socklen_t len)


回复

使用道具 举报

1

主题

12

帖子

66

积分

注册会员

Rank: 2

积分
66
 楼主| 发表于 2019-1-10 19:35:55 | 显示全部楼层
本帖最后由 abcrazy 于 2019-1-17 21:33 编辑

UDP接收时调用的函数:
格式:文件名        函数名        调用语句
esp_wifi.c                        esp_err_t esp_wifi_init(const wifi_init_config_t *config)
esp_event_set_default_wifi_handlers();

event_default_handlers.c        void esp_event_set_default_wifi_handlers()
default_event_handlers[SYSTEM_EVENT_STA_START]        = system_event_sta_start_handle_default;

event_default_handlers.c        esp_err_t system_event_sta_start_handle_default(system_event_t *event)
tcpip_adapter_start(TCPIP_ADAPTER_IF_STA, sta_mac, &sta_ip);

tcpip_adapter_lwip.c                esp_err_t tcpip_adapter_start(tcpip_adapter_if_t tcpip_if, uint8_t *mac, tcpip_adapter_ip_info_t *ip_info)
netif_add(esp_netif[tcpip_if], &ip_info->ip, &ip_info->netmask, &ip_info->gw, (void *)s, ethernetif_init, tcpip_input);

netif.c                        struct netif *netif_add(struct netif *netif,const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,void *state, netif_init_fn init, netif_input_fn input)
netif->input = input;

-----------以上是初始化时设置 netif->input(p, netif) 调用函数,必须找出来,因为有好几个相同功能的函数,以便知道 ethernetif.c 文件中调用哪个回调函数-----------
以下是 ESP8266 内部接收到数据后,与 LWIP 的接口调用流程:

event_loop.c                esp_err_t esp_event_loop_init(system_event_cb_t cb, void *ctx)
if(wifi_task_create(esp_event_loop_task, "esp_event_loop_task", EVENT_LOOP_STACKSIZE, NULL, wifi_task_get_max_priority() - 5) == NULL) {

event_loop.c                static void esp_event_loop_task(void *pvParameters)
esp_err_t ret = esp_event_process_default(&evt);

event_default_handlers.c        esp_err_t esp_event_process_default(system_event_t *event)
if (default_event_handlers[event->event_id] != NULL) {

event_default_handlers.c        esp_err_t system_event_sta_start_handle_default(system_event_t *event)
tcpip_adapter_start(TCPIP_ADAPTER_IF_STA, sta_mac, &sta_ip);

tcpip_adapter_lwip.c                esp_err_t tcpip_adapter_start(tcpip_adapter_if_t tcpip_if, uint8_t *mac, tcpip_adapter_ip_info_t *ip_info)
s = tcpip_adapter_bind_netcard(netcard_name, esp_netif[tcpip_if]);

tcpip_adapter_lwip.c                static int tcpip_adapter_bind_netcard(const char *name, struct netif *netif)
ret = esp_aio_event(s, ESP_SOCKET_RECV_EVENT, tcpip_adapter_recv_cb, netif);

tcpip_adapter_lwip.c                static int tcpip_adapter_recv_cb(struct esp_aio *aio)
ethernetif_input(netif, pbuf);

ethernetif.c                void ethernetif_input(struct netif* netif, struct pbuf* p)
if (netif->input(p, netif) != ERR_OK) {
上面已经找出回调函数其实是调用  err_t tcpip_input(struct pbuf *p, struct netif *inp) 函数

tcpip.c                        err_t tcpip_input(struct pbuf *p, struct netif *inp)
return tcpip_inpkt(p, inp, ethernet_input);

tcpip.c                        err_t tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
ret = input_fn(p, inp);

ethernet.c                        err_t ethernet_input(struct pbuf *p, struct netif *netif)
ip4_input(p, netif);

ip4.c                        err_t ip4_input(struct pbuf *p, struct netif *inp)
udp_input(p, inp);

udp.c                        void udp_input(struct pbuf *p, struct netif *inp)
pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src);

--------------这里好像到底部了,看看上层应用谁设置了 pcb->recv 回调函数----------------
api.h
#define netconn_new(t)                  netconn_new_with_proto_and_callback(t, 0, NULL)
#define netconn_new_with_callback(t, c) netconn_new_with_proto_and_callback(t, 0, c)

api_lib.c                        struct netconn* netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback)
err = netconn_apimsg(lwip_netconn_do_newconn, &API_MSG_VAR_REF(msg));

api_msg.c                        void lwip_netconn_do_newconn(void *m)
  if (msg->conn->pcb.tcp == NULL) {
    pcb_new(msg);
  }
注意:这里的意思是 TCP 回调函数不是在这条线上设置的

api_msg.c                        static void pcb_new(struct api_msg *msg)
udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
注意:这里默认将 recv_udp 这个函数,设为回调函数。我们无权设置回调函数。

udp.c                        void udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg)
pcb->recv = recv;

--------------知道回调函数后,看看这个回调函数如何返回 pbuf 给我们的----------------
api_msg.c                        static void recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
buf->p = p;
if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
    netbuf_delete(buf);
这里得知从内存池申请一个 pbuf 然后丢给了一个函数处理

sys_arch.c                        err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
if (xQueueSend(*mbox, &msg, (portTickType)0) == pdPASS) {
这里是 freeRTOS 的消息队列,第一个参数是队列句柄,第二个是内容,第三个是等待时间0立即返回

--------------再看 netconn 接收函数是否一样有一个 freeRTOS 消息队列等待任务--------------
api_lib.c                        err_t netconn_recv(struct netconn *conn, struct netbuf **new_buf)
err = netconn_recv_data(conn, (void **)&p);

api_lib.c                        static err_t netconn_recv_data(struct netconn *conn, void **new_buf)
if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {

sys_arch.c                u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)        
if (pdTRUE == xQueueReceive(*mbox, &(*msg), timeout / portTICK_RATE_MS)) {
这里是 freeRTOS 的消息队列,第一个参数是队列句柄,第二个是内容,第三个是等待时间,如何 timeout=0 ,等待时间会取最大值

回复

使用道具 举报

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

本版积分规则

手机版|小黑屋|安信可论坛    

GMT+8, 2019-1-22 05:37 , Processed in 0.027226 second(s), 14 queries , Redis On.

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表