Подключить внешнюю память к микроконтроллеру

Обновлено: 05.07.2024

В одной из прошлых статей ( Подключение внешней памяти EEPROM к Ардуино ) я на примере чипа 24LC512 рассказывал как подключить к Ардуино внешнюю EEPROM память . Там было подробно рассказано о характеристиках данного модуля памяти и о схеме его подключения к Arduino UNO.

В этой статье мы рассмотрим как подключить дополнительную память EEPROM к микроконтроллерам семейства ATtiny .

Общий вид подключения ATtiny к модулю памяти EEPROM 24LC512 Общий вид подключения ATtiny к модулю памяти EEPROM 24LC512

Запись во внутреннюю EEPROM память

Для начала разберемся с записью во внутреннюю EEPROM память, чтобы в дальнейшем были видны отличия.

Для примера возьмем ATtiny13 . После выбора нужной платы в Менеджере плат , в примерах для ATtiny13 открываем скетч eeprom_put .

Здесь вначале подключается библиотека EEPROM .

Далее объявляется структура, содержащая одну вещественную переменную, одну целочисленную, типа байт и массив из 10 символов.

После этого в нулевую ячейку памяти (которых на ATtiny13 у нас всего 64) записываем некоторое вещественное число.

После этого создаем экземпляр ранее объявленной структуры, который называется customVar и заполняем его значениями. Только изменим число 65 на 5. Дальше будет понятно почему.

Сдвигаем адрес ячейки для записи на размер записанной переменной типа float и записываем экземпляр структуры в энергонезависимую память по новому адресу.

Загружаем скетч в ATtiny. Естественно, Arduino UNO к этому моменту должен быть в режиме программатора. Как это делается, я рассказывал в статье " ATtiny13 и ATtiny85. Обзор и программирование с помощью Arduino ".

После загрузки скетча, данные в EEPROM память записаны.

Чтение из внутренней EEPROM памяти

Теперь прочитаем данные из EEPROM памяти. Для этого там же в примерах найдем и откроем скетч eeprom_get .

В нем мы определяем те же переменные и структуру. И с помощью функции EEPROM.get() считываем данные в оперативную память.

На Arduino UNO мы могли бы всё вывести в монитор порта. Но здесь поэтому поступим по-другому.

Видоизменим скетч. В настройках определим нулевой пин, который соответствует 5-й ножке ATtiny на работу с выходным сигналом.

А после чтения из EEPROM памяти данных структуры добавим цикл, который повторится столько раз, сколько мы записали во вторую переменную структуры, т.е. 5 раз.

А в теле цикла, будем подавать высокий и низкий сигнал на 5-й ножку ATtiny. Т.е. подключенный к этой ножке светодиод должен мигнуть 5 раз.

Подключаем светодиод с уже припаянным резистором на 220 ом к 5-й ножке и 4-й ножке (земле). Загружаем скетч.

Светодиод мигает 5 раз подряд.

Данный метод универсален и подходит и для других микроконтроллеров ATtiny и для микроконтроллеров семейства Arduino и многих других.

Запись и чтение из внешней EEPROM памяти

Теперь поработаем с внешней памятью. Сразу скажу, что этот метод будет работать начиная с ATtiny25. ATtiny13 слишком прост для таких задач.

Для работы с внешней памятью на микроконтроллерах ATtiny служит специальная библиотека TinyWireM , которая используется вместо библиотеки Wire на Arduino UNO. Ссылка на скачивание библиотеки -

Скачиваем библиотеку. И добавляем ее в Arduino IDE.

Для демонстрации работы библиотеки у меня есть вот такой скетч. Собственно это переработка скетча для работы с внешней EEPROM памятью на Arduino UNO. Ссылка на скачивание самого скетча в конце статьи.

Код скетча чтения и записи во внешнюю EEPROM для ATtiny Код скетча чтения и записи во внешнюю EEPROM для ATtiny

Точно так же, мы вначале мы проводим инициализацию библиотеки и начинаем передачу данных, указывая в качестве аргумента функции TinyWireM.beginTransmission() адрес внешней памяти.

Запись во внешнюю память ничем не отличается от записи при использовании Arduino UNO, только меняем имя библиотеки с Wire на TinyWireM .

Чтение осуществляется таким же образом, как и в случае с Arduino UNO. Только после отправки запроса на получение 1 байта от подключенного модуля памяти TinyWireM.requestFrom() добавим проверку, доступен ли обмен данных, и с помощью функции read() прочитаем байт данных из EEPROM памяти в переменную data .

