Ещё раз хочу написать про простой старт с STM32, только на этот раз без использования чьих–то шаблонов или примеров — с объяснением каждого шага. В статьях будет сквозная нумерация шагов.

0. Добываем плату STM32VLDiscovery

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

1. Устанавливаем IAR

Работать будем в IAR — хорошая IDE с прекрасным компилятором. Ей недостаёт удобства написания кода — но для наших целей её вполне достаточно. Я использую IAR версии 6.50.3, взять — сами знаете где.

2. Скачиваем библиотеку периферии

Я не сторонник работы с регистрами на этапе обучения. Поэтому предлагаю скачать библиотеку периферии от ST для получения удобных функций доступа ко всем нужным настройкам.

Создаём папку «STM32_Projects», складываем туда папку Libraries из скачанного архива (stsw-stm32078.zip/an3268/stm32vldiscovery_package), в ней лежит CMSIS (библиотека от ARM для всех микроконтроллеров Cortex, описание и адреса всех ресурсов) и STM32F10x_StdPeriph_Driver — библиотека периферии от ST со всеми функциями.

Также создаём там папку «1. GPIO», в которой и будет лежать наш первый проект.

Дерево папок — на рисунке. Сделайте именно так, потому что потом будут очень важны относительные пути в этом дереве.

Ну и чтобы понимать, о чём идёт речь — скачайте 1100–страничный документ на эти контроллеры.

Сборка проекта в IAR

Необходимо чётко понимать суть процесса сборки проекта. Для удобства разобьём её на стадии.

1. Препроцессор

По всем .c–файлам проекта (и по main.c, и по всем файлам в workspace) проходит препроцессор. Он делает следующее:

  1. удаляет комментарии
  2. раскрывает директивы #include, подставляя вместо них содержимое указанного файла. Этот процесс проходит рекурсивно, начиная с .c–файла и заходя в каждый встреченный #include .h, и если в .h–файле тоже встретились директивы #include — препроцессор зайдёт и в них. Получается такое дерево инклудов. Обратите внимание: он не обрабатывает ситуацию двойного включения инклудов, т.е. один и тот же .h-файл может включиться несколько раз, если он указан в #include в нескольких местах проекта. Такую ситуацию нужно обрабатывать дефайнами.
  3. совершает макроподстановки — раскрывает макросы
  4. собирает директивы компилятора.

Препроцессор генерирует файлы .i, которые довольно удобны при поиске ошибок сборки — хотя бы потому что в них полностью раскрыты все макросы. Сохранение этих файлов можно включить в настройках проекта.

На этом этапе сборщик имеет все .c–файлы в проекте, готовые для компиляции — в виде файлов .i. Никаких связей между файлами пока нет.

2. Компилятор

После прохода препроцессора компилятор оптимизирует и компилирует каждый .i–файл, создавая бинарный код. Именно здесь требуется указание типа процессора, доступной памяти, языка программирования, уровня оптимизации и подобных вещей.

Что делает компилятор, встречая в каком-нибудь .c–файле вызов функции, которая не описана в этом файле? Он ищет её в заголовках. Если заголовки говорят, что функция лежит в другом .c–файле — он просто оставляет на этом месте указатель на этот другой файл.

На этом этапе сборщик имеет все .c–файлы проекта, собранные в .o–файлы. Они называются скомпилированными модулями. Теперь между файлами есть связи в виде указателей в местах вызова «чужих» функций — но это до сих пор несколько разных файлов.

3. Линкер

Уже почти всё готово, нужно лишь проверить все связи между файлами — пройти по main.o и подставить на место указателей на чужие функции — скомпилированные модули. Если какая-то функция из библиотек не используется — она либо не будет вообще скомпилирована на предыдущем этапе, либо не будет никуда подставлена линкером (зависит от метода работы сборщика). В любом случае в готовый бинарный код она не попадёт.

Линкер также может выполнить какие-нибудь финальные действия с бинарником, например посчитать его контрольную сумму.

Первый проект — работа с портами ввода–вывода

3. Создаём новый проект в IAR

После запуска IAR появляется окно информационного центра, который нам не нужен. Нажимаем меню Project –> Create New Project. Выбираем toolchain: ARM (вряд ли у вас будет что–то ещё в том списке), Project templates: C –> main.

new_project_c_main

Он предложит сохранить проект, сохраните в папку «STM32_Projects/1. GPIO/GPIO.ewp». Теперь сразу сохраните воркспейс, нажав на «сохранить всё» в панели инструментов, под именем GPIO.eww.

Теперь у вас есть новый пустой проект на C и файл main.c.

4. Подключаем к проекту библиотеки

В левом окне («Workspace») правой кнопкой мыши вызываем меню и создаём новую группу (Add –> Add Group), назовём её CMSIS. Точно так же создадим группы StdPeriphLib, Startup и User. Теперь добавляем файлы в группы (я подчеркну все файлы, чтобы было удобнее следить).

Щёлкаем правой кнопкой по CMSIS, Add, Add files — заходим в Libraries/CMSIS/CM3, из папки DeviceSupport/ST/STM32F10x (поддержка кристалла) берём system_stm32f10x.c (это описание периферии конкретного кристалла и настройка тактирования). В папке CoreSupport (поддержка ядра) лежит ещё и core_cm3.c (это описание ядра Cortex M3), но мы не станем его брать — потому что он уже есть в компиляторе. Я напишу про это дальше.

В группу Startup складываем файл startup_stm32f10x_md_vl.s из папки Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/iar. Это — действия, которые нужно выполнить при старте. Практически полностью это настройка обработчиков различных прерываний (сами обработчики будут немного дальше). Там ещё есть файлы для других кристаллов, но нас интересует именно md_vl — это значит medium density (средний объём памяти, есть ещё кристаллы с маленьким и большим объёмом), value line (оценочная линия — кристалл STM32F100 предназначен лишь для оценки возможностей, и перехода на следующие семейства).

С CMSIS закончили.

В группу StdPeriphLib добавляем из папки Libraries/STM32F10x_StdPeriph_Driver/src файлы stm32f10x_rcc.c и stm32f10x_gpio.c. Первый — это функции работы с системой тактирования, а второй — работа с ножками ввода–вывода.

В группу User перетаскиваем наш main.c. Это необязательно, но так красивее.

Теперь дерево проекта GPIO выглядит так:

Воркспейс готов, больше добавлять в него ничего не будем.

Осталось только положить в папку с проектом ещё один файл, подключающий заголовки ко всем файлам библиотеки периферии. Можно написать его самостоятельно, но проще взять готовый. Заходим в stsw-stm32078.zip/an3268/stm32vldiscovery_package/Project/Examples/GPIOToggle — там забираем файл stm32f10x_conf.h (конфигурация проекта) и кладём его в папку «1. GPIO». Это — единственный готовый файл, который мы берём.

stm32f10x_conf.h — просто свалка инклудов нужных модулей и функций assert. Эта функция будет вызываться при ошибках работы с функциями библиотеки периферии: например, засунуть в функцию GPIO_WriteBit вместо GPIOC какую–нибудь фигню — короче, ST замечательно перестраховалась. В этой функции можно просто запустить бесконечный цикл — while(1); Нам всё равно потребуется лезть в stm32f10x_conf.h — чтобы закомментировать строки включения файлов ненужной периферии, оставив только stm32f10x_rcc.h, stm32f10x_gpio.h и misc.h — поэтому можно было написать его и самостоятельно.

5. Настраиваем проект

Нажимаем правой кнопкой мыши в окне Workspace по названию проекта:

  1. General options –> Target –> Processor variant: выбираем «Device», нажимаем кнопку правее
<img class="alignnone" src="http://static.catethysis.ru/files/STM32_lessons_GPIO_options_uc_type.png" alt="" width="319" height="148" />
  
Выбираем ST –> STM32F100 –> ST STM32F100xB. Это наш контроллер.   2. General options –> Library Configuration –> CMSIS: ставим галочку Use CMSIS. Так мы будем использовать встроенную в компилятор библиотеку CMSIS. С версии 6.30 IAR стал поставляться со встроенной CMSIS, и это вроде как лучше — но внесло некоторую неразбериху со старыми проектами.   3. C/C++ compiler –> Preprocessor. Здесь пишем пути до папок библиотеки: <div style="border: 1px solid black; padding: 3px; margin: 3px; max-width: 500px;">
  $PROJ_DIR$\<br /> $PROJ_DIR$\..\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x<br /> $PROJ_DIR$\..\Libraries\STM32F10x_StdPeriph_Driver\inc
</div>

Макрос $PROJ_DIR$ означает текущую папку (папку проекта), а .. — переход на один уровень выше. Мы прописали пути до папки с описанием кристалла, а также до заголовочных файлов библиотеки периферии, поскольку все .c файлы в проекте подключают свои заголовки, и компилятор должен знать где их искать.

Ещё здесь нужно написать USE\_STDPERIPH\_DRIVER в Defined symbols. Это подключит нужные файлы конфигурации (например, упомянутый stm32f10x_conf.h) к проекту.

Таким образом, вкладка Preprocessor будет выглядеть так:
  
<img class="alignnone" src="http://static.catethysis.ru/files/STM32_lessons_GPIO_options_preproc.png" alt="" width="550" height="513" /></li> 

  * Debugger –> Setup –> Driver: выбираем ST–Link, поскольку именно такой программатор встроен в плату Discovery. Теперь настраиваем сам программатор:
  * Debugger –> ST–LINK –> Interface: выбираем SWD (программатор на плате подключен к контроллеру по SWD, а не по JTAG).
  * Debugger –> Download: ставим галочку Use flash loader(s), &#171;Заливать прошивку во flash–память&#187;. Логично, без неё ничего не зальётся.</ol> 

## 6. Пишем код

А для начала напишу, что этот код будет делать. Он будет демонстрировать простую вещь, мигание светодиодом (PC8 на плате Discovery) с паузой в бесконечном цикле.

Подключаем заголовочный файл конфигурации проекта, stm32f10x\_conf.h. В нём находим строчку #include &#171;stm32f10x\_exti.h&#187; — это 35 строка, и закомментируем её двумя слешами. Дело в том, что в нашем проекте не понадобится модуль EXTI.

В файле main.c уже есть функция int main, а в ней единственное действие — return 0. Удаляем эту строчку (мы не собираемся возвращать никаких значений), меняем тип функции на void (по той же причине), и пишем бесконечный цикл:

<pre><code class="cpp">#include "stm32f10x_conf.h"

void main() { while(1) { } }</code></pre>

### Запускаем модуль GPIO

Порты ввода–вывода в STM32 называются GPIO — General Purpose Input/Output. Поэтому мы и подключали библиотеку stm32f10x_gpio.c. Однако это не всё что нам нужно, немного теории:
  
Вся периферия на кристалле по умолчанию отключена, и от питания и от тактовой частоты. Чтобы её включить, нужно подать сигнал тактирования. Этим заведует модуль RCC, а для работы с ним существует файл stm32f10x_rcc.c. Модуль GPIO висит на шине APB2. Есть ещё AHB (аналог шины процессор–северный мост) и APB1 (так же как и APB2 — аналог шины северный мост–южный мост).

Поэтому первое, что нам нужно сделать — включить тактирование модуля GPIOC. Это модуль, отвечающий за PORTC; есть ещё GPIOA, GPIOB и так далее. Делается это так:

RCC\_APB2PeriphClockCmd(RCC\_APB2Periph_GPIOC, ENABLE);

Всё просто — вызываем функцию подачи тактового сигнала с шины APB2 на модуль GPIOC, и тем самым включаем этот модуль. Конечно, это делаем в самом начале функции void main.

Здесь — только основы, нужные для понимания. У меня также есть гораздо более [подробная статья про модуль GPIO](http://catethysis.ru/index.php/stm32-%e2%86%92-%d0%bf%d0%be%d1%80%d1%82%d1%8b-gpio/ "STM32 → Порты GPIO").

### Настраиваем модуль GPIOC

Осталось совсем немного, нужно сконфигурировать модуль GPIOC. Устанавливаем ножку на выход (ещё бывает вход и альтернативные функции), настраиваем резкость фронтов (в целях ЭМ совместимости), выходной драйвер (пуш–пулл или открытый исток). Это делаем сразу после инициализации порта.

GPIO\_InitTypeDef GPIO\_InitStructure;
  
GPIO\_InitStructure.GPIO\_Speed = GPIO\_Speed\_2MHz;
  
GPIO\_InitStructure.GPIO\_Mode = GPIO\_Mode\_Out_PP;
  
GPIO\_InitStructure.GPIO\_Pin = GPIO\_Pin\_8;
  
GPIO\_Init(GPIOC, &GPIO\_InitStructure);

Ну всё, после этого ножка PC8 будет работать как пуш–пулл выход с относительно плавными фронтами (максимальная частота переключения 2МГц. Острые фронты — это 50МГц). Плавность фронтов глазом не заметим, а на осциллографе это видно.

### Включаем светодиод

Вызываем функцию GPIO\_WriteBit(GPIOC, GPIO\_Pin\_8, Bit\_SET); Светодиод включится.

### Включаем–выключаем его в цикле

В цикле while(1) пишем код включения, паузы, выключения и снова паузы:

<pre><code class="cpp">GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET); for(i=0; i&lt;1000000; i++);

GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_RESET);
for(i=0; i<1000000; i++);</code></pre>

Таким образом, полностью весь файл main.c выглядит так:

<pre><code class="cpp">#include "stm32f10x_conf.h"

void main() { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_Init(GPIOC, &GPIO_InitStructure);

GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET);

int i; while(1) { GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET); for(i=0; i<1000000; i++);

GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_RESET);  
for(i=0; i&lt;1000000; i++);   } }</code></pre>

## 7. Запускаем!

Подключаем плату STM32VLDiscovery к компьютеру через microUSB, нажимаем в IAR кнопку Download and Debug.
  
<img class="alignnone" src="http://static.catethysis.ru/files/STM32_lessons_GPIO_download_debug.png" alt="" width="211" height="89" />

Программа заливается в микроконтроллер (можно заметить окошко с прогрессбаром, которое быстро закрывается — настолько мал размер программы), и начинается отладка. IAR останавливается на первой инструкции кода (это довольно удобно при отладке), нужно запустить его кнопкой Go.
  
<img class="alignnone" src="http://static.catethysis.ru/files/STM32_lessons_GPIO_go.png" alt="" width="188" height="92" />

Всё должно работать — синий светодиод PC8 на плате STM32VLDiscovery должен

Как всегда, вы можете скачать <a target="_blank" rel="nofollow" href="http://catethysis.ru/goto/http://static.catethysis.ru/files/STM32_Projects_1_GPIO.rar" >архив с проектом GPIO</a>.
  
К счастью, этот проект можно сохранить и дальше использовать его как шаблон, чтобы не проводить всю настройку заново.

Весь цикл:
  
1. Порты ввода–вывода
  
