donatello1996 发表于 2020-7-28 00:06

【STM32F769Discovery开发板试用】能用但并不好用的TCP服务器通信代码简单尝试

<p>&nbsp; &nbsp; &nbsp; &nbsp;官方的网口TCP例程都是要带操作系统的,并且使用难度较高,因此不太想用官方的TCP例程,好在ST官方给出了使用CubeMX搭建裸机TCP例程的步骤,可以很方便地让开发者搭建TCP通信代码工程,不过经过实测,这种方式搭建出来的例程并不好用,这不是CubeMX的问题,而是单片机软硬件资源限制的问题,不知道以后ST会不会改进这点,我看是不会了,况且对于开发者来说,开发板与电脑的高速通信,使用USB-HID明显要比用TCP方便得多,也更好用。<br />
&nbsp; &nbsp; &nbsp; &nbsp;官方的CubeMX搭建步骤其实很简单,基本上造轮子的部分都集成好了,只需要配置LWIP打开,ETH(以太网)配置为RMII模式,MAC(数据链路层)地址为02:00:00:00:00:00,PHY(物理层)为0,配置DHCP关闭:</p>

<p></p>

<p>我这边也懒得用CubeMX生成工程了,直接用网上已经有的例程来尝试:</p>

<p></p>

<p>&nbsp;</p>

<p>LWIP裸机工程的核心步骤只有几个:<br />
-tcp_new()函数用于新建TCP进程控制块(PCB);<br />
-tcp_bind()函数用于绑定TCP服务器的IP地址和端口,这里我用的是192.168.1.10和6666;<br />
-tcp_listen()函数监听客户端接入,可以任选特定IP地址允许接入或全开放(任意地址)接入,默认全开放;<br />
-这个TCP的进程控制块(PCB)被封装成一个巨大的结构体(struct tcp_pcb *tcp_echoserver_pcb;),如果要读懂里面的参数含义,需要一定的网络工程知识,我们不需要关心实现原理,也不需要关心结构体怎么用,只需要知道,TCP通信就靠这个结构体就行了;</p>

<p><br />
-tcp_accept()函数是注册接收回调函数tcp_recv()的函数,通过两层回调将tcp_recv()和tcp_echoserver_pcb绑定在一起。</p>

<pre>
<code>void TCP_Server_Init(void)
{
err_t err;
tcp_echoserver_pcb = tcp_new();

if(tcp_echoserver_pcb !=NULL)
{
    err = tcp_bind(tcp_echoserver_pcb,IP_ADDR_ANY,6666);
    if(err == ERR_OK)
    {
      tcp_echoserver_pcb = tcp_listen(tcp_echoserver_pcb);
      tcp_accept(tcp_echoserver_pcb,TCP_Server_Accept);
    }
    else
    {
      memp_free(MEMP_TCP_PCB, tcp_echoserver_pcb);
    }
}
}</code></pre>

<p><br />
&nbsp;</p>

<p>TCP接收通过两层回调函数的形式实现,接收函数tcp_recv()封装于TCP_Server_Accept()函数中,而这个TCP_Server_Accept()函数由tcp_accept()函数调用:</p>

<pre>
<code>static err_t TCP_Server_Accept(void *arg, struct tcp_pcb *newpcb,err_t err)
{
    /* initialize lwip tcp_recv callback function for newpcb*/
    tcp_recv(newpcb, TCP_Server_Recv);
   
    return ERR_OK;
}</code></pre>

<p>接收函数的具体动作则在TCP_Server_Recv()函数中实现,其中tcp_write()就是用于TCP发送的阻塞型函数:</p>

<pre>
<code>static err_t TCP_Server_Recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p,err_t err)
{
struct pbuf *ptr;
u16_t plen;
uint8_t *val;
val=p-&gt;payload;
tcp_write(tpcb,"66666",5,1);
tcp_write(tpcb,p-&gt;payload,p-&gt;len,1);
return ERR_OK;
}</code></pre>

<p>&nbsp;</p>

<p>依照个人对于线程和网络工程的理解,可以看出来ST的代码还是有点东西的(虽然总体上是一坨屎),tcp_recv函数是通过回调函数的形式实现的,避免了阻塞,相比Linux系统下的TCP编程,recv()函数是阻塞函数,如果一份Linux代码要同时实现发送和接收,则需要开两条线程,STM32F769实现的TCP通信与之相比实时性要高得多,唯一不足的就是那少得可怜的存储池,基本存不下多少TCP数据,轻易就爆满了,这也是单片机不适合用于复杂应用级TCP通信的原因。这次测评TCP功能,仅仅是为了尊重STM32F769Discovery这块强大的探索板子,更是测评STM32F769自带的MAC性能。如果要使用单片机做简单网页通信的话,一般常规做法是外接QSPI/SPI以太网模块,比如W5500,ENC28J60等,这些模块集成了MAC和PHY,目标主控只需要引出串行或并行接口就可以实现基于这些接口的IP层(网络层)通信,非常的方便。</p>

