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

в 12:30, , рубрики: DIY, diy или сделай сам, diy-проекты, esp8266, LED, stm32, программирование микроконтроллеров

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

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

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

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

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

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

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

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

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

В справочном руководстве, которое поставляется вместе с микроконтроллером (страница 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 (E:1110 B:1011 F:1111 F:1111 F:1111 C:1100 F:1111 7:0111), 0xFFFFFFFD (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 (я предпочитаю использовать его вместе с VSCode) , либо их IDE STM32CubeIDE .

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

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

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

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

Автор: Cloud4Y

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js