笔记:stm32移植threadx netx网络协议栈

写在前面

注意本文以分析为主,主要讲解实现过程和注意点,并非从零开始

标题虽然是移植,但是完全参考官方提供的一份例程,即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());

开始搬运

  • 添加netx的源码到工程中
    需要netx\common文件夹的内容
    需要netx\netx\ports\cortex_m4文件夹的内容
    netx\addons文件夹暂时没用,可以不添加

编译后如果报错__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 <stdint.h>
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)
  • 拷贝例程中nxd目录nx_port.h内的配置宏
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
  • 拷贝例程中nxd目录nx_driver_stm32f746.c和nx_driver_stm32f746.h文件到工程中
  • 编译工程,根据报错信息,去掉一些无用的内容
  • 添加测试程序
    将例程中sample_netx_duo_ping目录内sample_netx_duo_ping.c文件的内容添加到tx_application_define函数中。

分析

几个重要的数据结构:

// 用来管理数据包内存池
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文件,这是官方提供的一个给用户参考的移植文件

扫描二维码关注公众号,回复: 11478894 查看本文章

总结

总体来说,个人觉得官方的这份例程不太容易理解,就NX_INTERFACE数据结构来说,其是在nx_ip_create函数执行时填充一部分,用户移植时填充一部分,还有一部分是netx运行时反馈信息;然后在底层的实现中,还需要用户来操作这些数据结构的内容,移植非常不便,全程几乎没使用HAL库的API,但是有些代码和一些API的内容又极为相似,要是ST官方能够出一份移植文档就好了。
本文的源码工程可在我的代码仓库获取,已对移植部分代码大部分内容进行了注释。

参考文档:
LAN8720A数据手册
以太网帧结构
官方用户指导文档

欢迎扫码关注我的微信公众号
漫长当下

猜你喜欢

转载自blog.csdn.net/a1598025967/article/details/107667297