а сверху ваши же примеры написаны самостоятельно
?
ЗЫ. Чтож за обработка в реальном времени, которой обычной рам скорости не хватает ![]()
Мне кажется в начале пути надо что попроще пробовать ![]()
а сверху ваши же примеры написаны самостоятельно
?
ЗЫ. Чтож за обработка в реальном времени, которой обычной рам скорости не хватает ![]()
Мне кажется в начале пути надо что попроще пробовать ![]()
Сравни скрипты линкера у этих двух проектов. Думаю, что для G431 нужно будет дополнительную секцию памяти добавить.
Если хотел умело повесить интригу - у тебя это удалось! ![]()
Продолжай повествование ![]()
ЗЫ: На скриншоте ошибка видимо - по даташиту 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
Можно и без линкера и секций. В некоторых случаях это даже удобнее ![]()
У вас же адрес есть уже.
Для некоторых применений (типа массив, там, или структура здоровенная, или еще что-то такое, типа массива структур) даже аллокатор не нужен.
Можно написать что-нибудь вроде (псевдо-кот, я его не компилировал):
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) и поэтому выполнение кода в ней быстрее. Наверное и тестировать нужно соответствующим образом.
====================================
У меня еще вот какие мысли появились.
Если я буду просто обрабатывать сигналы в реальном времени - это все понятно. А вот если я еще дисплей добавлю (да еще энкодер с кнопками
), то уже придется как-то учитывать временные интервалы на выполнение каждого элемента кода, так ведь? Может быть стОит тогда сразу посмотреть в сторону FreeRTOS?
Или хватит и элементарных флагов в лупе?
if (do_real_time) {
// Выполняем работу с буфером
}
if (do_update_display) {
// Обновляем экран
}
Все зависит от задачи, соотвественно и решения разные. Мне например FreeRTOS не зашло, проще флагами/таймерами разруливать действия, впрочем дело вкуса.
а мне зашло, я даже “вляпаться” успел, в одном проекте))
там главное понять концепцию и все эти мьютексы, семафоры, приоритеты (я еще в процессе)
и тогда наступает эйфория, как с указателями в С ![]()
не взлетел 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 для банального вывода на дисплей.