RL-TCPnet无操作系统移植(一) - LAN8720A的配置

最近在看硬汉写的RL-TCPnet教程的时候,感觉硬汉对底层驱动移植那一块讲得不是很清楚,看了原子的也是差不多,只是大概的讲了一下,对于刚学的人来说可能会有点不理解,跟教程写把程序写了出来,能用,但是不知道为什么这样写。自己研究了一下,大概弄懂了一些,在这里记录一下吧。我使用的开发平台是stm32F4,HAL库,PHY为LAN8720A.

在移植之前先要对STM32F4的以太网控制部分有一个了解,从官方文档中可以查到:STM32F407芯片自带以太网模块,该模包括带专用DMA控制器的MAC802.3(介质访问控制)控制器,支持介质独立接口(MII)和简化介质独立接口(RMII),并自带了一个用于外部PHY通信的SMI接口,通过一组配置寄存器,用户可以为MAC控制器和DMA控制器选择所需模式和功能。

 从这段话中可以看到,F4的以太网控制模块是带专用DMA控制器的,而DMA主要是用来转移数据的,因此我们应该主要理解DMA在这里面起得是什么作用。


1、首先对PHY使用的引脚进行配置

配置引脚的话没有什么好讲的,这个按照手册要未去配置就行,记得打开时钟以及引脚的复用功能。简单点的话可以使用STM32CUBE,非常简单,配置代码如下:


void bsp_lan_GPIOConfig(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	
	/* 打开外设时钟 */
	__HAL_RCC_GPIOA_CLK_ENABLE();
	__HAL_RCC_GPIOB_CLK_ENABLE();
	__HAL_RCC_GPIOC_CLK_ENABLE();
	__HAL_RCC_ETH_CLK_ENABLE();
	__ETH_CLK_ENABLE() ;
	
	GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
	GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
	HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

	GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_7;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
	GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

	GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
	GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	GPIO_InitStruct.Pin = GPIO_PIN_7;   /*  该引脚为PHY复位引脚  */
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
	HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}

2、配置NVIC

代码如下:
void ETH_NVICConfig(void)
{	
	HAL_NVIC_SetPriority(ETH_IRQn, 0, 0);
	HAL_NVIC_EnableIRQ(ETH_IRQn);
}

3.ETH的配置

在配置以太网控制器之前,先看看32官方是怎样用DMA来控制以太网的数据的,在官方手册可以查看到以太网控制框图:

RL-TCPnet无操作系统移植(一) - LAN8720A的配置_第1张图片
  

从图中可以看到32的以太网DMA有两个FIFO,一个管发送,一个管接收,同时还有DMA控制与状态寄存器,在程序内部刚将这些组织为一个结构体,叫做DMA描述符,其定义如下:

typedef struct  
{
  __IO uint32_t   Status;           /*!< Status */
  
  uint32_t   ControlBufferSize;     /*!< Control and Buffer1, Buffer2 lengths */
  
  uint32_t   Buffer1Addr;           /*!< Buffer1 address pointer */
  
  uint32_t   Buffer2NextDescAddr;   /*!< Buffer2 or next descriptor address pointer */
  
  /*!< Enhanced ETHERNET DMA PTP Descriptors */
  uint32_t   ExtendedStatus;        /*!< Extended status for PTP receive descriptor */
  
  uint32_t   Reserved1;             /*!< Reserved */
  
  uint32_t   TimeStampLow;          /*!< Time Stamp Low value for transmit and receive */
  
  uint32_t   TimeStampHigh;         /*!< Time Stamp High value for transmit and receive */

} ETH_DMADescTypeDef;

看起来好像很复杂,其实每个成员的基本上都是处注释的,看名字就知道他的意义,我再分别说一下吧。

Status:这个变量其实就是上面面框图里的状态寄存器,指示着DMA描述符的状态,比如说收到的数据是不是该描述符的。

ControlBufferSize:这个变量指示这个描述符里面数据的大小。

