Почему мы используем побитовые операторы для назначения PORTx, DDRx и Pinx?
Я наткнулся на множество примеров использования способов, подобных показанным ниже, для присвоения значений контактам.
PORTB |= (1<<PORTB2); //установить бит 2
PORTB &= ~(1<<PORTB1); //очистить бит 1
DDRD = DDRD | Б11111100; //Настраиваем выводы для вывода
Мой вопрос: зачем нам нужно использовать побитовые операции для выполнения присваиваний?
@ArduinoSap, 👍2
Обсуждение4 ответа
Лучший ответ:
Мой вопрос: зачем нам нужно использовать побитовые операции для выполнения присваиваний?
Поскольку вы не назначаете, вы изменяете.
Если вы хотите включить только контакт 3 (например), вам нужно управлять только битом 3 порта PORTB и оставить остальные в покое.
Это означает, что вам нужно "или" значение только бита 3, включенного с существующим PORTB, чтобы включить бит, или И его с обратным (все контакты, кроме бита 3), чтобы отключить его.
Чтобы получить значение бита 3, вы можете вспомнить, что это 4
, или вы можете просто использовать 1<<3
, чтобы сказать "Бит 3" - или используйте макрос PORTB3
, чтобы сделать его более читабельным, а не просто цифру 3.
Эта операция широко известна как "Чтение, изменение, запись". и в некоторых младших микроконтроллерах вы должны соблюдать особую осторожность при этом.
Например, если вы хотите управлять выходным контактом 3 и в настоящее время у вас есть контакты 2, 5 и 6, а остальные контакты 0-7 отключены (это те, которые подключены к порту B на Uno), последовательность может быть таким:
Turn on:
Calculate bit value 1<<3 => 0b00001000
Read PORTB => 0b01100100
OR the two values together => 0b01101100
Write to PORTB <= 0b01101100
Turn off:
Calculate bit value 1<<3 => 0b00001000
Invert it => 0b11110111
Read PORTB => 0b01101100
AND the two values together => 0b01100100
Write to PORTB <= 0b01100100
Не могли бы вы привести примеры в свой ответ, как вы пишете (ссылаетесь) на них? Я узнал из вашего ответа и ответа &Gerbens, но было бы легче понять, если бы ссылка и пример были видны одновременно. Спасибо, @Tomas
@ Томас Тебе это помогает?, @Majenko
Спасибо. Это просто стало одним из постов, которые нужно прочитать при изучении Arduino., @Tomas
Возможно, стоит отметить, что некоторые чипы (включая AVR) имеют специальные инструкции «установить бит», и, по крайней мере, gcc-avr распознает эту идиому и использует одну инструкцию., @chrylis -cautiouslyoptimistic-
Регистры имеют размер 8 бит. И в приведенных выше двух случаях вы хотите установить/очистить 1 бит, оставив остальные без изменений.
В последнем примере вы хотите настроить контакты D2-D7 на выход, оставив контакты D0 и D1 без изменений.
Это упрощает написание и повторное использование кода.
Допустим, вашей программе нужно сделать 3 не связанных друг с другом действия на разных выводах. Управляйте двигателем, вспыхивайте светом, считайте несколько входных импульсов.
Поскольку этот побитовый метод позволяет вам читать/изменять только те биты, которые вам нужны, и не трогать остальные, вы можете полностью разделить код для трех задач. Его даже можно написать в отдельных файлах разными людьми, не зная, для чего используются другие пины в проекте. Код для одной из трех задач можно скопировать и вставить в отдельный проект без необходимости отделять его от проекта 1 и вносить серьезные изменения в проект 2. На жаргоне разработчиков программного обеспечения это называется сделать код «более удобным в сопровождении»; а также "модульный"
Если вы хотите изменить все контакты порта одновременно, вам не нужно этого делать.
Дело не только в том, что вы хотите установить/очистить нужные вам выводы, но и в том, что вы хотите не изменять состояние других булавок. Для этого вам нужно изменить только биты для контактов, которые будут изменены, и оставить все остальные биты как есть. Единственный способ сделать это — использовать побитовые операторы.
Но если вы хотите изменить все контакты сразу, вы можете просто установить значение в регистре порта. Вывод значения на 8-битной шине был бы классическим случаем, когда вы делаете это.
Однако в вашем вопросе мне нужно уточнить один момент. Во многих случаях читать состояние порта, изменять биты и записывать значение обратно — плохая идея. Причина в том, что почти все микроконтроллеры используют один и тот же адрес порта для установки состояния вывода и чтения состояния ввода. Таким образом, считывая состояние порта, вы не обязательно считываете фактические выходные данные, которые вы установили. Выходы с высоким логическим уровнем, как правило, слабее, чем с низким логическим уровнем, поэтому подключенная электроника вполне может получить высокий логический уровень. Последовательность ошибок для ошибки следующая: -
- Ваша программа устанавливает бит (pin) в 1.
- Другой компонент переводит этот контакт в низкий логический уровень.
- В следующий раз, когда вы прочитаете этот порт обратно, чтобы изменить другой вывод, ввод для вывода будет равен 0.
- Вы изменяете значение вывода, которую намеревались изменить.
- Вы записываете значение обратно. Теперь изменилось состояние не только предполагаемого вывода, но и выходных данных.
- Другой компонент перестает тянуть штифт вниз. Но ваш микроконтроллер продолжает выводить 0 (низкий логический уровень) для этого вывода.
Решение состоит в том, что вы не выполняете побитовые операции непосредственно с регистром. Вместо этого вы держите «тень». переменная, содержащая состояние, в котором вы хотите, чтобы выходные данные были. Вы выполняете все свои побитовые операции с этой теневой переменной, а затем записываете теневую переменную непосредственно в регистр. И ошибка исчезнет.
- Миграция проекта Arduino Uno R3 в Wemos D1 R2 — проблемы с распиновкой
- Почему некоторые контакты Arduino Nano (D3, D4, A3, A4, A6, A7) не могут быть установлены на высокий уровень?
- Какой аналог PORTx для Teensy (4.0)?
- Как настроить контакт как двунаправленный и с открытым коллектором?
- Могу ли я отключить контакт TX, установив для него режим INPUT, при этом получая данные на контакт RX UART?
- Сопоставление выводов платы Arduino Metro M4 Express с платой SAMD51
- ATMega328 работает, но не выводит ни на какие контакты
- Одновременное переключение двух и более цифровых выходов ESP8266
У вас есть «установить бит 2» в комментарии, а не «установить бит 2 и очистить все остальные»., @KIIV
@KIIV, который является побитовым оператором ИЛИ ... он не может очищать биты, @jsotola
@jsotola точно моя точка зрения.
PORTB |= (1<<PORTB2); // устанавливаем бит 2, а остальные оставляем без изменений
иPORTB = (1<<PORTB2); //установить бит 2 и очистить все остальное
., @KIIVСм. [Руководство пользователя LPC3250] (https://www.nxp.com/docs/en/user-guide/UM10326.pdf) на стр. 64 (Таблица 20. Регистр управления PLL HCLK (HCLKPLL_CTRL — 0x4000 4058) ). А теперь скажите мне, как бы вы перевели PLL в режим отключения питания, а затем снова включили его, ничего больше не меняя? :) Вам нужно изменить только бит 16, в 32-битном регистре. В языке C нет явного указания «Установить/изменить/прочитать только этот конкретный бит» (хотя вы можете делать кое-что интересное с маской элементов структуры/бита с «битовыми полями»), поэтому вам нужно проявить творческий подход и использовать доступные операции. изменить подмножество битов., @Gizmo