Escribir en pines en un puerto sin afectar otros pines en ese puerto

Estoy emitiendo un número de 6 bits en PORTA (RA0-RA5), ¿cómo escribo en esos bits sin estropear lo que ya está en RA6 y RA7?

Por ejemplo, si quiero generar 0x3F en los bits RA0-RA5. Si lo uso PORTA = 0x3F;, generará 00111111, borrando efectivamente lo que ya estaba escrito en los bits RA6 y RA7.

Estoy codificando en C con MikroC en un PIC18

Los operadores de asignación compuesta son sus amigos: &=, |=, ^=son todos los operadores con los que debería estar muy familiarizado en el desarrollo integrado.
Leer-Modificar-Escribir.
Use las instrucciones de un solo bit (BTFSS, BTFSC o lo que sea para su MCU). Un buen compilador de C puede generarlos a partir de los operadores &= |= (gcc puede, pero hasta donde yo sé, no es compatible con PIC). Inspeccione la salida del ensamblado de su compilador para ver qué hace. (Los programadores de Ada lo tienen mucho más fácil, para MCU a los que gcc puede apuntar: simplemente defina una matriz empaquetada de booleanos y puede configurarlos/borrarlos directamente).

Respuestas (3)

Un procedimiento llamado "Leer-Modificar-Escribir".

Lo que implica está enteramente en el nombre. Tú lees. Luego modificas. Entonces escribes.

Leer:

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

Modificar:

//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];

Escribir:

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

La clave es básicamente terminar con los valores de los bits que desea que no cambien para que se vuelvan a escribir en el registro de salida junto con los nuevos valores de los bits que desea cambiar.

Para determinar cuál es el registro de salida para su dispositivo, debe consultar su hoja de datos.


No podemos simplemente escribir en el registro directamente porque también afectará los bits que no queremos cambiar. Entonces, necesitamos una secuencia de operaciones que cambie solo los bits que queremos. Aquí es donde entran los operadores bit a bit.

Hay varios operadores bit a bit, pero los dos importantes son &(y) y |(o). A nivel de bits y cualquier cosa con un 0 y establece ese bit en 0, a nivel de bits y cualquier cosa con 1 y permanece igual. A nivel de bits o cualquier cosa con un 1 y establece que ese bit sea un 1, a nivel de bits o cualquier cosa con 0 y permanece igual. Estos dos operadores nos permiten realizar los cambios necesarios porque ahora tenemos una forma de establecer solo algunos bits en 0 y una forma de configurar solo algunos bits en 1.

El nuevo valor que desea escribir requerirá que algunos bits se configuren en 0 y algunos bits se configuren en 1. Podemos lograr esto haciendo un bit a bit y seguido de un bit a bit o . El and se usa para configurar todos los bits que queremos cambiar a 0 para permitirnos luego hacer el o que establece solo los bits que queremos que sean 1 a 1.

Un ejemplo ayudará. Digamos que desea modificar los 5 bits inferiores a un valor de 0b01011pero deja los 3 bits superiores sin cambios. Digamos también que el valor actual es 0b10111101. Así que seguimos el procedimiento:

Paso 1, mascarilla:

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

Paso 2, modificar:

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

Y ahí lo tiene: observe que los 3 bits superiores no cambiaron en ambas operaciones, mientras que los bits inferiores se actualizaron para coincidir con el nuevo valor.


Para mencionar un punto mencionado en los comentarios y la otra respuesta, que esto debería hacerse en el registro de salida, que era la intención original de mi respuesta. Parece que hay cierta confusión al suponer que por puerto me refería a los registros PORTx en los PIC; de hecho, el registro de salida en algunos dispositivos es el registro LATx. Algunos PIC no tienen un registro LATx. En los AVR, por ejemplo, PORTx es el registro de salida. La hoja de datos de su dispositivo le dirá cuál es el registro de salida.

Además, la técnica se puede usar para modificar variables y registros, y se puede usar al modificar los registros para cosas que no sean solo puertos de E/S; también puede modificar cosas como registros de control para periféricos en serie y similares.

Debido a las diferencias en la denominación de los registros y al hecho de que el proceso es un enfoque muy universal, en lo anterior traté de ser genérico, ya que lo mismo se aplica no solo a los PIC sino a cualquier microcontrolador; de hecho, prácticamente cualquier cosa que requiera algún bits de un registro a modificar pero no otros.

Y en las arquitecturas y los compiladores que lo admiten, las operaciones bit a bit en los puertos se pueden convertir en operaciones bit set/clear si requieren menos tiempo o menos instrucciones.
Quienquiera que votó negativamente, ¿quiere explicar por qué?
Esto no funciona en los puertos. En cada micro que he usado (y he estado en este juego durante más de 20 años), incluido el PIC, leer un puerto lee el estado actual de los pines del puerto, NO el estado de las salidas. Esta es una diferencia muy importante si algunos de esos pines son entradas o salidas de colector abierto. En su lugar, debe mantener una variable para almacenar lo último que escribió en el puerto, modificar esa variable y luego escribir esa variable en el puerto.
@TomCarpenter No fui el votante negativo original. Pero también he rechazado su respuesta por la razón que acabo de dar.
@Graham Lo corregiré. Sin embargo, vale la pena señalar que por puerto me refiero al registro de salida: en un AVR se llama PORTx. Mientras que en PIC se llama LATx.
@TomCarpenter En PIC se llama LATx solo en micros PIC18F. Otros micros de 8 bits (PIC10F, PIC12F, PIC16F) no tienen registro LATx.

En general, en la arquitectura PIC18, nunca debe usar comandos de lectura, modificación y escritura como

PORTA |= 0x3F; // establecer bits 0 a 5

Más bien, usa

LATA |= 0x3F; // establecer bits 0 a 5

o

LATA &= ~0x80; // borrar el bit 7

La razón es que la instrucción PORTA |= xx primero lee los niveles de bits en los pines, los modifica y luego escribe el resultado en el latch del puerto.

La instrucción LATA lee los bits en el pestillo del puerto, los modifica y luego escribe el resultado en el pestillo del puerto.

Si, por algún motivo (como retrasos en la carga o la propagación), los pines del puerto no están en los niveles lógicos correctos y válidos, la instrucción de lectura, modificación y escritura puede modificar inadvertidamente bits que no tenía la intención de modificar. Si está intercambiando pines de entrada a salida para simular pines de drenaje abiertos, surge un problema similar para los pines que son entradas temporales: el pestillo de salida de un pin diferente al que está modificando intencionalmente cambia, y luego cuando cambia el registro TRIS de nuevo a 0 para activar el drenaje abierto simulado, el estado de bloqueo para ese bit se ha modificado.

Para los PIC más antiguos que no tienen LATx, si tiene que usar RMW, puede mantener un registro de sombra manualmente, modificarlo y luego transferir el resultado al registro del puerto.

Un poco más de detalle sobre lo que escribí anteriormente, de su proveedor de compilador aquí .

Para manipular solo el bit menos significativo (LSB) de un registro

Colocar

GPIO_DATA = GPIO_DATA | 0x01;

Claro

GPIO_DATA = GPIO_DATA & (~0x01);

A continuación hay un conjunto de notas que mantengo a mano que podrían ser útiles. Presta atención a las notas en Rojo.

ingrese la descripción de la imagen aquí

Para una explicación detallada, le sugiero que consulte aquí .


Referencias: