【ThreadX全家桶】STM32CubeMX+NetX Duo(二)


前言

提供一个基于STM32CubeMX+STM32F429BIT6+DP83848+NetX Duo的工程

https://download.csdn.net/download/mdzz6666/54176335


一、驱动框架

在NetX Duo驱动中,主要需要实现ETH的初始化(包括ETH控制器、TX描述符、RX描述符的初始化),ETH的开启与关闭、帧发送函数,接收函数是放在中断中调用的。

VOID  nx_driver_stm32f429(NX_IP_DRIVER *driver_req_ptr)
{
	switch (driver_req_ptr -> nx_ip_driver_command)
	{
		case NX_LINK_INTERFACE_ATTACH:					_nx_driver_interface_attach(driver_req_ptr);					break;
		case NX_LINK_INITIALIZE:						_nx_driver_initialize(driver_req_ptr);								break;
		case NX_LINK_ENABLE:							_nx_driver_enable(driver_req_ptr);										break;
		case NX_LINK_DISABLE:							_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:						_nx_driver_packet_send(driver_req_ptr);								break;
		case NX_LINK_MULTICAST_JOIN:					_nx_driver_multicast_join(driver_req_ptr);						break;
		case NX_LINK_MULTICAST_LEAVE:					_nx_driver_multicast_leave(driver_req_ptr);						break;
		default:				driver_req_ptr -> nx_ip_driver_status =  NX_UNHANDLED_COMMAND;					break;
	}
}

二、ETH初始化

1、ETH控制器初始化
ETH控制器初始化直接调用HAL的初始化函数即可。但是默认接收模式是循环检测,需要设置中断接收。

HETH.Init.RxMode = ETH_RXINTERRUPT_MODE;		//循环检测 改为 中断接收

2、TX描述符初始化
①因为NetX在发出发送请求时,才能根据NX_PACK链表确定包地址。所以在HAL的TX初始化API中不给描述符分配BUFF地址。
②将所有的TX描述符的“完成时中断”打开,默认是关闭的。
③注释掉关于ETH_CHECKSUM_BY_HARDWARE的处理。

		dmatxdesc->Status = ETH_DMATXDESC_TCH | ETH_DMATXDESC_IC;
		f429_nx_driver_information.nx_packets_send[i] = NX_NULL;
//    /* Set Second Address Chained bit */
//    dmatxdesc->Status = ETH_DMATXDESC_TCH;  
    
//    /* Set Buffer1 address pointer */
//    dmatxdesc->Buffer1Addr = (uint32_t)(&TxBuff[i*ETH_TX_BUF_SIZE]);
    
//    if ((heth->Init).ChecksumMode == ETH_CHECKSUM_BY_HARDWARE)
//    {
//      /* Set the DMA Tx descriptors checksum insertion */
//      dmatxdesc->Status |= ETH_DMATXDESC_CHECKSUMTCPUDPICMPFULL;
//    }

3、RX描述符初始化
RX描述符的Buffer1Addr、ControlBufferSize并不从连续的RX_BUFF分配,而是先从NETX的包池申请NX_RECEIVE_PACKET,然后将NX_RECEIVE_PACKET的包缓存地址赋值给Buffer1Addr,NX_RECEIVE_PACKET的包缓存长度赋值给ControlBufferSize。
①注释掉HAL的RX初始化API中的Buffer1Addr、ControlBufferSize以及关于ETH_RXINTERRUPT_MODE的处理。
②从NetX包池中申请NX_RECEIVE_PACKET。
③将NX_RECEIVE_PACKET的包缓存地址赋值给Buffer1Addr,NX_RECEIVE_PACKET的包缓存长度赋值给ControlBufferSize。
注:不注释掉heth->RxDesc = DMARxDescTab;,程序可能无法运行,但注释后,HAL的RX接收函数将无法使用。该情况仅供参考,可能是我什么地方没有设置好。

//  heth->RxDesc = DMARxDescTab; 
	.
	.
	.
