STM32CUBEMX+STM32H750VB+LWIP+LAN8720A+FREERTOS TCP伺服器、UDP資料接收發送、網線熱插拔、IAR-8.32開發
- STM32H750VB
-
- 使用STM32CUBEMX生成工程
- IAR+J-LINK除錯準備工作
- CUBEMX生成的程式碼問題所在
STM32H750VB
這是一款比較新的Cortex-M7內核的MCU,因為專案原因,需要一款RAM大(1024KB),主頻高(480M)的晶片,我司的硬體工程師選擇了他。
使用STM32CUBEMX生成工程
需要注意的是為什麼不使用標準庫,而選用大多數嵌入式工程師不是很熟悉的STM32CUBEMX生成的HAL庫(完全是因為找不到標準庫啊!啊!啊!,當然也有學習新開發工具的想法)
我使用的是STM32CUBEMX_V5.6.1
1.首先選擇晶片(STM32H750VB)

2.配置時鐘,選擇HSE(根據實際PCB選擇)

3.配置HAL庫時鐘,選擇TIM1,因為需要上FreeRTOS系統,而系統的節拍器通常會選擇系統滴答定時器systick,所以我們這裡選擇TIM1作為庫定時器而不是systick。

4.使能Cache和MPU記憶體保護單元
因為H7結構體的特殊,LWIP網路需要使用的RAM要MPU保護,並且地址也是定死的0x30040000開始,和F4隨意定義不同
具體為何這麼設定得去看H750的使用者手冊

5.PHY晶片設定,PCB選用的是LAN8720A,雖然STM32CUBEMX選用的是LAN8742,但其實並沒有什麼很大的區別(即使區別不大,CUBEMX直接生成的程式也並不能讓你ping通網路,可能是晶片比較新,好多功能沒有得到實際驗證,具體如何修改,下面會進行詳細描述)。

6.FREERTOS系統設定,需要注意的也就衹有TOTAL_HEAP_SIZE,你需要根據實際需要設定大小,其他的就是用什麼使能什麼,譬如:使用訊號量的話就使能SEMAPHORE 等等。

7.LWIP設定,主要設定3個地方,注重注意的就是LWIP_NETIF_LINK_CALLBACK需要使能
通用設定很常規,這裡我不要DHCP,就除能設定成了靜態IP

使能了LINK_CALLBACK之後,就多了一個網線插拔時會進入的回呼函式,你就可以在裡面處理一些事情,不做也可以,他會自動deinit-ETH

PHY晶片選擇LAN8742A
8.隨意設定一個執行燈新增在TASK裡面,用來檢視程式執行狀態就結束可以生成程式了。
IAR+J-LINK除錯準備工作
因為STM32H750是一款很新的晶片,所以會出現,J-LINK和IAR中都沒有這款晶片的情況,這個時候就需要更新IDE和J-LIN了,我更新到了IAR8.32和J-LINK6.84。
這裡要吐槽的一點是IAR8.32真的是個垃圾,除錯的時候,只是把滑鼠放在變數名字上都會閃退,更別說在watch中看陣列了。
CUBEMX生成的程式碼問題所在
1.因為我task多,MX_LWIP_Init();這個LWIP初始化函式是在任務中,是開啟了系統任務排程之後才初始化的,就導致了PHY晶片初始化沒有完成的問題。
解決方法:
ethernetif.c中、
1 | static void low_level_init(struct netif *netif) |
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* USER CODE BEGIN PHY_PRE_CONFIG */ HAL_GPIO_WritePin(GPIOC,GPIO_PIN_2,GPIO_PIN_RESET); HAL_Delay(50); HAL_GPIO_WritePin(GPIOC,GPIO_PIN_2,GPIO_PIN_SET); //LAN8720A重定 /* USER CODE END PHY_PRE_CONFIG */ /* Set PHY IO functions */ LAN8742_RegisterBusIO(&LAN8742, &LAN8742_IOCtx); /* Initialize the LAN8742 ETH PHY */ while(LAN8742_Init(&LAN8742) != LAN8742_STATUS_OK) //需要寫一個while確保LAN8742_Init成功 ; |
新增一個重定功能和while等待初始化完成功能。
2.因為硬體上面沒有硬體上拉採用的自動協商,所以在軟體中也要完成自動協商,在初始化完成後新增
1 | static void low_level_init(struct netif *netif) |
1 2 3 | /* USER CODE BEGIN PHY_POST_CONFIG */ LAN8742_StartAutoNego(&LAN8742); //啟動自動協商 /* USER CODE END PHY_POST_CONFIG */ |
2.ETH_RX_BUFFER_SIZE的巨集定義是 1536UL
而生成程式碼
1 | heth.Init.RxBuffLen = 1528; |
需要改成
1 | heth.Init.RxBuffLen = ETH_RX_BUFFER_SIZE; |
3.清除高速Cache
1 | static err_t low_level_output(struct netif *netif, struct pbuf *p) |
1 2 3 4 5 6 7 | TxConfig.Length = framelen; TxConfig.TxBuffer = Txbuffer; SCB_CleanInvalidateDCache(); //清除Cache緩衝區 HAL_ETH_Transmit(&heth, &TxConfig, ETH_DMA_TRANSMIT_TIMEOUT); return errval; |
1 | static struct pbuf * low_level_input(struct netif *netif) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | if (HAL_ETH_GetRxDataBuffer(&heth, &RxBuff) == HAL_OK) {<!-- --> SCB_CleanInvalidateDCache(); //清除Cache緩衝區 HAL_ETH_GetRxDataLength(&heth, &framelength); /* Build Rx descriptor to be ready for next data reception */ HAL_ETH_BuildRxDescriptors(&heth); SCB_InvalidateDCache_by_Addr((uint32_t *)Rx_Buff, (ETH_RX_DESC_CNT*ETH_RX_BUFFER_SIZE)); #if !defined(DUAL_CORE) || defined(CORE_CM7) /* Invalidate data cache for ETH Rx Buffers */ SCB_InvalidateDCache_by_Addr((uint32_t *)RxBuff.buffer, framelength); #endif custom_pbuf = (struct pbuf_custom*)LWIP_MEMPOOL_ALLOC(RX_POOL); custom_pbuf->custom_free_function = pbuf_free_custom; p = pbuf_alloced_custom(PBUF_RAW, framelength, PBUF_REF, custom_pbuf, RxBuff.buffer, ETH_RX_BUFFER_SIZE); } |
4.系統時鐘初始化完成後需要初始化SRAM3的時鐘
1 2 3 | /* USER CODE BEGIN SysInit */ __HAL_RCC_D2SRAM3_CLK_ENABLE(); //啟動SRAM3的時鐘 /* USER CODE END SysInit */ |
這樣就差不多了,我會把裡面的相關資源上傳
https://download.csdn.net/download/qq_37433166/15599386 J-LINK下載