Подключение стандартной библиотеки периферии к любому семейству STM32. Подключаем к проекту библиотеки. Упрощенная реализация printf

При создании первого приложения на микроконтроллере STM32 можно пойти несколькими путями. Первый, классический, берём точное описание контроллера на сайте www.st.com , которое фигурирует под названием «Reference Manual» и читаем описание регистров периферии. Дальше пробуем записывать их и смотрим, как работает периферия. Чтение этого документа очень полезно, но на первом этапе освоения микроконтроллера от этого можно отказаться, как это не странно. Инженеры STMicroelectronics написали библиотеку драйверов стандартной периферии. Более того, они написали множество примеров использования данных драйверов, что может свести программирование вашего приложения к нажатию клавиш Ctrl+C и Ctrl+V, с последующей небольшой правкой примера использования драйвера под свои потребности. Таким образом, подключение библиотеки драйверов периферии к вашему проекту является вторым методом построения приложения. Кроме скорости написания есть и другие достоинства этого метода: универсальность кода и применение других фирменных библиотек, таких как USB, Ethernet, управление приводом и т.д., которые предоставляются в исходниках и использующих стандартный драйвер периферии. Недостатки данного метода тоже есть: Там где можно обойтись одной строкой кода стандартный драйвер периферии STM32 напишет 10. Сама библиотека периферии также предоставляется в виде исходных файлов, так что можно проследить какой бит какого регистра меняет та или иная функция. При желании можно будет перейти от второго метода написания программы к первому, закомментировав часть кода, использующего стандартную библиотеку на свою, управляющую непосредственно регистром периферии. В результате такого действия вы выиграете в скорости управления, объёме ОЗУ и ПЗУ, а потеряете в универсальности кода. В любом случае, инженеры компании "Промэлектроника" рекомендуют использовать библиотеку стандартной периферии хотя бы на первом этапе.

Наибольшие трудности ожидают разработчика при подключении библиотеки к своему проекту. Если не знать, как это делать, можно потратить много времени на это мероприятие, что противоречит самой идее использования готового драйвера. Материал посвящён подключению стандартной библиотеки к любому семейству STM32.

Каждое семейство STM32 имеет свою библиотеку стандартной периферии. Это связано с тем, что сама периферия разная. Например, периферия контроллеров STM32L в качестве одной из задач имеет функцию энергосбережения, что тянет за собой добавление функций управления. Классическим примером, можно считать АЦП, который в STM32L имеет возможность аппаратного отключения, при длительном отсутствии команды преобразования – одно из следствий задачи энергосбережения. АЦП контроллеров семейств STM32F не имеют такой функции. Собственно говоря, в силу наличия аппаратной разницы периферии имеем разные библиотеки драйверов. Кроме очевидного различия функций контроллера имеет место улучшение периферии. Так, периферия контроллеров семейств, которые были выпущены позже, может быть более продуманной и удобной. Например, периферия контроллеров STM32F1 и STM32F2 имеет различия по управлению. На взгляд автора управление периферией STM32F2 более удобное. И это понятно почему: STM32F2 семейство было выпущено позже и это позволило разработчикам учесть некоторые нюансы. Соответственно, для данных семейств – индивидуальные библиотеки управления периферией. Идея вышесказанного проста: на странице микроконтроллера, который вы собираетесь использовать, находится подходящая ему библиотека периферии.

Несмотря на различие периферии в семействах драйверы скрывают 90% различий внутри себя. Например, функция настройки, упомянутого выше АЦП, для всех семейств выглядит одинаково:

void ADC_Init(ADC_Nom, ADC_Param) ,

где ADC_Nom – номер АЦП в виде ADC1, ADC2, ADC3 и т. д.

ADC_Param – указатель структуры данных, каким образом надо настроить АЦП (от чего запускаться, сколько каналов оцифровывать, выполнять ли это циклически и т.д.)

10% различий семейств, в данном примере, которые придётся поправить при переходе с одного семейства STM32 на другое, спрятаны в структуре ADC_Param. В зависимости от семейства количество полей этой структуры может быть разным. Общая же часть, имеет одинаковый синтаксис. Таким образом, перевод приложения для одного семейства STM32, написанного на базе стандартных библиотек периферии на другое, весьма прост. В части универсализации решений на микроконтроллерах STMicroelectronics неотразим!

Итак, мы скачали библиотеку для применяемого STM32. Что дальше? Далее, нам надо создать проект и подключить к нему требуемые файлы. Создание проекта рассмотрим на примере среды разработки IAR Embedded Workbench. Запускаем среду разработки и заходим на вкладку “Project”, выбираем пункт создания проекта “Create project”:

В появившемся новом проекте вводим настройки, наведя курсор на название проекта, нажав правую клавишу мыши и выбрав в выпавшем меню «Options»:

Области памяти ОЗУ и ПЗУ:

При нажатии кнопки “Save” среда предложит записать новый файл описания контроллера в папку проекта. Автор рекомендует создавать каждому проекту индивидуальный файл *.icp и хранить его в папке с проектом.

Если вы собираетесь внутрисхемно отлаживать свой проект, что рекомендуется, то вводим тип применяемого отладчика:

На вкладке выбранного отладчика указываем интерфейс подключения отладчика (в нашем случае выбран ST-Link) к контроллеру:



С этого момента наш проект без библиотек готов к компиляции и загрузке в контроллер. В других средах, таких как Keil uVision4, Resonance Ride7 и т. п. потребуется выполнить эти же действия.

Если в файле main.c написать строку:

#include "stm32f10x.h" или

#include "stm32f2xx.h" или

#include "stm32f4xx.h" или

#include "stm32l15x.h" или

#include "stm32l10x.h" или

#include "stm32f05x.h"

с указанием расположения данного файла, либо копированием данного файла в папку проекта, то произойдёт ассоциация некоторых областей памяти с регистрами периферии соответствующего семейства. Сам файл находится в папке библиотеки стандартной периферии в разделе: \CMSIS\CM3\DeviceSupport\ST\STM32F10x (или похожее по названию для других семейств). С этого момента вы заменять адрес регистра периферии в виде числа его названием. Даже если вы не собираетесь использовать функции стандартной библиотеки, рекомендуется сделать такое подключение.

Если вы собираетесь использовать в своём проекте прерывания, то рекомендуется подключить стартовый файл с расширением *.s, который находится по пути \CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\iar, или подобном, для других семейств. Важно отметить, что для каждой среды файл свой. Соответственно, если мы используем IAR EWB, то должны взять файл из папки IAR. Это обусловлено небольшим различием синтаксиса сред. Поэтому, чтобы проект запустился сразу, инженеры STMicroelectronics написали несколько вариантов стартовых файлов для нескольких наиболее популярных сред разработки. Большинство семейств STM32 имеют один файл. Семейство STM32F1 имеет несколько запускающих фалов:

  • startup_stm32f10x_cl.s – для микроконтроллеров STM32F105/107
  • startup_stm32f10x_xl.s - для микроконтроллеров STM32F101/STM32F103 768кб и более
  • startup_stm32f10x_hd.s - для микроконтроллеров STM32F101/STM32F103 c памятью Flash 256-512 кб
  • startup_stm32f10x_md.s - для микроконтроллеров STM32F101/ STM32F102/STM32F103 c памятью Flash 64-128 кб
  • startup_stm32f10x_ld.s - для микроконтроллеров STM32F101/ STM32F102/STM32F103 c памятью Flash менее 64кб
  • startup_stm32f10x_hd_vl.s для микроконтроллеров STM32F100 c памятью Flash 256-512 кб
  • startup_stm32f10x_md_vl.s для микроконтроллеров STM32F100 c памятью Flash 64-128 кб
  • startup_stm32f10x_ld_vl.s для микроконтроллеров STM32F100 c памятью Flash 32кб и менее

Итак, в зависимости семейства, подсемейства и среды разработки добавляем запускающий файл в проект:

Именно здесь оказывается микроконтроллер при старте программы. Прерывание последовательно вызывает функцию SystemInit(), а затем __iar_program_start. Вторая функция обнуляет либо записывает заранее заданные значения глобальных переменных, после чего осуществляет переход в программу пользователя main(). Функция SystemInit() настраивает тактирование микроконтроллера. Именно она даёт ответы на вопросы:

  • Надо ли переключаться на внешний кварц (HSE)?
  • Как умножить частоту от HSI/HSE?
  • Требуется ли подключение очереди загрузки команд?
  • Какая требуется задержка при загрузке команды (из-за низкой скорости работы Flash памяти)
  • Как поделить тактирование шин периферии?
  • Требуется ли разместить код во внешней ОЗУ?

Функцию SystemInit() можно прописать вручную в своём проекте. Если оформить эту функцию как пустую, то контроллер будет работать на внутреннем RC-генераторе с частотой порядка 8МГц (в зависимости от типа семейства). Вариант 2 – подключить к проекту файл system_stm32f10x.c (либо похожий по названию в зависимости от типа используемого семейства), который расположен в библиотеке по пути: Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x. В этом файле имеется функция SystemInit(). Обратите внимание на частоту внешнего кварца HSE_VALUE. Данный параметр выставляется в заголовочном файле stm32f10x.h. Стандартное значение 8 и 25МГц, в зависимости от семейства STM32. Основная задача функции SystemInit() – переключить тактирование на внешний кварц и умножить определённым образом данную частоту. Что произойдёт, если значение HSE_VALUE указано 8МГц, ядро должно тактироваться частотой 72МГц, а по факту на плате стоит кварц 16МГц? В результате таких некорректных действий ядро получит тактирование 144МГц, которые могут оказаться за пределами гарантированной работы системы на STM32. Т.е. при подключении файла system_stm32f10x.c потребуется указать значение HSE_VALUE. Всё это означает, что файлы system_stm32f10x.c, system_stm32f10x.h и stm32f10x.h (или похожие по названию для других семейств) должны быть индивидуальными для каждого проекта. И

нженеры STMicroelectronics создали инструмент Clock Configuration Tool, который позволяет правильно настроить тактирование системы. Это файл Excel, генерирующий файл system_stm32xxx.c (похожий по названию для заданного семейства семейств), после задания входных и выходных параметров системы. Рассмотрим его работу на примере STM32F4 семейства.

Варианты: внутренний RC-генератор, внутренний RC-генератор с умножением частоты, либо внешний кварц с умножением частоты. После выбора источника тактирования вводим параметры желаемой конфигурации системы, такие как входную частоту (при использовании внешнего кварца), частоту тактирования ядра, делители частоты тактирования шин периферии, работу буфера выборки команд и другие. Нажав на кнопку “Generate”, получаем окно


Подключение файла system_stm32f4xx.c и его аналогов потребует подключение ещё одного файла стандартной библиотеки периферии. Для управления тактированием имеется целый набор функций, которые вызываются из файла system_stm32xxxxxx.c. Эти функции расположены в файле stm32f10x_rcc.c и его заголовке. Соответственно, подключая к проекту файл файла system_stm32xxxxxx.c требуется подключить stm32f10x_rcc.c, иначе линкер среды сообщит об отсутствии описания функций с именем RCC_xxxxxxx. Указанный файл находится в библиотеке периферии по пути: Libraries\STM32F10x_StdPeriph_Driver\src, а его заголовок \Libraries\STM32F10x_StdPeriph_Driver\inc.

Подключение заголовочных файлов драйвера периферии происходит в файле stm32f10x_conf.h, на который ссылается stm32f10x.h. Файл stm32f10x_conf.h – это просто набор заголовочных файлов драйверов конкретных периферий контроллера, подлежащих включению в проект. Изначально все заголовки «#include» отмечены как комментарии. Подключение заголовочного файла периферии заключается в снятии комментария с соответствующего названия файла. В нашем случае – это строка #include "stm32f10x_rcc.h". Очевидно, что файл stm32f10x_conf.h индивидуален для каждого проекта, т.к. разные проекты используют разную периферию.

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



Пути к заголовочным файлам могут быть иными, в зависимости от расположения библиотеки периферии относительно папки проекта, а вот наличие “ USE_STDPERIPH_DRIVER” – обязательно при подключении драйверов периферии стандартной библиотеки.

Итак, мы подключили стандартную библиотеку, к проекту. Более того, мы подключили один из стандартных драйверов периферии к проекту, выполняющих управление тактированием системы.

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



Таким образом, подключение заголовочного файла stm32f10x.h в приложении тянет за собой подключение других заголовочных файлов и файлов кода. Некоторые из представленных на рисунке описаны выше. Несколько слов об остальных. Файлы STM32F10x_PPP.x – это файлы-драйверы периферии. Пример подключения такого файла показан выше, это RCC – периферия управления тактированием системы. Если мы хотим подключить драйверы другой периферии, то название подключаемых файлов получается заменой «PPP» названием периферии, например АЦП - STM32F10x_ADC.с, или порты ввода-вывода STM32F10x_GPIO.с, или ЦАП - STM32F10x_DAC.с. В целом интуитивно понятно какой файл нужно подключить при подключении заданной периферии. Файлы «misc.c», «misc.h» - это по большому счёту те же STM32F10x_PPP.x, только выполняют управление ядром. Например настройку векторов прерываний, который встроен в ядро или управление таймером SysTick, который является частью ядра. Файлы xxxxxxx_it.c описывают вектора немаскируемых прерываний контроллера. Они могут быть дополнены векторами прерываний периферии. Файл core_m3.h описывает ядро CortexM3. Данное ядро стандартизовано и может встречаться в микроконтроллерах других производителей. Для кросс-платформенной универсализации компания STMicroelectronics провела работу по созданию отдельной библиотеки ядра CortexM, после чего компания ARM стандартизировала её и распространила на других производителей микроконтроллеров. Так что переход на STM32 с контроллеров других производителей с ядром CortexM будет чуть проще.

Итак, мы можем подключить библиотеку стандартной периферии к любому семейству STM32. Того, кто научился это делать ждёт приз: очень простое программирование микроконтроллеров. Библиотека кроме драйверов в виде исходных файлов содержит множество примеров применения периферии. Для примера, рассмотрим создание проекта с участием выходов сравнения таймера. При традиционном подходе мы будем внимательно изучать описание регистров данной периферии. Но сейчас мы можем изучить текст работающей программы. Заходим в папку примеров стандартной периферии, которая находится по пути ProjectSTM32F10x_StdPeriph_Examples. Здесь находятся папки примеров с названием применяемой периферии. Заходим в папку «TIM». Таймеры в STM32 имеют множество функций и настроек, поэтому одним примером возможности контроллера продемонстрировать невозможно. Поэтому внутри указанного каталога имеется множество примеров применения таймеров. Нас интересует генерация ШИМ сигнала таймером. Заходим в папку «7PWM_Output». Внутри имеется описание работы программы на английском языке и набор файлов:

main.c stm32f10x_conf.h stm32f10x_it.h stm32f10x_it.c system_stm32f10x.c

Если проект не имеет прерываний, то содержательная часть полностью расположена в файле main.c. Копируем эти файлы в каталог проекта. Скомпилировав проект, мы получим программу для STM32, которая настроит таймер и порты ввода-вывода на генерацию 7-ми ШИМ сигналов от таймера 1. Далее, мы можем приспособить уже написанный код под свою задачу. Например, уменьшить число ШИМ сигналов, изменить скважность, направление счёта и т.п. Функции и их параметры хорошо описаны в файле stm32f10x_stdperiph_lib_um.chm. Названия же функций и их параметров легко ассоциируются с их назначением для тех, кто немного знает английский язык. Для наглядности приводим часть кода взятого примера:

/* Time Base configuration */ TIM_TimeBaseStructure.TIM_Prescaler = 0; // предделение счётных импульсов отсутствует (16-битный регистр) TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // направление счёта вверх TIM_TimeBaseStructure.TIM_Period = TimerPeriod; // счёт выполнять до значения TimerPeriod (константа в программе) TIM_TimeBaseStructure.TIM_ClockDivision = 0; // предделение счётных отсутствует TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; // счётчик переполнений для генерации событий (не используется в программе) TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); // ввод значений TimeBaseStructure в регистры таймера 1 (ввод данных в эту // переменную - выше) /* Channel 1, 2,3 and 4 Configuration in PWM mode */ // настройка ШИМ выходов TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; // режим работы ШИМ2 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // разрешитьвыход ШИМ сигналов таймера TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; // разрешить комплиментарный выходШИМтаймера TIM_OCInitStructure.TIM_Pulse = Channel1Pulse; // ширина импульс Channel1Pulse – константа в программе TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; // настройка полярности выхода TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; // настройка полярности комплиментарного выхода TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set; // установка безопасного состояния выхода ШИМ TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset; // установка безопасного состояния комплиментарного выхода ШИМ TIM_OC1Init(TIM1, &TIM_OCInitStructure); // ввод значений переменной TIM_OCInitStructure в регистры ШИМ канала 1 // таймера1 TIM_OCInitStructure.TIM_Pulse = Channel2Pulse; // меняем ширину импульса в переменной OCInitStructure и вводим её в TIM_OC2Init(TIM1, &TIM_OCInitStructure); // регистры ШИМ канала 2 таймера1 TIM_OCInitStructure.TIM_Pulse = Channel3Pulse; // меняем ширину импульса в переменной OCInitStructure и вводим её в TIM_OC3Init(TIM1, &TIM_OCInitStructure); // регистры ШИМ канала 3 таймера1 TIM_OCInitStructure.TIM_Pulse = Channel4Pulse; // меняем ширину импульса в переменной OCInitStructure и вводим её в TIM_OC4Init(TIM1, &TIM_OCInitStructure); // регистры ШИМ канала 4 таймера1 /* TIM1 counter enable */ TIM_Cmd(TIM1, ENABLE); // запускаем таймер1 /* TIM1 Main Output Enable */ TIM_CtrlPWMOutputs(TIM1, ENABLE); // разрешаем работу выходов сравнения таймера 1

В правой части автор оставил комментарий на русском языке к каждой строке программы. Если открыть этот же пример в описании функций библиотек stm32f10x_stdperiph_lib_um.chm, то мы увидим, что все используемые параметры функций имеют ссылку на собственное описание, где будут указаны их возможные значения. Сами функции тоже имеют ссылку на собственное описание и исходный код. Это очень полезно, т.к. зная, что функция делает, мы можем проследить, каким образом она это делает, на какие биты регистров периферии и как она воздействует. Это, во-первых, ещё один источник информации для освоения контроллера, основанный на практическом использовании контроллера. Т.е. вы сначала решите техническую задачу, а потом изучите само решение. Во-вторых, это поле для оптимизации программы тому, кого библиотека не устроит по скорости работы и объёму кода.



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

Ниже представлен рисунок платы STM32F3 Discovery , где: 1 — MEMS датчик. 3-осевой цифровой гироскоп L3GD20. 2 — МЕМС система-в-корпусе, содержащая 3-осевой цифровой линейный акселерометр и 3-осевой цифровой геомагнитный сенсор LSM303DLHC. 4 – LD1 (PWR) – питание 3.3V. 5 – LD2 – красный/зеленый светодиод. По умолчанию красный. Зеленый означает связь между ST-LINK/v2 (or V2-B) и ПК. У меня ST-LINK/v2-B, а также индикации пользовательского порта USB. 6. -LD3/10 (red), LD4/9 (blue), LD5/8 (orange) and LD6/7 (green). В прошлой записи мы с Вами мигали светодиодом LD4. 7. – Две кнопки: пользовательская USER и сброса RESET. 8. — USB USER with Mini-B connector.

9 — USB отладчик/программатор ST-LINK/V2. 10. — Микроконтроллер STM32F303VCT6. 11. — Внешний высокочастотный генератор 8 Мгц. 12. – Здесь должен быть низкочастотный генератор, к сожелению не запаян. 13. – SWD – интерфейс. 14. – Джемперы для выбора программирования внешних контроллеров или внутреннего, в первом случае должны быть удалены. 15 – Джемпер JP3 – перемычка, предназначена для подключения амперметра, что б измерить потребление контроллера. Понятно если она удалена, то наш камушек не запустится. 16. – STM32F103C8T6 на нем находится отладочная плата. 17. — LD3985M33R Регулятор с низким падением напряжения и уровнем шума, 150мА, 3.3В.

Теперь познакомимся поближе с архитектурой микроконтроллера STM32F303VCT6. Его техническая характеристика: корпус LQFP-100, ядро ARM Cortex-M4, максимальная частота ядра 72МГц, объём памяти программ 256 Кбайт, тип памяти программ FLASH, объём оперативной памяти SRAM 40 кбайт, RAM 8 кбайт, количество входов/выходов 87, интерфейсы (CAN, I²C, IrDA, LIN, SPI, UART/USART, USB), периферия (DMA, I2S, POR, PWM, WDT), АЦП/ЦАП 4*12 bit/2*12bit, напряжение питания 2 …3.6 В, рабочая температура –40 …...+85 С. На рисунке ниже распиновка, где видим 87 портов ввода/вывода, 45 из них Normal I/Os (TC, TTa), 42 5-volt tolerant I/Os (FT, FTf) – совместимые с 5 В. (на плате справа 5В выводы, слева 3,3В). Каждая цифровая линия ввода-вывода может выполнять функцию линии ввода-вывода общего
назначения или альтернативную функцию. По ходу продвижения проектов мы с Вами будем последовательно знакомится с периферией.

Рассмотрим блок диаграмму ниже. Сердцем является 32-битное ядро ARM Cortex-M4, работающее до 72 МГц. Имеет встроенный блок с плавающей запятой FPU и блок защиты памяти MPU, встроенные макро-ячейки трассировки — Embedded Trace Macrocell (ETM), которые могут быть использованы для наблюдения за процессом выполнения основной программы внутри микроконтроллера. Они способны непрерывно выводить данные этих наблюдений через контакты ETM до тех пор, пока устройство работает. NVIC (Nested vectored interrupt controller) – модуль контроля прерываний. TPIU (Trace Port Interface Unit). Содержит память FLASH –256 Кбайт, SRAM 40 кбайт, RAM 8 кбайт. Между ядром и памятью расположена Bus matrix (Шинная матрица), которая позволяет соединить устройства напрямую. Также здесь видим два типа шинной матрицы AHB и APB, где первая более производительная и используется для связи высокоскоростных внутренних компонентов, а последняя для периферии (устройств ввода/вывода). Контроллер имеет 4 –ри 12-разрядных ADC (АЦП) (5Мбит/с) и датчик температуры, 7 компараторов (GP Comparator1 …7), 4-ри программируеммых операционных усилителя (OpAmp1…4) (PGA (Programmable Gain Array)), 2 12 разрядных канала DAC (ЦАП), RTC (часы реального времени), два сторожевых таймера — независимый и оконный (WinWatchdog and Ind. WDG32K), 17 таймеров общего назначения и многофункциональные.

В общих чертах мы рассмотрели архитектуру контроллера. Теперь рассмотори доступные программные библиотеки. Сделав обзор можна выделить следующие: CMSIS, SPL и HAL. Рассмотрим каждую применив в простом примере мигая светодиодом.

1). CMSIS (Cortex Microcontroller Software Interface Standard) - стандартная библиотека для Cortex®-M. Обеспечивает поддержку устройств и упрощает программные интерфейсы. CMSIS предоставляет последовательные и простые интерфейсы для ядра, его периферии и операционных систем реального времени. Ее использования является профессиональным способом написания программ, т.к. предполагает прямую запись в регистры и соответственно необходимо постоянное чтение и изучение даташитов. Независим от производителя аппаратного уровня.
CMSIS включает в себя следующие компоненты:
— CMSIS-CORE: Consistent system startup and peripheral access (Постоянный запуск системы и периферийный доступ);
— CMSIS-RTOS : Deterministic Real-Time Software Execution (Детерминированное исполнение программного обеспечения реального времени);
— CMSIS-DSP: Fast implementation of digital signal processing (Быстрая реализация цифровой обработки сигналов);
— CMSIS-Driver: Generic peripheral interfaces for middleware and application code (Общие периферийные интерфейсы для промежуточного программного обеспечения и кода приложения);
— CMSIS-Pack : Easy access to reusable software components (Легкий доступ к многократно используемым программным компонентам);
— CMSIS-SVD: Consistent view to device and peripherals (Согласованное представление устройства и периферийных устройств);
— CMSIS-DAP: Connectivity to low-cost evaluation hardware (Возможность подключения к недорогому оборудованию для оценки). ПО для отладки.

Для примера напишем программу – мигаем светодиодом. Для этого нам понадабится документация описывающая регистры. В моем случае RM0316 Reference manual STM32F303xB/C/D/E, STM32F303x6/8, STM32F328x8, STM32F358xC, STM32F398xE advanced ARM ® -based MCUs , а также описние конткретной ножки за что отвечает DS9118 : ARM®-based Cortex®-M4 32b MCU+FPU, up to 256KB Flash+ 48KB SRAM, 4 ADCs, 2 DAC ch., 7 comp, 4 PGA, timers, 2.0-3.6 V. Для начала в программе затактируем порт, т.к. по умолчанию все отключено чем достигается уменьшенное эрергопотребление. Открываем Reference manual и смотрим раздел Reset and clock control далее RCC register map и смотрим что за включение IOPEEN отвечает регистр

Перейдем в описание тактирования перефирии данного регистра AHB peripheral clock enable register (RCC_AHBENR) , где видим что данный порт находится под 21-м битом. Включаем его RCC->AHBENR|=(1<<21) . Далее сконфигурируем регистры GPIO. Нас интересует три: GPIOE_MODER и GPIOx_ODR . C помощью них повторим программу с предыдущей статьи, затактируем PE8. Первый отвечает за конфигурацию входа выхода, выбираем 01: General purpose output mode. GPIOE->MODER|=0×10000 . Второй за включение низкого/высокого уровня на ножке. Ниже программа:

#include "stm32f3xx.h" //Заголовочный файл микроконтроллера
unsigned int i;
void delay () {
for (i=0;i<500000;i++);
}
int main (void) {
RCC->AHBENR|=(1<<21);
GPIOE->MODER|=0×10000;
while (1){
delay ();
GPIOE->ODR|=0×100;
delay ();
GPIOE->ODR&=~(0×100);
} }

2). SPL (Standard Peripherals Library) - данная библиотека предназначена для объединения всех процессоров фирмы ST Electronics. Разработана для урощения преносимости кода и в первую очередь расчитана на начинающег разаработчика. ST работала над заменой SPL под названием «low layer», которая совместима с HAL. Драйверы Low Layer (LL) разработаны для обеспечения почти легкого экспертно-ориентированного уровня, который ближе к оборудованию, чем HAL. В дополнение к HAL также доступны LL API . Пример тойже программы на SPL.

#include
#include
#include
#define LED GPIO_Pin_8
int main() {
long i;
GPIO_InitTypeDef gpio;
// Blue LED is connected to port E, pin 8 (AHB bus)
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ENABLE);
// Configure port E (LED)
GPIO_StructInit(&gpio); //объявляем и инициализируем переменную структуры данных
gpio.GPIO_Mode = GPIO_Mode_OUT;
gpio.GPIO_Pin = LED;
GPIO_Init(GPIOE, &gpio);
// Blinking LEDS
while (1) {
// On
GPIO_SetBits(GPIOE, LED);
for (i = 0; i < 500000; i++);
// All off
GPIO_ResetBits(GPIOE, LED);
for (i = 0; i < 500000; i++);
} }

Каждая функция описывается в технической документации UM1581 User manual Description of STM32F30xx/31xx Standard Peripheral Library . Здесь подключаем три заголовочных файла которые содержат необходимоые данные, структуры, функции управления сбросом и синхронизацией, а также для конфигурации портов ввода/вывода.

