Está en la página 1de 59

C

orientado a microcontroladores 8051


Jos Miguel Gil-Garca JULIO 1999

MOTIVACIN ................................................................................................................................................. 1 ESTRUCTURA GENERAL DE UN PROGRAMA EN C............................................................................ 3 OCUPANDO MEMORIA................................................................................................................................ 4 DEFINICIN DE CONSTANTES .......................................................................................................................... 4 DEFINICIN DE VARIABLES ............................................................................................................................. 4 Tipos de datos ........................................................................................................................................... 4 Tipos de variables ..................................................................................................................................... 5 TIEMPO DE VIDA DE UNA VARIABLE ................................................................................................................ 7 OPERADORES Y EXPRESIONES................................................................................................................ 8 OPERADORES .................................................................................................................................................. 8 PRIORIDAD Y DIRECCIN DE EVALUACIN:..................................................................................................... 9 EFECTOS LATERALES: ..................................................................................................................................... 9 CONVERSIN DE LOS TIPOS DE DATOS:.......................................................................................................... 10 SENTENCIAS................................................................................................................................................. 11 SENTENCIAS DE EXPRESIN .......................................................................................................................... 11 SENTENCIAS COMPUESTAS (BLOQUES).......................................................................................................... 11 SENTENCIAS DE CONTROL ............................................................................................................................. 11 if ... else ................................................................................................................................................... 11 while ........................................................................................................................................................ 12 do ... while ............................................................................................................................................... 12 for ............................................................................................................................................................ 12 switch, case, break, default: .................................................................................................................... 13 break........................................................................................................................................................ 14 continue................................................................................................................................................... 14 FUNCIONES................................................................................................................................................... 15 DECLARACIN DE PROTOTIPOS ..................................................................................................................... 15 LLAMADAS A FUNCIONES.............................................................................................................................. 15 DEFINICIN DE LAS FUNCIONES .................................................................................................................... 15 PASO POR VALOR Y PASO POR REFERENCIA ................................................................................................... 16 FICHEROS CABECERA O HEADERS (.H)........................................................................................................... 17 LA FUNCIN MAIN Y SUS ARGUMENTOS ARGC Y ARGV.................................................................................. 17 DEFINICIN DE MACROS ............................................................................................................................... 18

C orientado a microcontroladores 8051

FUNCIONES ESTNDAR DEL C ............................................................................................................... 21 FUNCIONES DE ENTRADA-SALIDA: STDIO.H .................................................................................................. 21 FUNCIONES DE ENTRADA SALIDA DE DISCO: STDIO.H .................................................................................... 26 SALIDA DE DATOS POR LA IMPRESORA .......................................................................................................... 28 FUNCIONES MATEMTICAS: MATH.H ............................................................................................................ 29 FUNCIONES CON CADENAS: STRING.H, CTYPE.H ............................................................................................ 30 FUNCIONES PARA LA CONVERSIN DE NMEROS Y CADENAS: STDLIB.H ....................................................... 31 FUNCIONES DE ASIGNACIN DINMICA DE MEMORIA: STDLIB.H ................................................................... 32 Modelos de memoria ............................................................................................................................... 32 FUNCIONES CON DIRECTORIOS: DIR.H ........................................................................................................... 33 FUNCIONES DE TIEMPO: TIME.H .................................................................................................................... 34 FUNCIONES DEL DOS: DOS.H ....................................................................................................................... 35 Interrupciones de software...................................................................................................................... 35 Acceso a puertos ..................................................................................................................................... 37 Interrupciones por hardware .................................................................................................................. 37 Modificar y leer vectores de interrupcin ............................................................................................... 38 Inclusin de cdigo asm.......................................................................................................................... 40 EXTENSIONES AL LENGUAJE C PARA EL 8051.................................................................................. 42 REAS DE MEMORIA EN EL 8051................................................................................................................... 42 MODELOS DE MEMORIA EN EL 8051.............................................................................................................. 43 DECLARACIN EXPLCITA DE TIPOS .............................................................................................................. 44 PUNTEROS..................................................................................................................................................... 46 Punteros genricos.................................................................................................................................. 46 Punteros a zonas especficas de memoria ............................................................................................... 46 DECLARACIONES DE FUNCIONES ................................................................................................................... 47 [small|compact|large] ............................................................................................................................. 48 [using n] .................................................................................................................................................. 48 [interrupt n] ............................................................................................................................................ 48 [reentrant]............................................................................................................................................... 49 MACROS PREDEFINIDAS ................................................................................................................................ 50 REGISTROS.................................................................................................................................................... 51 FUNCIONES DE ENTRADA Y SALIDA .............................................................................................................. 51 OTRAS FUNCIONES ........................................................................................................................................ 51 EJEMPLO 8051: ............................................................................................................................................. 54

C orientado a microcontroladores 8051

ii

Motivacin
Estos apuntes han sido escritos como documentacin del curso C orientado a microcontroladores 8051 que tuvo lugar del 5 al 16 de Julio de 1999 en los III Cursos Estivales del Campus de Alava organizados por IAESTE. El nmero de horas total del curso fue de 30. Estas notas no pretenden ser una explicacin exhaustiva ni de la sintaxis ni de la gramtica del lenguaje, sino tan solo una aproximacin a los aspectos ms bsicos del lenguaje C. De hecho, siempre ser necesario completarlos con las explicaciones de clase... y, probablemente, ni an as. Tampoco estn orientados a grandes y complejas estructuras de listas enlazadas o rboles binarios, sino ms bien, lo que buscan es el uso del PC a bajo nivel, explicando cmo acceder a los puertos, cmo ejecutar los servicios de interrupcin de la BIOS y del DOS, o cmo instalar las propias rutinas de servicio de interrupcin (isr) para interrupciones de hardware (IRQs). Es, por tanto, un complemento de la asignatura Tecnologa y Medidas Electrnicas, que se imparte en el ltimo curso de la especialidad de electrnica de la EUITI. En este curso se ensea (o se intenta, lo juro) la arquitectura y programacin del microcontrolador 8051. Uno de los objetivos del curso es que el alumno conozca la forma en que se puede programar ese procesador en lenguajes de alto nivel, explicndose las extenciones al C estndar que se aplican a la arquitectura del 8051. Se usa para ello la extensin de la casa Keil Software en su versin de evaluacin 1.24. Por ltimo, slo queda resear que el curso, por s slo, no es suficiente para afirmar que una persona sabe C (nunca un curso es suficiente y menos si de lo que se trata es de programacin) pero s debiera de ser acicate para que el alumno quitase los miedos a dicho lenguaje, y lo use como elemento de desarrollo en sus siguientes proyectos tanto fin de carrera como una vez terminada esta. Por motivos varios, nunca ajenos a mi persona, estas notas estarn llenas de erratas, fallos de mayor o menor importancia, o simplemente ser posible replantear secciones para mayor (o quiza alguna) claridad. Cualquier consejo, crtica, o simplemente observacin ser bienvenida. 19.07.1999 Jos Miguel Gil-Garca Versin 1.0 C orientado a microcontroladores 8051 1

Nota del Autor: Este documento ha sido posible a pesar de usarse Microsoft Word 97 para su redaccin, con lo que se demuestra, una vez ms, que la luz del bien siempre triunfa sobre las tinieblas del mal.

C orientado a microcontroladores 8051

Estructura general de un programa en C


// Seccion de includes #include <stdio.h> #include miheader.h /* Seccion de defines */ #define PI 3.14 // Declaracin de prototipos de funciones int Suma(int,int); // Declaracin de variables globales

float flVarGlobal; main(){ // Declaracion de variables locales a la funcion unsigned char Cadena[]=Hola; // comienzo y desarrollo del programa } //Definicion de las funciones cuyo prototipo se declararon int Suma(int a, int b){

// Declaracion de variables locales a la funcion }

C orientado a microcontroladores 8051

Ocupando memoria...
Definicin de constantes La forma ms sencilla de definir una constante es mediante la directiva #define
#define #define PI SALUDO 3.1416 HOLA

No llevan el punto y coma al final de la lnea, porque no son instrucciones, sino directivas. Ahora podramos usar en nuestro programa el identificador PI que sera siempre equivalente a 3.1416. Un identificador declarado con #define, no puede cambiar de valor a lo largo del programa. Otra manera de definir una serie de constantes es con enum. Por ejemplo:
enum COLORES {NEGRO,AZUL,VERDE,CIAN,ROJO}; 0 1 3 {NEGRO = 30,AZUL,VERDE = 50,CIAN, ROJO};

Que sera equivalente a


#define #define #define NEGRO AZUL CIAN

No es necesario empezar desde cero, as


enum COLORES

Definicin de variables La estructura general es:


Tipo <*> Identificador <[numero]>

Tipos de datos Tipo Char Unsigned Char Int Unisgned int Long Unsigend long Float Carcter Carcter Entero Entero Entero Entero Punto Flotante N de Bytes 1 1 2 2 4 4 4 Val. mx -128 0 -32768 0 -2147483648 0 -3.4E+38 -1.18E-38 Double Doble Precisin 8 -1.7E+308 -2.7E-307 Val. Min. 127 255 32767 65535 2147483647 4294967295 +3.4E+38 +1.18E-38 +1.7E+308 +2.7E-307

Se pueden definir nuevos tipos con la ayuda de typedef de la siguiente manera: C orientado a microcontroladores 8051 4

typedef

unsigned int

WORD

De esta manera se crea un sinnimo, en este caso WORD, para unsigned int, que puede ser ms cmodo para el programador. Un tipo especial es el tipo void (vaco) que se usa para indicar que una funcin no devuelve ningn valor, o no recibe ningn parmetro. Tipos de variables Variables: Ocupan de 1 a 8 bytes y es la forma mas simple de reservar memoria para ciertos usos.
char var1;

Punteros: Son variables que en vez de contener un dato normal, contienen la direccin donde se encuentra un dato especfico.
char * ptr;

Arrays: Es un conjunto de datos estrechamente relacionados, del mismo tipo, y con el mismo identificador. Para hacer referencia a un elemento concreto del array se emplea su ndice o nmero de orden desde el comienzo del array, puesto entre corchetes [ ]. La numeracin comienza desde cero. Se pueden definir arrays de una dimensin

[0]

[1]

[2]

[3]

[4]

[5]

[6]

[7]

O multidimensionales. En este caso se usan tantos corchetes como dimensiones tenga el array. Por ejemplo, un array bidimensional (una tabla) tendra dos grupos de corchetes, [i][j], que haran referencia al elemento de fila i, columna j. As se pueden definir matrices tridimensionales:
int arr[4][3][5];

Cadenas: Es una array unidemsional especial, por estar constituida de caracteres ascii, ms un carcter adicional que es el carcter nulo /0 (Cadenas ASCIIZ)
char cad_var[8];

Estructuras: Est definida por la palabra struct y compuesta por otras variables que, al contrario que en los arrays, no tienen que ser del mismo tipo.
struct empleado{ char nombre[30]; int empl_id; char Calle[20]; float salario; };

C orientado a microcontroladores 8051

As definimos la estructura con todas sus variables miembros. Si ahora queremos declarar una variable que tenga esa estructura escribiremos los siguiente:
struct empleado persona = {Txema Pamundi,23,Su calle,23400.0};

De paso hemos inicializado la variable persona con los valores indicados entre llaves. Si queremos modificar alguno de los valores de la estructura deberemos de seguir el siguiente esquema
VariableDeTipoStruct.VariableMiembro = Nuevo valor;

As, para cambiar el salario sera persona.salario = 22000.0 (Cuidado, el nombre no se cambia poniendo persona.nombe = Nuevo Nombre. Es debido a que es una cadena) Un caso especial es la estructura cuyas variables miembro no responden a los tipos estndar (char, int, float, incluso otro struct) sino que son mapas de bits.
struct MapaBits{ unsigned unisgned } Status; PrimerCampo : 1; Segundo Campo : 2; //Ocupa un nico bit, //el menos significativo //Ocupa los dos bits siguientes

En este ltimo ejemplo se ha definido la estructura y se ha declarado la variable Status a la vez. Uniones: Est definido por la palabra union y est compuesta por miembros cuya direccin base en memoria es la misma (a diferencia de la estructura donde que cada variable miembro empezaba donde acababa la anterior)
union ejmplo{ float fltnum; int intnum; }; union ejemplo VariableUnion; VaraibleUnion.fltnum = 3.14; VariableUnion.intnum = 88;

Definir una variable para que contenga: La edad Un conjunto de 100 edades Una temperatura Una medida de longitud en metros que pueda definir hasta centmetros Un punto ms el color de un monitor VGA (640x480x256) El registro AX 100 fichas de trabajadores C orientado a microcontroladores 8051 6

Qu es int *ptrarr[20]();?

Tiempo de vida de una variable Una variable puede ser global o local. La primera significa que los valores en ella definidos duran, tienen sentido, mientras dure la ejecucin del programa. Sern locales si cada vez que se ejecuta un bloque de cdigo se asigna memoria para esa variable, liberndose al salir de dicho bloque y quedando la variable con un contenido indefinido. Las variables definidas antes del main son globales y se les asigna memoria en el momento de compilar, mientras que aquellas que son definidas dentro del cuerpo de una funcin, son locales y se ubicarn en la pila mientras dure la llamada a esa funcin. Al retornar, se devuelve la memoria de la pila que se cogi prestada, por lo que las variables locales dejan de tener significado. A la hora de definir una variable se le pueden aadir algunos modificadores Auto: rara vez se emplea Register: La variable se almacenar en un registro, y as ser su acceso mucho ms rapido. Static: Equivale a hacer global una variable que por su lugar de declaracin slo sera local, es decir, su contenido no se destruir al salir del bloque donde se defini. Extern: La variable se usar en el fichero actual pero se define en otro fichero fuente. Finalmente se puede declarar e inicializar una variable a la vez, en la misma lnea.
float flVar = -15.34;

C orientado a microcontroladores 8051

Operadores y expresiones
Operadores Operadores de expresin primaria () [] . -> Llamada a funcin ndice Miembro de una estructura Puntero en la estructura Operadores de igualdad == != Igual que No igual que

Operadores lgicos bit a bit & ^ | AND bit a bit OR EX bit a bit OR bit a bit

Operadores monarios & * ! ~ ++ -Direccin Puntero Negacin aritmtica Negacin lgica Complemento a 1 Incremento Decremento

Operadores lgicos && || AND OR

Operador ternario ? : Equivalente a if else Operadores de asignacin = += -= *= /= %= &= ^= |= <<= >>= Asignacin simple Asignacin suma Asignacin resta Asignacin multiplicacin Asignacin divisin Asignacin mdulo asignacin AND Asignacin OR-EX Asignacin OR Asignacin desp. a izq. Asignacin desp. a dch.

sizeof Tamao (tipo) Variar tipo, cast Operadores de multiplicacin * / % Multiplicacin Divisin Mdulo

Operadores de desplazamiento de bits << >> A izquierda A derecha

Operadores relacionales < > <= >= Menor que Mayor que Menor o igual que Mayor o igual que

Operador coma , Coma

C orientado a microcontroladores 8051

Prioridad y direccin de evaluacin: Operador ( ) [ ] -> . Post ++ post Pre++ pre-- ! - ~ + & (type) sizeof */% +<< >> < <= == & ^ | && || ?: = %= += -= *= /= &= ^= |= >>= <<= , != > >= Direccin Prioridad Baja Alta

Efectos laterales: Ejemplo 1: Var = x * y / z Ejemplo 2: int var,x=2,y=3,z=5; Var = x * ++y * z *y Cunto vale Var? Ejemplo 3: Ejemplo 4: Var = x * y + y * z int x = 0xA3, y = 0x2C, var; Var = 3 * x & y >> 2; Ejemplo 5: int var,x=2,y=3,z=5; Var = x * ++y + y * z (Efectos laterales)

C orientado a microcontroladores 8051

Para evitar la aparicin de efectos laterales NUNCA deben utilizarse los operadores de incremento (o decremento) sobre una variable que aparezca ms de una vez en la misma expresin (o ms de una vez en los argumentos de una funcin). Conversin de los tipos de datos: Qu ocurre cuando se operan variables de distinto tipo? El C automticamente promociona el tipo de la variable al superior a los dos que se operan. Se pueden usar las siguiente reglas. Float

Double

Char

Int

Unsigned

Long

Unsigned Long

De todas formas, si no se est seguro con el tipo de conversin que se est aplicando, lo mejor es usar el operador cast para especificar el tipo deseado.
int var1,var4=7,var5=3; float var2,var3; var2 = (float)var1/var3; var3 = var4/var5

//Cual es el valor de var3?,2.333?

C orientado a microcontroladores 8051

10

Sentencias
Sentencias de expresin Es una expresion de operandos y operadores acabada en punto y coma.
X = 2 * PI * r; Rd = sqrt(X); //expresin de asignacin //expresin de funcin

Sentencias compuestas (bloques) Es un conjunto de sentencias de expresiones encabezadas por una llave a la izquierda, {, y finalizadas con una llave a la derecha, }. Ese bloque se entiende como un conjunto de expresiones que deben ejecutarse juntas. Sentencias de control if ... else Tiene la sintaxis general:
if (condicion) Sentencia1; else Sentencia2;

Si condicion produce una expresin distinta de cero Sentencia1 se ejecutar, si no, se ejecutar Sentencia2 (si existe, ya que es opcional). Si tenemos ms de una sentencia se debern usar las llaves para agruparlas, as.
if (condicion){ Sentencia01; Sentencia02; Sentencia03 } else{ Sentencia11; Sentencia12; Sentencia12; }

Tambin se puede combinar la palabra clave else con if como en el siguiente pseudcdigo:
if (condicion) Sentencia; else if (condicion) Sentencia; else Sentencia; Ejemplo: if(var == 10) puts(La variable vale 10); else if (var == 12){ puts(La variable vale 12); puts(La variable no vale 10); }

C orientado a microcontroladores 8051

11

else puts(Ni 10 ni 12);

while Tiene la sintaxis general:


while(condicion) Sentencia

Mientras condicion sea distinto de cero, se ejecutar Sentencia. Tambin aqu pueden ir varias sentencias agrupadas en un bloque mediante el uso de llaves. Ejemplo:
while(1){ Ptr++; puts(Hola); }

Es un lazo cerrado que se estar ejecutando eternamente ya que la condicin, al ser evaluada, siempre da distinta de cero. do ... while Su sintaxis es:
do sentencia while (condicion);

Es similar a while, con la diferencia de que en este caso sentencia se ejecutar al menos una vez. Se puede agrupar bloques de sentencias con llaves. for Las sentencias while y do...while se usan cuando el numero de veces que se debe de realizar una tarea es indefinido, o dependiente de un acontecimiento que no se sabe cuando ocurrir. Si, por el contrario se conoce a priori el nmero de veces que debemos de ejecutar una(s) sentencia(s), ser mejor usar un bucle for. Ejemplos: para contar 10 veces:
for(i=0;i<10;++i)Sentencia; for(i=10;i>0;--i)Sentencia; for(i=0;i<20;i+=2)Sentencia for(;i<10;){ Sentencia; i++; }

Su forma de operar se explica en la siguiente figura:

C orientado a microcontroladores 8051

12

For(expr_inic;condicion;expr_incremento)
Sentencia;

switch, case, break, default: Suele sustituir a una serie de if encadenados. Su sintaxis es:
switch(ExpresionEntera){ case ExpresionConstante1: case ExpresionConstante2: default: Sentencias3; } Sentencias1; break; Sentencias2; break;

Se evala ExperesionEntera (que debe dar un entero, no valen flotantes o dobles) y si es igual a la constante ExpresionConstante1, se ejecutaran las Sentencias1 hasta el break. Si ExpresionEntera fuera ExpresionConstante2, se ejecutaran Sentencias2... si no hubiera ninguna constante igual a ExpresionEntera, entonces se ejecutaran las sentencias asociadas al default. Ejemplo:
switch(tecla){ case A: //Hacer lo que sea si tecla == A break; case B: // Hacer lo que sea si tecla == B break; case C: //Hacer lo que sea si tecla == C case D: /*Omitimos el break en C.La opcion C y D compartirn parte o todo el cdigo */ break; default: //Si no es ninguna de ellas }

C orientado a microcontroladores 8051

13

En este caso, si un case lleva varias sentencias, NO hay que poner las llaves para formar el bloque. Este se considera que va desde los dos puntos, hasta el break. break No se emplea nicamente con las sentencias switch ... break, sino que tambin se usa con las sentencias while, do...while, for. El efecto de break es que la ejecucin del programa se sita justo al final del bloque de sentencias a las que afectan esas instrucciones. Se puede decir que se sale del bucle en ejecucin. continue Tambin salta desde la posicin en que se encuentra hasta el final del bucle, pero con la diferencia de que no sale del bucle, sino que este se sigue ejecutando.

C orientado a microcontroladores 8051

14

Funciones
Una funcin o subrutina es un conjunto de sentencias que realizan una operacin concreta. Para poder usar una funcin sern necesarios tres pasos: a) Declarar el prototipo de la funcin b) Definir la funcin c) Llamar a la funcin Declaracin de prototipos La declaracin del prototipo es la definicin de los tipos de los parmetros que se van a usar, as como el tipo del parmetro que se devolver. Void indica que no hay parmetros. Por ejemplo: int suma (int,int);

Declara una funcin llamada suma que necesita dos parmetros del tipo entero, y devuelve un parmetro de tipo entero tambin. Se pueden, para mayor claridad, dar nombres a los parmetros, cuando se declara el prototipo de la funcin, para saber qu hace cada uno. En principio, una funcin en C slo podr devolver un parmetro. Llamadas a funciones La llamada a la funcin se realizar bien dentro del cuerpo principal del programa, main, o bien desde otra funcin del programa. Es preciso tener especial cuidado en enviar el nmero de paramteros especificado en el prototipo, as como del mismo tipo. Si no se hace as tendremos errores a la hora de la compilacin.
Por ejemplo: int s,var1=2,var2=5; s = suma(2,5);

Definicin de las funciones La parte ms difcil es precisamente la de la definicin de las funcines, la de determinar el algoritmo que usaremos para obtener el valor de vuelta. La definicin normalemente va despus del cuerpo principal del programa, o en otro mdulo. Se debe de volver a repetir la declaracin del prototipo, pero ahora, adems, debe seguir el cuerpo de la funcin encerrado entre llaves. Por ejemplo: int suma(int a, int b){
return a+b; }

En la definicin de la funcin ahora ponemos nombres a los parmetros de entrada (a y b en este caso) y sern los que se usen dentro de la funcin. Para devolver un valor de retorno se C orientado a microcontroladores 8051 15

usar return seguido de la variable o expresin que hemos calculado. Si no se devuelve ningn valor, se puede poner return sin ms.

Todo junto quedara algo as: Hemos usado dos identificadores iguales Suma.c
Int suma (int,int); main(){ int a,b,s; a=12; b=15; s=suma(a,b); } int suma(int a, int b){ return a+b; }

para las variables que se usan en main, as como para las que se usan en suma. Esto es posible debido a que tanto la variable a como b estn definidas dentro del

procedimiento main, y por tanto son locales y se pueden definir igualmente en otra funcin de manera igualmente local. Pero de manera general, no tienen porqu tener los mismos nombres los parmetros que se usan para llamar a las funciones y los que se usan dentro de la definicin de la misma.

Paso por valor y paso por referencia Los parmetros de llamada a las funciones merecen una especial atencin. Se pueden pasar de dos maneras: por valor o por referencia. Paso por valor: El compilador hace una copia de los valores que se quieren pasar en otra zona de memoria diferente a la original (concretamente en la pila), de tal manera que si modificamos en la funcin llamada esos valores, los originales no cambian. Paso por referencia: La copia de los parmetros que se pasan ya no corresponden con sus valores, sino con las direcciones (punteros) donde dichas variables residen. Ahora si es posible modificar (consciente o inconscientemente) el valor de la variable original, pues sabemos donde reside en memoria. Un caso especial es el de las cadenas y arrays. El compilador transforma inmediatamente sus identificadores para que sean punteros, no necesitndose (ni permitindose) el uso del operador direccin &. (Si arr es un array a enteros, no podr usar la expresin &arr para pasarla como direccin, sino que solo con poner arra ya estaremos facilitando la direccin donde comienza la matriz. La forma C orientado a microcontroladores 8051 16

&arr[0] tambin est permitida). La manera ms habitual y lgica de pasar matrices y estructuras complejas es por variable, ya que si lo hicisemos por valor, tendramos que copiar en la pila todo el contenido de dicha matriz. Cmo pasaramos una matriz multidimensional? Ejercicios: Transformar el programa anterior para que el paso de valores sea por variable. Transformar el programa suma.c para que realice la suma de dos nmeros sin devolver ningn valor. Ficheros cabecera o headers (.h) Muchas veces se prepara un fichero header donde se incluyen todas las definiciones de constantes (defines), los nuevos tipos definidos (typedef) y los prototipos de las funciones. Un fichero header no tiene nada de especial, pero aporta claridad y modularidad al programa, haciendo ms sencilla la lectura del fichero fuente (.c). Si el fichero creado se llama miheader.h y su directorio es el de trabajo (donde reside el fichero fuente) la linea que se incluir ser #include miheader.h Ejercicio Transformar el programa suma.c creando una cabecera con todas las definiciones del programa. Crear un proyecto con dos mdulos, el programa principal por un lado y la funcin suma por otro, redefiniendo el header. La funcin main y sus argumentos argc y argv Normalmente usamos main sin argumentos, pero si queremos que el programa que escribimos soporte el paso de argumentos desde la lnea de comandos del sistema operativo, deberemos usar los parmetros argc y argv, redefiniendo la funcin main como main(int argc, char *argv[ ]). Argc indica el nmero de entradas separadas por un espacio que se introdujeron, argv es una matriz de punteros a caracteres que contiene cada una de las partes que se introdujeron separadas por comas. Por ejemplo, si el comando que se introduce para ejecutar el programa es miprog.exe hola pepe. Argc valdr 3, argv[0] apunta a la cadena miprog.exe, argv[1] tiene la direccin de hola y argv[3] apunta a la cadena pepe.

C orientado a microcontroladores 8051

17

Definicin de Macros La diferencia entre una macro y una funcin es que la primera se expande dentro del programa al compilarse, mientras que una funcin produce un salto a la direccin donde dicha funcin queda definida. La primera por tanto es ms rpida, pero ocupa ms memoria que la segunda. Para definir macros se usa tambin la directiva define con la siguiente sintaxis
#define nombre_macro(lista_de_argumentos)(expresion_de_la_macro)

Por ejemplo, para pasar a radianes se podra usar el siguiente macro:


#define rad(x) (x * 3.14159/180.0)

o para obtener el mximo entre dos nmeros podramos definir


#define max(a,b) (a>b?a:b)

C orientado a microcontroladores 8051

18

Suma1.c
void suma(int *,int *,int *); main(){ int a=2,b=3,s; suma(&a,&b,&s); } void suma(int *a,int *b, int *s){ *s = *a + *b; }

Suma2.c #include "suma1.h" main(){ int a=2,b=3,s; suma(&a,&b,&s); } void suma(int *a,int *b, int *s){ *s = *a + *b; }

Suma1.h
void suma(int *,int *,int *);

C orientado a microcontroladores 8051

19

Suma.prj Suma30.c
#include "suma3.h" main(){ int a=2,b=3,s; suma(&a,&b,&s); }

Suma31.c
void suma(int *a,int *b, int *s){ *s = *a + *b; }

Suma3.h
extern void suma(int *,int *,int *);

La palabra clave extern tambin se puede usar para acceder a variables globales que fueron definidas en un mdulo pero que se usan en otro. En esta caso la variable se re-definir en el segundo mdulo igual que en el primero, pero anteponiendo extern.

C orientado a microcontroladores 8051

20

Funciones estndar del C


C contiene toda una serie de libreras precompiladas que ahorran tiempo y esfuerzo a la hora de programar. Las libreras estn agrupadas por funcionalidades y lo nico que hay que hacer para usarlas es aadir la directiva #include asociada a la librara en la que reside la funcin que estamos usando. Por ejemplo, las funciones matemticas se encuentran agrupadas en la misma librera y su cabecera es math.h, por lo que la lnea a aadir sera #include <math.h>. Funciones de entrada-salida: stdio.h Son las instrucciones encargadas de la entrada de datos por el dispositivo estndar de entrada y la salida de datos por el dispositivo estndar de salida (normalmente teclado y pantalla respectivamente) printf:
int printf (const char *,[argumentos])

const char * es una cadena formateada. Con formateada se quiere indicar que contendr diferentes caracteres especiales: secuencias de escape y caracteres de formato que darn sentido a la cadena final. Argumento Secuencia de escape Especificacin de formato

printf(El hombre pesa %d kilos \n,peso); Las secuencias de escape realizan tareas especficas dentro de la cadena.

Sec. Escape \a \b \f \n \t \v Beep

Carcter de salida

Retroceso Salto de pgina Salto de lnea Tabulador horizontal Tabulador vertical 21

C orientado a microcontroladores 8051

\r \ \ \\ \? \DDD \xHHH \XHHH

Retorno de carro Comillas dobles Comillas simples Barra invertida Signo de interrogacin Cdigo octal de un carcter Cdigo hexadecimal de un carcter Cdigo hexadecimal de un carcter

Qu salida producen las siguientes instrucciones?


printf(el path es c:\\borlandc\\bin\\); printf(El C es \divertido\\n\a); printf(compras \t\t\t\t ventas \x1B);

Las especificaciones de formato vienen dadas por el smbolo de tanto por ciento, %, seguido de uno o mas especificadores permitidos, con la sintaxis:
%<banderas> <ancho> <.prec> <F|N|h|1> tipo

De todos ellos el nico obligatorio es tipo, que especifica si el argumento asociado se va a convertir en un carcter, una cadena o un nmero. Carcter tipo Tipo de argumento d i u o x X f Entero Entero Entero Entero Entero Entero Punto flotante Conversin del formato Entero decimal con signo Entero decimal con signo Entero decimal sin signo Entero sin signo Entero hexadecimal sin signo (1....9,a..f) Entero hexadecimal sin signo (1....9,A..F) Valor con signo [-]dddd.dddd donde dddd es uno o ms digitos decimales. Saca ceros redundantes a la derecha. e Punto flotante Valor con signo con notacin cientica [-]d.dddd e[signo]ddd, donde signo es + o E g Punto flotante Punto flotante Igual que e, pero aparece E en lugar de e Valor con signo, como e o f, dependiendo del valor o 22

C orientado a microcontroladores 8051

de la precisin. No saca ceros redundantes. G c s n Punto flotante Carcter Cadena Puntero a un int Valor con signo, como g pero con una E Carcter simple Cadena de caracteres ASCIIZ Almacena el nmero de caracteres impresos hasta la direccin especificada. P Puntero Direccin del puntero, hhhh:hhhh para punteros FAR y hhhh para punteros NEAR. Qu salidas originan las siguientes instrucciones?
int var = 484; printf(El valor es %d \n y al dividirlo por 4 da %d\n,var,var/4) printf(-Var vale %i o vale %u\?\n,-var,-var); double var = 57.29578,var1=57.0; printf(Var vale: %f\t %e \t %E,var,var,var); printf(la diferencia es con f: %f y g:%g,var1,var1); char c = !; printf(El carcter es \%c\,c); char frase[]=Pamundi; printf(Txema %s,frase);

Entre el smbolo del tanto por ciento y el tipo podemos situar el ancho, el nmero de caracteres que se van a poner al sacar en pantalla la cadena que se forme con printf. Por ejemplo, la especificacin %12f indica que se usarn doce espacios para poner el valor al que vaya asociada la f, justificado a la derecha. Si no se rellenan los doce huecos, nos pondr espacios en blanco . Pero tambin aqu podemos modificar algunas cosas con banderas. Banderas Menos (-) Ms (+) Blanco ( ) Cero (0) Signo de nmero (#) Efecto Justifica el resultado a la izquierda Imprime el signo + o del resultado Imprime espacio si el resultado es positivo y - si es negativo Se fuerza a rellenar con ceros Imprime el resultado de un modo alternativo: Cuando se usa con los tipos o, x, X saca los prefijos 0, 0x y 0X respectivamente. Si se usa con f, e o E, fuerza la salida de un punto decimal.
double fvar = 57.29578

C orientado a microcontroladores 8051

23

printf(El printf(El printf(El printf(El printf(El

nmero nmero nmero nmero nmero

es: es: es: es: es:

|%12f|,fvar); |%-12f|,fvar); |%-012f|,fvar); |%-+12f|,fvar); |%- 12f|,fvar);

| 57.29578| |57.29578 | |57.295780000| |+57.25978 | | 57.25978 |

Por ltimo podemos indicar la precisin del valor de salida convertido y siempre aparecer precedida por un punto para separarla de otras especificaciones.
int var 484 double fvar = 57.29578 printf(Entero: %10.5d \t Flotante:%10.0f,var,fvar); Entero: 00484 Flotante: 57

Todava hay ms formas de dar formato, pero con estas es suficiente. Otras funciones para la salida de caracteres son las siguientes. sprintf:
int sprintf (char *, const char *, [argument]);

funciona exactamente igual que fprintf y tiene los mismos caracteres de escape y de formato (que ya sern as siempre), pero el resultado no lo mandar a pantalla, sino que lo dejar en la cadena a la que apunta el primer parmtero.
char cadena[10]; int peso = 100; sprintf(cadena,El peso es: %d kilos.,peso);

Dar como resultado que la variable cadena contenga El peso es: 100 kilos puts:
int puts(const char *s);

Escribe una cadena de caracteres ASCIIZ en el dispositivo de salida estndar (normalmente el monitor) aadiendo una nueva lnea. Sera equivalente a printf(%s\n,cadena);
char Cadena[]=Hola mundo; puts(Cadena);

putchar:
int putchar(int c);

Pone un nico carcter en la salida estndar, normalmente el monitor. Devuelve el carcter que se ha puesto.
putchar(A);

scanf:
int scanf(const char*,[arguemntos]);

La otra gran funcin del C. Tiene la misma funcin que readln en PASCAL, examina la cadena de entrada para los campos de entrada, convierte los campos de entrada de acuerdo a las especificaciones de formato, y coloca los valores resultantes en las posiciones de memoria dadas. C orientado a microcontroladores 8051 24

Operador de direccin Nombre de variable

scanf(%d,&var); Cadena de formato Lista de argumentos La instruccin del cuadro adjunto contiene la especificacin del formato %d para indicar que la entrada se transformar en un entero decimal, y el valor de la entrada ser colocado en la direccin donde se aloja la variable var. Si un carcter de espacio en blanco aparece dentro de la cadena de formato, indicar que se debern de pasar por alto sin almacenarlos las entradas de tipo enter, tabulador, o espacio en blanco. En el momento en que scanf se encuentre con un carcter que no pueda ser transformado al tipo especificado en la cadena de formato, la funcin termina. Por ejemplo, la sentencia scanf( %d,&var) con las entradas747, 747 (con blancos

o tabs delante), [Enter]747, 747.321 y 747ABC produce en todos los casos que la variable var contenga el entero 747, sin embargo la entrada #747 nos dara un valor indeterminado para var, ya que la cadena de formato no comienza con #. La forma adecuada para ello sera scanf(#%d,&var); si quisiramos seguir conservando la capacidad de ignorar espacios en blanco la cadena de formato debera ser #%d. Se pueden leer varios parmetros con un solo scanf de la siguiente manera scanf(%d %f

%s,&var1,&var2,&var3); . Scanf plantea el siguiente problema: si encuentra un entrada que no entiende, no la trata y por lo tanto quedar pendiente para la siguienete llamada a scanf. Esto puede dar problemas, por lo que se suele usar una combinacin de dos funciones, gets y sscanf para recoger los valores del teclado. gets:
gets(char *);

Recoge en la cadena a la que apunta el parmetro todas las entradas hasta el retorno de carro, sin cuestionarse lo que son o sin intentar transformalos.
char cadena[30];

C orientado a microcontroladores 8051

25

gets(cadena);

sscanf:
int sscanf (char*, const char* [, address, ...]);

Realiza la misma tarea que scanf, pero sobre la cadena que se le pasa como primer parmetro, completndose las dos lneas de cdigo anteriores con sscanf(cadena,%d,
&var);.

getch y getche:
int getch(void); int getche(void);

Estn asociadas al header conio.h y no al stdio.h. No tienen parmetros (as lo indica void) y recogen un carcter del teclado. Ambas funciones detienen la ejecucin del programa hasta que se pulsa una tecla. La diferencia entre ellas es que getch no hace eco de la tecla pulsada al monitor, mientras que getche s lo hace.
char c; c = getch(); switch( c ){ case A: .....;break; case B: .... ;break; default: ....; }

kbhit: conio.h
int kbhit(void);

Tambin pertenece a conio.h. Si no se desea que el programa se pare esperando a que se pulse una tecla, se puede chequear con esta funcin. Devolver cero si no se ha pulsado una tecla, y uno si s se ha pulsado.
if(kbhit()){ ch=getch(); ..../*Se ha pulsado una tecla*/ } else{ .../* No se ha pulsado ninguna tecla*/ } tiles asociadas a la salida de caracteres son void clrscr(void)

Otras funciones

para

borrar la pantalla y void gotoxy(int x, int y) para situar el cursor, donde (x,y) es la coordenada de la pantalla donde se desea posicionar el cursor. Funciones de entrada salida de disco: stdio.h Para trabajar con el almacenamiento de informacin en disco son necesarios tres pasos: a) Abrir el fichero con la funcin fopen

C orientado a microcontroladores 8051

26

b)

Escribir datos en l o leer datos del disco con funciones anlogas a las vistas para pantalla y teclado.

c)

Cerrar el fichero con la funcin fclose.

Para poder trabajar con ficheros es preciso usar una estructura que est predefinida en el header y que se llama FILE. Un puntero a esa estructura es el encargado de identificar a todas las operaciones que en adelante se hagan con ese fichero. Una forma de abrir un fichero es:
#include <stdio.h FILE *ptrFichero; char Path[]=c:\\midir\\mifich.txt; if((ptrFichero = fopen(Path,wt))!=0){ /*Cdigo para manejar el fichero*/ } else{ /*El fichero no se pudo abrir*/ /*Cdigo para el tratamiento de errores*/ }

Se puede ver como el primer parmetro es la ruta del fichero a abrir, mientras que el segundo es una cadena que define el modo en que ese fichero debe ser abierto. Pueden ser algunos de los siguientes: Tipo E/S r w a r+ w+ Explicacin Slo lectura (el fichero ya debe existir) Slo escritura (crea un nuevo fichero o borra el contenido de uno existente) Apndice (crea un nuevo fichero o aade datos al fichero actual) Lectura y escritura (el fichero ya debe existir) Lectura y escritura (crea un nuevo fichero o destruye el contenido de uno ya existente) a+ Lectura y escritura (aade datos al fichero ya existente o crea un nuevo fichero)

Tipo de modo t b Fichero de texto Fichero binario

Las funciones para escribir o leer de un fichero son similares a las vistas hasta ahora para las entradas y salidas estndar, aadiendo una f por delante y pasando como primer parmetro el puntero a fichero que nos devolvi la funcin fopen. As nos queda: C orientado a microcontroladores 8051 27

fprintf(FILE , conts char,[argumentos]); fputs(char *, FILE *); fputc(char,FILE*); fscanf (FILE *,const char *,[, address, ...]); fgets(char*, int, FILE*); int = fgetc(FILE*);

por ejemplo
fputs(hola mundo, ptrFichero);

Adems existen una funcin para poder moverse por el fichero y avanzar y retroceder un nmero determinado de bytes a partir de la posicin del puntero al fichero.
fseek(FILE*,int, int);

Donde el segundo parmetro indica el nmero de bytes en que nos tenemos que desplazar (positivos o negativos), y el tercer parmetro indica desde dnde de la siguiente manera: SEEK_SET, (0) desde el comienzo del fichero, SEEK_CUR (1) desde la posicin actual y SEEK_END (2) desde el final del fichero. Las constantes mencionadas son #defines incluidos en stdio.h Por ltimo, para cerrar el fichero deberemos de usar las funciones
fclose(FILE*);

O bien
fcloseall(void);

Para cerrar todos los fichero abiertos. As cerraramos el fichero que hemos abierto anteriormente.
fclose(ptrFichero);

Otras funciones relacionadas con ficheros y definidas en io.h son int unlink(const char *filename); Borra el fichero especificado, devuelve cero si tuvo xito int access(const char *filename, int amode); Chequea si un fichero existe y si se puede acceder en el modo especificado en amode Consultar ayuda para mas indicaciones

Salida de datos por la impresora La impresora se puede usar como si fuera un fichero de la siguiente manera:
#include <stdio.h> FILE * ImpresoraPtr; ImpresoraPtr = fopen(PRN,wt); fputs(Hola impresora,ImpresoraPtr); ... fclose(ImpresoraPtr);

C orientado a microcontroladores 8051

28

Funciones matemticas: math.h Nos proporciona una manera sencilla de obtener valores trigonomtricos, exponenciales, logartmicos, Para funciones complicadas, deberemos de programar nuestras propias rutinas. Para resolver ecuaciones diferenciales, integrales, crear generadores random serios, mtodos de ordenacin, interpolacin, extrapolacin, resolucin de ecuaciones algebricas, transformadas rpida de Fourier, funciones especiales (Bessel, Gamma, Beta...)... existe un libro titulado Numerical recipes in C que resuelve estos y otros problemas... Los mismos contenidos han sido codificados en Pascal, en Fortran y en Basic (Numerical recipes in Pascal o Fortran o Basic). Pero adems hay una edicin on-line del libro del que se pueden bajar los captulos o apartados que interesen en la direccin:

http://beta.ul.cs.cmu.edu/webRoot/Books/Numerical_Recipes/bookc.html en su formato postscript, aunque tambin lo hay en pdf. En la siguiente tabla se resumen las funciones matemticas ms importantes, aunque siempre se recomienda consultar la ayuda para ver todas las versiones de la misma funcin, sobre todo las orientadas a long doubles.
double sin(double), double asin(double) double cos(double), double acos(double) double tan(double), double atan(double) double pow(double x,double y) double log(double) double log10(double) double sqrt(double) int abs(int)

Funciones trigonomtricas Potencia de x a la y Logaritmo neperiano Logaritmo decimal Raz cuadrada Valor absoluto (para enteros, pero hay ms versiones)

double fmod(double, double)

Resto

de

divisin

entre

flotantes
double ceil(double) double floor(double) double poly(double x, int grado, double coefs[]);

Redondea hacia arriba Redondea hacia abajo Evala el polinomio de

grado grado y coeficientes en la matriz coefs[] en el punto x. C orientado a microcontroladores 8051 29

En stdlib.h se declaran los prototipos de las funciones para obtener valores aleatorios (random) aunque para aplicaciones donde sea necesario un uso intensivo de estos valores, estas funciones no son nada recomendables, y es mejor construirse generadores random de mayor aleatoriedad a partir de estas funciones. Se puede usar void ranodmize (void) para inicializar la semilla (aunque se requerir tambin time.h) e int rand(int num) nos devolver un entero entre 0 y num-1. Funciones con cadenas: string.h, ctype.h Existe un riego muy importante de caer en la tentacin de asignar una cadena a un puntero o a una matriz de caracteres de la siguiente manera:
char p[23]; P=As no se hace una asignacin;

La forma de hacerlo es mediante el uso de funciones especializadas en el tratamiento de cadenas que se definen en el archivo string.h. La manara ms sencilla de hacerlo es con la funcin strcpy(char * dest, char const * cadena) de tal forma que la usaramos as
strcpy(p,As s se puede hacer la asignacin);

Al ejecutar la sentencia anterior la matriz p contendr la cadena sealada. As se encuentran otras funciones tiles como son:
strcpy(char * dest, char const * cadena) strcat(char * dest, char * source)

Copia cadena en dest Aadea a dest cadena Compara s1 y s2 y devuelve: <0 si s1 < s2 ==0 si s==s2 >0 si s1>s2

int strcmp(const char *s1, const char*s2);

char *strchr(const char *s, int c);

Busca en s1 por el carcter c y devuelve la direcccin donde se encontr (0 si no)

char *strstr(const char *s1, const char *s2); size_t strlen(const char *s);

Busca en s1 por la subcadena s2 Devuleve el n de caracteres en s (No confundir con sizeof)

C orientado a microcontroladores 8051

30

Pero hay muchas ms que se pueden consultar en la ayuda, desde para localizar la ltima vez que aparece un carcter, o para invertir una cadena, o para ver cundo dos cadenas dejan de ser iguales... En ctype.h se incluyen una serie de funciones tiles para el control del cadenas y de sus contenido.
int toupper(int) int tolower(int) int int int int int int int int isalpha(int) isascii(int) isdigit(int) isxdigit(int) islower(int) isupper(int) isprint(int) iscntrl(int)

Transforme el carcter pasado como parmetro a mayscula o a minuscula, respectivamente. Es una letra (a-z o A-Z) El byte bajo del parmetro es < 0x7F Es un nmero (0-9) Es un dgito hexadecimal(0-9 a-f) Es una minscula Es una mayscula Es un carcter printable (0x20 - 0x7E) Es un delete o carcter de control ((0x7F o de 0x00 a 0x1F)

Funciones para la conversin de nmeros y cadenas: stdlib.h Existen funciones para transformar variables definidas, como nmeros, a cadenas. Aunque estas conversiones puede realizarse con ayuda de sprintf con el formato adecuado, se dispone de funciones especficas para ello.
long atol(char *) int atoi(char *) float atof(char *) double strtod(char *) unsigned long strtoul(char *)

un long Convierte una cadena en un int un float un double unsigned long

itoa(int value, char *string, int radix); ltoa(long value, char *string, int radix); ultoa(unsigned long value, char *string, int radix); char *ecvt(double value, int ndig, int *dec, int *sign); char *fcvt(double value, int ndig, int *dec, int *sign);

Coloca en value en la cadena string cuya direccin facilitamos, representndolo en base radix. Devuelven un puntero a una cadena que ha transformado el doble con ndig dgitos. La 31

C orientado a microcontroladores 8051

posicin del punto decimal relativa al comienzo de la cadena viene devuelta en dec.
gcvt(double *buf); value, int ndec, char

Transforma valor a una cadena cuya direccin es buf de ndec dgitos.

Funciones de asignacin dinmica de memoria: stdlib.h Modelos de memoria Cuando se genera un mismo programa la manera de compilarlo puede generar diferentes cdigo, dependiendo del modelo de memoria escogido. Existen varios modelos: Tiny: El programa entero, cdigo y datos, necesitan menos de 64Kb Small: Los datos necesitan como mucho 64Kb y los datos tambin como mucho 64Kb, el total por tanto ser de 128 Kb. Medium: El cdigo necesita ms de 64Kb con un mximo de 1Mb, los datos siguen teniendo como mucho 64Kb. Compact: El cdigo como mucho ser de 64Kb, los datos estticos (los que se definen antes de compilar) tambin son menores de 64Kb, pero los dinmicos (la memoria que se pide al sistema operativo durante la ejecucin del programa) pueden llegar a 1Mb. Large: Cdigo y datos dinmicos hasta 1 Mb, y datos estticos hasta 64Kb Huge: Cdigo y datos estticos hasta 1Mb A pesar de ofrecernos la posibilidad de llegar hasta 1Mb de cdigo o datos, cada mdulo (fichero .c) slo puede tener hasta 64 Kb, teniendo que descomponer el programa en diferentes mdulos que no violen esta imposicin. A veces no podemos saber cunta memoria reservar para una determinada variable, por ejemplo, si vamos a leer de un fichero, no podemos saber a priori lo grande que ese fichero puede llegar a ser. Hay dos soluciones, reservar buffers sobredimensionados, pensando que tendremos suficiente (y que nunca sern lo suficientemente grandes) o pedir al sistema operativo en tiempo de ejecucin la cantidad de memoria que necesitemos. Esta estrategia es mucho ms aconsejada y se denomina asignacin dinmica de memoria. Estas funciones son las siguientes:
void *calloc(size_t size); nitems, size_t

Devuelve un puntero a la zona de memoria

C orientado a microcontroladores 8051

32

asignada

que

ser

de

tamao

nitems*sizeof(size_t). Si falla devuelve 0


void *realloc(void size); *block, size_t

Reasignamos nueva memoria a un bloque previamente asignado.

void free(void *block);

Libera la memoria asignada dinmicamente

El uso es sencillo pero quiz se vea mejor con un ejemplo. Si tenemos una estructura tipo llamada PERSONA, y la aplicacin no nos permite saber que declarando un array de 100 personas tendremos suficiente, deberemos de pedir memoria al sistema operativo.
struct PERSONA{ char nombre[50]; char apellido[100]; int edad; }; struct PERSONA * ptrPERSONA; ptrPERSONA = (struct PERSONA *)calloc(50,sizeof (struct PERSONA)); //Habremos pedido sitio para 50 estrucuturas // podremos asignar la edad de la ficha de la sptima persona como (ptrPERSONA+7)->edad = 24; /*esta forma es equivalente a arrayPERSONA[7].edad = 24, si hubisemos declarado struct arrayPERSONA[25];, De hecho, traga ptrPERSONA[7].edad= 24;*/ //o aadir el nombre de la 4 strcpy((ptrPERSONA+4)->nombre,MANOLO); //Si ahora necesitamos ms, solo tenemos que pedirlo ptrPERSONA=(struct PERSONA*)realloc(ptrPERSONA ,100*(sizeof(struct PERSONA))); //Y cuando por fin hemos acabado, deberemos //devolver lo que cogimos prestado... free(ptrPERSONA);

Existen las alternativas far de estas funciones para poder usar memoria ms all de nuestro segmento: farcalloc, farrealloc, farfree. Funciones con directorios: dir.h Es muchas aplicaciones es necesario abrir y cerrar ficheros, y para ello se necesitan de funciones que sepan devolver el nombre del directorio presente, o crear un subdirectorio... estas funciones son las siguientes.
Int mkdir(char *) Int rmdir(char *) int chdir(char *)

Crea, borra o cambia a directorio de trabajo(respectivamente) el directorio

especificado por la cadena parmetro. si C orientado a microcontroladores 8051 33

hubo xito devuelven 0


int getdisk(void) int setdisk(int) char *getcwd(char *buf, int buflen);

Fijan la unidad o preguntan por la unidad activa. 0 = A, 1 =B, 2 = C.... Se le pasa la direccin de un array donde el devolver el directorio de trabajo actual

int getcurdir(int drive, char *directory); char *searchpath(const char *file);

Como getcwd pero especificando la unidad. Intenta buscar el fichero cuyo nombre es el parmetro, en los directorios de PATH, si lo encuentra devuelve su path completo.

void fnmerge (char path, const char drive, const char * dir, const char *name, const char *ext); void fnsplit (char path, const char drive, const char * dir, const char *name, const char *ext);

Construye el path completo a partir de las componentes del mismo Descompone el path pasado en sus diversos componentes.

Funciones de tiempo: time.h El tiempo puede ser representado de dos maneras, mediante un nmero que representa el nmero de segundos transcurridos desde 1970 llamado t_time, o mediante una estructura llamada tm, que descompone en sus variables miembros la informacin que contiene t_time... se puede decir que tm contiene decodificado en unos campos de una estructura la misma informacin que t_time.
struct int int int int int int int int int }; tm { tm_sec; tm_min; tm_hour; tm_mday; tm_mon; tm_year; tm_wday; tm_yday; tm_isdst; /* /* /* /* /* /* /* /* /* Segundos */ Minutos */ Horas (0--23) */ Dia del mes (1--31) */ Mes (0--11) */ Ao (Ao actual - 1900 */ da de la semana(0--6; Domingo = 0) */ da del ao (0--365) */ 0 si los cambio del horario de verano no tienen efecto) */

Existen funciones que producen el mismo efecto, pero que reciben uno u otro tipo de parmetro.
char *asctime(const struct *tblock); char *ctime(const time_t *time); tm

Devuelve un puntero a una cadena de 26 caracteres que contiene la fecha parametro. 34

C orientado a microcontroladores 8051

time_t time(time_t *timer); int stime(time_t *tp); time_t mktime(struct tm *t); struct tm *localtime(const *timer); struct tm *timer); *gmtime(const

Devuelve la hora. Fija la hora. Pasa de una representacin t_time a struct tm


time_t

y viceversa.
time_t

Devuelve la diferencia entre dos tiempos.

Existen unas funciones adicionales declaradas dentro de dos.h usan de uan estructura llamada time:
struct time { unsigned char ti_min; /* unsigned char ti_hour; /* unsigned char ti_hund; /* unsigned char ti_sec; /* }; void gettime(struct time *timep); void settime(struct time *timep); minutos */ horas */ centsimas de segundos */ segundos */

Fija o lee la hora presente

Funciones del DOS: Dos.h Interrupciones de software C nos proporciona las funciones necesarias para ejecutar servicios de interrupciones. Para ello define una serie de estructuras que contienen los registros del 8086. Registros
struct BYTEREGS { unsigned char al, ah, bl, bh; unsigned char cl, ch, dl, dh; }; struct WORDREGS { unsigned int ax, bx, cx, dx; unsigned int si, di, cflag, flags; }; union REGS { struct WORDREGS x; struct BYTEREGS h; };

Segmentos
struct SREGS { unsigned int unsigned int unsigned int unsigned int }; es; cs; ss; ds;

Para llamar a un servicio de interrupcin del 8086, lo nico que hay que hacer es rellenar los miembros de la estructura adecuadamente y llamar a alguna de las siguientes funciones:
int int86(int intno, union REGS *inregs, union REGS *outregs);

C orientado a microcontroladores 8051

35

int int86x(int intno, union REGS *inregs, union REGS *outregs, struct SREGS *segregs);

Intno tiene el nmero la interrupcin a ejecutar. Inregs deber de tener los valores necesarios, y el resultado de la operacin se podr chequear en outregs. Inregs y outregs pueden ser la misma estructura. Si los registros de segmentos estn implicados en la interrupcin, se deber usar int86x,. Por ejemplo, si queremos obtener los atributos de un archivo usando llamadas al sistema operativo podremos usar el siguiente cdigo
#include <dos.h> #include <process.h> #include <stdio.h> int main(void) { char filename[80]=Fichero.txt; union REGS inregs, outregs; struct SREGS segregs; printf(Nombre del Fihero: ); gets(filename); inregs.h.ah = 0x43; inregs.h.al = 0x21; inregs.x.dx = FP_OFF(filename); segregs.ds = FP_SEG(filename); int86x(0x21, &inregs, &outregs, &segregs); printf(File attribute: %X\n, outregs.x.cx); return 0; }

En ejemplo anterior merece la pena destacar la manera en que podemos obtener el segmento y el offset de una variable con ayuda de las macros FP_OFF y FP_SEG. Para realizar la operacin inversa, es decir, generar una direccin usable a partir de segmento y offset devueltos en outreg, deberemos usar la macro MK_FP que forma un puntero tipo FAR (Segmento ms offset, no slo offset como sera un NEAR), como en el siguiente ejemplo, donde se manipula directamente la memoria de programa.
#include <graphics.h> int main(void) { int gd, gm, i; unsigned int far *screen; detectgraph(&gd, &gm); if (gd == HERCMONO) screen = (unsigned int *) MK_FP(0xB000, 0); else screen = (unsigned int *) MK_FP(0xB800, 0); for (i=0; i<26; i++) screen[i] = 0x0700 + (a + i);

C orientado a microcontroladores 8051

36

return 0; }

Para llamar a servicios del DOS (int 21h) podemos usar las funciones siguientes que operan anlogamente a como lo hacan int86 e int86x
int intdos(union REGS *inregs, union REGS *outregs); int intdosx(union REGS *inregs, union REGS *outregs, *segregs) struct SREGS

Acceso a puertos Para el acceso a dispositivos mapeados en la zona de memoria de perifricos (I/O Mapped) accesibles mediante las instrucciones IN y OUT en ensamblador, el C dispone de unas funciones que nos dan la misma funcionalidad.
unsigned inport (unsigned portid); unsigned char inportb (unsigned portid); void value); void char value); outport (unsigned portid, unsigned outportb(unsigned portid, unsigned

Leen un byte o un word del puerto especificado Escriben un byte o un word en el puerto especificado

En conio.h se definen unos macros que realizan semejantes menesteres.


int inp(unsigned portid); unsigned inpw(unsigned portid); int outp(unsigned portid, int value); unsigned outpw(unsigned portid, unsigned value);

Leen un byte o un word del puerto especificado Escriben un byte o un word en el puerto especificado.

Interrupciones por hardware Podemos habilitar o deshabilitar globalmente las interrupciones con ayuda de las funciones
void disable(void);

Impide que se produzcan interrupciones por hardware (excepto NMI)

void enable(void);

Habilita las interrupciones por hardware que no estn enmascaradas.

Para definir una funcin que se active cuando se genere una interrupcin de harwdare (interrupt handler o isr=interrupt service routine) hay que poner la palabra clave interrupt en la declaracin del prototipo y en la definicin de la funcin. As se salvarn todos los registros y se terminar con IRET.
void interrupt NuevaFuncionTratadoraInterrupcion (void) . .

C orientado a microcontroladores 8051

37

. void interrupt NuevaFuncionTratadoraInterrupcion (void){ //Se hace algo util ViejaFuncionTratadoraInterrupcion(); //Se llama a la vieja ISR para encadenar interrupciones }

Cuando se use interrupt hay que desactivar la opcin Options->Linker->Settings->Stack Warning y la opcin Options->Compiler->Optimizations->Register Variables debe ser puesta a None, para que la funcin de interrupcin opere correctamente. Por si acaso, tambin hay que comprobar que la opcin Test Stack Overflow en Options->Compiler>Entry/Exit Code->Stack Options no est chequeada... y luego mucha suerte... Modificar y leer vectores de interrupcin Para poder usar interrupciones de harware es preciso salvar el vector de interrupcin antes de redireccionarlo a la nueva rutina. Dentro de la nueva funcin se debe realizar una llamada a la funcin antigua, ya que est podra realizar tareas necesarias para el sistema. Las funciones asociadas son:
void interrupt interruptno))(); (*getvect(int

Devuelve el vector de interrupcin de la interrupcin especificada

void setvect(int interruptno, interrupt (*isr) ( ));

void

Redirecciona el vector de interrupcin.

Ejemplo: instalamos una handler de la interrupcin del puerto paralelo. Esta interrupcin se produce cuando llega un flanco de bajada en la seal ACK.
#include <stdio.h> #include <bios.h> #include <dos.h> #define DATA 0x0378 #define STATUS DATA+1 #define CONTROL DATA+2 #define TRUE 1 #define FALSE 0 #ifdef __cplusplus #define __CPPARGS ... #else #define __CPPARGS #endif void abir_intserv(void); void cerrar_intserv(void); void int_procesada(void); void interrupt far intserv(__CPPARGS); int intlev=0x0f; /* vector de interrupcion asociado a IRQ7 */

C orientado a microcontroladores 8051

38

void interrupt far (*oldfunc)(__CPPARGS); int main(void) { abrir_intserv(); outportb(CONTROL, inportb(CONTROL) | 0x10); /* Fijar bit 4 en puerto contro a 1 */ while(!kbhit()); cerrar_intserv(); return(0); } void interrupt far intserv(__CPPARGS) /* Funcin de servicio de interrupcin escrita por el usuario. No es ** bueno pasarse mucho tiempo aqu dejar un flag que se atienda en la ** rutinaprincipal ** El PIC debe saber que se ha acabado la rutina. Debemos ** llamar a int_procesada() o al antiguo vector de interrupcin. ** */ { disable(); //Inhibimos interrupciones puts(Interrupcion); int_procesada(); enable(); //Permitimos de nuevo } void abrir_intserv(void) /* habitila IRQ7 al activarse flanco de bajada */ { int int_mask; disable(); /* inhabilita todas las interrupciones */ oldfunc=getvect(intlev); /* salva el vecto antiguo */ setvect (intlev, intserv); /* y redirecciona al nuevo */ int_mask=inportb(0x21); /* 1101 1111 */ outportb(0x21, int_mask & ~0x80); /* bit 7 a cero */ /* los otro igual */ enable(); } void cerrar_intserv(void) /* inhabilita IRQ7*/ { int int_mask; disable(); setvect(intlev, oldfunc); int_mask=inportb (0x21) | 0x80; outportb(0x21, int_mask); enable(); }

/* bit 7 a uno */

void int_procesada(void) /* seala al PIC 8259 que la interrupcin ha sido procesada */ { outportb(0x20,0x20); }

C orientado a microcontroladores 8051

39

Inclusin de cdigo asm Para los irreductibles del lenguaje ensamblador, se puede incluir de forma directa cdigo en ese lenguaje en rutinas en C con la ayuda de la sentencia asm. Tal y como se muestra en el siguiente ejemplo, se pueden usar las variables definidas en el cdigo en C dentro de la parte en ensamblador, incluso se puede llamar a funciones declaradas y definidas en C desde la parte en ensamblador... aunque aqu es donde empieza a complicarse la cosa... los parmetros se pasan por la pila, y si la llamada es near con un modelo small se puede hacer como en el ejemplo. Si la funcin es far (declara en otro segmeto, o como llamable desde otro segmento, deberemos de empilar el registro CS; as mismo, si los parmetros son algo ms complicados que enteros, el pasarlos a la pila empieza a ser ms dificultoso... bastante ms dificultoso. El valor de retorno se devuelve en AX y si es de 4 bytes en DS:AX.
#include <c:\borland\include\dos.h> int suma(int,int); main() { int Fila = 5,Columna = 10,s1,s2,r; char Modo=2; asm{ mov ah,0x0; mov al,Modo; int 0x10; //Fijar modo de video }; _AH=2; //Con guion bajo (underscore) accedemos _BH=0; //directamente a los registros _DH=Fila; _DL=Columna; s1=3; s2=9; asm{ //Comienzo de codigo assambler int 0x10 mov ax,s2 push ax //Ultimo parmetro,primero a la pila mov ax,s1 push ax //Primer parmetero,ultimo a la pila jmp etiq /*Las etiquetas no pueden estar dentro del asm*/ nop nop }; //Fin de cdigo ensamblador etiq: asm{ mov ax,OFFSET(suma) /*Esta forma de llamar la funcin no es como para estar orgulloso, pero va mejor que NEAR PTR suma, que es como

C orientado a microcontroladores 8051

40

debera de ser. Si la llamada fuese FAR, habra que salvar CS y luego lo mismo*/ call mov pop pop } return 0; } int suma(int a,int b){ return a+b; } ax r,ax ax ax //r contiene la suma de a y b //restauramos profundidad de pila

C orientado a microcontroladores 8051

41

Extensiones al lenguaje C para el 8051


Todo lo visto hasta ahora presupona que el microprocesador que iba a ejecutar las instrucciones era un 8086 o compatible con l. Podemos usar la misma metodologa, la misma forma de declarar variables o funciones, pero que sern trasladadas a otro tipo de cdigo mquina que podr ser ejecutado por un 8051. Pero como un 8051 no es un 8086, existirn una serie de diferencias que suelen estar englobadas bajo el nombre de extensiones para un determinado micro. Estas diferencias suelen venir dadas por la diferencia entre las arquitecturas de los micros. El 8086 puede direccionar hasta 1Mb de memoria, cosa que le viene un poco grande al 8051; sin embargo este es capaz de tener memoria RAM interna (desde 128 bytes a 256 bytes, dependiendo del modelo. No es mucho, pero menos da una piedra), y dentro de esta memoria hay una memoria especial que es capaz de ser direccionada tanto byte a byte, como bit a bit. De esta forma se van conformando una serie de diferencias entre una plataforma y otra que afectan la forma de programar. Las diferencias bsicas sern: Las reas de memoria en el 8051 Los modelos de memoria Las especificaciones de tipos Las declaraciones de funciones Durante el curso se usar la versin de evaluacin del compilador de la marca Keil. La versin de evaluacin tiene unos lmites en cuanto a cantidad de cdigo en formato .hex generable de 2Kb. El tamao del fichero fuente deber de ser menor de 16Kb. La direccin de comienzo del programa (el main) se sita en la direccin 0x4000, pero en general, nos puede servir para cumplir el objetivo de aprender a usar el lenguaje C orientado a un microprocesador. Cabe resear que el compilador de C de Keil no es el nico que en el mundo ha sido, ni ser, y que otra empresa puede hacer una implementacin de su extensin diferente, por lo que ser conveniente leer el manual de usuario de ese compilador para ver cmo han decidido implementar las diferentes funciones. reas de memoria en el 8051 Memoria de programa: Se usara code para referirnos a esta rea. Es de slo lectura, podremos almacenar datos que no podrn ser cambiados y es donde ir el cdigo C orientado a microcontroladores 8051 42

ejecutable. Puede ser interna o externa dependiendo del tipo de micro, y como mucho albergar 64k. Memoria interna de datos: Los 128 bytes (256 en algunos casos) de memoria interna pueden descomponerse en tres tipos de memoria: Data: Siempre se referir a los 128 primeros bytes de memoria interna. Se direcciona de forma directa. Idata: Se referir a los 256 bytes de memoria interna a los que se accede de modo indirecto, que siempre es ms lenta que el directo. Bdata: Abarca los 16 bytes (de la direccin 0x20 a la 0x2F) cuyo contenido se puede usar tanto de byte en byte (16 bytes) como en forma de bits (128 bits) Memoria externa de datos: Est fsicamente localizada fuera del circuito integrado y su acceso es el ms lento de todos. Como mucho sern 64Kb. Se puede descomponer en dos tipos de zonas. Xdata: Puede ser cualquier byte dentro de los 64Kb disponibles. (Se referir a intrucciones equivalentes a MOVX A,@DPTR y MOVX @DPTR,A, con DPTR podamos acceder a cualquier byte de la memoria externa disponible, ya que DPTR es un registro de 16 bits). Pdata: Se refiere a uno de los 256 bytes que tiene una pgina. Cada pgina se forma por el contenido del puerto 2, como direccin base, y el contenido de R1 o R0 como offset a partir de la direccin de P2 (Equivale a las instrucciones en ensamblador MOVX @Ri,A y MOVX A,@Ri). Modelos de memoria en el 8051 Al contrario que en el 8086, tendremos nicamente 3 modelos de memoria: SMALL: Todas las variables residen dentro de la memoria interna de datos, por lo tanto, siempre que nos quepan, tendremos que intentar usar este modelo de memoria. COMPACT: Las variables, por defecto, (es decir, si nadie dice lo contrario durante su declaracin), se definen dentro de una pgina de memoria externa. Como si todas hubieran sido declaradas con pdata. P2 determinar la pgina en uso, y ser el programador quien determine qu pagina ser la activa, durante la inicializacin. LARGE: Por defecto, todas las variables residen en alguno de los 64Kb de memoria externa. Como si fueran declaradas con xdata.

C orientado a microcontroladores 8051

43

Para trabajar con un modelo u otro iremos a Options->C51->Memory Models->Memory Model (variable location) y fijar el modelo deseado. Declaracin explcita de tipos Si declaramos una variable de forma estndar, se le reservar memoria en la zona de memoria especificada por el modelo de memoria que hayamos elegido. Sin embargo, podemos manipular la declaracin por defecto aadiendo la ubicacin que deseemos para la variable que en esos momentos declaramos. As una declaracin de una variable deber seguir el siguiente esquema general
tipo estndar [localizacin] identificador [_at_ direccin]

Por ejemplo, si hemos elegido el modelo de memoria LARGE, y declaramos la siguiente variable
char saludo[100]=Hola;

estaremos reservando 100 bytes en memoria RAM externa. Ahora bien, si queremos que esa parte de programa se defina en la memoria de programa (porque es un saludo que no va a variar nunca) deberemos escribir algo as como
char code saludo[100]=Hola;

As podremos elegir arbitrariamente la zona donde queremos que se defina una variable.
unsigned long xdata array[100]; char bdata flags; float idata x,y,z

Es muy comn cuando se trabaja con un sistema microcontrolador tener diferentes dispositivos mapeados en memoria. Es decir, reaccionan cuando se escribe o se lee de determinadas direcciones. Se dice que el dispositivo est en tal direccin. Aqu cobra importancia, por tanto, el poder definir exactamente la direccin donde se declara la variable, pues de esta forma, asignar un valor a esa variable corresponder exactamente con escribir ese mismo valor en el dispositivo de entrada o salida. Para ello contamos con la posibilidad de usar la palabra _at_ seguida de una direccin. Por ejmplo:
unsigend char xdata ConvertidorAD _at_ 0x0040;

declara una variable que estar exactamente en la posicin 0x0040, que ser justamente la direccin en la que habremos cableado el convertidor. Adems de los tipos de datos estndar (excepto el double, nos tendremos que conformar con los floats), tenemos otros definidos exclusivamente para el 8051. Tipo bit C orientado a microcontroladores 8051 Bits/Bytes 1/0 Rango 0a1 44

sbit sfr sfr16

1/0 8/1 16/2

0a1 0 a 255 0 a 65535

Tipo bit: Se declarar dentro del rea de bdata (De la direccion 0x20 a la 0x2F de la memoria interna). Tiene algunas restricciones: Podrn ser declaradas como parmetro de retorno execpto cuando las funciones sean declaradas usando un banco de registros explcito (using n). No pueden ser declarados como punteros. No pueden declararse matrices de bits.

Tipo sbit: Se pueden declarar variables dentro del rea de memoria que es direccionable bit a bit (bdata), y a la vez, declarar bits pertenecientes a esa misma zona, solapados con la variable peviamente definida. Por ejmplo:
int bdata var; //varaible direccionable bit a bit, //pero que es un entero char bdata p[4]; //matriz de 4 bytes direccionables bit a bit sbit mibit = var ^ 0; //mibit es el bit de menor peso de var; sbit otrobit = p[0] ^ 4 //4 bit de la variable p[0]

Si se declaran referencias externas a los bits definidos el tipo ya no sera sbit, sino solo bit.
extern bit mibit; extern bit otrobit;

No se pueden declarar sbits pertenecientes a floats, pero s se pueden declarar sbits sobre miembros de estructuras y uniones, as que nadie nos impide declarar una union de un float y un entero long, y trabajar sobre los bits del entero long... el efecto es como si se trabajara sobre los del float.
union lft{ float fl; long ln; }var; sbit signo = var.ln ^ 31;

El tipo sbit tambin se usa para acceder a bits concretos dentro de los bytes de los SFRs. Por ejemplo:
sbit EA = 0xAF;

Consultar el archivo reg51.h para ver la lista de bits. Tipo sfr: La familia del 8051 tiene una serie de registros mapeados de la direccin 128 a la 255 de memoria interna que sirven para la programacin y control de los perifricos de entrada salida que estn incluidos dentro del chip: timers, puerto serie, puertos de I/O... Estos registros deben definirse con el tipo sfr C orientado a microcontroladores 8051 45

sfr P0 = 0x80;

Consultar el archivo reg51.h para ver la lista completa de registros. Tipo sfr16: como el tipo sfr pero para registros de 16 bits

Punteros En C51 se pueden definir los mismos tipos de punteros que se pueden realizar en ANSI C, pero debido a la arquitectura del microcontrolador, estos pueden ser de dos tipos: Punteros genricos o especficos a un rea concreta de memoria Punteros genricos Se declaran de la misma manera en que se declaraban en C. Usarn tres bytes. El primero para el tipo de memoria, y los dos siguientes para especificar el offset del elemento apuntado. Se pueden usar para acceder a cualquier variable independientemente de sus localizacin en memoria. Son ms lentos y siempre que sea posible habr que definir punteros a reas de memoria especfica.
char * xdata ptr;

declara un puntero genrico que se almacenar en la memoria de datos externa, pero los datos a los que apuntan pueden estar almacenados en cualquier zona. Punteros a zonas especficas de memoria Incluyen siempre una especificacin de zona de memoria y se refieren siempre a esa zona en la que han sido especificados.
char xdata *ptr;

Define un puntero (no se sabe donde, depende del modelo de memoria adoptado) pero que apuntara siempre a datos en memoria externa. Claro que para los muy virtuosos se pueden hacer declaraciones conjuntas:
int xdata * data ptr

donde ptr no es ms que un puntero declarado en memoria interna (data) que apuntarn a enteros que se declararn en memoria externa (xdata). Pues eso. El cdigo con este tipo de puntero es ms rpido que el que se genera con punteros genricos. Se puede realizar cualquier tipo de conversin entre punteros genricos y especficos a reas de memoria mediante cast.

C orientado a microcontroladores 8051

46

Declaraciones de funciones C51 facilita una serie de expresiones que permiten hacer la declaraciones de funciones ms flexibles para: Especificar una funcin como tratadora de una interrupcin de hardware. Elegir el banco de registros que se desea usar con dicha funcin. Seleccionar el modelo de memoria Especificar si esa funcin ser reentrante o no.

La forma general para declarar una funcin es : [tipo de retorno] NombreDeFuncion([argumentos]) [small|cmpact|large] [reentrant][interrupt n][using n]

En C convencional, el paso de parmetros a una funcin se realiza poniendo estos en la pila. Eso esta muy bien cuando la pila puede tener un tamao ms o menos generoso, pero en el 51 nos movemos con poca memoria, por lo que esta deber ser administrada con tacaera. Una de las medidas que se toman para ahorra pila, es pasar los prmetros mediante los registros. La manera en que estos parmetros se pasarn viene definida en la siguiente tabla. Orden del argumento 1 2 3 Char, 1byte ptr R7 R5 R3 Int, 2-byte ptr R6-R7 R4-R5 R2-R3 Long, float R4-R7 R4-R7 Generic ptr R1-R3 R1-R3 R1-R3

Si todos los parmetros no entran en los registros especificados, se usarn posiciones de memoria fijas. Los valores de retorno se depositan por parte de la funcin en los siguientes registros Tipo de retorno bit char, 1-byte ptr int, 2-byte ptr long, float C orientado a microcontroladores 8051 Registro Flag de Carry R7 R6&R7 R4-R7 MSB en R6, LSB en R7 MSB en R4, LSB en R7 Formato 32 bit IEEE 47 Descripcin

generic ptr

R1-R3

Tipo Memoria en R3 MSB en R2 LSB en R3

Los parmtero de tipo bit que se pasen a una funcin deben ir siempre al final de la lista. [small|compact|large] La especificacin del tipo de memoria para una funcin afecta al tipo de pila interna en que se crean las variables locales. Siempre que se pueda se declarar small, pero pudiera ser necesaria una declaracin alternativa si tuviramos problemas de espacio. [using n] Los 32 bytes de direcciones ms bajas de memoria interna estn organizados en 4 bancos de 8 bytes cada uno. En todo momento solo hay un banco activo. El nombre de los registros, en ensamblador, es R0, R1..., R7. Cuando se trabaja en tiempo real, es ms rpido conmutar a otro banco activo, que salvar todos los registros en la pila. Para facilitar eso podemos aadir a la funcin la palabra using n, donde n va de 0 a 3, para especificar cul va a ser el banco activo durante esa funcin. Using no puede ser usado en funciones que deban devolver valores en registros. Una estrategia puede ser usar un banco para todas las funciones que no sean de interrupcin, otro para las interrupciones de alta prioridad, y otro para las interrupciones de prioridad mas baja. [interrupt n] El 8051 tiene una serie de interrupciones de harware que pueden necesitar de una funcin que las atienda. Para instalar una funcin que sea llamada cuando se produzca una interrupcin, hay que usar la palabra clave interrupt n, donde n especifica la interrupcin a la que se asociara esa funcin, segn la siguiente tabla: N de interrupcin n 0 1 2 3 4 Descripcin Externa 0 Contador/Timer 0 Externa 1 Contador/Timer 1 Puerto Serie Vector de interrupcin 0x0003 0x000B 0x0013 0x001B 0x0023 48

C orientado a microcontroladores 8051

Para los hermanos mayores de la familia 51, existen toda una serie de nmeros de interrupcin ms altos que 4 relacionados con otros vectores. Consltese el manual para cada caso especfico.
void timer0_1ms(void) interrupt 1 using 2{ if (++ctr==10){ ctr=0; t0_10ms(); } }

Las funciones de tratamiento de interrupciones no pueden tener parmetros ni de entrada ni de salida. Las palabras clave interrupt y using no son necesarias en la declaracin de prototipos, pero si en la definicin de la funcin. Las funciones que dan servicio a una interrupcin al no ser llamadas nunca dentro del cdigo ejecutable (al igual que no se llama desde ningn sitio a main()), generan un warning al linkar (warning 16: uncalled segment) que puede ser ignorado. Ese warning se produce cuando se define una funcin que no es llamada desde ninguna lnea de cdigo. [reentrant] Una funcin reentrante puede ser compartida por varios procesos a la vez. Es decir, en un sistema operativo multitarea, una tarea puede llamar a una funcin y en medio de la ejecucin de esta, el sistema operativo conmuta a otra tarea, por lo que suspende la presente. Esa nueva tarea puede invocar los servicios de la misma funcin que se qued colgada en la anterior tarea... al ser invocada ejecutar lo que tenga que hacer sin ningn problema, pero el asunto no ser tan sencillo cuando la tarea primitiva recupere el control y prosiga con la ejecucin de la funcin donde lo dej. Esa es la madre del cordero. Si s puede, se dice que la funcin es reentrante, si no, no lo es. El DOS, por ejemplo no es reentrante. En un sistema microcontrolador, que funciona en tiempo real, debamos de tener en cuenta la naturaleza de las funciones que estamos usando, funciones recursivas, o que puedan servir de apoyo a dos o ms funciones de servicios de interrupcin que pueden necesitar de esta cualidad. El motivo es que las variables locales se generan en posiciones fijas de memoria, y por tanto quedan re-escritas cuando se vuelve a llamar a la funcin. Las funciones reentrantes necesitan de mayor cantidad de memoria, ya que usarn la pila, para su correcto funcionamiento y debern de limitarse al mximo. Funciones reentrantes no pueden tener bits como argumentos. C orientado a microcontroladores 8051 49

Macros predefinidas Vienen en intrins.h _crol_ _cror_ _irol_ _iror_ _lrol_ _lror_ _nop_ _chkfloat_ Rota un unsigned char a la izquierda un nmero determinado de bits Rota un unsigned char a la derecha un nmero determinado de bits Rota un unsigned int a la izquierda un nmero determinado de bits Rota un unsigned int a la derecha un nmero determinado de bits Rota un unsigned long a la izquierda un nmero determinado de bits Rota un unsigned long a la derecha un nmero determinado de bits No hace nada Cheque el estado de un numero flotante. Devuelve si no ha ocurrido nada, si es cero, +infinito (overflow positivo), -infinito (overflow negatico) o NaN (Not a Number) si hubo error. Consultar gua de usuario del compilador. _testbit_ Su uso sera
int a=23,rot=2,c; c =_lrol_(a,b); // c tiene el valor de a rotado // b veces hacia la izquierda

Comprueba el valor de un bit y lo pone a cero (equivale a JBC)

Existen adems otra serie de macros definidas en ABSACC.h para el acceso absoluto a memoria CBYTE[direccin en code]: Devuelve el byte de la direccin de memoria especificada CWORD[direccion en code]: Devuelve el word dela direccin de memoria especificada DBYTE[direccion en data]: Devuelve el byte la direccin de memoria interna especificada DWORD[direccion en idata]: Devuelve el word de la direccin de memoria interna PBYTE[direccion en pdata]: Devuelve el byte de la direccin de la pgina especificada PWORD[direccin en pdata]: Devuelve el word de la direccin de la pgina espcecificada XBYTE[direccin en xdata]: Devuelve el byte de memoria externa especificada XWORD[direccin en xdata]: Devuelve el word de memoria externa especificado Su uso sera
char p; int l; P = XBYTE[0x002]; XBYTE[0x0002] = 57; l = DWORD[0x0003];

// Lee la posicin de memoria externa 2 // Escribe en la posicin de memoria externa 2 // Lee de la posicin de memoria

C orientado a microcontroladores 8051

50

DWORD[0x0003]=34;

// 6 = 3*sizeof(unsigned int) // Escribe en la posicin de memoria interna // 6 = 3 * sizeof(unsigend int)

Registros Para usar los registros con los mismos mnemotcnicos que en ensamblador es necesario incluir el archivo regs51.h. Si se usa otro micro derivado del 51, se deber usar el correspondiente #include o bien, se deber de generar de la misma manera en que se ha escrito el regs51.h.

Funciones de Entrada y Salida El 8051 no tiene ningn monitor al que asociar la salida de caracteres con printf ni ningn teclado del que leer con scanf. Sin embargo, usa estas funciones redireccionando su salida al puerto serie. As putchar escribe un carcter en el puerto serie y _getkey espera a leer un carcter desde el puerto serie. Getchar llama a _getkey y el carcter ledo se lo pasa a putchar para que lo devuelva por el mismo sitio por donde lleg. Gets lee y devuelve una cadena hasta que llega el \n, o bien hasta que se llena su buffer (cuya longitud es un parmetro). Puts envia una cadena aadiendo \n. De la misma manera existen printf y scanf, que como mucho aceptan 15 bytes con un modelo de memoria small o compact, y 40 bytes en un modelo de memoria large. Estas funciones no inicializan el puerto serie, para ello se puede usar el siguiente cdigo. La siguiente secuencia inicializa el puerto serie a 9600 baudios (con un cristal de 11.0592 MHz)
SCON = 0x50; // // // // // // // // // // // Modo 1: uart de 8 bit y baud rate variable REN = 1 habilita recepcin Limpia SMOD para no doblar la velocidad de transmision Timer 1 en modo 2, 8 bit autoreload Valor de autorrecarga No es necesario, pero asi empezamos bien contando Deshabilita interrupcin timer 1 Arranca el temporizador

PCON =& 0x7F; TMOD |= 0xCF; TH1 = 0xFD; TL1 = 0xFD; ET1 = 0; TR1 = 1

Otras funciones Para el tratamiento de errores se pueden usar las instrucciones setjmp y longjmp que salvan y restauran el stack y el contador de programa en buffer que se especifique. Una llamada a C orientado a microcontroladores 8051 51

setjmp salva la direccin de la siguiente instruccin, as como otros registros. Para poder restaurarlos posteriormente, ser necesario hacer una llamada a longjmp. Setjmp devuelve cero cuando se ha salvado el estado de la CPU en el buffer. Un valor no cero indica que longjmp fue ejecutado para volver a la setjmp.

Listado de Funciones y prototipos ABSACC.H - 8051 Absolute Memory Accesses


#define #define #define #define #define #define #define #define CBYTE DBYTE PBYTE XBYTE CWORD DWORD PWORD XWORD ((unsigned ((unsigned ((unsigned ((unsigned ((unsigned ((unsigned ((unsigned ((unsigned char volatile char volatile char volatile char volatile int volatile int volatile int volatile int volatile code data pdata xdata code data pdata xdata *) *) *) *) *) *) *) *) 0) 0) 0) 0) 0) 0) 0) 0)

CTYPE.H - ANSI Standard Functions


bit isalnum (char); bit isalpha (char); bit iscntrl (char); bit isdigit (char); bit isgraph (char); bit islower (char); bit isprint (char); bit ispunct (char); bit isspace (char); bit isupper (char); bit isxdigit (char); char toint (char); char tolower (char); char toupper (char);

INTRINS.H - 8051 Specific Functions


unsigned char _crol_ (unsigned char, unsigned char); left */ unsigned char _cror_ (unsigned char, unsigned char); right */ unsigned int _irol_ (unsigned int, unsigned char); left */ unsigned int _iror_ (unsigned int, unsigned char); right */ unsigned long _lrol_ (unsigned long, unsigned char); left */ unsigned long _lror_ (unsigned long, unsigned char); right */ void _nop_ (void); */ /* Rotate char /* Rotate char /* Rotate int /* Rotate int /* Rotate long /* Rotate long /* NOP instruction

C orientado a microcontroladores 8051

52

bit _testbit_ (bit); */

/* JBC instruction

MATH.H - ANSI Standard Functions


int abs (int); float acos (float); float asin (float); float atan (float); float atan2 (float, float); char cabs (char); float ceil (float); float cos (float); float cosh (float); float exp (float); float fabs (float); float floor (float); long labs (long); float log (float); float log10 (float); float modf (float, float *); float pow (float, float); float sin (float); float sinh (float); float sqrt (float); float tan (float); float tanh (float); (not (not (not (not (not (not (not (not (not (not (not (not (not (not (not (not (not (not (not present present present present present present present present present present present present present present present present present present present in in in in in in in in in in in in in in in in in in in PK51 PK51 PK51 PK51 PK51 PK51 PK51 PK51 PK51 PK51 PK51 PK51 PK51 PK51 PK51 PK51 PK51 PK51 PK51 Eval) Eval) Eval) Eval) Eval) Eval) Eval) Eval) Eval) Eval) Eval) Eval) Eval) Eval) Eval) Eval) Eval) Eval) Eval)

SETJMP.H - ANSI Standard Functions


void longjmp (jmp_buf, int); int setjmp (jmp_buf);

STDIO.H - ANSI Standard Functions


extern extern extern extern extern extern extern extern extern extern char _getkey (void); char getchar (void); char *gets (char *, int); int printf (const char *, ...); char putchar (char); int puts (const char *); int scanf (const char *, ...); int sprintf (char *, const char *, ...); int sscanf (char *, const char *, ...); char ungetchar (char);

STDLIB.H - ANSI Standard Functions


extern extern extern extern extern extern float atof (void *); (not present in PK51 Eval) int atoi (void *); long atol (void *); void *calloc (unsigned int, unsigned int); void free (void xdata *); void init_mempool (void xdata *, unsigned int);

C orientado a microcontroladores 8051

53

extern extern extern extern

void *malloc (unsigned int); int rand (void); void *realloc (void xdata *, unsigned int); void srand (int);

STRING.H - ANSI Standard Function


extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern extern void *memchr (void *s, char c, int len); void *memccpy (void *dest, void *src, char, int len); char memcmp (void *s1, void *s2, int len); void *memcpy (void *dest, void *src, int len); void *memmove (void *dest, void *src, int len); void *memset (void *s, char c, int len); char *strcat (char *dest, char *src); char *strchr (const char *s, char c); char strcmp (char *s1, char *s2); char *strcpy (char *dest, char *src); int strcspn (char *s, char *set); int strlen (char *s); char *strncat (char *dest, char *src, int len); char strncmp (char *s1, char *s2, int len); char *strncpy (char *dest, char *src, int len); char *strpbrk (char *s, char *set); int strpos (const char *s, char c); char *strrchr (const char *s, char c); char *strrpbrk (char *s, char *set); int strrpos (const char *s, char c); int strspn (char *s, char *set);

Ejemplo 8051 Como ejemplo se va a implementar parte de un programa que incializa el vector de interrupcin del puerto serie y hace uso de algunas de las capacidades de las extenisones del C para este microcontrolador (aunque no se le debe buscar sentido alguno a lo que hace).
#include <reg51.h> //Definiciones de los registros #include "princip.h" #include <intrins.h> #define INHIBE_INT EA=0 #define PERMITE_INT EA=1 #define PARAR_T0 TR0=0 #define START_T0 TR0=1 #define Envia(x) SBUF = x char code text[]="Hola"; char xdata PuertoSalida char xdata PuertoEntrada char idata MemInterna _at_ int r; unsigned char ctr;

_at_ 0x0040; _at_ 0x0041; 0x30;

C orientado a microcontroladores 8051

54

bit int

unbit; bdata ibase;

sbit

otrobit = ibase^15;

/*Entero declarado en area direccionable bit a bit */ /* otrobit sera el bit 15 de ibase*/

void main(void){ _testbit_(EA); SetUp_Serie(); Envia(3); PuertoSalida = 0x55;

/* Se saca el valor 55 al dispositivo mapeado en memoria externa en la direccion 0x0040 */

r = suma(3,2); while(1){ // Se hara algo til para la humanidad } } void timer0_1ms(void) interrupt 1 using 2{ INHIBE_INT; //Definidos como Macro PARAR_T0; //Se hace lo que se tenga que hacer if (++ctr==10){ ctr=0; t0_10ms(); } START_T0; //Definidos como Macro PERMITE_INT; } int suma(int a,int b){ return a+b; } void t0_10ms(void){ MemInterna = PuertoEntrada; /* Se recoge un valor mapeado en una direccion de memoria externa (0x0041 exactamente) */ } void Isr_Serie (void) interrupt 4 using 2 { /*-----------------------------------------------Interrupcion serie se activo por recepcion ------------------------------------------------*/ if (RI != 0){ RI = 0; //Se hace lo que se tenga que hacer } /*------------------------------------------------

C orientado a microcontroladores 8051

55

Interrupcion serie se activo por recepcion ------------------------------------------------*/ if (TI != 0){ TI = 0; //Se hace lo que se tenga que hacer } } /*--------------------------------------------------------------------------------------------------------------------------------------------*/ void SetUp_Serie (void) { INHIBE_INT; /*-----------------------------------------------Fijar TIMER1 para generar el baud rate adecuado ------------------------------------------------*/ TR1 = 0; /* para timer 1 */ ET1 = 0; /* inhabilitar interrupcion timer 1*/ PCON |= 0x80; /* fiajr doblador de baud rate a cero */ TMOD &= ~0xF0; TMOD |= 0x20; /* fija a 0 los bits de timer 1 en TMOD */ /* timer 1 en MODO 2: AutoRecarga */

TH1 = 0xFD; /* valores de autorecarga */ TL1 = 0xFD; /* para 9600 baudios y clk = 11059kHz*/ /*-----------------------------------------------Configuracion de los registros serie ------------------------------------------------*/ SM0 = 0; SM1 = 1; /* puerto serie MOD0 1 */ SM2 = 0; REN = 1; /* permite recepcion serie */ TI = 0; RI = 0; ES = 1; PS = 0; TR1 = 1; PERMITE_INT; } /* limpia flag int por transmision */ /* limpia flag int por recepcion */ /* habilita interrupcion serie */ /* establece baja prioridad */ /* arranca Timer1 para poder trabajar*/

C orientado a microcontroladores 8051

56

También podría gustarte