Регистры портов STM32F103C8T6 (Arduino IDE)

Использование отладочной платы STM32 в Arduino IDE — http://rcl-radio.ru/?p=68376

Использование регистров портов позволяют выполнять низкоуровневые высокоскоростные манипуляции с выводами микроконтроллера. Микроконтроллер STM32F103C8T6 существуют 37 портов ввода-вывода:

  • GPIOA – 16 выводов: PA0-PA15
  • GPIOB – 16 выводов: PB0-PB15
  • GPIOC – 3 вывода: PC13-PC15
  • GPIOD – 2 выводов: PD0, PD1

При работе с портами следует обратить особое внимание на то, что РА11 и РА12 — это вход USB, поэтому не желательно их использовать (использование этих портов может привести к неработоспособности микроконтроллера). Так же к РС14 и РС15 подключён внешний кварц для часов (32.768). Все остальные пины могут выдать максимум 20 мА, рекомендуется 8 мА. Общая максимальная нагрузка на все порты не должна превышать 150 мА.

Управление портами STM32 осуществляется при помощи наборов из нескольких 32-разрядных регистров.

В этой статье будут рассмотрены следующие регистры портов:

  • GPIOx_CRL, GPIOx_CRH – задают режимы работы каждого из битов порта в качестве входа или выхода, определяют конфигурацию входных и выходных каскадов
  • GPIOx_IDR – входной регистр данных для чтения физического состояния выводов порта x
  • GPIOx_ODR– выходной регистр осуществляет запись данных непосредственно в порт

Регистр CRL конфигурирует выводы 0…7 порта, а регистр CRH выводы 8…15.

Регистр CRL

Биты MODE указанных регистров определяют направление вывода и ограничение скорости переключения в режиме выхода:

  • MODE[1:0] = 00: Режим входа (состояние после сброса)
  • MODE[1:0] = 01: Режим выхода, максимальная скорость – 10 МГц
  • MODE[1:0] = 10: Режим выхода, максимальная скорость – 2 МГц
  • MODE[1:0] = 11: Режим выхода, максимальная скорость – 50 МГц

Биты CNF задают конфигурацию выходных каскадов соответствующих выводов.

В режиме входа:

  • CNF[1:0] = 00: Аналоговый вход
  • CNF[1:0] = 01: Вход в третьем состоянии (состояние после сброса)
  • CNF[1:0] = 10: Вход с притягивающим резистором pull-up (если PxODR=1) или pull-down (если PxODR=0)
  • CNF[1:0] = 11: Зарезервировано

В режиме выхода:

  • CNF[1:0] = 00: Двухтактный выход общего назначения
  • CNF[1:0] = 01: Выход с открытым стоком общего назначения
  • CNF[1:0] = 10: Двухтактный выход с альтернативной функцией
  • CNF[1:0] = 11: Выход с открытым стоком с альтернативной функцией

Например если необходимо настроить работу выхода PA0, использует биты MODE0 и CNF0 регистра CRL:

Например если необходимо настроить работу выхода PB11, использует биты MODE3 и CNF3 регистра CRH:

Разберем два примера, для наглядности можно взять светодиод (через резистор 300 Ом) который подключим к выходу PA0 в первом примере и PB11 для второго примера.

После загрузки скетча светодиод начнет мигать с интервалом 1 секунда, светодиод подключен к выходу PA0:

void setup() {
 GPIOA-> regs-> CRL = 0b0011; // CNF[1:0] = 00 | MODE[1:0] = 11
}
 
void loop() {
  GPIOA-> regs-> ODR = 0b00;
  delay(1000);
  GPIOA-> regs-> ODR = 0b01;
  delay(1000);
}

Регистр ODR устанавливает на выходе PA0 лог. единицу.

После загрузки скетча светодиод начнет мигать с интервалом 1 секунда, светодиод подключен к выходу PB11:

void setup() {
 GPIOB-> regs-> CRH = 0b0011000000000000; // CNF[1:0] = 00 | MODE[1:0] = 11
}
 
void loop() {
  GPIOB-> regs-> ODR = 0b00;
  delay(1000);
  GPIOB-> regs-> ODR = 0b100000000000;
  delay(1000);
}

Регистр ODR устанавливает на выходе PB11 лог. единицу.

Если необходимо менять лог. состояние выхода и не менять состояние других выходов, то можно воспользоваться следующим примером:

void setup() {
 GPIOB-> regs-> CRH = 0x3000; // 0b0011000000000000
}
 
void loop() {
  GPIOB-> regs-> ODR &= ~(1 << 11); // установить лог. 0 на выходе PB11
  delay(1000);
  GPIOB-> regs-> ODR |= (1 << 11);  // установить лог. 1 на выходе PB11
  delay(1000);
}

Если у Вас простой скетч не требовательный к размеру памяти и ресурсам микроконтроллера, то проще использовать стандартные функции. Но если скетч большой и требовательный к скорости переключения цифровых выводов, то использование регистров будет самым оптимальным для Вас решением.

Сравним для примера два скетча, которые выполняют одно и тоже действие — переключают лог. состояние выхода 1000 раз.

unsigned long times;

void setup() {
 Serial.begin(9600);
 pinMode(PB11,OUTPUT);
}
 
void loop() {
  times = micros();
  for(int i = 0; i < 1000; i++){
  digitalWrite(PB11,LOW);
  digitalWrite(PB11,HIGH);
  }
  times = micros() - times;
  Serial.println(times);
  delay(1000);
}

Время выполнения 1000 переключений лог. состояния выхода 1377 мкс.

unsigned long times;

void setup() {
 Serial.begin(9600);
 GPIOB-> regs-> CRH = 0x3000; // 0b0011000000000000
}
 
void loop() {
  times = micros();
  for(int i = 0; i < 1000; i++){
  GPIOB-> regs-> ODR &= ~(1 << 11); // установить лог. 0 на выходе PB11
  GPIOB-> regs-> ODR |= (1 << 11);  // установить лог. 1 на выходе PB11
  }
  times = micros() - times;
  Serial.println(times);
  delay(1000);
}

Время выполнения 1000 переключений лог. состояния выхода 293 мкс.

Далее рассмотрим режим чтения состояния порта и отдельного выхода. Для чтения состояния порта или выхода используется регистр IDR. Рассмотрим пример чтения входа PA0:

bool data;

void setup() {
 Serial.begin(9600);
 GPIOA-> regs-> CRL = 0b0100; // CNF[1:0] = 01 | MODE[1:0] = 00
}
 
void loop() {
  data = GPIOA-> regs-> IDR & 0b01; // чтение входа порта 0b0000000000000001 (PA0)
  Serial.println(data,BIN);
  delay(1000);
}

При подаче на вход PA0 лог. 1 (3,3 В) или 0, в мониторе порта видно изменение его логического состояния.

Далее рассмотрим одновременное чтение нескольких входов порта (PA0…PA3):

unsigned int data;

void setup() {
 Serial.begin(9600);
 GPIOA-> regs-> CRL = 0b0100010001000100; // 0100 0100 0100 0100
}
 
void loop() {
 data = GPIOA-> regs-> IDR & 0b1111; // чтение входов порта 0b0000000000001111 (PA0...PA3)
  Serial.println(data,BIN);
  delay(1000);
}

Использование подтягивающего резистора на входе:

bool data;

void setup() {
 Serial.begin(9600);
 GPIOB-> regs-> CRL = 0b1000; 
 GPIOB-> regs-> ODR |= (1 << 0);
}
 
void loop() {
  data = GPIOB-> regs-> IDR & 0b01; // PB0 INPUT
  Serial.println(data,BIN);
  delay(200);
}

Вход PB0 использует подтягивающий резистор к 3.3 В, в мониторе порта на входе определяется лог. единица, при замыкании входа на GND, лог 1 меняется на 0.


Сброс и установка битов порта

С помощью регистров сброса и установки —  BRR и BSRR можно сбросить или установить любой бит регистра ODR. Иначе говоря поменять логическое состояние любого выхода, не затрагивая остальные выходы.

BRR: Сбрасывает бит у регистра ODR

  • 0: не оказывает влияние на соответствующий бит ODR
  • 1: Сбрасывает в ноль соответствующий бит ODR

BSy: Устанавливает бит у регистра ODR

  • 0: не оказывает влияние на соответствующий бит ODR
  • 1: Устанавливает в единицу соответствующий бит ODR

Пример:

Подключите светодиод к выходу PA0, после загрузки скетча будет мигать светодиод с интервалом 1 секунда.

void setup() {
 Serial.begin(9600);
 GPIOA-> regs-> CRL = 0b0011; 
}
 
void loop() {
  GPIOA-> regs-> BRR =  0b0000000000000001; // сброс в 0 выхода PA0
  delay(1000);
  GPIOA-> regs-> BSRR = 0b0000000000000001; // установка 1 выхода PA0
  delay(1000);
}

Добавить комментарий

Войти с помощью: