| Ваш IP: 3.230.1.126 | Online(12) - гости: 9, боты: 3 | Загрузка сервера: 1.54 ::::::::::::


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

Использование регистров портов позволяют выполнять низкоуровневые высокоскоростные манипуляции с выводами микроконтроллера. Плата Arduino Nano (Uno) имеет три порта:

  • B цифровые выводы от D8 до D13
  • C аналоговые входы от A0 до А5
  • D цифровые выводы от D0 до D7

Управление портами осуществляется при помощи трех регистров:

  • DDR — определяет какой из выводов указанного порта будет выходом, а какой входом
  • PORT — переводит выводы порта в состояние HIGH или LOW
  • PIN — считывает состояние порта

Каждый бит в регистре отвечает за соответствующий вывод порта, например за вывод PD0 отвечает самый младший байт, за PD7 самый старший.

Пример:

void setup(){ 
  DDRB =  0b00100000; // 5 бит соответствует PB5 или D13
                      // 1 - вывод сконфигурирован как выход (0 - вход)
  PORTB = 0b00100000; // 5 бит соответствует PB5 или D13
                      // установить на PB5 HIGH
}
void loop(){}

В данном примере регистр DDR обращается к порту B, лог. 1 в 5 байте переводит вывод в режим выхода, регистр PORT так же обращается к порту B, лог. 1 в 5 байте переводит вывод в HIGH. После загрузки скетча должен засветится светодиод который расположен на плате Arduino.

Показанный выше пример позволяет всего двумя строчками кода сконфигурировать все выходы порта и установить необходимые логические состояния выводов.

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

void setup(){ 
  DDRB = 0B00100000;
  PORTB |= (1 << 5); // см. пример https://rcl-radio.ru/?p=80784
}

void loop(){}

или установить лог. ноль:

void setup(){ 
  DDRB = 0B00100000;
  PORTB &= ~(1 << 5); // см. пример https://rcl-radio.ru/?p=80784
}

void loop(){}

Для чтения состояния порта используется регистр PIN:

void setup(){ 
  Serial.begin(9600);
  DDRB =  0b00111111; // назначить выводы PB0...PB5 как выходы (D8...D13)
  PORTB = 0b00111011; // PB0...PB1 и PB3...PB5 = HIGH, PB2 = LOW
}

void loop(){
  Serial.println(PINB,BIN); // чтение порта и вывод в монитор порта
  delay(1000);
  }

Информация о состоянии порты выводится в монитор порта:

Для чтения одного вывода порта используйте следующий пример:

void setup(){ 
  Serial.begin(9600);
  DDRB =  0b00111111;
  PORTB = 0b00111011;
}

void loop(){
  Serial.println(((PINB >> 5) & 1),BIN); // чтение состояния PB5 
  delay(1000);
  }

Далее переведем все выводы порта в режим чтения:

void setup(){ 
  Serial.begin(9600);
  DDRB =  0b00000000; // все выводы порта в режиме чтения
}

void loop(){
  Serial.println(((PINB >> 0) & 1),BIN); // чтение состояния PB0 (D8)
  delay(1000);
  }

На вход D8 подавать уровни LOW или HIGH, в мониторе порта Вы увидите 0 или 1.

Так же в цифровом входе можно использовать подтягивающий резистор, при этом на входе будет HIGH. Этот режим удобно использовать для подключения кнопки (коммутация кнопки на GND).

void setup(){ 
  Serial.begin(9600);
  DDRB =  0b00000000;// все выводы порта в режиме чтения
  PORTB = 0b00000001;// подключить подтягивающий резистор к выводу PB0 (D8)
}

void loop(){
  Serial.println(((PINB >> 0) & 1),BIN);
  delay(1000);
  }

У Вас может возникнуть вопрос: — «Зачем все это? Ведь существуют такие функции как pinMode(), digitalWrite() и digitalRead(). Эти функции понятны и просты в применении.»

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

Далее приведу несколько примеров использования регистров портов, в которых будет сравнение с использованием стандартных для Arduino функций.

Мигание светодиодом D13 (Blink)

void setup() {
  pinMode(13,OUTPUT);
}

void loop() {
  digitalWrite(13,HIGH);
  delay(1000);
  digitalWrite(13,LOW);
  delay(1000);
}

Скетч использует 924 байт (2%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 9 байт (0%) динамической памяти, оставляя 2039 байт для локальных переменных. Максимум: 2048 байт.

void setup(){ 
  DDRB =  0B00100000;
}

void loop(){
  PORTB |= (1 << 5);
  delay(1000);
  PORTB &= ~(1 << 5);
  delay(1000);
}

Скетч использует 642 байт (1%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 9 байт (0%) динамической памяти, оставляя 2039 байт для локальных переменных. Максимум: 2048 байт.

Оба скетча выполняют одно и тоже действие, второй скетч занимает меньше памяти. А если Вам не важна точность задержки функции delay(), то можно применить _delay_ms() и использование памяти микроконтроллера станет еще меньше:

void setup(){ 
  DDRB = 0b00100000;
}

void loop(){
 PORTB |= (1 << 5);
 _delay_ms(1000);
 PORTB &= ~(1 << 5);
 _delay_ms(1000);
}

Скетч использует 488 байт (1%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 9 байт (0%) динамической памяти, оставляя 2039 байт для локальных переменных. Максимум: 2048 байт.

Скорость переключения выхода D13

void setup() {
  Serial.begin(9600);
  pinMode(13,OUTPUT);
}

void loop() {
  unsigned long times = micros();
  for(int i=0;i<100;i++){
  digitalWrite(13,HIGH);
  digitalWrite(13,LOW);
  }
  Serial.println(micros()-times);
  delay(1000);
}

Скетч использует 2104 байт (6%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 188 байт (9%) динамической памяти, оставляя 1860 байт для локальных переменных. Максимум: 2048 байт.

void setup(){ 
  Serial.begin(9600);
  DDRB =  0B00100000;
}

void loop(){
  unsigned long times = micros();
  for(int i=0;i<100;i++){
  PORTB |= (1 << 5);
  PORTB &= ~(1 << 5);
  }
  Serial.println(micros()-times);
  delay(1000);
}

Скетч использует 1820 байт (5%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 188 байт (9%) динамической памяти, оставляя 1860 байт для локальных переменных. Максимум: 2048 байт.

Как видно на скриншотах, переключение цифрового выхода D13 100 раз занимает в среднем 680 мкс, тот же процесс во втором скетче занимает  в среднем 54 мкс.

Аналогичная ситуация и при чтении выхода:

void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
}

void loop() {
  unsigned long times = micros();
  int x = 0;
  for (int i = 0; i < 1000; i++) {
    if (digitalRead(13) == 1) {
      x++;
    };
  }
  Serial.println(micros() - times);
  Serial.println(x);
  delay(1000);
}

Скетч использует 2242 байт (6%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 188 байт (9%) динамической памяти, оставляя 1860 байт для локальных переменных. Максимум: 2048 байт.

1000 — кол-во измерений, 2576 — затраченное время в мкс

void setup() {
  Serial.begin(9600);
  DDRB =  0B00111111;
  PORTB |= (1 << 5);
}

void loop() {
  unsigned long times = micros();
  int x = 0;
  for (int i = 0; i < 1000; i++) {
    if (((PINB >> 5) & 1) == 1) {
      x++;
    }
  }
  Serial.println(micros() - times);
  Serial.println(x);
  delay(1000);
}

Скетч использует 1906 байт (5%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 188 байт (9%) динамической памяти, оставляя 1860 байт для локальных переменных. Максимум: 2048 байт.

1000 — кол-во измерений, 444 — затраченное время в мкс

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

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

Случайные статьи

  • Радиомикрофон

    Схема радиомикрофона показана на рисунке. Радиомикрофон работает на частоте 87,9 МГц и представляет собой автогенератор с емкостной обратной связью и дополнительной емкостью С4 в индуктивной ветви (схема Клаппа). Дополнительная емкость необходима, во-первых, для развязки по постоянному току цепей питания и смещения. Во2вторых, она обеспечивает дополнительную степень свободы для получения оптимального …Подробнее...
  • HEF4051B — увеличение количества аналоговых входов Arduino

    HEF4051B — увеличение количества аналоговых входов Arduino

    ИМС HEF4051B (4051) представляет собой 8 канальный аналоговый CMOS мультиплексор/демультиплексор. HEF4051B можно применить для увеличения числа аналоговых входов Arduino, для этого будет задействован одни аналоговый вход и три цифровых входа Arduino. #include <HEF4051.h> // http://forum.rcl-radio.ru/misc.php?action=pan_download&item=748&download=1 HEF4051 hef(A0,2,3,4);// analog_input A0, D2,D3,D4 void setup() {  Serial.begin(9600); } void loop() {  for(byte i=0;i<8;i++){ …Подробнее...
  • Два простых УМЗЧ

    Два простых УМЗЧ

    Характеристики Номинальная чувствительность 0,35В Номинальная выходная мощность при нагрузке 4 Ом — 10Вт Диапазон воспроизводимых частот от 40 до 20000 Гц Скорость нарастания выходного напряжения 25В\мкс Коэф. нелинейных искажений по всем диапазоне 0,35% Напряжение питания 11…16В Первый каскад на VT1 работает в усилителе напряжения, а остальные VT2-VT5 образуют эмиттерный повторитель …Подробнее...
  • Однофазная мостовая схема выпрямления

    Однофазная мостовая схема выпрямления

    Схема мостового выпрямителя показана на рисунке а), которая состоит из двухобмоточного трансформатора, четырех диодов включенных по схеме моста и нагрузки выпрямителей Rн. К одной диагонали моста подключена вторичная обмотка трансформатора, к другой нагрузка. В схеме четыре диода соединены так, что напряжение вторичной обмотки трансформатора подается на нагрузку в течении одного …Подробнее...
  • Регулятор температуры воды

    Регулятор предназначен для регулировки температуры воды в аквариуме или в другой емкости в диапазоне 10-40°С. В основе уст-ва интегральный компаратор напряжения К553СА3. Эта микросхема имеет мощный выход, достаточный для подключения реле. Принцип действия схемы: На прямом входе (вывод3) компаратора создается образцовое напряжение при помощи делителя R1\R2. Это напряжение можно менять …Подробнее...