Очистка флеш памяти stm32
Очень часто возникает вопрос, связанный с использованием Flash-памяти микроконтроллеров STM32 в качестве EEPROM. И в этой статье рассмотрим один из вариантов эмуляции памяти EEPROM.
Итак, в чем изначально заключается проблема, и почему нельзя просто выделить сектор/страницу во Flash и использовать ее? Ответ, по большому счету, кроется в трех словах - количество циклов перезаписи. Для Flash максимально допустимое число на порядки меньше, чем для EEPROM, навскидку, 10000 циклов против 1000000.
Конечно, есть и другие отличия - скорость записи, чтения и т. д. Но упирается все именно в продолжительность жизни (что соответствует кол-ву циклов перезаписи).
Поэтому и возникает необходимость в программных алгоритмах, которые позволят искусственно увеличить это число для Flash-памяти микроконтроллера. Собственно, у ST есть целый application note, посвященный данному вопросу, более того с готовым примером кода. Так что, в принципе, можно спокойно использовать решение от производителя.
Первый ключевой аспект заключается в том, как будут храниться данные. Для каждой переменной существует определенное фиксированное значение - ключ. Выделим под ключ и под саму переменную по 4 байта. Допустим нам надо хранить во Flash значения трех переменных:
Переменная | Значение переменной | Значение ключа |
---|---|---|
data_1 | 0x1010 | 0x11111111 |
data_2 | 0x2020 | 0x22222222 |
data_3 | 0x3030 | 0x33333333 |
Непосредственно в памяти данные будут выглядеть так:
Для каждой переменной сохраняется пара ключ-значение.
А если нам потребуется обновить значение переменной data_2 на новое, к примеру - 0x4567, то мы просто записываем еще одну пару ключ-значение в свободное место памяти:
Получается, что для переменной data_2 сохранено два значения, при этом актуальным является второе, записанное позже. Но при этом первое не удаляется. Таким образом, значения переменных будут обновляться (записываться в свободные адреса) до тех пор, пока страница памяти не окажется полностью заполненной:
Далее в дело вступает второй ключевой аспект. Он заключается в использовании двух страниц Flash-памяти. По заполнению 1-ой страницы мы переносим данные из нее во 2-ую страницу. Но! Переносим не все значения подряд, а только актуальные, то есть записанные последними. И так для каждой переменной:
Заполняем 2-ую страницу этими величинами и очищаем страницу 1:
И далее все эти действия повторяются до тех пор, пока не заполнится и вторая страница, после чего они снова меняются местами.
Что это дает в итоге?
- В первую очередь, количество циклов перезаписи изначально увеличивается на порядки, поскольку мы не очищаем Flash каждый раз, когда нам нужно обновить значение одной переменной. Очистка происходит только по фактическому заполнению страницы.
- Кроме того, мы используем две страницы, что позволяет распределить эти циклы перезаписи между ними.
Для того, чтобы идентифицировать, в каком состоянии находятся страницы Flash-памяти, которые мы используем, добавим в начальные адреса каждой из них ее статус:
В итоге первые 4 байта у нас заняты текущим статусом страницы. Поскольку для каждого значения переменной суммарно у нас используются 8 байт, то добавим смещение еще на 4 байта относительно начала страницы (для удобства). Результат будет таким:
Жизнедеятельность страниц может выглядеть так:
И здесь мы получаем еще один бонус от использование нескольких страниц. Рассмотрим такую ситуацию - 1-я страница заполнена и начинается копирование ее данных во 2-ю страницу. И в середине этого процесса возникает сбой питания, и процесс обрывается. После устранения проблемы и включения платы первая страница окажется в состоянии Active, а вторая - в состоянии Cleared. Что просто приведет к повторной попытке переноса данных, то есть данные не утеряны, а по-прежнему спокойно себе лежат в странице 1.
А если бы у нас была только одна страница, то по ее заполнению нам нужно было бы перенести данные в некий массив в оперативной памяти (RAM) контроллера, чтобы затем очистить страницу и обновить ее же из этого массива (оставив таким образом только актуальные, то есть последние, значения переменных). Так вот при обрыве питания в момент, когда страница уже очищена, а данные еще в RAM, они были бы безвозвратно потеряны.
Итак, возвращаемся к эмуляции EEPROM и теперь бегло рассмотрим конкретные функции, которые все это реализуют.
Все функции возвращают одно из значений:
Для части функций просто рассмотрим их назначение, а некоторые разберем подробнее, если это важно для понимания сути процессов.
- EepromResult EEPROM_ClearPage(PageIdx idx) . Производит очистку страницы, номер которой передан в качестве аргумента.
- EepromResult EEPROM_CopyPageData(PageIdx oldPage, PageIdx newPage) . Копирует содержимое страницы oldPage в newPage. При этом обновляет статусы обеих страниц по результатам копирования/очистки.
- EepromResult EEPROM_Format() . Полная очистка EEPROM - то есть очистка страниц Flash, которые используются для эмуляции EEPROM.
- EepromResult EEPROM_GetActivePageIdx(PageIdx *idx) . Позволяет определить номер активной страницы.
- EepromResult EEPROM_Init() . Функция инициализации, на ней как раз и остановимся подробнее.
Здесь мы пробегаем по возможным комбинациям состояний страниц. Если статус страниц совпадает, то это сигнализирует об ошибке, форматируем память полностью и начинаем с чистого листа:
Если каким-то образом случилось так, что одна страница в состоянии Receiving Data, а вторая очищена - Cleared, то лучшее из того, что мы можем предпринять, это сделать 1-ую из них активной, поскольку в ней по сравнению со 2-ой явно хранятся какие-то полезные данные:
И, наконец, ситуация, когда одна из страниц - Receiving Data, а вторая - Active, как раз соответствует рассмотренному нами случаю с обрывом питания. Повторяем операцию копирования:
Идем дальше по функциям:
- EepromResult EEPR OM_PageTransfer(PageIdx activePage, uint32_t varId, uint32_t varValue) . Выполняет перенос актуальных данных из одной страницы в другую, кроме того в новую страницу добавляет пару ключ - значение, переданную через аргументы ( varId / varValue ). Осуществляется это так:
- Записываем упомянутые данные из аргументов: varId / varValue .
- Проходим по всему списку ключей и для каждого находим актуальное значение, которое и пишем в новую страницу.
- Стираем старую страницу.
- Функции управления состоянием страницы:
- PageState EEPROM_ReadPageState(PageIdx idx)
- EepromResult EEPROM_SetPageState(PageIdx idx, PageState state).
Код функции записи:
Первым делом проверяем, что переданный в функцию ключ varId является валидным, то есть находится в списке допустимых ключей (этот момент мы разберем подробнее на практическом примере ниже).
Далее определяем номер активной страницы. Остается найти свободное место и записать туда новые данные. При поиске свободного места читаем данные, которые располагаются там, где должны быть ключи. Соответственно, по тому адресу, где на месте ключа значение 0xFFFFFFFF (ячейка памяти пуста), и производим запись.
Если же свободного места не обнаружено, то переходим к процессу переноса данных в другую страницу - EEPROM_PageTransfer() .
И код для чтения данных:
Находим номер активной страницы и начинаем поиск нужного нам ключа ( varId ) среди сохраненных в памяти. При этом в отличие от функции записи поиск ведем от конца страницы к началу, то есть в обратном направлении. Связано это с тем, что как мы уже разобрали в начале статьи, при обновлении значений для определенного ключа новые значения записываются в память последовательно. То есть среди всех значений с одним и тем же ключом актуальным будет последнее из них (которое ближе к концу страницы).
Вот и все, находим ключ, вычисляем адрес, где хранится значение (добавив смещение на длину ключа, равную 4 байтам) и читаем данные.
Переходим к практическому примеру использования - будем сохранять величины двух переменных, каждой из которых поставим в соответствие значение ключа.
В файле eeprom.h нам нужно выполнить конфигурацию эмулятора:
- VAR_NUM - это количество ключей, а значит разных переменных, которые мы будем сохранять.
- Для каждого из этих ключей задаем значения: PARAM_1 , PARAM_2 .
В файле eeprom.c помещаем их в массив:
Помимо этого указываем адреса использующихся страниц. У меня для этого примера микроконтроллер STM32F103C8, и адресация Flash-памяти выглядит следующим образом:
Берем две последние страницы:
И переходим в функцию main() :
Инициализируем эмуляцию EEPROM, записываем значения для наших ключей и сразу же считываем их из памяти. Ожидаемо получаем:
И в Flash-памяти:
Обновим значение второго параметра и снова считаем:
И в памяти видим, что новое значение записалось в свободное место страницы, при этом и старое физически никуда не делось, хотя оно нам больше и не нужно:
Здесь мы просто передавали числа в функцию записи, соответственно, в общем случае там будут некие переменные:
Из ближайших доработок - необходимо добавить в записываемые данные контрольную сумму для проверки корректности сохраненных значений. Как вариант - сократить ключ на 4 байта и освободившееся место использовать под CRC. И на этом заканчиваем на сегодня - до скорого 🤝
Компания STMicroelectronics прекратила поддержку библиотеки SPL, которая использовалась в этом курсе. Поэтому я создал новую рубрику, посвященную работе уже с новыми инструментами, так что буду рад видеть вас там - STM32CubeMx. Также вот глобальная рубрика по STM32 - ссылка.
Уже не раз обсуждались на нашем сайте темы, касающиеся связи микроконтроллеров STM32 и ПК по интерфейсу USB, вот они:
Так вот, сегодня в очередной раз вернемся к этой теме, и задачей сегодняшней статьи станет реализовать Mass Storage Device (MSD) на микроконтроллере STM32. В этой статье я буду использовать STM32F10x и среду разработки Keil, но как и всегда с STM32 (ну или почти всегда) особых проблем с портированием на микроконтроллеры других семейств возникнуть не должно.
При работе с USB за базу возьмем готовые библиотеки от ST. Вообще у них на официальном сайте есть куча готовых проектов под разные отладочные платы с реализацией разных режимов работы USB (и MSD в том числе). Там, в частности, есть примеры Mass Storage Device для следующих случаев:
- в качестве внешней памяти используется SD-карта
- в качестве внешней памяти используется микросхема NAND-памяти
Предлагаю добавить в нашу задачу немного оригинальности ) Поэтому давайте для хранения информации задействуем внутреннюю память микроконтроллера. То есть часть flash-памяти будет определяться как внешний накопитель. Это конечно не очень практично, все-таки объем памяти микроконтроллера измеряется сотнями килобайт, поэтому использовать контроллер для хранения каких-нибудь данных не получится. С другой стороны, решение этой задачи будет полезно для понимания основных принципов, которых нужно придерживаться при реализации драйвера Mass Storage Device на STM32. Кроме того, можно использовать этот пример как базу при написании USB-bootloader'а для перепрограммирования готовых устройств.
В общем, давайте переходить к практической части!
В первую очередь, в библиотеках для работы с MSD нужно настроить USB Disconnect Pin, а точнее, задать тот порт ввода-вывода и тот номер пина, ко торый задействован на используемой плате для программного отключения/подключения USB. На моей плате для этого используется вывод микроконтроллера PA10, поэтому в файле platform_config.h я определил:
Непосредственная настройка этого вывода производится в функции USB_Disconnect_Config():
Здесь нам ничего менять и настраивать дополнительно не требуется.
Переходим к функциям USB. И тут нас интересует файл mass_mal.c. В этом файле нам нужно реализовать функции записи/чтения и инициализации, в соответствии с тем типом памяти, который мы будем использовать. То есть, если бы мы хотели использовать внешнюю карту памяти, то нам нужно было использовать функции записи/чтения для SD-карты. Поскольку мы будем работать с внутренней flash-памятью контроллера, то и использовать мы будем соответствующие функции.
Как вы помните из статьи, посвященной работе с flash (ссылка), для того, чтобы начать работать с памятью ее нужно разблокировать. Поэтому функция MAL_Init() будет выглядеть так:
При инициализации мы разблокируем память и после этого можем вызывать функции чтения/записи из SPL. Но для начала нам нужно определить сектора, которые будут использоваться в качестве внешнего накопителя (всю flash-память мы использовать не можем, поскольку часть памяти будет занята нашей программой). Итак, в файле mass_mal.h задаем:
Flash-память у нас начинается с адреса 0x08000000. Для основной прошивки мы выделили 40 кБ (адреса с 0x08000000 - по 0x0800A000). Общий объем памяти моего контроллера - 256 кБ. Тогда объем внешнего диска получаем равным 221184 байт (256 кБ - 40 кБ). А размер страницы памяти для данного контроллера составляет 2кБ или 2048 байт. Все эти данные мы и задали в файле mass_mal.h.
В общем то, теперь нам осталось только написать функции чтения и записи - MAL_Write() и MAL_Read(). Тут нам надо учесть несколько факторов:
- перед началом записи страницы, в которые будет произведена запись, необходимо предварительно очистить
- запись и чтение производятся целыми секторами
- перед вызовом функций работы с flash-памятью необходимо убедиться, что предыдущая операция завершена (тут нам нужна функция FLASH_WaitForLastOperation() ).
Учтем все вышеперечисленное и в итоге получаем следующую реализацию функций:
У нас осталась еще одна важная функция, необходимая для работы с MSD, а именно функция MAL_GetStatus(). В этой функции мы должны передать в специальные переменные значения, соответствующие размеру памяти, размеру страницы, а также количеству блоков:
Собираем проект, прошиваем микроконтроллер и видим, что в диспетчере устройств у нас появилось новое запоминающее устройство. А кроме того, система обнаружила внешний накопитель, объем которого соответствует тому значению, которое мы определили в нашей программе. Таким образом, цель сегодняшней статьи можно считать достигнутой!
Как и всегда, прилагаю готовый проект для среды разработки Keil: USB_MassStorage_Project.
Компания STMicroelectronics прекратила поддержку библиотеки SPL, которая использовалась в этом курсе. Поэтому я создал новую рубрику, посвященную работе уже с новыми инструментами, так что буду рад видеть вас там - STM32CubeMx. Также вот глобальная рубрика по STM32 - ссылка.
Сегодняшняя статья, как вы уже поняли из названия, будет посвящена микроконтроллерам 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, так что до скорого!
Речь пойдёт о плате Blue Pill (в простонародье Arduino STM32), построенной на базе микроконтроллера STM32F103C8T6, которая по своим размерам и цене сравнима с Arduino Nano, но значительно превосходит её по возможностям и может программироваться в IDE Arduino .
Основные тех. характеристики
• ARM Cortex M3
• Разрядность — 32-х битный
• Частота — 72МГц
• Flash-память — 64КБ / 128КБ
• Оперативная память — 20КБ
• SPI — две штуки
• I2C — две штуки
• UART — три аппаратных UART'а + USB (то есть получается четыре интерфейса)
• CAN-шина
• АЦП — 12-и битный
• ШИМ — 16-и битный
• Напряжение питания — от 2 до 3.6В (то есть можно запитать от двух «пальчиковых» батареек)
• Внутренние часы реального времени (на плате есть специальный пин — Vbat, для подключения батарейки)
Повторяющиеся названия, например Serial1, говорят о том, что этот интерфейс можно запрограммировать для работы на одних либо на других «ножках».
По умолчанию, Serial1 «сидит» на «ножках» РА9 и РА10, на «ножках» РВ6 и РВ7 I2C1, а на РВ8 и РВ9 CAN-шина. Однако ничто не мешает перенастроить эти пины (равно как и любые другие, в том числе «аналоговые») на работу в дискретном режиме, то есть как обычные «цифровые».
Warning!Будьте осторожны с пинами РА11 и РА12 — это USB, если что-то пойдёт не так, то помимо потери возможности прошивки МК по usb, можно ещё и комп повредить.
К пинам РС14 и РС15 подключён внешний кварц для часов (32.768).
Другие пины могут выдать максимум 20мА, рекомендуется 8мА. Общая максимальная нагрузка не должна превышать 150мА.
Не смотря на то, что напряжение питания ~3В, половина «ножек» терпимы к пяти вольтам. Что конечно же значительно облегчает использование различной периферии.
Светодиод РС13 (который у ардуины D13) включается не плюсом, а минусом. Это на всякий случай, чтоб у Вас не произошло когнитивного диссонанса, когда напишите — digitalWrite(PC13, HIGH), а диод погаснет.
У МК отсутствует EERPOM, но это решается подключением внешней флешки по I2C или SPI. Помимо этого можно эмулировать EEPROM используя встроенную flash-память. Есть в примерах IDE .
Вот здесь, говорится о том, что стандарт USB требует подтяжки линии D+ резистором 1.5kΩ, вместо этого почему то ставят 10kΩ или 4.7kΩ. Его рекомендуют заменить либо добавить резистор на 1.8kΩ между PA12 и 3.3V.
На моей плате стоит 10kΩ и я ничего не менял.И ещё, когда будете припаивать «гребёнки» , пропаяйте места крепления разъёма micro-usb.
Подготовка к работе
Описание будет сделано для и
Изначально, заливать скетчи через встроенный USB не получится, для этого нужно прошить специальный загрузчик (про бутлоадер в конце статьи) . Чтобы это осуществить, понадобится USB to UART конвертер (далее по тексту я буду часто использовать этот «термин») .Если такой штуковины нет, то есть два варианта превращения ардуины в конвертер…
1. Замкнуть RESET на GND (может не прокатить).
2. Загрузить в неё вот такой незамысловатый скетч:Чтобы проверить работает ли ардуина в качестве конвертера, замкните RX и TX, откройте и отправьте какие-нибудь символы. Если они вернулись, значит всё работает.
В «этих ваших интернетах» пишут, что нужно согласовывать уровни напряжения (5v to 3.3v), но в этом нет смысла так как нужные нам ножки толерантны к 5v.
Далее переставляем джамперы на платке вот так:Для чего это делается — прочтете в конце статьи.
Соединяем конвертер и STM следующим образом…
Конвертер RX PA9 STM
Конвертер TX PA10 STM
Конвертер GND GND STM… и подключаем конвертер и STM к компьютеру.
Железки подготовили, теперь займёмся средой . Скачайте архив STM32.zip и куда-нибудь его распакуйте.
Папку ArduinoSTM32 (это ядро stm, взято отсюда) нужно положить в ../arduino-1.8.5/hardware/…
Справедливо для windows и linux, только пути разные (философская фразочка получилась).Далее запускаем среду и открываем «Менеджер плат. »
Если покрутить список вниз, то будет видно что наша платка уже появились — «Generic STM32F103C series».В «Менеджере плат» нужно установить пакет Arduino SAM Boards (32-bits ARM Cortex-M3)
Всё, среду тоже подготовили, теперь перейдём к установке загрузчика.
Инструкция по прошивке загрузчика для
Первый вариант — из командной строки.Опишу этот вариант не смотря на то, что это противоречит мировоззрению большинства поклонников windows.
Открываем терминал и переходим в папку с утилитой stm32flash.exe, у меня это — c:\arduino-1.8.5\hardware\ArduinoSTM32\tools\win\Копируем в эту папку файл generic_boot20_pc13.bin (он есть в архиве, взят отсюда) и даём команду…
COM12 — это порт на котором висит конвертер.
Всё, загрузчик прошит. С помощью этой утилиты можно выполнять любые действия — загружать bin-файлы, очищать МК, читать прошивку и т.д. Посмотреть справку — stm32flash.exe --help
Второй вариант — с помощью программы Flash Loader Demonstrator.
Переходим в папку Flash Loader Demonstrator и запускаем Flash Loader Demonstrator.exe
Выбираем СОМ-порт конвертера и жмем Next.
Если светофор даёт зеленый свет, то смело жмите Next.
Жмем Next.
В пункте Download to device указываем файл — generic_boot20_pc13.bin (он есть в архиве) и жмем Next.
Прошивка прошла успешно. Закрываем программу, больше она не нужна.Теперь идём в папку ..\Drivers\win\ (из архива) и от имени администратора запускаем файл install_drivers.bat, это установит драйвера для платы.
Инструкция по прошивке загрузчика для
Перейдите в папку…
Для 64-х битной системы.Для 32-х битной системы.
Забросьте в эту папку файл generic_boot20_pc13.bin (он лежит в архиве, взят отсюда) и дайте комаду…
Всё, загрузчик прошит. С помощью этой утилиты можно выполнять любые действия — загружать bin-файлы, очищать МК, читать прошивку и т.д. Посмотреть справку — ./stm32flash --help
Позже, при попытке залить скетч, я столкнулся с тем, что IDE не видела устройства…Вылечил следующим образом…
Установил утилиту dfu-util
Так и не понял нужно ли это было делать. Попробуйте без этой команды.
Создал правило в /etc/udev/rules.d/Вот с таким содержимым:
Так же это работает если заменить группу plugdev на dialout (это просто к сведению).Да, проверьте состоит ли Ваш пользователь в группе plugdev (сделать это можно через пункт меню «Настройки» ⇨ «Пользователи и группы», после добавления пользователя необходимо перелогиниться).
Далее идёт описание для обоих ОС
Возвращаем джампер в исходное положение…… подключаем USB и нажимаем кнопочку RESET на плате. Светодиод начнёт часто мигать.
Здесь есть несколько пунктов, которые требуют пояснения…
Во-первых, у Вас ещё пока не появился usb-порт «Maple mini» (будет только порт конвертера), но об этом ниже.
Во-вторых, пункт — Variant: «STM32F103C8 (20k RAM.64k Flash)»…
Если внимательно посмотреть на картинки из главы про прошивку загрузчика, то будет видно, что в варианте прошивки через «командную строку», утилита stm32flash.exe показала — Flash: 128KiB, а в варианте с Flash Loader Demonstrator было — 64КВ.
Вот здесь (в разделе 128 KB flash on C8 version) пишут, что практически все протестированные ими (видимо имеется в виду ихний форум) микроконтроллеры имеют 128КБ флэша.
Ещё один пункт, который может показаться непонятным — Optimize: «Smallest(default)». Он отвечает за уровень оптимизации при компиляции и добавление отладочных символов, если Вы не знаете что это такое, то оставьте как на картинке.
Теперь про порт «Maple mini». Чтобы он появился, нужно один раз загрузить любой скетч. Вот здесь (в разделе Software installation) про это пишут, но причин не объясняют, поэтому давайте просто сделаем…… и прошейте этот скетч:
Загрузка прошла успешно.После загрузки сразу же появится порт «Maple mini». Возня с загрузчиками на этом закончена, тернистый путь Вы прошли.
Отключите конвертер (или не отключайте, как хотите), выбирайте «Maple mini» и загружайте свои скетчи.
На картинке видно ошибку — error resetting after download: usb_reset: could not reset device, win error:
На работу это не влияет.Иногда происходят вот такие ошибки…
МК не хочет прошиваться. Помогает нажатие RESET.
Если не помогает, тогда зажмите RESET, отпустите его, и спустя мгновение нажмите кнопку Должно прошиться.Не отрабатывает авторесет МК.
Эта проблема описана здесь (в разделе Software installation) . Для Linux (про win ничего не говорят) предлагается увеличить задержку после ресета в файле maple_upload из папки ../ArduinoSTM32/tools/linux/.
Речь идёт об этой строчке — "$/upload-reset" $ 750
Я «поигрался» этим значением, но в силу того, что ошибка проявляется редко, так и не понял, помогает это или нет.Собственно это и проблемой то нельзя назвать, не такой уж и великий труд нажать иногда кнопку ресет. На работу МК это никак не влияет.
Примеры
В разделе «Примеры» есть различные примеры
В примерах указаны неправильные обозначения «ножек», например в скетче Blink прописан пин PB1, это связано с тем, что примеры делались для платы Maple Mini, поэтому сверяйтесь с распиновкой.
Небольшое пояснение функции Serial.println();Serial.println() — печать в USB.
Serial1.println() — печать в UART 1.
Serial2.println() — печать в UART 2.
Serial3.println() — печать в UART 3.Настройка скорости . begin (baud rate) действует только на Serial1,2,3 (UARTы).
Для Serial (USB) функцию . begin () прописывать не обязательно. В «Мониторе порта» можно указывать любую скорость.Реальная скорость USB может зависеть от различных факторов, от принимающей стороны (какую максимальную скорость поддерживает устройство), от посылаемых данных (например длиная строка или побайтно). Примерная скорость — от 1Мбит/с. до 5Мбит/с.
Большой размер занимаемой скетчем памяти, связан с подгружаемыми библиотеками.
Про джамперы и bootloader
Bootloader (загрузчик) – это обычная программа, которая в момент старта МК решает, что должно происходить дальше…Сейчас, когда у Вас уже всё сделано, процесс работы выглядит так: как только Вы подали питание или нажали кнопку RESET, в дело вступает прошитый bootloader (файлик generic_boot20_pc13.bin). В течении первой секунды он ожидает заливки новой прошивки (по USB), если новой прошивки не поступает (то есть Вы ничего не заливаете в МК), тогда bootloader проверяет есть ли в МК какая-либо прошивка и если есть, то передаёт управление ей. Если ничего нет, то ничего и не происходит.
Когда Вы в своей IDE нажимаете кнопку , в МК посылается спец. сигнал вызывающий программный RESET и пошло-поехало.
Джамперы
Обычный режим. За загрузку программ отвечает установленный нами bootloader. Если его не установить, то прошить МК можно только в режиме «системного bootloader'а». Спец. программаторы в статье не рассматриваются.
Режим «системного bootloader'а». Системный bootloader ожидает прошивку — можно загружать в МК всё что угодно. Либо через IDE Arduino, выбрав Upload method: «Serial» и порт конвертера, либо утилитой stm32flash, либо Flash Loader Demonstrator'ом.Системный bootloader зашивается в System memory на этапе производства, его нельзя удалить или изменить.
В этом режиме, с помощью специального программатора, можно заливать прошивку в оперативную память. Это продлевает жизнь Flash при отладке программ.
В разделе Option bytes хранятся некоторые настройки, с помощью которых можно защитить Flash от записи/чтения и вкл/откл watchdog. Для установки воспользуйтесь Flash Loader Demonstartor или stm32flash.На этом первая часть окончена…
Следите за обновлениями ядра (это то, что лежит в папке ArduinoSTM32).
Перед тем как приступать к изучению, настоятельно рекомендую прочесть эту статью.
Большинство микроконтроллеров stm32 не имеют EEPROM-памяти что конечно же вызывает трудности с долговременным хранением каких-либо данных. Для этих целей можно использовать флеш, благо её в избытке, однако проблема в том, что что у флеша довольно таки ограниченное количество циклов перезаписи, производитель гарантирует 10000. В связи с этим, нужно записывать сохраняемые данные каждый раз в новое место. Производитель предлагает свой механизм реализации этой задачи в документе EEPROM emulation AN2594, однако всегда хочется придумать что-то своё.Представленная ниже библиотека не моя идея, в сети много подобного. Суть заключается в следующем: выбирается участок флеша не используемый программой, то есть несколько страниц в самом конце, и очищается — заполняется единицами (0хFF). Далее по ходу программы производится запись сохраняемых данных в самое начало этого участка, а перед каждой последующей записью производится поиск места где закончилась запись предыдущих данных, и новые данные записываются с этого места. Так продолжается пока не закончится вся память. Когда память заканчивается, весь участок очищается и всё повторяется заново. Таким образом количество циклов перезаписи увеличивается в десятки тысяч раз.
Далее пояснения делать лучше наглядно, поэтому скачайте пример. Если у вас BluePill, тогда ничего делать не нужно, если же плата другая, тогда надо создать проект с включённым USART'ом и активированным блоком CRC…
Подключить в проект файлы FlashPROM.c и FlashPROM.h . В main.c добавить инклюд…
И объявить глобальную переменную…
Теперь идём в файл FlashPROM.h , там лежат все настройки…typedef uint16_t myBuf_t; — если элемент буфера 16-ти битный, тогда оставляем как есть, если 32-ух битный, то меняем uint16_t на uint32_t .
Теперь возвращаемся в main.c …Очищаем память один раз и комментируем эту функцию…
Далее запускаем функцию поиска места с которого будет происходить запись новых данных…
При старте микроконтроллера поиск будет сделан с самого начала выделенной памяти, об этом говорит аргумент STARTADDR . Функция вернёт адрес с которого будет происходить сохранение новых данных.Далее объявляем буфер из пяти 16-ти битных значений, четыре полезных, и один с нулём…
Разумеется буфер может быть любых размеров, главное чтоб был завершающий ноль.
Теперь самое основное, как работает поиск: когда мы запускаем функцию…Вне зависимости сделаны ли уже какие-то записи ранее, или память только что очищена, функция начинает перебирать ячейки и искать место с которого будут идти подряд 10 ячеек со значением 0хFF.
Визуально это выглядит так…
Ищем десять ячеек после последней записи.
Программно это происходит так…
Если 10 ячеек найдено, тогда возвращается адрес с которого нужно записывать очередные данные. Если память закончилась if(address == ENDMEMORY — 1) тогда очищаем всю память и возвращаем адреса начала памяти.Последний элемент буфера должен быть равен нулю потому, что если его не будет, и в последнем элементе полезных данных будет записано значение 0хFF, тогда функция поиска воспримет это как незаписанную ячейку.
В дальнейшем, по ходу программы, поиск будет происходить уже не с начала памяти, а с адреса который был возвращён при последнем поиске. То есть поиск с начала памяти ведётся только при старте МК. Может возникнуть вопрос — «зачем делать последующие поиски, ведь мы и так уже знаем где закончилась предыдущая запись» — да, это так, но нам в любом случае нужно проверить не закончилась ли память.
В бесконечном цикле создаём демонстрационный код, который раз в 10 секунд делает новую запись, и раз в 30 секунд читает последнюю запись…В функцию write_to_flash(wdata) передаётся указатель на массив с данными для сохранения. Внутри функции происходит поиск последней записи, записываются новые данные, создаётся контрольная сумма этих данных, потом записанные данные читаются, создаётся их контрольная сумма, и происходит сравнение обоих сумм. Таким образом проверяется корректность записи.
С помощью функции read_last_data_in_flash(rdata) можно читать последнюю запись когда угодно.Если нужно прочитать сохранённые данные при старте программы, тогда добавляем функцию чтения сразу после поиска адреса…
Это всё, всем спасибоЧитайте также: