Открытый сток stm32 максимальное питание. STM32 Порты GPIO. Работа в разных режимах
GPIO (general-purpose input/output,Интерфейс ввода/ вывода общего назначения)- Интерфейс связывающий микроконтроллер с внешними устройствами (Кнопками, светодиодами, датчиками и так далее).
Как и любой другой микроконтроллер, микроконтроллер STM 32, имеет в своем составе интерфейс ввода/вывода. Данный интерфейс позволяет управлять внешними устройствами путем передачи сигналов низкого и высокого уровня через контакты GPIO , а так же принимать данные с них, путем приема сигналов низкого или высокого уровня. Контакты GPIO , группируются в порты(GPIOA ,GPIOB ,GPIOC …). Каждый контакт может работать на прием или передачу данных.
Рассмотрим режимы работы контакта GPIO :
Hi — Z вход - В таком состоянии сопротивление входа стремится к бесконечности, и он ведет себя как отключенный от схемы.
Вход с подтяжкой к питанию - Контакт работает на прием данных. Когда данных нет, контакт подтягивается к логической “1” (напряжению питания). Это делается, что бы избежать помех и искажения данных.
Вход с подтяжкой к земле - Контакт работает на прием данных. Когда данных нет, контакт подтягивается к логическому “0” (земле). Это делается, что бы избежать помех и искажения данных.
Аналоговый вход - Контакт работает в аналоговом режиме, что позволяет выводить на него сигнал ЦАП или считывать сигнал для АЦП.
Выход с открытым коллектором .
Двухтактный выход - Контакт работает на передачу данных. Контакт может быть установлен в логическую “1” либо в логический “ ”, соответствующим регистром.
Альтернативный режим с подтяжкой .
Альтернативный режим с открытым коллектором .
Альтернативные режимы пока рассматривать не будем, равно как выход с открытым коллектором и аналоговый вход. Рассмотрим подробнее режимы работы “Вход с подтяжкой к питанию/земле” и “Двухтактный выход”.
Как и любую другую периферию, перед использованием GPIO необходимо настроить. Так как периферия микроконтроллеров STM 32 очень богата и разнообразна, разработчики потрудились облегчить жизнь пользователям и избавить их от ручной правки регистров, путем создания библиотек HAL и SPL . Разумеется, никто не запрещает вам вручную править регистры. Хоть ручная правка регистров и сложна, однако позволяет лучше понять работу микроконтроллера и оптимизировать прошивку.
Но мы не будет сильно углубляться в дебри даташитов и регистров. Возьмем библиотеку SPL и с её помощью инициализируем порты ввода/вывода нашей платы STM 32 F 3 DISCOVERY .
Работать будем в среде Keil uVision 5.
Как создать свой первый проект в данной среде смотрите здесь:
Добавим следующий код:
#include "stm32f30x_gpio.h" #include "stm32f30x_rcc.h" void InitGPIO(void) { GPIO_InitTypeDef PORTE; GPIO_InitTypeDef PORTA; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE,ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA,ENABLE); GPIO_StructInit (&PORTE); PORTE.GPIO_Mode = GPIO_Mode_OUT; PORTE.GPIO_OType = GPIO_OType_PP; PORTE.GPIO_Pin = GPIO_Pin_10; PORTE.GPIO_PuPd = GPIO_PuPd_DOWN; PORTE.GPIO_Speed = GPIO_Speed_50MHz; GPIO_StructInit (&PORTA); PORTA.GPIO_Mode = GPIO_Mode_IN; PORTA.GPIO_Pin = GPIO_Pin_0; PORTA.GPIO_PuPd = GPIO_PuPd_DOWN; PORTA.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init (GPIOE,&PORTE); GPIO_Init (GPIOA,&PORTA); }; int main(void) { InitGPIO(); while (1) { if (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1) { GPIO_SetBits(GPIOE,GPIO_Pin_10); } else { GPIO_ResetBits(GPIOE,GPIO_Pin_10); } } }
#include "stm32f30x_gpio.h" #include "stm32f30x_rcc.h" void InitGPIO (void ) GPIO_InitTypeDef PORTE ; GPIO_InitTypeDef PORTA ; RCC_AHBPeriphClockCmd (RCC_AHBPeriph_GPIOE , ENABLE ) ; RCC_AHBPeriphClockCmd (RCC_AHBPeriph_GPIOA , ENABLE ) ; GPIO_StructInit (& PORTE ) ; PORTE . GPIO_Mode = GPIO_Mode_OUT ; PORTE . GPIO_OType = GPIO_OType_PP ; PORTE . GPIO_Pin = GPIO_Pin_10 ; PORTE . GPIO_PuPd = GPIO_PuPd_DOWN ; PORTE . GPIO_Speed = GPIO_Speed_50MHz ; GPIO_StructInit (& PORTA ) ; PORTA . GPIO_Mode = GPIO_Mode_IN ; PORTA . GPIO_Pin = GPIO_Pin_0 ; PORTA . GPIO_PuPd = GPIO_PuPd_DOWN ; PORTA . GPIO_Speed = GPIO_Speed_50MHz ; GPIO_Init (GPIOE , & PORTE ) ; GPIO_Init (GPIOA , & PORTA ) ; int main (void ) InitGPIO () ; while (1 ) if (GPIO_ReadInputDataBit (GPIOA , GPIO_Pin_0 ) == 1 ) GPIO_SetBits (GPIOE , GPIO_Pin_10 ) ; else GPIO_ResetBits (GPIOE , GPIO_Pin_10 ) ; |
Итак, разберем подробнее, что же происходит в данном коде.
Точка входа - функция main , именно с неё начинается работа нашей прошивки. Сначала вызывается функция InitGPIO ,в которой мы настраиваем работу наших портов. Остановимся на ней подобробнее.
Вначале создаются две структуры GPIO_InitTypeDef инициализации портов GPIOE ,GPIOA , названные мною как PORTA ,PORTE . Данные структуры являются частью библиотеки SPL , и содержат в себе параметры работы соответствующих портов.
Затем, происходит включение тактирование портов GPIOA ,GPIOE командами RCC_AHBPeriphClockCmd. Следует помнить что, изначально тактирование периферии контроллера отключено, в целях снижения энергопотребления. Данной командой мы включаем или выключаем тактирование периферии шины AHB . Подробнее о тактировании поговорим в следующих уроках.
После этого мы заполняем поля структуры GPIO_InitTypeDef , для портов GPIOE ,GPIOA . Предварительно структуру можно проинициализировать (записать в неё стандартные значения) командой GPIO_StructInit. Рассмотрим поля структуры GPIO_InitTypeDef:
- GPIO _Mode - Режим работы порта.
- GPIO_OType - При настройке порта на выход, данным полем задается его тип (С открытым стоком или двухтактный).
- GPIO_Pin - Данным полем мы выбираем, какие ножки порта настраивать.
- GPIO_PuPd - Выбираем режим подтяжки к напряжению питания или к земле.
- GPIO_Speed - Скорость работы порта.
Итак, поля заполнены, теперь инициализируем порты согласно тому, что прописано в соответствующей структуре командой GPIO_Init.
На этом функция InitGPIO заканчивается.
После вызова функции InitGPIO , наступает основной цикл программы while . В нем мы считываем входящее значение 0 ножки порта GPIOA (К которой подключена кнопка USER платы STM 32 F 3 DISCOVERY ). Делаем мы это командой GPIO_ReadInputDataBit, которая возвращает входное значение выбранной ножки порта. В соответствии с состоянием ножки 0 порта GPIOA , выставляем значение 10 ножки порта GPIOE командами GPIO_ResetBits и GPIO_S etBits. Команда GPIO _ResetBits устанавливает логический ноль на выбранной ножке порта, а команда GPIO _SetBits устанавливает логическую единицу на выбранной нами ножке.
Когда вы загрузите данную программу в плату STM 32 F 3 DISCOVERY , не забудьте нажать кнопку RESET , которая сбросит микроконтроллер. После этого нажимайте кнопку USER и смотрите за результатом.
Любое копирование, воспроизведение, цитирование материала, или его частей разрешено только с письменного согласия администрации MKPROG .RU . Незаконное копирование, цитирование, воспроизведение преследуется по закону!
Порты ввода/вывода GPIO в STM32 имеют по 16 линий, каждая из которых может быть настроена необходимым образом. Поддерживаются функции цифрового ввода, цифрового вывода, входа внешнего прерывания, а также функции ввода/вывода других модулей микроконтроллера. Программирование STM32 для работы с GPIO основано на использовании регистров конфигурации, чтения, записи, защиты конфигурации и регистра битового доступа.
Регистры конфигурации порта.
Port configuration register low (GPIOx_CRL) (x=A..G)
Port configuration register high (GPIOx_CRH) (x=A..G)
Для программирования режимов работы портов ввода/вывода STM32, используются два 32 разрядных регистра для каждого GPIO. Они позволяют произвольно настроить режим работы любой отдельной линии. Регистр GPIOx_CRL отвечает за линии с номерами от 0 до 7, GPIOx_CRH – за линии 8-15. Для каждой из них в регистре имеется два двухразрядных поля CNFy и MODEy. Первое определяет тип работы линии, второе – направление обмена по линии. все биты доступны для чтения/записи.
Регистр GPIOx_CRL
Бит регистра |
||||||||||||||||
Поле |
||||||||||||||||
Линия ввода/вывода |
||||||||||||||||
Бит регистра |
||||||||||||||||
Поле |
||||||||||||||||
Линия ввода/ вывода |
Регистр GPIOX_CRH
Бит регистра |
||||||||||||||||
Поле |
||||||||||||||||
Линия ввода/вывода |
||||||||||||||||
Бит регистра |
||||||||||||||||
Поле |
||||||||||||||||
Линия ввода/вывода |
Поле MODEy может принимать следующие значения:
- 00 – линия работает на ввод. Данное состояние устанавливается после сброса.
- 01 – линия работает на выход, с максимальной частотой переключения 10 МГц
- 10 – линия работает на выход, с максимальной частотой переключения 20 МГц
- 11 – линия работает на выход, с максимальной частотой переключения 50 МГц
Поле CNFy зависит от направления передачи. При работе на вход (MODEy=0) доступны следующие состояния:
- 00 – аналоговый вход.
- 01 – вход в третьем состоянии. (Устанавливается после сброса).
- 10 – вход с подтягивающим резистором
- 11 – зарезервировано для будущих применений.
При работе на выход (MODEy>0) поле CNFy может иметь следующие состояния:
- 00 – цифровой выход
- 01 – цифровой выход с открытым стоком
- 10 – цифровой выход, подключенный специализированным блокам
- 11 – цифровой выход, подключенный специализированным блокам с открытым стоком
Регистр защиты от изменения настроек
Port configuration lock register (GPIOx_LCKR) (x=A..G)
Поле |
||||||||||||||||
Поле |
Установить блокируемый бит в GPIOx_LCKRДля невозможности изменения настроек порта в микроконтроллерах STM32 используется регистр GPIOx_LCKR. Его младщие 15 бит отвечают за соответсвующие линии порта ввода/вывода. Бит 16, установленный в 1, разрешает блокировку изменения настроек. все биты доступны на чтение/запись. Для усложнения жизни пользователям ;-) , используется специальный алгоритм установки защиты. Если он применен, то следующее изменение конфигурации доступно только после сброса. Алгоритм установки защиты выглядит следующим образом:
- Установить бит 16 GPIOx_LCKR.
- Сбросить бит 16 GPIOx_LCKR.
- Установить бит 16 GPIOx_LCKR.
- Прочитать GPIOx_LCKR
- Повторно прочитать GPIOx_LCKR
Регистры установки состояния линий
В отличие от привычных 8-ми битных моделей, в STM32 имеется несколько регистров, отвечающих за состояние линий порта ввода вывода. Условно они разделены на две группы – регистры порта и регистры установки отдельных битов.
Выходной регистр порта ввода/вывода
Port output data register (GPIOx_ODR) (x=A..G)
Поле |
||||||||||||||||
Поле |
Данный регистр имеет разрядность 32, но используются только младшие 16 бит. Биты с 16 по 31 не используются. При записи в GPIOx_ODR какого-либо значения, это значение устанавливается на выходных линиях соответствующего порта. Биты регистра доступны только для чтения/записи.
Входной регистр
Port input data register (GPIOx_IDR) (x=A..G)
Бит | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Поле | Резерв | |||||||||||||||
Бит | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 1 |
Поле | IDR15 | IDR14 | IDR13 | IDR12 | IDR11 | IDR10 | IDR9 | IDR8 | IDR7 | IDR6 | IDR5 | IDR4 | IDR3 | IDR2 | IDR1 | IDR0 |
Аналогично регистру выхода, регистр входа имеет толь 16 младших действующих бит из 32. Чтение GPIOx_IDR возвращает значение состояния всех линий порта. Биты регистра доступны только для чтения.
Регистр битовых операций
Port bit set/reset register (GPIOx_BSRR) (x=A..G)
Бит | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Поле | BR15 | BR14 | BR13 | BR12 | BR11 | BR10 | BR9 | BR8 | BR7 | BR6 | BR5 | BR4 | BR3 | BR2 | BR1 | BR0 |
Бит | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 1 |
Поле | BS15 | BS14 | BS13 | BS12 | BS11 | BS10 | BS9 | BS8 | BS7 | BS6 | BS5 | BS4 | BS3 | BS2 | BS1 | BS0 |
Данный регистр позволяет обращаться к конкретной линии ввода вывода микроконтроллера STM32. Запись единицы в один из старших разрядов сбрасывает выход линии, а запись единицы в младшие разряды устанавливает высокий уровень сигнала на соответствующей линии. Запись в регистр производится в формате слова, при этом нулевые биты никакого действия не оказывают. Биты регистра доступны только для записи.
Регистр сброса
Port bit reset register (GPIOx_BRR) (x=A..G)
Бит | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Поле | Резерв | |||||||||||||||
Бит | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 1 |
Поле | BR15 | BR14 | BR13 | BR12 | BR11 | BR10 | BR9 | BR8 | BR7 | BR6 | BR5 | BR4 | BR3 | BR2 | BR1 | BR0 |
Данный регистр производит сброс высокого уровня линии, установленной в регистре GPIOx_ODR. Задействованы только младшие 16 бит, доступных только для записи.
Каждый порт STM32
состоит из 16 выводов, а каждый вывод может быть сконфигурирован одним из 8 способов.
Ниже изображена структура порта ввода-вывода.
Для того чтобы порт заработал его надо подключить к шине APB2 , установив соответствующий бит IOPxEN , в регистре разрешения тактирования периферийных блоков RCC_APB2ENR .
RCC->APB2ENR |= RCC_APB2ENR_IOPxEN; // Разрешить тактирование PORTx.
После включения все выводы находятся в состоянии плавающего входа , он же высокоимпедансный вход , он же Hi-Z , он же третье состояние .
- Выходной драйвер выключен
- Триггер Шмитта отключён
- Подтягивающие резисторы отключены
- В регистре IDR всегда “0”
В режиме входа
- Выходной драйвер выключен
- В зависимости от настройки, включаются резисторы подтяжки
- Каждый такт шины APB2 данные с входа поступают в регистр IDR, считав этот регистр можно узнать состояние ножки
В режиме выхода
- В режиме Open Drain при записи “0” открывается нижний транзистор, при записи “1” линия остаётся не подключённой
- В режиме Push Pull при записи “1” открывается верхний транзистор, при записи “0” - нижний
- Входной Триггер Шмитта включён
- Резисторы подтяжки отключены
В режиме альтернативной функции
- Драйвер включается в режиме Push Pull или Open Drain, в зависимости от конфигурации
- Выходной драйвер управляется сигналами периферии, а не регистром ODR
- Входной триггер Шмитта включён
- Резисторы подтяжки отключены
- По каждому такту шины APB2 данные c выхода передаются в регистр IDR, оттуда же их можно считать в режиме Open Drain
- Чтение регистра ODR возвращает последнее записанное значение в режиме Push Pull
Из таблицы видно, что возможны два варианта конфигурации, в режиме альтернативной функции: Push Pull
и Open Drain
. Например, мы хотим, настроить в режим альтернативной функции ножку, отвечающую за приём данных по USART. Для этого в Reference Manual RM0008, начиная с 161 страницы, идут таблицы, в которых можно посмотреть как cконфигурировать вывод, для разной периферии.
Нам подойдет Input floating или Input pull-up .
Конфигурация выводов задаётся в регистрах GPIOx_CRL
, GPIOx_CRH
, в этих регистрах для конфигурации каждого вывода отведено 4 бита, MODE и CNF
. В GPIOx_CRL
конфигурируются выводы с 0 по 7, а в GPIOx_CRH
с 8 по 15.
Если MODE = 00 , то вывод настроен на вход, конфигурация входа в таком случае задаётся в регистрах CNF . Если MODE не равен 00, в таком случае вывод настроен как выход, а значение MODE задаёт максимальную частоту, с которой может он переключаться.
//Полагаем что выводы после сброса в режиме плавающего входа //разрешаем тактирование порта A RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; //вход с подтяжкой к + GPIOA->CRL &= ~GPIO_CRL_CNF0; GPIOA->CRL |= GPIO_CRL_CNF0_1; GPIOA->ODR |= GPIO_ODR_ODR0; //вход с подтяжкой к - GPIOA->CRL &= ~GPIO_CRL_CNF1; GPIOA->CRL |= GPIO_CRL_CNF1_1; GPIOA->ODR &= ~GPIO_ODR_ODR1; //аналоговый режим GPIOA->CRL &= ~GPIO_CRL_CNF2; //выход с открытым стоком 2MHz GPIOA->CRL &= ~GPIO_CRL_CNF3; GPIOA->CRL |= GPIO_CRL_CNF3_0; GPIOA->CRL |= GPIO_CRL_MODE3_1; //двухтактный выход 10MHz GPIOA->CRL &= ~GPIO_CRL_CNF4; GPIOA->CRL |= GPIO_CRL_MODE4_0; //альтернативная ф-ция, двухтактный выход, 50 MHz GPIOA->CRL &= ~GPIO_CRL_CNF5; GPIOA->CRL |= GPIO_CRL_CNF5_1; GPIOA->CRL |= GPIO_CRL_MODE5; //альтернативная ф-ция, выход с открытым стоком, 50 MHz GPIOA->CRL |= GPIO_CRL_CNF6; GPIOA->CRL |= GPIO_CRL_MODE6;
Считать состояние входа можно с помощью Port input data register или коротко GPIOx_IDR , где x – название порта, может быть от A до G. Считать состояние любого вывода можно из 16 младших бит, старшие 16 бит не используются.
//проверяем значение нулевого вывода порта А if (GPIOА->IDR & GPIO_IDR_IDR0)
Если порт настроен на выход, управлять его состоянием можно с помощью регистра Port output data register или GPIOx_ODR . Значение, которое мы запишем в этот регистр, появится на соответствующих выводах порта. Для установки состояния порта, выделены 16 младших бит, старшие 16 бит не используются.
//если вывод в режиме входа то активируется подтяжка к питанию GPIOA->ODR |= GPIO_ODR_ODR0; //или к земле GPIOA->ODR &= ~GPIO_ODR_ODR0; //если в режиме выхода, то на нём установится соответствующий лог.уровень //например так можно установить все выходы порта в 1 GPIOA->ODR = 0xFFFF;
В STM32 возможно атомарно управлять отдельными битами порта с помощью регистров GPIOx_BSRR (Port Bit Set/Reset Register) и GPIOx_BRR (Port Bit Reset Register).
Для установки отдельного бита порта вручную, надо считать значение порта, изменить нужный бит с помощью маски и результат вернуть обратно в GPIOx_ODR . Так как действий целых три, то возникшее между ними прерывание, может подпортить данные. С помощью описанных выше регистров, это делается в одно действие.
Для сброса бита надо в нулевой бит GPIOx_BRR записать единичку, при этом в нулевой бит GPIOx_ODR запишется 0, для этой операции выделены младшие 16 бит, старшие 16 бит не используются.
//сбросить нулевой бит порта А GPIOA->BRR = GPIO_BRR_BR0;
С GPIOx_BSRR всё чуть интереснее, младшие 16 бит отвечают за установку 1, старшие 16 бит за сброс в 0. Чтобы установить 1 в нулевой бит, надо в нулевой бит GPIOx_BSRR записать 1. Чтобы установить 0 в нулевой бит, надо в 16 бит установить 1.
//сбросить нулевой бит GPIOA->BSRR = GPIO_BSRR_BR0; //установить нулевой бит GPIOA->BSRR = GPIO_BSRR_BS0;
У STM32 есть возможность защитить конфигурацию порта от изменения, для этого выделен регистр GPIOx_LCKR . Младшие 16 бит используются для выбора вывода, который хотим заблокировать (выбор бита осуществляется установкой единицы), затем специальной последовательностью записей в 16 бит(LCKK ) осуществляется блокировка.
Последовательность следующая: записать в LCKK 1 , записать 0 ,записать 1, затем из регистра LCKR считать 0, считать 1. Последняя считанная единица говорит о том, что вывод заблокирован. Разблокировка вывода произойдёт только после перезагрузки контроллера.
#include "stm32f10x.h" uint32_t temp; int main(void) { //разрешаем тактирование порта RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; //настраиваем как двухтактный выход GPIOA->CRL &= ~GPIO_CRL_CNF0; //с максимальной частотой 50MHz GPIOA->CRL |= GPIO_CRL_MODE0; //выбираем вывод который хотим залочить GPIOA->LCKR |= GPIO_LCKR_LCK0; //записываем 1 GPIOA->LCKR |= GPIO_LCKR_LCKK; //записываем 0 GPIOA->LCKR &= ~GPIO_LCKR_LCKK; //записываем 1 GPIOA->LCKR |= GPIO_LCKR_LCKK; //считываем 2 раза temp = GPIOA->LCKR; temp = GPIOA->LCKR; }
Для получения более подробной информации можно обратиться Reference Manual RM0008 , к разделу General-purpose and alternate-function I/Os (GPIOs and AFIOs) .
Во втором уроке цикла, посвященного работе с микроконтроллерами STM32, речь пойдет о портах ввода/вывода.
Порты микроконтроллера позволяют взаимодействовать с внешними устройствами, начиная от светодиода и кнопки и заканчивая более сложными устройствами: дисплеями, GPS и GSM модемами и так далее. Также порты позволяют организовать связь с другими устройствами, например с компьютером.
General Purpose Input/Output (GPIO). GPIO основной и часто применяемый способ связи с внешней средой. Порты могут работать в двух режимах: вход (прием сигнала) и выход (передача сигнала). Работают они только с логическими уровнями 0 (низкий уровень) или 1 (высокий уровень).
Например, если подключить к порту в режиме выхода светодиод, то при подаче сигнала высокого уровня светодиод будет светиться, а при подаче низкого – потухнет.
Если включить вывод в режим входа и подключить к нему кнопку, то с помощью микроконтроллера можно отслеживать ее состояние: нажатое или отпущенное.
По сути GPIO самый простой и примитивный способ организации работы с внешними устройствами, но использование обработки прерываний и таймеров значительно расширяет возможности. Речь о них пойдет немного позже.
Решим первую практическую задачу: управление светодиодами и считывание состояние кнопки.
Следует отметить очень важный момент – порты микроконтроллера могут выдать ток не более 20 мА. Хотя выдать он их может, но один раз и ненадолго, до хлопка и сизого дыма;). Для подключения более мощных нагрузок следует использовать силовые ключи.
Итак, начнем. Для работы возьмем плату STM32F4 Discovery. На ней изначально установлена пользовательская кнопка, подключенная к порту PA0 и 4 светодиода, подключенные к портам PD12-PD15.
Схема подключение кнопки и светодиодов показаны на рисунке.
Резистор R1 номиналом 10кОм – «подтяжка к земле», позволяет избежать ситуации, когда порт не подключен ни к «0», ни к «1» - этого необходимо избегать, а резистор решает эту проблему. Такую подтяжку можно включить и программно, но лучше обезопасить себя так.
Резисторы R2-R5 330Ом ограничивают ток, протекающий через светодиоды. Их можно выбрать в диапазоне от 200Ом до 1кОм, все зависит от необходимой яркости.
Теперь перейдем к написанию программы. В качестве среды разработки я использую . Среда бесплатная и, на мой взгляд, удобная. Как начинать в ней работать рассказывать не буду – в интернете по ней достаточно информации, для прошивки использую STM32 ST-LINK Utility.
Для начала включаем тактирование порта A, к которому подключена кнопка:
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
Теперь нужно правильно сконфигурировать порт:
//Структура содержащая настройки порта GPIO_InitTypeDef GPIO_InitStructure; //задаем номер вывода, если кнопка подключена, например к 6 порту, то пишем GPIO_Pin_6 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //порт будет работать как цифровой вход GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
Существует несколько вариантов режима работы порта:
GPIO_Mode_IN – цифровой вход;
GPIO_Mode_OUT – цифровой выход;
GPIO_Mode_AF – альтернативная функция (UART и т.д.);
GPIO_Mode_AN – аналоговый режим.
//включаем подтяжку к «земле» GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
Возможны следующие режимы «подтяжки»:
GPIO_PuPd_NOPULL – без подтяжки, вывод «болтается в воздухе»
GPIO_PuPd_UP – подтяжка к 3,3В
GPIO_PuPd_DOWN – подтяжка к «земле»
//вызов функции инициализации GPIO_Init(GPIOA, &GPIO_InitStructure);
Теперь сконфигурируем выводы, к которым подключены светодиоды:
//Включаем тактирование порта D RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); //Выбираем нужные выводы GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12| GPIO_Pin_13| GPIO_Pin_14| GPIO_Pin_15; //Включаем режим выхода GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //вызов функции инициализации GPIO_Init(GPIOD, &GPIO_InitStructure);
Вот и все, порты сконфигурированы. Теперь напишем обработку в основном цикле программы:
While(1) { //Если кнопка нажата, то… if (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1) { GPIO_SetBits(GPIOD, GPIO_Pin_12); //Подаем «1» на PD12 delay(); //Функция задержки GPIO_SetBits(GPIOD, GPIO_Pin_13); //Подаем «1» на PD13 delay(); GPIO_SetBits(GPIOD, GPIO_Pin_14); //Подаем «1» на PD14 delay(); GPIO_SetBits(GPIOD, GPIO_Pin_15); //Подаем «1» на PD15 delay(); GPIO_ResetBits(GPIOD, GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); //Сбрасываем все пины в «0» delay(); } }
Вот и все, программа готова. Полная версия в архиве с проектом. Работа платы показана на видео.
В этой статье перейдем к разбору действий для управления портами ввода/вывода. В технической документации к плате Discovery с чипом STM32f407VG указано, что светодиоды подключены к порту GPIOD и ножкам 12, 13, 14, 15. Смотрим в RM0090 Reference manual, и видим, что регистры управления порта D (GPIOD) занимают пространство в памяти, начиная с адреса 0x4002 0C00. Дальше в документации идет описания регистров управления GPIOx (где "х" имя порта:A,B,C...). Первый регистр - GPIOx_MODER , адрес смещения - ноль, то есть он первый в блоке и находится по адресу 0x40020C00. Он отвечает за направление тока: на вход, выход, или режим альтернативной функции (то есть к ножкам можно подключать разные внутренние блоки/интерфейсы передачи\приема данных, а не только использовать для вкл/выкл +3V). Так как нам нужно зажечь светодиод, устанавливаем режим вывода ножек 12,13,14,15 . Записываем "01" в каждую пару битов нужных ножек, и записываем получившееся число в указатель на регистр GPIOD moder:
*((uint32_t*)0x40020C00)=0х55000000;
Смотрим дальше, следующий регистр GPIOxOTYPER
, можно оставить по умолчанию. GPIOxSPEEDR
, скорость считывания
состояния ножки, можем оставить по умолчанию. GPIOxPUPDR
-регистр позволяет "прижать" ножку к положительному или
отрицательному значению тока.
GPIOx_IDR
- Регистр только для чтения, указывает на
логическое значение на соответствующей ножке. Можно его использовать,
например, если нужно узнать нажата ли кнопка, или пришел ли какой нибудь
сигнал на ножку.
GPIOx_ODR
- регистр для чтения и записи. Запись "1" в
нужный бит подаст логическую единицу на соответствующую ножку. Запись
"0" - сбросит бит, и установит на ножке логический ноль.
GPIOx_BSRR
- Регистр устанавливает логический ноль или
единицу на ножках. в отличии от регистра GPIOx_ODR, значение которого
нужно переписывать целиком ради установки отдельного бита, GPIOx_BSRR -
можно точечно устанавливать соответствующие биты регистра GPIOx_ODR, не
перезаписывая его значения целиком. Установка "1" в биты 0-15 ,
установит биты 0-15 в GPIOx_ODR. Установка "1" в биты 16-31 установит
"0" в биты 0-15 в GPIOx_ODR. запись "0" в регистр GPIOx_BSRR не дает
никакого эффекта.
GPIOx_LCKR
- регистр устанавливающий блокировку на изменение настроек регистров портов.
GPIOx_AFRL
- Регистр альтернативных функций ножек 0-7.
GPIOx_AFRH
- Регистр альтернативных функций ножек 7-15.
Адреса регистров GPIO
#define RCC_GPIO *((uint32_t*)0x40023830)
//.................
#define GPIOA_MODER *((uint32_t*)0x40020000)
#define GPIOA_OTYPER *((uint32_t*)0x40020004)
#define GPIOA_OSPEEDR *((uint32_t*)0x40020008)
#define GPIOA_IDR *((uint32_t*)0x40020010)
#define GPIOA_PUPDR *((uint32_t*)0x4002000C)
#define GPIOA_ODR *((uint32_t*)0x0x40020014)
#define GPIOA_BSRR *((uint32_t*)0x40020018)
#define GPIOA_AFRL *((uint32_t*)0x40020020)
#define GPIOA_AFRH *((uint32_t*)0x40020024)
//.................
#define GPIOB_MODER *((uint32_t*)0x40020400)
#define GPIOB_OTYPER *((uint32_t*)0x40020404)
#define GPIOB_OSPEEDR *((uint32_t*)0x40020408)
#define GPIOB_IDR *((uint32_t*)0x40020410)
#define GPIOB_PUPDR *((uint32_t*)0x4002040C)
#define GPIOB_ODR *((uint32_t*)0x40020414)
#define GPIOB_BSRR *((uint32_t*)0x40020418)
#define GPIOB_AFRL *((uint32_t*)0x40020420)
#define GPIOB_AFRH *((uint32_t*)0x40020424)
//................
#define GPIOC_MODER *((uint32_t*)0x40020800)
#define GPIOC_OTYPER *((uint32_t*)0x40020804)
#define GPIOC_OSPEEDR *((uint32_t*)0x40020808)
#define GPIOC_IDR *((uint32_t*)0x40020810)
#define GPIOC_PUPDR *((uint32_t*)0x4002080C)
#define GPIOC_ODR *((uint32_t*)0x40020814)
#define GPIOC_BSRR *((uint32_t*)0x40020818)
#define GPIOC_AFRL *((uint32_t*)0x40020820)
#define GPIOC_AFRH *((uint32_t*)0x40020824)
//.................
#define GPIOD_MODER *((uint32_t*)0x40020C00)
#define GPIOD_OTYPER *((uint32_t*)0x40020C04)
#define GPIOD_OSPEEDR *((uint32_t*)0x40020C08)
#define GPIOD_IDR *((uint32_t*)0x40020C10)
#define GPIOD_PUPDR *((uint32_t*)0x40020C0C)
#define GPIOD_ODR *((uint32_t*)0x40020C14)
#define GPIOD_BSRR *((uint32_t*)0x40020C18)
#define GPIOD_AFRL *((uint32_t*)0x40020C20)
#define GPIOD_AFRH *((uint32_t*)0x40020C24)
//................
#define GPIOE_MODER *((uint32_t*)0x40021000)
#define GPIOE_OTYPER *((uint32_t*)0x40021004)
#define GPIOE_OSPEEDR *((uint32_t*)0x40021008)
#define GPIOE_IDR *((uint32_t*)0x40021010)
#define GPIOE_PUPDR *((uint32_t*)0x4002100C)
#define GPIOE_ODR *((uint32_t*)0x40021014)
#define GPIOE_BSRR *((uint32_t*)0x40021018)
#define GPIOE_AFRL *((uint32_t*)0x40021020)
#define GPIOE_AFRH *((uint32_t*)0x40021024)