从零开始Cubemx配置STM32搭载freeRTOS以及lwip实现网络通信
引言
看见名字自己就笑了,标题的长度已经快赶上日本轻小说了,正好最近在追无职转生。
说回正题,上篇文章介绍了如何从零开始使用CubeMX生成一个带有freeRTOS操作系统的程序,嵌入式进阶指南以及必备知识学习路线,朋友们可以去看一下,这篇文章我将开始重头戏,如何在STM32F407上移植lwip实现TCP通信,最终实现一个httpd服务器。
自己写的另外两篇文章
从零开始Cubemx配置STM32搭载freeRTOS实现多路ADC(一)
从零开始使用CubeMX配置STM32使用lwip实现httpd服务器(三)
CubeMX配置STM32实现httpd服务器CGI功能并使用网页控制STM32单片机(四)
CubeMX配置以太网以及LWIP实现一个回环功能(裸机)
实现这个功能需要配置2部分,一部分的EHT也就是以太网的配置,另一部分是lwip协议栈的配置
ETH配置
ETH的配置主要有三个器件,单片机,物理网卡(在本文中我们使用的是LAN8720A芯片)以及网口。在这里面最重要的是物理网卡,单片机ETH的配置主要以物理网卡为主,与其电路连接有关系。LAN8720A的知识主要看这一篇文章 https://blog.youkuaiyun.com/sinat_20265495/article/details/79628283?spm=1001.2014.3001.5506
现在开始使用Cubemx配置ETH,在Connectivity选择中选择ETH
MII(Medium Independent Interface)是IEEE802.3u规定的一种介质无关接口,主要作用是连接介质访问控制层(MAC)子层与物理层(PH-Y)之间的标准以太网接口,负责MAC和PHY之间的通信。由于MII需要多达16根信号线,由此产生的I/O口需求及功耗较大,有必要对MII引脚数进行简化,因此提出了RMII(Reduced Medium Independent Interface,精简的介质无关接口),即简化了的MII。更详细的看这篇文章 https://blog.youkuaiyun.com/fun_tion/article/details/70270632
采用MII接口,PYH的时钟频率要求25M,不需要与MAC层时钟一致。
采用RMII接口,PYH的时钟频率要求50M,需与MAC层时钟一致,通常从MAC层获取该时钟源。
现在一般都用RMII模式
LWIP配置
接下来生成代码就可以了
在主函数中,添加这两个函数
实现回环代码,新建一个tcpecho.c文件,代码
#include "tcpecho.h"
#include "lwip/netif.h"
#include "lwip/ip.h"
#include "lwip/tcp.h"
#include "lwip/init.h"
#include "netif/etharp.h"
#include "lwip/udp.h"
#include "lwip/pbuf.h"
#include <stdio.h>
#include <string.h>
static err_t tcpecho_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
if (p != NULL)
{
/* 更新窗口*/
tcp_recved(tpcb, p->tot_len);
/* 返回接收到的数据*/
tcp_write(tpcb, p->payload, p->tot_len, 1);
memset(p->payload, 0 , p->tot_len);
pbuf_free(p);
}
else if (err == ERR_OK)
{
return tcp_close(tpcb);
}
return ERR_OK;
}
static err_t tcpecho_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
{
tcp_recv(newpcb, tcpecho_recv);
return ERR_OK;
}
void TCP_Echo_Init(void)
{
struct tcp_pcb *pcb = NULL;
/* 创建一个TCP控制块 */
pcb = tcp_new();
/* 绑定TCP控制块 */
tcp_bind(pcb, IP_ADDR_ANY, TCP_ECHO_PORT);
/* 进入监听状态 */
pcb = tcp_listen(pcb);
/* 处理连接 */
tcp_accept(pcb, tcpecho_accept);
}
这样裸机就成功了,可以ping成功
CubeMX配置以太网以及LWIP实现一个回环功能(freeRTOS)
在CubeMX中打开freeRTOS后,lwip会自动切换到带有os的模式,其他的和裸机一样。不懂如何移植freeRTOS的去看我上一篇文章。
生成代码,可以看到LWIP初始化函数以及被加入默认线程中了。
搭载freeRTOS后就可以使用我们最熟悉的socket编程了,这时tcpecho.c可以写成,这个代码,使用过socket编程的人应该就很熟悉了,实验效果和裸机一样。推荐使用带操作系统的框架进行网络编程。
#include "tcpecho.h"
#include "lwip/opt.h"
#if LWIP_SOCKET
#include <lwip/sockets.h>
#include "lwip/sys.h"
#include "lwip/api.h"
/*-----------------------------------------------------------------------------------*/
#define RECV_DATA (1024)
static void tcpecho_thread(void *arg)
{
int sock = -1,connected;
char *recv_data;
struct sockaddr_in server_addr,client_addr;
socklen_t sin_size;
int recv_data_len;
printf("本地端口号是%d\n\n",LOCAL_PORT);
recv_data = (char *)pvPortMalloc(RECV_DATA);
if (recv_data == NULL)
{
printf("No memory\n");
goto __exit;
}
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
printf("Socket error\n");
goto __exit;
}
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(LOCAL_PORT);
memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
{
printf("Unable to bind\n");
goto __exit;
}
if (listen(sock, 5) == -1)
{
printf("Listen error\n");
goto __exit;
}
while(1)
{
sin_size = sizeof(struct sockaddr_in);
connected = accept(sock, (struct sockaddr *)&client_addr, &sin_size);
printf("new client connected from (%s, %d)\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
{
int flag = 1;
setsockopt(connected,
IPPROTO_TCP, /* set option at TCP level */
TCP_NODELAY, /* name of option */
(void *) &flag, /* the cast is historical cruft */
sizeof(int)); /* length of option value */
}
while(1)
{
recv_data_len = recv(connected, recv_data, RECV_DATA, 0);
if (recv_data_len <= 0)
break;
printf("recv %d len data\n",recv_data_len);
write(connected,recv_data,recv_data_len);
}
if (connected >= 0)
closesocket(connected);
connected = -1;
}
__exit:
if (sock >= 0) closesocket(sock);
if (recv_data) free(recv_data);
}
/*-----------------------------------------------------------------------------------*/
void tcpecho_init(void)
{
sys_thread_new("tcpecho_thread", tcpecho_thread, NULL, 512, 4);
}
结论
还是那个观点,在进行stm32开发的时候cubemx可以省去大量的库移植的操作,可以大幅缩减开发周期,把时间更集中在任务上,在这篇文章中,本文分别在裸机以及带有freeRTOS的情况下实现了TCP连接,并简单做了回环,推荐使用操作系统进行网络开发,因为可以用socket编程,方便在Linux等系统上进行使用。
在下一篇文章中,我将实现一个httpd服务器,可以用浏览器直接访问。