STM32. Контроллер внешней параллельной памяти FSMC

У микроконтроллеров собственной памяти мало, даже если говорить о каком-нибудь жирном прежирном Corteх, все равно: как волка ни корми, а у медведя, т.е. полноценного компьютера, толще. Поэтому практически все микроконтроллеры, в своем жирном исполнении так или иначе позволяют подцеплять к себе внешнюю параллельную память. Даже древний, как говно мамонта, АТ89С51 это умел. Что уж говорить про AVR и STM32.

▌Параллельная память

Для такой памяти характерны две черты: наличие двух шин: адреса и данных, и разных там стробов. На чтение, запись, тактовых и прочих. А вся суть работы с ними предельно простая. Чтение — мы выставляем на ногах адресной шины (коих обычно от 16 до 32) адрес нужной ячейки, отдельной ногой указываем, что у нас чтение, дергаем стробом и на шине данных (8 или 16 бит, обычно) появляется желанные байты. Запись похожим образом, только тут на шину данных мы выкладываем то, что хотим записать, на адресную шину кладем адрес куда надо записать, расставляем контрольную линию в режим записи и дергаем стробом. Опа, данные в памяти. Поскольку тут не требуется совершать сложных логических действий, то это все работает очень быстро, а реализовать можно даже на логике рассыпной. Естественно, что в разных МК такие интерфейсы были всегда.

