一、实验平台
用的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
最终效果如下图
引脚配置图和时钟配置图如下
评论前必须登录!
注册