¿Se pueden asignar pines individuales de diferentes puertos de un microcontrolador a un registro y cambiar sus valores al cambiar el valor del registro?

P: ¿Se pueden asignar pines individuales de diferentes puertos de un microcontrolador a un registro y cambiar sus valores al cambiar el valor del registro?

Escenario: he usado algunos pines de cada puerto (8 bits) del microcontrolador. Ahora quiero conectar un dispositivo que necesita un bus de 8 bits (supongamos que D0 a D7 EN SECUENCIA), es decir, necesito 8 pines del controlador para poder conectarlos uno a uno.

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

pero no tengo un puerto completo de 8 pines que pueda conectar con este dispositivo, sino que tengo algunos pines de portx, algunos de porty y algunos pines de portz. El nuevo escenario de conexión es como (conexión del microcontrolador al dispositivo respectivamente)

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

En esta condición si quiero enviar un valor diga

unsigned char dataReg = 0xFA;

a mi dispositivo desde el controlador, tengo que realizar operaciones bit a bit en el valor que se enviará y configurar cada pin de acuerdo con el valor en el registro individualmente. Por ejemplo

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 );

Ahora, volviendo a la pregunta principal, para evitar estos cálculos individuales en cada bit en diferentes puertos, ¿se pueden asignar pines individuales de diferentes puertos de un microcontrolador a un registro y cambiar sus valores al cambiar el valor del registro?

Yo tuve la misma idea hace un tiempo. Con PIC, esto no es posible: microchip.com/forums/tm.aspx?high=&m=696277 - No creo que sea posible con ningún micro, pero sería útil enumerar su dispositivo.

Respuestas (4)

Parece que su pregunta se reduce a tener un valor de 8 bits en el firmware y querer leer y escribir eso desde y hacia una colección arbitraria de pines de puerto.

No hay una forma directa de hardware para hacer esto. Tienes que escribir dos rutinas, una para leer el valor de 8 bits y otra para escribirlo. Otros han mencionado el uso de uniones, pero esa es una mala idea. Con las uniones, tiene que lidiar con cada bit por separado, y el código se vuelve dependiente del orden de bits del micro. Este podría ser el camino a seguir de todos modos si los 8 bits se dispersan de forma completamente independiente. Si es así, es poco lo que puede hacer salvo crear un código especial para cada bit.

La mejor manera de hacer esto, especialmente si puede agrupar los bits en unos pocos fragmentos contiguos en los puertos físicos, es usar enmascaramiento, desplazamiento y ORing. Por ejemplo, si los tres bits inferiores del byte interno están en los bits <6-4> de un puerto, desplace a la derecha ese valor de puerto en 4 y haga AND con 7 para colocar esos bits en su posición final. Desplace y enmascare (o enmascare y desplace) bits de otros puertos a su lugar y ensamble el byte final de 8 bits mediante la operación OR de los resultados en él.

Este tipo de cambio de bits de bajo nivel es más fácil de hacer en ensamblador que en C. Probablemente pondría las rutinas de lectura y escritura de bytes en un solo módulo ensamblador y haría que la interfaz se pueda llamar desde C.

Mi respuesta sería casi idéntica a la tuya, excepto que no usaría ensamblaje en absoluto; las manipulaciones de bits son triviales en C. Creo que sería más un dolor de cabeza (re)aprender la convención específica de llamadas de C para el compilador y cómo ejecutar el enlazador. Depende realmente del compilador y de lo difícil que haga las cosas. :-)
@Andrew: ¿En serio? Las convenciones de llamadas se explican claramente en cualquier manual del compilador que he visto donde podría haber una necesidad de interactuar con el código ensamblador. La manipulación de bits puede ser "trivial" para escribir en C, pero esta es un área donde los compiladores pueden producir un código horrendo. Si la velocidad o el espacio del código no importan, use lo que le resulte más cómodo. Me siento más cómodo con el ensamblador para juegos de bits de bajo nivel, así que lo usaría. Si se trata de una rutina crítica de velocidad de bajo nivel, debe hacerlo en ensamblador. Realmente debería ser fácil.
Lo que digo es que tener que jugar con eso para algo tan trivial como la manipulación de bits no es algo que haría a menos que hubiera una muy buena razón para ello. No conocemos los detalles de su bus paralelo, pero la mayoría de los buses tienen señales estroboscópicas que eliminan la necesidad de actualizaciones "casi atómicas" de todos los pines del bus, por lo que pasar al ensamblaje probablemente sea una optimización innecesaria y una complejidad innecesaria (incluso si es directo).
@Andrew: solo es complicado o complejo si no sabes lo que estás haciendo. Creo que el verdadero problema es que algunas personas le tienen miedo al ensamblador y no lo conocen bien. Eso es un error. Tiene que ser una herramienta lista en su caja de herramientas. Si no lo conoces bien o te incomoda, siempre estarás justificando cómo se deben hacer las cosas de otra manera. Algunas cosas son más fáciles en ensamblador si lo conoce y HLL igualmente bien. La mayoría de las personas no lo hacen, pero ese es un problema con ellos, no con el uso del ensamblador.
Estoy bien versado en lenguaje ensamblador en varios microcontroladores/microprocesadores. No estoy de acuerdo con que deba ser una herramienta lista; debe usarse con moderación y solo cuando sea necesario, generalmente para inicialización de muy bajo nivel, código de tiempo o tamaño crítico o, en el caso más común, optimización de un área que ya ha determinado que es un cuello de botella. Encuentro que los proyectos en los que los autores que saltan al ensamblaje porque está allí a menudo escriben un código menos claro o no reconocen cuándo se está aplicando mal un algoritmo. No estoy diciendo específicamente que eres tú, sino más bien en el caso más general.

En general esto no es posible. Que yo sepa, no es posible con PIC.

Solo conozco un microcontrolador que puede hacer esto, el Cypress PSoC . Es un sistema altamente configurable en chip. De las muchas cosas que le permite hacer es definir literalmente su propio registro (1-8 bits) y conectarlo a cualquier pin que desee, o incluso a circuitos internos.

Cableado PSoC

Por ejemplo, aquí he creado un registro de control de 6 bits. 5 de los bits van directamente a los pines, mientras que el sexto bit lo estoy usando para XOR con la entrada de un séptimo pin.

Pines PSoC

En el chip, puedo optar por asignar estos pines a cualquiera de los pines GPIO disponibles. (Son los grises uno de la imagen)

LPC800 también debería poder hacerlo, ya que las funciones se pueden asignar libremente a los pines.

Puede intentar lo siguiente. Escriba una estructura propia que se asigne a los pines respectivos de los 2 puertos (que se utilizarán) Ahora, al actualizar el valor en este registro, se establecerán/reiniciarán los pines de esos 2 puertos. ¡Solo intente y háganos saber si funcionó!

Estoy seguro de que esto debería funcionar.

En C, puede asignar una estructura a una ubicación de memoria, y puede asignar bits de su estructura (campos de bits) a compensaciones de bits, pero no hay forma de evitar que el compilador juegue con los bits 'intermedios', y ahora hay forma de ver la estructura 'general' como un único valor entero. Esto no funcionará.

Si he entendido la pregunta correctamente, es bastante fácil en C:

Declaración de tipo genérico, se puede reutilizar para cualquier registro:

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;

Entonces, para definir un puerto que queremos abordar:

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

Y para girar directamente un pin en ese puerto:

#define MCU_PORTx_PINn  (MCU_GPO_PORTx.bit.bit0)

En codigo:

MCU_PORTx_PINn = 1; // Set pin high

Todo el registro:

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

Vale la pena leer sobre estructuras, uniones, typedefs y enumeraciones: ¡todo esto hace que la vida sea mucho más agradable en incrustado y en general!

OP quiere combinar varios bits de diferentes puertos en 'un byte'. No veo cómo esto haría eso? Olin Lathrop explica por qué no es posible.
Esto en realidad no soluciona el problema y, dependiendo de qué tan "inteligente" sea su compilador, podría generar un nuevo conjunto de problemas para depurar.