Skocz do zawartości

STM32 H723, nie działa DMA w jakimkolwiek programie


DeadGeneratio

Pomocna odpowiedź

Dzień dobry, problem jak w temacie. Przechodząc na rodzinę cortex M7 okazuje się, że występują problemy z transferami DMA, nie tylko u mnie a u dużej liczby osób. Próbowałem kilka rozwiązań proponowanych przez ludzi w tym temacie: https://community.st.com/t5/stm32-mcus/dma-is-not-working-on-stm32h7-devices/ta-p/49498

Odnośnie pierwszego pomysłu - nie jestem pewien, ale sądzę, że D-Cache jest wyłączone u mnie, w końcu jeśli jest odznaczone w pliku ioc to nie powinno być włączone, ale teraz po tym jak skopali DMA nic mnie nie zdziwi. Zatem po modyfikacji pliku jak na zdjęciu poniżej, ciągle DMA nie działa. 

image.thumb.png.4de06625b3f0f7674bb55236956a065a.png

Dodanie bufera w określonym miejscu pamięci także nie działa - zdjęcie poniżej.

image.thumb.png.2dbb1380c79f39336954fcc020ff6978.png

Czy ktoś miał takie problemy z cortexami m7 i udało mu się to obejść? Proszę o pomoc.

DMA chwilowo ma odczytywać tylko napięcie z potencjometrów, ale w przyszłości zastąpi płytkę STM32 F407 z wykorzystanym interfejsem I2S do obsługi korektora graficznego

Link do komentarza
Share on other sites

@DeadGeneratio a odpalałeś jakiś przykład z folderu repozytorium gdzie Cube pobiera sobie HAL da H7? Widzę że jest coś tu: STM32Cube\Repository\STM32Cube_FW_H7_V1.11.1\Projects\NUCLEO-H723ZG\Examples\DMA

19 godzin temu, DeadGeneratio napisał:

Dodanie bufera w określonym miejscu pamięci także nie działa - zdjęcie poniżej.

Nie jestem ekspertem i na pewnie nie wypowiem się w temacie cache. Patrzac na RM0468 wydaje mi się, że mastera DMA może działać na D1:

image.thumb.png.8024d86be75aadab4082dc5942a6488c.png

image.thumb.png.51c2c0ea2a717fdb8fd9cb0dcb817173.png

U mnie kiedyś DMA nie działało, bo przy generowaniu kodu funkcje inicjalizujące były w złej kolejności. Może odznacz generowanie "function call" i wstaw samemu na końcu.

 

  • Lubię! 1
Link do komentarza
Share on other sites

(edytowany)

Aktualizacja: Problem rozwiązał się sam a właściwie zniknął nie wiem czemu, niepokoi mnie to jeśli mam pracować dalej na tym projekcie, nie znam dnia ani godziny kiedy płytka odmówi współpracy.

Początkowo dodałem do plików ld kilka linijek kodu, mój kod zaznaczony komentarzem modyfikacja.

/*
******************************************************************************
**
**  File        : LinkerScript.ld (debug in RAM dedicated)
**
**  Author      : STM32CubeIDE
**
**  Abstract    : Linker script for STM32H7 series
**                320Kbytes RAM_EXEC and 240Kbytes RAM
**
**                Set heap size, stack size and stack location according
**                to application requirements.
**
**                Set memory bank area and size if external memory is used.
**
**  Target      : STMicroelectronics STM32
**
**  Distribution: The file is distributed as is, without any warranty
**                of any kind.
**
*****************************************************************************
** @attention
**
** Copyright (c) 2023 STMicroelectronics.
** All rights reserved.
**
** This software is licensed under terms that can be found in the LICENSE file
** in the root directory of this software component.
** If no LICENSE file comes with this software, it is provided AS-IS.
**
****************************************************************************
*/

/* Entry Point */
ENTRY(Reset_Handler)

/* Highest address of the user mode stack */
_estack = ORIGIN(DTCMRAM) + LENGTH(DTCMRAM);    /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200;      /* required amount of heap  */
_Min_Stack_Size = 0x400; /* required amount of stack */

/* Specify the memory areas */
MEMORY
{
  RAM_EXEC (xrw)  : ORIGIN = 0x24000000, LENGTH = 320K
  DTCMRAM  (xrw)  : ORIGIN = 0x20000000, LENGTH = 128K
  ITCMRAM (xrw)   : ORIGIN = 0x00000000, LENGTH = 64K
  RAM_D2  (xrw)   : ORIGIN = 0x30000000, LENGTH = 32K
  RAM_D3  (xrw)   : ORIGIN = 0x38000000, LENGTH = 16K
}

/* Define output sections */
SECTIONS
{
  /* The startup code goes first into RAM_EXEC */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >RAM_EXEC

  /* The program code and other data goes into RAM_EXEC */
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)
    *(.RamFunc)        /* .RamFunc sections */
    *(.RamFunc*)       /* .RamFunc* sections */

    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
  } >RAM_EXEC

  /* Constant data goes into RAM_EXEC */
  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >RAM_EXEC

  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >RAM_EXEC
  .ARM : {
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
  } >RAM_EXEC

  .preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  } >RAM_EXEC

  .init_array :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
  } >RAM_EXEC

  .fini_array :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
  } >RAM_EXEC

  /* used by the startup to initialize data */
  _sidata = LOADADDR(.data);

  /* Initialized data sections goes into RAM, load LMA copy after code */
  .data :
  {
    . = ALIGN(4);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */

    . = ALIGN(4);
    _edata = .;        /* define a global symbol at data end */
  } >RAM_D1 AT> FLASH

  /* Uninitialized data section */
  . = ALIGN(4);
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss section */
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)

    . = ALIGN(4);
    _ebss = .;         /* define a global symbol at bss end */
    __bss_end__ = _ebss;
  } >RAM_D1 AT> FLASH

  /* User_heap_stack section, used to check that there is enough RAM left */
  ._user_heap_stack :
  {
    . = ALIGN(8);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    _estack = .;
    . = ALIGN(8);
  } >RAM_D1 AT> FLASH

  /* Remove information from the standard libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }
  /* MODYFIKACJA */
  	.buffer :
  	{
  	. = ALIGN(2);
  	*(.rxBuf)
  	*(.txBuf)
  	} > RAM_D2
  	
  	
  	
  .ARM.attributes 0 : { *(.ARM.attributes) }
}

Rozwiązałem zatem problem dostępu do SRAM1 oraz SRAM2 przez DMA wykorzystując zmienne w programie w następujący sposób:

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

__attribute__ ((section(".txBuf"), used)) volatile uint32_t txBuf[2];
__attribute__ ((section(".rxBuf"), used)) volatile uint32_t rxBuf[2];


/* USER CODE END 0 */

volatile jest tylko po to abym miał pewność, że w debugerze pokażą mi się wartości.

Dodatkowo musiałem dopisać dwie linijki uruchamiające zegar do SRAM1 oraz SRAM2