//    /* Set Buffer1 size and Second Address Chained bit */
//    DMARxDesc->ControlBufferSize = ETH_DMARXDESC_RCH | ETH_RX_BUF_SIZE;  
//    
//    /* Set Buffer1 address pointer */
//    DMARxDesc->Buffer1Addr = (uint32_t)(&RxBuff[i*ETH_RX_BUF_SIZE]);
//    
//    if((heth->Init).RxMode == ETH_RXINTERRUPT_MODE)
//    {
//      /* Enable Ethernet DMA Rx Descriptor interrupt */
//      DMARxDesc->ControlBufferSize &= ~ETH_DMARXDESC_DIC;
//    }
		if (nx_packet_allocate(RecPackPool, &packet_ptr, NX_RECEIVE_PACKET, NX_NO_WAIT) == NX_SUCCESS)
		{
			packet_ptr -> nx_packet_prepend_ptr += 2;
			DMARxDesc->Buffer1Addr = (uint32_t) packet_ptr -> nx_packet_prepend_ptr;
			
			DMARxDesc->ControlBufferSize = ETH_DMARXDESC_RCH | (packet_ptr -> nx_packet_data_end - packet_ptr -> nx_packet_data_start);  
    
			f429_nx_driver_information.nx_packets_receive[i] = packet_ptr;
		}
		else
			return HAL_ERROR;

三、帧发送函数

NetX Duo协议栈发出发送请求时,帧数据保存在请求机构体driver_req_ptr 的 nx_ip_driver_packet中,是一个链表。该链表在调用ETH发送前,需要进行一些规定预处理,可参考官方文档,此处不做赘述。
因为NetX的拆包逻辑和HAL不同,所以HAL的发送函数改动很大,但基本原理相同。

/* 1、使用 TX_NX_PACK 链表,代替FrameLength
 *		a、根据 packet_ptr -> nx_packet_next 是否为NULL,判断头包/身体包/尾包
 * 2、尾包描述符添加 中断 功能,使其在发送完一帧后,产生发送中断
 * 3、send_nx_pack管理
 */
