Pull to refresh

Comments 39

Регистры у F4xx и F1xx отличаются, у тех же GPIO, допустим. Настраиваются по-разному, у F1xx режим с настройками упиханы в общий регистр, у F4xx — отдельные регистры.
Ремэп тоже сильно отличается, у F1xx это один регистр с битами вида «ремэпнуть таймер1», у F4xx — полноценный ремэпер, где можно на конкретный пин задать конкретную периферию.

Поэтому я бы не стал говорить что не важно, что юзать — F1xx или F4xx, у начинающих запросто могут вылезти проблемы, оттого, что, скажем, они пишут код как для F1xx, пытаются его использовать на F4xx, но не включили мэпинг периферии.

P.S. Магические числа в коде — это адЪ. Хоть бы константы именованные юзали.
Для этого я специально указал, что лучше использовать F2 или F4:
Если у вас есть в наличии любая другая плата на базе STM32F2xx/STM32F4xx, вы сможете работать с ней.

Вообще говоря, складывается впечатление, что на F1 ST «обкатывали» технологии и накапливали проблемы и баги. F2 сильно отличается от F1, а вот последующие уже очень похожи.
К сожалению, невозможно было выбрать один универсальный МК. Также не вижу смысла писать обязательные требования по какой-то определенной отладочной плате.

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

На счет магических чисел я тоже вас поддерживаю, но считаю, на первых шагах код должен выглядеть именно так. Отчасти из-за того, что при желании воспользоваться «готовым» куском кода вам придется лезть в описание регистров МК.

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

//Enable port D clocking
  *(unsigned long*)(0x40023830) |= 0x8;


или

#define RCCAHB1_ENR (unsigned long*)0x40023830
//Enable port D clocking
  *RCCAHB1_ENR  |= 0x8;


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

Во-вторых, каким образом ваш код больше стимулирует к изучению документации, чем код с использованием дефайнов от ST?
RCC->AHB1ENR	|= RCC_AHB1ENR_IOPDEN;

Человеку в любом случае придется полезть в документацию, чтобы узнать, что это за регистр AHB1ENR и что за флаг RCC_AHB1ENR_IOPDEN, так зачем тогда намеренно портить код?
1) Не согласен с вашей категоричностью.
Я считаю, что на первых порах важно помнить про то, что мы работаем со спецрегистрами. Магические числа, лично мне, в этом помогают.
До знакомства с библиотеками я делал немного иначе:
#define RCCAHB1_ENR (*(unsigned long*)0x40023830)
#define RCCAHB1_PORTD 0x8
//Enable port D clocking
RCCAHB1_ENR  |= RCCAHB1_PORTD;

Как вы, наверное, знаете в большинстве начальных программ для МК быстро конфигурируется периферия, и дальше уже используются дефайны для работы с определенными регистрами (даже без использования библиотек). И этих регистров не так много. Так вот, используя определения, люди часто забывают о том, что на самом деле происходит с их коде. На курсах программирования МК часто встречались случаи, когда студенты делали записи:
#define EnableLED1()  ((unsigned long*)(0x40020C14) |= 0x2000)

А по прошествии недели спрашивали: «Какой хэддер нужно подключить, чтобы заработала функция EnableLED1?».
Выносить что-либо в макросы стоит только когда вы на 100% уверены, что знаете как это работает.
Все выше сказанное только мое мнение, а не руководство к действию. Каждый сам решает как ему оформлять код. По этому поводу написано миллионы страниц текста.

2) По поводу:
RCC->AHB1ENR	|= RCC_AHB1ENR_IOPDEN

Есть несколько соображений:
1. Нужно немного разобраться с библиотекой, покопаться в примерах и т.д. Если пытаться все делать сразу — будет каша в голове (ИМХО). Я предпочитаю начать с документов.
2. Я стараюсь описать принцип программирования МК на примре STM32, а не описать библиотеку и возможности STM32. У TI, например, библиотека устроена совершенно иначе.
3 Ваш пример не показателен. Часто встречается ошибка — путаница с GPIO_PinSource8 и GPIO_Pin_8.
4. Не стимулирует. Вот пример который компилируется но не cработает:

RCC->AHB1ENR |= RCC_AHB1Periph_SRAM1;


После 3 инициализаций периферии люди автоматически начинают экстраполировать методы инициализации. Это не верно. Главный посыл такой: сначала документация — затем программирование.
Регистры у F4xx и F1xx отличаются, у тех же GPIO, допустим. Настраиваются по-разному, у F1xx режим с настройками упиханы в общий регистр, у F4xx — отдельные регистры.
Ещё ощутимо различается настройка AF in, т. к. на F1xx используется IN_FLOATING, а на F4xx AF. Плюс ремаппер, да.
В библиотеках от производителя иногда встречаются ошибки
Я как-то час убил пытаясь сопоставить логику работы кода по инициализации UART в примере на TI'шный stellaris launchpad и в мануале на него же. В итоге оказалось что алгоритм в примере был не правильный, но в частном случае «по умолчанию» он таки работал.

Впрочем, бывают варианты с опечатками в документации, или даже с принципиально неправильно работающей перифирией, так что когда не работает что-то, что в принципе должно, есть хороший повод заглянуть в errata по МК (так я узнал что у lpc810 надо делать дополнительные танцы с бубном для прошивки флеша прямо из МК).
Пример был от TI или шел со средой разработки?
Иногда примеры для среды разработки делают совершенно другие люди.
От TI, если мне не изменяет память. Я никогда не заморачивался Keil'ами и IAR'ами, потому что «vim'а хватит везде» :-)
А как же Trace или банальный просмотр спецрегистров с описанием битовых полей?
Очень помогает в работе.
У меня, наверно, было тяжелое детство, приходилось дебажить раннюю загрузку linux миганиями светодиода, так что я вполне могу пережить без этих вот новомодных отладчиков (а по сути — я никогда профессионально разработкой ПО в windows не занимался, потому нет привычки ко всяким армовым иде).

Вообще есть openocd и gdb, их всегда хватает для отладки. Gdb вообще очень функционален и удобен, если к нему попривыкнуть. А компилировать проект из Makefile — это вообще на порядок очевиднее чем IDE должно быть, потому что видно, что происходит.
Скажите гуру, как лучше писать для контроллеров:
volatile int i = 0;
или
int volatile i = 0;
?
Спасибо
Гуру я себя не называл…
В данном случае разницы нет.
Хитрости с указателями. Можно почитать тут.
publib.boulder.ibm.com/infocenter/comphelp/v101v121/index.jsp?topic=/com.ibm.xlcpp101.aix.doc/language_ref/cv_qualifiers.html

В первом примере как-то не понадобилось. Но вот когда дойдем до считывания данных из регистров — вот тогда затронем этот вопрос.
Поэтому все примеры для Discovery.
Да и клонов от дружелюбных нам соседей предостаточно.
Хехе. У меня брат один такой уже умер от прошивки 4.80. Как говорится, работает — не трогай.
Отдал убитый клон старшим товарищам, может и вылечат. Вроде там надо слово GDBFULL
обязательно писать большими буквами.

Segger J-Link очень хорошо подходит к дешовым платам типа LC Technology по 7.99$
www.lctech-inc.com/Hardware/Default.aspx?Id=6e665f5e-20d7-4986-94da-bd5a89058f72
www.aliexpress.com/store/group/Cortex-Study-Board/524881_251395501.html

Но мне кажется, что Discovery все же круче и периферии в ней нашпиговано
больше.
Недавно купил stm32f4 discovery — решил поиграться. Цель написать приложения
— без использования IDE, только make и command-line tools для прошивки (st-util)
— без использования библиотек, будь то стандартная от stm или libopencm3
— сделать приложение с минимум ассемблера, но при этом эффективно.

Все дело должно работать на Linux Arch (пришлось несколько пакетов переместить из AUR в [community]).

В общем после курения мануалов и интернетов получилась вот такое вот минималистическое приложение (конечно же диодиком моргать) github.com/anatol/stm32f4-examples/tree/master/blink01
Какой смысл не использовать CMSIS (для STM32 проще взять и выдрать её из SPL), которая минималистична и даёт готовые define для адресов периферии? Проверить, что это возможно?

Удобство падает, вероятность сделать ошибку растет, больше внимания и усилий тратится не на логику и control flow программы (а при наличии мало-мальски нетривиального набора прерываний он уже довольно сложен), а на аккуратное вбивание адресов и смещений из datasheet'а.

Опять же это не спасет от, например, неправильной задержки после инициализации RCC (которая в разных версиях для разных шин — разная). Или от попадания на какой-нибудь баг из errata.

пришлось несколько пакетов переместить из AUR в [community]
Вы Anatol Pomozov?
Проверить, что это возможно?


Да! Выучить вещи такие инициализация устройства, инициализация приложения (bss/data секции); что есть вектор прерываний, как работает линковка…

Если действительно хотите выучить программирование но нахер выбросить IDE/библиотеки и взять написать простое приложение с минимумом зависимостей и пониманием каждой строчки что написана. И только после этого брать и работать с библиотеками.
Постойте, но текст вроде не для тех, кто хочет научиться программировать? Он для тех, кто хочет пощупать новую архитектуру. Думаю, читатель уже должен представлять, что такое библиотека и как с ней работать, а уж заглянуть в файл с дефайнами и подавно.
Да, в рамках обучения это полезно. У вас, например, нет важных хэндлеров в таблице векторов прерываний (NMI, HardFault, MemManage, BusFault, UsageFault, SVC, DebugMon, PendSV). В процессе отладки stlink2 умеет сваливаться в HardFault, а у вас undefined behavior, т. к. секция .text будет лежать в той части, где должна быть таблица векторов прерываний. Пока исполнительных устройств нет, может и ничего, но при их наличии — довольно неприятно.
Обучение это прежде всего формирование понимания и правильных навыков. Потом, в процессе реальной работы, когда важна скорость выдачи продукта, можно обвешиваться какими угодно IDE и бирюльками. Но на данном этапе, я считаю, только так…
При чём здесь IDE? В нормальной IDE вы можете использовать make для сборки, написать руками скрипт для линковщика и т. п.

Использование визардов IDE на начальном этапе (без понимания того, что они генерируют) — зло.

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

Я вообще считаю, что обучаться Си лучше на простом контроллере, например, AVR. Ассемблер там простой, спецрегистров немного, периферии — тоже.
Э-э… Вы отвечаете googol, но он не автор статьи.

Си лучше изучить на обычном компе, а на контроллеры идти с уже устоявшимися знаниями языка. Там цена ошибки меньше. Запороть контроллер случайной опечаткой в коде как два пальца об асфальт…
Что до магических чисел. Я, например, как адепт старой школы, на автопилоте всем константам прописываю мнемонические названия.
В данном случае надо всё-таки определиться, учим правильным навыкам или всё-таки программированию микроконтроллеров. В каждом случае фокусироваться надо на своём.
Крайний раз я отвечал вам, т. к. вы ответили на мой комментарий, адресованный googol. Хотя в моём комментарии не было ничего про IDE.

Что до магических чисел. Я, например, как адепт старой школы, на автопилоте всем константам прописываю мнемонические названия.
Это не старая школа, это — обычный, но необходимый, навык. Присутствие магических чисел в коде — очень серьезный code smell.

Убить контроллер опечаткой — довольно сложно. Обычно на критичных регистрах есть защита (например, перед изменением записать пару магических констант в спецрегистр, и изменять в течении не более чем N тактов).

Я приверженец мнения, что Си можно изучить только работая, как минимум с памятью, на низком уровне. Будет это на x86, x51, AVR, PIC, ARM — не суть важно. На x86, разве что, порог вхождения ниже за счёт легкой работы с libc.

Правильные навыки, что там, что там по большей части одинаковы. Если, конечно, не замахиваться на какой-нибудь MISRA C, который ещё ужесточает ограничения.
IDE я, конечно, для красного словца приплёл…

Прямо убить контроллер — это, конечно, надо постараться. А вот из-за набитой с опечаткой чужой программки, как-то пришлось полдня потом промудохаться, чтобы привести контроллер в чувство. Было дело… Он был уже запаян на плату…
Сравнивая с habrahabr.ru/post/189484/ бирюльки в виде IAR появляются уже у вас :-) Впрочем я нисколько не дискредитирую вашу статью — тема разбора reference manual у вас раскрыта лучше.
Пардон, но лично я давно и счастливо сижу на Linux'e, и IAR стараюсь обходить стороной. Лично моё отношение к подобным IDE сугубо отрицательное. Правда, я не занимаюсь микроконроллерами профессионально. Для меня это хобби…

И статья увы не моя… ))
Извиняюсь, что-то у меня было минутное помутнение :-)

Кстати говоря, коллеги, которые занимаются эмбеддедами профессионально, комментируют все вот эти «сборка вручную» и «мейкфайлы» как игры в песочнице :-)
Да. знаю таких товарищей. Они обычно от Linux'а шарахаются как от огня. Этим и объясняется их «снисходительность» к «сборкам вручную» и «мейкфайлам»…

Каких либо серьезных аргументов за IAR' ы и Keil'ы я от них так и не услышал. Обычно приводят аргумент с интеграцией с инструментами сквозного проектирования, типа Altium и иже сними… Когда например, port mappings может генерироваться в зависимости от разводки платы. И еще пара специфических фишек, которые на деле ими мало используются…

Зато, послушаешь рассказы как глючат эти IAR'ы порой, это вообще пипец… Когда про цену на Keil/IAR'ы говорить начинаешь они тоже грустнеют… В общем всё говорит за то, что это больше вопрос привычки а не каких-то реальных профитов…
Моя информация, вероятно, устарела, но, вроде, в Keil uVision (могу путать с IAR, т. к. в то время работал в одном, делал хоббийные проекты в другом) для x51 в 2006, примерно, году не было:
— нормальной отладки (да-да, embedded среда),
— почти не было подсветки синтаксиса,
— навигации по коду (например, перейти к объявлению/реализации, перейти в заголовочный файл),
— автодополнения (имен файлов, названий функций, полей в структурах).

Про всякие фичи типа элементарного рефакторинга и говорить нечего.
Ага, так вот кому надо сказать за перемещение arm-none-eabi-gcc и иже с ним в community repo… Спасибо… )
:D Завсегда пожалуйста, пользуйтесь на здоровье.
Я бы не рекомендовал напрямую работать с регистрами, особенно для новичков. У STM есть неплохая прослойка в виде библиотек, и читаемость кода выше, и портирование кода на другие версии процессора будет проще. Конечно полезно знать, что делает библиотечная функция, но совсем не обязательно это помнить — всегда можно посмотреть, открыв ее код или define
Ох, помню как я втыкал в архитектуру F1, а потом заказал себе F4, и долго пытался понять разницу работы и победить компилятор :)). Потом прозрел и читаю документацию можно сказать прямо в заголовках. Пишу на кокосе. Уже объяснил паре товарищей как работать с кокосом и программированием под микроконтроллеры. :) Один радиолюбитель был, какие же у него были азартные глаза когда он понял что он делает и что может сделать :)
А обучение мое довольно жестокое, грызи, грызи, грызи. Вот это глянь и пойми почему так, а не этак. Так что я за либы от stm как никак а переделки малы, и можно даже окружиться в своих либах дефайнами по платформе что бы компилилось и под f1 и под f4. Конечно не универсальное решение, но вполне сгодится для общих задач. ЗА статью определенно +, все таки описывает многие аспекты программирования для начинающих.
«Как видно из описания для совершения требуемой нам настройки необходимо записать значение 01b в 26-27 биты регистра GPIOx_MODER. Адрес регистра можно определить тем же методом, что описан выше.»
Помогите вкурить, как число можно в биты впихнуть?
Only those users with full accounts are able to leave comments. Log in, please.