Можно ли сопоставить отдельные контакты разных портов микроконтроллера с регистром и изменить их значения при изменении значения регистра?

В: Можно ли привязать к регистру отдельные выводы разных портов микроконтроллера и изменить их значения при изменении значения регистра?

Сценарий: я израсходовал несколько контактов от каждого порта (8 бит) микроконтроллера. Теперь я хочу подключить устройство, которому требуется 8-битная шина (предположим, от D0 до D7 ПОСЛЕДОВАТЕЛЬНО), то есть мне нужно 8 контактов от контроллера, чтобы я мог соединить их один к одному.

portx0  -> D0 // x is the name of port followed by bit location on that port
portx1  -> D1
...
portx7  -> D7

но у меня нет целого порта из 8 контактов, который я могу подключить к этому устройству, скорее у меня есть несколько контактов от portx, несколько от porty и несколько контактов от portz. Новый сценарий подключения такой (соответственно подключение от микроконтроллера к устройству)

portx0  -> D0
portx1  -> D1
portx2  -> D2
porty4  -> D3
porty5  -> D4
porty6  -> D5
porty7  -> D6
portz1  -> D7

В этом состоянии, если я хочу отправить значение, скажем

unsigned char dataReg = 0xFA;

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

portx0 = ((dataReg & 0x01) >> 0 );  // Masking and shifting as bit position
portx1 = ((dataReg & 0x02) >> 1 );
portx2 = ((dataReg & 0x04) >> 2 );
porty4 = ((dataReg & 0x08) >> 3 );
porty5 = ((dataReg & 0x10) >> 4 );
porty6 = ((dataReg & 0x20) >> 5 );
porty7 = ((dataReg & 0x40) >> 6 );
portz1 = ((dataReg & 0x80) >> 7 );

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

У меня была такая же идея некоторое время назад. С PIC это невозможно: microchip.com/forums/tm.aspx?high=&m=696277 - я не думаю, что это возможно с любым микро, но перечисление вашего устройства было бы полезно.

Ответы (4)

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

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

Лучший способ сделать это, особенно если вы можете сгруппировать биты в несколько смежных фрагментов на физических портах, — использовать маскирование, сдвиг и операцию ИЛИ. Например, если младшие три бита внутреннего байта находятся в битах <6-4> порта, сдвиньте значение этого порта вправо на 4 и И на 7, чтобы получить эти биты в их конечной позиции. Сдвиньте и замаскируйте (или замаскируйте и сдвиньте) биты из других портов на место и соберите окончательный 8-битный байт, объединив с ним результаты ИЛИ.

Такого рода перестановку битов на низком уровне легче выполнить на ассемблере, чем на C. Я бы, вероятно, поместил процедуры чтения и записи байтов в один модуль ассемблера и сделал интерфейс вызываемым из C.

Мой ответ был бы почти идентичен вашему, за исключением того, что я бы вообще не использовал сборку; битовые манипуляции в C тривиальны. Я думаю, что было бы больше головной боли (повторно) изучать конкретное соглашение о вызовах C для компилятора и как запускать компоновщик. На самом деле зависит от компилятора и от того, насколько он усложняет работу. :-)
@ Эндрю: Серьезно? Соглашения о вызовах четко изложены в любом руководстве по компилятору, которое я видел, где может возникнуть необходимость взаимодействия с ассемблерным кодом. Манипуляции с битами могут быть «тривиальными» для написания на C, но это та область, где компиляторы могут создавать ужасный код. Если скорость или объем кода не имеют значения, используйте то, что вам удобнее. Мне удобнее использовать ассемблер для низкоуровневых битов, поэтому я бы использовал его. Если это критическая по скорости подпрограмма низкого уровня, вы должны делать это на ассемблере. Это действительно должно быть легко.
Я хочу сказать, что возиться с этим для такой тривиальной вещи, как битовая манипуляция, я бы не стал, если бы для этого не было очень веской причины. Мы не знаем специфики его параллельной шины, но большинство шин имеют стробирующие сигналы, которые устраняют необходимость «почти атомарных» обновлений всех выводов шины, поэтому переход к сборке, вероятно, является ненужной оптимизацией и ненужной сложностью (даже если это прямолинейный).
@Andrew: Это сложно или сложно только в том случае, если вы не знаете, что делаете. Я думаю, что настоящая проблема в том, что некоторые люди боятся ассемблера и плохо его знают. Это ошибка. Это должен быть готовый инструмент в вашем наборе инструментов. Если вы плохо это знаете или чувствуете себя некомфортно, вы всегда будете оправдывать то, что все должно быть сделано по-другому. Некоторые вещи на ассемблере проще , если вы одинаково хорошо знаете его и HLL. Большинство людей этого не делают, но это проблема с ними, а не с использованием ассемблера.
Я хорошо разбираюсь в языке ассемблера на ряде микроконтроллеров/микропроцессоров. Я не согласен с тем, что это должен быть готовый инструмент; его следует использовать с осторожностью и только при необходимости, обычно для очень низкоуровневой инициализации, кода, критичного по времени или размеру, или, в более общем случае, для оптимизации области, которую вы уже определили как узкое место. Я нахожу проекты, в которых авторы, которые переходят к ассемблеру, потому что он там, часто пишут менее четкий код или не распознают неправильное применение алгоритма. Я не говорю конкретно, что это вы, а скорее в более общем случае.

В общем случае это невозможно. Насколько я знаю, с PIC это невозможно.

Я знаю только один микроконтроллер, который может это сделать, Cypress PSoC . Это хорошо настраиваемая система на чипе. Из многих вещей, которые он позволяет вам сделать, это буквально определить свой собственный регистр (1-8 бит) и подключить его к любым контактам, которые вам нравятся, или даже к внутренним схемам.

Проводка PSoC

Например, здесь я создал 6-битный регистр управления. 5 бит идут прямо на контакты, а 6-й бит я использую для XOR с вводом с 7-го контакта.

Контакты PSoC

На чипе я могу назначить эти контакты любому из доступных контактов GPIO. (Это серые на изображении)

LPC800 также должен уметь это делать, так как функции можно свободно назначать контактам.

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

Я уверен, что это должно сработать.

В C вы можете сопоставить структуру с ячейкой памяти, и вы можете сопоставить биты вашей структуры (битовые поля) с битовыми смещениями, но нет никакого способа предотвратить компилятор от возни с «промежуточными» битами, и теперь способ просмотра «общей» структуры одного целочисленного значения. Это не сработает.

Если я правильно понял вопрос, в C это достаточно просто:

Объявление универсального типа можно повторно использовать для любого регистра:

typedef union    // Generic 8-bit register Type
{
  uint8 reg; // Whole register
  struct
  {
    unsigned  bit7     : 1;  // Bit 7 
    unsigned  bit6     : 1;  // Bit 6 
    unsigned  bit5     : 1;  // Bit 5 
    unsigned  bit4     : 1;  // Bit 4 
    unsigned  bit3     : 1;  // Bit 3 
    unsigned  bit2     : 1;  // Bit 2 
    unsigned  bit1     : 1;  // Bit 1 
    unsigned  bit0     : 1;  // Bit 0 
  } bit;
} typ_GENERIC_REG8;

Итак, чтобы определить порт, к которому мы хотим обратиться:

#define MCU_GPO_PORTx   (*(volatile typ_GENERIC_REG8 *)(0x12345678)) // Number is address

И чтобы напрямую покрутить булавку на этом порту:

#define MCU_PORTx_PINn  (MCU_GPO_PORTx.bit.bit0)

В коде:

MCU_PORTx_PINn = 1; // Set pin high

Весь реестр:

MCU_GPO_PORTx.reg = 0xF; // All pins high

Стоит прочитать о структурах, объединениях, определениях типов и перечислениях — все это делает жизнь намного приятнее во встраиваемых системах и в целом!

OP хочет объединить несколько битов из разных портов в «один байт». Я не понимаю, как это могло бы сделать это? Олин Латроп объясняет , почему это невозможно.
На самом деле это не решает проблему, и в зависимости от того, насколько «умным» является ваш компилятор, может создать совершенно новый набор проблем для отладки.