Продолжая тему программирования контроллера STM32F4 и также работу с последовательной FLASH-памятью серии W25Q, мы попытаемся с помощью программы для ПК прочитать данные из памяти микросхемы. Причём прочитаем мы все данные полностью, и, мало того, попробуем их сохранить в файл. Это позволит нам полностью сохранить нашу прошивку, как говорят, сделать дамп FLASH-памяти, для дальнейшего её использования.
Программу также можно будет скачать по ссылке внизу страницы.
Схема урока не изменилась, осталась такая же, как и в прошлом уроке
И проект мы за основу также возьмём из прошлого урока с именем SPI_25Q32_INFO_TO_PC и присвоим ему новое имя SPI_25Q32_READ_TO_PC.
Откроем наш проект в Cube MX и прибавим скорость для UART, иначе мы будем очень долго считывать информацию даже из 32-килобитной микросхемы
stm32f4 работа с внутренней flash как записать — считать
Сгенерируем проект, откроем его в Keil, подключим файл w25q_spi.c, настроим программатор на автоперезагрузку, а также отключим оптимизацию.
Если мы теперь соберём прошьём проект, то, когда будем пробовать нашу программу в действии, то в программе для ПК, мы должны будем изменить значение битрейта
Прочитаем служебную информацию
Всё у нас работает.
Возвращаемся к нашему проекту.
Первым делом из файла w25q_spi.c в файл w25q_spi.h перенесём объявление структуры w25_info_t, иначе её тип не виден в других файлах. Объявление переменной данного типа не переносим, только самой структуры
Источник: narodstream.ru
STM32. Обзор и работа с Flash-памятью микроконтроллера.
Сегодняшняя статья, как вы уже поняли из названия, будет посвящена микроконтроллерам STM32 и работе со встроенной Flash-памятью. Да-да, именно с той памятью, в которой хранится прошиваемая нами программа. Поскольку в STM32 нет EEPROM (энергонезависимой памяти) для хранения данных можно использовать Flash-память контроллера, и сегодня мы как раз и разберемся, как же это работает.
Сразу же скажу, что согласно документации Flash-память в STM32 позволяет осуществить минимум 10000 циклов перезаписи, что в принципе достаточно для того, чтобы использовать ее и в качестве энергонезависимой памяти для хранения неких данных.
Давайте для начала разберемся со структурой. Возьмем в качестве примера контроллер семейства STM32F10x, относящийся к High-Density устройствам (например, STM32F103VET6). Его память выглядит следующим образом:
Считывание защищенной прошивки из флеш-памяти STM32F1xx с использованием ChipWhisperer
Как видите, все жестко структурировано. 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 необходимо записать два числа, одно за другим:
FLASH_KEYR = 0x45670123; FLASH_KEYR = 0xCDEF89AB;
В SPL для этого реализована функция FLASH_Unlock() . После разблокировки уже можно стирать и записывать данные. Для очистки будем использовать функцию:
FLASH_ErasePage(uint32_t Page_Address)
В качестве параметра мы должны передать в функцию адрес стираемой страницы. Итак, страница стерта, как записать данные? А для этого у нас есть:
FLASH_ProgramWord(uint32_t Address, uint32_t Data)
С аргументами тут все понятно — передаем адрес ячейки памяти и собственно записываемые данные. Осталось понять, как же считать данные из Flash-памяти. А для этого просто:
uint32_t FLASH_Read(uint32_t address)
Вот и все, ничего сложного, на этом сегодняшняя небольшая статья подходит к концу, в следующий раз мы будем обсуждать Bootloader, так что до скорого!
Источник: microtechnics.ru
STM32F030. Минимальная программа
Будем считать, что binutils установлен. Теперь не мешало бы убедиться, что даже с таким минимальным набором инструментов мы можем что-то творить.
В иных микроконтроллерах для таких дел обычно создается только файл программы. Но Cortex-ы — это сила и сложность, которые требуют к себе особого внимания. Это внимание выражается в тех дополнительных файлах, которые у других микроконтроллеров отсутствую напрочь. Наверно из-за этой сложности Cortex-ы не очень-то пошли в народ.
Сейчас, для нашей не столь великой цели, нам потребуется Только один дополнительный файл — файл для Линковщика.
Система Cortex действительно сложная. Собирать проекты по умолчанию (как это делается, например, для AVR) тут в принципе тоже можно. Но когда сам управляешь процессом сборки, то получаешь совершенно «другой коленкор»!
Программист, в этом случае, может указать на какие конкретные адреса распределять код и данные. Это огромная гибкость при создании проектов. И пока с этой гибкостью не поиграешься — не понятно — зачем все это?
Давайте теперь прикоснемся к великому и ужасному Cortex-у и научимся направлять его силу (чуть не сказал — дурь!) в нужном нам направлении. Давайте создадим для наших упражнений директорий и поместим в него два файла.
Первый файл — это исходник, файл нашей программы на языке ассемблера. Я его назвал start-0.S. Второй файл linker.ld — срипт для Линковщика.
Исходник start-0.S:
Скрипт для Линковщика linker.ld:
/* Зададим выходной формат поумолчанию elf32-littlearm */ OUTPUT_FORMAT(«elf32-littlearm», «elf32-bigarm», «elf32-littlearm») /* Определим целевую архитектуру */ OUTPUT_ARCH(arm) /* Зададим точку входа */ ENTRY(Reset_Handler) /* Теперь займемся распределением памяти */ SECTIONS < /* Флешь в STM32F030 начинается с адреса 0x08000000 */ .flash 0x08000000 : < /* Скажем Линковщику собрать таблицу обработчиков векторов прерываний на этот адрес */ KEEP(*(.vector_table)); /* Затем укажем Линковщику собрать все секции исполняемого кода в одну кучку */ *(.text .text.*); /* Затем — собрать все константы в другую кучку */ *(.rodata .rodata.*); >/* Оперативная память в STM32F030 начинается с адреса 0x20000000. */ /* Отсюда и начнем размечать память для размещения переменных */ /* Сначала должна разместиться секция инициализированных данных. */ /**************************************************************************************** Понятно, что начальные значения (значения для инициализации) должны где-то храниться, когда питание микроконтроллера выключено. Они и храняться во флешь-пямяти.
Но флешь-память — постоянная память, а нам для размещения переменых нужна опреативная память. Вот, в оперативной память мы и разместим переменные, а проинициализируем их значениями из флешь-памяти.
Хитрожопо, но по другому никак! *****************************************************************************************/ .data_at_ram 0x20000000: AT (LOADADDR(.flash) + SIZEOF(.flash)) < *(.data .data.*); >.ram : < /* Теперь скажем Линковщику собрать в этом месте все неинициализированные глобальные и статические переменные */ *(.bss .bss.*); >/* Теперь установим указатель стека на конец оперативной памяти. TOS = top of stack — вершина стека. BOS = bottom of stack — дно стека.
В момент инициализации вершина стека совпадает с его дном. Конец оперативной памяти зависит от конкретного типа микроконтроллера. Например, у меня SRM32F030F4, который имеет 4 кБ оперативы. */ __Initial_Stack_Value = 0x20000000 + (4 * 1024); /* Нехитрые вычисления дают верный результат*/ >
Можете их тупо закопипастить отсюда.
Процесс сборки нашей программы будет состоять из двух шагов. Сначала мы отассемблируем исходный текст программы, в результате чего получим объектный модуль. Затем мы произведем линковку этого модуля с… ничем. Проект наш крошечный, состоит из одного файла, библиотек не использует — поэтому линковать его не с чем.
Линковку производим только потому, чтобы получить из объектника файл формата .elf. У объектника адреса переменных, адреса функций, адреса переходов — непределены, а в .elf-е — все уже намертво привязано к своим адресам.
Ассемблирование производим командой
$ arm-none-eabi-as -mcpu=cortex-m0 -o start-0.o start-0.S
Специфика Линукса заключается в том, чтобы не орать (не засорять эфир) лишний раз, когда в этом нет необходимости. А необходимости нет только в одном случае — когда все получилось. Вот когда что-то не срослось, то нужно поднимать шум. И это правильно!
А если все хорошо и получилось именно то, что планировали — в чем прикол?
— Папа, я покакала! (с) из к/ф «О чем говорят мужчины» и из мой жизни тоже.
Поэтому, если после ассемблирования будет тишина, то паниковать не надо. Все под контролем. Наслаждайтесь умиротворяющей тишиной и спокойствием.
После ассемблирования давайте произведем линковку:
$ arm-none-eabi-ld -T linked.ld -o start-0.elf start-0.o
И в этом случае, если все прошло так, как задумывалось, тоже будет тишина. Но если посмотреть в директорий, то можно заметить, что появились два файла — с суфиксами .o и .elf.
Теперь давайте посмотрим на размеры программы:
$ arm-none-eabi-size start-0.elf text data bss dec hex filename 68 0 0 68 44 start-0.elf
Целых 68 байт для программы, которая ничего не делает. Но зато откомпилировалась без ошибок. Круто! Я надеюсь у вас тоже, как и у меня, — без ошибок.
Круто три раза!
Через какое-то время мы начнем наращивать мясо. Но сейчас немного поиграемся с инструментами binutils.
Давайте дизассемблируем нашу прогу обратно, и сравним с исходником:
$ arm-none-eabi-objdump -S start-0.elf
У меня получилась следующая хрень:
start-0.elf: формат файла elf32-littlearm Дизассемблирование раздела .flash: 08000000 : 8000000: 20001000 .word 0x20001000 8000004: 08000041 .word 0x08000041 8000008: 08000043 .word 0x08000043 800000c: 08000043 .word 0x08000043 8000010: 08000043 .word 0x08000043 8000014: 08000043 .word 0x08000043 8000018: 08000043 .word 0x08000043 . 800002c: 08000043 .word 0x08000043 8000030: 08000043 .word 0x08000043 8000034: 00000000 .word 0x00000000 8000038: 08000043 .word 0x08000043 800003c: 08000043 .word 0x08000043 08000040 : 8000040: e7fe b.n 8000040 08000042 : 8000042: e7fe b.n 8000042
В начале листинга идет таблица векторов. Обратите внимание на нечетный адрес переходов. Например, обработчик располагается по адресу 8000042, а в таблице указан адрес перехода на него как 08000043.
Это не глюк. Это так и должно быть. Дело в том, что в Cortex-ах принято соглашение, что если адрес перехода нечетный, то это будут команды из набора Thumb-2.
После таблицы мы видим две подпрограммы — два одинаковых обработчика, которые размещаются на адресах 08000040 и 08000042. Смешная команда (имя команды состоит из одной буквы — b) — команда перехода. Это так незатейливо организуется бесконечный цикл.
И еще прошу обратить внимание на размер команды перехода — команда двухбайтовая. А это дает посулы, что размеры программ для Cortex-ов не будут значительно больше аналогичных программ для AVR и MSP430. А теперь сравните у них цены и размеры флешь памяти, и станет ясно направление — куда катится мир.
Теперь давайте попробуем вытащить на свет божий адреса функций и переменных. (Адреса, куда они отлинковались)
$ arm-none-eabi-nm start-0.elf
У меня получился вот такой расклад:
08000042 t BusFault_Handler 08000042 t DebugMon_Handler 08000042 t HardFault_Handler 20001000 A __Initial_Stack_Value 08000042 t MemManage_Handler 08000042 t NMI_Handler 08000042 t PendSV_Handler 08000040 T Reset_Handler 08000042 t SVC_Handler 08000042 t SysTick_Handler 08000042 t UsageFault_Handler
Сейчас, разумеется, смотреть особо не на что — программа-то никакая! Задача этого этапа (этой программы) состоит в том, чтобы проверить работоспособность нашего инструментария и немного потренироваться на сборке программ.
Следующим этапом нам предстоит получить файлы описаний регистров и битов для микроконтроллера (STM32F030) и попытаться написать программу моргания светодиодом.
Источник: zhevak.wordpress.com