Использование отладочной платы 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); }