日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

STM32F7xx —— LAN8720(FreeRTOS+LWIP)

發布時間:2023/12/20 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 STM32F7xx —— LAN8720(FreeRTOS+LWIP) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? STM32F7xx —— LAN8720

?

?

? ? STM32F767自帶以太網模塊,需要外接PHY芯片,完成以太網通信(MII/RMII接口)。LAN8720詳細資料看手冊。LWIP:1.4.1 FreeRTOS V8.2.3。

#define ETH_CHANNEL ETH #define ETH_PREEMPT_PRIO ETHERNET_PRIORITY #define ETH_CLK_ENABLE() __HAL_RCC_ETH_CLK_ENABLE() #define ETH_IRQ ETH_IRQn #define ETH_IRQ_FUNC ETH_IRQHandler#define ETH_RESET_PORT GPIOH #define ETH_RESET_PIN GPIO_PIN_11 #define ETH_RESET_CONFIG() GPIOConfig(ETH_RESET_PORT, ETH_RESET_PIN, GPIO_MODE_OUTPUT_PP, GPIO_NOPULL); #define ETH_RESET_HIGH() HAL_GPIO_WritePin(ETH_RESET_PORT, ETH_RESET_PIN, GPIO_PIN_SET) #define ETH_RESET_LOW() HAL_GPIO_WritePin(ETH_RESET_PORT, ETH_RESET_PIN, GPIO_PIN_RESET)#define ETH_MDIO_PORT GPIOA #define ETH_MDIO_PIN GPIO_PIN_2 #define ETH_MDIO_AF GPIO_AF11_ETH #define ETH_MDIO_CONFIG() GPIOConfigExt(ETH_MDC_PORT, ETH_MDC_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_MDC_AF);#define ETH_MDC_PORT GPIOC #define ETH_MDC_PIN GPIO_PIN_1 #define ETH_MDC_AF GPIO_AF11_ETH #define ETH_MDC_CONFIG() GPIOConfigExt(ETH_MDIO_PORT, ETH_MDIO_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_MDIO_AF);#define ETH_RMII_REF_CLK_PORT GPIOA #define ETH_RMII_REF_CLK_PIN GPIO_PIN_1 #define ETH_RMII_REF_CLK_AF GPIO_AF11_ETH #define ETH_RMII_REF_CLK_CONFIG() GPIOConfigExt(ETH_RMII_REF_CLK_PORT, ETH_RMII_REF_CLK_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_RMII_REF_CLK_AF);#define ETH_RMII_CRS_DV_PORT GPIOA #define ETH_RMII_CRS_DV_PIN GPIO_PIN_7 #define ETH_RMII_CRS_DV_AF GPIO_AF11_ETH #define ETH_RMII_CRS_DV_CONFIG() GPIOConfigExt(ETH_RMII_CRS_DV_PORT, ETH_RMII_CRS_DV_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_RMII_CRS_DV_AF);#define ETH_RMII_RXD0_PORT GPIOC #define ETH_RMII_RXD0_PIN GPIO_PIN_4 #define ETH_RMII_RXD0_AF GPIO_AF11_ETH #define ETH_RMII_RXD0_CONFIG() GPIOConfigExt(ETH_RMII_RXD0_PORT, ETH_RMII_RXD0_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_RMII_RXD0_AF);#define ETH_RMII_RXD1_PORT GPIOC #define ETH_RMII_RXD1_PIN GPIO_PIN_5 #define ETH_RMII_RXD1_AF GPIO_AF11_ETH #define ETH_RMII_RXD1_CONFIG() GPIOConfigExt(ETH_RMII_RXD1_PORT, ETH_RMII_RXD1_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_RMII_RXD1_AF);#define ETH_RMII_TXEN_PORT GPIOB #define ETH_RMII_TXEN_PIN GPIO_PIN_11 #define ETH_RMII_TXEN_AF GPIO_AF11_ETH #define ETH_RMII_TXEN_CONFIG() GPIOConfigExt(ETH_RMII_TXEN_PORT, ETH_RMII_TXEN_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_RMII_TXEN_AF);#define ETH_RMII_TXD0_PORT GPIOG #define ETH_RMII_TXD0_PIN GPIO_PIN_13 #define ETH_RMII_TXD0_AF GPIO_AF11_ETH #define ETH_RMII_TXD0_CONFIG() GPIOConfigExt(ETH_RMII_TXD0_PORT, ETH_RMII_TXD0_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_RMII_TXD0_AF);#define ETH_RMII_TXD1_PORT GPIOG #define ETH_RMII_TXD1_PIN GPIO_PIN_14 #define ETH_RMII_TXD1_AF GPIO_AF11_ETH #define ETH_RMII_TXD1_CONFIG() GPIOConfigExt(ETH_RMII_TXD1_PORT, ETH_RMII_TXD1_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, ETH_RMII_TXD1_AF); lan8720_dev_t lan8720_dev;static void lan8720_var_init(void) {lan8720_dev.dma_rx = MemAlloc(SRAM_TYPE_DTCM, ETH_RXBUFNB * sizeof(ETH_DMADescTypeDef)); //申請內存lan8720_dev.dma_tx = MemAlloc(SRAM_TYPE_DTCM, ETH_TXBUFNB * sizeof(ETH_DMADescTypeDef)); //申請內存lan8720_dev.rx_buffer = MemAlloc(SRAM_TYPE_DTCM, ETH_RX_BUF_SIZE * ETH_RXBUFNB); //申請內存lan8720_dev.tx_buffer = MemAlloc(SRAM_TYPE_DTCM, ETH_TX_BUF_SIZE * ETH_TXBUFNB); //申請內存 }static void lan8720_gpio_init(void) {ETH_CLK_ENABLE();ETH_RESET_CONFIG();ETH_MDIO_CONFIG();ETH_MDC_CONFIG();ETH_RMII_REF_CLK_CONFIG();ETH_RMII_CRS_DV_CONFIG();ETH_RMII_RXD0_CONFIG();ETH_RMII_RXD1_CONFIG();ETH_RMII_TXEN_CONFIG();ETH_RMII_TXD0_CONFIG();ETH_RMII_TXD1_CONFIG(); }static void lan8720_mode_init(void) {uint8_t mac[6] = {0};ETH_RESET_CONFIG();ETH_RESET_HIGH();delay_ms(100);ETH_RESET_LOW();delay_ms(100);mac[0] = lwip_dev.mac[0];mac[1] = lwip_dev.mac[1];mac[2] = lwip_dev.mac[2];mac[3] = lwip_dev.mac[3];mac[4] = lwip_dev.mac[4];mac[5] = lwip_dev.mac[5];lan8720_dev.handle.Instance = ETH;lan8720_dev.handle.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE; // 使能自協商模式lan8720_dev.handle.Init.Speed = ETH_SPEED_100M; // 速度100M,如果開啟了自協商模式,此配置就無效lan8720_dev.handle.Init.DuplexMode = ETH_MODE_FULLDUPLEX; // 全雙工模式,如果開啟了自協商模式,此配置就無效lan8720_dev.handle.Init.PhyAddress = LAN8720_PHY_ADDRESS; // LAN8720地址lan8720_dev.handle.Init.MACAddr = mac; // MAC地址lan8720_dev.handle.Init.RxMode = ETH_RXINTERRUPT_MODE; // 中斷接收模式lan8720_dev.handle.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE; // 硬件幀校驗lan8720_dev.handle.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII; // RMII接口HAL_ETH_Init(&lan8720_dev.handle); }static void lan8720_nvic_init(void) {HAL_NVIC_SetPriority(ETH_IRQ, 3, 0); // 優先級必須小于configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITYHAL_NVIC_EnableIRQ(ETH_IRQ); }uint32_t LAN8720ReadPHY(uint16_t reg) {uint32_t regval;HAL_ETH_ReadPHYRegister(&lan8720_dev.handle, reg, &regval);return regval; }void LAN8720WritePHY(uint16_t reg, uint16_t value) {uint32_t temp = value;HAL_ETH_ReadPHYRegister(&lan8720_dev.handle, reg, &temp); }static uint32_t lan8720_rx_size(ETH_DMADescTypeDef *dma_rx) {uint32_t length = 0;if(((dma_rx->Status & ETH_DMARXDESC_OWN) == (uint32_t)RESET) &&((dma_rx->Status & ETH_DMARXDESC_ES) == (uint32_t)RESET) &&((dma_rx->Status & ETH_DMARXDESC_LS) != (uint32_t)RESET)){length = ((dma_rx->Status & ETH_DMARXDESC_FL) >> ETH_DMARXDESC_FRAME_LENGTHSHIFT);}return length; }void LAN8720Init(void) {lan8720_var_init();lan8720_gpio_init();lan8720_mode_init();lan8720_nvic_init(); }void ETH_IRQ_FUNC(void) {while(lan8720_rx_size(lan8720_dev.handle.RxDesc)){LWIPCommProcess(); // 處理以太網數據,即將數據提交給LWIP}//清除中斷標志位__HAL_ETH_DMA_CLEAR_IT(&lan8720_dev.handle, ETH_DMA_IT_R);__HAL_ETH_DMA_CLEAR_IT(&lan8720_dev.handle, ETH_DMA_IT_NIS); }

sys_arch文件:

#ifndef _ARCH_SYS_ARCH_H_ #define _ARCH_SYS_ARCH_H_#include "arch/cc.h" #include "FreeRTOS.h" #include "queue.h" #include "semphr.h"#define MAX_QUEUE_ENTRIES 20 // 每個消息郵箱的大小// LWIP消息郵箱結構體 typedef struct {QueueHandle_t xQueue; } lwip_mbox;typedef SemaphoreHandle_t sys_sem_t; // LWIP使用的信號量 typedef lwip_mbox sys_mbox_t; // LWIP使用的消息郵箱,其實就是UCOS中的消息隊列 typedef unsigned char sys_thread_t; // 線程ID,也就是任務優先級#endif /* _ARCH_SYS_ARCH_H_ */ // 參照lwip_sys.hstatic const uint32_t console_null;// 創建一個消息郵箱 err_t sys_mbox_new(sys_mbox_t *mbox, int size) {if(size > MAX_QUEUE_ENTRIES){size = MAX_QUEUE_ENTRIES;}mbox->xQueue = xQueueCreate(size, sizeof(void *));if(mbox->xQueue != NULL){return ERR_OK;}else{return ERR_MEM;} }// 釋放并刪除一個消息郵箱 void sys_mbox_free(sys_mbox_t *mbox) {vQueueDelete(mbox->xQueue);mbox->xQueue = NULL; }// 向消息郵箱中發送一條消息(必須發送成功) void sys_mbox_post(sys_mbox_t *mbox, void *msg) {BaseType_t xHigherPriorityTaskWoken = pdFALSE;if(msg == NULL){msg = (void*)&console_null; //當msg為空時 msg等于pvNullPointer指向的值}if((SCB_ICSR_REG & 0xFF) == 0) //線程執行{while(xQueueSendToBack(mbox->xQueue, &msg, portMAX_DELAY) != pdPASS);//portMAX_DELAY,死等直到發送成功}else{while(xQueueSendToBackFromISR(mbox->xQueue, &msg, &xHigherPriorityTaskWoken) != pdPASS);portYIELD_FROM_ISR(xHigherPriorityTaskWoken);} }// 嘗試向一個消息郵箱發送消息 // 此函數相對于sys_mbox_post函數只發送一次消息,發送失敗后不會嘗試第二次發送 err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg) {BaseType_t xHigherPriorityTaskWoken = pdFALSE;if(msg == NULL){msg = (void*)&console_null; //當消息為空,則用常量NullMessage的地址替換}if((SCB_ICSR_REG & 0xFF) == 0){if(xQueueSendToBack(mbox->xQueue, &msg, 0) != pdPASS){return ERR_MEM;}}else{if(xQueueSendToBackFromISR(mbox->xQueue, &msg, &xHigherPriorityTaskWoken) != pdPASS){return ERR_MEM;}portYIELD_FROM_ISR(xHigherPriorityTaskWoken);}return ERR_OK; }// 等待郵箱中的消息 u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) { #if 0u32_t rtos_timeout;BaseType_t temp;if(timeout == 0){rtos_timeout = 2;}else{rtos_timeout = timeout;temp = xQueueReceive(mbox->xQueue, msg, rtos_timeout);}return rtos_timeout;#elseu32_t rtos_timeout, timeout_new;BaseType_t temp;temp = xQueueReceive(mbox->xQueue, msg, 0);if((temp == pdPASS) && (*msg != NULL)){if(*msg == (void*)&console_null){*msg = NULL;}return 0;}if(timeout != 0){rtos_timeout = (timeout * configTICK_RATE_HZ) / 1000; // 轉換為節拍數,因為freertos延時使用的是節拍數,而LWIP是用msif(rtos_timeout < 1){rtos_timeout = 1; // 至少1個節拍}else if(rtos_timeout >= portMAX_DELAY){rtos_timeout = portMAX_DELAY - 1;}}else{rtos_timeout = 0;}timeout = HAL_GetTick(); //獲取系統時間if(rtos_timeout != 0){temp = xQueueReceive(mbox->xQueue, msg, rtos_timeout); // 請求消息隊列,等待時限為rtos_timeout}else{temp = xQueueReceive(mbox->xQueue, msg, portMAX_DELAY); // 為0則無限等}if(temp == errQUEUE_EMPTY){timeout = SYS_ARCH_TIMEOUT; //請求超時*msg = NULL;}else{if(*msg != NULL){if(*msg == (void*)&console_null){*msg = NULL;}}timeout_new = HAL_GetTick();if (timeout_new > timeout){timeout_new = timeout_new - timeout; //算出請求消息或使用的時間}else{timeout_new = 0xffffffff - timeout + timeout_new;}timeout = timeout_new * 1000 / configTICK_RATE_HZ + 1;}return timeout; #endif }// 嘗試獲取消息 u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg) {BaseType_t temp;temp = xQueueReceive(mbox->xQueue, msg, 0);if((temp == pdPASS) && (*msg != NULL)){if(*msg == (void*)&console_null){*msg = NULL;}return 0;}else{return SYS_MBOX_EMPTY;} }// 檢查一個消息郵箱是否有效 // 返回值:1,有效. 0,無效 int sys_mbox_valid(sys_mbox_t *mbox) {if(mbox->xQueue != NULL){return 1;}return 0; }// 設置一個消息郵箱為無效 void sys_mbox_set_invalid(sys_mbox_t *mbox) {mbox->xQueue = NULL; }// 創建一個信號量 err_t sys_sem_new(sys_sem_t* sem, uint8_t count) {*sem = xSemaphoreCreateCounting(0xFF, count);if(*sem == NULL){return ERR_MEM;}return ERR_OK; }// 等待一個信號量 u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) {u32_t rtos_timeout, timeout_new;BaseType_t temp;if(xSemaphoreTake(*sem, 0) == pdPASS){return 0;}if( timeout != 0){rtos_timeout = (timeout * configTICK_RATE_HZ) / 1000; // 轉換為節拍數,因為UCOS延時使用的是節拍數,而LWIP是用msif(rtos_timeout < 1){rtos_timeout = 1;}}else{rtos_timeout = 0;}timeout = HAL_GetTick();if(rtos_timeout != 0){temp = xSemaphoreTake(*sem, rtos_timeout);}else{temp = xSemaphoreTake(*sem, portMAX_DELAY);}if(temp != pdPASS){timeout = SYS_ARCH_TIMEOUT; // 請求超時}else{timeout_new = HAL_GetTick();if (timeout_new >= timeout){timeout_new = timeout_new - timeout;}else{timeout_new = 0xffffffff - timeout + timeout_new;}timeout = (timeout_new * 1000 / configTICK_RATE_HZ + 1); // 算出請求消息或使用的時間(ms)}return timeout; }// 發送一個信號量 void sys_sem_signal(sys_sem_t *sem) {while(xSemaphoreGive(*sem) != pdTRUE); }// 釋放并刪除一個信號量 void sys_sem_free(sys_sem_t *sem) {vSemaphoreDelete(*sem);*sem = NULL; }// 查詢一個信號量的狀態,無效或有效 int sys_sem_valid(sys_sem_t *sem) {if(*sem != NULL){return 1;}else{return 0;} }// 設置一個信號量無效 void sys_sem_set_invalid(sys_sem_t *sem) {*sem = NULL; }// arch初始化 void sys_init(void) {// 不做任何事情 }TaskHandle_t os_lwip_handle;sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio) {xTaskCreate((TaskFunction_t)thread,(const char* )name,(uint16_t )stacksize,(void* )NULL,(UBaseType_t )prio,(TaskHandle_t*)&os_lwip_handle);//創建TCP IP內核任務return 0; }// lwip延時函數 void sys_msleep(u32_t ms) {delay_ms(ms); }// 獲取系統時間,LWIP1.4.1增加的函數 // 返回值:當前系統時間(單位:毫秒) u32_t sys_now(void) {return (HAL_GetTick() * 1000 / configTICK_RATE_HZ + 1); // 將節拍數轉換為LWIP的時間MS }// 用在cc.h的SYS_ARCH_PROTECT(lev) uint32_t SysCriticalEnter(void) {if(SCB_ICSR_REG & 0xFF) //在中斷里{return taskENTER_CRITICAL_FROM_ISR();}else // 在任務{taskENTER_CRITICAL();}return 0; }// 用在cc.YS_ARCH_UNPROTECT(lev) void SysCriticalExit(uint32_t lev) {if(SCB_ICSR_REG & 0xFF) // 在中斷里{taskEXIT_CRITICAL_FROM_ISR(lev);}else // 在任務{taskEXIT_CRITICAL();} }