▌FSMC
В STM32 за работу с внешней памятью отвечает такой блок как FSMC — Flexible Static Memory Controller. Который позволяет подключать к МК множество разных типов памяти (SRAM, NAND Flash, NOR Flash, PSRAM, PC Card и практически все что угодно, что имеет интерфейс схожий с i8080 или MC6800, например, LCD дисплей, с параллельной шиной. Причем автоматически будут переключаться банки памяти, если таковые нужны.

Со стороны софта же обращение к внешней памяти выглядит совершенно нативным образом, у STM32 огромное адресное пространство и неслабый кусок его выделен под работу с внешней памятью — адреса с 0х60000000 по 0x9FFFFFFF. Мы просто настраиваем контроллер и пишем в нужный сегмент этого адресного пространства. Как будто это наша собственная оперативная память. Банально, через указатели. А контроллер сам развернет это во внешний мир и запишет-считает из внешней микросхемы памяти.

Фактически же ,контроллера там два. Один отвечает за работу с типом NOR/PSRAM, другой с NAND/PC Card у этих интерфейсов несколько различается логика работы, но они довольно похожи. При этом у них те сигналы, что совпадают для каждого типа тут общие, ну там шина данных, шина адреса, линии Write-Read, а какие то специфичные линии для каждого типа памяти раздельно свои. Что позволяет одновременно цепляться на одну шину разные девайсы и они могут работать вместе, разумеется попеременно.

Под каждый тип памяти свои адреса. При этом внутри они еще делятся на четыре банки, каждой из которой выделяется своя нога Chip Enable снаружи, а также свои настройки скорости и таймингов. Т.е. можно вообще сделать всратую солянку из разных микросхем, сконфигурировать и это будет работать.

Например, контроллер NOR/PSRAM может адресовать четыре микросхемы памяти по 64 мегабайта каждая. Они будут сидеть параллельно на шине данных и шине адреса, а также линиях строба чтения и записи. Но к каждой будет еще идти индивидуальная линия Chip Enable, от ног контроллера FMSC зовутся они NE1…NE4. (т.е. Not Enable1.. Жутко меня вымораживает эта нотация, писать инверсное состояния бита буквой N перед именем. Ну хоть не заглавной бы писали, как это микрочип делает, nE1 всяко читабельней чем NE1. А лучше надчеркиванием, как это делал всегда ATMEL, бесит!)

При этом уровень на ногах NE1…NE4 не нужно ставить вручную, он зависит от адреса банки в которую мы пишем, и ставится автоматически. А конкретней, зависит от битов адреса [27:26]. Т.е. изнутри нам эти четыре микросхемы будут выглядеть как единое, плоское адресное пространство, мы в него пишем/читаем свободно, а контроллер сам подключает нужную микросхему. При этом тайминги настраиваются для каждой микросхемы отдельно! То есть они могут быть разными. Красота же!

Выводы той части контроллера, что NOR/PSRAM там в даташите расписаны прям отдельно.

▌Ошибки
Также этот контроллер может генерировать ошибки, которые приводят к исключению Hard Fault.

Ошибки возникают в следующих случаях:

  • Когда мы пишем или читаем из адресного пространства FSMC, но оно не включено. Т.е. мы пытаемся обращаться в пустоту. Это, кстати, характерно не только для FSMC, но и для многих других блоков.
  • Когда мы обращаемся к NOR Flash памяти, но бит FACCEN регистра FSMC_BCRx для конкретного банка сброшен. Напомню, что банок там четыре.
  • Когда мы обращаемся к PC Card, а пин ее физического наличия не выставлен.

И на этот раздел прошу обратить особое внимание, т.к. можно просто забыть где-то поставить битик, или подтянуть ножку, а потом долго пытаться понять какого хрена у нас все крашится. Если же стучаться туда пытается DMA, то это приводит к тому, что DMA канал автоматически отключается. Так что если у вас вдруг стал не с того ни с сего выключаться DMA проверьте адреса и наличие получателя на том конце.

▌Пример
Вообще работать с этим контроллером довольно просто. Главное не запутаться в множестве ног. Подключать будем дисплей на контроллере RA8875L3N. Стандартная довольно шняга, дисплей 800×480, 7 дюймов диагональ. С тачскрином. Умеет и режим шина типа i8080 и M68, а еще spi и i2c. В общем, навороченная зверюшка. Нам же нужен i8080. По такому же протоколу работает и дисплей на SSD1963, разница там может быть разве что в командах инициализации.

В интернете полно примеров подключения этой шняги, но везде все сводится к тому, что а давайте мы запустим куб, натыкаем галочек, впишем сюда вот эти магические цифры и у нас будет магия. Порочная практика, из-за нее хер найдешь теперь нормальный пример с подробностями, все засрано этим кубом. Который еще и генерит лютую кашу в виде проекта, нет бы давал снипеты для копипаста ,то было бы куда ни шло. А так… БУЭЭЭЭЭ…
Нахер магию, нахер куб, нахер HAL и даже SPL, подключать будем по даташиту с подробным описанием что, откуда, ручками, на регистрах. Тем более подключать то там нечего.

Выводы? С выводами все довольно просто, ищем спецификацию интерфейса и узнаем, что у нас тут есть:

  • D0…D15 — шина данных.
  • CS — Активация чипа ака Chip Select
  • RS — Данные/команда. Определяет что будет на линии данных, команда или данные изображения
  • RST — Сброс контроллера дисплея
  • WR — Строб записи. Выставляем на линию данных байт и дергаем этой ногой для записи в дисплей
  • RD — Строб чтения. Дергаем этой ногой и дисплей выдает байт на шину данных, который мы потом читаем
  • INT, TINT — выходы прерывания. Через него дисплей может пнуть наш контроллер на предмет внешних событий, например нажатие на тачскрин. В данном случае не нужен.

Так как у нас интерфейс i8080, то подключаться мы будем как NOR/PSRAM устройство к соответствующему контроллеру.

Подключается это все следующим образом.

  • D0… D15 — FMSC_D0…D15. Шина данных, тут все просто, линия к линии. Можно и по 8 битному формату подключить. дисплей такое вроде поддерживает, но обновляться картинка будет ну очень уж медленно. Считай три цвета надо будет протащить за три операции записи. А тут все в одной пролезет.
  • RS — FMSC_Ax Адресной шины у нас нет, дисплей подключается не как микросхема памяти (а жаль, было бы круто). Зато надо переключать режим данные/команда. Лучше всего для этого применить один из адресных выводов шины. И тогда получится, что для записи команды нам надо будет закинуть их по адресу Х, а для записи данных по адресу Х+1 и все.

    Но тут есть одно очень и очень неоднозначное западло.
    Дело в том, что внешняя адресация FMSC зависит от разрядности шины данных (RM00088 стр. 511 табл. 101). Если у нас разрядность шины данных составляет 8 бит, то все нормально, адрес в адресном пространстве равен адресу на линиях данных. Т.е. если банк начинается с адреса 0x60000000, то при записи в 0x60000000 А0=0, а запись в 0x60000001 сделает линию А0 = 1. Т.е. младший бит адресной шины соответствует младшему биту адреса.

    Но если мы переводим шину в 16 разрядный режим, то у нас включается внешняя адресация в словах.

    Выглядит это как сдвиг адреса вправо перед тем как он попадет на шину адреса Ах. Т.е. адрес делится на два, оно и логично, данные то у нас уже словами пошли. И тогда для того, чтобы поднять А0 надо записать не по адресу 0x60000001 (это будет всего лишь адрес второго байта первого слова, что был на шине данных при адресе 0x60000000), а по адресу следующего слова, т.е. 0x60000002 и тогда у нас выйдет бит на А0. То же касается, естественно, и других линий А0 в 16 разрядном режиме.

    Запомните этот момент. Т.к. однажды, решив подключить дисплей по 8 битной шине, ну чтобы ноги сэкономить, вы лихо сломете мозги себе, пытаясь понять почему работающий код перестал работать.

  • CS — FMSC_NEx Выбор чипа, ака Chip Select берется от той банки с которой удобней работать или разводить плату. Разница лишь в адресе куда мы будем писать/читать данные и по которому мы будем дергать линию Ах. Т.е. если у нас для дрыга RS взята А0 и надо вычислить адрес куда же нам валить данные, то пляшем от ноги NE. За NE1 отвечает банк 1, с началом в 0x60000000, а за ногу NE2 уже банк 2, с началом в 0x64000000 и соответственно у вас будет адрес для данных 0x64000002. Аналогично для банка 3 и 4. Можно выбрать любой, единственное, что это не всегда возможно, но об этом будет ниже.
  • RD — FMSC_NOE Линия строба чтения идет на Output Enable (почему его переименовали с RE или RD вдруг я хз, неужто патентные войны, как было с i2c когда то?)
  • WR — FMSC NWE Линия строба записи. Идет на Write Enable
  • INT — GPIO Линии сброса дисплея, а также линии прерываний от дисплея и тача идут на любые удобные порты GPIO тут вообще без разницы.
  • TINT — GPIO
  • RST — GPIO

Что ж, попробуем все это привести ближе к теме. Контроллером будет STM32F103VGT6, у меня он просто есть уже разведенный, на готовой плате, взял его с платы одного своего текущего проекта. У него 100-ногий корпус, а в этом случае у нас FMSC сильно кастрированный. У него нет полноценной адресной шины, от линий адреса А0…А25 остались только жалкие огрызки — адресные линии А16…А23. Ну и, соответственно, линия NE выведена только у одного банка, у первого. Okay :(

Настроим GPIO, у меня используется самописная настройка портов в виде матрицы. Про нее я уже писал как то раз. Так что вся настройка портов в одном месте и выглядит так:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const tGPIO_Line IOs[] = 	{
                                { GPIOB, 0,  OUT_10MHz + OUT_PP, LOW},  	// Reset
                                { GPIOA, 7,  IN + IN_PULL, HIGH},  	    	// Int Line
				{ GPIOD, 0, OUT_50MHz + OUT_APP, LOW},        	//
				{ GPIOD, 1, OUT_50MHz + OUT_APP, LOW},        	// 
				{ GPIOD, 4, OUT_50MHz + OUT_APP, LOW},        	// RD - NOE
				{ GPIOD, 5, OUT_50MHz + OUT_APP, LOW},        	// WR - NWE
				{ GPIOD, 7, OUT_50MHz + OUT_APP, LOW},        	// CS -NE1
				{ GPIOD, 8, OUT_50MHz + OUT_APP, LOW},        	// 
				{ GPIOD, 9, OUT_50MHz + OUT_APP, LOW},        	// 
				{ GPIOD, 10, OUT_50MHz + OUT_APP, LOW},       	//
				{ GPIOD, 11, OUT_50MHz + OUT_APP, LOW},       	// RS - A16
				{ GPIOD, 14, OUT_50MHz + OUT_APP, LOW},       	//
				{ GPIOD, 15, OUT_50MHz + OUT_APP, LOW},       	//
				{ GPIOE, 7, OUT_50MHz + OUT_APP, LOW},        	// 
				{ GPIOE, 8, OUT_50MHz + OUT_APP, LOW},        	// 
				{ GPIOE, 9, OUT_50MHz + OUT_APP, LOW},        	// 
				{ GPIOE, 10, OUT_50MHz + OUT_APP, LOW},       	// 
				{ GPIOE, 11, OUT_50MHz + OUT_APP, LOW},       	// 
                                { GPIOE, 12, OUT_50MHz + OUT_APP, LOW},       	// 
				{ GPIOE, 13, OUT_50MHz + OUT_APP, LOW},       	// 
                                { GPIOE, 14, OUT_50MHz + OUT_APP, LOW},       	// 
                                { GPIOE, 15, OUT_50MHz + OUT_APP, LOW}        	// 
                                                                  };

Тут все довльно просто, сразу указано как настроена нога, поясню только что что OUT_APP, означает режим альтернативной функции (AFIO PushPull), с подтяжкой в LOW. Настраивайте это как хотите у себя, хоть через HAL, хоть через spl, хоть LL.

Также, надо настроить тактирование периферии:

1
2
3
4
5
6
7
8
9
10
11
12
13
RCC->APB2ENR	|= RCC_APB2ENR_IOPAEN;	// Тактирование портов
RCC->APB2ENR 	|= RCC_APB2ENR_IOPBEN;
//RCC->APB2ENR	|= RCC_APB2ENR_IOPCEN;
RCC->APB2ENR	|= RCC_APB2ENR_IOPDEN;
RCC->APB2ENR	|= RCC_APB2ENR_IOPEEN;
//RCC->APB2ENR	|= RCC_APB2ENR_IOPFEN;
//RCC->APB2ENR	|= RCC_APB2ENR_IOPGEN;
RCC->APB2ENR	|= RCC_APB2ENR_AFIOEN;	// Включаем AFIO
RCC->AHBENR     |= RCC_AHBENR_FSMCEN;	// Включаем FSMC
 
 
AFIO->MAPR &=~AFIO_MAPR_SWJ_CFG_Msk;		// Делаем ремап линий JTAG, точнее включаем JTAG, оставляем только SWD
AFIO->MAPR |= AFIO_MAPR_SWJ_CFG_JTAGDISABLE;	// Иначе он попадает на ноги.

Теперь определяем наш базовый адрес. Для удобства сведено все в структуру. Сидеть все будет так, CS на NE1, нога RS на А16

1
2
3
4
5
6
7
8
9
typedef struct
{                             		// Структура адресуется по порядку. Базовый адрес в начале. 
	volatile uint16_t LCD_RAM;   	// 0x6001FFFE  >> 1 = 0x0000FFFF bit 16 = 0 т.е. DATA
	volatile uint16_t LCD_REG;   	// 0x6001FFFE + 2 (т.к. uint16) = 0x60020000 >> 1 = 0x00010000 bit 16 = 1 т.е. CMD
} LCD_TypeDef;
 
/* LCD is connected to the FSMC_Bank1 and NE1 is used as ship select signal, A16 use as RS */
#define LCD_BASE    ((uint32_t)(0x60000000 | 0x0001fffE))
#define LCD         ((LCD_TypeDef *) LCD_BASE)

Я себе вот такой вот кусочек заготовил и если надо копирую его себе и правлю под нужную задачу. Никаких структур, никаких левых функций. Все вот, на виду. Заодно добавил в комменты ряд выдержек из даташита.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#define FSMC_Bank1_NORSRAM1	((uint32_t)0x00000000)
#define FSMC_Bank1_NORSRAM2	((uint32_t)0x00000002)
#define FSMC_Bank1_NORSRAM3	((uint32_t)0x00000004)
#define FSMC_Bank1_NORSRAM4	((uint32_t)0x00000006)
#define FSMC_Bank2_NAND		((uint32_t)0x00000010)
#define FSMC_Bank3_NAND		((uint32_t)0x00000100)
#define FSMC_Bank4_PCCARD	((uint32_t)0x00001000)
 
    FSMC_Bank1E->BWTR[FSMC_Bank1_NORSRAM1] = 0x0FFFFFFF;
 
                                                                                 // BCR&BTR Register see RM000008 p. 541
    FSMC_Bank1->BTCR[FSMC_Bank1_NORSRAM1] = 
			     		0 << FSMC_BCRx_CBURSTRW_Pos  |       // write 0 - async 1 - sycnc
                                        0 << FSMC_BCRx_ASYNCWAIT_Pos |       // Wait signal during asynchronous transfers
                                        0 << FSMC_BCRx_EXTMOD_Pos    |       // Extended mode enable. Use BWTR register or no
                                        0 << FSMC_BCRx_WAITEN_Pos    |       // Wait enable bit.
                                        1 << FSMC_BCRx_WREN_Pos      |       // Write enable bit
                                        0 << FSMC_BCRx_WAITCFG_Pos   |       // Wait timing configuration. 0: NWAIT signal is active one data cycle before wait state 1: NWAIT signal is active during wait state
                                        0 << FSMC_BCRx_WRAPMOD_Pos   |       // Wrapped burst mode support
                                        0 << FSMC_BCRx_WAITPOL_Pos   |       // Wait signal polarity bit. 0: NWAIT active low. 1: NWAIT active high
                                        0 << FSMC_BCRx_BURSTEN_Pos   |       // Burst enable bit
                                        1 << FSMC_BCRx_FACCEN_Pos    |       // Flash access enable
                                        1 << FSMC_BCRx_MWID_Pos      |       // 0 = 8b 1 = 16b
                                        2 << FSMC_BCRx_MTYP_Pos      |       // 0 = SRAM 1 = CRAM 2 = NOR
                                        0 << FSMC_BCRx_MUXEN_Pos     |       // Multiplexing Address/Data
                                        1 << FSMC_BCRx_MBKEN_Pos;            // Memory bank enable bit
 
 
 
    FSMC_Bank1->BTCR[FSMC_Bank1_NORSRAM1 + 1] = 
                                        0 << FSMC_BTRx_ADDSET_Pos  |    // Address setup phase duration 0..F * HCLK
                                        0 << FSMC_BTRx_ADDHLD_Pos  |    // Address-hold phase duration 1..F * 2 * HCLK
                                        1 << FSMC_BTRx_DATAST_Pos  |    // Data-phase duration 1..FF * 2 * HCLK
                                        0 << FSMC_BTRx_BUSTURN_Pos |    // Bus turnaround phase duration 0...F
                                        1 << FSMC_BTRx_CLKDIV_Pos  |    // for FSMC_CLK signal 1 = HCLK/2, 2 = HCLK/3 ...  F= HCLK/16
                                        0 << FSMC_BTRx_DATLAT_Pos  |    // Data latency for synchronous NOR Flash memory 0(2CK)...F(17CK)
                                        0 << FSMC_BTRx_ACCMOD_Pos;      // Access mode 0 = A, 1 = B, 2 = C, 3 = D Use w/EXTMOD bit

Теперь немного поясню. Т.к. контроллер у нас NORSRAM используется, то настраивать и включать будем его. Банк у нас 1, без вариантов. В CMSIS все это определено как то коряво, как массив, где регистры FSMC_BTR и FSMC_ BCR никак не разделены, а слиты в кучу, словно они поленились. Приходится их индексным методом доставать, благо они парами идут в памяти, друг за другом. поэтому для BCR1 адрес от базового FSMC_Bank1_NORSRAM1, а для BTR адрес FSMC_Bank1_NORSRAM1+1.

Дальше там биты, сделал как в старом добром AVR, я так люблю :))))

Начинаем настраивать. Сначала определимся в каком режиме все должно работать. Смотрим времянки на интерфейс и на то, что контроллер нам предлагает. В нашем случае это NOR Flash или Mode 2.

Смотрим табличку 114 в RM00008

Определиться надо всего с парой бит. Все остальное или предрешено или не имеет значения :)

  • ASYNCWAIT будет ли использоваться вход для сигнала занятности памяти. В нашем случае нет, у нас на дисплее есть нога WAIT, но она более выскоуровневая. Так что там 0.
  • EXTMOD — расширенный режим. Если включить его, то мы можем задавать раздельные тайминги как для чтения, так и для записи. В этом случае регистр BWTR[FSMC_Bank1_NORSRAM1] будет содержать тайминги на запись, а обычный FSMC_BTR тайминги на чтение. Если же бит расширенного режима выключен, то FSMC_BTR содержит тайминги для всех вариантов данного банка.
  • WREN — мы будем писать в дисплей, так что конечно этот бит нужен. Он включает ногу строба NWE.
  • MWID — определяет ширину шины данных. Я решил применить 16 бит, так быстрей. Но можно сделать и 8 бит — сэкономите ноги, но помните про сдвиг адресации!!!

Теперь надо определиться с таймингами записи и чтения. У нас за них отвечает регистр FSMC_BTR1. И нам в Mode 2 доступны только две настройки:


