Эквивалент PORTB в Arduino Mega 2560

Как я уже говорил в теме, хотелось бы узнать аналог инструкции:

PORTB |= 0x01;
PORTB &= ~0x01;

Точнее, я использовал его для запуска быстрого преобразования импульсов на моем шилд-АЦП на Arduino Uno. Сейчас я использую Arduino Mega 2560.

Вы можете написать точно такую же инструкцию для ATMega2560?

, 👍4


2 ответа


Лучший ответ:

4

Этот код посылает импульс на контакт PB0, который на Uno эквивалентен цифровой 8. На Mega цифровая 8 — это PH5, поэтому вы должны написать

PORTH |= _BV(PH5);
PORTH &= ~_BV(PH5);

Эквивалентность контактов Arduino и AVR представлена в официальный документации, но на pighixxx.com есть более приятные версии:

  • Распиновка Uno
  • Мега распиновка
,

4

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

В этом более переносимом методе используются некоторые стандартные (хотя и недостаточно документированные на arduino.cc) функции для получения номеров портов, указателей портов и битовых масок по номеру контакта. Эти функции – digitalPinToPort(), portInputRegister() и digitalPinToBitMask() — может использоваться во время выполнения для вычисления физических портов и контактов на основе номеров контактов Arduino. В некоторых случаях вычисления могут происходить во время компиляции, если компилятор достаточно умен или ему разрешено оптимизировать. Но avr-gcc, настроенный для Arduino IDE, не разрешен, что является разумным значением по умолчанию для встроенного программирования.

В этом методе также используется тот факт, что вывод AVR, как и в большинстве Arduino на базе Atmel, будет переключаться, когда вы записываете 1 в его бит во входном регистре. Например, см. §14.1 в Atmel doc8271, техническое описание ATmega48A/PA/88A/PA/168A/PA/328/P, в котором говорится:

Для каждого порта выделяются три адреса памяти ввода-вывода, по одному для регистра данных — PORTx, регистра направления данных — DDRx и входных контактов порта — PINx. Место ввода/вывода портов входных контактов доступно только для чтения, в то время как регистр данных и регистр направления данных доступны для чтения/записи. Однако запись логической единицы в бит в регистре PINx приведет к переключению соответствующего бита в регистре данных. ...


Пример программы

/*  jiw - Jan 2017 - toggle Arduino digital pin 8 several ways
Re: http://arduino.stackexchange.com/questions/33033/equivalent-for-portb-in-arduino-mega-2560 */
volatile uint8_t *portofpin;
uint8_t bitofpin;
void setup() {
  pinMode(8, OUTPUT);       // Устанавливаем пин как выход
  portofpin = portInputRegister(digitalPinToPort(8));
  bitofpin = digitalPinToBitMask(8);
}
void udelay() {
  delayMicroseconds(5);
}
void loop() {
  PORTB |= 0x01;        // Установить 8-й контакт Uno
  PORTB &= ~0x01;       // Очистить вывод 8 Uno
  udelay();
  PINB = 1;         // Переключить контакт 8 Uno
  PINB = 1;         // Переключить контакт 8 Uno
  udelay();
  PINB = bitofpin;      // Переключить контакт 8 Uno
  PINB = bitofpin;      // Переключить контакт 8 Uno
  udelay();
  *portofpin = bitofpin;    // Переключить контакт 8 Arduino
  *portofpin = bitofpin;    // Переключить контакт 8 Arduino
  udelay();
}

Подборка на Uno

loop(), из компиляции примера программы для платы Uno:

