使用stm32cubeide stm32f407 lan8720a freertos lwip 实现tcp客户端、服务端及网络数据转串口数据过程详解
1前言
项目需要使用MCU实现网络功能,后续确定方案stm32f407 外接lan8720a实现硬件平台搭建,针对lan8720a也是用的比较多的phy,网上比较多的开发板,硬件上都是选用了这个phy,项目周期比较短,选用了这个常用phy。记得十年前,刚参加工作那会接触的第一个项目,就是MCU实现网络控制协议,当时lwip也不是很流行呢,网络协议栈只能手撕了,当时硬着头皮写,最多也写到UDP,不过对付当时的项目也够用了。抽时间,可以把当时手撕UDP的协议拿出来再看看。技术都是不断的更新迭代,十年后,现在stm32cubeide、stm32cubemx等软件都集成了lwip,这让开发更快速、便捷。但是作为过来人建议,初学者,最好刚开始还是使用固件库、lwip的源码移植,这样对系统理解的更深刻,毕竟网络协议栈真是一门大学问。
文章使用工程下载地址
tcp客户端,具体测试过程参考5.3章节
https://download.csdn.net/download/li171049/90592463
tcp服务端,具体测试过程参考5.4章节
https://download.csdn.net/download/li171049/90592486
2硬件设计
lan8720a的硬件原理图比较成熟,没啥特别要注意的,lan8720a和stm32f407采用rmii的接口。
stm32f407采用串口6作为调试串口,直接通过ttl,也就是该串口的数据可以通过网络数据发送出来,也可以接收到网络数据通过该串口打印出来。
3软件设计
采用STM32CubeIDE 1.17.0,针对硬件配置如下
3.1时钟配置
3.2总体配置
如下图所示
3.3Phy的复位管教
3.4ETH的配置
如下图所示,选用RMII模式,这里需要注意,Rx Mode的配置为默认即可,针对采用操作系统、和不使用操作系统,该选项的配置会自动更改。
3.5freeRTOS的配置
这里增大默认任务堆栈,不然会出现堆栈溢出错误。
3.6Lwip的配置
主要是采用静态IP,不开启DHCP,针对电脑进行ping包测试。
4关键代码
4.1Phy开机复位
针对phy复位IO,进行上电复位操作
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET);
HAL_Delay(55);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET);
HAL_Delay(55);
4.2tcp客户端初始化
这里尝试使用raw api进行编程,但是测试效果不理想,太差了,不够稳定,很多时候不能建立链接。排查问题后发现,tcp_new();后的网卡地址默认为0,无法和远端地址建立路由关系,Don't even try to send a SYN packet if we have no route since that will fail。就替换为NETCONN API进行tcp客户端设计。
采用NETCONN API实现tcp 客户端,考虑tcp连接的断掉重连,优化设计,实现客户端断弦、断电,开机重连等设计。
初始化代码如下
/* init code for LWIP */
MX_LWIP_Init();
/* USER CODE BEGIN 5 */
/* Infinite loop */
osDelay(2000);
//tcp_client_init();//初始化tcp客户端
client_init();
for(;;)
{
osDelay(1000);
time_cnt++;
printf("time_cnt = %ld\r\n", time_cnt);
}
/* USER CODE END 5 */
主要功能代码如下
while (1)
{
conn = netconn_new(NETCONN_TCP);
if (conn == NULL)
{
printf("Create conn failed!\r\n");
osDelay(1000);
continue;
}
IP4_ADDR(&ipaddr,192,168,1,22);
ret = netconn_connect(conn,&ipaddr,TCP_CLIENT_PORT);
if (ret != ERR_OK)
{
printf("Connect failed!\r\n");
netconn_disconnect(conn);
netconn_close(conn);
netconn_delete(conn);
osDelay(1000);
continue;
}
//printf("Connect to iperf server successful!\r\n");
while (1)
{
//客户端发送数据
ret = netconn_write(conn,send_buf,sizeof(send_buf),0);
if(ret != ERR_OK)
{
netconn_disconnect(conn);
netconn_close(conn);
netconn_delete(conn);
printf("netconn_write error!\r\n");
osDelay(1000);
break;
}
osDelay(1000);
//客户端接收数据
if(1)
{
struct netbuf *buf;
void *data;
u16_t len;
while ((err = netconn_recv(conn, &buf)) == ERR_OK)
{
do
{
netbuf_data(buf, &data, &len);
err= netconn_write(conn, data, len, NETCONN_COPY);
printf("Rev:%s\r\n", (char*)data);//接收数据打印到串口
}while (netbuf_next(buf) >= 0);
netbuf_delete(buf);
}
}
}
}
4.3tcp服务端初始化
Tcp服务端初始化
void StartDefaultTask(void *argument)
{
/* init code for LWIP */
MX_LWIP_Init();
/* USER CODE BEGIN 5 */
/* Infinite loop */
osDelay(2000);
tcpecho_init();
for(;;)
{
osDelay(1000);
time_cnt++;
printf("time_cnt = %ld\r\n", time_cnt);
}
/* USER CODE END 5 */
}
5测试记录
5.1电脑IP配置
同一网段,192.168.1.22
5.2ping包测试
电脑的的ping包测试
5.3网络调试助手模拟tcp服务端,STM32为tcp客户端
使用网络调试助手,作为tcp服务端监听192.169.1.22:8000端口号,
测试如下图所示,可以看到能够链接成功后,接收到This is a TCP Client test...
同时网络调试助手发送,QQQ,可以看到串口能够收到对应字样,STM32收到QQQ后又返回发送给网络调试助手。同时可以看到串口也打印了接收的数据信息。
5.4网络调试助手模拟tcp客户端,STM32为tcp服务端
使用网络调试助手,作为tcp客户端连接192.169.1.70:5001端口号,
测试如下图所示,可以看到能够链接成功后,接收到The server is connected...
同时网络调试助手发送,QQQ,可以看到串口能够收到对应字样,STM32收到QQQ后又返回发送给网络调试助手。同时可以看到串口也打印了接收的数据信息。
6资源下载
文章使用工程下载地址
tcp客户端,具体测试过程参考5.3章节
https://download.csdn.net/download/li171049/90592463
tcp服务端,具体测试过程参考5.4章节
https://download.csdn.net/download/li171049/90592486