HAL_StatusTypeDef ETH_TransmitFrame(ETH_HandleTypeDef *heth, NX_PACKET *packet_ptr)
{
	NX_PACKET       *pktIdx;
	ULONG           curIdx = f429_nx_driver_information.send_current_index;
  uint32_t bufcount = 0U;
  
  /* Process Locked */
  __HAL_LOCK(heth);
  
  /* Set the ETH peripheral state to BUSY */
  heth->State = HAL_ETH_STATE_BUSY;
  
	for (pktIdx = packet_ptr; 		pktIdx != NX_NULL; 		pktIdx = pktIdx -> nx_packet_next)	//判断是否是尾包
	{
		bufcount++;
		
		if(((heth->TxDesc)->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET)		//判断该描述符是否空闲
		{  
			heth->State = HAL_ETH_STATE_BUSY_TX;
			__HAL_UNLOCK(heth);  
			return HAL_ERROR;
		}
	
		/* Clear FIRST and LAST segment bits */
		heth->TxDesc->Status &= ~(ETH_DMATXDESC_FS | ETH_DMATXDESC_LS);		//清除头包/尾包标志
		
		if (pktIdx == packet_ptr) 	//头包
			heth->TxDesc->Status |= ETH_DMATXDESC_FS;  
		else
			curIdx = (curIdx + 1) & (F429_NX_TXDESC_COUNT - 1);			//计数,非头包 = 自减1
		
		if (pktIdx -> nx_packet_next == NX_NULL)		//尾包
			heth->TxDesc->Status |= ETH_DMATXDESC_LS | ETH_DMATXDESC_IC;		//添加中断标志位,使其在发送完一帧后,产生一次中断
		
		heth->TxDesc->Buffer1Addr = (ULONG)packet_ptr->nx_packet_prepend_ptr;
		heth->TxDesc->ControlBufferSize = ((packet_ptr -> nx_packet_append_ptr - packet_ptr->nx_packet_prepend_ptr) & ETH_DMATXDESC_TBS1);
		
		heth->TxDesc->Status |= ETH_DMATXDESC_OWN;
		heth->TxDesc = (ETH_DMADescTypeDef *)(heth->TxDesc->Buffer2NextDescAddr);
	}
  
	f429_nx_driver_information.nx_packets_send[curIdx] = packet_ptr;		//保存帧头到当前指针指向的数组,但是保存的数据并不连续????
	f429_nx_driver_information.send_current_index = (curIdx + 1) & (F429_NX_TXDESC_COUNT - 1);
	
	f429_nx_driver_information.send_in_use_index += bufcount;
	
  /* When Tx Buffer unavailable flag is set: clear it and resume transmission */
  if (((heth->Instance)->DMASR & ETH_DMASR_TBUS) != (uint32_t)RESET)
  {
    /* Clear TBUS ETHERNET DMA flag */
    (heth->Instance)->DMASR = ETH_DMASR_TBUS;
    /* Resume DMA transmission*/
    (heth->Instance)->DMATPDR = 0U;
  }
  
  /* Set ETH HAL State to Ready */
  heth->State = HAL_ETH_STATE_READY;
  
  /* Process Unlocked */
  __HAL_UNLOCK(heth);
  
  /* Return function status */
  return HAL_OK;
}

四、ETH中断函数

帧发送完成、接收到帧都会产生中断,所以需要在中断中处理这两种情况。
注意:HAL提供的RX与TX回调函数有问题,不能直接使用。所以我们直接修改void ETH_IRQHandler(void);

void ETH_IRQHandler(void)
{
	ULONG status;
	status = ETH->DMASR;
	ETH->DMASR = ETH_DMA_IT_R | ETH_DMA_IT_T | ETH_DMA_IT_NIS;
	
	if(status & ETH_DMA_IT_T)
		_nx_driver_packet_transmitted();	//发送完成的后续处理
	if(status & ETH_DMA_IT_R)
		_nx_driver_packet_received();		//接收包处理
}	

五、发送完成的后续处理

帧发送完成后,需要将保存帧数据的NX_PACK链表释放掉,使其返回NetX的包池中,供后续业务申请使用。
在发送函数中将NX_PACK链表中首节点地址保存在数组f429_nx_driver_information.nx_packets_send[16]中。

VOID	_nx_driver_packet_transmitted(VOID)
{
	ULONG numOfBuf =  f429_nx_driver_information.send_in_use_index;
	ULONG idx =       f429_nx_driver_information.send_release_index;
	
	while (numOfBuf--)
	{
		if (f429_nx_driver_information.nx_packets_send[idx] == NX_NULL) 	//在 nx_packets_send 找到刚才发送的帧头
		{		
				idx = (idx + 1) & (F429_NX_TXDESC_COUNT - 1);
				continue;
		}
		
		if ((ETH_DMATxDescTab[idx].Status & ETH_DMATXDESC_OWN) == 0)			//idx也指向 帧尾 的描述符,该判断语句表示帧尾发送完成,表明整个帧都发送完成
		{
			f429_nx_driver_information.nx_packets_send[idx] -> nx_packet_prepend_ptr =  f429_nx_driver_information.nx_packets_send[idx] -> nx_packet_prepend_ptr + NX_DRIVER_ETHERNET_FRAME_SIZE;  
			f429_nx_driver_information.nx_packets_send[idx] -> nx_packet_length =  f429_nx_driver_information.nx_packets_send[idx] -> nx_packet_length - NX_DRIVER_ETHERNET_FRAME_SIZE;   				 
			nx_packet_transmit_release(f429_nx_driver_information.nx_packets_send[idx]);
			f429_nx_driver_information.nx_packets_send[idx] = NX_NULL;
			
			idx = (idx + 1) & (F429_NX_TXDESC_COUNT - 1);
			f429_nx_driver_information.send_in_use_index = numOfBuf;
			f429_nx_driver_information.send_release_index = idx;
		}
		else
			break;
	}
}

六、中断接收函数及接收完成后的后续处理

因为RX初始化中的BUG,在注释掉heth->RxDesc = DMARxDescTab;之后,HAL提供的RX接收函数无法使用,所以此处基本参考的是“安富莱”的代码,后续解决该BUG后,再来更新。


总结

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值