Funciones

Llega un punto en que nuestros programas tienen trozos de código que se repiten constantemente o solo deben ejecutase según ciertas condiciones, pero que básicamente es el mismo código y no tiene gracia que lo escribamos cada vez que lo necesitemos.

Las funciones son eso, trozos de código que usamos repetidamente dentro de nuestro programa y al que podemos acceder al momento que lo requiramos. También hay momentos en que solo lo usemos una única vez, pero que por organización y fácil lectura es bueno que también este dentro de una función.

Una función básica tiene esta apariencia:

void nombreFuncion(void){
	// Código que ejecuta
}

Una función como la anterior solo ejecuta un código en específico y no hace más.

Pero hay momentos en los que necesitamos que una función nos entregue un resultado o a la que debamos pasar algún o algunos valores para que procese.

Es por eso que al momento de crearla debemos saber qué hará en concreto nuestra función, que tipo de resultado nos puede arrojar y si necesitamos pasarle información para que la procese.

Una función, por ejemplo, que nos realice la suma de dos números y nos entregue un resultado puede ser así:

int suma(int numero1, int numero2){
	return numero1 + numero2;
}

Veamos entonces, esta función de nombre “suma” recibe entre sus paréntesis dos números tipo entero (int), estos valores son llamados parámetros, para este caso recibe dos, uno llamado numero1 y otro numero2.
Dentro de la función se encuentra la palabra reservada return, luego de esta estará escrito lo que queremos devolver como resultado de la función, que no es más que la suma de los dos parámetros de entrada.
Tal resultado será otro valor de tipo entero (int) como se especifica al iniciar la función.

Un ejemplo corto del uso de esta función es el siguiente:

int suma(int numero1, int numero2){	// Creamos la función suma, recibe 
						// dos números enteros como parámetros y
 						// como resultado devuelve otro valor entero.
	return numero1 + numero2;		// Con la palabra reservada return
 // entregamos el resultado de la función 
}						// Al final cerramos con llaves “}”

int a = 100;					// Una variable a con un valor
int b = 50;					// Una variable b con otro valor
int c;						// Una variable c vacía.

c = suma(a, b);					// La variable c la hacemos igual a la función suma
						// a la que le pasamos separados por comas los
						// parámetros.

Al ejecutar la función a, actuará como numero1 y b como numero2, y al final el resultado será pasado a la variable c, que almacenará el valor de la suma, es decir, 150.

Una función puede recibir tantos parámetros como necesitemos, pero solo entregar uno o ningún resultado, los tipos de variables pueden ser int, char, float o el que deseemos, solo debemos tener en cuenta ser cuidadosos al escogerlos, para no sobredimensionar el tamaño de las variables o hacerlas tan pequeñas que no nos quepa el valor resultado o del parámetro.

En este próximo ejemplo vamos a ver el uso de las funciones en un programa real con algo que ya aprendimos a hacer, usemos la conversión análogo digital y la multiplexación para mostrar en 4 display de 7 segmentos la temperatura medida por un sensor LM35.

Usemos el mismo circuito de la multiplexación, solo añadamos el sensor LM35.

Recordemos que el sensor LM35 se alimenta a 5 voltios y por cada grado Celsius entrega 10 milivoltios (1°C = 10mV).

Con eso en mente este es el código:

#include <xc.h>

#pragma config FOSC = XT        // Oscilador con cristal de cuarzo de 4MHz conectado en los pines 15 y 16
#pragma config WDTE = OFF       // Perro guardián (WDT deshabilitado)
#pragma config MCLRE = ON       // Master clear habilitado (pin reset)

#define _XTAL_FREQ 4000000      // Oscilador a 4MHz

//****************************************************
// Primero declaramos las funciones que vamos a usar.
// Al final del codigo las creamos.
//****************************************************
void configPuertos(void);           // Función que configura los puertos.
void configADC(void);               // Función para configurar la conversión.
unsigned int leerCanal(unsigned char canal); // Función para leer una entrada análoga.
void int2bcd(int numero);           // Función que separa un numero en digitos.
void mostrarDisplay(void);          // Función para mostrar el valor en los display

//****************************************************
// Ahora las variables globales.
// Se usan para el enviar el valor digito a digito
//****************************************************
int aux = 0;
char unidades = 0;
char decenas = 0;
char centenas = 0;
char miles = 0;

void main(){
    configPuertos();            // Llamamos la función que configura los puertos.
    configADC();                // Ahora configuramos la conversión.
    unsigned int temperatura = 0;   // Variable donde se almacena el valor obtenido
                                    // luego de la conversión.
    
    while(1){                   // Inicio del código a ejecutar constantemente
        
        temperatura = leerCanal(4); // El resultado de leer el canal 4 (AN4),
                                    // será almacenado en temperatura.
        
        temperatura = (temperatura * 100) / 205;
        /***********************************************************************
         * Sabemos que el LM35 entrega 10mV por cada grado, es decir que 
         * a los 100°C entregará 1000mV, o sea 1V.
         * Si a los 5V el resultado de la conversión es 1023, entonces 
         * 1023/5 = 205
         * Tenemos que a los 100°C hay 1V y como resultado de la conversión un
         * 205 en la variable temperatura.
         * Al trabajar con variables tipo entero (int), no almacenamos decimales
         * ni comas flotantes, por eso el resultado lo multiplicamos por 100,
         * para eliminar las comas y luego dividimos por el valor en 100°C,
         * (205) obteniendo el valor medido.
         * 
         * Ejemplo: Valor medido por el sensor: 50°C
         * 50°C es apróximadamente 102, entonces:
         * 102 * 100 / 205 = 49 (Que se apróxima al valor real).
         **********************************************************************/
        
        int2bcd(temperatura);   // Separamos los digitos de temperatura
        mostrarDisplay();       // Una vez separados, mostramos la temperatura
                                // en los diplay.
    }
}

// Función que configura los puertos
void configPuertos(void){
    TRISA = 0xF0;               // RA0, RA1, RA2 y RA3 salidas para transistores
                                // de multiplexación.
                                // Restantes como entrada
    ANSELA = 0x10;              // RA4 (AN4) del Puerto A como Analogo para LM35,
                                // restantes como digitales.
    TRISB = 0x00;               // Puerto B como salida para display
    ANSELB = 0x00;              // Puerto B como Digital.
}

// Función para configurar la conversión
void configADC(void){
    ADCON1bits.ADPREF = 0;      // Referencia voltaje positivo conectado a la alimentación
                                // del microcontrolador (VDD).
    ADCON1bits.ADNREF = 0;      // Referencia voltaje negativo conectado a la tierra
                                // del microcontrolador (VSS).
    ADCON1bits.ADCS = 1;        // Frecuencia del oscilador dividida entre 8.
    ADCON1bits.ADFM = 1;        // Justificación del resultado a la derecha.
  
    ADRESL = 0;                 // Limpiamos los registros ADRESL
    ADRESH = 0;                 // y ADRESH
}

// Función para leer una entrada análoga
unsigned int leerCanal(unsigned char canal){
    ADCON0bits.CHS = canal;         // Seleccionamos el canal AN0
    ADCON0bits.ADON = 1;            // Habilitamos la conversión Análgo Digital
    ADCON0bits.GO_nDONE = 1;        // Iniciamos el proceso de conversión
                                    // este cambia automaticamente a 0 (cero)
                                    // cuando el proceso finalice.
    while(ADCON0bits.GO_nDONE){     // En espera a que la conversion termine
                                    // cuando el bit pase a cero el ciclo while termina
    }
        
    // En este punto la conversión finalizo y el resultado esta en los registros
    // ADRESH y ADRESL
    
    ADCON0bits.ADON = 0;            // Deshabilitamos la conversión Análogo Digital
    
    return ((ADRESH << 8) + ADRESL); // La función devuelve el resultado en ADRESH y ADRESL
}                                          // Función que separa un numero en dígitos independientes 
                                           // Requiere crear las variables globales:
                                           // aux, unidades, decenas, centenas, miles
void int2bcd(int numero){
 aux = numero;                             // Guardamos en aux el valor en cont centenas = 0;
                                           // Ponemos en cero todos los dígitos decenas = 0; 
 unidades = 0;
 miles = 0;
 while(aux > 999){          // Mientras el valor en aux sea superior a 999
        aux -= 1000;        // Le restamos de a 1000
        miles++;            // y por cada resta aumentamos una unidad al digito miles
    }
    while(aux > 99){        // Mientras el valor en aux sea superior a 99
        aux -= 100;         // Le restamos de 100
        centenas++;         // y por cada resta aumentamos una unidad al digito centenas
    }
    while(aux > 9){         // Mientras el valor en aux sea superior a 9
        aux -= 10;          // Le resto de 10
        decenas++;          // y por cada resta aumentamos una unidad al digito decenas
    }

    unidades = aux;         // Por último, el valor restante lo asignamos a unidades    
}

// Función para mostrar el resultado en los display
void mostrarDisplay(void){
    PORTA = 1;              // Activamos el transistor para las unidades
    PORTB = unidades;       // y pasamos el valor de las unidades al display
    __delay_ms(200);        // Damos un retardo, por lo general en una aplicación real
                            // debe ser de unos 3 a 5 milisegundos, para el caso de la
                            // simulación dejamos 200 ms para apreciar el cambio.

    PORTA = 2;              // Activamos el transistor para las decenas
    PORTB = decenas;        // y pasamos el valor de las decenas al display
    __delay_ms(200);        // Damos el retardo
    PORTA = 4;              // Activamos el transistor para las centenas
    PORTB = centenas;       // y pasamos el valor de las centenas al display
    __delay_ms(200);        // Damos el retardo
    PORTA = 8;              // Activamos el transistor para los miles
    PORTB = miles;          // y pasamos los miles al display
    __delay_ms(200);        // Damos el retardo   
}

Como vemos en el código anterior, todo el programa está organizado por funciones, algunas se ejecutan una vez, como el caso de las que configuran los puertos y la conversión y otras que se repiten indefinidamente como la que lee las entradas, divide el resultado en dígitos, y la que realiza la multiplexación en los display de 7 segmentos.

También hay las que no reciben ni entregan resultados, las que reciben parámetros pero no devuelven información y por ultimo las que requieren de recibir parámetros para devolver el resultado.