Перейти к содержанию
    

Подключение ESP-PSRAM64 или APS6404L по FMC STM32

Недавно появилась задача подключить небольшой объем оперативной памяти к микроконтроллеру STM32, чтобы буферизировать видеопоток.

PSRAM64(H) - это псевдо статическая оперативная память, подключаемая по QuadSPI (можно конечно и по 1 или 2 проводному SPI, но не суть).
В некоторых линейках STM32 если встроенный интерфейс QSPI с возможностью мапинга адресного пространства, правда только на чтение.
Так же интерфейс QSPI в данных микроконтроллерах может работать в сдвоенном режиме, т.е. можно подключить сразу 2 флэшки с параллельной шиной в 8 бит и хранить пол байта на одном чипе, а вторые пол байта на втором.

Но все это Вы можете изучить прочитав мануал, а в моем случае я решил подключить те же самые 2 чипа памяти в параллель по FSMC, просто из-за физического отсутствия интерфейса QSPI в выбранном контроллере STM32F407VETx.

Реализация следующая:
- Подключаем BANK1 FSMC в режиме LCD интерфейса
2023-09-03175411.png.b34f5b806e05aaf1c9aebc25e4e66453.png

- Ножка NE1 - будет использоваться как SCK;
- Ножки NWE, NOE и A просто игнорируем и даже не инициализируем;
- Любую ножку инициализируем на выход для выбора кристалла CS.

И в программе просто обращаемся к фиксированной ячейке памяти по адресу 0x60000000 для записи или чтения данных из памяти.
Та же самая ячейка памяти используется для посылки команд и выбора адреса ячейка памяти.
С ней можно работать как напрямую, так и по DMA

Пример работы с данной памятью

Spoiler
volatile void *const sram = (volatile void*)0x60000000;
static DmaStream dma(DMA2_Stream0, DmaStream::Channel0);
//---------------------------------------------------------------------------
uint32_t initPSRAM64() {
	initFsmc();
	Timer::wait(2_mSec);

	select(); write(u8ToSingleRev(PSRAM64::ResetEnable)); unselect();
	select(); write(u8ToSingleRev(PSRAM64::ResetMemory)); unselect();

	select();
	write(u8ToSingleRev(PSRAM64::ReadId));
	write(uint64_t(0));
	write(uint64_t(0));
	write(uint64_t(0));
	for (size_t i = 0; i < sizeof(id1); ++i) {
		uint64_t buf = read<uint64_t>();
		((uint8_t*)&id1)[i] = singleToU8Rev(buf, 0x02);
		((uint8_t*)&id2)[i] = singleToU8Rev(buf, 0x20);
	}
	unselect();

	if (id1.manufacturer != id2.manufacturer || id1.kgd != id2.kgd)
		return 0;
	if (id1.manufacturer != 0x0D || (id1.kgd != 0x55 && id1.kgd != 0x5D))
		return 0;
	uint32_t size = 8*1024*1024;
	return size;
}
//---------------------------------------------------------------------------
constexpr uint64_t u8ToSingleRev(uint8_t value) {
	return  (((value & 0x80) ?0x11ULL :0x00ULL) <<  0) |
			(((value & 0x40) ?0x11ULL :0x00ULL) <<  8) |
			(((value & 0x20) ?0x11ULL :0x00ULL) << 16) |
			(((value & 0x10) ?0x11ULL :0x00ULL) << 24) |
			(((value & 0x08) ?0x11ULL :0x00ULL) << 32) |
			(((value & 0x04) ?0x11ULL :0x00ULL) << 40) |
			(((value & 0x02) ?0x11ULL :0x00ULL) << 48) |
			(((value & 0x01) ?0x11ULL :0x00ULL) << 56);
}
//---------------------------------------------------------------------------
constexpr uint16_t u8ToQuadRev(uint8_t value) {
	return ((value & 0xF0) >> 0) | ((value & 0xF0) >> 4) | ((value & 0x0F) << 8) | ((value & 0x0F) << 12);
}
//---------------------------------------------------------------------------
constexpr uint8_t singleToU8Rev(uint64_t value, uint8_t mask) {
	return  ((value & (uint64_t(mask) <<  0)) ?0x80 :0) |
			((value & (uint64_t(mask) <<  8)) ?0x40 :0) |
			((value & (uint64_t(mask) << 16)) ?0x20 :0) |
			((value & (uint64_t(mask) << 24)) ?0x10 :0) |
			((value & (uint64_t(mask) << 32)) ?0x08 :0) |
			((value & (uint64_t(mask) << 40)) ?0x04 :0) |
			((value & (uint64_t(mask) << 48)) ?0x02 :0) |
			((value & (uint64_t(mask) << 56)) ?0x01 :0);
}
//---------------------------------------------------------------------------
inline void select() {
	FSMC_CS_Port.resPin(FSMC_CS_Pin);
}
//---------------------------------------------------------------------------
inline void unselect() {
	for (volatile int i = 0; i < 3; ++i);
	FSMC_CS_Port.setPin(FSMC_CS_Pin);
}
//---------------------------------------------------------------------------
template<typename T>
inline void write(T data) {
	*(volatile T*)sram = data;
}
//---------------------------------------------------------------------------
template<typename T>
inline T read() {
	return *(volatile T*)sram;
}
//---------------------------------------------------------------------------
void writeDMA(const void *data, size_t size) {
	dma.init(DmaStream::M2M, DmaStream::VeryHigh, DmaStream::Normal, DmaStream::PInc, DmaStream::MNoInc, DmaStream::P16, DmaStream::M16);
	dma.setMemoryFrom(data);
	dma.setMemoryTo0(sram);
	dma.setCount(size / 2);
	dma.transferCompleteClear();
	dma.enable();
}
//---------------------------------------------------------------------------
void readDMA(void *data, size_t size) {
	dma.init(DmaStream::M2M, DmaStream::VeryHigh, DmaStream::Normal, DmaStream::PNoInc, DmaStream::MInc, DmaStream::P16, DmaStream::M16);
	dma.setMemoryFrom(sram);
	dma.setMemoryTo0(data);
	dma.setCount(size / 2);
	dma.transferCompleteClear();
	dma.enable();
}
//---------------------------------------------------------------------------
inline void flushDMA() {
	while (!dma.transferCompleteIs());
}
//---------------------------------------------------------------------------

