文章目录
- 目的
- 基础说明
- 关键配置
- 关键代码
- 示例链接
- 总结
目的
以太网是比较常用到的功能,STM32系列单片机使用CubeMX配置使用以太网功能比非常方便。不过对于H7系列来说需要使能 DCache 才能启用LwIP,启用Cache后又会带来一些需要特别注意的事情。这篇文章将对相关内容进行介绍。
基础说明
STM32单片机以太网基础的配置启用等内容可以参考我之前的文章:
《STM32单片机示例:ETH_DP83848_DHCP_NonOS_Poll_F407》
目前虽然在CubeMX中需要配置 DCache 才能启用LwIP,但是配置生成的代码里直接注释掉主函数头部的 SCB_EnableDCache();
,以太网和LwIP也能正常工作,如果没有特别要求的话完全可以这样就直接用。
如果要使用 DCache 的话就需要特别注意DMA和CPU读写同一片内存数据不同步的问题了。
一种简单的解决方案是在 low_level_output
中使用 SCB_CleanDCache...
将Cache中数据更新到SRAM;在 low_level_input
中将使用 SCB_InvalidateDCache...
将SRAM中数据加载到Cache。
当然只要合理设置MPU,也可以不用清Cache和无效化Cache操作,比如官方例程的设置:
https://github.com/stm32-hotspot/STM32H7-LwIP-Examples
需要注意的是上面官方例程中虽然都是H7系列的,但是根据内存大小的不同,内存分布设置也是不同的,可以通过全局搜索 ETH_CODE
关键词来查看各个项目中相关的改动。
上面官方例程都是使用了 FreeRTOS ,下面将不使用操作系统的情况进行演示。
关键配置
时钟:
Cache和MPU:
以太网:
LwIP:
堆栈:
关键代码
main.c
中一些手动添加的代码:
#include "main.h"
#include "lwip.h"
UART_HandleTypeDef huart6;
/* With GCC, small printf (option LD Linker->Libraries->Small printf set to 'Yes') calls __io_putchar() */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch) // 实现__io_putchar函数
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 0xFFFF); // for printf()
return ch;
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_LWIP_Init(); // 初始化网络功能
MX_USART6_UART_Init();
while (1)
{
MX_LWIP_Process(); // 处理网络相关事务
static uint32_t previous = 0;
if((HAL_GetTick() - previous)>=1000)
{
previous = HAL_GetTick();
extern struct netif gnetif; // 网卡对象,在lwip.c文件中定义
// 打印时间和IP地址
printf("%ld - loop: ip addr %s\n", HAL_GetTick(), ip4addr_ntoa(netif_ip_addr4(&gnetif)));
}
}
}
lwip.c
中一些手动添加的代码:
/**
* @brief Notify the User about the network interface config status
* @param netif: the network interface
*/
static void ethernet_link_status_updated(struct netif *netif)
{
if (netif_is_up(netif))
{
printf("%ld - link status callback: netif_is_up!\n", HAL_GetTick());
}
else /* netif is down */
{
printf("%ld - link status callback: netif_is_down!\n", HAL_GetTick());
}
}
STM32H743ZITX_FLASH.ld
中一些手动添加的代码:
...
} >RAM_D1
/* ETH_CODE: add placement of DMA descriptors and RX buffers */
.lwip_sec (NOLOAD) :
{
. = ABSOLUTE(0x30040000);
*(.RxDecripSection)
. = ABSOLUTE(0x30040060);
*(.TxDecripSection)
. = ABSOLUTE(0x30040200);
*(.Rx_PoolSection)
} >RAM_D2
/* Remove information from the standard libraries */
/DISCARD/ :
...
ethernetif.c
中一些手动添加的代码:
/* USER CODE BEGIN 2 */
/* ETH_CODE: placement of RX_POOL
* Please note this was tested only for GCC compiler.
* Additional code needed in linkerscript for GCC.
*
* Also this buffer can be placed in D1 SRAM
* if there is not sufficient space in D2.
* This can be case of STM32H72x/H73x devices.
* However the 32-byte alignment should be forced.
* Below is example of placement into BSS section
*
* . = ALIGN(32);
* *(.Rx_PoolSection)
* . = ALIGN(4);
* _ebss = .;
* __bss_end__ = _ebss;
* } >RAM_D1
*/
#if defined ( __ICCARM__ ) /*!< IAR Compiler */
#pragma location = 0x30040200
extern u8_t memp_memory_RX_POOL_base[];
#elif defined ( __CC_ARM ) /* MDK ARM Compiler */
__attribute__((at(0x30040200)) extern u8_t memp_memory_RX_POOL_base[];
#elif defined ( __GNUC__ ) /* GNU Compiler */
__attribute__((section(".Rx_PoolSection"))) extern u8_t memp_memory_RX_POOL_base[];
#endif
/* USER CODE END 2 */
上面的修改可能会在有些编译环境下保存,可以 lwipopts.h
中一些手动添加的代码:
/* USER CODE BEGIN 1 /
#undef LWIP_PROVIDE_ERRNO
#define LWIP_ERRNO_STDINCLUDE
/ USER CODE END 1 */
示例链接
仓库地址: https://github.com/NaisuXu/STM32_MCU_Examples
本文中的示例位于仓库中 ETH_LAN8742_DHCP_NonOS_Poll_H743
。
总结
通过上面的配置和改动就网络部分就可以正常使用了。之后网络应用的开发同样还是参考官方例程和文档:
《UM1713 使用 LwIP TCP/IP 栈,在 STM32Cube 上开发应用》