programacion.pdf

24
UNIVERSIDAD DE ALCALÁ Departamento de Electrónica P P ROGRAMACIÓN EN ROGRAMACIÓN EN C C DEL DEL 8051 8051 José Manuel Villadangos Carrizo Julio Pastor Mendoza

Transcript of programacion.pdf

  • UNIVERSIDAD DE ALCALDepartamento de Electrnica

    PPROGRAMACIN EN ROGRAMACIN EN CC DEL DEL 80518051

    Jos Manuel Villadangos Carrizo

    Julio Pastor Mendoza

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 1

    1. INTRODUCCIN

    Cuando se desarroll el 8051, el lenguaje ensamblador era utilizado para producir cdigo eficienteen aplicaciones en las que el tiempo era crtico. La reducida capacidad de la memoria interna delchip, tanto de datos como de programa, haca que el cdigo generado por los primeroscompiladores de alto nivel que salieron al mercado, no fuese muy eficiente. Hoy en da existenvarios fabricantes de software, que han desarrollado compiladores de alto nivel capaces de generarcdigo en C tan eficiente como aquellas partes de programa que sean necesarias escribir enensamblador.

    Trataremos de demostrar las ideas de programacin en C del 8051 a partir del compilador deFranklin C51, del cual es fcil disponer de un versin de evaluacin (www.fsinc.com) queaunque est limitada a generar un tamao mximo de cdigo de 4 Kbytes, es suficiente para llevara cabo numerosas aplicaciones de inters, y sobre todo para conocer un entorno integrado deprogramacin y depuracin en C totalmente profesional, para la familia MCS-51 de Intel.

    Despus de su aparicin en 1978, el C supuso un gran cambio para los programadores debido alas ventajeas que tena frente a otros lenguajes de alto nivel. En diciembre de 1989, el InstitutoNacional de Standards de Amrica (ANSI) defini formalmente el standard del lenguaje C,conocido como ANSI C. La mayora de los compiladores se rigen por este standard, y algunoscontienen extensiones al ANSI C para solucionar generalmente problemas de direccionamiento,relacionados con aplicaciones especificas.

    El lenguaje C tiene la capacidad de generar de manera rpida, comandos de bajo nivel paraacceder directamente a la memoria de datos y perifricos, mecanismos para combinar datos

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 2

    simples de tamao byte y word en estructuras complejas de datos que permiten accesos rpidos,y herramientas para crear funciones complejas de comandos simples de bajo nivel. tambin tieneuna gran potencia para el control de estructuras, operadores, y libreras de funciones para llevara cabo operaciones de alto nivel y programacin estructurada.

    El C se desarroll sin pensar indudablemente en el 8051. Un microcontrolador es bsicamente unmicroprocesador especializado, con numerosos perifricos internos y pensado para aplicacionesde control. Por esta razn, los programadores que habitualmente programan en C, necesitanhabituarse con el estilo de programacin en C para el 8051, debido a las restricciones yextensiones que existen para el 8051.

    2. ORGANIZACIN DE LA MEMORIA EN EL 8051

    El 8051 tiene un espacio separado de memoria de programa y datos, dependiendo de laconfiguracin particular del sistema, y tambin puede tener un direccionamiento interno o externode la memoria de programa y un slo un rea interna, o interna y externa de la memoria de datos.En un sistema tpico basado en el 8051, pueden existir por tanto, tres espacios de memoria, todosellos comenzando a partir de la direccin cero.

    Adems todo esto se complica por la estructura de la RAM interna, donde los primeros 128 bytesson utilizados por el banco de registros, el area de direccionamiento bit a bit, y se completan conposiciones de memoria de propsito general. A esto hay que aadir el segundo rea de 128 bytesdonde se localiza la zona de registros de funcin especial (SFR). Adems en el caso del 8052,existe un nuevo rea de 128 bytes que se solapa con la zona SFR, y que slo permite undireccionamiento indirecto, para distinguir los accesos de la zona SFR a la que slo se permite undireccionamiento directo.

    Dada esta estructura de la memoria tan compleja, existen 6 tipos de especificadores de memoriacuyos mrgenes se detallan a continuacin:

    code Memoria de programa (64 Kbytes).

    data Memoria de datos direccionable directamente para permitir accesos a variablesms rpidos, dentro de los primeros 128 bytes de RAM interna.

    idata Memoria de datos con direccionamiento indirecto a los 256 bytes de memoriaRAM interna (en el 8052).

    bdata rea de datos (16 bytes) con direccionamiento bit a bit, para permitir accesos tipobit a variables de carcter y enteras (128 bits).

    xdata Memoria de datos externa (64 Kbytes).

    pdata Memoria de datos externa paginada, para permitir accesos a los primeros 256bytes (pgina 0) a travs del puerto 0.

    A continuacin se muestran algunas sentencias en C, para especificar que las variables x e y

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 3

    residan en la RAM interna y sean direccionadas directamente, y que el array buffer[100] se siteen RAM externa.

    char data x, y;unsigned int xdata buffer[100];

    En aplicaciones de control es habitual situar ciertas constantes en una tabla dentro de la memoriade programa (ROM). Esto se define fcilmente, con la siguiente sintaxis:

    unsigned char code parametros[]={0x10, 0x20, 0x40, 0x80};

    La seleccin del tipo de memoria para las diferentes variables afecta directamente a la eficienciadel programa final. Como regla general, se usa el rea de memoria de tipo data para almacenarlas variables que requieren un acceso rpido y frecuente. El tipo idata resulta ms pequeo queel tipo data debido al empleo de direccionamiento indirecto. Se recomienda su empleo con arrayso estructuras de pequeo tamao. Todas las variables que no requieran un acceso crtico en eltiempo, y aquellos arrays y estructuras de tamao grande son propias de residir en el rea de tipoxdata.

    Pueden existir variables cuyos tipos de memoria no sean declarados explcitamentemediante los especificadores tipo, o puede que no se desee especificar el tipo de memoria devarias variables porque todas ellas residan en el mismo rea de memoria. Tambin, nuestroprograma puede ser pequeo y por tanto residir en la memoria interna del chip. En estos casos,las siguientes definiciones de modelos de memoria permiten elegir, o un tipo de memoria paratodo el programa (o funcin), o un tipo de memoria por defecto para todas aquellas variables queno se han declarado explcitamente.

    small Las variables y los datos locales se definen para residir en la memoria dedatos interna, de igual forma que si hubiesen sido definidas por elespecificador data.

    compact Las variables y los datos locales se definen para residir en la memoria dedatos externa, de igual forma que si hubiesen sido definidas por elespecificador pdata.

    large Las variables y los datos locales se definen para residir en la memoria dedatos externa, de igual forma que si hubiesen sido definidas por elespecificador xdata.

    La seleccin del modelo de memoria puede hacerse o en tiempo de compilacin ( opcindel software de desarrollo ProView ) o usando la directiva #pragma dentro del cdigo fuente. Elmodelo de memoria por defecto es small.

    Todas las variables automticas de una funcin pueden forzarse al definir la funcin, aresidir en un rea de memoria particular. Por ejemplo la funcin:

    int func(int x, int y) large{x=10;

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 4

    y=20;printf(La suma es %d\n, x+y);return (x-y+5);}

    sita sus variables locales en memoria externa debido a que el modelo de memoria se hadeclarado como large. Una forma de seleccionar globalmente un tipo de memoria para todo elprograma es utilizando la directiva #pragma COMPACT al comienzo del programa, y decidircuidadosamente que modelos de memoria son apropiados, para aquellas funciones individualesy rutinas de interrupcin. Aquellas variables que son declaradas explcitamente mediante losespecificadores de tipo de memoria, no se ven afectadas por el modelo de memoria seleccionada.

    3. CONSTANTES, VARIABLES, Y TIPOS DE DATOS

    Los programas se escriben o para procesar datos, ya sean recibidos por alguna entrada.

    El compilador C51 de Franklin soporta los siguientes tipos de datos:

    Tabla 1. Tipos de datos soportados por C51

    Tipo de datos Bits Bytes Rango de valores

    signed charunsigned charenumsigned shortunsigned shortsigned intunsigned intsigned longunsigned longfloatbitsbitsfrsfr16

    88161616161632323211816

    1122222444

    12

    -128 a +1270 a 255-32768 a +32768-32768 a +327680 a 65535-32768 a +327680 a 65535-2147483648 a 21474836470 a 42949672951.175494E-38 a 3.402823E+380 a 10 a 10 a 2550 a 65535

    Tal como puede observarse en la tabla, el compilador C51 soporta todos los tipos de datos delstandard C. Sin embargo, los tipos de datos sealados en negrita son especficos del 8051 y noforman parte del ANSI C.

    Por definicin, C no soporta accesos directos a datos de tipo bit ni su procesamiento. La formaen que C accede a datos de tipo bit es mediante el uso de mscaras de bit con loscorrespondientes operadores. Debido a que el 8051 est diseado para aplicaciones de control,el acceso directo a los bit de informacin es esencial en su programacin. Los datos tipo bitpueden utilizarse para declarar variables tipo bit que residirn en el rea de memoria interna dedireccionamiento a nivel de bit ( posiciones comprendidas entre la 20H y 2FH de la RAM interna).Por ejemplo, las siguientes declaraciones

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 5

    bit flag1=0, flag2=0, semaforo;

    declaran tres variables tipo bit y se inicializan dos de ellas a cero.

    Los datos tipo sbit se diferencian de los datos tipo bit, en que se usan fundamentalmente paradeclarar variables tipo bit asociadas con los registros de funcin especial (SFR). Recordar quela mayora de los registros de funcin especial del 8051 son direccionables bit a bit(concretamente aquellos que son divisibles por 8 ). Por ejemplo, la declaracin siguiente

    sbit at 0xAF EA;

    establece EA como una variable tipo bit con un direccionamiento absoluto, cuya direccin es la0xAF de la memoria interna. Para el 8051, todos los bit de estado y control de aquellos registrosde funcin especial son predeclarados en el archivo reg51.h. Adems, al incluir el archivo reg51.hen nuestro programa, podemos hacer uso de todos los smbolos de bit tales como TR0, TI, ET0,CY, ...

    Se puede emplear la declaracin sbit para acceder a los bit internos de los objetos declarados porel tipo de especificador de memoria bdata . Por ejemplo, las declaraciones:

    int bdata itemp;sbit bit_ten=itemp^10;

    permiten que una nueva variable bit_ten, acceder al dcimo bit de una variable entera.

    Los datos tipo sfr se usan para declarar y acceder a los registros de la zona de memoria SFR,aunque tal como ocurra con los datos tipo sbit, todos los registros definidos en el rea SFR estnpredefinidos en el archivo reg51.h. Los siguientes ejemplos muestran como acceder a los puertosy registros internos del 8051:

    TMOD=0x20;TH1=0xfd;TCON=0x40;IE=0x90;P1=P1&0xf0;

    4. ARRAYS, ESTRUCTURAS, Y UNIONES

    Las variables de los tipos de datos bsicos pueden agruparse para formar tipos de datos de nivelsuperior, con objeto de mejorar la eficiencia del cdigo.

    4.1. Arrays

    Un array es un grupo de variables del mismo tipo, referenciadas con un nombre comn. Porejemplo, la sentencia:

    int temp[20];

    declara un array capaz de almacenar 20 enteros. Las 20 variables de tipo entero se referenciandesde temp[0] hasta temp[19].

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 6

    A continuacin se muestran algunos ejemplos de declaracin de arrays:

    unsigned int pdata temp[40];float xdata num[20][10];unsigned char code texto[]=Hola;unsigned char code tabla[]={10, 20, 30, 40};unsigned char code tabla_orden[]=

    {Primero, Segundo, Tercero, Cuarto};

    Si el tamao de un array no est explicitamente definido en la declaracin, C automticamentecalcula el tamao a partir de los datos que se han utilizado en la inicializacin. Cuando el tamaodel array sea grande, conviene que sea inicializado en el rea xdata.

    4.2. Estructuras

    Una estructura es un conjunto de variables relacionadas, que pueden o no ser del mismo tipo dedatos. Por ejemplo:

    struct alarma{unsigned char dia_string[10];unsigned int hora;unsigned int minuto;unsigned int segundo;

    };

    declara una estructura de datos para representar el da y la hora de activacin de una alarma enun programa. Con la estructura ya declarada, una variable con ese tipo de estructura seradeclarada de la forma siguiente:

    struct alarma alarma_on;

    Es importante diferenciar entre la declaracin del tipo de datos de la estructura y la declaracinde la variable de tipo estructura. La declaracin del tipo de datos de la estructura, le dice slo alcompilador el tipo de datos que sern procesados, mientras que la declaracin de la variable ledice al ordenador la cantidad de memoria necesaria para almacenar los datos de la estructura enla definicin. En el ejemplo anterior, alarma es una estructura formada por un array de caracteresseguido de tres enteros sin signo. Sin embargo, la variable alarma_on de tipo alarma, toma 15bytes de espacio en memoria. La inicializacin de la variable podra ser:

    struct alarma alarma_lunes={Lunes, 12, 30, 00};

    Para acceder a las variables de la estructura, se utiliza el operador (.). Veamos un ejemplo:

    printf(La activacin el %s sera a las %d:%d:%d,alarma_lunes.dia_string, alarma_lunes.hora, alarma_lunes.minuto,alarma_lunes.segundo);

    Y el resultado de la ejecucin sera:

    La activacin de la alarma el Lunes sera a las 12:30:00

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 7

    En aplicaciones de control, suele ser habitual que un grupo de datos sea recogido de la mismafuente pero almacenado en variables de diferente tipo. Una estructura que permita accesos rpidosa las diferentes variables recogidas bajo el mismo nombre simblico, hace que el programa seams comprensible. Sin embargo, los bytes de la estructura se almacenan en memoria de formacontigua, y el C51 no permite mezclar variables tipo bit con otros tipos de variables, debido a quelos datos tipo bit son restringidos a los 16 bytes de memoria interna que permiten undireccionamiento tipo bit.

    El tipo de especificador bdata, que declara una variable entera o de carcter para serdireccionada bit a bit, puede utilizarse para forzar a una estructura a ser almacenada dentro delrea de direccionamiento de bit:

    bdata estruct swithches {unsigned char sw1;unsigned char sw2;} swset;

    sbit sw_read1 = swset.sw1 ^ 0;sbit sw_read2 = swset.sw2 ^ 4;

    4.3. Uniones

    Las uniones son similares a las estructuras y ambas son utilizadas para agrupar variables dediferente tipo bajo un nombre comn. Sin embargo, mientras que en una estructura las variablesse sitan en memoria de forma contigua, una unin guarda todos sus datos en las mismasposiciones, solapandose unos con otros, es decir, que ocupan las mismas posiciones pero no almismo tiempo.

    Una unin tiene aplicacin cuando parte de un dato se trata de forma diferente, en diferentesinstantes de tiempo. Supongamos que queremos almacenar el valor de un timer de 16 bits, paraleerlo como dos bytes.

    Como ejemplo, podemos definir una unin constituda por una estructura de 2 bytes y un entero.Cuando se quiere escribir en el byte alto, referenciamos dicho espacio como 2 bytes, pero cuandoqueremos usar el resultado lo consideramos como un entero.

    union split{unsigned int word; struct {unsigned char hi;unsignedchar low;} bytes};

    Declaremos la variable new_count tipo union, y veamos la forma de utilizarla:

    union split new_count;

    new_count.bytes.hi = TH1;new_count.bytes.low = TL1;old_count=new_count.word;

    5. PUNTEROS

    La potencia de los punteros en C, es ya conocida. El concepto de puntero es simplemente unavariable especial que guarda la direccin de otras variables o constantes. Por ejemplo, en lenguaje

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 8

    ensamblador del 8051, las funciones principales de los registros R0, R1, DPTR, y SP sonalmacenar las direcciones de los datos para ser accedidos mediante direccionamiento indirecto,y por tanto, pueden ser definidos como punteros a los datos direccionados.

    La principal utilizacin de los punteros es el direccionamiento indirecto de los datos. Como todaslas variables residen en RAM interna o externa, pueden ser accedidas mediante el uso depunteros. Se utilizan normalmente en la indexacin, direccionamiento y movimiento de datos.

    Una variable px se declara como variable puntero, de la forma siguiente:

    char *px

    y sirve para apuntar a datos de tipo carcter. Por definicin, las variables puntero son siempre detipo unsigned int, pues guardan slo direcciones. Actualmente, el tipo de datos de los punterosdepende del espacio de memoria de la tarjeta y del compilador utilizado. No hemos de confundirla direccin de la variable....

    El ejemplo siguiente resume los conceptos de indireccin de los punteros:

    void main (void){char x, *px;px = &x;x = d;printf(El puntero px apunta a %c, *px);

    }

    Este programa imprimira el carcter contenido en la variable x, es decir, d.

    Con px apuntando a x, el programa es capaz de acceder indirectamente al contenido de x.Tambin existe otra forma de inicializar la variable puntero:

    char x;char *px = &x;

    que asigna a px la direccin de x (no a *px !).

    5.1. Tipos de punteros en el 8051

    Cuando se declara un puntero, se necesita especificar no slo dnde el puntero va a seralmacenado, sino tambin a qu tipo de zona de memoria va a apuntar. La declaracin siguiente

    unsigned char data * xdata x_ptr = 0x4a;

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 9

    data

    4A

    49

    4Bxxxxxxxx

    4A4

    x_ptr

    xdata

    Tipo de memoria donde reside el puntero

    Fig.1. Punteros

    declara a x_ptr como una variable puntero que residir en memoria xdata, para apuntar a losdatos o variables de tipo carcter almacenados en la memoria de tipo data. El puntero x_ptr esinicializado con el valor 0x4a, que ha de ser una direccin de tamao byte para apuntar dentro delrea de memoria tipo data. En la figura 1 puede verse como x_ptr apunta a una posicin dememoria del rea data.

    La variable puntero consta de 3 bytes, uno para almacenar un nmero ndice y otros dos paraalmacenar la direccin sin signo 4Ah.

    Observe las posiciones de los dos tipos de especificadores de memoria de la declaracinanteriormente vista: xdata se sita inmediatamente antes de la variable puntero y especifica eltipo de memoria donde se sita el puntero. El especificador data seguido del asterisco (*) le diceal compilador que este puntero se usa para apuntar los datos ubicados en el rea data de memoria.Los punteros as declarados reciben el nombre de punteros tipo porque los tipos de memoriaestn explcitamente declarados.

    Se puede resumir diciendo que un puntero tipo queda declarado bajo la siguiente sintaxis:

    dato_tipo mem_tipo_d * [mem_tipo_ptr] ptr_nombre [ = direccin];

    dato_tipo: Tipo de dato a ser apuntado, y puede ser uno de los habitualmenteutilizados: char, int, float, ...

    mem_tipo_d: Tipo de especificador de memoria que define el espacio dememoria donde los datos van a ser apuntados. Puede ser de tipocode, data, idata, pdata, o xdata.

    mem_tipo_ptr: Tipo de especificador de memoria (opcional) que define el espaciode memoria donde reside el puntero. Puede ser de tipo code, data,idata, pdata, o xdata. En el caso que no se defina, el tipo dememoria seleccionado es el que exista por defecto.

    ptr_nombre: N o m b r e

    asignado a lav a r i a b l epuntero.

    direccin: Opcionalmen t e ,constituye elv a l o r d edireccin conel que seinicializa. Unpuntero datapuede tomarvalores entre0x00 y 0x7F;

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 10

    un puntero idata, entre 0x00 y 0xFF; un puntero xdata, entre0x0000 y 0xFFFF; un puntero pdata, entre 0x00 y 0xFF; y unpuntero code, entre 0x0000 y 0xFFFF

    Algunos ejemplos de declaracin de punteros son:

    unsigned char xdata * data s_ptr = 0x1234;int code * tabla_ptr = 0x1000;unsigned float xdata * xdata u_ptr;

    El compilador de C de Franklin permite operar con los denominados punteros sin tipo, o tambindenominados genricos, que se utilizan para acceder a cualquier variable, sin importarnos suposicin en la memoria del 8051. Varias de las libreras del C51 utilizan este tipo de puntero, puesmediante estos punteros genricos, una funcin puede acceder a los datos sin tener en cuenta ellugar de memoria donde estn almacenados. Se declaran de igual forma a como especifica elANSI C:

    char *p;int *ptr;long *x;

    Los punteros genricos siempre se almacenan en tres bytes. El primero de ellos es el queespecifica el tipo de memoria, el segundo es el byte alto que representa a la direccin, y el terceroes el byte bajo. La tabla 2 muestra los valores del byte que identifica al tipo de memoria:

    Tabla 2. Identificacin de la memoria del puntero

    Tipo de memoria idata xdata pdata data/bdata code

    Valor 1 2 3 4 5

    Cuando a un puntero genrico se le asigna la direccin de la variable despus de su declaracin,por ejemplo:

    int xdata x;ptr = &x;

    el primer byte de ptr, representa al tipo de memoria de la variable x. Como x se ha definido enmemoria xdata, el primer byte toma el valor 2. Los siguientes dos bytes de ptr, constituyen ladireccin.

    Un puntero genrico a xdata localizado en la memoria por defecto, puede inicializarse con unadireccin xdata de forma directa:

    unsigned char * ptr = 0x24536L;

    donde el valor 2, representa al tipo de memoria xdata que se toma por defecto (primer byte deptr) y el sufijo L inicializador long. Tal declaracin es equivalente a haber escrito:

    unsigned char xdata * ptr = 0x4536;

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 11

    El cdigo que resulta al emplear punteros declarados de forma genrica es ms lento que elequivalente considerando la declaracin bajo un puntero tipo. Esto se debe a que el rea dememoria no se conoce hasta el tiempo de ejecucin. El compilador no puede optimizar losaccesos a memoria y produce un cdigo genrico que puede acceder a cualquier posicin dememoria.

    Puede especificarse el rea de memoria en la que se almacenar un puntero genrico, medianteel uso del especificador de tipo de memoria:

    char * xdata sptr;int * data nptr;long * idata vptr;

    Las anteriores declaraciones son punteros a variables que pueden almacenarse en cualquier reade memoria. Sin embargo, los punteros se almacenan en xdata, data e idata respectivamente.

    5.2. Conversin de punteros

    El compilador C51 de Franklin permite conversiones entre punteros tipo y punteros genricos.La conversin de punteros puede forzarse explcitamente en el programa usando un molde (typecast) o puede ser forzado por el compilador.

    Por ejemplo, el compilador C51 fuerza un puntero tipo en un puntero genrico cuando el punterotipo se pasa como argumento de una funcin que requiere un puntero genrico. Este es el casode funciones tales como printf, sprintf, y gets que utilizan punteros genricos como argumentos.

    Un puntero tipo usado como argumento de una funcin, se convierte siempre a un punterogenrico si no est presente en el prototipo de la funcin. Esto puede causar errores si la llamadaa la funcin espera un puntero ms corto como argumento. Con objeto de evitar este problema,se recomienda utilizar ficheros include y prototipos en todas las funciones. Esto garantiza laconversin de los tipos por el compilador e incrementa la probabilidad de que al compilar sedetecten errores de conversin.

    En el men de ayuda del entrono de desarrollo ProView de Franklin, se pueden observar los tiposde conversin entre punteros.

    Los ejemplos siguientes ilustran algunas conversiones de punteros y el cdigo resultante:

    int *ptr1; /* puntero genrico (3 bytes) */int xdata *ptr2; /* puntero xdata (2 bytes) */int idad *ptr3; /* puntero idata (1 byte) */int code *ptr4; /* puntero code (2 bytes) */

    void pconv(void){ptr1 = ptr2; // xdata * a genrico * ptr1 = ptr3; // idata * a genrico *ptr1 = ptr4; // code * a genrico

    ptr4 = ptr1; // genrico * a code *ptr3 = ptr1; // genrico * a idata *ptr2 = ptr1; // genrico * a xdata *ptr2 = ptr3; // idata * a xdata *

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 12

    ptr3 = ptr4; // code * a idata *}

    5.2. Puntero a arrays

    El nombre de cualquier array es un puntero fijo (constante puntero) que apunta al primerelemento del array. Por ejemplo, un array de enteros temp[20], consta de los elementos temp[0],temp[1], ... temp[19]. Entonces, temp, que es el nombre del array es un puntero a una constanteque es la direccin de temp[0], y por tanto, no puede modificarse. No es legal temp++, al igualque tampoco lo es 5++. Una contante puntero puede utilizarse en un programa al igual que cualquier otra constante, peroha de seguir ciertas reglas. Por ejemplo, temp+1 apunta al siguiente elemento del array, es decira temp[1], y no a la siguiente posicin de memoria donde el array est almacenado. Esto esdebido a que cada elemento del array requiere varios bytes de memoria. De ah que podamosacceder a los elementos del array ya sea utilizando la notacin temp[n], o *(temp+n). Adems,el operador de direccin puede tambin ser utilizado para direccionar cualquier elemento delarray. Por ejemplo, &temp[2] es la direccin del tercer elemento del array, temp[20].

    Considerando arrays de mayor dimensin, las reglas son las mismas. As, para el caso de un arraybidimensional, *(*(temp+n)+m) es equivalente a temp[n][m].

    Una variable puntero puede crearse para servir de ndice y acceder a los elementos de un array,al mismo tiempo que permite ser manipulada fcilmente con ayuda de los diferentes operadoresde C. El ejemplo siguiente muestra como una variable puntero se utiliza para acceder a loselementos de un array por indexacin.

    #include

    void main(void){int temp[3];int sum=0, sum_celsius=0;int num, dia=0;int *ptemp;

    printf(\n);ptemp = temp;do

    {printf(Introduce para el dia %d: , ++dia)scanf(%d, ptemp);}

    while ( *(ptem++) > 0 );ptemp = temp;num = dia-1;

    for (dia=0; dia

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 13

    }

    El programa toma hasta 31 valores de temperatura positivos e imprime en pantalla el valor medio.

    Un uso comn de los punteros a arrays es la declaracin de un array de caracteres mediante unavariable puntero.

    char *parray = {el sol, el mar, y la tierra};

    5.3. Arrays de punteros

    Un grupo de variables puntero pueden formar un array de punteros, que de forma simultneaapuntan a diferentes elementos en un grupo de datos, de manera similar a un array bidimensional.Una aplicacin de un array de punteros es en la indexacin de un grupo de mensajes para sermostrados por un display, y acceder de forma rpida con referencias de unos a otros. Acontinuacin se muestra un programa ejemplo de como un array de punteros ordena un grupode nombres.

    #include #include #include #define MAXNUM 30#define MAXLEN 30void ordena(char *, int);char nombre[MAXNUM][MAXLEN];

    void main (void){char *temp;int cuenta=0;int in, out;

    clrscr();printf(\nIntroduce 8 nombre. Pulsa return para finalizar laentrada\n);

    while (cuenta < MAXNUM){printf (Nombre %d: , cuenta+1);gets(nombre[cuenta]);if (strcmp(nombre[cuenta++], )==0)break;}

    ordena(nombre[0],cuenta-1);printf(\n\n Pulsa una tecla para comenzar.);getch();}

    void ordena(char *pp, int cnt){char *kp, *pt[MAXNUM];int x, y;

    for(x=0; x

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 14

    pt[x]=pp+40*x;

    for(x=0; xretardo = 30;ptr->patron = 0x40;

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 15

    donde el valor 30 se sita en la variable entera tiempo, y el dato 0x40 en la variable carcterpatron. Adems, en el caso de arrays de caracteres, C permite la declaracin de una varible detipo estructura, sin el nombre de la variable.

    struct medida {int tiempo;int retardo;char patron;}*ptr;

    En este caso el acceso a los elementos de la estructura es idntico al visto anteriormente.

    Una unin es similar a una estructura, de ah que se use el mismo formato en su declaracin:

    union medida {int tiempo;int retardo;char patron;}*ptr;

    6. SENTENCIAS DE CONTROL

    Se detallan a continuacin las diferentes sentencias de control que se pueden emplear en unprograma en C. Se utilizan para realizar bucles y mecanismos de decisin para controlar el flujodel programa. Permiten que una sentencia de C o un grupo de ellas puedan ejecutarse de maneraselectiva, o repetirse un nmero determinado de veces, o que sean ejecutadas si se cumple unadeterminada condicin dentro del programa.

    6.1. Sentencias while y do...while

    En aplicaciones de control, es habitual monitorizar una determinada entrada o registro, mientrasse lleva a cabo una determinada tarea. La sentencia while puede utilizarse para llevar a cabo dichamonitorizacin. En el ejemplo siguiente,

    while (P1&0x20){x=P1;P1=0x00;P2=0x0C;}

    las sentencias que se encuentran entre las llaves, se ejecutan slo si el bit 5 del puerto 1 es un 1".

    La forma de esperar a que suceda una determinada seal para continuar con la ejecucin delprograma, es utilizando la siguiente sentencia:

    while (semaforo);

    Se trata de un bucle infinito, mientras el valor de semaforo permanezca a nivel alto.

    La sentencia do-while es similar a while salvo que la condicin se evala despus de haberseejecutado el cdigo que resida dentro del cuerpo. Esto trae como consecuencia que el bucle se

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 16

    ejecuta siempre al menos una vez.

    do {una o ms sentencias de C;

    } while (expresin);

    6.2. Sentencia if...else

    La sentencia if constituye la manera ms simple de tomar una decisin en C. Tiene la sintaxisgeneral:

    if (test) sentencia1;

    else sentencia2;

    Donde test es una expresin cuyo resultado se evala como cierto (distinto de cero) o falso (iguala cero). Si la expresin test es cierta se ejecuta la sentencia1 y si es falsa la sentencia2. En laexpresin que condiciona la ejecucin, pueden emplearse los operadores lgicos y de igualdad.

    if (++conta == limite){if(!CY) {set_alarma();flag=1;}

    }else

    x=P1;

    Observe en el ejemplo anterior que existe una nueva sentencia if, dentro de la primera. Slo si lacondicin ms externa es cierta, se entra en el segundo if. No debe confundir el operador deigualdad == con el de asignacin =.

    La palabra clave else se puede combinar con la palabra clave if para generar secuencias anidadas.

    if (var=10)printf(La variable es 10");

    else if (var==20)printf(La variable es 20);

    elseprintf(La variable no es ni 10 ni 20);

    6.3. Sentencia for

    Tiene la sintaxis general:

    for (expresin_inico, expresin_test; expresin_repeticin){una o ms sentencias de C;}

    Esta sentencia al igual que while consiste en la repeticin de un bucle. Dentro del for, se debe

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 17

    indicar el valor inicial de la expresin, la condicin que debe cumplirse para finalizar el bucle yqu modificaciones deben realizarse en cada iteracin.

    void main (void){int n, factorial=1;for(n=6;n==1;n--) factorial*=n;}

    6.4. Sentencias switch, case, break y default

    La sintaxis es la siguiente:

    switch (num){case 1:

    sentencia1;break;

    case 2:sentencia2break;

    defaul:otras_sentencias;

    }

    Esta sentencia permite ejecutar una serie de sentencias en funcin del valor de una variable. Enel ejemplo anterior la variable a consultar es num y los valores que se contemplan son 1 y 2.

    En caso de que la variable valga 1 se ejecutara la sentencia1, despus de lo cual con la sentenciabreak el flujo del programa se va a la lnea siguiente del final del bucle switch. Si num es 2 seejecuta la sentencia2, y si no fuese ninguno de los dos valores ira a default y ejecutaraotras_sentencias.

    6.5. Sentencia continue

    La sentencia continue provoca el salto de control del programa desde el lugar en que se encuentrehasta el final del bloque en curso, saltando todas las sentencias que haya entre ellas, peropermitiendo que el bucle contine.

    7. FUNCIONES, MDULOS Y PROGRAMAS

    Las funciones permiten construir bloques de un programa. Un programa en C es un conjunto defunciones especialmente diseadas para realizar ciertas tareas especficas, y en conjunto llevar acabo el objetivo final del programa.

    Generalmente una funcin se crea para realizar una determinada tarea por s misma, ycomunicarse con otras funciones ya sea mediante el paso de parmetros que ha de recibir comoentrada para ser procesados, o devolviendo algn resultado que otras funciones precisen. Numerosas tareas bsicas pueden implementarse a travs de funciones y as ser utilizadas endiferentes programas, o dentro de un mismo programa en diferentes instantes de tiempo.

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 18

    Cuando una funcin se llama, el control se pasa a la misma para su ejecucin y cuando estafinaliza, el control es devuelto de nuevo al mdulo que llam, para continuar con la ejecucin delmismo, a partir de la sentencia siguiente a la que se efectu la llamada.

    7.1. Declaracin de una funcin (prototipos)

    Teniendo en cuenta la convencin que existe en las funciones escritas en C, para declarar unafuncin es necesario es necesario especificar el prototipo de la funcin, es decir, el nombre de lafuncin, los argumentos, y el tipo de datos que pudiese retornar. Tambin puede ocurrir que sedesee modificar el modelo de memoria por defecto para una funcin particular, con objeto de quetenga unas determinadas caractersticas particulares.

    Para especificar un modelo de memoria para una funcin, es necesario que las variables localesy los argumentos de la funcin se almacenen en el rea de memoria especificada. Como ya seindic, la memoria RAM interna del 8051, especificada bajo el modelo small, es buena paraguardar datos que necesiten accesos rpidos. Aquellas funciones que tengan tiempos de ejecucincrticos, deberan utilizar el modelo de memoria small. Cuando la memoria interna del 8051 nosea suficiente para almacenar datos, dada su pequea capacidad, ser necesario entonces elegirun modelo de memoria adecuado.

    El 8051 tiene cuatro bancos de registros en RAM interna, cada uno de ellos formado por ochoregistros (R0-R7). El banco seleccionado po defecto despus del reset es el banco 0. Existe laposibilidad de seleccionar el banco de trabajo por medio de la directiva REGISTERBANK. Estopermite seleccionar un banco diferente para una determinada funcin. La conmutacin de losbancos es habitual al trabajar con interrupciones o en sistemas de tiempo real, a fin de minimizarel tiempo empleado en salvar los registros de inters al entrar en la funcin.

    El formato standard para declarar una funcin de C, en el software de Franklin es:

    return_tipo nombre_func(arg) [mem_tipo] [reentrant] [interrupt][using]

    return_tipo: Tipo del valor retornado por la funcin. Si no se especifica, seasume por defecto el tipo int.

    nombre_func: Nombre de la funcin.

    args: Es la lista de argumentos de la funcin.

    mem_tipo: Especificacin explcita del modelo de memoria (small, compact,o large)

    reentrant: Indica que la funcin es recursiva o reentrante.

    interrupt: Indica que se trata de una funcin de interrupcin.

    using: Especifica el banco de registros utilizado por la funcin (ej. using

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 19

    2).

    A continuacin se muestra un ejemplo con diferentes modelos de memoria, y el uso del banco3 de registros en la funcin xfunc().

    #pragma small

    void main (void){

    int i=10, j=20, k;k= xfunc(i, j);

    }

    int xfunc(int x, int y) large using 2{

    int z;z=x*y;return(z-x-y);

    }

    Las variables locales (i, j, k) de main() se guardan en memoria RAM interna, debido a la directiva#pragma small. Sin embargo, los valores i y j pasados a la funcin xfunc(), se localizan en lamemoria xdata, debido a que los argumentos y variables locales (x, y, z) de la funcin xfunc() se les aplica el modelo de memoria large. Adems, el banco de registros utilizado por la funcinxfunc(), es el banco 2.

    7.2. Paso de parmetros a una funcin

    La comunicacin entre las funciones se lleva a cabo a travs de los argumentos que se usan enla llamada a la funcin y el valor retornado por la funcin. Una funcin es un bloque deinstrucciones que residen en memoria de cdigo. Las variables son, por otro lado, posiciones dememoria que pueden ser de cualquiera de los tipos: small, compact, o large. Tradicionalmente enlos microprocesadores de 8 bits, los valores pasados a una funcin eran almacenados en la pila(stack), la cual tena un tamao de hasta 64Kbytes. En el 8051 la pila reside en memoria RAMinterna (despus del reset el SP se sita en la posicin 07) y su tamao mximo es de 128 bytes,pudiendo ser tan pequea como 64 bytes para algunas versiones de la familia (87C751 de Philips).Debido a estas limitaciones existen diferentes formas de llevar a cabo el paso de parmetros a unafuncin.

    Paso de parmetros por registros

    Con compilador C51 de Franklin los parmetros se pasan por defecto, de una funcin a otra atravs de registros, estando limitado el nmero de ellos a tres. Los parmetros que no pueden serlocalizados en registros, se pasan automticamente a travs de posiciones fijas de memoria siendoel modelo de memoria utilizado el que se especifique en la declaracin. Los tres parmetros quepermiten pasarse por registro siguen ciertas reglas, debido a que existen slo 8 registrosdisponibles en cada banco, y el tipo de dato de cada parmetro pueden llegar en cualquier orden.

    La tabla 3 muestra los registros usados en el paso de parmetros a funciones cuando losargumentos son de tipo diferente.

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 20

    Tabla 3. Registros usados en paso de parmetros a funciones

    argumento char/ptr de 1byte int/ptr de 2 bytes long/float ptr genrico

    123

    R7R5R3

    R6,R7R4,R5R2,R3

    R4-R7R4-R7

    R1-R3R1-R3R1-R3

    Obviamente, no todos los tres parmetro pueden pasarse por registros. Dependiendo de el tipode datos de los argumentos, un parmetro que no sea pasado por registro se pasa a travs del reade memoria asociada con la funcin. En el men de ayuda de ProView32, se puede consultar unalista con la combinacin de varios parmetros y el tipo de memoria utilizado cuando se pasan auna funcin.

    Una funcin puede retornar un valor a travs de un registro. La tabla 4 muestra los registrosusados en funcin del tipo de dato retornado.

    Tabla 4. Registros usados para el retorno de valores

    Tipo de dato Registro

    char (1 byte) R4

    int (2 bytes) R4,R5

    ptr genrico (3 bytes) R0,R2,R3

    float (4 bytes) R4-R7

    double (6 bytes) R2-R7

    long double (7 bytes) R1-R7

    Paso de parmetros a travs de memoria

    El paso de parmetros a una funcin por registro podra utilizarse en caso de aplicaciones en lasque el tiempo es crtico, debido a que su direccionamiento es ms rpido. Sin embargo, nosiempre es posible utilizar los registros, pues slo existen 8 disponibles. Otra limitacin est enque no pueden pasarse parmetros tipo bit a una funcin a travs de registros. Por ello, elcompilador C51 permite utilizar la memoria para llevar a cabo el proceso.

    La directiva de control NOREGPARMS hace que slo se permita el paso de parmetros a travsde memoria, desactivandose el modo por defecto (uso de registros). Igualmente, esiste la directivade control REGPARMS, para activar el modo de paso por registro. El ejemplo siguiente ilustra eluso de estas direstivas:

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 21

    #include #pragma NOREGPARMSextern int bfunc(float, int, char)

    void main (void){

    int i=10, j=20, n, k;float x=3.1416;char ch=65;n=bfunc(x, i, ch);k=xfunc(i, j);printf (%d, %d , n, k);

    }

    #pragma REGPARMSint xfunc(int x, int y)

    {int z;z=x*y;return(z-x-y);

    }

    7.3. Funciones reentrantes

    Una funcin reentrante es aquella que puede ser llamada simultneamente por otras funciones orecursivamente por ella misma. Los parmetros pasados a una funcin de este tipo se almacenanjunto con las variables locales de la funcin en una pila creada a tal efecto para la funcin. Laubicacin de la pila queda determinada por el modelo de memoria definido en la funcin,pudiendo ser cualquiera de los tres posibles.

    Una funcin reeentrante se declara con el atributo reentrant tal como se vio en el apartado 7.1.Por ejemplo, si la funcin xfunc(int x, int y) del programa anterior necesita ser reentrante, ladirectiva REGPARMS no podra usarse debido a que las funciones reentrantes no recibenparmetros de registros. La funcin entonces, se declarara as:

    int xfunc (int x, int y) large reentrant{

    int z;z=x*y;return(z-x-y);

    }

    Los argumentos x, y, as como la variable local z, se ubicaran en xdata debido a que es dondese genera la pila (stack) para la funcin. El modelo de memoria elegido para la funcin determinano slo donde se guardan las variables, sino tambin la memoria para el paso de parmetros.

    Debemos tener precaucin cuando queramos situar la pila en memoria interna, ya que puede serfcilmente desbordar la capacidad de la RAM interna. Al comienzo de la RAM y por encima dela zona de registros, es donde se ubican las variables que se declaran tipo bit, data, e idata, y lasvariables locales de aquellas funciones que se declaran usando el modelo small de memoria. Porencima de estas posiciones es donde se inicializa el puntero de pila (SP). La pila que se creaautomticamente y se asocia con una funcin que usa el modelo de memoria small, se localizaen la parte alta de la RAM interna, y crece hacia posiciones ms bajas a medida que recibe datos.

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 22

    Debido al nmero de veces que puede ser llamada la funcin, puede dar lugar al overflow de laRAM interna.

    7.4. Funciones de interrupcin y conmutacin de los bancos de registros

    Una funcin de interrupcin es un elemento importante en aplicaciones de tiempo real conmicrocontroladores. Cuando ocurre una interrupcin el sistema entra en un estado especial, quedemanda la ejecucin de una rutina particular del programa (funcin de interrupcin)suspendiendose la ejecucin en curso, hasta que el servicio de interrupcin se completa.

    El 8051 tiene un nmero de interrupciones hardware que permiten detectar eventos externos,operaciones de los timers, y el control del puerto serie. El compilador C51 soporta interrupcionesy permite escribir en C la rutina asociada con la fuente de interrupcin. A partir de el nmero deinterrupcin y del banco de registros especificado para la funcin de interrupcin, el compiladorgenera automticamente el vector de interrupcin as como la entrada y la salida al cdigo queconstituye la funcin de interrupcin.

    El atributo interrupt en la declaracin de una funcin permite especificar que dicha funcin esde interrupcin. El argumento del atributo de la funcin especifica el nmero de interrupcinasociado con dicha funcin. Por ejemplo,

    void tiempo (void) interrupt 3{

    TH1=-100;TL1=-100;ET1=1;if (dtime=256) P1=1;dtime++;

    }

    donde el nmero de interrupcin especifica que esta funcin corresponde a la funcin deinterrupcin del Timer 1. La correspondencia entre el nmero de interrupcin y el tipo deinterrupcin se muestra en la tabla 5.

    Cuando se llama a una funcin de interrupcin, la CPU lleva a cabo las siguientes operaciones:

    1. Salva el contenido de los registros ACC, B, DPH, DPL, y PSW en la pila cuando seanecesario.

    2. Salva todos los registros de trabajo que sern utilizados por la funcin de interrupcinen la pila.

    3. Ejecuta la funcin de interrupcin.

    4. Restaura todos los registros de la pila salvados antes de salir de la funcin deinterrupcin.

    5. Ejecuta la instruccin RETI para finalizar la funcin.

    Tabla 5. Nmeros de interrupcin

  • Programacin en C del 8051

    Laboratorio de Sistemas Electrnicos Digitales II Pg. 23

    Interrupcin Nmero

    Externa 0 0

    Timer 0 1

    Externa 1 2

    Timer 1 3

    P. Serie 4

    Con objeto de emplear el menor tiempo posible en salvar los registros en la pila al entrar a unafuncin de interrupcin, se puede cambiar de banco de registros. Para ello, el compilador C51permite utilizar el atributo using N en la declaracin de una funcin de interrupcin, donde elvalor N representa al banco seleccionado al entrar en la funcin. Para el ejemplo anterior, y si sequisiese usar el banco 1 al entrar en la funcin, la declaracin sera:

    void tiempo (void) interrupt 3 using 1{

    TH1=-100;TL1=-100;ET1=1;if (dtime=256) P1=1;dtime++;

    }