本章实现了基于STM32F407MAC的数据收发功能,通过开发板的RJ45接口连接网线到电脑,电脑使用Wiershark工具抓包验证。
参考文档:
STM32F4xx参考手册
一、工程模板以及参考源码的获取
工程源码我使用的正点原子的探索者开发板STM32F407(V2)参考源码:正点原子文档中心,在网盘文件中的工程源码->扩展例程->FreeRTOS例程->FreeRTOS实验6-1 FreeRTOS任务创建和删除实验(动态方法),这个实验直接使用裸机也没有问题,不过后面移植LwIP协议栈时就需要使用操作系统了。
参考源码使用的是STM官方的F407移植LwIP的例程-STSW-STM32070,这个例程主要是STM32移植LwIP实现TP/IP通讯的,我们只需要参考里面关于MAC配置部分代码即可。
二、开发板STM32以及DP83848 PHY的硬件配置
开发板的单片机使用的是STM32F407ZGT6,与原子的例程一致就不需要修改了,开发板的DP83848硬件连接如下,完整原理图放在文末。
主要的连接就是与单片机连接的RMII接口和连接网线的RJ45接口,其他的外部配置可以参考上一章或者DP83848的手册。DP83848IV与DP83848EP只是性能不同,但是硬软件配置是一样的。
三、软件配置
在准备好开发板以及工程源码后就可以开始配置工程和写相关代码了。
3.1、添加STM32F4x7_ETH驱动库文件到工程
STM32F4xx的固件库是没有ETH驱动的,STM32F4x7_ETH驱动库文件在我们下载的STSW-STM32070的STM32F4x7_ETH_LwIP_V1.1.0-Libraries-STM32F4x7_ETH_Driver,这个里面是ETH驱动的头文件和源码,注意stm32f4x7_eth_conf_template.h文件是PHY的接口示例文件,使用不同的PHY就需要修改里面的配置,主要为不同PHY的部分寄存器地址不同,需要将文件名修改为stm32f4x7_eth_conf.h直接拿来使用。官方例程使用的也是DP83848,因此不需要修改。
ETH外设需要用到延时函数,通过定义 USE_Delay可以使用我们自定的延时,比如原子例程提供的sys.c中的系统定时器延时,注释掉#define USE_Delay的话就是用ETH驱动库自带的简单延时。
新建dp83848.c和dp83848.h文件并添加到工程,添加过程就不赘述了。
添加好文件的工程如下(注意需要将.h文件的路径添加到工程)
3.2、PYH接口(RMII、SMI)的配置(dp83848.c)
文件添加好之后接下来就是硬件接口的配置了,STM32的RMII、SMI接口配置如下
/* Private function prototypes -----------------------------------------------*/
static void ETH_GPIO_Config(void);
/** * @brief Configures the different GPIO ports. * @param None * @retval None */ void ETH_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; /*Enable GPIO clocks*/ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOG, ENABLE); /*Enable SYSCFG clock*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); /*DP83848 使用单独的外部晶振,不使用STM32提供时钟源*/ #ifdef PHY_CLOCK_MCO /* Configure MCO (PA8) */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; 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); #endif /* MII/RMII Media interface selection --------------------------------------*/ SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_RMII); /* Ethernet pins configuration ************************************************/ /*网络引脚设置 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 -------------------> PG11 ETH_RMII_TXD0 --------------------> PG13 ETH_RMII_TXD1 --------------------> PG14*/ //配置PA1 PA2 PA7 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_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_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); //配置PD3为推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; 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); }
主要就是将对应RMII、SMI接口配置为复用,对于MDC的PA8接口,DP83848直接使用外部50MHz晶振(原理图的Y3),所以不需要配置。
3.3、配置ETH_MACDMA以太网接口(dp83848.c)
/* Private function prototypes -----------------------------------------------*/ static void ETH_MACDMA_Config(void);
/** * @brief Configures the Ethernet Interface * @param None * @retval None */ static void ETH_MACDMA_Config(void) { ETH_InitTypeDef ETH_InitStructure; /* Enable ETHERNET clock */ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_ETH_MAC | RCC_AHB1Periph_ETH_MAC_Tx | RCC_AHB1Periph_ETH_MAC_Rx, ENABLE); /* Reset ETHERNET on AHB Bus */ ETH_DeInit(); /* Software reset */ ETH_SoftwareReset(); /* Wait for software reset */ while (ETH_GetSoftwareResetStatus() == SET); /* ETHERNET Configuration --------------------------------------------------*/ /* Call ETH_StructInit if you don't like to configure all ETH_InitStructure parameter */ ETH_StructInit(Ð_InitStructure); /* Fill ETH_InitStructure parametrs */ /*------------------------ MAC -----------------------------------*/ ETH_InitStructure.ETH_AutoNegotiation = ETH_AutoNegotiation_Enable; // ETH_InitStructure.ETH_AutoNegotiation = ETH_AutoNegotiation_Disable; // ETH_InitStructure.ETH_Speed = ETH_Speed_10M; // ETH_InitStructure.ETH_Mode = ETH_Mode_FullDuplex; ETH_InitStructure.ETH_LoopbackMode = ETH_LoopbackMode_Disable; ETH_InitStructure.ETH_RetryTransmission = ETH_RetryTransmission_Disable; ETH_InitStructure.ETH_AutomaticPadCRCStrip = ETH_AutomaticPadCRCStrip_Disable; 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; #endif
/*------------------------ DMA -----------------------------------*/ /* When we use the Checksum offload feature, we need to enable the Store and Forward mode:
the store and forward guarantee that a whole frame is stored in the FIFO, so the MAC can insert/verify
the checksum,if the checksum is OK the DMA can handle the frame otherwise the frame is dropped */ ETH_InitStructure.ETH_DropTCPIPChecksumErrorFrame = ETH_DropTCPIPChecksumErrorFrame_Enable; 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; ETH_InitStructure.ETH_FixedBurst = ETH_FixedBurst_Enable; ETH_InitStructure.ETH_RxDMABurstLength = ETH_RxDMABurstLength_32Beat; ETH_InitStructure.ETH_TxDMABurstLength = ETH_TxDMABurstLength_32Beat; ETH_InitStructure.ETH_DMAArbitration = ETH_DMAArbitration_RoundRobin_RxTx_2_1; /* Configure Ethernet */ ETH_Init(Ð_InitStructure, DP83848_PHY_ADDRESS); /* Enable the Ethernet Rx Interrupt */ ETH_DMAITConfig(ETH_DMA_IT_NIS | ETH_DMA_IT_R, ENABLE); }
以上函数参考了官方代码stm32f4x7_eth_bsp.c中的 static void ETH_MACDMA_Config(void)函数代码。
首先初始化ETH相关时钟的时钟,然后ETH_DeInit()复位ETH外设寄存器的值、ETH外设软件复位,复位完成后就可以配置ETH结构体了。
/* Call ETH_StructInit if you don't like to configure all ETH_InitStructure parameter */
ETH_StructInit(Ð_InitStructure);
以上直接配置了ETH的默认值,然后可以根据需要配置需要修改的结构体成员。
/* Configure Ethernet */ ETH_Init(Ð_InitStructure, DP83848_PHY_ADDRESS);
之后就是初始化PHY了。在默认情况下DP83848的配置地址为0x01,这是由DP83848的硬件配置决定的,开发板没有进行额外配置,因此PHY地址为0x01,地址宏定义在dp83848.h。如果需要配置PHY地址可以参考第二章的第三节配置或者DP83848参考手册。
/* Enable the Ethernet Rx Interrupt */ ETH_DMAITConfig(ETH_DMA_IT_NIS | ETH_DMA_IT_R, ENABLE);
最后使能了ETH的输入。
3.4、配置ETH中断和中断处理函数
/* Private function prototypes -----------------------------------------------*/ static void ETH_NVIC_Config(void); /** * @brief Configures and enable the Ethernet global interrupt. * @param None * @retval None */ void ETH_NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; /* Enable the Ethernet global Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = ETH_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } /** * @brief This function handles ethernet DMA interrupt request. * @param None * @retval None */ void ETH_IRQHandler(void) { /* Handles all the received frames */ /* check if any packet received */ while(ETH_CheckFrameReceived()){ /* process received ethernet packet */ Pkt_Handle(); } /* Clear the Eth DMA Rx IT pending bits */ ETH_DMAClearITPendingBit(ETH_DMA_IT_R); ETH_DMAClearITPendingBit(ETH_DMA_IT_NIS); }
以上为ETH的接收中断配置中断函数stm32f4x7_eth_bsp.c中配置的分组为1子优先级为0,优先级低于系统定时器,当接收到数据包时就会触发中断。
/* check if any packet received */ while(ETH_CheckFrameReceived()){ /* process received ethernet packet */ Pkt_Handle();
}
ETH_CheckFrameReceived()会检查是否接收到数据包,如果接收到数据包则会调用Pke_Handle()回调函数。
在处理完后,调用ETH_DMAClearITPendingBit()将相关的中断标志清除,等待下一次触发中断。
在官方例程中,ETH_IRQHandler(void)中则是使用信号量唤醒LwIP任务,之后的LwIP移植也是这样实现。
3.5、中断回调函数Pkt_Handle()
/* Private function prototypes -----------------------------------------------*/ void Pkt_Handle(void);
/**
* @brief Configures the Ethernet Interface
* @param None
* @retval None
*/ void Pkt_Handle(void) { struct pbuf *p= NULL, *q; uint32_t receiveLen; FrameTypeDef frame; u8 *receiveBuffer; __IO ETH_DMADESCTypeDef *DMARxNextDesc; uint32_t i=0; /* get received frame */ frame = ETH_Get_Received_Frame(); /* Obtain the size of the packet and put it into the "len" variable. */ receiveLen = frame.length; receiveBuffer = (u8 *)frame.buffer; printf("0011%d0022\n", receiveLen); //将每一个的包长度发往串口 if(receiveBuffer[41] == 201){ /*如果第42字节是十进制201,则将整个包内容发往串口*/ for (i = 0; i < receiveLen; i++){ printf("%c", receiveBuffer[i]); } } /* Check if frame with multiple DMA buffer segments */ if (DMA_RX_FRAME_infos->Seg_Count > 1) { DMARxNextDesc = DMA_RX_FRAME_infos->FS_Rx_Desc; }else{ DMARxNextDesc = frame.descriptor; }
/* Release descriptors to DMA */
//DMARxNextDesc = frame.descriptor; /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ for (i = 0; i < DMA_RX_FRAME_infos->Seg_Count; i++) { DMARxNextDesc->Status = ETH_DMARxDesc_OWN; DMARxNextDesc = (ETH_DMADESCTypeDef *)(DMARxNextDesc->Buffer2NextDescAddr); } /* Clear Segment_Count */ DMA_RX_FRAME_infos->Seg_Count = 0; /* When Rx Buffer unavailable flag is set: clear it and resume reception */ if ((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET) { /* Clear RBUS ETHERNET DMA flag */ ETH->DMASR = ETH_DMASR_RBUS; /* Resume DMA reception */ ETH->DMARPDR = 0; } }
函数Pkt_Handle(void) 参考了官方例程中Utilities/Third_Party_lwip-1.4.1中的ethernetif.c文件中的low_level_output()函数,这个是官方例程的发包函数,我们只需要将受到的数据包的大小以及数据发送到串口。
ETH_Get_Received_Frame()
用于获取网络数据包,获取数据包有两个函数分别为ETH_Get_Received_Frame(void)和ETH_Get_Received_Frame_interrupt(void),前者用于轮询接收后者用于中断接收。
/* Obtain the size of the packet and put it into the "len" variable. */ receiveLen = frame.length; receiveBuffer = (u8 *)frame.buffer; printf("0011%d0022\n", receiveLen); //将每一个的包长度发往串口 if(receiveBuffer[41] == 10){ /*如果第42字节是十进制201,则将整个包内容发往串口*/ for (i = 0; i < receiveLen; i++){ printf("%c", receiveBuffer[i]); } }
接下来就是获取数据包的长度receiveLen与数据receiveBuffer,通过串口打印出来。通过检测数据包的第42个字节为10,然后将收到的帧打印,我们使用 ping 192.168.2.10的第42个字节就是10。
/* Check if frame with multiple DMA buffer segments */ if (DMA_RX_FRAME_infos->Seg_Count > 1) { DMARxNextDesc = DMA_RX_FRAME_infos->FS_Rx_Desc; }else{ DMARxNextDesc = frame.descriptor; }
已经完成了这一帧数据的操作,需要将帧状态的分段计数Seg_Count减小到1,再向DMA释放描述符。
3.6、ETH发送函数
/* Global pointers to track current transmit and receive descriptors */ extern ETH_DMADESCTypeDef *DMATxDescToSet; extern ETH_DMADESCTypeDef *DMARxDescToGet; /** * @brief ETH_BSP_Config * @param Data、Length * @retval None */ void DP83848Send(u8* data, u16 length){ memcpy((u8 *)DMATxDescToSet->Buffer1Addr, data, length); /* Prepare transmit descriptors to give to DMA*/ ETH_Prepare_Transmit_Descriptors(length); }
ETH发送函数参考了官方例程中Utilities/Third_Party_lwip-1.4.1中的ethernetif.c中的low_level_output()函数,这里直接将data的指针的数据复制到当前发送描述符数据地址Addr1中,直接使用ETH_Prepare_Transmit_Descriptors();发送数据。
3.7、ETH_DMA初始化
#include "DP83848.h" #include "stm32f4x7_eth.h" #include <string.h> #include <stdio.h> /* Ethernet Rx & Tx DMA Descriptors */ extern ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB]; /* Ethernet Receive buffers */ extern uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; /* Ethernet Transmit buffers */ extern uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; void DP83848Init(uint8_t* HWADDR){ int i; /* Configure ethernet (GPIOs, clocks, MAC, DMA) */ ETH_BSP_Config(); /* initialize MAC address in ethernet MAC */ ETH_MACAddressConfig(ETH_MAC_Address0, HWADDR); /* Initialize Tx Descriptors list: Chain Mode */ ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB); /* Initialize Rx Descriptors list: Chain Mode */ ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB); /* Enable the TCP, UDP and ICMP checksum insertion for the Tx frames */ for(i = 0; i < ETH_TXBUFNB; i++) { ETH_DMATxDescChecksumInsertionConfig(&DMATxDscrTab[i], ETH_DMATxDesc_ChecksumTCPUDPICMPFull); } ETH_Start(); }
ETH初始化函数参考了官方例程中Utilities/Third_Party_lwip-1.4.1中的ethernetif.c中的low_level_init()函数,ETH_BSP_Config()初始化了相关配置,ETH_MACAddressConfig()初始化MAC地址,ETH_DMATxDescChainInit()和ETH_DMARxDescChainInit()创建了两个链状描述符,FIFO最大只有2KB,因此FIFO设定的最大阈值不会超过2KB(具体值参考STM32F4x手册),当单个帧大于FIFO设定阈值时,单个描述符描述不了一个帧,大于的数据的地址将会存入下一个描述符,以此类推。
描述符两种结构可以参考STMF4xx官方手册的29.6节:以太网功能说明:DMA 控制器操作
官方手册的帧发送处理和帧接收处理两小节提到了描述符再数据处理过程的作用
描述符可参考:以太网DMA描述符
3.8、DP83848.h文件
#ifndef __DP83848_H #define __DP83848_H #include "sys.h" /* Exported constants --------------------------------------------------------*/ #define DP83848_PHY_ADDRESS 0x01 /* Exported functions ------------------------------------------------------- */ void DP83848Init(uint8_t* HWADDR); void DP83848Send(u8* data, u16 length); #endif /* __DP83848_H */
dp83848.h 写了PHY地址,以及DP83848初始化函数和发送函数供外部调用。
3.9、main.c文件
#include "sys.h" #include "delay.h" #include "usart.h" #include "led.h" #include "timer.h" #include "FreeRTOS.h" #include "task.h" #include "stm32f4x7_eth.h" #include "DP83848.h" /************************************************ ALIENTEK 探索者STM32F407开发板 FreeRTOS实验6-1 FreeRTOS任务创建和删除(动态方法)-库函数版本 ************************************************/ u8 mydata[60] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0xa8, 0x02, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // 任务优先级 #define START_TASK_PRIO 1 // 任务堆栈大小 #define START_STK_SIZE 128 // 任务句柄 TaskHandle_t StartTask_Handler; // 任务函数 void start_task(void *pvParameters); // 任务优先级 #define TASK1_TASK_PRIO 3 // 任务堆栈大小 #define TASK1_STK_SIZE 128 // 任务句柄 TaskHandle_t Task1Task_Handler; // 任务函数 void task1_task(void *pvParameters); // 任务优先级 #define TASK2_TASK_PRIO 2 // 任务堆栈大小 #define TASK2_STK_SIZE 256 // 任务句柄 TaskHandle_t Task2Task_Handler; // 任务函数 void task2_task(void *pvParameters); int main(void){ u8 MyMacAddr[6] = {0x08, 0x00, 0x06, 0x00, 0x00, 0x09}; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断分组配置 delay_init(168);// 初始化延时函数 uart_init(115200); // 初始化串口 LED_Init(); // 初始化LED端口 DP83848Init(MyMacAddr); // 创建开始任务 xTaskCreate((TaskFunction_t)start_task, // 任务函数 (const char *)"start_task", // 任务名称 (uint16_t)START_STK_SIZE, // 任务堆栈大小 (void *)NULL, // 传递给任务函数的参数 (UBaseType_t)START_TASK_PRIO, // 任务优先级 (TaskHandle_t *)&StartTask_Handler); // 任务句柄 vTaskStartScheduler(); // 开启任务调度 } // 开始任务任务函数 void start_task(void *pvParameters){ taskENTER_CRITICAL(); // 进入临界区 // 创建TASK1任务 xTaskCreate((TaskFunctiontask1_task, (const char *)"task1_task", (uint16_t)TASK1_STK_SIZE, (void *)NULL, (UBaseType_t)TASK1_TASK_PRIO, (TaskHandle_t *)&Task1Task_Handler); // 创建TASK2任务 xTaskCreate((TaskFunction_t)task2_task, (const char *)"task2_task", (uint16_t)TASK2_STK_SIZE, (void *)NULL, (UBaseType_t)TASK2_TASK_PRIO, (TaskHandle_t *)&Task2Task_Handler); vTaskDelete(StartTask_Handler); // 删除开始任务 taskEXIT_CRITICAL(); // 退出临界区 } // task1任务函数 void task1_task(void *pvParameters){ while (1) { LED1 = !LED1; LED0 = !LED0; vTaskDelay(1000); } } void task2_task(void *pvParameters){ while (1) { DP83848Send(mydata, 60); vTaskDelay(1000); } }
主要说一下创建的任务二(task2_task),周期性的发送mydata ARP数据包再上位机能够被监听到,接收处理函数则在中断回调函数中。
四、测试
在烧录完程序之后将开发板的串口和以太网连接置电脑,打卡串口助手和Wireshark。
Wireshark能够监听到开发板每隔一秒发送的ARP数据包,与我们定义的mydata中的数据是一致的。
电脑打开CMD,ping一个网络地址192.168.2.10,开发板接收到数据包的42位为10,串口将接收到的数据包打印出来。
抓包工具也能看到ping 192.168.2.10发送的ARP数据包。
开发板原理图:
链接:https://pan.baidu.com/s/1RLsQHhn_vnjngnw9yaL2aA?pwd=p2dw
提取码:p2dw