一、目标实现

     通过网络通信的方式,当上位机发出对应指令给STM32,STM32根据收到的指令来执行对应的操作(例如:亮灯、灭灯、闪灯等)。还有可以将STM32连上路由器,当电脑连上路由器后,也可以通过上位机给STM32发送命令。

二、实现步骤

1、首先使用STM32CubeMX按照对应的开发板生成对应的程序模版。

本人使用的正点原子的STM32F407ZGT6探索者开发板,对应的以太网接口是LAN8720A

在STM32CubeMX中选上ETH、Lwip(不带操作系统)、对应需要点灯的管脚。

  • ETH配制中:按照开发板LAN8720A对应的手册,将特殊标志位地址与芯片的信息一一配对选上(其实Cube生成的默认参数和LAN8720A是对应的上的,如果是其他PHY芯片,则需要查看手册来配制)
  • Lwip配制中:不使用DHCP,使用手动配制的IP。

 在这一步中,其实以及把要做的工作完成了90%了!只剩下在应用层面使用LwIP的raw api接口进行开发了。

注意:PHY Address Value 需要设置成0!

 相应配制如图所示:

esp32 连接功放_STM32

esp32 连接功放_STM32CubeMX_02

 2、当生成模版后,给ethernetif.c中的 HAL_ETH_MspInit(ETH_HandleTypeDef* ethHandle) 中添加LAN8720A的使能代码,然后在主函数的死循环中添加 MX_LWIP_Process()函数就可以实现在电脑上ping通STM32了

esp32 连接功放_STM32_03

esp32 连接功放_STM32CubeMX_04

 3、阅读LwIP官方给的RAW API 资料,实现在应用层上建立tcp/ip的应用

这里的流程与其他平台上tcp/ip或者upd通讯有很多的相似性

  • 首先先创建  struct tcp_pcb *tcp_new(void)  创建pcb块(类似创建socket)
  • 其次绑定IP err_t tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)//PCB绑定指定的ip 与 端口号
  • 然后看作为 客户端 还是 服务器
  • 若是作为服务器 则使用进行监听 struct tcp_pcb *tcp_listen(struct tcp_pcb *pcb)//返回新的pcb块

       然后当有连接进来后 则使用建立连接 :

       void tcp_accept(struct tcp_pcb *pcb, err_t (* accept)(void *arg, struct tcp_pcb *newpcb, err_t err)) //在回调函数中来执行建立连接后要执行的事情

      连接建立后则可以开始进行读写操作 :

      err_t tcp_write(struct tcp_pcb *pcb, const void *dataptr, u16_t len,  u8_t apiflags)//直接往PCB块中国写入len长度的dataptr指向内容

      void tcp_sent(struct tcp_pcb *pcb, err_t (* sent)(void *arg, struct tcp_pcb *tpcb, u16_t len)) //在回调函数中来执行接受到消息后要执行的事情

      void tcp_recv(struct tcp_pcb *pcb, err_t (* recv)(void *arg, struct tcp_pcb *tpcb,  struct pbuf *p, err_t err))//在回调函数中来执行接受到消息后要执行的事情

      void tcp_recved(struct tcp_pcb *pcb, u16_t len) //从PCB块中接受len长度的数据

 三、有关LwIP的一些理解与生成模版代码的注释

esp32 连接功放_Lwip_05

//lwip.c 
void MX_LWIP_Init(void)
{
  /* IP addresses initialization */
  //ip地址、子网掩码、网关
  IP_ADDRESS[0] = 192;
  IP_ADDRESS[1] = 168;
  IP_ADDRESS[2] = 1;
  IP_ADDRESS[3] = 100;
  NETMASK_ADDRESS[0] = 255;
  NETMASK_ADDRESS[1] = 255;
  NETMASK_ADDRESS[2] = 255;
  NETMASK_ADDRESS[3] = 0;
  GATEWAY_ADDRESS[0] = 192;
  GATEWAY_ADDRESS[1] = 168;
  GATEWAY_ADDRESS[2] = 1;
  GATEWAY_ADDRESS[3] = 1;
  
  /* Initilialize the LwIP stack without RTOS */
  //初始化lwip,初始化了mem、pbuf、netif、ip、tcp与udp,检查延迟时间的舒适化等等
  lwip_init();

  /* IP addresses initialization without DHCP (IPv4) */
  //将IP等信息转化成为 大端序 添加到IPV4信息中、ipaddr、netmask、gw
  IP4_ADDR(&ipaddr, IP_ADDRESS[0], IP_ADDRESS[1], IP_ADDRESS[2], IP_ADDRESS[3]);
  IP4_ADDR(&netmask, NETMASK_ADDRESS[0], NETMASK_ADDRESS[1] , NETMASK_ADDRESS[2], NETMASK_ADDRESS[3]);
  IP4_ADDR(&gw, GATEWAY_ADDRESS[0], GATEWAY_ADDRESS[1], GATEWAY_ADDRESS[2], GATEWAY_ADDRESS[3]);

  /* add the network interface (IPv4/IPv6) without RTOS */
  //添加网络接口到 lwip netifs 列表中,调用回调函数来初始化以太网,在netif 网络接口结构体中放置input
  //其中 ethernetif_init 中包括了 low_level_init(用于初始化 以太网管脚接口、MAC信息、使能DMA与MAC的数据交互)
  //其中 ethernet_input 用于接收以太网接口的数据帧
  netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, ðernet_input);

  /* Registers the default network interface */
  //将网络接口设置为默认的网络接口
  netif_set_default(&gnetif);

  if (netif_is_link_up(&gnetif))//查看是否有链接
  {
    /* When the netif is fully configured this function must be called */
	  //建立一个可以处理的网络接口
    netif_set_up(&gnetif);
  }
  else
  {
    /* When the netif link is down this function must be called */
	  //关闭网络接口
    netif_set_down(&gnetif);
  }

/* USER CODE BEGIN 3 */

/* USER CODE END 3 */
}