云计算百科
云计算领域专业知识百科平台

Cubemx实现LAN8720A+LWIP+Freertos+tcp服务器创建

一、实验平台

用的STM32G431CBU6主板,以太网卡PHY层芯片是LAN8720A。

二、配置步骤

选择芯片就略过了,第一步先配置一个串口,直接配成异步模式,其他默认。(方便调试用,记得给串口的地线接上)

因为添加了freertos实时操作系统,而freertos用的是systick作为时钟源,所以要将HAL库时钟源改为其他定时器。

RCC可以不配置,如果需要更高频率也可以配

配置ETH时,配置为RMII(其他的没有试过),注意MAC地址别和其他需要联机通讯单片机重复了,PHY Address要根据芯片的PHYAD0引脚是否悬空来配置,悬空配置为0,不悬空为1。

配置LWIP:先将DHCP关闭,用静态地址的方式配置IP地址、子网掩码、网关。ICMP要设置为使能,配置才可以ping通。UDP和TCP模式需要哪个就可以配置哪个使能。

配置PHY芯片复位引脚

在代码中加入这一段,就是将连接PHY芯片复位引脚的那个GPIO拉低拉高一下。

HAL_GPIO_WritePin(RMII_NRST_GPIO_Port, RMII_NRST_Pin, GPIO_PIN_RESET);
HAL_Delay(55);
HAL_GPIO_WritePin(RMII_NRST_GPIO_Port, RMII_NRST_Pin, GPIO_PIN_SET);
HAL_Delay(55);

还需要在代码中加上MX_LWIP_Process();和tcp_server_init();

tcp_server.c相关函数如下

#include "stm32f1xx_hal.h"
#include "lwip.h"
#include "tcp.h"
#include "string.h"
#include "tcp_server.h"

/* 定义端口号 */
#define TCP_LOCAL_PORT 8000 /* 本地端口 */

/******************************************************************************
* 描述 : 接收回调函数
* 参数 : –
* 返回 : –
******************************************************************************/
static err_t tcp_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
if (p == NULL) //检测到对方主动关闭连接时,也会调用recv函数,此时p为空
{
printf("ip %d:%d:%d:%d port:%d 客户端断开连接\\r\\n",
*((uint8_t *)&tpcb->remote_ip.addr),
*((uint8_t *)&tpcb->remote_ip.addr + 1),
*((uint8_t *)&tpcb->remote_ip.addr + 2),
*((uint8_t *)&tpcb->remote_ip.addr + 3),
tpcb->remote_port);

//tcp_write(tpcb, "tcp client closed", strlen("tcp client closed"), 0);

tcp_close(tpcb);

err = ERR_CLSD;
}
else if (err != ERR_OK)
{
pbuf_free(p);
}
else
{
//更新窗口
tcp_recved(tpcb, p->tot_len); //读取数据的控制块 得到所有数据的长度

void *p_buf=p->payload;
uint8_t *data = (uint8_t *)p_buf;
printf("len:%d \\n", p->tot_len); // 以十六进制形式打印每个字节
for (u16_t i = 0; i < p->tot_len; i++) {
printf("data[%d]=%d \\n", i , data[i]); // 以十六进制形式打印每个字节
}

//tcp_write(tpcb, "tcp send", strlen("tcp send"), 0);

memset(p->payload, 0 , p->tot_len);
pbuf_free(p);
}
return err;
}

/******************************************************************************
* 描述 : 客户端接入回调函数
* 参数 : –
* 返回 : –
******************************************************************************/

static err_t tcp_server_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
{

printf("ip %d:%d:%d:%d port:%d 客户端连接\\r\\n",
*((uint8_t *)&newpcb->remote_ip.addr),
*((uint8_t *)&newpcb->remote_ip.addr + 1),
*((uint8_t *)&newpcb->remote_ip.addr + 2),
*((uint8_t *)&newpcb->remote_ip.addr + 3),
newpcb->remote_port);

//tcp_write(newpcb, "tcp client connected", strlen("tcp client connected"), 0);

/* 注册接收回调函数 */
tcp_recv(newpcb, tcp_server_recv);
return ERR_OK;
}

/******************************************************************************
* 描述 : 创建tcp服务器
* 参数 : 无
* 返回 : 无
******************************************************************************/

void tcp_server_init(void)
{
struct tcp_pcb *tpcb;

/* 创建tcp控制块 */
tpcb = tcp_new();

if (tpcb != NULL)
{
err_t err;

/* 绑定端口接收,接收对象为所有ip地址 */
err = tcp_bind(tpcb, IP_ADDR_ANY, TCP_LOCAL_PORT);

if (err == ERR_OK)
{
/* 监听 */
tpcb = tcp_listen(tpcb);

/* 注册接入回调函数 */
tcp_accept(tpcb, tcp_server_accept);
printf("服务器创建成功\\r\\n");

}
else
{
tcp_close(tpcb);
}

}
}

tcp_server.h相关函数如下

#ifndef _TCPECHO_H_
#define _TCPECHO_H_

void tcp_server_init(void);

#endif

现在可以尝试ping通

如图就ping成功了

创建一个客户端,去连接到服务器上,得到了如图效果。

接下来添加freertos,我这里选择的时CMSIS_V1,V2没试过,注意这个堆空间大小尽量给大一点,不然会出错,还没找到原因

添加一个创建tcp服务器任务

将原先代码内的这两个函数屏蔽

再在任务函数里面添加创建服务器函数

新的tcp_server.c相关函数如下

#include "tcp_server.h"
#include "lwip/sockets.h"
#include <ctype.h>

char ReadBuff[BUFF_SIZE];

/* TCP服务器端(server)任务 */
void vTCPServer_Task(void)
{
int sfd, cfd;
int read_len; // 读的数据长度
struct sockaddr_in server_addr, client_addr; // 服务器和客户端的结构体
socklen_t client_addr_len;

err_t err;
// 创建socket
sfd = socket(AF_INET, SOCK_STREAM, 0); // IPV4 socker连接 默认协议
if(sfd < 0)
{
printf("创建失败\\r\\n");
}
else
{
printf("创建成功\\r\\n");
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 从本机获取
// 绑定socket
err = bind(sfd, (struct sockaddr *)&server_addr, sizeof(server_addr)); // 文件描述符 IP地址加端口号 addr长度 返回值
if(err < 0)
{
printf("绑定失败\\r\\n");
}
else
{
printf("绑定成功 %d \\r\\n",err);
}
// 监听socket
err = listen(sfd, 5); // 文件描述符 握手队列总和
if(err < 0)
{
printf("监听失败\\r\\n");
}
else
{
printf("监听成功 %d \\r\\n",err);
}

printf("等待客户端连接\\r\\n");

while(1)
{
client_addr_len = sizeof(client_addr);
cfd = accept(sfd, (struct sockaddr *)&client_addr, &client_addr_len); // 文件描述符 IP地址加端口号 返回值

printf("IP:%s 端口号%d 客户端连接成功\\r\\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

while (1) // 等待客户端发送数据,然后进行大小写转换,再写回客户端
{
read_len = read(cfd, ReadBuff, BUFF_SIZE);
if(read_len <= 0)
{
break;
}
for (int i = 0; i < read_len; i++)
{
ReadBuff[i] = toupper(ReadBuff[i]); // 小写转换为大写
}
write(cfd, ReadBuff, read_len);
printf("IP:%s 端口号%d 客户端发送数据 %s\\r\\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port),ReadBuff);
}

printf("IP:%s 端口号%d 客户端断开连接\\r\\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

closesocket(cfd);

}
}

新的tcp_server.h相关函数如下

#ifndef __SOCKET_TCP_SERVER_H
#define __SOCKET_TCP_SERVER_H

#define SERVER_PORT 8000 // 端口号
#define BUFF_SIZE 1024

void vTCPServer_Task(void);

#endif

最终效果如下图

引脚配置图和时钟配置图如下

三、代码地址(暂无)

赞(0)
未经允许不得转载:网硕互联帮助中心 » Cubemx实现LAN8720A+LWIP+Freertos+tcp服务器创建
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!