Buffer1Addr:这个变量存储着接收到的数据的基地址,我们读以太网的数据的话就是从这里读的。

Buffer2NextDescAddr:这个变量指示着下一个DMA描述符的地址,官方将这个DMA描述符组织成一个链表,便于使用和管理。


知道了DMA描述符,我们就接着配置剩下的。我们先声明两个DMA描述符指针,用于构成DMA描述符链表,另外再声明两个uint8_t型指针,一个用于接收数据,一个用于发送数据,上面有说到的Buffer1Addr其实只是一个地址,并不能存储数据,因此需要在这里另外申请空间,并将DMA描述符的Buffer1Addr指向这里,需要注意的是,这里声明的都是指针,需要给他们分配空间,不然会使用出错,当然你也可以直接声明数组,这样就不需要为他们申请空间了。


ETH_HandleTypeDef ETH_Handler; 

ETH_DMADescTypeDef *DMARxDescTab;	//发送DMA描述符
ETH_DMADescTypeDef *DMATxDescTab;	//接收DMA描述符 
uint8_t *Rx_Buff; 			//数据接收Buffer
uint8_t *Tx_Buff; 			//数据发送Buffer
申请内存:
uint8_t ETH_Malloc(void)
{
	DMARxDescTab = bsp_mem_Malloc(SRAMIN, ETH_RXBUFNB * sizeof(ETH_DMADescTypeDef));
	DMATxDescTab = bsp_mem_Malloc(SRAMIN, ETH_TXBUFNB * sizeof(ETH_DMADescTypeDef));
	
	Rx_Buff = bsp_mem_Malloc(SRAMIN, ETH_RX_BUF_SIZE * ETH_RXBUFNB);
	Tx_Buff = bsp_mem_Malloc(SRAMIN, ETH_TX_BUF_SIZE * ETH_TXBUFNB);
	
	if(!DMARxDescTab || !DMATxDescTab || !Rx_Buff || !Tx_Buff)
	{
		ETH_Free();
		return 1;
	}
	return 0;
}
这里的话我自己有实现内存管理函数,你也可以使用其他内存管理方式。
然后就是配置以太网:
	MAC[0] = own_hw_adr[0];
	MAC[1] = own_hw_adr[1];
	MAC[2] = own_hw_adr[2];
	MAC[3] = own_hw_adr[3];
	MAC[4] = own_hw_adr[4];
	MAC[5] = own_hw_adr[5];
	
	ETH_Handler.Instance = ETH;
	ETH_Handler.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;
//	ETH_Handler.Init.Speed = ETH_SPEED_100M;
//	ETH_Handler.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
	ETH_Handler.Init.PhyAddress = LAN_PHY_ADDR;
	ETH_Handler.Init.MACAddr = &MAC[0];
	ETH_Handler.Init.RxMode = ETH_RXINTERRUPT_MODE;
	ETH_Handler.Init.ChecksumMode = ETH_CHECKSUM_BY_SOFTWARE;
	ETH_Handler.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
	
	HAL_ETH_DeInit(Ð_Handler);	 /* 配置之前先复位 */
	
	/* 开启发送完成中断以及异常错误总中断 */
	__HAL_ETH_DMA_ENABLE_IT(Ð_Handler, ETH_DMA_IT_NIS | ETH_DMA_IT_T | ETH_DMA_IT_AIS | ETH_DMA_IT_FBE);	

	/* 正常初始化返回HAL_OK,否则返回错误消息,比如没有插网线则返回超时 */
	eth_err = HAL_ETH_Init(Ð_Handler);
 接下来就是将DMA描述符构成链表了,调用库函数就行:
	/* 构造DMA发送和接收描述符为链表 */
	HAL_ETH_DMATxDescListInit(Ð_Handler, DMATxDescTab, (uint8_t *)Tx_Buff, ETH_TXBUFNB);
	HAL_ETH_DMARxDescListInit(Ð_Handler,  DMARxDescTab, (uint8_t *)Rx_Buff, ETH_RXBUFNB);
最后启动以太网就OK了:
HAL_ETH_Start(Ð_Handler);

4、中断函数

上面的都配置好后,我们就需要编写中断函数了,因为我们开启了中断,中断函数如下:

extern void RL_TCPnet_ptk_Handler(void);
void ETH_IRQHandler(void)
{		
    while(ETH_GetRxPktSize(ETH_Handler.RxDesc))   
    {
	RL_TCPnet_ptk_Handler();//调用中断处理函数
    }
    //消除中断标志及错误标志
    __HAL_ETH_DMA_CLEAR_IT(Ð_Handler,ETH_DMA_IT_R); 
    __HAL_ETH_DMA_CLEAR_IT(Ð_Handler,ETH_DMA_IT_NIS); 
}  

5、读PHY及写PHY寄存器的函数

这些的话HAL库有编写好,我们直接调用就行:
uint32_t bsp_lan_ReadPHY(uint16_t reg)
{
	uint32_t regval;
	HAL_ETH_ReadPHYRegister(Ð_Handler,reg,®val);
	return regval;
}

void bsp_lan_WritePHY(uint16_t reg, uint16_t value)
{
	uint32_t regval = value;
	HAL_ETH_ReadPHYRegister(Ð_Handler, reg, ®val);
}

6、总结

其实LAN8720A驱动的编写也是比较简单的,特别是官方的库里面都做好了封装,实质上不懂得怎么编写还是对库的不熟悉,也就是对库组织以太网这一部分的方式不理解,我觉得难点应该在对DMA那一块的理解,我已经尽我最大的表述能力来描述了,如果有不理解的地方可以私信我。如果有错误的地方也希望各位能够批评指正,第一次写博客。最后给出整个代码:

# include "bsp_lan.h"
//# include "lwip_comm.h"
# include "app_sys.h"
# include "includes.h"
#include "ETH_STM32F4xx.h"
ETH_HandleTypeDef ETH_Handler; 

ETH_DMADescTypeDef *DMARxDescTab;	//ÒÔÌ«ÍøDMA½ÓÊÕÃèÊö·ûÊý¾Ý½á¹¹ÌåÖ¸Õë
ETH_DMADescTypeDef *DMATxDescTab;	//ÒÔÌ«ÍøDMA·¢ËÍÃèÊö·ûÊý¾Ý½á¹¹ÌåÖ¸Õë 
uint8_t *Rx_Buff; 					//ÒÔÌ«Íøµ×²ãÇý¶¯½ÓÊÕbuffersÖ¸Õë 
uint8_t *Tx_Buff; 					//ÒÔÌ«Íøµ×²ãÇý¶¯·¢ËÍbuffersÖ¸Õë
  
//extern lwip_dev lwip_info;

	/*ÍøÂçÒý½ÅÉèÖà RMII½Ó¿Ú
	  ETH_MDIO -------------------------> PA2
	  ETH_MDC --------------------------> PC1
	  ETH_RMII_REF_CLK------------------> PA1
	  ETH_RMII_CRS_DV ------------------> PA7
	  ETH_RMII_RXD0 --------------------> PC4
	  ETH_RMII_RXD1 --------------------> PC5
	  ETH_RMII_TX_EN -------------------> PB11
	  ETH_RMII_TXD0 --------------------> PB12
	  ETH_RMII_TXD1 --------------------> PB13
	  ETH_RESET-------------------------> PC7*/
		
/*
*********************************************************************************************************
*                                          
*
* Description: 
*             
* Arguments : 
*
* Note(s)   : 
*********************************************************************************************************
*/
void ETH_NVICConfig(void)
{	
	HAL_NVIC_SetPriority(ETH_IRQn, 0, 0);
	HAL_NVIC_EnableIRQ(ETH_IRQn);
}




void bsp_lan_GPIOConfig(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	
	/* ´ò¿ªÍâÉèʱÖÓ */
	__HAL_RCC_GPIOA_CLK_ENABLE();
	__HAL_RCC_GPIOB_CLK_ENABLE();
	__HAL_RCC_GPIOC_CLK_ENABLE();
	__HAL_RCC_ETH_CLK_ENABLE();
	__ETH_CLK_ENABLE() ;
	
	GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
	GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
	HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

	GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_7;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
	GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

	GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
	GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
	
	GPIO_InitStruct.Pin = GPIO_PIN_7;   /*  ¸ÃÒý½ÅΪPHYµÄ¸´Î»Òý½Å  */
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
	HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
		
/*
*********************************************************************************************************
*                                          
*
* Description: 
*             
* Arguments : 
*
* Note(s)   : 
*********************************************************************************************************
*/
extern uint8_t own_hw_adr[];
uint8_t bsp_lan_Config(void)
{
	uint8_t MAC[6];
	uint32_t phyreg = 0x00;
	HAL_StatusTypeDef eth_err;
	ETH_Malloc();
	bsp_lan_GPIOConfig();
	ETH_NVICConfig();
	
	DISABLE_INT();
	LAN_RST = 0;
	bsp_tim_DelayMs(100);
	LAN_RST = 1;
	ENABLE_INT();
	
	MAC[0] = own_hw_adr[0];
	MAC[1] = own_hw_adr[1];
	MAC[2] = own_hw_adr[2];
	MAC[3] = own_hw_adr[3];
	MAC[4] = own_hw_adr[4];
	MAC[5] = own_hw_adr[5];
	
	ETH_Handler.Instance = ETH;
	ETH_Handler.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;
//	ETH_Handler.Init.Speed = ETH_SPEED_100M;
//	ETH_Handler.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
	ETH_Handler.Init.PhyAddress = LAN_PHY_ADDR;
	ETH_Handler.Init.MACAddr = &MAC[0];
	ETH_Handler.Init.RxMode = ETH_RXINTERRUPT_MODE;
	ETH_Handler.Init.ChecksumMode = ETH_CHECKSUM_BY_SOFTWARE;
	ETH_Handler.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
	
	HAL_ETH_DeInit(Ð_Handler);	 /* ³õʼ»¯Ö®Ç°Ïȸ´Î» */
	
	/* ¿ªÆô·¢ËÍÍê³ÉÖжϺÍÒì³£´íÎó×ÜÖÐ¶Ï */
	__HAL_ETH_DMA_ENABLE_IT(Ð_Handler, ETH_DMA_IT_NIS | ETH_DMA_IT_T | ETH_DMA_IT_AIS | ETH_DMA_IT_FBE);	

	/* Õý³£³õʼ»¯·µ»ØHAL_OK£¬Èç¹ûû²åÍøÏßÔò·µ»Ø³¬Ê± */
	eth_err = HAL_ETH_Init(Ð_Handler);

	/* ¹¹ÔìDMA·¢ËͺͽÓÊÕÃèÊö·û³ÉΪÁ´±í·½Ê½ */
	HAL_ETH_DMATxDescListInit(Ð_Handler, DMATxDescTab, (uint8_t *)Tx_Buff, ETH_TXBUFNB);
	HAL_ETH_DMARxDescListInit(Ð_Handler,  DMARxDescTab, (uint8_t *)Rx_Buff, ETH_RXBUFNB);	
		
//		tx_descr_init();
//		rx_descr_init();
	/* Æô¶¯ETH */
	HAL_ETH_Start(Ð_Handler);
	
	/* ¼ì²éÊÇ·ñÁ¬½ÓÁËÍøÏß*/
	HAL_ETH_ReadPHYRegister(Ð_Handler, PHY_BSR, &phyreg);
	Sys_Info.LinkStatus = (phyreg & PHY_LINKED_STATUS);
	


	return eth_err;	
}





/*
*********************************************************************************************************
*                                          
*
* Description:
*             
* Arguments  :
*
* Reutrn     :
*
* Note(s)    : 
*********************************************************************************************************
*/
void bsp_lan_WritePHY(uint16_t reg, uint16_t value)
{
	uint32_t regval = value;
	HAL_ETH_ReadPHYRegister(Ð_Handler, reg, ®val);
}


/*
*********************************************************************************************************
*                                          
*
* Description:
*             
* Arguments  :
*
* Reutrn     :
*
* Note(s)    : 
*********************************************************************************************************
*/
uint32_t bsp_lan_ReadPHY(uint16_t reg)
{
	uint32_t regval;
	HAL_ETH_ReadPHYRegister(Ð_Handler,reg,®val);
	return regval;
}




/*
*********************************************************************************************************
*                                          
*
* Description: 
*             
* Arguments : 
*
* Note(s)   : 
*********************************************************************************************************
*/
uint8_t bsp_lan_GetSpeed(void)
{
	return ((bsp_lan_ReadPHY(31) & 0x1c) >> 2);
}


/*
*********************************************************************************************************
*                                          
*
* Description: 
*             
* Arguments : 
*
* Note(s)   : 
*********************************************************************************************************
*/
uint8_t ETH_Malloc(void)
{
	DMARxDescTab = bsp_mem_Malloc(SRAMIN, ETH_RXBUFNB * sizeof(ETH_DMADescTypeDef));
	DMATxDescTab = bsp_mem_Malloc(SRAMIN, ETH_TXBUFNB * sizeof(ETH_DMADescTypeDef));
	
	Rx_Buff = bsp_mem_Malloc(SRAMIN, ETH_RX_BUF_SIZE * ETH_RXBUFNB);
	Tx_Buff = bsp_mem_Malloc(SRAMIN, ETH_TX_BUF_SIZE * ETH_TXBUFNB);
	
	if(!DMARxDescTab || !DMATxDescTab || !Rx_Buff || !Tx_Buff)
	{
		ETH_Free();
		return 1;
	}
	return 0;
}



/*
*********************************************************************************************************
*                                          
*
* Description: 
*             
* Arguments : 
*
* Note(s)   : 
*********************************************************************************************************
*/
void ETH_Free(void)
{
	bsp_mem_Free(SRAMIN, DMARxDescTab);
	bsp_mem_Free(SRAMIN, DMATxDescTab);
	bsp_mem_Free(SRAMIN, Rx_Buff);
	bsp_mem_Free(SRAMIN, Tx_Buff);
}


/*
*********************************************************************************************************
*                                          
*
* Description: 
*             
* Arguments : 
*
* Note(s)   : 
*********************************************************************************************************
*/
extern void RL_TCPnet_ptk_Handler(void);
void ETH_IRQHandler(void)
{
    OSIntEnter(); 
		
    while(ETH_GetRxPktSize(ETH_Handler.RxDesc))   
    {
			RL_TCPnet_ptk_Handler();//´¦ÀíÒÔÌ«ÍøÊý¾Ý
    }
    //Çå³ýÖжϱê־λ
    __HAL_ETH_DMA_CLEAR_IT(Ð_Handler,ETH_DMA_IT_R); 
    __HAL_ETH_DMA_CLEAR_IT(Ð_Handler,ETH_DMA_IT_NIS); 
    OSIntExit();  
}  

/*
*********************************************************************************************************
*                                          
*
* Description:
*             
* Arguments  :
*
* Reutrn     :
*
* Note(s)    : 
*********************************************************************************************************
*/
uint32_t  ETH_GetRxPktSize(ETH_DMADescTypeDef *DMARxDesc)
{
    uint32_t frameLength = 0;
    if(((DMARxDesc->StatusÐ_DMARXDESC_OWN)==(uint32_t)RESET) &&
     ((DMARxDesc->StatusÐ_DMARXDESC_ES)==(uint32_t)RESET) &&
     ((DMARxDesc->StatusÐ_DMARXDESC_LS)!=(uint32_t)RESET)) 
    {
        frameLength=((DMARxDesc->StatusÐ_DMARXDESC_FL)>>ETH_DMARXDESC_FRAME_LENGTHSHIFT);
    }
    return frameLength;
}



你可能感兴趣的:(程序调试)