STM32 GPIO или порты ввода-вывода.

STM32 GPIO или порты ввода-вывода.

Каждый порт STM32 состоит из 16 выводов, а каждый вывод может быть сконфигурирован одним из 8 способов.
STM32 GPIO или порты ввода-вывода.

Ниже изображена структура порта ввода-вывода.
STM32 GPIO или порты ввода-вывода.

Для того чтобы порт заработал его надо подключить к шине APB2, установив соответствующий бит IOPxEN, в регистре разрешения тактирования периферийных блоков RCC_APB2ENR.

RCC->APB2ENR |= RCC_APB2ENR_IOPxEN; // Разрешить тактирование PORTx.

После включения все выводы находятся в состоянии плавающего входа, он же высокоимпедансный вход, он же Hi-Z, он же третье состояние.
  • Выходной драйвер выключен
  • Триггер Шмитта отключён
  • Подтягивающие резисторы отключены
  • В регистре IDR всегда “0”


В режиме входа
  • Выходной драйвер выключен
  • Входной Триггер Шмитта включён
  • В зависимости от настройки, включаются резисторы подтяжки
  • Каждый такт шины APB2 данные с входа поступают в регистр IDR, считав этот регистр можно узнать состояние ножки


В режиме выхода
  • В режиме Open Drain при записи “0” открывается нижний транзистор, при записи “1” линия остаётся не подключённой
  • В режиме Push Pull при записи “1” открывается верхний транзистор, при записи “0” — нижний
  • Входной Триггер Шмитта включён
  • Резисторы подтяжки отключены
  • По каждому такту шины APB2 данные c выхода передаются в регистр IDR, оттуда же их можно считать в режиме Open Drain
  • Чтение регистра ODR возвращает последнее записанное значение в режиме Push Pull

 
В режиме альтернативной функции
  • Драйвер включается в режиме Push Pull или Open Drain, в зависимости от конфигурации
  • Выходной драйвер управляется сигналами периферии, а не регистром ODR
  • Входной триггер Шмитта включён
  • Резисторы подтяжки отключены
  • По каждому такту шины APB2 данные c выхода передаются в регистр IDR, оттуда же их можно считать в режиме Open Drain
  • Чтение регистра ODR возвращает последнее записанное значение в режиме Push Pull

 
Из таблицы видно, что возможны два варианта конфигурации, в режиме альтернативной функции: Push Pull и Open Drain. Например, мы хотим, настроить в режим альтернативной функции ножку, отвечающую за приём данных по USART. Для этого в Reference Manual RM0008, начиная с 161 страницы, идут таблицы, в которых можно посмотреть как cконфигурировать вывод, для разной периферии.
STM32 GPIO или порты ввода-вывода.

Нам подойдет  Input floating или Input pull-up.
 
Конфигурация выводов задаётся в регистрах GPIOx_CRL, GPIOx_CRH, в этих регистрах для конфигурации каждого вывода отведено 4 бита, MODE[1:0] и CNF[1:0]. В GPIOx_CRL конфигурируются выводы с 0 по 7, а в GPIOx_CRH с 8 по 15.
STM32 GPIO или порты ввода-вывода.

STM32 GPIO или порты ввода-вывода.

Если MODE[1:0] = 00, то вывод настроен на вход, конфигурация входа в таком случае задаётся в регистрах CNF[1:0]. Если MODE[1:0] не равен 00, в таком случае вывод настроен как выход, а значение MODE [1:0] задаёт максимальную частоту, с которой может он переключаться.
 

//Полагаем что выводы после сброса в режиме плавающего входа
       
       //разрешаем тактирование порта 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 бит не используются.
STM32 GPIO или порты ввода-вывода.


//проверяем значение нулевого вывода порта А
if (GPIOА->IDR & GPIO_IDR_IDR0)

Если порт настроен на выход, управлять его состоянием можно с помощью регистра Port output data register или GPIOx_ODR. Значение, которое мы запишем в этот регистр, появится на соответствующих выводах порта. Для установки состояния порта, выделены 16 младших бит, старшие 16 бит не используются.
STM32 GPIO или порты ввода-вывода.


//если вывод в режиме входа то активируется подтяжка к питанию
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 бит не используются.
STM32 GPIO или порты ввода-вывода.


//сбросить нулевой бит порта А
GPIOA->BRR = GPIO_BRR_BR0;

С GPIOx_BSRR всё чуть интереснее, младшие 16 бит отвечают за установку 1, старшие 16 бит за сброс в 0. Чтобы установить 1 в нулевой бит, надо в нулевой бит GPIOx_BSRR записать 1. Чтобы установить 0 в нулевой бит, надо в 16 бит установить 1.
STM32 GPIO или порты ввода-вывода.


//сбросить нулевой бит 
GPIOA->BSRR = GPIO_BSRR_BR0;
//установить нулевой бит
GPIOA->BSRR = GPIO_BSRR_BS0;

У STM32 есть возможность защитить конфигурацию порта от изменения, для этого выделен регистр GPIOx_LCKR. Младшие 16 бит используются для выбора вывода, который хотим заблокировать (выбор бита осуществляется установкой единицы), затем специальной последовательностью записей в 16 бит(LCKK) осуществляется блокировка.
STM32 GPIO или порты ввода-вывода.

Последовательность следующая: записать в 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).
комментарии
1