基于stm32f107 stm32cube 和 LWIP 协议实现 udp 组播通信

最近在做一个基于stm32f107 实现 UDP 组播通信的项目,项目基于 stm32cube 配置生成,如下图:

UDP组播头文件:

#ifndef __MULTICAST_H__
#define __MULTICAST_H__

#include "lwip/udp.h"
#include "lwip/pbuf.h"
#include "lwip/igmp.h"
#include "User.h"

/*port */
#define UDP_MULTICASE_RECV_PORT 8642    // multicast port for recive
#define UDP_MULTICASE_SEND_PORT 8642    // multicast port for send
#define  MULTUDP_BUF_SIZE     2048

extern struct udp_pcb* udp_server_multi_pcb;             //组播PCB控制块
extern ip4_addr_t ipgroup_rev,ipgroup_send;


void multicast_send_data(unsigned char * data,unsigned short len);
void udp_server_rev(void *arg, struct udp_pcb *pcb, struct pbuf *p,const ip_addr_t *addr, u16_t port);
void Multicast_Config( void );
void MulticastUDP_SendTest(void);



#endif /* __MULTICAST_H__ */

UDP组播C文件:

/**
  ******************************************************************************
  * @file    udp_echoserver.c
  * @author  MCD Application Team
  * @version V1.1.0
  * @date    31-July-2013
  * @brief   UDP echo server
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2013 STMicroelectronics</center></h2>
  *
  * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
  * You may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
  *
  *        http://www.st.com/software_license_agreement_liberty_v2
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "lwip/pbuf.h"
#include "lwip/udp.h"
#include "lwip/tcp.h"
#include "lwip.h"
#include <string.h>
#include <stdio.h>
#include "multicast.h"
#include "User.h"

#define PRINT(...)

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
struct udp_pcb* udp_server_multi_pcb;             // 组播PCB控制块
ip4_addr_t ipgroup_rev,ipgroup_send;






// UDP发送
void multicast_send_data(unsigned char * data,unsigned short len)
{
    struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT,len, PBUF_RAM);

    memcpy(p->payload, data, len);
    udp_sendto(udp_server_multi_pcb, p,&ipgroup_send,UDP_MULTICASE_SEND_PORT);
    pbuf_free(p);

}

// 组播接收,回调函数
void udp_server_rev(void *arg, struct udp_pcb *pcb, struct pbuf *p,const ip_addr_t *addr, u16_t port)
{
#if 1

    ProtoFrame_t UdpRxTemp;

    if( p != NULL )
    {
        if( p->tot_len >= sizeof(ProtoFrame_t)  )
            memcpy( (void *)&UdpRxTemp, p->payload, sizeof(ProtoFrame_t) );
        else memcpy( (void *)&UdpRxTemp,p ->payload,p->tot_len );

        // 校验数据是否符合协议
        if( (FrameCheck(&UdpRxTemp) == 1) || (FrameCheck(&UdpRxTemp) == 2) )
        {
            UdpRxCmd[UdpRxCmdWrite] = UdpRxTemp;
            UdpRxCmdWrite = (UdpRxCmdWrite+1)%MAX_CMD_BUFF;
        }
		
        if( FrameCheck(&UdpRxTemp) > 1 )  // 转发数据到 485
        {
						TxCmd[TxCmdWrite] = UdpRxTemp;
            TxCmdWrite = (TxCmdWrite+1)%MAX_CMD_BUFF;
        }
		
        /* Free the p buffer */
        pbuf_free(p);
    }

#else

    // 回显功能
    /* Connect to the remote client */
    udp_connect(pcb, addr, UDP_MULTICASE_SEND_PORT);

    /* Tell the client that we have accepted it */
    //udp_send(pcb, p);
    udp_sendto(pcb, p,&ipgroup_send,UDP_MULTICASE_SEND_PORT);

    /* free the UDP connection, so we can accept new clients */
    udp_disconnect(pcb);

    /* Free the p buffer */
    pbuf_free(p);

#endif
}

void Multicast_Config( void )
{
    err_t err;

    IP4_ADDR(&ipgroup_rev, 224,0,0,100);   													// 用于接收组播的地址
    IP4_ADDR(&ipgroup_send, 224,0,0,100);  													// 用于发送组播的地址
#if LWIP_IGMP
    igmp_joingroup(IP_ADDR_ANY,&ipgroup_rev);   // 只需要将接收地址放入igmp组,发送的不需要
#endif

    udp_server_multi_pcb = udp_new();
    if ( udp_server_multi_pcb )
    {
        /* Bind the upcb to the UDP_PORT port */
        /* Using IP_ADDR_ANY allow the upcb to be used by any local interface */
        err = udp_bind(udp_server_multi_pcb,IP_ADDR_ANY,UDP_MULTICASE_RECV_PORT);

        if(err == ERR_OK)
        {
            /* Set a receive callback for the upcb */
            udp_recv(udp_server_multi_pcb,udp_server_rev,NULL);
        }
        else
        {
            udp_remove(udp_server_multi_pcb);
            PRINT("can not bind pcb");
        }
    }
    else
    {
        PRINT("can not create pcb");
    }


}


// 测试发送的方法
void MulticastUDP_SendTest(void)
{
    multicast_send_data("igmp test\r\n",11 );
}



/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

注意,我在做好上面的代码后发现,能实现UDP发送,但收到不信号,经过多方查找发现是因为网络接口配置有些问题,

找到stm32f1xx_hal_eth.c文件中的 ETH_MACDMAConfig 函数 ,如下图,将代码改成:

/**
  * @brief  Configures Ethernet MAC and DMA with default parameters.
  * @param  heth: pointer to a ETH_HandleTypeDef structure that contains
  *         the configuration information for ETHERNET module
  * @param  err: Ethernet Init error
  * @retval HAL status
  */