Смотрим табличку 115 в RM00008

  • BUSTURN нам не нужен, это для пакетной записи в PSRAM.
  • DATAST время за которое данные устаканятся на шине данных и дисплей готов будет их считать.
  • ADDSET время за которое установится и будет воспринят адрес на адресной шине. Т.к. у нас адресная шина рулит линией RS, то надо смотреть время от RS до готовности считать данные.

Смотрим даташит на дисплей:


Картинку конечно рисовал наркоман. Все в кашу. У них там чтоль вообще нормоконтроля нет? Наша Геннадьевна бы из за такой чертеж выдрала без вазелина шваброй.

Время от активации чипа через CS до готовности ловить строб совпадает со временем установки RS, его тут назвали А0, видимо подразумевая, что эта линия всегда будет на адресной линии А0. Говорю же, наркоманы. Это время tAS8 и табличка гласит, что оно может быть от 0. Т.е. на запись ADDSET можно смело ставить ноль.

А вот время от начала движухи на линии DB0..7 и до готовности, на перегибе строба WR обозначено как tDS8 И оно у нас тут 10ns для записи.

Для чтения же время от того как у нас упадет линия строба RD, и тем самым даст понять дисплею, что с него хотят читать равно tACC8 и составляет от 0 до 20ns. Берем по максимуму 20ns.

