【STM32F769Discovery开发板试用】能用但并不好用的TCP服务器通信代码简单尝试
[复制链接]
官方的网口TCP例程都是要带操作系统的,并且使用难度较高,因此不太想用官方的TCP例程,好在ST官方给出了使用CubeMX搭建裸机TCP例程的步骤,可以很方便地让开发者搭建TCP通信代码工程,不过经过实测,这种方式搭建出来的例程并不好用,这不是CubeMX的问题,而是单片机软硬件资源限制的问题,不知道以后ST会不会改进这点,我看是不会了,况且对于开发者来说,开发板与电脑的高速通信,使用USB-HID明显要比用TCP方便得多,也更好用。
官方的CubeMX搭建步骤其实很简单,基本上造轮子的部分都集成好了,只需要配置LWIP打开,ETH(以太网)配置为RMII模式,MAC(数据链路层)地址为02:00:00:00:00:00,PHY(物理层)为0,配置DHCP关闭:
我这边也懒得用CubeMX生成工程了,直接用网上已经有的例程来尝试:
LWIP裸机工程的核心步骤只有几个:
-tcp_new()函数用于新建TCP进程控制块(PCB);
-tcp_bind()函数用于绑定TCP服务器的IP地址和端口,这里我用的是192.168.1.10和6666;
-tcp_listen()函数监听客户端接入,可以任选特定IP地址允许接入或全开放(任意地址)接入,默认全开放;
-这个TCP的进程控制块(PCB)被封装成一个巨大的结构体(struct tcp_pcb *tcp_echoserver_pcb;),如果要读懂里面的参数含义,需要一定的网络工程知识,我们不需要关心实现原理,也不需要关心结构体怎么用,只需要知道,TCP通信就靠这个结构体就行了;
-tcp_accept()函数是注册接收回调函数tcp_recv()的函数,通过两层回调将tcp_recv()和tcp_echoserver_pcb绑定在一起。
- 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);
- }
- }
- }
TCP接收通过两层回调函数的形式实现,接收函数tcp_recv()封装于TCP_Server_Accept()函数中,而这个TCP_Server_Accept()函数由tcp_accept()函数调用:
- static err_t TCP_Server_Accept(void *arg, struct tcp_pcb *newpcb,err_t err)
- {
-
- tcp_recv(newpcb, TCP_Server_Recv);
-
- return ERR_OK;
- }
接收函数的具体动作则在TCP_Server_Recv()函数中实现,其中tcp_write()就是用于TCP发送的阻塞型函数:
- 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->payload;
- tcp_write(tpcb,"66666",5,1);
- tcp_write(tpcb,p->payload,p->len,1);
- return ERR_OK;
- }
依照个人对于线程和网络工程的理解,可以看出来ST的代码还是有点东西的(虽然总体上是一坨屎),tcp_recv函数是通过回调函数的形式实现的,避免了阻塞,相比Linux系统下的TCP编程,recv()函数是阻塞函数,如果一份Linux代码要同时实现发送和接收,则需要开两条线程,STM32F769实现的TCP通信与之相比实时性要高得多,唯一不足的就是那少得可怜的存储池,基本存不下多少TCP数据,轻易就爆满了,这也是单片机不适合用于复杂应用级TCP通信的原因。这次测评TCP功能,仅仅是为了尊重STM32F769Discovery这块强大的探索板子,更是测评STM32F769自带的MAC性能。如果要使用单片机做简单网页通信的话,一般常规做法是外接QSPI/SPI以太网模块,比如W5500,ENC28J60等,这些模块集成了MAC和PHY,目标主控只需要引出串行或并行接口就可以实现基于这些接口的IP层(网络层)通信,非常的方便。
做好了上述操作之后,还需要设置开发板的TCP服务器IP地址,不使用DHCP,用静态IP 192.168.1.10:
- void MX_LWIP_Init(void)
- {
-
- IP_ADDRESS[0] = 192;
- IP_ADDRESS[1] = 168;
- IP_ADDRESS[2] = 1;
- IP_ADDRESS[3] = 10;
- NETMASK_ADDRESS[0] = 255;
- NETMASK_ADDRESS[1] = 255;
- NETMASK_ADDRESS[2] = 255;
- NETMASK_ADDRESS[3] = 0;
- GATEWAY_ADDRESS[0] = 192;
- GATEWAY_ADDRESS[1] = 168;
- GATEWAY_ADDRESS[2] = 1;
- GATEWAY_ADDRESS[3] = 1;
-
- lwip_init();
-
- IP4_ADDR(&ipaddr, IP_ADDRESS[0], IP_ADDRESS[1], IP_ADDRESS[2], IP_ADDRESS[3]);
- IP4_ADDR(&netmask, NETMASK_ADDRESS[0], NETMASK_ADDRESS[1] , NETMASK_ADDRESS[2], NETMASK_ADDRESS[3]);
- IP4_ADDR(&gw, GATEWAY_ADDRESS[0], GATEWAY_ADDRESS[1], GATEWAY_ADDRESS[2], GATEWAY_ADDRESS[3]);
-
-
- netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, ðernet_input);
-
-
- netif_set_default(&gnetif);
-
- if (netif_is_link_up(&gnetif))
- {
- netif_set_up(&gnetif);
- }
- else
- {
- netif_set_down(&gnetif);
- }
- }
主循环其实就是个摆设,但摆设也不能忽略,不然通信失败:
-
- while (1)
- {
- MX_LWIP_Process();
- }
电脑IP是192.168.1.2:
使用网线连接开发板和电脑:
使用电脑端的TCP通信软件如SSCOM等接入开发板的IP和端口6666:
为什么我说板子的TCP通信不好用呢,这里就体现出来了,一是断开重连超级费劲,二是TCP通信了几条之后就哑火了,没法继续接收了,而且过了十来秒钟之后服务器还会神奇地自动断开连接:
STM32F769-TCP.zip
(4.26 MB, 下载次数: 4)
|