通过网络通信的方式,当上位机发出对应指令给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!
相应配制如图所示:
2、当生成模版后,给ethernetif.c中的 HAL_ETH_MspInit(ETH_HandleTypeDef* ethHandle) 中添加LAN8720A的使能代码,然后在主函数的死循环中添加 MX_LWIP_Process()函数就可以实现在电脑上ping通STM32了
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.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 */
}