Черная магия голубой таблетки (делаем программатор Black Magic Probe из модуля на базе STM32F103). Прошивка внешнего контроллера при помощи stm32 discovery - SWD Swd stm32 распиновка

Черная магия голубой таблетки (делаем программатор Black Magic Probe из модуля на базе STM32F103). Прошивка внешнего контроллера при помощи stm32 discovery - SWD Swd stm32 распиновка

17.09.2023

У STM32 есть очень удобный интерфейс для отладки и прошивки МК - Serial Wire Debug, сокращено SWD . Его удобство заключается в том, что для отладки надо подключить всего два информационных вывода и два вывода питания. Схема подключения выглядит следующим образом.

После того как выводы подключены, надо разрешить отладку по SWD в среде программирования, в KEIL это делается так.


Также у SWD есть вывод SWO , его подключать необязательно, но если его подключить(подтянув к питанию через 10K), то можно будет выводить сообщения в режиме реального времени. То есть в процессе исполнения кода, МК может слать нам отладочную информацию, например, какой участок кода сейчас выполняется, получается что-то типа usart c терминалом.
Разрешить отправлять отладочную информация по выводу SWO можно во вкладке trace: разрешив трассировку, указав частоту на которой работает МК и порт.


Ниже пример, который позволяет выводить сообщения с помощью SWO , в специальное окошко, само окошко можно открыть так: view->Serial windows->debug (printf) viewer.
#include "stm32f10x.h" #include #define ITM_Port8(n) (*((volatile unsigned char *)(0xE0000000+4*n))) #define ITM_Port16(n) (*((volatile unsigned short*)(0xE0000000+4*n))) #define ITM_Port32(n) (*((volatile unsigned long *)(0xE0000000+4*n))) #define DEMCR (*((volatile unsigned long *)(0xE000EDFC))) #define TRCENA 0x01000000 struct __FILE { int handle; /* Add whatever you need here */ }; FILE __stdout; FILE __stdin; int fputc(int ch, FILE *f) { if (DEMCR & TRCENA) { while (ITM_Port32(0) == 0); ITM_Port8(0) = ch; } return(ch); } int main(void) { while(1) { printf("Hello from stm32 printf!\r\n"); } }
Вот как это выглядит, картинку можно увеличить кликнув по ней.

Ну и главное, для чего задумывалась эта статья, в качестве отладчика использую китайский jlink , распиновка его коннектора стандартная и её легко можно найти в интернете, но это не мешает мне постоянно забывать какие выводы куда подключать, поэтому оставлю здесь фотку, какие пины на коннекторе jlik предназначены для SWD .

Данная статья, которая является еще одним "быстрым стартом" в освоении ARM-контроллеров, возможно поможет сделать первые шаги в освоении 32-битных контроллеров ARM на базе ядра Cortex-M3 - STM32F1xxx серии. Возможно данная статья (которых на эту тему появляется как грибов после дождя) станет для кого-то полезной.

Введение

Почему ARM?
1. Есть из чего выбрать (разными производителями сегодня выпускается более 240 ARM-контроллеров)
2. Низкая цена (например за 1$ можно получить 37хI / O, 16K Flash, 4K RAM, 2xUART, 10x12bitADC, 6x16bitPWM).

А начнем нашу работу с контроллеров фирмы ST Microelectronics. Контроллеры на основе ядра ARM Cortex-M3 характеризуются широким набором периферии, высоким уровнем рабочих характеристик, низкой цене
P.S. В самом начале создается впечатление, что ARM"ы это какие-то страшные (в пайке, разводке, программировании) существа. Но это только на первый взгляд:) и вы в этом сами убедитесь.

Итак, изучать ARMы будем на примере контроллеров STM32F1. Одновременно эта серия имеет несколько линеек:

  • Value line STM32F100 - 24 МГц CPU, motor control, CEC.
  • Access line STM32F101 - 36 МГц CPU, до 1 Mб Flash
  • USB access line STM32F102 - 48 МГц CPU with USB FS
  • Performance line STM32F103 - 72 МГц, до 1 Mб Flash, motor control, USB, CAN
  • Connectivity line STM32F105/107 - 72 МГц CPU, Ethernet MAC, CAN, USB 2.0 OTG

Также существует следующая классификация:

Контроллеры STM32 можно заставить загружаться с 3-х областей памяти (в зависимости от состояния ножек BOOT0 и BOOT1 при старте контроллера или после его сброса). Записать программу в память контроллера можно следующими способами:

1 способ:
Используя загрузчик (он уже записан в системную память) и USART1 (USART2 remaped): использует внутренний тактовый сигнал 8 МГц. Чтобы запустить встроенный загрузчик, зашитый в контроллер производителем, достаточно просто бросить на лапки контроллера TX1, RX1 сигнал с преобразователя RS232-3.3В (например на базе FT232RL) и выставить перед этим BOOT0 = 1 и BOOT1 = 0 жмем RESET и можем шить программу в контроллер. А зашивается она в программе Flash Loader Demonstartor от STM (для Windows).

PS. Если вы сидите под LINUX и не имеете отладочной платы типа дискавери, можно заливать прошивку в контроллер через всеми любимый rs-232 (собственно - через преобразователь rs-232-3,3В). Для этого нужно использовать python-скрипт (Ivan A-R) (для LINUX или MACOSX).
Для начала у вас должен быть установлен Python 2.6 версии и библиотека для работы с последовательным портом - PySerial library.
Теперь, чтобы запустить скрипт stmloader.py (из терминала, разумеется) нужно его немного подправить под свой компьютер: откроем его в текстовом редакторе.
Набираем в командной строке
~$ dmesg | grep tty
чтобы увидеть все последовательные порты ПК.
и после набора...
~$ setserial -g /dev/ttyS
мы узнаем путь к нашему 232-му порту. Если система ругается на setserial, установим его
~$ sudo apt-get install setserial
мы узнаем путь к нашему физическому порту (например, у меня - /dev/ttyS0). Теперь нужно записать этот путь в файл скрипта stm32loader.py вместо дефолтного «/dev/tty.usbserial-...». Набираем в терминале
~$ python stm32loader.py -h
...для вызова справки и заливаем прошивку в наш контроллер.

2 способ:
Через USB OTG, используя DFU-режим, требует внешнего кварца на 8 МГц, 14.7456 МГц или 25 МГц (этот загрузчик есть не у всех контроллерах с USB OTG надо внимательно смотреть на маркировку вашего контроллера)

3 способ:
JTAG/SWD. Ну и для тех, кто имеет демоплату типа Discovery или самопальный JTAG/SWD программатор, можно заливать код и уже отлаживать свой микроконтроллер этим способом. Для JTAG в микроконтроллере отведено 6 лапок (TRST, TDI, TMS, TCK, TDO, RST) + 2 на питание. SWD использует 4 сигнала (SWDIO, SWCLK SWO, RESET) и 2 на питание.

PS. В среде EAGLE я набросал несколько схем-заготовок для 48-ми, 64-х и 100-ногих контроллеров (папка eagle), а stm32loader содержит скрипт stm32loader.py

ST-Link/V2 специальное устройство разработанное компанией ST для отладки и программирования микроконтроллеров серии STM8 и STM32. Про сам прибор можно прочитать на сайте компании ST .

Основные его возможности:

    Выход 5В для питания устройства

    USB 2.0 высокоскоростной интерфейс

    SWIM, JTAG/serial wire debugging (SWD) интерфейсы

    SWIM поддержка низкоскоростного и высокоскоростного режимов

    SWD and serial wire viewer (SWV)

    Возможность Обновление прошивки

Так как микроконтроллеры STM32 построены на ядре ARM Cortex , которое имеет интерфейс отладки SWD, то ST-Link позволяет программировать и отлаживать и другие 32-битные микроконтроллеры на базе ARM-Cortex.

Это, можно сказать, единственный программатор микроконтроллеров STM8. Для программирования STM32 существуют и другие универсальные программаторы.

Где можно купить программатор STM8 STM32 ST-Link

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

Оригинальный ST Link от компании ST, как всегда, самый дорогой вариант. Стоит больше 2 000 руб.

Мини ST link (очень похож на наш вариант этого программатора) стоит около 600 руб. Купить его можно у крупных поставщиков электроники - Компэл, Терра электроника и другие.

Ali express (Китай) - тут предлагается большое количество самых простых вариантов Программатора, но в общем, они все рабочие, ими вполе можно пользоваться. Как правило они годятся для программирования STM8 и STM32. Единственное, они не имеют SWO выхода, но он нужен не так часто. Пожалуй, единственный минус тут, это ожидание покупки. Стоимость около 150-200 руб.

Если вам не нужен программатор STM8, а нужна только серия STM32, то хорошим вариантом будут платы Discovery от ST, они имеют на бору и программатор ST link. Однако, как правило, разъем для программирования STM8 там не разведен.

Ну и конечно, можно просто купить детали и сделать данное устройство самостоятельно. В основе лежит не самый дешевый микроконтроллер STM32, да и купить детали дешево не так просто, так что, стоимость будет от 300 до 400 рублей. В данной статье мы будем рассказывать, как собрать данный прибор самостоятельно из набора необходимых SMD компонент. Конечно же мы рекомендуем пойти этим путем. Только так вы сможете научится трассировке плат, их изготовлению и паянию.

Как изготовить программатор ST-LINK V2

2. Подготовить или приобрести необходимые инструменты: все для пайки , USB UART адаптер (будет нужен для программирования МК)

4. Скачать необходимые файлы по данному прибору с github .

5. Изготовить плату для прибора самостоятельно (это совсем несложно, в нашей инструкции все подробно описано).

6. Приобрести все необходимые комплектующие можно в нашем магазине за 300 руб.

7. Запаять все компоненты на плату, смотри наше видео .

ПРИБОР ГОТОВ , можно пользоваться!

Поиск схемы для ST Link программатора, отладчика

Сама компания ST не дает нам схему данного прибора, однако есть схемы ее ознакомительных плат серии DISCOVERY, в которых приводится и схема отладчика. Например документ UM0919. Но она не полная, там присутсвует только SWD интерфейс. В основе микроконтроллер STM32F103C8T6.


Вторая схема, которая есть в документе UM1670, содержит выводы SWIM выходов, но это уже версия V2.2 на другом микроконтролере STM32F103CBT6.


Также в интернет удалось найти схему ST-LINK v2, восстановленную по оригинальному прибору:

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

Требования к нашему ST-LINK

Мы будем делать приборы на базе STM8, а также STM32, процессоров NUVOTON Cortex-M0, ATMEL. Все они будут питаться от 3.3В или 5В. Так что, нам не нужна возможность работать с микроконтроллерами на напряжении 1.8В. Но сама возможность программировать STM8 нужна обязательно.

Мы делаем прибор для своих задач, поэтому у нас нет необходимости в стандартных разъемах SWIM и JTAG. Будет делать такой разъем, который удобнее для трассировки платы.

Версия 2.2 на микроконтролере STM32F103CBT6 добавляет второе USB устройство - COM порт UART, но он уже у нас есть, так что, нет смысла переплачивать, микроконтроллер там дороже. Правда у него есть хорошая возможность - прошивка через интерфейс DFU, то есть микроконтроллер видится как флешка при подключении по USB, и прошивку просто надо скопировать на диск. Но прошить надо будет один раз, и для этого у нас есть USB UART адаптер, прошивать первый раз будет через него. Дальнейшее обновление прошивки идет уже через программу от ST по USB. Мы будем делать версию 2.0 на базе STM32F103C8T6.

Оригинальная версия ST-Link содержит микросхему преобразования уровней, что удобно для отладки и прошивки готового устройства, и необходимо для работы с напряжением ниже 3.3В. У нас таких не будет, а для работы с 5В и 3.3В - преобразование уровней не нужно.

Прибор будем делать в формате USB dongle, соответсвенно будет использоваться разъем USB-A male.

На защите выходов можно сэкономить, так что не будем использовать защитные диоды. Достаточно будет сопротивлений на всех выходах разъемов на случай, если вдруг мы их подключим на 5В или землю. Надо обязательно иметь в виду, что пользоваться данным прибором надо аккуратно! Все выходы при подключении проверять несколько раз! Выход 3.3В больше защищен, он идет через регулятор напряжения, защищающий от КЗ. Так что, лучше питать тестовые схемы от него!

Теперь можно составить финальную схему нашего ST-Link.

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

Финальная схема

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

Основная часть:


Питание и разъемы:

Небольшие комментарии.

В качестве регулятора питания на 3.3в используем NCP603 - очень хороший LDO, выдает ток до 300ма с падением 300mv и точностью +-3%. Светодиоды индикации - обычные smd светодиоды двух цветов. Для программирования по UART необходимо вывод BOOT0 соединить с +3В, для этого выведем его на разъем. Также необходимо вывести сам UART - ножки RX TX. Все остальные выводы без защиты выведем на разъем. Пользуюсь этим программатором уже больше года, и кз были и помехи - ничего не сгорело ни разу.

В некоторых схемах ставится самовостанавливающийся предохранитель на питание от USB для защиты самого порта. Современные компьютеры имеют защиту на портах USB, в том числе предохранители и токовые ограничивающие ключи, так что он не нужен. Но лучше конечно не проверять это, и не ошибаться! Напряжение 3.3в идет с нашего LDO , который имеет защиту от КЗ и от перегрева, и не выдает больше 600ма , там тоже защищать нечего.

Очень удобно подключать STM8 для программирования с помощью ST-Link, нужно всего 3 провода - питание, земля и SWIM выход. Это так же удобно при разводке плат, можно разводить только SWIM выход, землю и питание всегда можно найти на плате.

Трассировка платы в Kicad с помощью автотрассировщика Topor

В приборе USB UART адаптер мы уже тренировались трассировать плату в Kicad вручную. Данный прибор чуть сложнее. На нем можно поучиться разводить плату в автотрассировщике TOPOR . Весь процесс лучше просмотреть на видео в конце статьи, здесь будут лишь небольшие комментарии к видео.

Подготовка платы к автотрассировке

Для того, чтобы работать с Topor, надо сначала подготовить плату в Kicad. Необходимо определить границы платы, импортировать все компоненты и предварительно их расположить. У нас нет требований к разъемам, поэтому на первом этапе лучше сам разъем удалить с платы. Так как каждый вывод разъема соединен через резистор, то резисторы и будут ориентиром выводов разъема. Также для расстановки компонент можно удалить все конденсаторы питания, кварцы, микросхемы питания (их лучше располагать на обратной стороне - там обычно много места) - это все можно расставить потом.

Теперь необходимо определить сторону кажого копонента. И примерно расположить их как необходимо, разъемы расположить у края. И на этом этапе можно все это перебросить в Topor и там продолжить размещение копонентов. USB разъем, светодиоды сразу располагаем на обратной стороне, все остальное на лицевой.

Размещение компонентов с помощью Topor

Теперь переносим это все в Topor и продолжаем там. Чем хорош Topor? Тем, что каждый раз, подвигав компоненты, можно перепроложить все трассы автоматически и посмотреть стало лучше или хуже. Также Topor умеет переворачивать простые компоненты - резисторы, конденсаторы. Нам важно понять как удобнее расположить выводы разъемов, и основные компоненты.

Покрутив и подвигав компоненты в Topor мы пришли к такому расположению:


Теперь необходимо этот результат перекинуть в Kicad обратно и добавить остальные компоненты. Перед финальной трассировкой необходимо:

    расположить микросхемы питания

    развести вручную цепи питания

    распложить и подключить кварц, и конденсаторы питания

    переопределить выводы разъема на схеме.

Автотрассировка

Перебрасываем нашу полутрассировку в Topor.

Необходимо сразу установить правила трассировки - ширину зазоров, дорожек, размеры переходных отверстий. При первом импорте из Kicad надо выделить все компоненты и зафиксировать их, чтобы можно было легко удалить кнопкой del и заново перепроложить трассировку, оставляя наш полуручной вариант. В параметрах автотрассировки обязательно необходимо установить галку «Использовать имеющуюся разводку в качестве начального варианта», иначе наши ручные трассы будут перепроложены (Сам процесс работы в Topor смотри на видео).

После автотрассировки перебрасываем все обратно и доводим до финального варианта - добавляем земляные полигоны, выравниваем где необходимо дорожки. Плата готова.

Финальный вариант платы

Лицевая сторона


Обратная сторона

Прошивка ST Link, установка драйверов

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

Для работы с нашим устройством необходимо установить драйвера, прошить его первый раз по UART стартовой прошивкой и потом обновить прошивку до последней версии специальной программой от ST.

Все микроконтроллеры STM32 имеют bootloader и прошиваются по UART. Для прошивки необходимо:

Теперь у нас есть ST LINK, но со старой прошивкой. Убираем все провода. Скачиваем с сайта ST программу обновления прошивки STSW-LINK007 и драйвера STSW-LINK009 для windows. Вставляем новоиспеченный ST-Link в USB порт компьютера, и запускаем программу обновления прошивки, в ней жмем CONNECT и потом обновить прошивку до последней версии. Прибор ГОТОВ! Теперь у вас есть программатор-отладчик и можно перейти к программированию.

Готовое устройство

Самостоятельная работа

Потренируйтесь разводить плату. Сделайте это вручную, с помощью программы Topor и без. Вы должны уметь быстро делать любую несложную плату.

Некоторые из микроконтроллеров stm32 поддерживают USB DFU протокол (их список можно посмотреть в app note AN3156), в такие МК прошивку можно заливать через обычный USB, используя например DFuSe demo от ST, либо опенсурсный dfu-util. С этим вариантом все понятно и описывать его я не буду.

Для тех же МК (в частности и того, который используется в плате BluePill — STM32F103C8T6), которые обделены поддержкой DFU, так или иначе нужен программатор, например ST-Link V2 Mini

Распиновка устройства:

Подключается к плате просто:

ST-Link STM32F103C8T6 3.3V --- 3.3V GND --- GND SWDIO --- DIO SWCLK --- DCLK

Также нужна утилита ST-Link Utility, скачать ее можно с официального сайта st.com — ссылка . При первом подключении неплохо было бы обновить firmware самого программатора. Выбираем ST-LINK -> firmware update, если доступна более свежая firmware, то будет что-то подобное:

Выбираем Yes >>>>, прошивка обновляется.

Далее открываем собственно файл с прошивкой, и выбираем Target -> Connect. В окне состояния утилиты появится информация и вашем МК — это значит, что программатор подключен корректно и утилита может контактировать с МК. Пример:

Затем нужно сделать полную очистку чипа, выбираем Target -> Erase Chip

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

  1. переставить оба желтых джампера. Плата в этом случае загрузится у вас сразу во внутренний бутлоадер
  2. можно сделать так называемый Connect under Reset. Для него последовательность будет такая:
  • в ST-Link Utility выбираем Target -> Settings
  • в Reset Mode выбираем Hardware Reset
  • нажимаем и держим кнопку Reset на плате
  • нажимаем OK в ST-Link Utility
  • отпускаем кнопку Reset на плате

PS. Если у вас есть плата для разработчиков SMT32F4Discovery, то она уже имеет в себе программатор и его также можно использовать для прошивки другой платы. В данном случае нужно использовать разъем SWD у STM32F4Discovery и убрать обе перемычки CN3. Разъем SWD имеет следующую распиновку:

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

Меня это сильно удивило. Если верить данным статьям, для программирования не обязательно даже читать документацию к программируемому контроллеру. Меня же учили премудростям «железного программирования» совершенно иначе.

В этой статье, путь от фразы «Да, я хочу попробовать!» до радостного подмигивания светодиода, будет значительно длиннее чем у других авторов. Я постараюсь раскрыть аспекты программирования микроконтроллеров, которые прячутся за использованием библиотечных функций и готовых примеров.
Если вы намерены серьезно изучать программирование микроконтроллеров данная статья для вас. Возможно, она может заинтересовать и тех, кто вдоволь наигрался с Arduino и хочет получить в свои руки все аппаратные возможности железа.

Выбор микроконтроллера

Многие могут сказать, что начинать изучение микроконтроллеров лучше с AVR, PIC, 8051 или чего-то еще. Вопрос многогранный и спорный. Я знаю достаточно примеров, когда люди изучив Cortex-M, программировали AVR, ARM7 и т.д. Сам же я начинал с Cortex-M3. Если перед вами стоит определенная задача, в интернете достаточно много информации со сравнением различных типов микроконтроллеров и решаемых с их помощью задач. На хабре этот вопрос тоже поднимался, например .

Будем считать, что с типом микроконтроллера мы разобрались. Но на рынке представлен огромнейший спектр различных модификаций от разных производителей. Они отличаются по множеству параметров - от размера флеш памяти до количества аналоговых входов. Для каждой задачи выбор стоит производить индивидуально. Ни каких общих рекомендаций тут нет и быть не может. Отмечу лишь, что стоит начинать изучение с МК производителей имеющих как можно больший ассортимент. Тогда, при выборе МК для определенной задачи достаточно велик шанс, что из представленного ассортимента вам что-нибудь да подойдет.

Я остановил свой выбор на STM32 (хотя и считаю, что лучше начинать изучение с МК от TexasInstruments - очень грамотно составлена документация), потому что они широко распространены среди российских разработчиков электроники. При возникновении проблем и вопросов вы сможете без труда найти решения на форумах. Еще одним плюсом является богатый выбор демонстрационных плат как от производителя, так и от сторонних организаций.

Что необходимо для изучения?

К сожалению, для начала программирования МК не достаточно одного лишь ПК. Придется где-то раздобыть демонстрационную плату и программатор. Хотя это и уменьшает конкуренцию на рынке труда.

Сам я использую демонстрационную плату STM3220G-EVAL и программатор J-Link PRO . Но для начала, будет вполне достаточно STM32F4DISCOVERY , которую можно купить без особых проблем за небольшую сумму.

Все примеры будут именно для отладочной платы STM32F4DISCOVERY . На данном этапе нам будет совершенно не важно, что этой плате стоит МК на базе ядра Cortex-M4. В ближайшее время мы не будем использовать его особенности и преимущества над Cortex-M3. А как там будет дальше - посмотрим.

Если у вас есть в наличии любая другая плата на базе STM32F2xx/STM32F4xx, вы сможете работать с ней. В изложении материала я постараюсь максимально подробно описывать почему мы делаем именно так, а не иначе. Надеюсь ни у кого не возникнет проблем с переносом примеров на другое железо.

Среда разработки

Как уже неоднократно упоминалось, для ARM микроконтроллеров существует достаточное количество сред разработки, как платных так и не очень. И снова хочется опустить полемику по этому поводу. Я использую IAR Embedded Workbench for ARM 6.60 . Все примеры будут именно в этой среде. Если вам по душе (или в вашей организации используется) что-то другое (Keil, Eclipse, CCS, CooCoc и т.д.) то это вам тоже не очень помешает. На особенности, связанные именно со средой разработки, я буду обращать отдельное внимание.

Почему платная среда разработки?

Возможно, кто-то будет не совсем доволен тем, что я предлагаю использовать платную среду разработки, но в IAR есть возможность получить временную лицензию без ограничения функционала, либо безлимитную лицензию с ограничением по размеру кода (32КБ для МК это очень много).
Помимо этого, сразу замечу, что для некоторых МК не существует бесплатных сред разработки. И к сожалению эти МК в некоторых областях незаменимы.


Процесс установки я описывать не буду.

С чего начать?

Создание проекта
Для начала создадим пустой проект. IAR позволяет создать проекты на ASM, C и C++. Мы будем использовать C.

Перед нами появится пустой проект с main файлом.

Теперь необходимо настроить проект для начала работы с «нашим» МК и отладчиком. На плате STM32F4DISCOVERY установлен MK STM32F407VG . Его необходимо выбрать в свойствах проекта (General Options->Target->Device):

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

После этого необходимо настроить отладчик. Отладка программы происходит непосредственно «в железе». Производится это с помощью JTAG отладчика. Более подробнее ознакомиться с тем, как это происходит можно на Википедии . На плату STM32F4DISCOVERY интегрирован отладчик ST-LINK/V2. Для работы с отладчиком необходимо выбрать его драйвер в меню Debugger->Setup->Driver . Так же необходимо указать, что отладка должна производиться непосредственно в железе. Для этого необходимо поставить флаг Debugger->Download->Use flash loader(s)


Для тех, кто увидел слово Simulator

Теоретически, IAR позволяет отлаживать программы с использованием симулятора. Но я ни разу на практике не встречал его использования.

Теперь проект готов для работы (программирования, заливки и отладки).

«ТЗ» для первого проекта
Подведем промежуточный итог: МК и отладочная плата выбраны, проект подготовлен. Пора определиться с задачей.

Не будем отходить от классики. Первым проектом будет мигающий светодиод. Благо на плате их предостаточно.Что же это означает с точки зрения программирования? Первым делом необходимо изучить принципиальную схему демонстрационной платы и понять как «заводится» светодиод.
доступен на сайте производителя. В данном описании даже есть отдельный раздел про светодиоды на плате -4.4 LEDs . Для примера, будем использовать User LD3 . Найдем его на схеме:

Простейший анализ схемы говорит о том, что для того, что бы «зажечь» светодиод необходимо на пин МК подать «1» (которая для данного МК соответствует 3.3В). Выключение производится подачей на этот пин «0». На схеме этот пин обозначается PD13 (это, наверное, самая важная информация из этого документа).

В итоге, мы можем написать «ТЗ» для нашей первой программы:
Программа для МК должна переводить состояние пина МК PD13 из состояния «0» в состояние «1» и обратно с некоторой периодичностью, различимой для человеческого глаза (важное замечание, если моргать светодиодом слишком часто глаз может этого не различить).

Прежде чем приступать к программированию, или немного теории
Прежде чем приступить к реализации нашего ТЗ, необходимо понять как производится управление МК.

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

Ядро - часть микроконтроллера, осуществляющая выполнение одного потока команд. В нашем МК тип ядра - Cortex-M4. Ядро МК можно сравнить с процессором в ПК. Оно умеет только выполнять команды и передавать данные другим блокам (в этом сравнении не учитываются процессоры с интегрированными графическими ускорителями).
При этом производитель МК не разрабатывает ядро. Ядро покупается у компании ARM Limited . Главное отличие между различными МК - в периферии.

Периферийные блоки - блоки осуществляющие взаимодействие с «внешним миром» или выполняющие специфические функции, недоступные ядру МК. Современные МК (в том числе и STM32) содержат огромный спектр периферийных блоков. Периферийные блоки предназначены для решения различных задач, от считывания значения напряжения с аналогового входа МК до передачи данных внешним устройствам по шине SPI.
В отличии от ядра МК периферийные блоки не выполняют инструкции. Они лишь выполняют команды ядра. При этом участие ядра при выполнении команды не требуется.

Пример

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

Взаимодействие ядра с периферийным блоком
Взаимодействие ядра МК с периферийным блоком осуществляется с помощью спецрегистров (есть еще взаимодействие через механизм прерываний и DMA, но об этом в следующих постах). С точки зрения ядра это просто участок памяти с определенным адресом, вот только на самом деле это не так . Запись данных в спецрегистр эквивалентна передаче команды или данных периферийному блоку. Считывание - получение данных от блока или считывание его состояния. Описание периферийных блоков и их спецрегистров занимает львиную долю описания МК.

ВАЖНО: После записи данных в спецрегистр и последующем чтении вы можете получить совершенно иные данные. Например, передача данных блоку UART для отправки, и считывание данных, полученных блоком от внешнего устройства, осуществляется с помощью одного и того же регистра.

Спецрегистры обычно разделены на битовые поля. Один (или несколько) бит управляют определенным параметром периферийного блока, обычно независимо. Например, разные биты одного регистра управляют состоянием разных выходов МК.

Вспоминаем С
Если вы гуру в языке C, то можете смело пропускать данный раздел. Он предназначен в первую очередь для тех, кого учили (или ктоучился сам) программировать для ПК. Опыт показывает, что люди часто не помнят важных команд. Здесь я вкратце напомню про побитовые операции и работу напрямую с памятью по ее адресу.

Запись данных по адресу в памяти

Предположим, что читая описание периферийного блока, мы поняли, что для его корректной работы необходимо записать в него число 0x3B. Адрес спецрегистра 0x60004012. Регистр 32-битный.
Если вы сразу не знаете как это сделать, попробую описать цепочку рассуждений для получения правильной команды.

Значение 0x60004012 есть не что иное, как значение указателя на ячейку памяти. Нужно именно это и указать в нашей программе, тоесть сделать преобразование типов согласно синтаксису языка C:

(unsigned long*)(0x60004012)

Таким образом, у нас есть указатель на элемент. Теперь нужно в этот элемент записать необходимое значение. Делается это разыменовыванием указателя. Таким образом получаем правильную команду:

*(unsigned long*)(0x60004012) = 0x3B;

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

Предположим, что необходимо установить «1» в 7 и 1 биты по адресу 0x60004012, при этом не изменив значение всех остальных бит в регистре. Для этого необходимо использовать бинарную операцию |. Сразу приведу правильный ответ:

*(unsigned long*)(0x60004012) |= 0x82;

Обратите внимание на 2 факта. Биты считаются с нулевого, а не с первого. Данная операция на самом деле занимает неменее 3 тактов - считывание значения, модификация, запись. Иногда это не допустимо, поскольку между считыванием и записью значение одного из бит, которые нам запрещено изменять, могло быть изменено периферийным блоком. Незабывайте про эту особенность, иначе могут полезть баги, которые крайне сложно отловить.

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

Предположим, что необходимо установить «0» в 7 и 1 биты по адресу 0x60004012, при этом не изменив значение всех остальных бит в регистре. Для этого необходимо использовать бинарную операцию &. Сразу приведу правильный ответ:

*(unsigned long*)(0x60004012) &= 0xFFFFFF7D;

Или его более простою запись (не переживайте за лишнюю операцию, компилятор все заранее посчитает даже при минимальной оптимизации):

*(unsigned long*)(0x60004012) &= (~0x82);

Некоторые особенности программ для МК
Здесь я постараюсь описать некоторые особенности программ для МК, которые важно помнить. Вещи достаточно очевидные, но все же.
У программы нет конца
В отличии от большинства программ для ПК, программа для МК не должна заканчиваться, НИКОГДА! А что собственно должен будет делать МК после завершения вашей программы? Вопрос, практически, риторический. Поэтому не забываем убедиться в том, что вы не забыли вечный цикл. При желании, можно перевести МК в режим сна.
Пользуйтесь целочисленными переменными
Не смотря на то, что мы используем МК с ядром Cortex-M4, который аппаратно выполняет операции над числами с плавающей точкой, советую вам отказаться от их использования. В МК без поддержки таких операций время вычислений будет просто огромным.
Откажитесь от динамического выделения памяти
Это только совет. Причина проста - памяти мало. Я не раз встречался с библиотеками, в которых были «медленные утечки» памяти. Было очень неприятно, когда после нескольких недель стабильной работы МК зависал с ошибкой. Лучше заранее продумать архитектуру своей программы так, чтобы не пришлось использовать динамическое выделение памяти.
Если же все-таки хочется использовать - внимательно изучите работу менеджера памяти или пишите свой.

Приступаем к работе!

Работа над программой для МК всегда начинается с чтения документации. Для нашего МК доступен на сайте производителя. Страниц много, но все читать пока не нужно. Как уже было сказано, большую часть документации составляет описание периферийных блоков и их регистров. Так же хочу обратить внимание на то, что этот Reference Manual написан не для одного МК, а для нескольких линеек. Это говорит о том, что код будет переносим при переходе на другие МК в этих линейках (если конечно не пытаться использовать периферийные блоки которых нет в используемом МК).

В первую очередь необходимо определиться с какими блоками предстоит работать. Для это достаточно изучит разделы Introduction и Main features .

Непосредственное управление состоянием пинов МК осуществляется с помощью блока GPIO. Как указано в документации в МК STM32 может быть до 11 независимых блоков GPIO. Различные периферийные блоки GPIO принято называть портами. Порты обозначаются буквам от A до K. Каждый порт может содержать до 16 пинов. Как мы отметили ранее, светодиод подключается к пину PD13. Это означает, что управление этим пином осуществляется периферийным блоком GPIO порт D. Номер пина 13.

Ни каких других периферийных блоков на это раз нам не понадобится.

Управление тактированием периферийных блоков
Для снижения электропотребления МК практически все периферийные блоки после включения МК отключены. Включение/выключение блока производится подачей/прекращением подачи тактового сигнала на его вход. Для корректной работы, необходимо сконфигурировать контроллер тактового сигнала МК, чтобы необходимому периферийному блоку поступал тактовый сигнал.
Важно: Периферийный блок не может начать работу сразу после включения тактового сигнала. Необходимо подождать несколько тактов пока он «запустится». Люди, использующие библиотеки для периферийных устройств, зачастую даже не знают об этой особенности.

За включение тактирования периферийных блоков отвечают регистры RCC XXX peripheral clock enable register .На месте XXX могут стоять шины AHB1, AHB2, AHB3, APB1 и APB2. После внимательного изучения описания соответствующих регистров, можно сделать вывод о том, тактирование периферийного блока GPIOD включается установкой «1» в третий бит регистра RCC AHB1 peripheral clock enable register (RCC_AHB1ENR) :

Теперь необходимо разобраться с тем, как узнать адрес самого регистра RCC_AHB1ENR .

Замечание: Описание системы тактирования МК STM32 достойно отдельной статьи. Если у читателей возникнет желание, я подробнее освещу этот раздел в одной из следующих статей.

Определение адресов спецрегистров
Определение адресов спецрегистров необходимо начинать с чтения раздела Memory map в Reference manual. Можно заметить, что каждому блоку выделен свой участок адресного пространства. Например, для блока RCC это участок 0x4002 3800 - 0x4002 3BFF:

Для получения адреса регистра, необходимо к начальному значению адресного пространства блока RCC прибавить Addr. offset нужного регистра. Addres offset указывается и в описании регистра (см. скриншот выше).

В итоге, мы определили адрес регистра RCC_AHB1ENR - 0x4002 3830.

Блок GPIO
Для общего ознакомления с блоком GPIO я настоятельно рекомендую полностью прочитать соответствующий раздел Reference Manual. Пока можно не особо обращать внимание на Alternate mode . Это оставим на потом.

Сейчас же наша задача научиться управлять состоянием пинов МК. Перейдем сразу к описанию регистров GPIO.

Режим работы
В первую очередь необходимо установить режим работы 13 пина порта D как General purpose output mode , что означает что блок GPIO будет управлять состоянием пина МК. Управление режимом работы пинов МК производитсяс помощью регистра GPIO port mode register (GPIOx_MODER) (x = A..I/J/K) :

Как видно из описания для совершения требуемой нам настройки необходимо записать значение 01b в 26-27 биты регистра GPIOx_MODER . Адрес регистра можно определить тем же методом, что описан выше.

Настройка параметров работы выходных пинов порта GPIO
Блок GPIO позволяет применить дополнительные настройки для выходных пинов порта. Данные настройки производятся в регистрах:
  • GPIO port output type register (GPIOx_OTYPER) - задается тип выхода push-pull или open-drain
  • GPIO port output speed register (GPIOx_OSPEEDR) - задается скорость работы выхода
Мы не будем менять данных параметров, поскольку нас вполне устраивают значения по умолчанию.
Установка значения на пине МК
Наконец-то мы подошли к моменту управления состоянием выхода МК. Для утановки выходного значения на определенном пине МК есть два метода.

Используем регистр GPIO port bit set/reset register (GPIOx_BSRR)

Запись «0» или «1» в биты 0-16 приводят к соответствующему изменению состояния пинов порта. Для того, чтобы установить определенное значение на выходе одного или нескольких пинов МК и не изменить состояния остальных, необходимо будет пользоваться операцией модификации отдельных бит. Такая операция выполняется не менее чем за 3 такта. Если же необходимо в часть битов записать 1, а в другие 0, то понадобится не менее 4 тактов. Данный метод предпочтительнее всего использовать для изменения состояния выхода на противоположное, если его изначальное состояние не известно.

GPIO port bit set/reset register (GPIOx_BSRR)

В отличии от предыдущего метода, запись 0 в любой из битов данного регистра не приведет ни к чему (да и вообще, все биты write-only!). Запись 1 в биты 0-15 приведет к установке «1» на соответствующем выходе МК. Запись 1 в биты 16-31 приведет к установке «0» на соответствующем выходе МК. Этот метод предпочтительнее предыдущего, если необходимо установить определенное значение на пине «МК», а не изменить его.

Зажигаем светодиод!
Найдя адреса всех необходимых регистров, можно написать программу, которая включает светодиод:
void main() { //Enable port D clocking *(unsigned long*)(0x40023830) |= 0x8; //little delay for GPIOD get ready volatile unsigned long i=0; i++; i++; i++; i=0; //Set PD13 as General purpose output *(unsigned long*)(0x40020C00) = (*(unsigned long*)(0x40020C00)& (~0x0C000000)) | (0x04000000); //Turn LED ON! *(unsigned long*)(0x40020C14) |= 0x2000; while(1); }
Можно компилировать (Project->Compile ) и заливать (Project->Download->Download active application ). Или запустить отладку (Project->Dpwnload and Debug ) и начать выполнение (F5).
Светодиод загорелся!
Мигаем светодиодом
Мигание светодиода есть ни что иное, как попеременное включение и выключение с задержкой между этими действиями. Самый простой способ - поместить включение и выключение в вечный цикл, а между ними вставить задержку.
void main() { //Enable port D clocking *(unsigned long*)(0x40023830) |= 0x8; //little delay for GPIOD get ready volatile unsigned long i=0; i++; i++; i++; i=0; //Set PD13 as General purpose output *(unsigned long*)(0x40020C00) = (*(unsigned long*)(0x40020C00)& (~0x0C000000)) | (0x04000000); while(1) { //Turn LED ON *(unsigned long*)(0x40020C14) |= 0x2000; //Delay for(i=0; i<1000000 ;++i); //Turn LED OFF *(unsigned long*)(0x40020C14) &= ~0x2000; //Delay for(i=0; i<1000000 ;++i); } }
Значение 1000000 в задержке подобрано экспериментально так, чтобы период мигания светодиода был различим глазом, но и не был слишком велик.
Оптимизируем алгоритм
Минусом выбранного подхода миганием светодиодом является то, что ядро МК большую часть времени проводит в пустых циклах, хотя мог бы заниматься чем-нибудь полезным (в нашем примере других задач нет, но в будущем они появятся).

Для того, чтобы этого избежать, обычно используется счетчик циклов, а переключение состояние пина МК происходит при прохождении программы определенного числа циклов.
void main() { //Enable port D clocking *(unsigned long*)(0x40023830) |= 0x8; //little delay for GPIOD get ready volatile unsigned long i=0; i++; i++; i++; i=0; //Set PD13 as General purpose output *(unsigned long*)(0x40020C00) = (*(unsigned long*)(0x40020C00)& (~0x0C000000)) | (0x04000000); while(1) { i++; if(!(i%2000000)) { //Turn LED ON *(unsigned long*)(0x40020С14) |= 0x2020; } else if(!(i%1000000)) { //Turn LED OFF *(unsigned long*)(0x40020С14) &= ~0x2000; } } }
Но и тут не обойдется без проблем, с изменением количества команд выполняемых внутри цикла, будет меняться период мигания светодиодом (или период выполнения других команд в цикле). Но на данном этапе мы не можем с этим бороться.

Немного об отладке
IAR позволяет осуществлять отладку приложения непосредственно в железе. Все выглядит практически так же, как и отладка приложения для ПК. Есть режим пошагового выполнения, входа в функцию, просмотр значения переменных (В режиме отладки View->Watch->Watch1/4 ).

Но помимо этого, присутствует возможность просмотра значений регистров ядра, спецрегистров периферийных блоков (View->Register) и т.п.
Я настоятельно рекомендую ознакомиться с возможностями дебаггера во время изучения программирования МК.

Несколько слов в заключение

Возможно, многие скажут, что ручное прописывание адресов в программе это не правильно, поскольку производитель предоставляет файлы с определениями регистров и битовых полей, библиотеки для работы с периферией и другие инструменты, облегчающие жизнь разработчику. Я с этим полностью согласен, но все равно считаю, что первые шаги в программировании МК необходимо делать перекапывая документацию к вручную, самостоятельно определяя необходимые регистры и битовые поля. В дальнейшем этим можно не пользоваться, но уметь нужно обязательно.
Приведу лишь несколько причин для этого утверждения:
  • В библиотеках от производителя иногда встречаются ошибки! Я один раз чуть не сорвал срок проекта из-за этого. Несколько раз перепаивал чип, думая, сто повредил кристалл при пайке (до этого такое случалось). А проблема заключалась в том, что в библиотеке был неверно прописан адрес спецрегистра. Обычно такое случается с МК или линейками МК только вышедшими на рынок.
  • Библиотеки для работы спериферией некоторых производителей не реализуют всех возможностей периферийных блоков. Особенно этим грешилb Luminary Micro , которых в последствии выкупили TI. Приходилось писать инициализацию периферии вручную.
  • Многие привыкают начинать программирование МК с изучения примеров. Я считаю, что сперва необходимо определиться с тем, что позволяет реализовать МК. Это можнопонять только прочитав документацию. Если чего-то нет в примерах, это не значит, что железоэто не поддерживает. Последний пример - аппаратная поддерка PTP STM32. В сети, конечно, можно кое-что найти, но это не входит в стандартный набор от производителя.
  • Драйверы периферийных блоков некоторых производителей настолько не оптимизированы, что на переключение состояния пина средствами библиотеки тратится до 20 тактов. Это непозволительная роскошь для некоторых задач.

Спасибо всем, кто прочитал мой пост, получилось значительно больше чем я ожидал в начале.
Жду ваших комментариев и аргументированной критики. Если у прочитавших возникнет желание - постараюсь продолжить цикл статей. Возможно у кого-то есть идеи по поводу тем, которые стоило бы осветить - я был бы рад их услышать.

© 2024 nimfettamine.ru - Windows. Железо. Программы. Безопасность. Операционные системы