Микроконтроллеры семейства STM32

а сверху ваши же примеры написаны самостоятельно :slight_smile: ?

ЗЫ. Чтож за обработка в реальном времени, которой обычной рам скорости не хватает :thinking:
Мне кажется в начале пути надо что попроще пробовать :slightly_smiling_face:

Сравни скрипты линкера у этих двух проектов. Думаю, что для G431 нужно будет дополнительную секцию памяти добавить.

да, в файле “STM32G431CB.._FLASH.ld”

но это еще не все))

Если хотел умело повесить интригу - у тебя это удалось! :slight_smile:
Продолжай повествование :slight_smile:

ЗЫ: На скриншоте ошибка видимо - по даташиту CCM RAM = 10KB, RAM = 22KB (у тебя там 32кб).

Так это всегда ручками в новом проекте линковщик править? А нельзя как-то поправить чтобы новые проекты уже исправленные создавались?)

Там пустой проект сгенерированный в кубе, и я только переменную добавил.

да ладно, не злись)
че по пятницам не бывает)

я имел ввиду, то как в эту память, либо секции целиком, либо данные помещать.

вот тут все расписано.
https://www.st.com/content/ccc/resource/technical/document/application_note/bb/09/ca/83/14/e9/44/c5/DM00083249.pdf/files/DM00083249.pdf/jcr:content/translations/en.DM00083249.pdf

Можно и без линкера и секций. В некоторых случаях это даже удобнее :slight_smile:

У вас же адрес есть уже.

Для некоторых применений (типа массив, там, или структура здоровенная, или еще что-то такое, типа массива структур) даже аллокатор не нужен.

Можно написать что-нибудь вроде (псевдо-кот, я его не компилировал):

struct MyBigTable *a =(struct MyBigTable *)((void *)(uintptr_t)(0x10000000));
struct MySmallTable *b =(struct MySmallTable *)((void *)(uintptr_t)(0x10000000UL + sizeof(*a)));

Но для многих маленьких переменных это неудобно.

PS: Я сейчас посмотрел на скриншот линкер-скрипта и подумал, что раз уж эта память RW в отличие от RAM, которая RWX, то может быть стоит этот кусок памяти отдать под стек? Там, наверху скрипта этого, на скриншоте, стек кладут в RAM. Можно сэкономить + убрать потенциальные атаки на железку, связанные с переполнением буфера на стеке и исполнением на стеке.

PPS: строчка про estack - исправить RAM на CCMRAM

Он имел ввиду, что нужно еще и саму секцию описать. Глазами делай вниз по скрипту, там пойдут секции. Вставь по образу и подобию туда свою секцию, назови ее “.ccmram”, задай ее местоположение (нужно указать, что она лежит в MEMORY REGION “CCMRAM”).

Вот тогда - все. Ну и у переменных , конечно, attribute(section…

Очень просто проверить - записать туда чего нибудь и прочитать назад. Адрес у тебя есть : 0x10000000

по даташиту памяти 32К, из них

SRAM1 - 16K (с аппаратной проверкой чётности)
SRAM2 - 6K

CCMRAM -10K

и поправив файл ld

/* Memories definition */
MEMORY
{
RAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 32K
FLASH  (rx)     : ORIGIN = 0x8000000,    LENGTH = 128K

SRAM1  (rw)     : ORIGIN = 0x20000000,   LENGTH = 16K
SRAM2  (rw)     : ORIGIN = 0x20004000,   LENGTH = 6K
CCMRAM (rw)     : ORIGIN = 0x20005800,   LENGTH = 10K
}

можно наблюдать в IDE

про интригу, @vvb333007 тему раскрыл
надо указать в какие регионы какие секции/переменные ложатся.

Ага…
То есть никаких ошибок быть не может, так как CCM RAM имеет алиас в области SRAM.

К тому же G431 CCM RAM имеет доступ к DMA. Значит “извращаться” с выделением в отдельный блок и не нужно. Видимо поэтому в CubeIDE просто общие 32кб ОЗУ?

Я хотел в CCM RAM положить переменные и функции для работы с буферами. Видимо не буду. Или стОит (производительность выше должна быть)?

попробуйте/сравните, нам расскажете.

Что то захотелось мне потестировать разницу в быстройдействии разного блока памяти.
Тестовая функция:

void h743testSpeedCPUbyST7735(void) {
	static unsigned long timerShowResult = 0UL; // расчет количества тактов потраченных на перемену байт в памяти и таймер показа информации каждую секунду
	static unsigned long ticksBySec = 0UL; // суммарное количество тактов за секунду
	if ((millis() - timerShowResult) >= 1000UL) { // прошла секунда - выводим результат
		timerShowResult = millis(); // сбросили таймер
		ticksBySec /= 1000UL; // число слишком большое получается для вывода в printf // на всякий случай поделим на 1000
		printf("t/1000 = %d\r\n", (unsigned int)ticksBySec); // вывели значение
		ticksBySec = 0UL; // обнулили счетчик
	} else { // не прошла секунда - считаем
		static unsigned long startTicks = 0UL, endTicks = 0UL;
		unsigned long countWordsScreen = ST7735_X_SIZE * ST7735_Y_SIZE * 2 / 4; // длина цикла
		unsigned long * pScr = ((unsigned long *)(pScreenBuf)); // начало видеобуфера
		startTicks = M7_DWT_CYCCNT; // начало отсчета тактов
		// тут начинаем что то делать тяжелое в памяти
		while(countWordsScreen--) { // цикл по всему видеобуферу
			//__asm volatile("rev %0, %1;" : "=r"(*(pScr)) : "r"(*(pScr))); // меняем байты в пикселях
			++pScr;
		}
		// закончили тяжелую работу
		endTicks = M7_DWT_CYCCNT; // окончание отсчета тактов
		ticksBySec += (endTicks - startTicks); // прибавили к общему счетчику
	}
}

С использованием 16 строки
__asm volatile(“rev %0, %1;” : “=r”((pScr)) : “r”((pScr)));

результат:
t/1000 = 32740
t/1000 = 32707
t/1000 = 32714
t/1000 = 32730

если строку закомментировать, результат:
t/1000 = 38009
t/1000 = 38007
t/1000 = 38008
t/1000 = 38008

Вопрос:
насколько корректно использовать ассемблерную инструкцию rev замены байт для замера быстродействия памяти?

Использую RAM_D3 , потом попробую разницу с DTCMRAM.

А она (DTCMRAM) точно быстрее по спецификации на мк? На G431 я этой информации не нашел (хотя особо и не искал), а вот у F303 точно написано, что CCMRAM (8KB) имеет “нулевую задержку” (подключена напрямую к CPU) и поэтому выполнение кода в ней быстрее. Наверное и тестировать нужно соответствующим образом.

====================================

У меня еще вот какие мысли появились.
Если я буду просто обрабатывать сигналы в реальном времени - это все понятно. А вот если я еще дисплей добавлю (да еще энкодер с кнопками :smiley: ), то уже придется как-то учитывать временные интервалы на выполнение каждого элемента кода, так ведь? Может быть стОит тогда сразу посмотреть в сторону FreeRTOS?

Или хватит и элементарных флагов в лупе?

if (do_real_time) {
  // Выполняем работу с буфером
}

if (do_update_display) {
  // Обновляем экран
}

Все зависит от задачи, соотвественно и решения разные. Мне например FreeRTOS не зашло, проще флагами/таймерами разруливать действия, впрочем дело вкуса.

а мне зашло, я даже “вляпаться” успел, в одном проекте))
там главное понять концепцию и все эти мьютексы, семафоры, приоритеты (я еще в процессе)
и тогда наступает эйфория, как с указателями в С :100:

не взлетел DTCMRAM, т к DMA с ним работать не хочет, надо переписывать тестовый код, позже…

Итоговый код если вдруг кому надо.


  /* The startup code goes first into DTCMRAM */
  .bufDT :
  {
    . = ALIGN(4);
    KEEP(*(.bufDT)) /* Startup code */
    . = ALIGN(4);
  } >DTCMRAM

  /* The startup code goes first into RAM_D2 */
  .bufD2 :
  {
    . = ALIGN(4);
    KEEP(*(.bufD2)) /* Startup code */
    . = ALIGN(4);
  } >RAM_D2

  /* The startup code goes first into RAM_D3 */
  .bufD3 :
  {
    . = ALIGN(4);
    KEEP(*(.bufD3)) /* Startup code */
    . = ALIGN(4);
  } >RAM_D3




__attribute__ ((section(".bufD3"), used)) unsigned char screenBuf[ST7735_SIZE_DMA_BUFF_BYTES];



void h743testSpeedCPUbyST7735(void) {
	static unsigned long timerShowResult = 0UL; // расчет количества тактов потраченных на перемену байт в памяти и таймер показа информации каждую секунду
	__attribute__ ((section(".bufDT"), used)) static unsigned long ticksBySec = 0UL; // суммарное количество тактов за секунду
	if ((millis() - timerShowResult) >= 1000UL) { // прошла секунда - выводим результат
		timerShowResult = millis(); // сбросили таймер
		ticksBySec /= 1000UL; // число слишком большое получается для вывода в printf // на всякий случай поделим на 1000
		printf("t/1000 = %d\r\n", (unsigned int)ticksBySec); // вывели значение
		ticksBySec = 0UL; // обнулили счетчик
	} else { // не прошла секунда - считаем
		__attribute__ ((section(".bufDT"), used)) static unsigned long startTicks = 0UL, endTicks = 0UL;
		unsigned long countWordsScreen = ST7735_X_SIZE * ST7735_Y_SIZE * 2 / 4; // длина цикла
		unsigned long * pScr = ((unsigned long *)(pScreenBuf)); // начало видеобуфера
		startTicks = M7_DWT_CYCCNT; // начало отсчета тактов
		// тут начинаем что то делать тяжелое в памяти
		while(countWordsScreen--) { // цикл по всему видеобуферу
			__asm volatile("rev %0, %1;" : "=r"(*(pScr)) : "r"(*(pScr))); // меняем байты в пикселях
			__asm volatile("rev %0, %1;" : "=r"(*(pScr)) : "r"(*(pScr))); // меняем байты в пикселях
			++pScr;
		}
		// закончили тяжелую работу
		endTicks = M7_DWT_CYCCNT; // окончание отсчета тактов
		ticksBySec += (endTicks - startTicks); // прибавили к общему счетчику
	}
}

Воооот, на F303 тоже явно указано, что с DMA память CCMRAM не работает.
У G431 вроде как нет с этим проблем (как я понял), но мк еще не приехал - поэтому пока не проверял.

А в примере F7xx серии микроконтроллер используется?

Ох, напугал словами страшными… ))
Подумать надо.

нету, яж давал ссылку на анпоту, продублирую

STM32H743

и кстати мне оно (DMA) тупо нужно чтоб на экране видеть результат операции с памятью. В реальных задачах я сомневаюсь в необходимости использовать столь ценный ресурс DTCMRAM для банального вывода на дисплей.