Está en la página 1de 4

Punteros a funciones y Callbacks en Code Warrior

Ernesto Tolocka
www.mundomicro.com.ar

Introducción

Veremos en este artículo un ejemplo sencillo de cómo utilizar punteros a funciones instalando
un callback en la rutina de atención de interrupciones del Módulo Timebase de un GP32.

Punteros a funciones

Algunas personas afirman que existen dos tipos de lenguajes de programación: los que
permiten el uso de punteros y los que sirven para jugar a programar.
Sin ir hasta ese extremo, debemos coincidir en que la habilidad de manejar punteros es una de
las características del lenguaje C que lo hacen tan versátil y tan poderoso, sobre todo en las
aplicaciones con microcontroladores, y al mismo tiempo una de las fuentes de errores mas
comunes cuando no se manejan cuidadosamente.
Sin pretender dar una explicación exhaustiva sobre el tema de los punteros, diremos sólo que
son variables que no contienen directamente un valor (como puede ser una variable de tipo
char) si no que generalmente contienen la dirección donde vive otra variable. Se dice que un
puntero “apunta” a una variable. Esta otra variable puede ser de cualquier tipo: char, int, float,
hasta puede ser otro puntero.
Además de apuntar a variables, se puede hacer que un puntero apunte a una función. Un
puntero a una función contiene la dirección donde reside esa función y por su intermedio, esa
función puede ser llamada o invocada. Esto tiene muchas aplicaciones. Por ejemplo, se puede
tener un array de punteros a funciones y llamar a una función en particular en base a un valor
numérico que se utiliza como índice de dicho array, sin tener que apelar a una estructura de
tipo switch.

Callbacks

Un callback es un recurso muy utilizado por los desarrolladores de kernels, sistemas operativos
o librerías de uso específico que permite que el programador de aplicaciones realice un
llamado a una función de su propiedad cuando es ejecutada un función del kernel, sistema
operativo o librería. Es el equivalente de un “gancho” que se deja disponible en el sistema
asociado a una determinada función y que puede ser usado o no. Si queremos llamar a una
función de nuestra propiedad cuando se active la función del sistema, simplemente la
“enganchamos” a ella. El gancho es el callback.
Por ejemplo, si escribimos una librería para manejar el módulo SCI de un GP32, con funciones
para recibir y transmitir caracteres con un buffer, sería conveniente dejar disponible un callback
en la función que recibe un carácter para darle la posibilidad al usuario de nuestra librería de
llamar a una función suya cada vez que llega un carácter por el SCI.

Implementación

El siguiente es un ejemplo que aprovecha el módulo Timebase del GP32 para generar
interrupciones periódicas. El aspecto de la ISR es el siguiente:

interrupt 17 void TBM_ISR(void) {


TBCR |= TACK; // Acknowledge Int

(*TBMCallBack)();
}
Como puede verse, además de borrar el pedido de interrupción, se llama a la función apuntada
por la variable TBMCallBack, que es un puntero definido de la siguiente forma:

void (*TBMCallBack)(void); //Puntero a la función

Este puntero inicialmente no está inicializado. Para que apunte a una función cualquiera,
simplemente debemos cargarle la dirección de dicha función, de la forma:

TBMCallBack=&MiFuncion;

En el programa que sigue a modo de ejemplo, se han definido dos funciones para controlar
esto. La primera, TBMInit, hace apuntar el Callback a una función dummy, es decir, que no
hace nada. Esto es por razones de seguridad, ya que una llamada a la ISR con el puntero sin
inicializar puede tener consecuencias impredecibles. Obviamente, esto también puede hacerse
directamente en la etapa de inicialización del código de la aplicación, sin necesidad de escribir
una función.
La otra, instala la función que nosotros deseamos llamar en el callback, por el mismo
procedimiento, esto es, cargando en el puntero su dirección:

void TBMFnc (void *mifuncion) {

TBMCallBack=mifuncion;

El resto de las funciones se emplean para controlar las funciones básicas del TBM, tales como
inicializarlo, prenderlo o apagarlo.

Código

El siguiente es el código, que fue compilado con CW 2.1 sobre un GP32.

//*****************************************************************//

/****************** VARIABLES GLOBALES DEL SISTEMA ******************************/


#pragma DATA_SEG SHORT _DATA_ZEROPAGE
//Indica al compilador que variables se almacenaran en la zona de memoria directa

#include "PE_Types.h"
#include "PE_Error.h"
#include "PE_Const.h"
#include "IOgp32.h"

//Macros para manejar bits

#define BIT_SET(x,bitNo) ((x) |= 1<<(bitNo))


#define BIT_CLR(x,bitNo) ((x) &= ~(1<<(bitNo)))

//Macros para interrupciones

#define ENABLE_INT {asm cli;}


#define DISABLE_INT {asm sei;}

//Funciones del TBM

#define TBON 2
#define TBIE 4 //Timebase Interrupt Enabled
#define TACK 8 //Ack del TBCR

void (*TBMCallBack)(void); //Puntero a la función

interrupt 17 void TBM_ISR(void) {


TBCR |= TACK; // Acknowledge Int

(*TBMCallBack)();
}

void TBMDefault (void) {

//Función que no hace nada por si no se instala el Callback

void TBMInit (void) {

//Inicializa el callback. Debe llamarse antes de prender el TBM!

TBMCallBack=&TBMDefault;

void TBMSet (unsigned char divisor) {

//Fija el valor del divisor del TBM


//En esta accion el TBM se detiene.

TBCR=divisor<<4; //Fijar Rate Selection

void TBMOn (void) {

//Arranca el TBM

TBCR |= TBIE;
TBCR |= TBON; //Prende TBM

void TBMOff (void) {

//Detiene el TBM

TBCR &= ~TBON;

void TBMFnc (void *mifuncion) {

TBMCallBack=mifuncion;

}
void MiFuncion (void) {

//Esta es la función que se instala en el callback


//A modo de ejemplo, invierte el bit 7 del PORTA cada vez que es invocada

PTA^=0x80;

//Fin funciones TBM

//*********************************************************************************//
//*********************************************************************************//

void main (void)


{

CONFIG1 = 1; //Parar COP

DDRA = 0xff; //Todo salida

TBMInit (); //Inicializar


TBMFnc (&MiFuncion); //Instalar nuestra función en la ISR del TBM
TBMSet (4); //Fijar divisor del TBM
TBMOn (); //Prender TBM

ENABLE_INT //Habilitar ints

while (1) { //No hace nada


}

Referencias

MC68HC908GP32 Technical Data (Motorola)


Motorola HC08 Compiler (Metrowerks)
Manual Smart Linker (Metrowerks)
Como implementar una rutina utilizando el módulo de Time Base de los microcontroladores
HC908 de Motorola. (Lucas Loizaga)

También podría gustarte