文章目录
- 1. ETH测试
- 第一步:cubemx配置。
- 第二步:board.h配置。
- 第三步:rtthread settings配置
- 第四步:以太网复位引脚设置
- 第五步:修改rtthread源码
- 第六步:修改 cubemx 生成的 main 函数
- 第七步:编译运行。
- 第七步:通信测试
- 2. UDP/TCP通信测试
- 2.1 UDP测试
- 2.1.1 UPD 服务端测试
- 2.1.2 UPD 客户端测试
- 2.2 TCP通信测试
- 2.1.2 TCP 客户端测试
- 2.2.2 TCP 服务端测试
====>>> 文章汇总(有代码汇总) <<<====
目标:使用网口和电脑通信。
- RT-Thread studio,版本: 2.2.6,不一样其实区别也不大
- RT-Thread:标准版,4.0.3版本
- 芯片包版本:0.1.9
- 开发板:自己做的,主控STM32F407VET6。
- 网口芯片:太网口LAN8720A
- cubemx版本:6.8.1
cubemx版本不一样的话,网口的配置过程也不大一样。我现在用的麻烦一点。老版本的cubemx相对简单。
Finsh组件是创建工程后就默认开启的,无需任何配置。
1. ETH测试
新建工程,选择自己的芯片和调试接口即可。
第一步:cubemx配置。
首先配置时钟,一般都拉满。
勾选上调试的串口、网口。参数默认。
我这里调试用的串口2,一般都是串口1,根据自己的选择即可。
网口也要根据自己的板子的引脚调整。
生成代码后,关闭cubemx,直接编译,一堆报错,基本都在cubemx下面的main函数里面。
第二步:board.h配置。
第三步:rtthread settings配置
勾选以太网驱动。
勾选网络层驱动。
设置板子 IP 信息
勾选之后,保存 关闭。
第四步:以太网复位引脚设置
这里根据自己的板卡引脚修改。
第五步:修改rtthread源码
修改 eth_demo\rt-thread\components\drivers\include\drivers\phy.h
文件,注释掉32行。图中 drivers 文件夹下的 drv_eth.c 报错是因为这个变量重定义了。
第六步:修改 cubemx 生成的 main 函数
注意,把下面的函数体 也删掉。
此时编译,应该是没有报错能通过的。cubemx中的main函数会用到我们删掉的那两个函数,所有会有警告,不想看可以在main函数中也注释或者删掉。
第七步:编译运行。
调试串口输入 ifconfig
就可以看到板子的ip信息了。
第七步:通信测试
第一步:把板子的网口和电脑的网口使用网线连接起来。
第二步:把电脑端 ip 更改为和板卡在同一个网段下。
更改本地电脑ip方法,自己百度一下。
第三步:打开电脑端 cmd 窗口,使用ping命令测试,可以ping通。
2. UDP/TCP通信测试
在上面的步骤都能通过以后,进行UDP/TCP通信测试。
2.1 UDP测试
在工程中新建一个 c 文件,添加如下代码。位置根据自己需要放。
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-10-24 Haozi the first version
*/
#include <rtthread.h>
#include <sys/socket.h> /* 使用BSD socket,需要包含socket.h头文件 */
#include <netdb.h>
#include <string.h>
#include <finsh.h>
/* UDP服务器参数 */
#define BUFSZ 1024 //接收缓存大小
#define BOARD_SERVER_PORT 5000 //板卡作为服务器的端口号
/* UDP客户端参数 */
#define BOARD_UDP_CLIENT_PORT 20000 //板卡作为客户端端口号
#define UPPER_UDP_SERVER_IP "192.168.1.125" //要连接的服务器ip
/*
* udp 服务端线程,一直等待接收其他udp客户端发来的消息。
* 本机ip:192.168.1.30
* 使用的端口号:5000
*/
void udp_server(void *para)
{
int sock;
int bytes_read;
char *recv_data;
socklen_t addr_len;
struct sockaddr_in server_addr, client_addr;
/* 分配接收用的数据缓冲 */
recv_data = rt_malloc(BUFSZ);
if (recv_data == RT_NULL)
{
/* 分配内存失败,返回 */
rt_kprintf("No memory\n");
return;
}
/* 创建一个socket,类型是SOCK_DGRAM,UDP类型 */
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
rt_kprintf("Socket error\n");
/* 释放接收用的数据缓冲 */
rt_free(recv_data);
return;
}
/* 初始化服务端地址 */
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(BOARD_SERVER_PORT);
server_addr.sin_addr.s_addr = INADDR_ANY;
rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
/* 绑定socket到服务端地址 */
if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
{
/* 绑定地址失败 */
rt_kprintf("Bind error\n");
/* 释放接收用的数据缓冲 */
rt_free(recv_data);
return;
}
addr_len = sizeof(struct sockaddr);
rt_kprintf("UDPServer Waiting for client on port 5000...\n");
while (1)
{
/* 从sock中收取最大BUFSZ - 1字节数据 */
bytes_read = recvfrom(sock, recv_data, BUFSZ - 1, 0, (struct sockaddr *)&client_addr, &addr_len);
/* UDP不同于TCP,它基本不会出现收取的数据失败的情况,除非设置了超时等待 */
recv_data[bytes_read] = '\0'; /* 把末端清零 */
/* 输出接收的数据 */
rt_kprintf("\n(%s , %d) said : ", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
rt_kprintf("%s", recv_data);
/* 如果接收数据是exit,退出 */
if (strcmp(recv_data, "exit") == 0)
{
closesocket(sock);
/* 释放接收用的数据缓冲 */
rt_free(recv_data);
break;
}
}
return;
}
MSH_CMD_EXPORT(udp_server, udp server);
/*
* udp 客户端,每隔2s就往上位机发送当前系统时钟的计数值。
* 本机ip:192.168.1.30
* 使用的端口号:20000
*/
static const char send_data[] = "This is UDP Client from RT-Thread.\n"; /* 发送用到的数据 */
void udp_client(void *para)
{
int sock;
struct hostent *host;
struct sockaddr_in server_addr;
/* 获得host地址(如果是域名,会做域名解析) */
host = (struct hostent *) gethostbyname(UPPER_UDP_SERVER_IP);
/* 创建一个socket,类型是SOCK_DGRAM,UDP类型 */
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
rt_kprintf("Socket error\n");
return;
}
/* 初始化预连接的服务端地址 */
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(BOARD_UDP_CLIENT_PORT);
server_addr.sin_addr = *((struct in_addr *)host->h_addr);
rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
/* 总计发送count次数据 */
while (1)
{
/* 发送数据到服务远端 */
sendto(sock, send_data, strlen(send_data), 0, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
/* 线程休眠一段时间 */
rt_thread_delay(2000);
}
/* 关闭这个socket */
closesocket(sock);
}
MSH_CMD_EXPORT(udp_client, udp client);
2.1.1 UPD 服务端测试
- 在串口调试助手中启动
udp_server
任务。 - 打开,网络调试助手。ip设置如下,然后点击连接。
- 在网络调试助手输入框输入消息并点击发送,串口会打印出板卡收到的消息。
电脑信息:端口是任意的,能连接即可。
板卡信息:端口是固定的,在上面板卡程序里面可以改。
2.1.2 UPD 客户端测试
- 打开,网络调试助手。ip设置如下。
- 重启板卡,在串口调试助手中启动
udp_client
任务。 - 网络调试助手会受到板卡每2s发送过来的一次数据。
电脑信息:端口是固定的,在上面板卡程序里面可以改。
板卡信息:随便填吧,没用其实。
2.2 TCP通信测试
在工程中新建一个 c 文件,添加如下代码。位置根据自己需要放。
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-10-24 Haozi the first version
*/
#include <rtthread.h>
#include <sys/socket.h> /* 使用BSD socket,需要包含socket.h头文件 */
#include <netdb.h>
#include <string.h>
#include <finsh.h>
#include <sys/errno.h>
#define BUFSZ (1024)
static const char send_data[] = "This is TCP Server from RT-Thread."; /* 发送用到的数据 */
/*
* 程序清单:tcp 服务端
*
* 这是一个 tcp 服务端的例程
* 导出 tcpserv 命令到控制终端
* 命令调用格式:tcpserv
* 无参数
* 程序功能:作为一个服务端,接收并显示客户端发来的数据 ,接收到 exit 退出程序
*/
void tcp_server(int argc, char **argv)
{
char *recv_data; /* 用于接收的指针,后面会做一次动态分配以请求可用内存 */
socklen_t sin_size;
int sock, connected, bytes_received;
struct sockaddr_in server_addr, client_addr;
rt_bool_t stop = RT_FALSE; /* 停止标志 */
int ret;
recv_data = rt_malloc(BUFSZ + 1); /* 分配接收用的数据缓冲 */
if (recv_data == RT_NULL)
{
rt_kprintf("No memory\n");
return;
}
/* 一个socket在使用前,需要预先创建出来,指定SOCK_STREAM为TCP的socket */
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
/* 创建失败的错误处理 */
rt_kprintf("Socket error\n");
/* 释放已分配的接收缓冲 */
rt_free(recv_data);
return;
}
/* 初始化服务端地址 */
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(20000); /* 服务端工作的端口 */
server_addr.sin_addr.s_addr = INADDR_ANY;
rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
/* 绑定socket到服务端地址 */
if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
{
/* 绑定失败 */
rt_kprintf("Unable to bind\n");
/* 释放已分配的接收缓冲 */
rt_free(recv_data);
return;
}
/* 在socket上进行监听 */
if (listen(sock, 5) == -1)
{
rt_kprintf("Listen error\n");
/* release recv buffer */
rt_free(recv_data);
return;
}
rt_kprintf("\nTCPServer Waiting for client on port 5000...\n");
while (stop != RT_TRUE)
{
sin_size = sizeof(struct sockaddr_in);
/* 接受一个客户端连接socket的请求,这个函数调用是阻塞式的 */
connected = accept(sock, (struct sockaddr *)&client_addr, &sin_size);
/* 返回的是连接成功的socket */
if (connected < 0)
{
rt_kprintf("accept connection failed! errno = %d\n", errno);
continue;
}
/* 接受返回的client_addr指向了客户端的地址信息 */
rt_kprintf("I got a connection from (%s , %d)\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
/* 客户端连接的处理 */
while (1)
{
/* 发送数据到connected socket */
ret = send(connected, send_data, strlen(send_data), 0);
if (ret < 0)
{
/* 发送失败,关闭这个连接 */
closesocket(connected);
rt_kprintf("\nsend error,close the socket.\r\n");
break;
}
else if (ret == 0)
{
/* 打印send函数返回值为0的警告信息 */
rt_kprintf("\n Send warning,send function return 0.\r\n");
}
/* 从connected socket中接收数据,接收buffer是1024大小,但并不一定能够收到1024大小的数据 */
bytes_received = recv(connected, recv_data, BUFSZ, 0);
if (bytes_received < 0)
{
/* 接收失败,关闭这个connected socket */
closesocket(connected);
break;
}
else if (bytes_received == 0)
{
/* 打印recv函数返回值为0的警告信息 */
rt_kprintf("\nReceived warning,recv function return 0.\r\n");
closesocket(connected);
break;
}
/* 有接收到数据,把末端清零 */
recv_data[bytes_received] = '\0';
if (strcmp(recv_data, "q") == 0 || strcmp(recv_data, "Q") == 0)
{
/* 如果是首字母是q或Q,关闭这个连接 */
closesocket(connected);
break;
}
else if (strcmp(recv_data, "exit") == 0)
{
/* 如果接收的是exit,则关闭整个服务端 */
closesocket(connected);
stop = RT_TRUE;
break;
}
else
{
/* 在控制终端显示收到的数据 */
rt_kprintf("RECEIVED DATA = %s \n", recv_data);
}
}
}
/* 退出服务 */
closesocket(sock);
/* 释放接收缓冲 */
rt_free(recv_data);
return ;
}
MSH_CMD_EXPORT(tcp_server, tcp server);
/*
* 程序清单:tcp 客户端
*
* 这是一个 tcp 客户端的例程
* 导出 tcp_client 命令到控制终端
* 命令调用格式:tcp_client URL PORT
* URL:服务器地址 PORT::端口号
* 程序功能:接收并显示从服务端发送过来的信息,接收到开头是 'q' 或 'Q' 的信息退出程序
*/
void tcp_client(int argc, char **argv)
{
int ret;
char *recv_data;
struct hostent *host;
int sock, bytes_received;
struct sockaddr_in server_addr;
const char *url;
int port;
if (argc < 3)
{
rt_kprintf("Usage: tcp_client URL PORT\n");
rt_kprintf("Like: tcp_client 192.168.1.125 20000\n");
return ;
}
url = argv[1];
port = strtoul(argv[2], 0, 10);
/* 通过函数入口参数url获得host地址(如果是域名,会做域名解析) */
host = gethostbyname(url);
/* 分配用于存放接收数据的缓冲 */
recv_data = rt_malloc(BUFSZ);
if (recv_data == RT_NULL)
{
rt_kprintf("No memory\n");
return;
}
/* 创建一个socket,类型是SOCKET_STREAM,TCP类型 */
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
/* 创建socket失败 */
rt_kprintf("Socket error\n");
/* 释放接收缓冲 */
rt_free(recv_data);
return;
}
/* 初始化预连接的服务端地址 */
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr = *((struct in_addr *)host->h_addr);
rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
/* 连接到服务端 */
if (connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
{
/* 连接失败 */
rt_kprintf("Connect fail!\n");
closesocket(sock);
/*释放接收缓冲 */
rt_free(recv_data);
return;
}
else
{
/* 连接成功 */
rt_kprintf("Connect successful\n");
}
while (1)
{
/* 从sock连接中接收最大BUFSZ - 1字节数据 */
bytes_received = recv(sock, recv_data, BUFSZ - 1, 0);
if (bytes_received < 0)
{
/* 接收失败,关闭这个连接 */
closesocket(sock);
rt_kprintf("\nreceived error,close the socket.\r\n");
/* 释放接收缓冲 */
rt_free(recv_data);
break;
}
else if (bytes_received == 0)
{
/* 默认 recv 为阻塞模式,此时收到0认为连接出错,关闭这个连接 */
closesocket(sock);
rt_kprintf("\nreceived error,close the socket.\r\n");
/* 释放接收缓冲 */
rt_free(recv_data);
break;
}
/* 有接收到数据,把末端清零 */
recv_data[bytes_received] = '\0';
if (strncmp(recv_data, "q", 1) == 0 || strncmp(recv_data, "Q", 1) == 0)
{
/* 如果是首字母是q或Q,关闭这个连接 */
closesocket(sock);
rt_kprintf("\n got a 'q' or 'Q',close the socket.\r\n");
/* 释放接收缓冲 */
rt_free(recv_data);
break;
}
else
{
/* 在控制终端显示收到的数据 */
rt_kprintf("\nReceived data = %s ", recv_data);
}
/* 发送数据到sock连接 */
ret = send(sock, send_data, strlen(send_data), 0);
if (ret < 0)
{
/* 接收失败,关闭这个连接 */
closesocket(sock);
rt_kprintf("\nsend error,close the socket.\r\n");
rt_free(recv_data);
break;
}
else if (ret == 0)
{
/* 打印send函数返回值为0的警告信息 */
rt_kprintf("\n Send warning,send function return 0.\r\n");
}
}
return;
}
MSH_CMD_EXPORT(tcp_client, tcp client);
2.1.2 TCP 客户端测试
- 网络调试助手设置为 TCP Server模式。并设置地址和端口号。
- 在串口调试助手中打开 TCP Client任务。接收电脑服务器发过去的数据。
- 在网络调试助手中输入要发送的消息点击发送,串口即可显示接收到的数据。
2.2.2 TCP 服务端测试
有问题,网络助手连接不上,后续再补。
百度说,把虚拟机的网络禁用就好了,这里测试没行,理由未知。