РадиоКот :: STM32F10x 32bit Cortex-M3 – ну очень быстрый старт или 10 простых примеров
Например TDA7294

РадиоКот >Статьи >

Теги статьи: Добавить тег

STM32F10x 32bit Cortex-M3 – ну очень быстрый старт или 10 простых примеров

Автор: Aheir
Опубликовано 14.02.2012
Создано при помощи КотоРед.

В последние пару лет микроконтроллеры на базе ядер ARM-семейства активно начали появляться не только в промышленных, но и во вполне себе любительских изделиях. Далеко не последнее место среди них занимают микроконтроллеры производства STMicroelectronics, о которых и пойдет речь ниже.

Причин для этого немало, судите сами: неплохая обеспеченность документацией, доступность средств разработки и отладки, вполне себе паябельные корпуса, более чем демократичная цена. А уж после того, как некоторое время назад была организована фактически бесплатная раздача отладочных комплектов STM32VLDiscovery, популярность этих микроконтроллеров стала весьма и весьма значительной.

Не вдаваясь глубоко в подробности (желающие без труда найдут дополнительную информацию), скажу лишь, что семейство микроконтроллеров STM32F10х построено на базе унифицированного ядра Cortex-M3 (это значит, что микроконтроллеры других производителей в части, касающейся ядра, совместимы между собой), обладает 32-разрядной архитектурой и может тактироваться с частотой до 72МГц. Различные микроконтроллеры семейства существенно отличаются по количеству набортной памяти и периферийных устройств, что позволяет подобрать МК, наиболее полно соответствующий поставленной задаче, а тот факт, что чипы в однотипных корпусах разных линеек совместимы по выводам, в случае необходимости позволяет повысить вычислительную производительность устройства без переработки печатной платы.

Короче говоря, мне тоже стало интересно, что это за звери такие, так что вот…

Для начала работы я приобрел уже упомянутую STM32VLDiscovery (цена в розницу порядка 400 руб).

 

На борту – микроконтроллер STM32F100RBT6B в 64-контактном корпусе cо 128кБ флеш-памяти и стандартным набором коммуникационных интерфейсов. Все выводы доступны на контактной гребенке, есть пользовательская кнопка и пара светодиодов. Кроме того, и это очень выручает, на плату интегрирован USB программатор-отладчик ST-LINK для STM32, который позволяет работать не только с установленным на плату контроллером, но и с любым другим: сигналы для программирования также доступны через разъем. Для начала работы вполне достаточно.

С точки зрения программного обеспечения я остановился на следующем наборе:

  1. Keil uVision посвежее для ARM
  2. утилита STM32 ST-LINK Utility для программирования микроконтроллера

Вообще, среди IDE для ARM вообще и STM32 в частности выбор имеется, так что вполне возможно для кого-то он будет иным.

Кроме того, для комфортной работы неплохо бы обзавестись набором из Cortex Microcontroller Software Interface Standard (CMSIS) и STM32F10x_StdPeriph_Driver (набор драйверов периферии STM32). CMSIS – библиотека ядра Cortex, как следует из унифицированности ядра – единая для всех МК на его основе. С набором драйверов все и так понятно. И то, и другое, доступно для скачивания с сайта производителя и содержится в архиве с обозначением AN3268: STM32VLDISCOVERY firmware package. Кроме того, полезным будет STM32_Init – набор для быстрого конфигурирования периферии (ту версию, которую я использую, можно скачать в конце статьи)

Вобщем, Keil и прошивальщик установили, исходников накачали, теперь надо со всем этим попытаться взлететь)

Сначала создаем где-нибудь папку под наши эксперименты, в ней подпапку под конкретный проект, формируем такую структуру (названия папок произвольны, но так хотя бы понятно, что где лежит, папку output создавать необязательно, сама сгенерится):

 

Теперь разархивируем STM32VLDISCOVERY firmware package и раскладываем файлы по папкам.

В CMSIS кладем «core_cm3.h» и «core_cm3.c» из «/stm32vldiscovery_package/Libraries/CMSIS/CM3/CoreSupport/»

и «system_stm32f10x.h» и «system_stm32f10x.c» из

«stm32vldiscovery_package/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/».

В config - «stm32f10x.h» из «stm32vldiscovery_package/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/»

и «stm32f10x_conf.h» из любого проекта в «stm32vldiscovery_package/Project/Examples/».

В startup – все файлы из «stm32vldiscovery_package/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/arm/».

В std_periph_lib – все файлы из

«stm32vldiscovery_package/Libraries/STM32F10x_StdPeriph_Driver/src/» и

«stm32vldiscovery_package/Libraries/STM32F10x_StdPeriph_Driver/inc/».

В STM32_Init – содержимое скачанного архива.

В корне папки «source» создаем файл «main.c».

В корневой папке проекта создаем файл «flash.bat» следующего содержания:

"…/ST-LINK_CLI.exe" -c SWD -ME -P "output/keil_project.hex" 0x08000000 -Rst –Run

где … путь к директории установки STM32 ST-LINK Utility (говоря по простому, это батник для запуска прошивальщика)

Уже готовый проект можно взять в конце статьи, так что потребуется только исправить путь к прошивальщику в батнике.

Теперь нужно настроить Keil. После запуска программы, выбираем «Проект-Создать новый», сохраняем файл проекта с приятным слуху именем в корневую папку проекта.

Далее Keil предложит выбрать целевой микроконтроллер, укажем то, что установлено на нашей плате:

Справа можно почитать кратенькую характеристику микроконтроллера.

Говорим ОК, Keil предлагает скопировать свой startup для выбранного микроконтроллера в папку проекта, отказываемся.

Создается структура проекта, пока что пустая.

Путем прямых и контекстных кликов на членах структуры проекта, выбирая пункты подменю «добавить группу» и «Добавить файлы в группу» добиваемся того, чтобы структура приобрела следующий вид:

Т.е. мы добавили в проект все файлы, распределенные по папкам ранее. Вообще, можно было не копировать вручную файлы по разным папкам, а использовать уже сформированную структуру каталогов из stm32vldiscovery_package. Однако лично мне так понятнее, что с чем взаимодействует и я точно знаю, что где лежит. Не претендую на истину, просто мне так удобнее.

Теперь идем в настройки проекта: через меню Проект-Опции для Project или просто Alt+F7. Device и Target просматриваем, но не трогаем. На вкладке Output жмакаем «Select Folder for Objects», в качестве целевой выбираем папку output в корне проекта (если еще не создали – создаем). Имя исполняемого файла – keil_project или любое другое. Ставим галку Create HEX File – пусть будет.

Если хотим получить еще и bin-файл (а это моет быть полезно при использовании сторонних программ-прошивальщиков, бутлоадеров или хотя бы просто для наглядного представления размера прошивки), на вкладке User заполняем строку:

 

Это запустит встроенную утилиту преобразования. Имя bin может быть любым (мы же его создаем), а вот axf должен назваться так же, как исполняемый файл на вкладке Output.

На вкладке С/С++ жмакаем на Include Paths и заполняем в появившемся окне пути, по которым компилятор будет искать все требуемые файлы.

Должно получиться что-то такое:

В итоге строка на вкладке заполнится этими же путями, перечисленными через точку с запятой:

На вкладке Debug оставляем все как есть, или выбираем в правой части окна ST-Link в качестве отладчика.

На последней вкладке делаем следующие настройки:

Тем самым при запросе на прошивку микроконтроллера мы будем вызывать STM32 ST-LINK Utility через батник, созданный ранее. Поясню, зачем все это надо. Если использовать встроенный в Keil прошивальщик, то в силу неких внутренних глюков особенностей, прошивка происходит только при запуске отладки через Debug-Start Session; при Flash-Download прошивки не происходит. Это весьма неудобно. Компромиссное решение и предложено выше.