int main(void)
{
  /* USER CODE BEGIN 1 */
	__HAL_RCC_D2SRAM1_CLK_ENABLE();
	__HAL_RCC_D2SRAM2_CLK_ENABLE();


  /* USER CODE END 1 */

Wczoraj skończyłem przy tym dłubać i dzisiaj znowu usiadłem. Okazało się, że przerwanie TxRxCpltCallback od I2S pomimo, że występuje, widać na breakpoincie, nic nie powoduje. Wcześniej nie dawało to żadnych objawów, bo przerzucałem z zmiennej rxBuf wartości do txBuf, a płytka na I2S przekazywałą mi dźwięk z laptopa na wzmacniacz. Gdy chciałem zaimplementować zwykłą funkcję zmiany głośności okazało się, że zapomniałem zadeklarować zmienną = 1. Zamiast tego kompilator przyjął ją za stan nieustalony, w moim przypadku wrzucił tam 0. Oznaczało to, że w skrócie rxBuf * vol = x, txBuf = x. I tutaj wyszedł na jaw problem - pomimo, że wynik mnożenia powinien być równy zero, na wzmacniacz ciągle trafiała muzyka z laptopa. Zacząłem kasować wszystkie linijki kodu co dodawałem wcześniej aby zmusić kod do działania - usunąłem przerwanie:

void HAL_I2SEx_TxRxCpltCallback(I2S_HandleTypeDef *hi2s) {

}

Dźwięk na wzmacniaczu dalej obecny. Potem usunąłem inicjalizację zegarów D2SRAM1 oraz D2SRAM2, ponownie dźwięk na wzmacniaczu dalej obecny. Niedowierzając usunąłem kod z plików ld służący do wymuszenia wykorzystania sekcji D2, muzyka dalej grała. W akcie rozpaczy usunąłem nawet atrybut przekazujący do nieistniejącego warunku wrzucania do D2 zmienne rxBuf oraz txBuf i zadeklarowałem je poniżej w zwyczajny sposób:

uint32_t rxBuf[2];
uint32_t txBuf[2];

No i jak się okazuje, zacząłem wykorzystywać RAM_D1, widziałem to w Memory Details, perfidnie w sekcji RAM_D1, .bss miałem zadeklarowane obie zmienne. Dźwięk oczywiście na wzmacniaczu dalej obecny. Przepraszam każdego czytającego ten post za sposób pisania jak nowela, ale jestem dalej w szoku co się tutaj stało, relacjonuję to chwilę po opisanych wydarzeniach.

Stwierdziłem, że jeżeli usunę:

HAL_I2SEx_TransmitReceive_DMA(&hi2s1, txBuffer, rxBuffer, 2);

A dźwięk na wzmacniaczu dalej będzie to wzywam egzorcystę od mikrokontrolerów. Okazało się na szczęście, chociaż byłem sceptycznie nastawiony, że dźwięk wreszcie umilkł. Zatem ponownie zacząłem wszystko dodawać w odwrotnej kolejności i nie mogłem przywrócić całego sprzętu do działania. Dosłownie ten sam kod nie chciał już ponownie zadziałać więc utworzyłem nowy projekt. 

W nowym projekcie wykonałem następującą listę czynności, w dokładnie takiej kolejności:

  1. Utworzenie nowego projektu o pewnej nazwie
  2. W pliku .ioc w zakładce System Core, podzakładka CORTEX_M7 zmieniłem Speculation default mode = Disabled
  3. W pliku .ioc w zakładce System Core, podzakładka RCC zmieniłem HSE na Crystal/Ceramic Resonator
  4. W pliku .ioc w zakładce System Core, podzakładka SYS ustawiłem Timebase Source = SysTick
  5. W pliku .ioc w zakładce Multimedia, podzakładka I2S1 ustawiłem Data and Frame Format = 24 Bits Data on 32 Bits Frame
  6. W pliku .ioc w zakładce Multimedia, podzakładka I2S1 ustawiłem Selected Audio Frequency = 96kHz
  7. W pliku .ioc w Clock Configuration ustawiłem maksymalną dostępną częstotliwość taktowania = 550MHz
  8. W pliku .ioc w zakładce Multimedia, podzakładka I2S1 ustawiłem DMA dla SPI1_RX oraz SPI1_TX, oba z priorytetem = HIGH, oba w trybie CIRCULAR, Data Width = WORD w obu, STREAM ustawiony dla RX = DMA1 Stream 0, TX = DMA1 Stream 1
  9. W pliku .ioc w zakładce DEBUG, ustawiłem Debug = Serial Wire
  10. W pliku main.c wpisałem następujący kod:
/* USER CODE BEGIN PV */
uint32_t rxBuffer[2];
uint32_t txBuffer[2];
/* USER CODE END PV */

/* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_I2S1_Init();
  /* USER CODE BEGIN 2 */
  HAL_I2SEx_TransmitReceive_DMA(&hi2s1, txBuffer, rxBuffer, 2);
  /* USER CODE END 2 */

void HAL_I2SEx_TxRxCpltCallback(I2S_HandleTypeDef *hi2s) {
	txBuffer[0] = rxBuffer[0];
	txBuffer[1] = rxBuffer[1];
}

I uwaga, wszystko teraz działa w pełni poprawnie. Dlaczego tak się stało, nie mam zielonego pojęcia. Transfer DMA pomimo, że wrzucany jest do sekcji D1, gdzie nie powinno być dostępu, jest wykonywany w pełni poprawnie, dla udowodnienia screen z instancji hi2s1.

image.thumb.png.84971ec62137e94122679759852b07a1.png

Edytowano przez DeadGeneratio
  • Lubię! 2
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

@DeadGeneratio nie ma co ukrywać, że embedded jest bardzo frustrujące. Przy H7 siedzę od blisko roku i nie mogę powiedzieć, że czuję się pewnie w tym temacie. Pomimo czytania artykułów, poradników, forum ST i filmików, dokumentacji dla H7 i coretexa moją najlepsza metodą jest metoda prób i błędów, porównywanie przykładów + git. Jak coś się sypie tak że nie wiesz co się dzieje to wracasz do ostatniego stabilnego punktu lub robisz git status, git diff i patrzysz co nabroiłeś lub co w kodzie zjadł cube. 

21 godzin temu, DeadGeneratio napisał:

Zatem ponownie zacząłem wszystko dodawać w odwrotnej kolejności i nie mogłem przywrócić całego sprzętu do działania.

Też to zauważyłem, dlatego chyba najlepiej jest metodą małych kroków dokładać cały czas nowe funkcjonalności i patrzeć na stare. Raz miałem taką sytuację, że nagle przestał działać wątek z RTOSa. Okazało się, że przy zmianie w konfiguracji która chyba dotyczyła DACa, Cube usunął wywołanie funkcji od RTOSa. Niedawno też miałem kolejne takie zdziwienie gdy dodanie obsługi USB wyrzuciło jednocześnie istniejący kod TouchGFX. W sumie było to logiczne, bo są w innych zakładkach middleware.

Także, nie poddawaj się i dalej przebijaj głową mur 😄 

  • Lubię! 1
Link do komentarza
Share on other sites

Całkiem możliwe, że zostało z poprzedniego zapisu, gdyby były to statyczne dane, w pełni bym się zgodził. Ale w tej całej magii chodzi o to, że muzyka ciągle działała, więc kolejne próbki trafiały z rx na tx w tej sekcji D2. To mnie najbardziej bawi a za razem przeraża. Obecnie wszystko działa, a właściwie działało dopóki nie zacząłem portować kodu z f407 na h723. Dodając ADC na dwóch wejściach, IN3 oraz IN5, ciekawa sprawa jest z tym związana. O ile IN5 normalnie został przypisany do jakiegoś pinu, załóżmy PB1, wejście IN3 zostało przypisane do PA6, gdzie wcześniej miałem I2S1 bodajże WS. A omawiany WS od I2S1 został przerzucony gdzieś na PC6 czy coś takiego. I walczę obecnie z DMA dla ADC, bo coś jednak nie działa. Jestem ciekawy czy jak wrócę do samego DMA z I2S, ciągle będzie działać, czy magicznie przestanie.

 

A odnośnie gita - ściągnąłem i zainstalowałem gita zarówno jako osobna aplikacja, oraz jako dodatek do eclipsa od cubeIDE. Jak zacząć archiwizować poprzednie snapshoty?

Link do komentarza
Share on other sites

@Elvis możesz coś doradzić jak dowiedzieć się więcej w tym temacie? Są jakieś materiały na YT od ST dla F7 sprzed 4 lat i ich konkluzją jest, że musisz zaufać kompilatorowi że przy wyższym stopniu optymalizacji przeorganizuje kod żeby użyć cache. Ale odnośnie spójności niewiele tam mówią. Raz byłem na spotkaniu z inżynierem od ST i ten temat też niezabardzo był mu znany.

Z publikacji to https://developer.arm.com/Processors/Cortex-M7 i jest książka The Designer's Guide to the Cortex-M Processor Family: A Tutorial Approach 3rd Edition, od 3 wydania z chyba 2022 roku jest coś o M7 i spójności cache.

 

Link do komentarza
Share on other sites

@Gieneq a chciałbyś może wyjaśnić mi czy może zdarzyć się sytuacja gdzie kod wykonywany na cortexie m4 będzie lepiej, wydajniej i szybciej wykonywany niż na m7? Teraz borykam się z problemem - portuję kod z f407 na h723, i okazuje się, że mogę wykonać maksymalnie 8 przeliczeń w przerwaniu I2S na liczbach float, podczas gdy na F407 udało mi się dotrzeć aż do 12. Sądziłem, że przesiadając się będę miał większą wydajność. Mogę porzucić kod albo cały projekt dla obu płytek aby można było porównać różnice 

Link do komentarza
Share on other sites

@Gieneq Kompilator nic nie pomoże na pamięć cache. Co więcej domyślne skrypty linkera dla F7 to zupełna porażka i przykłady od ST dla DMA działają tylko przypadkiem. Wystarczy dodać globalną zmienną, która zajmie więcej miejsca i kaboom - przestają działać.

ST udostępnia taki dokument odnośnie cache: https://www.st.com/content/ccc/resource/technical/document/application_note/group0/08/dd/25/9c/4d/83/43/12/DM00272913/files/DM00272913.pdf/jcr:content/translations/en.DM00272913.pdf

Nie jest to idealne źródło informacji, bo doradzanie używania pamięci z wyłączonym cache dla DMA to raczej anty-wzorzec. Niestety nic lepszego od ST nie znalazłem. Google zwraca trochę wyników odnośnie spójności cache, to temat który odbija się czkawką w F7/H7.

Na pewno warto zapoznać się z MPU i nauczyć wyłączać cache dla wybranych obszarów - tak działają np. przykłady dla F7 używające LCD o ile dobrze pamiętam. Natomiast dostęp do takiej pamięci jest bardzo powolny, więc to niekoniecznie idealna opcja.

Edit: Uczyłem się zabaw z cache na Cortex-A, w przypadku mniejszych Cortex-M, czyli <=M4 ten problem nie istnieje, ale już Cortex-M7 ma te same problemy co większe rodzeństwo. Jednak na cortex-m7 nie znam aż tak dokładnie szczegółów, ani dobrych źródeł informacji. Kiedyś mając spore problemy z cache na Cortex-A postanowiłem zobaczyć jak sobie z tym radzi HAL dla F7 i okazało się że radzi sobie bardzo łatwo - czyli ignoruje problem 😄

Edytowano przez Elvis
  • Lubię! 1
  • Pomogłeś! 1
Link do komentarza
Share on other sites

@Elvis dzięki za odpowiedź, postaram się zrobić z tego użytek.

W tym miesiącu pojawiła się seria o U5, są tam 2 filmy o DCache i ICache ale nie ma tam konkretów:

Wspomniany Hands On z F7, gdzie coś się dzieje.

40 minut temu, Elvis napisał:

bo doradzanie używania pamięci z wyłączonym cache dla DMA to raczej anty-wzorzec.

W tym filmie z tego co rozumiem raz mówią, że nie da się użyć DMA bo cache jest blisko rdzenia, później przy omówieniu cache policy już jest że da się. Poza tym jest to dość przydatne nakreślenie problemu:

 

 

Link do komentarza
Share on other sites

Natomiast na filmie brakuje jednej ważnej informacji - praca z cache wymaga troski o wyrównanie danych. Wiersz pamięci cache ma 16/32/64 bajty, więc wszystkie operacje będą wykonywane na blokach pamięci i trzeba uważać żeby czegoś nie popsuć niechcący. No chyba że wyłączymy cache.

  • Lubię! 2
Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.