Чтобы не заморачиваться с настройкой разных таймингов для чтения и записи через EXTMOD примем их по максимальному значению 20ns. А теперь давайте думать сколько записать в DATAST. Из графика 191 узнаем, что длина этих циклов это DATAST+1 тик HCLK

FSMC тактуется от шины HCLK, которая сидит после предделителя AHB. У меня частота настраивается вручную и я точно знаю, что у меня там 72МГц. Вам же этот вопрос надо будет выяснить. А раз так, то один тик HCLK = 1/72000000 = 1.4Е-8 т.е. около 14ns т.е. уже больше тайминга. А нам надо 20ns. Т.е. в DATAST запишем 1, будет даже немного с запасом.

Теперь код инициализации FSMC будет предельно понятен.

Осталось написать функции записи команды и данных. МЫ просто берем ранее сделанную структуру с адресами и просто пишем туда данные:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void LCD_CmdWrite(const uint16_t Command)
{
	LCD->LCD_REG = Command;
}
 
void LCD_DataWrite(const uint16_t Data)
{
	LCD->LCD_RAM = Data;
}
 
void LCD_WriteReg(const uint8_t LCD_Reg, const uint16_t LCD_RegValue)
{
	LCD->LCD_REG = LCD_Reg;
	LCD->LCD_RAM = LCD_RegValue;
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
	IO_SetLine(io_TFT_Reset, HIGH);
	delay_ms(5);
 
	LCD_WriteReg(0x01, 0x01); //PWRR: LCD on, sleep off, reset ON
 
	delay_ms(1);
 
	LCD_WriteReg(0x01, 0x00); //PWRR: LCD on, sleep off, reset OFF
 
	delay_ms(1);
 
	LCD_WriteReg(0x88, 0x0B); //PLL Control Register 1
 
	delay_ms(1);
 
	LCD_WriteReg(0x89, 0x02); //PLL Cntrol Register 2
 
 бла бла бла

Инициализация самого дисплея и работа с ним требует отдельной статьи. Там целый мир. Адреса, встроенные функции рисования графики, разные режимы цветов… ой дофига всего. Я ее тоже планирую написать, но по нему примеров масса, даташит подробный, бери да гони адреса в память, а FSMC вам в помощь.

Собранный проект на FreeRTOS который ничего не делает, только инициализирует дисплей и заливает его туда сюда разными цветами прилагаю. Там все проще некуда.

26 thoughts on “STM32. Контроллер внешней параллельной памяти FSMC”

  1. Там точно volaitle нигде не пропущен для обращения к памяти прямо по адресу? Ну, так, на всякий случай. А то уоптимизирует компилятор что-нибудь.

    По совести, надо и atomic, конечно, раз по 16 бит пишем.

  2. Вопрос дилетанта: а получится подвесить на эту шину сразу память и дисплей? И по DMA гонять картинку из внешней памяти на него?

    1. Только если через два канала. Т.е. один канал ДМА читает картинку из внешней памяти в ячейку озу, второй канал ДМА оттуда перекидывает в дисплей. Так должно сработать.

  3. Ди, ну где же ты раньше был?? :)
    У меня уже практически законченное устройство с кучей периферии,
    для TFT написан чумовой ногодрыг чтобы получить 20 FPS на 320*480.
    И вот вам… теперь переделывать всё. :)

    (сегодня уже спаял разъемчик, наговнякал тестовую прошивку — офигенная вещь!)

  4. Наконец то кто то сказал, что HAL ЗЛО. Все советы по STM32 теперь сводятся к тыкаем мышкой в Cube тут и тут вуаля у нас I2S с DMA но хрен знает как оно работает. Искал нормальные примеры по I2S с DMA на регистрах кроме англоязычных форумов НИЧЕГО нет, в русскоязычном сегменте все фанаты HAL. Возьму и этот пример с FSMC в копилку, GPIO уже давно ваш использую, только не на структурах, а для каждого пина отдельная функция (получилось много быстрее чем SPL, плюс дефайны для STM32F1,F2\F0,F3,F4) Спасибо DI HALT человеческое!

    1. Хал не то чтобы зло. Это замена спл, но более общая и жирная. Если использовать ее без куба, то почему бы и нет? Проблема ее в том, что хал тащит за собой тонну говна там где это не нужно. Захочешь подключить fsmc она затребует отдать ей систик, подтянуть рцц и еще гпио в придачу, а те еще какую срань. И зачем мне все это тут надо?

  5. «Нахер магию…» — проманифестировал автор и…
    » LCD_WriteReg(0x01, 0x01); //PWRR: LCD on, sleep off, reset ON
    delay_ms(1);
    LCD_WriteReg(0x01, 0x00); //PWRR: LCD on, sleep off, reset OFF
    delay_ms(1);
    LCD_WriteReg(0x88, 0x0B); //PLL Control Register 1
    delay_ms(1);
    »
    наструячил собственных магических чисел ;-)
    «Свой перфоратор благозвучнее Домского оргАна» — народная строительная мудрость…

    1. Это относится уже к дисплею. Я на него не писал либ и использую копипасту инициализации из даташита. Потом, как нибудь, напишу и для него нормальное апи

  6. Здравствуйте,

    Подскажите пожалуйста, а Вы случаем не планируете, как по AVR-ам небольшой курсик ассемблера в STM32 ?

    1. И??? Ну оно будет работать. А че нет то если она больше ничего не делает. А попробуй сделать полноценный проект с реалтаймом и зависимостями всего от всего. Да еще потом отладить все это. Все эти кубы, ардуины, фреймворки с либами под все на свете прекрасно работают на простых типовых задачах, но превращают все в магию. А у магии есть проблема — если она не работает, то это все, она не работает.

      1. Ди, ты не понимаешь. Я _отлично_ умею во все эти регистры, прерывания, асинхронности и прочую битовую магию. Даже побольше многих тут взятых. Да, с зависимостями всего от всего.

        Но. Вот конкретно сейчас.
        Я хотел попробовать то, что ты написал — FSMC. Плюс от меня dma, sdio, fatfs. Сколько бы километров инициализации пришлось написать? Вот то-то.

        >А попробуй сделать полноценный проект с реалтаймом и зависимостями всего от всего. Да еще потом отладить все это.

        Так есть полноценные проекты с реалтаймом и хуевой тучей периферии и зависимостей. Работают и не чихают. Посты с отчетами надо пилить? :-)

        А вобще-то мой камент был благодарностью за пост. Как кто то сказал на форуме — «STM32 это не про программирование, это про периферию»

  7. Насчёт «даже» наличия у 51 наверное стоит изменить на «раньше все микроконтроллеры умели, а потом разучились» и стала эта плюшка исключительно элитным наворотом и роскошью =)
    КМК среди PIC-ов (vs AVR/Cortex) гораздо больше моделей с аналогичным контроллером параллельного интерфейса, особенно начиная с PIC24, где можно встретить вариации 8/16-битности шин. В мелких и ногастых pic16/18 почти всегда есть параллельный слейв на портах D+E и иногда мастер.

    А вот еще одна тема — в плане нехватки памяти, можно же использовать SPI чипы RAM/ROM снаружи. И при наличии в Си-компиляторе костылей по встраиванию внешней памяти в пространство программы, создание переменных/массивов/структур в памяти (с особым атрибутом), выглядит весьма заманчиво.

  8. Здрасьте. Не нашёл, куда положено писать об этом, потому пишу в свежую тему.
    Мне очень хочется приобрести ещё одну Pinboard II R3, потому что в условиях кризиса коллеги заинтересовались новыми знаниями. Вижу, что в онлайн-магазине товара нет в наличии. Также нет информации: как давно нет в наличии, и самое главное — будет ли? Это временные трудности или трудные времена?
    Не томите, объясните!

    1. Снято с производства. Нерентабельно. Так что когда будет не знаю. Последние три вот продал на днях. Одна точнее еще есть, но пока в резерве.

Добавить комментарий для Denis Отменить ответ

Ваш e-mail не будет опубликован.

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.