Если теперь открыть в редакторе main.c и добавить туда что-то вроде «int main(void) {}», то проект вполне себе скомпилируется по команде «Project-Rebuild all target files».

На этом первичная настройка закончена, эту «рыбу» можно использовать далее при разработке приложений.

 

В процессе изучения STM32 я столкнулся с тем, что ответы на казалось бы совершенно простые вопросы, даже в условиях имеющихся вроде как примеров и т.д., приходилось искать самостоятельно. Это и хорошо, и неудобно одновременно. А говорю я это к тому, что далее следуют 10 проектов-примеров, реализующих какие-либо простые операции на микроконтроллере, «заточенные» и опробованные на STM32VLDiscovery, сделанные мной (как правило, с использованием каких-то примеров) и отвечающие на те вопросы, которые возникали у меня. Возможно, и даже скорее всего, у вас будут другие вопросы и эти проекты покажутся вам кривыми, неоптимальными, прививающими плохие привычки в программировании и т.д. Возможно, это и так, но мне они в чем-то помогли, чего и вам желаю.

 

Проект 1 – помигаем светодиодом.

 

Скачиваем архив проекта. Далее либо открываем имеющийся проект, либо копируем в main.c нашего шаблона все содержимое того же файла из скачанного архива. std_periph_lib мы пока пользоваться не будем, поэтому соответствующую группу можно просто удалить. Открываем файл stm32f10x.h из группы config, убеждаемся, что раскомментирован дефайн «STM32F10X_MD_VL»:

Этот дефайн задает линейку контроллера (читай – объемы памяти), с которым мы собираемся работать. Чуть ниже в этом же файле можно почитать, что это за «MD_VL» такой, какие еще бывают и чем отличаются. В данном проекте для нас не особо важна принадлежность МК к какой либо линейке, но лучше сразу привыкнуть отслеживать этот момент.

Смотрим в исходник.

Всего имеется 3 функции.

InitAll – инициализация порта PORTC.8 как выхода (к этой ножке на STM32VLDiscovery прицеплен синий светодиод)

Delay – задержка, основанная на пустых циклах

main – проводит инициализацию и затем в цикле меняет состояние ножки со светодиодом.

В ходе инициализации стоит обратить внимание на строку RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; Это – включение тактирования порта. Вообще, в STM работа почти с любой периферией начинается с включения тактирования. Логика понятна: если периферия не требуется, зачем тратить энергию на ее работу? Поэтому большинство узлов по умолчанию отключено от источников тактового сигнала. Далее идет запись в управляющий регистр порта ввода/вывода. Смысл операций рекомендую исследовать самостоятельно, описание определений можно подсмотреть в том же stm32f10x.h, а описание регистров в объемном документе «RM0041 STM32F100xx Reference Manual» - более 600 страниц описания микроконтроллера – доступен на сайте STMicroelectronics. Вобщем, даже со стороны документации ни разу не AVR… Там-то даташита вполне достаточно, а здесь в даташите можно посмотреть разве что распиновку да электрические параметры. Большая часть остальной необходимой информации в уже упомянутом документе, а так же куче других, не менее объемных.

Теперь можем нажать «Project-Rebuild all target files» и, если все сделано верно, в нижней части экрана увидим лог работы компилятора:

Ошибок не обнаружилось, можно прошить контроллер. Считаем, что Discovery к компу уже подключена (каких-либо настроек или драйверов она не требует), поэтому просто жмем «Flash-Download». Процесс прошивки много времени не займет:

К этому времени плата уже во всю будет мигать синим светодиодом…

Закончили упражнение)

 

Пример 2 – помигаем светодиодом с помощью StdPeriph_Lib

