В этой статье будет рассмотрено программное управление выводами портов STM32 на примере микроконтроллера STM32F407 в корпусе LQFP144 (такой микроконтроллер установлен на платах Olimex STM32-E407 и STM32-P407 [2]). Непонятные термины и сокращения см. в словарике [3].
[Обзор выводов GPIO]
У микроконтроллера STM32F407 всего имеется семь 16-выводных портов: GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, GPIOF, GPIOG (под GPIOx подразумеваются все эти порты). Они распределены по корпусу кристалла, как показано на рисунке. Также есть еще и порт GPIOH, но у него в корпусе LQFP144 представлены только 2 ножки, и те задействованы под кварцевый резонатор, так что скорее GPIOH использоваться будет очень редко.
Любая ножка порта GPIO может быть настроена в одной из четырех возможных конфигураций:
• Input (вход): Floating (плавающий вход с высоким входным сопротивлением, ничем не нагруженный), Pull-up (с верхним нагрузочным резистором), Pull-down (с нижним нагрузочным резистором). • Output (выход): Push-Pull двухтактный ключ (Pull-up с верхней нагрузкой, Pull-down с нижней нагрузкой или no Pull просто двухтактный выход), Open Drain открытый сток (Pull-up, Pull-down или no Pull). В режиме выхода может быть запрограммирована скорость работы порта: 2 МГц, 25 МГц, 50 МГц или 100 МГц. • Alternate Function (альтернативная функция): Push-Pull (Pull-up, Pull-down или no Pull), Open Drain (Pull-up, Pull-down или no Pull). • Analog (аналоговая линия): этот режим необходим, когда вывод используется как канал ADC (АЦП) или выход DAC (ЦАП).
Во время активного сигнала сброса и сразу после его окончания все альтернативные функции на выводах портов не активны (за исключением выводов, задействованных под JTAG), и все порты оказываются сконфигурированными как плавающие входы с высоким сопротивлением.
Выводы генератора LSE (OSC32_IN и OSC32_OUT) могут использоваться как GPIO (PC14 и PC15 соответственно), если генератор LSE выключен. LSE имеет приоритет перед функцией GPIO. Выводы генератора HSE (OSC_IN и OSC_OUT) могут использоваться как GPIO (PH0 и PH1), когда генератор HSE выключен (основной кварцевый генератор не используется, что бывает очень редко). HSE имеет приоритет над функцией GPIO.
Чтобы порт начал работать в режиме программного ввода/вывода, нужно 2 вещи: разрешить его тактирование, и настроить параметры порта.
[Настройка тактирования порта]
Чтобы порт GPIOx начал работать, и можно было программно управлять состоянием его ножек, нужно включить тактирование порта. Каждый из портов GPIOx подключен к ядру микроконтроллера через шину AHB1, и для управления включения тактирования в нормальном режиме работы служит регистр RCC_AHB1ENR (описание его бит см. [1]).
31 |
30 |
29 |
28 |
27 |
26 |
25 |
24 |
23 |
22 |
21 |
20 |
19 |
18 |
17 |
16 |
Res. |
OTG HSULPI EN |
OTG HS EN |
ETH MACPTP EN |
ETH MACRX EN |
ETH MACTX EN |
ETH MAC EN |
Res. |
DMA2 EN |
DMA1 EN |
CCM DATARAM EN |
Res. |
BKP SRAM EN |
Res. |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
Reserved |
CRCEN |
Reserved |
GPIOI EN |
GPIOH EN |
GPIOG EN |
GPIOF EN |
GPIOE EN |
GPIOD EN |
GPIOC EN |
GPIOB EN |
GPIOA EN |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
rw |
Например, чтобы пользоваться ножкой PA3 как портом ввода/вывода, нужно разрешить тактирование GPIOA, для чего в разряд GPIOAEN надо записать единицу. Для этой цели служат удобные библиотечные функции (в этом примере разрешено тактирование порта GPIOA):
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
[Управление портом как выходом]
Для того, чтобы можно было начать управлять выводом, переводя его в состояние лог. 0 или лог. 1, порт надо настроить. Пример настройки порта PF6 как выхода:
/* Разрешить тактирование GPIOF */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
/* Конфигурирование ножки PF6, туда подключен светодиод STAT1
платы разработчика Olimex STM32-P407 */
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOF, &GPIO_InitStructure);
После того, как порт настроен, можно его программно переключать в любое нужное состояние. Для переключения порта служат регистры BSRRL и BSRRH, пример:
/* Бесконечный цикл мигания светодиодом STAT1 */
while(1)
{
GPIOF->BSRRL = GPIO_Pin_6; //STAT==1, светодиод горит
Delay(3000000L);
GPIOF->BSRRH = GPIO_Pin_6; //STAT==0, светодиод погас
Delay(3000000L);
}
Для управления ножками можно также применять удобные функции GPIO_SetBits и GPIO_ResetBits.
[Чтение состояния порта как входа]
Получить текущее состояние ножки порта, работающей в режиме входа, можно с помощью функции GPIO_ReadInputDataBit(). Но для начала нужно соответствующие ножки настроить как входы, и разрешить тактирование порта. Вот пример настройки портов для чтения джойстика платы Olimex STM32-P407 [2]:
/* Разрешить тактирование GPIOG, туда подключены контакты джойстика. */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
/* Конфигурирование ножек джойстика платы разработчика Olimex STM32-P407.
* PG6 RIGHT
* PG7 UP
* PG8 DOWN
* PG11 LEFT
* PG15 CENT */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_11 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_Init(GPIOG, &GPIO_InitStructure);
Как опрашивать состояние порта:
if(GPIO_ReadInputDataBit(GPIOG, GPIO_Pin_15))
{
//сюда попадаем, если с входа прочитана лог. 1.
...
}
[Ссылки]
1. STM32F407, разрешение тактирования периферийных устройств. 2. Olimex STM32-P407. 3. STM32: аббревиатуры и термины. |