/*
Programa para controlar un display de ánodo comun
con cuatro dígitos, usando registro por desplazamiento
el IC CD4094 utilizando tres señales para controlarlo
Dato // Clock // Output Enable que se encargará de
encender los leds del display. Se USARA PUNTEROS
Cada display estará conectado con un transistor PNP 2N3906
para hacer la multiplexación de los cuatro display
Todo se controlará por el puerto A
Se manejará por interrupción del temporizador 0
*/
#define F_CPU 10000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <interrupt.h>
#define PinDato 1 //definimos los pines para
#define PinClk 2 //controlar el IC
#define PinEnable 0 //CD4096 en el puerto A
#define PuntoOn 0 //Muestra el display sin el punto
#define PuntoOff 128 // muestra el display con el punto
//Definimos los valores para el display de ánodo común
unsigned char const Display[10]={0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};
unsigned char Punto=128; //Variable para seleccionar si tendrá punto..por defecto no.
unsigned char volatile Segundos=0; //Se usa para multiplicar el tiempo de TMR0
unsigned short volatile Cuenta=0; //variable auxiliar para decidir si encender un display o no
unsigned char volatile DisplayOn=0; //Variable auxiliar para el tiempo de encendido de cada display
//Estructura para ser controlada por el puntero *pUnidad
////////////////////////////////////////////////////////
struct {
unsigned char Unidad;
unsigned char Decena;
unsigned char Centena;
unsigned char Miles;
}Valor;
////////////////////////////////////////////////////////
// Rutina de envío de datos para el CD4094
// SPI solo como master
void SpiEnviar(unsigned char Numero){
PORTA &= ~(1<<PinEnable); //Alta impedancia en las salidas del CD4094
for(unsigned char i=0; i<8; i++){
if(((Display[Numero]+Punto)<<i)&0x80){
PORTA |=(1<<PinDato);
}else{
PORTA &= ~(1<<PinDato);
}
_delay_us(5); //Tiempo de esperea para enviar datos
PORTA |= (1<<PinClk); // y el clk al CD4096
_delay_us(5);
PORTA &= ~(1<<PinClk);
_delay_us(5);
}
PORTA |= (1<<PinEnable); //Muestra el dato
}
void Timer0Ini(){
TCCR0 = 0b00000100;
TIMSK|=(1<<OCIE0);
OCR0 = 38;
sei();
}
ISR(TIMER0_COMP_vect){
TCNT0=0;
DisplayOn++; //Tiempo para encender el display
Segundos++; //Usado para temporizador o contador de pulsos para pruebas
}
///**************************************************//
void EscribirDisplay(unsigned char *pUnidad){
for(int i=6; i>2; i--){
switch(i){ //Rutina para encender o no el display
//Dependiendo de decenas centenas o miles
case 3:{
if(Cuenta>999){
PORTA &= ~(1<<i);
Punto=PuntoOn;
}else{
PORTA |= (1<<i);
}
break;
}
case 4:{
if(Cuenta>99){
PORTA &= ~(1<<i);
}else{PORTA |= (1<<i);}
break;
}
case 5:{
if(Cuenta>9){
PORTA &= ~(1<<i);
}else{PORTA |= (1<<i);}
break;
}
case 6:{
PORTA &= ~(1<<i);
break;
}
}
SpiEnviar(*pUnidad);
while(DisplayOn<5){}; //5ms para encender cada display
PORTA |= (1<<i);
PORTA &= ~(1<<PinEnable); //Alta impedancia en las salidas del CD4094
DisplayOn=0; //Reiniciamos los 5ms
pUnidad++; //Aumentamos en 1 el puntero, siguiente dirección
//Punto=PuntoOff;
}
if(Segundos >= 200){ //Tiempo de conteo aprox 200ms
Cuenta++;
if(Cuenta>=10000){
Cuenta=0;
}
Segundos=0;
}
Punto=PuntoOff;
}
int main(void)
{
DDRA = 0b01111111;
PORTA |= (1<<3)|(1<<4)|(1<<5)|(1<<6); //Display apagados(lógica invertida)
PORTA &= ~(1<<PinClk); //CLK en cero
PORTA &= ~(1<<PinEnable);
Valor.Unidad=0;
Timer0Ini();
while(1){
Valor.Unidad = Cuenta%10; //Conversion para mostrar valor
Valor.Decena = (Cuenta%100)/10; // en cada display
Valor.Centena = (Cuenta%1000)/100;
Valor.Miles = Cuenta/1000;
EscribirDisplay(&Valor.Unidad);
}
return 0;
}