Как уже отмечалось, StdPeriph_Lib – набор драйверов для работы с периферией STM32. Удобно тем, что все управление осуществляется вызовом функций, более того, различная периферия имеет схожие по принципам построения и структуре названий функции, так что все это достаточно несложно запоминается. Минусы стандартные для такого подхода: дополнительные потери на вызовы функций, отсутствие прозрачности процесса настройки (чего оно там куда пишет, в какие регистры – неизвестно) и т.д. Тем не менее – весьма эффективно.

Открываем проект, смотрим на структуру:

std_periph_lib уже не пустая. Какие файлы за что там отвечают понятно по названиям, кроме того, стоит ознакомиться с содержимым h-файлов, многое станет понятнее. Для совсем глубокого понимания изучаем еще и с-файлы. Чтобы начать их использовать, в файле stm32f10x_conf.h нужно раскомментировать соответствующие строки:

Вообще, неиспользуемые файлы из этого набора лучше удалять из структуры проекта, в противном случае компилятор при сборке все равно ворошит их, что заметно замедляет процесс вцелом.

В самом проекте изменилась функция инициализации (теперь конфигурирование порта заключается в присвоении значений соответствующим полям структуры, описывающей единицу периферии – такой подход в рамках StdPeriph_Lib применяется для работы с любой периферией), в основном цикле сброс/установка порта так же происходит с помощью функции, описанной в stm32f10x_gpio.h

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

 

Пример 3 – чем нам поможет STM32_Init

Как уже отмечалось, STM32_Init – набор для быстрого конфигурирования микроконтроллера.

Открываем проект, видим в структуре группу STM32_Init. Если открыть STM32_Init.c, в левом нижнем углу поля текстового редактора можно увидеть следующую картину:

Если ткнуть на Configuration Wizard, увидим следующее:

Думаю, комментарии излишни?

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

Я этот механизм использовал в том числе и так: что-нибудь настроил, проверил, что работает, потом снял галочки и настроил все то же самое вручную. Работает? Прекрасно, значит, разобрался еще с кусочком.

 

Пример 4 – системный таймер

Системный таймер – часть стандартизованного ядра Cortex, т.е. есть у всех Cortex-процессоров. Особо гибкими настройками не обладает, но тем не менее может использоваться для отсчета временных интервалов для RTOS или иных задач.

Здесь мы помигаем светодиодом с использованием системного таймера для генерации задержек.

Открываем проект, в Визарде STM32_Init видим такие настройки:

Поскольку период счета настроен равным 1мс, весьма удобно сделать отдельную функцию задержки типа delay_ms(unsigned int).

Именно это и реализовано в проекте.

Функция-обработчик прерывания должна называться SysTick_Handler (). Вообще, все эти названия можно подсмотреть в startup_stm32f10x_md_vl.s Компилируем, прошиваем, видим, что частота перемигивания существенно возросла.

 

Пример 5 – таймер общего назначения.

В STM32 довольно много таймеров общего назначения. Исходно они 16-ти битные, однако можно включать их последовательно для образования 32-разрядного счетчика, имеется возможность генерации аппаратного ШИМ, входы захвата и т.д., и т.п. Настроек много, режимов работы тоже – нужно изучать мануал и сопутствующую документацию.

Мы пока ограничимся прерыванием и реализацией программного ШИМа для управления яркостью светодиода.

Открываем проект, в STM32_Init сделаны следующие настройки:

Период счета таймера (используем таймер 2) 40 мкс, разрешено прерывание по обновлению (т.е. каждые 40 мкс). Функция-обработчик - TIM2_IRQHandler ().

В самом начале обработчика прерывания происходит проверка причины, по которой мы там, собственно, оказались, и сброс соответствующего флага. Здесь это сделано с помощью непосредственной записи в регистры, но можно использовать и функции StdPeriph_Lib. Стоит взять за правило сразу после входа в прерывание обрабатывать его флаги. В противном случае, в ряде ситуаций, несброшенный вовремя флаг микроконтроллер, даже находясь внутри прерывания, может расценить как возникновение еще одной исключительной ситуации, в результате чего после выхода из прерывания мы сразу же влетим туда еще раз. Так что, во избежание…