3). HAL - (Hardware Acess Level , Hardware Abstraction Layer) - Еще одна общая библиотека для разработки. С которой вышла и программа CubeMX для конфигурации, которую мы с Вами использовали в прошлой статье. Там же мы написали программу мигания светодиодом используя данную библиотеку. Как видим на рисунке, ниже, куб генерирует драйвера HAL and CMSIS. Что ж опишем основные используемые файлы:
— system_stm32f3x.c и system_stm32f3x.h - предоставляют минимальные наборы функций для конфигурации системы тактирования;
— core_cm4.h – предоставляет доступ к регистрам ядра и его периферии;
— stm32f3x.h - заголовочный файл микроконтроллера;
— startup_system32f3x.s — стартовый код, содержит таблица векторов прерываний и др.

#include «main.h»
#include «stm32f3xx_hal.h»
void SystemClock_Config (void); /*Объявляем функции конфигурации тактирования*/
static void MX_GPIO_Init (void); /*Инициализация ввода/вывода*/
int main (void) {
/*Reset of all peripherals, Initializes the Flash interface and the Systick.*/
HAL_Init ();
/* Configure the system clock */
SystemClock_Config ();
/* Initialize all configured peripherals */
MX_GPIO_Init ();
while (1) {
HAL_GPIO_TogglePin (GPIOE, GPIO_PIN_8);//Переключаем состояние ножки
HAL_Delay (100); }
}
void SystemClock_Config (void) {
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = 16;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig (&RCC_OscInitStruct) != HAL_OK) {

}
/**Initializes the CPU, AHB and APB busses clocks */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig (&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) {
_Error_Handler (__FILE__, __LINE__);
}
/**Configure the Systick interrupt time*/
HAL_SYSTICK_Config (HAL_RCC_GetHCLKFreq ()/1000);
/**Configure the Systick */
HAL_SYSTICK_CLKSourceConfig (SYSTICK_CLKSOURCE_HCLK);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority (SysTick_IRQn, 0, 0);
}
/** Configure pins as Analog Input Output EVENT_OUT EXTI */
static void MX_GPIO_Init (void) {
GPIO_InitTypeDef GPIO_InitStruct;
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOE_CLK_ENABLE ();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin (GPIOE, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET);
/*Configure GPIO pins: PE8 PE9 PE10 PE11 PE12 PE13 PE14 PE15 */
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init (GPIOE, &GPIO_InitStruct);
}
void _Error_Handler (char * file, int line) {
while (1) {
} }
#ifdef USE_FULL_ASSERT

Void assert_failed (uint8_t* file, uint32_t line) {
}
#endif
Здесь также как и в предыдущем примере можем просмотреть описание каждой функции в документации, например UM1786 User Manual Description of STM32F3 HAL and low-layer drivers.

Можем подвести итог, что первый выриант, используя CMSIS, является менее громоздким. Для каждой библиотеки есть документация. В последующих проектах, мы будем использовать HAL и CMSIS, используя программу для конфигурации STCube и по возможности использовать регистры напрямую, без программных оберток. На этом сегодя и остановимся. В следующей статье мы с Вами рассмотрим основные принципы построения умного дома. Всем пока.

Я указывал, что к системе подключается стандартная библиотека. На самом деле, подключается CMSIS - система обобщенного структурного представления МК, а также SPL - стандартная библиотека периферии. Рассмотрим каждую из них:

CMSIS
Представляет собой набор заголовочных файлов и небольшого набора кода для унификации и структурировании работы с ядром и периферией МК. По сути, без этих файлов невозможно нормально работать с МК. Получить библиотеку можно на странице к МК.
Эта библиотека если верить описанию создавалась для унификации интерфейсов пр работе с любым МК семейства Cortex. Однако, на деле выходит, что это справедливо только для одного производителя, т.е. перейдя на МК другой фирмы вы вынуждены изучать его периферию почти с нуля.
Хотя те файлы которые касаются процессорного ядра МК у всех производителей идентичны (хотя бы потому, что модель процессорного ядра у них одна - предоставленная в виде ip-блоков компанией ARM).
Поэтому работа с такими частями ядра как регистры, инструкции, прерывания и сопроцессорные блоки стандартна для всех.
Что касается периферии то у STM32 и STM8 (внезапно) она почти похожа, также частично это справедливо и для других МК выпущенных компанией ST. В практической части, я покажу насколько просто использовать CMSIS. Однако трудности в его использовании связаны с нежеланием людей читать документацию и разбираться в устройстве МК.

SPL
Standard Peripheral Library - стандартная библиотека периферии. Как следует из названия, назначение этой библиотеки - создание абстракции для периферии МК. Библиотека состоит из заголовочных файлов где объявлены человеко-понятные константы для конфигурирования и работы с периферией МК, а также файлы исходного кода собираемые собственно в саму библиотеку для операций с периферией.
SPL является абстракцией над CMSIS представляя пользователю общий интерфейс для всех МК не только одного производителя, но и вообще всех МК с процессорным ядром Cortex-Mxx.
Считается, что она более удобна новичкам, т.к. позволяет не думать как работает периферия, однако качество кода, универсальность подхода и скованность интерфейсов накладывают на разработчика определенные ограничения.
Также функционал библиотеки не всегда позволяет точно реализовать настройку некоторых компонентов таких как USART (универсальный синхронный-асинхронный последовательный порт) в определённых условиях. В практической части, я также опишу работу с этой частью библиотеки.

Необходимое для разработки программное обеспечение. В этой статье я расскажу как его правильно настроить и связать. Все коммерческие среды такие как IAR EWARM или Keil uVision обычно сами выполняют эту интеграцию, однако в нашем случае все придется настраивать вручную, потратив на это немало времени. Преимуществом является то, что у вас есть шанс понять как это все работает изнутри, и в дальнейшем гибко настраивать все под себя. Перед началом настройки рассмотрим структуру среды в которой мы будем работать:

Eclipse будет использован для удобного редактирования файлов реализации функций (.c ), заголовочных файлов (.h ), а также файлов ассемблера (.S ). Под "удобным" я понимаю использование автодополненения кода, подсветки синтаксиса, рефакторинга, навигации по функциям и их прототипам. Файлы автоматически скармливаются нужным компиляторам, которые генерируют объектный код (в файлах .o ). Пока что этот код не содержит абсолютных адресов переменных и функций и по этому для выполнения не пригоден. Полученные объектные файлы собираются воедино сборщиком (linker-ом). Чтобы знать, какие участки адресного пространства использовать, сборщик использует специальный файл (.ld ), который называется линкер-скриптом. Он обычно содержит определение адресов секций и их размеров (секция кода, отображаемая на флеш, секция переменных, отображаемая на ОЗУ и т.д.).

В конце концов linker генерирует.elf файл (Executable and Linkable Format), который содержит в себе кроме инструкций и данных отладочную информацию (Debugging information), используемую отладчиком. Для обычной прошивки программой vsprog этот формат не подходит, поскольку для этого нужен более примитивный файл образа памяти (например Intel HEX - .hex). Для его генерации тоже есть инструмент из набора Sourcery CodeBench (arm-none-eabi-objcopy), и он отлично интегрируются в eclipse с помощью установленного ARM-плагина.

Для осуществления самой отладки используются три программы:

  1. сам eclipse, дающий возможность программисту "визуально" использовать отладку, ходить по строкам, наводить курсором мышки на переменные для просмотра их значений, и прочие удобности
  2. arm-none-eabi-gdb - GDB клиент - отладчик, который скрыто управляется eclips-ом(через stdin) в качестве реакции на действия, указанные в п.1. В свою очередь GDB подключается к Debug-серверу OpenOCD, и все поступающие на вход команды транслируются отладчиком GDB в команды, понятные для OpenOCD. Канал GDB <-> OpenOCD реализуется по протоколу TCP.
  3. OpenOCD - это debug-сервер, который может общаться непосредственно с программатором. Он запускается перед клиентом и ожидает подключения по TCP.

Данная схема может показаться вам весьма бесполезной: зачем использовать клиент и сервер в отдельности и выполнять лишний раз трансляцию команд, если все это можно было бы делать одним отладчиком? Дело в том, что такая архитектура теоретически позволяет удобно делать взаимозамену клиента и сервера. Например, если вам будет нужно вместо versaloon использовать другой программатор, который не будет поддерживать OpenOCD, а будет поддерживать другой специальный Debug-сервер (например texane/stlink для программатора stlink - который находится в отладочной плате STM32VLDiscovery), то вы просто вместо запуска OpenOCD будете запускать нужный сервер и все должно работать, без каких-либо дополнительных телодвижений. В то же время возможна обратная ситуация: допустим вы захотели использовать вместо связки Eclipse + CodeBench, среду IAR EWARM вместе с versaloon. У IAR есть свой встроенный Debug-клиент, который успешно свяжется с OpenOCD и будет им рулить, а также получать в ответ нужные данные. Однако все это иногда остается только в теории, поскольку стандарты общения клиента и сервера регламентированы не жестко, и местами могут отличатся, однако указанные мною конфигурации с st-link+eclipse и IAR+versaloon мне удавались.

Обычно клиент и сервер запускаются на одной машине и подключение к серверу происходит по адресу localhost:3333 (Для openocd), или localhost:4242 (для texane/stlink st-util). Но никто не мешает открыть порт 3333 или 4242 (и пробросить этот порт на роутере во внешнюю сеть) и ваши коллеги из другого города смогут подключится и отладить вашу железку. Данный трюк часто используется ембеддерами, работающими на удаленных объектах, доступ к которым ограничен.

Приступаем

Запускаем eclipse и выбираем File->New->C Project, выбираем тип проекта ARM Linux GCC (Sorcery G++ Lite) и имя "stm32_ld_vl" (Если у вас STV32VLDiscovery то логичнее будет назвать "stm32_md_vl"):

Нажимаем Finish, сворачиваем или закрываем окно Welcome. Итак, проект создан, и в вашем workspace должна появиться папка stm32_ld_vl. Теперь ее нужно наполнить необходимыми библиотеками.

Как вы поняли из названия проекта, я буду создавать проект для вида линейки low-density value line (LD_VL). Чтобы создать проект для других микроконтроллеров вы должны заменить все файлы и define-ы в названии которых присутствует _LD_VL (или _ld_vl ) на нужные вам, в соответствии с таблицей:

Вид линейки Обозначение Микроконтроллеры (х может менятся)
Low-density value line _LD_VL STM32F100x4 STM32F100x6
Low-density _LD STM32F101x4 STM32F101x6
STM32F102x4 STM32F102x6
STM32F103x4 STM32F103x6
Medium-density value line _MD_VL STM32F100x8 STM32F100xB
Medium-density
_MD
STM32F101x8 STM32F101xB
STM32F102x8 STM32F102xB
STM32F103x8 STM32F103xB
High density Value line _HD_VL STM32F100xC STM32F100xD STM32F100xE
High density _HD STM32F101xC STM32F101xD STM32F101xE
STM32F103xC STM32F103xD STM32F103xE
XL-density _XL STM32F101xF STM32F101xG
STM32F103xF STM32F103xG
Connectivity line _CL STM32F105xx и STM32F107xx

Чтобы понять логику таблицы, вы должны быть знакомы с маркировкой STM32 . То есть, если у вас VLDiscovery то дальше вам придется заменять все что связано с _LD_VL на _MD_VL, поскольку в дискавери распаян чип STM32F100RB, относящийся к Medium-density value line.

Добавление в проект библиотек CMSIS и STM32F10x Standard Peripherals Library

CMSIS (Cortex Microcontroller Software Interface Standard) - стандартизированная библиотека работы с микроконтроллерами Cortex, выполняющая реализацию уровня HAL (Hardware Abstraction Layer), тоесть позволяет абстрагироваться от деталей работы с регистрами, поиска адресов регистров по даташитам и т.д. Библиотека представляет собой набор из исходников на языке С и Asm. Ядерная (Core) часть библиотеки одинакова для всех Cortex-ов (Будь это ST, NXP, ATMEL, TI или еще кто другой), и разрабатывается компанией ARM. Другая же часть библиотеки отвечает за периферию, которая естественно различна у разных производителей. Поэтому в конечном итоге полная библиотека все равно распространяется производителем, хотя ядерную часть все же можно скачать отдельно на сайте ARM. Библиотека содержит определения адресов, код инициализации тактового генератора (удобно настраиваемый define-ами), и все прочее, что избавляет программиста от ручного введения в свои проекты определения адресов всяческих регистров периферии и определения битов значений этих регистров.

Но ребята из ST пошли дальше. Помимо поддержки CMSIS они предоставляют еще одну библиотеку для STM32F10x под названием Standard Peripherals Library (SPL ), которая может использоваться в дополнение к CMSIS. Библиотека обеспечивает более быстрый и удобный доступ к периферии, а также контролирует (в некоторых случаях) правильность работы с периферией. Поэтому данную библиотек часто называют набором драйверов к периферийным модулям. Она сопровождается пакетом примерчиков, разделенных по категориям для разной перифериии. Библиотека также есть не только для STM32F10x, но и под другие серии.

Скачать всю SPL+CMSIS версии 3.5 можно тут: STM32F10x_StdPeriph_Lib_V3.5.0 или на сайте ST. Разархивируйте архив. Создайте папки CMSIS и SPL в папке проекта и начнем копировать файлы к себе в проект:

Что копировать

Куда копировать (учитывая,
что папка проекта stm32_ld_vl)

Описание файла
Libraries/CMSIS/CM3/
CoreSupport/core_cm3.c
stm32_ld_vl/CMSIS/core_cm3.c Описание ядра Cortex M3
Libraries/CMSIS/CM3/
CoreSupport/core_cm3.h
stm32_ld_vl/CMSIS/ core_cm3.h Заголовки описания ядра

ST/STM32F10x/system_stm32f10x.c
stm32_ld_vl/CMSIS/ system_stm32f10x.c Функции инициализации и
управления тактовой частотой
Libraries/CMSIS/CM3/DeviceSupport/
ST/STM32F10x/system_stm32f10x.h
stm32_ld_vl/CMSIS/ system_stm32f10x.h Заголовки к этим функциям
Libraries/CMSIS/CM3/DeviceSupport/
ST/STM32F10x/stm32f10x.h
stm32_ld_vl/CMSIS/ stm32f10x.h Основное описание периферии
Libraries/CMSIS/CM3/DeviceSupport/
ST/STM32F10x/startup/gcc_ride7/
startup_stm32f10x_ld_vl.s
stm32_ld_vl/CMSIS/ startup_stm32f10x_ld_vl.S
(!!! Внимание расширение файла CAPITAL S)
Файл с таблицей векторов
прерываний и init-ами на asm
Project/STM32F10x_StdPeriph_Template/
stm32f10x_conf.h
stm32_ld_vl/CMSIS/stm32f10x_conf.h Шаблон для настройки
периферийных модулей

inc/*
stm32_ld_vl/SPL/inc/* Заголовочные файлы SPL
Libraries/STM32F10x_StdPeriph_Driver/
src/*
stm32_ld_vl/SPL/src/* Реализация SPL

После копирования зайдите в Eclipse и сделайте Refresh в контекстном меню проекта. В результате в Project Explorer вы должны получить такую же структуру как на картинке справа.

Возможно вы заметили, что в папке Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/ есть папки для разных IDE (в разных IDE используются разные компиляторы). Я выбрал IDE Ride7, так как в ней используется компилятор GNU Tools for ARM Embedded, совместимый с нашим Sourcery CodeBench.

Вся библиотека конфигурируется с помощью препроцессора (с помощью define-ов), это позволят решить все необходимые ветвления еще на стадии компиляции (вернее даже перед ней) и избежать нагрузки в работе самого контроллера (которая наблюдалась бы, если бы конфигурирование выполнялось в RunTime). Например все оборудование различное для разных линеек и поэтому чтобы библиотека "узнала", какую линейку вы хотите использовать, вас просят раскомментировать в файле stm32f10x.h один из define-ов (соответствующих вашей линейке):

/* #define STM32F10X_LD */ /*!< STM32F10X_LD: STM32 Low density devices */
/* #define STM32F10X_LD_VL */ /*!< STM32F10X_LD_VL: STM32 Low density Value Line devices */
/* #define STM32F10X_MD */ /*!< STM32F10X_MD: STM32 Medium density devices */

И так далее...

Но делать этого я не советую. Файлы библиотек мы трогать пока не будем, а define мы сделаем позже с помощью настроек компилятора в Eclipse. И тогда Eсlipse будет вызвать компилятор с ключем -D STM32F10X_LD_VL , что для препроцессора абсолютно эквивалентно ситуации если бы вы расскомментировали "#define STM32F10X_LD_VL" . Таким образом код мы менять не будем, в следствии, при желании, когда-нибудь вы сможете вынести библиотеку в отдельную директорию и не копировать в папку каждого нового проекта.

Linker-скрипт

В контекстном меню проекта выбираем New->File->Other->General->File, Next. Выбираем корневую папку проекта (stm32_ld_vl). Вводим имя файла "stm32f100c4.ld" (или "stm32f100rb.ld" для дискавери). Теперь копируем и вставляем в eclipse:

ENTRY(Reset_Handler) MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 16K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 4K } _estack = ORIGIN(RAM) + LENGTH(RAM); MIN_HEAP_SIZE = 0; MIN_STACK_SIZE = 256; SECTIONS { /* Interrupt vector table */ .isr_vector: { . = ALIGN(4); KEEP(*(.isr_vector)) . = ALIGN(4); } >FLASH /* The program code and other data goes into FLASH */ .text: { . = ALIGN(4); /* Code */ *(.text) *(.text*) /* Constants */ *(.rodata) *(.rodata*) /* ARM->Thumb and Thumb->ARM glue code */ *(.glue_7) *(.glue_7t) KEEP (*(.init)) KEEP (*(.fini)) . = ALIGN(4); _etext = .; } >FLASH .ARM.extab: { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH .ARM: { __exidx_start = .; *(.ARM.exidx*) __exidx_end = .; } >FLASH .ARM.attributes: { *(.ARM.attributes) } > FLASH .preinit_array: { PROVIDE_HIDDEN (__preinit_array_start = .); KEEP (*(.preinit_array*)) PROVIDE_HIDDEN (__preinit_array_end = .); } >FLASH .init_array: { PROVIDE_HIDDEN (__init_array_start = .); KEEP (*(SORT(.init_array.*))) KEEP (*(.init_array*)) PROVIDE_HIDDEN (__init_array_end = .); } >FLASH .fini_array: { PROVIDE_HIDDEN (__fini_array_start = .); KEEP (*(.fini_array*)) KEEP (*(SORT(.fini_array.*))) PROVIDE_HIDDEN (__fini_array_end = .); } >FLASH _sidata = .; /* Initialized data */ .data: AT (_sidata) { . = ALIGN(4); _sdata = .; /* create a global symbol at data start */ *(.data) *(.data*) . = ALIGN(4); _edata = .; /* define a global symbol at data end */ } >RAM /* Uninitialized data */ . = ALIGN(4); .bss: { /* This is used by the startup in order to initialize the .bss secion */ _sbss = .; /* define a global symbol at bss start */ __bss_start__ = _sbss; *(.bss) *(.bss*) *(COMMON) . = ALIGN(4); _ebss = .; /* define a global symbol at bss end */ __bss_end__ = _ebss; } >RAM PROVIDE(end = _ebss); PROVIDE(_end = _ebss); PROVIDE(__HEAP_START = _ebss); /* User_heap_stack section, used to check that there is enough RAM left */ ._user_heap_stack: { . = ALIGN(4); . = . + MIN_HEAP_SIZE; . = . + MIN_STACK_SIZE; . = ALIGN(4); } >RAM /DISCARD/ : { libc.a(*) libm.a(*) libgcc.a(*) } }

Данный линкер-скрипт будет предназначен именно для контроллера STM32F100C4 (у которого 16 Кб флеша и 4 Кб ОЗУ), если у вас другой, то придется поменять параметры LENGTH у областей FLASH и RAM в начале файла (для STM32F100RB, который в Discovery: Flash 128K и ОЗУ 8К).

Сохраняем файл.

Настройка сборки (C/C++ Build)

Заходим в Project->Properties->C/C++ Build-> Settings->Tool Settings, и начинаем настраивать инструменты сборки:

1) Target Precessor

Выбираем под какое именно ядро Cortex компилятор будет работать.

  • Processor: cortex-m3

2) ARM Sourcery Linux GCC C Compiler -> Preprocessor

Добавляем два define-a путем передачи их через ключ -D компилятору.

  • STM32F10X_LD_VL - определяет линейку (о этом define-е я писал выше)
  • USE_STDPERIPH_DRIVER - указание библиотеке CMSIS, что она должна использовать драйвер SPL

3) ARM Sourcery Linux GCC C Compiler -> Directories

Добавляем пути к includ-ам библиотек.

  • "${workspace_loc:/${ProjName}/CMSIS}"
  • "${workspace_loc:/${ProjName}/SPL/inc}"

Теперь, например, если мы напишем:

#include "stm32f10x.h

То компилятор должен сначала поискать файл stm32f10x.h в директории проекта (он это делает всегда), он его там не найдет и приступит к поиску в папке CMSIS, путь к которой мы указали, ну и найдет его.

4) ARM Sourcery Linux GCC C Compiler -> Optimization

Включим оптимизацию функций и данных

  • -ffunction-sections
  • -fdata-sections

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

5) ARM Sourcery Linux GCC C Compiler -> General

Добавляем путь к нашему linker-скрипту: "${workspace_loc:/${ProjName}/stm32f100c4.ld}" (или как он у вас называется).

И ставим опции:

  • Do not use standard start files - не использовать стандартные файлы запуска.
  • Remove unused sections - удалить неиспользованные секции

Все, настройка закончена. OK.

С момента создания проекта мы много всего сделали, и кое-чего Eclipse мог не заметить, по этому нам нужно сказать ему чтобы он пересмотрел структуру файлов проекта. Для этого из контекстного меню проекта нужно сделать Index -> rebuild .

Hello светодиоды на STM32

Пора создать главный файл проекта: File -> New -> C/C++ -> Source File. Next. Имя файла Source file: main.c.

Копируем и вставляем в файл следующее:

#include "stm32f10x.h" uint8_t i=0; int main(void) { RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; // Enable PORTB Periph clock RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // Enable TIM2 Periph clock // Disable JTAG for release LED PIN RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; AFIO->MAPR |= AFIO_MAPR_SWJ_CFG_JTAGDISABLE; // Clear PB4 and PB5 control register bits GPIOB->CRL &= ~(GPIO_CRL_MODE4 | GPIO_CRL_CNF4 | GPIO_CRL_MODE5 | GPIO_CRL_CNF5); // Configure PB.4 and PB.5 as Push Pull output at max 10Mhz GPIOB->CRL |= GPIO_CRL_MODE4_0 | GPIO_CRL_MODE5_0; TIM2->PSC = SystemCoreClock / 1000 - 1; // 1000 tick/sec TIM2->ARR = 1000; // 1 Interrupt/1 sec TIM2->DIER |= TIM_DIER_UIE; // Enable tim2 interrupt TIM2->CR1 |= TIM_CR1_CEN; // Start count NVIC_EnableIRQ(TIM2_IRQn); // Enable IRQ while(1); // Infinity loop } void TIM2_IRQHandler(void) { TIM2->SR &= ~TIM_SR_UIF; //Clean UIF Flag if (1 == (i++ & 0x1)) { GPIOB->BSRR = GPIO_BSRR_BS4; // Set PB4 bit GPIOB->BSRR = GPIO_BSRR_BR5; // Reset PB5 bit } else { GPIOB->BSRR = GPIO_BSRR_BS5; // Set PB5 bit GPIOB->BSRR = GPIO_BSRR_BR4; // Reset PB4 bit } }

Хоть мы подключали библиотек SPL, тут она использована не была. Все обращения к полям вроде RCC->APB2ENR полностью описаны в CMSIS.

Можно выполнять Project -> Build All. Если все получилось, то в папке Debug проекта должен появится файл stm32_ld_vl.hex. Он был автоматически сгенерирован из elf встроенными инструментами. Прошиваем файл и видим как мигают светодиоды с частотой раз в секунду:

Vsprog -sstm32f1 -ms -oe -owf -I /home/user/workspace/stm32_ld_vl/Debug/stm32_ld_vl.hex -V "tvcc.set 3300"

Естественно вместо /home/user/workspace/ вы должны вписать свой путь к workspace.

Для STM32VLDiscovery

Код немного отличается от того, который я дал выше для своей отладочной платки. Отличие заключается в пинах, на которых "висят" светодиоды. Если у меня в плате это были PB4 и PB5, то в Discovery это PC8 и PC9.

#include "stm32f10x.h" uint8_t i=0; int main(void) { RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; // Enable PORTC Periph clock RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // Enable TIM2 Periph clock // Clear PC8 and PC9 control register bits GPIOC->CRH &= ~(GPIO_CRH_MODE8 | GPIO_CRH_CNF8 | GPIO_CRH_MODE9 | GPIO_CRH_CNF9); // Configure PC8 and PC9 as Push Pull output at max 10Mhz GPIOC->CRH |= GPIO_CRH_MODE8_0 | GPIO_CRH_MODE9_0; TIM2->PSC = SystemCoreClock / 1000 - 1; // 1000 tick/sec TIM2->ARR = 1000; // 1 Interrupt/sec (1000/100) TIM2->DIER |= TIM_DIER_UIE; // Enable tim2 interrupt TIM2->CR1 |= TIM_CR1_CEN; // Start count NVIC_EnableIRQ(TIM2_IRQn); // Enable IRQ while(1); // Infinity loop } void TIM2_IRQHandler(void) { TIM2->SR &= ~TIM_SR_UIF; //Clean UIF Flag if (1 == (i++ & 0x1)) { GPIOC->BSRR = GPIO_BSRR_BS8; // Set PC8 bit GPIOC->BSRR = GPIO_BSRR_BR9; // Reset PC9 bit } else { GPIOC->BSRR = GPIO_BSRR_BS9; // Set PC9 bit GPIOC->BSRR = GPIO_BSRR_BR8; // Reset PC8 bit } }

Под Windows, прошить полученный hex(/workspace/stm32_md_vl/Debug/stm32_md_vl.hex) можно утилитой от ST.

Ну а под linux утилитой st-flash. НО!!! Утилита не хавает hex формата Intel HEX (который генерируется по дефолту), поэтому крайне важно в настройках создания Flash-образа, выбрать формат binary:

Расширение файла при этом не поменяется (останется hex как и было), но формат файла изменится. И только после этого можно выполнять:

St-flash write v1 /home/user/workspace/stm32_md_vl/Debug/stm32_md_vl.hex 0x08000000

Кстати, на счет расширения и формата: обычно бинарные файлы помечают расширением.bin, в то время как файлы формата Intel HEX именуют расширением.hex. Отличие в этих двух форматах скорей техническое, чем функциональное: бинарный формат содержит просто байты инструкций и данных, которые будут просто записываться в контроллер программатором "как есть". IntelHEX же имеет не бинарный формат, а текстовый: точно те же байты разбиты по 4 бита и представлены посимвольно в формате ASCII, причем использованы только символы 0-9, A-F (bin и hex - системы счисления с кратными основаниями, то есть 4 бита в bin можно представить одной цифрой в hex). Так что формат ihex более чем в 2 раза превышает размер обычного бинарного файла (каждые 4 бита заменяются байтом + переносы строк для удобного чтения), но его можно читать в обычном текстовом редакторе. Поэтому, если вы собираетесь отправить этот файл кому-то, или использовать его в других программах-программаторах, то желательно переименовать его в stm32_md_vl.bin, дабы не вводить в заблуждение тех, кто будет смотреть на его имя.

Итак мы настроили сборку прошивки для stm32. В следующий раз я расскажу как

Взаимодействие пользовательского кода с регистрами ядра и периферии микроконтроллеров STM32 может быть осуществлено двумя способами: с помощью стандартных библиотек или с помощью наборов сниппетов (программных подсказок). Выбор между ними зависит от объема собственной памяти контроллера, требуемого быстродействия, срока выполнения разработки. В статье анализируются особенности структуры, достоинства и недостатки наборов сниппетов для микроконтроллеров семейств STM32F1 и STM32L0 производства компании STMicroelectronics.

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

Программное обеспечение для STM32 включает в себя собственное ПО производства компании STMicroelectronics, источники Open Source, коммерческое ПО.

ПО от STMicroelectronics обладает важными достоинствами. Во-первых, оно доступно для бесплатного скачивания. Во-вторых, программные библиотеки представлены в виде исходных кодов – пользователь сам может модифицировать код, учитывая незначительные ограничения, описанные в лицензионном соглашении.

Библиотеки STMicroelectronics соответствуют ANSI-C и могут быть разделены по уровню абстракции (рисунок 1):

  • CMSIS (Core Peripheral Access Layer) – уровень регистров ядра и периферии, ARM библиотека;
  • Hardware Abstraction Layer – низкоуровневые библиотеки: стандартные библиотеки периферии (standard peripheral library), наборы сниппетов (snippets);
  • Middleware – библиотеки среднего уровня: операционные системы реального времени (RTOS), файловые системы, USB, TCP/IP, Bluetooth, Display, ZigBee, Touch Sensing и другие;
  • Application Field – библиотеки прикладного уровня: аудио, управление двигателями, автомобильные и промышленные решения.

На рисунке 1 видно, что для взаимодействия с уровнем CMSIS компания STMicroelectronics предлагает использовать два основных инструмента – стандартные библиотеки и сниппеты.

Стандартная библиотека – это набор драйверов. Каждый драйвер предоставляет пользователю функции и определения для работы с конкретным периферийным блоком (SPI, USART, ADC и так далее). Напрямую пользователь с регистрами уровня CMSIS не взаимодействует.

Наборы сниппетов – это высокоэффективные программные примеры, использующие прямой доступ к регистрам CMSIS. Разработчики ПО могут использовать реализации функций из этих примеров в собственном коде.

Каждый из способов имеет достоинства и недостатки. Выбор между ними делается с учетом доступного объема FLASH и ОЗУ, требуемого быстродействия, срока выполнения разработки, опытности программистов и других обстоятельств.

Уровень CMSIS

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

С точки зрения программистов, микроконтроллер представляет собой пространство памяти. В нем размещены не только ОЗУ, FLASH и EEPROM, но и программные регистры. Каждому аппаратному регистру соответствует ячейка памяти. Таким образом, чтобы записать данные в регистр или вычитать его значение, программисту необходимо обратиться к соответствующей ячейке адресного пространства.

Человек имеет некоторые особенности восприятия. Например, символьные названия воспринимаются им гораздо лучше, чем адреса ячеек памяти. Это особенно заметно, когда используется большое число ячеек. В микроконтроллерах ARM число регистров, а значит, и используемых ячеек, превышает тысячу. Чтобы упростить работу, необходимо произвести определение символьных указателей. Это определение выполнено на уровне CMSIS.

Например, чтобы установить состояние выводов порта А, нужно записать данные в регистр GPIOA_ODR. Это можно сделать двумя способами – воспользоваться указателем с адресом ячейки 0xEBFF FCFF со смещением 0x14 или применить указатель с символьным названием GPIOA и готовую структуру, определяющую смещение. Очевидно, что второй вариант гораздо проще для восприятия.

CMSIS выполняет и другие функции. Он реализован в виде следующей группы файлов:

  • startup_stm32l0xx.s содержит ассемблерный стартовый код Cortex-M0+ и таблицу векторов прерываний. После выполнения стартовой инициализации происходит передача управления сначала функции SystemInit() (ниже будут приведены пояснения), а затем – основной функции int main(void);
  • stm32l0xx.h содержит определения, необходимые для выполнения основных операций с битами и определение типа используемого микропроцессора;
  • system_stm32l0xx.c/.h. После начальной инициализации выполняется функция SystemInit(). Она производит первичную настройку системной периферии, таймингов блока RCC;
  • stm32l0yyxx.h – файлы реализации конкретных микроконтроллеров (например, stm32l051xx.h). Именно в них определяются символьные указатели, структуры данных, битовые константы и смещения.

Взаимодействие со CMSIS. Стандартные библиотеки и сниппеты

Число регистров для микроконтроллеров STM32 в большинстве моделей превышает тысячу. Если использовать прямое обращение к регистрам, пользовательский код станет нечитаемым и абсолютно непригодным для поддержки и модернизации. Эта проблема может быть решена при использовании стандартной библиотеки периферии (standard peripheral library).

Стандартная библиотека периферии – это набор низкоуровневых драйверов. Каждый драйвер предоставляет пользователю набор функций для работы с периферийным блоком. Таким образом пользователь использует функции, а не обращается напрямую к регистрам. При этом уровень CMSIS оказывается скрытым от программиста (рисунок 2а).

Рис. 2. Взаимодействие с CMSIS с помощью стандартной библиотеки (а) и сниппетов (б)

Например, взаимодействие с портами ввода/вывода в STM32L0 реализовано с помощью драйвера, выполненного в виде двух файлов: stm32l0xx_hal_gpio.h и stm32l0xx_hal_gpio.c. В stm32l0xx_hal_gpio.h даны основные определения типов и функций, а в stm32l0xx_hal_gpio.c представлена их реализация.

Такой подход имеет вполне очевидные достоинства (таблица 1):

  • Быстрота создания кода. Программисту не требуется изучать перечень регистров. Он сразу начинает работать на более высоком уровне. Например, для прямого взаимодействия с портом ввода/вывода в STM32L0 необходимо знать и уметь работать с одиннадцатью регистрами управления/состояния, большинство из которых имеют до 32 настраиваемых битов. При использовании библиотечного драйвера достаточно освоить восемь функций.
  • Простота и наглядность кода. Пользовательский код не забит названиями регистров, может быть прозрачным и легко читаемым, что важно при работе команды разработчиков.
  • Высокий уровень абстракции. При использовании стандартной библиотеки код оказывается достаточно платформо-независимым. Например, если сменить микроконтроллер STM32L0 на микроконтроллер STM32F0 , часть кода, работающего с портами ввода/вывода, вообще не придется менять.

Таблица 1. Сравнение способов реализации пользовательского кода

Параметр сравнения При использовании стандартной
библиотеки периферии
При использовании наборов сниппетов
Размер кода средний минимальный
Затраты ОЗУ средние минимальные
Быстродействие среднее максимальное
Читаемость кода отличная низкая
Уровень независимости от платформы средний низкий
Скорость создания программ высокая низкая

Наличие дополнительной оболочки в виде драйверов имеет и очевидные недостатки (таблица 1):

  • Увеличение объема кода программы. Реализованные в библиотечном коде функции требуют дополнительного места в памяти.
  • Повышенные затраты ОЗУ за счет увеличения числа локальных переменных и использования громоздких структур данных.
  • Снижение быстродействия за счет увеличения накладных расходов при вызове библиотечных функций.

Именно наличие этих недостатков приводило к тому, что пользователь зачастую был вынужден оптимизировать код – самостоятельно реализовывать функции взаимодействия с CMSIS, оптимизировать библиотечные функции, убирая все лишнее, копировать реализации библиотечных функций непосредственно в свой код, использовать __INLINE-директивы для увеличения скорости выполнения. В результате, тратилось дополнительное время на доработку кода.

Компания STMicroelectronics, идя навстречу разработчикам, выпустила сборники сниппетов STM32SnippetsF0 и STM32SnippetsL0 .

Сниппеты входят в пользовательский код (рисунок 2б).

Использование сниппетов предоставляет очевидные преимущества:

  • повышение эффективности и быстродействия кода;
  • уменьшение объема программы;
  • снижение объемов используемой ОЗУ и нагрузки на стек.

Впрочем, стоит отметить и недостатки:

  • уменьшение простоты и наглядности кода за счет «загрязнения» его названиями регистров и самостоятельной реализацией низкоуровневых функций;
  • исчезновение платформо-независимости.

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

Стандартные библиотеки драйверов и сниппетов STM32F0 и STM32L0 (таблица 2) доступны для свободного скачивания на сайте www.st.com.

Таблица 2. Низкоуровневые библиотеки для STM32F10 и STM32L0

Более тесное знакомство со сниппетами, как и с любым ПО, следует начинать с рассмотрения особенностей лицензионного соглашения.

Лицензионное соглашение

Любой ответственный программист перед использованием сторонних программных продуктов внимательно изучает лицензионное соглашение. Несмотря на то, что сборники сниппетов производства ST Microelectronics не требуют лицензирования и доступны для свободного скачивания, это не значит, что на их использование не накладываются ограничения.

Лицензионное соглашение входит в комплект всех свободно скачиваемых продуктов производства компании STMicroelectronics. После загрузки STM32SnippetsF0 и STM32SnippetsL0 в корневом каталоге легко обнаружить документ MCD-ST Liberty SW License Agreement V2.pdf, который знакомит пользователя с правилами использования данного ПО.

В папке Project содержатся подкаталоги с примерами для конкретных периферийных блоков, готовые проекты для ARM Keil и EWARM, а также файлы main.c.

Запуск и особенности использования наборов сниппетов STM32SnippetsF0 и STM32SnippetsL0

Особенностью данных наборов снипетов является их платформозависимость. Они предназначены для работы с конкретными платами. STM32SnippetsL0 использует платформу STM32L053 Discovery board, а STM32SnippetsF0 – плату STM32F072 Discovery board.

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

