Как записать переменную во флеш память 80c51f120
Здесь приведен перевод апноута AVR106: C functions for reading and writing to Flash memory [1], посвященного подпрограммам чтения и записи памяти FLASH на языке C для микроконтроллеров AVR. Рассмотрены функции чтения и записи одного байта FLASH, чтение и записи одной страницы FLASH, опциональное восстановление после неожиданного пропадания питания. Рассмотренные функции могут использоваться с любым устройством (микроконтроллером AVR), которое имеет возможность записи памяти программ из кода приложения [2] (это почти вся линейка AVR микроконтроллеров Atmel). Вместе с апноутом AVR106 приведен также проект примера программы [3], использующей секцию памяти программ для сохранения параметров.
Почти все микроконтроллеры AVR® компании Atmel имеют так называемую возможность самопрограммирования (Self programming Program memory). Самопрограммирование является одной из особенностей технологии AVR. Чтобы лучше разобраться с этим, обратитесь к апноуту AVR109 [2]. Самопрограммирование позволяет AVR перепрограммировать собственную память FLASH во время работы программы. Это подходит для приложений, которые нуждаются в самостоятельном обновлении кода firmware (bootloader, загрузчики, бутлоадеры) или сохранении какой-либо информации в области памяти FLASH. Этот апноут предоставляет функции на языке C, которые позволяют получить доступ к области памяти FLASH.
[Ассемблерная инструкция SPM]
Память FLASH может быть запрограммирована с помощью инструкции SPM (Store Program Memory). На устройствах, поддерживающих самопрограммирование, память программ FLASH часто делится на 2 основные секции: секция основной программы (память приложения, Application Flash Section) и секция загрузки (Boot Flash Section). Прим. переводчика: секция основной программы начинается с адреса 0x0000, а секция загрузки занимает маленький блок памяти обычно 2..4 килобайта в конце FLASH.
На устройствах, которые имеют блок памяти загрузки (boot block), инструкция SPM имеет возможность записи во всю область памяти FLASH, но только в том случае, если она выполняется из области памяти загрузки. Выполнение SPM из секции приложения не дает никакого эффекта. На младших устройствах AVR, к которых нет блока загрузки, инструкция SPM может работать из любого места памяти FLASH.
Во время записи FLASH в секции загрузки работа CPU всегда останавливается. Однако большинство устройств могут выполнить код (чтение) из секции загрузки, когда происходит запись области памяти приложения (Application section). Очень важно, чтобы код, выполняемый при записи Application section, не пытался читать из Application section. Если это вдруг произойдет, то может быть нарушено общее выполнение программы.
Размер и область размещения этих двух областей памяти FLASH зависит от типа устройства и установки его фьюзов. Некоторые устройства имеют возможность выполнять инструкцию SPM из любого места области памяти FLASH.
[Процедура записи FLASH]
Технология памяти FLASH устроена так, что записывается эта память постранично. Запись осуществляется путем сохранения всей страницы во временном буфере страницы, перед тем, как страница будет записана во FLASH. Адрес, по которому будет записаны данные страницы, определяется по содержимому Z-регистра и RAMPZ-регистра. Страница должна быть очищена (erase) перед тем, как на страницу могут быть записаны данные из временного буфера. Функции, которые содержит этот апноут, используют следующую процедуру для записи страницы FLASH:
• Заполнение буфера страницы
• Очистка страницы
• Запись страницы
Как можно заметить, возможна потеря данных, если во время этой процедуры неожиданно произойдет сброс или пропадание питания сразу после очистки страницы. Потери данных можно избежать, если предварительно применить буферизирование записываемых данных в энергонезависимой памяти (nonvolatile memory, обычно EEPROM). Функции записи, содержащиеся в этом апноуте, предоставляют опциональное буферизирование при записи. Эти функции далее рассматриваются в секции «Описание firmware». Для устройств, которые имеют фичу read-while-write (чтение во время записи), бутлоадер может выполняться во время записи, и из функций не произойдет возврат, пока запись не завершится.
[Адресация FLASH]
Память FLASH в AVR поделена на 16-битные слова. Это означает, что каждый адрес FLASH означает ячейку из 2 байт данных. Для ATmega128 можно адресовать до 65k слов или 128k байт данных FLASH. В некоторых случаях память FLASH упоминается адресованной словами, и в других случаях — адресованной побайтно, что вносит некоторую путаницу. Все функции, содержащиеся в этом апноуте, используют байтовую адресацию. Соотношение между байтовым адресом и адресом слова следующее:
Байтовый адрес = Адрес слова * 2
Страница FLASH адресуется с использованием адреса байта для первого байта на странице. Соотношение между номером станицы (0, 1, 2, . ) и адресом байта следующее:
Байтовый адрес = номер страницы * размер страницы (в байтах)
Пример байтовой адресации: размер страницы FLASH у ATmega128 равен 256 байт. Байтовый адрес 0x200 (512) укажет на следующее:
• Байт FLASH 0x200 (512), что соответствует байту 0 на странице 2
• Страница 2 FLASH
Когда адресуется страница ATmega128, младший байт адреса всегда 0. Когда адресуется слово, то младший бит (LSB) адреса всегда 0.
[Реализация проекта примера, работающего с FLASH]
Проект firmware сделан для компилятора IAR. Функции могут быть портированы на другие компиляторы, но могут потребоваться некоторые усилия, поскольку используется специфическая мнемоника (intrinsic functions) компилятора IAR. Функции доступны через подключение файла Self_programming.h в главный модуль C, и добавление модуля Self_programming.c к проекту. Когда используется Self-programming (самопрограммирование), то важно, чтобы функции для записи размещались в пределах секции загрузки (Boot section) памяти FLASH. Размещение функций управляется путем использования определений сегмента памяти в файле конфигурации линкера (*.xcl). Все другие необходимые конфигурации, касающиеся firmware, сделаны в файле Self_programming.h.
PAGESIZE. Константа PAGESIZE должна быть определена равной размеру страницы FLASH (в байтах) используемого устройства.
__FLASH_RECOVER. Определение константы __FLASH_RECOVER разрешает опцию восстановления Flash, чтобы избежать потери данных при случайных пропаданиях питания. Когда восстановление разрешено, одна страница Flash выделена под буфер восстановления. Значение __FLASH_RECOVER будет определять адрес страницы FLASH, которая используется для этого. Адрес должен быть байтовым, указывающим на начало страницы FLASH, и функции записи не смогут записывать в эту страницу. Восстановление FLASH происходит вызовом функции RecoverFLASH() при старте программы (program startup).
ADR_LIMIT_LOW и ADR_LIMIT_HIGH. Диапазон памяти, в котором функциям разрешено записывать FLASH, задается константами ADR_LIMIT_LOW и ADR_LIMIT_HIGH. Функции могут записывать по адресам больше или равным ADR_LIMIT_LOW и меньше или равным ADR_LIMIT_HIGH.
[Размещение всего кода внутри секции загрузки]
Необходимо перезадать диапазон сегментов, определенных в файле настройки линкера по умолчанию (файл *.xcl), чтобы разместить весь код приложения в секции загрузки (Boot section) FLASH. Размещение и размер Boot section зависит от устройства и установок его фьюзов. Программирование фьюза BOOTRST перенесет вектор сброса в начало секции загрузки. Также можно перенести все вектора прерывания в секцию загрузки. Обратитесь к секции даташита на устройство, посвященной прерываниям, за инструкциями, как это сделать. Определения сегмента, которые должны быть изменены для размещения всего программного кода в секции загрузки:
TINY_F, NEAR_F, SWITCH, DIFUNCT, CODE, FAR_F, HUGE_F, INITTAB, TINY_ID, NEAR_ID и CHECKSUM.
В этом апноуте предоставляется в качестве примера файл lnkm128s.xcl, где задано размещение всего кода программы в 8 килобайт секцию загрузки Atmega128. Этот файл можно просто модифицировать для использования с другими устройствами, и в нем содержатся инструкции, как это осуществить.
[Размещение в секции загрузки только некоторых функций]
В качестве альтернативы можно разместить только некоторые функции в заданном сегменте памяти FLASH. В нашем случае в секции загрузки нужно разместить только функции записи. Это можно сделать заданием нового сегмента FLASH, эквивалентного области памяти загрузки, и использование оператора @ для размещения нужных функций в этом сегменте. Оператор @ не распространяется на функции, которые вызываются внутри функций, помеченных @.
Определение сегмента загрузки (Boot segment) в файле *.xcl для ATmega128 с размером 8 килобайт:
1. Сделайте новое определение размера секции загрузки.
2. Задайте новый сегмент для всей секции загрузки, основанный на определении шага 1.
Размещение функций C в заданном сегменте:
Пример кода на языке C, приведенный выше, разместит функцию ExampleFunction() в определенный сегмент памяти BOOT_SEGMENT.
[Описание firmware]
Код firmware состоит из 5 функций на языке C, и один проект примера для IAR версии 2.28a / 3.10c для ATmega128. Проект примера сконфигурирован так, чтобы разместить код всего приложения в секцию загрузки (Boot section) памяти FLASH. Этот код можно использовать как стартовую точку Вашей программы, которая может записывать FLASH. В таблице показаны функции, которые осуществляют доступ к памяти FLASH.
Функция | Аргументы | Возвращает |
ReadFlashByte() | MyAddressType flashAdr | unsigned char |
ReadFlashPage() | MyAddressType flashStartAdr, unsigned char *dataPage | unsigned char |
WriteFlashByte() | MyAddressType flashAddr, unsigned char data | unsigned char |
WriteFlashPage() | MyAddressType flashStartAdr, unsigned char *dataPage | unsigned char |
RecoverFlash() | void | unsigned char |
Тип данных MyAddressType определен в Self_programming.h. Размер этого типа зависит от используемого устройства. Для устройств, у которых FLASH больше 64 килобайт, он будет задан как long int, и int для устройств, у которых объем FLASH меньше или равен 64 килобайта (адрес байта 16-битный). Типы данных в реальности используются как указатели __flash или __farflash (соответственно 16 и 24 бит). Причина определения нового типа в том, что использование целочисленных типов более удобно, чем типов указателя.
ReadFlashByte() возвращает 1 байт, который размещен по адресу FLASH, указанном во входном аргументе.
ReadFlashPage() читает одну страницу памяти FLASH от адреса ucFlashStartAdr, и сохраняет данные в массиве pucDataPage[]. Количество сохраняемых байт зависит от размера страницы FLASH. Функция вернет FALSE, если входной адрес не является адресом страницы FLASH, иначе вернет TRUE.
WriteFlashByte() запишет байт ucData по адресу FLASH ucFlashAddr. Функция вернет FALSE, если входной адрес не является допустимым адресом FLASH для записи, иначе вернет TRUE.
WriteFlashPage() запишет данные из массива pucDataPage[] в страницу FLASH по адресу ucFlashStartAdr. Количество записываемых байт зависит от размера страницы FLASH. Функция вернет FALSE, если входной адрес не является допустимым адресом страницы FLASH для записи, иначе вернет TRUE.
RecoverFlash() прочитает переменную состояния в EEPROM, и восстановит страницу FLASH, если это необходимо. Функция должна быть вызвана в начале программы, если разрешена опция восстановления FLASH (__FLASH_RECOVER). Функция вернет TRUE, если имело место восстановление, иначе вернет FALSE.
[Как работает восстановление FLASH]
Когда разрешена опция восстановления FLASH, запись страницы вовлекает предварительное сохранение данных в выделенную страницу восстановления FLASH, перед тем как произойдет действительная запись во FLASH. Адрес записываемой страницы сохраняется в EEPROM совместно с байтом статуса, который показывает, что страница восстановления FLASH содержит данные. Этот байт состояния будет очищен, когда произойдет успешное завершение операции записи указанной страницы. Переменные в EEPROM и буфер восстановления FLASH используются функцией восстановления RecoverFlash(), чтобы восстановить данные, если это необходимо. Запись одного байта в EEPROM занимает примерно такое же время, как и запись всей страницы во FLASH. Так что нужно иметь в виду, что разрешение опции восстановления приведет к значительному увеличению времени записи. EEPROM используется вместо FLASH, потому что выделение нескольких байт в FLASH исключит гибкое использование всей страницы, которая будет содержать эти байты.
STM32 — память
В статье рассказано про организацию памяти микроконтроллера stm32, использования flash’а для хранения пользовательских данных, и про всякие пользовательские биты/биты защиты.
Конечно же в вашем микроконтроллере нет никаких гигабайтов памяти однако камень 32-х битный, а значит можно адресовать (обратится по адресу) до 4ГБ. Это стандартизированная модель обеспечивающая переносимость кода между различными микроконтроллерами stm32, а так же дающая возможность подключать внешнюю память и совершенно спокойно обращаться к ней. То же самое касается и периферии, производитель микроконтроллеров на базе ядра Cortex, может напихать туда оооочень много всего и при этом не беспокоиться о нехватке адресов. Что же касается размера, то некоторых адресов просто нет, вместо них зарезервированные пустоты. Если обратится к зарезервированной области, то произойдёт аппаратный сбой процессора.
Для примера часть адресов блока Peripheral микроконтроллера F103…
Reference manual — rm0008 стр. 51
Тут видны адреса некоторых таймеров, портов GPIO, и другой периферии. Все они находятся выше 0x40000000 и ниже 0x5fffffff.
Если попробовать прочитать зарезервированный адрес…
То получим Hard Fault…
Прежде чем продолжать, надо скачать программу STM32CubeProgrammer с помощью которой можно ковыряться в микроконтроллере. Прога хороша тем, что есть варианты для и
Запускаем и жмём Connect, подключатся можно через ST-Link или UART…
Нажимаем верхнюю левую кнопку (с карандашиком), и на вкладке Device memory видим программу залитую в камень. Можно указать адрес, с которого читать (по умолчанию стоит начальный адрес флеш-памяти) и сколько байт прочитать. Кнопочка Read считывает данные и показывает как на картинке. Если вместо Read выбрать Save As, то программа сдампится в указанный файл (надо только размер считываемых данных правильно указать, а то по умолчанию 1К).
Если нажать вкладку Open file, то можно открыть какой-нибудь .hex или .bin файл прямо в программе.
Кнопка открывает интерфейс для загрузки прошивки в МК…
Выбираем нужный файл, очищаем флеш полностью (Full chip erase) или выборочные страницы, и жмём Start Programming.
— что-то связанное с внешним загрузчиком или внешним девайсом, не знаю, не вникал.
— очищает флеш.
В нижней части лог подключения, а справа-снизу полезная информация о вашем МК. Здесь интересны два пункта…
Device — серия микроконтроллера и плотность (Medium-density).
Некоторые микроконтроллеры stm32, в рамках одной серии, например популярный F103, он же BluePill, выпускается не только в разных корпусах с разным количеством «ножек», но и разной плотности (объёмом флеш-памяти).
Connectivity line devices — это, на сколько я понимаю, микроконтроллеры которые умеют работать с интернетом. Programming manual — PM0075 стр. 5.
Device ID — код, который зашит в системный бутлоадер, и по нему ST-Link определяет что это за микроконтроллер.
Коды можно посмотреть в AN2606 стр. 307.
Теперь нажмём кнопочку чтоб появился список Option bytes…
… и вернёмся к изучению памяти.
Всё что выше Peripheral особого интереса не представляет, SRAM — это оперативная память, а Code Area выглядит так…
Option bytes — здесь находятся различные биты для настройки МК. Защита от чтения/записи, включение/отключение вачдогов, и несколько пользовательских битов для хранения какой-либо инфы.
Бит RDP — если установить, то нельзя будет ни прочитать прошивку, ни загрузить новую. Чтобы установить, нужно поставить галочку, нажать кнопку Apply и обресетить МК. Снимается так же. При снятии защиты, существующая в МК прошивка будет удалена. Такой механизм гарантирует невозможность прочесть прошивку в залоченом камне.
В более серьёзных камнях RDP имеет несколько уровней…
AA — нет защиты.
BB — защита включена.
СС — микроконтроллер безвозвратно заблокирован от чтения/записи. Warning! Если установите этот уровень защиты, то больше никогда не сможете прошить МК. Операция необратима.
Тут комментировать особо нечего, думаю и так всё понятно. Однако очень не рекомендую снимать галочку с вачдога — камень будет постоянно ресетится. Настройки применяются так же, галочку сняли/поставили, и кнопка Apply. В более мощных МК есть доп. настройки.
Сюда можно сохранить какие-то свои данные, правда не много
Защита от записи конкретных страниц флеш-памяти. К этому вернёмся чуть позже.
System memory — это область в которой располагается системный загрузчик (bootloader), он зашивается на заводе при изготовлении, его нельзя ни удалить, ни изменить.
Bootloader это такая небольшая программка, которая позволяет прошивать микроконтроллер через различные интерфейсы. Если подтянуть пин BOOT_0 к «земле» и нажать ресет, то загрузиться bootloader и будет ожидать поступления новой прошивки. Простенькие камни, типа F103, можно прошивать только через USART, а более «крутые» позволяют делать это и через другие интерфейсы. Посмотреть это можно в AN2606 стр. 25. Например вот…
Flash — это область энергонезависимой памяти, в которой хранится ваша прошивка. Выглядит эта область следующим образом…
RM0008 стр. 55. (Рис. 1)
Information block — это описанные выше System memory и Option Bytes.
Flash memory interface registers — это регистры отвечающие за работу Flash-памяти, они находятся в области Peripheral .
Main memory — это и есть наша Flash-память.
Flash -память организована постранично, а размер страницы колеблется от одного до нескольких килобайт, в зависимости от серии микроконтроллера. У F103 (Рис. 1) размер равен 1К, а вот например у F4 выглядит так…
Часть страниц по 16К, одна 64К, а часть по 128К. Здесь страницы называются секторами.
OTP area — это биты защиты, которые можно записать только один раз (One-Time-Programmable). Больше ничего про это сказать не могу — у меня нет такой платы.
Тут есть один любопытный момент. Компания ST выпускает два почти одинаковых камня F103, один F103C8хх c объёмом флеш-памяти 64К (BluePill), и F103CBхх c объёмом 128К. Однако на самом деле у обоих этих камней объём флеш-памяти равен 128К (128 страниц по 1К).
Во-первых в мануале нет камней с 64-мя страницами (см. рис. 1), а во-вторых, не смотря на то, что ST-Link показывает объём 64К, у всех моих F103C8хх читаются и пишутся все 128 страниц. То есть программу размером больше 64К залить через TrueStudio не удаётся, а вот производить чтение/запись этих областей из своей программы можно. Да и STM32CubeProgrammer их тоже совершенно спокойно читает…
127-я страница на камне F103C8хх.
Выше я говорил что мы вернёмся к вопросу защиты конкретных страниц от чтения/записи…
Биты WRPх защищают сразу по несколько страниц. Количество защищаемых страниц зависит от плотности (density) МК.
Вот вырезка из Programming manual стр. 21…
У low-density используется только WRP0 и защищает весь флеш, а у medium-density можно делать это выборочно. Зачем нужны остальные биты (WRP4 и т.д.) я так и не понял, может они зарезервированы.
Такой способ выборочной защиты очень удобен тем, что можно заблокировать область где находится программа, а остальное использовать для хранения каких-то данных и не боятся затереть программу, ну или наоборот, защитить какие-то данные, которые вы положили например в конец флеша.
CCM SRAM — у некоторых микроконтроллеров есть дополнительная оперативная память под названием Core Coupled Memory. Эта память подключена непосредственно к ядру, благодаря чему скорость доступа и исполнение кода будет быстрее чем в обычной SRAM. Соответственно в CCM выгодно размещать всякие статистические переменные/массивы, а при желании можно перенести туда стек/кучу/.data/.bss.
Cледует помнить, что доступ к CCM имеет только процессор, а значит DMA не сможет к ней обратиться. CCM бывают разные для разных микроконтроллеров, поэтому прежде чем начинать использование следует изучить документацию. Ещё инфа, и ещё инфа.
В качестве примера я перенёс пару массивов в ССМ (среда TrueStudio, камень F303)…
Объявляем массивы глобально:
Чтоб компилятор не «оптимизировал» эти массивы, сделайте что-нибудь с ними, например выведите на печать.
Вуаля, они лежат где надо…
Прежде чем говорить о переносе стека/кучи/.data/.bss в ССМ, я очень кратко и обобщённо поясню что означают эти термины:
Стек — область памяти в ОЗУ, куда сохраняется адреса программы в момент возникновения прерывания или перехода в другую функцию.
Пошагово выглядит это так:
Наша программа работает-работает (указатель «бежит» последовательно по адресам).
Происходит прерывание — указатель перепрыгивает в обработчик этого прерывания.
Адрес, где находилась программа в момент возникновения прерывания, записывается в стек (чтоб запомнить куда возвращаться).
После того как обработка прерывания закончится, из стека вытаскивается адрес на который нужно вернуться.
То же самое происходит при переходе из одной функции в другую, например из в
Технически, стек можно представить в виде стопки книг где нельзя взять вторую сверху книгу не сняв предварительно первую. То есть стек работает по принципу LIFO буфера (Last In First Out) — «последним пришёл, первым вышел». Такая организация очень хорошо себя оправдывает. Представьте себе такую ситуацию:
Указатель перепрыгнул из функции main() в bla_bla() .
В стек записался адрес того места откуда выпрыгнул указатель.
Идёт обработка функции bla_bla() и в этот момент происходит прерывание.
В стек записывается (ложится поверх предыдущего) ещё один адрес, и указатель переходит в обработчик прерывания.
В результате на стеке лежат два адреса — адрес, на котором была приостановлена функция main() , а поверх него адрес на котором приостановлена функция bla_bla() .
По окончании обработки прерывания система лезет в стек, «снимает со стопки» самый последний адрес, переходит по нему и заканчивает выполнение функции bla_bla() с того места где она была прервана. Больше этот адрес не нужен и он выкидывается в помойку, на стеке остаётся адрес приостановки функции main() .
Как только функция bla_bla() завершается, система опять лезет в стек и «снимает» адрес приостановки функции main() . Адрес опять же летит в помойку, а функция main() продолжает выполняться. Таким образом, снимая «книжку за книжкой» система не сможет вернуться куда-то не туда и нарушить целостность программы.
Помимо адресов, в стек сохраняются локальные переменные функций и параметры передающиеся в функции. То есть в момент вызова функции, вместе с адресом для возврата, на стек кладутся ещё и локальные переменные, которые есть в этой функции. Как только функция отработает, эти переменные будут сняты со стека и уничтожены.
Куча — эта область ОЗУ, которая используется для динамического выделения памяти в процессе работы программы. То есть, когда вы делаете malloc/calloc/realloc, то память выделяется на куче.
bss — область ОЗУ, сюда помещаются не инициализированные глобальные переменные (uint8_t var;).
data — область ОЗУ, сюда помещаются инициализированные глобальные переменные (uint8_t var = 0;).
text — Flash, здесь лежит сама программа.
Выглядит всё это хозяйство следующим образом…
Стек начинается от самого большого адреса и при увеличении размера ползёт вниз (записывая данные в меньшие адреса), а куча наоборот, при увеличении размера ползёт вверх, поэтому при выделении памяти на куче нужно следить чтоб эти области не налезли друг на друга.
Чтоб лучше понять иллюстрацию выше, откройте файл STM32F103C8_FLASH.ld и найдите там такие строки…
Нас интересует переменная _estack , которая указывает на конец оперативной памяти. У микроконтроллера stm32F103c8 оперативная память располагаться в области от 0x20000000 до 0x20005000 , что равно 20480 байтам (20Кб). Визуально это выглядит так…
Вся наша оперативка находится внутри узенькой полоски, а всё что выше не существует. То есть стек, куча, bss и data находится внутри этой полоски, а text в жёлтой области.
Посмотреть сколько места занимают некоторые данные можно в среде разработки. Вот картинка из TrueStudio…
dec и hex это общий размер первых трёх значений.
Чтоб перенести какую-то из этих областей в ССМ, нужно подкорректировать файл компоновщика (или линковщика, называйте как вам удобнее) с расширением .ld …
Находим там интересующие нас блоки…
Меняем у каких-нибудь блоков, или у всех сразу, слово RAM на CCMRAM …
В результате получаем — до:
Запись данных во флеш
У большинства микроконтроллеров stm32 (кроме серии L0) нету EEPROM, поэтому сохранять пользовательские данные приходится во флеш-памяти. Количество циклов записи 10000.
Чтобы записать во флеш какие-то данные нужно вначале её очистить (во время очистки все биты заполняются единицами — 0xFF). Очистка происходит постранично/посекторно.
Очистка одной страницы на F103…
Объявляем структуру в которую заносятся параметры очистки:
TypeErase — что хотим очистить, какую-то конкретную страницу (или несколько), или всю флеш полностью.
PageAddress — начальный адрес страницы, которую хотим очистить. Адреса можно посмотреть в мануале (правда там не все видны), либо посчитать самостоятельно, либо в примерах Куба — STM32CubeFx в файле main.h (для F103 — /STM32Cube_FW_F1_V1.8.0/Projects/STM32F103RB-Nucleo/Examples/FLASH/FLASH_EraseProgram/Inc/main.h). В конце есть ссылка на гитхаб с этим примером, там есть хедер (addr_pages.h) с задефайнеными адресами для BluePill.
NbPages — кол-во страниц для очистки. Если указать несколько, то они будут очищены начиная с адреса указанного выше.
Banks — у «жирных» камней память делиться на банки, поэтому нужно указать в каком именно банке находятся страницы.
HAL_FLASH_Unlock() — снимаем блокировку стирания/записи во флеш (для чтения этого делать не нужно). Это не относится к битам WRPx, если они установлены для текущей страницы, то стереть/записать страницу не получится.
Ну, а дальше стираем страницу/страницы, и возвращаем блокировку. Если что-то пойдёт не так, то программа выведет ошибку и зациклится.
После этой операции вся страница готова для записи. Необязательно записывать всю страницу за один раз, можно добавлять записи по мере необходимости, при условии что новые данные не будут записываться поверх старых.
Почему нужно очищать (заполнять значениями 0xFF) память?
Дело в том, что когда происходит запись байта в ячейку, то биты в этой ячейки не просто так берут и перезаписываются, а совершается операция логического «И» над тем что есть в ячейке и новым значением.
Наглядно это выглядит так. Ячейка у нас очищена (биты заполнены единицами) и мы записываем в неё число 7 (0х07)…
Выполняется логическое «И», и в ячейку записывается нужное нам число 7.
А теперь допустим что мы хотим записать в эту же ячейку число 13 (0x0d) поверх старого значения…
Выполняется логическое «И» между старым значением 0х07 и новым 0x0d. В результате вместо желаемого числа 13, в ячейку записывается число 5.
Запись
У разных МК можно записывать разную длину «слова», например у F103 можно записать «слова» размером 16, 32 и 64 бита. Подсмотреть это можно в файле stm32f1xx_hal_flash.h …
Запишем два 16-ти (FLASH_TYPEPROGRAM_HALFWORD) битных числа в начало только что очищенной страницы…
Разблокируем флеш, указываем адрес начала страницы (не обязательно начало, можно в любое место), записываем во флеш массив из двух чисел, а в цикле увеличиваем адрес на два. После этого блокируем память.
Если записывать 32-х (FLASH_TYPEPROGRAM_WORD) битное число, то увеличим адрес на четыре, а если 64-х (FLASH_TYPEPROGRAM_DOUBLEWORD) битное, то на 8. Если в дальнейшем захотите добавить в эту страницу ещё что-то, то нужно запомнить адрес.
Идём в STM32CubeProgrammer, вписываем адрес 127-ой страницы и смотрим чего понаписали…
Всё окей. Не забывайте нажимать Disconnect
Прочитаем то, что записали…
Разблокировать не надо, а адрес опять же увеличиваем на два. Получаем ожидаемое…
Чтоб записать 8-ми битное значение (не смотря на то, что производитель не предоставил такой возможности), надо просто записывать по два байта в одно 16-ти битное «слово» со сдвигом. Всё происходит почти так же как и с 16-ти битным числом…
Увеличиваем адрес на 4 (чтоб добавить данные вслед за предыдущими), записывать будем массив, размер которого должен быть кратен двум (для этого сделана проверка), а в цикле запихиваем два символа в одно 16-ти битное «слово» и отправляем по адресу.
Читать можно по одному 8-ми битному символу.
Вот и всё, остаётся придумать как хранить адрес (со смещением) последней записи для добавления последующих, чтобы не мучить одну и туже ячейку, и не прикончить её в самом скором времени. Вот вариант как смещать очередную запись.
Всем спасибо
Как записать переменную во флеш память 80c51f120
Artwork by Benny Kusnoto
Когда я писал драйвер FM-модуля RDA5807, то у меня возникла необходимость сохранять куда-то найденные станции, чтобы на них можно было переключиться минуя поиск. Но когда я полез в документацию STM8, чтобы поискать, как это можно было бы осуществить, то понял, что EEPROM и FLASH в STM8 — это отдельная подсистема микроконтроллера, и изучать ее надо всю.
Один из режимов записи в EEPROM/FLASH требует выполнения из ОЗУ. Вопрос копирования кода в ОЗУ и выполнения его оттуда я затрагивал в предыдущей статье, однако там вся реализация была на ассемблере. Сейчас же мне захотелось показать как это делается в Си.
В качестве компилятора я выбрал COSMIC, по которому уже как-то писал быстрый старт. Но тогда я писал об использованию COSMIC совместно с SPL библиотекой. На этот раз мне хочется раскрыть тему программирования в COSMIC, используя «чистый» Си в связке с ассемблером. Правда должен оговориться, что несколько отредактированные загловочные файлы из SPL в этой статье я все-таки использовать буду, т.к. нужны будут именновые константы масок периферийных регистров.
В итоге статья получилось составленной из двух взаимосвязанных тем: сначала рассматривается вопрос использования компилятора COSMIC, а затем, как с его помощью сохранять данные в EEPROM/FLASH памяти микроконтроллера.
В качестве Develop Board я буду использовать собственную плату с чипом STM8S105C4T6. Это Medium-Density чип с 16 КБ флеш-памяти, 2 КБ ОЗУ и 1 КБ ЭСППЗУ(EEPROM). Он более интересен чем STM8S103-й чип, т.к. в 105-ом имеется встроенный загрузчик(bootloader), механизм read-while-write (RWW), а размер блока составляет 128 байт вместо 64 байт на 103-м чипе. Вы в свою очередь можете использовать фирменную отладочную плату STM8S-DISCOVERY с чипом STM8S105C6T6. Там флеш-памяти будет побольше — 32 КБ. На худой конец, можно воспользоваться ещё одной китайской платой на 105-м чипе. Также как в STM8S-DISCOVERY в ней установлен кварц на 8 МГц. Сама плата выполнена в форм-факторе удобном для установки в беспаячную макету.
Cosmic у меня работает в связке с STVD, обе программы установлены на виртуалку, которая в свою очередь установлена в Linux. Гостевой ОС в виртуалке служит Windows XP SP3. О превратностях установки Cosmic я уже писал в вышеупомянутой статье два года назад. К сожалению, я тогда я не упомянул, что получить регистрационный ключ можно онлайн. Т.е. не надо ждать несколько дней чтобы ключ скинули на e-mail, как было в моем случае. Если не ошибаюсь, ключ действует один год, и по истечении регистрационного периода, его нужно получать заново. Кроме того, ключ «слетает» при копировании виртуальной машины. В этом случае его также следует получать по новой. В последнем случае я просто удалял Cosmic и затем ставил его заново, получая свежий ключ. Сейчас у меня следующая версия компилятора:
Так же как и в предыдущей статье, для контроля кода прошивки я буду использовать дизассемблер из комплекта утилит stm8-binutils.
-
Список используемой документации:
- Cosmic CXST7 — User Manual — документация по компилятору COSMIC
- Reference Manual STM8S — RM0016, глава 4 «Flash program memory and data EEPROM»
- Programming Manual PM0051 — руководство по записи в EEPROM и FLASH память для микроконтроллеров STM8S/STM8A
- Application note — AN2659, — глава пятая — «Configuring the Cosmic compiler for RAM code execution».
- Работа с EEPROM и Flash / STM8 / Сообщество EasyElectronics.ru хорошая вводная статья по теме на русском языке. Написано по существу, без лишней воды.
-
I. Основы работы со связкой COSMIC + STVD
- Создание базового проекта в среде разработки STVD+COSMIC
- Добавление ассемблерного файла к проекту
- Добавление ассемблерных обработчиков прерываний
- Маппинг на физические адреса
-
II. Основы работы с EEPROM/FLASH подсистемой в микроконтроллерах STM8
- Особенности EEPROM/FLASH подсистемы в микроконтроллерах STM8
- Регистры подсистемы EEPROM/FLASH
- Запись в EEPROM средствами COSMIC
- Безопасное снятие защиты MASS, однобайтный режим записи в EEPROM/FLASH
- Четырехбайтный режим записи в EEPROM/FLASH
- Блоковый режим записи в EEPROM/FLASH. Копирование кода в ОЗУ и выполнение его оттуда средствами COSMIC
Скачать исходники, workspace с проектами и скомпилированными прошивками к статье можно будет по ссылке в конце статьи.
1 Создание базового проекта в среде разработки STVD+COSMIC
В STVD откроем новый Си проект с использованием тулчейна COSMIC. Выглядеть он будет так:
Если проект скомпилировать и после заглянуть в «Debug» каталог проекта, то можно будет увидеть следующие, сгенерированные при компиляции файлы:
Здесь s19 и elf — бинарные файлы с итоговой прошивкой. Файлы с расширением «o» — это объектные файлы. lkf — это скрипт компоновщика.
Заглянем например в 01_blink.lkf:
В начале идёт объявление стандартных сегментов, затем подключается библиотечный файл содержащий обработчик прерывания reset, потом собственно наша программа, после добавляется ещё пара библиотек, а затем объявляется сегмент с таблицей векторов и файл содержащий саму таблицу с обработчиком пустого прерывания.
Технически, мы можем использовать COSMIC без STVD, программировать в командной строке, писать Makefile’ы и пр. Например составим такой main.c
Скомпилируем его в объектный файл командой:
На выходе мы получим объектный файл main.o. Теперь составим скрипт компоновщика main.lkf следующего вида:
В этот раз на выходе получаем main.sm8. Следующим шагом будет получение elf-файла:
Смотрим дизассемблером что у нас получилось:
Таким образом работает компилятор COSMIC в backend режиме STVD. Сегодня я не собираюсь что-то делать в таком стиле, но на мой взгляд иметь представление об этапах компиляции полезно.
Возвращаясь с списку сгенерированых файлов проекта в STVD, хочу обратить ещё внимание на файлы с расширением ls. В них помещается ассемблерный вариант программы:
У вас есть выбор: пользоваться дизассемблером или поглядывать в эти файлы для контроля компиляции.
Ок. Начинаем писать базовый проект на COSMIC. В начале файла main.c добавляем следующие заголовочные файлы:
Первый заголовочный файл добавляет целочисленные типы uint8_t, uint16_t и прочее. Второй заголовочный файл добавляет адреса регистров периферийных устройств. Путь к заголовочным файлам задается в свойствах проекта:
Ниже представлены заголовочные файлы которые идут вместе с компилятором COSMIC:
Теперь добавляем именованные константы для светодиодов, и функцию задержки.
Главная функция main() не будет отличаться оригинальностью:
Компилируем, прошиваем. Если все прошло успешно, то в дизассемблерном виде программа будет выглядеть так:
Можно видеть, что параметр функции передаётся через X регистр, а не через стек как в SDCC.
Я хочу обратить внимание на то, что для вызова функции delay() используется инструкция вызова подпрограммы по относительному адресу — CALLR. Если мы зайдём в настройки проекта и откроем вкладку настройки компилятора, то в выпадающем списке сможем настроить используемую модель памяти.
По умолчанию используется Short Stack (+mods0), когда по мере возможности используются относительные вызовы CALLR, а в стек помещается один байт в качестве адреса возврата. Если поменять модель использования памяти на Long Stack (+modsl0), то по умолчанию, для вызова подпрограмм будет использоваться инструкция CALL, и в стек будет помещаться двухбайтный адрес возврата. Если же переключиться на модель Long Stack (+modsl), то забегая вперёд, метка вызываемой функции должна будет иметь префикс f_ вместо простого символа подчёркивания. Во-вторых, вместо инструкции CALL будет использоваться расширенный вызов CALLF с 3-х байтным адресом.
В модели использования памяти Short Stack (+mods0), внешние функции из других модулей которые мы в дальнейшем будем добавлять, будут вызываться через «обычную» инструкцию вызова CALL.
2 Добавление ассемблерного файла в проект
Теперь заменим функцию задержки на ассемблерный вариант. Для этого добавим в проект файл с именем, допустим: «asm.s», в который будем складывать ассемблерные функции и обработчики прерываний на ассемблере.
Синтаксис ассемблера в COSMIC самый что ни на есть классический, т.е. после метки должно идти двоеточие, шестнадцатеричное число начинается с префикса «0x»(не совсем канон, да), а комментарий должен начинаться с точки с запятой. В макросы я вдаваться не хочу, т.к. предполагаю, что ассемблер будет использоваться только для самого необходимого.
Содержимое asm.s пусть пока будет таким:
Если функция вызывается из Си-программы, то метка должна начинаться с символа подчёркивания. Директива xref делает метку видимой компоновщику, а директива switch указывает рабочий сегмент.
В main.c заменим функцию delay объявлением внешней функции:
В главном цикле, в параметре вызова функции delay(), заменим абстрактное число 0xffff на 1000, т.е. на одну секунду.
Если в функции указать более одного параметра, то первый будет по прежнему помещён в Х регистр, а второй будет передан через стек.
Хочу обратить внимание, что если дизассмблировать прошивку с флагом «S», то мы увидим следующую картинку:
Здесь нам дизассеблировали только тот код который мы сами написали. Таблица векторов и библиотеки COSMIC отображались массивом данных, т.к. там нет отладочной информации. Можно использовать флаг D вместо S для полного дизассемблирования, но тогда на выходе получим «портянку» без указания периферийных регистров и сегментов.
3 Добавление ассемблерных обработчиков прерываний
Теперь займёмся прерываниями. Предполагаем, что обработчики прерываний мы будем писать на ассемблере. Тогда первое за что следует взяться, это файл по умолчанию входящий в шаблон проекта: «stm8_interrupt_vector.c»
Ассемблер COSMIC также как ST-Assembler не знает о существовании инструкции INT, поэтому таблица векторов задана в виде массива констант: опкода инструкции INT, плюс адреса обработчика прерывания. Для удобства, я добавил комментарии к таблице:
В обработчик пустого прерывания NonHandledInterrupt я вставил строку:
Не могу сказать сколько раз меня это выручало. Если во время отладки перестаёт мигать светодиод главного цикла, значит я снова забыл вставить обработчик прерывания в таблицу векторов.
В качестве примера добавим функцию задержки на таймере TIM4. Тема не нова и была не раз уже разобрана: Задержка по таймеру TIM4, с обработчиком прерывания на ассемблере, STM8+SDCC+SPL: функции delay_ms() и delay_us() на таймере TIM4. Так что дело за техникой.
Для наглядности, я добавлю заголовочный файл stm8s_tim4.h с именованными константами из SPL. Там закомментированы функции, и добавлены маски битовых флагов из stm8s.h Скачать файл можно отсюда , а разместить его нужно будет в каталоге проекта.
Также в каталог проекта нужно добавить заголовочный файл tim4.h с объявлением функции delay_ms(uint16_t ms):
После этого, добавим в проект файл: «tim4.c» следующего содержания:
Далее, в ассемблерный файл «asm.s» добавим обработчик прерывания таймера TIM4:
Во-первых, обращаю внимание, что ассемблерные обработчики прерываний объявляются через директиву xdef, тогда как ассемблерные функции через директиву xref.
Во-вторых, здесь используется мнемоника для регистра TIM4_SR, что означает, что в проект нужно добавить ещё один файл, на этот раз ассемблерный, с адресами периферийных регистров. С помощью потокового редактора sed, я конвертировал файл: «STM8S105C_S.h» из комплекта STVD в нужный формат. Скачать его можно здесь. Файл следует добавить в каталог проекта, после чего в начале «asm.s» вставить объявление: #include «stm8s105c_s.inc».
Теперь в начало файла: «stm8_interrupt_vector.c» добавим объявление внешнего обработчика:
После чего, у 23-го прерывания поменяем обработчик «NonHandledInterrupt» на наш «(interrupt_handler_t)tim4_handler».
Осталось в main.c добавить объявление заголовочного файла tim4.h, перед главным циклом разрешить прерывания командой: _asm(«rim»), и после, в главном цикле можно менять вызов функции delay(1000) на delay_ms(1000).
У этого простого примера есть интересный нюанс. Если из функции main() убрать инструкцию разрешения прерывания — «rim», то прерывание все-равно будет работать. Судя по отладчику, разрешение прерываний происходит после выполнения инструкции iret. При этом, каким-то образом этот обработчик вызывается. Могу предположить, что инструкция wfi разрешает прерывания.
4 Маппинг на физические адреса
Fun Art by Derek Ring — «He is Risen»
В COSMIC есть способ привязать переменные к реальным физическим адресам. Делается это так:
Здесь массив foo будет занимать адреса с 0х120 по 0х129 включительно.
Таким образом можно маппить не только адреса в ОЗУ но и адреса периферийных регистров. В документации настаивают, что бы при маппинге на регистры ввода-вывода, переменные объявлялись с модификатором volatile:
Переходя ближе к теме скажу, что с помощью модификатора @eeprom можно назначать переменные в области EEPROM-памяти. Примеры:
Маппить можно не только адреса но и отдельные биты. В COSMIC есть встоенный булевый тип: _Bool. С его помощью можно сделать например так:
Приведу реальный пример. Для этого в каталог проекта нужно будет добавить ещё один заголовочный файл из SPL stm8s_clk.h. Далее в начале функции main() заменим строку:
на код переключения на внешний кварц:
Так же нужно будет добавить обработчик второго(CLK) прерывания:
Я предполагаю что мы используем модель сборки по умолчанию т.е. «Debug» в которой оптимизация отключена. Теперь посмотрим как компилятор раскладывает цикл while (CLK_SWCR & CLK_SWCR_SWBSY):
Вышло на три инструкции. Если бы STM8 не имел битовых инструкций, то на этом бы и закончилось. Тип _Bool предполагает, что для обработки таких переменных используются битовые инструкции. В начале функции main() объявим булеву переменную:
После чего поменяем цикл:
В этом случае компилятор уже использует битовую инструкцию:
Если использовать модель сборки Release, то там оба варианта цикла будут реализованы через одну инструкцию btjt, здесь мне придраться не к чему.
Еще нужно учесть такой момент. Если уж мы используем в проекте заголовочные файлы SPL, то в свойствах проекта нужно будет задать версию своего чипа:
5 Особенности EEPROM/FLASH подсистемы в микроконтроллерах STM8
В STM8, запись в EEPROM практически не отличается от записи во флеш-память, меняются местами некоторые регистры и флаги, но алгоритм остаётся тем же. Поэтому, если подходить к вопросу алгоритмически, то флеш-память можно рассматривать как дополнительную EEPROM, или наоборот — EEPROM-память рассматривать как довесок к флеш-памяти. Но не следует забывать, что ресурс флеш-памяти в 30 раз ниже чем у EEPROM.
Одномоментно записать можно: байт, слово или блок. Давайте разбираться, что есть что.
Карта памяти для Medium-Density чипов STM8S
- Блок — количество байтов которые можно стереть или записать одномоментно.
- Страница — некоторое количество блоков. В страницах считается размер областей UBC и PCODE(только для STM8L high-density).
- Слово — четыре байта.
- MASS — система защиты ППЗУ от нежелательного изменения.
- ROP — системы защиты памяти от нежелательного чтения. Устанавливается через Option Bytes.
- RWW — позволяет записывать в EEPROM не останавливая при этом работу ядра микроконтроллера. Не доступна на low-density чипах. Запись во флеш-память останавливает работу ядра микроконтроллера на все время записи.
- UBC — область пользовательского загрузчика. Размер задаётся в страницах через Option Bytes. Всегда начинается с адреса 0x8000. Таблица векторов соответственно переносится на размер области UBC. UBC закрыта для записи через IAP
- PCODE — имеется только в STM8L medium и high density- чипах. Защищённая от записи и чтения область для размещения проприетарного кода. Доступ к этому участку памяти можно получить только через программное (TRAP) прерывание.
- IAP — программный программатор, пользовательский загрузчик или любое другое изменение ППЗУ программными средствами.
- ICP — аппаратный программатор использующий протокол SWIM.
Область EEPROM в STM8S чипах начинается с адреса 0х4000, в STM8L с 0х1000. Ниже приводится карта памяти для STM8S Medium Density чипов:
Карта памяти для STM8L high-density чипов:
Кстати, в STM32 чипах нет EEPROM памяти вообще. Во флеш там можно писать только страницами. Страница там если не ошибаюсь, равна одному килобайту. И если вам нужно будет записать меньшее количество байт, содержимое страницы придётся куда-то сохранить перед стиранием. Так что, когда вам будут говорить, что STM32F0xx тоже самое что STM8 только лучше, не верьте.
Ок. Я пока предлагаю не трогать такие штуки как UBC и PCODE. Если мы в Option Bytes выставим какое-либо значение UBC отличное от нуля, то лишимся возможности отладки. Значение же PCODE байта вообще выставляется только однажды, сбросить его потом не получится.
Во всех чипах с размером флеш-памяти 16 Кбайт и выше имеется встроенный загрузчик. Он начинается с адреса 0x6000. Блок-схема с алгоритмом его работы представлена ниже:
В отличие от 103-го чипа, в начале трассировки прошивки отладчиком мы попадаем не в таблицу векторов, а именно в этот загрузчик. Его можно увидеть в окне дизассемблера:
Здесь в начале проверяется, есть ли по адресу 0х8000 опкод инструкций INT или JRF. Затем идёт проверка, включён ли bootloader в Option Bytes и нет ли установки ROP. После этого идёт или переход или на адрес 0x8000 или на код загрузчика. В этом фрагменте ещё можно увидеть код переключения на внешний кварц и снятие защиты на запись (MASS) с флеш-памяти и EEPROM. Код загрузчика в STM8L151C8 немного отличается, но суть та же:
6 Регистры подсистемы EEPROM/FLASH
Регистров здесь немного, предлагаю их бегло рассмотреть:
Первый управляющий регистр, сегодня нам не придётся им пользоваться, так что можно принять просто к сведению. Флаги HALT и AHALT позволяют отключать питание с флеш-памяти в режимах энергосбережения halt и active-halt. IE — разрешает прерывание, FIX переключает режим стандартного программирования на быстрое при режиме записи блоками. По умолчанию там стоит ноль, т.е. стандартный режим программирования. Им мы и будем пользоваться.
В L-серии на третьем битe висит EEPM, а на втором WAITM. Они отвечают за перевод флеш-памяти в режим пониженного энергопотребления IDDQ. EEPM делает возможным перевод флеш-памяти в режим пониженного энергопотребления при выполнении программы из ОЗУ, а WAITM переводит флеш-память в режим пониженного энергопотребления IDDQ при ждущем и спящем режимах.
Второй управляющий регистр уже поинтереснее, он переключает режимы записи. OPT — защищает область Option Bytes которая расположена в последнем блоке EEPROM (у S-серии). WPRG — включает 4-х байтный режим записи. ERASE — стирает содержимое блока в «быстром» режиме блочной записи. FPRG — включает «быстрый» режим блочной записи, PRG Включает режим стандартной блочной записи.
Регистру FLASH_CR2 соответствует комплементарный регистр FLASH_NCR2. Т.е. изменяя один регистр, следует изменить и другой, до того как что-то писать в ППЗУ.
Ещё одна комплементарная пара регистров, которых мы не будем сегодня касаться: FLASH_FPR и FLASH_NFPR. Определяет размер секции защищённой от записи — UBC.
FLASH_PUKR — регистр снятия защиты MASS с флеш-памяти. Для с снятия защиты потребуется последовательно записать в этот регистр числа 0х56 и 0xAE. При записи неверной комбинации, содержимое флеш-памяти будет блокироваться до следующего Reset.
FLASH_DUKR — регистр снятия защиты MASS с EEPROM. Для с снятия защиты потребуется последовательно записать в этот регистр числа 0хAE и 0x56. При записи неверной комбинации, содержимое EEPROM будет заблокировано до следующего Reset.
Статусный регистр. Флаг HVOFF выставляется и сбрасывается аппаратно во время записи в EEPROM. Служит для определения окончания записи в EEPROM для чипов с RWW. В чипах без RWW следить за этим флагом не надо, там выполнение программы «подвешивается» на время записи. DUL — флаг разблокировки EEPROM. PUL Флаг разблокировки флеш-памяти. EOP — конец операции записи. Флаг устанавливается аппаратно, и сбрасывается программно путём чтения регистра или при новых операциях записи/стирании. Служит для определения момента завершения записи во флеш-память. Флаг WG_PG_DIS устанавливается при попытке писать в защищённую область. Используется для определения ошибок в работе.
7 Запись в EEPROM средствами СOSMIC
COSMIC нам существенно упрощает работу когда мы хотим что-то записать в EEPROM или FLASH память.
Для работы с EEPROM/FLASH нам понадобится ещё один заголовочный файл c константами из SPL: stm8s_flash.h Скачать его можно здесь или сделать самому (требуется небольшая доработка). Файл нужно будет поместить в каталог проекта.
Далее добавим в проект файл с исходным кодом следующего содержания:
Здесь функция write_to_eeprom() записывает некие байты в массив data, определённый в области EEPROM. Используется одно-байтный режим работы.
Остаётся добавить объявление функции void write_to_eeprom( void ); в main.c и ПЕРЕД(!) главным циклом поставить вызов этой функции. Замечу, что в процессе отладки бесполезно отслеживать содержимое eeprom. Когда после прошивки мы откроем ST Visual Programmer и считаем содержимое EEPROM то увидим результат работы:
Программа работает должным образом. Но если мы посмотрим на дизассемблерный листинг:
То можно увидеть, что для операции присваивания используется вызов непонятной функции (выделено красным). Вызываемая функция выглядит так:
Все бы ничего, но если вы планируете записывать данные блоками, то весь код должен будет выполняться из ОЗУ. А если функция располагающаяся в ОЗУ будет вызывать подпрограмму находящуюся на флеше в который же и пишется, то ничего хорошего из этого не выйдет.
Теперь попробуем изменить тип данных массива data c uint8_t на uint16_t. Тогда строка кода data[i]=(uint16_t)i; обернётся вызовом такой функции:
Здесь я красным обвёл финальную часть функции, где устанавливается 4-х байтный режим записи в регистре FLASH_CR2, после чего идёт сама запись в EEPROM. Результат можно увидеть опять же в STVP:
Первое, что здесь бросается в глаза, то что двухбайтные числа пишутся в обычном, а не перевёрнутом порядке, когда пишется сначала младший байт, а потом старший. Это нужно учитывать, когда будете читать с ППЗУ.
В целом, нужно отдать должное COSMIC, — это замечательный инструмент для работы с EEPROM. Им удобно пользоваться если вам нужно скинуть туда какие-то небольшой объем данных, например: показания сенсора, станции FM-модуля, но если мы хотим писать во флеш-память и EEPROM блоками, то нам нужно научиться обходиться без его помощи.
8 Безопасное снятие защиты MASS, одно-байтный режим записи в EEPROM/FLASH
Undefined behavior (UB) is the result of executing computer code whose behavior is not prescribed by the language specification to which the code adheres, for the current state of the program. (c) wiki
Но прежде всего мне хотелось бы коснуться, вопроса о защите от дребезга питания или от неопределённого поведения при разблокировке MASS. На мой взгляд, последовательная запись ключей в регистр разблокировки не очень хорошая идея. Будет лучше, если объявить две глобальные переменные которые будут хранить в оперативной памяти значения ключей. Глобальные переменные инициализируются в начале работы программы, и перед их использованием должно пройти какое-то время. Это даёт некоторую защиту от неопределённого поведения. Идею можно развить и сделать как в защите серийных номеров от дизассамблирования, когда номера хранятся не в чистом виде, а являются результатом вычисления какой-нибудь функции. Допустим, можно объявить глобальный массив, со значениями которого будут производиться некие арифметические операции, в результате выполнения которых будут выдаваться числа: 0хАЕ и 0х56. В идеале можно написать генератор псевдослучайного числа, который при инициализации тем или иным значением будет выдавать нужные числа.
Писать генератор псевдослучайного числа мне показалось чересчур и поэтому в онлайн-редакторе я на скорую руку состряпал пару функций:
Здесь первая функция использует 140 итераций, вторая — 90. Ассемблерный листинг функций можно посмотреть под спойлером.
Если весь этот код выполняется успешно, то ППЗУ разблокируется. Если нет, то происходит блокировка до следующего Reset.
Самописная функция write_to_eeprom() у меня получилась такой:
где data — это следующий массив:
В отличии от варианта COSMIC’а, здесь ожидается установка флага HVOFF вместо EOP (так делается в SPL) в качестве признака окончания записи. Кроме того, здесь анализируется флаг FLASH_IAPSR_WR_PG_DIS на случай, если что-то пойдёт не так. В low-density чипах без RWW ждать окончания записи не нужно. Там на время записи приостанавливается работа CPU. Учтите, что прерывания тоже перестают работать, так что там можно писать байты без всяких задержек. Я сам не побывал работать с EEPROM на STM8S103, но здесь: «STM8S EEPROM надо ли ждать EOP флага после записи 1 байта? — Форум разработчиков электроники ELECTRONIX.ru» утверждается, что все именно так и обстоит.
Из дизассемблерного листинга видно, что никаких внешних подпрограмм больше не вызывается, что нам и нужно было добиться.
Функция записи во флеш-память будет не сильно отличаться:
9 Четырехбайтный режим записи в EEPROM/FLASH
Четырех-байтный режим записи устанавливается флагами WPRG в регистре FLASH_CR2 и NWPRG в FLASH_NCR2. От однобайтного режима отличается увеличением скорости записи в четыре раза, т.к. за одну операцию записи пишется разом четыре байта.
Алгоритм практически аналогичен предыдущим примерам:
где data — это следующий массив:
Обращаю внимание, что здесь запись двухбайтных данных организована в классическом «перевёрнутом» порядке. Т.е. сначала пишется младший байт, затем старший.
10 Блоковый режим записи в EEPROM/FLASH. Копирование кода в ОЗУ и выполнение его оттуда средствами COSMIC
Artwork by Su Jian
Последний самый интересный режим записи — блоковый режим. Позволяет за раз записать в ППЗУ целый блок — 128 байт. Имеется два варианта этого режима: стандартный и быстрый. Я буду рассматривать стандартный режим как самый удобный, требующий меньше телодвижений и следовательно, с ним меньше шансов наделать ошибок. Быстрый режим очень походит на режим записи флеш-памяти в STM32.
Самое сложное в этом режиме то, запись должна выполняться из ОЗУ. Данные должны быть выровнены по границам блока. В STM32 есть регистр в котором можно выбирать записываемую страницу, в STM8 местоположение блока придётся рассчитывать самим.
Я ничего не нашёл в документации относительно особенностей чипов 208/207/007, но анализ кода SPL для этих чипов говорит о том, что там за одну операцию можно записать целую страницу, это четыре блока по 128Б т.е. 512 байт. Сам лично пока не проверял.
Копирование кода в ОЗУ подробно рассмотрено в руководстве: Application note — AN2659, — глава пятая — «Configuring the Cosmic compiler for RAM code execution».
Весь процесс примерно делится на три этапа.
Этап первый. Перед началом функций write_to_eeprom() и write_to_flash() нужно поставить объявление новой секции: #pragma section(FLASH_CODE)
После функций write_to_eeprom() и write_to_flash() нужно будет объявить завершение новой секции: #pragma section()
Этап второй. В настройках проекта перейти на вкладку компоновщика, выбрать там категорию Input, и щёлкнув правой кнопкой мыши по сегменту «RAM» добавить туда новую секцию «.FLASH_CODE» (точку в префиксе не забудьте):
Щёлкнув правой кнопкой по ряду «Option» добавленной секции, измените пустое поле на «-ic». Это даст линковщику команду считать данную секцию перемещаемым кодом. При ее линковке не будет использоваться абсолютная адресация (проверьте!).
Этап третий. Теперь нам понадобится механизм копирования секции в OЗУ. В COSMIC для этого существует готовая функция: int _fctcpy(char name). она поставляется в ассемблерном виде, в файле fctcpy.s. Его нужно будет скопировать в папку проекта и далее добавить к проекту.
Осталось дело за малым. Нужно добавить объявление функции в начало файла main.c:
В качестве параметра функции используется первая буква в названии секции. В начало функции main() вставим вызов этой функции: _fctcpy(‘F’); и дело в шляпе. Это всё.
Пример функций write_to_eeprom() и write_to_flash() реализующих запись двух блоков в EEPROM и флеш-память приведён ниже:
Где массив data в этот раз начинается с начала блока:
В основе лежит алгоритм однобайтной записи, просто добавлен ещё один цикл для записи блока.
Проверяем. Ставим точку останова на вызове функции write_to_eeprom() и смотрим куда ведёт вызов функции:
Вызов идёт в ОЗУ, как и должно быть. Прыгаем, и в окне дизассемлера видим код функций write_to_eeprom() и write_to_flash() перенесённых сюда с помощью _fctcpy().
Результат работы как обычно можно посмотреть в STVP. На этом всё. Остался не рассмотренным быстрый режим блочной записи. Если когда-нибудь соберусь писать статью по написанию своего загрузчика, то начну именно с этого режима.
В заключении могу ещё рекомендовать почитать Executing code from RAM on STM8 | lujji, где автор исследовал возможности перемещения исполняемого кода в ОЗУ для компилятора SDCC. Мне лично система показалась чересчур «костыльной», но возможно вы будете другого мнения.
Как записать переменную во флеш память 80c51f120
Встроенная EEPROM стремительно заканчивается, а данные куда-то записывать надо. Знакомая ситуация, не правда-ли?
Что мы обычно делаем в таких случаях? Ставим внешнюю EEPROM, флеш или SD карточку на 32 гига. Это оправдано, если устройство достаточно сложное. А если оно состоит из одной тиньки и двух с половиной светодиодов? Тогда подключение внешней памяти грозит кардинальными изменениями в алгоритме, а может и пинов банально не хватит.
Но ведь у нас есть своя флеш память, которая в подавляющем большинстве случаев заполнена чуть менее, чем на половину. Отлично! Её и используем для записи данных.
Механизм записи во флеш память на первый взгляд немного запутанный. Но если присмотреться — все просто. Он состоит из трех основных частей:
— Очистка страницы памяти.
— Подготовка (закидываем данные во временный буфер)
— Запись.
Причем первые два пункта можно невозбранно менять местами.
Флеш память разделена на страницы. Размер страницы зависит от общего объема флеш памяти — смотри в даташите.
Например, для тини13 есть такая табличка:
Размер страницы 32 байта (16 слов), а всего таких страниц 32 штуки.
Так-же там написано, что в регистре адреса первые 4 бита (PC[3:0]) занимает адрес слова, а адрес страницы начинается с 5го бита. Значит, если нам надо записать адрес 3й страницы, то в регистр уйдет 3<<4.
При операциях со страницами (запись и очистка) от нас будут требовать номер страницы. А при работе со временным буфером — адрес слова в странице. К счастью, МК сам выделяет нужные части из адреса, который мы кидаем в регистр Z — поэтому можно не задумываться и пихать просто адрес слова в памяти.
При прошивке МК надо включить фьюз SELFPRGEN. Без этого ничего работать не будет.
1) Расчистка места.
Для очистки страницы памяти надо выполнить такую последовательность действий:
— Пихаем в регистр Z адрес. Из него МК выделит адрес страницы, а на первые 4 бита забьет.
— Поднимаем в регистре SPMCSR биты PGERS (Page Erase) и SELFPRGEN (Self Programming Enable).
— Быстро (в течении 4х тактов после записи в SPMCSR) выполняем команду SPM.
На время очистки страницы (а это около 4 мс) процессор подвисает.
2) Готовим данные к записи.
— Записываем в регистр Z адрес слова. Теперь все наоборот. МК будет ориентироваться по первым 4 битам адреса.
— В пару регистров R1:R0 пишем данные, которые хотим запихать в буфер.
— Поднимаем бит SELFPRGEN в регистре SPMCSR.
— Выполняем команду SPM.
… повторить, пока не будет заполнен весь буфер.
У этого буфера есть одна нехорошая особенность:
Записать два раза в одну ячейку нельзя. Т.е. если мы хотим переписать уже записанные в буфер данные, то его придется сначала очистить. Для очистки надо просто поднять бит CTPB в SPMCSR.
Алсо, буфер очищается сам после записи во флеш или перезагрузки.
Забавно так-же то, что данные в буфере будут потеряны, если записать что-то в EEPROM. Как-то хитро память устроена, не находите?
Последнее особенно актуально, если мы собираемся наполнять буфер постепенно, прерываясь на другие задачи (типа записи в EEPROM).
3) Пишем!
— Запихиваем адрес страницы в Z
— Устанавливаем биты SELFPRGEN и PGWRT.
— Выполняем SPM.
При записи страницы МК зависает на те-же 4 мс.
Пример устройства.
Для примера я хотел замутить термологгер на базе тини25. С записью температуры в флеш, работой со встроенным термометром и выдачей лога в UART. Но выяснилось, что UART у тини25 нету, а «родной» термометр уж больно кривой. Поэтому будем делать что по-проще. Например, записывать во флеш память напряжение, измеренное АЦП.
Алгоритм дешевого и сердитого логгера такой:
0) Инициализация периферии. Устанавливаем адрес записи на первую пустую страницу.
1) Ждем 1 сек — простым циклом, безо всяких таймеров и прерываний. Дешево и сердито.
2) Запускаем АЦП и ждем, пока он закончит преобразование.
3) Если мы начали писать новую страницу, то
3.1) Очишаем её.
4) Записываем во временнный буфер (по текущему адресу) значение АЦП.
5) Если мы уже заполнили весь буфер, то
5.1) Записываем его во флеш память.
6) Инкрементируем адрес записи.
7) Если дошли до конца памяти — затупляем в вечном цикле. Иначе идем на шаг 1.
Код на ассемблере:
Проверять код в протеусе бесполезно — не работает
Но в железе все прекрасно крутится.
Небольшое дополнение: RWW и NRWW
В МК с поддержкой бутлоадера на команду SPM накладываются ограничения. Её можно выполнить только из BLS (Bootloader section) — области памяти в самом конце флеша, где живут бутлоадеры. Размер этой области устанавливается фьюзами BOOTSZ.
Вообще, в контроллерах с поддержкой бутлоадера, весь флеш поделен на 2 части: RWW (Read-While-Write) и NRWW (No Read-While-Write). NRWW распологается в конце памяти и занимает место отведенное под бутлоадер. Т.е. размер этой области памяти равен максимальному размеру BLS. Остальное место (с начала флеша и до NRWW) занимает RWW область.
Разница между RWW и NRWW заключается вот в чем: Если бутлоадер (который находится в NRWW) пишет или стирает страницу в RWW области, то МК не останавливается на время выполения этой операции. Бутлоадер будет продолжать работать, пока страница памяти в RWW записывается или стирается. По этому поводу даже придумали прерывание SPM_RDY, которое возникает по завершении операции.
А если бутлоадер попытается записать данные в секцию NRWW (это не обязательно должна быть секция самого бутлоадера), то МК замрет на 4мс, пока производится запись.
STM32 — память
В статье рассказано про организацию памяти микроконтроллера stm32, использования flash’а для хранения пользовательских данных, и про всякие пользовательские биты/биты защиты.
Конечно же в вашем микроконтроллере нет никаких гигабайтов памяти однако камень 32-х битный, а значит можно адресовать (обратится по адресу) до 4ГБ. Это стандартизированная модель обеспечивающая переносимость кода между различными микроконтроллерами stm32, а так же дающая возможность подключать внешнюю память и совершенно спокойно обращаться к ней. То же самое касается и периферии, производитель микроконтроллеров на базе ядра Cortex, может напихать туда оооочень много всего и при этом не беспокоиться о нехватке адресов. Что же касается размера, то некоторых адресов просто нет, вместо них зарезервированные пустоты. Если обратится к зарезервированной области, то произойдёт аппаратный сбой процессора.
Для примера часть адресов блока Peripheral микроконтроллера F103…
Reference manual — rm0008 стр. 51
Тут видны адреса некоторых таймеров, портов GPIO, и другой периферии. Все они находятся выше 0x40000000 и ниже 0x5fffffff.
Если попробовать прочитать зарезервированный адрес…
То получим Hard Fault…
Прежде чем продолжать, надо скачать программу STM32CubeProgrammer с помощью которой можно ковыряться в микроконтроллере. Прога хороша тем, что есть варианты для и
Запускаем и жмём Connect, подключатся можно через ST-Link или UART…
Нажимаем верхнюю левую кнопку (с карандашиком), и на вкладке Device memory видим программу залитую в камень. Можно указать адрес, с которого читать (по умолчанию стоит начальный адрес флеш-памяти) и сколько байт прочитать. Кнопочка Read считывает данные и показывает как на картинке. Если вместо Read выбрать Save As, то программа сдампится в указанный файл (надо только размер считываемых данных правильно указать, а то по умолчанию 1К).
Если нажать вкладку Open file, то можно открыть какой-нибудь .hex или .bin файл прямо в программе.
Кнопка открывает интерфейс для загрузки прошивки в МК…
Выбираем нужный файл, очищаем флеш полностью (Full chip erase) или выборочные страницы, и жмём Start Programming.
— что-то связанное с внешним загрузчиком или внешним девайсом, не знаю, не вникал.
— очищает флеш.
В нижней части лог подключения, а справа-снизу полезная информация о вашем МК. Здесь интересны два пункта…
Device — серия микроконтроллера и плотность (Medium-density).
Некоторые микроконтроллеры stm32, в рамках одной серии, например популярный F103, он же BluePill, выпускается не только в разных корпусах с разным количеством «ножек», но и разной плотности (объёмом флеш-памяти).
Connectivity line devices — это, на сколько я понимаю, микроконтроллеры которые умеют работать с интернетом. Programming manual — PM0075 стр. 5.
Device ID — код, который зашит в системный бутлоадер, и по нему ST-Link определяет что это за микроконтроллер.
Коды можно посмотреть в AN2606 стр. 307.
Теперь нажмём кнопочку чтоб появился список Option bytes…
… и вернёмся к изучению памяти.
Всё что выше Peripheral особого интереса не представляет, SRAM — это оперативная память, а Code Area выглядит так…
Option bytes — здесь находятся различные биты для настройки МК. Защита от чтения/записи, включение/отключение вачдогов, и несколько пользовательских битов для хранения какой-либо инфы.
Бит RDP — если установить, то нельзя будет ни прочитать прошивку, ни загрузить новую. Чтобы установить, нужно поставить галочку, нажать кнопку Apply и обресетить МК. Снимается так же. При снятии защиты, существующая в МК прошивка будет удалена. Такой механизм гарантирует невозможность прочесть прошивку в залоченом камне.
В более серьёзных камнях RDP имеет несколько уровней…
AA — нет защиты.
BB — защита включена.
СС — микроконтроллер безвозвратно заблокирован от чтения/записи. Warning! Если установите этот уровень защиты, то больше никогда не сможете прошить МК. Операция необратима.
Тут комментировать особо нечего, думаю и так всё понятно. Однако очень не рекомендую снимать галочку с вачдога — камень будет постоянно ресетится. Настройки применяются так же, галочку сняли/поставили, и кнопка Apply. В более мощных МК есть доп. настройки.
Сюда можно сохранить какие-то свои данные, правда не много
Защита от записи конкретных страниц флеш-памяти. К этому вернёмся чуть позже.
System memory — это область в которой располагается системный загрузчик (bootloader), он зашивается на заводе при изготовлении, его нельзя ни удалить, ни изменить.
Bootloader это такая небольшая программка, которая позволяет прошивать микроконтроллер через различные интерфейсы. Если подтянуть пин BOOT_0 к «земле» и нажать ресет, то загрузиться bootloader и будет ожидать поступления новой прошивки. Простенькие камни, типа F103, можно прошивать только через USART, а более «крутые» позволяют делать это и через другие интерфейсы. Посмотреть это можно в AN2606 стр. 25. Например вот…
Flash — это область энергонезависимой памяти, в которой хранится ваша прошивка. Выглядит эта область следующим образом…
RM0008 стр. 55. (Рис. 1)
Information block — это описанные выше System memory и Option Bytes.
Flash memory interface registers — это регистры отвечающие за работу Flash-памяти, они находятся в области Peripheral .
Main memory — это и есть наша Flash-память.
Flash -память организована постранично, а размер страницы колеблется от одного до нескольких килобайт, в зависимости от серии микроконтроллера. У F103 (Рис. 1) размер равен 1К, а вот например у F4 выглядит так…
Часть страниц по 16К, одна 64К, а часть по 128К. Здесь страницы называются секторами.
OTP area — это биты защиты, которые можно записать только один раз (One-Time-Programmable). Больше ничего про это сказать не могу — у меня нет такой платы.
Тут есть один любопытный момент. Компания ST выпускает два почти одинаковых камня F103, один F103C8хх c объёмом флеш-памяти 64К (BluePill), и F103CBхх c объёмом 128К. Однако на самом деле у обоих этих камней объём флеш-памяти равен 128К (128 страниц по 1К).
Во-первых в мануале нет камней с 64-мя страницами (см. рис. 1), а во-вторых, не смотря на то, что ST-Link показывает объём 64К, у всех моих F103C8хх читаются и пишутся все 128 страниц. То есть программу размером больше 64К залить через TrueStudio не удаётся, а вот производить чтение/запись этих областей из своей программы можно. Да и STM32CubeProgrammer их тоже совершенно спокойно читает…
127-я страница на камне F103C8хх.
Выше я говорил что мы вернёмся к вопросу защиты конкретных страниц от чтения/записи…
Биты WRPх защищают сразу по несколько страниц. Количество защищаемых страниц зависит от плотности (density) МК.
Вот вырезка из Programming manual стр. 21…
У low-density используется только WRP0 и защищает весь флеш, а у medium-density можно делать это выборочно. Зачем нужны остальные биты (WRP4 и т.д.) я так и не понял, может они зарезервированы.
Такой способ выборочной защиты очень удобен тем, что можно заблокировать область где находится программа, а остальное использовать для хранения каких-то данных и не боятся затереть программу, ну или наоборот, защитить какие-то данные, которые вы положили например в конец флеша.
CCM SRAM — у некоторых микроконтроллеров есть дополнительная оперативная память под названием Core Coupled Memory. Эта память подключена непосредственно к ядру, благодаря чему скорость доступа и исполнение кода будет быстрее чем в обычной SRAM. Соответственно в CCM выгодно размещать всякие статистические переменные/массивы, а при желании можно перенести туда стек/кучу/.data/.bss.
Cледует помнить, что доступ к CCM имеет только процессор, а значит DMA не сможет к ней обратиться. CCM бывают разные для разных микроконтроллеров, поэтому прежде чем начинать использование следует изучить документацию. Ещё инфа, и ещё инфа.
В качестве примера я перенёс пару массивов в ССМ (среда TrueStudio, камень F303)…
Объявляем массивы глобально:
Чтоб компилятор не «оптимизировал» эти массивы, сделайте что-нибудь с ними, например выведите на печать.
Вуаля, они лежат где надо…
Прежде чем говорить о переносе стека/кучи/.data/.bss в ССМ, я очень кратко и обобщённо поясню что означают эти термины:
Стек — область памяти в ОЗУ, куда сохраняется адреса программы в момент возникновения прерывания или перехода в другую функцию.
Пошагово выглядит это так:
Наша программа работает-работает (указатель «бежит» последовательно по адресам).
Происходит прерывание — указатель перепрыгивает в обработчик этого прерывания.
Адрес, где находилась программа в момент возникновения прерывания, записывается в стек (чтоб запомнить куда возвращаться).
После того как обработка прерывания закончится, из стека вытаскивается адрес на который нужно вернуться.
То же самое происходит при переходе из одной функции в другую, например из в
Технически, стек можно представить в виде стопки книг где нельзя взять вторую сверху книгу не сняв предварительно первую. То есть стек работает по принципу LIFO буфера (Last In First Out) — «последним пришёл, первым вышел». Такая организация очень хорошо себя оправдывает. Представьте себе такую ситуацию:
Указатель перепрыгнул из функции main() в bla_bla() .
В стек записался адрес того места откуда выпрыгнул указатель.
Идёт обработка функции bla_bla() и в этот момент происходит прерывание.
В стек записывается (ложится поверх предыдущего) ещё один адрес, и указатель переходит в обработчик прерывания.
В результате на стеке лежат два адреса — адрес, на котором была приостановлена функция main() , а поверх него адрес на котором приостановлена функция bla_bla() .
По окончании обработки прерывания система лезет в стек, «снимает со стопки» самый последний адрес, переходит по нему и заканчивает выполнение функции bla_bla() с того места где она была прервана. Больше этот адрес не нужен и он выкидывается в помойку, на стеке остаётся адрес приостановки функции main() .
Как только функция bla_bla() завершается, система опять лезет в стек и «снимает» адрес приостановки функции main() . Адрес опять же летит в помойку, а функция main() продолжает выполняться. Таким образом, снимая «книжку за книжкой» система не сможет вернуться куда-то не туда и нарушить целостность программы.
Помимо адресов, в стек сохраняются локальные переменные функций и параметры передающиеся в функции. То есть в момент вызова функции, вместе с адресом для возврата, на стек кладутся ещё и локальные переменные, которые есть в этой функции. Как только функция отработает, эти переменные будут сняты со стека и уничтожены.
Куча — эта область ОЗУ, которая используется для динамического выделения памяти в процессе работы программы. То есть, когда вы делаете malloc/calloc/realloc, то память выделяется на куче.
bss — область ОЗУ, сюда помещаются не инициализированные глобальные переменные (uint8_t var;).
data — область ОЗУ, сюда помещаются инициализированные глобальные переменные (uint8_t var = 0;).
text — Flash, здесь лежит сама программа.
Выглядит всё это хозяйство следующим образом…
Стек начинается от самого большого адреса и при увеличении размера ползёт вниз (записывая данные в меньшие адреса), а куча наоборот, при увеличении размера ползёт вверх, поэтому при выделении памяти на куче нужно следить чтоб эти области не налезли друг на друга.
Чтоб лучше понять иллюстрацию выше, откройте файл STM32F103C8_FLASH.ld и найдите там такие строки…
Нас интересует переменная _estack , которая указывает на конец оперативной памяти. У микроконтроллера stm32F103c8 оперативная память располагаться в области от 0x20000000 до 0x20005000 , что равно 20480 байтам (20Кб). Визуально это выглядит так…
Вся наша оперативка находится внутри узенькой полоски, а всё что выше не существует. То есть стек, куча, bss и data находится внутри этой полоски, а text в жёлтой области.
Посмотреть сколько места занимают некоторые данные можно в среде разработки. Вот картинка из TrueStudio…
dec и hex это общий размер первых трёх значений.
Чтоб перенести какую-то из этих областей в ССМ, нужно подкорректировать файл компоновщика (или линковщика, называйте как вам удобнее) с расширением .ld …
Находим там интересующие нас блоки…
Меняем у каких-нибудь блоков, или у всех сразу, слово RAM на CCMRAM …
В результате получаем — до:
Запись данных во флеш
У большинства микроконтроллеров stm32 (кроме серии L0) нету EEPROM, поэтому сохранять пользовательские данные приходится во флеш-памяти. Количество циклов записи 10000.
Чтобы записать во флеш какие-то данные нужно вначале её очистить (во время очистки все биты заполняются единицами — 0xFF). Очистка происходит постранично/посекторно.
Очистка одной страницы на F103…
Объявляем структуру в которую заносятся параметры очистки:
TypeErase — что хотим очистить, какую-то конкретную страницу (или несколько), или всю флеш полностью.
PageAddress — начальный адрес страницы, которую хотим очистить. Адреса можно посмотреть в мануале (правда там не все видны), либо посчитать самостоятельно, либо в примерах Куба — STM32CubeFx в файле main.h (для F103 — /STM32Cube_FW_F1_V1.8.0/Projects/STM32F103RB-Nucleo/Examples/FLASH/FLASH_EraseProgram/Inc/main.h). В конце есть ссылка на гитхаб с этим примером, там есть хедер (addr_pages.h) с задефайнеными адресами для BluePill.
NbPages — кол-во страниц для очистки. Если указать несколько, то они будут очищены начиная с адреса указанного выше.
Banks — у «жирных» камней память делиться на банки, поэтому нужно указать в каком именно банке находятся страницы.
HAL_FLASH_Unlock() — снимаем блокировку стирания/записи во флеш (для чтения этого делать не нужно). Это не относится к битам WRPx, если они установлены для текущей страницы, то стереть/записать страницу не получится.
Ну, а дальше стираем страницу/страницы, и возвращаем блокировку. Если что-то пойдёт не так, то программа выведет ошибку и зациклится.
После этой операции вся страница готова для записи. Необязательно записывать всю страницу за один раз, можно добавлять записи по мере необходимости, при условии что новые данные не будут записываться поверх старых.
Почему нужно очищать (заполнять значениями 0xFF) память?
Дело в том, что когда происходит запись байта в ячейку, то биты в этой ячейки не просто так берут и перезаписываются, а совершается операция логического «И» над тем что есть в ячейке и новым значением.
Наглядно это выглядит так. Ячейка у нас очищена (биты заполнены единицами) и мы записываем в неё число 7 (0х07)…
Выполняется логическое «И», и в ячейку записывается нужное нам число 7.
А теперь допустим что мы хотим записать в эту же ячейку число 13 (0x0d) поверх старого значения…
Выполняется логическое «И» между старым значением 0х07 и новым 0x0d. В результате вместо желаемого числа 13, в ячейку записывается число 5.
Запись
У разных МК можно записывать разную длину «слова», например у F103 можно записать «слова» размером 16, 32 и 64 бита. Подсмотреть это можно в файле stm32f1xx_hal_flash.h …
Запишем два 16-ти (FLASH_TYPEPROGRAM_HALFWORD) битных числа в начало только что очищенной страницы…
Разблокируем флеш, указываем адрес начала страницы (не обязательно начало, можно в любое место), записываем во флеш массив из двух чисел, а в цикле увеличиваем адрес на два. После этого блокируем память.
Если записывать 32-х (FLASH_TYPEPROGRAM_WORD) битное число, то увеличим адрес на четыре, а если 64-х (FLASH_TYPEPROGRAM_DOUBLEWORD) битное, то на 8. Если в дальнейшем захотите добавить в эту страницу ещё что-то, то нужно запомнить адрес.
Идём в STM32CubeProgrammer, вписываем адрес 127-ой страницы и смотрим чего понаписали…
Всё окей. Не забывайте нажимать Disconnect
Прочитаем то, что записали…
Разблокировать не надо, а адрес опять же увеличиваем на два. Получаем ожидаемое…
Чтоб записать 8-ми битное значение (не смотря на то, что производитель не предоставил такой возможности), надо просто записывать по два байта в одно 16-ти битное «слово» со сдвигом. Всё происходит почти так же как и с 16-ти битным числом…
Увеличиваем адрес на 4 (чтоб добавить данные вслед за предыдущими), записывать будем массив, размер которого должен быть кратен двум (для этого сделана проверка), а в цикле запихиваем два символа в одно 16-ти битное «слово» и отправляем по адресу.
Читать можно по одному 8-ми битному символу.
Вот и всё, остаётся придумать как хранить адрес (со смещением) последней записи для добавления последующих, чтобы не мучить одну и туже ячейку, и не прикончить её в самом скором времени. Вот вариант как смещать очередную запись.
Всем спасибо
Как записать переменную во флеш память 80c51f120
Как записать переменную во флеш память 80c51f120
TINY_F, NEAR_F, SWITCH, DIFUNCT, CODE, FAR_F, HUGE_F, INITTAB, TINY_ID, NEAR_ID и CHECKSUM.
Функция | Аргументы | Возвращает |
ReadFlashByte() | MyAddressType flashAdr | unsigned char |
ReadFlashPage() | MyAddressType flashStartAdr, unsigned char *dataPage | unsigned char |
WriteFlashByte() | MyAddressType flashAddr, unsigned char data | unsigned char |
WriteFlashPage() | MyAddressType flashStartAdr, unsigned char *dataPage | unsigned char |
RecoverFlash() | void | unsigned char |
STM32 — память
Конечно же в вашем микроконтроллере нет никаких гигабайтов памяти однако камень 32-х битный, а значит можно адресовать (обратится по адресу) до 4ГБ. Это стандартизированная модель обеспечивающая переносимость кода между различными микроконтроллерами stm32, а так же дающая возможность подключать внешнюю память и совершенно спокойно обращаться к ней. То же самое касается и периферии, производитель микроконтроллеров на базе ядра Cortex, может напихать туда оооочень много всего и при этом не беспокоиться о нехватке адресов. Что же касается размера, то некоторых адресов просто нет, вместо них зарезервированные пустоты. Если обратится к зарезервированной области, то произойдёт аппаратный сбой процессора.
Reference manual — rm0008 стр. 51
Прежде чем продолжать, надо скачать программу STM32CubeProgrammer с помощью которой можно ковыряться в микроконтроллере. Прога хороша тем, что есть варианты для и
Нажимаем верхнюю левую кнопку (с карандашиком), и на вкладке Device memory видим программу залитую в камень. Можно указать адрес, с которого читать (по умолчанию стоит начальный адрес флеш-памяти) и сколько байт прочитать. Кнопочка Read считывает данные и показывает как на картинке. Если вместо Read выбрать Save As, то программа сдампится в указанный файл (надо только размер считываемых данных правильно указать, а то по умолчанию 1К).
Кнопка открывает интерфейс для загрузки прошивки в МК…
Выбираем нужный файл, очищаем флеш полностью (Full chip erase) или выборочные страницы, и жмём Start Programming.
— что-то связанное с внешним загрузчиком или внешним девайсом, не знаю, не вникал.
— очищает флеш.
Connectivity line devices — это, на сколько я понимаю, микроконтроллеры которые умеют работать с интернетом. Programming manual — PM0075 стр. 5.
Теперь нажмём кнопочку чтоб появился список Option bytes…
… и вернёмся к изучению памяти.
Бит RDP — если установить, то нельзя будет ни прочитать прошивку, ни загрузить новую. Чтобы установить, нужно поставить галочку, нажать кнопку Apply и обресетить МК. Снимается так же. При снятии защиты, существующая в МК прошивка будет удалена. Такой механизм гарантирует невозможность прочесть прошивку в залоченом камне.
AA — нет защиты.
BB — защита включена.
СС — микроконтроллер безвозвратно заблокирован от чтения/записи. Warning! Если установите этот уровень защиты, то больше никогда не сможете прошить МК. Операция необратима.
Тут комментировать особо нечего, думаю и так всё понятно. Однако очень не рекомендую снимать галочку с вачдога — камень будет постоянно ресетится. Настройки применяются так же, галочку сняли/поставили, и кнопка Apply. В более мощных МК есть доп. настройки.
Сюда можно сохранить какие-то свои данные, правда не много
Защита от записи конкретных страниц флеш-памяти. К этому вернёмся чуть позже.
RM0008 стр. 55. (Рис. 1)
Часть страниц по 16К, одна 64К, а часть по 128К. Здесь страницы называются секторами.
127-я страница на камне F103C8хх.
У low-density используется только WRP0 и защищает весь флеш, а у medium-density можно делать это выборочно. Зачем нужны остальные биты (WRP4 и т.д.) я так и не понял, может они зарезервированы.
То же самое происходит при переходе из одной функции в другую, например из в
Стек начинается от самого большого адреса и при увеличении размера ползёт вниз (записывая данные в меньшие адреса), а куча наоборот, при увеличении размера ползёт вверх, поэтому при выделении памяти на куче нужно следить чтоб эти области не налезли друг на друга.
Вся наша оперативка находится внутри узенькой полоски, а всё что выше не существует. То есть стек, куча, bss и data находится внутри этой полоски, а text в жёлтой области.
dec и hex это общий размер первых трёх значений.
Выполняется логическое «И», и в ячейку записывается нужное нам число 7.
Выполняется логическое «И» между старым значением 0х07 и новым 0x0d. В результате вместо желаемого числа 13, в ячейку записывается число 5.
Всё окей. Не забывайте нажимать Disconnect
Всем спасибо
Как записать переменную во флеш память 80c51f120
Artwork by Benny Kusnoto
Как записать переменную во флеш память 80c51f120
Проект по сборке компактного клона ZX-80, в котором мы не только разберем сам процесс сборки, включая подготовку платы и схематику подключения на ней компонентов, но также познакомимся с используемым в ATX-80 интерпретатором Basic и научимся работать с памятью этого компьютера на примере загрузки 32 образцов программ.
ATX-80 – это наследник ZX-80, собранный на базе ATmega8. При этом он не эмулирует код, а заменяет оригинальный компьютер собственным кодом с аналогичной функциональностью на ассемблере AVR. Видео с него можно выводить как на VGA-монитор, так и на телевизор PAL/NTSC. Программы Basic можно хранить на внутренней/внешней EEPROM, внутренней флэш-памяти (32 слота), а также передавать между ПК и его хранилищем.
Получившийся клон отличается простотой сборки и компактностью. Его можно использовать как небольшой дешевый компьютер для обучения программированию на Basic. В общей сложности в него загружено 32 программы.
Общее описание проекта
Изначальной целью проекта было создание компьютера, сопоставимого по функционалу и техническим возможностям с оригинальным ZX-80. Это означало наличие полноценного интерпретатора Basic со всего 1Кб RAM и максимальное приближение к 4Кб ROM.
Еще одной задачей было изучить оригинальную ROM BIOS ZX-80, которая без преувеличения достойна похвалы. Помимо того, что она была инновационной, эта система создавалась с высокой оптимизацией размера кода, и несмотря на необходимость высокой ясности в потоке данных между функциями автор описал весь код с минимумом ошибок.
Я постарался сохранить функциональность исходного кода максимально точно и сопроводил его подробными комментариями. Невзирая на свой почтенный возраст, BIOS ZX-80 представляет очень полезный и информативный материал для изучения.
Для написания кода я выбрал процессор ATmega8, снаряженный 1Кб RAM (аналогично ZX-80) и 8Кб Flash. И хоть в оригинале объем флэш-памяти был вдвое меньше, переписанный код у меня получился где-то на 40% длиннее, поскольку инструкции AVR имеют размер 16 бит, плюс я добавил дополнительные функции, такие как более сложный генератор видео и обработку памяти, попутно задействовав 1Кб под хранение программ. В общем, 8Кб были распределены рационально.
ATX-80 оснащен клавиатурой с 40 микропереключателями. Он способен генерировать видео для VGA-монитора, а также выдавать сигнал в стандартах PAL и NTSC. Питание осуществляется от внешнего источника 5В через USB (USB-зарядки или ПК). Внешняя память EEPROM объемом 32Кб служит для хранения максимум 32 программ Basic в 32 слотах. Помимо этого, один внутренний слот флэш-памяти можно задействовать для копирования программ между внешними модулями EEPROM.
Редактируемая программа также автоматически отображается во внутреннюю EEPROM, размер которой составляет всего 512 байт, в связи с чем некоторые программы целиком в нее не входят, хотя большая часть все же имеет меньший размер (оперативная память разделяется между переменными и видеопамятью). В случае же с более объемными программами сохраняется, по крайней мере, их большая часть.
Преимущества ATX-80 перед оригинальным ZX-80:
- VGA-выход;
- изображение генерируется при выполнении программы (можно использовать режимы FAST и SLOW );
- редактируемая программа сохраняется даже после выключения;
- сохранение программ на внешнюю EEPROM;
- клавиши автоповтора;
- функция получения информации о памяти;
- повышенная скорость работы программ;
- меньший размер.
- невозможность выполнения программ в машинном коде (функция USR не работает);
- невозможность расширения хранилища внешней памятью;
- невозможность подключения дополнительных устройств через внешнюю шину;
- для хранения программ нельзя использовать кассетную пленку, доступна только EEPROM;
- сгенерированные символы содержат дополнительную 9-ю белую полоску (следствие генерации изображения через порт SPI)
Ошибки ZX-80 BIOS
В процессе изучения ZX-80 BIOS я обнаружил несколько мелких багов:
1) Ошибка команды REM . Адрес подпрограммы REM должен быть L0849 , а не L084A . В результате этой ошибки выполнение REM без параметров (без текста) не приведет к выполнению следующей строки.
2) Ошибка в подпрограмме ED-EDIT – приведенные ниже инструкции должны идти в обратном порядке. Данная ошибка может привести к сбою системы, если выполнить команду при заполненной памяти:
3) еще одна ошибка, которую заметить сложнее, представляет риск при изменении компиляции. В начале подпрограммы L03CB ED-EDIT отсутствует инициализация регистра B на значение, представляющее количество строк для вывода (требуется значение 2 или больше). В этот момент регистр B содержит значение 3 (полученное из адреса L03CB ), что вполне удовлетворительно, но переклмпиляция из другого адреса может привести к прекращению вывода программы. Хотя я допускаю, что автор намеренно опустил здесь эту инструкцию в качестве элемента оптимизации.
Электрическая схема
В качестве основного процессора использован ATmega8. При желании его можно заменить на ATmega88, ATmega168 или ATmega328. В этом случае потребуется использовать другую прошивку, которая также прилагается в пакете для скачивания. Однако больший объем памяти старших процессоров в этом случае не задействуется.
Управляется ЦПУ тактовым генератором с частотой 24МГц. Такая частота необходима для отображения требуемого разрешения на VGA-мониторе. Несмотря на то, что у данного кристалла она выше пределов, заявленных разработчиком, на практике выяснилось, что с подобным разгоном справляются практически все процессоры.
Тем не менее рекомендуется использовать процессорный сокет, чтобы при возникновении сложностей можно было легко установить другую модель. Причиной неполадок также может выступить и недостаточно высокое напряжение. Частота процессора зависит от подаваемого питания, и низкое напряжение может вызвать его некорректную работу с используемым тактовым генератором.
Клавиатура оформлена в матрицу 5х8 клавиш по аналогичной ZX-80 схеме, только с обратным током. При ее опросе сигналы COL1 и COL5 поступают в качестве входа (то есть состояния высокого сопротивления), и всего один из сигналов COL установлен как выход с уровнем LOW .
Сигналы от ROW1 до ROW8 подключены в качестве входа с помощью внутреннего подтягивающего резистора. Когда происходит опрос, один из сигналов COL активируется на уровень LOW , и считывание сигналов ROW определяет нажатую клавишу.
Выход MOSI от генератора SPI направляется в качестве видеосигнала на VGA-коннектор и на Cinch-разъем, служащий для вывода изображения на ТВ. Предполагается, что этот выход будет нагружаться входным резистором 75Ом, который обеспечит необходимое напряжение в 0.7В. За вывод сигнала SYNC отвечает Timer1 , обеспечивающий горизонтальную синхронизацию. Timer1 одновременно активирует прерывание для генератора видео, который отправляет видеоданные через контроллер SPI.
Сигнал VSYNC используется только в режиме старого VGA-дисплея. В других случаях на выходе SYNC обычно генерируется комбинированный синхросигнал CSYNC . Вертикальный сигнал создается в этой комбинации путем отрицания горизонтального синхросигнала.
Большинство современных VGA-мониторов поддерживают комбинированную синхронизацию и отлично работают в этом режиме. Только старые модели требуют дополнительного использования сигнала VSYNC . При этом на большинстве мониторов никакой разницы между режимами старых или новых моделей, как правило, не заметно.
Внешняя память EEPROM подключена посредством интерфейса TWI/I2C ( SDA и SCL ). В качестве наиболее подходящего варианта рекомендуется использовать память 24LC256P 32Кб, которая может содержать до 32 программ (имеет 32 слота по 1Кб). Помимо этого, можно также использовать модули 24LC32 (4Кб, т.е. 4 слота), 24LC64 (8Кб, т.е. 8 слотов), 24LC128 (16Кб, т.е. 16 слотов) и 24LC512 (64Кб, т.е. 64 слота). Отличаются они только количеством доступных слотов.
Также рекомендуется использовать для EEPROM панельку, чтобы при необходимости можно было менять модули. Содержащиеся в памяти программы можно копировать между разными модулями как через внутренний слот флэш-памяти, так и с помощью прилагающейся утилиты.
В ZX-80 у модулей EEPROM есть входной контакт защиты от записи. В ATX-80 такой особенности нет, но для активации/отключения возможности записи можно добавить на плату перемычку.
Сборка
ATX-80 я спроектировал под одностороннюю плату 100х75мм. Выбор пал на нее отчасти из-за ограничений бесплатной версии редактора Eagle, а отчасти из-за широкой доступности в таком размере печатных плат с фототравлением.
При изготовлении подобных плат я с помощью струйного принтера печатаю на прозрачной пленке фотошаблон (при этом склеиваю два для полноценного охвата), экспонирую их под УФ в течение 10 минут с расстояния 30см, проявляю в калийной щелочи и вытравливаю в хлорном железе.
Имейте ввиду, что в случае кастомизированного производства печатных плат необходимо проверять обозначения диаметров отверстий в шаблоне (я сверлил отверстия сам, поэтому не проверял).
Нижняя часть платы
Верхняя часть платы, можно заменить перемычками из проводов
Шелкография
Компоновка элементов. Используйте панельки для процессора и памяти.
Плата в сборе
Наклейка с метками кнопок. Я напечатал ее на обычной бумаге также с помощью струйного принтера, смазал обе стороны клеем и сделал отверстия под кнопки размером 4мм
Полный комплект
Программирование и запуск
Установите FUSE-биты следующим образом:
- ATmega8: нижний 0xEF (по умолчанию 0xE1 ), верхний 0xC7 (по умолчанию 0xD9 );
- ATmega88: нижний 0xF7 (по умолчанию 0x62 ), верхний 0xD7 (по умолчанию 0xDF ), расширенный 0xFF (по умолчанию 0xF9);
- ATmega168: нижний 0xF7 (по умолчанию 0x62 ), верхний 0xD7 (по умолчанию 0xDF ), расширенный 0xFF (по умолчанию 0xF9 );
- ATmega328: нижний 0xF7 (по умолчанию 0x62), верхний 0xD7 (по умолчанию 0xD9 ), расширенный 0xFF (по умолчанию 0xFF ).
Если вы подключите VGA-монитор после программирования процессора, то должны увидеть простой белый экран с курсором “K” в нижнем левом углу. Если этого не произойдет, то, возможно, режим VGA не выбран. Для переключения режима нужно будет включить питание, зажав одну из клавиш от 1 до 4:
- 1 + включение питания: старый VGA-монитор (использует сигнал VSYNC );
- 2 + включение питания: новый VGA-монитор (использует комбинированную синхронизацию CSYNC );
- 3 + включение питания: режим NTSC TV (вывод через коннектор Cinch);
- 4 + включение питания: режим PAL TV (вывод через коннектор Cinch).
Имейте ввиду, что нельзя использовать описанный выше способ для установки видеорежима на процессоре с помощью утилиты копирования. Это приведет к незапланированной активации программирования внешней EEPROM.
Проверить работоспособность процессора можно без подключения дисплея. Для этого нужно вставить в разъем VGA светодиод между контактами 14 (плюс, длинный) и 10 (короткий минус, короткий). Если вы включаете режим видео 1 (для старого монитора), то светодиод загорится (сигнал VSYNC активен). На включение других режимов он не отреагирует. В качестве альтернативы можете вставить светодиод в Cinch-разъем (плюс в центральное отверстие, минус к экрану) – в процессе генерации изображения светодиод будет гореть.
Программирование EEPROM
Загрузка программ в EEPROM
После тестирования базовой функциональности ATX-80 я советую запрограммировать внешнюю подключенную EEPROM. В прикрепленном архиве вы найдете набор утилит для загрузки 32 образцов программ. Для этого потребуется либо перепрограммировать основной процессор, либо использовать другой в качестве «копировальщика».
В наборе утилит есть каталог ATX80_Import, где лежат прошивки (BIN-файлы) для копирующего процессора, подготовленные под модели ATmega8/88/168/328 (первое число в имени файла указывает предполагаемую модель).
Для каждого типа процессора есть 3 набора программ: от слота 1 до 12, от слота 13 до 24 и от слота 25 до слота 32. Содержимое этих программ находится в том же пакете утилит в файлах Programs_1.txt, Programs_2.txt и Programs_3.txt. Описание программ также можно найти в файле Programs.txt из пакета исходного кода ATX-80.
Программа копирования с целью экономии памяти задействует только светодиодный индикатор, но не дисплей. Вставьте светодиод в VGA-разъем между контактами 14 и 10, как описано выше.
Загрузите файл *_1.bin с первым набором программ в процессор. Индикатор будет интенсивно мигать, указывая, что процессор находится в режиме ожидания. Если сейчас нажать 4, то светодиод перейдет в режим постоянного свечения, сообщая о выполнении программирования EEPROM. Затем он снова интенсивно замигает, указывая на завершение операции.
Загрузите остальные два набора программ аналогичным образом по очереди. Для регистрации нажатия кнопки она должна быть нажата не менее 0.5 секунды – светодиод должен светится не менее 1 секунды.
Копирование программ между EEPROM
Тот же пакет утилит содержит средства для скачивания или копирования программ из внешней EEPROM. Чтобы выполнить копирование из одной EEPROM в другую, следуйте описанной ниже процедуре.
Загрузите из каталога ATX80_Copy соответствующую программу в процессор. Мигающий светодиод будет сигнализировать о режиме готовности процессора. Нажмите одну из клавиш от 1 до 3. Светодиод загорится постоянно, указывая на выполнение операции, в результате которой программы из EEPROM будут скачаны во внутреннюю флэш-память процессора. Выбранная клавиша определяет скачиваемые слоты: 1 выбирает слоты с 1 по 11, 2 выбирает слоты с 12 по 22, а 3 выбирает слоты с 23 по 32.
В теории ATmega8 не способен вместить 11 слотов – это бы означало 11Кб памяти при имеющихся у этой модели 8Кб. Если в некоторых случаях программы входить не будут, то вам потребуется использовать более вместительный процессор или скорректировать количество слотов в исходном коде программы копирования.
После загрузки программ в процессор выключите питание, замените модуль EEPROM и снова его включите. Нажмите 4 для сохранения загруженных программ из флэш-памяти в новую внешнюю EEPROM. Все находящиеся в памяти программы сохраняются в те же слоты, из которых копировались.
Скачивание программ из EEPROM
Вы также можете скачивать программы на ваш ПК с помощью утилит. Для начала, как и в прошлом случае, нужно загрузить подходящий набор программ во флэш-память процессора, используя клавиши 1-3. Далее подключите программатор ЦПУ и скачайте содержимое флэш-памяти в каталог ATX80_Copy в файлы 1.bin и 3.bin. В каталоге ATX80_Export запустите командный файл !.bat. Так вы экспортируете программы из скачанных образов флэш-памяти в текстовые файлы Programs_1.txt, Programs_2.txt и Programs_3.txt, находящиеся в базовом каталоге утилит.
Содержимое этих файлов можно отредактировать в текстовом виде. В начале кода каждой программы прописана метка Slot x , сообщающая, к какому слоту она принадлежит. Эта метка указывает начало программы для дальнейшей обработки. Все последующие строки, начинающиеся с числа, обрабатываются как строки программы. Эти строки могут сопровождаться инструкциями LET или DIM , содержащими заполнение для численных или текстовых переменных.
Если вы хотите загрузить программы в текстовом виде обратно в EEPROM, то запустите файл !.bat в каталоге ATX80_Import. Это обеспечит импорт всех текстовых форм программ обратно в образы программы копирования. Далее вам останется лишь загрузить эти программы в процессор и нажать 4, чтобы сохранить их во внешней EEPROM аналогично тому, как это описывалось в начале текущего раздела.
Описание ATX-80 BASIC
Basic на ATX-80 почти полностью идентичен Basic с ZX-80, за исключением нескольких небольших отличий.
Номер строки программы может простираться от 1 до 9999. После каждого изменения в строках программы во внутренней EEPROM автоматически создается ее резервная копия. Емкость этой EEPROM составляет всего 510 байт, поэтому целиком программы в нее вмещаются не всегда. При включении ATX-80 программа, сохраненная во внутренней памяти, восстанавливается.
Basic на ATX-80 работает с целыми числами в диапазоне от -32768 до +32767.
Типы переменных:
- целочисленная переменная с произвольно длинным именем (начинается с буквы, которая может сопровождаться буквами или цифрами);
- целочисленная переменная со всего одной буквой может использоваться в качестве параметра цикла FOR ;
- текстовая переменная – 1 буква, сопровождаемая $ . Текст может иметь любую длину;
- массив представляется одной буквой и ограничен размером в 255 элементов.
Операции
- + — … сумма и разность;
- * /… умножение и деление (только для целых чисел);
- AND… побитовое логическое произведение;
- OR… побитовая логическая сумма;
- NOT… побитовое логическое «не»;
- **… степень
- =… проверка равенства чисел или текстов;
- <… сравнение чисел и текстов с меньшим;
- >… сравнение чисел и текстов с большим.
Команды
Могут идти только в начале строки или после THEN и вызываются нажатием соответствующей кнопки:
- LIST … (A) вывод листинга программы с заданной строки. Если номер строки не указывать, выводится листинг всей программы;
- RETURN … (B) возвращение из процедуры;
- CLS . (С) очистка экрана;
- DIM … (D) объявление массива. Массив должен начинаться с индекса 0 и может содержать до 255 элементов (например, при DIM A(255) в нем могут быть задействованы индексы от 0 до 255);
- SAVE … (E) сохранение программы и переменных в слот. Эта команда сопровождается номером слота внешней EEPROM от 1 до 32. Если число не указать или указать 0, программа и переменные сохранятся во внутренней флэш-памяти процессора.
- FOR … (F) цикл, сопровождаемый однобуквенной переменной с начальным значением, после которой идет TO с конечным значением;
- GO TO … (G) переход к указанной строке;
- POKE … (H) сохранение байта в RAM, сопровождаемое адресом содержимого этого байта. ATX-80 имитирует сохранение в адресах RAM идентично ZX-80;
- INPUT … (I) пользовательский ввод, сопровождаемый целевой численной или текстовой переменной;
- RANDOMISE … (J) инициализация генератора случайных чисел от указанного числа. Если ввести 0 или опустить параметр, инициализируется согласно счетчику фреймов;
- LET … (K) установка содержимого переменной;
- FAST … (L) установка ускоренного режима. Отображается только одна верхняя строка, и процессор ускоряется в 4 раза. Использовать можно только при выполнении программы;
- SLOW … (M) установка медленного режима. Отображается весь экран. Включается автоматически при выходе из программы.
- NEXT … (N) продолжение цикла для заданной переменной;
- PRINT … (O) вывод переменных или текста. Символ ; обеспечивает продолжение без пробела. Символ , выставляется после 8-символьных позиций табуляции;
- MEMORY … (P) вывод информации о памяти в байтах: размер программы, переменных, а также свободное пространство RAM;
- NEW … (Q) удаление программы и переменных;
- RUN . ® выполнение программы с заданной строки. Если номер строки не указать, программа выполняется с начала. В то же время происходит очистка переменных.
- STOP … (S) остановка программы. Для продолжения используется CONTINUE .
- CONTINUE … (T) возобновляет выполнение программы, остановленной командой STOP .
- IF … (U) условие. THEN сопровождается инструкцией, выполняемой при валидности условия или выражения (выражение считается валидным, если не является нулевым);
- GO SUB … (V) переход к процедуре. Из процедуры инструкция RETURN возвращает к строке, следующей за GO SUB .
- LOAD … (W) загрузка программы и переменных из слота. Эта команда сопровождается значением от 1 до 32 для внешней EEPROM. Если значение не ввести или ввести 0, программа и переменные будут загружены из внутренней флэш-памяти процессора;
- CLEAR … (X) очистка переменных;
- REM … (Y) комментарий.
Функции и прочие команды
функции необходимо прописывать, так как клавиатурных сокращений для них нет:
- «… (Shift+Y) указывает начало и конец текста;
- THEN … (Shift+3) часть условия IF , указывающая команду, которая выполняется при валидности условия;
- TO … (Shift+4) часть цикла FOR , указывающая конечное значение переменной;
- ; … (Shift+Z) часть инструкции PRINT , продолжающая вывод с последней позиции;
- , … (Shift+.) часть инструкции PRINT , продолжающая вывод из позиции в таблице;
- ( ) … (Shift+I, Shift+O) скобки;
- PEEK … загружает содержимое памяти из адреса, указанного в скобках. ATX-80 имитирует адреса RAM идентично ZX-80, включая адрес шрифта в ROM. Содержимое указателей не имитируется, их необходимо вычислять повторно;
- CHR$ … преобразует символьный код в текстовый символ;
- CODE … преобразует текстовый символ (первый символ текста) в численный код;
- RND … генератор случайных чисел. Генерирует число в диапазоне от 1 до заданного значения;
- TL$ … возвращает текст без первого символа;
- USR … в ATX-80 не поддерживается;
- STR$ … преобразует число в текст;
- ABS … абсолютное значение числа.
Примеры программ
Эти программы хранятся во внешней EEPROM в слотах 1 – 32. Содержимое EEPROM можно скопировать в другую EEPROM, скачать на ПК, либо загрузить с ПК в EEPROM, как описывалось в разделе «Программирование EEPROM».
Содержимое программ вместе с подробным описанием находится в исходном коде в файле Programs.txt.
1) Вывод набора символов
2) Деление с высокой точностью
3) Получение квадратного корня
4) Бросание кубиков
5) Таймер реагирования
6) Гистограмма случайного числа
7) Прыгающая лягушка
8) Кодирование сообщений
9) Конные скачки
10) Луноход (запускать с GO TO 100)
11) Генератор лабиринта
12) Крестики-нолики
13) Dr. ATX80 — психотерапевт (запускать с GO TO 120)
14) Игра Ним
15) Блэкджек
16) Сортировка пузырьком
17) Перенумерация строк (запускать с GO TO 9987)
18) Рисование картинки (восстановление сохраненной картинки с помощью GO TO 200)
AVR — Запись данных во флеш память
Встроенная EEPROM стремительно заканчивается, а данные куда-то записывать надо. Знакомая ситуация, не правда-ли?
Что мы обычно делаем в таких случаях? Ставим внешнюю EEPROM, флеш или SD карточку на 32 гига. Это оправдано, если устройство достаточно сложное. А если оно состоит из одной тиньки и двух с половиной светодиодов? Тогда подключение внешней памяти грозит кардинальными изменениями в алгоритме, а может и пинов банально не хватит.
Но ведь у нас есть своя флеш память, которая в подавляющем большинстве случаев заполнена чуть менее, чем на половину. Отлично! Её и используем для записи данных.
Механизм записи во флеш память на первый взгляд немного запутанный. Но если присмотреться — все просто. Он состоит из трех основных частей:
— Очистка страницы памяти.
— Подготовка (закидываем данные во временный буфер)
— Запись.
Причем первые два пункта можно невозбранно менять местами.
Флеш память разделена на страницы. Размер страницы зависит от общего объема флеш памяти — смотри в даташите.
Например, для тини13 есть такая табличка:
Размер страницы 32 байта (16 слов), а всего таких страниц 32 штуки.
Так-же там написано, что в регистре адреса первые 4 бита (PC[3:0]) занимает адрес слова, а адрес страницы начинается с 5го бита. Значит, если нам надо записать адрес 3й страницы, то в регистр уйдет 3<<4.
При операциях со страницами (запись и очистка) от нас будут требовать номер страницы. А при работе со временным буфером — адрес слова в странице. К счастью, МК сам выделяет нужные части из адреса, который мы кидаем в регистр Z — поэтому можно не задумываться и пихать просто адрес слова в памяти.
При прошивке МК надо включить фьюз SELFPRGEN. Без этого ничего работать не будет.
1) Расчистка места.
Для очистки страницы памяти надо выполнить такую последовательность действий:
— Пихаем в регистр Z адрес. Из него МК выделит адрес страницы, а на первые 4 бита забьет.
— Поднимаем в регистре SPMCSR биты PGERS (Page Erase) и SELFPRGEN (Self Programming Enable).
— Быстро (в течении 4х тактов после записи в SPMCSR) выполняем команду SPM.
На время очистки страницы (а это около 4 мс) процессор подвисает.
2) Готовим данные к записи.
— Записываем в регистр Z адрес слова. Теперь все наоборот. МК будет ориентироваться по первым 4 битам адреса.
— В пару регистров R1:R0 пишем данные, которые хотим запихать в буфер.
— Поднимаем бит SELFPRGEN в регистре SPMCSR.
— Выполняем команду SPM.
… повторить, пока не будет заполнен весь буфер.
У этого буфера есть одна нехорошая особенность:
Записать два раза в одну ячейку нельзя. Т.е. если мы хотим переписать уже записанные в буфер данные, то его придется сначала очистить. Для очистки надо просто поднять бит CTPB в SPMCSR.
Алсо, буфер очищается сам после записи во флеш или перезагрузки.
Забавно так-же то, что данные в буфере будут потеряны, если записать что-то в EEPROM. Как-то хитро память устроена, не находите?
Последнее особенно актуально, если мы собираемся наполнять буфер постепенно, прерываясь на другие задачи (типа записи в EEPROM).
3) Пишем!
— Запихиваем адрес страницы в Z
— Устанавливаем биты SELFPRGEN и PGWRT.
— Выполняем SPM.
При записи страницы МК зависает на те-же 4 мс.
Пример устройства.
Для примера я хотел замутить термологгер на базе тини25. С записью температуры в флеш, работой со встроенным термометром и выдачей лога в UART. Но выяснилось, что UART у тини25 нету, а «родной» термометр уж больно кривой. Поэтому будем делать что по-проще. Например, записывать во флеш память напряжение, измеренное АЦП.
Алгоритм дешевого и сердитого логгера такой:
0) Инициализация периферии. Устанавливаем адрес записи на первую пустую страницу.
1) Ждем 1 сек — простым циклом, безо всяких таймеров и прерываний. Дешево и сердито.
2) Запускаем АЦП и ждем, пока он закончит преобразование.
3) Если мы начали писать новую страницу, то
3.1) Очишаем её.
4) Записываем во временнный буфер (по текущему адресу) значение АЦП.
5) Если мы уже заполнили весь буфер, то
5.1) Записываем его во флеш память.
6) Инкрементируем адрес записи.
7) Если дошли до конца памяти — затупляем в вечном цикле. Иначе идем на шаг 1.
Код на ассемблере:
Проверять код в протеусе бесполезно — не работает
Но в железе все прекрасно крутится.
Небольшое дополнение: RWW и NRWW
В МК с поддержкой бутлоадера на команду SPM накладываются ограничения. Её можно выполнить только из BLS (Bootloader section) — области памяти в самом конце флеша, где живут бутлоадеры. Размер этой области устанавливается фьюзами BOOTSZ.
Вообще, в контроллерах с поддержкой бутлоадера, весь флеш поделен на 2 части: RWW (Read-While-Write) и NRWW (No Read-While-Write). NRWW распологается в конце памяти и занимает место отведенное под бутлоадер. Т.е. размер этой области памяти равен максимальному размеру BLS. Остальное место (с начала флеша и до NRWW) занимает RWW область.
Разница между RWW и NRWW заключается вот в чем: Если бутлоадер (который находится в NRWW) пишет или стирает страницу в RWW области, то МК не останавливается на время выполения этой операции. Бутлоадер будет продолжать работать, пока страница памяти в RWW записывается или стирается. По этому поводу даже придумали прерывание SPM_RDY, которое возникает по завершении операции.
А если бутлоадер попытается записать данные в секцию NRWW (это не обязательно должна быть секция самого бутлоадера), то МК замрет на 4мс, пока производится запись.
Как записать переменную во флеш память 80c51f120
- />15 часов назад
- Тема:Работа сайта и сервера
- От:makc
- />15 часов назад
- Тема:Работа сайта и сервера
- От:makc
Другие известные форумы и сайты по электронике
все что посвящено электронике и общению специалистов. реклама других ресурсов.
- Магазины
- Форумы и конференции
- Производители
- Информационные ресурсы
- Поисковики
- FTP-серверы
- />Воскресенье в 05:57
- Тема:www.eetop.cn
- От:Fachleute
- />Воскресенье в 05:57
- Тема:www.eetop.cn
- От:Fachleute
В помощь начинающему
вопросы начального уровня
Модераторы раздела VAI aosp
SergM
fill
vetal
KRS
Alexandr
des00
Uladzimir
Rst7
iosifk
ViKo
Herz
l1l1l1
Tanya
Сергей Борщ
Omen_13
Vasily_
Егоров
Walrus
- ARM, 32bit
- MCS51, AVR, PIC, STM8, 8bit
- Программирование
- Схемотехника
- Интерфейсы
- />3 минуты назад
- Тема:Как программа пользователя может найти в системе…
- От:jcxz
- />3 минуты назад
- Тема:Как программа пользователя может найти в системе…
- От:jcxz
International Forum
This is a special forum for English spoken people, read it first.
- />25 февраля
- От:HardEgor
- />25 февраля
- От:HardEgor
Образование в области электроники
все что касается образования, процесса обучения, студентам, преподавателям.
Модераторы раздела des00
- />2 часа назад
- Тема:Создание профессиональной ассоциации по микроэле…
- От:girts
- />2 часа назад
- Тема:Создание профессиональной ассоциации по микроэле…
- От:girts
Обучающие видео-материалы и обмен опытом
Обсуждение вопросов создания видео-материалов
Модераторы раздела iosifk
- />17 февраля
- Тема:Dilduino
- От:k155la3
- />17 февраля
- Тема:Dilduino
- От:k155la3
Cистемный уровень проектирования
-
Последнее сообщение
Вопросы системного уровня проектирования
Применение MATLAB, Simulink, CoCentric, SPW, SystemC ESL, SoC
Модераторы раздела Rst7
- />Пятница в 09:56
- Тема:Работа с GUI в Матлаб
- От:_sda
- />Пятница в 09:56
- Тема:Работа с GUI в Матлаб
- От:_sda
Математика и Физика
Модераторы раздела Rst7
- />19 часов назад
- Тема:Определение проекций на сенсоре камеры.
- От:Cianid
- />19 часов назад
- Тема:Определение проекций на сенсоре камеры.
- От:Cianid
Операционные системы
Linux, Win, DOS, QNX, uCOS, eCOS, RTEMS и другие
Модераторы раздела Rst7
- Программирование
- Linux
- uC/OS-II
- scmRTOS
- FreeRTOS
- Android
- />42 минуты назад
- Тема:Драйвер для I2C
- От:makc
- />42 минуты назад
- Тема:Драйвер для I2C
- От:makc
Документация
оформление документации и все что с ней связано
Модераторы раздела Rst7
- />12 марта
- Тема:Как делают документацию для микроконтроллеров их…
- От:Yra
- />12 марта
- Тема:Как делают документацию для микроконтроллеров их…
- От:Yra
Системы CAD/CAM/CAE/PLM
обсуждение САПР AutoCAD, Компас, SolidWorks и др.
- />5 февраля
- Тема:Ошибка установки Solidworks
- От:baumanets
- />5 февраля
- Тема:Ошибка установки Solidworks
- От:baumanets
Разработка цифровых, аналоговых, аналого-цифровых ИС
Модераторы раздела Rst7
- />1 минута назад
- Тема:JESD204B Frame Clock и LMFC
- От:warrior-2001
- />1 минута назад
- Тема:JESD204B Frame Clock и LMFC
- От:warrior-2001
Электробезопасность и ЭМС
Обсуждение вопросов электробезопасности и целостности сигналов
Модераторы раздела Rst7
- ЭМС
- Электробезопасность
- />3 марта
- Тема:Типовые схемы защиты от микросекундных помех
- От:HardEgor
- />3 марта
- Тема:Типовые схемы защиты от микросекундных помех
- От:HardEgor
Управление проектами
Управление жизненным циклом проектов, системы контроля версий и т.п.
Модераторы раздела Rst7
- />30 октября, 2022
- Тема:Как тестировать разработанную электронику и встр…
- От:KBH
- />30 октября, 2022
- Тема:Как тестировать разработанную электронику и встр…
- От:KBH
Нейронные сети и машинное обучение (NN/ML)
Форум для обсуждения вопросов машинного обучения и нейронных сетей
Модераторы раздела Rst7
- />4 марта
- Тема:Модуль на VHDL кусочно-линейной (семь участков) …
- От:Мур
- />4 марта
- Тема:Модуль на VHDL кусочно-линейной (семь участков) …
- От:Мур
Программируемая логика ПЛИС (FPGA,CPLD, PLD)
-
Последнее сообщение
Среды разработки — обсуждаем САПРы
Quartus, MAX, Foundation, ISE, DXP, ActiveHDL и прочие.
возможности, удобства.
Модераторы раздела vetal />des00 />
- />1 час назад
- Тема:Конфигурация компа для имплементации и симуляции
- От:alexadmin
- />1 час назад
- Тема:Конфигурация компа для имплементации и симуляции
- От:alexadmin
Работаем с ПЛИС, области применения, выбор
на чем сделать? почему не работает? кто подскажет?
Модераторы раздела vetal />des00 />
- />2 часа назад
- Тема:ПЛИС Gowin Semiconductor
- От:vladec
- />2 часа назад
- Тема:ПЛИС Gowin Semiconductor
- От:vladec
Языки проектирования на ПЛИС (FPGA)
Verilog, VHDL, AHDL, SystemC, SystemVerilog и др.
Модераторы раздела aosp vetal
des00
- />Суббота в 17:34
- Тема:найти 2 максимума в потоке
- От:tegumay
- />Суббота в 17:34
- Тема:найти 2 максимума в потоке
- От:tegumay
Системы на ПЛИС — System on a Programmable Chip (SoPC)
разработка встраиваемых процессоров и периферии для ПЛИС
Модераторы раздела vetal des00
Omen_13
- />Воскресенье в 16:21
- Тема:linux 2 устройства
- От:1891ВМ12Я
- />Воскресенье в 16:21
- Тема:linux 2 устройства
- От:1891ВМ12Я
Цифровая обработка сигналов — ЦОС (DSP)
-
Последнее сообщение
Сигнальные процессоры и их программирование — DSP
Обсуждение различных сигнальных (DSP) процессоров, возможностей, совместимости и связанных с этим тем.
Модераторы раздела des00
- />9 марта
- Тема:SAU510USB Iso-Plus
- От:Alex11
- />9 марта
- Тема:SAU510USB Iso-Plus
- От:Alex11
Алгоритмы ЦОС (DSP)
Обсуждение вопросов разработки и применения (программирования) алгоритмов цифровой обработки сигналов.
Модераторы раздела des00
- />13 марта
- Тема:организация семплирования
- От:_sda
- />13 марта
- Тема:организация семплирования
- От:_sda
Микроконтроллеры (MCU)
-
Последнее сообщение
Cредства разработки для МК
FAQ, How-to, тонкости работы со средствами разработки
Модераторы раздела haker_fox
- IAR
- Keil
- GNU/OpenSource средства разработки
- />2 минуты назад
- Тема:Камень в огород ненавистников inline-ассемблера …
- От:Arlleex
- />2 минуты назад
- Тема:Камень в огород ненавистников inline-ассемблера …
- От:Arlleex
—>
Модераторы раздела haker_fox
- STM
- NXP
- Microchip (Atmel)
- TI, Allwinner, GigaDevice, Nordic, Espressif и другие
- />1 час назад
- Тема:Как правильно перепрошить загрузчик
- От:MKdemiurg
- />1 час назад
- Тема:Как правильно перепрошить загрузчик
- От:MKdemiurg
RISC-V
Микроконтроллеры на базе ядер RISC-V, RISC-X
Модераторы раздела haker_fox
- />2 марта
- Тема:Тайминги интерфейсов CH569/CH565
- От:makc
- />2 марта
- Тема:Тайминги интерфейсов CH569/CH565
- От:makc
—>
Модераторы раздела haker_fox
- />28 февраля
- Тема:DS1302 не тикает
- От:borodach
- />28 февраля
- Тема:DS1302 не тикает
- От:borodach
MSP430
Модераторы раздела VAI />haker_fox />
- />3 часа назад
- Тема:Реверс прошивки MSP430F67791A
- От:girts
- />3 часа назад
- Тема:Реверс прошивки MSP430F67791A
- От:girts
Все остальные микроконтроллеры
и все что с ними связано
Модераторы раздела haker_fox
- PIC
- MCS51
- PowerQUICC
- HC(S)08
- AVR32
- STM8
- MIPS
- />13 марта
- Тема:Чем считать прошивку upsd3212cv-24u6 от ST?
- От:tiretrak
- />13 марта
- Тема:Чем считать прошивку upsd3212cv-24u6 от ST?
- От:tiretrak
Отладочные платы
Вопросы, связанные с отладочными платами на базе МК: заказ, сборка, запуск
Модераторы раздела haker_fox
- Arduino
- Raspberry Pi
- Rainbow
- Siberia
- EVMxxxx
- />Четверг в 07:12
- Тема:China-Link, Вариант отладчика из Китая
- От:Akakiy
- />Четверг в 07:12
- Тема:China-Link, Вариант отладчика из Китая
- От:Akakiy
Печатные платы (PCB)
-
Последнее сообщение
Разрабатываем ПП в САПР — PCB development
FAQ, вопросы проектирования в ORCAD, PCAD, Protel, Allegro, Spectra, DXP, SDD, WG и др.
Модераторы раздела SergM />fill />
- Библиотеки компонентов
- Altium Designer, DXP, Protel
- P-CAD 200x howto
- Эремекс, Delta Design
- Cadence
- Примеры
- Zuken CADSTAR
- Siemens EDA — Xpedition, PADS (ex. Mentor)
- Бесплатные САПР: KiCAD, EasyEDA, EAGLE и др.
- />1 час назад
- Тема:Обсуждение Xpedition/PADS Designer
- От:cniism
- />1 час назад
- Тема:Обсуждение Xpedition/PADS Designer
- От:cniism
Работаем с трассировкой
тонкости PCB дизайна, от Spectra и далее.
Модераторы раздела fill
- />17 февраля
- Тема:Как правильно перевести скоростную дифф. пару с …
- От:DSIoffe
- />17 февраля
- Тема:Как правильно перевести скоростную дифф. пару с …
- От:DSIoffe
Изготовление ПП — PCB manufacturing
Фирмы, занимающиеся изготовлением, качество, цены, сроки
Модераторы раздела fill
- ПСБ Технолоджи
- ТеПро
- PS-Electro
- Резонит
- PCB Professional
- Абрис
- ОАО "НИЦЭВТ"
- ООО "М-Плата"
- в домашних условиях
- />21 час назад
- Тема:JLCPCB — карты UnionPay
- От:tgruzd
- />21 час назад
- Тема:JLCPCB — карты UnionPay
- От:tgruzd
Сборка РЭУ
-
Последнее сообщение
Пайка и монтаж
вопросы сборки ПП, готовых изделий, а также устранения производственных дефектов
- />14 марта
- Тема:Sn64 Ag1 Bi35 как RoHS нормально паяет?
- От:Flood
- />14 марта
- Тема:Sn64 Ag1 Bi35 как RoHS нормально паяет?
- От:Flood
Корпуса
обсуждаем какие есть копруса, где делать и прочее
- />28 февраля
- Тема:Моделирование силы продавливания платы
- От:destroit
- />28 февраля
- Тема:Моделирование силы продавливания платы
- От:destroit
Вопросы надежности и испытаний
расчеты, методики, подбор компонентов
- />Воскресенье в 16:55
- Тема:Поверка контрольно-измерительного оборудования
- От:HardEgor
- />Воскресенье в 16:55
- Тема:Поверка контрольно-измерительного оборудования
- От:HardEgor
Аналоговая и цифровая техника, прикладная электроника
-
Последнее сообщение
Вопросы аналоговой техники
разработка аналоговых схем, моделирование схем в SPICE, расчёты и анализ, выбор элементной базы
Модераторы раздела Alexandr ViKo
Tanya
Егоров
- />3 часа назад
- Тема:Ультразвук
- От:Plain
- />3 часа назад
- Тема:Ультразвук
- От:Plain
Цифровые схемы, высокоскоростные ЦС
High Speed Digital Design
- />21 февраля
- Тема:Влияние положительных и отрицательных выбросов (…
- От:Plain
- />21 февраля
- Тема:Влияние положительных и отрицательных выбросов (…
- От:Plain
RF & Microwave Design
wireless технологии и не только
Модераторы раздела l1l1l1
- />6 минут назад
- Тема:Вопросы по CST
- От:Andrey188
- />6 минут назад
- Тема:Вопросы по CST
- От:Andrey188
Метрология, датчики, измерительная техника
Все что связано с измерениями: измерительные приборы (осциллографы, анализаторы спектра и пр.), датчики, обработка результатов измерений, калибровка, технологии измерений и др.
Модераторы раздела ViKo />Tanya />
- />Пятница в 05:03
- Тема:Назначение шунтирующих резисторов в тензометриче…
- От:SVNKz
- />Пятница в 05:03
- Тема:Назначение шунтирующих резисторов в тензометриче…
- От:SVNKz
АВТО электроника
особенности электроники любых транспортных средств: автомашин и мотоциклов, поездов, судов и самолетов, космических кораблей и летающих тарелок.
Модераторы раздела Vasily_
- />4 марта
- Тема:Провод для автомобильного компрессора
- От:byRAM
- />4 марта
- Тема:Провод для автомобильного компрессора
- От:byRAM
Умный дом
- />Четверг в 06:01
- Тема:Как контролировать большое количество реле с пом…
- От:yamantau
- />Четверг в 06:01
- Тема:Как контролировать большое количество реле с пом…
- От:yamantau
3D печать
3D принтеры, наборы, аксессуары, ПО
- />21 декабря, 2022
- Тема:slicer для 3d принтера
- От:Variant99
- />21 декабря, 2022
- Тема:slicer для 3d принтера
- От:Variant99
Робототехника
Модели, классификация, решения, научные исследования, варианты применения
- />31 марта, 2022
- Тема:Подключение дисплея 3.2inch 320×240 Touch LCD (А…
- От:Aaronli
- />31 марта, 2022
- Тема:Подключение дисплея 3.2inch 320×240 Touch LCD (А…
- От:Aaronli
Ремонт и отладка
обсуждение вопросов ремонта и отладки различных устройств и готовых изделий
Модераторы раздела Herz
- />5 марта
- Тема:Ремонт оссцилографа Hantek dso5102p — проблема с…
- От:Zversky
- />5 марта
- Тема:Ремонт оссцилографа Hantek dso5102p — проблема с…
- От:Zversky
Силовая электроника — Power Electronics
-
Последнее сообщение
Силовая Преобразовательная Техника
Источники питания электронной аппаратуры, импульсные и линейные регуляторы. Топологии AC-DC, DC-DC преобразователей (Forward, Flyback, Buck, Boost, Push-Pull, SEPIC, Cuk, Full-Bridge, Half-Bridge). Драйвера ключевых элементов, динамика, алгоритмы управления, защита. Синхронное выпрямление, коррекция коэффициента мощности (PFC)
Модераторы раздела Herz />Егоров />
- />Четверг в 11:03
- Тема:CMTI, устойчивость к транзиентам
- От:_gari
- />Четверг в 11:03
- Тема:CMTI, устойчивость к транзиентам
- От:_gari
Обратная Связь, Стабилизация, Регулирование, Компенсация
Организация обратных связей в цепях регулирования, выбор топологии, обеспечение стабильности, схемотехника, расчёт
Модераторы раздела Herz />Егоров />
- />17 сентября, 2022
- Тема:Ограничение скорости нарастания сигнала ШИМ на д…
- От:Alex-lab
- />17 сентября, 2022
- Тема:Ограничение скорости нарастания сигнала ШИМ на д…
- От:Alex-lab
Первичные и Вторичные Химические Источники Питания
Li-ion, Li-pol, литиевые, Ni-MH, Ni-Cd, свинцово-кислотные аккумуляторы. Солевые, щелочные (алкалиновые), литиевые первичные элементы. Применение, зарядные устройства, методы и алгоритмы заряда, условия эксплуатации. Системы бесперебойного и резервного питания
Модераторы раздела Herz />Егоров />
- />18 января
- Тема:Температура Li-ion аккумуляторов
- От:maksimdag0
- />18 января
- Тема:Температура Li-ion аккумуляторов
- От:maksimdag0
Высоковольтные Устройства — High-Voltage
Высоковольтные выпрямители, умножители напряжения, делители напряжения, высоковольтная развязка, изоляция, электрическая прочность. Высоковольтная наносекундная импульсная техника
Модераторы раздела Herz
- />7 февраля
- Тема:Маломощный трансформатор 220В -> 6000
- От:sanya221
- />7 февраля
- Тема:Маломощный трансформатор 220В -> 6000
- От:sanya221
Электрические машины, Электропривод и Управление
Электропривод постоянного тока, асинхронный электропривод, шаговый электропривод, сервопривод. Синхронные, асинхронные, вентильные электродвигатели, генераторы
Модераторы раздела Herz
- />4 часа назад
- Тема:Самодельный драйвер биполярного шагового двигате…
- От:Plain
- />4 часа назад
- Тема:Самодельный драйвер биполярного шагового двигате…
- От:Plain
Индукционный Нагрев — Induction Heating
Технологии, теория и практика индукционного нагрева
Модераторы раздела Herz
- />10 января
- Тема:Тиристорный инвертор
- От:Слесарь
- />10 января
- Тема:Тиристорный инвертор
- От:Слесарь
Системы Охлаждения, Тепловой Расчет – Cooling Systems
Охлаждение компонентов, систем, корпусов, расчёт параметров охладителей
Модераторы раздела Herz
- />23 февраля
- Тема:Тепловой расчет для КТ827А
- От:Valery-m
- />23 февраля
- Тема:Тепловой расчет для КТ827А
- От:Valery-m
Моделирование и Анализ Силовых Устройств – Power Supply Simulation
Моделирование силовых устройств в популярных САПР, самостоятельных симуляторах и специализированных программах. Анализ устойчивости источников питания, непрерывные модели устройств, модели компонентов
Модераторы раздела Herz />Егоров />
- />8 февраля
- Тема:Micro-Cap
- От:Lnd
- />8 февраля
- Тема:Micro-Cap
- От:Lnd
Компоненты Силовой Электроники — Parts for Power Supply Design
Силовые полупроводниковые приборы (MOSFET, BJT, IGBT, SCR, GTO, диоды). Силовые трансформаторы, дроссели, фильтры (проектирование, экранирование, изготовление), конденсаторы, разъемы, электромеханические изделия, датчики, микросхемы для ИП. Электротехнические и изоляционные материалы.
Модераторы раздела Herz />Егоров />
- />14 марта
- Тема:есть ли Российские драйверы двигателей?
- От:Xenia
- />14 марта
- Тема:есть ли Российские драйверы двигателей?
- От:Xenia
Интерфейсы
-
Последнее сообщение
Форумы по интерфейсам
все интерфейсы здесь
- ISDN/G.703/E1
- ISA/PCI/PCI-X/PCI Express
- Wireless/Optic
- RS232/LPT/USB/PCMCIA/FireWire
- Fast Ethernet/Gigabit Ethernet/FibreChannel
- Интерфейсы для "интеллектуального дома"
- от ТТЛ до LVDS здесь
- IDE/ATA/SATA/SAS/SCSI/CF
- Аудио/Видео интерфейсы
- Сотовая связь и ее приложения
- FAQ по XPort/WiPort
- Controller Area Network (CAN)
- />11 часов назад
- Тема:Почему FT232H хочет — работает, а хочет — нет?
- От:mmarc__
- />11 часов назад
- Тема:Почему FT232H хочет — работает, а хочет — нет?
- От:mmarc__
Поставщики компонентов для электроники
-
Последнее сообщение
Поставщики всего остального
от транзисторов до проводов
- />27 февраля
- Тема:Shenzhen BLS electronics
- От:Миша Чжу
- />27 февраля
- Тема:Shenzhen BLS electronics
- От:Миша Чжу
Компоненты
Закачка тех. документации, обмен опытом, прочие вопросы.
- Тех. документация
- Микросхемы
- Транзисторы
- Диоды
- Резисторы
- Средства индикации
- />39 минут назад
- Тема:Отечественный ОУ +5В
- От:byRAM
- />39 минут назад
- Тема:Отечественный ОУ +5В
- От:byRAM
Майнеры криптовалют и их разработка, BitCoin, LightCoin, Dash, Zcash, Эфир
-
Последнее сообщение
Обсуждение Майнеров, их поставки и производства
наблюдается очень большой спрос на данные устройства.
- />16 июля, 2021
- Тема:Материнские платы для майнинга
- От:Doka
- />16 июля, 2021
- Тема:Материнские платы для майнинга
- От:Doka
Дополнительные разделы — Additional sections
-
Последнее сообщение
Встречи и поздравления
Предложения встретиться, поздравления участников форума и обсуждение мест и поводов для встреч.
Модераторы раздела VAI aosp
SergM
vetal
KRS
Alexandr
des00
Uladzimir
Rst7
iosifk
ViKo
Herz
l1l1l1
Tanya
Сергей Борщ
Omen_13
Vasily_
Егоров
Walrus
- />5 марта
- Тема:TIMTOS 2023: кто-то собирается?
- От:Ruslan1
- />5 марта
- Тема:TIMTOS 2023: кто-то собирается?
- От:Ruslan1
Ищу работу
ищу работу, выполню заказ, нужны клиенты — все это сюда
Модераторы раздела VAI aosp
SergM
vetal
KRS
Alexandr
des00
Uladzimir
Rst7
iosifk
ViKo
Herz
l1l1l1
Tanya
Сергей Борщ
Omen_13
Vasily_
Егоров
Walrus
- />13 марта
- Тема:Разработка ПО — STM32, ПК
- От:Sasha7b9
- />13 марта
- Тема:Разработка ПО — STM32, ПК
- От:Sasha7b9
Предлагаю работу
нужен постоянный работник, разовое предложение, совместные проекты, кто возьмется за работу, нужно сделать.
Модераторы раздела VAI aosp
SergM
vetal
KRS
Alexandr
des00
Uladzimir
Rst7
iosifk
ViKo
Herz
l1l1l1
Tanya
Сергей Борщ
Omen_13
Vasily_
Егоров
Walrus
- />57 минут назад
- Тема:Требуются услуги схемотехника
- От:iBredihin
- />57 минут назад
- Тема:Требуются услуги схемотехника
- От:iBredihin
Куплю
микросхему; устройство; то, что предложишь ты
Модераторы раздела VAI aosp
SergM
vetal
KRS
Alexandr
des00
Uladzimir
Rst7
iosifk
ViKo
Herz
l1l1l1
Tanya
Сергей Борщ
Omen_13
Vasily_
Егоров
Walrus
- />15 марта
- Тема:stm32l011k4t6
- От:Coco
- />15 марта
- Тема:stm32l011k4t6
- От:Coco
Продам
есть что продать за деньги, пиво, даром ?
Реклама товаров и сайтов также здесь.
Модераторы раздела VAI aosp
SergM
vetal
KRS
Alexandr
des00
Uladzimir
Rst7
iosifk
ViKo
Herz
l1l1l1
Tanya
Сергей Борщ
Omen_13
Vasily_
Егоров
Walrus
- />3 часа назад
- Тема:Микроконтроллер 1986ве92у
- От:Seldom
- />3 часа назад
- Тема:Микроконтроллер 1986ве92у
- От:Seldom
Объявления пользователей
Тренинги, семинары, анонсы и прочие события
Модераторы раздела VAI aosp
SergM
vetal
KRS
Alexandr
des00
Uladzimir
Rst7
iosifk
ViKo
Herz
l1l1l1
Tanya
Сергей Борщ
Omen_13
Vasily_
Егоров
Walrus
- />19 часов назад
- Тема:Диодный лазер для обнаружения газа от Brandnew
- От:МакроГрупп
- />19 часов назад
- Тема:Диодный лазер для обнаружения газа от Brandnew
- От:МакроГрупп
Общение заказчиков и потребителей электронных разработок
Обсуждение проектов, исполнителей и конкурсов
Модераторы раздела VAI aosp
SergM
vetal
KRS
Alexandr
des00
Uladzimir
Rst7
haker_fox
iosifk
ViKo
Herz
l1l1l1
Tanya
Сергей Борщ
Omen_13
Vasily_
Егоров
Walrus
- />18 декабря, 2022
- Тема:Как купить если ты не юрлицо?
- От:Dejmos
- />18 декабря, 2022
- Тема:Как купить если ты не юрлицо?
- От:Dejmos
Русские Блоги
SPI — чтение и запись серийной FLASH (несущественные элементы по времени)
SPI — чтение и запись серийной FLASH
Полный набор видеоуроков из 200 эпизодов и учебников в формате PDF на 1000 страниц можно загрузить с форума Binghuo: www.firebbs.cn
Видеоурок Wildfire Youku смотреть URL: http://i.youku.com/firege
Справочные материалы для этой главы: «Китайское справочное руководство по STM32F4xx», «Спецификация STM32F4xx», справочный документ библиотеки «stm32f4xx_dsp_stdperiph_lib_um.chm» и «Введение в протокол шины SPI».
Если вы не понимаете протокол связи SPI, вы можете сначала ознакомиться с содержанием документа «Введение в протокол шины SPI».
Для получения информации о флэш-памяти обратитесь к разделу «Введение в часто используемую память». Чтобы понять конкретные параметры микросхемы FLASH в эксперименте, ознакомьтесь с ее спецификацией «W25Q128».
24.1 Введение в протокол SPI
Протокол SPI представляет собой протокол связи (последовательный периферийный интерфейс), предложенный компанией Motorola, то есть интерфейс последовательного периферийного устройства, который представляет собой высокоскоростную полнодуплексную коммуникационную шину. Он широко используется между АЦП, ЖК-дисплеем и другими устройствами и MCU, где требуется высокая скорость связи.
Изучая эту главу, вы можете сравнить ее с главой I2C, чтобы понять разницу между двумя коммуникационными шинами и разницу между памятью EEPROM и флэш-памятью. Ниже мы объясним физический уровень и уровень протокола SPI соответственно.
24.1.1 Физический уровень SPI
Общий способ соединения между устройствами связи SPI показан на рисунке 241.
Рисунок 241 Общая система связи SPI
Для связи SPI используются 3 шины и линии выбора микросхемы. Этими тремя шинами являются SCK, MOSI, MISO и линия выбора микросхемы. Их функции представлены следующим образом:
(1) (Slave Select): выберите сигнальную линию от устройства, часто называемую сигнальной линией выбора микросхемы, также известной как NSS, CS, в дальнейшем именуемой NSS. Когда к ведущему SPI подключено несколько подчиненных устройств SPI, другие сигнальные линии SCK, MOSI и MISO устройства подключаются параллельно к одной и той же шине SPI одновременно, то есть независимо от того, сколько подчиненных устройств они используют только эти три шины вместе, и Каждое подчиненное устройство имеет независимую сигнальную линию NSS.Эта сигнальная линия занимает исключительно штырь хоста, то есть столько подчиненных устройств, сколько имеется сигнальных линий выбора микросхемы. Адреса протокола I2C по адресу устройства, выбирает устройство на шине и обменивается данными с ним, но нет адреса устройства в протоколе SPI, он использует сигнальную линию NSS для адресации, когда хост хочет выбрать подчиненное устройство, подчиненное устройство Сигнальная линия NSS устройства установлена на низкий уровень, выбрано подчиненное устройство, то есть выбор микросхемы действителен, и затем хост начинает связь SPI с выбранным подчиненным устройством. Поэтому связь SPI начинается с низкого уровня линии NSS и заканчивается высоким уровнем линии NSS.
(2) SCK (Serial Clock): линия тактового сигнала, используемая для синхронизации данных связи. Он генерируется хостом связи и определяет скорость связи. Максимальная тактовая частота, поддерживаемая разными устройствами, отличается. Например, максимальная тактовая частота SPI STM32 равна fpclk / 2. При обмене данными между двумя устройствами скорость связи ограничивается низкоскоростными устройствами. ,
(3) MOSI (ведущий выход, ведомый вход): вывод основного / ведомого входа. Данные хоста выводятся из этой сигнальной линии, и ведомое устройство считывает данные, отправленные хостом из этой сигнальной линии, то есть направление данных в этой линии от хоста к ведомому.
(4) MISO (ведущий вход, ведомый выход): главный вход / выход ведомого контакта. Ведущий считывает данные из этой сигнальной линии, и данные ведомого устройства выводятся в ведущий из этой сигнальной линии, то есть направление данных в этой линии подчинено ведущему.
24.1.2 Уровень протокола
Подобно I2C, протокол SPI определяет ссылки, такие как сигналы запуска и остановки, достоверность данных и тактовая синхронизация.
1. Базовый процесс общения SPI
Сначала рассмотрим последовательность обмена данными SPI, см. Рисунок 242.
Рис. 242 Время обмена данными SPI
Это коммуникационная последовательность хоста. Сигналы NSS, SCK, MOSI генерируются управляющим устройством хоста, а сигнал MISO генерируется подчиненным устройством, и хост считывает данные подчиненного устройства через сигнальную линию. Сигналы MOSI и MISO действительны только тогда, когда NSS находится на низком уровне, а MOSI и MISO передают один бит данных за такт такта SCK.
Сигналы, содержащиеся в вышеупомянутом процессе связи, разбиты следующим образом:
2. Запуск и остановка сигналов связи
При ссылочном номере на рисунке 242 сигнальная линия NSS меняется с высокого на низкий уровень, который является сигналом запуска связи SPI. NSS является сигнальной линией, эксклюзивной для каждого ведомого устройства. Когда ведомое устройство обнаруживает сигнал запуска в своей собственной линии NSS, оно знает, что оно было выбрано хостом, и начинает готовиться к связи с хостом. При ссылочном номере на рисунке сигнал NSS изменяется с низкого на высокий, который является сигналом остановки связи SPI, указывая на то, что эта связь завершена и выбранное состояние ведомого устройства отменено.
3. Достоверность данных
SPI использует сигнальные линии MOSI и MISO для передачи данных, а сигнальные линии SCK — для синхронизации данных. Линии данных MOSI и MISO передают один бит данных за такт такта SCK, а ввод и вывод данных выполняются одновременно. Во время передачи данных первый MSB или первый LSB не обязателен, но для обеспечения того, чтобы один и тот же протокол использовался между двумя устройствами связи SPI, обычно используется первый режим MSB на рисунке 242.
Обратите внимание на числа на рисунке, данные MOSI и MISO изменяют выходной сигнал во время нарастающего фронта SCK и отбираются на падающем фронте SCK. То есть во время спада SCK данные MOSI и MISO являются действительными. Когда уровень высокий, это означает данные «1», а когда он низкий, это означает данные «0». В других случаях данные являются недействительными, и MOSI и MISO готовятся к следующему представлению данных.
Каждая передача данных SPI может осуществляться в 8-битных или 16-битных единицах, и количество блоков в каждой передаче не ограничено.
4. CPOL / CPHA и режим связи
Синхронизация на рисунке 242, описанном выше, является лишь одним из режимов связи в SPI. В SPI есть четыре режима связи. Основное различие между ними — это состояние тактового сигнала SCK и время выборки данных, когда шина находится в режиме ожидания. Для удобства пояснения здесь представлены понятия «CPOL полярности часов» и «CPHA фазы синхросигнала».
Полярность тактового импульса CPOL относится к сигналу уровня сигнальной линии SCK, когда устройство связи SPI находится в состоянии ожидания (то есть в состоянии SCK перед началом связи SPI и когда линия NSS высокая). Когда CPOL = 0, SCK является низким уровнем в состоянии ожидания, а когда CPOL = 1, верно обратное.
Фаза синхронизации CPHA относится ко времени выборки данных.Когда CPHA = 0, сигнал в линии данных MOSI или MISO будет дискретизироваться по «нечетному фронту» линии синхронизации SCK. Когда CPHA = 1, строка данных выбирается по «четному краю» SCK. Смотрите рисунок 243 и рисунок 244.
Рисунок 243 Режим связи SPI, когда CPHA = 0
Давайте проанализируем эту временную диаграмму с CPHA = 0. Во-первых, по уровню SCK в состоянии ожидания он делится на два случая. Когда состояние простоя сигнальной линии SCK низкое, CPOL = 0, когда состояние простоя высокое, CPOL = 1.
Независимо от того, был ли CPOL = 0 или = 1, поскольку настроенная нами фаза синхронизации CPHA = 0, на рисунке видно, что время выборки находится на нечетном фронте SCK. Обратите внимание, что когда CPOL = 0, нечетным фронтом часов является нарастающий фронт, а когда CPOL = 1, нечетным фронтом часов является падающий фронт. Следовательно, время выборки SPI не определяется нарастающим / падающим фронтом. Эффективные сигналы линий данных MOSI и MISO остаются неизменными на нечетном крае SCK. Сигналы данных будут дискретизироваться на нечетном крае SCK. В моменты без выборки эффективные сигналы MOSI и MISO переключаются.
Аналогично, когда CPHA = 1, CPOL не влияет на него, и сигнал данных дискретизируется по четному краю SCK, см. Рисунок 244.
Рисунок 244 Режим связи SPI, когда CPHA = 1
В соответствии с различными состояниями CPOL и CPHA, SPI делится на четыре режима, см. Таблицу 241. Ведущий и ведомый должны работать в одном и том же режиме для нормальной связи. На практике чаще используются «режим 0» и «режим 3». ».
Таблица 241 Четыре режима SPI
SCK часы в режиме ожидания
24.2 Характеристики SPI и архитектура STM32
Как и периферия I2C, микросхема STM32 также интегрирует периферию специально для обмена данными по протоколу SPI.
24.2.1 STM32 SPI периферийное введение
В качестве главного и подчиненного устройства может использоваться периферийное устройство SPI STM32, поддерживающее наивысшую тактовую частоту SCK fpclk / 2 (для чипа модели STM32F429 по умолчанию установлено значение 90 МГц для fpclk1 и 45 МГц для fpclk2). Он полностью поддерживает 4 режима протокола SPI, данные Длина кадра может быть установлена равной 8 или 16 битам, и данные MSB сначала или LSB сначала могут быть установлены. Он также поддерживает двухпроводной полнодуплексный режим (этот режим описан в предыдущем разделе), двухпроводной однонаправленный и однопроводной режимы. Среди них двухпроводной однонаправленный режим может использовать линии данных MOSI и MISO для одновременной передачи данных в одном направлении, что может удвоить скорость передачи в два раза. Однопроводный режим может уменьшить аппаратную проводку, конечно, эта скорость будет затронута. Мы объясняем только двухпроводной полнодуплексный режим.
Периферийное устройство SPI STM32 также поддерживает функцию I2S. Функция I2S — это протокол последовательной передачи аудиосигнала, который будет представлен в главе, где мы объясним MP3-плеер позже.
24.2.2 Анализ SPI архитектуры STM32
Рисунок 245 Схема архитектуры SPI
1. Коммуникационные контакты
Все аппаратные архитектуры SPI разработаны на основе линий MOSI, MISO, SCK и NSS слева на рисунке 245. Микросхема STM32 имеет несколько периферийных устройств SPI, и их сигналы связи SPI выводятся на разные выводы GPIO. При использовании они должны быть настроены на эти указанные выводы, см. Таблицу 242. Для функции мультиплексирования выводов GPIO, пожалуйста, обратитесь к «Спецификациям STM32F4xx», в зависимости от того, что преобладает.
Таблица 242 Выводы SPI STM32F4xx (отсортировано по «Спецификации STM32F4xx»)
Среди них SPI1, SPI4, SPI5, SPI6 — это устройства на APB2, самая высокая скорость связи — 45 Мбит / с, SPI2, SPI3 — устройства на APB1, самая высокая скорость связи — 22,5 Мбит / с. Помимо скорости связи, нет никакой разницы в других функциях.
2. Логика управления часами
Тактовый сигнал линии SCK управляется генератором скорости передачи в соответствии с битами BR [0: 2] в «Регистре управления CR1». Этот бит является коэффициентом деления частоты для тактовых импульсов fpclk, а результатом деления частоты для fpclk является вывод SCK Выходная тактовая частота рассчитана в таблице 243.
Таблица 243 Частотное деление битов BR на fpclk
Результат деления частоты (частота SCK)
Результат деления частоты (частота SCK)
Частота fpclk относится к частоте шины APB, на которой расположен SPI, APB1 — это fpclk1, а APB2 — это fpckl2.
Конфигурируя биты «CPOL» и «CPHA» «регистра управления CR», SPI может быть установлен на четыре режима SPI, проанализированных ранее.
3. Логика управления данными
MOSI и MISO SPI подключены к регистру сдвига данных, содержимое регистра сдвига данных поступает из приемного буфера, буфера отправки и линий MISO и MOSI. При отправке данных регистр сдвига данных принимает «буфер отправки» в качестве источника данных и отправляет данные побитно по линии данных, а при приеме данных извне регистр сдвига данных помещает строку данных Выбранные данные сохраняются в «приемном буфере» побитно. Заполните данные в буфере отправки, записав «регистр данных DR» SPI, и получите содержимое буфера приема через «регистр данных DR». Длина фрейма данных может быть сконфигурирована в 8-битном и 16-битном режимах через «бит DFF» «регистра управления CR1», при настройке «бита LSBFIRST» можно выбрать MSB первым или LSB первым.
4. Общая логика управления
Общая логика управления отвечает за координацию всей периферии SPI. Режим работы логики управления изменяется в соответствии с параметрами настраиваемого нами «регистра управления (CR1 / CR2)». Основные параметры управления включают вышеупомянутый режим SPI, скорость передачи в бодах и LSB Расширенный, ведущий-ведомый режим, однонаправленный режим и т. Д. Когда периферийное устройство работает, логика управления изменит «регистр состояния (SR)» в соответствии с рабочим состоянием периферийного устройства. Нам нужно только прочитать биты регистра, связанные с регистром состояния, чтобы понять рабочее состояние SPI. Кроме того, управляющая логика также отвечает за управление генерацией сигналов прерывания SPI, запросов DMA и управление сигнальными линиями NSS по мере необходимости.
В практических приложениях мы обычно не используем стандартную сигнальную линию NSS периферийного устройства SPM STM32, а просто используем обычный GPIO, и программное обеспечение контролирует его выходной уровень, тем самым генерируя сигналы начала и остановки связи.
24.2.3 Процесс общения
Когда STM32 использует периферийную связь SPI, она будет записывать параметры в разные биты данных «регистра состояния SR» на разных этапах связи. Мы можем понять состояние связи, прочитав эти флаги регистра.
На рисунке 246 показан поток «основного режима», то есть процесс передачи и приема данных, когда STM32 используется в качестве хост-конца связи SPI.
Рисунок 246 Процесс связи с главным передатчиком
Основной режим отправки и получения процесса и описание события:
(1) Управление сигнальной линией NSS для генерации сигнала запуска (не показано на рисунке);
(2) Записать данные для отправки в «регистр данных DR», данные будут сохранены в буфере отправки;
(3) Связь начинается, и часы SCK начинают работать. MOSI передает данные в буфере отправки по битам, MISO по битам сохраняет данные в буфере приема;
(4) Когда кадр данных отправляется, «флаг TXE» в «Регистре состояния SR» будет установлен в 1, указывая, что кадр был передан, а буфер передачи пуст, аналогично, когда кадр данных был принят В это время «флаг RXNE» будет установлен в 1, указывая, что кадр был передан и буфер приема не пуст;
(5) Дождитесь, пока «флаг TXE» станет равным 1, если вы хотите продолжить отправку данных, затем снова запишите данные в «регистр данных DR»; дождитесь, пока «флаг RXNE» будет равен 1, прочитав « Регистр данных "DR" может получить содержимое буфера приема.
Если мы включим прерывание TXE или RXNE, сигнал прерывания SPI будет сгенерирован, когда TXE или RXNE установлен в 1 и войдет в ту же функцию обслуживания прерываний.После прибытия в программу обслуживания прерываний SPI мы можем проверить, какой бит является событием, проверив биты регистра. Быть обработанным. Вы также можете использовать DMA для отправки и получения данных в «Регистре данных DR».
24.3. Подробное объяснение структуры инициализации SPI
Как и другие периферийные устройства, стандартная библиотека STM32 обеспечивает структуру инициализации SPI и функцию инициализации для настройки периферийных устройств SPI. Структура и функции инициализации определены в файлах библиотеки «stm32f4xx_spi.h» и «stm32f4xx_spi.c». При программировании мы можем использовать комментарии в этих двух файлах или обращаться к справочной документации библиотеки. После понимания структуры инициализации мы можем свободно использовать периферийные устройства SPI, см. Список кодов 241.
Список кодов 241 Структура инициализации SPI
1 typedef struct
3 uint16_t SPI_Direction; /* Настроить SPI однонаправленный режим * /
4 uint16_t SPI_Mode; /* Настроить SPI ведущий / ведомый режим * /
5 uint16_t SPI_DataSize; /* Настроить Длина кадра данных SPI, необязательно 8/16 бит *
6 uint16_t SPI_CPOL; /* Установите полярность часов CPOL, выбирается высокий / низкий уровень * /
7 uint16_t SPI_CPHA; /* Установить фазу часов, опционально нечетно / Четная выборка по краям * /
8 uint16_t SPI_NSS; /* Настроить Независимо от того, управляется ли вывод NSS аппаратным или программным обеспечением SPI * /
9 uint16_t SPI_BaudRatePrescaler; /* Установите коэффициент деления часов, fpclk / номер деления частоты = fSCK * /
10 uint16_t SPI_FirstBit; /* Настроить MSB / LSB первый * /
11 uint16_t SPI_CRCPolynomial; /* Настроить Контрольное выражение CRC * /
Члены этих структур описаны следующим образом: текст в скобках — это макрос, определяемый соответствующим параметром в стандартной библиотеке STM32:
Этот элемент устанавливает направление связи SPI, которое может быть установлено в двухпроводной полнодуплексный (SPI_Direction_2Lines_FullDuplex), только двухпроводный прием (SPI_Direction_2Lines_RxOnly), только однострочный прием (SPI_Direction_1Line_Rx), однострочный режим отправки только (SP1_Direction).
Этот элемент устанавливает SPI для работы в основном режиме (SPI_Mode_Master) или в подчиненном режиме (SPI_Mode_Slave). Самым большим различием между этими двумя режимами является синхронизация сигнальной линии SCK SPI. Синхронизация SCK генерируется хостом в процессе связи. При настройке в качестве подчиненного режима периферийное устройство SPI STM32 будет принимать внешний сигнал SCK.
Этот член может выбрать, будет ли размер кадра данных связи SPI 8 бит (SPI_DataSize_8b) или 16 бит (SPI_DataSize_16b).
(4) SPI_CPOL и SPI_CPHA
Эти два элемента конфигурируют полярность синхронизации CPOL и фазу синхронизации CPHA SPI. Эти две конфигурации влияют на режим связи SPI. Описание CPOL и CPHA см. В предыдущем разделе. Раздел «Режим связи».
Элемент CPOL полярности часов может быть установлен на высокий уровень (SPI_CPOL_High) или низкий уровень (SPI_CPOL_Low).
Фаза синхронизации CPHA может быть установлена в SPI_CPHA_1Edge (сбор данных по нечетному фронту SCK) или SPI_CPHA_2Edge (сбор данных по четному фронту SCK).
Этот элемент настраивает режим использования контакта NSS, который можно выбрать в качестве аппаратного режима ( SPI_NSS_Hard ) И программный режим ( SPI_NSS_Soft ), сигнал выбора микросхемы SPI в аппаратном режиме автоматически генерируется аппаратным обеспечением SPI, в то время как программный режим требует, чтобы мы лично поднимали соответствующий порт GPIO на высокий или низкий уровень, чтобы генерировать сигналы без микросхемы выбора и выбора микросхемы. На практике существует множество приложений программного режима.
Этот элемент устанавливает коэффициент деления скорости передачи, а разделенная тактовая частота является тактовой частотой сигнальной линии SPI SCK. Этот параметр-член может быть установлен в fpclk 2, 4, 6, 8, 16, 32, 64, 128, 256 с частотным разделением.
Все последовательные протоколы связи будут иметь сначала MSB (сначала более высокие данные) или сначала LSB (сначала более низкие данные), и модуль SPI STM32 может быть запрограммирован для управления этой функцией через этот элемент структуры.
Это полином в проверке CRC SPI. Если мы используем проверку CRC, параметр (полином) этого члена используется для вычисления значения CRC.
После настройки этих элементов структуры нам нужно вызвать функцию SPI_Init для записи этих параметров в регистр для реализации инициализации SPI, а затем вызвать SPI_Cmd для включения периферийного устройства SPI.
24.4. SPI — чтение и запись эксперимента с последовательной флэш-памятью
Память FLSAH также известна как флэш-память, и она, и EEPROM являются памятью, которая не теряет данные после сбоя питания, но емкость флэш-памяти обычно больше, чем EEPROM, и теперь она в основном сменила свой статус. Диски U, SD-карты, твердотельные накопители SSD, обычно используемые в нашей жизни, и устройства, используемые для хранения программ внутри наших микросхем STM32, представляют собой флэш-память. В управлении хранилищем основное отличие состоит в том, что микросхема FLASH может быть стерта и стерта только большими кусками, и в «главе I2C» мы узнали, что EEPROM может быть стерта и записана в один байт.
В этом разделе микросхема последовательной флэш-памяти, использующая коммуникацию SPI, читает и пишет эксперименты для всех, чтобы объяснить использование STM32 SPI. В эксперименте периферийное устройство SPI STM32 использует основной режим, а нормальная связь обеспечивается путем запроса событий.
24.4.1 аппаратный дизайн
Рисунок 247 Схема подключения оборудования SPI serial FLASH
Микросхема FLASH (модель: W25Q128) в этой экспериментальной плате является флэш-памятью NOR, использующей протокол связи SPI, ее выводы CS / CLK / DIO / DO подключены к соответствующим выводам SDI NSS / SCK / MOSI / STM32 соответственно. В MISO вывод NSS STM32 — это обычный GPIO, а не выделенный вывод NSS SPI, поэтому нам нужно использовать программное управление в программе.
В микросхеме FLASH также есть контакты WP и HOLD. Вывод WP может управлять функцией защиты от записи. Если этот вывод низкий, запись данных запрещена. Мы напрямую подключаемся к источнику питания и не используем функцию защиты от записи. Вывод HOLD можно использовать для приостановки обмена данными. Когда этот вывод низкий, обмен данными приостанавливается, вывод вывода данных выводит состояние высокого импеданса, а выводы синхронизации и ввода данных недопустимы. Мы напрямую подключаемся к источнику питания и не используем функцию паузы связи.
Для получения дополнительной информации о микросхеме FLASH, пожалуйста, обратитесь к ее техническому описанию "W25Q128", чтобы понять. Если модель или контрольный вывод FLASH используемой вами платы эксперимента отличаются, просто измените их в соответствии с нашим проектом, принцип управления программой тот же.
24.4.2 разработка программного обеспечения
Чтобы сделать проект более организованным, мы отдельно храним и читаем код, связанный с FLASH, чтобы облегчить будущую трансплантацию. Создайте новые файлы "bsp_spi_flash.c" и "bsp_spi_flash.h" поверх "шаблона проекта". Эти файлы также могут быть названы в соответствии с вашими предпочтениями. Они не являются частью стандартной библиотеки STM32 и написаны нами в соответствии с потребностями приложения. ,
1. Ключевые моменты программирования
(7) Целевые контакты и часы портов, используемые для первоначальной связи;
(8) включить синхронизацию периферийного устройства SPI;
(9) настроить режим периферийного устройства SPI, адрес, скорость и другие параметры и включить периферийное устройство SPI;
(10) Написать базовую функцию SPI для отправки и получения байтов;
(11) Функции записи для стирания FLASH и операций чтения и записи;
(12) Написать тестовую программу для проверки чтения и записи данных.
Определение макросов, связанных с оборудованием SPI
Мы определяем все связанные с оборудованием SPI конфигурации в виде макросов в файле "bsp_spi_flash.h", см. Список кодов 242.
Список кодов 242 Макросы, связанные с аппаратной конфигурацией SPI
1 //SPI число И функция инициализации часов
2 #define FLASH_SPI SPI3
3 #define FLASH_SPI_CLK RCC_APB1Periph_SPI3
4 #define FLASH_SPI_CLK_INIT RCC_APB1PeriphClockCmd
6 #define FLASH_SPI_SCK_PIN GPIO_Pin_3
7 #define FLASH_SPI_SCK_GPIO_PORT GPIOB
8 #define FLASH_SPI_SCK_GPIO_CLK RCC_AHB1Periph_GPIOB
9 #define FLASH_SPI_SCK_PINSOURCE GPIO_PinSource3
10 #define FLASH_SPI_SCK_AF GPIO_AF_SPI3
12 #define FLASH_SPI_MISO_PIN GPIO_Pin_4
13 #define FLASH_SPI_MISO_GPIO_PORT GPIOB
14 #define FLASH_SPI_MISO_GPIO_CLK RCC_AHB1Periph_GPIOB
15 #define FLASH_SPI_MISO_PINSOURCE GPIO_PinSource4
16 #define FLASH_SPI_MISO_AF GPIO_AF_SPI3
18 #define FLASH_SPI_MOSI_PIN GPIO_Pin_5
19 #define FLASH_SPI_MOSI_GPIO_PORT GPIOB
20 #define FLASH_SPI_MOSI_GPIO_CLK RCC_AHB1Periph_GPIOB
21 #define FLASH_SPI_MOSI_PINSOURCE GPIO_PinSource5
22 #define FLASH_SPI_MOSI_AF GPIO_AF_SPI3
24 #define FLASH_CS_PIN GPIO_Pin_8
25 #define FLASH_CS_GPIO_PORT GPIOI
26 #define FLASH_CS_GPIO_CLK RCC_AHB1Periph_GPIOI
28 // контроль CS (NSS) вывод вывода низкого уровня
29 #define SPI_FLASH_CS_LOW()
30 // контроль CS (NSS) вывод высокого уровня вывода
31 #define SPI_FLASH_CS_HIGH()
Приведенный выше код инкапсулирует номер SPI, номер контакта, источник вывода и отображение мультиплексной функции, используемые при обмене данными FLASH с макросами в соответствии с аппаратным соединением, и определяет макрос, который контролирует уровень вывода вывода CS (NSS), так что Используется, когда конфигурация генерирует сигналы запуска и остановки.
Инициализировать SPI GPIO
Используйте приведенный выше макрос для написания функции инициализации SPI, см. Листинг 243 кода.
Список кодов 243 Функция инициализации SPI (часть инициализации GPIO)
3 * @brief SPI_FLASH инициализация
7 void SPI_FLASH_Init( void )
9 GPIO_InitTypeDef GPIO_InitStructure;
11 /* включить Часы FLASH_SPI и GPIO * /
12 /*!< SPI_FLASH_SPI_CS_GPIO, SPI_FLASH_SPI_MOSI_GPIO,
13 SPI_FLASH_SPI_MISO_GPIO с SPI_FLASH_SPI_SCK_GPIO включение часов * /
14 RCC_AHB1PeriphClockCmd (FLASH_SPI_SCK_GPIO_CLK | FLASH_SPI_MISO_GPIO_CLK|
15 FLASH_SPI_MOSI_GPIO_CLK|FLASH_CS_GPIO_CLK, ENABLE);
17 /*!< SPI_FLASH_SPI Часы включения */
18 FLASH_SPI_CLK_INIT(FLASH_SPI_CLK, ENABLE);
20 // Установить пин-мультиплексирование
28 /*!< конфигурация SPI_FLASH_SPI: SCK * /
29 GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;
30 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
31 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
32 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
33 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
36 GPIO_Init(FLASH_SPI_SCK_GPIO_PORT, &GPIO_InitStructure);
38 /*!< конфигурация SPI_FLASH_SPI контакт: MISO * /
39 GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;
40 GPIO_Init(FLASH_SPI_MISO_GPIO_PORT, &GPIO_InitStructure);
42 /*!< конфигурация SPI_FLASH_SPI pin: MOSI * /
43 GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;
44 GPIO_Init(FLASH_SPI_MOSI_GPIO_PORT, &GPIO_InitStructure);
46 /*!< конфигурация SPI_FLASH_SPI pin: CS * /
47 GPIO_InitStructure.GPIO_Pin = FLASH_CS_PIN;
48 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
49 GPIO_Init(FLASH_CS_GPIO_PORT, &GPIO_InitStructure);
51 /* Стоп сигнал FLASH: CS pin высокий уровень * /
53 /* Для простоты объяснения следующее опущено Часть инициализации режима SPI * /
Как и для всех периферийных устройств, использующих GPIO, вы должны сначала инициализировать используемый режим вывода GPIO и настроить функцию мультиплексирования. Процесс инициализации GPIO выглядит следующим образом:
(1) Используйте GPIO_InitTypeDef для определения переменных структуры инициализации GPIO, чтобы можно было использовать следующее для хранения конфигурации GPIO;
(2) Вызвать библиотечную функцию RCC_AHB1PeriphClockCmd, чтобы включить часы порта GPIO, используемые выводами SPI, и использовать операцию «|» для одновременной настройки нескольких выводов. Вызовите макрос FLASH_SPI_CLK_INIT, чтобы включить периферийные часы SPI (этот макрос инкапсулирует библиотечную функцию для включения синхронизации APB).
(3) Присвойте значения структуре инициализации GPIO и инициализируйте выводы SCK / MOSI / MISO в мультиплексном двухтактном режиме. Поскольку вывод CS (NSS) управляется программным обеспечением, мы настраиваем его как обычный двухтактный выходной режим.
(4) Используя конфигурацию вышеуказанной структуры инициализации, вызовите функцию GPIO_Init для записи параметров в регистр для завершения инициализации GPIO.
Настроить режим SPI
Выше приведена только конфигурация контактов, используемых SPI, и конфигурация периферийного режима SPI. Перед настройкой режима SPI STM32 мы должны сначала понять режим SPI ведомой стороны. Этот пример можно получить, обратившись к спецификации FLASH "W25Q128". Согласно описанию микросхемы FLASH, он поддерживает режим SPI 0 и режим 3, поддерживает двухпроводной полный дуплекс, использует первый режим MSB, поддерживает самый высокий тактовый сигнал связи 104 МГц, а длина кадра данных составляет 8 бит. Нам необходимо настроить эти параметры в периферийном устройстве SPI STM32 для обеспечения согласованности. См код листинга 244.
Листинг 244 Настройка режима SPI
2 * @brief SPI_FLASH Инициализация булавки
6 void SPI_FLASH_Init( void )
8 /* Для простоты объяснения опущено Часть инициализации SPI GPIO * /
11 SPI_InitTypeDef SPI_InitStructure;
12 /* FLASH_SPI Конфигурация режима */
13 // FLASH чип Поддержка SPI Mode 0 и Mode 3, установите CPOL CPHA соответственно
14 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
15 SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
16 SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
17 SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
18 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
19 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
20 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
21 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
22 SPI_InitStructure.SPI_CRCPolynomial = 7;
23 SPI_Init(FLASH_SPI, &SPI_InitStructure);
25 /* включить FLASH_SPI */
26 SPI_Cmd(FLASH_SPI, ENABLE);
В этом коде периферийное устройство SPM STM32 сконфигурировано как сторона хоста, двухпроводный полнодуплексный режим, длина кадра данных составляет 8 битов, используется режим SPI 3 (CPOL = 1, CPHA = 1), вывод NSS управляется программным обеспечением И MSB первый режим. Последний элемент — формула расчета CRC. Поскольку нам не требуется проверка CRC для связи с микросхемой FLASH, а функция CRC SPI не включена, значение элемента формулы расчета CRC является недействительным.
После завершения назначения вызывается библиотечная функция SPI_Init для записи этих конфигураций в регистр, а функция SPI_Cmd вызывается для включения периферийного устройства.
Используйте SPI для отправки и получения одного байта данных
После инициализации периферийного устройства SPI можно использовать обмен SPI. Сложный обмен данными состоит из одного байта передачи и приема данных. Давайте посмотрим на его реализацию кода, см. Список кодов 245.
Листинг 245. Использование SPI для отправки и получения одного байта данных
1 #define Dummy_Byte 0xFF
3 * @brief использование SPI отправляет один байт данных
4 * @param byte : Данные для отправки
5 * @retval Вернуть полученные данные
7 u8 SPI_FLASH_SendByte(u8 byte)
9 SPITimeout = SPIT_FLAG_TIMEOUT;
11 /* Подождите, пока буфер отправки будет пустым, TXE событие * /
12 while (SPI_I2S_GetFlagStatus(FLASH_SPI, SPI_I2S_FLAG_TXE) == RESET)
14 if ((SPITimeout—) == 0) return SPI_TIMEOUT_UserCallback(0);
17 /* Записать в регистр данных, записать данные для записи в буфер отправки */
18 SPI_I2S_SendData(FLASH_SPI, byte);
20 SPITimeout = SPIT_FLAG_TIMEOUT;
22 /* Подождите, пока приемный буфер не будет пустым, Событие RXNE * /
23 while (SPI_I2S_GetFlagStatus(FLASH_SPI, SPI_I2S_FLAG_RXNE) == RESET)
25 if ((SPITimeout—) == 0) return SPI_TIMEOUT_UserCallback(1);
28 /* Прочитайте регистр данных, чтобы получить данные буфера приема */
29 return SPI_I2S_ReceiveData(FLASH_SPI);
33 * @brief использование SPI читает один байт данных
35 * @retval Вернуть полученные данные
37 u8 SPI_FLASH_ReadByte( void )
39 return (SPI_FLASH_SendByte(Dummy_Byte));
Однобайтовая функция отправки SPI_FLASH_SendByte содержит обработку тайм-аута ожидания событий.Эта часть принципа такая же, как в I2C, и здесь не будет повторяться.
Функция SPI_FLASH_SendByte реализует «процесс связи SPI», описанный ранее:
(1) Эта функция не содержит сигналов запуска и остановки SPI, а только основной процесс отправки и получения, поэтому операции с сигналами запуска и остановки должны выполняться до и после вызова этой функции;
(2) Назначьте макрос SPIT_FLAG_TIMEOUT переменной SPITimeout. Эта переменная SPITimeout уменьшается на 1 каждый раз в следующем цикле while. Цикл обнаруживает событие, вызывая библиотечную функцию SPI_I2S_GetFlagStatus. Если событие обнаружено, оно переходит на следующий этап связи. Если событие не обнаружено, оно остается здесь и было обнаружено. , Когда обнаруживается, что SPIT_FLAG_TIMEOUT не ожидал события, считается, что связь не удалась. Вызываемый SPI_TIMEOUT_UserCallback выводит информацию об отладке и выходит из связи;
(3) Проверяя флаг TXE, можно получить состояние буфера отправки. Если буфер отправки пуст, это означает, что последние данные, которые могут существовать, были отправлены;
(4) После ожидания, пока буфер отправки не станет пустым, вызовите библиотечную функцию SPI_I2S_SendData, чтобы записать «байт» данных для отправки в регистр данных SPI DR. Данные, записанные в регистр данных SPI, будут сохранены в буфере отправки. Набор для отправки;
(5) После записи дождитесь события RXNE, то есть буфер приема не пуст. Поскольку передача данных между MOSI и MISO является синхронной в двухпроводном полнодуплексном режиме SPI (для сравнения, пожалуйста, сравните с «процессом связи SPI»), когда буфер приема не пуст, это означает, что вышеуказанные данные отправлены, а также получен буфер приема. Новые данные
(6) При ожидании, пока приемный буфер не будет пустым, вы можете получить новые данные в приемном буфере, вызвав библиотечную функцию SPI_I2S_ReceiveData для чтения регистра данных SPI DR. В коде используется ключевое слово «return», чтобы принимать полученные данные в качестве возвращаемого значения функции SPI_FLASH_SendByte, поэтому мы можем видеть функцию получения данных SPI SPI_FLASH_ReadByte, определенную ниже, она просто вызывает функцию SPI_FLASH_SendByte для отправки данных «Dummy_Byte», Затем получите его возвращаемое значение (потому что вас не волнуют отправленные данные, поэтому входной параметр "Dummy_Byte" может быть любым значением) Причина, по которой это может быть сделано, заключается в том, что процесс получения и процесс отправки SPI по существу одинаковы, а отправка и получение синхронизированы.Ключевым моментом является то, что в наших приложениях верхнего уровня основное внимание уделяется отправленным или полученным данным.
Инструкция по контролю FLASH
После получения базового блока приемопередатчика SPI вам также необходимо понять, как читать и записывать данные в микросхему FLASH. Микросхема FLASH определяет множество инструкций. Мы используем шину SPI для отправки инструкций микросхеме FLASH, управляя STM32. После получения микросхемы FLASH она выполнит соответствующую операцию.
И эти инструкции для хост-стороны (STM32) следуют только за данными, отправляемыми по самому базовому протоколу связи SPI, но на стороне устройства (микросхема FLASH) интерпретируют эти данные в разных значениях, поэтому они становятся командой. Проверьте спецификацию "W25Q128" микросхемы FLASH, чтобы узнать о функциях и форматах команд различных команд, определенных им, см. Таблицу 244.
Таблица 244 Таблица инструкций общей микросхемы FLASH (из спецификации "W25Q128")
Как записать переменную во флеш память 80c51f120
Добрый день! Существует необходимость постоянного хранения в памяти (энергонезависимой) микроконтроллера c8051f320 достаточно большого массива чисел (255 строк 3 столбца), сами числа от 0x00 до 0xFF. Поглядев даташит мк (опыта работы с ним не имею), пришел к выводу, что для подобных задач предназначена 16-килобайтная FLASH память, однако инструкции, как ей пользоваться не нашел.
Кратко говоря, мне нужно:
1) Заранее записать в память мой массив (в наличии присутствует developing kit: http://www.silabs.com/products/mcu/Pages/C8051F320DK.aspx , подсоедененный через USB)
2) Считывать данные из массива в ходе работы основной программы МК (Пишу в стандартном IDE, язык C-51)
Буду благодарен за любую помощь!
Не совсем понятно что вы имеете в виду. Надо хранить массив данных в flash, который готовится на этапе создания прошивки и никогда более не изменяется, либо изменяется на лету самой программой?
В первом случае в компиляторе определено какое-то специальное ключевое слово вроде const или flash, говорящее линкеру воткнуть массив в определенный тип памяти. Обращаться с константными массивами и переменными как и с любыми остальными, только писать в них нельзя.
Во втором случае используются функции bootloader-а или программирования приложением(IAP). Как обстоят дела с этим конкретно у С8051 не знаю, но по идее все(почти) современные МК так умеют.
Сборка печатных плат от $30 + БЕСПЛАТНАЯ доставка по всему миру + трафарет
Приглашаем 13 октября всех желающих присоединиться к вебинару, который будет проводить компания КОМПЭЛ совместно с представителями бренда MEAN WELL. Вебинар будет посвящен новинкам продукции, планам MEAN WELL на следующий год, аналогам продукции ушедших из РФ брендов, особенностям работы в текущих условиях, возможностях субдистрибьюции и другим вопросам. Мероприятие пройдет в формате живого диалога.
Если в даташите указан режим самопрогорамммирования ПЗУ — возможно.
С силапсами не работал.
В "стандартном" mcs51 подобная операция отсутствует — однако вполне возможна при вводе дополнительных функций через "свободную" область РСФ (на усмотрение разработчиков фирмы-изготовителя). Требуется доскональное "вкуривание" даташита на конкретный МК и юзерс гвиде изготовителя на группу МК.
Ежли речь идет о записи в EEPROM — то также даташит в лапы.
Производитель популярных модулей электропитания MORNSUN помимо них предлагает также микросхемы ШИМ-контроллеров для AC/DC и DC/DC, микросхемы запуска (стартеры) для этих ШИМ-контроллеров, драйверы интерфейсов RS-485 и CAN и микросхемы изоляторов для гальванической развязки интерфейсов и цифровых сигнальных линий. В некоторых случаях эти изделия превосходят по характеристикам изделия европейских и американских производителей.
Сделаем мир удобнее. —>
Первое, из-за чего возникло желание более подробно разобраться с flash-памятью, явилось то, что в своих проектах хотелось иметь энергонезависимую память, но к сожалению серия микроконтроллеров STM32f1xx не имеет встроенной eeprom. Во вторых, без знания работы flash было бы проблематично написать собственный bootloader.
Кроме, собственно, встроенной flash-памяти, я также немного расскажу и о оперативной памяти. Как обычно, основным источником для статьи является родное руководство от компании STMicroelectronics (Reference Manual), а также обобщенная информация из различных форумов и статей в интернете, ну и на основе своего опыта.
Предисловие
Изначально планировалось объединить в одной статье информацию о структуре, регистрах и методах работы с Flash. Но в итоге, статья получилась бы слишком громоздкой, поэтому было принято решение написать две статьи: одна для начинающих, используя готовые библиотеки от STM, а вторая статья будет о том, как, собственно, эти библиотеки работают с памятью.
В данной статье я расскажу о структуре памяти, основных требованиях при работе с ней и о некоторых моментах, которые могут помочь быстрее разобрать с Flash.
Почему FLASH?
Во многих небольших проектах возникает необходимость сохранять какие-либо параметры работы устройства в независимой от электричества памяти (EEPROM). Но в STMicroelectronics решили, что использование этой памяти в большинстве микроконтроллеров не целесообразно, исключение составляет только серия LP (Low Power), в которой EEPROM присутствует.
В крупных проектах проблемы нет — используется внешняя память, а вот в небольших использование внешней памяти не всегда выход, так как, помимо всего прочего, нужно эту память разместить на плате и правильно развести.
В интернете полно различных примеров, есть даже изыски о том, как хранить небольшой объем данных в регистрах backup’a, но все же, если не прибегать к внешней памяти, основным способом хранения данных с защитой при отключении питания является хранение во внутренней flash-памяти.
В первую очередь flash-память предназначена для хранения инструкций для микроконтроллера, другими словами — программного кода. Но если Ваш программный код занимает не весь объем памяти, то почему бы не выделить ее часть под хранение наших настроек?
- Не требуется использование внешней памяти, соответственно сокращается время на разработку платы и происходит удешевление продукта;
- Меньше программного кода, следовательно меньшее время затрачивается на разработку.
Есть конечно и свои ограничения:
- Запись во Flash требует некоторого времени, что влияет на производительность МК в момент записи или очистки памяти;
- Чтобы внести изменения в уже существующие данные, нужно стереть всю страницу или записывать в «чистый» блок памяти;
- Количество циклов перезаписи гарантировано в районе 100 тысяч операций — вроде бы много, но перезаписывая данные в страницу раз в секунду, МК выработает ресурс flash чуть более, чем за сутки. Поэтому очень не рекомендую постоянно писать во flash, рекомендуется производить операции очистки / записи лишь для сохранения данных в «энергонезависимой» памяти. В остальных случаях работаем с оперативной памятью;
- Минимум Вы можете использовать 1 страницу памяти (даже для одного байта), а размер одной страницы составляет от одного до двух килобайт, в зависимости от модели микроконтроллера. Такова селяви устройства flash-памяти.
Но не стоит забывать что в первую очередь flash-память предназначена для хранения программного кода и, соответственно, сама структура интерфейса Flash в микроконтроллере устроена таким образом, чтобы оптимизировать загрузку инструкций для МК и организовать их (инструкций) кэширование, пока идет выполнение уже загруженных.
Структура
Как нам сообщает Reference Manual, модуль flash-памяти является высокопроизводительным и имеет следующие ключевые особенности:
- Для устройств серии XL-density: объем памяти составляет до 1Mb, разбитый на два банка памяти:
— 1-й банк памяти: 512KB;
— 2-й банк памяти: до 512KB, в зависимости от модели микроконтроллера.
- Для других устройств: объем памяти составляет до 512KB, состоящий из одного банка памяти размером до 512KB.
- Сама по себе Flash-память состоит из трех блоков: основной блок памяти, информационный блок и блока регистров управления Flash. Структура блоков памяти и их характеристики приведены на рисунке №1.
Интерфейс flash-памяти (FLITF (Flash Memory Interface)) позволяет выполнять следующие операции:
- Чтение из памяти с возможностью буферизации (2 слова по 64 бита);
- Операции записи и очистки памяти;
- Организация защиты flash от чтения и/или записи.
Основная flash-память разбита на страницы размером в 1 или 2 килобайта, это зависит от линейки микроконтроллеров STM. Использовать под свои нужды Вы можете только целое количество страниц, соответственно, если Ваша прошивка занимает все страницы памяти МК, то использование flash-памяти под свои нужды будет проблематично.
Два оставшихся блока памяти, информационный и блок регистров, достаточно редко используются напрямую, в основном используется функционал из готовых библиотек. Поэтому в дальнейшем описании я коснусь только регистра настройки, а подробное описание регистров памяти и информационного блока я приведу в отдельной статье.
Операции с flash-памятью
Операций работы с flash-памятью не так уж и много: можем прочитать, записать и очистить память. Причем запись и очистка памяти, как правило, осуществляется вместе. Также еще можно заблокировать память для чтения и/или для записи. Но перед началом работы необходимо произвести инициализацию модуля Flash.
Инициализация Flash.
Операции чтения flash-памяти и доступ к данным выполняются через шину AHB. Буферизация чтения, для выполнения команд, выполняется через шину ICode. Арбитраж выполнения осуществляется самой flash-памятью, а приоритет отдается доступу к данным на шине DCode. Другими словами, пока микроконтроллер выполняет текущую команду, интерфейс Flash-памяти передает МК данные по следующей.
Чтение данных из flash-памяти может быть сконфигурировано следующим образом:
• Задержка (Latency): количество состояний ожидания для операции чтения, устанавливается «на лету», не требуется инициализация МК;
• Буферизация (2 слова по 64 бита) (Prefetch buffer): активируется во время инициализации МК. Весь блок памяти может быть заменен за один цикл чтения из памяти, так как размер блока соответствует размеру полосы пропускания шины flash-памяти. Благодаря буферизации, возможна более быстрая работа МК, поскольку МК извлекает из памяти по одному «слову» за раз одновременно со следующим «словом», которое помещается в предварительный буфер.
• Половинный цикл (Half cycle): этот режим требуется для оптимизации питания, чаще всего используется при работе от батарейки с программами, которые не требуют особого быстродействия.
Эти параметры следует использовать в соответствии с временем доступа к flash-памяти. Значение периода ожидания представляет собой отношение периода SYSCLK (системных часов) к времени доступа к flash-памяти и устанавливается в зависимости от следующих параметров:
- Уровень «0», если 0 <SYSCLK ≤ 24 MHz;
- Уровень «1», если 24 MHz < SYSCLK ≤ 48 MHz;
- Уровень «2», если 48 MHz < SYSCLK ≤ 72 MHz;
Конфигурация с половинным циклом недоступна в сочетании с предделителем на AHB. Системные часы (SYSCLK) должны быть равны часам HCLK. Поэтому эту функцию можно использовать только с низкочастотными тактовыми частотами 8MHz или менее. Он может быть создан из HSI или HSE, но не из PLL.
Буфер предварительной выборки должен храниться при использовании пределителя, отличного от 1 на часах AHB.
Буфер предварительной выборки должен быть включен / выключен только тогда, когда SYSCLK ниже 24MHz и на часах AHB не применяется предварительный делитель (SYSCLK должен быть равен HCLK). Буфер предварительной выборки обычно включается / выключается во время процедуры инициализации, а микроконтроллер работает на встроенном генераторе 8MHz (HSI).
Это информация довольно сложна для восприятия при начальном уровне знаний, для понимания ее работы требуется умение настраивать тактирование МК и всех его интерфейсов.
Для инициализации Flash предназначен регистр FLASH_ACR, который используется для включения / выключения буферизации и полного цикла чтения, а также для управления задержкой времени доступа к flash-памяти в зависимости от настроенной частоты работы процессора. В приведенной ниже таблице приведены битовые карты и описания бит для этого регистра.
Address offset: 0x00
Reset value: 0x0000 0030
Биты | Название | Описание |
---|---|---|
31:6 | Зарезервировано | |
5 | PRFTBS — Prefetch buffer status | Состояние буферизации. 0: отключена; 1: включена. Бит доступен только для чтения. |
4 | PRFTBE — Prefetch buffer enable | Включение буферизации. 0: отключена; 1: включена. |
3 | HLFCYA — Flash half cycle access enable | Доступ к полному циклу Flash. 0: отключен; 1: включен. |
2:0 | LATENCY — Latency | Управление задержкой. Эти биты представляют отношение периода SYSCLK (системных часов) к времени доступа Flash. 000 Уровень «0», если 0 <SYSCLK≤ 24 МГц 001 Уровень «1», если 24 МГц <SYSCLK ≤ 48 МГц 010 Уровень «2», если 48 МГц <SYSCLK ≤ 72 МГц |
Теперь посмотрим, как это выглядит в программном коде:
Настройку задержки необходимо выполнять в соответствии с настройками Вашего микроконтроллера, здесь лишь приведены примеры команд.
Саму процедуру инициализации рекомендуется выполнять либо перед инициализацией микроконтроллера, либо непосредственно из функции инициализации МК (за исключением задержки, ее можно настраивать в любой момент времени работы программы).
Чтение данных из flash-памяти.
Чтение flash не представляет никаких сложностей для понимания, достаточно обратится к необходимой ячейки памяти:
Вот и все, для чтения нам больше ничего не нужно! Единственной о чем нужно помнить, это то, что чтение происходит блоками по 32 бита.
Чтобы сделать код более наглядным, чтение из flash-памяти я вынес в отдельную функцию
Запись данных во flash-память.
Запись данных во Flash устроена немного сложнее, чем чтение. Основным отличием Flash от оперативной памяти является то, что перед тем, как произвести запись значения в ячейку, ее необходимо очистить. Нельзя вот просто так записать в память сначала одно значение, затем другое — так не получится. Но и очистить ячейку памяти просто так нельзя! Можно очистить только страницу целиком или сразу всю память МК.
Операция стирания памяти не обнуляет, как казалось бы логичным, а устанавливает значение битов памяти равным единице. А операция записи только обнуляет необходимые биты. Перезаписать ноль в единицу нельзя, можно только стереть всю страницу памяти целиком. Это основное неудобство использования Flash. Поэтому используются два способа перезаписи пользовательских данных: первый — последовательно, когда параметры страницы не стираются, а дописываются в конец данных, пока не закончится страница; и второй способ, когда перед изменением данных стирается вся страница целиком, а потом уже все параметры записываются заново. При первом случае память прослужит дольше, а второй более удобный для использования.
Причина этого в том, что Flash память не является оперативной, она намного более медленная, чем оперативная, и предназначена для хранения набора инструкций для микроконтроллера. Но есть и большой плюс: при отключении питания она не стирается.
Обычно под свои нужды программисты используют одну-две последних страницы памяти МК. Это обусловлено тем, что код программы может модифицироваться и занимать все больше объема flash. А так как, запись инструкций для МК производится, как правило, с первой страницы, то может произойти ситуация, что запись новой прошивки просто перетрем пользовательские данные. Исключение составляют ситуации, когда, к примеру, в микроконтроллер «заливается» автозагрузчик (bootloader) и основная программа начинается не с первой страницы, а, к примеру, с десятой. Но это тема отдельной статьи и здесь упоминание будет только вскользь.
А теперь, когда мы ознакомились с теорией, разберем уже практические способы реализации очистки и перезаписи flash памяти.
Как Вы наверно заметили, запись в память происходит между двумя командами «FLASH_Unlock()» и «FLASH_Lock()». Нетрудно догадаться, что это связано с разблокировкой памяти для возможности производить запись во Flash. Соответственно, после записи, Flash нужно закрыть для изменения.
NVIC_VectTab_FLASH и PARAMS_PAGE_OFFSET — это определения адресов памяти и смещения, относительно начального адреса.
Чтобы было понятнее — разберем пример:
Размер прошивки автозагрузчика составляет менее 3Kb, соответственно нам нужно начать запись основной прошивки в третью страницу или выше с учетом того, чтобы прошивка уместилась в оставшуюся память. Не забывайте, что страницы flash-памяти начинаются с нуля, соответственно четвертая по счету страница называется «Page3».
NVIC_VectTab_FLASH — обычно равен 0x08000000, он определен в системе (в файле misc.h).
FIRMWARE_PAGE_OFFSET- это наше определение и формируется по следующей формуле: НомерСтраницыПамяти * РазмерСтраницыПамяти. Номер страницы — мы определяем самостоятельно, размер страницы памяти для большинства МК серии STM32F103 составляет 1Kb, за исключением микроконтроллеров линейки HD и CL (Connectivity Line), в которых она равна двум килобайтам.
В нашем случае получается: (4-1) * 0x0400 = 0x0C00 — это смещение относительно начального адреса. Соответственно писать данные мы будем начинать с адреса 0x08000C00.
Можно конечно и сразу указать адрес, куда будем писать (0x08000C00), но лучше сразу привыкать писать «правильно» и с уклоном на будущее, чтобы Ваш код без особой переделки мог мигрировать на другие МК.
Чтобы стереть страницу памяти, достаточно в команду FLASH_ErasePage в качестве параметра передать любой адрес из диапазона адресов страницы. Как правило передают адрес первого блока нужной страницы. Не ошибитесь с определением страницы памяти, так как можно стереть и саму прошивку ))).
Запись производится блоками по четыре байта — это называется «словом» размером в 32 бита. Если попытаться произвести запись по адресу, который не кратен четырем, то программа уйдет в бесконечный цикл ожидания окончания записи.
Также стоит обратить внимание на то, что перед записью Вам необходимо очистить столько страниц, сколько килобайт памяти Вам необходимо записать округляя до большего целого (при условии что одна страница = 1Kb).
Ну и после окончания записи, необходимо заблокировать Flash для дальнейшего изменения.
Использование структур данных.
Очень удобно хранить данные в виде структуры, но существуют некоторые тонкости, которые могут попортить немало нервных клеток не только у начинающих разработчиков, но и у бывалых.
Само определение структуры выглядит примерно так:
Как видите — ничего сложного нет.
Когда Вы определяете переменную, под нее резервируется место в оперативной памяти в размере, который Вы определили. Адреса памяти резервируются подряд, в блоках по четыре байта. Соответственно, если вы определяете подряд три переменных по байту, а затем одну, размером в два байта, то в памяти они займут в общей сложности 8 байт: 3 байта в первом блоке и два во втором. В силу специфики работы с оперативной памятью, занимать память частями не получится!
На рисунке №3 я отобразил, как будет распределяться память при определении переменных.
Как видно из рисунка, для экономии памяти и правильной ее организации, переменные необходимо объявлять таким образом, чтобы объем занимаемой имя памяти был кратным четырем. Это же правило касается и определения структуры памяти.
В целом, ошибки в размещения в неправильном порядке переменных нет, при современных объемах оперативки это не критично. Но правила хорошего тона в программировании настоятельно рекомендуют определять переменные с учетом их размещения в памяти. Проверить себя, можно через отладчик, посмотрев срез памяти и порядок заполнения оперативной памяти МК по адресу 0x20000000.
В данном примере, правильный порядок определения переменных это требование, а не рекомендация, так как нам нужно знать точное количество блоков памяти, в которых разместится переменная с нашим определением структуры. Второй пример с ошибкой на рисунке 3 показывает, что если мы не так определим порядок переменных, то объем занимаемой памяти в структуре будет занимать не один блок, как мы думали, а два. Соответственно при сохранении данных во Flash, мы можем потерять часть информации.
Теперь я приведу примеры чтения и записи параметров из/в Flash одним блоком.
Переменная с нашей структурой хранится в оперативной памяти, при ее определении в оперативной памяти выделяется определенный объем байт. Мы можем записывать во Flash не только каждый параметр в отдельности, а целиком всю структуру, для этого мы определяем адрес ячейки оперативной памяти, с которого начинается наша структура, и целиком копируем его во flash. Чтение данных происходит в обратном порядке.
Ну а теперь примеры:
Хочется обратить Ваше внимание на строчку, в которой происходит запись во flash-память:
В одном случае адрес увеличивается на «i*4», а в другом просто на «i» — Это не опечатка: в первом случае у нас переменная определена как «uint32_t», а в другом как ссылка на ячейку памяти размером «uint32_t». Соответственно в первом случае мы изменяем число на четыре байта, а во втором — изменяем на единицу ссылку на ячейку памяти размеров в 4 байта.
Теперь посмотрим на то, как выглядит заполнение нашей структуры данных из Flash:
Здесь все тоже самое, что и в предыдущем листинге, с той лишь разницей, что мы читаем записанные значения в данные нашей структуры.
Блокировка чтения / записи Flash
Подошло время ознакомится с защитой данных от чтения и/ или записи.
В какой-то момент времени у разработчиков наступает момент, когда хочется спрятать какой-либо код или данные от посторонних глаз или защитить от изменения свою прошивку. Выполнить это можно несколькими способами, рассмотрим два из них: программно или через стандартную утилиту «STM32 ST-LINK Utility».
Сразу оговорюсь, защита от чтения, это не панацея — в сети полно сайтов, которые предлагают прочитать прошивку не повредив данные. Каким то образом, неизвестным мне (пока )))), они снимают бит защиты от чтения и блокируют стирание flash. Поэтому в «крутых» прошивках, защищенных от чтения, присутствует еще и дополнительная защита, позволяющая избежать клонирования и декомпиляции кода.
Установка защиты чтения и записи данных устанавливается с помощью «Options Byte», находящихся в информационном блоке Flash. В этой статье подробно расписывать работу не буду, остановлюсь лишь на ключевых моментах.
Защита от чтения реализована почти гениально: устанавливается бит запрета чтения flash (RDP — Read Protection). Если бит снимается, то все содержимое Flash стирается. Установить бит и снять его можно как программно, так и с помощью сторонних утилит.
Защита от записи реализовано постранично, блоками по 4 килобайта: для микроконтроллеров с размером страницы равным одному килобайту, блокируется сразу по четыре страницы памяти, для МК с размером памяти равным двум килобайтом — блокируется по две страницы. Защита осуществляется в ячейках памяти «Options Byte» с помощью изменения битов WRPn (Write Protection).
Программное исполнение.
Самый простой способ защитить прошивку от чтения, это воспользоваться стандартной библиотекой от STM. Код будет выглядеть приблизительно так:
Как видите, ничего сложного нет.
Дефайн «DEBUGMODE» определяем в том случае, если нам надо отладить прошивку, иначе, при включенной защите от чтения, пройтись отладчиком по прошивке не получится.
Если установлена защита от чтения, но не установлена защита от записи, то мы не можем только прочитать прошивку, а залить новую — без проблем.
Кстати некоторое наблюдение, защиту от чтения можно не только установить, но и снять через программный код. Зачем это нужно — решать Вам ))).
С чтением все понятно, для чего оно нужно — тоже, а вот зачем нужна защита от записи? Первое, наверное, что приходит в голову, это наш автозагрузчик, чтобы случайно при прошивке микроконтроллера, мы не затерли свой Bootloader.
Вот как выглядит процедура установки защиты от записи на первые 8 килобайт памяти:
Данные о защищенных страницах памяти хранятся в битах Operation Byte. Защищенными считаются те страницы, бит которых сброшен. Если бит установлен, то страница доступна для записи! Принцип работы с Option Byte такой же, как и со всей Flash — перед установкой значения, его необходимо очистить, при этом все биты устанавливаются в 1.
Хочу обратить Ваше внимание на проверку условия «if(ProtectedPages != 0x00)» — условие верное, мы сравниваем ProtectedPages, а не WRPR_Value. В ProtectedPages установлены биты только тех блоков памяти, которые нам нужны и они не защищены от записи. А в WRPR_Value установлены биты защищенных блоков памяти.
Также, необходимо проверить, вдруг уже установлено то, что нам нужно и тогда не будем «мучать» Flash и перезаписывать данные.
Кстати, не пытайтесь менять значение Option Byte «на лету». Чтение этого блока происходит при перезагрузке микроконтроллера и сохраняется в регистрах FLASH_OBR и FLASH_WRPR, поэтому изменять значение в процессе выполнения кода без перезагрузки МК бессмысленно — сохранено будет последнее установленное Вами значение.
Модифицированная функция установки/снятия защиты от записи Flash.
По просьбе Serg, привожу пример функции установки или сброса защиты от записи. Почему-то разработчики от STM постеснялись добавить эту функцию в библиотеку для линейки МК STM32F1xx, хотя для других линеек МК такой (похожий) функционал присутствует. Основное ее отличие от родной функции в том, что перед записью в регистр Operation Byte необходимо производить его очистку, соответственно при использовании родной функции происходит сброс всех настроек защиты, а в моей функции реализовано изменение отдельных бит, что позволяет более гибко управлять защитой, хотя и пагубно влияет на МК, так как количество циклов перезаписи для Flash гарантировано в пределах 100000 циклов. (Даже на тестовых МК я еще ни разу не столкнулся с тем, что у меня количество циклов перезаписи перевалило за сотню тысяч )))).
Само тело функции желательно разместить в модуле «stm32f10x_flash.c» после функции «FLASH_EnableWriteProtection()»:
И необходимо добавить определение функции в модуле «stm32f10x_flash.h» также после функции «FLASH_EnableWriteProtection()», иначе Вы не сможете вызвать ее из свей программы:
Сама функция является переработанной функцией от STM «FLASH_EnableWriteProtection()»:
- добавлен параметр «NewState», который отвечает за установку или снятия флага защиты;
- в теле функции добавлена проверка на значение этого параметра и, в зависимости от него, устанавливаются или сбрасываются соответствующие биты.
- сброс OperationByte производится не перед вызов функции, а внутри нее. Это связано с особенностью записи во Flash, поэтому сначала запоминаются предыдущие значения OperationByte, производится очистка OperationByte, а затем уже записываются новые значения с учетом предыдущих.
Таким образом можно заменить по своему коду вызовы «FLASH_EnableWriteProtection(ProtectedPages)» на «FLASH_SetWriteProtection(ProtectedPages, ENABLE)» для включения защиты, или «FLASH_SetWriteProtection(ProtectedPages, DISABLE)» для ее отключения. Не забудьте в этом случае убрать очистку OperationByte с помощью функции «FLASH_EraseOptionBytes()», так как она сбросит все раннее установленные флаги защиты от записи.
Использование утилиты STM32 ST-LINK Utility.
Вы можете защитить свою прошивку от чтения и записи с помощью стандартной утилиты от STM. Скачать ее можно на официальном сайте, пройдя несложную регистрацию.
Из всех возможностей утилиты, мы обратимся только к работе с областью данных микроконтроллера, известной как «Option Byte».
Попасть в этот функционал программы можно либо комбинацией кнопок «Ctrl+B», либо через меню «Target» / «Option Byte». Перед Вами откроется похожее окно:
Нас сейчас интересуют всего два раздела: чтение и запись. Здесь Вы можете включить или выключить защиту от чтения и галочками установить страницы, которые хотите защитить от записи. Действия аналогичны описанным выше, поэтому описывать подробно не буду.
Не знаю почему, но если Вы установили защиту от записи, то после снятие этой защиты через утилиту, автоматически выставляется флаг защиты от чтения. а снятие флага защиты от чтения полностью стирает Flash.
Заключение
В этой статье я постарался кратко рассказать как можно использовать Flash в своих целях, как сделать свой автозагрузчик (Bootloader) и как защитить своий код. Все примеры в статье протестированы и готовы к использованию. На этот раз вложение добавлять не буду — весь материал содержит небольшой объем кода и легко воспринимается в тексте.
AVR — Запись данных во флеш память
Встроенная EEPROM стремительно заканчивается, а данные куда-то записывать надо. Знакомая ситуация, не правда-ли?
Что мы обычно делаем в таких случаях? Ставим внешнюю EEPROM, флеш или SD карточку на 32 гига. Это оправдано, если устройство достаточно сложное. А если оно состоит из одной тиньки и двух с половиной светодиодов? Тогда подключение внешней памяти грозит кардинальными изменениями в алгоритме, а может и пинов банально не хватит.
Но ведь у нас есть своя флеш память, которая в подавляющем большинстве случаев заполнена чуть менее, чем на половину. Отлично! Её и используем для записи данных.
Механизм записи во флеш память на первый взгляд немного запутанный. Но если присмотреться — все просто. Он состоит из трех основных частей:
— Очистка страницы памяти.
— Подготовка (закидываем данные во временный буфер)
— Запись.
Причем первые два пункта можно невозбранно менять местами.
Флеш память разделена на страницы. Размер страницы зависит от общего объема флеш памяти — смотри в даташите.
Например, для тини13 есть такая табличка:
Размер страницы 32 байта (16 слов), а всего таких страниц 32 штуки.
Так-же там написано, что в регистре адреса первые 4 бита (PC[3:0]) занимает адрес слова, а адрес страницы начинается с 5го бита. Значит, если нам надо записать адрес 3й страницы, то в регистр уйдет 3<<4.
При операциях со страницами (запись и очистка) от нас будут требовать номер страницы. А при работе со временным буфером — адрес слова в странице. К счастью, МК сам выделяет нужные части из адреса, который мы кидаем в регистр Z — поэтому можно не задумываться и пихать просто адрес слова в памяти.
При прошивке МК надо включить фьюз SELFPRGEN. Без этого ничего работать не будет.
1) Расчистка места.
Для очистки страницы памяти надо выполнить такую последовательность действий:
— Пихаем в регистр Z адрес. Из него МК выделит адрес страницы, а на первые 4 бита забьет.
— Поднимаем в регистре SPMCSR биты PGERS (Page Erase) и SELFPRGEN (Self Programming Enable).
— Быстро (в течении 4х тактов после записи в SPMCSR) выполняем команду SPM.
На время очистки страницы (а это около 4 мс) процессор подвисает.
2) Готовим данные к записи.
— Записываем в регистр Z адрес слова. Теперь все наоборот. МК будет ориентироваться по первым 4 битам адреса.
— В пару регистров R1:R0 пишем данные, которые хотим запихать в буфер.
— Поднимаем бит SELFPRGEN в регистре SPMCSR.
— Выполняем команду SPM.
… повторить, пока не будет заполнен весь буфер.
У этого буфера есть одна нехорошая особенность:
Записать два раза в одну ячейку нельзя. Т.е. если мы хотим переписать уже записанные в буфер данные, то его придется сначала очистить. Для очистки надо просто поднять бит CTPB в SPMCSR.
Алсо, буфер очищается сам после записи во флеш или перезагрузки.
Забавно так-же то, что данные в буфере будут потеряны, если записать что-то в EEPROM. Как-то хитро память устроена, не находите?
Последнее особенно актуально, если мы собираемся наполнять буфер постепенно, прерываясь на другие задачи (типа записи в EEPROM).
3) Пишем!
— Запихиваем адрес страницы в Z
— Устанавливаем биты SELFPRGEN и PGWRT.
— Выполняем SPM.
При записи страницы МК зависает на те-же 4 мс.
Пример устройства.
Для примера я хотел замутить термологгер на базе тини25. С записью температуры в флеш, работой со встроенным термометром и выдачей лога в UART. Но выяснилось, что UART у тини25 нету, а «родной» термометр уж больно кривой. Поэтому будем делать что по-проще. Например, записывать во флеш память напряжение, измеренное АЦП.
Алгоритм дешевого и сердитого логгера такой:
0) Инициализация периферии. Устанавливаем адрес записи на первую пустую страницу.
1) Ждем 1 сек — простым циклом, безо всяких таймеров и прерываний. Дешево и сердито.
2) Запускаем АЦП и ждем, пока он закончит преобразование.
3) Если мы начали писать новую страницу, то
3.1) Очишаем её.
4) Записываем во временнный буфер (по текущему адресу) значение АЦП.
5) Если мы уже заполнили весь буфер, то
5.1) Записываем его во флеш память.
6) Инкрементируем адрес записи.
7) Если дошли до конца памяти — затупляем в вечном цикле. Иначе идем на шаг 1.
Код на ассемблере:
Проверять код в протеусе бесполезно — не работает
Но в железе все прекрасно крутится.
Небольшое дополнение: RWW и NRWW
В МК с поддержкой бутлоадера на команду SPM накладываются ограничения. Её можно выполнить только из BLS (Bootloader section) — области памяти в самом конце флеша, где живут бутлоадеры. Размер этой области устанавливается фьюзами BOOTSZ.
Вообще, в контроллерах с поддержкой бутлоадера, весь флеш поделен на 2 части: RWW (Read-While-Write) и NRWW (No Read-While-Write). NRWW распологается в конце памяти и занимает место отведенное под бутлоадер. Т.е. размер этой области памяти равен максимальному размеру BLS. Остальное место (с начала флеша и до NRWW) занимает RWW область.
Разница между RWW и NRWW заключается вот в чем: Если бутлоадер (который находится в NRWW) пишет или стирает страницу в RWW области, то МК не останавливается на время выполения этой операции. Бутлоадер будет продолжать работать, пока страница памяти в RWW записывается или стирается. По этому поводу даже придумали прерывание SPM_RDY, которое возникает по завершении операции.
А если бутлоадер попытается записать данные в секцию NRWW (это не обязательно должна быть секция самого бутлоадера), то МК замрет на 4мс, пока производится запись.
STM32 — память
В статье рассказано про организацию памяти микроконтроллера stm32, использования flash’а для хранения пользовательских данных, и про всякие пользовательские биты/биты защиты.
Конечно же в вашем микроконтроллере нет никаких гигабайтов памяти однако камень 32-х битный, а значит можно адресовать (обратится по адресу) до 4ГБ. Это стандартизированная модель обеспечивающая переносимость кода между различными микроконтроллерами stm32, а так же дающая возможность подключать внешнюю память и совершенно спокойно обращаться к ней. То же самое касается и периферии, производитель микроконтроллеров на базе ядра Cortex, может напихать туда оооочень много всего и при этом не беспокоиться о нехватке адресов. Что же касается размера, то некоторых адресов просто нет, вместо них зарезервированные пустоты. Если обратится к зарезервированной области, то произойдёт аппаратный сбой процессора.
Для примера часть адресов блока Peripheral микроконтроллера F103…
Reference manual — rm0008 стр. 51
Тут видны адреса некоторых таймеров, портов GPIO, и другой периферии. Все они находятся выше 0x40000000 и ниже 0x5fffffff.
Если попробовать прочитать зарезервированный адрес…
То получим Hard Fault…
Прежде чем продолжать, надо скачать программу STM32CubeProgrammer с помощью которой можно ковыряться в микроконтроллере. Прога хороша тем, что есть варианты для и
Запускаем и жмём Connect, подключатся можно через ST-Link или UART…
Нажимаем верхнюю левую кнопку (с карандашиком), и на вкладке Device memory видим программу залитую в камень. Можно указать адрес, с которого читать (по умолчанию стоит начальный адрес флеш-памяти) и сколько байт прочитать. Кнопочка Read считывает данные и показывает как на картинке. Если вместо Read выбрать Save As, то программа сдампится в указанный файл (надо только размер считываемых данных правильно указать, а то по умолчанию 1К).
Если нажать вкладку Open file, то можно открыть какой-нибудь .hex или .bin файл прямо в программе.
Кнопка открывает интерфейс для загрузки прошивки в МК…
Выбираем нужный файл, очищаем флеш полностью (Full chip erase) или выборочные страницы, и жмём Start Programming.
— что-то связанное с внешним загрузчиком или внешним девайсом, не знаю, не вникал.
— очищает флеш.
В нижней части лог подключения, а справа-снизу полезная информация о вашем МК. Здесь интересны два пункта…
Device — серия микроконтроллера и плотность (Medium-density).
Некоторые микроконтроллеры stm32, в рамках одной серии, например популярный F103, он же BluePill, выпускается не только в разных корпусах с разным количеством «ножек», но и разной плотности (объёмом флеш-памяти).
Connectivity line devices — это, на сколько я понимаю, микроконтроллеры которые умеют работать с интернетом. Programming manual — PM0075 стр. 5.
Device ID — код, который зашит в системный бутлоадер, и по нему ST-Link определяет что это за микроконтроллер.
Коды можно посмотреть в AN2606 стр. 307.
Теперь нажмём кнопочку чтоб появился список Option bytes…
… и вернёмся к изучению памяти.
Всё что выше Peripheral особого интереса не представляет, SRAM — это оперативная память, а Code Area выглядит так…
Option bytes — здесь находятся различные биты для настройки МК. Защита от чтения/записи, включение/отключение вачдогов, и несколько пользовательских битов для хранения какой-либо инфы.
Бит RDP — если установить, то нельзя будет ни прочитать прошивку, ни загрузить новую. Чтобы установить, нужно поставить галочку, нажать кнопку Apply и обресетить МК. Снимается так же. При снятии защиты, существующая в МК прошивка будет удалена. Такой механизм гарантирует невозможность прочесть прошивку в залоченом камне.
В более серьёзных камнях RDP имеет несколько уровней…
AA — нет защиты.
BB — защита включена.
СС — микроконтроллер безвозвратно заблокирован от чтения/записи. Warning! Если установите этот уровень защиты, то больше никогда не сможете прошить МК. Операция необратима.
Тут комментировать особо нечего, думаю и так всё понятно. Однако очень не рекомендую снимать галочку с вачдога — камень будет постоянно ресетится. Настройки применяются так же, галочку сняли/поставили, и кнопка Apply. В более мощных МК есть доп. настройки.
Сюда можно сохранить какие-то свои данные, правда не много
Защита от записи конкретных страниц флеш-памяти. К этому вернёмся чуть позже.
System memory — это область в которой располагается системный загрузчик (bootloader), он зашивается на заводе при изготовлении, его нельзя ни удалить, ни изменить.
Bootloader это такая небольшая программка, которая позволяет прошивать микроконтроллер через различные интерфейсы. Если подтянуть пин BOOT_0 к «земле» и нажать ресет, то загрузиться bootloader и будет ожидать поступления новой прошивки. Простенькие камни, типа F103, можно прошивать только через USART, а более «крутые» позволяют делать это и через другие интерфейсы. Посмотреть это можно в AN2606 стр. 25. Например вот…
Flash — это область энергонезависимой памяти, в которой хранится ваша прошивка. Выглядит эта область следующим образом…
RM0008 стр. 55. (Рис. 1)
Information block — это описанные выше System memory и Option Bytes.
Flash memory interface registers — это регистры отвечающие за работу Flash-памяти, они находятся в области Peripheral .
Main memory — это и есть наша Flash-память.
Flash -память организована постранично, а размер страницы колеблется от одного до нескольких килобайт, в зависимости от серии микроконтроллера. У F103 (Рис. 1) размер равен 1К, а вот например у F4 выглядит так…
Часть страниц по 16К, одна 64К, а часть по 128К. Здесь страницы называются секторами.
OTP area — это биты защиты, которые можно записать только один раз (One-Time-Programmable). Больше ничего про это сказать не могу — у меня нет такой платы.
Тут есть один любопытный момент. Компания ST выпускает два почти одинаковых камня F103, один F103C8хх c объёмом флеш-памяти 64К (BluePill), и F103CBхх c объёмом 128К. Однако на самом деле у обоих этих камней объём флеш-памяти равен 128К (128 страниц по 1К).
Во-первых в мануале нет камней с 64-мя страницами (см. рис. 1), а во-вторых, не смотря на то, что ST-Link показывает объём 64К, у всех моих F103C8хх читаются и пишутся все 128 страниц. То есть программу размером больше 64К залить через TrueStudio не удаётся, а вот производить чтение/запись этих областей из своей программы можно. Да и STM32CubeProgrammer их тоже совершенно спокойно читает…
127-я страница на камне F103C8хх.
Выше я говорил что мы вернёмся к вопросу защиты конкретных страниц от чтения/записи…
Биты WRPх защищают сразу по несколько страниц. Количество защищаемых страниц зависит от плотности (density) МК.
Вот вырезка из Programming manual стр. 21…
У low-density используется только WRP0 и защищает весь флеш, а у medium-density можно делать это выборочно. Зачем нужны остальные биты (WRP4 и т.д.) я так и не понял, может они зарезервированы.
Такой способ выборочной защиты очень удобен тем, что можно заблокировать область где находится программа, а остальное использовать для хранения каких-то данных и не боятся затереть программу, ну или наоборот, защитить какие-то данные, которые вы положили например в конец флеша.
CCM SRAM — у некоторых микроконтроллеров есть дополнительная оперативная память под названием Core Coupled Memory. Эта память подключена непосредственно к ядру, благодаря чему скорость доступа и исполнение кода будет быстрее чем в обычной SRAM. Соответственно в CCM выгодно размещать всякие статистические переменные/массивы, а при желании можно перенести туда стек/кучу/.data/.bss.
Cледует помнить, что доступ к CCM имеет только процессор, а значит DMA не сможет к ней обратиться. CCM бывают разные для разных микроконтроллеров, поэтому прежде чем начинать использование следует изучить документацию. Ещё инфа, и ещё инфа.
В качестве примера я перенёс пару массивов в ССМ (среда TrueStudio, камень F303)…
Объявляем массивы глобально:
Чтоб компилятор не «оптимизировал» эти массивы, сделайте что-нибудь с ними, например выведите на печать.
Вуаля, они лежат где надо…
Прежде чем говорить о переносе стека/кучи/.data/.bss в ССМ, я очень кратко и обобщённо поясню что означают эти термины:
Стек — область памяти в ОЗУ, куда сохраняется адреса программы в момент возникновения прерывания или перехода в другую функцию.
Пошагово выглядит это так:
Наша программа работает-работает (указатель «бежит» последовательно по адресам).
Происходит прерывание — указатель перепрыгивает в обработчик этого прерывания.
Адрес, где находилась программа в момент возникновения прерывания, записывается в стек (чтоб запомнить куда возвращаться).
После того как обработка прерывания закончится, из стека вытаскивается адрес на который нужно вернуться.
То же самое происходит при переходе из одной функции в другую, например из в
Технически, стек можно представить в виде стопки книг где нельзя взять вторую сверху книгу не сняв предварительно первую. То есть стек работает по принципу LIFO буфера (Last In First Out) — «последним пришёл, первым вышел». Такая организация очень хорошо себя оправдывает. Представьте себе такую ситуацию:
Указатель перепрыгнул из функции main() в bla_bla() .
В стек записался адрес того места откуда выпрыгнул указатель.
Идёт обработка функции bla_bla() и в этот момент происходит прерывание.
В стек записывается (ложится поверх предыдущего) ещё один адрес, и указатель переходит в обработчик прерывания.
В результате на стеке лежат два адреса — адрес, на котором была приостановлена функция main() , а поверх него адрес на котором приостановлена функция bla_bla() .
По окончании обработки прерывания система лезет в стек, «снимает со стопки» самый последний адрес, переходит по нему и заканчивает выполнение функции bla_bla() с того места где она была прервана. Больше этот адрес не нужен и он выкидывается в помойку, на стеке остаётся адрес приостановки функции main() .
Как только функция bla_bla() завершается, система опять лезет в стек и «снимает» адрес приостановки функции main() . Адрес опять же летит в помойку, а функция main() продолжает выполняться. Таким образом, снимая «книжку за книжкой» система не сможет вернуться куда-то не туда и нарушить целостность программы.
Помимо адресов, в стек сохраняются локальные переменные функций и параметры передающиеся в функции. То есть в момент вызова функции, вместе с адресом для возврата, на стек кладутся ещё и локальные переменные, которые есть в этой функции. Как только функция отработает, эти переменные будут сняты со стека и уничтожены.
Куча — эта область ОЗУ, которая используется для динамического выделения памяти в процессе работы программы. То есть, когда вы делаете malloc/calloc/realloc, то память выделяется на куче.
bss — область ОЗУ, сюда помещаются не инициализированные глобальные переменные (uint8_t var;).
data — область ОЗУ, сюда помещаются инициализированные глобальные переменные (uint8_t var = 0;).
text — Flash, здесь лежит сама программа.
Выглядит всё это хозяйство следующим образом…
Стек начинается от самого большого адреса и при увеличении размера ползёт вниз (записывая данные в меньшие адреса), а куча наоборот, при увеличении размера ползёт вверх, поэтому при выделении памяти на куче нужно следить чтоб эти области не налезли друг на друга.
Чтоб лучше понять иллюстрацию выше, откройте файл STM32F103C8_FLASH.ld и найдите там такие строки…
Нас интересует переменная _estack , которая указывает на конец оперативной памяти. У микроконтроллера stm32F103c8 оперативная память располагаться в области от 0x20000000 до 0x20005000 , что равно 20480 байтам (20Кб). Визуально это выглядит так…
Вся наша оперативка находится внутри узенькой полоски, а всё что выше не существует. То есть стек, куча, bss и data находится внутри этой полоски, а text в жёлтой области.
Посмотреть сколько места занимают некоторые данные можно в среде разработки. Вот картинка из TrueStudio…
dec и hex это общий размер первых трёх значений.
Чтоб перенести какую-то из этих областей в ССМ, нужно подкорректировать файл компоновщика (или линковщика, называйте как вам удобнее) с расширением .ld …
Находим там интересующие нас блоки…
Меняем у каких-нибудь блоков, или у всех сразу, слово RAM на CCMRAM …
В результате получаем — до:
Запись данных во флеш
У большинства микроконтроллеров stm32 (кроме серии L0) нету EEPROM, поэтому сохранять пользовательские данные приходится во флеш-памяти. Количество циклов записи 10000.
Чтобы записать во флеш какие-то данные нужно вначале её очистить (во время очистки все биты заполняются единицами — 0xFF). Очистка происходит постранично/посекторно.
Очистка одной страницы на F103…
Объявляем структуру в которую заносятся параметры очистки:
TypeErase — что хотим очистить, какую-то конкретную страницу (или несколько), или всю флеш полностью.
PageAddress — начальный адрес страницы, которую хотим очистить. Адреса можно посмотреть в мануале (правда там не все видны), либо посчитать самостоятельно, либо в примерах Куба — STM32CubeFx в файле main.h (для F103 — /STM32Cube_FW_F1_V1.8.0/Projects/STM32F103RB-Nucleo/Examples/FLASH/FLASH_EraseProgram/Inc/main.h). В конце есть ссылка на гитхаб с этим примером, там есть хедер (addr_pages.h) с задефайнеными адресами для BluePill.
NbPages — кол-во страниц для очистки. Если указать несколько, то они будут очищены начиная с адреса указанного выше.
Banks — у «жирных» камней память делиться на банки, поэтому нужно указать в каком именно банке находятся страницы.
HAL_FLASH_Unlock() — снимаем блокировку стирания/записи во флеш (для чтения этого делать не нужно). Это не относится к битам WRPx, если они установлены для текущей страницы, то стереть/записать страницу не получится.
Ну, а дальше стираем страницу/страницы, и возвращаем блокировку. Если что-то пойдёт не так, то программа выведет ошибку и зациклится.
После этой операции вся страница готова для записи. Необязательно записывать всю страницу за один раз, можно добавлять записи по мере необходимости, при условии что новые данные не будут записываться поверх старых.
Почему нужно очищать (заполнять значениями 0xFF) память?
Дело в том, что когда происходит запись байта в ячейку, то биты в этой ячейки не просто так берут и перезаписываются, а совершается операция логического «И» над тем что есть в ячейке и новым значением.
Наглядно это выглядит так. Ячейка у нас очищена (биты заполнены единицами) и мы записываем в неё число 7 (0х07)…
Выполняется логическое «И», и в ячейку записывается нужное нам число 7.
А теперь допустим что мы хотим записать в эту же ячейку число 13 (0x0d) поверх старого значения…
Выполняется логическое «И» между старым значением 0х07 и новым 0x0d. В результате вместо желаемого числа 13, в ячейку записывается число 5.
Запись
У разных МК можно записывать разную длину «слова», например у F103 можно записать «слова» размером 16, 32 и 64 бита. Подсмотреть это можно в файле stm32f1xx_hal_flash.h …
Запишем два 16-ти (FLASH_TYPEPROGRAM_HALFWORD) битных числа в начало только что очищенной страницы…
Разблокируем флеш, указываем адрес начала страницы (не обязательно начало, можно в любое место), записываем во флеш массив из двух чисел, а в цикле увеличиваем адрес на два. После этого блокируем память.
Если записывать 32-х (FLASH_TYPEPROGRAM_WORD) битное число, то увеличим адрес на четыре, а если 64-х (FLASH_TYPEPROGRAM_DOUBLEWORD) битное, то на 8. Если в дальнейшем захотите добавить в эту страницу ещё что-то, то нужно запомнить адрес.
Идём в STM32CubeProgrammer, вписываем адрес 127-ой страницы и смотрим чего понаписали…
Всё окей. Не забывайте нажимать Disconnect
Прочитаем то, что записали…
Разблокировать не надо, а адрес опять же увеличиваем на два. Получаем ожидаемое…
Чтоб записать 8-ми битное значение (не смотря на то, что производитель не предоставил такой возможности), надо просто записывать по два байта в одно 16-ти битное «слово» со сдвигом. Всё происходит почти так же как и с 16-ти битным числом…
Увеличиваем адрес на 4 (чтоб добавить данные вслед за предыдущими), записывать будем массив, размер которого должен быть кратен двум (для этого сделана проверка), а в цикле запихиваем два символа в одно 16-ти битное «слово» и отправляем по адресу.
Читать можно по одному 8-ми битному символу.
Вот и всё, остаётся придумать как хранить адрес (со смещением) последней записи для добавления последующих, чтобы не мучить одну и туже ячейку, и не прикончить её в самом скором времени. Вот вариант как смещать очередную запись.
Всем спасибо
Как записать переменную во флеш память 80c51f120
Проект по сборке компактного клона ZX-80, в котором мы не только разберем сам процесс сборки, включая подготовку платы и схематику подключения на ней компонентов, но также познакомимся с используемым в ATX-80 интерпретатором Basic и научимся работать с памятью этого компьютера на примере загрузки 32 образцов программ.
ATX-80 – это наследник ZX-80, собранный на базе ATmega8. При этом он не эмулирует код, а заменяет оригинальный компьютер собственным кодом с аналогичной функциональностью на ассемблере AVR. Видео с него можно выводить как на VGA-монитор, так и на телевизор PAL/NTSC. Программы Basic можно хранить на внутренней/внешней EEPROM, внутренней флэш-памяти (32 слота), а также передавать между ПК и его хранилищем.
Получившийся клон отличается простотой сборки и компактностью. Его можно использовать как небольшой дешевый компьютер для обучения программированию на Basic. В общей сложности в него загружено 32 программы.
Общее описание проекта
Изначальной целью проекта было создание компьютера, сопоставимого по функционалу и техническим возможностям с оригинальным ZX-80. Это означало наличие полноценного интерпретатора Basic со всего 1Кб RAM и максимальное приближение к 4Кб ROM.
Еще одной задачей было изучить оригинальную ROM BIOS ZX-80, которая без преувеличения достойна похвалы. Помимо того, что она была инновационной, эта система создавалась с высокой оптимизацией размера кода, и несмотря на необходимость высокой ясности в потоке данных между функциями автор описал весь код с минимумом ошибок.
Я постарался сохранить функциональность исходного кода максимально точно и сопроводил его подробными комментариями. Невзирая на свой почтенный возраст, BIOS ZX-80 представляет очень полезный и информативный материал для изучения.
Для написания кода я выбрал процессор ATmega8, снаряженный 1Кб RAM (аналогично ZX-80) и 8Кб Flash. И хоть в оригинале объем флэш-памяти был вдвое меньше, переписанный код у меня получился где-то на 40% длиннее, поскольку инструкции AVR имеют размер 16 бит, плюс я добавил дополнительные функции, такие как более сложный генератор видео и обработку памяти, попутно задействовав 1Кб под хранение программ. В общем, 8Кб были распределены рационально.
ATX-80 оснащен клавиатурой с 40 микропереключателями. Он способен генерировать видео для VGA-монитора, а также выдавать сигнал в стандартах PAL и NTSC. Питание осуществляется от внешнего источника 5В через USB (USB-зарядки или ПК). Внешняя память EEPROM объемом 32Кб служит для хранения максимум 32 программ Basic в 32 слотах. Помимо этого, один внутренний слот флэш-памяти можно задействовать для копирования программ между внешними модулями EEPROM.
Редактируемая программа также автоматически отображается во внутреннюю EEPROM, размер которой составляет всего 512 байт, в связи с чем некоторые программы целиком в нее не входят, хотя большая часть все же имеет меньший размер (оперативная память разделяется между переменными и видеопамятью). В случае же с более объемными программами сохраняется, по крайней мере, их большая часть.
Преимущества ATX-80 перед оригинальным ZX-80:
- VGA-выход;
- изображение генерируется при выполнении программы (можно использовать режимы FAST и SLOW );
- редактируемая программа сохраняется даже после выключения;
- сохранение программ на внешнюю EEPROM;
- клавиши автоповтора;
- функция получения информации о памяти;
- повышенная скорость работы программ;
- меньший размер.
- невозможность выполнения программ в машинном коде (функция USR не работает);
- невозможность расширения хранилища внешней памятью;
- невозможность подключения дополнительных устройств через внешнюю шину;
- для хранения программ нельзя использовать кассетную пленку, доступна только EEPROM;
- сгенерированные символы содержат дополнительную 9-ю белую полоску (следствие генерации изображения через порт SPI)
Ошибки ZX-80 BIOS
В процессе изучения ZX-80 BIOS я обнаружил несколько мелких багов:
1) Ошибка команды REM . Адрес подпрограммы REM должен быть L0849 , а не L084A . В результате этой ошибки выполнение REM без параметров (без текста) не приведет к выполнению следующей строки.
2) Ошибка в подпрограмме ED-EDIT – приведенные ниже инструкции должны идти в обратном порядке. Данная ошибка может привести к сбою системы, если выполнить команду при заполненной памяти:
3) еще одна ошибка, которую заметить сложнее, представляет риск при изменении компиляции. В начале подпрограммы L03CB ED-EDIT отсутствует инициализация регистра B на значение, представляющее количество строк для вывода (требуется значение 2 или больше). В этот момент регистр B содержит значение 3 (полученное из адреса L03CB ), что вполне удовлетворительно, но переклмпиляция из другого адреса может привести к прекращению вывода программы. Хотя я допускаю, что автор намеренно опустил здесь эту инструкцию в качестве элемента оптимизации.
Электрическая схема
В качестве основного процессора использован ATmega8. При желании его можно заменить на ATmega88, ATmega168 или ATmega328. В этом случае потребуется использовать другую прошивку, которая также прилагается в пакете для скачивания. Однако больший объем памяти старших процессоров в этом случае не задействуется.
Управляется ЦПУ тактовым генератором с частотой 24МГц. Такая частота необходима для отображения требуемого разрешения на VGA-мониторе. Несмотря на то, что у данного кристалла она выше пределов, заявленных разработчиком, на практике выяснилось, что с подобным разгоном справляются практически все процессоры.
Тем не менее рекомендуется использовать процессорный сокет, чтобы при возникновении сложностей можно было легко установить другую модель. Причиной неполадок также может выступить и недостаточно высокое напряжение. Частота процессора зависит от подаваемого питания, и низкое напряжение может вызвать его некорректную работу с используемым тактовым генератором.
Клавиатура оформлена в матрицу 5х8 клавиш по аналогичной ZX-80 схеме, только с обратным током. При ее опросе сигналы COL1 и COL5 поступают в качестве входа (то есть состояния высокого сопротивления), и всего один из сигналов COL установлен как выход с уровнем LOW .
Сигналы от ROW1 до ROW8 подключены в качестве входа с помощью внутреннего подтягивающего резистора. Когда происходит опрос, один из сигналов COL активируется на уровень LOW , и считывание сигналов ROW определяет нажатую клавишу.
Выход MOSI от генератора SPI направляется в качестве видеосигнала на VGA-коннектор и на Cinch-разъем, служащий для вывода изображения на ТВ. Предполагается, что этот выход будет нагружаться входным резистором 75Ом, который обеспечит необходимое напряжение в 0.7В. За вывод сигнала SYNC отвечает Timer1 , обеспечивающий горизонтальную синхронизацию. Timer1 одновременно активирует прерывание для генератора видео, который отправляет видеоданные через контроллер SPI.
Сигнал VSYNC используется только в режиме старого VGA-дисплея. В других случаях на выходе SYNC обычно генерируется комбинированный синхросигнал CSYNC . Вертикальный сигнал создается в этой комбинации путем отрицания горизонтального синхросигнала.
Большинство современных VGA-мониторов поддерживают комбинированную синхронизацию и отлично работают в этом режиме. Только старые модели требуют дополнительного использования сигнала VSYNC . При этом на большинстве мониторов никакой разницы между режимами старых или новых моделей, как правило, не заметно.
Внешняя память EEPROM подключена посредством интерфейса TWI/I2C ( SDA и SCL ). В качестве наиболее подходящего варианта рекомендуется использовать память 24LC256P 32Кб, которая может содержать до 32 программ (имеет 32 слота по 1Кб). Помимо этого, можно также использовать модули 24LC32 (4Кб, т.е. 4 слота), 24LC64 (8Кб, т.е. 8 слотов), 24LC128 (16Кб, т.е. 16 слотов) и 24LC512 (64Кб, т.е. 64 слота). Отличаются они только количеством доступных слотов.
Также рекомендуется использовать для EEPROM панельку, чтобы при необходимости можно было менять модули. Содержащиеся в памяти программы можно копировать между разными модулями как через внутренний слот флэш-памяти, так и с помощью прилагающейся утилиты.
В ZX-80 у модулей EEPROM есть входной контакт защиты от записи. В ATX-80 такой особенности нет, но для активации/отключения возможности записи можно добавить на плату перемычку.
Сборка
ATX-80 я спроектировал под одностороннюю плату 100х75мм. Выбор пал на нее отчасти из-за ограничений бесплатной версии редактора Eagle, а отчасти из-за широкой доступности в таком размере печатных плат с фототравлением.
При изготовлении подобных плат я с помощью струйного принтера печатаю на прозрачной пленке фотошаблон (при этом склеиваю два для полноценного охвата), экспонирую их под УФ в течение 10 минут с расстояния 30см, проявляю в калийной щелочи и вытравливаю в хлорном железе.
Имейте ввиду, что в случае кастомизированного производства печатных плат необходимо проверять обозначения диаметров отверстий в шаблоне (я сверлил отверстия сам, поэтому не проверял).
Нижняя часть платы
Верхняя часть платы, можно заменить перемычками из проводов
Шелкография
Компоновка элементов. Используйте панельки для процессора и памяти.
Плата в сборе
Наклейка с метками кнопок. Я напечатал ее на обычной бумаге также с помощью струйного принтера, смазал обе стороны клеем и сделал отверстия под кнопки размером 4мм
Полный комплект
Программирование и запуск
Установите FUSE-биты следующим образом:
- ATmega8: нижний 0xEF (по умолчанию 0xE1 ), верхний 0xC7 (по умолчанию 0xD9 );
- ATmega88: нижний 0xF7 (по умолчанию 0x62 ), верхний 0xD7 (по умолчанию 0xDF ), расширенный 0xFF (по умолчанию 0xF9);
- ATmega168: нижний 0xF7 (по умолчанию 0x62 ), верхний 0xD7 (по умолчанию 0xDF ), расширенный 0xFF (по умолчанию 0xF9 );
- ATmega328: нижний 0xF7 (по умолчанию 0x62), верхний 0xD7 (по умолчанию 0xD9 ), расширенный 0xFF (по умолчанию 0xFF ).
Если вы подключите VGA-монитор после программирования процессора, то должны увидеть простой белый экран с курсором “K” в нижнем левом углу. Если этого не произойдет, то, возможно, режим VGA не выбран. Для переключения режима нужно будет включить питание, зажав одну из клавиш от 1 до 4:
- 1 + включение питания: старый VGA-монитор (использует сигнал VSYNC );
- 2 + включение питания: новый VGA-монитор (использует комбинированную синхронизацию CSYNC );
- 3 + включение питания: режим NTSC TV (вывод через коннектор Cinch);
- 4 + включение питания: режим PAL TV (вывод через коннектор Cinch).
Имейте ввиду, что нельзя использовать описанный выше способ для установки видеорежима на процессоре с помощью утилиты копирования. Это приведет к незапланированной активации программирования внешней EEPROM.
Проверить работоспособность процессора можно без подключения дисплея. Для этого нужно вставить в разъем VGA светодиод между контактами 14 (плюс, длинный) и 10 (короткий минус, короткий). Если вы включаете режим видео 1 (для старого монитора), то светодиод загорится (сигнал VSYNC активен). На включение других режимов он не отреагирует. В качестве альтернативы можете вставить светодиод в Cinch-разъем (плюс в центральное отверстие, минус к экрану) – в процессе генерации изображения светодиод будет гореть.
Программирование EEPROM
Загрузка программ в EEPROM
После тестирования базовой функциональности ATX-80 я советую запрограммировать внешнюю подключенную EEPROM. В прикрепленном архиве вы найдете набор утилит для загрузки 32 образцов программ. Для этого потребуется либо перепрограммировать основной процессор, либо использовать другой в качестве «копировальщика».
В наборе утилит есть каталог ATX80_Import, где лежат прошивки (BIN-файлы) для копирующего процессора, подготовленные под модели ATmega8/88/168/328 (первое число в имени файла указывает предполагаемую модель).
Для каждого типа процессора есть 3 набора программ: от слота 1 до 12, от слота 13 до 24 и от слота 25 до слота 32. Содержимое этих программ находится в том же пакете утилит в файлах Programs_1.txt, Programs_2.txt и Programs_3.txt. Описание программ также можно найти в файле Programs.txt из пакета исходного кода ATX-80.
Программа копирования с целью экономии памяти задействует только светодиодный индикатор, но не дисплей. Вставьте светодиод в VGA-разъем между контактами 14 и 10, как описано выше.
Загрузите файл *_1.bin с первым набором программ в процессор. Индикатор будет интенсивно мигать, указывая, что процессор находится в режиме ожидания. Если сейчас нажать 4, то светодиод перейдет в режим постоянного свечения, сообщая о выполнении программирования EEPROM. Затем он снова интенсивно замигает, указывая на завершение операции.
Загрузите остальные два набора программ аналогичным образом по очереди. Для регистрации нажатия кнопки она должна быть нажата не менее 0.5 секунды – светодиод должен светится не менее 1 секунды.
Копирование программ между EEPROM
Тот же пакет утилит содержит средства для скачивания или копирования программ из внешней EEPROM. Чтобы выполнить копирование из одной EEPROM в другую, следуйте описанной ниже процедуре.
Загрузите из каталога ATX80_Copy соответствующую программу в процессор. Мигающий светодиод будет сигнализировать о режиме готовности процессора. Нажмите одну из клавиш от 1 до 3. Светодиод загорится постоянно, указывая на выполнение операции, в результате которой программы из EEPROM будут скачаны во внутреннюю флэш-память процессора. Выбранная клавиша определяет скачиваемые слоты: 1 выбирает слоты с 1 по 11, 2 выбирает слоты с 12 по 22, а 3 выбирает слоты с 23 по 32.
В теории ATmega8 не способен вместить 11 слотов – это бы означало 11Кб памяти при имеющихся у этой модели 8Кб. Если в некоторых случаях программы входить не будут, то вам потребуется использовать более вместительный процессор или скорректировать количество слотов в исходном коде программы копирования.
После загрузки программ в процессор выключите питание, замените модуль EEPROM и снова его включите. Нажмите 4 для сохранения загруженных программ из флэш-памяти в новую внешнюю EEPROM. Все находящиеся в памяти программы сохраняются в те же слоты, из которых копировались.
Скачивание программ из EEPROM
Вы также можете скачивать программы на ваш ПК с помощью утилит. Для начала, как и в прошлом случае, нужно загрузить подходящий набор программ во флэш-память процессора, используя клавиши 1-3. Далее подключите программатор ЦПУ и скачайте содержимое флэш-памяти в каталог ATX80_Copy в файлы 1.bin и 3.bin. В каталоге ATX80_Export запустите командный файл !.bat. Так вы экспортируете программы из скачанных образов флэш-памяти в текстовые файлы Programs_1.txt, Programs_2.txt и Programs_3.txt, находящиеся в базовом каталоге утилит.
Содержимое этих файлов можно отредактировать в текстовом виде. В начале кода каждой программы прописана метка Slot x , сообщающая, к какому слоту она принадлежит. Эта метка указывает начало программы для дальнейшей обработки. Все последующие строки, начинающиеся с числа, обрабатываются как строки программы. Эти строки могут сопровождаться инструкциями LET или DIM , содержащими заполнение для численных или текстовых переменных.
Если вы хотите загрузить программы в текстовом виде обратно в EEPROM, то запустите файл !.bat в каталоге ATX80_Import. Это обеспечит импорт всех текстовых форм программ обратно в образы программы копирования. Далее вам останется лишь загрузить эти программы в процессор и нажать 4, чтобы сохранить их во внешней EEPROM аналогично тому, как это описывалось в начале текущего раздела.
Описание ATX-80 BASIC
Basic на ATX-80 почти полностью идентичен Basic с ZX-80, за исключением нескольких небольших отличий.
Номер строки программы может простираться от 1 до 9999. После каждого изменения в строках программы во внутренней EEPROM автоматически создается ее резервная копия. Емкость этой EEPROM составляет всего 510 байт, поэтому целиком программы в нее вмещаются не всегда. При включении ATX-80 программа, сохраненная во внутренней памяти, восстанавливается.
Basic на ATX-80 работает с целыми числами в диапазоне от -32768 до +32767.
Типы переменных:
- целочисленная переменная с произвольно длинным именем (начинается с буквы, которая может сопровождаться буквами или цифрами);
- целочисленная переменная со всего одной буквой может использоваться в качестве параметра цикла FOR ;
- текстовая переменная – 1 буква, сопровождаемая $ . Текст может иметь любую длину;
- массив представляется одной буквой и ограничен размером в 255 элементов.
Операции
- + — … сумма и разность;
- * /… умножение и деление (только для целых чисел);
- AND… побитовое логическое произведение;
- OR… побитовая логическая сумма;
- NOT… побитовое логическое «не»;
- **… степень
- =… проверка равенства чисел или текстов;
- <… сравнение чисел и текстов с меньшим;
- >… сравнение чисел и текстов с большим.
Команды
Могут идти только в начале строки или после THEN и вызываются нажатием соответствующей кнопки:
- LIST … (A) вывод листинга программы с заданной строки. Если номер строки не указывать, выводится листинг всей программы;
- RETURN … (B) возвращение из процедуры;
- CLS . (С) очистка экрана;
- DIM … (D) объявление массива. Массив должен начинаться с индекса 0 и может содержать до 255 элементов (например, при DIM A(255) в нем могут быть задействованы индексы от 0 до 255);
- SAVE … (E) сохранение программы и переменных в слот. Эта команда сопровождается номером слота внешней EEPROM от 1 до 32. Если число не указать или указать 0, программа и переменные сохранятся во внутренней флэш-памяти процессора.
- FOR … (F) цикл, сопровождаемый однобуквенной переменной с начальным значением, после которой идет TO с конечным значением;
- GO TO … (G) переход к указанной строке;
- POKE … (H) сохранение байта в RAM, сопровождаемое адресом содержимого этого байта. ATX-80 имитирует сохранение в адресах RAM идентично ZX-80;
- INPUT … (I) пользовательский ввод, сопровождаемый целевой численной или текстовой переменной;
- RANDOMISE … (J) инициализация генератора случайных чисел от указанного числа. Если ввести 0 или опустить параметр, инициализируется согласно счетчику фреймов;
- LET … (K) установка содержимого переменной;
- FAST … (L) установка ускоренного режима. Отображается только одна верхняя строка, и процессор ускоряется в 4 раза. Использовать можно только при выполнении программы;
- SLOW … (M) установка медленного режима. Отображается весь экран. Включается автоматически при выходе из программы.
- NEXT … (N) продолжение цикла для заданной переменной;
- PRINT … (O) вывод переменных или текста. Символ ; обеспечивает продолжение без пробела. Символ , выставляется после 8-символьных позиций табуляции;
- MEMORY … (P) вывод информации о памяти в байтах: размер программы, переменных, а также свободное пространство RAM;
- NEW … (Q) удаление программы и переменных;
- RUN . ® выполнение программы с заданной строки. Если номер строки не указать, программа выполняется с начала. В то же время происходит очистка переменных.
- STOP … (S) остановка программы. Для продолжения используется CONTINUE .
- CONTINUE … (T) возобновляет выполнение программы, остановленной командой STOP .
- IF … (U) условие. THEN сопровождается инструкцией, выполняемой при валидности условия или выражения (выражение считается валидным, если не является нулевым);
- GO SUB … (V) переход к процедуре. Из процедуры инструкция RETURN возвращает к строке, следующей за GO SUB .
- LOAD … (W) загрузка программы и переменных из слота. Эта команда сопровождается значением от 1 до 32 для внешней EEPROM. Если значение не ввести или ввести 0, программа и переменные будут загружены из внутренней флэш-памяти процессора;
- CLEAR … (X) очистка переменных;
- REM … (Y) комментарий.
Функции и прочие команды
функции необходимо прописывать, так как клавиатурных сокращений для них нет:
- «… (Shift+Y) указывает начало и конец текста;
- THEN … (Shift+3) часть условия IF , указывающая команду, которая выполняется при валидности условия;
- TO … (Shift+4) часть цикла FOR , указывающая конечное значение переменной;
- ; … (Shift+Z) часть инструкции PRINT , продолжающая вывод с последней позиции;
- , … (Shift+.) часть инструкции PRINT , продолжающая вывод из позиции в таблице;
- ( ) … (Shift+I, Shift+O) скобки;
- PEEK … загружает содержимое памяти из адреса, указанного в скобках. ATX-80 имитирует адреса RAM идентично ZX-80, включая адрес шрифта в ROM. Содержимое указателей не имитируется, их необходимо вычислять повторно;
- CHR$ … преобразует символьный код в текстовый символ;
- CODE … преобразует текстовый символ (первый символ текста) в численный код;
- RND … генератор случайных чисел. Генерирует число в диапазоне от 1 до заданного значения;
- TL$ … возвращает текст без первого символа;
- USR … в ATX-80 не поддерживается;
- STR$ … преобразует число в текст;
- ABS … абсолютное значение числа.
Примеры программ
Эти программы хранятся во внешней EEPROM в слотах 1 – 32. Содержимое EEPROM можно скопировать в другую EEPROM, скачать на ПК, либо загрузить с ПК в EEPROM, как описывалось в разделе «Программирование EEPROM».
Содержимое программ вместе с подробным описанием находится в исходном коде в файле Programs.txt.
1) Вывод набора символов
2) Деление с высокой точностью
3) Получение квадратного корня
4) Бросание кубиков
5) Таймер реагирования
6) Гистограмма случайного числа
7) Прыгающая лягушка
8) Кодирование сообщений
9) Конные скачки
10) Луноход (запускать с GO TO 100)
11) Генератор лабиринта
12) Крестики-нолики
13) Dr. ATX80 — психотерапевт (запускать с GO TO 120)
14) Игра Ним
15) Блэкджек
16) Сортировка пузырьком
17) Перенумерация строк (запускать с GO TO 9987)
18) Рисование картинки (восстановление сохраненной картинки с помощью GO TO 200)
AVR — Запись данных во флеш память
Встроенная EEPROM стремительно заканчивается, а данные куда-то записывать надо. Знакомая ситуация, не правда-ли?
Что мы обычно делаем в таких случаях? Ставим внешнюю EEPROM, флеш или SD карточку на 32 гига. Это оправдано, если устройство достаточно сложное. А если оно состоит из одной тиньки и двух с половиной светодиодов? Тогда подключение внешней памяти грозит кардинальными изменениями в алгоритме, а может и пинов банально не хватит.
Но ведь у нас есть своя флеш память, которая в подавляющем большинстве случаев заполнена чуть менее, чем на половину. Отлично! Её и используем для записи данных.
Механизм записи во флеш память на первый взгляд немного запутанный. Но если присмотреться — все просто. Он состоит из трех основных частей:
— Очистка страницы памяти.
— Подготовка (закидываем данные во временный буфер)
— Запись.
Причем первые два пункта можно невозбранно менять местами.
Флеш память разделена на страницы. Размер страницы зависит от общего объема флеш памяти — смотри в даташите.
Например, для тини13 есть такая табличка:
Размер страницы 32 байта (16 слов), а всего таких страниц 32 штуки.
Так-же там написано, что в регистре адреса первые 4 бита (PC[3:0]) занимает адрес слова, а адрес страницы начинается с 5го бита. Значит, если нам надо записать адрес 3й страницы, то в регистр уйдет 3<<4.
При операциях со страницами (запись и очистка) от нас будут требовать номер страницы. А при работе со временным буфером — адрес слова в странице. К счастью, МК сам выделяет нужные части из адреса, который мы кидаем в регистр Z — поэтому можно не задумываться и пихать просто адрес слова в памяти.
При прошивке МК надо включить фьюз SELFPRGEN. Без этого ничего работать не будет.
1) Расчистка места.
Для очистки страницы памяти надо выполнить такую последовательность действий:
— Пихаем в регистр Z адрес. Из него МК выделит адрес страницы, а на первые 4 бита забьет.
— Поднимаем в регистре SPMCSR биты PGERS (Page Erase) и SELFPRGEN (Self Programming Enable).
— Быстро (в течении 4х тактов после записи в SPMCSR) выполняем команду SPM.
На время очистки страницы (а это около 4 мс) процессор подвисает.
2) Готовим данные к записи.
— Записываем в регистр Z адрес слова. Теперь все наоборот. МК будет ориентироваться по первым 4 битам адреса.
— В пару регистров R1:R0 пишем данные, которые хотим запихать в буфер.
— Поднимаем бит SELFPRGEN в регистре SPMCSR.
— Выполняем команду SPM.
… повторить, пока не будет заполнен весь буфер.
У этого буфера есть одна нехорошая особенность:
Записать два раза в одну ячейку нельзя. Т.е. если мы хотим переписать уже записанные в буфер данные, то его придется сначала очистить. Для очистки надо просто поднять бит CTPB в SPMCSR.
Алсо, буфер очищается сам после записи во флеш или перезагрузки.
Забавно так-же то, что данные в буфере будут потеряны, если записать что-то в EEPROM. Как-то хитро память устроена, не находите?
Последнее особенно актуально, если мы собираемся наполнять буфер постепенно, прерываясь на другие задачи (типа записи в EEPROM).
3) Пишем!
— Запихиваем адрес страницы в Z
— Устанавливаем биты SELFPRGEN и PGWRT.
— Выполняем SPM.
При записи страницы МК зависает на те-же 4 мс.
Пример устройства.
Для примера я хотел замутить термологгер на базе тини25. С записью температуры в флеш, работой со встроенным термометром и выдачей лога в UART. Но выяснилось, что UART у тини25 нету, а «родной» термометр уж больно кривой. Поэтому будем делать что по-проще. Например, записывать во флеш память напряжение, измеренное АЦП.
Алгоритм дешевого и сердитого логгера такой:
0) Инициализация периферии. Устанавливаем адрес записи на первую пустую страницу.
1) Ждем 1 сек — простым циклом, безо всяких таймеров и прерываний. Дешево и сердито.
2) Запускаем АЦП и ждем, пока он закончит преобразование.
3) Если мы начали писать новую страницу, то
3.1) Очишаем её.
4) Записываем во временнный буфер (по текущему адресу) значение АЦП.
5) Если мы уже заполнили весь буфер, то
5.1) Записываем его во флеш память.
6) Инкрементируем адрес записи.
7) Если дошли до конца памяти — затупляем в вечном цикле. Иначе идем на шаг 1.
Код на ассемблере:
Проверять код в протеусе бесполезно — не работает
Но в железе все прекрасно крутится.
Небольшое дополнение: RWW и NRWW
В МК с поддержкой бутлоадера на команду SPM накладываются ограничения. Её можно выполнить только из BLS (Bootloader section) — области памяти в самом конце флеша, где живут бутлоадеры. Размер этой области устанавливается фьюзами BOOTSZ.
Вообще, в контроллерах с поддержкой бутлоадера, весь флеш поделен на 2 части: RWW (Read-While-Write) и NRWW (No Read-While-Write). NRWW распологается в конце памяти и занимает место отведенное под бутлоадер. Т.е. размер этой области памяти равен максимальному размеру BLS. Остальное место (с начала флеша и до NRWW) занимает RWW область.
Разница между RWW и NRWW заключается вот в чем: Если бутлоадер (который находится в NRWW) пишет или стирает страницу в RWW области, то МК не останавливается на время выполения этой операции. Бутлоадер будет продолжать работать, пока страница памяти в RWW записывается или стирается. По этому поводу даже придумали прерывание SPM_RDY, которое возникает по завершении операции.
А если бутлоадер попытается записать данные в секцию NRWW (это не обязательно должна быть секция самого бутлоадера), то МК замрет на 4мс, пока производится запись.
Как записать переменную во флеш память 80c51f120
- />15 часов назад
- Тема:Работа сайта и сервера
- От:makc
- />15 часов назад
- Тема:Работа сайта и сервера
- От:makc
Другие известные форумы и сайты по электронике
все что посвящено электронике и общению специалистов. реклама других ресурсов.
- Магазины
- Форумы и конференции
- Производители
- Информационные ресурсы
- Поисковики
- FTP-серверы
- />Воскресенье в 05:57
- Тема:www.eetop.cn
- От:Fachleute
- />Воскресенье в 05:57
- Тема:www.eetop.cn
- От:Fachleute
В помощь начинающему
вопросы начального уровня
Модераторы раздела VAI aosp
SergM
fill
vetal
KRS
Alexandr
des00
Uladzimir
Rst7
iosifk
ViKo
Herz
l1l1l1
Tanya
Сергей Борщ
Omen_13
Vasily_
Егоров
Walrus
- ARM, 32bit
- MCS51, AVR, PIC, STM8, 8bit
- Программирование
- Схемотехника
- Интерфейсы
- />3 минуты назад
- Тема:Как программа пользователя может найти в системе…
- От:jcxz
- />3 минуты назад
- Тема:Как программа пользователя может найти в системе…
- От:jcxz
International Forum
This is a special forum for English spoken people, read it first.
- />25 февраля
- От:HardEgor
- />25 февраля
- От:HardEgor
Образование в области электроники
все что касается образования, процесса обучения, студентам, преподавателям.
Модераторы раздела des00
- />2 часа назад
- Тема:Создание профессиональной ассоциации по микроэле…
- От:girts
- />2 часа назад
- Тема:Создание профессиональной ассоциации по микроэле…
- От:girts
Обучающие видео-материалы и обмен опытом
Обсуждение вопросов создания видео-материалов
Модераторы раздела iosifk
- />17 февраля
- Тема:Dilduino
- От:k155la3
- />17 февраля
- Тема:Dilduino
- От:k155la3
Cистемный уровень проектирования
-
Последнее сообщение
Вопросы системного уровня проектирования
Применение MATLAB, Simulink, CoCentric, SPW, SystemC ESL, SoC
Модераторы раздела Rst7
- />Пятница в 09:56
- Тема:Работа с GUI в Матлаб
- От:_sda
- />Пятница в 09:56
- Тема:Работа с GUI в Матлаб
- От:_sda
Математика и Физика
Модераторы раздела Rst7
- />19 часов назад
- Тема:Определение проекций на сенсоре камеры.
- От:Cianid
- />19 часов назад
- Тема:Определение проекций на сенсоре камеры.
- От:Cianid
Операционные системы
Linux, Win, DOS, QNX, uCOS, eCOS, RTEMS и другие
Модераторы раздела Rst7
- Программирование
- Linux
- uC/OS-II
- scmRTOS
- FreeRTOS
- Android
- />42 минуты назад
- Тема:Драйвер для I2C
- От:makc
- />42 минуты назад
- Тема:Драйвер для I2C
- От:makc
Документация
оформление документации и все что с ней связано
Модераторы раздела Rst7
- />12 марта
- Тема:Как делают документацию для микроконтроллеров их…
- От:Yra
- />12 марта
- Тема:Как делают документацию для микроконтроллеров их…
- От:Yra
Системы CAD/CAM/CAE/PLM
обсуждение САПР AutoCAD, Компас, SolidWorks и др.
- />5 февраля
- Тема:Ошибка установки Solidworks
- От:baumanets
- />5 февраля
- Тема:Ошибка установки Solidworks
- От:baumanets
Разработка цифровых, аналоговых, аналого-цифровых ИС
Модераторы раздела Rst7
- />1 минута назад
- Тема:JESD204B Frame Clock и LMFC
- От:warrior-2001
- />1 минута назад
- Тема:JESD204B Frame Clock и LMFC
- От:warrior-2001
Электробезопасность и ЭМС
Обсуждение вопросов электробезопасности и целостности сигналов
Модераторы раздела Rst7
- ЭМС
- Электробезопасность
- />3 марта
- Тема:Типовые схемы защиты от микросекундных помех
- От:HardEgor
- />3 марта
- Тема:Типовые схемы защиты от микросекундных помех
- От:HardEgor
Управление проектами
Управление жизненным циклом проектов, системы контроля версий и т.п.
Модераторы раздела Rst7
- />30 октября, 2022
- Тема:Как тестировать разработанную электронику и встр…
- От:KBH
- />30 октября, 2022
- Тема:Как тестировать разработанную электронику и встр…
- От:KBH
Нейронные сети и машинное обучение (NN/ML)
Форум для обсуждения вопросов машинного обучения и нейронных сетей
Модераторы раздела Rst7
- />4 марта
- Тема:Модуль на VHDL кусочно-линейной (семь участков) …
- От:Мур
- />4 марта
- Тема:Модуль на VHDL кусочно-линейной (семь участков) …
- От:Мур
Программируемая логика ПЛИС (FPGA,CPLD, PLD)
-
Последнее сообщение
Среды разработки — обсуждаем САПРы
Quartus, MAX, Foundation, ISE, DXP, ActiveHDL и прочие.
возможности, удобства.
Модераторы раздела vetal />des00 />
- />1 час назад
- Тема:Конфигурация компа для имплементации и симуляции
- От:alexadmin
- />1 час назад
- Тема:Конфигурация компа для имплементации и симуляции
- От:alexadmin
Работаем с ПЛИС, области применения, выбор
на чем сделать? почему не работает? кто подскажет?
Модераторы раздела vetal />des00 />
- />2 часа назад
- Тема:ПЛИС Gowin Semiconductor
- От:vladec
- />2 часа назад
- Тема:ПЛИС Gowin Semiconductor
- От:vladec
Языки проектирования на ПЛИС (FPGA)
Verilog, VHDL, AHDL, SystemC, SystemVerilog и др.
Модераторы раздела aosp vetal
des00
- />Суббота в 17:34
- Тема:найти 2 максимума в потоке
- От:tegumay
- />Суббота в 17:34
- Тема:найти 2 максимума в потоке
- От:tegumay
Системы на ПЛИС — System on a Programmable Chip (SoPC)
разработка встраиваемых процессоров и периферии для ПЛИС
Модераторы раздела vetal des00
Omen_13
- />Воскресенье в 16:21
- Тема:linux 2 устройства
- От:1891ВМ12Я
- />Воскресенье в 16:21
- Тема:linux 2 устройства
- От:1891ВМ12Я
Цифровая обработка сигналов — ЦОС (DSP)
-
Последнее сообщение
Сигнальные процессоры и их программирование — DSP
Обсуждение различных сигнальных (DSP) процессоров, возможностей, совместимости и связанных с этим тем.
Модераторы раздела des00
- />9 марта
- Тема:SAU510USB Iso-Plus
- От:Alex11
- />9 марта
- Тема:SAU510USB Iso-Plus
- От:Alex11
Алгоритмы ЦОС (DSP)
Обсуждение вопросов разработки и применения (программирования) алгоритмов цифровой обработки сигналов.
Модераторы раздела des00
- />13 марта
- Тема:организация семплирования
- От:_sda
- />13 марта
- Тема:организация семплирования
- От:_sda
Микроконтроллеры (MCU)
-
Последнее сообщение
Cредства разработки для МК
FAQ, How-to, тонкости работы со средствами разработки
Модераторы раздела haker_fox
- IAR
- Keil
- GNU/OpenSource средства разработки
- />2 минуты назад
- Тема:Камень в огород ненавистников inline-ассемблера …
- От:Arlleex
- />2 минуты назад
- Тема:Камень в огород ненавистников inline-ассемблера …
- От:Arlleex
—>
Модераторы раздела haker_fox
- STM
- NXP
- Microchip (Atmel)
- TI, Allwinner, GigaDevice, Nordic, Espressif и другие
- />1 час назад
- Тема:Как правильно перепрошить загрузчик
- От:MKdemiurg
- />1 час назад
- Тема:Как правильно перепрошить загрузчик
- От:MKdemiurg
RISC-V
Микроконтроллеры на базе ядер RISC-V, RISC-X
Модераторы раздела haker_fox
- />2 марта
- Тема:Тайминги интерфейсов CH569/CH565
- От:makc
- />2 марта
- Тема:Тайминги интерфейсов CH569/CH565
- От:makc
—>
Модераторы раздела haker_fox
- />28 февраля
- Тема:DS1302 не тикает
- От:borodach
- />28 февраля
- Тема:DS1302 не тикает
- От:borodach
MSP430
Модераторы раздела VAI />haker_fox />
- />3 часа назад
- Тема:Реверс прошивки MSP430F67791A
- От:girts
- />3 часа назад
- Тема:Реверс прошивки MSP430F67791A
- От:girts
Все остальные микроконтроллеры
и все что с ними связано
Модераторы раздела haker_fox
- PIC
- MCS51
- PowerQUICC
- HC(S)08
- AVR32
- STM8
- MIPS
- />13 марта
- Тема:Чем считать прошивку upsd3212cv-24u6 от ST?
- От:tiretrak
- />13 марта
- Тема:Чем считать прошивку upsd3212cv-24u6 от ST?
- От:tiretrak
Отладочные платы
Вопросы, связанные с отладочными платами на базе МК: заказ, сборка, запуск
Модераторы раздела haker_fox
- Arduino
- Raspberry Pi
- Rainbow
- Siberia
- EVMxxxx
- />Четверг в 07:12
- Тема:China-Link, Вариант отладчика из Китая
- От:Akakiy
- />Четверг в 07:12
- Тема:China-Link, Вариант отладчика из Китая
- От:Akakiy
Печатные платы (PCB)
-
Последнее сообщение
Разрабатываем ПП в САПР — PCB development
FAQ, вопросы проектирования в ORCAD, PCAD, Protel, Allegro, Spectra, DXP, SDD, WG и др.
Модераторы раздела SergM />fill />
- Библиотеки компонентов
- Altium Designer, DXP, Protel
- P-CAD 200x howto
- Эремекс, Delta Design
- Cadence
- Примеры
- Zuken CADSTAR
- Siemens EDA — Xpedition, PADS (ex. Mentor)
- Бесплатные САПР: KiCAD, EasyEDA, EAGLE и др.
- />1 час назад
- Тема:Обсуждение Xpedition/PADS Designer
- От:cniism
- />1 час назад
- Тема:Обсуждение Xpedition/PADS Designer
- От:cniism
Работаем с трассировкой
тонкости PCB дизайна, от Spectra и далее.
Модераторы раздела fill
- />17 февраля
- Тема:Как правильно перевести скоростную дифф. пару с …
- От:DSIoffe
- />17 февраля
- Тема:Как правильно перевести скоростную дифф. пару с …
- От:DSIoffe
Изготовление ПП — PCB manufacturing
Фирмы, занимающиеся изготовлением, качество, цены, сроки
Модераторы раздела fill
- ПСБ Технолоджи
- ТеПро
- PS-Electro
- Резонит
- PCB Professional
- Абрис
- ОАО "НИЦЭВТ"
- ООО "М-Плата"
- в домашних условиях
- />21 час назад
- Тема:JLCPCB — карты UnionPay
- От:tgruzd
- />21 час назад
- Тема:JLCPCB — карты UnionPay
- От:tgruzd
Сборка РЭУ
-
Последнее сообщение
Пайка и монтаж
вопросы сборки ПП, готовых изделий, а также устранения производственных дефектов
- />14 марта
- Тема:Sn64 Ag1 Bi35 как RoHS нормально паяет?
- От:Flood
- />14 марта
- Тема:Sn64 Ag1 Bi35 как RoHS нормально паяет?
- От:Flood
Корпуса
обсуждаем какие есть копруса, где делать и прочее
- />28 февраля
- Тема:Моделирование силы продавливания платы
- От:destroit
- />28 февраля
- Тема:Моделирование силы продавливания платы
- От:destroit
Вопросы надежности и испытаний
расчеты, методики, подбор компонентов
- />Воскресенье в 16:55
- Тема:Поверка контрольно-измерительного оборудования
- От:HardEgor
- />Воскресенье в 16:55
- Тема:Поверка контрольно-измерительного оборудования
- От:HardEgor
Аналоговая и цифровая техника, прикладная электроника
-
Последнее сообщение
Вопросы аналоговой техники
разработка аналоговых схем, моделирование схем в SPICE, расчёты и анализ, выбор элементной базы
Модераторы раздела Alexandr ViKo
Tanya
Егоров
- />3 часа назад
- Тема:Ультразвук
- От:Plain
- />3 часа назад
- Тема:Ультразвук
- От:Plain
Цифровые схемы, высокоскоростные ЦС
High Speed Digital Design
- />21 февраля
- Тема:Влияние положительных и отрицательных выбросов (…
- От:Plain
- />21 февраля
- Тема:Влияние положительных и отрицательных выбросов (…
- От:Plain
RF & Microwave Design
wireless технологии и не только
Модераторы раздела l1l1l1
- />6 минут назад
- Тема:Вопросы по CST
- От:Andrey188
- />6 минут назад
- Тема:Вопросы по CST
- От:Andrey188
Метрология, датчики, измерительная техника
Все что связано с измерениями: измерительные приборы (осциллографы, анализаторы спектра и пр.), датчики, обработка результатов измерений, калибровка, технологии измерений и др.
Модераторы раздела ViKo />Tanya />
- />Пятница в 05:03
- Тема:Назначение шунтирующих резисторов в тензометриче…
- От:SVNKz
- />Пятница в 05:03
- Тема:Назначение шунтирующих резисторов в тензометриче…
- От:SVNKz
АВТО электроника
особенности электроники любых транспортных средств: автомашин и мотоциклов, поездов, судов и самолетов, космических кораблей и летающих тарелок.
Модераторы раздела Vasily_
- />4 марта
- Тема:Провод для автомобильного компрессора
- От:byRAM
- />4 марта
- Тема:Провод для автомобильного компрессора
- От:byRAM
Умный дом
- />Четверг в 06:01
- Тема:Как контролировать большое количество реле с пом…
- От:yamantau
- />Четверг в 06:01
- Тема:Как контролировать большое количество реле с пом…
- От:yamantau
3D печать
3D принтеры, наборы, аксессуары, ПО
- />21 декабря, 2022
- Тема:slicer для 3d принтера
- От:Variant99
- />21 декабря, 2022
- Тема:slicer для 3d принтера
- От:Variant99
Робототехника
Модели, классификация, решения, научные исследования, варианты применения
- />31 марта, 2022
- Тема:Подключение дисплея 3.2inch 320×240 Touch LCD (А…
- От:Aaronli
- />31 марта, 2022
- Тема:Подключение дисплея 3.2inch 320×240 Touch LCD (А…
- От:Aaronli
Ремонт и отладка
обсуждение вопросов ремонта и отладки различных устройств и готовых изделий
Модераторы раздела Herz
- />5 марта
- Тема:Ремонт оссцилографа Hantek dso5102p — проблема с…
- От:Zversky
- />5 марта
- Тема:Ремонт оссцилографа Hantek dso5102p — проблема с…
- От:Zversky
Силовая электроника — Power Electronics
-
Последнее сообщение
Силовая Преобразовательная Техника
Источники питания электронной аппаратуры, импульсные и линейные регуляторы. Топологии AC-DC, DC-DC преобразователей (Forward, Flyback, Buck, Boost, Push-Pull, SEPIC, Cuk, Full-Bridge, Half-Bridge). Драйвера ключевых элементов, динамика, алгоритмы управления, защита. Синхронное выпрямление, коррекция коэффициента мощности (PFC)
Модераторы раздела Herz />Егоров />
- />Четверг в 11:03
- Тема:CMTI, устойчивость к транзиентам
- От:_gari
- />Четверг в 11:03
- Тема:CMTI, устойчивость к транзиентам
- От:_gari
Обратная Связь, Стабилизация, Регулирование, Компенсация
Организация обратных связей в цепях регулирования, выбор топологии, обеспечение стабильности, схемотехника, расчёт
Модераторы раздела Herz />Егоров />
- />17 сентября, 2022
- Тема:Ограничение скорости нарастания сигнала ШИМ на д…
- От:Alex-lab
- />17 сентября, 2022
- Тема:Ограничение скорости нарастания сигнала ШИМ на д…
- От:Alex-lab
Первичные и Вторичные Химические Источники Питания
Li-ion, Li-pol, литиевые, Ni-MH, Ni-Cd, свинцово-кислотные аккумуляторы. Солевые, щелочные (алкалиновые), литиевые первичные элементы. Применение, зарядные устройства, методы и алгоритмы заряда, условия эксплуатации. Системы бесперебойного и резервного питания
Модераторы раздела Herz />Егоров />
- />18 января
- Тема:Температура Li-ion аккумуляторов
- От:maksimdag0
- />18 января
- Тема:Температура Li-ion аккумуляторов
- От:maksimdag0
Высоковольтные Устройства — High-Voltage
Высоковольтные выпрямители, умножители напряжения, делители напряжения, высоковольтная развязка, изоляция, электрическая прочность. Высоковольтная наносекундная импульсная техника
Модераторы раздела Herz
- />7 февраля
- Тема:Маломощный трансформатор 220В -> 6000
- От:sanya221
- />7 февраля
- Тема:Маломощный трансформатор 220В -> 6000
- От:sanya221
Электрические машины, Электропривод и Управление
Электропривод постоянного тока, асинхронный электропривод, шаговый электропривод, сервопривод. Синхронные, асинхронные, вентильные электродвигатели, генераторы
Модераторы раздела Herz
- />4 часа назад
- Тема:Самодельный драйвер биполярного шагового двигате…
- От:Plain
- />4 часа назад
- Тема:Самодельный драйвер биполярного шагового двигате…
- От:Plain
Индукционный Нагрев — Induction Heating
Технологии, теория и практика индукционного нагрева
Модераторы раздела Herz
- />10 января
- Тема:Тиристорный инвертор
- От:Слесарь
- />10 января
- Тема:Тиристорный инвертор
- От:Слесарь
Системы Охлаждения, Тепловой Расчет – Cooling Systems
Охлаждение компонентов, систем, корпусов, расчёт параметров охладителей
Модераторы раздела Herz
- />23 февраля
- Тема:Тепловой расчет для КТ827А
- От:Valery-m
- />23 февраля
- Тема:Тепловой расчет для КТ827А
- От:Valery-m
Моделирование и Анализ Силовых Устройств – Power Supply Simulation
Моделирование силовых устройств в популярных САПР, самостоятельных симуляторах и специализированных программах. Анализ устойчивости источников питания, непрерывные модели устройств, модели компонентов
Модераторы раздела Herz />Егоров />
- />8 февраля
- Тема:Micro-Cap
- От:Lnd
- />8 февраля
- Тема:Micro-Cap
- От:Lnd
Компоненты Силовой Электроники — Parts for Power Supply Design
Силовые полупроводниковые приборы (MOSFET, BJT, IGBT, SCR, GTO, диоды). Силовые трансформаторы, дроссели, фильтры (проектирование, экранирование, изготовление), конденсаторы, разъемы, электромеханические изделия, датчики, микросхемы для ИП. Электротехнические и изоляционные материалы.
Модераторы раздела Herz />Егоров />
- />14 марта
- Тема:есть ли Российские драйверы двигателей?
- От:Xenia
- />14 марта
- Тема:есть ли Российские драйверы двигателей?
- От:Xenia
Интерфейсы
-
Последнее сообщение
Форумы по интерфейсам
все интерфейсы здесь
- ISDN/G.703/E1
- ISA/PCI/PCI-X/PCI Express
- Wireless/Optic
- RS232/LPT/USB/PCMCIA/FireWire
- Fast Ethernet/Gigabit Ethernet/FibreChannel
- Интерфейсы для "интеллектуального дома"
- от ТТЛ до LVDS здесь
- IDE/ATA/SATA/SAS/SCSI/CF
- Аудио/Видео интерфейсы
- Сотовая связь и ее приложения
- FAQ по XPort/WiPort
- Controller Area Network (CAN)
- />11 часов назад
- Тема:Почему FT232H хочет — работает, а хочет — нет?
- От:mmarc__
- />11 часов назад
- Тема:Почему FT232H хочет — работает, а хочет — нет?
- От:mmarc__
Поставщики компонентов для электроники
-
Последнее сообщение
Поставщики всего остального
от транзисторов до проводов
- />27 февраля
- Тема:Shenzhen BLS electronics
- От:Миша Чжу
- />27 февраля
- Тема:Shenzhen BLS electronics
- От:Миша Чжу
Компоненты
Закачка тех. документации, обмен опытом, прочие вопросы.
- Тех. документация
- Микросхемы
- Транзисторы
- Диоды
- Резисторы
- Средства индикации
- />39 минут назад
- Тема:Отечественный ОУ +5В
- От:byRAM
- />39 минут назад
- Тема:Отечественный ОУ +5В
- От:byRAM
Майнеры криптовалют и их разработка, BitCoin, LightCoin, Dash, Zcash, Эфир
-
Последнее сообщение
Обсуждение Майнеров, их поставки и производства
наблюдается очень большой спрос на данные устройства.
- />16 июля, 2021
- Тема:Материнские платы для майнинга
- От:Doka
- />16 июля, 2021
- Тема:Материнские платы для майнинга
- От:Doka
Дополнительные разделы — Additional sections
-
Последнее сообщение
Встречи и поздравления
Предложения встретиться, поздравления участников форума и обсуждение мест и поводов для встреч.
Модераторы раздела VAI aosp
SergM
vetal
KRS
Alexandr
des00
Uladzimir
Rst7
iosifk
ViKo
Herz
l1l1l1
Tanya
Сергей Борщ
Omen_13
Vasily_
Егоров
Walrus
- />5 марта
- Тема:TIMTOS 2023: кто-то собирается?
- От:Ruslan1
- />5 марта
- Тема:TIMTOS 2023: кто-то собирается?
- От:Ruslan1
Ищу работу
ищу работу, выполню заказ, нужны клиенты — все это сюда
Модераторы раздела VAI aosp
SergM
vetal
KRS
Alexandr
des00
Uladzimir
Rst7
iosifk
ViKo
Herz
l1l1l1
Tanya
Сергей Борщ
Omen_13
Vasily_
Егоров
Walrus
- />13 марта
- Тема:Разработка ПО — STM32, ПК
- От:Sasha7b9
- />13 марта
- Тема:Разработка ПО — STM32, ПК
- От:Sasha7b9
Предлагаю работу
нужен постоянный работник, разовое предложение, совместные проекты, кто возьмется за работу, нужно сделать.
Модераторы раздела VAI aosp
SergM
vetal
KRS
Alexandr
des00
Uladzimir
Rst7
iosifk
ViKo
Herz
l1l1l1
Tanya
Сергей Борщ
Omen_13
Vasily_
Егоров
Walrus
- />57 минут назад
- Тема:Требуются услуги схемотехника
- От:iBredihin
- />57 минут назад
- Тема:Требуются услуги схемотехника
- От:iBredihin
Куплю
микросхему; устройство; то, что предложишь ты
Модераторы раздела VAI aosp
SergM
vetal
KRS
Alexandr
des00
Uladzimir
Rst7
iosifk
ViKo
Herz
l1l1l1
Tanya
Сергей Борщ
Omen_13
Vasily_
Егоров
Walrus
- />15 марта
- Тема:stm32l011k4t6
- От:Coco
- />15 марта
- Тема:stm32l011k4t6
- От:Coco
Продам
есть что продать за деньги, пиво, даром ?
Реклама товаров и сайтов также здесь.
Модераторы раздела VAI aosp
SergM
vetal
KRS
Alexandr
des00
Uladzimir
Rst7
iosifk
ViKo
Herz
l1l1l1
Tanya
Сергей Борщ
Omen_13
Vasily_
Егоров
Walrus
- />3 часа назад
- Тема:Микроконтроллер 1986ве92у
- От:Seldom
- />3 часа назад
- Тема:Микроконтроллер 1986ве92у
- От:Seldom
Объявления пользователей
Тренинги, семинары, анонсы и прочие события
Модераторы раздела VAI aosp
SergM
vetal
KRS
Alexandr
des00
Uladzimir
Rst7
iosifk
ViKo
Herz
l1l1l1
Tanya
Сергей Борщ
Omen_13
Vasily_
Егоров
Walrus
- />19 часов назад
- Тема:Диодный лазер для обнаружения газа от Brandnew
- От:МакроГрупп
- />19 часов назад
- Тема:Диодный лазер для обнаружения газа от Brandnew
- От:МакроГрупп
Общение заказчиков и потребителей электронных разработок
Обсуждение проектов, исполнителей и конкурсов
Модераторы раздела VAI aosp
SergM
vetal
KRS
Alexandr
des00
Uladzimir
Rst7
haker_fox
iosifk
ViKo
Herz
l1l1l1
Tanya
Сергей Борщ
Omen_13
Vasily_
Егоров
Walrus
- />18 декабря, 2022
- Тема:Как купить если ты не юрлицо?
- От:Dejmos
- />18 декабря, 2022
- Тема:Как купить если ты не юрлицо?
- От:Dejmos
Русские Блоги
SPI — чтение и запись серийной FLASH (несущественные элементы по времени)
SPI — чтение и запись серийной FLASH
Полный набор видеоуроков из 200 эпизодов и учебников в формате PDF на 1000 страниц можно загрузить с форума Binghuo: www.firebbs.cn
Видеоурок Wildfire Youku смотреть URL: http://i.youku.com/firege
Справочные материалы для этой главы: «Китайское справочное руководство по STM32F4xx», «Спецификация STM32F4xx», справочный документ библиотеки «stm32f4xx_dsp_stdperiph_lib_um.chm» и «Введение в протокол шины SPI».
Если вы не понимаете протокол связи SPI, вы можете сначала ознакомиться с содержанием документа «Введение в протокол шины SPI».
Для получения информации о флэш-памяти обратитесь к разделу «Введение в часто используемую память». Чтобы понять конкретные параметры микросхемы FLASH в эксперименте, ознакомьтесь с ее спецификацией «W25Q128».
24.1 Введение в протокол SPI
Протокол SPI представляет собой протокол связи (последовательный периферийный интерфейс), предложенный компанией Motorola, то есть интерфейс последовательного периферийного устройства, который представляет собой высокоскоростную полнодуплексную коммуникационную шину. Он широко используется между АЦП, ЖК-дисплеем и другими устройствами и MCU, где требуется высокая скорость связи.
Изучая эту главу, вы можете сравнить ее с главой I2C, чтобы понять разницу между двумя коммуникационными шинами и разницу между памятью EEPROM и флэш-памятью. Ниже мы объясним физический уровень и уровень протокола SPI соответственно.
24.1.1 Физический уровень SPI
Общий способ соединения между устройствами связи SPI показан на рисунке 241.
Рисунок 241 Общая система связи SPI
Для связи SPI используются 3 шины и линии выбора микросхемы. Этими тремя шинами являются SCK, MOSI, MISO и линия выбора микросхемы. Их функции представлены следующим образом:
(1) (Slave Select): выберите сигнальную линию от устройства, часто называемую сигнальной линией выбора микросхемы, также известной как NSS, CS, в дальнейшем именуемой NSS. Когда к ведущему SPI подключено несколько подчиненных устройств SPI, другие сигнальные линии SCK, MOSI и MISO устройства подключаются параллельно к одной и той же шине SPI одновременно, то есть независимо от того, сколько подчиненных устройств они используют только эти три шины вместе, и Каждое подчиненное устройство имеет независимую сигнальную линию NSS.Эта сигнальная линия занимает исключительно штырь хоста, то есть столько подчиненных устройств, сколько имеется сигнальных линий выбора микросхемы. Адреса протокола I2C по адресу устройства, выбирает устройство на шине и обменивается данными с ним, но нет адреса устройства в протоколе SPI, он использует сигнальную линию NSS для адресации, когда хост хочет выбрать подчиненное устройство, подчиненное устройство Сигнальная линия NSS устройства установлена на низкий уровень, выбрано подчиненное устройство, то есть выбор микросхемы действителен, и затем хост начинает связь SPI с выбранным подчиненным устройством. Поэтому связь SPI начинается с низкого уровня линии NSS и заканчивается высоким уровнем линии NSS.
(2) SCK (Serial Clock): линия тактового сигнала, используемая для синхронизации данных связи. Он генерируется хостом связи и определяет скорость связи. Максимальная тактовая частота, поддерживаемая разными устройствами, отличается. Например, максимальная тактовая частота SPI STM32 равна fpclk / 2. При обмене данными между двумя устройствами скорость связи ограничивается низкоскоростными устройствами. ,
(3) MOSI (ведущий выход, ведомый вход): вывод основного / ведомого входа. Данные хоста выводятся из этой сигнальной линии, и ведомое устройство считывает данные, отправленные хостом из этой сигнальной линии, то есть направление данных в этой линии от хоста к ведомому.
(4) MISO (ведущий вход, ведомый выход): главный вход / выход ведомого контакта. Ведущий считывает данные из этой сигнальной линии, и данные ведомого устройства выводятся в ведущий из этой сигнальной линии, то есть направление данных в этой линии подчинено ведущему.
24.1.2 Уровень протокола
Подобно I2C, протокол SPI определяет ссылки, такие как сигналы запуска и остановки, достоверность данных и тактовая синхронизация.
1. Базовый процесс общения SPI
Сначала рассмотрим последовательность обмена данными SPI, см. Рисунок 242.
Рис. 242 Время обмена данными SPI
Это коммуникационная последовательность хоста. Сигналы NSS, SCK, MOSI генерируются управляющим устройством хоста, а сигнал MISO генерируется подчиненным устройством, и хост считывает данные подчиненного устройства через сигнальную линию. Сигналы MOSI и MISO действительны только тогда, когда NSS находится на низком уровне, а MOSI и MISO передают один бит данных за такт такта SCK.
Сигналы, содержащиеся в вышеупомянутом процессе связи, разбиты следующим образом:
2. Запуск и остановка сигналов связи
При ссылочном номере на рисунке 242 сигнальная линия NSS меняется с высокого на низкий уровень, который является сигналом запуска связи SPI. NSS является сигнальной линией, эксклюзивной для каждого ведомого устройства. Когда ведомое устройство обнаруживает сигнал запуска в своей собственной линии NSS, оно знает, что оно было выбрано хостом, и начинает готовиться к связи с хостом. При ссылочном номере на рисунке сигнал NSS изменяется с низкого на высокий, который является сигналом остановки связи SPI, указывая на то, что эта связь завершена и выбранное состояние ведомого устройства отменено.
3. Достоверность данных
SPI использует сигнальные линии MOSI и MISO для передачи данных, а сигнальные линии SCK — для синхронизации данных. Линии данных MOSI и MISO передают один бит данных за такт такта SCK, а ввод и вывод данных выполняются одновременно. Во время передачи данных первый MSB или первый LSB не обязателен, но для обеспечения того, чтобы один и тот же протокол использовался между двумя устройствами связи SPI, обычно используется первый режим MSB на рисунке 242.
Обратите внимание на числа на рисунке, данные MOSI и MISO изменяют выходной сигнал во время нарастающего фронта SCK и отбираются на падающем фронте SCK. То есть во время спада SCK данные MOSI и MISO являются действительными. Когда уровень высокий, это означает данные «1», а когда он низкий, это означает данные «0». В других случаях данные являются недействительными, и MOSI и MISO готовятся к следующему представлению данных.
Каждая передача данных SPI может осуществляться в 8-битных или 16-битных единицах, и количество блоков в каждой передаче не ограничено.
4. CPOL / CPHA и режим связи
Синхронизация на рисунке 242, описанном выше, является лишь одним из режимов связи в SPI. В SPI есть четыре режима связи. Основное различие между ними — это состояние тактового сигнала SCK и время выборки данных, когда шина находится в режиме ожидания. Для удобства пояснения здесь представлены понятия «CPOL полярности часов» и «CPHA фазы синхросигнала».
Полярность тактового импульса CPOL относится к сигналу уровня сигнальной линии SCK, когда устройство связи SPI находится в состоянии ожидания (то есть в состоянии SCK перед началом связи SPI и когда линия NSS высокая). Когда CPOL = 0, SCK является низким уровнем в состоянии ожидания, а когда CPOL = 1, верно обратное.
Фаза синхронизации CPHA относится ко времени выборки данных.Когда CPHA = 0, сигнал в линии данных MOSI или MISO будет дискретизироваться по «нечетному фронту» линии синхронизации SCK. Когда CPHA = 1, строка данных выбирается по «четному краю» SCK. Смотрите рисунок 243 и рисунок 244.
Рисунок 243 Режим связи SPI, когда CPHA = 0
Давайте проанализируем эту временную диаграмму с CPHA = 0. Во-первых, по уровню SCK в состоянии ожидания он делится на два случая. Когда состояние простоя сигнальной линии SCK низкое, CPOL = 0, когда состояние простоя высокое, CPOL = 1.
Независимо от того, был ли CPOL = 0 или = 1, поскольку настроенная нами фаза синхронизации CPHA = 0, на рисунке видно, что время выборки находится на нечетном фронте SCK. Обратите внимание, что когда CPOL = 0, нечетным фронтом часов является нарастающий фронт, а когда CPOL = 1, нечетным фронтом часов является падающий фронт. Следовательно, время выборки SPI не определяется нарастающим / падающим фронтом. Эффективные сигналы линий данных MOSI и MISO остаются неизменными на нечетном крае SCK. Сигналы данных будут дискретизироваться на нечетном крае SCK. В моменты без выборки эффективные сигналы MOSI и MISO переключаются.
Аналогично, когда CPHA = 1, CPOL не влияет на него, и сигнал данных дискретизируется по четному краю SCK, см. Рисунок 244.
Рисунок 244 Режим связи SPI, когда CPHA = 1
В соответствии с различными состояниями CPOL и CPHA, SPI делится на четыре режима, см. Таблицу 241. Ведущий и ведомый должны работать в одном и том же режиме для нормальной связи. На практике чаще используются «режим 0» и «режим 3». ».
Таблица 241 Четыре режима SPI
SCK часы в режиме ожидания
24.2 Характеристики SPI и архитектура STM32
Как и периферия I2C, микросхема STM32 также интегрирует периферию специально для обмена данными по протоколу SPI.
24.2.1 STM32 SPI периферийное введение
В качестве главного и подчиненного устройства может использоваться периферийное устройство SPI STM32, поддерживающее наивысшую тактовую частоту SCK fpclk / 2 (для чипа модели STM32F429 по умолчанию установлено значение 90 МГц для fpclk1 и 45 МГц для fpclk2). Он полностью поддерживает 4 режима протокола SPI, данные Длина кадра может быть установлена равной 8 или 16 битам, и данные MSB сначала или LSB сначала могут быть установлены. Он также поддерживает двухпроводной полнодуплексный режим (этот режим описан в предыдущем разделе), двухпроводной однонаправленный и однопроводной режимы. Среди них двухпроводной однонаправленный режим может использовать линии данных MOSI и MISO для одновременной передачи данных в одном направлении, что может удвоить скорость передачи в два раза. Однопроводный режим может уменьшить аппаратную проводку, конечно, эта скорость будет затронута. Мы объясняем только двухпроводной полнодуплексный режим.
Периферийное устройство SPI STM32 также поддерживает функцию I2S. Функция I2S — это протокол последовательной передачи аудиосигнала, который будет представлен в главе, где мы объясним MP3-плеер позже.
24.2.2 Анализ SPI архитектуры STM32
Рисунок 245 Схема архитектуры SPI
1. Коммуникационные контакты
Все аппаратные архитектуры SPI разработаны на основе линий MOSI, MISO, SCK и NSS слева на рисунке 245. Микросхема STM32 имеет несколько периферийных устройств SPI, и их сигналы связи SPI выводятся на разные выводы GPIO. При использовании они должны быть настроены на эти указанные выводы, см. Таблицу 242. Для функции мультиплексирования выводов GPIO, пожалуйста, обратитесь к «Спецификациям STM32F4xx», в зависимости от того, что преобладает.
Таблица 242 Выводы SPI STM32F4xx (отсортировано по «Спецификации STM32F4xx»)
Среди них SPI1, SPI4, SPI5, SPI6 — это устройства на APB2, самая высокая скорость связи — 45 Мбит / с, SPI2, SPI3 — устройства на APB1, самая высокая скорость связи — 22,5 Мбит / с. Помимо скорости связи, нет никакой разницы в других функциях.
2. Логика управления часами
Тактовый сигнал линии SCK управляется генератором скорости передачи в соответствии с битами BR [0: 2] в «Регистре управления CR1». Этот бит является коэффициентом деления частоты для тактовых импульсов fpclk, а результатом деления частоты для fpclk является вывод SCK Выходная тактовая частота рассчитана в таблице 243.
Таблица 243 Частотное деление битов BR на fpclk
Результат деления частоты (частота SCK)
Результат деления частоты (частота SCK)
Частота fpclk относится к частоте шины APB, на которой расположен SPI, APB1 — это fpclk1, а APB2 — это fpckl2.
Конфигурируя биты «CPOL» и «CPHA» «регистра управления CR», SPI может быть установлен на четыре режима SPI, проанализированных ранее.
3. Логика управления данными
MOSI и MISO SPI подключены к регистру сдвига данных, содержимое регистра сдвига данных поступает из приемного буфера, буфера отправки и линий MISO и MOSI. При отправке данных регистр сдвига данных принимает «буфер отправки» в качестве источника данных и отправляет данные побитно по линии данных, а при приеме данных извне регистр сдвига данных помещает строку данных Выбранные данные сохраняются в «приемном буфере» побитно. Заполните данные в буфере отправки, записав «регистр данных DR» SPI, и получите содержимое буфера приема через «регистр данных DR». Длина фрейма данных может быть сконфигурирована в 8-битном и 16-битном режимах через «бит DFF» «регистра управления CR1», при настройке «бита LSBFIRST» можно выбрать MSB первым или LSB первым.
4. Общая логика управления
Общая логика управления отвечает за координацию всей периферии SPI. Режим работы логики управления изменяется в соответствии с параметрами настраиваемого нами «регистра управления (CR1 / CR2)». Основные параметры управления включают вышеупомянутый режим SPI, скорость передачи в бодах и LSB Расширенный, ведущий-ведомый режим, однонаправленный режим и т. Д. Когда периферийное устройство работает, логика управления изменит «регистр состояния (SR)» в соответствии с рабочим состоянием периферийного устройства. Нам нужно только прочитать биты регистра, связанные с регистром состояния, чтобы понять рабочее состояние SPI. Кроме того, управляющая логика также отвечает за управление генерацией сигналов прерывания SPI, запросов DMA и управление сигнальными линиями NSS по мере необходимости.
В практических приложениях мы обычно не используем стандартную сигнальную линию NSS периферийного устройства SPM STM32, а просто используем обычный GPIO, и программное обеспечение контролирует его выходной уровень, тем самым генерируя сигналы начала и остановки связи.
24.2.3 Процесс общения
Когда STM32 использует периферийную связь SPI, она будет записывать параметры в разные биты данных «регистра состояния SR» на разных этапах связи. Мы можем понять состояние связи, прочитав эти флаги регистра.
На рисунке 246 показан поток «основного режима», то есть процесс передачи и приема данных, когда STM32 используется в качестве хост-конца связи SPI.
Рисунок 246 Процесс связи с главным передатчиком
Основной режим отправки и получения процесса и описание события:
(1) Управление сигнальной линией NSS для генерации сигнала запуска (не показано на рисунке);
(2) Записать данные для отправки в «регистр данных DR», данные будут сохранены в буфере отправки;
(3) Связь начинается, и часы SCK начинают работать. MOSI передает данные в буфере отправки по битам, MISO по битам сохраняет данные в буфере приема;
(4) Когда кадр данных отправляется, «флаг TXE» в «Регистре состояния SR» будет установлен в 1, указывая, что кадр был передан, а буфер передачи пуст, аналогично, когда кадр данных был принят В это время «флаг RXNE» будет установлен в 1, указывая, что кадр был передан и буфер приема не пуст;
(5) Дождитесь, пока «флаг TXE» станет равным 1, если вы хотите продолжить отправку данных, затем снова запишите данные в «регистр данных DR»; дождитесь, пока «флаг RXNE» будет равен 1, прочитав « Регистр данных "DR" может получить содержимое буфера приема.
Если мы включим прерывание TXE или RXNE, сигнал прерывания SPI будет сгенерирован, когда TXE или RXNE установлен в 1 и войдет в ту же функцию обслуживания прерываний.После прибытия в программу обслуживания прерываний SPI мы можем проверить, какой бит является событием, проверив биты регистра. Быть обработанным. Вы также можете использовать DMA для отправки и получения данных в «Регистре данных DR».
24.3. Подробное объяснение структуры инициализации SPI
Как и другие периферийные устройства, стандартная библиотека STM32 обеспечивает структуру инициализации SPI и функцию инициализации для настройки периферийных устройств SPI. Структура и функции инициализации определены в файлах библиотеки «stm32f4xx_spi.h» и «stm32f4xx_spi.c». При программировании мы можем использовать комментарии в этих двух файлах или обращаться к справочной документации библиотеки. После понимания структуры инициализации мы можем свободно использовать периферийные устройства SPI, см. Список кодов 241.
Список кодов 241 Структура инициализации SPI
1 typedef struct
3 uint16_t SPI_Direction; /* Настроить SPI однонаправленный режим * /
4 uint16_t SPI_Mode; /* Настроить SPI ведущий / ведомый режим * /
5 uint16_t SPI_DataSize; /* Настроить Длина кадра данных SPI, необязательно 8/16 бит *
6 uint16_t SPI_CPOL; /* Установите полярность часов CPOL, выбирается высокий / низкий уровень * /
7 uint16_t SPI_CPHA; /* Установить фазу часов, опционально нечетно / Четная выборка по краям * /
8 uint16_t SPI_NSS; /* Настроить Независимо от того, управляется ли вывод NSS аппаратным или программным обеспечением SPI * /
9 uint16_t SPI_BaudRatePrescaler; /* Установите коэффициент деления часов, fpclk / номер деления частоты = fSCK * /
10 uint16_t SPI_FirstBit; /* Настроить MSB / LSB первый * /
11 uint16_t SPI_CRCPolynomial; /* Настроить Контрольное выражение CRC * /
Члены этих структур описаны следующим образом: текст в скобках — это макрос, определяемый соответствующим параметром в стандартной библиотеке STM32:
Этот элемент устанавливает направление связи SPI, которое может быть установлено в двухпроводной полнодуплексный (SPI_Direction_2Lines_FullDuplex), только двухпроводный прием (SPI_Direction_2Lines_RxOnly), только однострочный прием (SPI_Direction_1Line_Rx), однострочный режим отправки только (SP1_Direction).
Этот элемент устанавливает SPI для работы в основном режиме (SPI_Mode_Master) или в подчиненном режиме (SPI_Mode_Slave). Самым большим различием между этими двумя режимами является синхронизация сигнальной линии SCK SPI. Синхронизация SCK генерируется хостом в процессе связи. При настройке в качестве подчиненного режима периферийное устройство SPI STM32 будет принимать внешний сигнал SCK.
Этот член может выбрать, будет ли размер кадра данных связи SPI 8 бит (SPI_DataSize_8b) или 16 бит (SPI_DataSize_16b).
(4) SPI_CPOL и SPI_CPHA
Эти два элемента конфигурируют полярность синхронизации CPOL и фазу синхронизации CPHA SPI. Эти две конфигурации влияют на режим связи SPI. Описание CPOL и CPHA см. В предыдущем разделе. Раздел «Режим связи».
Элемент CPOL полярности часов может быть установлен на высокий уровень (SPI_CPOL_High) или низкий уровень (SPI_CPOL_Low).
Фаза синхронизации CPHA может быть установлена в SPI_CPHA_1Edge (сбор данных по нечетному фронту SCK) или SPI_CPHA_2Edge (сбор данных по четному фронту SCK).
Этот элемент настраивает режим использования контакта NSS, который можно выбрать в качестве аппаратного режима ( SPI_NSS_Hard ) И программный режим ( SPI_NSS_Soft ), сигнал выбора микросхемы SPI в аппаратном режиме автоматически генерируется аппаратным обеспечением SPI, в то время как программный режим требует, чтобы мы лично поднимали соответствующий порт GPIO на высокий или низкий уровень, чтобы генерировать сигналы без микросхемы выбора и выбора микросхемы. На практике существует множество приложений программного режима.
Этот элемент устанавливает коэффициент деления скорости передачи, а разделенная тактовая частота является тактовой частотой сигнальной линии SPI SCK. Этот параметр-член может быть установлен в fpclk 2, 4, 6, 8, 16, 32, 64, 128, 256 с частотным разделением.
Все последовательные протоколы связи будут иметь сначала MSB (сначала более высокие данные) или сначала LSB (сначала более низкие данные), и модуль SPI STM32 может быть запрограммирован для управления этой функцией через этот элемент структуры.
Это полином в проверке CRC SPI. Если мы используем проверку CRC, параметр (полином) этого члена используется для вычисления значения CRC.
После настройки этих элементов структуры нам нужно вызвать функцию SPI_Init для записи этих параметров в регистр для реализации инициализации SPI, а затем вызвать SPI_Cmd для включения периферийного устройства SPI.
24.4. SPI — чтение и запись эксперимента с последовательной флэш-памятью
Память FLSAH также известна как флэш-память, и она, и EEPROM являются памятью, которая не теряет данные после сбоя питания, но емкость флэш-памяти обычно больше, чем EEPROM, и теперь она в основном сменила свой статус. Диски U, SD-карты, твердотельные накопители SSD, обычно используемые в нашей жизни, и устройства, используемые для хранения программ внутри наших микросхем STM32, представляют собой флэш-память. В управлении хранилищем основное отличие состоит в том, что микросхема FLASH может быть стерта и стерта только большими кусками, и в «главе I2C» мы узнали, что EEPROM может быть стерта и записана в один байт.
В этом разделе микросхема последовательной флэш-памяти, использующая коммуникацию SPI, читает и пишет эксперименты для всех, чтобы объяснить использование STM32 SPI. В эксперименте периферийное устройство SPI STM32 использует основной режим, а нормальная связь обеспечивается путем запроса событий.
24.4.1 аппаратный дизайн
Рисунок 247 Схема подключения оборудования SPI serial FLASH
Микросхема FLASH (модель: W25Q128) в этой экспериментальной плате является флэш-памятью NOR, использующей протокол связи SPI, ее выводы CS / CLK / DIO / DO подключены к соответствующим выводам SDI NSS / SCK / MOSI / STM32 соответственно. В MISO вывод NSS STM32 — это обычный GPIO, а не выделенный вывод NSS SPI, поэтому нам нужно использовать программное управление в программе.
В микросхеме FLASH также есть контакты WP и HOLD. Вывод WP может управлять функцией защиты от записи. Если этот вывод низкий, запись данных запрещена. Мы напрямую подключаемся к источнику питания и не используем функцию защиты от записи. Вывод HOLD можно использовать для приостановки обмена данными. Когда этот вывод низкий, обмен данными приостанавливается, вывод вывода данных выводит состояние высокого импеданса, а выводы синхронизации и ввода данных недопустимы. Мы напрямую подключаемся к источнику питания и не используем функцию паузы связи.
Для получения дополнительной информации о микросхеме FLASH, пожалуйста, обратитесь к ее техническому описанию "W25Q128", чтобы понять. Если модель или контрольный вывод FLASH используемой вами платы эксперимента отличаются, просто измените их в соответствии с нашим проектом, принцип управления программой тот же.
24.4.2 разработка программного обеспечения
Чтобы сделать проект более организованным, мы отдельно храним и читаем код, связанный с FLASH, чтобы облегчить будущую трансплантацию. Создайте новые файлы "bsp_spi_flash.c" и "bsp_spi_flash.h" поверх "шаблона проекта". Эти файлы также могут быть названы в соответствии с вашими предпочтениями. Они не являются частью стандартной библиотеки STM32 и написаны нами в соответствии с потребностями приложения. ,
1. Ключевые моменты программирования
(7) Целевые контакты и часы портов, используемые для первоначальной связи;
(8) включить синхронизацию периферийного устройства SPI;
(9) настроить режим периферийного устройства SPI, адрес, скорость и другие параметры и включить периферийное устройство SPI;
(10) Написать базовую функцию SPI для отправки и получения байтов;
(11) Функции записи для стирания FLASH и операций чтения и записи;
(12) Написать тестовую программу для проверки чтения и записи данных.
Определение макросов, связанных с оборудованием SPI
Мы определяем все связанные с оборудованием SPI конфигурации в виде макросов в файле "bsp_spi_flash.h", см. Список кодов 242.
Список кодов 242 Макросы, связанные с аппаратной конфигурацией SPI
1 //SPI число И функция инициализации часов
2 #define FLASH_SPI SPI3
3 #define FLASH_SPI_CLK RCC_APB1Periph_SPI3
4 #define FLASH_SPI_CLK_INIT RCC_APB1PeriphClockCmd
6 #define FLASH_SPI_SCK_PIN GPIO_Pin_3
7 #define FLASH_SPI_SCK_GPIO_PORT GPIOB
8 #define FLASH_SPI_SCK_GPIO_CLK RCC_AHB1Periph_GPIOB
9 #define FLASH_SPI_SCK_PINSOURCE GPIO_PinSource3
10 #define FLASH_SPI_SCK_AF GPIO_AF_SPI3
12 #define FLASH_SPI_MISO_PIN GPIO_Pin_4
13 #define FLASH_SPI_MISO_GPIO_PORT GPIOB
14 #define FLASH_SPI_MISO_GPIO_CLK RCC_AHB1Periph_GPIOB
15 #define FLASH_SPI_MISO_PINSOURCE GPIO_PinSource4
16 #define FLASH_SPI_MISO_AF GPIO_AF_SPI3
18 #define FLASH_SPI_MOSI_PIN GPIO_Pin_5
19 #define FLASH_SPI_MOSI_GPIO_PORT GPIOB
20 #define FLASH_SPI_MOSI_GPIO_CLK RCC_AHB1Periph_GPIOB
21 #define FLASH_SPI_MOSI_PINSOURCE GPIO_PinSource5
22 #define FLASH_SPI_MOSI_AF GPIO_AF_SPI3
24 #define FLASH_CS_PIN GPIO_Pin_8
25 #define FLASH_CS_GPIO_PORT GPIOI
26 #define FLASH_CS_GPIO_CLK RCC_AHB1Periph_GPIOI
28 // контроль CS (NSS) вывод вывода низкого уровня
29 #define SPI_FLASH_CS_LOW()
30 // контроль CS (NSS) вывод высокого уровня вывода
31 #define SPI_FLASH_CS_HIGH()
Приведенный выше код инкапсулирует номер SPI, номер контакта, источник вывода и отображение мультиплексной функции, используемые при обмене данными FLASH с макросами в соответствии с аппаратным соединением, и определяет макрос, который контролирует уровень вывода вывода CS (NSS), так что Используется, когда конфигурация генерирует сигналы запуска и остановки.
Инициализировать SPI GPIO
Используйте приведенный выше макрос для написания функции инициализации SPI, см. Листинг 243 кода.
Список кодов 243 Функция инициализации SPI (часть инициализации GPIO)
3 * @brief SPI_FLASH инициализация
7 void SPI_FLASH_Init( void )
9 GPIO_InitTypeDef GPIO_InitStructure;
11 /* включить Часы FLASH_SPI и GPIO * /
12 /*!< SPI_FLASH_SPI_CS_GPIO, SPI_FLASH_SPI_MOSI_GPIO,
13 SPI_FLASH_SPI_MISO_GPIO с SPI_FLASH_SPI_SCK_GPIO включение часов * /
14 RCC_AHB1PeriphClockCmd (FLASH_SPI_SCK_GPIO_CLK | FLASH_SPI_MISO_GPIO_CLK|
15 FLASH_SPI_MOSI_GPIO_CLK|FLASH_CS_GPIO_CLK, ENABLE);
17 /*!< SPI_FLASH_SPI Часы включения */
18 FLASH_SPI_CLK_INIT(FLASH_SPI_CLK, ENABLE);
20 // Установить пин-мультиплексирование
28 /*!< конфигурация SPI_FLASH_SPI: SCK * /
29 GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;
30 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
31 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
32 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
33 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
36 GPIO_Init(FLASH_SPI_SCK_GPIO_PORT, &GPIO_InitStructure);
38 /*!< конфигурация SPI_FLASH_SPI контакт: MISO * /
39 GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;
40 GPIO_Init(FLASH_SPI_MISO_GPIO_PORT, &GPIO_InitStructure);
42 /*!< конфигурация SPI_FLASH_SPI pin: MOSI * /
43 GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;
44 GPIO_Init(FLASH_SPI_MOSI_GPIO_PORT, &GPIO_InitStructure);
46 /*!< конфигурация SPI_FLASH_SPI pin: CS * /
47 GPIO_InitStructure.GPIO_Pin = FLASH_CS_PIN;
48 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
49 GPIO_Init(FLASH_CS_GPIO_PORT, &GPIO_InitStructure);
51 /* Стоп сигнал FLASH: CS pin высокий уровень * /
53 /* Для простоты объяснения следующее опущено Часть инициализации режима SPI * /
Как и для всех периферийных устройств, использующих GPIO, вы должны сначала инициализировать используемый режим вывода GPIO и настроить функцию мультиплексирования. Процесс инициализации GPIO выглядит следующим образом:
(1) Используйте GPIO_InitTypeDef для определения переменных структуры инициализации GPIO, чтобы можно было использовать следующее для хранения конфигурации GPIO;
(2) Вызвать библиотечную функцию RCC_AHB1PeriphClockCmd, чтобы включить часы порта GPIO, используемые выводами SPI, и использовать операцию «|» для одновременной настройки нескольких выводов. Вызовите макрос FLASH_SPI_CLK_INIT, чтобы включить периферийные часы SPI (этот макрос инкапсулирует библиотечную функцию для включения синхронизации APB).
(3) Присвойте значения структуре инициализации GPIO и инициализируйте выводы SCK / MOSI / MISO в мультиплексном двухтактном режиме. Поскольку вывод CS (NSS) управляется программным обеспечением, мы настраиваем его как обычный двухтактный выходной режим.
(4) Используя конфигурацию вышеуказанной структуры инициализации, вызовите функцию GPIO_Init для записи параметров в регистр для завершения инициализации GPIO.
Настроить режим SPI
Выше приведена только конфигурация контактов, используемых SPI, и конфигурация периферийного режима SPI. Перед настройкой режима SPI STM32 мы должны сначала понять режим SPI ведомой стороны. Этот пример можно получить, обратившись к спецификации FLASH "W25Q128". Согласно описанию микросхемы FLASH, он поддерживает режим SPI 0 и режим 3, поддерживает двухпроводной полный дуплекс, использует первый режим MSB, поддерживает самый высокий тактовый сигнал связи 104 МГц, а длина кадра данных составляет 8 бит. Нам необходимо настроить эти параметры в периферийном устройстве SPI STM32 для обеспечения согласованности. См код листинга 244.
Листинг 244 Настройка режима SPI
2 * @brief SPI_FLASH Инициализация булавки
6 void SPI_FLASH_Init( void )
8 /* Для простоты объяснения опущено Часть инициализации SPI GPIO * /
11 SPI_InitTypeDef SPI_InitStructure;
12 /* FLASH_SPI Конфигурация режима */
13 // FLASH чип Поддержка SPI Mode 0 и Mode 3, установите CPOL CPHA соответственно
14 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
15 SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
16 SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
17 SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
18 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
19 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
20 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
21 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
22 SPI_InitStructure.SPI_CRCPolynomial = 7;
23 SPI_Init(FLASH_SPI, &SPI_InitStructure);
25 /* включить FLASH_SPI */
26 SPI_Cmd(FLASH_SPI, ENABLE);
В этом коде периферийное устройство SPM STM32 сконфигурировано как сторона хоста, двухпроводный полнодуплексный режим, длина кадра данных составляет 8 битов, используется режим SPI 3 (CPOL = 1, CPHA = 1), вывод NSS управляется программным обеспечением И MSB первый режим. Последний элемент — формула расчета CRC. Поскольку нам не требуется проверка CRC для связи с микросхемой FLASH, а функция CRC SPI не включена, значение элемента формулы расчета CRC является недействительным.
После завершения назначения вызывается библиотечная функция SPI_Init для записи этих конфигураций в регистр, а функция SPI_Cmd вызывается для включения периферийного устройства.
Используйте SPI для отправки и получения одного байта данных
После инициализации периферийного устройства SPI можно использовать обмен SPI. Сложный обмен данными состоит из одного байта передачи и приема данных. Давайте посмотрим на его реализацию кода, см. Список кодов 245.
Листинг 245. Использование SPI для отправки и получения одного байта данных
1 #define Dummy_Byte 0xFF
3 * @brief использование SPI отправляет один байт данных
4 * @param byte : Данные для отправки
5 * @retval Вернуть полученные данные
7 u8 SPI_FLASH_SendByte(u8 byte)
9 SPITimeout = SPIT_FLAG_TIMEOUT;
11 /* Подождите, пока буфер отправки будет пустым, TXE событие * /
12 while (SPI_I2S_GetFlagStatus(FLASH_SPI, SPI_I2S_FLAG_TXE) == RESET)
14 if ((SPITimeout—) == 0) return SPI_TIMEOUT_UserCallback(0);
17 /* Записать в регистр данных, записать данные для записи в буфер отправки */
18 SPI_I2S_SendData(FLASH_SPI, byte);
20 SPITimeout = SPIT_FLAG_TIMEOUT;
22 /* Подождите, пока приемный буфер не будет пустым, Событие RXNE * /
23 while (SPI_I2S_GetFlagStatus(FLASH_SPI, SPI_I2S_FLAG_RXNE) == RESET)
25 if ((SPITimeout—) == 0) return SPI_TIMEOUT_UserCallback(1);
28 /* Прочитайте регистр данных, чтобы получить данные буфера приема */
29 return SPI_I2S_ReceiveData(FLASH_SPI);
33 * @brief использование SPI читает один байт данных
35 * @retval Вернуть полученные данные
37 u8 SPI_FLASH_ReadByte( void )
39 return (SPI_FLASH_SendByte(Dummy_Byte));
Однобайтовая функция отправки SPI_FLASH_SendByte содержит обработку тайм-аута ожидания событий.Эта часть принципа такая же, как в I2C, и здесь не будет повторяться.
Функция SPI_FLASH_SendByte реализует «процесс связи SPI», описанный ранее:
(1) Эта функция не содержит сигналов запуска и остановки SPI, а только основной процесс отправки и получения, поэтому операции с сигналами запуска и остановки должны выполняться до и после вызова этой функции;
(2) Назначьте макрос SPIT_FLAG_TIMEOUT переменной SPITimeout. Эта переменная SPITimeout уменьшается на 1 каждый раз в следующем цикле while. Цикл обнаруживает событие, вызывая библиотечную функцию SPI_I2S_GetFlagStatus. Если событие обнаружено, оно переходит на следующий этап связи. Если событие не обнаружено, оно остается здесь и было обнаружено. , Когда обнаруживается, что SPIT_FLAG_TIMEOUT не ожидал события, считается, что связь не удалась. Вызываемый SPI_TIMEOUT_UserCallback выводит информацию об отладке и выходит из связи;
(3) Проверяя флаг TXE, можно получить состояние буфера отправки. Если буфер отправки пуст, это означает, что последние данные, которые могут существовать, были отправлены;
(4) После ожидания, пока буфер отправки не станет пустым, вызовите библиотечную функцию SPI_I2S_SendData, чтобы записать «байт» данных для отправки в регистр данных SPI DR. Данные, записанные в регистр данных SPI, будут сохранены в буфере отправки. Набор для отправки;
(5) После записи дождитесь события RXNE, то есть буфер приема не пуст. Поскольку передача данных между MOSI и MISO является синхронной в двухпроводном полнодуплексном режиме SPI (для сравнения, пожалуйста, сравните с «процессом связи SPI»), когда буфер приема не пуст, это означает, что вышеуказанные данные отправлены, а также получен буфер приема. Новые данные
(6) При ожидании, пока приемный буфер не будет пустым, вы можете получить новые данные в приемном буфере, вызвав библиотечную функцию SPI_I2S_ReceiveData для чтения регистра данных SPI DR. В коде используется ключевое слово «return», чтобы принимать полученные данные в качестве возвращаемого значения функции SPI_FLASH_SendByte, поэтому мы можем видеть функцию получения данных SPI SPI_FLASH_ReadByte, определенную ниже, она просто вызывает функцию SPI_FLASH_SendByte для отправки данных «Dummy_Byte», Затем получите его возвращаемое значение (потому что вас не волнуют отправленные данные, поэтому входной параметр "Dummy_Byte" может быть любым значением) Причина, по которой это может быть сделано, заключается в том, что процесс получения и процесс отправки SPI по существу одинаковы, а отправка и получение синхронизированы.Ключевым моментом является то, что в наших приложениях верхнего уровня основное внимание уделяется отправленным или полученным данным.
Инструкция по контролю FLASH
После получения базового блока приемопередатчика SPI вам также необходимо понять, как читать и записывать данные в микросхему FLASH. Микросхема FLASH определяет множество инструкций. Мы используем шину SPI для отправки инструкций микросхеме FLASH, управляя STM32. После получения микросхемы FLASH она выполнит соответствующую операцию.
И эти инструкции для хост-стороны (STM32) следуют только за данными, отправляемыми по самому базовому протоколу связи SPI, но на стороне устройства (микросхема FLASH) интерпретируют эти данные в разных значениях, поэтому они становятся командой. Проверьте спецификацию "W25Q128" микросхемы FLASH, чтобы узнать о функциях и форматах команд различных команд, определенных им, см. Таблицу 244.
Таблица 244 Таблица инструкций общей микросхемы FLASH (из спецификации "W25Q128")
Как записать переменную во флеш память 80c51f120
Добрый день! Существует необходимость постоянного хранения в памяти (энергонезависимой) микроконтроллера c8051f320 достаточно большого массива чисел (255 строк 3 столбца), сами числа от 0x00 до 0xFF. Поглядев даташит мк (опыта работы с ним не имею), пришел к выводу, что для подобных задач предназначена 16-килобайтная FLASH память, однако инструкции, как ей пользоваться не нашел.
Кратко говоря, мне нужно:
1) Заранее записать в память мой массив (в наличии присутствует developing kit: http://www.silabs.com/products/mcu/Pages/C8051F320DK.aspx , подсоедененный через USB)
2) Считывать данные из массива в ходе работы основной программы МК (Пишу в стандартном IDE, язык C-51)
Буду благодарен за любую помощь!
Не совсем понятно что вы имеете в виду. Надо хранить массив данных в flash, который готовится на этапе создания прошивки и никогда более не изменяется, либо изменяется на лету самой программой?
В первом случае в компиляторе определено какое-то специальное ключевое слово вроде const или flash, говорящее линкеру воткнуть массив в определенный тип памяти. Обращаться с константными массивами и переменными как и с любыми остальными, только писать в них нельзя.
Во втором случае используются функции bootloader-а или программирования приложением(IAP). Как обстоят дела с этим конкретно у С8051 не знаю, но по идее все(почти) современные МК так умеют.
Сборка печатных плат от $30 + БЕСПЛАТНАЯ доставка по всему миру + трафарет
Приглашаем 13 октября всех желающих присоединиться к вебинару, который будет проводить компания КОМПЭЛ совместно с представителями бренда MEAN WELL. Вебинар будет посвящен новинкам продукции, планам MEAN WELL на следующий год, аналогам продукции ушедших из РФ брендов, особенностям работы в текущих условиях, возможностях субдистрибьюции и другим вопросам. Мероприятие пройдет в формате живого диалога.
Если в даташите указан режим самопрогорамммирования ПЗУ — возможно.
С силапсами не работал.
В "стандартном" mcs51 подобная операция отсутствует — однако вполне возможна при вводе дополнительных функций через "свободную" область РСФ (на усмотрение разработчиков фирмы-изготовителя). Требуется доскональное "вкуривание" даташита на конкретный МК и юзерс гвиде изготовителя на группу МК.
Ежли речь идет о записи в EEPROM — то также даташит в лапы.
Производитель популярных модулей электропитания MORNSUN помимо них предлагает также микросхемы ШИМ-контроллеров для AC/DC и DC/DC, микросхемы запуска (стартеры) для этих ШИМ-контроллеров, драйверы интерфейсов RS-485 и CAN и микросхемы изоляторов для гальванической развязки интерфейсов и цифровых сигнальных линий. В некоторых случаях эти изделия превосходят по характеристикам изделия европейских и американских производителей.
Сделаем мир удобнее. —>
Первое, из-за чего возникло желание более подробно разобраться с flash-памятью, явилось то, что в своих проектах хотелось иметь энергонезависимую память, но к сожалению серия микроконтроллеров STM32f1xx не имеет встроенной eeprom. Во вторых, без знания работы flash было бы проблематично написать собственный bootloader.
Кроме, собственно, встроенной flash-памяти, я также немного расскажу и о оперативной памяти. Как обычно, основным источником для статьи является родное руководство от компании STMicroelectronics (Reference Manual), а также обобщенная информация из различных форумов и статей в интернете, ну и на основе своего опыта.
Предисловие
Изначально планировалось объединить в одной статье информацию о структуре, регистрах и методах работы с Flash. Но в итоге, статья получилась бы слишком громоздкой, поэтому было принято решение написать две статьи: одна для начинающих, используя готовые библиотеки от STM, а вторая статья будет о том, как, собственно, эти библиотеки работают с памятью.
В данной статье я расскажу о структуре памяти, основных требованиях при работе с ней и о некоторых моментах, которые могут помочь быстрее разобрать с Flash.
Почему FLASH?
Во многих небольших проектах возникает необходимость сохранять какие-либо параметры работы устройства в независимой от электричества памяти (EEPROM). Но в STMicroelectronics решили, что использование этой памяти в большинстве микроконтроллеров не целесообразно, исключение составляет только серия LP (Low Power), в которой EEPROM присутствует.
В крупных проектах проблемы нет — используется внешняя память, а вот в небольших использование внешней памяти не всегда выход, так как, помимо всего прочего, нужно эту память разместить на плате и правильно развести.
В интернете полно различных примеров, есть даже изыски о том, как хранить небольшой объем данных в регистрах backup’a, но все же, если не прибегать к внешней памяти, основным способом хранения данных с защитой при отключении питания является хранение во внутренней flash-памяти.
В первую очередь flash-память предназначена для хранения инструкций для микроконтроллера, другими словами — программного кода. Но если Ваш программный код занимает не весь объем памяти, то почему бы не выделить ее часть под хранение наших настроек?
- Не требуется использование внешней памяти, соответственно сокращается время на разработку платы и происходит удешевление продукта;
- Меньше программного кода, следовательно меньшее время затрачивается на разработку.
Есть конечно и свои ограничения:
- Запись во Flash требует некоторого времени, что влияет на производительность МК в момент записи или очистки памяти;
- Чтобы внести изменения в уже существующие данные, нужно стереть всю страницу или записывать в «чистый» блок памяти;
- Количество циклов перезаписи гарантировано в районе 100 тысяч операций — вроде бы много, но перезаписывая данные в страницу раз в секунду, МК выработает ресурс flash чуть более, чем за сутки. Поэтому очень не рекомендую постоянно писать во flash, рекомендуется производить операции очистки / записи лишь для сохранения данных в «энергонезависимой» памяти. В остальных случаях работаем с оперативной памятью;
- Минимум Вы можете использовать 1 страницу памяти (даже для одного байта), а размер одной страницы составляет от одного до двух килобайт, в зависимости от модели микроконтроллера. Такова селяви устройства flash-памяти.
Но не стоит забывать что в первую очередь flash-память предназначена для хранения программного кода и, соответственно, сама структура интерфейса Flash в микроконтроллере устроена таким образом, чтобы оптимизировать загрузку инструкций для МК и организовать их (инструкций) кэширование, пока идет выполнение уже загруженных.
Структура
Как нам сообщает Reference Manual, модуль flash-памяти является высокопроизводительным и имеет следующие ключевые особенности:
- Для устройств серии XL-density: объем памяти составляет до 1Mb, разбитый на два банка памяти:
— 1-й банк памяти: 512KB;
— 2-й банк памяти: до 512KB, в зависимости от модели микроконтроллера.
- Для других устройств: объем памяти составляет до 512KB, состоящий из одного банка памяти размером до 512KB.
- Сама по себе Flash-память состоит из трех блоков: основной блок памяти, информационный блок и блока регистров управления Flash. Структура блоков памяти и их характеристики приведены на рисунке №1.
Интерфейс flash-памяти (FLITF (Flash Memory Interface)) позволяет выполнять следующие операции:
- Чтение из памяти с возможностью буферизации (2 слова по 64 бита);
- Операции записи и очистки памяти;
- Организация защиты flash от чтения и/или записи.
Основная flash-память разбита на страницы размером в 1 или 2 килобайта, это зависит от линейки микроконтроллеров STM. Использовать под свои нужды Вы можете только целое количество страниц, соответственно, если Ваша прошивка занимает все страницы памяти МК, то использование flash-памяти под свои нужды будет проблематично.
Два оставшихся блока памяти, информационный и блок регистров, достаточно редко используются напрямую, в основном используется функционал из готовых библиотек. Поэтому в дальнейшем описании я коснусь только регистра настройки, а подробное описание регистров памяти и информационного блока я приведу в отдельной статье.
Операции с flash-памятью
Операций работы с flash-памятью не так уж и много: можем прочитать, записать и очистить память. Причем запись и очистка памяти, как правило, осуществляется вместе. Также еще можно заблокировать память для чтения и/или для записи. Но перед началом работы необходимо произвести инициализацию модуля Flash.
Инициализация Flash.
Операции чтения flash-памяти и доступ к данным выполняются через шину AHB. Буферизация чтения, для выполнения команд, выполняется через шину ICode. Арбитраж выполнения осуществляется самой flash-памятью, а приоритет отдается доступу к данным на шине DCode. Другими словами, пока микроконтроллер выполняет текущую команду, интерфейс Flash-памяти передает МК данные по следующей.
Чтение данных из flash-памяти может быть сконфигурировано следующим образом:
• Задержка (Latency): количество состояний ожидания для операции чтения, устанавливается «на лету», не требуется инициализация МК;
• Буферизация (2 слова по 64 бита) (Prefetch buffer): активируется во время инициализации МК. Весь блок памяти может быть заменен за один цикл чтения из памяти, так как размер блока соответствует размеру полосы пропускания шины flash-памяти. Благодаря буферизации, возможна более быстрая работа МК, поскольку МК извлекает из памяти по одному «слову» за раз одновременно со следующим «словом», которое помещается в предварительный буфер.
• Половинный цикл (Half cycle): этот режим требуется для оптимизации питания, чаще всего используется при работе от батарейки с программами, которые не требуют особого быстродействия.
Эти параметры следует использовать в соответствии с временем доступа к flash-памяти. Значение периода ожидания представляет собой отношение периода SYSCLK (системных часов) к времени доступа к flash-памяти и устанавливается в зависимости от следующих параметров:
- Уровень «0», если 0 <SYSCLK ≤ 24 MHz;
- Уровень «1», если 24 MHz < SYSCLK ≤ 48 MHz;
- Уровень «2», если 48 MHz < SYSCLK ≤ 72 MHz;
Конфигурация с половинным циклом недоступна в сочетании с предделителем на AHB. Системные часы (SYSCLK) должны быть равны часам HCLK. Поэтому эту функцию можно использовать только с низкочастотными тактовыми частотами 8MHz или менее. Он может быть создан из HSI или HSE, но не из PLL.
Буфер предварительной выборки должен храниться при использовании пределителя, отличного от 1 на часах AHB.
Буфер предварительной выборки должен быть включен / выключен только тогда, когда SYSCLK ниже 24MHz и на часах AHB не применяется предварительный делитель (SYSCLK должен быть равен HCLK). Буфер предварительной выборки обычно включается / выключается во время процедуры инициализации, а микроконтроллер работает на встроенном генераторе 8MHz (HSI).
Это информация довольно сложна для восприятия при начальном уровне знаний, для понимания ее работы требуется умение настраивать тактирование МК и всех его интерфейсов.
Для инициализации Flash предназначен регистр FLASH_ACR, который используется для включения / выключения буферизации и полного цикла чтения, а также для управления задержкой времени доступа к flash-памяти в зависимости от настроенной частоты работы процессора. В приведенной ниже таблице приведены битовые карты и описания бит для этого регистра.
Address offset: 0x00
Reset value: 0x0000 0030
Биты | Название | Описание |
---|---|---|
31:6 | Зарезервировано | |
5 | PRFTBS — Prefetch buffer status | Состояние буферизации. 0: отключена; 1: включена. Бит доступен только для чтения. |
4 | PRFTBE — Prefetch buffer enable | Включение буферизации. 0: отключена; 1: включена. |
3 | HLFCYA — Flash half cycle access enable | Доступ к полному циклу Flash. 0: отключен; 1: включен. |
2:0 | LATENCY — Latency | Управление задержкой. Эти биты представляют отношение периода SYSCLK (системных часов) к времени доступа Flash. 000 Уровень «0», если 0 <SYSCLK≤ 24 МГц 001 Уровень «1», если 24 МГц <SYSCLK ≤ 48 МГц 010 Уровень «2», если 48 МГц <SYSCLK ≤ 72 МГц |
Теперь посмотрим, как это выглядит в программном коде:
Настройку задержки необходимо выполнять в соответствии с настройками Вашего микроконтроллера, здесь лишь приведены примеры команд.
Саму процедуру инициализации рекомендуется выполнять либо перед инициализацией микроконтроллера, либо непосредственно из функции инициализации МК (за исключением задержки, ее можно настраивать в любой момент времени работы программы).
Чтение данных из flash-памяти.
Чтение flash не представляет никаких сложностей для понимания, достаточно обратится к необходимой ячейки памяти:
Вот и все, для чтения нам больше ничего не нужно! Единственной о чем нужно помнить, это то, что чтение происходит блоками по 32 бита.
Чтобы сделать код более наглядным, чтение из flash-памяти я вынес в отдельную функцию
Запись данных во flash-память.
Запись данных во Flash устроена немного сложнее, чем чтение. Основным отличием Flash от оперативной памяти является то, что перед тем, как произвести запись значения в ячейку, ее необходимо очистить. Нельзя вот просто так записать в память сначала одно значение, затем другое — так не получится. Но и очистить ячейку памяти просто так нельзя! Можно очистить только страницу целиком или сразу всю память МК.
Операция стирания памяти не обнуляет, как казалось бы логичным, а устанавливает значение битов памяти равным единице. А операция записи только обнуляет необходимые биты. Перезаписать ноль в единицу нельзя, можно только стереть всю страницу памяти целиком. Это основное неудобство использования Flash. Поэтому используются два способа перезаписи пользовательских данных: первый — последовательно, когда параметры страницы не стираются, а дописываются в конец данных, пока не закончится страница; и второй способ, когда перед изменением данных стирается вся страница целиком, а потом уже все параметры записываются заново. При первом случае память прослужит дольше, а второй более удобный для использования.
Причина этого в том, что Flash память не является оперативной, она намного более медленная, чем оперативная, и предназначена для хранения набора инструкций для микроконтроллера. Но есть и большой плюс: при отключении питания она не стирается.
Обычно под свои нужды программисты используют одну-две последних страницы памяти МК. Это обусловлено тем, что код программы может модифицироваться и занимать все больше объема flash. А так как, запись инструкций для МК производится, как правило, с первой страницы, то может произойти ситуация, что запись новой прошивки просто перетрем пользовательские данные. Исключение составляют ситуации, когда, к примеру, в микроконтроллер «заливается» автозагрузчик (bootloader) и основная программа начинается не с первой страницы, а, к примеру, с десятой. Но это тема отдельной статьи и здесь упоминание будет только вскользь.
А теперь, когда мы ознакомились с теорией, разберем уже практические способы реализации очистки и перезаписи flash памяти.
Как Вы наверно заметили, запись в память происходит между двумя командами «FLASH_Unlock()» и «FLASH_Lock()». Нетрудно догадаться, что это связано с разблокировкой памяти для возможности производить запись во Flash. Соответственно, после записи, Flash нужно закрыть для изменения.
NVIC_VectTab_FLASH и PARAMS_PAGE_OFFSET — это определения адресов памяти и смещения, относительно начального адреса.
Чтобы было понятнее — разберем пример:
Размер прошивки автозагрузчика составляет менее 3Kb, соответственно нам нужно начать запись основной прошивки в третью страницу или выше с учетом того, чтобы прошивка уместилась в оставшуюся память. Не забывайте, что страницы flash-памяти начинаются с нуля, соответственно четвертая по счету страница называется «Page3».
NVIC_VectTab_FLASH — обычно равен 0x08000000, он определен в системе (в файле misc.h).
FIRMWARE_PAGE_OFFSET- это наше определение и формируется по следующей формуле: НомерСтраницыПамяти * РазмерСтраницыПамяти. Номер страницы — мы определяем самостоятельно, размер страницы памяти для большинства МК серии STM32F103 составляет 1Kb, за исключением микроконтроллеров линейки HD и CL (Connectivity Line), в которых она равна двум килобайтам.
В нашем случае получается: (4-1) * 0x0400 = 0x0C00 — это смещение относительно начального адреса. Соответственно писать данные мы будем начинать с адреса 0x08000C00.
Можно конечно и сразу указать адрес, куда будем писать (0x08000C00), но лучше сразу привыкать писать «правильно» и с уклоном на будущее, чтобы Ваш код без особой переделки мог мигрировать на другие МК.
Чтобы стереть страницу памяти, достаточно в команду FLASH_ErasePage в качестве параметра передать любой адрес из диапазона адресов страницы. Как правило передают адрес первого блока нужной страницы. Не ошибитесь с определением страницы памяти, так как можно стереть и саму прошивку ))).
Запись производится блоками по четыре байта — это называется «словом» размером в 32 бита. Если попытаться произвести запись по адресу, который не кратен четырем, то программа уйдет в бесконечный цикл ожидания окончания записи.
Также стоит обратить внимание на то, что перед записью Вам необходимо очистить столько страниц, сколько килобайт памяти Вам необходимо записать округляя до большего целого (при условии что одна страница = 1Kb).
Ну и после окончания записи, необходимо заблокировать Flash для дальнейшего изменения.
Использование структур данных.
Очень удобно хранить данные в виде структуры, но существуют некоторые тонкости, которые могут попортить немало нервных клеток не только у начинающих разработчиков, но и у бывалых.
Само определение структуры выглядит примерно так:
Как видите — ничего сложного нет.
Когда Вы определяете переменную, под нее резервируется место в оперативной памяти в размере, который Вы определили. Адреса памяти резервируются подряд, в блоках по четыре байта. Соответственно, если вы определяете подряд три переменных по байту, а затем одну, размером в два байта, то в памяти они займут в общей сложности 8 байт: 3 байта в первом блоке и два во втором. В силу специфики работы с оперативной памятью, занимать память частями не получится!
На рисунке №3 я отобразил, как будет распределяться память при определении переменных.
Как видно из рисунка, для экономии памяти и правильной ее организации, переменные необходимо объявлять таким образом, чтобы объем занимаемой имя памяти был кратным четырем. Это же правило касается и определения структуры памяти.
В целом, ошибки в размещения в неправильном порядке переменных нет, при современных объемах оперативки это не критично. Но правила хорошего тона в программировании настоятельно рекомендуют определять переменные с учетом их размещения в памяти. Проверить себя, можно через отладчик, посмотрев срез памяти и порядок заполнения оперативной памяти МК по адресу 0x20000000.
В данном примере, правильный порядок определения переменных это требование, а не рекомендация, так как нам нужно знать точное количество блоков памяти, в которых разместится переменная с нашим определением структуры. Второй пример с ошибкой на рисунке 3 показывает, что если мы не так определим порядок переменных, то объем занимаемой памяти в структуре будет занимать не один блок, как мы думали, а два. Соответственно при сохранении данных во Flash, мы можем потерять часть информации.
Теперь я приведу примеры чтения и записи параметров из/в Flash одним блоком.
Переменная с нашей структурой хранится в оперативной памяти, при ее определении в оперативной памяти выделяется определенный объем байт. Мы можем записывать во Flash не только каждый параметр в отдельности, а целиком всю структуру, для этого мы определяем адрес ячейки оперативной памяти, с которого начинается наша структура, и целиком копируем его во flash. Чтение данных происходит в обратном порядке.
Ну а теперь примеры:
Хочется обратить Ваше внимание на строчку, в которой происходит запись во flash-память:
В одном случае адрес увеличивается на «i*4», а в другом просто на «i» — Это не опечатка: в первом случае у нас переменная определена как «uint32_t», а в другом как ссылка на ячейку памяти размером «uint32_t». Соответственно в первом случае мы изменяем число на четыре байта, а во втором — изменяем на единицу ссылку на ячейку памяти размеров в 4 байта.
Теперь посмотрим на то, как выглядит заполнение нашей структуры данных из Flash:
Здесь все тоже самое, что и в предыдущем листинге, с той лишь разницей, что мы читаем записанные значения в данные нашей структуры.
Блокировка чтения / записи Flash
Подошло время ознакомится с защитой данных от чтения и/ или записи.
В какой-то момент времени у разработчиков наступает момент, когда хочется спрятать какой-либо код или данные от посторонних глаз или защитить от изменения свою прошивку. Выполнить это можно несколькими способами, рассмотрим два из них: программно или через стандартную утилиту «STM32 ST-LINK Utility».
Сразу оговорюсь, защита от чтения, это не панацея — в сети полно сайтов, которые предлагают прочитать прошивку не повредив данные. Каким то образом, неизвестным мне (пока )))), они снимают бит защиты от чтения и блокируют стирание flash. Поэтому в «крутых» прошивках, защищенных от чтения, присутствует еще и дополнительная защита, позволяющая избежать клонирования и декомпиляции кода.
Установка защиты чтения и записи данных устанавливается с помощью «Options Byte», находящихся в информационном блоке Flash. В этой статье подробно расписывать работу не буду, остановлюсь лишь на ключевых моментах.
Защита от чтения реализована почти гениально: устанавливается бит запрета чтения flash (RDP — Read Protection). Если бит снимается, то все содержимое Flash стирается. Установить бит и снять его можно как программно, так и с помощью сторонних утилит.
Защита от записи реализовано постранично, блоками по 4 килобайта: для микроконтроллеров с размером страницы равным одному килобайту, блокируется сразу по четыре страницы памяти, для МК с размером памяти равным двум килобайтом — блокируется по две страницы. Защита осуществляется в ячейках памяти «Options Byte» с помощью изменения битов WRPn (Write Protection).
Программное исполнение.
Самый простой способ защитить прошивку от чтения, это воспользоваться стандартной библиотекой от STM. Код будет выглядеть приблизительно так:
Как видите, ничего сложного нет.
Дефайн «DEBUGMODE» определяем в том случае, если нам надо отладить прошивку, иначе, при включенной защите от чтения, пройтись отладчиком по прошивке не получится.
Если установлена защита от чтения, но не установлена защита от записи, то мы не можем только прочитать прошивку, а залить новую — без проблем.
Кстати некоторое наблюдение, защиту от чтения можно не только установить, но и снять через программный код. Зачем это нужно — решать Вам ))).
С чтением все понятно, для чего оно нужно — тоже, а вот зачем нужна защита от записи? Первое, наверное, что приходит в голову, это наш автозагрузчик, чтобы случайно при прошивке микроконтроллера, мы не затерли свой Bootloader.
Вот как выглядит процедура установки защиты от записи на первые 8 килобайт памяти:
Данные о защищенных страницах памяти хранятся в битах Operation Byte. Защищенными считаются те страницы, бит которых сброшен. Если бит установлен, то страница доступна для записи! Принцип работы с Option Byte такой же, как и со всей Flash — перед установкой значения, его необходимо очистить, при этом все биты устанавливаются в 1.
Хочу обратить Ваше внимание на проверку условия «if(ProtectedPages != 0x00)» — условие верное, мы сравниваем ProtectedPages, а не WRPR_Value. В ProtectedPages установлены биты только тех блоков памяти, которые нам нужны и они не защищены от записи. А в WRPR_Value установлены биты защищенных блоков памяти.
Также, необходимо проверить, вдруг уже установлено то, что нам нужно и тогда не будем «мучать» Flash и перезаписывать данные.
Кстати, не пытайтесь менять значение Option Byte «на лету». Чтение этого блока происходит при перезагрузке микроконтроллера и сохраняется в регистрах FLASH_OBR и FLASH_WRPR, поэтому изменять значение в процессе выполнения кода без перезагрузки МК бессмысленно — сохранено будет последнее установленное Вами значение.
Модифицированная функция установки/снятия защиты от записи Flash.
По просьбе Serg, привожу пример функции установки или сброса защиты от записи. Почему-то разработчики от STM постеснялись добавить эту функцию в библиотеку для линейки МК STM32F1xx, хотя для других линеек МК такой (похожий) функционал присутствует. Основное ее отличие от родной функции в том, что перед записью в регистр Operation Byte необходимо производить его очистку, соответственно при использовании родной функции происходит сброс всех настроек защиты, а в моей функции реализовано изменение отдельных бит, что позволяет более гибко управлять защитой, хотя и пагубно влияет на МК, так как количество циклов перезаписи для Flash гарантировано в пределах 100000 циклов. (Даже на тестовых МК я еще ни разу не столкнулся с тем, что у меня количество циклов перезаписи перевалило за сотню тысяч )))).
Само тело функции желательно разместить в модуле «stm32f10x_flash.c» после функции «FLASH_EnableWriteProtection()»:
И необходимо добавить определение функции в модуле «stm32f10x_flash.h» также после функции «FLASH_EnableWriteProtection()», иначе Вы не сможете вызвать ее из свей программы:
Сама функция является переработанной функцией от STM «FLASH_EnableWriteProtection()»:
- добавлен параметр «NewState», который отвечает за установку или снятия флага защиты;
- в теле функции добавлена проверка на значение этого параметра и, в зависимости от него, устанавливаются или сбрасываются соответствующие биты.
- сброс OperationByte производится не перед вызов функции, а внутри нее. Это связано с особенностью записи во Flash, поэтому сначала запоминаются предыдущие значения OperationByte, производится очистка OperationByte, а затем уже записываются новые значения с учетом предыдущих.
Таким образом можно заменить по своему коду вызовы «FLASH_EnableWriteProtection(ProtectedPages)» на «FLASH_SetWriteProtection(ProtectedPages, ENABLE)» для включения защиты, или «FLASH_SetWriteProtection(ProtectedPages, DISABLE)» для ее отключения. Не забудьте в этом случае убрать очистку OperationByte с помощью функции «FLASH_EraseOptionBytes()», так как она сбросит все раннее установленные флаги защиты от записи.
Использование утилиты STM32 ST-LINK Utility.
Вы можете защитить свою прошивку от чтения и записи с помощью стандартной утилиты от STM. Скачать ее можно на официальном сайте, пройдя несложную регистрацию.
Из всех возможностей утилиты, мы обратимся только к работе с областью данных микроконтроллера, известной как «Option Byte».
Попасть в этот функционал программы можно либо комбинацией кнопок «Ctrl+B», либо через меню «Target» / «Option Byte». Перед Вами откроется похожее окно:
Нас сейчас интересуют всего два раздела: чтение и запись. Здесь Вы можете включить или выключить защиту от чтения и галочками установить страницы, которые хотите защитить от записи. Действия аналогичны описанным выше, поэтому описывать подробно не буду.
Не знаю почему, но если Вы установили защиту от записи, то после снятие этой защиты через утилиту, автоматически выставляется флаг защиты от чтения. а снятие флага защиты от чтения полностью стирает Flash.
Заключение
В этой статье я постарался кратко рассказать как можно использовать Flash в своих целях, как сделать свой автозагрузчик (Bootloader) и как защитить своий код. Все примеры в статье протестированы и готовы к использованию. На этот раз вложение добавлять не буду — весь материал содержит небольшой объем кода и легко воспринимается в тексте.
AVR — Запись данных во флеш память
Встроенная EEPROM стремительно заканчивается, а данные куда-то записывать надо. Знакомая ситуация, не правда-ли?
Что мы обычно делаем в таких случаях? Ставим внешнюю EEPROM, флеш или SD карточку на 32 гига. Это оправдано, если устройство достаточно сложное. А если оно состоит из одной тиньки и двух с половиной светодиодов? Тогда подключение внешней памяти грозит кардинальными изменениями в алгоритме, а может и пинов банально не хватит.
Но ведь у нас есть своя флеш память, которая в подавляющем большинстве случаев заполнена чуть менее, чем на половину. Отлично! Её и используем для записи данных.
Механизм записи во флеш память на первый взгляд немного запутанный. Но если присмотреться — все просто. Он состоит из трех основных частей:
— Очистка страницы памяти.
— Подготовка (закидываем данные во временный буфер)
— Запись.
Причем первые два пункта можно невозбранно менять местами.
Флеш память разделена на страницы. Размер страницы зависит от общего объема флеш памяти — смотри в даташите.
Например, для тини13 есть такая табличка:
Размер страницы 32 байта (16 слов), а всего таких страниц 32 штуки.
Так-же там написано, что в регистре адреса первые 4 бита (PC[3:0]) занимает адрес слова, а адрес страницы начинается с 5го бита. Значит, если нам надо записать адрес 3й страницы, то в регистр уйдет 3<<4.
При операциях со страницами (запись и очистка) от нас будут требовать номер страницы. А при работе со временным буфером — адрес слова в странице. К счастью, МК сам выделяет нужные части из адреса, который мы кидаем в регистр Z — поэтому можно не задумываться и пихать просто адрес слова в памяти.
При прошивке МК надо включить фьюз SELFPRGEN. Без этого ничего работать не будет.
1) Расчистка места.
Для очистки страницы памяти надо выполнить такую последовательность действий:
— Пихаем в регистр Z адрес. Из него МК выделит адрес страницы, а на первые 4 бита забьет.
— Поднимаем в регистре SPMCSR биты PGERS (Page Erase) и SELFPRGEN (Self Programming Enable).
— Быстро (в течении 4х тактов после записи в SPMCSR) выполняем команду SPM.
На время очистки страницы (а это около 4 мс) процессор подвисает.
2) Готовим данные к записи.
— Записываем в регистр Z адрес слова. Теперь все наоборот. МК будет ориентироваться по первым 4 битам адреса.
— В пару регистров R1:R0 пишем данные, которые хотим запихать в буфер.
— Поднимаем бит SELFPRGEN в регистре SPMCSR.
— Выполняем команду SPM.
… повторить, пока не будет заполнен весь буфер.
У этого буфера есть одна нехорошая особенность:
Записать два раза в одну ячейку нельзя. Т.е. если мы хотим переписать уже записанные в буфер данные, то его придется сначала очистить. Для очистки надо просто поднять бит CTPB в SPMCSR.
Алсо, буфер очищается сам после записи во флеш или перезагрузки.
Забавно так-же то, что данные в буфере будут потеряны, если записать что-то в EEPROM. Как-то хитро память устроена, не находите?
Последнее особенно актуально, если мы собираемся наполнять буфер постепенно, прерываясь на другие задачи (типа записи в EEPROM).
3) Пишем!
— Запихиваем адрес страницы в Z
— Устанавливаем биты SELFPRGEN и PGWRT.
— Выполняем SPM.
При записи страницы МК зависает на те-же 4 мс.
Пример устройства.
Для примера я хотел замутить термологгер на базе тини25. С записью температуры в флеш, работой со встроенным термометром и выдачей лога в UART. Но выяснилось, что UART у тини25 нету, а «родной» термометр уж больно кривой. Поэтому будем делать что по-проще. Например, записывать во флеш память напряжение, измеренное АЦП.
Алгоритм дешевого и сердитого логгера такой:
0) Инициализация периферии. Устанавливаем адрес записи на первую пустую страницу.
1) Ждем 1 сек — простым циклом, безо всяких таймеров и прерываний. Дешево и сердито.
2) Запускаем АЦП и ждем, пока он закончит преобразование.
3) Если мы начали писать новую страницу, то
3.1) Очишаем её.
4) Записываем во временнный буфер (по текущему адресу) значение АЦП.
5) Если мы уже заполнили весь буфер, то
5.1) Записываем его во флеш память.
6) Инкрементируем адрес записи.
7) Если дошли до конца памяти — затупляем в вечном цикле. Иначе идем на шаг 1.
Код на ассемблере:
Проверять код в протеусе бесполезно — не работает
Но в железе все прекрасно крутится.
Небольшое дополнение: RWW и NRWW
В МК с поддержкой бутлоадера на команду SPM накладываются ограничения. Её можно выполнить только из BLS (Bootloader section) — области памяти в самом конце флеша, где живут бутлоадеры. Размер этой области устанавливается фьюзами BOOTSZ.
Вообще, в контроллерах с поддержкой бутлоадера, весь флеш поделен на 2 части: RWW (Read-While-Write) и NRWW (No Read-While-Write). NRWW распологается в конце памяти и занимает место отведенное под бутлоадер. Т.е. размер этой области памяти равен максимальному размеру BLS. Остальное место (с начала флеша и до NRWW) занимает RWW область.
Разница между RWW и NRWW заключается вот в чем: Если бутлоадер (который находится в NRWW) пишет или стирает страницу в RWW области, то МК не останавливается на время выполения этой операции. Бутлоадер будет продолжать работать, пока страница памяти в RWW записывается или стирается. По этому поводу даже придумали прерывание SPM_RDY, которое возникает по завершении операции.
А если бутлоадер попытается записать данные в секцию NRWW (это не обязательно должна быть секция самого бутлоадера), то МК замрет на 4мс, пока производится запись.
STM32 — память
В статье рассказано про организацию памяти микроконтроллера stm32, использования flash’а для хранения пользовательских данных, и про всякие пользовательские биты/биты защиты.
Конечно же в вашем микроконтроллере нет никаких гигабайтов памяти однако камень 32-х битный, а значит можно адресовать (обратится по адресу) до 4ГБ. Это стандартизированная модель обеспечивающая переносимость кода между различными микроконтроллерами stm32, а так же дающая возможность подключать внешнюю память и совершенно спокойно обращаться к ней. То же самое касается и периферии, производитель микроконтроллеров на базе ядра Cortex, может напихать туда оооочень много всего и при этом не беспокоиться о нехватке адресов. Что же касается размера, то некоторых адресов просто нет, вместо них зарезервированные пустоты. Если обратится к зарезервированной области, то произойдёт аппаратный сбой процессора.
Для примера часть адресов блока Peripheral микроконтроллера F103…
Reference manual — rm0008 стр. 51
Тут видны адреса некоторых таймеров, портов GPIO, и другой периферии. Все они находятся выше 0x40000000 и ниже 0x5fffffff.
Если попробовать прочитать зарезервированный адрес…
То получим Hard Fault…
Прежде чем продолжать, надо скачать программу STM32CubeProgrammer с помощью которой можно ковыряться в микроконтроллере. Прога хороша тем, что есть варианты для и
Запускаем и жмём Connect, подключатся можно через ST-Link или UART…
Нажимаем верхнюю левую кнопку (с карандашиком), и на вкладке Device memory видим программу залитую в камень. Можно указать адрес, с которого читать (по умолчанию стоит начальный адрес флеш-памяти) и сколько байт прочитать. Кнопочка Read считывает данные и показывает как на картинке. Если вместо Read выбрать Save As, то программа сдампится в указанный файл (надо только размер считываемых данных правильно указать, а то по умолчанию 1К).
Если нажать вкладку Open file, то можно открыть какой-нибудь .hex или .bin файл прямо в программе.
Кнопка открывает интерфейс для загрузки прошивки в МК…
Выбираем нужный файл, очищаем флеш полностью (Full chip erase) или выборочные страницы, и жмём Start Programming.
— что-то связанное с внешним загрузчиком или внешним девайсом, не знаю, не вникал.
— очищает флеш.
В нижней части лог подключения, а справа-снизу полезная информация о вашем МК. Здесь интересны два пункта…
Device — серия микроконтроллера и плотность (Medium-density).
Некоторые микроконтроллеры stm32, в рамках одной серии, например популярный F103, он же BluePill, выпускается не только в разных корпусах с разным количеством «ножек», но и разной плотности (объёмом флеш-памяти).
Connectivity line devices — это, на сколько я понимаю, микроконтроллеры которые умеют работать с интернетом. Programming manual — PM0075 стр. 5.
Device ID — код, который зашит в системный бутлоадер, и по нему ST-Link определяет что это за микроконтроллер.
Коды можно посмотреть в AN2606 стр. 307.
Теперь нажмём кнопочку чтоб появился список Option bytes…
… и вернёмся к изучению памяти.
Всё что выше Peripheral особого интереса не представляет, SRAM — это оперативная память, а Code Area выглядит так…
Option bytes — здесь находятся различные биты для настройки МК. Защита от чтения/записи, включение/отключение вачдогов, и несколько пользовательских битов для хранения какой-либо инфы.
Бит RDP — если установить, то нельзя будет ни прочитать прошивку, ни загрузить новую. Чтобы установить, нужно поставить галочку, нажать кнопку Apply и обресетить МК. Снимается так же. При снятии защиты, существующая в МК прошивка будет удалена. Такой механизм гарантирует невозможность прочесть прошивку в залоченом камне.
В более серьёзных камнях RDP имеет несколько уровней…
AA — нет защиты.
BB — защита включена.
СС — микроконтроллер безвозвратно заблокирован от чтения/записи. Warning! Если установите этот уровень защиты, то больше никогда не сможете прошить МК. Операция необратима.
Тут комментировать особо нечего, думаю и так всё понятно. Однако очень не рекомендую снимать галочку с вачдога — камень будет постоянно ресетится. Настройки применяются так же, галочку сняли/поставили, и кнопка Apply. В более мощных МК есть доп. настройки.
Сюда можно сохранить какие-то свои данные, правда не много
Защита от записи конкретных страниц флеш-памяти. К этому вернёмся чуть позже.
System memory — это область в которой располагается системный загрузчик (bootloader), он зашивается на заводе при изготовлении, его нельзя ни удалить, ни изменить.
Bootloader это такая небольшая программка, которая позволяет прошивать микроконтроллер через различные интерфейсы. Если подтянуть пин BOOT_0 к «земле» и нажать ресет, то загрузиться bootloader и будет ожидать поступления новой прошивки. Простенькие камни, типа F103, можно прошивать только через USART, а более «крутые» позволяют делать это и через другие интерфейсы. Посмотреть это можно в AN2606 стр. 25. Например вот…
Flash — это область энергонезависимой памяти, в которой хранится ваша прошивка. Выглядит эта область следующим образом…
RM0008 стр. 55. (Рис. 1)
Information block — это описанные выше System memory и Option Bytes.
Flash memory interface registers — это регистры отвечающие за работу Flash-памяти, они находятся в области Peripheral .
Main memory — это и есть наша Flash-память.
Flash -память организована постранично, а размер страницы колеблется от одного до нескольких килобайт, в зависимости от серии микроконтроллера. У F103 (Рис. 1) размер равен 1К, а вот например у F4 выглядит так…
Часть страниц по 16К, одна 64К, а часть по 128К. Здесь страницы называются секторами.
OTP area — это биты защиты, которые можно записать только один раз (One-Time-Programmable). Больше ничего про это сказать не могу — у меня нет такой платы.
Тут есть один любопытный момент. Компания ST выпускает два почти одинаковых камня F103, один F103C8хх c объёмом флеш-памяти 64К (BluePill), и F103CBхх c объёмом 128К. Однако на самом деле у обоих этих камней объём флеш-памяти равен 128К (128 страниц по 1К).
Во-первых в мануале нет камней с 64-мя страницами (см. рис. 1), а во-вторых, не смотря на то, что ST-Link показывает объём 64К, у всех моих F103C8хх читаются и пишутся все 128 страниц. То есть программу размером больше 64К залить через TrueStudio не удаётся, а вот производить чтение/запись этих областей из своей программы можно. Да и STM32CubeProgrammer их тоже совершенно спокойно читает…
127-я страница на камне F103C8хх.
Выше я говорил что мы вернёмся к вопросу защиты конкретных страниц от чтения/записи…
Биты WRPх защищают сразу по несколько страниц. Количество защищаемых страниц зависит от плотности (density) МК.
Вот вырезка из Programming manual стр. 21…
У low-density используется только WRP0 и защищает весь флеш, а у medium-density можно делать это выборочно. Зачем нужны остальные биты (WRP4 и т.д.) я так и не понял, может они зарезервированы.
Такой способ выборочной защиты очень удобен тем, что можно заблокировать область где находится программа, а остальное использовать для хранения каких-то данных и не боятся затереть программу, ну или наоборот, защитить какие-то данные, которые вы положили например в конец флеша.
CCM SRAM — у некоторых микроконтроллеров есть дополнительная оперативная память под названием Core Coupled Memory. Эта память подключена непосредственно к ядру, благодаря чему скорость доступа и исполнение кода будет быстрее чем в обычной SRAM. Соответственно в CCM выгодно размещать всякие статистические переменные/массивы, а при желании можно перенести туда стек/кучу/.data/.bss.
Cледует помнить, что доступ к CCM имеет только процессор, а значит DMA не сможет к ней обратиться. CCM бывают разные для разных микроконтроллеров, поэтому прежде чем начинать использование следует изучить документацию. Ещё инфа, и ещё инфа.
В качестве примера я перенёс пару массивов в ССМ (среда TrueStudio, камень F303)…
Объявляем массивы глобально:
Чтоб компилятор не «оптимизировал» эти массивы, сделайте что-нибудь с ними, например выведите на печать.
Вуаля, они лежат где надо…
Прежде чем говорить о переносе стека/кучи/.data/.bss в ССМ, я очень кратко и обобщённо поясню что означают эти термины:
Стек — область памяти в ОЗУ, куда сохраняется адреса программы в момент возникновения прерывания или перехода в другую функцию.
Пошагово выглядит это так:
Наша программа работает-работает (указатель «бежит» последовательно по адресам).
Происходит прерывание — указатель перепрыгивает в обработчик этого прерывания.
Адрес, где находилась программа в момент возникновения прерывания, записывается в стек (чтоб запомнить куда возвращаться).
После того как обработка прерывания закончится, из стека вытаскивается адрес на который нужно вернуться.
То же самое происходит при переходе из одной функции в другую, например из в
Технически, стек можно представить в виде стопки книг где нельзя взять вторую сверху книгу не сняв предварительно первую. То есть стек работает по принципу LIFO буфера (Last In First Out) — «последним пришёл, первым вышел». Такая организация очень хорошо себя оправдывает. Представьте себе такую ситуацию:
Указатель перепрыгнул из функции main() в bla_bla() .
В стек записался адрес того места откуда выпрыгнул указатель.
Идёт обработка функции bla_bla() и в этот момент происходит прерывание.
В стек записывается (ложится поверх предыдущего) ещё один адрес, и указатель переходит в обработчик прерывания.
В результате на стеке лежат два адреса — адрес, на котором была приостановлена функция main() , а поверх него адрес на котором приостановлена функция bla_bla() .
По окончании обработки прерывания система лезет в стек, «снимает со стопки» самый последний адрес, переходит по нему и заканчивает выполнение функции bla_bla() с того места где она была прервана. Больше этот адрес не нужен и он выкидывается в помойку, на стеке остаётся адрес приостановки функции main() .
Как только функция bla_bla() завершается, система опять лезет в стек и «снимает» адрес приостановки функции main() . Адрес опять же летит в помойку, а функция main() продолжает выполняться. Таким образом, снимая «книжку за книжкой» система не сможет вернуться куда-то не туда и нарушить целостность программы.
Помимо адресов, в стек сохраняются локальные переменные функций и параметры передающиеся в функции. То есть в момент вызова функции, вместе с адресом для возврата, на стек кладутся ещё и локальные переменные, которые есть в этой функции. Как только функция отработает, эти переменные будут сняты со стека и уничтожены.
Куча — эта область ОЗУ, которая используется для динамического выделения памяти в процессе работы программы. То есть, когда вы делаете malloc/calloc/realloc, то память выделяется на куче.
bss — область ОЗУ, сюда помещаются не инициализированные глобальные переменные (uint8_t var;).
data — область ОЗУ, сюда помещаются инициализированные глобальные переменные (uint8_t var = 0;).
text — Flash, здесь лежит сама программа.
Выглядит всё это хозяйство следующим образом…
Стек начинается от самого большого адреса и при увеличении размера ползёт вниз (записывая данные в меньшие адреса), а куча наоборот, при увеличении размера ползёт вверх, поэтому при выделении памяти на куче нужно следить чтоб эти области не налезли друг на друга.
Чтоб лучше понять иллюстрацию выше, откройте файл STM32F103C8_FLASH.ld и найдите там такие строки…
Нас интересует переменная _estack , которая указывает на конец оперативной памяти. У микроконтроллера stm32F103c8 оперативная память располагаться в области от 0x20000000 до 0x20005000 , что равно 20480 байтам (20Кб). Визуально это выглядит так…
Вся наша оперативка находится внутри узенькой полоски, а всё что выше не существует. То есть стек, куча, bss и data находится внутри этой полоски, а text в жёлтой области.
Посмотреть сколько места занимают некоторые данные можно в среде разработки. Вот картинка из TrueStudio…
dec и hex это общий размер первых трёх значений.
Чтоб перенести какую-то из этих областей в ССМ, нужно подкорректировать файл компоновщика (или линковщика, называйте как вам удобнее) с расширением .ld …
Находим там интересующие нас блоки…
Меняем у каких-нибудь блоков, или у всех сразу, слово RAM на CCMRAM …
В результате получаем — до:
Запись данных во флеш
У большинства микроконтроллеров stm32 (кроме серии L0) нету EEPROM, поэтому сохранять пользовательские данные приходится во флеш-памяти. Количество циклов записи 10000.
Чтобы записать во флеш какие-то данные нужно вначале её очистить (во время очистки все биты заполняются единицами — 0xFF). Очистка происходит постранично/посекторно.
Очистка одной страницы на F103…
Объявляем структуру в которую заносятся параметры очистки:
TypeErase — что хотим очистить, какую-то конкретную страницу (или несколько), или всю флеш полностью.
PageAddress — начальный адрес страницы, которую хотим очистить. Адреса можно посмотреть в мануале (правда там не все видны), либо посчитать самостоятельно, либо в примерах Куба — STM32CubeFx в файле main.h (для F103 — /STM32Cube_FW_F1_V1.8.0/Projects/STM32F103RB-Nucleo/Examples/FLASH/FLASH_EraseProgram/Inc/main.h). В конце есть ссылка на гитхаб с этим примером, там есть хедер (addr_pages.h) с задефайнеными адресами для BluePill.
NbPages — кол-во страниц для очистки. Если указать несколько, то они будут очищены начиная с адреса указанного выше.
Banks — у «жирных» камней память делиться на банки, поэтому нужно указать в каком именно банке находятся страницы.
HAL_FLASH_Unlock() — снимаем блокировку стирания/записи во флеш (для чтения этого делать не нужно). Это не относится к битам WRPx, если они установлены для текущей страницы, то стереть/записать страницу не получится.
Ну, а дальше стираем страницу/страницы, и возвращаем блокировку. Если что-то пойдёт не так, то программа выведет ошибку и зациклится.
После этой операции вся страница готова для записи. Необязательно записывать всю страницу за один раз, можно добавлять записи по мере необходимости, при условии что новые данные не будут записываться поверх старых.
Почему нужно очищать (заполнять значениями 0xFF) память?
Дело в том, что когда происходит запись байта в ячейку, то биты в этой ячейки не просто так берут и перезаписываются, а совершается операция логического «И» над тем что есть в ячейке и новым значением.
Наглядно это выглядит так. Ячейка у нас очищена (биты заполнены единицами) и мы записываем в неё число 7 (0х07)…
Выполняется логическое «И», и в ячейку записывается нужное нам число 7.
А теперь допустим что мы хотим записать в эту же ячейку число 13 (0x0d) поверх старого значения…
Выполняется логическое «И» между старым значением 0х07 и новым 0x0d. В результате вместо желаемого числа 13, в ячейку записывается число 5.
Запись
У разных МК можно записывать разную длину «слова», например у F103 можно записать «слова» размером 16, 32 и 64 бита. Подсмотреть это можно в файле stm32f1xx_hal_flash.h …
Запишем два 16-ти (FLASH_TYPEPROGRAM_HALFWORD) битных числа в начало только что очищенной страницы…
Разблокируем флеш, указываем адрес начала страницы (не обязательно начало, можно в любое место), записываем во флеш массив из двух чисел, а в цикле увеличиваем адрес на два. После этого блокируем память.
Если записывать 32-х (FLASH_TYPEPROGRAM_WORD) битное число, то увеличим адрес на четыре, а если 64-х (FLASH_TYPEPROGRAM_DOUBLEWORD) битное, то на 8. Если в дальнейшем захотите добавить в эту страницу ещё что-то, то нужно запомнить адрес.
Идём в STM32CubeProgrammer, вписываем адрес 127-ой страницы и смотрим чего понаписали…
Всё окей. Не забывайте нажимать Disconnect
Прочитаем то, что записали…
Разблокировать не надо, а адрес опять же увеличиваем на два. Получаем ожидаемое…
Чтоб записать 8-ми битное значение (не смотря на то, что производитель не предоставил такой возможности), надо просто записывать по два байта в одно 16-ти битное «слово» со сдвигом. Всё происходит почти так же как и с 16-ти битным числом…
Увеличиваем адрес на 4 (чтоб добавить данные вслед за предыдущими), записывать будем массив, размер которого должен быть кратен двум (для этого сделана проверка), а в цикле запихиваем два символа в одно 16-ти битное «слово» и отправляем по адресу.
Читать можно по одному 8-ми битному символу.
Вот и всё, остаётся придумать как хранить адрес (со смещением) последней записи для добавления последующих, чтобы не мучить одну и туже ячейку, и не прикончить её в самом скором времени. Вот вариант как смещать очередную запись.
Всем спасибо
Как записать переменную во флеш память 80c51f120
Здесь приведен перевод апноута AVR106: C functions for reading and writing to Flash memory [1], посвященного подпрограммам чтения и записи памяти FLASH на языке C для микроконтроллеров AVR. Рассмотрены функции чтения и записи одного байта FLASH, чтение и записи одной страницы FLASH, опциональное восстановление после неожиданного пропадания питания. Рассмотренные функции могут использоваться с любым устройством (микроконтроллером AVR), которое имеет возможность записи памяти программ из кода приложения [2] (это почти вся линейка AVR микроконтроллеров Atmel). Вместе с апноутом AVR106 приведен также проект примера программы [3], использующей секцию памяти программ для сохранения параметров.
Почти все микроконтроллеры AVR® компании Atmel имеют так называемую возможность самопрограммирования (Self programming Program memory). Самопрограммирование является одной из особенностей технологии AVR. Чтобы лучше разобраться с этим, обратитесь к апноуту AVR109 [2]. Самопрограммирование позволяет AVR перепрограммировать собственную память FLASH во время работы программы. Это подходит для приложений, которые нуждаются в самостоятельном обновлении кода firmware (bootloader, загрузчики, бутлоадеры) или сохранении какой-либо информации в области памяти FLASH. Этот апноут предоставляет функции на языке C, которые позволяют получить доступ к области памяти FLASH.
[Ассемблерная инструкция SPM]
Память FLASH может быть запрограммирована с помощью инструкции SPM (Store Program Memory). На устройствах, поддерживающих самопрограммирование, память программ FLASH часто делится на 2 основные секции: секция основной программы (память приложения, Application Flash Section) и секция загрузки (Boot Flash Section). Прим. переводчика: секция основной программы начинается с адреса 0x0000, а секция загрузки занимает маленький блок памяти обычно 2..4 килобайта в конце FLASH.
На устройствах, которые имеют блок памяти загрузки (boot block), инструкция SPM имеет возможность записи во всю область памяти FLASH, но только в том случае, если она выполняется из области памяти загрузки. Выполнение SPM из секции приложения не дает никакого эффекта. На младших устройствах AVR, к которых нет блока загрузки, инструкция SPM может работать из любого места памяти FLASH.
Во время записи FLASH в секции загрузки работа CPU всегда останавливается. Однако большинство устройств могут выполнить код (чтение) из секции загрузки, когда происходит запись области памяти приложения (Application section). Очень важно, чтобы код, выполняемый при записи Application section, не пытался читать из Application section. Если это вдруг произойдет, то может быть нарушено общее выполнение программы.
Размер и область размещения этих двух областей памяти FLASH зависит от типа устройства и установки его фьюзов. Некоторые устройства имеют возможность выполнять инструкцию SPM из любого места области памяти FLASH.
[Процедура записи FLASH]
Технология памяти FLASH устроена так, что записывается эта память постранично. Запись осуществляется путем сохранения всей страницы во временном буфере страницы, перед тем, как страница будет записана во FLASH. Адрес, по которому будет записаны данные страницы, определяется по содержимому Z-регистра и RAMPZ-регистра. Страница должна быть очищена (erase) перед тем, как на страницу могут быть записаны данные из временного буфера. Функции, которые содержит этот апноут, используют следующую процедуру для записи страницы FLASH:
• Заполнение буфера страницы
• Очистка страницы
• Запись страницы
Как можно заметить, возможна потеря данных, если во время этой процедуры неожиданно произойдет сброс или пропадание питания сразу после очистки страницы. Потери данных можно избежать, если предварительно применить буферизирование записываемых данных в энергонезависимой памяти (nonvolatile memory, обычно EEPROM). Функции записи, содержащиеся в этом апноуте, предоставляют опциональное буферизирование при записи. Эти функции далее рассматриваются в секции «Описание firmware». Для устройств, которые имеют фичу read-while-write (чтение во время записи), бутлоадер может выполняться во время записи, и из функций не произойдет возврат, пока запись не завершится.
[Адресация FLASH]
Память FLASH в AVR поделена на 16-битные слова. Это означает, что каждый адрес FLASH означает ячейку из 2 байт данных. Для ATmega128 можно адресовать до 65k слов или 128k байт данных FLASH. В некоторых случаях память FLASH упоминается адресованной словами, и в других случаях — адресованной побайтно, что вносит некоторую путаницу. Все функции, содержащиеся в этом апноуте, используют байтовую адресацию. Соотношение между байтовым адресом и адресом слова следующее:
Байтовый адрес = Адрес слова * 2
Страница FLASH адресуется с использованием адреса байта для первого байта на странице. Соотношение между номером станицы (0, 1, 2, . ) и адресом байта следующее:
Байтовый адрес = номер страницы * размер страницы (в байтах)
Пример байтовой адресации: размер страницы FLASH у ATmega128 равен 256 байт. Байтовый адрес 0x200 (512) укажет на следующее:
• Байт FLASH 0x200 (512), что соответствует байту 0 на странице 2
• Страница 2 FLASH
Когда адресуется страница ATmega128, младший байт адреса всегда 0. Когда адресуется слово, то младший бит (LSB) адреса всегда 0.
[Реализация проекта примера, работающего с FLASH]
Проект firmware сделан для компилятора IAR. Функции могут быть портированы на другие компиляторы, но могут потребоваться некоторые усилия, поскольку используется специфическая мнемоника (intrinsic functions) компилятора IAR. Функции доступны через подключение файла Self_programming.h в главный модуль C, и добавление модуля Self_programming.c к проекту. Когда используется Self-programming (самопрограммирование), то важно, чтобы функции для записи размещались в пределах секции загрузки (Boot section) памяти FLASH. Размещение функций управляется путем использования определений сегмента памяти в файле конфигурации линкера (*.xcl). Все другие необходимые конфигурации, касающиеся firmware, сделаны в файле Self_programming.h.
PAGESIZE. Константа PAGESIZE должна быть определена равной размеру страницы FLASH (в байтах) используемого устройства.
__FLASH_RECOVER. Определение константы __FLASH_RECOVER разрешает опцию восстановления Flash, чтобы избежать потери данных при случайных пропаданиях питания. Когда восстановление разрешено, одна страница Flash выделена под буфер восстановления. Значение __FLASH_RECOVER будет определять адрес страницы FLASH, которая используется для этого. Адрес должен быть байтовым, указывающим на начало страницы FLASH, и функции записи не смогут записывать в эту страницу. Восстановление FLASH происходит вызовом функции RecoverFLASH() при старте программы (program startup).
ADR_LIMIT_LOW и ADR_LIMIT_HIGH. Диапазон памяти, в котором функциям разрешено записывать FLASH, задается константами ADR_LIMIT_LOW и ADR_LIMIT_HIGH. Функции могут записывать по адресам больше или равным ADR_LIMIT_LOW и меньше или равным ADR_LIMIT_HIGH.
[Размещение всего кода внутри секции загрузки]
Необходимо перезадать диапазон сегментов, определенных в файле настройки линкера по умолчанию (файл *.xcl), чтобы разместить весь код приложения в секции загрузки (Boot section) FLASH. Размещение и размер Boot section зависит от устройства и установок его фьюзов. Программирование фьюза BOOTRST перенесет вектор сброса в начало секции загрузки. Также можно перенести все вектора прерывания в секцию загрузки. Обратитесь к секции даташита на устройство, посвященной прерываниям, за инструкциями, как это сделать. Определения сегмента, которые должны быть изменены для размещения всего программного кода в секции загрузки:
TINY_F, NEAR_F, SWITCH, DIFUNCT, CODE, FAR_F, HUGE_F, INITTAB, TINY_ID, NEAR_ID и CHECKSUM.
В этом апноуте предоставляется в качестве примера файл lnkm128s.xcl, где задано размещение всего кода программы в 8 килобайт секцию загрузки Atmega128. Этот файл можно просто модифицировать для использования с другими устройствами, и в нем содержатся инструкции, как это осуществить.
[Размещение в секции загрузки только некоторых функций]
В качестве альтернативы можно разместить только некоторые функции в заданном сегменте памяти FLASH. В нашем случае в секции загрузки нужно разместить только функции записи. Это можно сделать заданием нового сегмента FLASH, эквивалентного области памяти загрузки, и использование оператора @ для размещения нужных функций в этом сегменте. Оператор @ не распространяется на функции, которые вызываются внутри функций, помеченных @.
Определение сегмента загрузки (Boot segment) в файле *.xcl для ATmega128 с размером 8 килобайт:
1. Сделайте новое определение размера секции загрузки.
2. Задайте новый сегмент для всей секции загрузки, основанный на определении шага 1.
Размещение функций C в заданном сегменте:
Пример кода на языке C, приведенный выше, разместит функцию ExampleFunction() в определенный сегмент памяти BOOT_SEGMENT.
[Описание firmware]
Код firmware состоит из 5 функций на языке C, и один проект примера для IAR версии 2.28a / 3.10c для ATmega128. Проект примера сконфигурирован так, чтобы разместить код всего приложения в секцию загрузки (Boot section) памяти FLASH. Этот код можно использовать как стартовую точку Вашей программы, которая может записывать FLASH. В таблице показаны функции, которые осуществляют доступ к памяти FLASH.
Функция | Аргументы | Возвращает |
ReadFlashByte() | MyAddressType flashAdr | unsigned char |
ReadFlashPage() | MyAddressType flashStartAdr, unsigned char *dataPage | unsigned char |
WriteFlashByte() | MyAddressType flashAddr, unsigned char data | unsigned char |
WriteFlashPage() | MyAddressType flashStartAdr, unsigned char *dataPage | unsigned char |
RecoverFlash() | void | unsigned char |
Тип данных MyAddressType определен в Self_programming.h. Размер этого типа зависит от используемого устройства. Для устройств, у которых FLASH больше 64 килобайт, он будет задан как long int, и int для устройств, у которых объем FLASH меньше или равен 64 килобайта (адрес байта 16-битный). Типы данных в реальности используются как указатели __flash или __farflash (соответственно 16 и 24 бит). Причина определения нового типа в том, что использование целочисленных типов более удобно, чем типов указателя.
ReadFlashByte() возвращает 1 байт, который размещен по адресу FLASH, указанном во входном аргументе.
ReadFlashPage() читает одну страницу памяти FLASH от адреса ucFlashStartAdr, и сохраняет данные в массиве pucDataPage[]. Количество сохраняемых байт зависит от размера страницы FLASH. Функция вернет FALSE, если входной адрес не является адресом страницы FLASH, иначе вернет TRUE.
WriteFlashByte() запишет байт ucData по адресу FLASH ucFlashAddr. Функция вернет FALSE, если входной адрес не является допустимым адресом FLASH для записи, иначе вернет TRUE.
WriteFlashPage() запишет данные из массива pucDataPage[] в страницу FLASH по адресу ucFlashStartAdr. Количество записываемых байт зависит от размера страницы FLASH. Функция вернет FALSE, если входной адрес не является допустимым адресом страницы FLASH для записи, иначе вернет TRUE.
RecoverFlash() прочитает переменную состояния в EEPROM, и восстановит страницу FLASH, если это необходимо. Функция должна быть вызвана в начале программы, если разрешена опция восстановления FLASH (__FLASH_RECOVER). Функция вернет TRUE, если имело место восстановление, иначе вернет FALSE.
[Как работает восстановление FLASH]
Когда разрешена опция восстановления FLASH, запись страницы вовлекает предварительное сохранение данных в выделенную страницу восстановления FLASH, перед тем как произойдет действительная запись во FLASH. Адрес записываемой страницы сохраняется в EEPROM совместно с байтом статуса, который показывает, что страница восстановления FLASH содержит данные. Этот байт состояния будет очищен, когда произойдет успешное завершение операции записи указанной страницы. Переменные в EEPROM и буфер восстановления FLASH используются функцией восстановления RecoverFlash(), чтобы восстановить данные, если это необходимо. Запись одного байта в EEPROM занимает примерно такое же время, как и запись всей страницы во FLASH. Так что нужно иметь в виду, что разрешение опции восстановления приведет к значительному увеличению времени записи. EEPROM используется вместо FLASH, потому что выделение нескольких байт в FLASH исключит гибкое использование всей страницы, которая будет содержать эти байты.
STM32 — память
В статье рассказано про организацию памяти микроконтроллера stm32, использования flash’а для хранения пользовательских данных, и про всякие пользовательские биты/биты защиты.
Конечно же в вашем микроконтроллере нет никаких гигабайтов памяти однако камень 32-х битный, а значит можно адресовать (обратится по адресу) до 4ГБ. Это стандартизированная модель обеспечивающая переносимость кода между различными микроконтроллерами stm32, а так же дающая возможность подключать внешнюю память и совершенно спокойно обращаться к ней. То же самое касается и периферии, производитель микроконтроллеров на базе ядра Cortex, может напихать туда оооочень много всего и при этом не беспокоиться о нехватке адресов. Что же касается размера, то некоторых адресов просто нет, вместо них зарезервированные пустоты. Если обратится к зарезервированной области, то произойдёт аппаратный сбой процессора.
Для примера часть адресов блока Peripheral микроконтроллера F103…
Reference manual — rm0008 стр. 51
Тут видны адреса некоторых таймеров, портов GPIO, и другой периферии. Все они находятся выше 0x40000000 и ниже 0x5fffffff.
Если попробовать прочитать зарезервированный адрес…
То получим Hard Fault…
Прежде чем продолжать, надо скачать программу STM32CubeProgrammer с помощью которой можно ковыряться в микроконтроллере. Прога хороша тем, что есть варианты для и
Запускаем и жмём Connect, подключатся можно через ST-Link или UART…
Нажимаем верхнюю левую кнопку (с карандашиком), и на вкладке Device memory видим программу залитую в камень. Можно указать адрес, с которого читать (по умолчанию стоит начальный адрес флеш-памяти) и сколько байт прочитать. Кнопочка Read считывает данные и показывает как на картинке. Если вместо Read выбрать Save As, то программа сдампится в указанный файл (надо только размер считываемых данных правильно указать, а то по умолчанию 1К).
Если нажать вкладку Open file, то можно открыть какой-нибудь .hex или .bin файл прямо в программе.
Кнопка открывает интерфейс для загрузки прошивки в МК…
Выбираем нужный файл, очищаем флеш полностью (Full chip erase) или выборочные страницы, и жмём Start Programming.
— что-то связанное с внешним загрузчиком или внешним девайсом, не знаю, не вникал.
— очищает флеш.
В нижней части лог подключения, а справа-снизу полезная информация о вашем МК. Здесь интересны два пункта…
Device — серия микроконтроллера и плотность (Medium-density).
Некоторые микроконтроллеры stm32, в рамках одной серии, например популярный F103, он же BluePill, выпускается не только в разных корпусах с разным количеством «ножек», но и разной плотности (объёмом флеш-памяти).
Connectivity line devices — это, на сколько я понимаю, микроконтроллеры которые умеют работать с интернетом. Programming manual — PM0075 стр. 5.
Device ID — код, который зашит в системный бутлоадер, и по нему ST-Link определяет что это за микроконтроллер.
Коды можно посмотреть в AN2606 стр. 307.
Теперь нажмём кнопочку чтоб появился список Option bytes…
… и вернёмся к изучению памяти.
Всё что выше Peripheral особого интереса не представляет, SRAM — это оперативная память, а Code Area выглядит так…
Option bytes — здесь находятся различные биты для настройки МК. Защита от чтения/записи, включение/отключение вачдогов, и несколько пользовательских битов для хранения какой-либо инфы.
Бит RDP — если установить, то нельзя будет ни прочитать прошивку, ни загрузить новую. Чтобы установить, нужно поставить галочку, нажать кнопку Apply и обресетить МК. Снимается так же. При снятии защиты, существующая в МК прошивка будет удалена. Такой механизм гарантирует невозможность прочесть прошивку в залоченом камне.
В более серьёзных камнях RDP имеет несколько уровней…
AA — нет защиты.
BB — защита включена.
СС — микроконтроллер безвозвратно заблокирован от чтения/записи. Warning! Если установите этот уровень защиты, то больше никогда не сможете прошить МК. Операция необратима.
Тут комментировать особо нечего, думаю и так всё понятно. Однако очень не рекомендую снимать галочку с вачдога — камень будет постоянно ресетится. Настройки применяются так же, галочку сняли/поставили, и кнопка Apply. В более мощных МК есть доп. настройки.
Сюда можно сохранить какие-то свои данные, правда не много
Защита от записи конкретных страниц флеш-памяти. К этому вернёмся чуть позже.
System memory — это область в которой располагается системный загрузчик (bootloader), он зашивается на заводе при изготовлении, его нельзя ни удалить, ни изменить.
Bootloader это такая небольшая программка, которая позволяет прошивать микроконтроллер через различные интерфейсы. Если подтянуть пин BOOT_0 к «земле» и нажать ресет, то загрузиться bootloader и будет ожидать поступления новой прошивки. Простенькие камни, типа F103, можно прошивать только через USART, а более «крутые» позволяют делать это и через другие интерфейсы. Посмотреть это можно в AN2606 стр. 25. Например вот…
Flash — это область энергонезависимой памяти, в которой хранится ваша прошивка. Выглядит эта область следующим образом…
RM0008 стр. 55. (Рис. 1)
Information block — это описанные выше System memory и Option Bytes.
Flash memory interface registers — это регистры отвечающие за работу Flash-памяти, они находятся в области Peripheral .
Main memory — это и есть наша Flash-память.
Flash -память организована постранично, а размер страницы колеблется от одного до нескольких килобайт, в зависимости от серии микроконтроллера. У F103 (Рис. 1) размер равен 1К, а вот например у F4 выглядит так…
Часть страниц по 16К, одна 64К, а часть по 128К. Здесь страницы называются секторами.
OTP area — это биты защиты, которые можно записать только один раз (One-Time-Programmable). Больше ничего про это сказать не могу — у меня нет такой платы.
Тут есть один любопытный момент. Компания ST выпускает два почти одинаковых камня F103, один F103C8хх c объёмом флеш-памяти 64К (BluePill), и F103CBхх c объёмом 128К. Однако на самом деле у обоих этих камней объём флеш-памяти равен 128К (128 страниц по 1К).
Во-первых в мануале нет камней с 64-мя страницами (см. рис. 1), а во-вторых, не смотря на то, что ST-Link показывает объём 64К, у всех моих F103C8хх читаются и пишутся все 128 страниц. То есть программу размером больше 64К залить через TrueStudio не удаётся, а вот производить чтение/запись этих областей из своей программы можно. Да и STM32CubeProgrammer их тоже совершенно спокойно читает…
127-я страница на камне F103C8хх.
Выше я говорил что мы вернёмся к вопросу защиты конкретных страниц от чтения/записи…
Биты WRPх защищают сразу по несколько страниц. Количество защищаемых страниц зависит от плотности (density) МК.
Вот вырезка из Programming manual стр. 21…
У low-density используется только WRP0 и защищает весь флеш, а у medium-density можно делать это выборочно. Зачем нужны остальные биты (WRP4 и т.д.) я так и не понял, может они зарезервированы.
Такой способ выборочной защиты очень удобен тем, что можно заблокировать область где находится программа, а остальное использовать для хранения каких-то данных и не боятся затереть программу, ну или наоборот, защитить какие-то данные, которые вы положили например в конец флеша.
CCM SRAM — у некоторых микроконтроллеров есть дополнительная оперативная память под названием Core Coupled Memory. Эта память подключена непосредственно к ядру, благодаря чему скорость доступа и исполнение кода будет быстрее чем в обычной SRAM. Соответственно в CCM выгодно размещать всякие статистические переменные/массивы, а при желании можно перенести туда стек/кучу/.data/.bss.
Cледует помнить, что доступ к CCM имеет только процессор, а значит DMA не сможет к ней обратиться. CCM бывают разные для разных микроконтроллеров, поэтому прежде чем начинать использование следует изучить документацию. Ещё инфа, и ещё инфа.
В качестве примера я перенёс пару массивов в ССМ (среда TrueStudio, камень F303)…
Объявляем массивы глобально:
Чтоб компилятор не «оптимизировал» эти массивы, сделайте что-нибудь с ними, например выведите на печать.
Вуаля, они лежат где надо…
Прежде чем говорить о переносе стека/кучи/.data/.bss в ССМ, я очень кратко и обобщённо поясню что означают эти термины:
Стек — область памяти в ОЗУ, куда сохраняется адреса программы в момент возникновения прерывания или перехода в другую функцию.
Пошагово выглядит это так:
Наша программа работает-работает (указатель «бежит» последовательно по адресам).
Происходит прерывание — указатель перепрыгивает в обработчик этого прерывания.
Адрес, где находилась программа в момент возникновения прерывания, записывается в стек (чтоб запомнить куда возвращаться).
После того как обработка прерывания закончится, из стека вытаскивается адрес на который нужно вернуться.
То же самое происходит при переходе из одной функции в другую, например из в
Технически, стек можно представить в виде стопки книг где нельзя взять вторую сверху книгу не сняв предварительно первую. То есть стек работает по принципу LIFO буфера (Last In First Out) — «последним пришёл, первым вышел». Такая организация очень хорошо себя оправдывает. Представьте себе такую ситуацию:
Указатель перепрыгнул из функции main() в bla_bla() .
В стек записался адрес того места откуда выпрыгнул указатель.
Идёт обработка функции bla_bla() и в этот момент происходит прерывание.
В стек записывается (ложится поверх предыдущего) ещё один адрес, и указатель переходит в обработчик прерывания.
В результате на стеке лежат два адреса — адрес, на котором была приостановлена функция main() , а поверх него адрес на котором приостановлена функция bla_bla() .
По окончании обработки прерывания система лезет в стек, «снимает со стопки» самый последний адрес, переходит по нему и заканчивает выполнение функции bla_bla() с того места где она была прервана. Больше этот адрес не нужен и он выкидывается в помойку, на стеке остаётся адрес приостановки функции main() .
Как только функция bla_bla() завершается, система опять лезет в стек и «снимает» адрес приостановки функции main() . Адрес опять же летит в помойку, а функция main() продолжает выполняться. Таким образом, снимая «книжку за книжкой» система не сможет вернуться куда-то не туда и нарушить целостность программы.
Помимо адресов, в стек сохраняются локальные переменные функций и параметры передающиеся в функции. То есть в момент вызова функции, вместе с адресом для возврата, на стек кладутся ещё и локальные переменные, которые есть в этой функции. Как только функция отработает, эти переменные будут сняты со стека и уничтожены.
Куча — эта область ОЗУ, которая используется для динамического выделения памяти в процессе работы программы. То есть, когда вы делаете malloc/calloc/realloc, то память выделяется на куче.
bss — область ОЗУ, сюда помещаются не инициализированные глобальные переменные (uint8_t var;).
data — область ОЗУ, сюда помещаются инициализированные глобальные переменные (uint8_t var = 0;).
text — Flash, здесь лежит сама программа.
Выглядит всё это хозяйство следующим образом…
Стек начинается от самого большого адреса и при увеличении размера ползёт вниз (записывая данные в меньшие адреса), а куча наоборот, при увеличении размера ползёт вверх, поэтому при выделении памяти на куче нужно следить чтоб эти области не налезли друг на друга.
Чтоб лучше понять иллюстрацию выше, откройте файл STM32F103C8_FLASH.ld и найдите там такие строки…
Нас интересует переменная _estack , которая указывает на конец оперативной памяти. У микроконтроллера stm32F103c8 оперативная память располагаться в области от 0x20000000 до 0x20005000 , что равно 20480 байтам (20Кб). Визуально это выглядит так…
Вся наша оперативка находится внутри узенькой полоски, а всё что выше не существует. То есть стек, куча, bss и data находится внутри этой полоски, а text в жёлтой области.
Посмотреть сколько места занимают некоторые данные можно в среде разработки. Вот картинка из TrueStudio…
dec и hex это общий размер первых трёх значений.
Чтоб перенести какую-то из этих областей в ССМ, нужно подкорректировать файл компоновщика (или линковщика, называйте как вам удобнее) с расширением .ld …
Находим там интересующие нас блоки…
Меняем у каких-нибудь блоков, или у всех сразу, слово RAM на CCMRAM …
В результате получаем — до:
Запись данных во флеш
У большинства микроконтроллеров stm32 (кроме серии L0) нету EEPROM, поэтому сохранять пользовательские данные приходится во флеш-памяти. Количество циклов записи 10000.
Чтобы записать во флеш какие-то данные нужно вначале её очистить (во время очистки все биты заполняются единицами — 0xFF). Очистка происходит постранично/посекторно.
Очистка одной страницы на F103…
Объявляем структуру в которую заносятся параметры очистки:
TypeErase — что хотим очистить, какую-то конкретную страницу (или несколько), или всю флеш полностью.
PageAddress — начальный адрес страницы, которую хотим очистить. Адреса можно посмотреть в мануале (правда там не все видны), либо посчитать самостоятельно, либо в примерах Куба — STM32CubeFx в файле main.h (для F103 — /STM32Cube_FW_F1_V1.8.0/Projects/STM32F103RB-Nucleo/Examples/FLASH/FLASH_EraseProgram/Inc/main.h). В конце есть ссылка на гитхаб с этим примером, там есть хедер (addr_pages.h) с задефайнеными адресами для BluePill.
NbPages — кол-во страниц для очистки. Если указать несколько, то они будут очищены начиная с адреса указанного выше.
Banks — у «жирных» камней память делиться на банки, поэтому нужно указать в каком именно банке находятся страницы.
HAL_FLASH_Unlock() — снимаем блокировку стирания/записи во флеш (для чтения этого делать не нужно). Это не относится к битам WRPx, если они установлены для текущей страницы, то стереть/записать страницу не получится.
Ну, а дальше стираем страницу/страницы, и возвращаем блокировку. Если что-то пойдёт не так, то программа выведет ошибку и зациклится.
После этой операции вся страница готова для записи. Необязательно записывать всю страницу за один раз, можно добавлять записи по мере необходимости, при условии что новые данные не будут записываться поверх старых.
Почему нужно очищать (заполнять значениями 0xFF) память?
Дело в том, что когда происходит запись байта в ячейку, то биты в этой ячейки не просто так берут и перезаписываются, а совершается операция логического «И» над тем что есть в ячейке и новым значением.
Наглядно это выглядит так. Ячейка у нас очищена (биты заполнены единицами) и мы записываем в неё число 7 (0х07)…
Выполняется логическое «И», и в ячейку записывается нужное нам число 7.
А теперь допустим что мы хотим записать в эту же ячейку число 13 (0x0d) поверх старого значения…
Выполняется логическое «И» между старым значением 0х07 и новым 0x0d. В результате вместо желаемого числа 13, в ячейку записывается число 5.
Запись
У разных МК можно записывать разную длину «слова», например у F103 можно записать «слова» размером 16, 32 и 64 бита. Подсмотреть это можно в файле stm32f1xx_hal_flash.h …
Запишем два 16-ти (FLASH_TYPEPROGRAM_HALFWORD) битных числа в начало только что очищенной страницы…
Разблокируем флеш, указываем адрес начала страницы (не обязательно начало, можно в любое место), записываем во флеш массив из двух чисел, а в цикле увеличиваем адрес на два. После этого блокируем память.
Если записывать 32-х (FLASH_TYPEPROGRAM_WORD) битное число, то увеличим адрес на четыре, а если 64-х (FLASH_TYPEPROGRAM_DOUBLEWORD) битное, то на 8. Если в дальнейшем захотите добавить в эту страницу ещё что-то, то нужно запомнить адрес.
Идём в STM32CubeProgrammer, вписываем адрес 127-ой страницы и смотрим чего понаписали…
Всё окей. Не забывайте нажимать Disconnect
Прочитаем то, что записали…
Разблокировать не надо, а адрес опять же увеличиваем на два. Получаем ожидаемое…
Чтоб записать 8-ми битное значение (не смотря на то, что производитель не предоставил такой возможности), надо просто записывать по два байта в одно 16-ти битное «слово» со сдвигом. Всё происходит почти так же как и с 16-ти битным числом…
Увеличиваем адрес на 4 (чтоб добавить данные вслед за предыдущими), записывать будем массив, размер которого должен быть кратен двум (для этого сделана проверка), а в цикле запихиваем два символа в одно 16-ти битное «слово» и отправляем по адресу.
Читать можно по одному 8-ми битному символу.
Вот и всё, остаётся придумать как хранить адрес (со смещением) последней записи для добавления последующих, чтобы не мучить одну и туже ячейку, и не прикончить её в самом скором времени. Вот вариант как смещать очередную запись.
Всем спасибо
Как записать переменную во флеш память 80c51f120
Artwork by Benny Kusnoto
Когда я писал драйвер FM-модуля RDA5807, то у меня возникла необходимость сохранять куда-то найденные станции, чтобы на них можно было переключиться минуя поиск. Но когда я полез в документацию STM8, чтобы поискать, как это можно было бы осуществить, то понял, что EEPROM и FLASH в STM8 — это отдельная подсистема микроконтроллера, и изучать ее надо всю.
Один из режимов записи в EEPROM/FLASH требует выполнения из ОЗУ. Вопрос копирования кода в ОЗУ и выполнения его оттуда я затрагивал в предыдущей статье, однако там вся реализация была на ассемблере. Сейчас же мне захотелось показать как это делается в Си.
В качестве компилятора я выбрал COSMIC, по которому уже как-то писал быстрый старт. Но тогда я писал об использованию COSMIC совместно с SPL библиотекой. На этот раз мне хочется раскрыть тему программирования в COSMIC, используя «чистый» Си в связке с ассемблером. Правда должен оговориться, что несколько отредактированные загловочные файлы из SPL в этой статье я все-таки использовать буду, т.к. нужны будут именновые константы масок периферийных регистров.
В итоге статья получилось составленной из двух взаимосвязанных тем: сначала рассматривается вопрос использования компилятора COSMIC, а затем, как с его помощью сохранять данные в EEPROM/FLASH памяти микроконтроллера.
В качестве Develop Board я буду использовать собственную плату с чипом STM8S105C4T6. Это Medium-Density чип с 16 КБ флеш-памяти, 2 КБ ОЗУ и 1 КБ ЭСППЗУ(EEPROM). Он более интересен чем STM8S103-й чип, т.к. в 105-ом имеется встроенный загрузчик(bootloader), механизм read-while-write (RWW), а размер блока составляет 128 байт вместо 64 байт на 103-м чипе. Вы в свою очередь можете использовать фирменную отладочную плату STM8S-DISCOVERY с чипом STM8S105C6T6. Там флеш-памяти будет побольше — 32 КБ. На худой конец, можно воспользоваться ещё одной китайской платой на 105-м чипе. Также как в STM8S-DISCOVERY в ней установлен кварц на 8 МГц. Сама плата выполнена в форм-факторе удобном для установки в беспаячную макету.
Cosmic у меня работает в связке с STVD, обе программы установлены на виртуалку, которая в свою очередь установлена в Linux. Гостевой ОС в виртуалке служит Windows XP SP3. О превратностях установки Cosmic я уже писал в вышеупомянутой статье два года назад. К сожалению, я тогда я не упомянул, что получить регистрационный ключ можно онлайн. Т.е. не надо ждать несколько дней чтобы ключ скинули на e-mail, как было в моем случае. Если не ошибаюсь, ключ действует один год, и по истечении регистрационного периода, его нужно получать заново. Кроме того, ключ «слетает» при копировании виртуальной машины. В этом случае его также следует получать по новой. В последнем случае я просто удалял Cosmic и затем ставил его заново, получая свежий ключ. Сейчас у меня следующая версия компилятора:
Так же как и в предыдущей статье, для контроля кода прошивки я буду использовать дизассемблер из комплекта утилит stm8-binutils.
-
Список используемой документации:
- Cosmic CXST7 — User Manual — документация по компилятору COSMIC
- Reference Manual STM8S — RM0016, глава 4 «Flash program memory and data EEPROM»
- Programming Manual PM0051 — руководство по записи в EEPROM и FLASH память для микроконтроллеров STM8S/STM8A
- Application note — AN2659, — глава пятая — «Configuring the Cosmic compiler for RAM code execution».
- Работа с EEPROM и Flash / STM8 / Сообщество EasyElectronics.ru хорошая вводная статья по теме на русском языке. Написано по существу, без лишней воды.
-
I. Основы работы со связкой COSMIC + STVD
- Создание базового проекта в среде разработки STVD+COSMIC
- Добавление ассемблерного файла к проекту
- Добавление ассемблерных обработчиков прерываний
- Маппинг на физические адреса
-
II. Основы работы с EEPROM/FLASH подсистемой в микроконтроллерах STM8
- Особенности EEPROM/FLASH подсистемы в микроконтроллерах STM8
- Регистры подсистемы EEPROM/FLASH
- Запись в EEPROM средствами COSMIC
- Безопасное снятие защиты MASS, однобайтный режим записи в EEPROM/FLASH
- Четырехбайтный режим записи в EEPROM/FLASH
- Блоковый режим записи в EEPROM/FLASH. Копирование кода в ОЗУ и выполнение его оттуда средствами COSMIC
Скачать исходники, workspace с проектами и скомпилированными прошивками к статье можно будет по ссылке в конце статьи.
1 Создание базового проекта в среде разработки STVD+COSMIC
В STVD откроем новый Си проект с использованием тулчейна COSMIC. Выглядеть он будет так:
Если проект скомпилировать и после заглянуть в «Debug» каталог проекта, то можно будет увидеть следующие, сгенерированные при компиляции файлы:
Здесь s19 и elf — бинарные файлы с итоговой прошивкой. Файлы с расширением «o» — это объектные файлы. lkf — это скрипт компоновщика.
Заглянем например в 01_blink.lkf:
В начале идёт объявление стандартных сегментов, затем подключается библиотечный файл содержащий обработчик прерывания reset, потом собственно наша программа, после добавляется ещё пара библиотек, а затем объявляется сегмент с таблицей векторов и файл содержащий саму таблицу с обработчиком пустого прерывания.
Технически, мы можем использовать COSMIC без STVD, программировать в командной строке, писать Makefile’ы и пр. Например составим такой main.c
Скомпилируем его в объектный файл командой:
На выходе мы получим объектный файл main.o. Теперь составим скрипт компоновщика main.lkf следующего вида:
В этот раз на выходе получаем main.sm8. Следующим шагом будет получение elf-файла:
Смотрим дизассемблером что у нас получилось:
Таким образом работает компилятор COSMIC в backend режиме STVD. Сегодня я не собираюсь что-то делать в таком стиле, но на мой взгляд иметь представление об этапах компиляции полезно.
Возвращаясь с списку сгенерированых файлов проекта в STVD, хочу обратить ещё внимание на файлы с расширением ls. В них помещается ассемблерный вариант программы:
У вас есть выбор: пользоваться дизассемблером или поглядывать в эти файлы для контроля компиляции.
Ок. Начинаем писать базовый проект на COSMIC. В начале файла main.c добавляем следующие заголовочные файлы:
Первый заголовочный файл добавляет целочисленные типы uint8_t, uint16_t и прочее. Второй заголовочный файл добавляет адреса регистров периферийных устройств. Путь к заголовочным файлам задается в свойствах проекта:
Ниже представлены заголовочные файлы которые идут вместе с компилятором COSMIC:
Теперь добавляем именованные константы для светодиодов, и функцию задержки.
Главная функция main() не будет отличаться оригинальностью:
Компилируем, прошиваем. Если все прошло успешно, то в дизассемблерном виде программа будет выглядеть так:
Можно видеть, что параметр функции передаётся через X регистр, а не через стек как в SDCC.
Я хочу обратить внимание на то, что для вызова функции delay() используется инструкция вызова подпрограммы по относительному адресу — CALLR. Если мы зайдём в настройки проекта и откроем вкладку настройки компилятора, то в выпадающем списке сможем настроить используемую модель памяти.
По умолчанию используется Short Stack (+mods0), когда по мере возможности используются относительные вызовы CALLR, а в стек помещается один байт в качестве адреса возврата. Если поменять модель использования памяти на Long Stack (+modsl0), то по умолчанию, для вызова подпрограмм будет использоваться инструкция CALL, и в стек будет помещаться двухбайтный адрес возврата. Если же переключиться на модель Long Stack (+modsl), то забегая вперёд, метка вызываемой функции должна будет иметь префикс f_ вместо простого символа подчёркивания. Во-вторых, вместо инструкции CALL будет использоваться расширенный вызов CALLF с 3-х байтным адресом.
В модели использования памяти Short Stack (+mods0), внешние функции из других модулей которые мы в дальнейшем будем добавлять, будут вызываться через «обычную» инструкцию вызова CALL.
2 Добавление ассемблерного файла в проект
Теперь заменим функцию задержки на ассемблерный вариант. Для этого добавим в проект файл с именем, допустим: «asm.s», в который будем складывать ассемблерные функции и обработчики прерываний на ассемблере.
Синтаксис ассемблера в COSMIC самый что ни на есть классический, т.е. после метки должно идти двоеточие, шестнадцатеричное число начинается с префикса «0x»(не совсем канон, да), а комментарий должен начинаться с точки с запятой. В макросы я вдаваться не хочу, т.к. предполагаю, что ассемблер будет использоваться только для самого необходимого.
Содержимое asm.s пусть пока будет таким:
Если функция вызывается из Си-программы, то метка должна начинаться с символа подчёркивания. Директива xref делает метку видимой компоновщику, а директива switch указывает рабочий сегмент.
В main.c заменим функцию delay объявлением внешней функции:
В главном цикле, в параметре вызова функции delay(), заменим абстрактное число 0xffff на 1000, т.е. на одну секунду.
Если в функции указать более одного параметра, то первый будет по прежнему помещён в Х регистр, а второй будет передан через стек.
Хочу обратить внимание, что если дизассмблировать прошивку с флагом «S», то мы увидим следующую картинку:
Здесь нам дизассеблировали только тот код который мы сами написали. Таблица векторов и библиотеки COSMIC отображались массивом данных, т.к. там нет отладочной информации. Можно использовать флаг D вместо S для полного дизассемблирования, но тогда на выходе получим «портянку» без указания периферийных регистров и сегментов.
3 Добавление ассемблерных обработчиков прерываний
Теперь займёмся прерываниями. Предполагаем, что обработчики прерываний мы будем писать на ассемблере. Тогда первое за что следует взяться, это файл по умолчанию входящий в шаблон проекта: «stm8_interrupt_vector.c»
Ассемблер COSMIC также как ST-Assembler не знает о существовании инструкции INT, поэтому таблица векторов задана в виде массива констант: опкода инструкции INT, плюс адреса обработчика прерывания. Для удобства, я добавил комментарии к таблице:
В обработчик пустого прерывания NonHandledInterrupt я вставил строку:
Не могу сказать сколько раз меня это выручало. Если во время отладки перестаёт мигать светодиод главного цикла, значит я снова забыл вставить обработчик прерывания в таблицу векторов.
В качестве примера добавим функцию задержки на таймере TIM4. Тема не нова и была не раз уже разобрана: Задержка по таймеру TIM4, с обработчиком прерывания на ассемблере, STM8+SDCC+SPL: функции delay_ms() и delay_us() на таймере TIM4. Так что дело за техникой.
Для наглядности, я добавлю заголовочный файл stm8s_tim4.h с именованными константами из SPL. Там закомментированы функции, и добавлены маски битовых флагов из stm8s.h Скачать файл можно отсюда , а разместить его нужно будет в каталоге проекта.
Также в каталог проекта нужно добавить заголовочный файл tim4.h с объявлением функции delay_ms(uint16_t ms):
После этого, добавим в проект файл: «tim4.c» следующего содержания:
Далее, в ассемблерный файл «asm.s» добавим обработчик прерывания таймера TIM4:
Во-первых, обращаю внимание, что ассемблерные обработчики прерываний объявляются через директиву xdef, тогда как ассемблерные функции через директиву xref.
Во-вторых, здесь используется мнемоника для регистра TIM4_SR, что означает, что в проект нужно добавить ещё один файл, на этот раз ассемблерный, с адресами периферийных регистров. С помощью потокового редактора sed, я конвертировал файл: «STM8S105C_S.h» из комплекта STVD в нужный формат. Скачать его можно здесь. Файл следует добавить в каталог проекта, после чего в начале «asm.s» вставить объявление: #include «stm8s105c_s.inc».
Теперь в начало файла: «stm8_interrupt_vector.c» добавим объявление внешнего обработчика:
После чего, у 23-го прерывания поменяем обработчик «NonHandledInterrupt» на наш «(interrupt_handler_t)tim4_handler».
Осталось в main.c добавить объявление заголовочного файла tim4.h, перед главным циклом разрешить прерывания командой: _asm(«rim»), и после, в главном цикле можно менять вызов функции delay(1000) на delay_ms(1000).
У этого простого примера есть интересный нюанс. Если из функции main() убрать инструкцию разрешения прерывания — «rim», то прерывание все-равно будет работать. Судя по отладчику, разрешение прерываний происходит после выполнения инструкции iret. При этом, каким-то образом этот обработчик вызывается. Могу предположить, что инструкция wfi разрешает прерывания.
4 Маппинг на физические адреса
Fun Art by Derek Ring — «He is Risen»
В COSMIC есть способ привязать переменные к реальным физическим адресам. Делается это так:
Здесь массив foo будет занимать адреса с 0х120 по 0х129 включительно.
Таким образом можно маппить не только адреса в ОЗУ но и адреса периферийных регистров. В документации настаивают, что бы при маппинге на регистры ввода-вывода, переменные объявлялись с модификатором volatile:
Переходя ближе к теме скажу, что с помощью модификатора @eeprom можно назначать переменные в области EEPROM-памяти. Примеры:
Маппить можно не только адреса но и отдельные биты. В COSMIC есть встоенный булевый тип: _Bool. С его помощью можно сделать например так:
Приведу реальный пример. Для этого в каталог проекта нужно будет добавить ещё один заголовочный файл из SPL stm8s_clk.h. Далее в начале функции main() заменим строку:
на код переключения на внешний кварц:
Так же нужно будет добавить обработчик второго(CLK) прерывания:
Я предполагаю что мы используем модель сборки по умолчанию т.е. «Debug» в которой оптимизация отключена. Теперь посмотрим как компилятор раскладывает цикл while (CLK_SWCR & CLK_SWCR_SWBSY):
Вышло на три инструкции. Если бы STM8 не имел битовых инструкций, то на этом бы и закончилось. Тип _Bool предполагает, что для обработки таких переменных используются битовые инструкции. В начале функции main() объявим булеву переменную:
После чего поменяем цикл:
В этом случае компилятор уже использует битовую инструкцию:
Если использовать модель сборки Release, то там оба варианта цикла будут реализованы через одну инструкцию btjt, здесь мне придраться не к чему.
Еще нужно учесть такой момент. Если уж мы используем в проекте заголовочные файлы SPL, то в свойствах проекта нужно будет задать версию своего чипа:
5 Особенности EEPROM/FLASH подсистемы в микроконтроллерах STM8
В STM8, запись в EEPROM практически не отличается от записи во флеш-память, меняются местами некоторые регистры и флаги, но алгоритм остаётся тем же. Поэтому, если подходить к вопросу алгоритмически, то флеш-память можно рассматривать как дополнительную EEPROM, или наоборот — EEPROM-память рассматривать как довесок к флеш-памяти. Но не следует забывать, что ресурс флеш-памяти в 30 раз ниже чем у EEPROM.
Одномоментно записать можно: байт, слово или блок. Давайте разбираться, что есть что.
Карта памяти для Medium-Density чипов STM8S
- Блок — количество байтов которые можно стереть или записать одномоментно.
- Страница — некоторое количество блоков. В страницах считается размер областей UBC и PCODE(только для STM8L high-density).
- Слово — четыре байта.
- MASS — система защиты ППЗУ от нежелательного изменения.
- ROP — системы защиты памяти от нежелательного чтения. Устанавливается через Option Bytes.
- RWW — позволяет записывать в EEPROM не останавливая при этом работу ядра микроконтроллера. Не доступна на low-density чипах. Запись во флеш-память останавливает работу ядра микроконтроллера на все время записи.
- UBC — область пользовательского загрузчика. Размер задаётся в страницах через Option Bytes. Всегда начинается с адреса 0x8000. Таблица векторов соответственно переносится на размер области UBC. UBC закрыта для записи через IAP
- PCODE — имеется только в STM8L medium и high density- чипах. Защищённая от записи и чтения область для размещения проприетарного кода. Доступ к этому участку памяти можно получить только через программное (TRAP) прерывание.
- IAP — программный программатор, пользовательский загрузчик или любое другое изменение ППЗУ программными средствами.
- ICP — аппаратный программатор использующий протокол SWIM.
Область EEPROM в STM8S чипах начинается с адреса 0х4000, в STM8L с 0х1000. Ниже приводится карта памяти для STM8S Medium Density чипов:
Карта памяти для STM8L high-density чипов:
Кстати, в STM32 чипах нет EEPROM памяти вообще. Во флеш там можно писать только страницами. Страница там если не ошибаюсь, равна одному килобайту. И если вам нужно будет записать меньшее количество байт, содержимое страницы придётся куда-то сохранить перед стиранием. Так что, когда вам будут говорить, что STM32F0xx тоже самое что STM8 только лучше, не верьте.
Ок. Я пока предлагаю не трогать такие штуки как UBC и PCODE. Если мы в Option Bytes выставим какое-либо значение UBC отличное от нуля, то лишимся возможности отладки. Значение же PCODE байта вообще выставляется только однажды, сбросить его потом не получится.
Во всех чипах с размером флеш-памяти 16 Кбайт и выше имеется встроенный загрузчик. Он начинается с адреса 0x6000. Блок-схема с алгоритмом его работы представлена ниже:
В отличие от 103-го чипа, в начале трассировки прошивки отладчиком мы попадаем не в таблицу векторов, а именно в этот загрузчик. Его можно увидеть в окне дизассемблера:
Здесь в начале проверяется, есть ли по адресу 0х8000 опкод инструкций INT или JRF. Затем идёт проверка, включён ли bootloader в Option Bytes и нет ли установки ROP. После этого идёт или переход или на адрес 0x8000 или на код загрузчика. В этом фрагменте ещё можно увидеть код переключения на внешний кварц и снятие защиты на запись (MASS) с флеш-памяти и EEPROM. Код загрузчика в STM8L151C8 немного отличается, но суть та же:
6 Регистры подсистемы EEPROM/FLASH
Регистров здесь немного, предлагаю их бегло рассмотреть:
Первый управляющий регистр, сегодня нам не придётся им пользоваться, так что можно принять просто к сведению. Флаги HALT и AHALT позволяют отключать питание с флеш-памяти в режимах энергосбережения halt и active-halt. IE — разрешает прерывание, FIX переключает режим стандартного программирования на быстрое при режиме записи блоками. По умолчанию там стоит ноль, т.е. стандартный режим программирования. Им мы и будем пользоваться.
В L-серии на третьем битe висит EEPM, а на втором WAITM. Они отвечают за перевод флеш-памяти в режим пониженного энергопотребления IDDQ. EEPM делает возможным перевод флеш-памяти в режим пониженного энергопотребления при выполнении программы из ОЗУ, а WAITM переводит флеш-память в режим пониженного энергопотребления IDDQ при ждущем и спящем режимах.
Второй управляющий регистр уже поинтереснее, он переключает режимы записи. OPT — защищает область Option Bytes которая расположена в последнем блоке EEPROM (у S-серии). WPRG — включает 4-х байтный режим записи. ERASE — стирает содержимое блока в «быстром» режиме блочной записи. FPRG — включает «быстрый» режим блочной записи, PRG Включает режим стандартной блочной записи.
Регистру FLASH_CR2 соответствует комплементарный регистр FLASH_NCR2. Т.е. изменяя один регистр, следует изменить и другой, до того как что-то писать в ППЗУ.
Ещё одна комплементарная пара регистров, которых мы не будем сегодня касаться: FLASH_FPR и FLASH_NFPR. Определяет размер секции защищённой от записи — UBC.
FLASH_PUKR — регистр снятия защиты MASS с флеш-памяти. Для с снятия защиты потребуется последовательно записать в этот регистр числа 0х56 и 0xAE. При записи неверной комбинации, содержимое флеш-памяти будет блокироваться до следующего Reset.
FLASH_DUKR — регистр снятия защиты MASS с EEPROM. Для с снятия защиты потребуется последовательно записать в этот регистр числа 0хAE и 0x56. При записи неверной комбинации, содержимое EEPROM будет заблокировано до следующего Reset.
Статусный регистр. Флаг HVOFF выставляется и сбрасывается аппаратно во время записи в EEPROM. Служит для определения окончания записи в EEPROM для чипов с RWW. В чипах без RWW следить за этим флагом не надо, там выполнение программы «подвешивается» на время записи. DUL — флаг разблокировки EEPROM. PUL Флаг разблокировки флеш-памяти. EOP — конец операции записи. Флаг устанавливается аппаратно, и сбрасывается программно путём чтения регистра или при новых операциях записи/стирании. Служит для определения момента завершения записи во флеш-память. Флаг WG_PG_DIS устанавливается при попытке писать в защищённую область. Используется для определения ошибок в работе.
7 Запись в EEPROM средствами СOSMIC
COSMIC нам существенно упрощает работу когда мы хотим что-то записать в EEPROM или FLASH память.
Для работы с EEPROM/FLASH нам понадобится ещё один заголовочный файл c константами из SPL: stm8s_flash.h Скачать его можно здесь или сделать самому (требуется небольшая доработка). Файл нужно будет поместить в каталог проекта.
Далее добавим в проект файл с исходным кодом следующего содержания:
Здесь функция write_to_eeprom() записывает некие байты в массив data, определённый в области EEPROM. Используется одно-байтный режим работы.
Остаётся добавить объявление функции void write_to_eeprom( void ); в main.c и ПЕРЕД(!) главным циклом поставить вызов этой функции. Замечу, что в процессе отладки бесполезно отслеживать содержимое eeprom. Когда после прошивки мы откроем ST Visual Programmer и считаем содержимое EEPROM то увидим результат работы:
Программа работает должным образом. Но если мы посмотрим на дизассемблерный листинг:
То можно увидеть, что для операции присваивания используется вызов непонятной функции (выделено красным). Вызываемая функция выглядит так:
Все бы ничего, но если вы планируете записывать данные блоками, то весь код должен будет выполняться из ОЗУ. А если функция располагающаяся в ОЗУ будет вызывать подпрограмму находящуюся на флеше в который же и пишется, то ничего хорошего из этого не выйдет.
Теперь попробуем изменить тип данных массива data c uint8_t на uint16_t. Тогда строка кода data[i]=(uint16_t)i; обернётся вызовом такой функции:
Здесь я красным обвёл финальную часть функции, где устанавливается 4-х байтный режим записи в регистре FLASH_CR2, после чего идёт сама запись в EEPROM. Результат можно увидеть опять же в STVP:
Первое, что здесь бросается в глаза, то что двухбайтные числа пишутся в обычном, а не перевёрнутом порядке, когда пишется сначала младший байт, а потом старший. Это нужно учитывать, когда будете читать с ППЗУ.
В целом, нужно отдать должное COSMIC, — это замечательный инструмент для работы с EEPROM. Им удобно пользоваться если вам нужно скинуть туда какие-то небольшой объем данных, например: показания сенсора, станции FM-модуля, но если мы хотим писать во флеш-память и EEPROM блоками, то нам нужно научиться обходиться без его помощи.
8 Безопасное снятие защиты MASS, одно-байтный режим записи в EEPROM/FLASH
Undefined behavior (UB) is the result of executing computer code whose behavior is not prescribed by the language specification to which the code adheres, for the current state of the program. (c) wiki
Но прежде всего мне хотелось бы коснуться, вопроса о защите от дребезга питания или от неопределённого поведения при разблокировке MASS. На мой взгляд, последовательная запись ключей в регистр разблокировки не очень хорошая идея. Будет лучше, если объявить две глобальные переменные которые будут хранить в оперативной памяти значения ключей. Глобальные переменные инициализируются в начале работы программы, и перед их использованием должно пройти какое-то время. Это даёт некоторую защиту от неопределённого поведения. Идею можно развить и сделать как в защите серийных номеров от дизассамблирования, когда номера хранятся не в чистом виде, а являются результатом вычисления какой-нибудь функции. Допустим, можно объявить глобальный массив, со значениями которого будут производиться некие арифметические операции, в результате выполнения которых будут выдаваться числа: 0хАЕ и 0х56. В идеале можно написать генератор псевдослучайного числа, который при инициализации тем или иным значением будет выдавать нужные числа.
Писать генератор псевдослучайного числа мне показалось чересчур и поэтому в онлайн-редакторе я на скорую руку состряпал пару функций:
Здесь первая функция использует 140 итераций, вторая — 90. Ассемблерный листинг функций можно посмотреть под спойлером.
Если весь этот код выполняется успешно, то ППЗУ разблокируется. Если нет, то происходит блокировка до следующего Reset.
Самописная функция write_to_eeprom() у меня получилась такой:
где data — это следующий массив:
В отличии от варианта COSMIC’а, здесь ожидается установка флага HVOFF вместо EOP (так делается в SPL) в качестве признака окончания записи. Кроме того, здесь анализируется флаг FLASH_IAPSR_WR_PG_DIS на случай, если что-то пойдёт не так. В low-density чипах без RWW ждать окончания записи не нужно. Там на время записи приостанавливается работа CPU. Учтите, что прерывания тоже перестают работать, так что там можно писать байты без всяких задержек. Я сам не побывал работать с EEPROM на STM8S103, но здесь: «STM8S EEPROM надо ли ждать EOP флага после записи 1 байта? — Форум разработчиков электроники ELECTRONIX.ru» утверждается, что все именно так и обстоит.
Из дизассемблерного листинга видно, что никаких внешних подпрограмм больше не вызывается, что нам и нужно было добиться.
Функция записи во флеш-память будет не сильно отличаться:
9 Четырехбайтный режим записи в EEPROM/FLASH
Четырех-байтный режим записи устанавливается флагами WPRG в регистре FLASH_CR2 и NWPRG в FLASH_NCR2. От однобайтного режима отличается увеличением скорости записи в четыре раза, т.к. за одну операцию записи пишется разом четыре байта.
Алгоритм практически аналогичен предыдущим примерам:
где data — это следующий массив:
Обращаю внимание, что здесь запись двухбайтных данных организована в классическом «перевёрнутом» порядке. Т.е. сначала пишется младший байт, затем старший.
10 Блоковый режим записи в EEPROM/FLASH. Копирование кода в ОЗУ и выполнение его оттуда средствами COSMIC
Artwork by Su Jian
Последний самый интересный режим записи — блоковый режим. Позволяет за раз записать в ППЗУ целый блок — 128 байт. Имеется два варианта этого режима: стандартный и быстрый. Я буду рассматривать стандартный режим как самый удобный, требующий меньше телодвижений и следовательно, с ним меньше шансов наделать ошибок. Быстрый режим очень походит на режим записи флеш-памяти в STM32.
Самое сложное в этом режиме то, запись должна выполняться из ОЗУ. Данные должны быть выровнены по границам блока. В STM32 есть регистр в котором можно выбирать записываемую страницу, в STM8 местоположение блока придётся рассчитывать самим.
Я ничего не нашёл в документации относительно особенностей чипов 208/207/007, но анализ кода SPL для этих чипов говорит о том, что там за одну операцию можно записать целую страницу, это четыре блока по 128Б т.е. 512 байт. Сам лично пока не проверял.
Копирование кода в ОЗУ подробно рассмотрено в руководстве: Application note — AN2659, — глава пятая — «Configuring the Cosmic compiler for RAM code execution».
Весь процесс примерно делится на три этапа.
Этап первый. Перед началом функций write_to_eeprom() и write_to_flash() нужно поставить объявление новой секции: #pragma section(FLASH_CODE)
После функций write_to_eeprom() и write_to_flash() нужно будет объявить завершение новой секции: #pragma section()
Этап второй. В настройках проекта перейти на вкладку компоновщика, выбрать там категорию Input, и щёлкнув правой кнопкой мыши по сегменту «RAM» добавить туда новую секцию «.FLASH_CODE» (точку в префиксе не забудьте):
Щёлкнув правой кнопкой по ряду «Option» добавленной секции, измените пустое поле на «-ic». Это даст линковщику команду считать данную секцию перемещаемым кодом. При ее линковке не будет использоваться абсолютная адресация (проверьте!).
Этап третий. Теперь нам понадобится механизм копирования секции в OЗУ. В COSMIC для этого существует готовая функция: int _fctcpy(char name). она поставляется в ассемблерном виде, в файле fctcpy.s. Его нужно будет скопировать в папку проекта и далее добавить к проекту.
Осталось дело за малым. Нужно добавить объявление функции в начало файла main.c:
В качестве параметра функции используется первая буква в названии секции. В начало функции main() вставим вызов этой функции: _fctcpy(‘F’); и дело в шляпе. Это всё.
Пример функций write_to_eeprom() и write_to_flash() реализующих запись двух блоков в EEPROM и флеш-память приведён ниже:
Где массив data в этот раз начинается с начала блока:
В основе лежит алгоритм однобайтной записи, просто добавлен ещё один цикл для записи блока.
Проверяем. Ставим точку останова на вызове функции write_to_eeprom() и смотрим куда ведёт вызов функции:
Вызов идёт в ОЗУ, как и должно быть. Прыгаем, и в окне дизассемлера видим код функций write_to_eeprom() и write_to_flash() перенесённых сюда с помощью _fctcpy().
Результат работы как обычно можно посмотреть в STVP. На этом всё. Остался не рассмотренным быстрый режим блочной записи. Если когда-нибудь соберусь писать статью по написанию своего загрузчика, то начну именно с этого режима.
В заключении могу ещё рекомендовать почитать Executing code from RAM on STM8 | lujji, где автор исследовал возможности перемещения исполняемого кода в ОЗУ для компилятора SDCC. Мне лично система показалась чересчур «костыльной», но возможно вы будете другого мнения.
Как записать переменную во флеш память 80c51f120
STM32. Обзор и работа с Flash-памятью микроконтроллера.
Сегодняшняя статья, как вы уже поняли из названия, будет посвящена микроконтроллерам STM32 и работе со встроенной Flash-памятью. Да-да, именно с той памятью, в которой хранится прошиваемая нами программа. Поскольку в STM32 нет EEPROM (энергонезависимой памяти) для хранения данных можно использовать Flash-память контроллера, и сегодня мы как раз и разберемся, как же это работает.
Сразу же скажу, что согласно документации Flash-память в STM32 позволяет осуществить минимум 10000 циклов перезаписи, что в принципе достаточно для того, чтобы использовать ее и в качестве энергонезависимой памяти для хранения неких данных.
Давайте для начала разберемся со структурой. Возьмем в качестве примера контроллер семейства STM32F10x, относящийся к High-Density устройствам (например, STM32F103VET6). Его память выглядит следующим образом:
Как видите, все жестко структурировано. Information Block содержит 2 раздела:
- System memory — тут хранится системный bootloader (забегая вперед скажу, что следующие статьи на нашем сайте будут целиком и полностью посвящены именно работе с bootloader’ом)
- Option bytes — информация о защите основной области памяти.
И, собственно, второй блок — Main memory — именно тут хранится записанная нами в контроллер программа. Этот блок, в свою очередь, разделен на страницы по 2 Кб (в данном случае мы имеем 256 страниц и, соответственно, общий объем памяти составляет целых 512 Кб). Как вы уже поняли, Flash-памяти у STM32 более чем достаточно, почти всегда остается несколько свободных от основной прошивки страниц, которые как раз-таки можно использовать для хранения данных после выключения питания контроллера.
Но тут нельзя не упомянуть о некоторых ограничениях при работе с Flash. Перед записью определенной страницы она должна быть предварительна стерта («стертому» состоянию памяти соответствуют все биты, установленные в единицу). Соответственно, во время записи нужные биты могут быть «обнулены». Это приводит к ряду неудобств — например, у нас уже сохранено некоторое количество байт в определенной странице Flash-памяти. Для перезаписи одного байта нам нужно считать все ранее записанные, стереть страницу, а потом записать все байты обратно, включая измененный байт, который мы хотим сохранить.
Вот так вот в общем чертах это и работает. Кстати, лучше всего для своих целей использовать последние страницы памяти, которые с большей вероятностью будут свободны от основной прошивки, но, конечно же, нужно четко представлять сколько места в памяти занимает основная программа, и сколько есть свободного места.
С теорией все понятно, давайте рассмотрим некоторые практические моменты. Я буду, как и обычно, использовать SPL, а значит нам понадобятся файлы stm32f10x_flash.c и stm32f10x_flash.h в нашем проекте. И для того, чтобы работать с Flash-памятью нужно сначала ее разблокировать. Для этого в специальный регистр FLASH_KEYR необходимо записать два числа, одно за другим:
В SPL для этого реализована функция FLASH_Unlock() . После разблокировки уже можно стирать и записывать данные. Для очистки будем использовать функцию:
В качестве параметра мы должны передать в функцию адрес стираемой страницы. Итак, страница стерта, как записать данные? А для этого у нас есть:
С аргументами тут все понятно — передаем адрес ячейки памяти и собственно записываемые данные. Осталось понять, как же считать данные из Flash-памяти. А для этого просто:
Вот и все, ничего сложного, на этом сегодняшняя небольшая статья подходит к концу, в следующий раз мы будем обсуждать Bootloader, так что до скорого!
Как записать переменную во флеш память 80c51f120
Как записать переменную во флеш память 80c51f120
Почему FLASH?
- Не требуется использование внешней памяти, соответственно сокращается время на разработку платы и происходит удешевление продукта;
- Меньше программного кода, следовательно меньшее время затрачивается на разработку.
- Запись во Flash требует некоторого времени, что влияет на производительность МК в момент записи или очистки памяти;
- Чтобы внести изменения в уже существующие данные, нужно стереть всю страницу или записывать в «чистый» блок памяти;
- Количество циклов перезаписи гарантировано в районе 100 тысяч операций — вроде бы много, но перезаписывая данные в страницу раз в секунду, МК выработает ресурс flash чуть более, чем за сутки. Поэтому очень не рекомендую постоянно писать во flash, рекомендуется производить операции очистки / записи лишь для сохранения данных в «энергонезависимой» памяти. В остальных случаях работаем с оперативной памятью;
- Минимум Вы можете использовать 1 страницу памяти (даже для одного байта), а размер одной страницы составляет от одного до двух килобайт, в зависимости от модели микроконтроллера. Такова селяви устройства flash-памяти.
- Для устройств серии XL-density: объем памяти составляет до 1Mb, разбитый на два банка памяти:
- Для других устройств: объем памяти составляет до 512KB, состоящий из одного банка памяти размером до 512KB.
- Сама по себе Flash-память состоит из трех блоков: основной блок памяти, информационный блок и блока регистров управления Flash. Структура блоков памяти и их характеристики приведены на рисунке №1.
- Чтение из памяти с возможностью буферизации (2 слова по 64 бита);
- Операции записи и очистки памяти;
- Организация защиты flash от чтения и/или записи.
Операции с flash-памятью
Инициализация Flash.
- Уровень «0», если 0 <SYSCLK ≤ 24 MHz;
- Уровень «1», если 24 MHz < SYSCLK ≤ 48 MHz;
- Уровень «2», если 48 MHz < SYSCLK ≤ 72 MHz;
Address offset: 0x00
Reset value: 0x0000 0030
Биты | Название | Описание |
---|---|---|
31:6 | Зарезервировано | |
5 | PRFTBS — Prefetch buffer status | Состояние буферизации. 0: отключена; 1: включена. Бит доступен только для чтения. |
4 | PRFTBE — Prefetch buffer enable | Включение буферизации. 0: отключена; 1: включена. |
3 | HLFCYA — Flash half cycle access enable | Доступ к полному циклу Flash. 0: отключен; 1: включен. |
2:0 | LATENCY — Latency | Управление задержкой. Эти биты представляют отношение периода SYSCLK (системных часов) к времени доступа Flash. 000 Уровень «0», если 0 <SYSCLK≤ 24 МГц 001 Уровень «1», если 24 МГц <SYSCLK ≤ 48 МГц 010 Уровень «2», если 48 МГц <SYSCLK ≤ 72 МГц |
Чтение данных из flash-памяти.
Запись данных во flash-память.
Использование структур данных.
Блокировка чтения / записи Flash
Программное исполнение.
Модифицированная функция установки/снятия защиты от записи Flash.
- добавлен параметр «NewState», который отвечает за установку или снятия флага защиты;
- в теле функции добавлена проверка на значение этого параметра и, в зависимости от него, устанавливаются или сбрасываются соответствующие биты.
- сброс OperationByte производится не перед вызов функции, а внутри нее. Это связано с особенностью записи во Flash, поэтому сначала запоминаются предыдущие значения OperationByte, производится очистка OperationByte, а затем уже записываются новые значения с учетом предыдущих.
Использование утилиты STM32 ST-LINK Utility.
Флеш память разделена на страницы. Размер страницы зависит от общего объема флеш памяти — смотри в даташите.
Например, для тини13 есть такая табличка:
Небольшое дополнение: RWW и NRWW
Вообще, в контроллерах с поддержкой бутлоадера, весь флеш поделен на 2 части: RWW (Read-While-Write) и NRWW (No Read-While-Write). NRWW распологается в конце памяти и занимает место отведенное под бутлоадер. Т.е. размер этой области памяти равен максимальному размеру BLS. Остальное место (с начала флеша и до NRWW) занимает RWW область.
STM32 — память
Конечно же в вашем микроконтроллере нет никаких гигабайтов памяти однако камень 32-х битный, а значит можно адресовать (обратится по адресу) до 4ГБ. Это стандартизированная модель обеспечивающая переносимость кода между различными микроконтроллерами stm32, а так же дающая возможность подключать внешнюю память и совершенно спокойно обращаться к ней. То же самое касается и периферии, производитель микроконтроллеров на базе ядра Cortex, может напихать туда оооочень много всего и при этом не беспокоиться о нехватке адресов. Что же касается размера, то некоторых адресов просто нет, вместо них зарезервированные пустоты. Если обратится к зарезервированной области, то произойдёт аппаратный сбой процессора.
Reference manual — rm0008 стр. 51
Прежде чем продолжать, надо скачать программу STM32CubeProgrammer с помощью которой можно ковыряться в микроконтроллере. Прога хороша тем, что есть варианты для и
Нажимаем верхнюю левую кнопку (с карандашиком), и на вкладке Device memory видим программу залитую в камень. Можно указать адрес, с которого читать (по умолчанию стоит начальный адрес флеш-памяти) и сколько байт прочитать. Кнопочка Read считывает данные и показывает как на картинке. Если вместо Read выбрать Save As, то программа сдампится в указанный файл (надо только размер считываемых данных правильно указать, а то по умолчанию 1К).
Кнопка открывает интерфейс для загрузки прошивки в МК…
Выбираем нужный файл, очищаем флеш полностью (Full chip erase) или выборочные страницы, и жмём Start Programming.
— что-то связанное с внешним загрузчиком или внешним девайсом, не знаю, не вникал.
— очищает флеш.
Connectivity line devices — это, на сколько я понимаю, микроконтроллеры которые умеют работать с интернетом. Programming manual — PM0075 стр. 5.
Теперь нажмём кнопочку чтоб появился список Option bytes…
… и вернёмся к изучению памяти.
Бит RDP — если установить, то нельзя будет ни прочитать прошивку, ни загрузить новую. Чтобы установить, нужно поставить галочку, нажать кнопку Apply и обресетить МК. Снимается так же. При снятии защиты, существующая в МК прошивка будет удалена. Такой механизм гарантирует невозможность прочесть прошивку в залоченом камне.
AA — нет защиты.
BB — защита включена.
СС — микроконтроллер безвозвратно заблокирован от чтения/записи. Warning! Если установите этот уровень защиты, то больше никогда не сможете прошить МК. Операция необратима.
Тут комментировать особо нечего, думаю и так всё понятно. Однако очень не рекомендую снимать галочку с вачдога — камень будет постоянно ресетится. Настройки применяются так же, галочку сняли/поставили, и кнопка Apply. В более мощных МК есть доп. настройки.
Сюда можно сохранить какие-то свои данные, правда не много
Защита от записи конкретных страниц флеш-памяти. К этому вернёмся чуть позже.
RM0008 стр. 55. (Рис. 1)
Часть страниц по 16К, одна 64К, а часть по 128К. Здесь страницы называются секторами.
127-я страница на камне F103C8хх.
У low-density используется только WRP0 и защищает весь флеш, а у medium-density можно делать это выборочно. Зачем нужны остальные биты (WRP4 и т.д.) я так и не понял, может они зарезервированы.
То же самое происходит при переходе из одной функции в другую, например из в
Стек начинается от самого большого адреса и при увеличении размера ползёт вниз (записывая данные в меньшие адреса), а куча наоборот, при увеличении размера ползёт вверх, поэтому при выделении памяти на куче нужно следить чтоб эти области не налезли друг на друга.
Вся наша оперативка находится внутри узенькой полоски, а всё что выше не существует. То есть стек, куча, bss и data находится внутри этой полоски, а text в жёлтой области.
dec и hex это общий размер первых трёх значений.
Выполняется логическое «И», и в ячейку записывается нужное нам число 7.
Выполняется логическое «И» между старым значением 0х07 и новым 0x0d. В результате вместо желаемого числа 13, в ячейку записывается число 5.
Всё окей. Не забывайте нажимать Disconnect
Всем спасибо
Как записать переменную во флеш память 80c51f120
TINY_F, NEAR_F, SWITCH, DIFUNCT, CODE, FAR_F, HUGE_F, INITTAB, TINY_ID, NEAR_ID и CHECKSUM.
Функция | Аргументы | Возвращает |
ReadFlashByte() | MyAddressType flashAdr | unsigned char |
ReadFlashPage() | MyAddressType flashStartAdr, unsigned char *dataPage | unsigned char |
WriteFlashByte() | MyAddressType flashAddr, unsigned char data | unsigned char |
WriteFlashPage() | MyAddressType flashStartAdr, unsigned char *dataPage | unsigned char |
RecoverFlash() | void | unsigned char |
STM32 — память
Конечно же в вашем микроконтроллере нет никаких гигабайтов памяти однако камень 32-х битный, а значит можно адресовать (обратится по адресу) до 4ГБ. Это стандартизированная модель обеспечивающая переносимость кода между различными микроконтроллерами stm32, а так же дающая возможность подключать внешнюю память и совершенно спокойно обращаться к ней. То же самое касается и периферии, производитель микроконтроллеров на базе ядра Cortex, может напихать туда оооочень много всего и при этом не беспокоиться о нехватке адресов. Что же касается размера, то некоторых адресов просто нет, вместо них зарезервированные пустоты. Если обратится к зарезервированной области, то произойдёт аппаратный сбой процессора.
Reference manual — rm0008 стр. 51
Прежде чем продолжать, надо скачать программу STM32CubeProgrammer с помощью которой можно ковыряться в микроконтроллере. Прога хороша тем, что есть варианты для и
Нажимаем верхнюю левую кнопку (с карандашиком), и на вкладке Device memory видим программу залитую в камень. Можно указать адрес, с которого читать (по умолчанию стоит начальный адрес флеш-памяти) и сколько байт прочитать. Кнопочка Read считывает данные и показывает как на картинке. Если вместо Read выбрать Save As, то программа сдампится в указанный файл (надо только размер считываемых данных правильно указать, а то по умолчанию 1К).
Кнопка открывает интерфейс для загрузки прошивки в МК…
Выбираем нужный файл, очищаем флеш полностью (Full chip erase) или выборочные страницы, и жмём Start Programming.
— что-то связанное с внешним загрузчиком или внешним девайсом, не знаю, не вникал.
— очищает флеш.
Connectivity line devices — это, на сколько я понимаю, микроконтроллеры которые умеют работать с интернетом. Programming manual — PM0075 стр. 5.
Теперь нажмём кнопочку чтоб появился список Option bytes…
… и вернёмся к изучению памяти.
Бит RDP — если установить, то нельзя будет ни прочитать прошивку, ни загрузить новую. Чтобы установить, нужно поставить галочку, нажать кнопку Apply и обресетить МК. Снимается так же. При снятии защиты, существующая в МК прошивка будет удалена. Такой механизм гарантирует невозможность прочесть прошивку в залоченом камне.
AA — нет защиты.
BB — защита включена.
СС — микроконтроллер безвозвратно заблокирован от чтения/записи. Warning! Если установите этот уровень защиты, то больше никогда не сможете прошить МК. Операция необратима.
Тут комментировать особо нечего, думаю и так всё понятно. Однако очень не рекомендую снимать галочку с вачдога — камень будет постоянно ресетится. Настройки применяются так же, галочку сняли/поставили, и кнопка Apply. В более мощных МК есть доп. настройки.
Сюда можно сохранить какие-то свои данные, правда не много
Защита от записи конкретных страниц флеш-памяти. К этому вернёмся чуть позже.
RM0008 стр. 55. (Рис. 1)
Часть страниц по 16К, одна 64К, а часть по 128К. Здесь страницы называются секторами.
127-я страница на камне F103C8хх.
У low-density используется только WRP0 и защищает весь флеш, а у medium-density можно делать это выборочно. Зачем нужны остальные биты (WRP4 и т.д.) я так и не понял, может они зарезервированы.
То же самое происходит при переходе из одной функции в другую, например из в
Стек начинается от самого большого адреса и при увеличении размера ползёт вниз (записывая данные в меньшие адреса), а куча наоборот, при увеличении размера ползёт вверх, поэтому при выделении памяти на куче нужно следить чтоб эти области не налезли друг на друга.
Вся наша оперативка находится внутри узенькой полоски, а всё что выше не существует. То есть стек, куча, bss и data находится внутри этой полоски, а text в жёлтой области.
dec и hex это общий размер первых трёх значений.
Выполняется логическое «И», и в ячейку записывается нужное нам число 7.
Выполняется логическое «И» между старым значением 0х07 и новым 0x0d. В результате вместо желаемого числа 13, в ячейку записывается число 5.
Всё окей. Не забывайте нажимать Disconnect
Всем спасибо
Как записать переменную во флеш память 80c51f120
Artwork by Benny Kusnoto
Работа с PROGMEM памятью
Часто бывает нужно сохранить в памяти микроконтроллера большой объём данных, которые не будут меняться в процессе работы, например:
- Калибровочный массив
- Текст названий пунктов меню
- Просто какой-то текст
- Посчитанная тригонометрия (синус, косинус)
- Изображения для дисплея (bitmap)
- И многое другое
Хранить такие данные в оперативной памяти (в виде обычной переменной) – не самая лучшая идея, ведь они не будут меняться, а место займут! Оперативной памяти всегда гораздо меньше, чем программной (Flash) памяти: в той же ATmega328 (Arduino UNO/Nano/Pro mini) – 32 кб Flash и 2 кб SRAM, в 16 раз меньше! Так что гораздо эффективнее хранить такие данные во Flash, он же программная память, он же program memory, он же PROGMEM. Но как?
Мы привыкли к тому, что переменные мы можем менять во время выполнения программы, на то они и переменные, на то и память называется динамической. А вот с Flash памятью всё не так просто – писать в неё может только программатор, при помощи которого загружается код программы, либо загрузчик (bootloader), который практически выполняет функцию программатора. Есть кстати модифицированный загрузчик, который позволяет иметь доступ к Flash памяти прямо из программы, но в этих уроках мы рассматриваем стандартные средства, в данном случае – утилиту PROGMEM. Для работы с PROGMEM используется встроенная библиотека avr/pgmspace.h, подключать её не нужно, она подключится сама (в версиях Arduino IDE выше 1.0).
Запись
Ключевое слово (модификатор переменной) PROGMEM позволяет записать данные во Flash память. Синтаксис такой:
Всё! Данные, в показанном случае массивы тип_данных будут помещены во Flash память. PROGMEM может работать со всеми целочисленными типами (8, 16, 32, 64 бита), float и char.
Важный момент! Модификатор PROGMEM можно применять только к глобальным (определённым вне функций) или статическим (глобальным или локальным, но со словом static ) переменным! Читай урок про типы данных. Полный список возможностей pgmspace можно посмотреть в документации.
Чтение
Если с записью всё очень просто (добавляется ОДНО ключевое слово), то с чтением всё гораздо интереснее: оно осуществляется при помощи специальных функций. Основная функция чтения из progmem – pgm_read_тип(адрес) . Мы можем использовать вот эти 4:
- pgm_read_byte(data); – для 1-го байта (char, byte, int8_t, uint8_t)
- pgm_read_word(data); – для 2-х байт (int, word, unsigned int, int16_t, int16_t)
- pgm_read_dword(data); – для 4-х байт (long, unsigned long, int32_t, int32_t)
- pgm_read_float(data); – для чисел с плавающей точкой
Где data – адрес (или указатель) сохранённого блока данных! Вспомните урок про указатели, чтобы понимать, о чём речь. Полный список возможностей pgmspace можно посмотреть в документации.
Одиночные числа
Рассмотрим запись и чтение одиночных чисел:
Что здесь важно помнить: читая отрицательные числа (например типы int и long ) нужно обязательно приводить тип, потому что PROGMEM хранит числа в беззнаковом представлении. Обратите внимание на чтение signed_data из примера выше, без приведения к int число выводится некорректно!
Одномерные массивы
С массивами чисел всё весьма ожидаемо:
Читай урок по массивам в блоке базовых уроков программирования.
Двумерные массивы
При создании двумерного массива нужно обязательно указывать размер хотя бы одной из размерностей:
Массив массивов
Можно хранить несколько массивов в одном, объявив так называемую таблицу ссылок, то есть ещё один массив, который содержит указатели на массивы данных. Этот вариант отличается от двумерного массива тем, что количество “столбцов” в каждой строке может быть любое, необязательно одинаковое:
Другая ситуация – есть у нас функция, которая принимает 1-мерный массив байтов (например), который хранится в PROGMEM. Например эта функция выводит на дисплей изображение, которое как раз и закодировано в массиве, и функция ожидает, что массив будет PROGMEMный и сама знает, как с ним работать (например так сделано в библиотеках GyverOLED и GyverGFX). Условный пример вывода:
Если изображений нужно вывести много и подряд, или иметь возможность вызывать “по номеру” – такой подход будет очень громоздким. Можно заменить его на список массивов и выводить по индексу. Но для этого придётся спуститься на уровень ниже – получить адрес массива из списка, и уже его передать в функцию:
Как это работает – мы читаем адрес указанного массива (т.к. массив – указатель сам на себя) в памяти и преобразуем его к (const uint8_t*) т.к. наша функция ожидает именно его. Что тут важно – мы читаем адрес массива в памяти, в AVR Arduino (Nano, UNO…) адресация 16 битная, поэтому адрес из PROGMEM списка мы читаем как pgm_read_word . Если работать на 32-битных МК (ESP8266, ESP32…), то нужно будет использовать pgm_read_dword !
Строки в PROGMEM
Строка (как массив символов) хранится в оперативной памяти программы. Речь идёт о строках вида:
PROGMEM позволяет хранить символьные строки в программной памяти. Это очень удобно, ведь большинство текста не меняется в процессе работы программы: названия пунктов меню, имена параметров запросов к сайту, статичные части веб-страниц и так далее. Далее по тексту для краткости будем называть такие строки PGM-строки, или строки в программной памяти.
Для манипуляций с PGM-строкой (вывод, сложение с другими строками, передача в функции) понадобятся дополнительные преобразования. Почему? Программа не знает, что строка хранится не в оперативной памяти: для неё это обычная const char* строка. Но данные ведь находятся в другой области памяти! Если начать читать их как обычную строку – можно прочитать “мусор”, либо программа и вовсе зависнет из за ошибок чтения по указанному адресу.
- Для удобства программиста существует “тип данных” PGM_P , который является макросом на const char* , то есть это просто указатель на строку. Так сделано для того, чтобы визуально разделить в программе обычные строки от PGM-строк:
- const char* – строка в оперативной памяти
- PGM_P – строка в программной памяти
Рассмотрим сначала запись, а потом чтение, при помощи разных инструментов.
Глобальная строка
Строка объявляется глобально, то есть вне функций в программе. Это удобно и выгодно в том случае, когда строка будет использоваться в программе несколько раз. Объявив её один раз, мы будем избегать дубликатов. Синтаксис следующий, строка объявляется как массив в примерах выше:
Обращаться к этой строке в программе можно по её имени, message .
Массив строк
Иногда бывает удобно хранить несколько строк под одним именем, например для пунктов меню. В таком случае можно использовать массив строк, про него мы говорили в уроке про строки. Механизм абсолютно такой же, как у массива массивов, который мы рассмотрели чуть выше: создаём сами строки, а затем – массив с указателями на них:
Обращаться к нужной строке в программе можно по индексу в массиве names , например names[1] .
Локальная строка
Иногда бывает удобно объявлять и использовать PGM-строки локально внутри функции, например если строка используется только в этой функции и больше нигде. Для этого нужно обернуть текст в макрос PSTR() , который поместит текст в PROGMEM и вернёт на него указатель типа const char* (используем PGM_P , чтобы не перепутать с обычной строкой):
Чтение строк из PROGMEM
Переписать в буфер
Можно скопировать строку из PROGMEM в оперативную память для решения следующих задач:
- Изменение строки
- Работа как с обычной си-строкой в оперативной памяти
- Отправка в функцию, которая принимает тип char* (строка в оперативной памяти)
Для этого нужно:
- Определить размер PGM-строки при помощи функции strlen_P() (вернёт длину строки без учёта завершающего символа)
- Создать буфер – массив char такого же размера +1 символ (для завершающего нулевого символа строки)
- Скопировать в буфер PGM-строку при помощи функции strcpy_P() (копирует строку вместе с завершающим символом)
Преобразовать к __FlashStringHelper*
Способ подходит для следующих задач:
- Отправка PGM-строки в функцию, которая принимает тип __FlashStringHelper*
- Прибавить PGM-строку к String -строке ( String поддерживает работу с __FlashStringHelper )
- “Напечатать” PGM-строку при помощи print() / println() в монитор порта/на дисплей/веб/любой объект стандартного класса Print
В Arduino-фреймворке есть очень удобный инструмент, позволяющий работать с PGM-строками, называется он __FlashStringHelper . Не углубляясь в подробности, будем считать что это просто ещё один тип строковых данных наряду с char* и String . Некоторые функции в библиотеках принимают этот тип данных (можно посмотреть в документации или заголовочном файле библиотеки), что позволяет передавать в них PGM-строки без лишних действий, нужно просто преобразовать переменную к (const __FlashStringHelper*) . Например:
В “ядре” esp8266/esp32 для такого преобразования есть удобный макрос FPSTR(строка) , непонятно почему его не сделали для AVR Arduino. Можно объявить макрос самостоятельно и поместить его в начале программы:
И предыдущий код будет выглядеть более компактно:
Передать в _P функцию
Способ подходит для следующих задач:
- Передача PGM-строки в функцию, которая принимает тип PGM_P (в основном это функции с постфиксом _P )
- Удобное помещение текста в PROGMEM и сразу передача в функцию, которая принимает тип PGM_P
Некоторые функции в библиотеках поддерживают работу напрямую с PGM строками: они принимают тип данных const char* или PGM_P , а в имени обязательно имеют постфикс _P . Например write_P(PGM_P buf) из библиотеки к esp8266 (можно посмотреть в документации или заголовочном файле библиотеки). Это означает, что в такую функцию можно передать PGM-строку без дополнительных преобразований:
F() макро
Способ подходит для следующих задач:
- Удобное помещение текста в PROGMEM и сразу передача в функцию, которая принимает тип __FlashStringHelper*
- В том числе для сборки String -строк
Вы могли подумать, а зачем создавать PSTR() строку и PGM_P переменную ради одного вывода? Действительно, может просто можно сделать print((const __FlashStringHelper*)PSTR(«Hello, World!»)) ? Да, можно! Причём всё уже придумано за нас и называется “F() macro”, этот удобный макрос позволяет ещё проще хранить строки в программной памяти для отправки в функции, которые поддерживают __FlashStringHelper (можно посмотреть в документации или заголовочном файле библиотеки):
F() макро + __FlashStringHelper
Также F-макро позволяет создавать и хранить строки типа __FlashStringHelper* . F-строки не оптимизируются компилятором, то есть например здесь
Строки займут место в памяти программы как две строки, то есть компилятор не объединит две одинаковые строки, как это происходит при обычной работе со строками. Поэтому F-строку можно создать отдельно и передать в нужные функции или сложить со стрингой, но сделать это можно только локально:
Проблема с циклами (AVR)
Осталось нам обсудить ещё один момент: рассмотрим “массив строк” в прогмем, который мы называли str_list в примерах выше. Если выводить из него строки вручную – всё будет прекрасно работать, например через FPSTR() :
Но стоит нам вывести все строки в цикле – программа сломается:
Это происходит по крайней мере на AVR (Arduino Nano, UNO и так далее), на ESP8266/ESP32 чтение в цикле работает корректно.
На AVR компилятор не смог оптимизировать процесс чтения, потому что он не знает значения переменной-счётчика в конкретной строке кода. Это можно обойти, преобразовав данные через функции из самого начала урока. Преобразовывать будем к размеру, который занимает указатель в памяти – это pgm_read_word для AVR (16 бит). В функцию нужно передать “адрес” переменной, а массив по сути является указателем сам на себя, то есть просто pgm_read_word(str_list) . Это адрес первого (нулевого) элемента. Соответственно для получения доступа к следующему элементу нужно увеличить адрес на 1 и получится pgm_read_word(str_list + i) . Да, отличается от доступа к массиву!
Для удобства можно сохранить результат в указатель типа PGM_P и дальше использовать его: