Запись на контакты порта без воздействия на другие контакты этого порта.

Я вывожу 6-битное число на PORTA (RA0-RA5), как мне записать эти биты, не испортив то, что уже есть на RA6 и RA7?

Например, если я хочу вывести 0x3F на биты RA0-RA5. Если я использую PORTA = 0x3F;это, будет выведено 00111111, которое эффективно стирает все, что уже было записано в биты RA6 и RA7.

Я пишу на C с помощью MikroC на PIC18.

Составные операторы присваивания — ваши друзья: &=, |=, ^=— это все операторы, с которыми вы должны быть хорошо знакомы при разработке встраиваемых систем.
Чтение-изменение-запись.
Используйте однобитные инструкции (BTFSS, BTFSC или любые другие для вашего MCU). Хороший компилятор C может генерировать их с помощью операторов &= |= (gcc может, но, насколько мне известно, не поддерживает PIC). Проверьте выходные данные сборки от вашего компилятора, чтобы увидеть, что он делает. (Программистам Ады намного проще, для микроконтроллеров, на которые может ориентироваться gcc - просто определите упакованный массив логических значений, и вы можете установить/очистить их напрямую).

Ответы (3)

Процедура называется «Чтение-изменение-запись».

То, что он включает, полностью указано в названии. Ты читаешь. Затем вы модифицируете. Потом пишешь.

Читать:

//Read in the value of the output register
tempVariable = [output register]

Изменить:

//set all bits you want to modify to be 0.
tempVariable &= [some mask];
//or in the values of the bits with those bits you want unchanged set to 0
tempVariable |= [new value of bits];

Писать:

//Write the new value back to the output register
[output register] = tempVariable;

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

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


Мы не можем просто писать в регистр напрямую, потому что это также повлияет на биты, которые мы не хотим изменять. Поэтому нам нужна последовательность операций, которая изменит только те биты, которые мы хотим. Здесь на помощь приходят побитовые операторы.

Существует несколько побитовых операторов, но наиболее важными являются два &(и) и |(или). Побитовое и все с 0, и он устанавливает этот бит равным 0, побитовое и все с 1, и он остается прежним. Побитовый или что-то с 1, и он устанавливает этот бит равным 1, побитовый или что-то с 0, и он остается прежним. Эти два оператора позволяют нам вносить необходимые изменения, потому что теперь у нас есть способ установить только некоторые биты в 0 и способ установить только некоторые биты в 1.

Новое значение, которое вы хотите записать, потребует, чтобы некоторые биты были установлены в 0, а некоторые биты были установлены в 1. Мы можем добиться этого, выполнив побитовое, а затем побитовое или . И используется для установки всех битов, которые мы хотим изменить, на 0, чтобы позволить нам затем выполнить операцию или , которая устанавливает только биты, которые мы хотим, чтобы были 1, равными 1.

Пример поможет. Допустим, вы хотите изменить младшие 5 битов на значение, 0b01011но оставить старшие 3 бита без изменений. Допустим также, что текущее значение равно 0b10111101. Итак, следуем процедуре:

Шаг 1, маска:

Current: 0b101 11101
Bitmask: 0b111 00000 <- remember a 1 means don't change, a 0 means clear.
Result : 0b101 00000

Шаг 2, изменить:

Masked : 0b101 00000
New Val: 0b000 01011 <- remember a 1 means set to 1, a 0 means unchanged
Result : 0b101 01011

И вот оно – обратите внимание, что старшие 3 бита не изменились в обеих операциях, в то время как младшие биты были обновлены, чтобы соответствовать новому значению.


Чтобы поднять вопрос, упомянутый в комментариях и другом ответе, что это действительно должно быть сделано в выходном регистре, что и было первоначальным намерением моего ответа. Кажется, есть некоторая путаница в предположении, что под портом я имел в виду регистры PORTx в PIC - на самом деле выходным регистром на некоторых устройствах является регистр LATx. Некоторые PIC не имеют регистра LATx. Например, на AVR PORTx является выходным регистром. Техническое описание вашего устройства расскажет вам, что такое выходной регистр.

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

Из-за различий в именах регистров и того факта, что процесс является очень универсальным подходом, в приведенном выше я попытался быть универсальным, поскольку то же самое относится не только к PIC, но и к любому микроконтроллеру - фактически почти ко всему, что требует некоторого биты регистра должны быть изменены, но не другие.

А на архитектурах и компиляторах, которые его поддерживают, побитовые операции над портами можно преобразовать в операции установки/очистки битов, если они занимают меньше времени или меньше инструкций.
Кто бы ни проголосовал за это, объясните, почему?
Это не работает на портах. На каждом микроконтроллере, который я когда-либо использовал (и я был в этой игре более 20 лет), включая PIC, чтение порта считывает текущее состояние контактов порта, а НЕ состояние выходов. Это очень важное отличие, если некоторые из этих выводов являются входами или выходами с открытым коллектором. Вместо этого вам нужно сохранить переменную для хранения того, что вы в последний раз записали в порт, изменить эту переменную, а затем записать эту переменную в порт.
@TomCarpenter Я не был первым голосующим против. Но я также отклонил ваш ответ по причине, которую я только что назвал.
@ Грэм, я исправлю это. Однако стоит отметить, что под портом я подразумеваю выходной регистр — на AVR он называется PORTx. В то время как на PIC это называется LATx.
@TomCarpenter На PIC это называется LATx только на микроконтроллерах PIC18F. Другие 8-битные микросхемы (PIC10F, PIC12F, PIC16F) не имеют регистра LATx.

Как правило, в архитектуре PIC18 вы никогда не должны использовать команды чтения-изменения-записи, такие как

ПОРТ |= 0x3F; // устанавливаем биты от 0 до 5

Скорее используйте

ЛАТА |= 0x3F; // устанавливаем биты от 0 до 5

или

ЛАТА &= ~0x80; // очистить бит 7

Причина в том, что инструкция PORTA |= xx сначала считывает битовые уровни на выводах, изменяет их, а затем записывает результат в защелку порта.

Инструкция LATA считывает биты в защелке порта, изменяет их, а затем записывает результат в защелку порта.

Если по какой-либо причине (например, задержки загрузки или распространения) выводы порта не находятся на правильных и допустимых логических уровнях, инструкция чтения-изменения-записи может непреднамеренно изменить биты, которые вы не собирались изменять. Если вы меняете контакты с входа на выход, чтобы имитировать контакты с открытым стоком, аналогичная проблема возникает для контактов, которые временно являются входами — выходная защелка другого контакта, который вы намеренно изменяете, изменяется, а затем, когда вы переключаете регистр TRIS обратно на 0, чтобы включить имитацию открытого стока, состояние защелки для этого бита было изменено.

Для более старых PIC, не имеющих LATx, если вам нужно использовать RMW, вы можете поддерживать теневой регистр вручную, изменять его и затем передавать результат в регистр порта.

Чуть подробнее относительно того, что я написал выше, от вашего поставщика компилятора здесь .

Для управления только младшим значащим битом (LSB) регистра

Набор

GPIO_DATA = GPIO_DATA | 0x01;

Прозрачный

GPIO_DATA = GPIO_DATA & (~0x01);

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

введите описание изображения здесь

Для подробного объяснения я предлагаю вам обратиться сюда .


Использованная литература: