Tu problema:
Tu configuración de DDR es incorrecta: has escrito
DDRC |= _BV(PC0) | _BV(PC1) | _BV(PC3) & _BV(PC2);
pero tú quieres
DDRC |= _BV(PC0) | _BV(PC1) | _BV(PC3)
DDRC &= ~_BV(PC2);
¿Dice que desea más detalles porque simplemente emuló otras asignaciones de DDRC y no entiende realmente lo que escribió? OK, aquí va:
Cómo configurar registros con operadores bitwise en el AVR.
Desempaquetemos las definiciones de macro y PCx _BV()
por un momento. _BV(x)
es simplemente 1 << x
. Si escribe _BV(5)
, la macro evaluará el número 0b 0001 0000
. PCx es simplemente una macro definida para el valor de x, por lo que _BV(PC5)
se evaluará de la misma manera que _BV(5)
. Probablemente esperas que ocurra algo como lo siguiente:
0b 0000 0001 // PC0
| 0b 0000 0010 // PC1
| 0b 0000 1000 // PC3
& 0b 0000 0100 // Misconception: AND does not cause this to output a zero at the set bit
|= ------------
0b 0000 1011
Sin embargo, no estás eliminando PC2 correctamente. Estás aplicando un binario y al final. Y tiene una prioridad más alta que OR en C, por lo que su operación es en realidad
DDRC |= _BV(PC0) | _BV(PC1) | ( _BV(PC3) & _BV(PC2) );
PC3 es 0b 0000 1000, y PC2 es 0b 0000 0100, por lo que la sección entre paréntesis se evalúa a cero. Esto es realmente afortunado para PC2, pero desafortunado para PC3, que queda claro cuando se desea configurar. Para borrar un poco, use &
, pero necesita tomar el complemento del número usando el operador ~
. En las siguientes ecuaciones, he usado ?
para los bits que no queremos cambiar:
0b ???? ???1 // Set PC0
| 0b ???? ??10 // Set PC1
| 0b ???? 1?00 // Set PC3
|= ------------
0b ???? 1?11
0b ???? 1?11
& 0b 1111 1011 // Clear PC2
&= ------------
0b ???? 1011
En el código, estas ecuaciones son:
DDRC |= _BV(PC0) | _BV(PC1) | _BV(PC3)
DDRC &= ~_BV(PC2);
como se muestra arriba. Puede hacerlo en dos pasos como lo hice, o aplicar el valor anterior de DDRC con una máscara de bits:
DDRC = (DDRC & 0xF0) | ( ( _BV(PC0) | _BV(PC1) | _BV(PC3) ) & ~_BV(PC2) );
0xF0, o 0b 1111 0000, cuando AND'ed con el valor anterior de DDRC, devuelve 0b ???? 0000, a lo que podemos O nuestros cuatro bits más bajos. Creo que el método de dos pasos es más claro: sus entradas y salidas están en líneas diferentes (tenga en cuenta que puede agregar entradas adicionales, por ejemplo, DDRC &= ~_BV(PC2) & ~_BV(PC4)
). A veces, incluso lo dividiré en más líneas:
DDRC |= _BV(PC0) // RELAY STATUS LED - O/P
DDRC |= _BV(PC1) // RELAY CTL LINE - O/P
DDRC &= ~_BV(PC2) // PUSH BUTTON - I/P
DDRC |= _BV(PC3) // SPARE - O/P
Para mí, este es el mecanismo más claro posible y el más fácil de entender para los futuros lectores. También es el más detallado, pero a quién le importa? El compilador optimizará los tres mecanismos al mismo código.