Советы и вопросы по программированию STM32

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

  1. Я заметил, что во всех примерах программ, которые предоставляет STM, локальные переменные для main () всегда определяются вне функции main () (с периодическим использованием ключевого слова static). Есть ли для этого какие-то причины? Следует ли мне следовать подобной практике? Следует ли мне избегать использования локальных переменных внутри основного?

  2. У меня есть глобальная переменная, которая обновляется в дескрипторе прерывания часов. Я использую ту же переменную внутри другой функции как условие цикла. Разве мне не нужно обращаться к этой переменной с помощью некоторой формы атомарной операции чтения? Как я могу узнать, что прерывание часов не меняет своего значения в середине выполнения функции? Должен ли я отменять прерывание часов каждый раз, когда мне нужно использовать эту переменную внутри функции? (Однако мне это кажется крайне неэффективным, поскольку я использую его как условие цикла. Я считаю, что должны быть лучшие способы сделать это).

  3. Keil автоматически вставляет код запуска, написанный на ассемблере (например, startup_stm32f4xx.s). Этот код запуска имеет следующие операторы импорта: IMPORT SystemInit IMPORT __main. В "C" это имеет смысл. Однако в C ++ и main, и system_init имеют разные имена (например, _ int _main__void). Как этот стартовый код может работать на C ++ даже без использования "extern" C "" (я пробовал, и это сработало). Как может компоновщик c ++ (armcc --cpp) связать эти операторы с правильными функциями?


person tantuni    schedule 12.11.2011    source источник
comment
Все эти вопросы практически не связаны. Почему бы не разбить их на три разных вопроса по stackoverflow, чтобы, если кто-то знает ответ на один из них, но не на все, он все равно мог дать вам совет?   -  person David Grayson    schedule 13.11.2011
comment
@DavidGrayson, я согласен. Как опаздывающий читатель, я бы также предпочел, чтобы они были разделены, чтобы я мог найти то, что мне интересно, более эффективно.   -  person cp.engr    schedule 28.01.2017


Ответы (3)


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

Я бы сделал копию глобальной задачи переднего плана, которая ее использует.

unsigned int myglobal;

void fun ( void )
{
   unsigned int myg;

   myg=myglobal;

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

Кейл знает, что они делают, если они поддерживают C ++, то на системном уровне они это проработали. Я не использую Keil. Я использую gcc и llvm для таких микроконтроллеров, как этот.

Редактировать:

Вот пример того, о чем я говорю

https://github.com/dwelch67/stm32vld/tree/master/stm32f4d/blinker05

stm32 с использованием прерываний на основе таймера, обработчик прерывания изменяет переменную, совместно используемую с задачей переднего плана. Задача переднего плана делает один снимок общей переменной (для каждого цикла) и при необходимости использует снимок более одного раза в цикле, а не общую переменную, которая может изменяться. Это C, а не C ++, я это понимаю, и я использую gcc и llvm, а не Keil. (обратите внимание, что у llvm есть проблемы с оптимизацией жестких циклов while, очень старая ошибка, не знаю, почему они не заинтересованы в ее исправлении, llvm работает для этого примера).

person old_timer    schedule 13.11.2011
comment
Спасибо за ответ. Однако меня все еще беспокоит один вопрос. В программировании на основе ОС мы предполагаем, что даже один оператор присваивания (т.е. a = b) не является атомарным. Следовательно, если b может измениться вне потока, мы должны использовать какой-то мьютекс. Во встроенном программировании (с подпрограммами прерывания) не так ли? Даже если b можно изменить в обработчике прерывания, могу ли я предположить, что a = b является атомарной операцией? Могу ли я с уверенностью предположить, что a равно прошлому или текущему значению b? (В программировании на основе ОС a может быть чем угодно, не связанным с b) - person tantuni; 13.11.2011
comment
Я не согласен с первым предложением Двелча. Когда вы меняете переменную с локальной на глобальную, вы увеличиваете использование ОЗУ, потому что глобальная переменная все время занимает ОЗУ, тогда как локальная переменная занимает ОЗУ только тогда, когда функция находится в области видимости, и даже тогда она может быть сохранена в реестре, а не в ОЗУ. Поскольку вы увеличиваете использование ОЗУ, это увеличивает вероятность столкновения вашего стека с вашими данными (переполнение стека). - person David Grayson; 13.11.2011
comment
@ David-Grayson, у вас есть контроль над использованием оперативной памяти, и это очень детерминировано, если вы используете глобальные переменные, с локальными переменными, которые требуют гораздо больше работы и могут варьироваться с небольшими изменениями. если вы готовы провести такой анализ, сделайте это. Я, конечно, согласен с тем, что местные жители дают вам больше оперативной памяти в том смысле, что вы часто используете ее повторно и получаете гораздо больше переменных, доступных для вашей программы, и тому подобное. - person old_timer; 14.11.2011
comment
@ user460153 Я должен был упомянуть, что общая переменная может быть объявлена ​​как изменчивая, чтобы гарантировать, что компилятор действительно читает ее из оперативной памяти (все зависит от того, как выглядит ваш код). Когда вы читаете a = b и гарантируете, что b читается из оперативной памяти, а не в регистре переднего плана, И память - это то, что можно прочитать за одну передачу (32 бит или меньше, возможно, 64 бит, но я сомневаюсь, что эта вещь имеет 64-битная шина AXI / amba) ЗАТЕМ вы получите полный образец этой переменной ... - person old_timer; 14.11.2011
comment
@ user460531 да, это может быть после или до того, как обработчик изменит его. Важно это или нет, зависит от конструкции вашей системы. Если оба пользователя общего ресурса выполняют чтение-изменение-запись этого элемента, то да, вам нужно обоим использовать атомарную операцию, и вам придется управлять этим стандартными методами, отключать прерывания, блокировки и т. Д. читает и другие операции чтения-изменения-записи, которые могут вам не понадобиться, зависит от того, как работает ваш код. Если это цикл, то в следующий раз вы поймаете новое значение. - person old_timer; 14.11.2011
comment
@ user460531 получает TRM для ядра руки, которое они используют, в семействе stm32 есть cortex-m3 и cortex-m4f и, возможно, другие. Глядя на общий armv7m, я вижу, что для 8, 16 или 32 битов функция _Mem [] выполняет однокопийные атомарные, выровненные, прямой порядок байтов доступов памяти к базовому массиву байтов физической памяти. Как и во всем остальном, я всегда хотел бы убедиться, что вы выполняете согласованный доступ, который может повлиять на ваши атомарные проблемы. - person old_timer; 14.11.2011
comment
Cortex-m4f (stm32f, тот, у которого есть cortex-m4 и fpu) является 32-битным AHB-lite как на шинах инструкций, так и на шинах данных. - person old_timer; 14.11.2011
comment
Я бы не стал так бесцеремонно относиться к использованию глобальных переменных! Обязательное чтение: eetimes.com/discussion/break-point / 4025723 / A-pox-on-globals - person Clifford; 15.11.2011
comment
Если использование стека вызывает беспокойство, вам не нужно делать переменные глобальными только для статического распределения. - person Clifford; 15.11.2011
comment
Конечно, я вижу локальные глобальные переменные, статически выделяемые и фактические глобальные переменные, как одно и то же, я понимаю, что такое использование контекста, с точки зрения распределения они оказываются в одном и том же месте. - person old_timer; 15.11.2011

Вопрос 1. Локальные переменные

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

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

В некоторых встроенных средах, таких как архитектура PIC18 с компилятором C18, локальные переменные намного дороже (больше места для программы, медленнее время выполнения), чем глобальные. На Cortex M3 это не так, поэтому вы можете свободно использовать локальные переменные. Проверьте список сборки и убедитесь в этом сами.

Вопрос 2. Совместное использование переменных между прерываниями и основным циклом

Люди написали целые главы, объясняющие ответы на эту группу вопросов. Всякий раз, когда вы разделяете переменную между основным циклом и прерыванием, вы обязательно должны использовать в ней ключевые слова volatile. К переменным размером 32 или менее бит можно получить доступ атомарно (если они не выровнены).

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

Вопрос 3: основная функция в C ++

Я не уверен. Вы можете использовать arm-none-eabi-nm (или то, что nm называется в вашей инструментальной цепочке) в своем объектном файле, чтобы увидеть, какое имя символа компилятор C ++ присваивает main(). Могу поспорить, что компиляторы C ++ воздерживаются от искажения основной функции именно по этой причине, но я не уверен.

person David Grayson    schedule 13.11.2011

Образец кода STM не является образцом хорошей практики кодирования, он просто предназначен для иллюстрации использования их стандартной периферийной библиотеки (при условии, что это те примеры, о которых вы говорите). В некоторых случаях может случиться так, что переменные объявлены внешними по отношению к main(), потому что доступ к ним осуществляется из контекста прерывания (разделяемая память). Также возможно, что это было сделано таким образом просто для того, чтобы разрешить наблюдение за переменными в отладчике из любого контекста; но это не повод копировать технику. Мое мнение о коде примера STM состоит в том, что он, как правило, довольно плох даже в качестве примера кода, не говоря уже о том, что с точки зрения разработки программного обеспечения.

В этом случае ваша переменная прерывания часов является атомарной, если она 32-битная или меньше, если вы не используете семантику чтение-изменение-запись с несколькими модулями записи. Вы можете спокойно иметь одного писателя и несколько читателей в любом случае. Это верно для этой конкретной платформы, но не обязательно для всех; ответ может быть другим для 8- или 16-битных систем или, например, для многоядерных систем. Переменная в любом случае должна быть объявлена ​​volatile.

Я использую C ++ на STM32 с Keil, и проблем нет. Я не уверен, почему вы думаете, что точки входа в C ++ разные, их здесь нет (Keil ARM-MDK v4.22a). Код запуска вызывает SystemInit(), который, например, инициализирует ФАПЧ и тайминги памяти, затем вызывает __main(), который выполняет глобальную статическую инициализацию, затем вызывает конструкторы C ++ для глобальных статических объектов перед вызовом main(). Если сомневаетесь, просмотрите код в отладчике. Важно отметить, что __main() - это не main() функция, которую вы пишете для своего приложения, это оболочка с различным поведением для C и C ++, но которая в конечном итоге вызывает вашу main() функцию.

person Clifford    schedule 15.11.2011