Laboratorio 2: Display de 7 Segmentos
Control de perifericos mediante una comunicacion serie síncrona
1. ¿Qué es un Display de 7 Segmentos?
Es un componente optoelectrónico compuesto por 7 LEDs (segmentos) dispuestos en forma de "8", más un octavo LED para el punto decimal (DP). Permite representar números y algunos caracteres alfanuméricos.
Tipos de Configuración:
- Cátodo Común: Todos los cátodos de los LEDs están unidos a masa (GND). Se iluminan con un '1' lógico (HIGH).
- Ánodo Común: Todos los ánodos están unidos a VCC. Se iluminan con un '0' lógico (LOW).
2. Registro de Desplazamiento 74HC595
Para ahorrar pines en el microcontrolador (solo usamos 3), empleamos este chip que convierte datos serie en paralelo (SISO/SIPO).
DS (Serial Data)
Pin por donde entra el bit de datos actual.
SHCP (Shift Clock)
Desplaza el dato de DS al interior del registro en cada flanco de subida.
STCP (Latch Clock)
Actualiza las salidas físicas (QA-QH) con el contenido del registro.
3. Funcionamiento Dinámico (Multiplexado)
En sistemas con varios dígitos, se utiliza el multiplexado para ahorrar componentes. Se activan los dígitos uno a uno de forma secuencial.
- Primer 595 (U3): Controla qué segmentos (a-g, dp) se encienden. Sus salidas están conectadas a todos los dígitos en paralelo.
- Segundo 595 (cascada - U2): Controla los "comunes" (dígitos). Activa uno a uno cada dígito a gran velocidad.
4. Ejemplo de Implementación: Contador 0-9
A continuación se muestra un ejemplo completo para implementar un contador de 0 a 9 en un display de 7 segmentos conectado a un 74HC595. Se presentan dos implementaciones: Bare-Metal (manipulando registros directamente) y CMSIS (usando la biblioteca de periféricos).
Esquema de Conexión
Display 7 segmentos (cátodo común)
Tabla de Códigos para Display (Cátodo Común)
Código hexadecimal a enviar al 74HC595 para mostrar cada dígito (formato: 0bHGFEDCBA):
| Dígito | Segmentos Encendidos | Binario | Hexadecimal |
|---|---|---|---|
| 0 | a,b,c,d,e,f | 0b00111111 | 0x3F |
| 1 | b,c | 0b00000110 | 0x06 |
| 2 | a,b,d,e,g | 0b01011011 | 0x5B |
| 3 | a,b,c,d,g | 0b01001111 | 0x4F |
| 4 | b,c,f,g | 0b01100110 | 0x66 |
| 5 | a,c,d,f,g | 0b01101101 | 0x6D |
| 6 | a,c,d,e,f,g | 0b01111101 | 0x7D |
| 7 | a,b,c | 0b00000111 | 0x07 |
| 8 | a,b,c,d,e,f,g | 0b01111111 | 0x7F |
| 9 | a,b,c,d,f,g | 0b01101111 | 0x6F |
Implementación del Programa
#include "stm32f10x.h"
// Pines conectados al 74HC595
#define HC595_DS_PORT GPIOB
#define HC595_DS_PIN GPIO_ODR_ODR0
#define HC595_SHCP_PORT GPIOB
#define HC595_SHCP_PIN GPIO_ODR_ODR1
#define HC595_STCP_PORT GPIOB
#define HC595_STCP_PIN GPIO_ODR_ODR2
// Tabla de códigos para display 7 segmentos (cátodo común)
const uint8_t seg_codes[10] = {
0x3F, // 0: a,b,c,d,e,f
0x06, // 1: b,c
0x5B, // 2: a,b,d,e,g
0x4F, // 3: a,b,c,d,g
0x66, // 4: b,c,f,g
0x6D, // 5: a,c,d,f,g
0x7D, // 6: a,c,d,e,f,g
0x07, // 7: a,b,c
0x7F, // 8: a,b,c,d,e,f,g
0x6F // 9: a,b,c,d,f,g
};
void delay_ms(uint32_t ms) {
for (uint32_t i = 0; i < ms * 1000; i++) {
__NOP();
}
}
void hc595_send_byte(uint8_t data) {
//-enviamos los 8 bits (MSB primero)
for (int8_t i = 7; i >= 0; i--) {
//-establecer el bit de datos
if (data & (1 << i)) {
HC595_DS_PORT->BSRR = HC595_DS_PIN; // DS = 1
} else {
HC595_DS_PORT->BRR = HC595_DS_PIN; // DS = 0
}
//-pulso de reloj (SHCP)
HC595_SHCP_PORT->BSRR = HC595_SHCP_PIN; // SHCP = 1
delay_ms(1);
HC595_SHCP_PORT->BRR = HC595_SHCP_PIN; // SHCP = 0
delay_ms(1);
}
//-pulso de cerrojo (STCP) para actualizar salidas
HC595_STCP_PORT->BSRR = HC595_STCP_PIN; // STCP = 1
delay_ms(1);
HC595_STCP_PORT->BRR = HC595_STCP_PIN; // STCP = 0
}
int main(void) {
//-habilitar reloj para GPIOB
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
//-configurar PB0, PB1, PB2 como salida push-pull a 2MHz
GPIOB->CRL &= ~(0xFFF << 0); // Limpiar configuración de PB0-PB2
GPIOB->CRL |= (0x222 << 0); // Mode=2MHz Output, CNF=0 (Push-Pull)
while (1) {
//-contador de 0 a 9
for (uint8_t i = 0; i < 10; i++) {
hc595_send_byte(seg_codes[i]);
delay_ms(500);
}
}
}
#include "stm32f10x.h"
// Definiciones de pines usando macros CMSIS
#define HC595_DS_PIN GPIO_ODR_ODR0
#define HC595_SHCP_PIN GPIO_ODR_ODR1
#define HC595_STCP_PIN GPIO_ODR_ODR2
// Tabla de códigos para display 7 segmentos (cátodo común)
const uint8_t seg_codes[10] = {
0x3F, // 0
0x06, // 1
0x5B, // 2
0x4F, // 3
0x66, // 4
0x6D, // 5
0x7D, // 6
0x07, // 7
0x7F, // 8
0x6F // 9
};
void delay_ms(uint32_t ms) {
for (uint32_t i = 0; i < ms * 8000; i++) {
__NOP();
}
}
void hc595_send_byte(uint8_t data) {
//-enviamos los 8 bits (MSB primero)
for (int8_t i = 7; i >= 0; i--) {
//-establecer el bit de datos (DS)
if (data & (1 << i)) {
GPIOB->BSRR = HC595_DS_PIN;
} else {
GPIOB->BRR = HC595_DS_PIN;
}
//-pulso de reloj (SHCP)
GPIOB->BSRR = HC595_SHCP_PIN;
delay_ms(1);
GPIOB->BRR = HC595_SHCP_PIN;
delay_ms(1);
}
//-pulso de cerrojo (STCP) para actualizar salidas
GPIOB->BSRR = HC595_STCP_PIN;
delay_ms(1);
GPIOB->BRR = HC595_STCP_PIN;
}
int main(void) {
//-habilitar reloj para GPIOB usando RCC_APB2ENR
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
//-configurar pines como salida a 2MHz push-pull
//-usar máscaras CMSIS para limpiar y configurar
GPIOB->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_MODE1 | GPIO_CRL_MODE2 |
GPIO_CRL_CNF0 | GPIO_CRL_CNF1 | GPIO_CRL_CNF2);
//-configurar como salida velocidad 2MHz (MODE = 10)
GPIOB->CRL |= (GPIO_CRL_MODE0_1 | GPIO_CRL_MODE1_1 | GPIO_CRL_MODE2_1);
//-CNF = 00 (Push-Pull) ya es el valor por defecto
while (1) {
for (uint8_t i = 0; i < 10; i++) {
hc595_send_byte(seg_codes[i]);
delay_ms(500);
}
}
}
- Bare-Metal: Manipulación directa de registros con operaciones de bits simples. Acceso directo a BSRR/BRR para set/reset de pines.
- CMSIS: Uso de macros predefinidas (GPIO_ODR_ODR0, RCC_APB2ENR_IOPBEN) que mejoran la legibilidad y portabilidad del código.
4. Laboratorio: Decodificador Manual
Introduce un valor hexadecimal (0x00 a 0xFF) para ver cómo se comportarían los segmentos si enviaras ese dato al registro de desplazamiento.
Orden de los bits (MSB a LSB): DP, G, F, E, D, C, B, A