前言
提供一个基于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;
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接收函数将无法使用。该情况仅供参考,可能是我什么地方没有设置好。
.
.
.
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的发送函数改动很大,但基本原理相同。
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;
__HAL_LOCK(heth);
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;
}
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);
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;
if (((heth->Instance)->DMASR & ETH_DMASR_TBUS) != (uint32_t)RESET)
{
(heth->Instance)->DMASR = ETH_DMASR_TBUS;
(heth->Instance)->DMATPDR = 0U;
}
heth->State = HAL_ETH_STATE_READY;
__HAL_UNLOCK(heth);
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)
{
idx = (idx + 1) & (F429_NX_TXDESC_COUNT - 1);
continue;
}
if ((ETH_DMATxDescTab[idx].Status & ETH_DMATXDESC_OWN) == 0)
{
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后,再来更新。
总结
|