[技术讨论] 倒腾以太网又发现新问题了

[复制链接]
3008|27
qinlu123 发表于 2025-9-2 10:14 | 显示全部楼层 |阅读模式
STM32F407+LAN8720跑TCP server,网络助手为客户端,数据量不大只有300Kbps左右。用我2011年买的win7系统笔记本来测试很稳定可以跑到330Kbps,但是用台式机(试了好几台都是win10)跑不稳定轻则卡顿,重则丢包(通讯速率只有不到150Kbps,单片机被迫丢弃一半数据),再重网络助手直接掉线重连后能恢复传输。不得已减少一半数据发生量,让网络通讯降到160Kbps左右就稳定了。通过读取LAN8720的0x1F寄存器返回的速度模式都是100M全双工,要说电脑问题吧但是电脑连接的千兆设备通讯又很稳定。

评论

终于搞定了,打包函数之前在大循环调用不好使,挪到定时器4中断里就好了。两种情况下调用周期都是1mS,不知道为啥会这样,也解释不了为啥我用笔记本电脑每问题。   发表于 2025-9-5 17:37
 楼主| qinlu123 发表于 2025-9-2 10:19 | 显示全部楼层
使用同样的网线,无论是接交换机还是直连都一个鸟样,我还更新过网卡驱动,有些版本的网卡驱动掉线掉得没法用,感觉电脑嫌弃我这个百兆设备一样。
mwxpk 发表于 2025-9-2 10:53 | 显示全部楼层
一个无线  何必网线?
mini1986 发表于 2025-9-2 13:27 | 显示全部楼层
电脑网卡强制百兆试试,还有网线是几芯的?
 楼主| qinlu123 发表于 2025-9-2 13:47 | 显示全部楼层
mini1986 发表于 2025-9-2 13:27
电脑网卡强制百兆试试,还有网线是几芯的?

8芯的
dffzh 发表于 2025-9-2 14:38 | 显示全部楼层
兄弟, 按下面的逐个操作试试:

1、PC端换一个网络调试助手;
2、关闭windows防火墙和杀毒软件;
3、把windows的电源模式改成高性能模式试试;
4、去官网下载网卡驱动;

实在不行的话,你就用wireshark抓包看下是否存在大量的TCP重传或者窗口变小;
这种现象,其实也不能完全排除不是MCU端软件存在的问题吧。
 楼主| qinlu123 发表于 2025-9-2 14:42 | 显示全部楼层
dffzh 发表于 2025-9-2 14:38
兄弟, 按下面的逐个操作试试:

1、PC端换一个网络调试助手;

追踪了下代码,发现报ERR_MEM,不知道为啥会把发送缓冲区用光

评论

发不出去?堵住了?发送缓冲区定了多少大小?改大试试,可能真的是频繁重传导致的  发表于 2025-9-2 14:48
 楼主| qinlu123 发表于 2025-9-2 14:55 | 显示全部楼层
dffzh 发表于 2025-9-2 14:38
兄弟, 按下面的逐个操作试试:

1、PC端换一个网络调试助手;

这么一丁点的数据量没道理把缓冲区用光,应该是频繁重发导致的,我的笔记本连续跑七八几个小时都没问题
 楼主| qinlu123 发表于 2025-9-2 14:58 | 显示全部楼层
qinlu123 发表于 2025-9-2 14:55
这么一丁点的数据量没道理把缓冲区用光,应该是频繁重发导致的,我的笔记本连续跑七八几个小时都没问题 ...

#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__

#define SYS_LIGHTWEIGHT_PROT    0

//NO_SYS==1:不使用操作系统
#define NO_SYS                  1  //不使用UCOS操作系统

//使用4字节对齐模式
#define MEM_ALIGNMENT           4  

//MEM_SIZE:heap内存的大小,如果在应用中有大量数据发送的话这个值最好设置大一点
#define MEM_SIZE                16000 //内存堆大小

//MEMP_NUM_PBUF:memp结构的pbuf数量,如果应用从ROM或者静态存储区发送大量数据时,这个值应该设置大一点
#define MEMP_NUM_PBUF           60

//MEMP_NUM_UDP_PCB:UDP协议控制块(PCB)数量.每个活动的UDP"连接"需要一个PCB.
#define MEMP_NUM_UDP_PCB        6

//MEMP_NUM_TCP_PCB:同时建立激活的TCP数量
#define MEMP_NUM_TCP_PCB        1

//MEMP_NUM_TCP_PCB_LISTEN:能够监听的TCP连接数量
#define MEMP_NUM_TCP_PCB_LISTEN 6

//MEMP_NUM_TCP_SEG:最多同时在队列中的TCP段数量
#define MEMP_NUM_TCP_SEG        15

//MEMP_NUM_SYS_TIMEOUT:能够同时激活的timeout个数
#define MEMP_NUM_SYS_TIMEOUT    20


/* ---------- Pbuf选项---------- */
//PBUF_POOL_SIZE:pbuf内存池个数.
#define PBUF_POOL_SIZE          20

//PBUF_POOL_BUFSIZE:每个pbuf内存池大小.
#define PBUF_POOL_BUFSIZE       2880


/* ---------- TCP选项---------- */
#define LWIP_TCP                1  //为1是使用TCP
#define TCP_TTL                 255//生存时间

/*当TCP的数据段超出队列时的控制位,当设备的内存过小的时候此项应为0*/
#define TCP_QUEUE_OOSEQ         0

//最大TCP分段
#define TCP_MSS                 (1500 - 40)          //TCP_MSS = (MTU - IP报头大小 - TCP报头大小

//TCP发送缓冲区大小(bytes).
#define TCP_SND_BUF             (4*TCP_MSS)

//TCP_SND_QUEUELEN: TCP发送缓冲区大小(pbuf).这个值最小为(2 * TCP_SND_BUF/TCP_MSS)
#define TCP_SND_QUEUELEN        (2 * TCP_SND_BUF/TCP_MSS)

//TCP发送窗口
//
#define TCP_WND                 (4*TCP_MSS)


/* ---------- ICMP选项---------- */
#define LWIP_ICMP                 1 //使用ICMP协议

/* ---------- DHCP选项---------- */
//当使用DHCP时此位应该为1,LwIP 0.5.1版本中没有DHCP服务.
#define LWIP_DHCP               0

/* ---------- UDP选项 ---------- */
#define LWIP_UDP                1 //使用UDP服务
#define UDP_TTL                 255 //UDP数据包生存时间


/* ---------- Statistics options ---------- */
#define LWIP_STATS 0
#define LWIP_PROVIDE_ERRNO 1


//STM32F746允许通过硬件识别和计算IP,UDP和ICMP的帧校验和
#define CHECKSUM_BY_HARDWARE //定义CHECKSUM_BY_HARDWARE,使用硬件帧校验

#ifdef CHECKSUM_BY_HARDWARE
  //CHECKSUM_GEN_IP==0: 硬件生成IP数据包的帧校验和
  #define CHECKSUM_GEN_IP                 0
  //CHECKSUM_GEN_UDP==0: 硬件生成UDP数据包的帧校验和
  #define CHECKSUM_GEN_UDP                0
  //CHECKSUM_GEN_TCP==0: 硬件生成TCP数据包的帧校验和
  #define CHECKSUM_GEN_TCP                0
  //CHECKSUM_CHECK_IP==0: 硬件检查输入的IP数据包帧校验和
  #define CHECKSUM_CHECK_IP               0
  //CHECKSUM_CHECK_UDP==0: 硬件检查输入的UDP数据包帧校验和
  #define CHECKSUM_CHECK_UDP              0
  //CHECKSUM_CHECK_TCP==0: 硬件检查输入的TCP数据包帧校验和
  #define CHECKSUM_CHECK_TCP              0
  /* CHECKSUM_CHECK_ICMP==0:硬件检查输入的ICMP数据包帧校验和*/  
  #define CHECKSUM_GEN_ICMP               0
#else
  //CHECKSUM_GEN_IP==1: 软件生成IP数据包帧校验和
  #define CHECKSUM_GEN_IP                 1
  // CHECKSUM_GEN_UDP==1: 软件生成UDOP数据包帧校验和
  #define CHECKSUM_GEN_UDP                1
  //CHECKSUM_GEN_TCP==1: 软件生成TCP数据包帧校验和
  #define CHECKSUM_GEN_TCP                1
  // CHECKSUM_CHECK_IP==1: 软件检查输入的IP数据包帧校验和
  #define CHECKSUM_CHECK_IP               1
  // CHECKSUM_CHECK_UDP==1: 软件检查输入的UDP数据包帧校验和
  #define CHECKSUM_CHECK_UDP              1
  //CHECKSUM_CHECK_TCP==1: 软件检查输入的TCP数据包帧校验和
  #define CHECKSUM_CHECK_TCP              1
   /* CHECKSUM_CHECK_ICMP==0:软件检查输入的ICMP数据包帧校验和*/  
  #define CHECKSUM_GEN_ICMP               1
#endif


/*
   ----------------------------------------------
   ---------- SequentialAPI选项----------
   ----------------------------------------------
*/