В итоге мы получаем 16МБ оперативной памяти (правда без мапинга адресного пространства);
Дешевая стоимость ~300р за пару на Алиэкспресс;
Минимальное место на плате (2 по SO8);
Минимальное количество занятых ножек (всего 10 ног);
Скорость чтения/записи до 133 МБ в секунду.

Из недостатков только отсутствие возможности подключения к адресному пространству, но при работе по QSPI на запись его тоже не будет.

Изменено пользователем haker_fox
Длинный код нужно прятать под спойлер.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

42 минуты назад, Ivan. сказал:

Из недостатков только отсутствие возможности подключения к адресному пространству, но при работе по QSPI на запись его тоже не будет.

"Только"? Да ладно! А то, что стоит как чугунный мост (раз в 10 дороже SDRAM аналогичного объёма)? И работать будет так же раз в 10 медленнее (так как обычная внешняя ОЗУ, отображаемая на адресное пространство, в современных МК как правило может кешироваться, а ваша - нет).

Так себе решение.... За эту цену можно было пару МК со встроенной быстрой ОЗУ купить. И места на плате заняло бы меньше и скорость была бы выше.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

SDRAM занимает много места и половину ног контроллера
И какой это контроллер можно найти с 16 МБ за 500р? (~200р STM32F407 и ~300р PSRAM64)
В третьих мне не нужно кешировать данные, мне нужно собирать поток данных с DCMI и класть его в буфер, а по событию выгружать на SD

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Гусары, молчать! (Но подумали про allwinner t113-s).

 

Вам кадр или несколько секунд видео? Если кадр это не 16 мегабайт. 512k вполне у stm32h7 есть.

Изменено пользователем GenaSPB

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 9/3/2023 at 5:49 PM, Ivan. said:

И какой это контроллер можно найти с 16 МБ за 500р? (~200р STM32F407 и ~300р PSRAM64)

Где вы STM32F407 за 200 руб. берете ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

4 минуты назад, GenaSPB сказал:

Гусары, молчать! (Но подумали про allwinner t113-s).

В том числе, но не только. Есть также старенькие Nuvoton-ы. В коих (если не путаю) = up to 64MB stacked RAM.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

5 минут назад, GenaSPB сказал:

Но подумали про allwinner t113-s

Да тут даже v3s будет более чем)))

3 минуты назад, Ivan. сказал:

AliExpress

Можно не в напряг на подделку нарваться...

8 минут назад, GenaSPB сказал:

Вам кадр или несколько секунд видео?

Мне тоже интересно, что за "видео" ТС решил крутить на СТМ 160МГц )))

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

STM можно и в проме купить, а вот allwinner где брать?
STM можно в любой момент на GD заменить пин2пин

13 минут назад, mantech сказал:

Мне тоже интересно, что за "видео" ТС решил крутить на СТМ 160МГц )))

Видеофиксатор события возникновения пожара. мне не нужно раскодировать поток, мне нужно только зафиксировать.
Требуется зафиксировать 10 секунд до возникновения пожара

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

7 минут назад, Ivan. сказал:

STM можно и в проме купить, а вот allwinner где брать?

Алиэкспресс вроде еще не запрещен)) По крайне мере мои заказчики трудностей не испытывают (покупаем модули на v3s и Т113).

8 минут назад, Ivan. сказал:

Требуется зафиксировать 10 секунд до возникновения пожара

Ну для СТМ это уже "подвиг" с каким разрешением и сколь кадров будет за эти 10 сек?

10 минут назад, Ivan. сказал:

в любой момент на GD заменить

Вероятность, что GD тоже пропадет выше, чем у аллвиннера.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

14 минут назад, mantech сказал:

Ну для СТМ это уже "подвиг" с каким разрешением и сколь кадров будет за эти 10 сек?

И в чем же подвиг прокачать 30 кадров/с HD качества в JPEG-е? Камера уже шлет кадры в JPEG в среднем 50kB на кадр

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Основной задачей в данном проекте планируется FFT, а камера так, довесок, практически не занимающий ресурсов контроллера. Все на DMA

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

14 часов назад, Ivan. сказал:

HD качества в JPEG-е

 

14 часов назад, Ivan. сказал:

в среднем 50kB на кадр

Или у вас JPEG какой-то волшебный, детализации там никакой или все на одном фоне,  или качество там будет, ммм...вообщем)))

Ну вам виднее, если действительно 50к\кадр хватает, то СТМ с таким конечно справится...

Изменено пользователем mantech

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Пока проблем с захватом видео не возникло, нагрузка на процессор составляет около 5% (могу немного приврать, т.к. сейчас нет под рукой картинки с анализатора), это с учетом того, что в момент выгрузки в оперативку программа приостанавливается. Это я испытывал на разрешении 1600x1200, 15 кадров в секунду. Если перевести на прерывания - освободится еще уйма времени вплоть до <1%
Структура следующая:
- По DMA с DCMI поступают данные. DMA настроен с двойным буфером по 1КБ;
- По заполнению одного из буфером происходит прерывание и начинается выгрузка во внешнюю оперативку (в данный момент по одному QUAD SPI, потом будет 2 QUAD SPI в параллель, а точнее по FMC с 8-битной шиной как было задумано);
- В это время заполняется второй буфер; и так по циклу, пока не произойдет прерывание окончания кадра;
- По окончанию приема кадра он фиксируется в псевдо файловой системе для дальнейшего анализа;
- Далее по какому то условию любой кадр может быть заблокирован от удаления, чтобы он не затерся в циклическом буфере;

Сжатие в MPEG или другие форматы не планируется. Видеофайл будет сохраняться в формате MJPEG. т.е. покадровое сжатие, что уже выполнено самой камерой. Кодирование/декодирование не нужно.

Вы будете смеяться, но в данный проект на STM32F407 планируется впихнуть еще много чего, и видео захват - это мелочь.
- Планируется поднять TCP и транслировать видеопоток;
- По событию обнаружения пожара выгрузка видеофайла на SD карту в формате MJPEG;
- Modbus;
- HART;
- Ну и непосредственно сам анализ пламени на основе FFT с 3(4) ИК/УФ сенсоров.

Сейчас я игрался с камерой OV2640 и качество картинки с нее, мягко говоря, очень плохенькое. Заказал OV5640, посмотрим какая у нее будет картинка.

У меня сейчас появилась другая проблема: Свою библиотеку работы с SD картой мне писать было не охота и я воспользовался CUBE-вским примером. Так вот создание файла размером в 100КБ у меня заняло около 114мс

Размер кадра с OV2640 с разрешение 1600*1200 составляет около 120КБ, в разрешении 320*240 ~6КБ.
Картинку с разрешение 320*240 удается передать на ПЛК по Modbus (RS-485 57600 baud) примерно за 2 секунды, что позволит оператору принять решение пожаротушения

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...