| Ваш IP: 3.236.132.132 | Online(30) - гости: 24, боты: 6 | Загрузка сервера: 0.59 ::::::::::::


Регистры портов (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 — затраченное время в мкс

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

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

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

  • Удвоитель напряжения на микросхеме NE555

    Описание. Схема простейшего удвоителя напряжения использованием микросхемы NE555 показана на рисунке. Здесь IC NE555 подключен в схеме как нестабильный мультивибратор с частотой генерации около 9KHz. Базы двух транзисторов (Q1 и Q2) подключены напрямую к выходу мультивибратора (контакт 3). При выходе сигнала из мультивибратора в первый момент Q1 будет OFF и …Подробнее...
  • Преобразователь =12В\ 220В

    Преобразователь =12В\ 220В

    Схема преобразователя состоит из 3-х узлов: задающего мультивибратора, двухтактного транзисторного ключевого усилителя и повышающего трансформатора. Мультивибратор выполнен на микросхеме D1(D1.1  D1.2). Его частота зависит от R1 C1. На выходе мультивибратора включен инвертор на D1.4 который создает противофазные сигналы поступающие на базы VT1 VT2. Затем следует двухтактный усилитель на VT3 VT4 …Подробнее...
  • Термопары

    Термопары

    Термопара (термоэлектрический преобразователь) — устройство, применяемое для измерения температуры в промышленности, научных исследованиях, медицине, в системах автоматики. Принцип действия основан на эффекте Зеебека или, иначе, термоэлектрическом эффекте. Между соединёнными проводниками имеется контактная разность потенциалов; если стыки связанных в кольцо проводников находятся при одинаковой температуре, сумма таких разностей потенциалов равна нулю. …Подробнее...
  • TDA7499 — УМЗЧ 2х6 Вт

    TDA7499 — УМЗЧ 2х6 Вт

    TDA7499 — усилитель мощности звуковой частоты (класс АВ), с выходной мощность 6 Вт на канал (стерео). Усилитель относиться к классу Hi-Fi аппаратуры. В усилителе имеются функции STANDBY и MUTE, беззвучное включение и выключение (отсутствие щелчков), защита выхода от КЗ на землю, тепловая защита и защита от перегрузки. ИМС TDA7499 может …Подробнее...
  • Логический пробник для ТТЛ и ТТЛШ

    Схема отличается высокой точностью и возможностью контроля логических уровней «1» и «0», К3 и «Не определено». При неподключенном входе пробника светится светодиод «Не определено». Резисторы R1.R4 желательно применить с допуском 1%. ОУ любые, со своими частотными коррекциями, важно только, чтобы выходной ток был не менее 15 мА и Rвх не …Подробнее...