//LWIP_NETCONN==1:使能NETCON函数(要求使用api_lib.c)
#define LWIP_NETCONN                    0

/*
   ------------------------------------
   ---------- Socket API选项----------
   ------------------------------------
*/
//LWIP_SOCKET==1:使能Socket API(要求使用sockets.c)
#define LWIP_SOCKET                     0

#define LWIP_COMPAT_MUTEX               1

#define LWIP_SO_RCVTIMEO                1 //通过定义LWIP_SO_RCVTIMEO使能netconn结构体中recv_timeout,使用recv_timeout可以避免阻塞线程


/*
   ----------------------------------------
   ---------- Lwip调试选项----------
   ----------------------------------------
*/
//#define LWIP_DEBUG                     1 //开启DEBUG选项

#define ICMP_DEBUG                      LWIP_DBG_OFF //开启/关闭ICMPdebug

#endif /* __LWIPOPTS_H__ */

dffzh 发表于 2025-9-2 14:58 | 显示全部楼层
qinlu123 发表于 2025-9-2 14:55
这么一丁点的数据量没道理把缓冲区用光,应该是频繁重发导致的,我的笔记本连续跑七八几个小时都没问题 ...
那你用wireshark抓包看下,看下是不是有很多重传包;
观察TCP重传的数量,如果有很多重传包,说明数据包确实在网络上丢了(可能是PC端处理慢导致缓冲区满,TCP主动丢弃)。
观察 TCP Window Size 的变化,如果窗口大小变得很小甚至为0,说明接收端(PC)通告发送端(STM32)“我的缓冲区满了,别发了”,这明确指向PC端处理能力不足。
以上是网上解释的,你可以研究一下。

 楼主| qinlu123 发表于 2025-9-2 15:07 | 显示全部楼层
dffzh 发表于 2025-9-2 14:58
那你用wireshark抓包看下,看下是不是有很多重传包;
观察TCP重传的数量,如果有很多重传包,说明数据包确 ...

好的谢谢老哥

评论

不客气,一起探讨,有进展或者找到根因了,回复一下我哦。  发表于 2025-9-2 15:22
 楼主| qinlu123 发表于 2025-9-2 15:27 | 显示全部楼层
dffzh 发表于 2025-9-2 14:58
那你用wireshark抓包看下,看下是不是有很多重传包;
观察TCP重传的数量,如果有很多重传包,说明数据包确 ...

有可能是硬件问题,我找了之前一个项目的板子,跑同样速率没事。区别是新板子用的4层板,集成变压器的RJ45。老板子是外置变压器,双层板。布线都很随意,新板子相对来说更规整一些。

评论

硬件问题?那可以再研究一下。  发表于 2025-9-2 15:48
 楼主| qinlu123 发表于 2025-9-2 16:18 | 显示全部楼层
dffzh 发表于 2025-9-2 14:58
那你用wireshark抓包看下,看下是不是有很多重传包;
观察TCP重传的数量,如果有很多重传包,说明数据包确 ...

还是程序问题,旧板子跑新程序也一个鸟样。奇怪了程序都是复制过来的

评论

今天休息一下,放空一下脑子,明天继续。  发表于 2025-9-2 16:39
 楼主| qinlu123 发表于 2025-9-2 17:38 | 显示全部楼层
dffzh 发表于 2025-9-2 14:58
那你用wireshark抓包看下,看下是不是有很多重传包;
观察TCP重传的数量,如果有很多重传包,说明数据包确 ...

我又发现定长传输1440字节一包就没事,不定长打包一会就完蛋。在我笔记本上测试就啥事没有。

评论

可能是你的客户端环境(包括电脑配置环境+客户端软件环境)接收不定长的TCP包时出问题了。  发表于 2025-9-3 08:46
chineseboyzxy 发表于 2025-9-3 15:12 | 显示全部楼层
可能你的笔记本年代比台式机老一些吧
 楼主| qinlu123 发表于 2025-9-3 20:28 | 显示全部楼层
chineseboyzxy 发表于 2025-9-3 15:12
可能你的笔记本年代比台式机老一些吧

老多了,笔记本是2011年买的,现在的电脑的PHY芯片都是RTL8111
 楼主| qinlu123 发表于 2025-9-4 15:27 | 显示全部楼层
dffzh 发表于 2025-9-2 14:58
那你用wireshark抓包看下,看下是不是有很多重传包;
观察TCP重传的数量,如果有很多重传包,说明数据包确 ...

