Параллельный ввод-вывод - возможно, использовать вызов функции машинного кода

Я новичок в arduino, но много программировал. У меня есть проект, в котором я хотел бы вывести 4 бита на соседние контакты и прочитать 4 бита с других (4) соседних контактов. (Представьте себе приложение для матричных клавиатур). Чтобы сделать это на C-C++, потребуется много инструкций (и времени), но машинный код

ВЫХОД: ИЛИ (маска) -> выходной порт ; это очищает целевые биты, оставляя другие биты такими, какими они были И (newdata) -> порт вывода ; это устанавливает новые данные в целевые биты>

ВВОД: порт -> регистрация И (регистрация) (маска) -> регистрация>

Есть ли способ реализовать это, например, в виде вызова из IDE. Я сталкивался со многими системами, в которых многоязычные компоненты могут быть скомпилированы в один исполняемый файл.

Если это обычно не делается, то почему бы и нет? Это что-то, чего мне следует избегать по какой-то причине?

Спасибо Дейв

, 👍0

Обсуждение

Смотрит на регистры портов и контактов (при условии, что AVR). Ими можно управлять непосредственно с C или C++, сборка не требуется., @Mat

Если у вас есть AVR Arduino (например, Uno, Nano, ...) [это](https://www.arduino.cc/en/Reference/PortManipulation) обязательно прочтите этот раздел в документации., @chrisl

Что вы подразумеваете под "_a звонком из IDE_"? Возможно, вы захотите пройти несколько уроков, чтобы изучить основы и технические термины. Я уверен, что есть примеры матричных клавиатур., @the busybee

Спасибо Мэту и Крису. Отличная информация. Юрай, я использую "клон" UNO, но хочу, чтобы эта штука работала эффективно, сводя программу к минимуму. Назойливый, Извини. Я знаю, что IDE-неправильный термин, но хотел, чтобы вопрос был как можно более элементарным., @Dave

Если речь идет о механических кнопках с ручным запуском, лучше позаботьтесь о том, чтобы быть достаточно медленным, чтобы избавиться от отскакивающих эффектов. Пока вы ждете несколько мс, пока кнопка не установится, не имеет значения, как вы читаете состояния кнопки., @DataFiddler


1 ответ


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

1

Вы можете управлять портами ввода-вывода непосредственно из C++, не прибегая к ассемблерному коду.

Микроконтроллер AVR в вашем Arduino (ATmega328P) имеет три порта (PORTB, PORTC и PORTD) с восемью выводами каждый (Px0, Px1, ... Px7). Вы управляете портами с помощью трех 8-разрядных регистров. Каждый бит в регистре представляет один физический вывод (LSB-это вывод 0, MSB-вывод 7):

  • DDRx: регистр направления данных. Бит, установленный на "1", соответствует режиму вывода, "0" - входу.
  • PORTx: регистр данных. В режиме вывода "1" устанавливает ВЫСОКИЙ уровень порта, "0" - НИЗКИЙ. В режиме ввода определяет, включены ли подтягивания ("1") или нет ("0").
  • PINx: регистр ввода и переключения. Прочитайте его, чтобы получить статус всех восьми контактов порта одновременно. Запись в него переключает (НИЗКИЙ -> ВЫСОКИЙ или ВЫСОКИЙ ->> НИЗКИЙ) выбранные контакты (например, >>PINC = 0b00100001; переключает контакты PC0 и PC5).

С Arduino у вас нет доступа ко всем выводам всех портов. Чтобы получить сопоставление между именами выводов и портами Arduino, посмотрите любую хорошую ссылку на вывод Arduino (например, на странице Arduino Uno на вкладке "Документация"). Например, вы можете видеть, что все контакты порта D доступны в качестве цифровых контактов Arduino от D0 до D7, например, в Uno. Аналоговые контакты от PORTC.

Arduino Uno Rev 3 pinout

Макросы предоставляются (с помощью набора инструментов AVR) для имен портов и номеров контактов, чтобы упростить задачу. Например, PORTB-это псевдоним для регистра PORTB, который вы можете присвоить ему, как если бы это была обычная переменная C++. PB5-это индекс вывода 5 в порту B. Поэтому настройка вывода 5 порта B на вывод без влияния на остальные может быть записана следующим образом:

DDRB |= (1 << PB5);

Если вы хотите установить 4 нижних вывода порта D для вывода, а 4 других для ввода, вы можете:

DDRD = 0b00001111; // or 0x0f

Вот пример минимального скетча мигания:

void setup() {
  // Set PB5 (D13 - builtin led) LOW
  PORTB &= ~(1 << PB5);
  // Set PB5 mode to OUTPUT
  DDRB |= (1 << PB5);
}

void loop() {
  delay(1000);
  // переключение PB5
  PINB |= (1 << PB5);
}

Сгенерированная сборка является оптимальной, отдельные инструкции для установки или очистки немного в памяти ввода-вывода: (Для получения информации см. Раздел Выходные данные скомпилированной сборки (Визуальный микро))

// настройка
  PORTB &= ~(1 << PB5);
 1f4:   2d 98           cbi     0x05, 5 ; 5
  DDRB |= (1 << PB5);
 1f6:   25 9a           sbi     0x04, 5 ; 4

// цикл
  PINB |= (1 << PB5);
 246:   1d 9a           sbi     0x03, 5 ; 3

Другой пример, который мигает светодиодом, подключенным к A3, только когда A2 находится на высоком уровне:

void setup() {
  // Установите НИЗКИЕ значения PC2 и PC3 (A2 и A3)
  PORTC &= ~((1 << PC2) | (1 << PC3));
  // Установите режим PC2 на вход, режим PC3 на выход
  DDRC = (DDRC & ~(1 << PC2)) | (1 << PC3);
}

void loop() {
  delay(1000);
  // переключать PC3 только в том случае, если значение PC2 велико
  if (PINC & (1 << PC2)) {
    PINC = (1 << PC3);
  }
}

Опять же, сгенерированная сборка довольно лаконична:

  if (PINC & (1 << PC2)) {
    PINC = (1 << PC3);
 202:   18 e0           ldi     r17, 0x08       ; 8
 252:   32 99           sbic    0x06, 2 ; 6
 254:   16 b9           out     0x06, r17       ; 6

(Инструкция SBIC забавна: пропустите следующую инструкцию, если немного в памяти ввода-вывода чисто.)

Вы даже можете сохранить использование этого регистра r17 с помощью PINC |= ... в этом случае:

  // переключать PC3 только в том случае, если PC2 высокий
  if (PINC & (1 << PC2)) {
    PINC |= (1 << PC3);
 250:   32 99           sbic    0x06, 2 ; 6
 252:   33 9a           sbi     0x06, 3 ; 6

Конечно, вы также можете прибегнуть к встроенной сборке, но это само по себе искусство, а синтаксис немного своеобразен. Но для простых вещей это просто:

asm("sbi 0x03, 5;"); // переключить PB5

Некоторые ссылки:

  • Данные ATmega328P, связанные здесь: Страница микрочипа ATmega328P. См. раздел 14 "Порты ввода-вывода" и 36 "Краткое описание регистрации" для адресов портов ввода-вывода.
  • Руководство по набору инструкций AVR (PDF), если вы хотите погрузиться в это.
  • Кулинарная книга встроенного ассемблера AVR-GCC для получения подробной информации об использовании встроенного ассемблера GCC для AVR
,