- PVSM.RU - https://www.pvsm.ru -

Давайте зажжём светодиод на STM32

В интернете куча статей о том, как мигать светодиодом на esp8266. Предлагаем рассмотреть ту же задачу, но на альтернативном микроконтроллере - stm32. 

Перед вами небольшое руководство, в котором описано, как зажечь светодиод с помощью микроконтроллера STM32, настроив контакты GPIO. В посте разберём основы регистров микроконтроллера и как ими манипулировать напрямую. Также здесь вы найдёте пошаговое руководство по написанию кода на ассемблер и на C для управления светодиодом. 

Я работал над драйвером для массива из 110 светодиодов Чарлиплексинг [1] с таймером обратного отсчета. Использовался микроконтроллер STM32L010K4 [2]. Это прочный чип с ультранизким энергопотреблением 32 МГц, с 16 КБ флэш-памяти и колоссальными 2 КБ ОЗУ. 

Я выбрал его в основном из-за того, что он был первым в огромной линейке микроконтроллеров STM32. Тогда я думал, что на чипе с наименьшим количеством периферийных устройств мне будет проще обучаться. Я спроектировал светодиоды так, чтобы они располагались по кругу и двум дугам. Внутренний круг представляет минуты, средняя дуга - часы, а внешняя - дни:

Давайте зажжём светодиод на STM32 - 1

Задним умом я думаю, что мог бы добавить чип драйвера светодиода (например, IS31FL3746A [3]) и избавить себя от кучи проблем, но на тот момент один микроконтроллер казался более простым решением. И теперь, когда я разложил печатную плату (сначала вручную на макетной плате, с более чем 300 паяными соединениями, но это совсем другая история) и запрограммировал работающий драйвер, то понял, что отдельный чип для этой цели не нужен. В общем, я рад, что для первого раза взял именно такое решение. 

Управление светодиодами

При таком раскладе светодиод включается переключением бита в очень специфической ячейке памяти. Это бит, который соответствует одному из контактов ввода-вывода общего назначения GPIO микроконтроллера. На рисунке ниже отмечены те два контакта, к которым подключен мой первый светодиод. В моём случае это были контакты 7 и 14 , в таблице данных [4]они обозначены как контакты PA1 и PB0 соответственно; что в свою очередь означает, что они находятся в группе контактов GPIOA и GPIOB соответственно. Всё это позволит нам найти их адрес.

Давайте зажжём светодиод на STM32 - 2

В справочном руководстве [5], которое поставляется вместе с микроконтроллером (страница 40 из 784), есть карта памяти, показывающая, что порты ввода/вывода контроллера (IOPORTS) находятся где-то между 0x50000000 и 0x50001FFF .

Давайте зажжём светодиод на STM32 - 3

А на странице ниже мы можем заметить, что GPIOA начинается с 0x5000 0000, а GPIOB - с 0x5000 0400:

Давайте зажжём светодиод на STM32 - 4

Чтобы подключить светодиод с анодом (+), подключенным к PB0 (GPIOB / контакт 14 ), и катодом (-), подключенным к PA1 (GPIOA / контакт 7 ), мы должны убедиться, что PB0 посылает напряжение (в моем случае 3,3 В), а PA1 выступает в качестве земли. Сначала мы настраиваем режим этих контактов на «режим вывода общего назначения»(1) , а затем переключаем бит, соответствующий контакту 14, в регистре установки/сброса битов (BSRR) GPIOB (2). Позже я объясню, как это работает, но, если коротко, эти три шага таковы:

1. 0x50000000 ← 0xEBFFFCF7

   0x50000400 ← 0xFFFFFFFD

2. 0x500004181

Первый вопрос, который у вас может возникнуть: Откуда взялись 0xEBFFFCF7 [6](E:1110 B:1011 F:1111 F:1111 F:1111 C:1100 F:1111 7:0111), 0xFFFFFFFD [7](F:1111 F:1111 F:1111 F:1111 F:1111 F:1111 D:1101) и 1. Чтобы ответить на этот вопрос, ниже приведен еще один фрагмент из справочного руководства. В нем показаны биты, которые необходимо установить для настройки режима GPIOA и GPIOB:

Давайте зажжём светодиод на STM32 - 5

Давайте сначала разберемся с 0xFFFFFFFD (значение GPIOB_MODER). F в шестнадцатеричном формате, конечно, будет 1111 , а D — это 1101, поэтому 0xFFFFFFFD (F:1111 F:1111 F:1111 F:1111 F:1111 F:1111 F:1111 D:1101) — это:

Давайте зажжём светодиод на STM32 - 6

Видите ли, все контакты, за исключением 0-го, установлены в положение «11 : Аналоговый режим (состояние сброса)», а контакт 0 GPIOB — в положение « 01 : Режим вывода общего назначения».

0xEBFFFCF7 (значение GPIOA_MODER), с другой стороны, использует ту же идею, только вместо полностью аналогового режима GPIOA запускается в другом состоянии сброса. Посмотрите на рисунок 8.4.1 выше, обратите внимание, что под заголовком, выделенным жирным шрифтом, написано: «Значение сброса: 0xEBFF FCFF для порта A», потому что некоторые контакты по умолчанию установлены в аналоговый режим (14 и 13) и режим ввода (4), чтобы включить программирование и отладку микроконтроллера на определенных контактах. И мы устанавливаем контакт 1 в режим «выход общего назначения». Итак, 0xEBFFFCF7 (E:1110 B:1011 F:1111 F:1111 F:1111 C:1100 F:1111 7:0111):

Давайте зажжём светодиод на STM32 - 7

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

Наконец, 1 (in 0x50000418 ← 1) устанавливается в регистр установки и сброса битов (BSRR) для отправки напряжения на 0-й вывод GPIOB:

Давайте зажжём светодиод на STM32 - 8

Обратите внимание на «Adress offset: 0x18» сверху. Зная, что GPIOB находится по адресу 0x50000 400 + 0x 18 , мы получаем, что BSRR для GPIOB равен 0x50000418. Таким образом, чтобы «установить» 0-й бит в 1, мы должны записать 1 в память по этому адресу.

Вот вся последовательность сборки :

ldr r0, =0x50000000    // load the GPIOA address into register r0

ldr r1, =0xEBFFFCF7    // load the mode for GPIOA into register r1

str r1, [r0, #0x00]    // write value of r1 into address at r0

ldr r0, =0x50000400    // same as above but for GPIOB

ldr r1, =0xFFFFFFFD 

str r1, [r0, #0x00]

ldr r1, =1             // load 1, which is pin 0 in PB0, into r1

str r1, [r0, #0x18]    // write that 1 into GPIOB with BSRR offset of 18

И в Си :

*(volatile uint32_t *)(0x50000000) = 0xEBFFFCF7;

*(volatile uint32_t *)(0x50000400) = 0xFFFFFFFD;

*(volatile uint32_t *)(0x50000418) = 1;

Но, скорее всего, вы захотите, использовать поверх CMSIS (Cortex Microcontroller Software Interface Standard), что сделает код намного более читабельным:

#include "stm32l010xb.h"

void turnOnLED() {

  GPIOA->MODER = 0xEBFFFCF7;

  GPIOB->MODER = 0xFFFFFFFD;

  GPIOB->BSRR = 1;

}

И чтобы облегчить нам жизнь (и избежать вычисления этого шестнадцатеричного значения самостоятельно) , STM поддерживает библиотеку под названием HAL (Hardware Abstraction Layer) . Вот как это выглядит:

#include "stm32l0xx_hal.h"

void turnOnLED() {

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  GPIO_InitStruct.Pin = GPIO_PIN_0;

  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_1;

  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);

}

Для полноты картины добавлю, что вам придется проделать еще немного работы, прежде чем вышеуказанный код заработает, а именно настроить и включить тактовые генераторы, управляющие GPIOA/B; но эта часть выполняется либо генератором кода/инструментом начальной загрузки, который поддерживает STM и называется STM32CubeMX [8] (я предпочитаю использовать его вместе с VSCode) , либо их IDE STM32CubeIDE [9].

Вот и все: мы устанавливаем три значения в трех очень конкретных разделах памяти, и это посылает 3,3 В на один контакт, а другой действует как земля. Получилось как-то так:

Давайте зажжём светодиод на STM32 - 9

Спасибо за внимание!

Давайте зажжём светодиод на STM32 - 10

Автор: Cloud4Y

Источник [10]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/diy/406268

Ссылки в тексте:

[1] Чарлиплексинг : https://ru.wikipedia.org/wiki/%D0%A7%D0%B0%D1%80%D0%BB%D0%B8%D0%BF%D0%BB%D0%B5%D0%BA%D1%81%D0%B8%D0%BD%D0%B3

[2] STM32L010K4: https://www.st.com/en/microcontrollers-microprocessors/stm32l010k4.html

[3] IS31FL3746A : https://www.digikey.com/en/products/detail/lumissil-microsystems/IS31FL3746A-QFLS4-TR/9759696

[4] таблице данных : https://www.st.com/resource/en/datasheet/stm32l010k4.pdf

[5] справочном руководстве: https://www.st.com/resource/en/reference_manual/rm0451-ultralowpower-stm32l0x0-advanced-armbased-32bit-mcus-stmicroelectronics.pdf

[6] 0xEBFFFCF7 : https://www.google.com/search?q=0xEBFFFCF7+in+binary

[7] 0xFFFFFFFD : https://www.google.com/search?q=0xFFFFFFFD+in+binary

[8] STM32CubeMX: https://www.st.com/en/development-tools/stm32cubemx.html

[9] STM32CubeIDE : https://www.st.com/en/development-tools/stm32cubeide.html

[10] Источник: https://habr.com/ru/companies/cloud4y/articles/870016/?utm_campaign=870016&utm_source=habrahabr&utm_medium=rss