<p>做好了上述操作之后,还需要设置开发板的TCP服务器IP地址,不使用DHCP,用静态IP 192.168.1.10:</p>

<pre>
<code>void MX_LWIP_Init(void)
{
/* IP addresses initialization */
IP_ADDRESS = 192;
IP_ADDRESS = 168;
IP_ADDRESS = 1;
IP_ADDRESS = 10;
NETMASK_ADDRESS = 255;
NETMASK_ADDRESS = 255;
NETMASK_ADDRESS = 255;
NETMASK_ADDRESS = 0;
GATEWAY_ADDRESS = 192;
GATEWAY_ADDRESS = 168;
GATEWAY_ADDRESS = 1;
GATEWAY_ADDRESS = 1;

lwip_init();

IP4_ADDR(&amp;ipaddr, IP_ADDRESS, IP_ADDRESS, IP_ADDRESS, IP_ADDRESS);
IP4_ADDR(&amp;netmask, NETMASK_ADDRESS, NETMASK_ADDRESS , NETMASK_ADDRESS, NETMASK_ADDRESS);
IP4_ADDR(&amp;gw, GATEWAY_ADDRESS, GATEWAY_ADDRESS, GATEWAY_ADDRESS, GATEWAY_ADDRESS);

/* add the network interface (IPv4/IPv6) without RTOS */
netif_add(&amp;gnetif, &amp;ipaddr, &amp;netmask, &amp;gw, NULL, &amp;ethernetif_init, &amp;ethernet_input);

/* Registers the default network interface */
netif_set_default(&amp;gnetif);

if (netif_is_link_up(&amp;gnetif))
{
    netif_set_up(&amp;gnetif);
}
else
{
    netif_set_down(&amp;gnetif);
}
}</code></pre>

<p>主循环其实就是个摆设,但摆设也不能忽略,不然通信失败:</p>

<pre>
<code>
while (1)
{
    MX_LWIP_Process();
}</code></pre>

<p>电脑IP是192.168.1.2:</p>

<p></p>

<p>使用网线连接开发板和电脑:</p>

<p></p>

<p>使用电脑端的TCP通信软件如SSCOM等接入开发板的IP和端口6666:</p>

<p></p>

<p>为什么我说板子的TCP通信不好用呢,这里就体现出来了,一是断开重连超级费劲,二是TCP通信了几条之后就哑火了,没法继续接收了,而且过了十来秒钟之后服务器还会神奇地自动断开连接:<br />
</p>

littleshrimp 发表于 2020-7-28 05:32

这个问题有没有办法解决?官方例程有这个问题吗?

donatello1996 发表于 2020-7-28 18:22

littleshrimp 发表于 2020-7-28 05:32
这个问题有没有办法解决?官方例程有这个问题吗?

<p>目前没找到解决方案,这个用CubeMX生成的简化裸机例程已经比官方CubeF7例程要好用得多了,官方的例程调用的函数是一样的,并且封装更多,更难用,目前用单片机实现以太网通信,还是NXP,新塘这些比ST专业得多</p>

led2015 发表于 2020-7-29 00:38

<p>之前看了一段时间,感觉找不到很好的实验,正好学习下</p>

damiaa 发表于 2020-8-4 09:02

<p>楼主加油。把问题给解决掉。产品中。经常死机和掉线是不允许发生的。这样无法用到产品中去的。<img height="50" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/onion/Onion--16.gif" width="50" /></p>

donatello1996 发表于 2020-8-4 09:47

damiaa 发表于 2020-8-4 09:02
楼主加油。把问题给解决掉。产品中。经常死机和掉线是不允许发生的。这样无法用到产品中去的。

<p>如果是用于产品的话,别说死机掉线了,就是速率有一丁点不满足要求或是传输中有一些错误码都是不允许的,必须要无任何可见问题。对于STM32来说,最好用的高速传输方式还是USB,TCP网络传输比较少用,我昨晚发了一帖USBHID的帖子,你可以去看看。</p>

freebsder 发表于 2020-8-6 22:16

<div class='shownolgin' data-isdigest='no'><p>啥问题?简单说。。。</p>
</div><script>showreplylogin();</script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script>

donatello1996 发表于 2020-8-10 10:34

<div class='shownolgin' data-isdigest='no'>freebsder 发表于 2020-8-6 22:16
啥问题?简单说。。。

<p>使用echo方式测试TCP,第一,接收TCP报文的次数有限,接收了几条之后就哑火了,没法接续接收了,第二,TCP服务器在接入一段时间之后自动断开了</p>
</div><script>showreplylogin();</script>

freebsder 发表于 2020-8-10 18:35

<div class='shownolgin' data-isdigest='no'>donatello1996 发表于 2020-8-10 10:34
使用echo方式测试TCP,第一,接收TCP报文的次数有限,接收了几条之后就哑火了,没法接续接收了,第二,TC ...

<p>跑飞了吧,内存看看。试试开那个啥keepalive试试。</p>
</div><script>showreplylogin();</script>
页: [1]
查看完整版本: 【STM32F769Discovery开发板试用】能用但并不好用的TCP服务器通信代码简单尝试