void loop() {
  PORTB |= 0x01;                // Установить 8-й контакт Uno
 10e:   28 9a           sbi     0x05, 0 ; 5
  PORTB &= ~0x01;               // Очистить вывод 8 Uno
 110:   28 98           cbi     0x05, 0 ; 5
  udelay();
 112:   0e 94 83 00     call    0x106   ; 0x106 <_Z6udelayv>
  PINB = 1;                     // Переключить контакт 8 Uno
 116:   81 e0           ldi     r24, 0x01       ; 1
 118:   83 b9           out     0x03, r24       ; 3
  PINB = 1;                     // Переключить контакт 8 Uno
 11a:   83 b9           out     0x03, r24       ; 3
  udelay();
 11c:   0e 94 83 00     call    0x106   ; 0x106 <_Z6udelayv>
  PINB = bitofpin;              // Переключить контакт 8 Uno
 120:   80 91 00 01     lds     r24, 0x0100
 124:   83 b9           out     0x03, r24       ; 3
  PINB = bitofpin;              // Переключить контакт 8 Uno
 126:   80 91 00 01     lds     r24, 0x0100
 12a:   83 b9           out     0x03, r24       ; 3
  udelay();
 12c:   0e 94 83 00     call    0x106   ; 0x106 <_Z6udelayv>
  *portofpin = bitofpin;        // Переключить контакт 8 Arduino
 130:   e0 91 01 01     lds     r30, 0x0101
 134:   f0 91 02 01     lds     r31, 0x0102
 138:   80 91 00 01     lds     r24, 0x0100
 13c:   80 83           st      Z, r24
  *portofpin = bitofpin;        // Переключить контакт 8 Arduino
 13e:   e0 91 01 01     lds     r30, 0x0101
 142:   f0 91 02 01     lds     r31, 0x0102
 146:   80 91 00 01     lds     r24, 0x0100
 14a:   80 83           st      Z, r24
  udelay();
 14c:   0c 94 83 00     jmp     0x106   ; 0x106 <_Z6udelayv>

Как видно из приведенного выше, вместо использования последовательностей чтения/изменения/записи для реализации PORTB |= 0x01; PORTB &= ~0x01;, компилятор выдал sbi 0x05,0; cbi 0x05,0;, чтобы напрямую установить, а затем очистить бит 0 регистра 5, PORTB. Это использует всего 4 такта, создавая импульс, высокий для 2 циклов.

avr-gcc реализован PINB = 1; PINB = 1; через ldi r24, 1; вых 3,г24; out 3,r24, что занимает в общей сложности 3 такта и создает импульс, высокий для 1 цикла.

Он реализовал PINB = bitofpin; PINB = bitofpin; через lds r24, 0x0100; вых 3,г24; lds r24, 0x0100; out 3,r24, что занимает в общей сложности 6 циклов и создает импульс с высоким уровнем в течение 3 циклов. (В setup() значение bitofpin хранилось в ОЗУ по адресу 0x100, а значение portofpin сохранялось по адресам 0x101, 0x102. )

Наконец, он реализовал *portofpin = bitofpin; *portofpin = bitofpin; с помощью последовательности из шести инструкций, которая занимает 14 циклов и создает импульс с высоким уровнем в течение 7 циклов. Очевидно, что компилятор мог создать последовательность из 4 инструкций (не перезагружая r30, r31 и r24), которая занимает 8 тактов и имеет высокий импульс для 1 такта, но этого не произошло.


Подборка на Меге

loop(), из примера программы для платы Mega или Mega 2560:

void loop() {
  PORTB |= 0x01;                // Установить 8-й контакт Uno
 218:   28 9a           sbi     0x05, 0 ; 5
  PORTB &= ~0x01;               // Очистить вывод 8 Uno
 21a:   28 98           cbi     0x05, 0 ; 5
  udelay();
 21c:   fa df           rcall   .-12            ; 0x212 <_Z6udelayv>
 21e:   81 e0           ldi     r24, 0x01       ; 1
  PINB = 1;                     // Переключить контакт 8 Uno
 220:   83 b9           out     0x03, r24       ; 3
 222:   83 b9           out     0x03, r24       ; 3
  PINB = 1;                     // Переключить контакт 8 Uno
 224:   f6 df           rcall   .-20            ; 0x212 <_Z6udelayv>
  udelay();
 226:   80 91 00 02     lds     r24, 0x0200
  PINB = bitofpin;              // Переключить контакт 8 Uno
 22a:   83 b9           out     0x03, r24       ; 3
 22c:   80 91 00 02     lds     r24, 0x0200
  PINB = bitofpin;              // Переключить контакт 8 Uno
 230:   83 b9           out     0x03, r24       ; 3
 232:   ef df           rcall   .-34            ; 0x212 <_Z6udelayv>
 234:   e0 91 01 02     lds     r30, 0x0201
  udelay();
 238:   f0 91 02 02     lds     r31, 0x0202
  *portofpin = bitofpin;        // Переключить контакт 8 Arduino
 23c:   80 91 00 02     lds     r24, 0x0200
 240:   80 83           st      Z, r24
 242:   e0 91 01 02     lds     r30, 0x0201
 246:   f0 91 02 02     lds     r31, 0x0202
  *portofpin = bitofpin;        // Переключить контакт 8 Arduino
 24a:   80 91 00 02     lds     r24, 0x0200
 24e:   80 83           st      Z, r24
 250:   e0 cf           rjmp    .-64            ; 0x212 <_Z6udelayv>

Ассемблёрный код для Mega почти такой же, как и для Uno, за исключением того, что значения bitofpin и portofpin хранились в ОЗУ с адресами 0x200–0x202 вместо 0x100–0x102. , а для вызова udelay(); использовался rcall или rjmp вместо call или jmp .


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

Чтобы создать файлы со списком сборок для программы, используйте команду типа

avr-objdump -CSI$PWD $TMPPATH/togglepin8.cpp.elf > togglepin8.ino.mega.asm

где $PWD представляет текущий рабочий каталог, содержащий скетч, такой как togglepin8.ino, а $TMPPATH представляет собой любой временный каталог IDE. вставил исходник, чтобы скомпилировать и связать его. Переключатель C (не используется в приведенных выше листингах) сообщает avr-objdump, что нужно расшифровать символы. Коммутатор S говорит отображать исходный код, смешанный с дизассемблированием, если это возможно. Переключатель I указывает местоположение исходного кода.


Условная компиляция

Условная компиляция — это еще один способ сделать исходный код переносимым на несколько Arduino. Например, в начале скетча можно включить следующие операторы #ifdef и #define для настройки BITOFPIN и PORTOFPIN. значения для последующего использования:

#ifdef ARDUINO_AVR_UNO
#define BITOFPIN  1
#define PORTOFPIN PINB
#endif
#ifdef ARDUINO_AVR_MEGA2560
#define BITOFPIN  _BV(PH5)
#define PORTOFPIN PINH
#endif

Чтобы узнать, какие символы, зависящие от платы, определены во время компиляции, включите подробный вывод во время компиляции и найдите флаги вида -Dsym=val.

,