STM32: Урок 2 — Quickstart


В прошлой статье мы настроили IDE, и теперь просто обязаны испытать STM32 в деле. Этот урок будет служить этаким трамплином для программерского прыжка в STM32: помигаем светодиодами, поиграемся с таймером — легко и непринуждённо, без копошения в несущественных сейчас деталях. Цель урока — дать общее представление о том, как программируются эти МК.

На всякий случай, проясню ситуацию с курсом: он не для чайников. Этим словом я не хочу никого обидеть, а лишь хочу указать на нижнюю планку необходимых знаний. Я предполагаю, что для освоения курса падаван должен:

  • Сносно писать на языке программирования C. То есть, уметь кодить на C консольные утилитки для ПК, хотя бы поверхностно представлять себе, что получается из кода при компиляции, уверенно пользоваться указателями (в т.ч. указателями на указатели).
  • Уметь работать в командной строке и знать про переменные окружения вроде PATH.
  • Уметь читать. Это не шутка, часто люди пробегают глазами сообщения об ошибках, даже не понимая написанного, а то и вовсе их не читают, а потом задают вопросы: «А что значит ошибка gcc: fatal error: no input files?», хотя ответ очевиден.
  • Естественно, знать азы электроники. Курс — не про подключение светодиодов к плате, а про микроконтроллеры STM32 и их особенности.

И, конечно же, приветствуется умение работать в Linux (:

Итак, приступим к практическому использованию STM32. Для всех микроконтроллеров Cortex-M3 есть одна общая для семейства библиотека CMSIS, которая содержит в себе описание констант, функций, адресов регистров и т.п. для работы с системным таймером SysTick и контроллером прерываний. Доступ к остальной периферии, которая уже зависит от производителя, осуществляется через библиотеки, предоставляемые производителем конкретного МК. У ST Microelectronics такая библиотека для каждого семейства МК называется Standard Peripheral Library. Название длинноватое, так что далее я её буду звать просто SPL.

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

Сделайте копию скелетного проекта для своей платы и настройте его, если ещё не сделали этого (описание настройки тут). Я использую плату STM32VLDiscovery, и буду писать код для неё, так что назвал копию stm32vld_quickstart. Всё, что нужно сделать после копирования, чтобы можно было полноценно работать с проектом — это заменить название ELF-бинарника в файлах gdb_commands_debug и gdb_commands_release с stm32vld_template.elf на stm32vld_quickstart.elf. К эльфам, кстати, эти файлы никакого отношения не имеют: ELF = Executable and Linkable Format.

Для начала, зажгём два пользовательских светодиода, которые находятся на краю платы, противоположном разъёму mini-USB. Они подключены к порту C, к пинам 8 и 9. У STM32 порты 16-битные, так что не удивляйтесь такой нумерации пинов.

Как вы, наверное, заметили, плата STM32VLDiscovery поставляется с прошивкой, которая при включении платы мигает светодиодом PC9, а при нажатии пользовательской кнопки (синяя) на секунду зажигает светодиод PC8 и увеличивает скорость мигания светодиода PC9. А мы возьмём, и всё сломаем!

Откройте в проекте исходник main.c и скопируйте в него следующий код:

#include <stm32f10x.h>
/* Подключаем функции управления генератором частоты и GPIO */
#include <stm32f10x_rcc.h>
#include <stm32f10x_gpio.h>

int main()
{
  /* Включаем тактирование порта C */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

  /* Заполняем структуру gpio данными для инициализации:
   * - Режим: вывод, Push-Pull
   * - Пины: 8 и 9
   * - Частота обновления: 2 МГц
   */
  GPIO_InitTypeDef gpio;
  gpio.GPIO_Mode = GPIO_Mode_Out_PP;
  gpio.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
  gpio.GPIO_Speed = GPIO_Speed_2MHz;

  /* Инициализируем GPIO на порту C */
  GPIO_Init(GPIOC, &gpio);

  /* Устанавливаем единички на выводах 8 и 9 */
  GPIO_SetBits(GPIOC, GPIO_Pin_8 | GPIO_Pin_9);

  do __NOP(); while (1); // зависаем
}

Жмите Ctrl+B, чтобы собрать проект, запускайте утилиту stlink (командой «st-util -1») и отладочную конфигурацию в Eclipse через меню Debug→Debug configurations…, чтобы программа залилась в МК. Когда запустится отладка и курсор встанет на первом выражении в main(), жмите F8 (continue). Оба светодиода должны зажечься. Кстати, если вас напрягает долгая прошивка через stlink, в Windows вы можете использовать улитилу CoFlash от создателей CoIDE — тогда из файлов gdb_commands* нужно будет убрать строки «load <конфигурация>/stm32vld_quickstart.elf».

Наверняка у вас возникли некоторые вопросы — например, про тактирование порта C и частоту GPIO. Это особенности STM32: при включении МК тактирование на периферию не подаётся — это сделано для снижения энергопотребления, так что прежде чем что-то пытаться делать с периферией, нужно подать на неё тактовый сигнал. Это касается всей периферии, не только GPIO. Что до частоты — это частота обновления состояния вывода GPIO. Сам контроллер STM32F100RBT6B может работать на частоте 24 МГц, но скорость работы пинов может быть другой. Для STM32F10x доступны 3 частоты — 2, 10 и 50 МГц. В случае с нашим МК работать будут только 2 и 10 МГц, а сейчас нам и 2 МГц хватит за глаза.

Ну, одними светодиодами сыт не будешь, так что давайте-ка задействуем пользовательскую кнопку на PA0. Пусть при её нажатии зажигается один светодиод и гаснет другой:

#include <stm32f10x.h>
/* Подключаем функции управления генератором частоты и GPIO */
#include <stm32f10x_rcc.h>
#include <stm32f10x_gpio.h>

const uint16_t
  LED1 = GPIO_Pin_8, // PC8
  LED2 = GPIO_Pin_9, // PC9
  LEDS = GPIO_Pin_8 | GPIO_Pin_9,
  BUTTON = GPIO_Pin_0; // PA0

void init_button();
void init_leds();

int main()
{
  init_button();
  init_leds();
  /* Один светодиод зажигаем, другой - гасим */
  GPIO_ResetBits(GPIOC, LED1);
  GPIO_SetBits(GPIOC, LED2);

  while (1)
  {
    static uint8_t btn_old_state = 0;

    /* Читаем бит состояния кнопки */
    uint8_t btn_state = GPIO_ReadInputDataBit(GPIOA, BUTTON);

    /* По нажатию инвертируем биты в порту C, сответствующие светодиодам */
    if (btn_old_state == 0 && btn_state == 1)
      GPIO_Write(GPIOC, ~GPIO_ReadOutputData(GPIOC) & LEDS);

    btn_old_state = btn_state;
  }
}

void init_button()
{
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  /* Всё то же, что и со светодиодами, только конфигурируем
   * на вход без подтяжки (GPIO_Mode_IN_FLOATING).
   */
  GPIO_InitTypeDef gpio;
  GPIO_StructInit(&gpio);
  gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  gpio.GPIO_Pin = BUTTON;
  gpio.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_Init(GPIOA, &gpio);
}

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

  GPIO_InitTypeDef gpio;
  GPIO_StructInit(&gpio);
  gpio.GPIO_Mode = GPIO_Mode_Out_PP;
  gpio.GPIO_Pin = LEDS;
  gpio.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_Init(GPIOC, &gpio);
}

Как видите, сконфигурировать пин на вход ничуть не сложнее, чем на выход. Функции SPL позволяют читать/писать как порты целиком, так и отдельные комбинации битов, что очень удобно. Код получается не сложнее ардуинистого. Кстати, в общей сложности режимов GPIO имеется 8 — входы/выходы со всякими подтяжками и Open Drain. Но, если заглянуть в STM32VLDiscovery User manual (скачать у нас), то там вы найдёте принципиальную схему платы, на которой видно, что пользовательская кнопка уже прижата к земле через резистор R21 (10 K). О режимах GPIO я расскажу в следующей статье.