cpu.h:#define BYTE_ORDER LITTLE_ENDIAN ?// 小端模式

cc.h:

#include "cpu.h" #include "stdio.h" #include "FreeRTOS.h" #include "task.h"// LWIP需要的類型重命名 typedef unsigned char u8_t; /* Unsigned 8 bit quantity */ typedef signed char s8_t; /* Signed 8 bit quantity */ typedef unsigned short u16_t; /* Unsigned 16 bit quantity */ typedef signed short s16_t; /* Signed 16 bit quantity */ typedef unsigned long u32_t; /* Unsigned 32 bit quantity */ typedef signed long s32_t; /* Signed 32 bit quantity */ typedef u32_t mem_ptr_t; /* Unsigned 32 bit quantity */ typedef int sys_prot_t;// LWIP需要的臨界段 #define OS_CRITICAL_METHOD#ifdef OS_CRITICAL_METHOD#define SCB_ICSR_REG (*((volatile uint32_t * ) 0xe000ed04))extern uint32_t SysCriticalEnter(void); extern void SysCriticalExit(uint32_t lev);#define SYS_ARCH_DECL_PROTECT(lev) u32_t lev#define SYS_ARCH_PROTECT(lev) lev = SysCriticalEnter()#define SYS_ARCH_UNPROTECT(lev) SysCriticalExit(lev)#endif// 對齊方案 #if defined (__ICCARM__)#define PACK_STRUCT_BEGIN #define PACK_STRUCT_STRUCT #define PACK_STRUCT_END #define PACK_STRUCT_FIELD(x) x #define PACK_STRUCT_USE_INCLUDES#elif defined (__CC_ARM)#define PACK_STRUCT_BEGIN __packed #define PACK_STRUCT_STRUCT #define PACK_STRUCT_END #define PACK_STRUCT_FIELD(x) x#elif defined (__GNUC__)#define PACK_STRUCT_BEGIN #define PACK_STRUCT_STRUCT __attribute__ ((__packed__)) #define PACK_STRUCT_END #define PACK_STRUCT_FIELD(x) x#elif defined (__TASKING__)#define PACK_STRUCT_BEGIN #define PACK_STRUCT_STRUCT #define PACK_STRUCT_END #define PACK_STRUCT_FIELD(x) x#endif#define U16_F "4d" #define S16_F "4d" #define X16_F "4x" #define U32_F "8ld" #define S32_F "8ld" #define X32_F "8lx"/*--------------macros--------------------------------------------------------*/ #ifndef LWIP_PLATFORM_ASSERT #define LWIP_PLATFORM_ASSERT(x) \do \{ printf("Assertion \"%s\" failed at line %d in %s\r\n", x, __LINE__, __FILE__); \} while(0) #endif#ifndef LWIP_PLATFORM_DIAG #define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0) #endif

lwipopts.h

#ifndef _LWIPOPTS_H_ #define _LWIPOPTS_H_// 與FreeRTOS配置類似(FreeRTOSConfig.h)// 線程優先級 #ifndef TCPIP_THREAD_PRIO #define TCPIP_THREAD_PRIO (configMAX_PRIORITIES - 1) // 定義內核任務的優先級為最高 #endif #undef DEFAULT_THREAD_PRIO #define DEFAULT_THREAD_PRIO 8#define SYS_LIGHTWEIGHT_PROT 1 // 為1時使用實時操作系統的輕量級保護,保護關鍵代碼不被中斷打斷 #define NO_SYS 0 // 使用UCOS操作系統 #define MEM_ALIGNMENT 4 // 使用4字節對齊模式 #define MEM_SIZE 16000 // 內存堆heap大小 #define MEMP_NUM_PBUF 20 // MEMP_NUM_PBUF:memp結構的pbuf數量,如果應用從ROM或者靜態存儲區發送大量數據時,這個值應該設置大一點 #define MEMP_NUM_UDP_PCB 6 // MEMP_NUM_UDP_PCB:UDP協議控制塊(PCB)數量.每個活動的UDP"連接"需要一個PCB. #define MEMP_NUM_TCP_PCB 10 // MEMP_NUM_TCP_PCB:同時建立激活的TCP數量 #define MEMP_NUM_TCP_PCB_LISTEN 6 // MEMP_NUM_TCP_PCB_LISTEN:能夠監聽的TCP連接數量 #define MEMP_NUM_TCP_SEG 15 // MEMP_NUM_TCP_SEG:最多同時在隊列中的TCP段數量 #define MEMP_NUM_SYS_TIMEOUT 8 // MEMP_NUM_SYS_TIMEOUT:能夠同時激活的timeout個數// pbuf內存池 #define PBUF_POOL_SIZE 20 // PBUF_POOL_SIZE:pbuf內存池個數 #define PBUF_POOL_BUFSIZE 512 // PBUF_POOL_BUFSIZE:每個pbuf內存池大小#define LWIP_TCP 1 // 使用TCP #define TCP_TTL 255 // 生存時間#undef TCP_QUEUE_OOSEQ #define TCP_QUEUE_OOSEQ 0 // 當TCP的數據段超出隊列時的控制位,當設備的內存過小的時候此項應為0#undef TCPIP_MBOX_SIZE #define TCPIP_MBOX_SIZE MAX_QUEUE_ENTRIES // tcpip創建主線程時的消息郵箱大小#undef DEFAULT_TCP_RECVMBOX_SIZE #define DEFAULT_TCP_RECVMBOX_SIZE MAX_QUEUE_ENTRIES#undef DEFAULT_ACCEPTMBOX_SIZE #define DEFAULT_ACCEPTMBOX_SIZE MAX_QUEUE_ENTRIES#define TCP_MSS (1500 - 40) // 最大TCP分段,TCP_MSS = (MTU - IP報頭大小 - TCP報頭大小 #define TCP_SND_BUF (4*TCP_MSS) // TCP發送緩沖區大小(bytes). #define TCP_SND_QUEUELEN (2* TCP_SND_BUF/TCP_MSS) //TCP_SND_QUEUELEN: TCP發送緩沖區大小(pbuf).這個值最小為(2 * TCP_SND_BUF/TCP_MSS) #define TCP_WND (2*TCP_MSS) // TCP發送窗口 #define LWIP_ICMP 1 // 使用ICMP協議 #define LWIP_DHCP 0 // 使用DHCP #define LWIP_UDP 1 // 使用UDP服務 #define UDP_TTL 255 // UDP數據包生存時間 #define LWIP_STATS 0 #define LWIP_PROVIDE_ERRNO 1// 幀校驗和選項,STM32F7xx允許通過硬件識別和計算IP,UDP和ICMP的幀校驗和 #define CHECKSUM_BY_HARDWARE // 定義CHECKSUM_BY_HARDWARE,使用硬件幀校驗 #ifdef CHECKSUM_BY_HARDWARE //CHECKSUM_GEN_IP==0: 硬件生成IP數據包的幀校驗和 #define CHECKSUM_GEN_IP 0 //CHECKSUM_GEN_UDP==0: 硬件生成UDP數據包的幀校驗和 #define CHECKSUM_GEN_UDP 0 //CHECKSUM_GEN_TCP==0: 硬件生成TCP數據包的幀校驗和 #define CHECKSUM_GEN_TCP 0 //CHECKSUM_CHECK_IP==0: 硬件檢查輸入的IP數據包幀校驗和 #define CHECKSUM_CHECK_IP 0 //CHECKSUM_CHECK_UDP==0: 硬件檢查輸入的UDP數據包幀校驗和 #define CHECKSUM_CHECK_UDP 0 //CHECKSUM_CHECK_TCP==0: 硬件檢查輸入的TCP數據包幀校驗和 #define CHECKSUM_CHECK_TCP 0 //CHECKSUM_CHECK_ICMP==1:硬件檢查輸入的ICMP數據包幀校驗和 #define CHECKSUM_GEN_ICMP 0 #else //CHECKSUM_GEN_IP==1: 軟件生成IP數據包幀校驗和 #define CHECKSUM_GEN_IP 1 // CHECKSUM_GEN_UDP==1: 軟件生成UDOP數據包幀校驗和 #define CHECKSUM_GEN_UDP 1 //CHECKSUM_GEN_TCP==1: 軟件生成TCP數據包幀校驗和 #define CHECKSUM_GEN_TCP 1 // CHECKSUM_CHECK_IP==1: 軟件檢查輸入的IP數據包幀校驗和 #define CHECKSUM_CHECK_IP 1 // CHECKSUM_CHECK_UDP==1: 軟件檢查輸入的UDP數據包幀校驗和 #define CHECKSUM_CHECK_UDP 1 //CHECKSUM_CHECK_TCP==1: 軟件檢查輸入的TCP數據包幀校驗和 #define CHECKSUM_CHECK_TCP 1 //CHECKSUM_CHECK_ICMP==1:軟件檢查輸入的ICMP數據包幀校驗和 #define CHECKSUM_GEN_ICMP 1 #endif#define LWIP_NETCONN 1 // LWIP_NETCONN==1:使能NETCON函數(要求使用api_lib.c) #define LWIP_SOCKET 1 // LWIP_SOCKET==1:使能Socket API(要求使用sockets.c) #define LWIP_COMPAT_MUTEX 1 #define LWIP_SO_RCVTIMEO 1 // 通過定義LWIP_SO_RCVTIMEO使能netconn結構體中recv_timeout,使用recv_timeout可以避免阻塞線程// 有關系統的選項 #define TCPIP_THREAD_STACKSIZE 1024 // 內核任務堆棧大小 #define DEFAULT_UDP_RECVMBOX_SIZE 2000 #define DEFAULT_THREAD_STACKSIZE 512//LWIP調試選項 #define LWIP_DEBUG 0 // 關閉DEBUG選項 #define ICMP_DEBUG LWIP_DBG_OFF // 開啟/關閉ICMPdebug#endif /* __LWIPOPTS_H__ */

主函數需調用的初始化接口:

lwip_dev_t lwip_dev; // lwip控制結構體 static struct netif lwip_netif; // 網絡接口extern uint32_t memp_get_memorysize(void); // 在memp.c里面定義 extern uint8_t *memp_memory; // 在memp.c里面定義. extern uint8_t *ram_heap; // 在mem.c里面定義.// 釋放內存 static void lwip_comm_mem_free(void) {MemFree(SRAM_TYPE_IN, memp_memory);MemFree(SRAM_TYPE_IN, ram_heap); }// 分配內存 static uint8_t lwip_comm_mem_malloc(void) {uint32_t mempsize;uint32_t ramheapsize;mempsize = memp_get_memorysize(); // 得到memp_memory數組大小memp_memory = MemAlloc(SRAM_TYPE_IN, mempsize); // 為memp_memory申請內存ramheapsize = LWIP_MEM_ALIGN_SIZE(MEM_SIZE) + 2 * LWIP_MEM_ALIGN_SIZE(4 * 3) + MEM_ALIGNMENT; // 得到ram heap大小ram_heap = MemAlloc(SRAM_TYPE_IN, ramheapsize); // 為ram_heap申請內存if(!memp_memory || !ram_heap){lwip_comm_mem_free();return 1;}return 0; }// 網絡默認配置 static void lwip_comm_default_ip_set(lwip_dev_t *dev) {uint32_t sn0;SocIDGet(&sn0, CONFIG_SYSTEM_HARDWARE_TYPE);// MAC地址設置(高三字節固定為:2.0.0,低三字節用STM32唯一ID)dev->mac[0] = 2;dev->mac[1] = 0;dev->mac[2] = 0;dev->mac[3] = (sn0 >> 16) & 0xFF;dev->mac[4] = (sn0 >> 8) & 0xFF;dev->mac[5] = sn0 & 0xFF;// 默認遠程IPdev->remoteip[0] = CONFIG_ETHERNET_REMOTE_IP0;dev->remoteip[1] = CONFIG_ETHERNET_REMOTE_IP1;dev->remoteip[2] = CONFIG_ETHERNET_REMOTE_IP2;dev->remoteip[3] = CONFIG_ETHERNET_REMOTE_IP3;// 本地IPdev->ip[0] = CONFIG_ETHERNET_LOCAL_IP0;dev->ip[1] = CONFIG_ETHERNET_LOCAL_IP1;dev->ip[2] = CONFIG_ETHERNET_LOCAL_IP2;dev->ip[3] = CONFIG_ETHERNET_LOCAL_IP3;// 子網掩碼dev->netmask[0] = CONFIG_ETHERNET_NETMASK0;dev->netmask[1] = CONFIG_ETHERNET_NETMASK1;dev->netmask[2] = CONFIG_ETHERNET_NETMASK2;dev->netmask[3] = CONFIG_ETHERNET_NETMASK3;// 網關dev->gateway[0] = CONFIG_ETHERNET_GATEWAY0;dev->gateway[1] = CONFIG_ETHERNET_GATEWAY1;dev->gateway[2] = CONFIG_ETHERNET_GATEWAY2;dev->gateway[3] = CONFIG_ETHERNET_GATEWAY3;dev->dhcpstatus = 0; // 沒有DHCP }// LWIP初始化(LWIP啟動的時候使用) uint8_t LWIPCommInit(void) {struct netif *netif_init; // 調用netif_add()函數時的返回值,用于判斷網絡初始化是否成功struct ip_addr ipaddr, netmask, gw; //ip地址 子網掩碼 網關if(lwip_comm_mem_malloc()){return LWIP_COMM_STATUS_RAM_ERROR; //內存申請失敗}lwip_comm_default_ip_set(&lwip_dev); //設置默認IP等信息LAN8720Init(); // 硬件網口初始化tcpip_init(NULL, NULL); // 初始化tcp ip內核,該函數里面會創建tcpip_thread內核任務IP4_ADDR(&ipaddr, lwip_dev.ip[0], lwip_dev.ip[1], lwip_dev.ip[2], lwip_dev.ip[3]);IP4_ADDR(&netmask, lwip_dev.netmask[0], lwip_dev.netmask[1], lwip_dev.netmask[2], lwip_dev.netmask[3]);IP4_ADDR(&gw, lwip_dev.gateway[0], lwip_dev.gateway[1], lwip_dev.gateway[2], lwip_dev.gateway[3]);printf("網卡en的MAC地址%d.%d.%d.%d.%d.%d\r\n", lwip_dev.mac[0], lwip_dev.mac[1], lwip_dev.mac[2], lwip_dev.mac[3], lwip_dev.mac[4], lwip_dev.mac[5]);printf("靜態IP地址.....%d.%d.%d.%d\r\n", lwip_dev.ip[0], lwip_dev.ip[1], lwip_dev.ip[2], lwip_dev.ip[3]);printf("子網掩碼.......%d.%d.%d.%d\r\n", lwip_dev.netmask[0], lwip_dev.netmask[1], lwip_dev.netmask[2], lwip_dev.netmask[3]);printf("默認網關.......%d.%d.%d.%d\r\n", lwip_dev.gateway[0], lwip_dev.gateway[1], lwip_dev.gateway[2], lwip_dev.gateway[3]);netif_init = netif_add(&lwip_netif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &tcpip_input); // 向網卡列表中添加一個網口if(netif_init == NULL){return LWIP_COMM_STATUS_NET_ADD_FAIL; // 網卡添加失敗}else // 網口添加成功后,設置netif為默認值,并且打開netif網口{netif_set_default(&lwip_netif); // 設置netif為默認網口netif_set_up(&lwip_netif); // 打開netif網口}return LWIP_COMM_STATUS_OK; // OK. }// 用于以太網中斷調用 void LWIPCommProcess(void) {ethernetif_input(&lwip_netif); }

ethernetif.c:

// 由ethernetif_init()調用用于初始化硬件 // netif:網卡結構體指針 // 返回值:ERR_OK,正常 // 其他,失敗 static err_t low_level_init(struct netif *netif) {netif->hwaddr_len = ETHARP_HWADDR_LEN; // 設置MAC地址長度,為6個字節// 初始化MAC地址,設置什么地址由用戶自己設置,但是不能與網絡中其他設備MAC地址重復netif->hwaddr[0] = lwip_dev.mac[0];netif->hwaddr[1] = lwip_dev.mac[1];netif->hwaddr[2] = lwip_dev.mac[2];netif->hwaddr[3] = lwip_dev.mac[3];netif->hwaddr[4] = lwip_dev.mac[4];netif->hwaddr[5] = lwip_dev.mac[5];netif->mtu = 1500; //最大允許傳輸單元,允許該網卡廣播和ARP功能netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;HAL_ETH_DMATxDescListInit(&lan8720_dev.handle, lan8720_dev.dma_tx, lan8720_dev.tx_buffer, ETH_TXBUFNB); // 初始化發送描述符HAL_ETH_DMARxDescListInit(&lan8720_dev.handle, lan8720_dev.dma_rx, lan8720_dev.rx_buffer, ETH_RXBUFNB); // 初始化接收描述符HAL_ETH_Start(&lan8720_dev.handle); // 開啟MAC和DMAreturn ERR_OK; }// 用于發送數據包的最底層函數(lwip通過netif->linkoutput指向該函數) // netif:網卡結構體指針 // p:pbuf數據結構體指針 // 返回值:ERR_OK,發送正常 // ERR_MEM,發送失敗 static err_t low_level_output(struct netif *netif, struct pbuf *p) {err_t errval;struct pbuf *q;uint8_t *buffer = (uint8_t *)(lan8720_dev.handle.TxDesc->Buffer1Addr);__IO ETH_DMADescTypeDef *DmaTxDesc;uint32_t framelength = 0;uint32_t bufferoffset = 0;uint32_t byteslefttocopy = 0;uint32_t payloadoffset = 0;DmaTxDesc = lan8720_dev.handle.TxDesc;bufferoffset = 0;// 從pbuf中拷貝要發送的數據for(q = p; q != NULL; q = q->next){// 判斷此發送描述符是否有效,即判斷此發送描述符是否歸以太網DMA所有if((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET){errval = ERR_USE;goto error; // 發送描述符無效,不可用}byteslefttocopy = q->len; // 要發送的數據長度payloadoffset = 0;// 將pbuf中要發送的數據寫入到以太網發送描述符中,有時候我們要發送的數據可能大于一個以太網// 描述符的Tx Buffer,因此我們需要分多次將數據拷貝到多個發送描述符中while((byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE ){// 將數據拷貝到以太網發送描述符的Tx Buffer中memcpy((uint8_t*)((uint8_t*)buffer + bufferoffset), (uint8_t*)((uint8_t*)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset));// DmaTxDsc指向下一個發送描述符DmaTxDesc = (ETH_DMADescTypeDef *)(DmaTxDesc->Buffer2NextDescAddr);// 檢查新的發送描述符是否有效if((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET){errval = ERR_USE;goto error; // 發送描述符無效,不可用}buffer = (uint8_t *)(DmaTxDesc->Buffer1Addr); // 更新buffer地址,指向新的發送描述符的Tx Bufferbyteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset);payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset);framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset);bufferoffset = 0;}// 拷貝剩余的數據memcpy( (uint8_t*)((uint8_t*)buffer + bufferoffset), (uint8_t*)((uint8_t*)q->payload + payloadoffset), byteslefttocopy );bufferoffset = bufferoffset + byteslefttocopy;framelength = framelength + byteslefttocopy;}// 當所有要發送的數據都放進發送描述符的Tx Buffer以后就可發送此幀了HAL_ETH_TransmitFrame(&lan8720_dev.handle, framelength);errval = ERR_OK; error:// 發送緩沖區發生下溢,一旦發送緩沖區發生下溢TxDMA會進入掛起狀態if((lan8720_dev.handle.Instance->DMASR & ETH_DMASR_TUS) != (uint32_t)RESET){// 清除下溢標志lan8720_dev.handle.Instance->DMASR = ETH_DMASR_TUS;// 當發送幀中出現下溢錯誤的時候TxDMA會掛起,這時候需要向DMATPDR寄存器// 隨便寫入一個值來將其喚醒,此處我們寫0lan8720_dev.handle.Instance->DMATPDR = 0;}return errval; }// 用于接收數據包的最底層函數 // neitif:網卡結構體指針 // 返回值:pbuf數據結構體指針 static struct pbuf * low_level_input(struct netif *netif) {struct pbuf *p = NULL;struct pbuf *q;uint16_t len;uint8_t *buffer;__IO ETH_DMADescTypeDef *dmarxdesc;uint32_t bufferoffset = 0;uint32_t payloadoffset = 0;uint32_t byteslefttocopy = 0;uint32_t i = 0;if(HAL_ETH_GetReceivedFrame(&lan8720_dev.handle) != HAL_OK) // 判斷是否接收到數據{return NULL;}len = lan8720_dev.handle.RxFrameInfos.length; // 獲取接收到的以太網幀長度buffer = (uint8_t *)lan8720_dev.handle.RxFrameInfos.buffer; // 獲取接收到的以太網幀的數據bufferif(len > 0){p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); // 申請pbuf}if(p != NULL) // pbuf申請成功{dmarxdesc = lan8720_dev.handle.RxFrameInfos.FSRxDesc; // 獲取接收描述符鏈表中的第一個描述符bufferoffset = 0;for(q = p; q != NULL; q = q->next){byteslefttocopy = q->len;payloadoffset = 0;// 將接收描述符中Rx Buffer的數據拷貝到pbuf中while((byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE ){// 將數據拷貝到pbuf中memcpy((uint8_t*)((uint8_t*)q->payload + payloadoffset), (uint8_t*)((uint8_t*)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset));// dmarxdesc向下一個接收描述符dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);// 更新buffer地址,指向新的接收描述符的Rx Bufferbuffer = (uint8_t *)(dmarxdesc->Buffer1Addr);byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset);payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset);bufferoffset = 0;}// 拷貝剩余的數據memcpy((uint8_t*)((uint8_t*)q->payload + payloadoffset), (uint8_t*)((uint8_t*)buffer + bufferoffset), byteslefttocopy);bufferoffset = bufferoffset + byteslefttocopy;}}// 釋放DMA描述符dmarxdesc = lan8720_dev.handle.RxFrameInfos.FSRxDesc;for(i = 0; i < lan8720_dev.handle.RxFrameInfos.SegCount; i++){dmarxdesc->Status |= ETH_DMARXDESC_OWN; //標記描述符歸DMA所有dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);}lan8720_dev.handle.RxFrameInfos.SegCount = 0; //清除段計數器if((lan8720_dev.handle.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET) //接收緩沖區不可用{// 清除接收緩沖區不可用標志lan8720_dev.handle.Instance->DMASR = ETH_DMASR_RBUS;// 當接收緩沖區不可用的時候RxDMA會進去掛起狀態,通過向DMARPDR寫入任意一個值來喚醒Rx DMAlan8720_dev.handle.Instance->DMARPDR = 0;}return p; }// 網卡接收數據(lwip直接調用) // netif:網卡結構體指針 // 返回值:ERR_OK,發送正常 // ERR_MEM,發送失敗 err_t ethernetif_input(struct netif *netif) {err_t err;struct pbuf *p;p = low_level_input(netif); //調用low_level_input函數接收數據if(p == NULL){return ERR_MEM;}err = netif->input(p, netif); //調用netif結構體中的input字段(一個函數)來處理數據包if(err != ERR_OK){LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));pbuf_free(p);p = NULL;}return err; }// 使用low_level_init()函數來初始化網絡 // netif:網卡結構體指針 // 返回值:ERR_OK,正常 // 其他,失敗 err_t ethernetif_init(struct netif *netif) {LWIP_ASSERT("netif!=NULL", (netif != NULL)); #if LWIP_NETIF_HOSTNAME // LWIP_NETIF_HOSTNAME netif->hostname = "lwip"; // 初始化名稱 #endifnetif->name[0] = IFNAME0; // 初始化變量netif的name字段netif->name[1] = IFNAME1; // 在文件外定義這里不用關心具體值netif->output = etharp_output; // IP層發送數據包函數netif->linkoutput = low_level_output; // ARP模塊發送數據包函數low_level_init(netif); // 底層硬件初始化函數return ERR_OK; }

TCP客戶端實現:

#ifndef _LWIP_TCP_CLIENT_H_ #define _LWIP_TCP_CLIENT_H_void TCPClientProcess(void);void TCPClientSend(uint8_t *data, uint16_t length);#endif /* _LWIP_TCP_CLIENT_H_ */ TaskHandle_t os_tcp_handle;#define TCP_CLIENT_RX_BUFSIZE 1500 // 接收緩沖區長度typedef struct {struct netconn *sockfd; // TCP CLIENT網絡連接結構體err_t connect_err;err_t recv_err;err_t send_err;// 服務器IP和端口ip_addr_t server_ipaddr;uint16_t server_port;// 客戶端IP和端口ip_addr_t loca_ipaddr;uint16_t loca_port;uint8_t state; // 網絡連接的狀態uint8_t recv_buf[TCP_CLIENT_RX_BUFSIZE];uint16_t recv_len; } tcp_client_t;static tcp_client_t tcp_client;static void tcp_client_close(void) {netconn_close(tcp_client.sockfd);netconn_delete(tcp_client.sockfd);printf("服務器 %d.%d.%d.%d斷開連接 \r\n", lwip_dev.remoteip[0], lwip_dev.remoteip[1], lwip_dev.remoteip[2], lwip_dev.remoteip[3]); }static void tcp_client_recv(void) {struct netbuf *recvbuf;struct pbuf *q;while(1){tcp_client.recv_err = netconn_recv(tcp_client.sockfd, &recvbuf);if(tcp_client.recv_err == ERR_OK) //接收到數據{taskENTER_CRITICAL();tcp_client.recv_len = 0;for(q = recvbuf->p; q != NULL; q = q->next) // 遍歷完整個pbuf鏈表{printf(" %d ... \r\n", q->len);memcpy(tcp_client.recv_buf + tcp_client.recv_len, q->payload, q->len);tcp_client.recv_len += q->len;}taskEXIT_CRITICAL();LWIPParse(tcp_client.recv_buf, tcp_client.recv_len);netbuf_delete(recvbuf);}else if(tcp_client.recv_err == ERR_CLSD) //關閉連接{tcp_client_close();break;}// 網線斷掉 刪除連接tcp_client.state = LAN8720ReadPHY(PHY_BSR);if((tcp_client.state & 0x04) == 0){printf("close\r\n");tcp_client_close();break;}} }static void task_tcp_client(void *arg) {tcp_client.server_port = CONFIG_ETHERNET_REMOTE_PORT;IP4_ADDR(&tcp_client.server_ipaddr, lwip_dev.remoteip[0], lwip_dev.remoteip[1], lwip_dev.remoteip[2], lwip_dev.remoteip[3]);while (1){tcp_client.sockfd = netconn_new(NETCONN_TCP); // 創建一個TCP鏈接tcp_client.connect_err = netconn_connect(tcp_client.sockfd, &tcp_client.server_ipaddr, tcp_client.server_port); //連接服務器if(tcp_client.connect_err != ERR_OK){netconn_delete(tcp_client.sockfd); // 刪除tcp_clientconn連接}else{tcp_client.sockfd->recv_timeout = 5;netconn_getaddr(tcp_client.sockfd, &tcp_client.loca_ipaddr, &tcp_client.loca_port, 1); // 獲取本地IP主機IP地址和端口號printf("連接上服務器: %d.%d.%d.%d, 本機端口:%d\r\n", lwip_dev.remoteip[0], lwip_dev.remoteip[1], lwip_dev.remoteip[2], lwip_dev.remoteip[3], tcp_client.loca_port);tcp_client_recv();}} }void TCPClientProcess(void) {LWIPCommInit();xTaskCreate((TaskFunction_t)task_tcp_client,(const char* )"tcp_client_task",(uint16_t )OS_TCP_STK_SIZE,(void* )NULL,(UBaseType_t )OS_PRIO_TCP,(TaskHandle_t*)&os_tcp_handle); }void TCPClientSend(uint8_t *data, uint16_t length) {tcp_client.send_err = netconn_write(tcp_client.sockfd, data, length, NETCONN_COPY); //發送數據if(tcp_client.send_err != ERR_OK){printf("發送失敗\r\n");tcp_client_close();} }

?

?

?

?

總結

以上是生活随笔為你收集整理的STM32F7xx —— LAN8720(FreeRTOS+LWIP)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。