20240305 更新
后续文章对该文中一些错误做了修正,本文未作修改,点击这里->二探LWIP协议栈-CSDN博客
一、项目背景
项目需要使用到ModbusTCP,使用的是MCU是F407,内置MAC控制器,外挂PHY芯片。需要先实现tcp/ip功能,再增加modbus协议栈。
二、工具介绍
以前自己做实验也调试过lwip,但是要不就是软件问题,要不就是硬件问题,始终没有完整调通过一次,这次趁项目需求,搞来了一块开发板,杜绝硬件问题,软件有点问题但网上前辈们已经把踩坑的地方都走过一遍了,只要自己再整合整合就好。下面把需要用到的工具列一下,基本就是那几样:
1.CubeMX(v6.9)+ keil
2.sscom
3.正点原子探索者F4开发板
三、配置步骤
(一)CubeMX配置
1、软件版本说明
本人使用的版本是6.9.1,与较老的版本有点不同,注意即可。
2、创建工程,配置时钟
使用外部高速时钟
3、配置时基和调试端口
使用sw调试口,TIM6作为HAL库的时基
4、配置ETH外设
我拿到的探索者开发板使用的PHY芯片是LAN8720A,接口形式是RMII,需要选择正确。打开中断,并且默认生成的引脚配置与开发板实际不符,需要修改过来。
注:之前的版本需要手动更改引脚速度配置,6.9所有的引脚都默认配置位very high,无需修改。
5、配置PHY芯片复位引脚
在配置PHY芯片之前需要进行硬件复位,否则有可能会出现配置失败的情况,开发板的复位引脚是PD3,配置成OUTPUT即可。
6、配置FREERTOS
使用CMSIS_V2接口,并且将TOTAL_HEAP_SIZE 定义为50k,即51200,小了会直接内存溢出进入hardfault,实测30k仍然会崩,各位同学也可以自己测试一下合适的大小,ZGT6内存够大,直接拉满。其余默认即可。
7、配置LwIP协议栈
这里没有使用DHCP,使用的固定IP地址。platform选择LAN8742,和8720通用。其余默认。
8、时钟树配置
外部晶振是8M,HCLK配置为160M。(个人习惯,160M是因为这个频率的时钟可以使CAN总线的500k准确)。
至此cubemx的配置已经完成,生成MDK工程并打开,下面的步骤在keil里完成。
(二)keil配置
1、使能微库
2、配置栈空间
默认是0x400,修改为0x4000,小了会溢出进入hardfault,直接拉满。
3、增加复位操作
复位引脚拉低20ms。
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_3, GPIO_PIN_SET);
HAL_Delay(20);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_3, GPIO_PIN_RESET);
HAL_Delay(20);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_3, GPIO_PIN_SET);
HAL_Delay(20);
4、实现tcp server功能
找到tcpecho示例代码如下,加入工程,该代码实现了tcp服务端功能,创建端口为7的服务端,ip地址为自己配置的地址,并监听,将客户端发送的信息原封不动地回复。tcpecho_init()初始化操作放到MX_FREERTOS_Init(void)函数内。
端口号在这个函数里被定义,可以按照实际需求修改。
/* Bind connection to well known port number 7. */
err = netconn_bind(conn, NULL, 7);
完整代码:
/*
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Adam Dunkels <[email protected]>
*
*/
#include "lwip/opt.h"
#if LWIP_NETCONN
#include "lwip/sys.h"
#include "lwip/api.h"
#define TCPECHO_THREAD_PRIO ( tskIDLE_PRIORITY + 4 )
/*-----------------------------------------------------------------------------------*/
static void tcpecho_thread(void *arg)
{
struct netconn *conn, *newconn;
err_t err, accept_err;
struct netbuf *buf;
void *data;
u16_t len;
LWIP_UNUSED_ARG(arg);
/* Create a new connection identifier. */
conn = netconn_new(NETCONN_TCP);
if (conn!=NULL)
{
/* Bind connection to well known port number 7. */
err = netconn_bind(conn, NULL, 7);
if (err == ERR_OK)
{
/* Tell connection to go into listening mode. */
netconn_listen(conn);
while (1)
{
/* Grab new connection. */
accept_err = netconn_accept(conn, &newconn);
/* Process the new connection. */
if (accept_err == ERR_OK)
{
while (netconn_recv(newconn, &buf) == ERR_OK)
{
do
{
netbuf_data(buf, &data, &len);
netconn_write(newconn, data, len, NETCONN_COPY);
}
while (netbuf_next(buf) >= 0);
netbuf_delete(buf);
}
/* Close connection and discard connection identifier. */
netconn_close(newconn);
netconn_delete(newconn);
}
}
}
else
{
netconn_delete(newconn);
}
}
}
/*-----------------------------------------------------------------------------------*/
void tcpecho_init(void)
{
sys_thread_new("tcpecho_thread", tcpecho_thread, NULL, DEFAULT_THREAD_STACKSIZE, TCPECHO_THREAD_PRIO);
}
/*-----------------------------------------------------------------------------------*/
#endif /* LWIP_NETCONN */
至此,软件已经搭建完毕,下面进行测试。
四、测试
(一)ping命令测试
将程序下载到开发板,并将电脑与开发板接到同一个交换机,将电脑的ip地址与开发板的ip地址设到同一网段,运行cmd命令行,ping开发板。
(二)通讯测试
打开sscom软件,选择TCPClient,远程框填写开发板的IP地址与端口号,连接上后发送测试字符,开发板将其原样发回,测试通过。
五、总结
至此lwip的完整实现已经整理完毕,只是粗略地过了一遍流程,许多细节并未涉及,后续开发过程中如有新的问题也会继续与各位分享,谢谢大家。