貌似不仅仅是不定长的问题,我现在修改了字符串的打包代码,把每帧长度都做成固定的,这样每包数据也就固定长度了,但是依然白搭跑几秒就出错(只有300Kbps多一点),单片机甚至触发看门狗重启。如果用另一种模式打包,跑2Mbps都稳稳的。我的老笔记本依然稳健发挥,两种模式都没问题,只是接收2Mbps很吃力。
void DataPack(void)
{
    int8_t *p;
    char buf1[16],buf2[16],buf3[16],buf4[16];
    static char Complete[10] = "COMPLETE";
    uint8_t Single[24] = {0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5};
    static char Buf[64];
    uint16_t i=0;
    float ActAngle;
    uint32_t TIM5_CNT;
    int32_t TIM2_CNT;
    static uint8_t Finish = 1;

    if(CheckDelay(&DelayTimePack) == 0)
    {
        tcp_server_test();
        Set_Delay_Time(PackPeriod,&DelayTimePack);
        if((PackEn==0) &&  (PackCount==0))
        {
            if(Finish == 0)
            {
                Finish = 1;
                if(PackMode == 1)
                {
                    GetServerSendData(Complete,8);
                }
                else
                {
                    for(i=0;i<8;i++)
                    {
                        DataPackBuf[i] = 0xA6;
                    }
                    for(i=8;i<1440;i++)
                    {
                        DataPackBuf[i] = 0;
                    }
                    GetServerSendData((char *)DataPackBuf,1440);
                }
            }
            return;
        }
        Finish = 0;
        TIM2_CNT = TIM2->CNT;
        TIM5_CNT = TIM5->CNT;
        ActAngle = (float)TIM2_CNT * 0.0018;


        if(PackMode == 1)
        {
            sprintf(buf1,"%10u",TIM5_CNT);
            sprintf(buf2,"%8.3f",TargetAngle);
            sprintf(buf3,"%8.3f",ActAngle);
            sprintf(buf4,"%8.3f",FeedBackAngle);
            sprintf(Buf,"%s,%s,%s,%s,\n\r",buf1,buf2,buf3,buf4);

            for(i=0;i<40;i++)
            {
                DataPackBuf[PackCount*40+i] = Buf[i];
            }
            PackCount++;
            if(PackCount >= 36)
            {
                GetServerSendData((char *)DataPackBuf,1440);
                PackCount = 0;
            }
        }
        else
        {
            Single[8] = TIM5_CNT;
            Single[9] = TIM5_CNT>>8;
            Single[10] = TIM5_CNT>>16;
            Single[11] = TIM5_CNT>>24;

            p = (int8_t *)&TargetAngle;
            Single[12] = p[0];
            Single[13] = p[1];
            Single[14] = p[2];
            Single[15] = p[3];

            p = (int8_t *)&ActAngle;
            Single[16] = p[0];
            Single[17] = p[1];
            Single[18] = p[2];
            Single[19] = p[3];

            p = (int8_t *)&FeedBackAngle;
            Single[20] = p[0];
            Single[21] = p[1];
            Single[22] = p[2];
            Single[23] = p[3];

            for(i=0;i<24;i++)
            {
                DataPackBuf[PackCount*24+i] = Single[i];
            }
            PackCount++;
            
            if(PackCount >=6)
            {
                GetServerSendData((char *)DataPackBuf,1440);
                PackCount = 0;
            }
        }
    }
   
}

这个函数就丢在大循环里,里边的if(cheakdelay())每1ms为真一次,也就是说1ms打一帧到数据包里。
下边是协议,我开始怀疑是命令应答导致的,但是把应答关了也白搭。
10 命令的应答和数据传输
在字符串模式下板卡会将上位机发来的命令原样返回作为应答,非协议内指令返回字符串ERROR”。HEX模式无应答。
字符串模式上传:
数据以csv格式传输,数据内容为:“时间戳,目标角度,编码器计数值,实际角度,\n\r”;其中目标角度和实际角度为浮点数。完成测试后发送字符串“COMPLETE
HEX模式上传:
帧头位置:Byte0-Byte7
帧头内容:0xA5 0xA5 0xA5 0xA5 0xA5 0xA5 0xA5 0xA5
时间戳位置:Byte8-Byte11
时间戳数据类型:uint32(低位在前)
目标角度位置:Byte12-Byte15
目标角度数据类型:float(低位在前)
实际角度位置:Byte16-Byte19
实际角度数据类型:float(低位在前)
电机反馈角度位置:Byte20-Byte23
电机反馈角度数据类型:float(低位在前)
60帧数据为1包。
完成测试后发送帧头为0xA6 0xA6 0xA6 0xA6 0xA6 0xA6 0xA6 0xA6的停止包。