В основном цикле с помощью флага, выставляемого в прерывании, каждые 200 мс изменяется состояние зеленого светодиода, кроме того, каждые 5 мс происходит изменение значения яркости синего светодиода, что в сочетании с 8-битным счетчиком дает 256*5 = 1280 мс период его плавного угасания.

Компилируем, прошиваем, наслаждаемся…

 

Пример 6 – USART

Здесь освоим простую отправку единичных байт информации через USART микроконтроллера. Чем мне понравились STM, так это наличием как минимум двух USART даже на младших контроллерах. Весьма часто лично мне такое требуется, весьма…

Открываем проект, USART настроен так:

Поскольку собрались использовать новую периферию, не забываем подтаскивать нужные файлы в std_periph_lib и раскомментировать соответствующие строки в stm32f10x_conf.h

В основном цикле программы добавлена строка, которая синхронно с изменением состояния зеленого светодиода производит отправку абстрактных данных в UART1.

Компилируем, прошиваем.

Подцепив это к COM-порту компьютера (с использование соответствующего согласования на MAX232 или USB-RS232 преобразователя, что гораздо удобнее) в терминале получим следующую картину:

 

Пример 7 – прерывание по приему USART

Скачиваем проект. В настройках разрешены прерывания для USART1 по приему символа и по ошибке приема.

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

Кроме того, подцеплены функции форматированного вывода. Для этого к проекту добавлен файл retarget.c (есть в составе Keil’a), который изменен так, чтобы отправка единичного символа осуществлялась через функцию sendchar.

В зависимости от принятых данных система обеспечивает  различный отклик:

Пример 8 – декодер RC5

В принципе, среди аппнотов STM32 есть реализация RC5 декодера, но лично у меня с наскока она не заработала (да и не только у меня), так что вернусь к ней позднее, а пока просто адаптируем исходник из статьи «Некоторые протоколы ИК-пультов. Часть 2» . Там можно ознакомиться с принципами построения и алгоритмом работы этого декодера. Писано было под AVR, ну дык на Си же, так что какие проблемы?

Используем более тонкую настройку таймера:

Предделитель – 768, что при тактировании от частоты 24МГц (а это именно так, поскольку умолчальные настройки системы тактирования (внешний кварц на 8МГц * 4) мы в наших проектах не трогали) дает частоту счета таймера  24МГц / 768 = 31250Гц – как и в исходнике.

Ограничив таймер значением счета в 256 получаем практически 8-битный счетчик – аналог таймера AVR.

Далее все сводится к включению внешнего прерывания:

Весьма радует, кстати, что можно назначить прерывание на любую ногу микроконтроллера. Их всего (или целых?) 16, и номер пина должен быть уникальным (т.е. одновременно использовать С.9 и В.9 как входы прерывания, насколько я понимаю, не получится), но при должном подходе к проектированию это не проблема. Нога В.9 толерантна к 5В уровню, поэтому выход TSOP1736 подключаем к ней напрямую.

Ну а дальше все сводится к использованию функций для работы с таймером, типа старт/стоп счета, считывание/установка значения.

Результат выводится опять же через USART:

Timer Overflow – ложные срабатывания фотоприемника (внешние помехи).

 

Пример 9 – эмуляция ЕЕПРОМ

При всем богатстве периферии STM32F100RBT6B, ЕЕПРОМ в нем, как впрочем и во всех STM32F10х, отсутствует. Производитель мотивирует это тем, что в противном случае не удалось бы поддерживать настолько низкую цену. Впрочем, сознавая полезность энергонезависимой памяти, STMicroelectronics предлагает воспользоваться документом AN2594 - EEPROM Emulation. В нем описывается способ записи во флеш-память микроконтроллера произвольных данных пользователя непосредственно из программы. Flash имеет свои плюсы и минусы при таком использовании, однако если писать нужно не сильно часто, а вопрос цены изделия приоритетен, это вполне оправданное решение. Производитель предлагает тестовый проект с исходниками, однако пример в нем не очень интересен. Я взял из него файлы eeprom.c и eeprom.h (собственно это и есть драйвер для записи во flash) и сделал свою демку на их основе.

Как всегда скачиваем, компилируем, прошиваем проект.

При каждом включении проверяется содержимое ячейки памяти по указанному адресу. Если оно не совпадает с определенным значением, это значение туда записывается и программа останавливается. Если совпадает – проверяется вторая ячейка, если и она совпадает – третья. Так в цикле по трем ячейкам; если все 3 заполнены нужными значениями, они стираются и начинается новый цикл. Вся информация выводится в терминал:

Один момент. Для работы с flash должен быть включен внутренний тактовый генератор. Этот момент описан в PM0075 STM32F10xxx Flash memory microcontrollers.

 

Пример 10 – I2C

Микроконтроллер, который мы на данный момент мучаем, имеет на борту два аппаратных I2C интерфейса. Вообще, к I2C на STM32 масса претензий, вплоть до полной неработоспособности у некоторых разработчиков и отказа от аппаратного модуля в пользу программной эмуляции интерфейса. Конечно, многое зависит от конкретного разработчика, но дыма без огня, как известно, почти не бывает, а нареканий в И-нете встречается ну уж больно много… Как бы там ни было, у меня после небольшого допиливания вполне пристойно заработал пример от уважаемого коллеги GYUR22   отсюда . Результаты приведены в примере в папке «010.1 Discovery Hard I2C». Я подключал EEPROM AT24C256-10PI, сделал простейший счетчик стартов микроконтроллера. При старте микроконтроллер читает значение байта по нулевому адресу, увеличивает на 1, записывает полученное значение по нулевому адресу и по адресу, равному полученному значению. Т.е. при первом старте содержимое первых 256 байт EEPROM выглядит так:

После нескольких включений:

После 11 включений происходит сброс и перезапись EEPROM значениями по умолчанию.

0x77 в последнем байте – просто контрольное значение.

Позволю себе заметить, что упомянутая в статье по ссылке выше необходимость задержки после акта записи в ЕЕПРОМ вполне укладывается в указанные в даташите тайминги: время цикла записи при питании от 2.7 до 5 В до 10 мс.

В папке «010.2 Discovery Soft I2C» скачанного архива содержится проект программной эмуляции I2C интерфейса. Это адаптированные под STM32 исходники, которые я довольно долго и успешно использовал на AVR. В данном случае я просто подменил все «аппаратные» процедуры из первого примера программными аналогами, поэтому с точки зрения main.c и логики работы никаких изменений нет.

 

Я в результате всех этих изысканий получил базу простых заведомо рабочих примеров, которые могут служить отправной точкой для дальнейшего изучения этих все же довольно сложных микроконтроллеров. Очень много осталось за кадром: система тактирования, контроллер прерываний, масса аппаратных интерфейсов и модулей микроконтроллера, особенности программирования под него…  Тем не менее надеюсь, что у читателя уже появилась возможность самостоятельно сделать какие-то простейшие программы, хотя бы на уровне мигания светодиодом. Как говорится, лиха беда – начало. Так что не все так страшно в ARM-процессорах, все можно освоить, чего и вам желаю, и чем сам планирую заниматься.

 


Файлы:
Пример1
Пример2
Пример3
Пример4
Пример5
Пример6
Пример7
Пример8
Пример9
Пример10
Шаблон проекта
STM32_Init


Все вопросы в Форум.




Как вам эта статья?

Заработало ли это устройство у вас?

70 6 2
11 1 0