/* ============================================================
SISTEMAS DIGITALES II – RETO 2 (TEST4)
Contador 0..9999 con encoder EC11 (CLK=PD2, DT=PD3, SW=PD4)
100% bajo nivel (máscaras explícitas, sin digitalWrite/Read, sin LUT)
Display 4×7 segmentos (ánodo común) con multiplexado.
Decodificación por secuencia completa (Gray 00→01→11→10→00), sin delays.
============================================================ */
// ================== ENCODER ROTATORIO (SIN INTERRUPCIONES) ==================
#define DT 2 // Canal A -> PD2
#define CLK 3 // Canal B -> PD3
#define SW 4 // Pulsador -> PD4 (reset a 0)
// ================== DISPLAY 7 SEGMENTOS (COMÚN ÁNODO) ==================
#define segA 11 // PB3
#define segB 10 // PB2
#define segC 9 // PB1
#define segD 8 // PB0
#define segE 7 // PD7
#define segF 6 // PD6
#define segG 5 // PD5
#define dig1 A3 // PC3 -> millares
#define dig2 A2 // PC2 -> centenas
#define dig3 A1 // PC1 -> decenas
#define dig4 A0 // PC0 -> unidades
// ================== ESTADO ==================
int count = 0; // 0..9999
int cifras[4] = {0,0,0,0}; // {u, d, c, m} (se muestran m c d u)
// Estado para la FSM sin antirrebote temporal
static uint8_t last_ab = 0; // último estado AB (2 bits)
static uint8_t last_idx = 0; // índice Gray 0..3 del último estado
static int8_t seq = 0; // progreso dentro de la vuelta (+3/-3 = 1 detent)
// ================== PROTOTIPOS ==================
void refreshDisplay();
void setSegments(int number);
void allDigitsOff();
void updateDigitsFromCount();
void readEncoderFSM();
static inline uint8_t grayIndex(uint8_t ab);
// ================== SETUP ==================
void setup() {
// --- Segmentos como SALIDA ---
DDRB |= ((1 << PB3) | (1 << PB2) | (1 << PB1) | (1 << PB0)); // A..D
DDRD |= ((1 << PD7) | (1 << PD6) | (1 << PD5)); // E,F,G
// --- Dígitos como SALIDA (PC3..PC0) ---
DDRC |= ((1 << PC3) | (1 << PC2) | (1 << PC1) | (1 << PC0));
// --- Encoder y Pulsador como ENTRADA con PULL-UP ---
DDRD &= ~((1 << PD2) | (1 << PD3) | (1 << PD4)); // entradas
PORTD |= ((1 << PD2) | (1 << PD3) | (1 << PD4)); // pull-ups
// --- Inicializar salidas en OFF ---
PORTB |= ((1 << PB3) | (1 << PB2) | (1 << PB1) | (1 << PB0)); // A..D OFF (HIGH)
PORTD |= ((1 << PD7) | (1 << PD6) | (1 << PD5)); // E,F,G OFF
PORTC &= ~((1 << PC3) | (1 << PC2) | (1 << PC1) | (1 << PC0)); // dígitos OFF (LOW)
// --- Estado inicial del encoder AB = [A B] = [PD2 PD3] ---
uint8_t A = ((PIND & (1 << PD2)) >> PD2);
uint8_t B = ((PIND & (1 << PD3)) >> PD3);
last_ab = ((A << 1) | B);
last_idx = grayIndex(last_ab);
// Conteo y dígitos
count = 0;
updateDigitsFromCount();
}
// ================== LOOP ==================
void loop() {
// Multiplexado + lectura del encoder en cada dígito (baja latencia)
refreshDisplay();
// Pulsador SW (reset a 0) con latch por software
static uint8_t swLatch = 0;
uint8_t swRaw = ((PIND & (1 << PD4)) >> PD4); // 1=no presionado, 0=presionado
if ((swRaw == 0) && (swLatch == 0)) {
count = 0;
updateDigitsFromCount();
swLatch = 1;
} else if (swRaw == 1) {
swLatch = 0;
}
}
// ================== REFRESCO DISPLAY 4 DÍGITOS ==================
// Lee el encoder en cada dígito para respuesta rápida, sin delays.
void refreshDisplay() {
// millares -> dig1 (PC3)
allDigitsOff();
setSegments(cifras[3]);
PORTC |= (1 << PC3);
readEncoderFSM();
delay(1);
// centenas -> dig2 (PC2)
allDigitsOff();
setSegments(cifras[2]);
PORTC |= (1 << PC2);
readEncoderFSM();
delay(1);
// decenas -> dig3 (PC1)
allDigitsOff();
setSegments(cifras[1]);
PORTC |= (1 << PC1);
readEncoderFSM();
delay(1);
// unidades -> dig4 (PC0)
allDigitsOff();
setSegments(cifras[0]);
PORTC |= (1 << PC0);
readEncoderFSM();
delay(1);
allDigitsOff();
}
// ================== APAGAR TODOS LOS DÍGITOS ==================
void allDigitsOff() {
PORTC &= ~((1 << PC3) | (1 << PC2) | (1 << PC1) | (1 << PC0)); // LOW en PC3..PC0
}
// ================== ACTUALIZAR CIFRAS DESDE count ==================
void updateDigitsFromCount() {
cifras[0] = count % 10; // unidades
cifras[1] = (count / 10) % 10; // decenas
cifras[2] = (count / 100) % 10; // centenas
cifras[3] = (count / 1000) % 10; // millares
}
// ================== SEGMENTOS (0–9) A PURAS MÁSCARAS ==================
void setSegments(int number) {
// Apagar todos (HIGH = apagado en ánodo común)
PORTB |= ((1 << PB3) | (1 << PB2) | (1 << PB1) | (1 << PB0)); // A..D HIGH
PORTD |= ((1 << PD7) | (1 << PD6) | (1 << PD5)); // E,F,G HIGH
switch (number) {
case 0: // A B C D E F
PORTB &= ~((1 << PB3) | (1 << PB2) | (1 << PB1) | (1 << PB0));
PORTD &= ~((1 << PD7) | (1 << PD6));
break;
case 1: // B C
PORTB &= ~((1 << PB2) | (1 << PB1));
break;
case 2: // A B D E G
PORTB &= ~((1 << PB3) | (1 << PB2) | (1 << PB0));
PORTD &= ~((1 << PD7) | (1 << PD5));
break;
case 3: // A B C D G
PORTB &= ~((1 << PB3) | (1 << PB2) | (1 << PB1) | (1 << PB0));
PORTD &= ~(1 << PD5);
break;
case 4: // B C F G
PORTB &= ~((1 << PB2) | (1 << PB1));
PORTD &= ~((1 << PD6) | (1 << PD5));
break;
case 5: // A C D F G
PORTB &= ~((1 << PB3) | (1 << PB1) | (1 << PB0));
PORTD &= ~((1 << PD6) | (1 << PD5));
break;
case 6: // A C D E F G
PORTB &= ~((1 << PB3) | (1 << PB1) | (1 << PB0));
PORTD &= ~((1 << PD7) | (1 << PD6) | (1 << PD5));
break;
case 7: // A B C
PORTB &= ~((1 << PB3) | (1 << PB2) | (1 << PB1));
break;
case 8: // A B C D E F G
PORTB &= ~((1 << PB3) | (1 << PB2) | (1 << PB1) | (1 << PB0));
PORTD &= ~((1 << PD7) | (1 << PD6) | (1 << PD5));
break;
case 9: // A B C D F G
PORTB &= ~((1 << PB3) | (1 << PB2) | (1 << PB1) | (1 << PB0));
PORTD &= ~((1 << PD6) | (1 << PD5));
break;
}
}
// ================== ENCODER SIN ANTIRREBOTE (FSM FULL-STEP) ==================
static inline uint8_t grayIndex(uint8_t ab) {
if (ab == 0b00) return 0;
if (ab == 0b01) return 1;
if (ab == 0b11) return 2;
return 3; // 0b10
}
void readEncoderFSM() {
// Leer A (CLK=PD2) y B (DT=PD3)
uint8_t A = ((PIND & (1 << PD2)) >> PD2);
uint8_t B = ((PIND & (1 << PD3)) >> PD3);
uint8_t ab = ((A << 1) | B); // estado actual 2b
if (ab == last_ab) return; // sin cambio
uint8_t idx = grayIndex(ab);
uint8_t next_cw = (last_idx + 1) & 0x03; // siguiente válido horario
uint8_t next_ccw = (last_idx + 3) & 0x03; // (last_idx - 1) mod 4
if (idx == next_cw) {
if (seq < 3) seq++; // 1 subpaso CW
} else if (idx == next_ccw) {
if (seq > -3) seq--; // 1 subpaso CCW
} else {
seq = 0; // transición inválida (rebote) -> reinicio
}
// Al regresar a 00 (idx==0) y completar secuencia, contar 1 detent
if (idx == 0) {
if (seq == 3) {
if (count < 9999) { count++; updateDigitsFromCount(); }
seq = 0;
} else if (seq == -3) {
if (count > 0) { count--; updateDigitsFromCount(); }
seq = 0;
} else {
seq = 0;
}
}
last_idx = idx;
last_ab = ab;
}
/* ===== Si el sentido queda invertido =====
Intercambia físicamente los cables del encoder:
- Conecta CLK al pin D3 (PD3)
- Conecta DT al pin D2 (PD2)
(o viceversa)
*/