W tym poście chciałbym opisać sposób obsługi biblioteki LWIP w mikrokontrolerach STM32H7. Jako przykład wykorzystam płytkę Nucleo-144 STM32H753.
[Źródło: https://www.st.com/content/st_com/en/products/evaluation-tools/product-evaluation-tools/mcu-mpu-eval-tools/stm32-mcu-mpu-eval-tools/stm32-nucleo-boards/nucleo-h753zi.html#overview]
Informacje o układzie można pobrać ze strony producenta.
Konfiguracja z CubeMX:
Konfiguracja zegara:
Na płytce Nucleo nie został umieszczony kwarc zewnętrzny taktujący główny układ (jedynie kwarc 32kHz został dołożony), Wobec tego STM jest pędzony z HSI. Konfiguracja w celu uzyskania 480MHz wygląda następująco:
Uruchomienie ETH:
Ta część sprowadza się do uruchomienia linii do komunikacji z zredukowaną listą rozkazów (RMII) oraz ustawienia prędkości na liniach GPIO:
ETH uruchamiam w zakładce Connectivty:
Konfiguracja Cortex M7:
Jeżeli ta część nie zostanie skonfigurowana to nie ma możliwości na uruchomienie biblioteki LWIP.
Na samym początku należy uruchomić CPU ICache (Instruction Cache) oraz CPU DCache (Data Cache). Następnie MPU należy ustawić na Background Region Privileged accesses only + MPU Disabled during hard fault.
Dalej należy skonfigurować MPU Region 0 zgodnie z podanymi informacjami na zdjęciu.
Spowoduje to wygenerowanie następującego kodu w projekcie:
- void MPU_Config(void)
- {
- MPU_Region_InitTypeDef MPU_InitStruct = {0};
- /* Disables the MPU */
- HAL_MPU_Disable();
- /** Initializes and configures the Region and the memory to be protected
- */
- MPU_InitStruct.Enable = MPU_REGION_ENABLE;
- MPU_InitStruct.Number = MPU_REGION_NUMBER0;
- MPU_InitStruct.BaseAddress = 0x30000000;
- MPU_InitStruct.Size = MPU_REGION_SIZE_256MB;
- MPU_InitStruct.SubRegionDisable = 0x0;
- MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
- MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
- MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
- MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
- MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
- MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
- HAL_MPU_ConfigRegion(&MPU_InitStruct);
- /* Enables the MPU */
- HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
- }
Uruchomienie LWIP:
Następnie uruchomiona zostaje biblioteka LWIP gdzie w tym przypadku zostanie ustawiony statyczny adres IP oraz uruchomiony serwer DHCP.
Po wprowadzeniu powyższych ustawień można wygenerować projekt. Do dalszej obsługi projektu wykorzystałem STM32CubeIde.
Konfiguracja w projekcie:
Na samym początku należy dołożyć miejsce do przechowywania w pamięci buforów TX oraz RX.
- .RxDecripSection 0x30040000 :
- {
- KEEP(*(.RxDecripSection*))
- } >RAM_D2
- .TxDecripSection 0x30040060 :
- {
- KEEP(*(.TxDecripSection*))
- } >RAM_D2
- .RxArraySection 0x30040200 :
- {
- KEEP(*(.RxArraySection*))
- } >RAM_D2
Miejsce w pamięci na bufor danych umieszczony został w pamięci RAM_D2.
Podział pamięci powinien zostać wygenerowany poprawnie. Powinien wyglądać tak:
- /* Memories definition */
- MEMORY
- {
- DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
- ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K
- RAM_D1 (xrw) : ORIGIN = 0x24000000, LENGTH = 512K
- RAM_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 288K
- RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K
- FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 2048K
- }
Adresy pamięci w pamięci mają odzwierciedlenie w adresach zapisanych w dokumentacji (RM0433 Reference Manual). Poniżej znajduje się część tabeli 7 ze strony 130 wspomnianego dokumentu.
- ITCMRAM - (0x00000000 - 0x0000FFFF) - (Instruction Tightly Coupled Memmory)
- DTCMRAM - (0x20000000 - 0x2001FFFF) - (Data Tightly Coupled Memmory)
- FLASH - (0x08000000) - Flash memmory bank 1
- RAM_D1 - (0x24000000) - AXI SRAM
- RAM_D2 - (0x30000000) - SRAM1
- RAM_D3 - (0x38000000) - SRAM4
DTCM oraz ITCM są to pamięci podłączone bezpośrednio z procesorem. Wykorzystywane są do mapowania ważnych instrukcji oraz danych w celu uzyskania szybkiego dostępu (np. ITCM - obsługa przerwań, kod krytyczny. DTCM często wykorzystywane dane, stos. współczynniki DSP). Pamięci TCM zostały wyposażone także w ECC (pamięć ram z systemem kodowania korekcyjnego).
Dodajemy definicję DATA_IN_D2_SRAM do projektu:
Dodatkowo w pętli while należy umieścić funkcję MX_LWIP_Process();
- while (1) {
- /* USER CODE END WHILE */
- /* USER CODE BEGIN 3 */
- MX_LWIP_Process();
- }
- /* USER CODE END 3 */
- void MX_LWIP_Process(void)
- {
- /* USER CODE BEGIN 4_1 */
- /* USER CODE END 4_1 */
- ethernetif_input(&gnetif);
- /* USER CODE BEGIN 4_2 */
- /* USER CODE END 4_2 */
- /* Handle timeouts */
- sys_check_timeouts();
- Ethernet_Link_Periodic_Handle(&gnetif);
- /* USER CODE BEGIN 4_3 */
- /* USER CODE END 4_3 */
- }
Sprawdzenie komunikacji
Sprawdzenie komunikacji można dokonać przez prostą komendę ping. Dodatkowo można wykorzystać program Advanced IP Scanner, który pozwoli na zlokalizowanie urządzenia w podanej sieci.
HTTP:
Tutaj wykorzystam przykład udostępniony przez ST w repozytorium dla STM32H7. Wystarczy przekopiować sam plik fsdata.c
Przykładowa lokalizacja pliku: STM32Cube\Repository\STM32Cube_FW_H7_V1.8.0\Middlewares\Third_Party\LwIP\src\apps\http
Plik należy umieścić w wygenerowanym projekcie w takiej samej lokalizacji:
Middlewares\Third_Party\LwIP\src\apps\http
Dodatkowo w opcji pliku należy odłączyć go od kompilacji, w innym przypadku będzie generował błędy (C/C++ Build -> Exclude resource from build).
Ostatnim elementem jest dołożenie funkcji httpd_init();
- /* USER CODE BEGIN 2 */
- httpd_init();
- /* USER CODE END 2 */
Po wprowadzeniu adresu IP w przeglądarce nastąpi wyświetlenie następujących informacji:
TCP Serwer:
Kod dla tego rozwiązania jest prostą implementacją serwera TCP ECHO.
Ważnym elementem jest ustawienie odpowiedniej wielkości bufora RX.
- #define ETH_RX_BUFFER_SIZE (1536UL)
Testy układu można przeprowadzić w programie Hercules: