18.04.2013 Views

Curso Programación MCUs PIC en lenguaje C - Edudevices

Curso Programación MCUs PIC en lenguaje C - Edudevices

Curso Programación MCUs PIC en lenguaje C - Edudevices

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

Apr<strong>en</strong>di<strong>en</strong>do a programar<br />

Microcontroladores <strong>PIC</strong> <strong>en</strong><br />

L<strong>en</strong>guaje C con CCS<br />

Los compiladores de l<strong>en</strong>guaje C hoy son ampliam<strong>en</strong>te utilizados para la creación<br />

de programas con microcontroladores <strong>PIC</strong>. El compilador que mejor soluciona las<br />

necesidades del programador <strong>en</strong>mascarando el hardware y simplificando la<br />

implem<strong>en</strong>tación de una aplicación es el fabricado por la compañía CCS.<br />

Por Andrés Raúl Bruno Saravia<br />

Entrega Nº 2.<br />

En nuestra <strong>en</strong>trega anterior apr<strong>en</strong>dimos a crear un proyecto d<strong>en</strong>tro del MPLAB<br />

para escribir nuestro primer código <strong>en</strong> l<strong>en</strong>guaje C, hoy crearemos nuestro primer<br />

código y apr<strong>en</strong>deremos la función que cumpl<strong>en</strong> sus elem<strong>en</strong>tos principales .<br />

Creando Nuestro Primer Código<br />

Ya que hemos apr<strong>en</strong>dido a crear nuestro primer proyecto, escribiremos nuestro primer<br />

código, el cual adicionaremos a nuestro proyecto. Para ello nos moveremos con el<br />

Mouse hasta el ícono de New File y haremos clic sobre el mismo:


Y se desplegará una nueva v<strong>en</strong>tana <strong>en</strong> la cual escribiremos el código de nuestro<br />

programa.<br />

Por ser este nuestro primer programa, simplem<strong>en</strong>te haremos titilar un LED con un<br />

tiempo de flashing de 500ms. La idea es introducirnos de a poco, y muchas veces querer<br />

com<strong>en</strong>zar con un código demasiado elaborado es complejo.<br />

#include<br />

#use delay(osc=4000000)<br />

//configura fusibles de configuración<br />

#fuses XT //Oscilador a cristal standar<br />

#fuses NOWDT //sin WatchDog Timer<br />

#fuses NOPROTECT //sin proteccion de memoria de programa<br />

#fuses NOPUT //sin PowerUp Timer<br />

#fuses NOBROWNOUT //sin brownout<br />

#fuses NOLVP //sin programación <strong>en</strong> baja t<strong>en</strong>sión<br />

//rutina principal<br />

void main(void)<br />

{ //abrimos la función principal<br />

Setup_adc_ports(NO_ANALOGS);//configuramos los puertos digitales<br />

while(1) //creamos un bucle infinito<br />

{ //abrimos el bucle<br />

output_high(PIN_B0); //pr<strong>en</strong>demos RB0<br />

delay_ms(500); //esperamos 500 ms<br />

output_low(PIN_B0); //borramos RB0<br />

delay_ms(500); //esperamos 500 ms<br />

} //cerramos el bucle<br />

} //cerramos la función principal<br />

Este código lo hemos escrito <strong>en</strong> lo que se conoce <strong>en</strong> la jerga técnica como “codigo <strong>en</strong> C<br />

nativo” ya que usamos las funciones de control de <strong>en</strong>trada / salida de CCS<br />

El código inicia siempre <strong>en</strong> lo que se conoce como “cabecera”, es decir el principio.<br />

En esta cabecera <strong>en</strong>contraremos instrucciones dirigidas al compilador y no al<br />

microcontrolador, dichas instrucciones se d<strong>en</strong>ominan “directivas”. Las directivas se<br />

difer<strong>en</strong>cian de las instrucciones <strong>en</strong> que:<br />

• Siempre se <strong>en</strong>cu<strong>en</strong>tran <strong>en</strong> la cabecera del programa<br />

• Todas comi<strong>en</strong>zan con el símbolo del numeral #<br />

En nuestro caso estas son las directivas:<br />

#include<br />

#use delay(crystal=4000000)<br />

//configura fusibles de configuración<br />

#fuses XT //Oscilador a cristal standar<br />

#fuses NOWDT //sin WatchDog Timer<br />

#fuses NOPROTECT //sin proteccion de memoria de programa<br />

#fuses NOPUT //sin PowerUp Timer<br />

#fuses NOBROWNOUT //sin brownout<br />

#fuses NOLVP //sin programación <strong>en</strong> baja t<strong>en</strong>sión<br />

La directiva : #include nos permite decirle al compilador para que<br />

microcontrolador hemos escrito el código.


Seguidam<strong>en</strong>te con la directiva #use delay(crystal=4000000) le indicamos a que<br />

frecu<strong>en</strong>cia esta funcionando nuestro oscilador.<br />

El ord<strong>en</strong> de las directivas es crucial ya que siempre primero debemos indicarle al<br />

compilador cual es el <strong>PIC</strong> que estamos usando y luego cual es la frecu<strong>en</strong>cia del<br />

oscilador. Posteriorm<strong>en</strong>te podemos agregar el resto de directivas. Si esto no se<br />

respeta podemos t<strong>en</strong>er errores <strong>en</strong> el proceso de compilación sobre todo cuando<br />

usamos alguna función propia del compilador (llamadas funciones embebidas)<br />

para manejar algún periférico, y que la misma necesita saber la frecu<strong>en</strong>cia del<br />

oscilador.<br />

La directiva puede t<strong>en</strong>er distintas formas ya que amolda la configuración interna a la<br />

frecu<strong>en</strong>cia que le indicamos. Así esta directiva nos permite activar multiplicadores y<br />

divisores internos, o accionar los osciladores internos cuando el microcontrolador los<br />

trae; por ejemplo:<br />

#use delay(crystal=4000000, clock=16000000)<br />

Le indica al compilador que t<strong>en</strong>emos un cristal externo de 4Mhz, y que la frecu<strong>en</strong>cia<br />

que llega a la CPU es de 16Mhz, por lo tanto el compilador configurará correctam<strong>en</strong>te<br />

el PLL de la CPU para alcanzar los 32Mhz. Otro ejemplo pero usando el reloj interno es<br />

el sigui<strong>en</strong>te:<br />

#use delay(internal=8000000, clock=16000000)<br />

Sin embargo esta directiva debe usarse con precaución ya que el clock que definimos<br />

nunca debe sobrepasar la máxima velocidad de procesami<strong>en</strong>to del <strong>PIC</strong> que se esté<br />

usando. Este parámetro se d<strong>en</strong>omina MIPS (Millones de Instrucciones por Segundo) y<br />

se obti<strong>en</strong>e dividi<strong>en</strong>do la frecu<strong>en</strong>cia de <strong>en</strong>trada por cuatro.<br />

MIPS = fosc/4<br />

La directiva #fuse xx nos permite activar o desactivar las características del nucleo,<br />

como ser el circuito de Watch Dog Timer, que reseta al microcontrolador ante un<br />

cuelgue del mismo, el Brown Out Detect, que resetea el microcontrolador ante un fallo<br />

de la alim<strong>en</strong>tación, el tipo de oscilador, etc.<br />

Las etiquetas usadas para activar o desactivar la propiedad, están incluidas <strong>en</strong> el archivo<br />

de cabecera y deb<strong>en</strong> ser consultadas siempre, ya que las mismas suel<strong>en</strong> cambiar <strong>en</strong>tre<br />

versiones del compilador o tipos de microcontroladores.<br />

En líneas g<strong>en</strong>erales podemos decir que anteponi<strong>en</strong>do la palabra NO al fusible de<br />

configuración (así se llama al seteo de las propiedades), se le informa al compilador que<br />

el fusible <strong>en</strong> cuestión está desactivado, mi<strong>en</strong>tras que colocando solo el nombre<br />

activamos la propiedad.<br />

Por otra parte para activar o desactivar los distintos fusibles se puede realizar <strong>en</strong> varias<br />

líneas (como <strong>en</strong> el ejemplo) o se pued<strong>en</strong> activar y desactivar <strong>en</strong> una sola línea separando<br />

cada fusible con comas:<br />

#fuse NOWDT,HS,NOPUT,NOLVP,NOMCLR,NOPROTECT,NOBROWNOUT<br />

La cabecera además puede incorporar redefinición de nombres de pines, definición de<br />

