Что нужно знать о прерываниях
В этой статье я планирую поделиться с читателями своими скромными познаниями в области прерываний. Начать следует с того, что же представляют собой прерывания. Прерывание – это событие как правило связанное с каким-либо блоком периферии микроконтроллера STM32.
Событий которые могут породить прерывание может быть множество. Например если речь о таком блоке периферии как UART, то там могут быть такие события: передача завершена, приём завершен, возникла ошибка чётности итд. Использование прерываний позволит нашей программе мгновенно регировать на подобные события.
Сам термин прерывание говорит о том, что что-то должно прерваться и в нашем случае прервется выполнение основного кода вашей программы и управление будет передано некоторому другому куску кода который называется обработчиком прерывания. Таких обработчиков достаточно много, ибо периферийных устройств в STM32 предостаточно.
Стоит отметить важный момент: В случае возникновения двух разных прерываний от одного блока периферии возникает одно и тоже прерывание. Например если произойдет прерывание по приёму байта через UART и прерывание по завершению передачи через тот же UART, то в обоих случаях будет вызван один и тот же обработчик.
Для того чтоб определить какое из возможных прерываний произошло нужно смотреть на флаги состояния. И само собой очищать их перед выходом из прерывания. Когда обработчик прерывания отработает, управление будет передано той самой строчке кода, во время выполнения которой наступило прерывание.
То есть основная программа продолжит работать дальше как ни в чем не бывало.
- Они все независимо включаются/выключаются
- Имеют приоритет
- Могут быть вызваны программно
- Если для прерывания нет обработчика, а оно возникло, то будет вызван обработчик по умолчанию
С первым пунктом всё более-менее понятно, но есть один нюанс. Прерывание не возникнет, если оно запрещено (а оно по умолчанию запрещено) для какого-либо конкретного блока периферии. Чтоб его включить существует функция NVIC_EnableIRQ, ну а для выключения соответственно NVIC_DisableIRQ.
В качестве параметра этим двум функциям надо передать переменную типа IRQn. Этот тип является перечислением и соответственно переменная может принимать одно значение из списка который описан в заголовочном файле для вашего контроллера (у меня этот файл называется stm32f10x.h).
Пример использования:
NVIC_EnableIRQ (USART1_IRQn); // Разрешаем прерывания от USART1 NVIC_EnableIRQ (ADC1_IRQn); // Разрешаем прерывания от АЦП NVIC_DisableIRQ (USART1_IRQn); // Запрещаем обратно NVIC_DisableIRQ (ADC1_IRQn); // Для АЦП запрещаем прерывания тоже
Следующий важный момент это приоритеты прерываний. Предположим, что у нас возникло прерывание с приоритетом 10 и управление было передано в его обработчик. Если в ходе исполнения кода обработчика возникнет прерывание приоритет которого меньше 10, то исполнение прервется и управление будет передано обработчику более высокоприоритетного прерывания.
Чем меньше приоритет – тем прерывание более приоритетное. Если ничего не менять, то все прерывания имеют такие приоритеты, как это написано в референс мануале (см. таблицу Interrupt and exception vectors).
Интересно, что будет если назначить одинаковые приоритеты всем? Судя по той табличке о которй я писал выше, в случае одновременного возникновения двух прерываний с одинаковыми приоритетами, будет вызван обработчик того прерывания которое имеет наименьшее значение в столбце “Position”. Но это всего лишь моё предположение и точно я ничего сказать не могу.
Прерывания могут быть вызваны вашим кодом, просто поставьте нужный флаг и оно случится (если конечно его ни кто не запрещал). Хотя ни кто не запрещает вам вызвать обработчик просто как обычную функцию без всех этих извратов с флагами.
И наконец последнее: Если вдруг каким-то чудом у вас случается прерывание для которого нет обработчика, то управление передается в обработчик по умолчанию, в теле которого находится пустой бесконечный цикл. Ну а теперь еще немного теории и попробуем написать несложную программу которая обрабатывает прерывания от обыкновенного порта ввода/вывода.
Такие прерывания могут возникать в случае если на ноге: 1) появилась лог.1, 2) появился лог.0, 3) лог. уровень сменился на противоположный. Прерывание может генерировать любая ножка любого порта, но таких ног может быть не более 16 штук. Вот тут-то STM32 страшно рулит по сравнению с большинством AVRок у которых подобных прерываний всего два и они жестко привязаны к определённым ногам.
Всего может существовать семь обработчиков внешних прерываний: EXTI0, EXTI1, EXTI2, EXTI3, EXTI4, EXTI9_5, EXTI15_10 а что касается выводов, то их у нас куда больше. Прерывание от нулевой ноги любого порта всегда будет обрабатываться при помощи обработчика EXTI0, прерывание второй ноги будет вызывать обработчик EXTI2 и так далее.
Что касает ножек с 5-й по 9-ю то для любой из них будет вызван обработчик EXTI9_5, аналогичная ситуация и с EXTI15_10. Существует ограничение: нельзя настроить прерывание таким образом чтоб оно возникало от двух ног с одинаковым номером. Например нельзя PA1 и PB1 одновременно настроить. Для того чтоб определить ноги каких портов будут вызывать возникновение прерываний, служат 4 регистра AFIO_EXTICR1…AFIO_EXTICR4. Они все похожи друг на друга, покажу тут только первый:
В каждом таком регистре есть 4 группы по 4 бита в каждой. Каждая группа соответствует ноге от нулевой до пятнадцатой. Изменяя биты группы можно задать какому порту соответствует эта нога. Возможны такие комбинации:
EXTI3 | EXTI2 | EXTI1 | EXTI0 | Порт |
PA | ||||
1 | PB | |||
1 | PC | |||
1 | 1 | PD | ||
1 | PE | |||
1 | 1 | PF | ||
1 | 1 | PG |
Если мы хотим чтоб ноги PB3, PC2, PA1 и PD0 могли порождать прерывания то нужно записать в регистр AFIO_EXTICR1 такую комбинацию бит: 0001 0010 0000 0011.
По умолчанию во всех 4-х регистрах AFIO_EXTICR записаны нули, а то означает, что каждая нога порта А готова генерировать прерывания.
Но чтоб это происходило нужно явно разрешить это при помощи регистра EXTI_IMR
Нам интересны первые 16 бит, каждый бит соответствует ноге, если он установлен то нога может генерить прерывания. Как я уже писал выше, прерывание может возникнуть по нарастающему или спадающему фронту а так же при любом изменении лог. уровня на ноге.
Настраивается это в регистрах EXTI_RTSR и EXTI_FTSR. В них нас так же интересуют первые 16 бит. Если мы установим бит в регистре EXTI_RTSR то соответствующая нога при появлении на ней лог. 1 вызовет прерывание.
Ну а соответственно установив бит в EXTI_FTSR мы разрешим ноге генерить прерывание в момент появления лог 0. Если для одной ноги стоят биты в обоих этих регистрах, то прерывание будет возникать всякий раз когда происходит изменение логического уровня.
Еще есть регистр EXTI_SWIER при помощи которого вы можете программно вызвать прерывание от имени любой ноги. Достаточно просто записать единицу в интересующий нас бит. И последний важный регистр EXTI_PR. Это регистр флагов, флаги устанавливаются в зависимости от того какая нога вызвала прерывание.
Находясь в обработчике прерывания EXTI9_5, при помощи этого регистра легко проверить какая из ног (от 5 до 9) вызвала прерывание. Важный момент: Флаг надо сбрасывать самому в обработчике прерывания. Сбрасывается он записью единицы в соответствующий бит. Теперь попробуем сделать небольшой практический пример.
Есть две кнопки и два светодиода. Нажимаем кнопку и светодиод меняет своё состояние на противоположное. Я использовал как обычно свою дискавери прикрутив к ней еще одну кнопку с резистором:
А вот собственно код который вдохнет жизнь в железку:
#include “stm32f10x.h”
#include “stm32f10x_gpio.h”
#include “stm32f10x_rcc.h” int main() { GPIO_InitTypeDef PORT; //Затактируем все три порта RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC , ENABLE); //Прерывания – это альтернативная функция порта //поэтому надо установить бит Alternate function I/O clock enable //в регистре RCC_APB2ENR RCC_APB2PeriphClockCmd(RCC_APB2ENR_AFIOEN , ENABLE); // Настроим ноги со светодиодами на выход PORT.GPIO_Pin = (GPIO_Pin_9 | GPIO_Pin_8); PORT.GPIO_Mode = GPIO_Mode_Out_PP; PORT.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init(GPIOC, &PORT); //Наша задача получить в регистре EXTICR[0] такую комбинацию бит // 0000 0000 0001 0000 // по умолчанию там ноли, поэтому установим только 1 бит AFIO->EXTICR[0]|=AFIO_EXTICR1_EXTI1_PB; //Прерывания от нулевой и первой ноги разрешены EXTI->IMR|=(EXTI_IMR_MR0 | EXTI_IMR_MR1); //Прерывания на обоих ногах по нарастающему фронту EXTI->RTSR|=(EXTI_RTSR_TR0 | EXTI_RTSR_TR1); //Разрешаем оба прерывания NVIC_EnableIRQ (EXTI0_IRQn); NVIC_EnableIRQ (EXTI1_IRQn); while(1) { //Программа ничего не делает в пустом цикле }
} // Обработчик прерывания EXTI0
void EXTI0_IRQHandler(void)
{ GPIOC->ODR^=GPIO_Pin_8; //Инвертируем состояние светодиода EXTI->PR|=0x01; //Очищаем флаг } // Обработчик прерывания EXTI1
void EXTI1_IRQHandler(void)
{ GPIOC->ODR^=GPIO_Pin_9; //Инвертируем состояние светодиода EXTI->PR|=0x02; //Очищаем флаг }
Как обычно я написал пояснения к программе в самом коде, но если есть вопросы то пишите ниже.
P.S. если я что-то не так написал, то тем более пишите.
Источник: http://easystm32.ru/for-beginners/25-interrupts-handling-in-stm32
Alex_EXE
«Много информации не бывает —
бывает мало места для её хранения»
какая-то реклама в каком-то популярном
компьютерном журнале 90-х годов.
Если в проекте нужно выводить или получать большие объемы данных то на помощь придут различные flash микросхемы памяти.
Если же нежно получать или выводить ОГРОМНЫЕ объемы данных, то стоит задуматься о более вместительном хранилище информации. На роль такого вместилища подходит SD карта памяти.
Это – огромный носитель информации, по сравнению с обычными микросхемами памяти, объём которых редко превышает 64Мбита.
Подключение SD карты к STM32vlDiscovery
В статье рассмотрим подключение и диалог SD карты памяти с микроконтроллером STM32, через SPI. Без использование файловой системы.
В качестве контроллера и платы выступит демонстрационная плата STM32vlDiscovery с контроллером STM32F100RB на борту. Диалог с платой будет производиться по UART, точнее, через преобразователь USB-UART будем общаться через COM терминальную программу AL Terminal. Связь с картой памяти будет осуществляться через SPI.
Подключение карты памяти к STM32F100RB установленному в Discovery будет выглядеть следующим образом:
Схема
Контроллер установлен на демонстрационной плате и его подключение расписывать не буду. Преобразователь USB-UART описан в статье — USB-UART на cp2102 , можете применить и любой другой, например ft232 . Карта памяти подключается напрямую к микроконтроллеру, т.к. напряжение питание и логические уровни сигналов, в 3.3В, у них одинаковые.
Распиновка SD карты в SPI режиме
С подключением ничего сложного нет, переходим к коду.
Проект написан в Keil с использованием CMSIS и Standard Peripheral Library. Из SPL применены GPIO, RCC И USART библиотеки. Библиотеку для работы с картой памяти воспользовался готовой, взял с сайта «My Controller» — http://mycontroller.ru/category/vneshnie-ustroystva/karta-pamyati-sd/ .
Возможности библиотеки:
SD_init() – инициализация SPI и карты памяти
в случае удачи возвращает 0.
SD_sendCommand(cmd, arg) – посылка команды SD карте
Где: cmd – команда, arg – аргумент Основные команды:
GO_IDLE_STATE 0 – программная перезагрузка
SEND_IF_COND 8 – для SDC V2 — проверка диапазона напряжений
READ_SINGLE_BLOCK 17 – чтение указанного блока данных
WRITE_SINGLE_BLOCK 24 – запись указанного блока данных
SD_SEND_OP_COND 41 – начало процесса инициализации
APP_CMD 55 – главная команда из ACMD команд
READ_OCR 58 – чтение регистра OCR
Команд для работы с картой памяти на самом деле гораздо больше, но в библиотеки задействованы только эти, чего для минимальной работы достаточно.
SD_ReadSector(BlockNumb,*buff) – прочитать сектор в случае неудачи возвращает 0
Где: BlockNumb – адрес блока карты памяти, *buff – ссылка для буфера
SD_WriteSector(BlockNumb,*buff) – записать сектор в случае неудачи возвращает 0
Где: BlockNumb – адрес блока карты памяти, *buff – ссылка для буфера
На основе библиотеки написал небольшую оболочку для ознакомления с картой памяти:
unsigned long address,i; unsigned char c; init(); // инициализация переферии if(SD_init()==0) // инициализация SD карты памяти { send_Uart_str(USART1,”init sd ok
“); } else { send_Uart_str(USART1,”init sd fail
“); } send_Uart_str(USART1,”alex-exe.ru”); // выводим сообщение в UART while(1) { send_Uart_str(USART1,”
—————————-
“); send_Uart_str(USART1,”Read or write sd card r/w = “); c=getch_Uart(USART1); // читаем байт с uart while(getch_Uart(USART1)!=13){} // дожидаемся ввода enter с uart send_Uart_str(USART1,”
Press enter address = “); buff_clear(); // очистка буфера address=read_int_uart(USART1); // читаем число с uart, адрес сектора на sd карте send_Uart_str(USART1,”
Address = “); send_int_Uart(USART1,address); // выводим для проверки введенное число send_Uart(USART1,'
'); if(c=='w') // проверка на запись или чтение карты памяти { send_Uart_str(USART1,”Press enter data blok (max 512B), to exit press enter
“); read_str_uart(USART1,Buff); // читаем строку с uart, окончание enter i=0; while((i < 512)&&(Buff[i]!=0))i++; // ищем конец текстовой строки send_Uart_str(USART1,"Length text data = "); send_int_Uart(USART1,i); // выводим длину текстового послания send_Uart(USART1,' '); if(SD_WriteSector(address, Buff)==0) // запись буфера на SD карту { send_Uart_str(USART1,"write sd ok "); } else { send_Uart_str(USART1,"write sd fail "); } } else { if(SD_ReadSector(address, Buff)==0) // чтения SD карты в буфер { send_Uart_str(USART1,"read sd ok "); } else { send_Uart_str(USART1,"read sd fail "); } for(i=0;i < 512;i++) // вывод содержимого буфера в терминал { send_Uart(USART1,Buff[i]); } } // на последок мигнём светодиодом GPIO_SetBits(GPIOC, GPIO_Pin_8); // включить синий светодиод на PC8 delay(1000000); GPIO_ResetBits(GPIOC, GPIO_Pin_8); // выключить синий светодиод на PC8 }
В коде реализована возможность чтения и записи блоков карты памяти через терминал.
Эксперименты проводил с картами памяти SD Transcend 2 GB и SDHC Qumo 8GB.
Скачать исходник
В статье использовались материалы с сайта «My Controller» — http://mycontroller.ru/category/vneshnie-ustroystva/karta-pamyati-sd/.
Схема обновлена 2 октября 2013 года
Источник: https://alex-exe.ru/radio/stm32/connect-sd-card-stm32-spi/
Таймер SysTick, реализация задержек в программе | arm | programming
Источник: http://microsin.net/programming/ARM/stm32-systick.html
Программирование STM32. Часть 1. GPIO, порты ввода-вывода STM32
Недавно коллега меня подсадил на идею создания умного дома, я даже успел заказать себе десятки разных датчиков. Встал вопрос о выборе Микроконтроллера (далее МК) или платы. После некоторых поисков нашёл несколько вариантов.
Среди них были и Arduino (включая его клоны, один из которых себе заказал ради того, чтобы просто побаловаться) и Launchpad, но всё это избыточно и громоздко (хотя в плане программирования гораздо проще, но тему холиваров поднимать не буду, у каждого свои вкусы). В итоге решил определяться не с готовой платой, а взять только МК и делать всё с нуля.
В итоге выбирал между Atmel ATtiny (2313), Atmel ATmega (решил отказаться т.к. не смог найти за адекватные деньги), STM32 (Cortex на ядре ARM). С тинькой я уже успел побаловаться, так что взял себе STM32VL-Discovery. Это можно назвать вступлением к циклу статей по STM32.
Оговорюсь сразу, автором большинства этих статей буду являться не я, т.к. сам только познаю, здесь я публикую их в первую очередь для себя, чтоб удобнее было искать если что-то забуду. И так поехали!
Общие сведения
Микроконтроллеры семейства STM32 содержат в своём составе до семи 16-разрядных портов ввода-вывода c именами от PORTA до PORTG. В конкретной модели микроконтроллера без исключений доступны все выводы портов, общее количество которых зависит от типа корпуса и оговорено в DataSheet на соответствующее подсемейство.
Для включения в работу порта x необходимо предварительно подключить его к шине APB2 установкой соответствующего бита IOPxEN в регистре разрешения тактирования периферийных блоков RCC_APB2ENR:
RCC->APB2ENR |= RCC_APB2ENR_IOPxEN; // Разрешить тактирование PORTx.
Управление портами STM32 осуществляется при помощи наборов из семи 32-разрядных регистров:
- GPIOx_CRL, GPIOx_CRH – задают режимы работы каждого из битов порта в качестве входа или выхода, определяют конфигурацию входных и выходных каскадов.
- GPIOx_IDR – входной регистр данных для чтения физического состояния выводов порта x.
- GPIOx_ODR– выходной регистр осуществляет запись данных непосредственно в порт.
- GPIOx_BSRR – регистр атомарного сброса и установки битов порта.
- GPIOx_BSR – регистр сброса битов порта.
- GPIOx_LCKR – регистр блокировки конфигурации выводов.
Режимы работы выводов GPIO
Режимы работы отдельных выводов определяются комбинацией битов MODEy[1:0] и CNFy[1:0] регистров GPIOx_CRL и GPIOx_CRH (здесь и далее: x-имя порта, y- номер бита порта).
GPIOx_CRL — регистр конфигурации выводов 0…7 порта x:
Структура регистра GPIOx_CRH аналогична структуре GPIOx_CRL и предназначена для управления режимами работы старших выводов порта (биты 8…15).
Биты MODEy указанных регистров определяют направление вывода и ограничение скорости переключения в режиме выхода:
- MODEy[1:0] = 00: Режим входа (состояние после сброса);
- MODEy[1:0] = 01: Режим выхода, максимальная скорость – 10МГц;
- MODEy[1:0] = 10: Режим выхода, максимальная скорость – 2МГц;
- MODEy[1:0] = 11: Режим выхода, максимальная скорость – 50МГц.
Биты CNF задают конфигурацию выходных каскадов соответствующих выводов:
в режиме входа:
- CNFy[1:0] = 00: Аналоговый вход;
- CNFy[1:0] = 01: Вход в третьем состоянии (состояние после сброса);
- CNFy[1:0] = 10: Вход с притягивающим резистором pull-up (если PxODR=1) или pull-down (если PxODR=0);
- CNFy[1:0] = 11: Зарезервировано.
в режиме выхода:
- CNFy[1:0] = 00: Двухтактный выход общего назначения;
- CNFy[1:0] = 01: Выход с открытым стоком общего назначения;
- CNFy[1:0] = 10: Двухтактный выход с альтернативной функцией;
- CNFy[1:0] = 11: Выход с открытым стоком с альтернативной функцией.
С целью повышения помехоустойчивости все входные буферы содержат в своём составе триггеры Шмидта. Часть выводов STM32, снабженных защитными диодами, соединёнными с общей шиной и шиной питания, помечены в datasheet как FT (5V tolerant) — совместимые с напряжением 5 вольт.
Защита битов конфигурации GPIO
Для защиты битов в регистрах конфигурации от несанкционированной записи в STM32 предусмотрен регистр блокировки настроек GPIOx_LCKR
GPIOx_LCKR — регистр блокировки настроек вывода порта:
Для защиты настроек отдельного вывода порта необходимо установить соответствующий бит LCKy. После чего осуществить последовательную запись в разряд LCKK значений “1” — “0” — “1” и две операции чтения регистра LCKR, которые в случае успешной блокировки дадут для бита LCKK значения “0” и “1” . Защита настроечных битов сохранит своё действие до очередной перезагрузки микроконтроллера.
Файл определений для периферии микроконтроллеров STM32 stm32f10x.h определяет отдельные группы регистров, объединённые общим функциональным назначением (в том числе и GPIO), как структуры языка Си, а сами регистры как элементы данной структуры. Например:
GPIOC->BSRR – регистр BSRR установки/сброса порта GPIOC.
Воспользуемся определениями из файла stm32f10x.h для иллюстрации работы с регистрами ввода-вывода микроконтроллера STM32F100RB установленного в стартовом наборе STM32VLDISCOVERY:
#include “stm32F10x.h” u32 tmp; int main (void) { RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; // Разрешить тактирование PORTC. GPIOC->CRH |= GPIO_CRH_MODE8; // Вывод светодиода LED4 PC8 на выход. GPIOC->CRH &=~GPIO_CRH_CNF8; // Двухтактный выход на PC8. GPIOC->CRH |= GPIO_CRH_MODE9; // Вывод светодиода LED3 PC9 на выход. GPIOC->CRH &=~GPIO_CRH_CNF9; // Двухтактный выход на PC9. GPIOA->CRL&=~GPIO_CRL_MODE0; // Кнопка “USER” PA0 – на вход. // Заблокировать настройки выводов PC8, PC9. GPIOC->LCKR = GPIO_LCKR_LCK8|GPIO_LCKR_LCK9| GPIO_LCKR_LCKK; GPIOC->LCKR = GPIO_LCKR_LCK8|GPIO_LCKR_LCK9; GPIOC->LCKR = GPIO_LCKR_LCK8|GPIO_LCKR_LCK9| GPIO_LCKR_LCKK; tmp=GPIOC->LCKR; tmp=GPIOC->LCKR; }
Запись и чтение GPIO
Для записи и чтения портов предназначены входной GPIOx_IDR и выходной GPIOx_ODR регистры данных.
Запись в выходной регистр ODR порта настроенного на вывод осуществляет установку выходных уровней всех разрядов порта в соответствии с записываемым значением. Если вывод настроен как вход с подтягивающими резисторами, состояние соответствующего бита регистра ODR активирует подтяжку вывода к шине питания (pull-up, ODR=1) или общей шине микроконтроллера (pull-down, ODR=0).
Чтение регистра IDR возвращает значение состояния выводов микроконтроллера настроенных как входы:
// Если кнопка нажата (PA0=1), установить биты порта C, иначе сбросить. if (GPIOA->IDR & GPIO_IDR_IDR0) GPIOC->ODR=0xFFFF; else GPIOC->ODR=0x0000;
Сброс и установка битов порта
Для атомарного сброса и установки битов GPIO в микроконтроллерах STM32 предназначен регистр GPIOx_BSRR.
Традиционный для архитектуры ARM способ управления битами регистров не требующий применения операции типа “чтение-модификация-запись” позволяет устанавливать и сбрасывать биты порта простой записью единицы в биты установки BS (BitSet) и сброса BR (BitReset) регистра BSRR. При этом запись в регистр нулевых битов не оказывает влияния на состояние соответствующих выводов.
GPIOx_BSRR – регистр сброса и установки битов порта:
GPIOC->BSRR=GPIO_BSRR_BS8|GPIO_BSRR_BR9; // Зажечь LED4 (PC8), погасить LED3.
GPIOC->BSRR=GPIO_BSRR_BS9|GPIO_BSRR_BR8; // Зажечь LED3 (PC9), погасить LED4.
Альтернативные функции GPIO и их переназначение (remapping)
Практически все внешние цепи специального назначения STM32 (включая выводы для подключения кварцевых резонаторов, JTAG/SWD и так далее) могут быть разрешены на соответствующих выводах микроконтроллера, либо отключены от них для возможности их использования в качестве выводов общего назначения. Выбор альтернативной функции вывода осуществляется при помощи регистров с префиксом “AFIO”_.
Помимо этого регистры AFIO_ позволяют выбирать несколько вариантов расположения специальных функций на выводах микроконтроллера. Это в частности относится к выводам коммуникационных интерфейсов, таймеров (регистры AFIO_MAPR), выводам внешних прерываний (регистры AFIO_EXTICR) и т. д.
Подробнее смотрите документы “Reference manual” на соответствующую подгруппу микроконтроллеров.
Проекты к статье:
Для управления GPIO STM32 Вы можете применить макросы написанные как альтернативу далеко не оптимальным по мнению многих библиотекам от ST: gpio_emcu.h
Дополнительный материал:
Другие части
Источник: https://ergoz.ru/programmirovanie-stm32-chast-1-gpio-portyi-vvoda-vyivoda-stm32/
Adblockdetector