Эквивалент PORTB в Arduino Mega 2560
Как я уже говорил в теме, хотелось бы узнать аналог инструкции:
PORTB |= 0x01;
PORTB &= ~0x01;
Точнее, я использовал его для запуска быстрого преобразования импульсов на моем шилд-АЦП на Arduino Uno. Сейчас я использую Arduino Mega 2560.
Вы можете написать точно такую же инструкцию для ATMega2560?
2 ответа
Лучший ответ:
Этот код посылает импульс на контакт PB0, который на Uno эквивалентен цифровой 8. На Mega цифровая 8 — это PH5, поэтому вы должны написать
PORTH |= _BV(PH5);
PORTH &= ~_BV(PH5);
Эквивалентность контактов Arduino и AVR представлена в официальный документации, но на pighixxx.com есть более приятные версии:
- Распиновка Uno
- Мега распиновка
Другой ответ предоставил правильную информацию и полезную документацию по заданному вопросу. В этом ответе используется немного более портативный метод создания короткого импульса на данном цифровом выводе.
В этом более переносимом методе используются некоторые стандартные (хотя и недостаточно документированные на 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
.
- Выводы прерываний Arduino Mega 2560 и отображение портов с помощью поворотного энкодера
- Допустимые параметры выходного порта на Mega 2560
- Не использовать прерывание восходящего края для внешних часов Arduino 2650
- Как разделить входящую строку?
- Как использовать SPI на Arduino?
- Как сбросить или отформатировать Arduino?
- Управление скоростью вентилятора с помощью библиотеки Arduino PID
- Arduino Due vs Mega 2560