STM32 HAL. Часть 1 - GPIO и внешние прерывания.

Приветствую коллеги и просто любопытствующие. Я несколько лет занимаюсь разработкой ПО для встраиваемых систем. В основном для STM32 с использованием Standart Peripheral Library. Недавно попытался пересесть на их HAL под названием CUBE. Мягко говоря, разочаровался этим непродуманным продуктом и окончательно решил, что надо поделиться своим собственным HAL-ом, который накатывается поверх Standart Peripheral Library. В этой и, очень надеюсь, последующих статьях я выложу коды, опишу их и примеры их использования. Кому это интересно — прошу под кат.

В первой части я расскажу об использовании GPIO и внешних прерываний на этих самых GPIO.
Для начала нужно скачать HAL GPIO.
Далее необходимо определиться, будут ли использоваться внешние прерывания (с 0-ой по 15-ую линию). В зависимости от этого в header-файле необходимо закомментировать, либо раскомментировать следующий дефайн

    // использование внешних прерываний (закомментировать если не используются)
    #define USE_EXTI

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

    // пин кнопки 1
    sGpio gpioKey1 = {C, 12, HIGH, IN_PU};

    // пин лампочки 1
    sGpio gpioLed1 = {B, 7, HIGH, OUT_PP};

Где первый параметр — название порта, второй — номер пина, третий — скорость работы пина, четвертый — функция пина, пятый — при необходимости определяет ремап пина.
Функции пина могут быть как стандартными так и альтернативными:

    // определение стандартных функций
    #define OUT_PP      GPIO_Mode_Out_PP        // выход с подтяжкой
    #define OUT_OD      GPIO_Mode_Out_OD        // выход с открытым коллектором
    #define IN_PU       GPIO_Mode_IPU           // вход подтянутый к питанию
    #define IN_PD       GPIO_Mode_IPD           // вход подтянутый к земле
    #define IN_FLOAT    GPIO_Mode_IN_FLOATING   // вход Hi-Z
    #define IN_ADC      GPIO_Mode_AIN           // вход аналоговый

    // определение альтернативных функций
    #define USART_TX    GPIO_Mode_AF_PP         // USART отправитель 
    #define USART_RX    IN_FLOAT                // USART приёмник
    #define CAN_TX      GPIO_Mode_AF_PP         // CAN отправитель
    #define CAN_RX      IN_PU                   // CAN приёмник
    #define SPI_SCK     GPIO_Mode_AF_PP         // SPI тактирование
    #define SPI_MOSI    GPIO_Mode_AF_PP         // SPI мастер -> слэйв
    #define SPI_MISO    GPIO_Mode_AF_PP         // SPI слэйв -> мастер


Теперь остаётся лишь инициализировать пины там где это нужно.

    // инициализация пинов
    GPIOInit(&gpioKey1);
    GPIOInit(&gpioLed1);


И при необходимости сконфигурировать внешние прерывания для этих пинов.

    // конфигурация внешнего прерывания
    GPIOExtiConfig(2, LedToggle, (void*)&gpioLed1, &gpioKey1);

Где первый операнд — приоритет прерывания, второй — функция вызываемая при срабатывании прерывания, третий — операнд передаваемый в эту функцию, четвертый — пин для которого конфигурируется внешнее прерывание.
В использовании внешних прерываний есть пара ограничений:
1) Единовременно может быть сконфигурировано только по одному внешнему прерыванию на каждый номер пина, независимо от порта. То есть невозможно одновременно иметь внешнее прерывание на пине PB1 и PC1, а на PB1, PB2 и PC3 — без проблем.
2) Функция передаваемая в конфигуратор прерывания должна иметь входной операнд типа void*.

Теперь остаётся лишь включить сконфигурированное внешнее прерывание когда это понадобится

    // включение внешнего прерывания
    GPIOExtiEnable (eGpioTriger_FALL, &gpioKey1);

Где первый операнд — тип тригера внешнего прерывания, второй — пин внешнее прерывание которого включается.

    // тип тригера
    typedef enum
    {
        eGpioTriger_RISE  = EXTI_Trigger_Rising,            // от земли к питанию
        eGpioTriger_FALL  = EXTI_Trigger_Falling,           // от питания к земле
        eGpioTriger_ALL   = EXTI_Trigger_Rising_Falling,    // оба варианта
    } eGpioTriger;


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

В завершение статьи коротко опишу, что делает пример идущий вместе с модулем (main.c в папке GPIO). Его функционал заключается в следующем: при нажатии на кнопку состояние светодиода переключается (вкл/выкл).

#include "GPIO\GPIO.h"

//==============================================================================
//                          Глобальные переменные
//==============================================================================

    // внешне определенные переменные
    extern sGpio        gpioKey1, 
                        gpioLed1;
    
//==============================================================================
//              Переключение состояния светодиода
//------------------------------------------------------------------------------
//  gpioLed    - пин управления светодиодом
//==============================================================================    
    
void LedToggle (void* gpioLed)
{   
    GPIOToggle((sGpio*)gpioLed);
}

//==============================================================================
//                         Основная программа
//==============================================================================

int main (void)
{   
    // инициализация пинов
    GPIOInit(&gpioKey1);
    GPIOInit(&gpioLed1);
   
    // конфигурация внешнего прерывания
    GPIOExtiConfig(2, LedToggle, (void*)&gpioLed1, &gpioKey1);
    
    // включение внешнего прерывания
    GPIOExtiEnable (eGpioTriger_FALL, &gpioKey1);

    while(1) 
    {
      
    }
}

В примере создаётся 2 пина (вход подтянутый к питанию — считывает кнопку; выход с подтяжкой — запитывает светодиод). Оба пина инициализируются при старте программы. Следом конфигурируется внешнее прерывание, в которое передаётся функция переключающая состояние светодиода и указатель на пин запитывающий светодиод. То есть будь у нас 2 кнопки и 2 светодиода с аналогичным управлением, то при конфигурации внешнего прерывания была бы передана одна и та же функция, а отличие состояло бы в пинах запитывающих светодиоды и пинах на которые вешаются прерывания. Далее программа включает сконфигурированное внешнее прерывание используя вид тригера «падение от питания к земле» и уходит в бесконечный цикл делать ничего. При нажатии на кнопку входной пин подтягивается к земле, срабатывает прерывание, и обработчик вызывает функцию переключения состояния светодиода, передав в неё указатель на пин управляющий светодиодом. И в этой функции состояние этого пина инвертируется. Следовательно и состояние светодиода также инвертируется.

На этом всё. Прошу не судить строго. Это моя первая публикация. Изложил как смог. Основной материал собственно в самом коде, а не здесь. Приветствуются вопросы, предложения и прочие комментарии.
П.С. Также выкладываю проект GPIO project под ИАР с примером.

Комментарии (52)

RSS свернуть / развернуть
Хорошо и понятно все описали.
Надеюсь, продолжите описание в длинной серии статей?
0
Спасибо. Приложу все возможные усилия для этого.
0
не плохо.
Теперь все маленькие функции переносим в хедер и делаем inline и будет вернее.
0
А не вылезут косяки при использовании разных компиляторов?
0
Если компилятор соответствует стандарту хотябы ANSI С — нет. Если не соответствует — им нельзя пользоваться
Для компиляторов специфичными являюдся директивы pragma и attribute.
Вообще, хорошим тоном является объявлять эти функции через препроцессор
Что-то вроде
INLIVE void turnOff(){}

Где INLINE определяется в зависимости от компилятора. Для GCC это будет
__attribute__ ((always_inline)) static inline 
0
Попробовал так сделать с ИАРовским компилятором. Компилируется только на максимально выставленной оптимизации. Иначе линкер выдаёт ошибку что функция не определена.
0
Иначе линкер выдаёт ошибку что функция не определена.
Покажи код и ругань компилятора.
0
в GPIO.h
__INLINE void GPIOToggle (sGpio* gpio)
    {
        if(gpio)
            GPIOWrite(!GPIOReadOut(gpio), gpio);
    };