评论

项目上开发还是自己研究?没实际开发过,都帮不上你。  发表于 2025-9-4 16:24
一旦使用过字符串模式,必须重启板卡和上位机,否则即使使用HEX模式也会出问题。  发表于 2025-9-4 15:36
 楼主| qinlu123 发表于 2025-9-4 15:28 | 显示全部楼层
qinlu123 发表于 2025-9-4 15:27
貌似不仅仅是不定长的问题,我现在修改了字符串的打包代码,把每帧长度都做成固定的,这样每包数据也就固 ...

void DataPack(void)
{
    int8_t *p;
    char buf1[16],buf2[16],buf3[16],buf4[16];
    static char Complete[10] = "COMPLETE";
    uint8_t Single[24] = {0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5};
    static char Buf[64];
    uint16_t i=0;
    float ActAngle;
    uint32_t TIM5_CNT;
    int32_t TIM2_CNT;
    static uint8_t Finish = 1;

    if(CheckDelay(&DelayTimePack) == 0)
    {
        tcp_server_test();
        Set_Delay_Time(PackPeriod,&DelayTimePack);
        if((PackEn==0) &&  (PackCount==0))
        {
            if(Finish == 0)
            {
                Finish = 1;
                if(PackMode == 1)
                {
                    GetServerSendData(Complete,8);
                }
                else
                {
                    for(i=0;i<8;i++)
                    {
                        DataPackBuf = 0xA6;
                    }
                    for(i=8;i<1440;i++)
                    {
                        DataPackBuf = 0;
                    }
                    GetServerSendData((char *)DataPackBuf,1440);
                }
            }
            return;
        }
        Finish = 0;
        TIM2_CNT = TIM2->CNT;
        TIM5_CNT = TIM5->CNT;
        ActAngle = (float)TIM2_CNT * 0.0018;


        if(PackMode == 1)
        {
            sprintf(buf1,"%10u",TIM5_CNT);
            sprintf(buf2,"%8.3f",TargetAngle);
            sprintf(buf3,"%8.3f",ActAngle);
            sprintf(buf4,"%8.3f",FeedBackAngle);
            sprintf(Buf,"%s,%s,%s,%s,\n\r",buf1,buf2,buf3,buf4);

            for(i=0;i<40;i++)
            {
                DataPackBuf[PackCount*40+i] = Buf;
            }
            PackCount++;
            if(PackCount >= 36)
            {
                GetServerSendData((char *)DataPackBuf,1440);
                PackCount = 0;
            }
        }
        else
        {
            Single[8] = TIM5_CNT;
            Single[9] = TIM5_CNT>>8;
            Single[10] = TIM5_CNT>>16;
            Single[11] = TIM5_CNT>>24;

            p = (int8_t *)&TargetAngle;
            Single[12] = p[0];
            Single[13] = p[1];
            Single[14] = p[2];
            Single[15] = p[3];

            p = (int8_t *)&ActAngle;
            Single[16] = p[0];
            Single[17] = p[1];
            Single[18] = p[2];
            Single[19] = p[3];

            p = (int8_t *)&FeedBackAngle;
            Single[20] = p[0];
            Single[21] = p[1];
            Single[22] = p[2];
            Single[23] = p[3];

            for(i=0;i<24;i++)
            {
                DataPackBuf[PackCount*24+i] = Single;
            }
            PackCount++;
            
            if(PackCount >=6)
            {
                GetServerSendData((char *)DataPackBuf,1440);
                PackCount = 0;
            }
        }
    }
   
}
 楼主| qinlu123 发表于 2025-9-4 15:38 | 显示全部楼层
qinlu123 发表于 2025-9-4 15:27
貌似不仅仅是不定长的问题,我现在修改了字符串的打包代码,把每帧长度都做成固定的,这样每包数据也就固 ...


使用HEX模式跑2Mbps,老化测试1个小时没问题。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| qinlu123 发表于 2025-9-4 19:01 | 显示全部楼层
qinlu123 发表于 2025-9-4 15:27
貌似不仅仅是不定长的问题,我现在修改了字符串的打包代码,把每帧长度都做成固定的,这样每包数据也就固 ...

是传输速度的问题,我的字符串模式下数据量是330Kbps,在这个速度下无论我发什么都不好使,把速度控制在200K或者600K就没事了。不知道什么情况网卡对300K的数据处理有bug
您需要登录后才可以回帖 登录 | 注册

本版积分规则

199

主题

2209

帖子

14

粉丝
快速回复 在线客服 返回列表 返回顶部