基于STM32的无协议栈实现UDP通信
前段时间使用STM32收发UDP消息,由于系统只需简单的接收和发送UDP消息,不需要ARP、TCP等复杂功能,首先在网上搜索相关例程,发现有不少人在问这个问题,但是给出的建议多是让移植LWIP,于是本人就在分析LWIP的基础上,找到了网口收发最底层接口,通过最底层收发接口实现UDP消息收发。
STM32的网口只具有MAC层协议,没有物理层PHY;在硬件上首先要外接一个物理层PHY芯片,本人使用的PHY芯片为lan8720,连接方式如图1所示。
图1 STM32与网口物理层芯片连接电路图
网口需要两个缓冲区,发送缓冲区和接收缓冲器。STM32网口收发流程为:首先将数据写入发送缓冲区,然后启动DMA传输开始发送;接收是通过中断去读接收缓冲区,接收完清除DMA传输完成标志。
my_mem_init(SRAMIN); //初始化内存管理
DMARxDscrTab = mymalloc(s32Sram_Address, ETH_RXBUFNB * sizeof(ETH_DMADESCTypeDef));//申请内存
DMATxDscrTab = mymalloc(s32Sram_Address, ETH_TXBUFNB * sizeof(ETH_DMADESCTypeDef));
Rx_Buff = mymalloc(s32Sram_Address, ETH_RX_BUF_SIZE * ETH_RXBUFNB);
Tx_Buff = mymalloc(s32Sram_Address, ETH_TX_BUF_SIZE * ETH_TXBUFNB);
ETH_DMATxDescChainInit(DMATxDscrTab, Tx_Buff, ETH_TXBUFNB); //将发送缓冲区设置为循环链表
ETH_DMARxDescChainInit(DMARxDscrTab, Rx_Buff, ETH_RXBUFNB);//将接收缓冲区设置为循环链表
for(i = 0; i < ETH_TXBUFNB; i++)//使能TCP、UDP和ICMP发送帧校验
{
ETH_DMATxDescChecksumInsertionConfig(&DMATxDscrTab[i], ETH_DMATxDesc_ChecksumTCPUDPICMPFull);
}
if(LAN8720_Init() != 0)
{
PHY_Register_Print(LAN8720_PHY_ADDRESS);//µ÷ÊÔʱ²é¿´LAN8720AËùÓмĴæÆ÷µÄ״̬
printf("LAN8720_Init error! \r\n");
}
LAN8720_Init函数的实现如下:
u8 LAN8720_Init(void)
{
u8 rval
= 0;
int i = 0;
GPIO_InitTypeDef GPIO_InitStructure = {0};
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOG | RCC_AHB1Periph_GPIOD, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_RMII);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_ETH);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_ETH);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_ETH);
//ÅäÖÃPC1,PC4 and PC5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource1, GPIO_AF_ETH);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource4, GPIO_AF_ETH);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource5, GPIO_AF_ETH);
//ÅäÖÃPG11, PG14 and PG13
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_14;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOG, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOG, GPIO_PinSource11, GPIO_AF_ETH);
GPIO_PinAFConfig(GPIOG, GPIO_PinSource13, GPIO_AF_ETH);
GPIO_PinAFConfig(GPIOG, GPIO_PinSource14, GPIO_AF_ETH);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//ÍÆÍêÊä³ö
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIOD, &GPIO_InitStructure);
LAN8720_RST = 1;
delay_ms(50);
LAN8720_RST = 0;
delay_ms(50);
//delay_us(200);
LAN8720_RST = 1;
ETHERNET_NVICConfiguration();//配置以太网终端优先级
rval = ETH_MACDMA_Config();//配置MAC和DMA
if(ETH_SUCCESS != rval)
{
printf("%s: ETH MACDMA config error\r\n",__func__);
}
ETH_MACAddressConfig(ETH_MAC_Address0, src_MAC);
//向MAC地址寄存器写入本机MAC地址
ETH_Start();
return !rval;
}
以太网终端优先级配置如下:
void ETHERNET_NVICConfiguration(void)
{
NVIC_InitTypeDef NVIC_InitStructure = {0};
NVIC_InitStructure.NVIC_IRQChannel = ETH_IRQn; //ÒÔÌ«ÍøÖжÏ
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0X00; //ÖжϼĴæÆ÷×é2×î¸ßÓÅÏȼ¶
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0X00;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
MAC和DMA配置函数如下:
u8 ETH_MACDMA_Config(void)
{
u8 rval = 0;
int s32ETH_Get_Reset_Status_Time = 1024;
ETH_InitTypeDef ETH_InitStructure = {0};
//ʹÄÜÒÔÌ«ÍøMACÒÔ¼°MAC½ÓÊպͷ¢ËÍʱÖÓ
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_ETH_MAC | RCC_AHB1Periph_ETH_MAC_Tx | RCC_AHB1Periph_ETH_MAC_Rx, ENABLE);
ETH_DeInit();
//AHB×ÜÏßÖØÆôÒÔÌ«Íø
ETH_SoftwareReset();
//Èí¼þÖØÆôÍøÂç
while (ETH_GetSoftwareResetStatus() == SET)//µÈ´ýÈí¼þÖØÆôÍøÂçÍê³É £¬ÕâÒ»¿éÈÝÒ×ËÀÔÚÕâÀï
{
/******мÓÄÚÈÝ£¬·ÀÖ¹½øÈëËÀÑ»·ÎÞ·¨Í˳ö*****/
s32ETH_Get_Reset_Status_Time--;
if(!(s32ETH_Get_Reset_Status_Time--))
{
printf("%s(%d):wait ETH softreset timeout\r\n",__func__,__LINE__);
break;
}
}
ETH_StructInit(Ð_InitStructure);
//³õʼ»¯ÍøÂç½á¹¹ÌåΪĬÈÏÖµ
///ÍøÂçMAC²ÎÊýÉèÖÃ
ETH_InitStructure.ETH_AutoNegotiation = ETH_AutoNegotiation_Enable; //¿ªÆôÍøÂç×ÔÊÊÓ¦¹¦ÄÜ
ETH_InitStructure.ETH_LoopbackMode = ETH_LoopbackMode_Disable;//¹Ø±Õ·´À¡
ETH_InitStructure.ETH_RetryTransmission = ETH_RetryTransmission_Disable;//¹Ø±ÕÖØ´«¹¦ÄÜ
ETH_InitStructure.ETH_AutomaticPadCRCStrip = ETH_AutomaticPadCRCStrip_Disable;//¹Ø±Õ×Ô¶¯È¥³ýPDA/CRC¹¦ÄÜ
ETH_InitStructure.ETH_ReceiveAll = ETH_ReceiveAll_Disable;//¹Ø±Õ½ÓÊÕËùÓеÄÖ¡
ETH_InitStructure.ETH_BroadcastFramesReception = ETH_BroadcastFramesReception_Enable;//ÔÊÐí½ÓÊÕËùÓй㲥֡
ETH_InitStructure.ETH_PromiscuousMode = ETH_PromiscuousMode_Disable;//¹Ø±Õ»ìºÏģʽµÄµØÖ·¹ýÂË
ETH_InitStructure.ETH_MulticastFramesFilter = ETH_MulticastFramesFilter_Perfect;//¶ÔÓÚ×é²¥µØÖ·Ê¹ÓÃÍêÃÀµØÖ·¹ýÂË
ETH_InitStructure.ETH_UnicastFramesFilter = ETH_UnicastFramesFilter_Perfect;//¶Ôµ¥²¥µØÖ·Ê¹ÓÃÍêÃÀµØÖ·¹ýÂË
#ifdef CHECKSUM_BY_HARDWARE
ETH_InitStructure.ETH_ChecksumOffload = ETH_ChecksumOffload_Enable;//¿ªÆôipv4ºÍTCP/UDP/ICMPµÄ֡УÑéºÍÐ¶ÔØ
#endif
//µ±ÎÒÃÇʹÓÃ֡УÑéºÍÐ¶ÔØ¹¦ÄܵÄʱºò£¬Ò»¶¨ÒªÊ¹Äܴ洢ת·¢Ä£Ê½,´æ´¢×ª·¢Ä£Ê½ÖÐÒª±£Ö¤Õû¸öÖ¡´æ´¢ÔÚFIFOÖÐ,
//ÕâÑùMACÄܲåÈë/ʶ±ð³ö֡УÑéÖµ,µ±ÕæÐ£ÑéÕýÈ·µÄʱºòDMA¾Í¿ÉÒÔ´¦ÀíÖ¡,·ñÔò¾Í¶ªÆúµô¸ÃÖ¡
ETH_InitStructure.ETH_DropTCPIPChecksumErrorFrame = ETH_DropTCPIPChecksumErrorFrame_Enable; //¿ªÆô¶ªÆúTCP/IP´íÎóÖ¡
ETH_InitStructure.ETH_ReceiveStoreForward = ETH_ReceiveStoreForward_Enable; //¿ªÆô½ÓÊÕÊý¾ÝµÄ´æ´¢×ª·¢Ä£Ê½
ETH_InitStructure.ETH_TransmitStoreForward = ETH_TransmitStoreForward_Enable; //¿ªÆô·¢ËÍÊý¾ÝµÄ´æ´¢×ª·¢Ä£Ê½
ETH_InitStructure.ETH_ForwardErrorFrames = ETH_ForwardErrorFrames_Disable; //½ûֹת·¢´íÎóÖ¡
ETH_InitStructure.ETH_ForwardUndersizedGoodFrames = ETH_ForwardUndersizedGoodFrames_Disable;//²»×ª·¢¹ýСµÄºÃÖ¡
ETH_InitStructure.ETH_SecondFrameOperate = ETH_SecondFrameOperate_Enable; //´ò¿ª´¦ÀíµÚ¶þÖ¡¹¦ÄÜ
ETH_InitStructure.ETH_AddressAlignedBeats = ETH_AddressAlignedBeats_Enable; //¿ªÆôDMA´«ÊäµÄµØÖ·¶ÔÆë¹¦ÄÜ
ETH_InitStructure.ETH_FixedBurst = ETH_FixedBurst_Enable; //¿ªÆô¹Ì¶¨Í»·¢¹¦ÄÜ
ETH_InitStructure.ETH_RxDMABurstLength = ETH_RxDMABurstLength_32Beat; //DMA·¢Ë͵Ä×î´óÍ»·¢³¤¶ÈΪ32¸ö½ÚÅÄ
ETH_InitStructure.ETH_TxDMABurstLength = ETH_TxDMABurstLength_32Beat;//DMA½ÓÊÕµÄ×î´óÍ»·¢³¤¶ÈΪ32¸ö½ÚÅÄ
ETH_InitStructure.ETH_DMAArbitration = ETH_DMAArbitration_RoundRobin_RxTx_2_1;
rval = ETH_Init(Ð_InitStructure,LAN8720_PHY_ADDRESS);//ÅäÖÃETH
#if 1 //ÔΪ0
if(rval == ETH_SUCCESS)//ʹÄÜÒÔÌ«ÍøµÄ·¢ËͺͽÓÊÕÖжÏ
{
//ETH_DMAITConfig(ETH_DMA_IT_NIS | ETH_DMA_IT_T, ENABLE);
ETH_DMAITConfig(ETH_DMA_IT_NIS | ETH_DMA_IT_R | ETH_DMA_IT_T, ENABLE);
}
#endif
return rval;
}
网口接收中断函数如下:
void ETH_IRQHandler(void)
//最好不要在中断函数中处理数据,而是将数据尽快拷出来在其他地方处理
{
static unsigned int i = 0;
FrameTypeDef frame = {0};
printf("%s(%d)\r\n",__func__,__LINE__);
if((FrameLength_Receice = ETH_GetRxPktSize(DMARxDescToGet)) != 0)//¼ì²âÊÇ·ñÊÕµ½Êý¾Ý°ü£¬²¢½«±¨µÄ³¤¶È±£´æÆðÀ´
{
frame = ETH_Rx_Packet(); //¼ì²âµ½½ÓÊÕÁËÊý¾Ý°üºó£¬½ÓÊÕµÄÊý¾Ý°üÔÚ½ÓÊÕ»º³åÇøÖÐ
if(ETH_ERROR != frame.length)
{
if(eth_buff[6] != 0)
{
i++;
printf("%s(%d) lost %d package\r\n",__func__,__LINE__,i);
}
else
{
memcpy(eth_buff, (void *)frame.buffer, frame.length);
}
}
frame.descriptor->Status = ETH_DMARxDesc_OWN;//ÉèÖÃRxÃèÊö·ûOWNλ,bufferÖØ¹éETH DMA
if((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET)//µ±Rx Buffer²»¿ÉÓÃλ(RBUS)±»ÉèÖõÄʱºò,ÖØÖÃËü.»Ö¸´´«Êä
{
ETH->DMASR = ETH_DMASR_RBUS;//ÖØÖÃETH DMA RBUSλ
ETH->DMARPDR = 0;//»Ö¸´DMA½ÓÊÕ
}
}
ETH_DMAClearITPendingBit(ETH_DMA_IT_R);
//Çå³ýDMAÖжϱê־λ
ETH_DMAClearITPendingBit(ETH_DMA_IT_NIS);//Çå³ýDMA½ÓÊÕÖжϱê־λ
//Receive_ARP(); //¿½±´»º³åÇøÄڵĽÓÊÕÊý¾Ýµ½Ö¸¶¨½á¹¹Ìå
//Answer_ARP(); //¸ø³öarpÓ¦´ð
}