ошибка:
Error[Li005]: no definition for «GPIOToggle» [referenced from E:\..\GPIO project\Debug\Obj\main.o]
0
Странно как-то. Если GPIO.h в main.c включен — то компилятор эту функцию туда вкомпилирует.
0
В том то и дело. Если оптимизацию выкрутить на максимум, то вопросов нет. А иначе вот такая загогулина.
0
Мне очень сильно кажется, что всё это проблема надуманная. Вот вы сделали надстройку на StdPeriphLibrary, и чем же она лучше чем сама StdPeriphLibrary?
Новый HAL действительно несколько «вырвиглазный», но такова дань универсальности. Не хотите — не используйте. Я придерживаюсь мнения, что для написания серьёзных вещей таки придётся разбираться в регистрах, рано или поздно. А при таком раскладе будь то HAL или StdPeriphLibrary уже и не так важно.
0
Посмотрел код. Принципиальных отличий от стандартной библиотеки не нашёл.
Зато нашёл то, за что надо по рукам бить металлической метровой линейкой:
#define A           GPIOA,RCC_APB2Periph_GPIOA,GPIO_PortSourceGPIOA

Это чё, мне теперь букву А использовать нельзя?
0
Если вы начнёте писать программы с качественной и гибкой архитектурой, вы заметите отличия от SPL. Приведите пример как вы собираетесь использовать букву А и вам это не даёт делать этот модуль.
0
int A;

Качественная и гибкая архитектура это что? При переходе с одного контроллера на другой, как правило, периферия меняется, и всё это похерится. Если же периферия не меняется, то и код, например, такого вида:
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);
//Конфигурируем порт для включения усилителя звукового сигнала
GPIO_InitTypeDef GPIO_InitStructure = {
.GPIO_Pin =  GPIO_Pin_5,
.GPIO_Mode = GPIO_Mode_OUT,
.GPIO_PuPd = GPIO_PuPd_NOPULL,
.GPIO_Speed = GPIO_Speed_50MHz,
.GPIO_OType = GPIO_OType_PP };
GPIO_Init(GPIOC, &GPIO_InitStructure);

Не изменится.
0
И где вы создаёте эту переменную А? И вы собираетесь для каждого пина писать такую портянку? А если ещё и EXTI добавить то получается, что инициализация занимает высоту экрана — это очень нечитаемо. А потом как вы будете управлять этим пином? Задефайните название порта и номер пина? И будете таскаться по программе с этими дефайнами? А если у вас 2 этих усилителя? Будет уже 4 дефайна? А когда пинов 80 используется — зароетесь в этих портянках. ИМХО
0
Переменную A создаю в своём коде, куда подключен ваш заголовок.
Я эту портянку для каждого случая просто скопирую и легко модифицирую под конкретный случай. А если хочу несколько пинов с одной и той же конфигурацией, то сделаю так:
.GPIO_Pin =  GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_12,

А в вашем случае, видимо будет три вызова функции.
Инициализация действительно занимает много в высоту. Сделайте мельче шрифт или больше монитор, чтобы влезало. Обычно инициализация вынесена в отдельную функцию, которая вызывается один раз в начале программы, так что чтение проблем не вызывает.
А нечитаемым будет как раз нагромождение
GPIOInit(&gpioKey1);

Ведь непонятно, какие именно настройки скрываются под
gpioKey1
.

Управлять пином можно, например так:
#define SOUND_ON() {GPIO_SetBits(GPIOA, GPIO_Pin_6);}

Можно и посимпатичнее.