Антидребезг я здесь никакой не применял, так что реакция на нажатие кнопки не всегда будет адекватной. Но мы можем это исправить примитивным способом — опрашивать кнопку не постоянно, а 100 раз в секунду, к примеру. Тут-то нам и пригодится таймер SysTick, который не умеет ШИМ и прочих вкусностей, но зато 24-битный и очень лёгкий в использовании. А для пущей крутизны нагрузим его миганием светодиода:

#include <stm32f10x.h>
/* Подключаем функции управления генератором частоты и GPIO */
#include <stm32f10x_rcc.h>
#include <stm32f10x_gpio.h>

const uint16_t
  LED1 = GPIO_Pin_8, // PC8
  LED2 = GPIO_Pin_9, // PC9
  BUTTON = GPIO_Pin_0; // PA0

void init_button();
void init_leds();

int main()
{
  init_button();
  init_leds();
  GPIO_ResetBits(GPIOC, LED1 | LED2);

  /* Конфигурируем таймер SysTick на срабатывание 100 раз в секунду */
  SysTick_Config(SystemCoreClock / 100);

  do ; while (1);
}

/* Обработчик прерывания по переполнению таймера SysTick */
void SysTick_Handler()
{
  /* Обработка кнопки */
  static uint8_t btn_old_state = 0;
  uint8_t btn_state = GPIO_ReadInputDataBit(GPIOA, BUTTON);

  if (btn_old_state == 0 && btn_state == 1)
    GPIO_WriteBit(GPIOC, LED1, !GPIO_ReadOutputDataBit(GPIOC, LED1));

  btn_old_state = btn_state;

  /* Мигание светодиодом */
  static uint8_t counter = 0;

  if (counter == 0)
  {
    GPIO_WriteBit(GPIOC, LED2, !GPIO_ReadOutputDataBit(GPIOC, LED2));
    counter = 10;
  }
  else
    --counter;
}

void init_button()
{
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  GPIO_InitTypeDef gpio;
  GPIO_StructInit(&gpio);
  gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  gpio.GPIO_Pin = BUTTON;
  gpio.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_Init(GPIOA, &gpio);
}

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

  GPIO_InitTypeDef gpio;
  GPIO_StructInit(&gpio);
  gpio.GPIO_Mode = GPIO_Mode_Out_PP;
  gpio.GPIO_Pin = LED1 | LED2;
  gpio.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_Init(GPIOC, &gpio);
}

Здесь мы настраиваем таймер так, чтобы он срабатывал каждые SystemCoreClock/100 тиков. Костанта SystemCoreClock в случае с STM32100RBT6B равна 24000000 (24 МГц) — это максимальная его частота при работе от кварца. Тут нужно учитывать, что таймер SysTick 24-битный, и значения больше 16777216 он использовать не может, но это легко обойти, используя свою переменную-счётчик в обработчике прерывания. В этом примере я использовал счётчик для переключения светодиода 10 раз в секунду. По-хорошему, обработку прерывания лучше делать в главном цикле, а в прерывании выставлять какой-нибудь флаг. Прерывания и правильную работу с ними мы тоже, со временем, рассмотрим под всеми углами.

Теперь вы получили представление о том, как работать с SPL, и готовы к более серьёзным вещам — в следующей статье мы изучим GPIO вдоль и поперёк.


30 комментариев на «“STM32: Урок 2 — Quickstart”»

  1. А почему линия А0 (к которой подключена кнопка) настроена как плавающий вход(GPIO_Mode_IN_FLOATING)??? Всегда использовал внутреннюю подтяжку к питанию. Соответственно когда кнопка не нажата, то на выводе уровень логической единицы, если нажали кнопку — на выводе контроллера логический ноль. Так же можно здесь сделать???

    • Можно, но если заглянуть в STM32VLDiscovery User manual, то там вы найдёте принципиальную схему платы, на которой видно, что пользовательская кнопка уже прижата к земле через резистор R21 (10 K). R22 (0) — это просто перемычка, если что. Пожалуй, не лишним будет упомянуть это в статье.

      Кстати, одного вопросительного знака мне вполне достаточно, чтобы заметить вопросительную интонацию предложения (:

    • Спасибо за ответ. За знаки вопроса извините — дурная привычка :).
      В одной из статей вы написали

      будем всё делать через библиотеку SPL — с регистрами возится на данном этапе не резон

      Чем обосновывается ваш выбор работы с библиотекой периферии, а не напрямую через регистры?

    • Тем, что названия функций, констант и структур из SPL куда понятнее, чем имена регистров вроде BSRR и т.п. — код становится проще читать. Буду кодить через регистры только когда упрусь в производительность или если через них окажется проще (бывает и такое).

    • А вот если я пойду на работу и буду кодить с использованием библиотеки, какое ко мне будет отношение со стороны начальства и коллег? Мне могут сказать: «Эх студент! И чего там тебя в универе учили? Так не пойдет, переделывай!»?..
      Просто я еще студент и нигде не работал вот и интересуюсь.

    • Зависит от того, к кому попадёте. В нормальных конторах велосипедостроительство — моветон, так как использование библиотек — залог более быстрого написания кода.

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

  2. Прикрутил примеры к HY-MINI. Эта плата очень похожа на дискавери, но пришлось разобраться с раскладкой входов-выходов 🙂
    Плата хороша тем, что в комплекте идет цветной дисплей, CD c кучей примеров, Keil, ULINK2.

    • Проблема в том, что вы невнимательно смотрели заголовочный файл и автодополнение в Eclipse, а также оставили часть полей неинициализированным. Вот так делать нельзя:

      GPIO_InitTypeDef gpio;
      //gpio.GPIO_Mode = GPIO_Mode_Out_PP;
      gpio.GPIO_Mode = GPIO_Mode_OUT;
      gpio.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13;
      gpio.GPIO_Speed = GPIO_Speed_2MHz;
      
      /* Инициализируем GPIO на порту C */
      GPIO_Init(GPIOD, &gpio);

      Вы объявили экземпляр структуры GPIO_InitTypeDef, но заполнили всего 3 поля из 5, забыв про GPIO_PuPd и GPIO_OType, так что в них может быть что угодно, что там было на стеке до выделения памяти под структуру — от длины ноги Обамы до веса какашки динозавра. Поэтому инженеры ST написали функции XXX_StructInit(), которые заполняют соответствующую структуру для периферии XXX некоторыми безопасными значениями по умолчанию. Приучайтесь писать после каждого

      GPIO_InitTypeDef gpio;

      также

      GPIO_StructInit(&gpio);

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

      Ну и универсальное правило любого сишника: всегда инициализируй переменные нулём или другим безопасным значением. Если нет готовой функции XXX_StructInit(), всегда можно забить нулями через memset():

      XXXStruct x;
      memset(&x, 0, sizeof(x));
    • Исправил:

      #include <stm32f4xx.h>
      #include <stm32f4xx_rcc.h>
      #include <stm32f4xx_gpio.h>
      
      int main()
      {
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
        GPIO_InitTypeDef gpio;
        GPIO_StructInit(&gpio);
      
        GPIO_Init(GPIOD, &gpio);
      
        GPIO_SetBits(GPIOD, GPIO_Pin_12 | GPIO_Pin_13);
      
        do __NOP(); while (1);
      }

      Никакой разници. Диоды не загорелись

    • Видимо, в код функций вы таки не заглядывали (чукча не читатель, чукча — писатель), и зачем-то убрали код, который конфигурирует пины. Надо так:

      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
      
      GPIO_InitTypeDef gpio;
      GPIO_StructInit(&gpio);
      gpio.GPIO_Mode = GPIO_Mode_OUT;
      gpio.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13;
      GPIO_Init(GPIOD, &gpio);

      И всё-таки, читайте код функций:

      void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct)
      {
        /* Reset GPIO init structure parameters values */
        GPIO_InitStruct->GPIO_Pin  = GPIO_Pin_All;
        GPIO_InitStruct->GPIO_Mode = GPIO_Mode_IN;
        GPIO_InitStruct->GPIO_Speed = GPIO_Speed_2MHz;
        GPIO_InitStruct->GPIO_OType = GPIO_OType_PP;
        GPIO_InitStruct->GPIO_PuPd = GPIO_PuPd_NOPULL;
      }
    • и опять никаких результатов.
      код смотрел))) проглядел что он как вход метиться.

      если можно взгляните на настройки проекта. Видимо в них дело.

    • Я когда-то выкладывал свой рабочий проект для STM32F4DISCOVERY — вот он. Сравнивайте сами (:

    • спасибо. разница в ld скприпте. с вашим всё работает.

  3. Здравствуйте! Столкнулся со следующей проблемой при работе с STM32L1xx_StdPeriph_Lib_V1.2.0. Не могу разобраться с функциями, где передача параметров происходит через структуры. Например функция: void TIM_OC1Init ( TIM_TypeDef * TIMx, TIM_OCInitTypeDef * TIM_OCInitStruct ). В ней есть структура TIM_OCInitStruct, которая имеет тип TIM_OCInitTypeDef. Рассмотрим одно из полей структуры, например: uint16_t TIM_OCMode.
    Это поле может принимать следующее значения:
    00184 #define TIM_OCMode_Timing ((uint16_t)0×0000)
    00185 #define TIM_OCMode_Active ((uint16_t)0×0010)
    00186 #define TIM_OCMode_Inactive ((uint16_t)0×0020)
    00187 #define TIM_OCMode_Toggle ((uint16_t)0×0030)
    00188 #define TIM_OCMode_PWM1 ((uint16_t)0×0060)
    00189 #define TIM_OCMode_PWM2 ((uint16_t)0×0070)
    Скопировал из файла stm32l1xx_tim.h.
    Проблема заключается в следующем. Как понять что значит каждый режим (как модуль работает в данном режиме)? Не всегда понятно из названия(. Насколько я понимаю, объявленную здесь константу нужно связать с регистрами и посмотреть референс мануал. Но непонятно к какому регистру относится константа.
    Заранее благодарен.

  4. На мой взгляд если эту платформу «обрастить» нормальными библиотеками, автоматизирующими процесс заполнения этой тучи полей с названиями данными какими-то злыми Си-шниками, то может быть её можно будет пользоваться, а так на фоне ардуины смотрится очень паршиво всякий бред типа:

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
      GPIO_InitTypeDef gpio;
      GPIO_StructInit(&gpio);
      gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;
      gpio.GPIO_Pin = BUTTON;
      gpio.GPIO_Speed = GPIO_Speed_2MHz;
      GPIO_Init(GPIOA, &gpio);
    

    А вообще я где-то видел платки ARM с ардуино-загрузщиком, поэтому поклонникам этого типа контроллера можно писать с Arduino IDE. Касаемо статьи в целом, очень информативно — респект автору.

    • libmaple — ребята пытаются прикрутить ардуино среду и wiring (или wirish ???) фрэймворк, к своей плате на stm32 leaflabs.com

    • ну в общем-то кто хотел тот уже прикрутил ардуино и к PIC-контроллерам и официальный разработчик ардуины выпустил на ARM свою плату DUE Таким образом я не вижу смысла тратить силы на изучение менее удобных платформ, в то время когда лучше изучать и развивать более перспективную ардуину))

    • Ну если быть точным, то всё же не к PIC контроллерам, а к MIPS32. 😉

    • Практика показывает что всё упирается в привычку 😉

    • Wirish — это что-то среднее между проводом (wire) и ирландцем (Irish). Ирландский электрик (:

  5. Я понял, в чем причина отсутствия у меня желания изучать программирование ARM.
    А также под андроид. Все дело в названиях функций и переменных, в стиле

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

    Помимо андроида и ARM, подобный подход кажется применяется на айфоне, при программировании интерфейсов в Windows.

    Префикс_ИмяКИЛОМетровойФункцииСПостояннымиСменамиРЕГИСТРАиИспользованиюВсяческих_итп();

    Дополняя разговоры выше о плате Mapple от LeafLabs, прикупил ее на днях, будем изучать 🙂


    • Вот же развелось лентяев, я фигею. Вы с execom — прямо два сапога пара: лень поставить IDE или просто редактор с автодополнением кода, лень настраивать периферию через короткие имена регистров, лень писать больше двух строк кода для настройки GPIO. Как вы двое программируете вообще? У вас же не хватит терпения что-то отлаживать, да и вообще разрабатывать мало-мальски серьёзный софт, где кода легко могут быть сотни и тысячи строк.

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

      Для всех тентяев привожу квинтессенцию моего негодования:

      • Лень писать, читать и настраивать — собирай ПО из кубиков в RAD-средах типа Delphi, и не выпендривайся.
      • Возводишь принцип «чем меньше кода, тем лучше» в абсолют — когда-нибудь потратишь полгода на написание простой утилиты, вылизывая код.
      • Любишь короткие имена функций — догадайся по названию, что делает функция strpbrk(), не подглядывая в мануал.
    • Вот же развелось лентяев, я фигею. Вы с execom — прямо два сапога пара: лень поставить IDE или просто редактор с автодополнением кода, лень настраивать периферию через короткие имена регистров, лень писать больше двух строк кода для настройки GPIO.

      Лень читать парший код… — да… лень писать паршивый код -да…

      Лень писать, читать и настраивать — собирай ПО из кубиков в RAD-средах типа Delphi, и не выпендривайся.

      ни чего лучше делфи пока не придумали по этому вам так же советую не выпендриаться)))
      касаемо объемов… в последней моей публичной программе исходник имеет 45тысяч строк и я его знаю от и до и проблем в его понимании у тех кто ковырял не было… просто есть кривые люди любящие имена типо

      Префикс_ИмяКИЛОМетровойФункцииСПостояннымиСменамиРЕГИСТРАиИспользованиюВсяческих_итп();

      а есть задроты которые им потакают… вот и все…

    • 1 Используя Delphi можно легко создать и 100 тысяч строк, не заметишь.
      2 Нравиться Delphi, пишите с его помощью ТОЛЬКО ДЕСКТОПНЫЕ программы под Шиндоус.
      3 Если у вас нет способности понимать непривычный код, не понимайте, видимо вам не нужно.
      4 Есть профессионалы, а есть низко-квалифицированные специалисты — в чём различие и так понятно. Задроты тоже есть.
      5 На Delphi прогресс не остановился. Повышайте квалификацию.

    • Да ну. Это девайс для самых маленьких.

  6. Добрый день!
    Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны!
    Хотел узнать зачем использовать переменную типа uint8_t для хранения одного бита состояния кнопки:

    static uint8_t btn_old_state = 0;
      uint8_t btn_state = GPIO_ReadInputDataBit(GPIOA, BUTTON);

    Я воспользовался переменной bool для хранения состояния кнопки:

    /* Читаем бит состояния кнопки */
        if (GPIOA->IDR & 1<<0) btn_state=true; //Бит с номером n=0 установлен
        else btn_state=false; 

    Есть ли преимущества в экономии памяти и времени обработки такого кода?
    Спасибо!

    • Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях сгенерирует одинаковый код. Можете проверить дизассемблером, входящим в тулчейн:

      $ arm-none-eabi-objdump -d <бинарник проекта>

      Кстати, ваш сниппет можно переписать компактнее, без if:

      btn_state = (GPIOA->IDR & 1<<0) != 0;

      Что касается курса, спасибо, рад, что он оказался полезен. Жаль, не было возможности его доделать, как планировал (а то и вовсе переделать, на свежую голову). Впрочем, на дворе конец 2018 года, и STM32 уже стали обыденностью, так информации сейчас куча, и вы можете найти что-нибудь посвежее 🙂

Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Android Arduino Bluetooth CraftDuino DIY IDE iRobot Kinect OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака телеприсутствие управление ходить шаг за шагом шаговый двигатель шилд юмор

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение