Tutorial Del or de C de CCS

download Tutorial Del or de C de CCS

of 69

Transcript of Tutorial Del or de C de CCS

Tutorial del compilador de C de Custom Computer Services (CCS) para PICs.

Introduccin

Esta guia o tutorial esta dedicada al lenguaje de programacin C, concretamente al del compilador CCS. A lo largo de este artculo iremos viendo los rudimentos del lenguaje y el repertorio de instrucciones incluidas en l. Este puede ser un buen punto de partida para aquellos programadores que estn empleando algn dialcto BASIC, como el PIC BASIC incluido en el PIC SIMULATOR IDE, PIC BASIC PRO (PBP) o PROTON

CCS - ComentariosLos comentarios son tiles para informar al que lee nuestro cdigo (o a nosotros mismos)el significado o funcionamiento de cada parte del programa. Todos los comentarios son ignorados por el compilador, por lo que no debes preocuparte por llenar la memoria del PIC.

Un comentario puede ser colocado en cualquier parte del programa, excepto en medio de una palabra reservada, del nombre de una funcin o del nombre de una variable. Los comentarios puede ocupar mas de una linea de largo. Pueden utilizarse para desabilitar momentanemente un trozo de cdigo. Hay dos formas de introducir un comentario. La primera es la misma que en cualquier otro compilador de C:/* Esto es un comentario */

Es decir, todo lo que haya escrito entre /* y */ sera tomado por el compilador como un comentario. La segunda manera es la siguiente:// Esto es un comentario

En este caso, el comentario comienza en // y se extiende hasta el final de la linea.

CCS - VariablesProgramando en CCS.

La programacin seria prcticamente imposible sin el uso de variables. Podemos hacernos una imagen mental de las variables consistente en una caja en la que podemos guardar algo. Esa caja es una de las muchas que disponemos, y tiene en su frente pegada una etiqueta con su nombre. Estas cajas tienen ciertas particularidades, que hace que solo se puedan guardar en ellas determinados tipos de objetos. En esta analoga, cada caja es una variable, su contenido es el valor que adopta, y la etiqueta es el nombre de la variable. Como su nombre lo indica, y como veremos mas adelante, el contenido de una variable puede ser modificado a lo largo del programa.

TiposEl lenguaje C proporciona cinco tipos bsico de datos, con cuatro modificadores posibles. Podemos utilizar variables de cualquiera de esos tipos. La tabla siguiente muestra los tipos disponibles:Tipo Ancho (Bits) Rango

short

1

0o1

short int

1

0o1

int

8

0 a 255

char

8

0 a 255

unsigned

8

0 a 255

unsigned int

8

0 a 255

signed

8

-128 a 127

signed int

8

-128 a 127

long

16

0 a 65536

long int

16

0 a 65536

signed long

16

-32768 a 32767

float

32

3.4E-38 a 3.4E+38

Si miras con atencin la tabla anterior, puedes ver que hay tipos que parecen estar repetidos. En realidad, ocurre que CCS permite una "forma corta" para escribir algunos de los tipos. Concretamente, podemos utilizar unsigned, short, o long en lugar de unsigned int, short int, o long int.

DeclaracinLas variables deben ser declaradas antes de ser utilizadas en el programa. El proceso de declaracin de variables le dice a CCS de que tipo son y como se llaman. Al igual las demas instrucciones CCS que veremos a lo largo de este tutorial, debe terminar con ;. La forma en que se declara una variable es la siguiente:tipo nombre_de_la_variable;

donde tipo es alguno de los enumerados en la tabla anterior. Veamos un ejemplo:int temperatura;

Esa linea permite a nuestro programa emplear la variable temperatura, que sera capaz de albergar cualquier valor comprendido entre 0 y 255.

Asignacin de valoresAsignar un valor a una variable es una tarea bien simple. Basta con hacer lo siguiente:nombre_de_variable = valor;

donde nombre_de_variable es el nombre de la variable que contendra el valor. Al igual que todas las instrucciones de CCS, debe terminar con un ; (punto y coma). Por ejemplo, supongamos que queremos asignar el valor "100" a la variable "count". Lo hacemos de la siguiente manera:count = 100;

donde 100 es una constante. Podemos asignar un valor a una variable en el momento en que la declaramos. lo siguientes son algunos ejemplos de esto:int a = 0;

Hace que la variable a sea del tipo entero, y le asigna el valor 0.signed long a = 125, b, c = -10;

a,b y c son declaradas como long. a toma el valor de "125" y c "-10". Si la variable es de tipo char, la constante que se le asigna debe estar entre tildes, como en el siguiente ejemplo:char nombre = 'juan perez';

Por ultimo, tambien podemo asignar a una variable el contenido de otra. En el siguiente ejemplo, el valor de i sera igual a 10.int i = 10; int j; j = 1;

Varibles Locales y GlobalesSi una variable se declara dento de una funcion, ser "visible" solo dentro de sta:funcion1 () { char letra; . . . . }

En el ejemplo anterior, la variable tipo char llamada letra solo podra utilizarse dentro de la funcion funcion1(). Si intentamos utilizarla fuera de ella, el compilador nos dar un error. Si declaramos una variable fuera de cualquier funcion, el alcance de esta sera global, lo que quiere decir que estar disponible en cualquier parte de nuestro programa. Vemos un ejemplo de este ltimo caso.char letra; main() { . . . .} funcion1() { . . .}

La variable tipo char llamada letra podr utilizarse dentro de main() o de funcion1().

Conversiones entre tiposCCS nos permite mezclar diferentes tipos de variables dentro de una misma expresin. Y existen un conjunto de reglas que nos permiten saber que de que tipo ser el resultado de la misma. Por ejemplo, el compilador convertir automaticamente a int cualquier expresin que contenga variables char, short o int. Esta conversin solo tiene efecto mientras se realizan los clculos. Las variables en s mismas no cambian su tipo. Las reglas de conversin de tipos hacen que el resultado de una operacin sea siempre el mismo que el de la variable ms larga que intervenga en ella. Sin embargo, podemos forzar a que el resultado sea de un tipo en particular, de la siguiente forma:(tipo) valor

donde tipo es el tipo al que queremos que pertenezca valor. El siguiente ejemplo nos aclarar todo esto:int a = 250, b = 10; long c; c = a * b;

Tal como explicamos, c no contendr el valor 2500 como podra paracer a simple vista, por que el tipo de c no se modica. CCS calcula a * b' y obtiene efectivamente el resultado 2500, pero c slo contendr los 8 bits menos significativos de ese resultado, es decir, el decimal 196. Si hubiesemos hecho: int a = 250, b = 10;long c; c = (long) (a * b);

el valor almacenado en c hubiese sido efectivamente 2500.

CCS - Directivas para el compiladorLlamadas en ingls "preprocessor directives", son comandos que interpreta el primer paso de la compilacon que lleva a cabo CCS. Las directivas ms comunes son #define e #include, pero deberas dar un vistazo a todas.

#ASM / #ENDASMEste par de instrucciones permite que utilicemos un bloque de instrucciones en assembler dentro de nuestro cdigo CCS. El siguiente es un ejemplo de uso tomado de la ayuda del CCS:int find_parity (int data) int count; #ASM movlw 0x8 movwf count movlw 0 loop: xorwf data,w rrf data,f decfsz count,f goto loop movlw 1 awdwf count,f movwf _return_ #ENDASM } {

La variable predefinida _RETURN_ puede utilzarse para transferir un valor desde el cdigo ASM a CCS. Si en lugar de #ASM utilizamos #ASM ASIS, CCS no intentar efectuar cambios de bancos de memria automaticos para las variables que no pueden ser accedidas desde el banco actual. El codigo assembler es utilizado "as-is" ("como es").

#BITPermite crear una nueva variable de un bit de tamao, que es colocada en la memoria del PIC en la posicin del byte x y el bit y. Esto es muy til para acceder de una manera sencilla a los registros. Por supuesto, estas variables puedem ser empleadas de la misma manera que cualquier otra variable tipo short. El formato de #BIT es el siguiente:#BIT nombre = x.y

donde nombre es un nombre de variable CCS vlido, x es una constante o una variable CCS vlida e y es una constante de 0 a 7. Estos son alguno ejemplos de uso:#BIT T0IF = 0xb.2 . . . T0IF = 0; // Limpia el flag de interrupcin del Timer 0

int resultado; #BIT resultado_primer_bit = resultado.0 . . . if (resultado_primer_bit)

#BYTEPermite crear una nueva variable de un Byte de tamao, que es colocada en la memoria del PIC en la posicin del byte x. Esta es una herramienta muy til para acceder de una manera sencilla a los registros. Por supuesto, estas variables puedem ser empleadas de la misma manera que cualquier otra variable tipo int. El formato de #BYTE es el siguiente:#BYTE nombre = x

donde nombre es un nombre de variable CCS vlido, y x es una constante o una variable CCS vlida. Estos son alguno ejemplos de uso:#BYTE STATUS = 3 #BYTE PORTB = 6

#DEFINELa instruccin #define tiene la siguiente forma:#DEFINE value

es la etiqueta que usaremos en nuestro programa. Y value es el valor que estamos asignando a esta etiqueta. Las instrucciones #DEFINE no generan codigo ASM, si no que el preprocesador realiza los reemplazos que ellas indican en el momento de la compilacin. El uso de #DEFINE permite construir programas ms ordenados y faciles de mantener. Veamos algunos ejemplos de #DEFINE#DEFINE TRUE 1

Cada vez que en nuestro programa aparezca la etiqueta TRUE, el precompilador la reemplazar por 1#DEFINE pi 3.14159265359

Cada vez que en nuestro programa aparezca la etiqueta pi, el precompilador la reemplazar por 3.14159265359#DEFINE MENOR_DE_EDAD (EDAD < 18) . . . . . if MENOR_DE_EDAD printf(JOVEN);

El ejemplo anterior permite una mayor claridad en el programa. Por supuesto, no hay que abusar de #DEFINE, por que podemos obtener el efecto contrario, haciendo nuestros programas bastante dificiles de comprender.

#DEFINE es una potente herramienta para la creacin de macroinstrucciones, ya que soporta el uso de variables. Veamos algunos ejemplos de esto:#DEFINE var(x,v) unsigned int x=v; var(a,1) var(b,2) var(c,3)

Cuando el preprocesador se encuentra con el cdigo anterior, hace lo mismo que si hubiesemos escrito lo siguiente:unsigned int a=1; unsigned int b=2; unsigned int c=3;

Como puedes ver, #DEFINE puede hacer mucho por tus programas.

#DEVICEEsta directiva informa al compilador que arquitectura de hardware utilizaremos, para que pueda generar cdigo apropiado para la cantidad de RAM, ROM y juego de instrucciones disponibles. Para los chips con ms de 256 bytes de RAM se puede seleccionar entre emplear punteros de 8 o 16 bits. Si deseamos emplear punteros de 16 bits basta con aadir *=16 a continuacin del nombre microcontrolador seleccionado. Veamos algunos ejemplos:#DEVICE PIC16C74 #DEVICE PIC16C67 *=16 //PIC 16C74, punteros de 8 bits. //PIC 16C67, punteros de 16 bits.

Hay ms opciones que podemos agregar en las lineas #DEVICE:ADC=x : Determina el nmero de [bit]]s que devuelve la funcin read_adc().#DEVICE PIC16F877 *=16 ADC=10 //PIC 1616F877, punteros de 16 bits y 10 bits en el ADC.

ICD=TRUE : Genera cdigo compatible con el ICD de [www.microchip.com Microchips]].#DEVICE PIC16F877 ICD=TRUE//PIC 1616F877, punteros de 8 bits y cdigo para ICD.

WRITE_EEPROM=ASYNC : HIGH_INTS=TRUE : Define la prioridad de las interrupciones en los PIC18.

#FUSEPermite modificar el valor de los fuses del microcontrolador que estamos empleando. Los valores posibles dependen de cada microcontrolador en particular, y los valores posibles se cargan al utilizar #ICNLUDE seguido del archivo correspondiente. La forma de #FUSE es la siguiente:#FUSE opciones

donde opciones es una lista de las opciones posibles separadas mediante comas. Antes de seguir, recuerda que puedes ver dentro del archivo con extensin .h correspondiente cuales son los valores posibles para ese microcontrolador. Estn al comienzo del archivo, en forma de comentarios. Algunos valores comunes sonTipo de oscilador: LP, XT, HS, RC Wach Dog Timer: WDT, NOWDT Proteccin de cdigo: PROTECT, NOPROTECT Power Up Timer: PUT, NOPUT Brown Out Reset: BROWNOUT, NOBROWNOUT

#INCLUDEPermite incluir en nuestro programa uno o mas archivos (conocidos como header file) que posean extensin .h. Estos archivos contienen informacin sobre funciones, sus argumentos, el nombre de los pines de un modelo determinado de PIC o cualquier otra cosa que usemos habitualmente en nuestros programas. Esto permite no tener que escribir un montn de cosas cada vez que comenzamos un programa nuevo: basta con incluir el .h correspondiente. La forma de utilizar esta instruccin es la siguiente:#INCLUDE

Esto har que el contenido de se compile junto con nuestro programa. Por ejemplo:#INCLUDE

hace que todas las especificaciones de nombres y registros del PIC16F877A se incluyan en nuestro programa. Esto permitir referirnos al pin 0 del PORTB del PIC mediante PIN_B0. Existe la posibilidad de utilizar #INCLUDE "archivo" en lugar de #INCLUDE . La diferencia es que si usamos "", el archivo se buscar primero en el directorio actual. Si empleamos , el archivo ser buscado primero en la ruta por defecto para los archivos .h.

#INT_xxx#INT_xxxindica que la funcin que le sigue (en el cdigo fuente CCS) es una funcin de interrupcin. Estas funciones no deben tener parmetros. Por supuesto, no todos los PICs soportan todas las directivas disponibles:1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. INT_AD Conversin A/D finalizada. I NT_ADOF Conversin A/D timeout. INT_BUSCOL Colisin en bus. INT_BUTTON Pushbutton. INT_CCP1 Unidad CCP1. INT_CCP2 Unidad CCP2. INT_COMP Comparador. INT_EEPROM Escritura finalizada. INT_EXT Interrupcin externa. INT_EXT1 Interrupcin externa #1. INT_EXT2 Interrupcin externa #2. INT_I2C Interrupcin por I2C. INT_LCD Actividad en el LCD. INT_LOWVOLT Bajo voltaje detectado. INT_PSP Ingreso de datos en el Parallel Slave Port. INT_RB Cambios en el port B (B4-B7). INT_RC Cambios en el port C (C4-C7). INT_RDA Datos disponibles en RS-232. INT_RTCC Desbordamiento del Timer 0 (RTCC). INT_SSP Actividad en SPI o I2C. INT_TBE Buffer de transmisin RS-232 vaco. INT_TIMER0 Desbordamiento del Timer 0 (RTCC). INT_TIMER1 Desbordamiento del Timer 1. INT_TIMER2 Desbordamiento del Timer 2. INT_TIMER3 Desbordamiento del Timer 3.

Ejemplo:#int_ad adc_handler() { adc_active=FALSE; } #int_rtcc noclear //"noclear" evita que se borre el flag correspondiente. isr() { ... }

CCS - OperadoresEn CCS los operadores cumplen un rol importante. Quizas C sea uno de los lenguajes que mas operadores tiene. Una expresin es una combinacion de operadores y operandos. En la mayora de los casos, los operadores de CCS siguen las mismas reglas que en lgebra, y se llaman de la misma manera.

Operadores aritmticosCCS posee cinco operadores aritmticos:+ (suma) - (substraccin) * (multiplicacin) / (divisin) % (mdulo)

Los primeros cuatro operadores mencionados se pueden utilizar con cualquier tipo de dato. Estos son algunos ejemplo de como usarlos:a a a a a a = = = = = = b + b b * b / -a; a + c; c; c; c; 1; //Cambia el signo de "a". //suma 1 al valor de "a".

El operador % (mdulo) solo puede emplearse con enteros. Devuelve el resto de una divisin de enteros. Veamos un par de ejemplos:int c = int c = a a a a = % = % 10, b; 20, b; b = 5, c; //"c" valdr cero. b = 3, c; //"c" valdr 2.

CCS tambin provee atajos para utilizar los operadores aritmticos. Hay algunas operaciones que se repiten a menudo cuando creamos nuestros programas, y estos atajos ayudan a que podamos escribir nuestro cdigo ms rapidamente. Los atajos provistos son los siguientes.a *= b es lo mismo que a = a * b a /= b es lo mismo que a = a / b a += b es lo mismo que a = a + b a -= b es lo mismo que a = a - b a %= b es lo mismo que a = a * b

Operadores RelacionalesLos operadores relacionales comparan dos valores, y devuelven un valor lgico basado en el resultado de la comparacin. Los operadores relacionales disponibles son los siguientes:

> mayor que >= mayor que o igual a < menor que > 2 //"corre" el contenido de a dos lugares a la derecha

Si a era igual a 120 ( 01111000 en binario) pasar a valer 30 (00011110 en binario).a = a b a &= b es lo mismo que a = a & b a |= b es lo mismo que a = a | b a ^= b es lo mismo que a = a ^ b

Otros operadoresQuedan por ver aun dos operadores ms:++ Operador incremento -- Operador decremento

Estos operadores permiten sumar (o restar) uno al valor de una variable. Lo que generalmente hariamos asi:a = a + 1

0 asi:a = a - 1

lo podemos hacer asi:a++

o asi:a--

el resultado sera el mismo, pero es mas corto de escribir, y mas fcil de utilizar en expresiones complejas.

Precedencia de los operadoresAl igual que ocurre en lgebra, en CCS los operadores se evalan en un orden determinado. La siguiente lista muestra este orden, ordenado de mayor a menor:() signo +, signo -, ++, --, !, () *, /, % +, = ==, != &&, || =, +=, -=, *=, /=, %=

CCS - PunterosUna de las caracteristicas mas interesantes de las diferentes versiones de C son los punteros. Por supuesto, CCS permite el manejo de punteros, con lo que nuestros progamas pueden aprovechar toda la potencia de esta herramienta. El presente artculo fue escrito por Pedro (PalitroqueZ), un amigo de uControl. Su direccin de correo elecrnico es [email protected].

Qu es un puntero?Un puntero es una variable cuya finalidad es almacenar nmeros ENTEROS POSITIVOS. Estos nmeros no son nmeros al azar, son direcciones de la memoria que posee el hardware del microcontrolador (memoria de programa o RAM).

Para que pueden servir los punteros?Esta es la pregunta que puede alborotar a mas de un programador de C. Sirve para muchsimas cosas:Acceso a la memoria RAM del PIC. Ahorrar memoria RAM. Modificar ms de una variable dentro de una funcin (y por consiguiente devolver mas de un valor) En arreglos y cadenas strings (arrays, matrices) juega un papel importantsimo. Permite crear tablas con montones de datos (en los PIC que soporten acceso a la memoria de programa). En un ordenador se ampla el abanico de opciones.

Ms abajo veremos detalladamente como hacer todo esto.

Como funcionan los punteros?Para entender el uso de estas variables especiales hay que comprender bien un concepto: Cuando se crea una variable en CCS (llamado registro en ensamblador), el compilador reserva un espacio de memoria cuyo tamao varia de acuerdo al tipo de dato. Como todo en el mundo electrnico/digital, est basado en 2 cosas:El registro: es la casilla donde se almacena el dato. La direccin del registro: es la posicin en la memoria donde est alojado el registro.

as pues tenemos 2 elementos diferentes pero que se relacionan. Conociendo la direccin del registro o variable y pudindolo manejar nos da un poderosa herramienta para agilizar/simplificar nuestros programas.

Como podemos acceder a la direccin de una variable?En CCS se hace a travs del operador &. Veamos un ejemplo: Ejemplo1:#include #use delay(clock=4000000) void main(){ int t,k; t=5; k= &t; delay_cycles(1); }

al simular en el MPLAB tenemos:

Cuando detenemos en delay_cycles(1) vemos que en k se guarda la direccin de la variable t, y que guarda t? guarda el nmero 5. todo se realiza usando memoria RAM el registro de propsito general GPR. Vamos a cambiar ligeramente el cdigo. Usemos 3 variables tipo entero (int):#include #use delay(clock=4000000) void main(){ int k,l,m; int t,u,v; t=0xfa; u=0xfb; v=0xfc; k= &t; l= &u; m= &v; delay_cycles(1); }

se repite lo mismo, el resultado de las direcciones en k, l y m son contiguas. Pero... por que? Para responder esta pregunta vamos a cambiar el cdigo otra vez, declarando los 3 tipos de registros conocidos, int, long y float:#include #use delay(clock=4000000) void main(){ int k,l,m,n; int t; long u; float v; int z; t=0xfa; z=0xff; u=0xfffa; v=3.45000000; k= &t; l= &u; m= &v; n=&z; delay_cycles(1); }

la simulacin:

Observa que las direcciones de t, u y v saltan. Por que? Dependiendo del tipo de dato se consume >= 1 byte de memoria. En el caso de t es un entero, y los enteros ocupan 1 byte (0..255). u es un dato "entero largo", ocupa dos bytes (0..65535) v es un dato "coma flotante", con parte fraccionaria en el

sistema decimal y toma 4 bytes de memoria (32 bits)en t tenemos una direccin que ocupa un byte [0xA] en u tenemos una direccin que ocupa 2 byte [0xB - 0xC] en v tenemos una direccin que ocupa 4 bytes [0xD - 0x10]

Probando punteros, primera parteSiempre que se declare una variable puntero, al momento de usarlo se debe especificar la direccin de apuntamiento de la variable normal, porque entonces no se puede guardar un dato sino sabemos donde lo vamos a guardar. (Es obvio pero es cierto) Esto quiere decir que se le debe pasar el nmero por valor de la direccin de la variable normal. Recordemos que:Pasar un dato por valor: se copia el dato de una variable a otra. Pasar un dato por referencia: se mueve/modifica el dato en la misma variable. Variable normal: la variable que normalmente usamos. Variable puntero: es la variable especial que estamos estudiando.

Veamos un ejemplo sencillo usando punteros:#include #use delay(clock=4000000) //******************************* void main(){ int k; // variable normal int *p; // la variable puntero k=0xfa; // k puntero q ocupa 2 bytes.

En ambos casos a pesar que cambiamos el tipo de declaracin de los punteros, se mantienen en 2 bytes, eso quiere decir que para el compilador el tamao de un puntero es de 2 bytes. No confundir con el tipo de datos a direccionar, pues eso es otra cosa. Vamos con otro ejemplo. Supongamos que i sea del tipo float (4 bytes) pero su apuntador lo declaramos como int (1 byte):#include #use delay(clock=4000000) //******************************* void main(){ float i; // variable normal int *p; // la variable puntero long j; long *q; int k; // i=2.51; // i