STM32F769IDISCOVERY开发板(STM32官网)集成了有线网络接口和WIFI接口,在上一篇帖子中介绍了一个网口应用实力,演示一个HTTPServer的demo,本文介绍自己开发一个简单的TCPServer,实现以下功能:
1.开发板作为服务器,等待客户端链接 2.客户端链接成功后,向服务器发送字符串或命令 3.开发板收到客户端的命令后,将收到的命令显示在LCD屏幕上 4.将收到的命令发送回客户端,客户端显示出来 5.几个特殊命令,控制开发板上两个LED的亮和灭
本文的程序基于上一篇帖子介绍的例子https://www.stmcu.org.cn/module/forum/thread-609498-1-1.html 移植而来,该例子库STM32CubeF7中STM32Cube_FW_F7_V1.5.0\Projects\STM32F769I-Discovery\Applications\LwIP\LwIP_HTTP_Server_Socket_RTOS
先简单介绍一下这个例子的代码结构,有利于自己移植的时候修改 main函数: -使能CPU的ICACHE和DCACHE -HAL 库初始化 -配置系统时钟 -定义和创建一个线程作为启动线程 -启动FreeRTOS内核,将CPU控制权交给内核 -进入死循环,CPU已经由内核控制,不会再运行到这里
接下来,FreeRTOS内核控制CPU后会执行启动线程 StartThread: -BSP初始化,主要是初始化了开发板上的LED和LCD,用来显示 -初始化TCP/IP协议栈 -网口配置,配置网口参数,MCU底层网口初始化,创建和配置网络状态改变,接收发送等相关回调函数,创建数据接收线程 -初始化httpserver,这就是修改的地方,将替换成我们自己的server相关函数,该函数里将会创建一个线程,用于运行我们自己的服务器代码 -在LCD显示前面初始化是否成功的状态 -如果使用DHCP,创建DHCP线程 -删除本线程,即删除启动线程
再接下来,内核已经有多个线程在运行,此处只关心服务器线程,也就是由http_server_socket_init函数创建的http_server_socket_thread线程,在之后的修改中,这两个函数都将会被替换为我们自己写的函数 http_server_socket_thread: -创建socket -绑定socket监听地址和端口 -设置监听socket -调用accept等待链接,该函数将会阻塞,线程挂起,直到有客户端链接才会返回 -一旦有了链接,就转入具体服务函数http_server_serve,就是发送和接收数据,这个函数也会被替换成自己的服务函数
讲完了例子的代码结构,总结起来需要修改的地方也不多,就3个: 1.http_server_socket_init换成my_server_init,完成初始化 2.http_server_socket_thread换成my_server_thread,服务器端代码 3.http_server_serve换成my_server_echo,具体功能实现
三个函数主要部分代码如下:
- void my_server_init(void)
- {
- sys_thread_new("MY_SERVER", my_server_thread, NULL, DEFAULT_THREAD_STACKSIZE * 2, TCPIP_THREAD_PRIO);
- }
复制代码创建新线程,运行my_server_thread函数 - void my_server_thread(void *arg)
- {
- int listenfd,connectfd;
- struct sockaddr_in server,client;
- socklen_t addrlen;
- if((listenfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
- {
- return ;
- }
- server.sin_family = AF_INET;
- server.sin_port = htons(PORT);
- server.sin_addr.s_addr = htonl(INADDR_ANY);
- if(bind(listenfd,(struct sockaddr *)&server,sizeof(server)) == -1)
- {
- return;
- }
- if(listen(listenfd,BACKLOG) == -1)
- {
- return;
- }
- addrlen = sizeof(client);
- while(1)
- {
- connectfd = accept(listenfd,(struct sockaddr*)&client,&addrlen);
- my_server_echo(connectfd);
- }
- close(listenfd);
- }
复制代码
一旦有客户端链接,就调用my_server_echo函数
- void my_server_echo(int connfd)
- {
- int rxsize;
- while(1)
- {
- if((rxsize = read(connfd, recvbuf, MAX_BUFF_SIZE)) == -1)
- {
- continue;
- }
- else
- {
- recvbuf[rxsize - 1] = '\0';
-
- if(strncmp(recvbuf,LED1_ON,sizeof(LED1_ON)) == 0)
- BSP_LED_On(LED1);
- else if(strncmp(recvbuf,LED1_OFF,sizeof(LED1_OFF)) == 0)
- BSP_LED_Off(LED1);
- else if(strncmp(recvbuf,LED2_ON,sizeof(LED2_ON)) == 0)
- BSP_LED_On(LED2);
- else if(strncmp(recvbuf,LED2_OFF,sizeof(LED2_OFF)) == 0)
- BSP_LED_Off(LED2);
- LCD_UsrLog("%s\n",recvbuf);
- write(connfd, recvbuf, rxsize);
- }
- osDelay(10);
- }
复制代码 由于客户端输入命令以回车键结束,所以接收到的最后一个字符recvbuf[rxsize- 1]原本应该是'\n',应该执行recvbuf[rxsize]= '\0';作为字符串结束,不过为了和设置的宏定义比较,直接把接收到的最后一个字符recvbuf[rxsize- 1]换成字符串结束符,以便strncmp能获得正确的结果
现在服务器有了,还需要客户端,客户端运行在linux系统,这里就不贴代码了,有需要的在文末链接去找。 代码写好了接下来编译运行,看看效果
复位运行,没有客户端链接 有一个客户端链接上了, ip地址 192.168.1.149 (我的电脑), port 59614 (分配的临时端口,每次连接不一定一样) 此时led1on,led2off状态 输入led1off,led2on 此时led1被关闭,led2打开
输入led1on,两个led都打开
输入led1off,led2off,连个led都关闭 测试一下回显功能,就是服务器发回客户端发送的命令,其实前面已经可以看到,电脑上的窗口中,输入的命令显示了两次,一次为输入,一次为从服务器读取的回显
需要完整代码的可以去下面链接下载:
|