基于STM32构建EtherCAT主站(SOEM方案)3

这里讲一下lan8720网卡驱动。
其实也没啥好讲的,参考原子哥和野火的lwip教程中的裸机移植部分就可以了。
我直接贴代码了,结合lwip的教程以及stm32手册中的以太网部分,消化理解吧。

有几点说一下:

1.以太网接收报文用的时轮询的方式,不是中断。即(ETH_Handler.Init.RxMode = ETH_RXPOLLING_MODE;//轮询接收模式)
2.注意lan8720的时钟配置,不同硬件平台是有差异的,注意看lan8720的芯片手册和原理图。
在这里插入图片描述
在这里插入图片描述

3.原子的f767开发板是有外部SDRAM的,以太网数据帧用的是外部SDRAM的空间。
在这里插入图片描述
在这里插入图片描述
4.如果用的是stm32片内的SDRAM的话,可以这么写:
在这里插入图片描述
5.如果是stm32f1x、stm32f4x之类的芯片,可以把下面红线部分删掉。
在这里插入图片描述
6.mac配置工作在混杂模式
在这里插入图片描述

基于原子f767开发板的代码

ETH_HandleTypeDef ETH_Handler;      //以太网句柄

ETH_DMADescTypeDef *DMARxDscrTab;	//以太网DMA接收描述符数据结构体指针
ETH_DMADescTypeDef *DMATxDscrTab;	//以太网DMA发送描述符数据结构体指针 
uint8_t *Rx_Buff; 					//以太网底层驱动接收buffers指针 
uint8_t *Tx_Buff; 					//以太网底层驱动发送buffers指针
  
u32 interrupt_state(void)
{
    int state;
    state=(ETH_Handler.Instance->DMAIER);
    return (u32)state;
}

//LAN8720初始化
//返回值:0,成功;
//    其他,失败
u8 LAN8720_Init(void)
{    
//		uint8_t macaddress[6]= { MAC_ADDR0, MAC_ADDR1, MAC_ADDR2, MAC_ADDR3, MAC_ADDR4, MAC_ADDR5 };

		u32 sn0;
		sn0=*(vu32*)(0x1FF0F420);//获取STM32的唯一ID的前24位作为MAC地址后三字节
		u8 macaddress[6];
		//MAC地址设置(高三字节固定为:2.0.0,低三字节用STM32唯一ID)
		macaddress[0]=2;//高三字节(IEEE称之为组织唯一ID,OUI)地址固定为:2.0.0
		macaddress[1]=0;
		macaddress[2]=0;
		macaddress[3]=(sn0>>16)&0XFF;//低三字节用STM32的唯一ID
		macaddress[4]=(sn0>>8)&0XFFF;
		macaddress[5]=sn0&0XFF; 
		
    INTX_DISABLE();                         //关闭所有中断,复位过程不能被打断!
    PCF8574_WriteBit(ETH_RESET_IO,1);       //硬件复位
    delay_ms(100);
    PCF8574_WriteBit(ETH_RESET_IO,0);       //复位结束
    delay_ms(100);
    INTX_ENABLE();                          //开启所有中断
    
        
		ETH_Handler.Instance=ETH;
//    ETH_Handler.Init.AutoNegotiation=ETH_AUTONEGOTIATION_ENABLE;//使能自协商模式 
		ETH_Handler.Init.AutoNegotiation=ETH_AUTONEGOTIATION_DISABLE;
    ETH_Handler.Init.Speed=ETH_SPEED_100M;//速度100M,如果开启了自协商模式,此配置就无效
    ETH_Handler.Init.DuplexMode=ETH_MODE_FULLDUPLEX;//全双工模式,如果开启了自协商模式,此配置就无效
    ETH_Handler.Init.PhyAddress=LAN8720_PHY_ADDRESS;//LAN8720地址  
    ETH_Handler.Init.MACAddr=macaddress;            //MAC地址  
//    ETH_Handler.Init.RxMode=ETH_RXINTERRUPT_MODE;   //中断接收模式 
		ETH_Handler.Init.RxMode = ETH_RXPOLLING_MODE;//轮询接收模式
    ETH_Handler.Init.ChecksumMode=ETH_CHECKSUM_BY_HARDWARE;//硬件帧校验  
    ETH_Handler.Init.MediaInterface=ETH_MEDIA_INTERFACE_RMII;//RMII接口  
//    if(HAL_ETH_Init(&ETH_Handler)==HAL_OK) return 0;   //成功
//    else return 1;  //失败  
		
		if(HAL_ETH_Init(&ETH_Handler)==HAL_OK) 
		{
			HAL_ETH_DMATxDescListInit(&ETH_Handler,DMATxDscrTab,Tx_Buff,ETH_TXBUFNB);//初始化发送描述符
			HAL_ETH_DMARxDescListInit(&ETH_Handler,DMARxDscrTab,Rx_Buff,ETH_RXBUFNB);//初始化接收描述符
			/* 使能 MAC 和 DMA 发送和接收 */
			HAL_ETH_Start(&ETH_Handler);
			return 0;   //成功
		}
		else return 1;  //失败 
}

//ETH底层驱动,时钟使能,引脚配置
//此函数会被HAL_ETH_Init()调用
//heth:以太网句柄
void HAL_ETH_MspInit(ETH_HandleTypeDef *heth)
{
    GPIO_InitTypeDef GPIO_Initure;
    
    __HAL_RCC_ETH_CLK_ENABLE();       //开启ETH时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();			//开启GPIOA时钟
		__HAL_RCC_GPIOB_CLK_ENABLE();			//开启GPIOB时钟
    __HAL_RCC_GPIOC_CLK_ENABLE();			//开启GPIOC时钟
    __HAL_RCC_GPIOG_CLK_ENABLE();			//开启GPIOG时钟
    
    /*网络引脚设置 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 --------------------> PG13
    ETH_RMII_TXD1 --------------------> PG14
    ETH_RESET-------------------------> PCF8574扩展IO*/
    
    //PA1,2,7
    GPIO_Initure.Pin=GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_7; 
    GPIO_Initure.Mode=GPIO_MODE_AF_PP;          //推挽复用
    GPIO_Initure.Pull=GPIO_NOPULL;              //不带上下拉
    GPIO_Initure.Speed=GPIO_SPEED_HIGH;         //高速
    GPIO_Initure.Alternate=GPIO_AF11_ETH;       //复用为ETH功能
    HAL_GPIO_Init(GPIOA,&GPIO_Initure);         //初始化
    
    //PB11
    GPIO_Initure.Pin=GPIO_PIN_11;               //PB11
    HAL_GPIO_Init(GPIOB,&GPIO_Initure);         //始化
    
    //PC1,4,5
    GPIO_Initure.Pin=GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5; //PC1,4,5
    HAL_GPIO_Init(GPIOC,&GPIO_Initure);         //初始化
	
    //PG13,14
    GPIO_Initure.Pin=GPIO_PIN_13|GPIO_PIN_14;   //PG13,14
    HAL_GPIO_Init(GPIOG,&GPIO_Initure);         //初始化
}


//读取PHY寄存器值
u32 LAN8720_ReadPHY(u16 reg)
{
    u32 regval;
    HAL_ETH_ReadPHYRegister(&ETH_Handler,reg,&regval);
    return regval;
}

//向LAN8720指定寄存器写入值
//reg:要写入的寄存器
//value:要写入的值
void LAN8720_WritePHY(u16 reg,u16 value)
{
    u32 temp=value;
    HAL_ETH_ReadPHYRegister(&ETH_Handler,reg,&temp);
}

//得到8720的速度模式
//返回值:
//001:10M半双工
//101:10M全双工
//010:100M半双工
//110:100M全双工
//其他:错误.
u8 LAN8720_Get_Speed(void)
{
	u8 speed;
	speed=((LAN8720_ReadPHY(31)&0x1C)>>2); //从LAN8720的31号寄存器中读取网络速度和双工模式
	return speed;
}

extern void lwip_pkt_handle(void);		//在lwip_comm.c里面定义

//中断服务函数
void ETH_IRQHandler(void)
{
    printf("进入中断\r\n");
    while(ETH_GetRxPktSize(ETH_Handler.RxDesc))   
    {
        printf("接收到数据\r\n");
//        lwip_pkt_handle();//处理以太网数据,即将数据提交给LWIP
    }
    //清除中断标志位
    __HAL_ETH_DMA_CLEAR_IT(&ETH_Handler,ETH_DMA_IT_NIS); 
    __HAL_ETH_DMA_CLEAR_IT(&ETH_Handler,ETH_DMA_IT_R); 

}

//获取接收到的帧长度
//DMARxDesc:接收DMA描述符
//返回值:接收到的帧长度
u32  ETH_GetRxPktSize(ETH_DMADescTypeDef *DMARxDesc)
{
    u32 frameLength = 0;
    if(((DMARxDesc->Status&ETH_DMARXDESC_OWN)==(uint32_t)RESET) &&
     ((DMARxDesc->Status&ETH_DMARXDESC_ES)==(uint32_t)RESET) &&
     ((DMARxDesc->Status&ETH_DMARXDESC_LS)!=(uint32_t)RESET)) 
    {
        frameLength=((DMARxDesc->Status&ETH_DMARXDESC_FL)>>ETH_DMARXDESC_FRAME_LENGTHSHIFT);
    }
    return frameLength;
}

//为ETH底层驱动申请内存
//返回值:0,正常
//    其他,失败
u8 ETH_Mem_Malloc(void)
{ 
	DMARxDscrTab=mymalloc(SRAMDTCM,ETH_RXBUFNB*sizeof(ETH_DMADescTypeDef));//申请内存
	DMATxDscrTab=mymalloc(SRAMDTCM,ETH_TXBUFNB*sizeof(ETH_DMADescTypeDef));//申请内存  
	Rx_Buff=mymalloc(SRAMDTCM,ETH_RX_BUF_SIZE*ETH_RXBUFNB);	//申请内存
	Tx_Buff=mymalloc(SRAMDTCM,ETH_TX_BUF_SIZE*ETH_TXBUFNB);	//申请内存
	if(!(u32)&DMARxDscrTab||!(u32)&DMATxDscrTab||!(u32)&Rx_Buff||!(u32)&Tx_Buff)
	{
		ETH_Mem_Free();
		return 1;	//申请失败
	}	
	return 0;		//申请成功
}

//释放ETH 底层驱动申请的内存
void ETH_Mem_Free(void)
{ 
	myfree(SRAMDTCM,DMARxDscrTab);//释放内存
	myfree(SRAMDTCM,DMATxDscrTab);//释放内存
	myfree(SRAMDTCM,Rx_Buff);		//释放内存
	myfree(SRAMDTCM,Tx_Buff);		//释放内存  
}

int EthWrPacket(void* pBuff, int Len)
{
    uint8_t* pDmaBuff;  
		HAL_StatusTypeDef HalStatus; 	
	  /* Clean and Invalidate data cache */
		SCB_CleanInvalidateDCache();   
    if ((ETH_Handler.TxDesc->Status & ETH_DMATXDESC_OWN) == (uint32_t)RESET)    
    {
        pDmaBuff = (uint8_t*)(ETH_Handler.TxDesc->Buffer1Addr);  
        memcpy (pDmaBuff, pBuff, Len);      
        HalStatus = HAL_ETH_TransmitFrame(&ETH_Handler, Len);
        if (HalStatus != HAL_OK)
        {
            printf ("HAL_ETH_TransmitFrame err %d\n", HalStatus);       
            return NULL;
        }                  
        return Len;
    }         
    else
    {
        return NULL;
    }
} 

int EthRdPacket(void* pBuff)
{
    int Len;
    uint8_t* pDmaBuff;
    HAL_StatusTypeDef HalStatus; 
    HalStatus = HAL_ETH_GetReceivedFrame(&ETH_Handler);            // check if a packet has been received
	  /* Clean and Invalidate data cache */
		SCB_CleanInvalidateDCache(); 
    if (HalStatus == HAL_OK)                                // packet received
    {
        Len = ETH_Handler.RxFrameInfos.length;                     // packet lenght        
        pDmaBuff = (uint8_t*)ETH_Handler.RxFrameInfos.buffer;      // DMA buffer pointe           
        memcpy (pBuff, pDmaBuff, Len);                      // read the data                                                                                              
        ETH_Handler.RxFrameInfos.FSRxDesc->Status |= ETH_DMARXDESC_OWN;// release the descriptor   
        ETH_Handler.RxFrameInfos.SegCount = 0;                     // reset segment count                
        return Len;                                         // return the number of bytes read
    }   
    else
    {   
        return NULL;                                        // no packet received
    }
}   
  • 9
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于STM32平台的EtherCAT主站的源代码例程是指用于实现EtherCAT主站功能的代码示例。EtherCATEthernet for Control Automation Technology)是一种以太网通信协议,它可以实现高性能、实时性的工业控制系统。STM32是一种微控制器系列,常用于嵌入式系统和物联网设备。 在该源码例程中,主要包括以下几个方面的功能: 1. 初始化EtherCAT主站:设置STM32EtherCAT总线之间的通信参数和初始化相关硬件资源,如CAN接口和中断。 2. EtherCAT主站通信:与其他EtherCAT设备进行通信,包括发送和接收数据报文,处理EtherCAT帧以及实现EtherCAT协议的各个功能。 3. 处理从站设备:与连接到EtherCAT总线上的从站设备进行通信,包括配置从站设备、发送和接收从站设备的数据等。 4. 实现EtherCAT主站的主要功能:根据实际需求,可以添加不同的功能模块,如数据采集、控制逻辑等。 该源代码例程可能包含多个文件,其中可能包括主函数文件、EtherCAT主站驱动文件、CAN通信文件、EtherCAT协议处理文件等。开发者可以根据具体需求进行修改和扩展。 通过使用该源代码例程,开发者可以在STM32平台上快速开发出具有EtherCAT通信功能的主站设备,以满足工业自动化控制系统的需求。但需要注意,由于EtherCAT协议本身较为复杂,对于初次接触EtherCAT的开发者来说,可能需要一定的学习和熟悉过程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值