Почему мы используем побитовые операторы для назначения PORTx, DDRx и Pinx?

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

PORTB |= (1<<PORTB2); //установить бит 2

PORTB &= ~(1<<PORTB1); //очистить бит 1

DDRD = DDRD | Б11111100; //Настраиваем выводы для вывода

Мой вопрос: зачем нам нужно использовать побитовые операции для выполнения присваиваний?

, 👍2

Обсуждение

У вас есть «установить бит 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


4 ответа


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

7

Мой вопрос: зачем нам нужно использовать побитовые операции для выполнения присваиваний?

Поскольку вы не назначаете, вы изменяете.

Если вы хотите включить только контакт 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-


0

Регистры имеют размер 8 бит. И в приведенных выше двух случаях вы хотите установить/очистить 1 бит, оставив остальные без изменений.

В последнем примере вы хотите настроить контакты D2-D7 на выход, оставив контакты D0 и D1 без изменений.

,

0

Это упрощает написание и повторное использование кода.

Допустим, вашей программе нужно сделать 3 не связанных друг с другом действия на разных выводах. Управляйте двигателем, вспыхивайте светом, считайте несколько входных импульсов.

Поскольку этот побитовый метод позволяет вам читать/изменять только те биты, которые вам нужны, и не трогать остальные, вы можете полностью разделить код для трех задач. Его даже можно написать в отдельных файлах разными людьми, не зная, для чего используются другие пины в проекте. Код для одной из трех задач можно скопировать и вставить в отдельный проект без необходимости отделять его от проекта 1 и вносить серьезные изменения в проект 2. На жаргоне разработчиков программного обеспечения это называется сделать код «более удобным в сопровождении»; а также "модульный"

,

1

Если вы хотите изменить все контакты порта одновременно, вам не нужно этого делать.

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

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

Однако в вашем вопросе мне нужно уточнить один момент. Во многих случаях читать состояние порта, изменять биты и записывать значение обратно — плохая идея. Причина в том, что почти все микроконтроллеры используют один и тот же адрес порта для установки состояния вывода и чтения состояния ввода. Таким образом, считывая состояние порта, вы не обязательно считываете фактические выходные данные, которые вы установили. Выходы с высоким логическим уровнем, как правило, слабее, чем с низким логическим уровнем, поэтому подключенная электроника вполне может получить высокий логический уровень. Последовательность ошибок для ошибки следующая: -

  • Ваша программа устанавливает бит (pin) в 1.
  • Другой компонент переводит этот контакт в низкий логический уровень.
  • В следующий раз, когда вы прочитаете этот порт обратно, чтобы изменить другой вывод, ввод для вывода будет равен 0.
  • Вы изменяете значение вывода, которую намеревались изменить.
  • Вы записываете значение обратно. Теперь изменилось состояние не только предполагаемого вывода, но и выходных данных.
  • Другой компонент перестает тянуть штифт вниз. Но ваш микроконтроллер продолжает выводить 0 (низкий логический уровень) для этого вывода.

Решение состоит в том, что вы не выполняете побитовые операции непосредственно с регистром. Вместо этого вы держите «тень». переменная, содержащая состояние, в котором вы хотите, чтобы выходные данные были. Вы выполняете все свои побитовые операции с этой теневой переменной, а затем записываете теневую переменную непосредственно в регистр. И ошибка исчезнет.

,