Apuntes de Programación

  • Materia: Programación
  • Destinado a: Ingenierias y Tecnicaturas de la Universidad Nacional de San Martín
  • Catedra: Alonso, Andrés Alberto

Planificación de la materia

Guías

Ejercicios Resueltos

  1. Probando punteros

     
  2. Más punteros
  3. El buen amigo Malloc();
  4. Strings y Malloc();
  5. Ejercicio 5 – Estructuras
  6. Ejercicio 12 – Listas y TADs
  7. Modelo de parcial resuelto

Lecturas recomendadas

  • http://c.conclase.net/
  • The C programming Language, Dennis Ritchie & Brian Keringhan

:wq!

Programando RTC MC146818 de motorola

rtcEl año pasado, cursando una materia de programaciòn para la facultad, me toco hacer un trabajo que consistia en programar este integrado. Como todos los integrados RTC ( Real Time Clock ) el MC146818 es usado para conservar un reloj de cierta precision dentro de dispositivos electronicos, como generador de ondas cuadradas, como alarma, etc.

A real-time clock (RTC) is a computerclock (most often in the form of an integrated circuit) that keeps track of the current time. Although the term often refers to the devices in personal computers, servers and embedded systems, RTCs are present in almost any electronic device which needs to keep accurate time.

Descripcion del RTC MC146818

Los primeros 10 bytes de esta RAM son empleados para gestionar la fecha y la hora y los 4 siguientes son registros (A, B, C y D); los 50 restantes quedan a disposición del usuario.

rtc

La salida SQW genera una onda cuadrada, cuya frecuencia es programable (útil para alarmas). La línea -IRQ seencarga de solicitar las interrupciones periódicas si están habilitadas. La línea de entrada -RESET reinicializa el integrado asignando valores por defecto a ciertos bits de los registros B y C, aunque no afecta a la fecha/hora ni a la memoria. La entrada PS debe mantenerse a nivel bajo cuando se alimenta el chip hasta que la tensión  se estabilice, poniéndose después en alto; esta entrada está asociada al bit VRT del registro D que indica si el integrado está en condiciones de operar. El bus bidireccional de direcciones y datos está multiplexado (líneas AD0..AD7): en los flancos de bajada de la entrada de validación de direcciones (línea AS) contiene direcciones, y datos en los flancos de subida de la entrada de validación de datos (línea DS). La línea -R/-W indica si la operación es de entrada o salida; -CE permite habilitar el chip o desconectarlo de los buses.

memoria

REGISTROS DEL MC146818

REGISTRO A (lectura/escritura, excepto UIP).
Este registro sirve para indicar al integrado qué tipo de reloj lo gobierna, así como elegir la frecuencia de la interrupción periódica programable y la de la salida SQW. También contiene un bit que indica si hay una actualización del reloj en curso, lo que sucede una vez cada segundo, ya que en ese preciso instante no se pueden leer los registros con objeto de evitar lecturas incorrectas.

regA

El bit UIP (Update In Progress), de sólo lectura, se pone a 1 mientras se actualizan los primeros 14 bytes de la memoria y poco tiempo antes de que comience dicha actualización. Antes de acceder a estos bytes, hay que esperar a que el bit UIP se ponga a cero (si no lo estaba ya): con el bit UIP a 0, es seguro que en un intervalo de al menos 244 microsegundos no se va a producir ninguna actualización, por lo que hay tiempo suficiente para acceder (sin prisas, pero tampoco con pausas). La actualización dura 248 microsegundos (1984 con relojes de 32768 Hz).

Los bits RS0..RS3, de selección de velocidad, definen la frecuencia de la onda cuadrada generada en SQW y/o la de la interrupción periódica,

REGISTRO B (lectura/escritura).

     En este registro hay bits útiles, entre otros, para controlar la inicialización de la fecha y hora, para habilitar o inhibir las diversas interrupciones y para establecer ciertas características de operación.

regB

     El bit SET puede ser establecido a 1, con lo que cualquier ciclo de actualización de los primeros 14 bytes de la RAM resulta abortado: de este modo, es factible proceder a inicializar la fecha y la hora sin el riesgo de que se produzca en medio una actualización. Este bit no se ve afectado por la señal -RESET.

El bit PIE (Periodic Interrupt Enable) sirve para permitir la interrupción periódica cuando es puesto a 1; tras una señal -RESET es puesto a 0. El bit AIE (Alarm Interrupt Enable) ha de estar a 1 para habilitar la interrupción de alarma; también es puesto a cero tras un -RESET. El bit UIE (Update Interrupt Enable) sirve para habilitar o inhibir la interrupción de fin de actualización, que se produciría tras cada actualización del reloj; la señal -RESET baja el bit UIE. Por último, el bit SQWE (Square Wave Enable) permite habilitar o inhibir la señal de onda cuadrada de la salida SQW; también es borrado ante una señal -RESET.

El bit DM (Data Mode) permite seleccionar datos en binario (1) o BCD (0) en los bytes de fecha y hora; la señal -RESET no afecta a este bit. El bit 24/12 sirve para elegir entre el modo 12 horas del reloj (bit a 0) o el de 24 (bit a 1): en el modo de 12 horas, el bit más significativo del byte de la hora estará activo para indicar “PM”. Si bit DSE está activo, el último domingo de abril la hora pasa de 1:59:59 AM a 3:00:00 AM; en el último domingo de octubre pasa de 1:59:59 AM a 1:00:00 AM (sólo la primera vez, claro) para ajustarse al cambio de hora oficial; este bit no es afectado por -RESET.

REGISTRO C (sólo lectura).

Este registro contiene bits que informan de las interrupciones que se producen. Permite identificar al ordenador qué o cuáles interrupción(es) se ha(n) producido.

regC

     El bit IRQF (Interrupt ReQuest Flag) se activa cuando el bit PF y el PIE (registro B) están activos, o bien cuando el bit AF y el AIE (registro B) están activos, o bien cuando UF y el bit UIE (registro B) están activos. Es decir, IRQF se pone en alto cuando es necesario que se produzca una interrupción: la línea -IRQ se encarga de pedirla entonces. Por su parte: PF (Periodic Flag), AF (Alarm Flag) y UF (Update Flag) indican si es necesario que se produzca la interrupción correspondiente. Todos los bits de este registro son borrados ante una señal -RESET, pero también ante una lectura por software del registro C.

REGISTRO D (sólo lectura).

Este registro contiene sólo el bit VRT (Valid RAM and Time). Este bit está a cero cuando la patilla PS está a cero (PS se eleva a 1 cuando la tensión de alimentación es correcta). Por software, el bit VRT puede ser puesto a 1 mediante una simple lectura del registro D (si la patilla PS=1), con objeto de indicar que la fecha y hora establecidas son correctas; si fallara la alimentación, al caer la tensión en la patilla PS este bit pasaría de nuevo a cero. VRT no es afectado por -RESET.

regD

Bien, ahora pasemos a lo concreto. El integrado fue programado en lenguaje C. Lo bueno de estar trabajando con este tipo de circuitos integrados es que la mayoria, por no decir todas, las motherboards tienen uno. Por lo que podemos jugar seteando el RTC interno de nuestra PC, graficando la onda cuadrada que se esta generando en tiempo real, etc, sin la necesidad de fabricarnos una interfaz y programar uno externo.

Consignas del trabajo practico:

Resolver los siguientes problemas accediendo a los puertos (ports) de hardware en C sobre Linux

1) Mostrar el contenido de los registros 0 al 13 del RTC (reloj de tiempo real, chip MC146818). El contenido se debe mostrar en formato hexadecimal, decimal y binario.

2) Utilizando el RTC programar un timer que se active:a) A determinada hora ingresada por teclado (HH:MM:SS).b) Cada minuto.

3) Utilizando el RTC obtener un tren de pulsos de 1Hz y graficarlo en pantalla usando caracteressimples, por ejemplo:

0—+—1—+—2—+—3—+—4—+—5—+—6—+—7—+—8—+—9—+—

___|_______|_______|_______|_______|_______|_______|_______|_______|_______|__

El programa debe graficar al menos 10 segundos.

4) Enviar por el puerto paralelo el campo minutos de la hora obtenida del RTC.

 

Codigo fuente:

TP_Programacion.cpp

//UNSAM
//Trabajo Practico Final de programacion




#include <sys/io.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "implementacion.h"




//Programa principal




int main(void){
    int opc;
    
    
    if(ioperm (RTC,2,1)){; //Agregamos permisos para acceder a los puertos
		printf("ERROR AL OBTENER PERMISOS PARA ABRIR PUERTOS\n");
		exit(1);
	}
	
    do{
		opc = menu();
		
		switch(opc){
			case 1:
				 mostrarRegistro();
			break;
			
			case 2:
				  setAlarma();
			break;
			
			case 3:
				  setMinutosAlarma();
			break;
			
			case 4:
				  trenPulso();
			break;
			
			case 5:
				  enviarLPT();
			break;
			
			case 6:
				 mostrarAlarmFlag();
			break;                   
		}
			if(opc<1 || opc>7){
				printf("La opcion ingresada es incorrecta. Vuelva a intentarlo. \n");         
			}  
    }while(opc!=7); 
        
    printf("\nADIOS!\n");    
    ioperm (RTC,2,0); //Quitamos permisos para acceder a los puertos
}

Implementacion.h

//Definimos constantes




#define RTC 0x70 //Real time Clock
#define LPT 0x378 //LPT1




//Definicion de Prototipos
int leer(int a); //Si el valor de retorno es 0, entonces no se pudo leer el puerto
void mostrarRegistro(void);
void setAlarma(void);
void setMinutosAlarma(void);
void trenPulso(void);
void enviarLPT(void);
//Funciones Auxiliares
int menu();
void decimalBinario(int a);
void UIP(void);
void ingresarValor(int &a, int cod);
void mostrarAlarmFlag(void);
int decimalABinario(int valor);




//Implementacion
void UIP(void){
    int registro = 0X0A;
    int mascara = 0X80; 
    int puerto;
    
    puerto = leer(registro)&mascara; //Vemos el estado del Update in progress para ver si estan disponible la memoria del RTC
    
    while(puerto==1){
        usleep(244);                         
        puerto = leer(registro)&mascara; //Vemos el estado del Update in progress para ver si estan disponible la memoria del RTC
    }     
}




int menu(){
    int opc=0,ss,hh,mm;
    
		ss = leer(0x00);
		mm = leer(0x02);  
		hh = leer(0x04);
    
		system("clear");
		printf("########### Reloj de Tiempo Real #############\n");
		printf("##############################################\n");
		printf("###          HORA ACTUAL: %02x:%02x:%02x         ###\n",hh,mm,ss);
		printf("##############################################\n");  
    
        printf("1- Mostrar registros (0-13)\n");
        printf("2- Programar alarma (HH:MM:SS)\n");
        printf("3- Programar alarma por minuto\n");
        printf("4- Graficar tren de pulso (1Hz)\n");
        printf("5- Enviar datos puerto LPT\n");
        printf("6- Mostrar Alarm Flag\n");
        printf("7- Salir\n");
        printf("#: ");
        scanf("%d", &opc);
        
    return opc;   
}




int leer(int DIR){ 
    int puerto = 0;
    
    outb(DIR,RTC);
    puerto = inb(RTC+1);
    
    return puerto;
}




void mostrarRegistro(void){
    char s;
    int i=0;
    int puerto=0;
    
    printf("\tREG \tDEC \tHEX \tBIN\n");
        
    for(i=0;i<=12;i++){
        puerto = leer(0x00+i);
        
        printf ("\t%02x",i);//REG
		printf ("\t%d",puerto);//DEC
		printf ("\t%x", puerto);//HEX
		printf ("\t%d\n",decimalABinario(puerto)); //BIN
    } 
                  
    printf("Presione una tecla para continuar...");
    scanf("%c",&s);
    getchar();
}




void setAlarma(void){
	 char s;
     int hh,mm,ss;
     int alarma;
     
     UIP();
     
     ingresarValor(hh,1);
     ingresarValor(mm,2);
     ingresarValor(ss,3);
     
     outb(0x01,RTC);  //Set segundo de alarma.
     outb(ss,RTC+1);




     outb(0x03,RTC);  //Set minuto de alarma.
     outb(mm,RTC+1);




     outb(0x05,RTC);  //Set hora de alarma.
     outb(hh,RTC+1);




     outb(0x0B,RTC);  //Seteamos AIE=1, seteamos 24/12=1 en Registro B. Mascara = 00100010
     alarma = inb(RTC+1); //Guardamos en la variable alarma el valor de el registro 0X0B 
     alarma = alarma|0x22; //Aplicamos la mascara deseada a la variable
     outb(alarma,RTC+1); //Escribimos en el RTC la variable con la mascara configurada
	 
	 //Leo lo que escribi para checkear que sea lo correcto
	 ss = leer(0x01);
	 mm = leer(0x03);  
	 hh = leer(0x05);
		  	  
     printf ("ALARMA\n"); //Muestra ALARMA.
	 printf ("HH:MM:SS =  %d:%d:%d\n",hh,mm,ss);              
     
     outb (0x0C,RTC);//0x70 apunta a Reg. C.
     //Lectura del Reg. C para resetear el AF(Alarm Flag)
     
    while(inb(RTC+1)&0x20!=0){
		usleep(1000);
	}




	printf("##########################");
	printf("\n######ALARMA ACTIVADA#####\n");
	printf("##########################");
	printf("\nHORA: %02x:%02x:%02x  \n",hh,mm,ss);
	
    printf("\nPresione una tecla para continuar...");
    scanf("%c",&s);
    getchar(); 
}




void setMinutosAlarma(void){
    char s;
    int alarma; 
    
    UIP();
              
    outb (0x01,RTC);
    outb (0x00,RTC+1); //Set segundo de alarma en cero.




    outb (0x03,RTC);
    outb (0xff,RTC+1); //Código indiferente para minuto de alarma.
    
    outb (0x05,RTC);
    outb (0xff,RTC+1); //Código indiferente para hora de alarma.




    outb(0x0B,RTC);  //Seteamos AIE=1, Mascara = 00100000
    alarma = inb(RTC+1); //Guardamos en la variable alarma el valor de el registro 0X0B 
    alarma = alarma|0x20; //Aplicamos la mascara deseada a la variable
    outb(alarma,RTC+1); //Escribimos en el RTC la variable con la mascara configurada




    printf("Alarma de minuto activada.\n");




    outb (0x0C,RTC);//0x70 apunta a Reg. C.
    inb(RTC+1);//Lectura del Reg. C para resetear el AF(Alarm Flag)     




    printf("Presione una tecla para ingresar el siguiente valor...\n");
    scanf("%c",&s);
    getchar();    
}




void ingresarValor(int &a, int cod){
    int opc;
    
    if(cod==1){
        printf("Ingrese HORAS: ");    
    }else if(cod==2){
        printf("Ingrese MINUTOS: ");
    }else{
        printf("Ingrese SEGUNDOS: ");      
    }
    
    scanf("%d", &opc);
    
    a = opc;
}




void enviarLPT(void){
     
     int mm,mn;
              
    if (ioperm (LPT,3,1)){
        perror ("0x378, 0x379, 0x37A: acceso denegado.");
        exit (1);
    }




    UIP();
    
    outb (0x02, RTC);          //0x70 apunta a Reg. 2 (minuto de reloj).
    mm = inb(RTC+1);           //Guarda valor de Reg. 2 en mm.
    mn = ~mm;                   //Niega mm.
    outb (mn, LPT);            //Envía nn al registro de datos (378) del LPT1.)
    outb (0x00, LPT+2);        //Set Strobe=0 (para enviar).
    outb (0x01, LPT+2);        //Set Strobe=1.




    if (ioperm(LPT,3,0)){
        perror ("0x378, 0x379, 0x3   7A: al quitar permisos.");
        exit (1);
    }     
}




void trenPulso(void){
    
    int a=0,regA,alarma,i=0;
    
    outb (0x0A, RTC);        //Set RS3, RS2, RS1, RS0 =1 en Reg. A para fijar la Frec. de la onda cuadrada en 2 Hz.
    regA = inb(RTC+1); 
    regA = regA|0x0F;
    outb (regA, RTC+1);




    outb (0x0B, RTC);         //Set PIE=1 en Reg. B
    alarma = inb(RTC+1);
    alarma = alarma|0x40;
    outb (alarma, RTC+1);




    outb (0x0C, RTC);   //0x70 apunta a Reg. C.
    
    for (i=0;i<20;i++){
		if(i%10==0){
		    printf("\n");	
		}
        //Bucle hasta que PF de Reg. C  se haya activado 2 veces (2x500ms).
        while (a!=2){
            printf ("_");
            fflush (stdout);
            usleep (200000);
            
            if((inb(RTC+1)&0x40)!=0)
				 a++;
			
        }
        
        a=0;                    //Contador de activaciones de PF vuelve a 0.
        printf("||*||");
    }
    
    printf ("\n\n");     
}




void mostrarAlarmFlag(){
     char s;
     int flag=0;
     
     outb(0X0C,RTC);
     flag = inb(RTC+1);
     flag = flag&0X20;
     
     printf("\n|######ESTADO######|\n");
     if(flag!=0){
         printf("      ACTIVADO\n");      
     }else{
           printf("     DESACTIVADO\n");      
     }
    printf("|##################|");




    printf("\nPresione una tecla para continuar...");
    scanf("%c",&s);
    getchar();
}




int decimalABinario(int valor){
	int resto,i=1,binario=0;
	
		while (valor > 0){ 
          	resto = valor % 2;
          	valor = valor / 2;
          	binario = binario + (resto*i);
          	i = i * 10;
        } 




    	return binario;   
}

En funcionamiento:

1

Registros

 

2

Alarma

 

3

Tren de pulso

Fuentes:

  • http://atc.ugr.es/docencia/udigital/1212.html
  • http://en.wikipedia.org/wiki/Real-time_clock

:wq!