Модуль FSMC (Flexible static memory controller) предназначен для подключения внешних устройств с параллельным интерфейсом (оперативная память, дисплеи, NAND, NOR Flash и т. д.). Для этого у него есть 26 линий адреса, 16 линий данных и линии управляющих сигналов (запись, чтение, выбор кристалла и т. д.).
Под модуль зарезервировано 1 ГБ в адресном пространстве МК (на рисунке выделено красной линией).
Доступная помять разделена на 4 банка по 256 МБ в каждом. При этом каждый банк разделен на 4 части по 64 МБ в каждой (линий адреса 26 и больше 64 МБ непосредственно адресовать не получится). Для доступа к частям банка есть специальные линии выбора кристалла NE1 - NE4. У каждого банка памяти свое назначение. Например 1 банк предназначен для NOR и SRAM памяти. Второй и третий для NAND памяти и т. д.
То есть при необходимости подключить оперативную память или дисплей следует выбирать банк 1. У него следующее разделение адресного пространства.
0x60000000 - 0x63FFFFFF - NE1 0x64000000 - 0x67FFFFFF - NE2 0x68000000 - 0x6BFFFFFF - NE3 0x6C000000 - 0x6FFFFFFF - NE4
Подключим память. Чтобы не изготавливать плату, можно купить китайскую с микроконтроллером STM32F407ZET6 или STM32F407ZGT6.
У нее на обратной стороне есть место для монтажа микросхемы статической оперативной памяти IS62WV51216BLL емкостью 1 МБ.
Кроме микросхемы, нужно припаять резистор R29 с сопротивлением 10К.
Схема подключения памяти к микроконтроллеру.
Теперь рассмотрим программную часть. Настройка FSMC.
void FSMC_Init(void)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOE |
RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOG, ENABLE);
{
GPIO_InitTypeDef gpio;
gpio.GPIO_Mode = GPIO_Mode_AF;
gpio.GPIO_OType = GPIO_OType_PP;
gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio.GPIO_Speed = GPIO_Speed_100MHz;
gpio.GPIO_Pin = (GPIO_Pin_All) & ~(GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_6 | GPIO_Pin_7);
GPIO_Init(GPIOD, &gpio);
gpio.GPIO_Pin = (GPIO_Pin_All) & ~(GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6);
GPIO_Init(GPIOE, &gpio);
gpio.GPIO_Pin = (GPIO_Pin_All) & ~(GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11);
GPIO_Init(GPIOF, &gpio);
gpio.GPIO_Pin = (GPIO_Pin_All) & ~(GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15);
GPIO_Init(GPIOG, &gpio);
}
GPIO_PinAFConfig(GPIOD, GPIO_PinSource0, GPIO_AF_FSMC); // D2
GPIO_PinAFConfig(GPIOD, GPIO_PinSource1, GPIO_AF_FSMC); // D3
GPIO_PinAFConfig(GPIOD, GPIO_PinSource4, GPIO_AF_FSMC); // NOE
GPIO_PinAFConfig(GPIOD, GPIO_PinSource5, GPIO_AF_FSMC); // NWE
GPIO_PinAFConfig(GPIOD, GPIO_PinSource8, GPIO_AF_FSMC); // D13
GPIO_PinAFConfig(GPIOD, GPIO_PinSource9, GPIO_AF_FSMC); // D14
GPIO_PinAFConfig(GPIOD, GPIO_PinSource10, GPIO_AF_FSMC); // D15
GPIO_PinAFConfig(GPIOD, GPIO_PinSource11, GPIO_AF_FSMC); // A16
GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF_FSMC); // A17
GPIO_PinAFConfig(GPIOD, GPIO_PinSource13, GPIO_AF_FSMC); // A18
GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_FSMC); // D0
GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_FSMC); // D1
GPIO_PinAFConfig(GPIOE, GPIO_PinSource0, GPIO_AF_FSMC); // NBLO
GPIO_PinAFConfig(GPIOE, GPIO_PinSource1, GPIO_AF_FSMC); // NBL1
GPIO_PinAFConfig(GPIOE, GPIO_PinSource7, GPIO_AF_FSMC); // D4
GPIO_PinAFConfig(GPIOE, GPIO_PinSource8, GPIO_AF_FSMC); // D5
GPIO_PinAFConfig(GPIOE, GPIO_PinSource9, GPIO_AF_FSMC); // D6
GPIO_PinAFConfig(GPIOE, GPIO_PinSource10, GPIO_AF_FSMC); // D7
GPIO_PinAFConfig(GPIOE, GPIO_PinSource11, GPIO_AF_FSMC); // D8
GPIO_PinAFConfig(GPIOE, GPIO_PinSource12, GPIO_AF_FSMC); // D9
GPIO_PinAFConfig(GPIOE, GPIO_PinSource13, GPIO_AF_FSMC); // D10
GPIO_PinAFConfig(GPIOE, GPIO_PinSource14, GPIO_AF_FSMC); // D11
GPIO_PinAFConfig(GPIOE, GPIO_PinSource15, GPIO_AF_FSMC); // D12
GPIO_PinAFConfig(GPIOF, GPIO_PinSource0, GPIO_AF_FSMC); // A0
GPIO_PinAFConfig(GPIOF, GPIO_PinSource1, GPIO_AF_FSMC); // A1
GPIO_PinAFConfig(GPIOF, GPIO_PinSource2, GPIO_AF_FSMC); // A2
GPIO_PinAFConfig(GPIOF, GPIO_PinSource3, GPIO_AF_FSMC); // A3
GPIO_PinAFConfig(GPIOF, GPIO_PinSource4, GPIO_AF_FSMC); // A4
GPIO_PinAFConfig(GPIOF, GPIO_PinSource5, GPIO_AF_FSMC); // A5
GPIO_PinAFConfig(GPIOF, GPIO_PinSource12, GPIO_AF_FSMC); // A6
GPIO_PinAFConfig(GPIOF, GPIO_PinSource13, GPIO_AF_FSMC); // A7
GPIO_PinAFConfig(GPIOF, GPIO_PinSource14, GPIO_AF_FSMC); // A8
GPIO_PinAFConfig(GPIOF, GPIO_PinSource15, GPIO_AF_FSMC); // A9
GPIO_PinAFConfig(GPIOG, GPIO_PinSource0, GPIO_AF_FSMC); // A10
GPIO_PinAFConfig(GPIOG, GPIO_PinSource1, GPIO_AF_FSMC); // A11
GPIO_PinAFConfig(GPIOG, GPIO_PinSource2, GPIO_AF_FSMC); // A12
GPIO_PinAFConfig(GPIOG, GPIO_PinSource3, GPIO_AF_FSMC); // A13
GPIO_PinAFConfig(GPIOG, GPIO_PinSource4, GPIO_AF_FSMC); // A14
GPIO_PinAFConfig(GPIOG, GPIO_PinSource5, GPIO_AF_FSMC); // A15
GPIO_PinAFConfig(GPIOG, GPIO_PinSource10, GPIO_AF_FSMC); // NE3
GPIO_PinAFConfig(GPIOG, GPIO_PinSource12, GPIO_AF_FSMC); // NE4
RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC, ENABLE);
{
FSMC_NORSRAMInitTypeDef s;
FSMC_NORSRAMTimingInitTypeDef p;
p.FSMC_AddressSetupTime = 8;
p.FSMC_AddressHoldTime = 2;
p.FSMC_DataSetupTime = 8;
p.FSMC_BusTurnAroundDuration = 0;
p.FSMC_CLKDivision = 0;
p.FSMC_DataLatency = 0;
p.FSMC_AccessMode = FSMC_AccessMode_A;
s.FSMC_Bank = FSMC_Bank1_NORSRAM3;
s.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
s.FSMC_MemoryType = FSMC_MemoryType_SRAM;
s.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
s.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
s.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable;
s.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
s.FSMC_WrapMode = FSMC_WrapMode_Disable;
s.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
s.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
s.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
s.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;
s.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
s.FSMC_ReadWriteTimingStruct = &p;
s.FSMC_WriteTimingStruct = &p;
FSMC_NORSRAMInit(&s);
/* Enable FSMC Bank1_SRAM3 Bank */
FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM3, ENABLE);
}
}
Большую часть кода заняла конфигурация выходов, а именно настройка их альтернативной функции работы. Выводов задействовано много, отсюда относительно большое число строк.
Согласно конфигурации FSMC используется 3 часть банка 1 (потому что вывод CE памяти соединен с NE3 микроконтроллера), а значит диапазон доступной памяти 0x68000000 - 0x6BFFFFFF, но поскольку у микросхемы размер 1 МБ, диапазон составляет 0x68000000 - 0x680FFFFF.
Один из способов получить доступ к памяти создать секцию в файле линкера и задать начальный адрес 0x68000000 и размер обрасти памяти. Тогда компилятор будет распределять память.
Достаточно будет написать что-то типа
uint16_t x __attribute__((section(".ex_sram")));
uint32_t y __attribute__((section(".ex_sram")));Для теста был создан массив на всю доступную память.
#define SizeArr (1024*1024/2)
static volatile uint16_t Arr[SizeArr] __attribute__((section(".ex_sram")));А после заполнен данными.
for (uint32_t i=0; i<SizeArr; i++)
{
Arr[i]=i; // Запись во внешнюю память.
}Содержимое памяти было проверено отладчиком. Ошибок нет.
Память была заполнена за 12.5 миллиона тактов, а учитывая что в секунде 168 миллионов тактов (ядро работает на частоте 168 МГц), память была заполнена за 78 миллисекунд.
Файлы. http://pure-basic.narod.ru/forum_files/ … C_SRAM.zip
Теперь подключим дисплей с контроллером ILI9341.
Для него предусмотрен разъем TFT.
Схема подключения дисплея.
С дисплеем соединен вывод NE4, а значит ему выделено адресное пространство 0x6C000000 - 0x6FFFFFFF. В FSMC нет понятия команд и данных и соответствующий вывод отсутствует, но можно использовать для этой цели любой вывод адреса. В данной плате использован вывод A6. Таким образом запись при 0 на A6 будет интерпретироваться дисплеем как команда, а при 1 на A6 как данные. Например запись по адресу 0x6C000000 это команда, а по 0x6C000080 данные. В виде кода это выглядит так.
// ********* Отправка КОМАНДЫ ************//
static inline void ILI9341_SendCommand(uint16_t cmd)
{
*(__IO uint16_t*)(0x6C000000) = cmd;
}
// ********* Отправка 1 байта ДАННЫХ ************//
static inline void ILI9341_SendData(uint16_t data)
{
*(__IO uint16_t*)(0x6C000080) = data;
}Настройка модуля FSMC.
static void FSMC_Init(void)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOE |
RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOG, ENABLE);
{
GPIO_InitTypeDef gpio;
gpio.GPIO_Mode = GPIO_Mode_AF;
gpio.GPIO_OType = GPIO_OType_PP;
gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio.GPIO_Speed = GPIO_Speed_100MHz;
gpio.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_8 | GPIO_Pin_9 |
GPIO_Pin_10 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_Init(GPIOD, &gpio);
gpio.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 |
GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_Init(GPIOE, &gpio);
gpio.GPIO_Pin = GPIO_Pin_12;
GPIO_Init(GPIOF, &gpio);
gpio.GPIO_Pin = GPIO_Pin_12;
GPIO_Init(GPIOG, &gpio);
}
GPIO_PinAFConfig(GPIOD, GPIO_PinSource0, GPIO_AF_FSMC); // D2
GPIO_PinAFConfig(GPIOD, GPIO_PinSource1, GPIO_AF_FSMC); // D3
GPIO_PinAFConfig(GPIOD, GPIO_PinSource4, GPIO_AF_FSMC); // NOE
GPIO_PinAFConfig(GPIOD, GPIO_PinSource5, GPIO_AF_FSMC); // NWE
GPIO_PinAFConfig(GPIOD, GPIO_PinSource8, GPIO_AF_FSMC); // D13
GPIO_PinAFConfig(GPIOD, GPIO_PinSource9, GPIO_AF_FSMC); // D14
GPIO_PinAFConfig(GPIOD, GPIO_PinSource10, GPIO_AF_FSMC); // D15
GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_FSMC); // D0
GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_FSMC); // D1
GPIO_PinAFConfig(GPIOE, GPIO_PinSource7, GPIO_AF_FSMC); // D4
GPIO_PinAFConfig(GPIOE, GPIO_PinSource8, GPIO_AF_FSMC); // D5
GPIO_PinAFConfig(GPIOE, GPIO_PinSource9, GPIO_AF_FSMC); // D6
GPIO_PinAFConfig(GPIOE, GPIO_PinSource10, GPIO_AF_FSMC); // D7
GPIO_PinAFConfig(GPIOE, GPIO_PinSource11, GPIO_AF_FSMC); // D8
GPIO_PinAFConfig(GPIOE, GPIO_PinSource12, GPIO_AF_FSMC); // D9
GPIO_PinAFConfig(GPIOE, GPIO_PinSource13, GPIO_AF_FSMC); // D10
GPIO_PinAFConfig(GPIOE, GPIO_PinSource14, GPIO_AF_FSMC); // D11
GPIO_PinAFConfig(GPIOE, GPIO_PinSource15, GPIO_AF_FSMC); // D12
GPIO_PinAFConfig(GPIOF, GPIO_PinSource12, GPIO_AF_FSMC); // A6
GPIO_PinAFConfig(GPIOG, GPIO_PinSource12, GPIO_AF_FSMC); // NE4
RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC, ENABLE);
{
FSMC_NORSRAMInitTypeDef s;
FSMC_NORSRAMTimingInitTypeDef p;
p.FSMC_AddressSetupTime = 2;
p.FSMC_AddressHoldTime = 2;
p.FSMC_DataSetupTime = 6;
p.FSMC_BusTurnAroundDuration = 0;
p.FSMC_CLKDivision = 0;
p.FSMC_DataLatency = 0;
p.FSMC_AccessMode = FSMC_AccessMode_A;
s.FSMC_Bank = FSMC_Bank1_NORSRAM4;
s.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
s.FSMC_MemoryType = FSMC_MemoryType_SRAM;
s.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
s.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
s.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable;
s.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
s.FSMC_WrapMode = FSMC_WrapMode_Disable;
s.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
s.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
s.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
s.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;
s.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
s.FSMC_ReadWriteTimingStruct = &p;
s.FSMC_WriteTimingStruct = &p;
FSMC_NORSRAMInit(&s);
/* Enable FSMC Bank1_SRAM2 Bank */
FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM4, ENABLE);
}
}Кода немного меньше чем в случае с памятью в основном за счет того что используется меньше выводов.
Дисплей 320x240 заполняется цветом за 922 тысячи тактов что равно 5 миллисекунд.









