注意本文以分析为主,主要讲解实现过程和注意点,并非从零开始
标题虽然是移植,但是完全参考官方提供的一份例程,即Azure_RTOS_6.0_STM32F746G-DISCO_STM32CubeIDE_Samples_2020_05_29.zip,其主控是STM32F746G,我用的是STM32F407,不过差别不大。
目前已经能够成功ping通网络,但是ping不了大包,测试出来大概包长度只能到1470字节左右,此仅为个人笔记,所以可能存在不妥或者理解错误的地方,后续我会不断完善并更正此文章。
先下载例程和next(非netxduo,不过估计没有什么差别)的源码,下载链接参考前一篇文章的链接,并且MDK工程也是基于前一篇文章移植过后作为模板,可在前一篇文章文末链接获得。
移植前最好先使用一个裸机程序测试下你的网卡能否正常工作,我的phy是LAN8720A,用的STM32F407自带的MAC控制器,这个过程很简单,只要寄存器地址设置正确,复位引脚的状态正确,就基本上没什么问题了,如果接上网线后能够看到接口的灯闪烁就可以继续后面的步骤了。
有一个注意点就是如果配置phy芯片模式为自动协商,在单片机上电时如果没接网线,那么即使后面再接上,网口灯也不会闪烁,这是因为HAL库网络初始化的时候如果没检测到link up则会超时退出,后面配置phy模式为自动协商的操作也就没有执行,解决方案就是在初始化的后面添加上这个配置:
/* init phy reset pin */
eth_phy_init_reset_gpio();
/* reset phy */
eth_phy_hard_reset();
HAL_ETH_Init(&heth);
/*开启自动协商*/
HAL_ETH_WritePHYRegister(&heth, PHY_BCR, PHY_AUTONEGOTIATION);
PRINTF("phy id:%08x\r\n",eth_read_phy_id());
编译后如果报错__builtin_bswap16未定义,需要手动实现,也可直接拷贝例程nxd目录nx_port.h文件内的实现,这个是用于大小端交换的:
netx\ports\cortex_m4\gnu\inc\nx_port.h
/* Define macros that swap the endian for little endian ports. */
#ifdef NX_LITTLE_ENDIAN
/* for __builtin_bswap16 */
#include
typedef uint16_t __u16;
#define __builtin_bswap16(x) __constant_swab16(x)
#define __constant_swab16(x) ((__u16)( \
(((__u16)(x) & (__u16)0x00ffU) << 8) | \
(((__u16)(x) & (__u16)0xff00U) >> 8)))
#define NX_CHANGE_ULONG_ENDIAN(arg) (arg) = __builtin_bswap32(arg)
#define NX_CHANGE_USHORT_ENDIAN(arg) (arg) = __builtin_bswap16(arg)
netx\ports\cortex_m4\gnu\inc\nx_port.h
/*失能错误检查*/
#define NX_DISABLE_ERROR_CHECKING
/*发送ACK之前接收TCP包的数量*/
#define NX_TCP_ACK_EVERY_N_PACKETS 2
/*失能接收包的大小检查*/
#define NX_DISABLE_RX_SIZE_CHECKING
/*失能ARP信息收集*/
#define NX_DISABLE_ARP_INFO
/*失能IP信息收集*/
#define NX_DISABLE_IP_INFO
/*失能ICMP信息收集*/
#define NX_DISABLE_ICMP_INFO
/*失能IGMPv2支持*/
#define NX_DISABLE_IGMPV2
/*失能IGMP信息收集*/
#define NX_DISABLE_IGMP_INFO
/*失能packet pool信息收集*/
#define NX_DISABLE_PACKET_INFO
/*失能RARP信息收集*/
#define NX_DISABLE_RARP_INFO
/*失能TCP信息收集*/
#define NX_DISABLE_TCP_INFO
/*失能UDP信息收集*/
#define NX_DISABLE_UDP_INFO
/*失能更多的回调钩子,BSD封装层使用,默认失能*/
#define NX_DISABLE_EXTENDED_NOTIFY_SUPPORT
几个重要的数据结构:
// 用来管理数据包内存池
NX_PACKET_POOL
// IP实例
NX_IP
// 接口
NX_INTERFACE
// 数据包
NX_PACKET
几个重要的函数:
// 创建数据包内存池
nx_packet_pool_create
// 创建一个IP实例
nx_ip_create
// 留给用户的一个回调函数,用来实现底层的移植
VOID nx_driver_entry(NX_IP_DRIVER *driver_req_ptr)
大致流程如下,我们主要的工作就是要根据各种命令实现底层的这一部分驱动代码:
VOID nx_driver_entry(NX_IP_DRIVER *driver_req_ptr)
{
/* Default to successful return. */
driver_req_ptr -> nx_ip_driver_status = NX_SUCCESS;
/* Process according to the driver request type in the IP control
block. */
switch (driver_req_ptr -> nx_ip_driver_command)
{
case NX_LINK_INTERFACE_ATTACH:
{
/* Process link interface attach requests. */
_nx_driver_interface_attach(driver_req_ptr);
break;
}
case NX_LINK_INITIALIZE:
{
/* Process link initialize requests. */
_nx_driver_initialize(driver_req_ptr);
break;
}
case NX_LINK_ENABLE:
{
/* Process link enable requests. */
_nx_driver_enable(driver_req_ptr);
break;
}
case NX_LINK_DISABLE:
{
/* Process link disable requests. */
_nx_driver_disable(driver_req_ptr);
break;
}
case NX_LINK_ARP_SEND:
case NX_LINK_ARP_RESPONSE_SEND:
case NX_LINK_PACKET_BROADCAST:
case NX_LINK_RARP_SEND:
case NX_LINK_PACKET_SEND:
{
/* Process packet send requests. */
_nx_driver_packet_send(driver_req_ptr);
break;
}
case NX_LINK_MULTICAST_JOIN:
{
/* Process multicast join requests. */
_nx_driver_multicast_join(driver_req_ptr);
break;
}
case NX_LINK_MULTICAST_LEAVE:
{
/* Process multicast leave requests. */
_nx_driver_multicast_leave(driver_req_ptr);
break;
}
case NX_LINK_GET_STATUS:
{
/* Process get status requests. */
_nx_driver_get_status(driver_req_ptr);
break;
}
#ifdef NX_DRIVER_ENABLE_DEFERRED
case NX_LINK_DEFERRED_PROCESSING:
{
/* Process driver deferred requests. */
/* Process a device driver function on behave of the IP thread. */
_nx_driver_deferred_processing(driver_req_ptr);
break;
}
#endif
case NX_LINK_GET_SPEED:
{
break;
}
case NX_LINK_GET_DUPLEX_TYPE:
{
break;
}
case NX_LINK_GET_ERROR_COUNT:
{
break;
}
case NX_LINK_GET_RX_COUNT:
{
break;
}
case NX_LINK_GET_TX_COUNT:
{
break;
}
case NX_LINK_GET_ALLOC_ERRORS:
{
break;
}
case NX_LINK_UNINITIALIZE:
{
break;
}
default:
/* Invalid driver request. */
/* Return the unhandled command status. */
driver_req_ptr -> nx_ip_driver_status = NX_UNHANDLED_COMMAND;
/* Default to successful return. */
driver_req_ptr -> nx_ip_driver_status = NX_DRIVER_ERROR;
}
}
贴张效果图:
这是一个单实例例程,也就是单网口的情况,如果是多网口的话可以参考源码src目录里面的nx_ram_network_driver.c文件,这是官方提供的一个给用户参考的移植文件。
总体来说,个人觉得官方的这份例程不太容易理解,就NX_INTERFACE数据结构来说,其是在nx_ip_create函数执行时填充一部分,用户移植时填充一部分,还有一部分是netx运行时反馈信息;然后在底层的实现中,还需要用户来操作这些数据结构的内容,移植非常不便,全程几乎没使用HAL库的API,但是有些代码和一些API的内容又极为相似,要是ST官方能够出一份移植文档就好了。
本文的源码工程可在我的代码仓库获取,已对移植部分代码大部分内容进行了注释。
参考文档:
LAN8720A数据手册
以太网帧结构
官方用户指导文档