Для запуска примера необходимо выполнить ряд шагов:

  • запустить готовый проект из директории с требуемым примером. Для простоты можно воспользоваться готовыми проектами для сред ARM Keil или EWARM, расположенными в папке MDK-ARM\ и EWARM\ соответственно;
  • включить питание отладочной платы STM32L053 Discovery/STM32F072 Discovery;
  • подключить питание отладочной платы к ПК с помощью USB-кабеля. Благодаря встроенному отладчику ST-Link/V2 дополнительного программатора не потребуется;
  • открыть, настроить и запустить проект;
    • Для ARM Keil:
      • открыть проект;
      • скомпилировать проект – Project → Rebuild all target files;
      • загрузить его в контроллер – Debug → Start/Stop Debug Session;
      • запустить программу в окне Debug → Run (F5).
    • Для EWARM:
      • открыть проект;
      • скомпилировать проект – Project → Rebuild all;
      • загрузить его в контроллер – Project → Debug;
      • запустить программу в окне Debug → Go(F5).
  • провести тестирование в соответствии с алгоритмом, описанном в main.c.

Для анализа программного кода рассмотрим конкретный пример из STM32SnippetsL0: Projects\LPUART\01_WakeUpFromLPM\.

Запуск примера для LPUART

Отличительной особенностью новых микроконтроллеров семейства STM32L0 на ядре Cortex-M0+ является возможность динамического изменения потребления за счет большого числа нововведений. Одним из таких новшеств стало появление Low Power-периферии: 16-битного таймера LPTIM и приемопередатчика LPUART. Эти блоки обладают способностью тактирования, не зависящего от тактирования основной периферийной шины APB. При необходимости снижения потребляемой мощности рабочая частота шины APB (PCLK) может быть уменьшена, а сам контроллер переведен в режим пониженного потребления. При этом Low Power-периферия продолжает работу с максимальной производительностью.

Рассмотрим пример из директории Projects\LPUART\01_WakeUpFromLPM\, в котором рассматривается возможность независимой работы LPUART в режиме пониженного потребления.

При открытии проекта в среде ARM Keil отображаются всего три файла: startup_stm32l053xx.s, system_stm32l0xx.c и main.c (рисунок 4). В случае применения стандартной библиотеки в проект было бы необходимо добавить файлы драйверов.

Функционирование и анализ структуры файла Main.c

Программа из выбранного примера выполняется в несколько этапов.

После старта запускается функция SystemInit(), реализованная в system_stm32l0xx.c. Она проводит настройку параметров блока тактирования RCC (тайминги и рабочие частоты). Далее осуществляется передача управления в основную функцию int main(void). В ней инициализируется пользовательская периферия – порты вводы/вывода, LPUART – после чего контроллер переводится в режим пониженного потребления STOP. В нем обычная периферия и ядро остановлены, работает только LPUART. Он ждет начала передачи данных от внешнего устройства. При приходе стартового бита LPUART пробуждает систему и принимает сообщение. Прием сопровождается мерцанием светодиода отладочной платы. После этого контроллер вновь переводится в состояние STOP и ждет следующей передачи данных, если не было обнаружено ошибок.

Передача данных происходит при помощи виртуального COM-порта и дополнительного ПО.

Рассмотрим main.c из нашего проекта. Этот файл представляет собой стандартный С-файл. Главной его особенностью является самодокументация – наличие подробных комментариев, пояснений и рекомендаций. Пояснительная часть содержит несколько разделов:

  • заголовок с указанием названия файла, версии, даты, автора, краткого пояснения назначения;
  • описание последовательности настройки системной периферии (RCC specific features): FLASH, ОЗУ, системы питания и тактирования, периферийных шин и так далее;
  • перечень используемых ресурсов микроконтроллера (MCU Resources);
  • краткое пояснение по использованию данного примера (How to use this example);
  • краткое пояснение по тестированию примера и алгоритм его проведения (How to test this example).

Функция int main(void) имеет компактную форму и снабжена комментариями, которые в листинге 1, для большей наглядности, переведены на русский.

Листинг 1. Пример реализация функции main

int main(void)
{
/* К началу выполнения этой части когда уже произведена конфигурация системных блоков в функции SystemInit(), реализованной в system_stm32l0xx.c. */
/* конфигурация периферийных блоков*/
Configure_GPIO_LED();
Configure_GPIO_LPUART();
Configure_LPUART();
Configure_LPM_Stop();
/* проверка наличия ошибок при приеме */
while (!error) /* бесконечный цикл */
{
/* ожидание готовности LPUART и переход в режим STOP */
if((LPUART1->ISR & USART_ISR_REACK) == USART_ISR_REACK)
{
__WFI();
}
}
/* при возникновении ошибки */
SysTick_Config(2000); /* установка периода прерываний системного таймера 1 мс */
while(1);
}

В файле main.c объявлены и определены функции конфигурации периферии и две функции обработки прерываний. Рассмотрим их особенности.

В приведенном примере используются четыре функции конфигурации (листинг 2). Все они не имеют аргументов и не возвращают значений. Их главное предназначение – быстро и с наименьшими затратами занимаемого кода произвести инициализацию периферии. Это реализуется за счет двух особенностей: применения прямого обращения к регистрам и использования директивы __INLINE (листинг 3).

Листинг 2. Объявление функций конфигурации периферии

void Configure_GPIO_LED(void);
void Configure_GPIO_LPUART(void);
void Configure_LPUART(void);
void Configure_LPM_Stop(void);

Листинг 3. Пример реализации __INLINE-функции с прямым доступом к регистрам LPUART

INLINE void Configure_LPUART(void)
{
/* (1) Enable power interface clock */
/* (2) Disable back up protection register to allow the access to the RTC clock domain */
/* (3) LSE on */
/* (4) Wait LSE ready */
/* (5) Enable back up protection register to allow the access to the RTC clock domain */
/* (6) LSE mapped on LPUART */
/* (7) Enable the peripheral clock LPUART */
/* Configure LPUART */
/* (8) oversampling by 16, 9600 baud */
/* (9) 8 data bit, 1 start bit, 1 stop bit, no parity, reception mode, stop mode */
/* (10) Set priority for LPUART1_IRQn */
/* (11) Enable LPUART1_IRQn */
RCC->APB1ENR |= (RCC_APB1ENR_PWREN); /* (1) */
PWR->CR |= PWR_CR_DBP; /* (2) */
RCC->CSR |= RCC_CSR_LSEON; /* (3) */
while ((RCC->CSR & (RCC_CSR_LSERDY)) != (RCC_CSR_LSERDY)) /*(4)*/
{
/* add time out here for a robust application */
}
PWR->CR &=~ PWR_CR_DBP; /* (5) */
RCC->CCIPR |= RCC_CCIPR_LPUART1SEL; /* (6) */
RCC->APB1ENR |= RCC_APB1ENR_LPUART1EN; /*(7) */
LPUART1->BRR = 0x369; /* (8) */
LPUART1->CR1 = USART_CR1_UESM | USART_CR1_RXNEIE | USART_CR1_RE | USART_CR1_UE; /* (9) */
NVIC_SetPriority(LPUART1_IRQn, 0); /* (10) */
NVIC_EnableIRQ(LPUART1_IRQn); /* (11) */
}

Обработчики прерываний от системного таймера и от LPUART также используют прямое обращение к регистрам.

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

Использование сниппетов в собственных разработках

Предложенные наборы сниппетов имеют ограничения: необходимо использовать отладочную плату STM32L053 Discovery board для STM32SnippetsL0 , а плату STM32F072 Discovery board – для STM32SnippetsF0 .

Для применения сниппетов в своих разработках потребуется произвести ряд изменений. Во-первых, необходимо переконфигурировать проект под нужный процессор. Для этого в нем нужно сменить стартовый файл startup_stm32l053xx.s на файл другого контроллера и определить нужную константу: STM32L051xx, STM32L052xx, STM32L053xx, STM32L062xx, STM32L063xx, STM32L061xx, STM32F030, STM32F031, STM32F051 и другие. После этого при компиляции stm32l0xx.h, будет автоматически подключен нужный файл с определением периферии контроллера stm32l0yyxx.h (stm32l051xx.h/stm32l052xx.h/stm32l053xx.h/stm32l061xx.h/stm32l062xx.h/stm32l063). Во-вторых, нужно выбрать соответствующий программатор в настройках свойств проекта. Во-третьих – изменить код функций из примеров, если они не отвечают требованиям пользовательского приложения.

Заключение

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

Стандартная библиотека дает возможность быстрого создания ясного кода с высоким уровнем абстракции.

Сниппеты позволяют повысить эффективность кода – увеличить производительность и сократить объем занимаемой памяти FLASH и ОЗУ.

Литература

  1. Data brief. STM32SnippetsF0. STM32F0xx Snippets firmware package. Rev. 1. – ST Microelectronics, 2014.
  2. Data brief. STM32SnippetsL0. STM32F0xx Snippets firmware package. Rev. 1. – ST Microelectronics, 2014.
  3. MCD-ST Liberty SW License Agreement V2.pdfElectromechanical Relays. Technical Information. – ST Microelectronics, 2011.
  4. Data brief. 32L0538DISCOVERY Discovery kit for STM32L053 microcontrollers. Rev. 1. – ST Microelectronics, 2014.
  5. http://www.st.com/.
О компании ST Microelectronics
Понравилась статья? Поделиться с друзьями: