La idea de usar un arreglo de datos y puntero surgío de la necesidad que tenia en mi programa de poder reducir la cantidad de codigo que estaba utilizando. No se si realmente ha reducido sustancialmente el codigo asembler (debe haber forma de saberlo), pero por lo menos se hace mas facil de entender ya que utilizo menos lineas en C.
Mi programa hace lo siguiente:
Utilizo 4 displays de 7 segmentos para mostrar unos datos numericos. En cada interrupcion generada por desbordamiento de TMR1 se debe actualizar un display, es decir quiero hacer un barrido de los displays cada cierto tiempo (lo suficiente como para no ver el parpadeo). El usar la interrupcion para el scaneo de los 4 displays es importante porque asi el microcontrolador puede hacer otras tareas. Asi no me preocupo por el tema de mostrar los valores en el display, simplemente actualizo los datos en un par de registro y en cada interrupcion estos registros van mostrandose en los display. Mas o menos he experimentado que cada 10 ms de barrido evito el parpadeo, si demoro mas el parpadeo se nota.
Como uso el arreglo de datos y punteros?. Pues he creado un arreglo de datos que me permite guarda alli los datos que debo mostrar en pantalla y otro arreglo que me permite guardar el valor del puerto que activaré. Tratare de explicar...
Tengo dos registros punteros:
*pointer_port
*pointer_display
y dos arreglos de datos
display_data[4]={5,6,7,8}
display_port[4]={1,2,4,8}
Como los arreglos de datos se guardan en direcciones consecutiva, aqui uso los punteros de tal manera que en cada interrupcion incremento los punteros hasta lograr barrer con todos los diplays. No olviden que el puntero guarda una direccion a que deseo acceder. Fijense que el arreglo "display_port" guarda los valores 1,2,4,8 que si lo convierten a binario nos permite en cada interrupcion activar solo un bit del puerto. Asi vamos haciendo un barrido uno por uno de cada display. En cada interrupcion saco por un puerto un dato que le corresponde a ese display.
Aqui dejo en programa que he probado en mi tarjeta de pruebas. Hago uso de Interrupcion por TMR1 y Funciones. Ojo que si simulan en proteus deberan hacer que la interrupcion por TMR1 se al menos cada 20ms incluso mas, de otro no van a poder ver nada. Espero se entienda.
//*****DECLARACION DE LAS FUNCIONES
void BIN_7SEG(int f_data1bin,int f_data2bin);//Usado en la conversion de datos a
//valor de 7segmentos. Ingresas dos registros por ejemplo el de los segundos y
//minutos y entrega los datos en los registros (displa[0] ... display[3])
int BCD_7SEG(int f_digit);//Usado en la conversion de los datos a 7 segmentos
//Convierte valor BCD a 7 segmentos, cada registro es convertido a BCD y guardado
//en dos registros.
//*****DECLARACION DE VARIABLES USADAS PARA MOSTRAR VALOR EN DISPLAYS
unsigned short int display_data[4]={5,6,7,8};//Carga cada valor a mostrar
unsigned short int display_port[4]={1,2,4,8};//Puerto a activar en interrupcion
unsigned short int *pointer_display;//Puntero para manejo sacar valor por puerto
unsigned short int *pointer_port;//Puntero para activar un bit del puerto
//*****DECLARACION DE VARIABLES USADAS PARA LA CONVERSION DE DATOS A VALORES DE
//7 SEGMENTOS. POR EJEMPLO EL REGISTRO SEGUNDOS ES CONVERTIDO PRIMERO A DOS
//REGISTROS EN BCD Y LUEGO PASADO A DOS REGISTROS CODIFICADO PARA 7 SEGMENTOS.
unsigned short int valor1_bcd=0;//Contiene el valor en BCD de f_data1bin
unsigned short int data1_7seg=0;//Contiene el primer digito en 7segmentos de Valor1_bcd
unsigned short int data2_7seg=0;//Contiene el segundo digito en 7segmentos de Valor2_bcd
unsigned short int valor2_bcd=0;//Contiene el valor en BCD de f_data2bin
unsigned short int data3_7seg=0;//Contiene el primer digito en 7segmentos de Valor2_bcd
unsigned short int data4_7seg=0;//Contiene el segundo digito en 7segmentos de Valor2_bcd
short int count_1=0;//Contador para lograr 1 segundo
short int minutes_bin=0;
short int seconds_bin=0;
void interrupt() {
//------------- RUTINA DE ATENCION A INTERRUPCIONES POR TMR1 -------------------
if (PIR1.TMR1IF==1){//Verificar si la interrupcion es por TMR1
//En cada interrupcion se muestra un display. Se hace uso de un arreglo de regis
//tros y punteros. display_port, display_data, pointer_display, pointer_port.
PORTA=*pointer_port;//Apuntar al valor guardado en el registro apuntado
PORTD=*pointer_display;//Apuntar al valor guardado en el registro apuntado
*pointer_display++;//Incrementar la direccion guardada en el registro
*pointer_port++;//Incrementar la direccion guardad en el registro puntero
if((pointer_display-1)==&display_data[3]) pointer_display=&display_data[0];
if((pointer_port-1)==&display_port[3]) pointer_port=&display_port[0];
}
//REINICIO DE INTERRUPCION POR TMR1
TMR1H=0XEA;//Cargamos TMR1 para lograr 0,25 ms
TMR1L=0X60;
PIR1.TMR1IF=0;//Reset flag de desbordamiento por TMR1
}
//--------------------------- RUTINA PRINCIPAL ---------------------------------
void main() {
ADCON1=0X06; //Configurar IOs como digitales
TRISA=TRISD=0X00;
PORTA=PORTD=0X00; //Inicializar con los puertos apagados
//Configurar la interrupcion por TMR1
PIR1.TMR1IF=0; //Reset flag de desbordamiento del tmr1
TMR1H=0XEA; //Cargamos TMR1 para lograr 0,25 ms
TMR1L=0X60;
T1CON=0b00000001;//<bit_0>: TMR1ON. Habilitar TMR1
//<bit_1>: TMR1CS. Usar reloj interno
//<bit_2>: T1SYNC: Bit de sincronizacion de reloj (ignorado)
//<bit_3>: T1OSCEN: Habilita las entradas RC para osc externo
//<bit_4,5>: Prescalador a 1
//<bit_6,7>: No habilitado
INTCON.PEIE=1;//Habilitar la interrupcion de perifericos
INTCON.GIE=1;//Habilitar la interrupcion general
PIE1.TMR1IE=1;//Habilitar interrupcion por tmr1
//Inicializamos los punteros declarados antes de ser usados. Cargamos punteros
//con la direccion de los primeros registros que debe mostrar en la primera inte
//rrupcion.
pointer_display=&display_data[0];//Cargar puntero con primera direccion
pointer_port=&display_port[0];//Cargar puntero con primera direccion a mostrar
while (1){
seconds_bin++;
Delay_ms(100);
if (seconds_bin==99) {
minutes_bin++;
seconds_bin=0;
}
BIN_7SEG(minutes_bin,seconds_bin);
if (minutes_bin==99 & seconds_bin==99) minutes_bin=seconds_bin=0;
}
}
//++++++++++++++++++++DESARROLLO DE LAS FUNCIONES+++++++++++++++++++++++++++++++
//*****DESARROLLO DE FUNCION PARA CONVERTIR LOS DATOS BINARIOS A 4 VALORES GUAR
//DADOS EN DISPLAY_1 AL 4. F_DATA1BIN CONTIENE EL PRIMER VALOR BINARIO QUE SE
//CONVERTIRA EN BCD Y LUEGO A 7SEGMENTOS. F_DATA2BIN ES EL SEGUNDO VALOR BINARIO
//f_data1bin es convertido y luego guardado en displays 1 y 2
//f_data2bin sobre los displays 3 y 4
void BIN_7SEG(int f_data2bin,int f_data1bin){
valor1_bcd=Dec2Bcd(f_data1bin);//Funcion para convertir binario a BCD
data1_7seg=valor1_bcd & 0b00001111;//Separar el primer digito BCD
data2_7seg=(valor1_bcd & 0b11110000)>>4;//Separar el segundo digito BCD
display_data[0]=BCD_7SEG(data1_7seg);
display_data[1]=BCD_7SEG(data2_7seg);
valor2_bcd=Dec2Bcd(f_data2bin);//Funcion para convertir binario a BCD
data3_7seg=valor2_bcd & 0b00001111;//Separar el primer digito BCD
data4_7seg=(valor2_bcd & 0b11110000)>>4;//Separar el segundo digito BCD
display_data[2]=(BCD_7SEG(data3_7seg)|0b10000000);//Colocamos el punto decimal
display_data[3]=BCD_7SEG(data4_7seg);//Funcion convierte el valor BCD a 7 Seg
}
//*****DESARROLLO DE FUNCION PARA PASAR BCD A 7 SEGMENTOS
int BCD_7SEG(int f_digit){
switch (f_digit){
case 0: return 0x3F; //0x3F es el código 7-segmentos del 0.
case 1: return 0x06; //0x06 es el código 7-segmentos del 1.
case 2: return 0x5B;
case 3: return 0x4F;
case 4: return 0x66;
case 5: return 0x6D;
case 6: return 0x7D;
case 7: return 0x07;
case 8: return 0x7F;
case 9: return 0x6F;
}
}