Вы дефайнов боитесь чтоли? Вот такой случай, усилителем управляют только в одном C файле, в нём же и прописан дефайн. Область видимости такого дефайна — только этот C файл, при этом он именован таким образом, что осуществляет смысловую связь, при этом нет ненужного вызова функции.
0
#define SOUND_ON() {GPIO_SetBits(GPIOA, GPIO_Pin_6);}
А ведь есть ещё и SOUND_OFF(). Вот это уже клиника (не хочу оскорбить, а хочу предостеречь). И таким образом что бы подключить усилитель например к пину С1 — вам нужно изменить
1) операнд во включении тактирования
2) номер пина при инициализации
3) название порта при инициализации
4) номер пина и название порта в дефайне включения усилителя
5) номер пина и название порта в дефайне выключения усилителя
А теперь кульминация: что будете делать если появится второй усилитель? Код дублировать? ну не могу этого вам запрещать. Утопайте в портянках инициализаций, дефайнов и повторов кода.
А теперь, что же понадобится мне исправить если надо будет перекинуть усилитель на другой пин?
лишь вот это: sGpio gpioKey1 = {C, 12, HIGH, IN_PU};
на вот это sGpio gpioKey1 = {A, 5, HIGH, IN_PU};
Я искренне считал это очевидными и довольно банальными вещами. Но видать это не так.
И кстати переменная ваша А без проблем создастся. Ибо дефайн в SOURCE.
0
Клиника — это делать надстройку над фирменным велосипедом и предлагать на неё пересесть, при том очевидно, что при ловле багов придётся разбираться и в надстройке и в самом велосипеде, и, вероятно, могут возникнуть проблемы от взаимодействия надстройки с велосипедом.
Желание избавится от дублирования понятно и похвально. Если очень хочется можно решить и без надстройки.
Масштабируемость в плане количества подключенных усилителей решается 1 раз на устройство. Цикл разработки ПО для устройства занимает ни как не меньше 3х месяцев (в зависимости от устройства, конечно, но я подразумеваю, что мы штампуем не мигалки для ёлочных гирлянд), из них инициализация периферии — 1 день. Если очень хочется пин поменять, жмём в IAR Ctrl+Sfit+F и пишем «Pin_6», все места где он использован теперь видны. И мест этих три: инициализация, включатель, выключатель.

Если ваш дефайн в сурсе, то как, не модифицируя ваш код, я буду писать свою инициализацию? Очевидно, в вашем C файле. Сомнительный подход.
0
Если очень хочется пин поменять, жмём в IAR Ctrl+Sfit+F и пишем «Pin_6», все места где он использован теперь видны.
Это как раз пример того, как делать не следует. Хрестоматийный, я бы даже сказал.
0
Ну сгруппируйте дефайны для одного пина в одном месте, если так хочется.
0
Это как раз пример того, как делать не следует. Хрестоматийный, я бы даже сказал.
Вы имеете в виду, что не стоит везде писать Pin_6, а стоит в одном месте ему сделать алиас, чтобы потом использоваться у себя в виде алиаса, а при необходимости замены просто переопределить алиас. В этом хрестоматийность?
0
Хрестоматийность в том, что требуется делать синхронизированные изменения в нескольких разнесенных местах. А как от этого избавляться — есть много методов, и конкретный можно выбрать только исходя из реалий задачи.
+1
Велосипед мной тщательно тестируется. И работает годами на ответственных объектах. Эх, не понимаете вы что я пытаюсь донести про архитектуру. Там ещё ниже вас ждёт вопрос. Ответьте пожалуйста на него.
0
Подход вполне нормальный. Разработчик установил рамки как использовать библиотеку. Хотите используйте, хотите нет.
0
int A;
Так тоже делать не следует. Но #define A — это нечто.
Алсо пока не совсем понимаю, что оно дает по сравнению с SPL, кроме сокращения портянок. Но портянки инициализации не проблема, не они на сложность кода и время разработки влияют.
0
Можно конечно и PA, и PORTA, и PortA задефайнить. Только сначала скажите по каким критериям вы даёте названия дефайнам?
0
Смотря какого уровня дефайны. Если чисто для проекта и проект мелкий — только по критерию наглядности и непересечения с другими. Если в библиотеке — то полновесное название типа LIBPREFIX_DEFINENAME. У тебя — именно второй вариант.
И кстати переменная ваша А без проблем создастся. Ибо дефайн в SOURCE.
Это меняет дело, хотя лично мне не нравится, что пины объявляются в библиотеке. На мой взгляд, они должны определяться в соответствующих им модулях. Скажем, в примере выше — в sound.h. И PORTA было бы нагляднее.

P.S. Встречал однобуквенные дефайны в коде VirtualDub. Но там они undef'айнятся сразу после использования, двумя-тремя десятками строк ниже define'а.
0
Да, можно их определять и в используемых их модулях. Но гибкость и наглядность от этого слегка ниже.
0
Просто тогда эти модули более высокого уровня в перспективе становятся более платфомо-зависимыми. При текущем решении на другой платформе мне нужно будет реализовать аналогичный описанному в статье модуль и всё. А иначе возможно придётся и в выше стоящих модулях менять что-то.
0
Алсо пока не совсем понимаю, что оно дает по сравнению с SPL

У меня пока не дошли руки до глубокого вникания в HAL, но из того что видел, принципиальное отличие от SPL — наличие высокоуровневого кода типа готовая реализация профилей USB устройств, TCP/IP и т. д. Насколько это все хорошо (или плохо) работает — не скажу (пока сам не попробовал).
0
Я про HAL автора, а не STM.
0
— #define A smthing
— Это чё, мне теперь букву А использовать нельзя?
— Приведите пример как вы собираетесь использовать букву А и вам это не даёт делать этот модуль?
— int A;

Весьма занятный диалог :DDD

Обычно считается моветоном обозначать константы или некие имена более-менее глобального действия одной буквой (а тем паче в библиотечных хидерах) — этому даже детей учат в начале обучения: юзай переменные а-ля i, j, k только как локальные, и желательно очень небольшой локальной области видимости! Это как навалить в чистый унитаз в общественном туалете и уйти — нюхайте юзайте на здоровье.

Но здесь приходит другой человек, и начинает вроде бы совершенно правомерно возмущаться! Но, оказывается, в итоге-то получается: А кто это тут уже навалил (#define A smthing)? Это я здесь навалить должен был (int A;), да еще возможно и не смыть (extern int A;) :DDD
-1
Удобнее скоростью разработки. А именно более удобная настройка самой периферии и более удобное её использование в абстракциях уровнем выше.
Что вы понимаете под серьёзной вещью? За все годы лез в регистры только когда по незнанию неправильно использовал SPL, разбирался в чём я накосячил, и так или иначе в результате реализовывал всё именно с помощью SPL. Лично мой опыт говорит, что на STM32 лезть в регистры вообще не придётся зная SPL. Единственный момент когда я оправдано лез в регистры — это запуск DWT. Но это уже ядро, а не периферия.
0
Скорость разработки за счёт чего достигается, не понимаю? Время на программирование управления периферией ничтожно мало, по сравнению с отладкой «мозгов» программы. Если речь, конечно, не про моргание светодиодом.
Вот вы пишете прерывание. Будете ради переписывания одного регистра функцию вызывать?
0
Цель — гибкость архитектуры. Да, буду. Потому что управление светодиодом не связано архитектурно с кнопкой а значит управлению светодиодом в обработчике прерывания кнопки делать нечего. Но связь логическая — и логика эта организуется вызовом функции из обработчика прерывания. Я надеюсь вы понимаете, о чём я говорю.
0
Гибкость архитектуры в чём выражается?
Я не могу позволить себе в прерываниях вызов функции, потому что прерываний много, и отрабатывать они должно быстро.
Я не понимаю, о чём вы говорите про логическую связь и то, как она связана с вызовом функции. Возможно вы привели плохой пример.
0
Ок. Давайте приведу другой пример. Есть акселерометр и радиомодем. у обоих есть пин IRQ. Эти пины зацеплены на наши линии внешних прерываний 11 и 12 соответственно. И вы что, свалите обработку прерывания от акселерометра и от модема в обработчик EXTI15_10_IRQHandler. Ладно, даже если так. Вы этот обработчик где разместите, в модуле драйвера акселерометра или модема? А если вы захотите поделится драйвером того или другого устройства, вы его передадите вместе с этим обработчиком? А кто будет из него лишнее выгребать от другого устройства, вы или получатель драйвера? А если у него пин будет например номер 3 использоваться, так это ему и обработчик придётся менять на EXTI3_IRQHandler, то есть лезть в код. А если 2 акселерометра? Кароч вот отсутствие всего этого гемороя и непродуманности я и называю гибкостью архитектуры.
0
Но ваш код заявленного не обеспечивает.
Да и пример вы привели с неграмотным разбиением программы на блоки. В обработчике прерывания, очевидно, надо распознать от кого пришло прерывание, а потом каким-либо механизмом отдать управление драйверу этого устройства для обработки события. Обработчик прерывания — отдельно, драйвер — отдельно. Как ваш код позволяет это сделать, и какое он к этому случаю имеет отношение, — не понятно.
0
каким-либо механизмом
— так мой код и даёт вам этот механизм. Хорошо опищите, как вы бы решили выше озвученную мной задачу: 2 абсолютно разных устройства, прерывание от одного на 11-ой, а другого — на 12-ой линии. Обрисуйте архитектуру такой программы. Глядишь, смекнете что такое гибкость.
0
Ах вот оно что. Вы написали свой обработчик внешних прерываний, который доставлял вам столько неприятностей.
Ну теперь понятно. Только он ужасно неоптимальный, отрабатывает все возможное прерывания, без оглядки на то, может оно возникнуть, или нет. Но гибкость архитектуры ради пары строк кода — на высоте.
Зачем отрывать программиста от понятия о способе обработки прерываний, писать ради этого столько кода, — мне всё же не понятно.
0
По-моему кое-какого программиста оторвали от понятия инкапсуляция ))). Вы код то вообще смотрели? Там всё лаконично и просто и ничего лишнего не отрабатывается. И вы уходите от ответа — конкретно опишите архитектуру «вашего» ПО для данного случая. По мере описания вы всё поймёте. Просто попробуйте.
-1
Я буду писать функцию прерывания для каждого конкретного случая. Этого достаточно для понятия о моей архитектуре?
Прерывания — это не шутки. Вот, скажем, нужно зарегистрировать срабатывание по прерыванию, а потом его отключить, а потом, через некоторое время, снова включить. Задача, в общем то, обычная, часто встречающаяся. В вашем случае мне что делать?

Какое из этих двух определений инкапсуляции относится к вашему случаю?
A language mechanism for restricting access to some of the object's components.[3][4]
A language construct that facilitates the bundling of data with the methods (or other functions) operating on that data.[5][6]
По моему, ни одно не подходит.
0
Я буду писать функцию прерывания для каждого конкретного случая.
— это всё вода. Вот вам конкретный случай, а вы только увиливаете.
Если внимательно посмотрите код и прочтёте статью — найдёте ответ на свой вопрос.
И прекрасно подходит второе определение.
-1
Если оба события падают в одно прерывание, значит в этом прерывании будет два (не двадцать) if'а, такие как у вас в коде, но от них будут вызываться специальные функции драйвера (если вы за драйвер переживаете), оптимизированные для вызова из прерывания. Теперь не вода?
С инкапсуляцией я согласен. Хотя у меня осадочек остался.
Я ещё раз обращаю внимание: суть вашей статьи сводится к хитрожопому обработчику прерываний и вертится вокруг него. Этого, на мой взгляд, не было заявлено, а с самого начала началось виляние жопой, что мы можем прерывания использовать, а можем и не использовать, но посмотрите какой классный инициализатор портов! И уже только потом становится понятно, что всё это затевалось главным образом ради разводки прерываний от пинов к функциям.
Кстати, тип прерывания (фронт/спад) нельзя развести по разным функциям, и придётся распознавать в «драйвере».
0
if'ов у меня по кол-ву линий в обработчике. Суть в том, что бы всё было систематизировано и объектно ориентировано. И лично для меня основным в этой статье является именно инкапсуляция gpio (именно отсутствие этого в CUBE HAL было одной из причин появления статьи), а прерываний в этом модуле долгое время вообще не было и я решил что использование их вне этого модуля — некрасивое решение, и добавил их в модуль. И теперь любой программер врубит любую мою программу в отладке и сразу увидит какие прерывания используются и как, посмотрев массив extiHandlers) — это удобно. возможность в любое мгновение разработчику поделиться своим модулем без единого изменения в его функционале с другим разработчиком — вот в чём суть.
0
мне удобней писать
PC12
вместо
C, 12
99% улучшателей дальше GPIO не делают, а где RCC, RTC, EXTI, I2C, SPI — видимо не используется в оптимизациях. GPIO конечно сильно сложнее — ~6 регистров и 16 бит. Вот проблема. Про BITBAND вообще слова нет.
+1
Это будет в следующих частях. И может вы сначала прочтёте, а уже потом будете критиковать? Это я об EXTI.
0
Да я и не критикую. Просто это занимает в коде меньше 1% и делается один раз.
+4
“Брат, прими хазават – Бог велит!”(с)

ПНИ УЖЕ ты этот SPL подальше (да и HAL/CUBE – туда же) и юзай Snippets (новомодный термин для старого и уже давно известного, но из уст самой, весьма смущенной таким поворотом событий, ST Micro :), т.е. РАБОТАЙ НА_РУЧНИКЕ С РЕГИСТРАМИ, юзая готовый name_space из портянки stm32fXXxxx.h, ну плюс еще горсть стандартных файлов CMSIS.

Dosikus просто прыгает от восторга — через 8 лет разного рода “поисков_истины” все в итоге вернулось на круги своя:

— вначале у них была либа STM32F10xxx FWLib (Firmware Library), которая благополучно почила в бозе в возрасте v.2.0.3, после появления у ARM стандарта CMSIS. Щас эту либу не найти даже в СЕТИ, не то что на самом сайте st.com;

— потом была CMSIS compliant SPL, которая для самого первого их “блина_комом” (семейства F10x) доросла аж до v.3.5.0, хотя в их некогда мегапопулярной библиотеке STM32F10x,L1xx,F3xx USB Full Speed device library v.4.0.0 присутствует в явном виде “неофициальная” SPL v.3.6.1 для F10x. Для более новых и новейших семейств SPL rip’нулась в более молодом возрасте – для F37x/38x аж в самом младенчестве (на v.1.0.0 :);

— потом производители пронюхали, что open_source наваял и отшлифовал почти до блеска много чего полезного (FATFS chan’а, lvIP TCP/IP стек, FreeRTOS), к чему надо срочно протянуть свои ручонки и заюзать на пользу себе и, возможно, людям. Плюс SEGGER “подарил” ST Micro свою граф.библиотечку STemWin. Т.е. в софте для поддержки чипов резко появилась и распухла некая папочка MiddleWares, т.к. по-видимому у SPL было изначально хреновато с интеграцией со всем этим MiddleWare – срочно состряпали HAL. Интересно, что для самого старого семейства F10x его (CUBE HAL) доделали самым последним и с жутким опозданием, чем для более новых или новейших семейств.

— Но самый последний писк моды в этой всей эпопее – это Snippets – короткие примеры кода для работы с регистрами на ручнике или с помощью “дебильно”-прелестных макросов а-ля SET_BIT/ CLEAR_BIT/ READ_BIT/ CLEAR_REG/ WRITE_REG/ READ_REG/ MODIFY_REG, которые уже очень-очень давно пылились без дела в файликах stm32fXXx.h еще SPL для F10x. Готовые кусочки кода для copy-paste-Generation.
-3
Snippets (новомодный термин для старого и уже давно известного
Это очень старый термин, который, собственно, и означает «отрывок», «фрагмент». Такая штука как «C Snippets» (сборник работающих фрагментов кода) существует с незапамятных времен, я ею пользовался еще, если не ошибаюсь, в 1992-м или что-то около того.
+2
evsi, а вот нахрена ты и твой корешок Калик понадкусили минусанули мой совершенно не затрагивающий морально-нравственные или политические проблемы, но совершеннейше технический пост!

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

Продолжение технического разговора оформил в виде статьи, т.к. получилось очень много для просто поста.
0
evsi, а вот нахрена
Не пишите чушь и ее не будут минусовать.
В любом случае, я это писал не для озабоченного личными разборками, некоего мстительного завсегдатая,
Охотно верю, что писали вы ее не для себя, а для своих учеников.
+1
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.