[2. Таймер и прерывания](http://catethysis.ru/index.php/stm32-from_zero_to_rtos-2_timers/ "STM32 — с нуля до RTOS. 2: Таймер и прерывания")
  
[3. Выходы таймера](http://catethysis.ru/index.php/stm32-from_zero_to_rtos-3_timer_outputs/ "STM32 — с нуля до RTOS. 3: Выходы таймера")
  
[Ещё раз хочу написать про простой старт с STM32, только на этот раз без использования чьих–то шаблонов или примеров — с объяснением каждого шага. В статьях будет сквозная нумерация шагов.

0. Добываем плату STM32VLDiscovery

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

1. Устанавливаем IAR

Работать будем в IAR — хорошая IDE с прекрасным компилятором. Ей недостаёт удобства написания кода — но для наших целей её вполне достаточно. Я использую IAR версии 6.50.3, взять — сами знаете где.

2. Скачиваем библиотеку периферии

Я не сторонник работы с регистрами на этапе обучения. Поэтому предлагаю скачать библиотеку периферии от ST для получения удобных функций доступа ко всем нужным настройкам.

Создаём папку «STM32_Projects», складываем туда папку Libraries из скачанного архива (stsw-stm32078.zip/an3268/stm32vldiscovery_package), в ней лежит CMSIS (библиотека от ARM для всех микроконтроллеров Cortex, описание и адреса всех ресурсов) и STM32F10x_StdPeriph_Driver — библиотека периферии от ST со всеми функциями.

Также создаём там папку «1. GPIO», в которой и будет лежать наш первый проект.

Дерево папок — на рисунке. Сделайте именно так, потому что потом будут очень важны относительные пути в этом дереве.

Ну и чтобы понимать, о чём идёт речь — скачайте 1100–страничный документ на эти контроллеры.

Сборка проекта в IAR

Необходимо чётко понимать суть процесса сборки проекта. Для удобства разобьём её на стадии.

1. Препроцессор

По всем .c–файлам проекта (и по main.c, и по всем файлам в workspace) проходит препроцессор. Он делает следующее:

  1. удаляет комментарии
  2. раскрывает директивы #include, подставляя вместо них содержимое указанного файла. Этот процесс проходит рекурсивно, начиная с .c–файла и заходя в каждый встреченный #include .h, и если в .h–файле тоже встретились директивы #include — препроцессор зайдёт и в них. Получается такое дерево инклудов. Обратите внимание: он не обрабатывает ситуацию двойного включения инклудов, т.е. один и тот же .h-файл может включиться несколько раз, если он указан в #include в нескольких местах проекта. Такую ситуацию нужно обрабатывать дефайнами.
  3. совершает макроподстановки — раскрывает макросы
  4. собирает директивы компилятора.

Препроцессор генерирует файлы .i, которые довольно удобны при поиске ошибок сборки — хотя бы потому что в них полностью раскрыты все макросы. Сохранение этих файлов можно включить в настройках проекта.

На этом этапе сборщик имеет все .c–файлы в проекте, готовые для компиляции — в виде файлов .i. Никаких связей между файлами пока нет.

2. Компилятор

После прохода препроцессора компилятор оптимизирует и компилирует каждый .i–файл, создавая бинарный код. Именно здесь требуется указание типа процессора, доступной памяти, языка программирования, уровня оптимизации и подобных вещей.

Что делает компилятор, встречая в каком-нибудь .c–файле вызов функции, которая не описана в этом файле? Он ищет её в заголовках. Если заголовки говорят, что функция лежит в другом .c–файле — он просто оставляет на этом месте указатель на этот другой файл.

На этом этапе сборщик имеет все .c–файлы проекта, собранные в .o–файлы. Они называются скомпилированными модулями. Теперь между файлами есть связи в виде указателей в местах вызова «чужих» функций — но это до сих пор несколько разных файлов.

3. Линкер

Уже почти всё готово, нужно лишь проверить все связи между файлами — пройти по main.o и подставить на место указателей на чужие функции — скомпилированные модули. Если какая-то функция из библиотек не используется — она либо не будет вообще скомпилирована на предыдущем этапе, либо не будет никуда подставлена линкером (зависит от метода работы сборщика). В любом случае в готовый бинарный код она не попадёт.

Линкер также может выполнить какие-нибудь финальные действия с бинарником, например посчитать его контрольную сумму.

Первый проект — работа с портами ввода–вывода

3. Создаём новый проект в IAR

После запуска IAR появляется окно информационного центра, который нам не нужен. Нажимаем меню Project –> Create New Project. Выбираем toolchain: ARM (вряд ли у вас будет что–то ещё в том списке), Project templates: C –> main.

new_project_c_main

Он предложит сохранить проект, сохраните в папку «STM32_Projects/1. GPIO/GPIO.ewp». Теперь сразу сохраните воркспейс, нажав на «сохранить всё» в панели инструментов, под именем GPIO.eww.

Теперь у вас есть новый пустой проект на C и файл main.c.

4. Подключаем к проекту библиотеки

В левом окне («Workspace») правой кнопкой мыши вызываем меню и создаём новую группу (Add –> Add Group), назовём её CMSIS. Точно так же создадим группы StdPeriphLib, Startup и User. Теперь добавляем файлы в группы (я подчеркну все файлы, чтобы было удобнее следить).

Щёлкаем правой кнопкой по CMSIS, Add, Add files — заходим в Libraries/CMSIS/CM3, из папки DeviceSupport/ST/STM32F10x (поддержка кристалла) берём system_stm32f10x.c (это описание периферии конкретного кристалла и настройка тактирования). В папке CoreSupport (поддержка ядра) лежит ещё и core_cm3.c (это описание ядра Cortex M3), но мы не станем его брать — потому что он уже есть в компиляторе. Я напишу про это дальше.

В группу Startup складываем файл startup_stm32f10x_md_vl.s из папки Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/iar. Это — действия, которые нужно выполнить при старте. Практически полностью это настройка обработчиков различных прерываний (сами обработчики будут немного дальше). Там ещё есть файлы для других кристаллов, но нас интересует именно md_vl — это значит medium density (средний объём памяти, есть ещё кристаллы с маленьким и большим объёмом), value line (оценочная линия — кристалл STM32F100 предназначен лишь для оценки возможностей, и перехода на следующие семейства).

С CMSIS закончили.

В группу StdPeriphLib добавляем из папки Libraries/STM32F10x_StdPeriph_Driver/src файлы stm32f10x_rcc.c и stm32f10x_gpio.c. Первый — это функции работы с системой тактирования, а второй — работа с ножками ввода–вывода.

В группу User перетаскиваем наш main.c. Это необязательно, но так красивее.

Теперь дерево проекта GPIO выглядит так:

Воркспейс готов, больше добавлять в него ничего не будем.

Осталось только положить в папку с проектом ещё один файл, подключающий заголовки ко всем файлам библиотеки периферии. Можно написать его самостоятельно, но проще взять готовый. Заходим в stsw-stm32078.zip/an3268/stm32vldiscovery_package/Project/Examples/GPIOToggle — там забираем файл stm32f10x_conf.h (конфигурация проекта) и кладём его в папку «1. GPIO». Это — единственный готовый файл, который мы берём.

stm32f10x_conf.h — просто свалка инклудов нужных модулей и функций assert. Эта функция будет вызываться при ошибках работы с функциями библиотеки периферии: например, засунуть в функцию GPIO_WriteBit вместо GPIOC какую–нибудь фигню — короче, ST замечательно перестраховалась. В этой функции можно просто запустить бесконечный цикл — while(1); Нам всё равно потребуется лезть в stm32f10x_conf.h — чтобы закомментировать строки включения файлов ненужной периферии, оставив только stm32f10x_rcc.h, stm32f10x_gpio.h и misc.h — поэтому можно было написать его и самостоятельно.

5. Настраиваем проект

Нажимаем правой кнопкой мыши в окне Workspace по названию проекта:

  1. General options –> Target –> Processor variant: выбираем «Device», нажимаем кнопку правее
<img class="alignnone" src="http://static.catethysis.ru/files/STM32_lessons_GPIO_options_uc_type.png" alt="" width="319" height="148" />
  
Выбираем ST –> STM32F100 –> ST STM32F100xB. Это наш контроллер.   2. General options –> Library Configuration –> CMSIS: ставим галочку Use CMSIS. Так мы будем использовать встроенную в компилятор библиотеку CMSIS. С версии 6.30 IAR стал поставляться со встроенной CMSIS, и это вроде как лучше — но внесло некоторую неразбериху со старыми проектами.   3. C/C++ compiler –> Preprocessor. Здесь пишем пути до папок библиотеки: <div style="border: 1px solid black; padding: 3px; margin: 3px; max-width: 500px;">
  $PROJ_DIR$\<br /> $PROJ_DIR$\..\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x<br /> $PROJ_DIR$\..\Libraries\STM32F10x_StdPeriph_Driver\inc
</div>

Макрос $PROJ_DIR$ означает текущую папку (папку проекта), а .. — переход на один уровень выше. Мы прописали пути до папки с описанием кристалла, а также до заголовочных файлов библиотеки периферии, поскольку все .c файлы в проекте подключают свои заголовки, и компилятор должен знать где их искать.

Ещё здесь нужно написать USE\_STDPERIPH\_DRIVER в Defined symbols. Это подключит нужные файлы конфигурации (например, упомянутый stm32f10x_conf.h) к проекту.

Таким образом, вкладка Preprocessor будет выглядеть так:
  
<img class="alignnone" src="http://static.catethysis.ru/files/STM32_lessons_GPIO_options_preproc.png" alt="" width="550" height="513" /></li> 

  * Debugger –> Setup –> Driver: выбираем ST–Link, поскольку именно такой программатор встроен в плату Discovery. Теперь настраиваем сам программатор:
  * Debugger –> ST–LINK –> Interface: выбираем SWD (программатор на плате подключен к контроллеру по SWD, а не по JTAG).
  * Debugger –> Download: ставим галочку Use flash loader(s), &#171;Заливать прошивку во flash–память&#187;. Логично, без неё ничего не зальётся.</ol> 

## 6. Пишем код

А для начала напишу, что этот код будет делать. Он будет демонстрировать простую вещь, мигание светодиодом (PC8 на плате Discovery) с паузой в бесконечном цикле.

Подключаем заголовочный файл конфигурации проекта, stm32f10x\_conf.h. В нём находим строчку #include &#171;stm32f10x\_exti.h&#187; — это 35 строка, и закомментируем её двумя слешами. Дело в том, что в нашем проекте не понадобится модуль EXTI.

В файле main.c уже есть функция int main, а в ней единственное действие — return 0. Удаляем эту строчку (мы не собираемся возвращать никаких значений), меняем тип функции на void (по той же причине), и пишем бесконечный цикл:

<pre><code class="cpp">#include "stm32f10x_conf.h"

void main() { while(1) { } }</code></pre>

### Запускаем модуль GPIO

Порты ввода–вывода в STM32 называются GPIO — General Purpose Input/Output. Поэтому мы и подключали библиотеку stm32f10x_gpio.c. Однако это не всё что нам нужно, немного теории:
  
Вся периферия на кристалле по умолчанию отключена, и от питания и от тактовой частоты. Чтобы её включить, нужно подать сигнал тактирования. Этим заведует модуль RCC, а для работы с ним существует файл stm32f10x_rcc.c. Модуль GPIO висит на шине APB2. Есть ещё AHB (аналог шины процессор–северный мост) и APB1 (так же как и APB2 — аналог шины северный мост–южный мост).

Поэтому первое, что нам нужно сделать — включить тактирование модуля GPIOC. Это модуль, отвечающий за PORTC; есть ещё GPIOA, GPIOB и так далее. Делается это так:

RCC\_APB2PeriphClockCmd(RCC\_APB2Periph_GPIOC, ENABLE);

Всё просто — вызываем функцию подачи тактового сигнала с шины APB2 на модуль GPIOC, и тем самым включаем этот модуль. Конечно, это делаем в самом начале функции void main.

Здесь — только основы, нужные для понимания. У меня также есть гораздо более [подробная статья про модуль GPIO](http://catethysis.ru/index.php/stm32-%e2%86%92-%d0%bf%d0%be%d1%80%d1%82%d1%8b-gpio/ "STM32 → Порты GPIO").

### Настраиваем модуль GPIOC

Осталось совсем немного, нужно сконфигурировать модуль GPIOC. Устанавливаем ножку на выход (ещё бывает вход и альтернативные функции), настраиваем резкость фронтов (в целях ЭМ совместимости), выходной драйвер (пуш–пулл или открытый исток). Это делаем сразу после инициализации порта.

GPIO\_InitTypeDef GPIO\_InitStructure;
  
GPIO\_InitStructure.GPIO\_Speed = GPIO\_Speed\_2MHz;
  
GPIO\_InitStructure.GPIO\_Mode = GPIO\_Mode\_Out_PP;
  
GPIO\_InitStructure.GPIO\_Pin = GPIO\_Pin\_8;
  
GPIO\_Init(GPIOC, &GPIO\_InitStructure);

Ну всё, после этого ножка PC8 будет работать как пуш–пулл выход с относительно плавными фронтами (максимальная частота переключения 2МГц. Острые фронты — это 50МГц). Плавность фронтов глазом не заметим, а на осциллографе это видно.

### Включаем светодиод

Вызываем функцию GPIO\_WriteBit(GPIOC, GPIO\_Pin\_8, Bit\_SET); Светодиод включится.

### Включаем–выключаем его в цикле

В цикле while(1) пишем код включения, паузы, выключения и снова паузы:

<pre><code class="cpp">GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET); for(i=0; i&lt;1000000; i++);

GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_RESET);
for(i=0; i<1000000; i++);</code></pre>

Таким образом, полностью весь файл main.c выглядит так:

<pre><code class="cpp">#include "stm32f10x_conf.h"

void main() { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_Init(GPIOC, &GPIO_InitStructure);

GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET);

int i; while(1) { GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET); for(i=0; i<1000000; i++);

GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_RESET);  
for(i=0; i&lt;1000000; i++);   } }</code></pre>

## 7. Запускаем!

Подключаем плату STM32VLDiscovery к компьютеру через microUSB, нажимаем в IAR кнопку Download and Debug.
  
<img class="alignnone" src="http://static.catethysis.ru/files/STM32_lessons_GPIO_download_debug.png" alt="" width="211" height="89" />

Программа заливается в микроконтроллер (можно заметить окошко с прогрессбаром, которое быстро закрывается — настолько мал размер программы), и начинается отладка. IAR останавливается на первой инструкции кода (это довольно удобно при отладке), нужно запустить его кнопкой Go.
  
<img class="alignnone" src="http://static.catethysis.ru/files/STM32_lessons_GPIO_go.png" alt="" width="188" height="92" />

Всё должно работать — синий светодиод PC8 на плате STM32VLDiscovery должен

Как всегда, вы можете скачать <a target="_blank" rel="nofollow" href="http://catethysis.ru/goto/http://static.catethysis.ru/files/STM32_Projects_1_GPIO.rar" >архив с проектом GPIO</a>.
  
К счастью, этот проект можно сохранить и дальше использовать его как шаблон, чтобы не проводить всю настройку заново.

Весь цикл:
  
1. Порты ввода–вывода
  
[2. Таймер и прерывания](http://catethysis.ru/index.php/stm32-from_zero_to_rtos-2_timers/ "STM32 — с нуля до RTOS. 2: Таймер и прерывания")
  
[3. Выходы таймера](http://catethysis.ru/index.php/stm32-from_zero_to_rtos-3_timer_outputs/ "STM32 — с нуля до RTOS. 3: Выходы таймера")

](http://catethysis.ru/index.php/stm32-from_zero_to_rtos-4_exti_nvic/ “STM32 — с нуля до RTOS. 4: Внешние прерывания и NVIC”) 5. Ставим FreeRTOS