variables y constantes. Y prototipo de funciones. Esto será visto <strong>en</strong> nuestras próximas<br />

lecciones.


Finalizada la cabecera, continúa el código, que es el que se traducirá <strong>en</strong> instrucciones al<br />

microcontrolador luego del proceso de compilación. En nuestro caso nuestro código es<br />

bastante s<strong>en</strong>cillo:<br />

void main(void)<br />

{ //abrimos la función principal<br />

setup_adc_ports(NO_ANALOGS);//configuramos los puertos digitales<br />

while(1) //creamos un bucle infinito<br />

{ //abrimos el bucle<br />

output_high(PIN_B0); //pr<strong>en</strong>demos RB0<br />

delay_ms(500); //esperamos 500 ms<br />

output_low(PIN_B0); //borramos RB0<br />

delay_ms(500); //esperamos 500 ms<br />

} //cerramos el bucle<br />

} //cerramos la función principal<br />

Todo programa siempre inicia <strong>en</strong> una rutina principal. En el l<strong>en</strong>guaje C las rutinas se<br />

d<strong>en</strong>ominan funciones. Las funciones son un conjunto de s<strong>en</strong>t<strong>en</strong>cias u ord<strong>en</strong>es que<br />

realizan una operación determinada, como lo hac<strong>en</strong> las rutinas, sin embargo las<br />

funciones ti<strong>en</strong>e una característica extra; a ellas se les puede pasar valores de variables<br />

para que las proces<strong>en</strong>, y son capaces de devolvernos los resultados de dichos procesos.<br />

Básicam<strong>en</strong>te actúan como las funciones matemáticas.<br />

Todo programa C siempre inicia <strong>en</strong> la función principal, la cual se d<strong>en</strong>omina main.<br />

Dicho nombre no puede ser distinto, todo programa debe t<strong>en</strong>er una función main, de lo<br />

contrario el compilador nos indicará un error.<br />

La función <strong>en</strong>cierra una serie de s<strong>en</strong>t<strong>en</strong>cias, las cuales forman el bloque de dicha<br />

función. Dicho bloque inicia con una llave { y finaliza con otra llave } .<br />

Entre estas dos llaves se <strong>en</strong>cu<strong>en</strong>tran las s<strong>en</strong>t<strong>en</strong>cias y las estructuras lógicas.<br />

La función main es una función especial ya que no puede recibir ningún valor, ni<br />

tampoco puede devolver uno. Por lo tanto observe que va acompañada por dos palabras<br />

void lo cual <strong>en</strong> el l<strong>en</strong>guaje C significa vacío; es decir que no devuelve ningún valor<br />

(primer void) ni puede recibir ningún valor, (segundo void, el cual esta <strong>en</strong>cerrado <strong>en</strong>tre<br />

paréntesis).<br />

void main(void)<br />

{<br />

}<br />

Nuestro código<br />

Nuestro código lo hemos escrito <strong>en</strong> formato “CCS nativo”. Esto significa que hemos<br />

usado todas las funciones embebidas (incluidas d<strong>en</strong>tro) del compilador para simplificar<br />

la escritura del código y que nos ahorran mucho tiempo.<br />

Debe observarse que cada línea la hemos decalado (separado del orig<strong>en</strong>) por medio del<br />

TABULADOR; esto es una bu<strong>en</strong>a práctica para poder advertir a simple vista cuales<br />

s<strong>en</strong>t<strong>en</strong>cias son las que estas anidadas d<strong>en</strong>tro de cada bloque del programa principal.<br />

La primera línea o s<strong>en</strong>t<strong>en</strong>cia le indica al compilador que debe desactivar todos los<br />

puertos analógicos del <strong>PIC</strong> y que debe configurarlos como puertos digitales:<br />

setup_adc_ports(NO_ANALOGS);


Usamos la función embebida setup_adc_ports, la cual esta embebida <strong>en</strong> el<br />

compilador, y que se <strong>en</strong>carga básicam<strong>en</strong>te de setear que puertos van a trabajar como<br />

analógicos y que puertos van a trabajar como digitales. La función configura los bits<br />

PCFG o ANSEL dep<strong>en</strong>di<strong>en</strong>do con que <strong>PIC</strong> estemos trabajando y lo realiza de forma<br />

automática.<br />

Observe que hemos escrito <strong>en</strong>tre paréntesis NO_ANALOGS lo cual le dice al<br />

compilador que no hay puertos analógicos. Esta etiqueta la obt<strong>en</strong>emos del archivo de<br />

cabecera del procesador (16f887.h).<br />

Es importante resaltar que las etiquetas siempre van <strong>en</strong> mayusculas, mi<strong>en</strong>tras que<br />

las instrucciones se escrib<strong>en</strong> <strong>en</strong> minúscula.<br />

Es muy importante que se respete el ord<strong>en</strong> mayúscula-minúscula pues el<br />

compilador es s<strong>en</strong>sible a ello. Si escribimos una instrucción con mayúscula, NO LA<br />

IDENTIFICARÁ.<br />

Debe observarse también que las s<strong>en</strong>t<strong>en</strong>cias siempre terminan con un punto y coma.<br />

La sigui<strong>en</strong>te s<strong>en</strong>t<strong>en</strong>cia de nuestro código es una instrucción estructural: while<br />

El while es una instrucción condicional la cual determina la ejecución de una o mas<br />

instrucciones <strong>en</strong> tanto y <strong>en</strong> cuanto se cumpla una condición, la cual se <strong>en</strong>cierra <strong>en</strong>tre<br />

paréntesis.<br />

En programación, si una condición se cumple, se dice que es verdadera, y esto se<br />

simboliza con el número 1; por el contrario, si la condición no se cumple, es falsa y<br />

se simboliza con el número 0.<br />

En un while, lo que este d<strong>en</strong>tro del bloque del mismo, se ejecutará, siempre que la<br />

condición de verdadera, caso contrario no se ejecutará ninguna s<strong>en</strong>t<strong>en</strong>cia que se<br />

<strong>en</strong>cu<strong>en</strong>tre d<strong>en</strong>tro del while.<br />

En nuestro caso hemos forzado la condición 1, con lo cual el while se ejecutará<br />

eternam<strong>en</strong>te. Es decir que las s<strong>en</strong>t<strong>en</strong>cias <strong>en</strong>cerradas d<strong>en</strong>tro del bloque while (limitado<br />

por las llaves{}), se ejecutaran por siempre.<br />

Observe que d<strong>en</strong>tro del while usamos también funciones embebidas:<br />

output_high(PIN_B0);<br />

output_low(PIN_B0);<br />

Estas son funciones de salida de datos, se <strong>en</strong>cargan de poner <strong>en</strong> uno o <strong>en</strong> cero un puerto,<br />

el cual la misma función se <strong>en</strong>carga de configurar como salida, no debe hacerlo el<br />

programador. Entre paréntesis le indicamos el PIN a <strong>en</strong>c<strong>en</strong>der o apagar. En nuestro caso<br />

es el RB0, al cual CCS lo d<strong>en</strong>omina d<strong>en</strong>tro del archivo de cabecera del<br />

microcontrolador como PIN_B0.<br />

Este formato si bi<strong>en</strong> parece <strong>en</strong> principio raro porque no se adapta al usado <strong>en</strong> el data<br />

sheet, es práctico para los programadores NO ELECTRÓNICOS, y es el que ha<br />

adoptado CCS.<br />

Por ejemplo el RA0 CCS lo llama PIN_A0, y al RC6, PIN_C6, y así sucesivam<strong>en</strong>te.<br />

Otra de las s<strong>en</strong>t<strong>en</strong>cias usadas es<br />

delay_ms(500);


La cual es una función de tiempo que nos permite crear un tiempo de espera <strong>en</strong><br />

milisegundos. También existe el delay_us que nos permite crear un delay de<br />

microsegundos.<br />

La exactitud de la función delay dep<strong>en</strong>de de que hayamos definido correctam<strong>en</strong>te la<br />

frecu<strong>en</strong>cia de clock.<br />

De esta forma hemos hecho falshear un LED <strong>en</strong> nuestro primer código.<br />

Bu<strong>en</strong>o, esto es todo por ahora , <strong>en</strong> las próximas <strong>en</strong>tregas iremos descubri<strong>en</strong>do paso a<br />

paso las utilidades de la programación <strong>en</strong> l<strong>en</strong>guaje C y del <strong>en</strong>torno CCS.<br />

Continuará ......

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!