Bootstrap

STM32F4 + CubeMX + RTOS + LwIP

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的完整实现已经整理完毕,只是粗略地过了一遍流程,许多细节并未涉及,后续开发过程中如有新的问题也会继续与各位分享,谢谢大家。

;