static void ETH_MACDMAConfig(ETH_HandleTypeDef *heth, uint32_t err)
{
  ETH_MACInitTypeDef macinit;
  ETH_DMAInitTypeDef dmainit;
  uint32_t tmpreg1 = 0U;

  if (err != ETH_SUCCESS) /* Auto-negotiation failed */
  {
    /* Set Ethernet duplex mode to Full-duplex */
    (heth->Init).DuplexMode = ETH_MODE_FULLDUPLEX;

    /* Set Ethernet speed to 100M */
    (heth->Init).Speed = ETH_SPEED_100M;
  }

  /* Ethernet MAC default initialization **************************************/
  macinit.Watchdog = ETH_WATCHDOG_ENABLE;
  macinit.Jabber = ETH_JABBER_ENABLE;
  macinit.InterFrameGap = ETH_INTERFRAMEGAP_96BIT;
  macinit.CarrierSense = ETH_CARRIERSENCE_ENABLE;
  macinit.ReceiveOwn = ETH_RECEIVEOWN_ENABLE;
  macinit.LoopbackMode = ETH_LOOPBACKMODE_DISABLE;
  if (heth->Init.ChecksumMode == ETH_CHECKSUM_BY_HARDWARE)
  {
    macinit.ChecksumOffload = ETH_CHECKSUMOFFLAOD_ENABLE;
  }
  else
  {
    macinit.ChecksumOffload = ETH_CHECKSUMOFFLAOD_DISABLE;
  }
  macinit.RetryTransmission = ETH_RETRYTRANSMISSION_DISABLE;
  macinit.AutomaticPadCRCStrip = ETH_AUTOMATICPADCRCSTRIP_DISABLE;
  macinit.BackOffLimit = ETH_BACKOFFLIMIT_10;
  macinit.DeferralCheck = ETH_DEFFERRALCHECK_DISABLE;
  macinit.ReceiveAll = ETH_RECEIVEAll_DISABLE; // ETH_RECEIVEALL_ENABLE; // 
  macinit.SourceAddrFilter = ETH_SOURCEADDRFILTER_DISABLE;
  macinit.PassControlFrames = ETH_PASSCONTROLFRAMES_BLOCKALL;
  macinit.BroadcastFramesReception = ETH_BROADCASTFRAMESRECEPTION_ENABLE;
  macinit.DestinationAddrFilter = ETH_DESTINATIONADDRFILTER_NORMAL;
  macinit.PromiscuousMode = ETH_PROMISCUOUS_MODE_DISABLE;
  macinit.MulticastFramesFilter = ETH_MULTICASTFRAMESFILTER_NONE;  // ETH_MULTICASTFRAMESFILTER_PERFECT;
  macinit.UnicastFramesFilter = ETH_UNICASTFRAMESFILTER_PERFECT;
  macinit.HashTableHigh = 0x0U;
  macinit.HashTableLow = 0x0U;
  macinit.PauseTime = 0x0U;
  macinit.ZeroQuantaPause = ETH_ZEROQUANTAPAUSE_DISABLE;
  macinit.PauseLowThreshold = ETH_PAUSELOWTHRESHOLD_MINUS4;
  macinit.UnicastPauseFrameDetect = ETH_UNICASTPAUSEFRAMEDETECT_DISABLE;
  macinit.ReceiveFlowControl = ETH_RECEIVEFLOWCONTROL_DISABLE;
  macinit.TransmitFlowControl = ETH_TRANSMITFLOWCONTROL_DISABLE;
  macinit.VLANTagComparison = ETH_VLANTAGCOMPARISON_16BIT;
  macinit.VLANTagIdentifier = 0x0U;

  /*------------------------ ETHERNET MACCR Configuration --------------------*/
  /* Get the ETHERNET MACCR value */
  tmpreg1 = (heth->Instance)->MACCR;
  /* Clear WD, PCE, PS, TE and RE bits */
  tmpreg1 &= ETH_MACCR_CLEAR_MASK;
  /* Set the WD bit according to ETH Watchdog value */
  /* Set the JD: bit according to ETH Jabber value */
  /* Set the IFG bit according to ETH InterFrameGap value */
  /* Set the DCRS bit according to ETH CarrierSense value */
  /* Set the FES bit according to ETH Speed value */
  /* Set the DO bit according to ETH ReceiveOwn value */
  /* Set the LM bit according to ETH LoopbackMode value */
  /* Set the DM bit according to ETH Mode value */
  /* Set the IPCO bit according to ETH ChecksumOffload value */
  /* Set the DR bit according to ETH RetryTransmission value */
  /* Set the ACS bit according to ETH AutomaticPadCRCStrip value */
  /* Set the BL bit according to ETH BackOffLimit value */
  /* Set the DC bit according to ETH DeferralCheck value */
  tmpreg1 |= (uint32_t)(macinit.Watchdog |
                        macinit.Jabber |
                        macinit.InterFrameGap |
                        macinit.CarrierSense |
                        (heth->Init).Speed |
                        macinit.ReceiveOwn |
                        macinit.LoopbackMode |
                        (heth->Init).DuplexMode |
                        macinit.ChecksumOffload |
                        macinit.RetryTransmission |
                        macinit.AutomaticPadCRCStrip |
                        macinit.BackOffLimit |
                        macinit.DeferralCheck);

  /* Write to ETHERNET MACCR */
  (heth->Instance)->MACCR = (uint32_t)tmpreg1;

  /* Wait until the write operation will be taken into account:
     at least four TX_CLK/RX_CLK clock cycles */
  tmpreg1 = (heth->Instance)->MACCR;
  HAL_Delay(ETH_REG_WRITE_DELAY);
  (heth->Instance)->MACCR = tmpreg1;

  /*----------------------- ETHERNET MACFFR Configuration --------------------*/
  /* Set the RA bit according to ETH ReceiveAll value */
  /* Set the SAF and SAIF bits according to ETH SourceAddrFilter value */
  /* Set the PCF bit according to ETH PassControlFrames value */
  /* Set the DBF bit according to ETH BroadcastFramesReception value */
  /* Set the DAIF bit according to ETH DestinationAddrFilter value */
  /* Set the PR bit according to ETH PromiscuousMode value */
  /* Set the PM, HMC and HPF bits according to ETH MulticastFramesFilter value */
  /* Set the HUC and HPF bits according to ETH UnicastFramesFilter value */
  /* Write to ETHERNET MACFFR */
  (heth->Instance)->MACFFR = (uint32_t)(macinit.ReceiveAll |
                                        macinit.SourceAddrFilter |
                                        macinit.PassControlFrames |
                                        macinit.BroadcastFramesReception |
                                        macinit.DestinationAddrFilter |
                                        macinit.PromiscuousMode |
                                        macinit.MulticastFramesFilter |
                                        macinit.UnicastFramesFilter);

  /* Wait until the write operation will be taken into account:
     at least four TX_CLK/RX_CLK clock cycles */
  tmpreg1 = (heth->Instance)->MACFFR;
  HAL_Delay(ETH_REG_WRITE_DELAY);
  (heth->Instance)->MACFFR = tmpreg1;

  /*--------------- ETHERNET MACHTHR and MACHTLR Configuration --------------*/
  /* Write to ETHERNET MACHTHR */
  (heth->Instance)->MACHTHR = (uint32_t)macinit.HashTableHigh;

  /* Write to ETHERNET MACHTLR */
  (heth->Instance)->MACHTLR = (uint32_t)macinit.HashTableLow;
  /*----------------------- ETHERNET MACFCR Configuration -------------------*/

  /* Get the ETHERNET MACFCR value */
  tmpreg1 = (heth->Instance)->MACFCR;
  /* Clear xx bits */
  tmpreg1 &= ETH_MACFCR_CLEAR_MASK;

  /* Set the PT bit according to ETH PauseTime value */
  /* Set the DZPQ bit according to ETH ZeroQuantaPause value */
  /* Set the PLT bit according to ETH PauseLowThreshold value */
  /* Set the UP bit according to ETH UnicastPauseFrameDetect value */
  /* Set the RFE bit according to ETH ReceiveFlowControl value */
  /* Set the TFE bit according to ETH TransmitFlowControl value */
  tmpreg1 |= (uint32_t)((macinit.PauseTime << 16U) |
                        macinit.ZeroQuantaPause |
                        macinit.PauseLowThreshold |
                        macinit.UnicastPauseFrameDetect |
                        macinit.ReceiveFlowControl |
                        macinit.TransmitFlowControl);

  /* Write to ETHERNET MACFCR */
  (heth->Instance)->MACFCR = (uint32_t)tmpreg1;

  /* Wait until the write operation will be taken into account:
  at least four TX_CLK/RX_CLK clock cycles */
  tmpreg1 = (heth->Instance)->MACFCR;
  HAL_Delay(ETH_REG_WRITE_DELAY);
  (heth->Instance)->MACFCR = tmpreg1;

  /*----------------------- ETHERNET MACVLANTR Configuration ----------------*/
  /* Set the ETV bit according to ETH VLANTagComparison value */
  /* Set the VL bit according to ETH VLANTagIdentifier value */
  (heth->Instance)->MACVLANTR = (uint32_t)(macinit.VLANTagComparison |
                                           macinit.VLANTagIdentifier);

  /* Wait until the write operation will be taken into account:
     at least four TX_CLK/RX_CLK clock cycles */
  tmpreg1 = (heth->Instance)->MACVLANTR;
  HAL_Delay(ETH_REG_WRITE_DELAY);
  (heth->Instance)->MACVLANTR = tmpreg1;

  /* Ethernet DMA default initialization ************************************/
  dmainit.DropTCPIPChecksumErrorFrame = ETH_DROPTCPIPCHECKSUMERRORFRAME_ENABLE;
  dmainit.ReceiveStoreForward = ETH_RECEIVESTOREFORWARD_ENABLE;
  dmainit.FlushReceivedFrame = ETH_FLUSHRECEIVEDFRAME_ENABLE;
  dmainit.TransmitStoreForward = ETH_TRANSMITSTOREFORWARD_ENABLE;
  dmainit.TransmitThresholdControl = ETH_TRANSMITTHRESHOLDCONTROL_64BYTES;
  dmainit.ForwardErrorFrames = ETH_FORWARDERRORFRAMES_DISABLE;
  dmainit.ForwardUndersizedGoodFrames = ETH_FORWARDUNDERSIZEDGOODFRAMES_DISABLE;
  dmainit.ReceiveThresholdControl = ETH_RECEIVEDTHRESHOLDCONTROL_64BYTES;
  dmainit.SecondFrameOperate = ETH_SECONDFRAMEOPERARTE_ENABLE;
  dmainit.AddressAlignedBeats = ETH_ADDRESSALIGNEDBEATS_ENABLE;
  dmainit.FixedBurst = ETH_FIXEDBURST_ENABLE;
  dmainit.RxDMABurstLength = ETH_RXDMABURSTLENGTH_32BEAT;
  dmainit.TxDMABurstLength = ETH_TXDMABURSTLENGTH_32BEAT;
  dmainit.DescriptorSkipLength = 0x0U;
  dmainit.DMAArbitration = ETH_DMAARBITRATION_ROUNDROBIN_RXTX_1_1;

  /* Get the ETHERNET DMAOMR value */
  tmpreg1 = (heth->Instance)->DMAOMR;
  /* Clear xx bits */
  tmpreg1 &= ETH_DMAOMR_CLEAR_MASK;

  /* Set the DT bit according to ETH DropTCPIPChecksumErrorFrame value */
  /* Set the RSF bit according to ETH ReceiveStoreForward value */
  /* Set the DFF bit according to ETH FlushReceivedFrame value */
  /* Set the TSF bit according to ETH TransmitStoreForward value */
  /* Set the TTC bit according to ETH TransmitThresholdControl value */
  /* Set the FEF bit according to ETH ForwardErrorFrames value */
  /* Set the FUF bit according to ETH ForwardUndersizedGoodFrames value */
  /* Set the RTC bit according to ETH ReceiveThresholdControl value */
  /* Set the OSF bit according to ETH SecondFrameOperate value */
  tmpreg1 |= (uint32_t)(dmainit.DropTCPIPChecksumErrorFrame |
                        dmainit.ReceiveStoreForward |
                        dmainit.FlushReceivedFrame |
                        dmainit.TransmitStoreForward |
                        dmainit.TransmitThresholdControl |
                        dmainit.ForwardErrorFrames |
                        dmainit.ForwardUndersizedGoodFrames |
                        dmainit.ReceiveThresholdControl |
                        dmainit.SecondFrameOperate);

  /* Write to ETHERNET DMAOMR */
  (heth->Instance)->DMAOMR = (uint32_t)tmpreg1;

  /* Wait until the write operation will be taken into account:
     at least four TX_CLK/RX_CLK clock cycles */
  tmpreg1 = (heth->Instance)->DMAOMR;
  HAL_Delay(ETH_REG_WRITE_DELAY);
  (heth->Instance)->DMAOMR = tmpreg1;

  /*----------------------- ETHERNET DMABMR Configuration ------------------*/
  /* Set the AAL bit according to ETH AddressAlignedBeats value */
  /* Set the FB bit according to ETH FixedBurst value */
  /* Set the RPBL and 4*PBL bits according to ETH RxDMABurstLength value */
  /* Set the PBL and 4*PBL bits according to ETH TxDMABurstLength value */
  /* Set the DSL bit according to ETH DesciptorSkipLength value */
  /* Set the PR and DA bits according to ETH DMAArbitration value */
  (heth->Instance)->DMABMR = (uint32_t)(dmainit.AddressAlignedBeats |
                                        dmainit.FixedBurst |
                                        dmainit.RxDMABurstLength |    /* !! if 4xPBL is selected for Tx or Rx it is applied for the other */
                                        dmainit.TxDMABurstLength |
                                        (dmainit.DescriptorSkipLength << 2U) |
                                        dmainit.DMAArbitration |
                                        ETH_DMABMR_USP); /* Enable use of separate PBL for Rx and Tx */

  /* Wait until the write operation will be taken into account:
     at least four TX_CLK/RX_CLK clock cycles */
  tmpreg1 = (heth->Instance)->DMABMR;
  HAL_Delay(ETH_REG_WRITE_DELAY);
  (heth->Instance)->DMABMR = tmpreg1;

  if ((heth->Init).RxMode == ETH_RXINTERRUPT_MODE)
  {
    /* Enable the Ethernet Rx Interrupt */
    __HAL_ETH_DMA_ENABLE_IT((heth), ETH_DMA_IT_NIS | ETH_DMA_IT_R);
  }

  /* Initialize MAC address in ethernet MAC */
  ETH_MACAddressConfig(heth, ETH_MAC_ADDRESS0, heth->Init.MACAddr);
}

这样就可以正常收到组播端口发来的数据了!!

而且实际发现,在我改了st32f1xx_hal_eth.c 中函数 后, LWIP_IGMP 宏即使为 0 也可以实现组播通信!!

代码量比开启 LWIP_IGMP 宏少了 2K 左右。但是就是所有的组播地址中的数据都会被收到,如果要针对某一地址进行组播通信, LWIP_IGMP 宏还是要设为1才行。

猜你喜欢

转载自blog.csdn.net/zhanglifu3601881/article/details/85080907