Далее, чтобы убедимся, что данные успешно прочитаны мигнем красным светодиодом, подключенным к 6-му пину ATtiny соответствующее число раз.

Для демонстрации я буду использовать ATtiny85 . Хотя годятся и младшее модели ATtiny25 и ATtiny45 . Но не ATtiny13 . Загружаем скетч в ATtiny. После успешной загрузки временно отключаем программатор от компьютера.

Далее можно не вынимать ATtiny из программатора соединить 4 пин ATtiny с минусом макетной платы, а 8 пин с плюсом.

Теперь согласно распиновке ATtiny25/45/85 5-й пин у нас SDA, соединяем с 5 пином EEPROM , а 7 пин ATtiny85 SCL с 6 пином EEPROM .


Рис. 3.8. Структурная схема ОЗУ (ПЗУ).

а) микросхема DS1 (фирма Samsung) — это «интеллектуальное» перепрограммируемое ПЗУ с собственой системой команд. Применяется, в частности, в USB-накопителях;



Рис. 3.9. Расположение выводов и названия сигналов в MK Atmel ATmega8515.


Рис. ЗЛО. Схемы подключения внешней памяти к MK (продолжение):

г) DS1 — это ферроэлектрическое последовательное «ОЗУ/ПЗУ» FRAM (фирма Ramtron), подключаемое к MK по шине PC. При поданном питании FRAM эквивалентна ОЗУ, а при выключенном — ПЗУ. Число перезаписей не ограничено (!), время хранения информации 45 лет;


Рис. 3.10. Схемы подключения внешней памяти к MK (продолжение):

е) в регистре DD1 хранятся младшие 8 бит шины адреса. Старшие 7 бит подаются от MK непосредственно на ОЗУ DS1 фирмы Hitachi. MK работает в режиме «External RAM». На вход «СЕ» ОЗУ DS1 вместо общего провода можно подать сигнал разрешения со свободного выхода MK. Это позволяет снизить общий расход энергии от источника питания, поскольку при ВЫСОКОМ уровне на входе «СЕ» микросхема DS1 переходит в экономичный режим хранения данных;

ж) подключение к MK последовательного флэш-ОЗУ DS1 фирмы Atmel. Если переключатель S1 замкнут, то в ОЗУ нельзя записывать данные, это режим защиты. Резисторы R3, R4 в некоторых схемах отсутствуют. Замена DS1 — любое ОЗУ большей/меньшей ёмкости из семейства DataFlash AT45DB фирмы Atmel, включая устаревшие модели AT45DB081B-CNU;


Рис. 3.10. Схемы подключения внешней памяти к MK (окончание):

з) при прямом подключении флэш-ПЗУ DS1 (фирма AMD) к MK требуется большое количество свободных линий портов. Некоторые выходные линии MK, например, «А7», могут быть одновременно задействованы для управления другими узлами, однако делать это допускается только тогда, когда отсутствует обращение к ПЗУ, т.е. при ВЫСОКОМ уровне сигнала «ОЕ»;

Источник: Рюмик, С. М., 1000 и одна микроконтроллерная схема. Вып. 2 / С. М. Рюмик. — М.:ЛР Додэка-ХХ1, 2011. — 400 с.: ил. + CD. — (Серия «Программируемые системы»).

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

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


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

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



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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

Теперь надо определиться с таймингами записи и чтения. У нас за них отвечает регистр 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 будет предельно понятен.

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

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)

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

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 бла бла бла

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

Спасибо. Вы потрясающие! Всего за месяц мы собрали нужную сумму в 500000 на хоккейную коробку для детского дома Аистенок. Из которых 125000+ было от вас, читателей EasyElectronics. Были даже переводы на 25000+ и просто поток платежей на 251 рубль. Это невероятно круто. Сейчас идет заключение договора и подготовка к строительству!

А я встрял на три года, как минимум, ежемесячной пахоты над статьями :)))))))))))) Спасибо вам за такой мощный пинок.

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

В устройствах на микроконтроллерах для хранения больших объемов данных используется внешняя память. Если требуется хранить единицы мегабайт, то подойдут микросхемы последовательной флэш памяти. Однако для больших объемов (десятки -сотни мегабайт) обычно применяются какие-нибудь карты памяти. В настоящий момент наибольшее распространение получили SD и microSD карты, о них я и хотел бы поговорить в серии материалов. В этой статье речь пойдет о подключении SD карт к микроконтроллеру, а в следующих мы будет разбираться как читать или записывать на них данные.

Назначение контактов SD карты в SD режиме

распиновка SD карты - SD режим


Назначение контактов SD карты в SPI режиме

распиновка SD карты - SPI режим

Назначение контактов microSD карты в SD режиме

распиновка microSD карты - SD режим


Назначение контактов microSD карты в SPI режиме

распиновка microSD карты - SPI режим

Напряжение питания SD карт составляет 2.7 - 3.3 В. Если используемый микроконтроллер запитывается таким же напряжением, то SD можно подключить к микроконтроллеру напрямую. Расово верная схема, составленная путем изучения спецификаций на SD карты и схем различных отладочных плат, показана на рисунке ниже. По такой схеме подключены карты на отладочных платах фирм Olimex и Atmel.

На схеме обозначены именно выводы SD карты, а не разъема.

Подключение SD карты к микроконтроллеру

L1 - феррит или дроссель, рассчитанный на ток >100 мА. Некоторые его ставят, некоторые обходятся без него. А вот чем действительно не стоит пренебрегать, так это полярным конденсатором C2. Потому что при подключении карты происходит бросок тока, напряжение питания "просаживается" и может происходить сброс микроконтроллера.

По поводу подтягивающих резисторов есть некоторая неоднозначность. Поскольку SD карты выпускаются несколькими производителями, на них существует несколько спецификаций. В одних документах четко указана необходимость подтягивающих резисторов (даже для неиспользуемых линий - 8, 9), в других документах этих указаний нет (или я не нашел).

Упрощенный вариант схемы (без подтягивающих резисторов) показан на рисунке ниже. Эта схема проверена на практике и используется в платах фирмы Microelectronika. Также она используется во многих любительских проектах, которые можно найти в сети.

Упрощенная схема подключения SD карты


Здесь сигнальные линии SD карты удерживаются в высоком состоянии микроконтроллером, а неиспользуемые линии (8, 9) никуда не подключены. По идее они должны быть подтянуты внутри SD карты. Далее я буду отталкиваться от этой схемы.

Если микроконтроллер запитывается напряжением отличным от напряжения питания SD карты, например 5 В, то нужно согласовать логические уровни. На схеме ниже показан пример согласования уровней карты и микроконтроллера с помощью делителей напряжения. Принцип согласования уровней простой - нужно из 5-и вольт получить 3.0 - 3.2 В.

Согласование уровней SD карты


Линия MISO - DO не содержит делитель напряжения, так как данные по ней передаются от SD карты к микроконтроллеру, но для защиты от дурака можно добавить аналогичный делитель напряжения и туда, на функционировании схемы это не скажется.

Резистивный делитель напряжения - это самый простой вариант согласования уровней, однако при высоких скоростях обмена или длинных проводах он может не подойти. Емкость входов SD карты, а также паразитная емкость линий, вместе с резисторами делителя образует RC фильтры, которые "заваливают" фронты передаваемых сигналов, а у SD карт есть определенные требования к этим фронтам.

Если использовать для согласования уровней буферную микросхему, например CD4050 или 74AHC125, этих недостатков можно избежать. Ниже приведена схема, в которой согласование уровней выполняется с помощью микросхемы 4050. Это микросхема представляет собой 6 неинвертирующих буферов. Неиспользуемые буферы микросхемы "заглушены".

Согласование уровней SD карты с помощью буфера


Подключение microSD карт аналогичное, только у них немного отличается нумерация контактов. Приведу только одну схему.

Подключение microSD к микроконтроллеру


На схемах я рассматривал подключение SD карт к микроконтроллеру напрямую - без разъемов. На практике, конечно, без них не обойтись. Существует несколько типов разъемов и они друг от друга немного отличаются. Как правило, выводы разъемов повторяют выводы SD карты и также содержать несколько дополнительных - два вывода для обнаружения карты в разъеме и два вывода для определения блокировки записи. Электрически эти выводы с SD картой никак не связаны и их можно не подключать. Однако, если они нужны, их можно подключить как обычную тактовую кнопку - один вывод на землю, другой через резистор к плюсу питания. Или вместо внешнего резистора использовать подтягивающий резистор микроконтроллера.

Подключение SD разъема к микроконтроллеру

Ну и для полноты картины приведу схему подключения SD карты в ее родном режиме. Он позволяет производить обмен данными на большей скорости, чем SPI режим. Однако аппаратный интерфейс для работы с картой в SD режиме есть не у всех микроконтроллеров . Например у Atmel`овских ARM микроконтроллеров SAM3/SAM4 он есть.

Подключение SD карты в режиме SD


Шина данных DAT[0..3] может использоваться в 1 битном или 4-х битном режимах.

Читайте также: