Está en la página 1de 112

ndice curso de programacin PIC en

C con CCS

Introduccin

Mi primer programa en C con el compilador PCW C.

Simular nuestro primer programa con proteus.

Variables y Tipos de Datos.

Constantes.

Depuracin de programas con Proteus y CCS

Funciones de Entrada / Salida serie RS232

La funcin printf().

Funciones getc(), getch() y getchar().

Funciones gets() y puts().

Operadores.

Sentencias repetitivas.

Bucle while

Bucle for

Bucle do-while

Sentencias condicionales

Sentencia if

Sentencia if-else

Sentencia switch

Funciones (1)

Funciones (2)

Punteros (1)

Sentencias goto, break, continue.

Sentencia return.

Arrays.

Cadenas de caracteres.

Funciones para manipular cadenas de caracteres.

Funcin strcyp (cadena 1, cadena 2).

Funcin strcat (cadena 1, cadena 2).

Funcin strlen (cadena).

Funcin strcmp (cadena 1, cadena 2).

Arrays Multidimensionales.

Arrays de cadenas.

Punteros y Arrays.

Array de punteros.

Relacin entre Arrays y Punteros.

Aritmtica de Punteros y errores mas comunes.

Estructuras.

Estructuras y Punteros.

Paso de Estructuras como argumentos a funciones.

Uniones.

Enumeraciones.

Uso de la palabra clave typedef.

Campos de bits

Este ndice puede sufrir variaciones a lo largo del curso.

Pgina 1 de 18
Curso para aprender a programar en lenguaje C utilizando un
compilador para PIC, en concreto el PCW compiler de la casa CCS. Cursos
sobre C en Internet a miles, pero todos los que yo he visto estn realizados
sobre compiladores de propsito general, como Vicual C++ de Microsoft
Builder C++ de Borlan, sin duda son excelentes compiladores que nos permiten realizar
aplicaciones para
ordenadores de escritorio tanto en C como en C++ (la versin orientada a objetos de C),
pero no sirven para programar PIC, es decir con el ejecutable que generan al compilar no
se puede programar un Microcontrolador.
Lo habitual hasta ahora es que los usuarios que se inician en este apasionante mundo de la
programacin de Microcontroladores, sea de la marca que sea, primero lo hacan utilizando
el lenguaje ensamblador, especifico no solo ya para cada marca de microcontrolador sino
para cada modelo, ya que hay que conocer perfectamente los recursos de cada
Microcontrolador (Nmero de puertos de Entrada/Salida Relojes internos, etc. ). Al principio
de los tiempos de estos dispositivos esto era obligatorio ya que los recursos de memoria y
velocidad de procesamiento no eran muy grandes y haba que optimizar el cdigo al
mximo, esto implicaba que haba que utilizar a la fuerza un lenguaje de programacin de
bajo nivel que bien utilizado explotara los recursos de estos dispositivos sin desperdiciar
memoria y velocidad de procesamiento, pero al igual que ha ocurrido con los ordenadores
personales las prestaciones de estos dispositivos ha ido creciendo exponencialmente con el
tiempo, siendo ya perfectamente factible el utilizar un lenguaje de alto nivel para
programar estos dispositivos y aprovecharnos de las ventajas de portabilidad que ofrecen
este tipo de lenguajes, de esta manera por ejemplo podemos hacer un programa para un
PIC en concreto y utilizarlo en otro de mayores prestaciones sin modificar apenas nada del
cdigo fuente.
Quien puede sacar provecho de este curso?. Este curso es para ti si:

Has programado PIC en Ensamblador y quieres hacerlo en un lenguaje de alto


nivel como el C.

No has programado nunca Microcontroladores pero conoces el lenguaje de


programacin C de

haberlo utilizado para otros propsitos.

No has programado nunca un PIC en Ensamblador, ni conoces ningn lenguaje de


alto nivel como el C. Es decir, no tienes ni idea de Microcontroladores ni de
programacin (Esto es posible porque el curso va ha empezar desde cero es decir

con el clsico Hola Mundo con el que empiezan todos los libros de iniciacin a la
programacin.
Bueno alguno pensar que para aprender a programar en C vale cualquier compilador de
uso general
y lo que realmente interesa es saber las instrucciones de C que tengo que utilizar para
configurar por ejemplo un puerto como entrada o salida, o que cdigo tengo que utilizar
para utilizar los convertidores A/D que incorporan ya casi todos los PIC, indudablemente
ese es el propsito final de este curso y para ello paralelamente a l va haber otro
donde se van a ver aplicaciones prcticas. Pero hay que tener en cuenta que los
compiladores para Microcontroladores son especficos para estos dispositivos embebidos y
no cumplen con el Estndar ANSI C al 100 %, por lo que cuando ests programando lo ms
seguro es que te vayas dando cuenta que una funcin que en el C estndar funciona
perfectamente aqu te da un error al compilar. Adems te irs quedando con mucho cdigo
que lo has probado y sabes que te funciona perfectamente, cuando tengas que hacer una
aplicacin prctica no tendrs la duda si puedes usar una determinada estructura en tu
programa si es posible utilizar punteros o no y como hacerlo, porque ya lo sabrs a la vez
que has ido aprendiendo el lenguaje de programacin y no solo eso, te irs familiarizando
con las instrucciones especficas del compilador: de que herramientas dispone, sus
funciones precompiladas, su sistema de depuracin de errores, etc.
Que herramientas voy a necesitar para realizar el curso?.
El compilador CCS seguro, vaya es de pago ya empezamos con problemas te puedes bajar
una versin de Evaluacin por 30 das desde aqu:

http://www.ccsinfo.com/ccsfreedemo.php
Despus de rellenar el formulario te descargas el programa de instalacin y lo instalas en tu
ordenador como un programa ms de Windows, aunque tienes que tener en cuenta que
solo podrs programar un pequeo conjunto de PIC de cada familia, otro inconveniente es
que tienes que estar conectado a Internet para que te funcione si no te aparecer esta
ventanita poco amigable:

Programacin PIC en C - Mi primer programa en C con el compilador PCW C


Escrito por biblioman

Pgina 2 de 18

Vamos a crear nuestro primer ejemplo paso a paso: abrimos el IDE de


nuestro compilador y seleccionamos New->Source File segn se
muestra en la figura de abajo:

Nos saldr un cuadro de dialogo de guardar de Windows, donde le


pondremos un nombre a nuestro archivo y lo guardaremos.
Despus escribimos el cdigo fuente que se muestra en la figura de
abajo y guardamos el documento:

Comentario del programa:


En primer lugar nos encontramos con tres directivas del prepocesador,
las identificaremos porque empiezan por el smbolo (#):

La primera de ellas es una directiva include su funcin es introducir un


documento dentro de otro. En la posicin del programa donde se encuentra esta
directiva, se incluir el archivo indicado. Se suele usar para incluir los archivos de
cabecera (generalmente con extensin.h). En este caso concreto se incluye el
archivo <16F877A.h>,en este archivo se incluyen las definiciones de los registros
del PIC.

#use delay (clock=4000000); directiva para el uso de retardos, entre parntesis

tenemos que poner la frecuencia de reloj que vamos a utilizar.

#use rs232 (baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8) esta


directiva es para la comunicacin del PIC con otro dispositivo va RS232, por
ejemplo un ordenador, en ella se encuentran definidas los prototipos de las
funciones de entrada y salida como printf().

En segundo y ltimo lugar se encuentra la funcin main. Este es el ncleo del programa, el
que va ha incluir todos los pasos a seguir durante su ejecucin. En nuestro primer ejemplo
solo contiene una sentencia que hace una llamada a la funcin printf(), esta funcin se
encarga de mostrar un mensaje por el dispositivo de salida RS-232.
El mensaje que muestra la funcin printf es el que recibe como parmetro (el texto entre
parntesis). Dicho mensaje es delimitado por las comillas dobles, que indican el principio y
el fin de una cadena de texto.
Bien una vez creado el archivo .c de nuestro programa tenemos que crear un proyecto y
asociarle el archivo que acabamos de crear, tenemos dos opciones crearlo manualmente
utilizar el wizard que tiene el IDE, en este primer ejemplo utilizaremos la opcin manual.
Despus seleccionamos New ->Project Manual

Y aadimos el Ejemplo1.c que hemos creado a nuestro proyecto:

Seleccionamos la pestaa Compile y pulsamos sobre Build All para construir todo.

Vemos que el archivo de salida no nos ha producido ningn error. Por tanto el proyecto se
ha generado correctamente.
Y si vamos a la carpeta donde habamos guardado nuestro primer ejemplo, tenemos todos
los archivos que nos ha creado el IDE:

De todos estos archivos los que mas nos interesa son los que estn marcados en la figura
de arriba. El archivo Ejemplo1.hex es el que tenemos que utilizar para programar el PIC y
el que termina con extensin .cof lo utilizaremos para cargarlo en el simulador Proteus y
poder simular el programa paso a paso, entre otras posibilidades muy tiles a la hora de
depurar nuestro cdigo.
Pero eso lo veremos el prximo da.
Un Saludo

Programacin PIC en C - Simulacin con Proteus


Escrito por biblioman

Pgina 3 de 18

Bien ya tenemos nuestro primer ejemplo generado y listo para cargarlo en nuestro
simulador Proteus. Vamos a ello:
Arrancamos nuestro simulador Proteus y pasamos a colocar nuestros dispositivos en el rea
de trabajo. Empezaremos colocando el PIC, para ello hacemos clic en el botn que pone
Pick Devices segn se muestra en la figura de abajo:

En la ventana que nos aparece en el campo Keywords escribimos el nombre de nuestro


PIC.

Una vez seleccionado hacemos doble clic sobre el para incorporarlo a nuestro proyecto.

Bien, vamos por el segundo y ltimo elemento que necesitamos para simular nuestro
programa. Hay que tener en cuenta que el simulador es capaz de hacer funcionar nuestro
circuito sin algunos elementos que seran necesarios si decidimos montar nuestro circuito
en una placa real (por ejemplo la alimentacin del PIC y el cristal de cuarzo).
El segundo elemento que necesitamos es un Terminal Virtual que har las veces de
monitor, para poder ver las salidas en formato texto de nuestro PIC como si se
tratar del smbolo del sistema en un ordenador de escritorio con el Windows instalado. En

la figura de abajo se muestra donde podemos incorporar dicho instrumento.

Con esto ya tendremos los dos elementos necesarios para simular nuestros programas,
recordemos que en este curso se va a ver las generalidades del lenguaje C aplicadas a este
compilador, en el caso de las aplicaciones prcticas que empezaremos pronto en otro
articulo tendremos que hacer un circuito independiente para cada ejemplo ya que cada uno
de ellos incorporar elementos diferentes como: diodos Led, motores, teclados, displays,
etc.
La interconexin de los dos dispositivos es muy sencilla segn se muestra en la figura de
abajo, solo hay que hacer clic con el puntero del ratn en forma de lpiz entre los
terminales que queremos conexionar:

El pin del PIC que habamos elegido como transmisin de datos en nuestro programa ir
conectado al terminal RXD de recepcin de datos en el Terminal Virtual y viceversa.
Bien ahora tenemos que cargar nuestro programa en el PIC para poder simularlo, para ello
hacemos doble clic sobre el PIC y nos aparecer la ventana de la figura de abajo:

Los valores que en un principio tenemos que introducir para que nuestra simulacin
funcione son los que estn sealados en la figura de arriba. En Program File pincharemos
sobre la carpeta y seleccionaremos el archivo con extensin .cof que se haba creado al
compilar nuestro programa, si en vez de este seleccionamos el que tiene extensin .Hex
funcionar igual pero no podremos realizar la simulacin paso a paso. El otro valor a tener
en cuenta es que la frecuencia del reloj del PIC debe coincidir con el valor que le habamos
puesto en el programa en nuestro caso 4 MHz.
Una vez hecho esto guardamos nuestro proyecto.

Programacin PIC en C - Simulacin con Proteus ( continuacin )


Escrito por biblioman

Pgina 4 de 18
Si ahora hacemos clic sobre el botn Play se nos abrir una terminal al estilo MSDos
donde nos mostrar la salida de nuestro programa:

Ahora si le damos al botn de simulacin paso a paso podremos simular nuestro


ejemplo

paso a paso.

Si en vez de ello nos sale una ventana mostrando dos advertencias de que no se puede
encontrar el cdigo fuente de nuestro ejemplo, como se muestra en la figura de abajo.

Seguiremos los siguientes pasos:


En el men seleccionamos Source y hacemos clic sobre Define Code Generation Tools

Nos aparecer la ventana de abajo en la que pulsaremos sobre el botn New

Buscamos en nuestro directorio donde se ha instalado el compilador y seleccionamos


CCsc.exe tal y como se muestra en la figura de abajo:

Despus en el combo Tool seleccionamos el compilador y configuramos el resto de


parmetros tal y como se muestra en la figura de abajo y pulsamos OK:

Ahora nos queda aadir nuestro cdigo fuente para ello vamos al men seleccionamos
Source Add/Remove Source files

Y aadimos nuestro cdigo fuente ejemplo1.c


Si ahora volvemos a simular nuestro ejemplo paso a paso nos aparecer la ventana
siguiente:

Donde podemos ver la ejecucin del programa lnea a lnea o poner puntos de interrupcin
en las partes del programa que nosotros queramos. Saber que existe un plugin que permite
integrar un visor de proteus en el famoso simulador MPLAB. Incluir un video en la seccin
de descargas donde explica como hacerlo, aunque nosotros seguiremos utilizando este
mtodo.

Bien a partir de ahora ya podemos empezar a estudiar el lenguaje de programacin C en


este compilador como si fuera un compilador cualquiera como Microsoft Visual C++ pero
comprobando las particularidades de este compilador. Todos los ejemplos van a seguir el
mismo procedimiento por lo que solo pondr el cdigo y la explicacin del mismo.

Programacin PIC en C - Variables y Tipos de Datos


Escrito por biblioman

Pgina 5 de 18
Qu son las variables? pues sencillamente el poder identificar con un nombre una o
varias posiciones de memoria de la RAM de nuestro PIC y de esta manera el poder
almacenar all los datos que va a utilizar nuestro programa.
En C para poder utilizar una variable primeramente hay que declararla siguiendo la
siguiente sintaxis:
tipo nombre_variable [=valor];

Lo que va entre corchetes es porque es opcional es decir, las variables se pueden


inicializar no al declararlas.
Ejemplo de variable declarada:

int i;
Ejemplo de variable declarada e inicializada:

int i=5;
En una misma lnea se puede declarar ms de una variable siguiendo el siguiente formato:

tipo nombre_variable1,nombre_variable2,....;

Hay que tener en cuenta que la lnea tiene que acabar en punto y coma.
El tipo de datos es obligatorio ponerlo y le dice al compilador cuantas celdillas de memoria
tiene que reservar para almacenar el valor de la variable. Los tipos de datos pueden variar

de un compilador a otro, vamos a ver los tipos de datos que podemos usar con nuestro
compilador CCS.
Los tipos de datos bsicos que utiliza nuestro compilador son los siguientes:

Sin embargo el compilador CCS tambin admite los siguientes tipos de datos definidos en
el estndar C y que son los que normalmente se utilizan a la hora de programar:

Todos los tipos excepto float son por defecto sin signo, aunque pueden llevar el
especificador unsigned signed y su rango de valores ser el que corresponda a su tipo
bsico.

Estos son los tipos bsicos, tambin estn los tipos de datos compuestos como
Enumeraciones, Estructuras y Uniones que estn formados por una combinacin de los
bsicos y que los veremos ms adelante.

El nombre de la variable no puede ser una palabra clave (reservada por el compilador para
realizar unas funciones determinadas y los caracteres que podemos utilizar son las letras:
a-z y A-Z ( ojo! la o no est permitida), los nmeros: 0-9 y el smbolo de subrayado
_. Adems hay que tener en cuenta que el primer carcter no puede ser un nmero.

Dnde se declaran las variables?

Las variables segn el lugar en que las declaremos pueden ser de dos tipos: globales o
locales.

La variables globales se declaran fuera de las funciones y pueden ser utilizadas


en cualquier parte del programa y se destruyen al finalizar ste.

Las variables locales se declaran en la funcin en que van a ser utilizadas. Slo
existen dentro de la funcin en que se declara y se destruye al finalizar dicha
funcin. Si una funcin va a usar argumentos (DATOS), entonces debe declarar
las variables que van a aceptar los valores de esos argumentos. Estas variables
son los parmetros formales de la funcin. Se comportan como cualquier otra
variable local de la funcin, crendose al entrar en la funcin y destruyndose al
salir. Cuando veamos el tema de las funciones veremos ejemplos de estas
variables.

Bueno ya est bien de teora vamos hacer un ejemplo donde vamos a declarar y a usar
varios tipos de variables:

Este programa generar la siguiente salida:

Comentario del programa:

El compilador utiliza 8 bits para representar los nmeros enteros sin signo con lo
cual podemos representar desde el 0 hasta el 255 que corresponde en binario al
nmero: 11111111. Por lo que al asignarle a la variable el valor 256 el
compilador no generar un error pero el dato guardado ser errneo, nos
mostrar 0 que es el siguiente valor a 255 en binario.

Para los nmeros enteros con signo tambin se utilizan 8 bits pero el ltimo bit
se reserva para el signo, con lo que se podrn representar los nmeros desde: 127 al 127.

El tipo short se utilizar para las variables de un bit y tendrn como valor 0 1.

Para los nmeros tipo long int se reservan 16 bits sin signo con lo que su rango
va de 0 a 65535

Para el tipo signed long se reservan tambin 16 bits pero se utiliza uno para el
signo, por lo que se tiene un rango que va desde -32767 a 32767.

El tipo float define un nmero de 32 bits en punto flotante. y con el podremos


representar los nmeros reales.

El tipo char se utiliza para almacenar los caracteres, utiliza 8 bits sin signo
suficientes para representar los 256 caracteres del cdigo ASCII.

Los smbolos %D, %lu, %ld, %c le indica a la funcin printf en que formato
tiene que representar el nmero. En la ayuda del compilador vienen los

diferentes especificadores que hay para los diferentes tipos de datos. A lo largo
de los siguientes ejemplos se irn mostrando algunos ms.

CONSIDERACIONES: Hay que intentar siempre utilizar el tipo de dato que menos
memoria ocupe dentro de los valores que pueda utilizar la variable. Si abusamos de los
tipos grandes para almacenar valores pequeos nos quedaremos sin memoria y en los
programas grandes es un dato que tenemos que tener en cuenta.

Nota: en los ejemplos que tengan poco cdigo fuente como este y para que el formato de
texto salga con los mismos colores que utiliza el compilador utilizar imgenes para
mostrar el cdigo y en la seccin de descargas ir incluyendo los ejemplos del curso para
que todo el que no quiera teclearlos a mano se los pueda descargar. Otra cosa no incluir
el circuito en Proteus ya que es el mismo para todos los ejemplos a excepcin de que en
algunos ejemplos pueda ir cambiando el tipo de PIC.

Programacin PIC en C - Constantes


Escrito por biblioman

Pgina 6 de 18
Antes de empezar con el tema de las constantes voy a comentar valga la redundancia la
forma de poner comentarios a nuestros programas.

Hay dos formas de poner comentarios en C:

Poniendo doble barra (la que hay encima del 7), esta forma es prctica para
comentar una lnea.

Ejemplo:
//Este texto es un comentario.
//y este otro tambin.

la otra forma es meter el texto a comentar dentro de estos smbolos /* mi

comentario*/. La ventaja de este sistema es que podemos comentar bloques de


textos enteros.
Ejemplo:

/*Mi comentario empieza aqu.....


mas comentarios ..
y termina aqu */

El comentar nuestro cdigo es una buena costumbre que no debemos pasar por alto, ya
que si pasado un tiempo queremos volver a un programa y modificar alguna parte de
l ayuda mucho el que su cdigo est comentado. Otra forma en la que se utilizan los
comentarios es a la hora de depurar cdigo, en vez de estar borrando y escribiendo trozos
de cdigo que no funcionan correctamente los comentamos, de est forma el compilador
no los tratar como cdigo fuente y podremos realizar ajustes y pruebas de una manera
ms fcil. Muchas veces tambin vemos que revisando cdigo que han hecho otras
personas hay partes del cdigo que estn comentadas esto es para hacerlo mas funcional,
es decir, por poner un ejemplo, si utilizas el PIC 16F877 des comenta esta parte y si
utilizas otro PIC lo dejas comentado, de esta manera comentando o descomentando unas
cuantas lneas podemos utilizar el programa en varias situaciones.

Bueno, todo esto para el que tenga una idea de programacin seguro que ya lo sabe, pero
como dije al principio voy ha intentar que este curso le sirva tambin al que no tenga ni
idea de programacin aunque, en este caso, hay que decir tambin si se es honesto, que
aprender un lenguaje de programacin al igual que aprender un idioma nuevo supone un
esfuerzo considerable y no vasta con leerse un libro de C y decir ya soy un programador
de C!, bajo mi modesta opinin lo que hay que hacer es practicar mucho, es decir teclear
mucho cdigo compilarlo y comprobar que funciona como nosotros queremos que lo haga,
al principio cometeremos muchos errores pero el descubrir cual es la causa del error nos
servir para aprender mas todava y sobre todo no desanimarse a la primera de cambio
cuando algo no funcione. La constancia y la perseverancia son las claves del xito para
conseguir cualquier objetivo, no solo el aprender a programar PIC en C. Y ya est bien
porque menudo rollo estoy soltando, as que vamos a empezar con lo que era el tema de
este capitulo: las constantes.

Las constantes se refieren a valores fijos que no se pueden alterar por medio del
programa.

Pueden definirse constantes de cualquiera de los tipos de datos simples que hemos visto.
Se declaran colocando el modificador const delante del tipo de datos.

Ejem:

const int MINIMO=10,INTERVALO=15;

Esto definir dos constantes MINIMO con el valor de 10 e INTERVALO con el valor de 15.

Otra forma de definir constantes es usando la directiva de compilacin #define.

Ejem.

#define MAXIMO 30

Esta orden se ejecuta de la siguiente forma: en la fase de compilacin al ejecutar #define


el compilador sustituye cada operacin de la primera cadena de caracteres por la segunda,
MAXIMO por el valor 30 adems, no se permite asignar ningn valor a esa constante.
Es decir si pusiramos:

#define MAXIMO = 30

Al compilar tendramos un error.

Nota: La declaracin #define no acaba en ";"

Tambin podemos tener en nuestro programa Constantes de cadena: una cadena de texto
es una secuencia de caracteres encerrados entre dobles comillas. Se usa para funciones de
entrada y salida estndar, como funcin de entrada y salida de texto estamos utilizando la
funcin printf que esta definida dentro de-> #use rs232, pero ya veremos que el
compilador CCS proporciona un nmero considerable de funciones listas para usarse y que

nos sirven para comunicarnos con el dispositivo de entrada y salida RS-232.

Hemos dicho que podemos definir constantes prcticamente de cualquier tipo de dato,
pero CCS nos permite tambin representar esas constantes en diferentes sistemas de
numeracin como hexadecimal, binario, octal, decimal y adems definir tambin
constantes de caracteres especiales que el compilador utilizar para realizar acciones
concretas. De los sistemas de numeracin permitidos los que ms se usan son los
siguientes:

Decimal
Ejemplo: 222
Hexadecimal empiezan por 0x
Ejemplo: 0x2A
Binario empiezan por 0b
Ejemplo:
0b00001011

Este ltimo formato es muy til, por ejemplo el PIC dispone de unos registros que sirven
para configurar los puertos del PIC como entradas de datos o salida de datos, por defecto
vienen configurados como entradas y si quiero utilizar algn pin como salida porque quiero
utilizarlo para encender un LED o lo que sea, tengo que poner a cero dicho registro. En el
formato binario se ve fcilmente que valores se le va asignar al registro, teniendo en
cuenta que los registros empiezan por 0.

Como siempre vamos hacer un ejemplo para ver si nuestro compilador se traga todo lo
que he dicho:

Bien si todo va bien obtendremos la siguiente salida:

Comentario: Como dije en la introduccin de este curso la finalidad es aprender a


programar PIC en lenguaje C eso conlleva saber el lenguaje C, que seguiremos viendo en
esta parte del curso, pero tambin el saber utilizar los recursos y funcionalidades que nos
ofrecen los PIC como por ejemplo saber programar sus contadores, como enviar datos a
un LCD, el utilizar los conversores A/D, etc. Para ello voy a iniciar prximamente un
segundo artculo donde empezaremos a estudiar ejemplos prcticos de los PIC.

Programacin PIC en C - Depuracin de programas con Proteus y CCS


Escrito por biblioman

Pgina 7 de 18

Volver al ndice

Una vez iniciada la parte de prcticas vamos a seguir con la teora, pero antes he incluido
este pequeo artculo sacado de un video tutorial que encontr en Internet y que he
adaptado al compilador CCS, el video tutorial lo colocare en la seccin descargas para el
que lo quiera ver. Pues, una vez dicho esto vamos a empezar. En la ltima prctica que
hemos visto (el uso del TMR0 como contador) vimos que el entorno de Proteus nos
proporciona una ventana de visualizacin del estado de los registro SFR de nuestro PIC ,
muy til cuando estamos depurando nuestro programa, pero Proteus nos proporciona ms
ventanas para ver el estado de los registros de nuestro PIC que podemos acceder a ellas
por medio del men Debug --> PIC-CPU cuando estamos ejecutando nuestro programa en
el modo de simulacin paso a paso cuando hemos pulsado el botn de pausa, una vista
condensada de todas esas ventanas la tenemos en la figura de abajo:

Como vemos aparte de poder ver el estado de los registros SFR del PIC podemos ver el
estado de la memoria EPROM del PIC, El contenido de la memoria de programa (donde se
encuentra grabado de forma permanente nuestro programa ), el estado de PILA (til
cuando se trabaja con interrupciones y funciones), otra ventana nos muestra el estado de
la memoria RAM reservada a los datos registros de propsito general (GPR) en formato
hexadecimal y otra donde podemos ver el estado de las variables que tenemos activas en
ese momento, recordar que si utilizamos variables locales por ejemplo dentro de una
funcin, ests se destruirn al salir de la funcin. Pero todo esto como he dicho lo tenemos
cuando estamos ejecutando nuestro programa en el modo paso a paso tenemos nuestro
programa en pausa.

Si estamos en modo Run e intentamos acceder a estas ventanas vemos que estn
deshabilitadas:

Qu otro sistema tenemos para depurar nuestros programas? Pues bien una manera que
siempre podemos utilizar es utilizar la funcin printf como herramienta de depuracin, es
decir, ponemos la funcin printf en determinadas partes del programa donde queramos
saber el estado de una o varias variables y por medio de la terminal podemos saber el valor
que van tomando, una vez comprobado que nuestro programa funciona como nosotros
queremos borramos las funciones printf que hayamos introducido con propsitos de
depuracin.

Pero Proteus nos proporciona otro mtodo para ver el estado de las variables cuando
estamos ejecutando nuestro programa ya sea en modo Run en modo paso, es la ventana
Watch Window y podemos acceder a ella por medio del men Debug --> Watch Windows.
Vamos a ver cmo podemos utilizarla. Para ello compilaremos el siguiente ejemplo:

#include <16F84A.h>
#use delay(clock=4000000)
#fuses XT,PUT,NOWDT
#use RS232(BAUD=9600, BITS=8, PARITY=N, XMIT=PIN_B4, RCV=PIN_B5)
int x;
int y;
void main() {
x,y=0;

while(TRUE)
{
if (x<=9)
{
x++;
delay_ms(500);
//printf("x = %d.\r",x);
}
else
{
if (y<=9)
{
y++;
delay_ms(500);
//printf("y = %d.\r",y);
}
}
}
}

Es un programa que lo nico que hace es incrementar la variable X de 0 a 10 y despus


hace lo mismo con la variable Y, pero es suficiente para ver cmo utilizar la ventana Watch
Windows para ver el valor que van tomando las variables X e Y.

Primeramente compilamos el ejemplo y despus dentro del IDE del compilador hacemos
clic en el icono Symbol Map segn se muestra en la figura de abajo:

Esto har que nos aparezca el archivo Symbol Map en modo lectura, en este archivo
podemos ver en qu posicin de memoria se guardarn las diferentes variables que
tengamos declaradas en nuestro programa, este archivo se actualizar en cada compilacin

que hagamos.

Como vemos en la figura de arriba las variable X e Y tienen asignadas las direcciones de
memoria 0x011 y 0x012 en la memoria RAM de propsito general (GPR), que como ya
sabemos es la que el programador dispone para almacenar los valores de sus variables.

Bien, una vez anotadas estas direcciones volvemos al entorno de Proteus y abrimos la
ventana Watch Windows, dentro de ella hacemos clic con el botn derecho del ratn y
seleccionamos Add Items (By Address) , segn se muestra en la figura de abajo:

Nos aparecer una nueva ventana donde iremos aadiendo las variables con su direccin
correspondiente:

Una vez aadidas las variables podemos ver el valor que van tomando mientras ejecutamos
nuestro programa en la ventana Watch Windows, segn se muestra en la figura de abajo:

Pero tenemos an mas opcciones, por ejemplo podemos establecer condiciones para ello
hacemos clic en la variable con el botn derecho y seleccionamos Watchpoint Condition
Nos aparecer la ventana que se muestra abajo:

Por ejemplo yo la he configurado para que cuando la variable X sea igual a cinco se pare la

simulacin, pero admite ms condiciones solo hay que ponerse y experimentar con las
diferentes opciones que tenemos, tambin decir que podemos hacer que la ventana Watch
Windows nos muestre los registros SFR que nos interesan junto con las variables que
nosotros hemos declarado, en fin muchas posibilidades de depuracin. El conocer estas
herramientas nos puede facilitar mucho el aprendizaje porque vemos la secuencia real que
sigue nuestro programa, que algunas veces puede que no coincida con nuestra lgica de
funcionamiento del programa.

Nota: el video tutorial lo podeis descargar desde:

Descargas --> Apuntes --> Electrnica --> Proteus --> Uso de Watch Windows en
Proteus.zip

Como siempre espero vuestros comentarios y os agradecera que las preguntas que tengis
sobre estos temas, pues las hagis directamente en el foro que para eso est.

Un saludo a todos y hasta el prximo articulo.

Programacin PIC en C - Funciones de Entrada/Salida serie RS232. Funcin printf()


Escrito por biblioman

Sentencias repetitivas. Bucle while()


Escrito por biblioman

Pgina 12 de 18
SENTENCIAS REPETITIVAS:

Son aquellas que ejecutan un bloque de sentencias mientras se cumpla una expresin
lgica. Este bloque de sentencias que se ejecuta repetidas veces, se denomina bucle, y
cada ejecucin se denomina iteracin.

De las diferentes sentencias repetitivas que hay vamos a empezar con while.

La sentencia while permite la ejecucin de un bloque de sentencias si se evala como


verdadera una expresin lgica. La expresin lgica aparece al principio del bloque de
sentencias.
En la figura de abajo se muestra el Pseudocdigo, el diagrama de flujo y la sintaxis de la
sentencia while.

El Pseudocdigo es una forma informal de representar la secuencia del programa, sin tener
en cuenta la sintaxis particular del lenguaje en que vayamos a programar y el diagrama de
flujo es una representacin grfica del Pseudocdigo.

Cuando vayamos a crear un programa el dibujar previamente un diagrama de flujo el


Pseudocdigo de la secuencia de nuestro programa puede ayudarnos en la tarea de
programacin, pero en ningn caso es un paso obligatorio.

El bloque delimitado por las llaves puede reducirse a una sentencia, y en este caso se
suprimen las llaves.

La expresin lgica debe estar delimitada por parntesis.

Cuando el programa llega a una sentencia while, sigue los siguientes pasos.

Evala la expresin.

Si es falsa, continua la ejecucin tras el bloque de sentencias.

Si es verdadera entra en el bloque de sentencias asociado al while.

Ejecuta dicho bloque de sentencias, evaluando de nuevo la expresin y actuando


en consecuencia.

Si la primera evaluacin resulta falsa, el bloque de sentencias no se ejecuta nunca.

Si la expresin es siempre cierta el bucle es infinito.

Vamos con el primer ejemplo:

Comentario
El ejemplo lo que hace es mostrar en la terminal la tabla de multiplicar del nmero cuatro,
utilizando un bucle while. Para ello necesitamos declarar una variable auxiliar de tipo entero
llamada i1, inicializada con el valor de 1, en cada iteracin se comprueba el valor de la
variable auxiliar, mientras el valor de i1 sea <=10 la evaluacin ser verdadera y se
ejecutarn las instrucciones que hay dentro del bloque while, dentro de ese bloque tenemos
que incrementar el valor de i1, de esta manera nos aseguramos en algn momento la
salida del bucle, cuando i1 llegue a 11 la condicin ser falsa y la secuencia del programa
saltar a la lnea 19 finalizando el programa.

La salida del programa ser el siguiente:

Si queremos que el programa este siempre ejecutndose (lo normal en un programa para
Microcontroladores), hay que colocar un bucle infinito, mira el siguiente ejemplo:

Comentario
true es una constante booleana que equivale a 1 verdadero. Por tanto la evaluacin del
bucle siempre ser cierta y no habr manera del salir de l. El programa estar siempre
esperando a que pulses una tecla y mostrar el valor de la tecla pulsada en la terminal.

Pgina 8 de 18
Volver al ndice

Aunque no hemos visto el tema de las funciones todava, pero ya que estamos utilizando
esta funcin muy a menudo, vamos a ver alguna de las posibilidades que nos ofrece. El que
tenga conocimientos del lenguaje C sabr que para utilizar esta funcin que pertenece al
estndar ANSI de C hay que incluir previamente el archivo de cabecera #include <stdio.h>,

pero esto con el compilador PCW de CCS no funciona, en este compilador esta funcin est
definida en la directiva:

#use RS232(BAUD=9600,BITS=8,PARITY=N,XMIT=PIN_B1,RCV=PIN_B2)

Esto quiere decir que cada vez que queramos utilizar la funcin printf tenemos que haber
incluido previamente esta directiva, que posibilita la comunicacin del PIC con otro
dispositivo utilizando el protocolo de comunicacin serie RS232, adems de la funcin printf
esta directiva permite el uso de otras funciones para la entrada y salida de datos serie
como: getc, getchar, gets, puts y kbhit que iremos viendo poco a poco, pero la ms
importante para la salida de datos sin duda es printf, porque nos permite formatear la
salida de esos datos de la forma que nosotros queramos.

Como vemos la directiva #use RS232 admite una serie de parmetros que son los que van
entre parntesis separados por comas, estos son los siguientes:

BAUD con este parmetro establecemos la velocidad en baudios a la que


queremos que se transmitan los datos por el puerto serie, 9600 es lo
normal.

BITS nmero de bits que utilizaremos en la transmisin, el estndar


establece que pueden ser 8 9, para la comunicacin con
microcontroladores con 8 son suficientes.

PARITY nos permite utilizar un bit de paridad para la comprobacin de


errores, est opcin la dejamos a No.

XMIT est opcin nos configura porque patilla del PIC saldrn los datos,
est opcin junto con la siguiente s que la tendremos que cambiar a
nuestras necesidades.

RCV nos configura porque patilla del PIC se recibirn los datos. En el
ejemplo, los datos se transmiten por el PIN RB1 y se reciben por RB2.

La forma de hacer la llamada a la funcin printf es la siguiente:

printf(Nombre Funcin, Cadena de caracteres , valores);

Como vemos la funcin printf tambin admite parmetros que podremos utilizar para
formatear el texto de salida. Vamos a ver cules son:

El primero es opcional y es el nombre de una funcin, si no lo ponemos los datos se


transmitirn va RS232 a travs de los pines que hayamos configurado en la directiva #use
RS232.
El segundo parmetro es una cadena de caracteres encerrada entre comillas dobles.
Y el tercero son datos o nombres de variables cuyo valor queremos que se muestren.
Vamos a ver todo esto con ejemplos que es como mejor se ven las cosas:

1 Ejemplo:
#include <16F877.h>
#use delay(clock=4000000)
#include <LCD.C>
#use RS232(BAUD=9600,BITS=8,PARITY=N,XMIT=PIN_B1,RCV=PIN_B2)
void main()
{
int i1=9;
lcd_init(); //funcin de inicializacin del LCD
//Mostramos una cadena en la terminal
printf("Esto es una cadena\r");

//Mostramos una cadena de texto junto con el valor de una variable


//en la terminal.
printf("El valor de la variable i1 es: %d",i1);
//Mostramos el valor de la variable por el LCD
printf (lcd_putc,"El valor de i1 es: %d",i1);

Comentario:
En este primer ejemplo vamos a ver el uso de la funcin printf utilizando diferentes
parmetros. Como vamos a utilizar la librera que incluye el compilador para el manejo de
un LCD tenemos que incluir la directiva:

#include <LCD.C>

Declaramos una variable i1 de tipo entero que nos va a servir para mostrar su valor en la
terminal y en un LCD.
Cuando utilicemos la librera LCD.C y antes de utilizar cualquier otra funcin incluida en la
librera tenemos que llamar a la siguiente funcin que sirve para inicializar el LCD.

lcd_init();

En la primera llamada a la funcin printf como parmetros solo incluimos una cadena de
caracteres constante que termina en (\r), esa barra invertida junto con la r se le llama
secuencia de escape y le est diciendo al compilador que al final de la cadena introduzca un

retorno de carro (tecla enter). Las secuencias de escape se utilizan para representar
caracteres o acciones especiales.

printf("Esto es una cadena\r");

En la tabla de abajo se muestran las secuencias de escape que tenemos disponibles para
utilizar con la funcin printf:

\r

Retorno de carro

\t

Tabulador

\'

Comilla simple

\"

Comillas dobles

\\

Barra invertida

\?

Smbolo de interrogacin

\0

Caracter nulo

\%

Smbolo Tanto por ciento

\b

Retroceder un caracter

Vamos con la segunda llamada a la funcin:

printf("El valor de la variable i1 es: %d",i1);

En este caso tampoco est definido el primer parmetro, por tanto, al igual que en la
primera llamada a la funcin, los datos se enviaran por el puerto serie al pin que hayamos
definido en la directiva #use RS232, en esta llamada vemos que tenemos la cadena de
caracteres limitada por las comillas dobles y separado por una coma, como tercer
parmetro el nombre de la variable i1 que habamos declarado previamente. En la cadena

de caracteres vemos que aparece el carcter de % seguido de la letra d, ese es un carcter


especial para la funcin y lo que le indica a la funcin es que en esa posicin muestre el
valor de la variable i1, la d le indica a la funcin que represente ese valor en formato de
nmero entero. Podemos representar el valor de la variable en diferentes formatos segn
se muestra en la tabla de abajo:

Caracter

Cadena caracter

Entero sin signo

Entero con signo

Lu

Entero largo sin signo

Ld

Entero largo con signo

Entero Hexadecimal (minsculas)

Entero Hexadecimal (maysculas)

Lx

Entero largo Hexadecimal (minsculas)

LX

Entero largo Hexadecimal (maysculas)

Nmero real en coma flotante con truncado

Nmero real en coma flotante con redondeo

Nmero real en formato exponencial

Entero sin signo con decimales insertados. Especifica dos nmeros para n. La 1 cifra indica el total y la 2 el nmero

Si quisisemos mostrar el valor de ms de una variable lo haramos de la siguiente forma:

printf("El valor i1 es: %d el de i2: %d y el de i3: %d",i1,i2,i3);

Vamos con la ltima llamada a la funcin del 1 ejemplo:

printf (lcd_putc,"El valor de i1 es: %d",i1);

En esta llamada hemos incluido el primer parmetro y hemos puesto el nombre de la


funcin lcd_putc, est funcin est definida en la librera LCD.C que trae el compilador
para ayuda del manejo de los dispositivos LCD y que hemos incluido en nuestro programa
por medio de la directiva #include <lcd.c>, vemos que la librera est encerrada entre los
smbolos de <> esto le indica al compilador que busque la librera en el directorio en que se
instalo el compilador, si copiramos esa librera en otro directorio tendramos que indicarle
la ruta completa, pero esta vez encerrada entre comillas dobles.

Ejemplo:

#include C:\Ejemplos de C\lcd.c

Pues bien ahora la funcin printf no enviar los datos al puerto serie, sino a la funcin
lcd_puct que ser la encargada de envirselos al LCD, esta funcin por defecto enva los
datos al puerto D del PIC, pero accediendo a la librera se puede modificar el puerto
fcilmente.

Aqu tenis un video demostrativo del ejemplo:

2 Ejemplo

Salida del programa:

Comentario del programa:

El especificador de formato %x indica al sistema que escriba en hexadecimal


(base 16) el valor sustituido.

El ejemplo tambin escribe el carcter 'A', apoyndose en cuatro formas


distintas de representaciones inciales. En todos los casos se almacenar el
mismo valor numrico, pero son diferentes las representaciones usadas.

El carcter (A) sale en la terminal en una lnea diferente cada vez que se
imprime, eso es debido a la secuencia de escape (\r) utilizada.

Observar que el ejemplo se ha hecho sobre el PIC 16f84 que no dispone de


una USART hardware para la comunicacin serie , pero sin embargo el
programa se ha ejecutado correctamente, eso es debido a que la
comunicacin serie se ha establecido por software por medio de las libreras
implementadas en el compilador PCW.

Programacin PIC en C - Funciones de Entrada/Salida serie RS232. Funciones getc(), getch() y


getchar()

Escrito por biblioman

Pgina 9 de 18
Volver al ndice

Vamos a continuar con las funciones disponibles en CCS para la entrada y salida de datos
a travs del puerto serie RS-232. Hasta ahora solo hemos visto que con la funcin
printf(), podemos enviar datos formateados a travs del pin que hayamos seleccionado en
la directiva:
#use RS232(BAUD=9600,BITS=8,PARITY=N,XMIT=PIN_D1,RCV=PIN_D2)

En este caso los datos saldrn por el pin RD1 del PIC. Pero de que funciones disponemos
para recibir datos desde fuera hacia nuestro PIC?. El que haya programado en C echar
de menos la funcin scanf() definida en la librera stdio.h y perteneciente al estndar
ANSI C. Pero desgraciadamente esa funcin tampoco est disponible en CCS. Pero
tampoco hay por qu preocuparse mucho, porque disponemos de otras. En este caso
vamos a ver las funciones: getc(), getch() y getchar(). Las tres hacen lo mismo por lo que
podemos usarlas indistintamente.

Estas funciones esperan un carcter por la patilla del PIC que hayamos definido en la
directiva #use RS232 con el parmetro RCV. En el caso del ejemplo de arriba, los datos
sern recibidos por el pin RD2 del PIC.

Pues vamos a ver nuestro primer ejemplo acerca del uso de estas funciones:

Comentario:
El ejemplo lo que hace es mostrar el valor de la tecla que pulsemos en el teclado y su
equivalente en cdigo ASCII

Vamos a explicar su funcionamiento paso a paso:

Primeramente, como siempre, incluimos por medio de la directiva #include el


archivo de cabecera del PIC que vamos a utilizar, en este caso el PIC16F877.

Por medio de #use delay le decimos al compilador la frecuencia de reloj que


vamos a utilizar en nuestro circuito.

Configuramos los parmetro de la directiva #use RS232, fijaros que


XMIT=PIN_D0 y que RCV=PIN_D1. Con lo cual los datos saldrn del PIC por el
pin RD0 y entrarn por el pin RD1.

Dentro de la funcin principal main(), escribimos lo que queremos que haga

nuestro programa. Las instrucciones siempre empezarn a ejecutarse una a una


a partir de esta funcin y de arriba hacia abajo.

Lo primero que hacemos es declarar una variable de tipo char donde


almacenaremos el valor de la tecla que pulsemos en el teclado.

Despus se nos mostrar un mensaje en la terminal invitndonos a que


introduzcamos un carcter.

printf("Introduzca un caracter :\r");

Despus se ejecutar la sentencia:

ch=getch()

Que esperar hasta que pulsemos una tecla y almacenar su valor en la variable ch.

La siguiente instruccin:

printf("El caracter %c tiene un valor ASCII decimal de %d.\r",ch,ch);

muestra el valor del carcter y su equivalente en cdigo ASCII

Despus se repite el proceso dos veces ms, pero esta vez utilizando las
funciones getc() y getchar()

Al utilizar solo la variable ch, el valor de la nueva tecla pulsada sobrescribir el valor
anterior de la variable.

La salida de nuestro programa ser el siguiente:

Bien, hay que decir que el programa finalizar al llegar a la ltima sentencia incluida en la
funcin main(). Para que el programa termine cuando nosotros queramos tenemos que
incluir como mnimo un bucle y establecer una condicin para que podamos salir de l,
vamos a ver esto con otro ejemplo:

En este ejemplo se irn mostrando en la terminal las teclas que vayamos pulsando por el
teclado hasta que pulsemos la tecla n momento en el cual finalizar el programa.
Los cdigos fuentes de los ejemplos los tenis aqu.

Gracias por vuestros comentarios y en especial al equipo de ABCdatos por incluirlo en su


directorio y considerarlo como el mejor tutorial de la semana.

Programacin PIC en C - Funciones de


Entrada/Salida serie RS232. Funciones gets() y
puts()
Escrito por biblioman

Pgina 10 de 18

Volver al ndice

Un par de funciones mas que se pueden utilizar en la entrada y salida de datos serie
RS232 son las funciones gets() y puts().

gets(string): esta funcin lee los caracteres que se introducen por el teclado hasta que
encuentra un retorno de carro (tecla Enter). El pin asignado para la lectura de los
caracteres es el que hayamos configurado en RCV. En el ejemplo de abajo el pin RD5.

puts(string): esta funcin enva la cadena de texto contenida dentro de los parntesis al
pin que hayamos configurado en el parmetro XMIT de la directiva #use RS232, en el
ejemplo de abajo el pin RD4. Una vez enviada la cadena aade un retorno de carro.

#use RS232(BAUD=9600, BITS=8, PARITY=N, XMIT=PIN_D4, RCV=PIN_D5)

Vamos a ver un ejemplo sencillo que utilice estas dos funciones:

Comentario

En este ejemplo se ha declarado un tipo de dato que todava no hemos visto, un array de
caracteres:

char nombre[9];

Aunque veremos los tipos de datos compuestos ms adelante, podemos adelantar que un
array es un conjunto de variable del mismo tipo de datos. Cada una de esas variables se
coloca de forma consecutiva en la memoria RAM del PIC y se les llama elementos del
array. Los elementos del array se enumeran empezando por el 0, (es una caracterstica
del lenguaje C). En el ejemplo de arriba se ha declarado un array de caracteres (tipo char)
y el nmero mximo de elementos que podemos almacenar en el es de 9. Sus elementos
estarn numerados del 0 al 8. Y podemos acceder a ellos de la siguiente forma:

valor = nombre[0];

.. = ..[.];

valor = nombre[8];

El ejemplo lo nico que hace es enviar un mensaje a la terminal dicindonos que


introduzcamos nuestro nombre (puede ser tambin una password o lo que queramos).
Cuando introduzcamos el nombre y pulsemos la tecla Enter, la cadena de caracteres ser
guardada en el array que hemos declarado previamente y luego con la primera funcin
prinf() mostramos el valor de la cadena de texto guardada en el array, con la segunda
funcin prinf() mostramos el tercer carcter del nombre introducido (nombre[2]). Este
ser la salida de nuestro programa:

Si intentamos introducir una cadena ms larga, el valor se mostrar en la terminal pero


truncado.

Por ejemplo si intentamos introducir la cadena:

Microcontroladores PIC

La terminal nos mostrar:

Microcont

Como he dicho antes los elementos del array se almacenan en posiciones consecutivas de
la memoria RAM del PIC. Esto lo gestiona automticamente el compilador, lo mismo que
cuando haces un programa en C en otro lenguaje de alto nivel para un PC de escritorio

el programador no est preocupado de en qu posicin de la memoria RAM se


almacenarn las variables que declara. Pero si a pesar de ello quieres saberlo haces lo
siguiente:

Despus de compilar el ejemplo, te vas al men compile > Symbol Map y nos aparecer
la ventana de abajo:

Donde vemos las posiciones de memoria donde se han mapeado nuestras variables. Como
vemos nuestro array ha ocupado las posiciones de memoria de la 0x21 a la 0x29 de los
registros GPR del PIC (en total 9 bytes), ya que los elementos que componen el Array son
de tipo char que son de un byte (8 bits) cada uno.

Si queremos ver los valores que va tomando cada uno de los elementos del Array en
tiempo de ejecucin. Lo podemos hacer por medio de la ventana Watch Windows en
Proteus. Si no te acuerdas de cmo se hace mralo aqu

Y obtendremos lo siguiente:

Consideraciones:
En C existe el concepto de memoria dinmica. La memoria dinmica es aquella que se
puede reservar y liberar en tiempo de ejecucin, es decir, durante la ejecucin del
programa se liberar y se asignar memoria para optimizar los recursos de la CPU, para
ello se dispone de funciones como malloc() y free(). El compilador CCS tambin admite
este tipo de funciones, para utilizarlas debemos de incluir el archivo de cabecera stdlibm.h,
ya veremos un ejemplo sobre la asignacin de memoria dinmica, si no utilizamos estas
funciones la reserva de memoria es esttica, es decir, si declaramos un array de nueve
elementos el compilador le reservar memoria contigua a dicho array al compilar el
programa en los registros de propsito general (GPR). Esa memoria se reserva cuando el
PIC empieza a ejecutar su programa y permanece reservada hasta que desconectamos el
PIC. Con todo esto quiero decir que tenemos que tener siempre claro de la memoria RAM
de que disponemos, segn el modelo de microcontrolador que utilicemos en nuestro
proyecto, si no lo sabes consulta el data sheet del PIC que ests utilizando. Por ejemplo si
en el PIC del ejemplo que hemos hecho (16f877) en vez de un array de 9 elementos
declaramos uno de 100 elementos el compilador nos mostrar el siguiente error al
compilar:

Y como ya he dicho en alguna ocasin, utiliza el tipo de datos ms pequeo posible, en el


ejemplo que he puesto he declarado un array de nueve elementos, para introducir el
nombre Antonio que tiene seis caracteres, con lo cual estoy desperdiciando dos bytes de
memoria RAM, eso en un ordenador de escritorio es insignificante pero en un
microcontrolador si que es importante y puede que nos quedemos sin memoria suficiente
para declarar todas las variables de nuestro programa.

El cdigo del ejemplo lo tenis aqu

Aunque existen algunas funciones ms para la entrada y salida de datos serie, las que
llevamos vistas hasta ahora son las ms importantes, as que el prximo da empezaremos
con los operadores, indispensables para hacer operaciones con los datos que
introduzcamos en nuestro programa.

No os olvidis de comentar el artculo y de participar en el foro.


Un saludo y hasta el prximo da.

Programacin PIC en C - Sentencias repetitivas. Bucle for()


Escrito por biblioman

Pgina 13 de 18
Volver al ndice

Bucle for()
En el ejemplo de la tabla de multiplicar utilizamos el bucle while para obtener los diez
valores de la tabla, y si recordis necesitbamos una variable de control que tenamos que
inicializar antes de entrar en el bucle, comprobar el valor de la variable para la continuacin
en el bucle y la modificacin posterior de la variable de control para poder salir del bucle en
un momento determinado.

Pues bien, casi siempre que se hace algo, C proporciona frecuentemente un modo ms
compacto de hacer lo mismo.

El bucle for permite indicar estos tres elementos en un solo lugar, al principio del bucle,
facilitando as la obtencin de un cdigo compacto, pero legible. Veamos cual es su
sintaxis:

En un bucle for, el parntesis que acompaa a la palabra reservada for generalmente


contiene tres expresiones: Expresin 1; inicializa la variable variables de control del
bucle. Expresin 2; representa la condicin de continuacin en el bucle. Expresin 3;
modifica el valor de las variables de control en cada iteracin del bucle.
Los puntos y comas que separan cada expresin son obligatorios.

Vamos a ver un ejemplo donde se muestran las diferentes posibilidades de que disponemos
cuando utilicemos el bucle for:

Comentario
1 bucle for:

indice=1, inicializa a la variable de control del bucle. La segunda expresin,


indice<=VAL_MAX, representa la condicin de continuacin. Por ltimo, la tercera
expresin, indice++, utiliza el operador de incremento para modificar a la variable de
control en cada iteracin del bucle.
Los pasos que sigue la sentencia for son los siguientes:

1. indice es la variable de control. Se inicializa a 1


2. se testea la condicin de expresin_2.
3. se ejecutan las sentencias
4. la variable de control indice se incrementa en uno
5. si se cumple que indice<=VAL_MAX va al paso 3. Si no va al paso 6.
6. Finalizar la ejecucin cuando indice=20

2 bucle for

Bucle for con varias variables de control, las variables tienen que ir separadas por comas.
En este caso tenemos las variables x e y, aunque podemos poner todas las que queramos,
ambas variables son inicializadas dentro de la sentencia for.

3 bucle for

Cuando el bucle for se escribe sin cuerpo sirve por ejemplo para generar retardos, esta
posibilidad la utilizaremos poco con este compilador ya que incluye funciones especficas de
retardo.

4 bucle for

El bucle for permite no incluir las expresiones 1 y 3, aunque los puntos y comas son
obligatorios ponerlos. En este caso se asemeja mucho a un bucle while.

5 bucle for

Se puede crear un bucle infinito por medio de la expresin for(;;).


Podemos salir de un bucle infinito por medio de la sentencia break, cuando se encuentra en
cualquier lugar dentro del cuerpo de un bucle da lugar a la terminacin inmediata de este,
en el caso del ejemplo saldremos del bucle cuando pulsemos la letra v. Las sentencias de
salto las veremos ms adelante.

La salida del programa ser la


siguiente:

El cdigo fuente lo tenis aqu.

Como veis el lenguaje C posee mucha flexibilidad y se puede hacer una misma cosa de
varias formas. Segn el caso ser ms cmodo y legible utilizar una sentencia que otra.

Como ejercicio podis hacer la tabla de multiplicar de un nmero utilizando el bucle for.

Un saludo

Programacin PIC en C - Sentencias Repetitivas. Bucle do-while()


Escrito por biblioman

Pgina 14 de 18

Volver al ndice

Bucle do-while()
A diferencia de los bucles for y while, que analizan la condicin del bucle al principio del
mismo, el bucle do-while analiza la condicin al final del bucle. Esto significa que el bucle
do-while siempre se ejecuta al menos una vez. La forma general del bucle do-while es la
que se muestra en la figura de abajo:

Vamos a ver un ejemplo:

Comentario
Este ejemplo pide un valor entre 1 y 10, ejecutndose repetidas veces hasta que se
introduce un valor entre ambos lmites. Por ltimo el programa visualiza el valor ledo.

Este bucle se ejecutar como mnimo una vez porque el programa no sabe cul es la
condicin de continuacin hasta que se encuentra el while del final del cuerpo del bucle. Si
la condicin sigue siendo cierta (es decir, si el valor ledo est fuera del intervalo deseado),
el programa regresa al principio del bucle do-while y lo ejecuta de nuevo.

Fijaros en la orden if que aparece dentro del cuerpo del bucle. Esto se permite porque las
estructuras de control se pueden anidar unas dentro de otras.

RECUERDA: En el bucle while la comprobacin de la condicin de control del bucle se


encuentra al principio, por lo que dicho bucle puede no ejecutarse nunca; la comprobacin
en el bucle do-while est al final del bucle, por lo que al menos se ejecutar una vez.

El cdigo fuente lo tenis aqu.

Programacin PIC en C - Sentencias Condicionales. Sentencia If


Escrito por biblioman

Pgina 15 de 18

Volver al ndice

If
Vamos a empezar las sentencias condicionales, con la ms simple de todas, la sentencia if.
Si se evala como cierta la expresin que hay entre parntesis al principio de la sentencia if
se ejecuta el bloque de sentencias contenido entre las llaves y si se evala como falsa la
condicin, el programa se salta ese bloque de instrucciones. En la figura de abajo tenis la
sintaxis de esta sentencia.

Si slo hay una sentencia se pueden suprimir las llaves, ejemplo:

if (x=1)
printf(Sin llaves solo una sentencia asociada al if);

Como ejemplo de sentencias if tenis el decodificador de binario a decimal.

Programacin PIC en C - Sentencias Condicionales. Sentencia if-else


Escrito por biblioman

Pgina 16 de 18

Volver al ndice

Sentencia IfElse
Cuando el programa llega a una sentencia condicional del tipo If Else, primero se evala
una expresin; si se cumple (es cierta) se ejecuta un bloque de sentencias y si es falsa se
ejecuta otro bloque.

En la figura de abajo se muestra la sintaxis de esta sentencia condicional.

Ejemplo:

Comentario
Este ejemplo visualiza en el display de ctodo comn, conectado a la puerta B del PIC, el
0 si el interruptor conectado a RA0 est abierto y 1 si est cerrado, para ello utiliza la
sentencia if-else, dentro de un bucle infinito para que el programa est siempre
chequeando el estado de la patilla RA0.

En este ejemplo hemos incluido una directiva nueva #use fast_io(puerto). Esta directiva
se utiliza para optimizar el cdigo generado por el compilador cuando se utilizan funciones
de manejo de entrada y salida como input(pin) definidas ya en CCS.
Si no se incluye esta directiva el compilador tomar por defecto la directiva #use
standard_io(A), que har que cada vez que se utilicen estas funciones se reprograme el
pin correspondiente como entrada salida, lo que har que el cdigo ASM generado tras la
compilacin sea mayor.

Podemos comprobar esto si despus de compilar nuestro ejemplo, dentro del IDE de CCS
seleccionamos Compile--> C/ASM List

Como se ve en la figura la memoria de programa (ROM) ocupa 21 palabras.

Ahora se pueden hacer las siguientes pruebas, la primera poner la directiva #use
standard_io(A), y la segunda simplemente quitar la directiva #use fast_io(A) y no
poner nada, segn se muestra en la figura de abajo:

Volvemos a compilar y en ambos casos obtendremos lo siguiente:

En ambos casos la memoria ROM utilizada es de 24 palabras, tres ms que cuando


utilizbamos la directiva #use fast_io(A).

Otras funciones para el manejo de bits de los puertos de entrada y salida que vienen
definidas en CCS y que dependen de la directiva #use*_io() son:

output_bit(Nombre_pin,valor) --> coloca el pin indicado a 0 1.

output_high(Nombre_pin) --> coloca el pin indicado a 1.

output_low(Nombre_pin) --> coloca el pin indicado a 0

Fijaros que no he utilizado la directiva #use fast_io(B) para el puerto B, ya que no se

utilizan funciones del compilador para el manejo de los bits de salida. En este caso el
puerto B del PIC se controla mapeando la direccin de memoria del puerto B como una
variable ms en la RAM del PIC, por medio del identificador port_b.

Circuito del ejemplo:

El cdigo del ejemplo y el circuito en Proteus lo tenis aqu

Un saludo

Programacin PIC en C - Sentecias Condicionales. Sentencia switch


Escrito por biblioman

Pgina 17 de 18

Volver al ndice

Sentencia switch
La sentencia switch se compone de las siguientes palabras clave: switch, case, default y
break.

Lo que hace est sentencia es comparar sucesivamente el valor de una expresin (dicha
expresin tan solo puede ser de tipo entero o de tipo carcter) con una lista de constantes
enteras o de caracteres. Cuando la expresin coincide con la constante, ejecuta las
sentencias asociadas a sta.

La estructura de la sentencia switch es la siguiente:

La sentencia break hace que el programa salte a la lnea de cdigo siguiente a la sentencia
switch. Si se omite se ejecutar el resto de casos case hasta encontrar el prximo break.

La sentencia default se ejecuta cuando no ha habido ninguna coincidencia. La parte default


es opcional y, si no aparece, no se lleva a cabo ninguna accin al fallar todas las pruebas y
el programa seguir a partir de la llave que cierra la sentencia switch

Consideraciones a la hora de usar esta sentencia:

En una sentencia switch No puede haber dos sentencias case con el mismo valor de
constante.

Una constante char se convierte automticamente a sus valores enteros.

Switch difiere del if en que switch solo puede comprobar la igualdad mientras que if
puede evaluar expresiones relacionales o lgicas. Adems cuando la comparacin se
basa en variables o se trabaja con expresiones que devuelven float deberemos usar
el if-else.

Hay que decir que la secuencia de sentencias en un case no es un bloque (no tiene
porque ir entre llaves). Por lo tanto no podramos definir una variable local en l.
Mientras que la estructura swith global s que es un bloque.

Vamos a ver un ejemplo para ver todo esto:

Comentario
En el ejemplo introducimos un carcter numrico, lo almacenamos en el array llamado
cadena1 y por medio de la funcin atoi() lo convertimos a un valor entero y guardamos su
valor en la variable de tipo entero num, no hay que olvidarse de incluir el archivo de
cabecera stdlib.h necesaria para la funcin atoi().

Ahora introducimos valores para ver que obtenemos a la salida.


Si introducimos un 1, coincidir con el valor de la constante asignada al primer case, por

lo cual se ejecutan las dos primeras sentencias y el programa para de ejecutar sentencias
porque se ha encontrado con un break, despus ejecuta el ltimo printf() por estar esta
sentencia fuera de las llaves que delimitan a switch.

Fijaros en el segundo case, he omitido su break correspondiente a posta (el compilador no


da error si se quita), para que veis el resultado cuando se introduce un 2:

Como veis en la figura de arriba se ejecutan las sentencias pertenecientes al segundo case,
pero al no encontrar la sentencia break, ejecuta tambin la sentencia del tercer case. Esto
hay que tenerlo en cuenta para tener claro que lo que hace salir de la sentencia switch es el
break correspondiente a cada case.
Si introducimos por ejemplo un 9 al no a ver coincidencia con el valor de ningn case, se
ejecutar la sentencia perteneciente a default.

El cdigo fuente del ejemplo lo tenis aqu.

Un ejemplo un poco ms prctico del uso de esta sentencia, lo tenis aqu, gracias a

QuijotescoXp, que ha hecho una mejora en el decodificador binario a decimal que en su da


inclu en la seccin de prcticas, os animo a que participes en el foro aportando mejoras
ejemplos nuevos, de esta forma ayudas a que el curso vaya para adelante.

Un saludo y hasta pronto

Curso de programacin PIC en C con CCS. Funciones (1)


Escrito por Biblioman

Volver al ndice

Las funciones son el pilar de los lenguajes estructurados como es el C, cualquier programa
medianamente grande debe de estar formado por diferentes funciones, cada una de ellas
har una tarea determinada y sern llamadas desde la funcin principal (main) o desde
otras funciones segn vayan haciendo falta a lo largo de la ejecucin del programa, por
tanto es de vital importancia el conocer a fondo todas sus posibilidades, no solo para
construir nuestras propias funciones, sino tambin para entender el cdigo de programas y
libreras hechos por otras personas.
Vamos ha empezar con algunas definiciones y ejemplos sencillos aplicados al compilador
CCS.

1. INTRODUCCIN

Las funciones son los elementos principales de un programa en C.

Son bloques en los cuales se realiza una tarea especfica.

Un programa en C est formado por la funcin main que es el bloque principal, por
funciones propias del programador y por funciones de libreras propias del
compilador.

Una caracterstica importante de las funciones, es que pueden recibir parmetros y


que pueden devolver un valor. La misma funcin nos puede servir para varios
casos, con tan solo variar el valor de los parmetros. El compilador de CCS incluye
muchas funciones "built-in" (listas para usarse) en sus libreras para el control
directo de muchos de los recursos del PIC, para utilizarlas slo necesitamos saber

los parmetros que reciben y los valores que devuelven.

2. PARTES DE UNA FUNCIN


En una funcin hay que distinguir tres partes:

La declaracin (tambin denominada prototipo).

La definicin (o la propia funcin).

La llamada a la funcin.

El hecho de que nuestro programa defina una funcin, no quiere decir que esa funcin sea
ejecutada. A menos que se produzca una llamada a la funcin, la funcin no ser
ejecutada, sino tan solo definida.
Por ejemplo, cuando nosotros incluimos la directiva #USE RS232 que es una directiva
asociada a las bibliotecas precompiladas, incluimos los prototipos de muchas funciones,
pero slo se ejecutan aquellas funciones a las que llamamos, como printf. Vamos a ver con
ms detalle cada una de estas partes.

2.1 Declaracin de una funcin

La declaracin de una funcin se denomina prototipo de la funcin.

El prototipo aparece antes del bloque main, o normalmente en los archivos de


cabecera (.h)

El prototipo de una funcin debe aparecer antes de su llamada, y le indica al


compilador el nmero de parmetros que utiliza una funcin, y de que tipo son.

La sintaxis del prototipo es:


tipo nombre_funcin(parmetros);
Donde:
tipo-> es el tipo de dato que va a devolver la funcin. Si no se indica ningn tipo de dato,
por defecto
se asume el tipo int. Si la funcin no va a devolver ningn dato hay que poner void.

nombre_funcion-> es el identificador de la funcin, con el que va a ser referenciada.

parmetros-> es la lista de parmetros (valores) que recibe la funcin; en la declaracin


tambin sera
correcto la siguiente expresin:
tipo nombre_funcin(tipo,tipo, tipo,....);
En el cual tenemos tantos tipos como parmetros vaya a aceptar la funcin.
Supongamos una funcin que define un parmetro de tipo float y que cuando es llamada en
el programa, se le pasa un dato de tipo int. Como en el prototipo se ha indicado que el
parmetro es de tipo float, el compilador convertir primero el dato de tipo int a float, y
despus le pasar como parmetro a la funcin ese dato convertido.
La declaracin de la funcin tambin controla que el nmero de argumentos usados en una
llamada a una funcin coincida con el nmero de parmetros de la definicin.

2.2 Definicin de una funcin


La definicin es la funcin en s, el bloque de sentencias que va a componer esa funcin. La
sintaxis
de la definicin de una funcin es:
tipo nombre_funcin(parmetros)
{
declaracin de datos de la funcin.
cuerpo de la funcin
}
La funcin va encabezada por el prototipo, pero esta vez sin finalizar en punto y coma.
Despus, se
incluye el bloque de sentencias de la funcin.
La lista de parmetros puede ser vaca, sin parmetros, si bien los parntesis de la funcin
deben
colocarse de igual forma.

Las definiciones de las funciones es aconsejable escribirlas a continuacin de la funcin


main()
Un ejemplo de la definicin de una funcin sera:
float division (float x, float y)
{
float resultado;
resultado=x/y;
return(resultado)
}
Nota: Una funcin C, no puede contener en su interior otras funciones.

2.3 Llamada a una funcin


Para ejecutar una funcin hay que llamarla. La llamada a una funcin consta del nombre de
la misma
y de una lista de argumentos o valores a pasar denominados parmetros actuales,
separados por comas y encerrados entre parntesis.
Cuando el programa llama a una funcin, la ejecucin del programa se transfiere a dicha
funcin. El
programa retorna a la sentencia posterior a la llamada cuando acaba esa funcin. La
sintaxis de una
llamada a una funcin es:
nombre(parmetros);
donde:
nombre-> es el identificador con que es definida la funcin a la que queremos llamar.
parmetros-> es la lista de valores que se asignan a cada parmetro de la funcin (en caso
de que
tenga), separados tambin por comas.

3. VALORES DEVUELTOS

Todas las funciones, excepto aquellas de tipo void, devuelven un valor. Este valor se
especifica
explcitamente en la sentencia return y si no existe sta, el valor es 0.
La forma general de return es:
return expresin;
Tres observaciones sobre la sentencia return:
A) La sentencia return tiene dos usos importantes. Primero, fuerza a una salida inmediata
de la
funcin, esto es, no espera a que se llegue a la ltima sentencia de la funcin para acabar.
Segundo, se puede utilizar para devolver un valor.
B) return no es una funcin sino una palabra clave del C, por lo tanto no necesita
parntesis
como las funciones, aunque tambin es correcto: return(expresin); pero teniendo en
cuenta
que los parntesis forman parte de la expresin, no representa una llamada a una funcin.
C) En las funciones de tipo void se puede hacer:
return;
y de esta forma se provoca la salida inmediata de la funcin.
La funcin (como hemos dicho, en realidad es una sentencia de C) admite cualquier
expresin
valida en C.
Ejemplo.
int multiplica (int x, int y)
{
return (x*y);
}
Si el tipo de datos de la expresin de la sentencia return no coincide con el tipo de datos

que debe devolver la funcin automticamente se convierte el tipo de datos para que haya
coincidencia.
Una funcin puede contener varias sentencias return() en su cdigo.
Ejemplo.
int compara (int x, int y)
{
if(x<y)
return (0);
else
return (1);
}
Todas las funciones pueden devolver variables de cualquiera de los tipos de datos vlidos
en C.
Si no se especifica el tipo, la funcin por defecto devuelve un entero.
Por ejemplo, las siguientes funciones son equivalentes:

int resta (int x, int y)


resta (int x, int y)
{

int z;

int z;

z = y-x;

z = y-x;

return (z);

return (z);
}

Una funcin que devuelva un tipo de datos vlido en C se puede usar como operando en
cualquier
expresin vlida en C.
Ejemplo.

int resultado;
resultado = resta(4,6)*10;
Una funcin no puede usarse a la izquierda de una asignacin.
Ejemplo.
resta(a,b)=12; //Esta asignacin es incorrecta
El valor devuelto por una funcin en la sentencia return() puede no ser usado en una
asignacin,
ni en una expresin vlida en C, con lo cual este valor devuelto se perder.
En otras palabras, aunque todas las funciones, excepto las declaradas como void,
devuelven valores,
no se tiene que usar necesariamente ese valor de vuelta para algo.
Una pregunta muy comn acerca de los valores devueltos por una funcin es: ya que se
devuelve un
valor?, no se tiene que asignar ese valor de vuelta a alguna variable? . La respuesta es no.
Si no
hay una asignacin especificada, el valor devuelto simplemente se ignora.
Vamos a ver algunos ejemplos sencillos de utilizacin de funciones:
view source

print?

01./*Ejemplo de una funcin que devuelve un valor de tipo entero y que no


tiene parmetros*/
02.
03.#include <16F877A.h>
04.#FUSES NOWDT, XT, NOPUT, NOPROTECT, NODEBUG, NOBROWNOUT, NOLVP, NOCPD,
WRT_50%
05.#use delay(clock=4000000)
06.#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)

07.
08.int producto();

/*Prototipo de la funcin*/

09.
10.void main() {
11.
12. int resultado;
13. resultado=producto();

/*llamada a la funcin producto*/

14. printf("El resultado de x*y es:

%d\r",resultado);

15.
16.}
17.
18./*DEFINICIN DE LA FUNCION*/
19.
20. int producto()
21. {
22. int x=4;
23. int y=5;
24. int producto;
25. printf("El valor de x=4\r");
26. printf("El valor de y=5\r");
27. producto=x*y;
28. return(producto);//La funcin se ejecuta y devuelve el valor de la
variable producto despus el programa continua en la funcin main, en la
sentencia siguiente a la llamada de la funcin.
29.}
Si simulamos el ejemplo con Proteus obtendremos obtendremos la siguiente salida en la
terminal:

En el siguiente ejemplo se muestra diferentes formas de usar el valor devuelto por una
funcin:
view source

print?

01.#include <16F877A.h>
02.#FUSES NOWDT, XT, NOPUT, NOPROTECT, NODEBUG, NOBROWNOUT, NOLVP, NOCPD,
WRT_50%
03.#use delay(clock=4000000)
04.#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
05.
06. multiplica(int a, int b);//declaracin de la funcin
07. void main(void)
08. {

09. int x,y,z;


10. x=5;
11. y=4;
12. z=multiplica (x,y); //en esta lnea el valor de vuelta de
multiplica() es asignado a z
13. printf("%d",multiplica(x,y)); //en esta, el valor de vuelta no se
asigna, sino que se usa en la funcin printf
14. multiplica (x,y); //aqu el valor de vuelta se pierde porque no se
asigna a otra variable ni se usa como parte de una expresin.
15. }
16.
17. //Definicin de la funcin
18. multiplica (int a, int b)
19.
20. {
21. return a*b;
22.
23. }

En el siguiente ejemplo se muestra lo que ocurre cuando no hay coincidencia en el nmero


de parmetros declarados en el prototipo con el nmero de argumentos en la llamada a la
funcin.

view source

print?

01.//Este programa no compilar porque no coincide el nmero de


parmetros especificados en el prototipo de la funcin con el nmero de
argumentos usados en la llamada

02.
03.#include <16F877A.h>
04.#FUSES NOWDT, XT, NOPUT, NOPROTECT, NODEBUG, NOBROWNOUT, NOLVP, NOCPD,
WRT_50%
05.#use delay(clock=4000000)
06.#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
07.
08. float func(int x, float y); //prototipo
09.
10. main(void)
11. {
12. func(2,2.0,4);
13. return 0;
14. }
15.
16. float func(int x, float y)
17. {
18. printf("%f",y/(float)x);
19. return y/(float)x;
20.
21. }

Un ejemplo un poco ms prctico del uso de las funciones lo tenis aqu.


En siguientes artculos se vern mas cosas sobre el tema de las funciones. Como siempre
podis hacer vuestros comentarios al respecto en el foro

Curso de programacin PIC en C con CCS. Funciones (2)


Escrito por Biblioman

Volver al ndice

Continuamos con el tema de las funciones, en este caso vamos a ver a que nos referimos
cuando hablamos sobre el mbito de las funciones y tambin profundizaremos mas en el
tema sobre el tipo de argumentos que pueden recibir las funciones, veremos que es eso de
pasar un argumento por valor por referencia y que diferencia hay entre ambas formas,
todo ello con ejemplos sencillos para entender el concepto y aplicados al compilador de
CCS.

4. REGLAS DE MBITO DE LAS


FUNCIONES
Las reglas de mbito de un lenguaje son las reglas que controlan si un fragmento de cdigo
conoce o tiene acceso a otro fragmento de cdigo o de datos.
En C, cada funcin es un bloque de cdigo discreto. El cdigo de una funcin es privado a
esa funcin, a menos que se haga a travs de una llamada a esa funcin. (No es posible,
por ejemplo, utilizar un goto para saltar en medio de otra funcin). El cdigo que
comprende el cuerpo de una funcin est oculto al resto del programa y, a no ser que se
usen datos o variables globales, no puede ser afectado por otras partes del programa ni
afectarlas. Dicho de otro modo, el cdigo y los datos que estn definidos dentro de una
funcin no pueden interactuar con el cdigo o los datos definidos dentro de otra funcin
porque las dos funciones tienen un mbito diferente.
Las variables que estn definidas dentro de una funcin se llaman variables locales. Una
variable local
comienza a existir cuando se entra en la funcin y se destruye al salir de ella. As, las
variables locales
no pueden conservar sus valores entre distintas llamadas a la funcin. La nica excepcin a
esta regla

se da cuando la variable se declara con el especificador de clase de almacenamiento static.


Esto hace que el compilador trate a la variable como si fuese una variable global en cuanto
a almacenamiento se refiere, pero que siga limitando su mbito al interior de la funcin.
En C todas las funciones estn al mismo nivel de mbito. Es decir no se puede definir una
funcin dentro de otra funcin. Sin embargo si se puede llamar a funciones desde otras
funciones.
Ahora, Vamos a profundizar un poco ms sobre los valores (argumentos) que le puedo
pasar a la funcin cuando la llamo.

5. ARGUMENTOS DE FUNCIONES
5.1 Funciones sin argumentos.

Cuando una funcin se declara con el tipo void como argumento, nos est indicando que
esa funcin
no espera argumentos.
Ejemplo:
int multiplica(void); //esta funcin no espera ningn argumento y devuelve un nmero
entero.
5.2 Funciones con argumentos.

Si una funcin va a usar argumentos, debe declarar variables que tomen los
valores de esos argumentos. A estas variables que se declaran en la propia funcin
se les suele llamar parmetros formales de la funcin. Se comportan como otras
variables locales dentro de la funcin, crendose al entrar en la funcin y
destruyndose al salir.

Hay que asegurarse que el tipo de datos de los parmetros formales sea

compatible con el tipo de datos usado para los argumentos de llamada a la


funcin. Aunque el compilador no muestre ningn error por esto, los resultados
obtenidos seran imprevisibles.

Se pueden pasar argumentos a las funciones de dos formas:

Por valor

Por referencia

Llamadas por valor.


Este mtodo copia el valor de un argumento en el parmetro formal de la funcin. De esta
forma, los cambios en los parmetros de la subrutina no afectan a las variables que se usan
en la llamada.
Esto significa, en general que no se pueden alterar las variables usadas para llamar a la
funcin.
Toda esta serie de nombres y conceptos, puede parecer un poco confuso al principio sobre
todo para el que empieza por primera vez a estudiar un lenguaje de programacin.
Personalmente, he programado en varios lenguajes de programacin y bajo mi punto de
vista la nica forma de entender y avanzar en el aprendizaje de un lenguaje de
programacin es: practicar, practicar,...., y practicar. As que vamos ha empezar a ello,
en el siguiente ejemplo vamos a ver como se le pasan los argumentos a una funcin por
valor, los ejemplos de funciones que hemos visto anteriormente y que reciben parmetros
utilizan este mtodo, por lo que la sintaxis utilizada nos ser familiar.

view source

print?

01./*Ejemplo que muestra como se pasan los argumentos a una funcin por
valor*/
02.#include <16F877A.h>
03.#FUSES NOWDT, XT, NOPUT, NOPROTECT, NODEBUG, NOBROWNOUT, NOLVP, NOCPD,
WRT_50%
04.#use delay(clock=4000000)
05.#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)

06.
07.int cuadrado(int x); //declaracin de la funcin.
08.
09.void main() {
10.
11. int t=5;
12. printf("%d %d", cuadrado(t),t);//la funcin printf muestra en
pantalla 2 valores: el valor devuelto por la funcin cuadrado y el valor
de la variable t, que como vemos se pasa como parmetro a la funcin
13.}
14.
15.int cuadrado(int x) //int x es la declaracin de la variable
(parmetro formal) que va a recibir el valor del argumento de t.
16.{
17. x=x*x;
18. return(x);//la funcin devuelve el cuadrado de x
19.}

Si simulamos el ejemplo con proteus, obtendremos la siguiente salida:

Como vemos en la imagen lo que se pasa a la funcin es una copia del valor del
argumento. Lo que ocurra dentro de la funcin no tiene efecto sobre la variable utilizada en
la llamada.
En este ejemplo, se copia el valor del argumento de cuadrado(t), t=5 en el parmetro x.
Cuando se realiza la asignacin x=x*x, el nico elemento que se modifica es la variable
local x. La variable t, usada para llamar a cuadrado(t), todava tiene el valor 5.
RECUERDA: lo que se pasa a la funcin es una copia del valor del argumento. Lo que
ocurra dentro de la funcin no tiene efecto sobre la variable utilizada en la llamada.

Bueno, ahora tocara hacer un ejemplo del paso de argumentos a una funcin por
referencia. Pero para comprender esto bien, antes debemos de tratar un tema muy
importante en C que es el uso de punteros, por lo que el prximo tema tratar sobre ellos,
aprenderemos a usarlos y las precauciones que tenemos que tener con ellos, veremos

tambin algunas peculiaridades que tiene el compilador CCS en su uso.


Como siempre espero vuestros comentarios en el foro.

Curso de programacin PIC en C con CCS. Punteros (1)


Escrito por Biblioman

Volver al ndice

Empezamos hoy un tema muy importante en C como son los punteros, veremos que son,
para que sirven y las precauciones que tenemos que tener en cuenta cuando los utilicemos.

Que es un puntero?
Un puntero es una variable ms, pero que almacena la direccin de memoria de otra
variable.

Para que sirven los punteros?


El conocer la direccin de memoria de una variable es muy til en C, como funciones
principales que tienen los punteros podemos citar las siguientes:

Pueden proporcionar una forma rpida de acceder o referenciar a tipos de dados


compuestos como arrays, estructuras y enumeraciones.

Sirven para pasar variables por referencia a las funciones.

Segn los casos pueden optimizar el cdigo y ahorrar recursos de memoria.

De que operadores dispongo para

manipular los punteros?


1. El primer operador de punteros es &, un operador monario que devuelve la direccin de
memoria del operando. (Un operador monario es aquel que solo requiere un operando).
Por ejemplo:
m=&contador;
Coloca en m la direccin de memoria de la variable contador, o sea la direccin del registro
del PIC donde se ha guardado la variable contador.
Para comprenderlo mejor, supongamos que la variable contador utiliza la posicin de
memoria 0x0C (primer registro de propsito general del banco 0 del PIC 16F84A) para
guardar su valor, tambin supongamos que el valor de contador es 100. Despus de la
asignacin, m tendr el valor de 0x0C. Se puede pensar en & como "la direccin de". Por
tanto, la sentencia anterior de asignacin significa "m recibe la direccin de contador".
2. El segundo operador de punteros es *, que es el complementario de &. Es otro operador
monario que devuelve el valor de la variable ubicada en la direccin que se especifica. Por
ejemplo, si m contiene la direccin de memoria de la variable contador, entonces:
q=*m;
Colocar el valor de contador en q. Siguiendo con el ejemplo, q tendr el valor 100, ya que
100 es lo guardado en el registro 0x0C, que es la direccin de memoria que indica m.
Piensa en * como "valor en la direccin". En este caso, la sentencia de asignacin significa
que recibe el valor en la direccin m.
Como vemos y eso puede despistar un poco, el smbolo de estos operadores coincide con
los operadores (&) AND lgico y el operador matemtico (*) Multiplicacin.
No hay ningn problema en esto porque el compilador se encarga de diferenciarlos segn el
contexto donde estn colocados
3. El tercer operador utilizado es: -> y se utiliza para acceder a tipo de datos compuestos
en C como las estructuras, lo veremos ms adelante cuando veamos este tipo de datos.

Como se declara una variable

puntero?
Las variables que vayan a contener direcciones de memoria, o punteros, como se llaman en
C, deben declararse colocando un * delante del nombre de la variable. Esto indica al
compilador que va a contener un puntero a ese tipo de variable. Por ejemplo, para declarar
c como puntero a carcter (char) escribiremos lo siguiente:
char *c;
Aqu, c no es un carcter, sino un puntero a un carcter, el tipo de dato al que apunta un
puntero, en este caso char, se denomina tipo base del puntero. Bueno puede que alguien
se pregunte lo siguiente de que tipo es la propia variable puntero? Pues esta claro que
tiene que ser de un tipo de datos cuyo tamao sea suficiente para guardar una direccin de
memoria tal y como est definida por la arquitectura del microcontrolador que se utilice.
Por ejemplo si utilizamos el PIC 16f84A, con un tipo de dato entero de 8 bits (int8) sera
suficiente para direccionar todos los registros de este PIC, pero ese tipo de datos sera
insuficiente para direccionar toda la memoria de otro PIC con mayor memoria RAM. Lo que
hace CCS es establecer como tipo de dato por defecto para los punteros el entero de 16
bits (int16 en el tipo de dato nativo de CCS) para que sea compatible con todos los PICs
que actualmente se pueden programar con CCS, pero da la opcin de modificar ese
parmetro a travs de la directiva #device:
Esta directiva se encuentra incluida en el archivo de cabecera y en la siguiente instruccin
se define para que el compilador le asigne un tipo de dato int8 (1 byte) al PIC 16f84A para
las variables puntero.
#device PIC16f84A *=8
Bueno, es una manera de ahorrar recursos cuando utilicemos punteros en PICs con poca
memoria RAM. Pero en la mayora de los casos la opcin por defecto ser la correcta y
adems de no tener que preocuparnos por ello, ganamos portabilidad en nuestro cdigo, al
saber que es compatible para todos los PICs.
Lo que si es responsabilidad del programador en cada momento es el tener en cuenta que
un puntero slo debe ser usado para apuntar a datos que sean del tipo base del puntero
declarado (este tipo de cosas se ver mas adelante cuando veamos las precauciones que
tenemos que tener en cuenta cuando usemos punteros).
Nota: se puede mezclar en una misma sentencia la declaracin de variables puntero con

variables normales, por ejemplo.


int x, *y, z; //declara x, z como variables de tipo entero, e y como puntero a un tipo
entero.
Vamos a ver un ejemplo del uso de punteros:
view source

print?

01.#include "16F84A.h"
02.#use delay(clock=4000000)
03.#fuses XT,NOWDT
04.#use rs232(baud=9600,parity=N,xmit=PIN_B1,rcv=PIN_B2,bits=8)
05.
06.void main() {
07.
08. int fuente, destino;
09. int *p; //declaracin de p como un puntero de tipo entero
10.
11. fuente=4;//asigno el valor 4 a la variable fuente
12. p=&fuente; //al puntero p se le asigna la direccin de memoria de la
variable fuente
13. destino=*p;// Le asigno a la variable destino el valor de fuente a
travs del puntero p
14.
15. //Mostramos los resultados en la terminal
16. printf("La variable fuente tiene la direccion: %x (hex)\r",&fuente);
17. printf("La variable destino tiene la direccion: %x
(hex)\r",&destino);
18. printf("La variable puntero p tiene la direccion: %x (hex)\r",&p);

19.
20. printf("La variable fuente tiene el valor de: %d\r",fuente);
21. printf("destino tiene el valor de: %d, asignacion hecha con el
puntero p\r",*p);
22.
23. //Muestro el numero de bytes que ocupa en memoria una variable
puntero
24. printf("El tama\xa4o de la variable puntero es de: %d\n byte" ,
sizeof(p));
25.
26.}
La salida del programa as como el valor de los registros del PiC se pueden visualizar si
simulamos nuestro ejemplo en Proteus.

Comentario

En este ejemplo se ha modificado el tipo de dato asignado por defecto por el compilador en
la declaracin de las variables puntero. Para ello como he dicho ya, hay que modificar la
directiva #device en el archivo de cabecera segn se muestra en la figura de abajo:

Podemos ver el tamao que tiene la variable puntero por medio del operador sizeof(), si
no modificis el parmetro de la directiva #device veris que el tamao en ese caso es de
2 bytes.

Nota: cada vez que se modifique alguna librera perteneciente a CCS es conveniente hacer
una copia de dicha librera en la carpeta de nuestro proyecto y modificarla all, acordarse en
este caso de incluir en el programa principal el archivo de cabecera entre comillas dobles y
no entre los signos < >.
En la declaracin de las variables el compilador reserva las posiciones de memoria RAM
necesarias para poder contener los datos de las variables declaradas, como los registros de
la RAM de propsito general del PIC 16FXXX son de un byte (8 bits) de tamao, las
posiciones de memoria reservadas por el compilador dependern del tipo de dato con el
que se ha declarado la variable, por ejemplo una variable declarada como tipo entero (int8)
necesita un solo registro para almacenar su valor, una variable declarada como tipo float
necesitar 4 registros (32 bits) para almacenar su valor.

En el siguiente paso se produce la asignacin de datos a la variable fuente y a la variable


puntero p, como veis en la figura de abajo, el dato almacenado en p es la direccin de
memoria de la variable fuente, a partir de aqu se suele decir que p apunta a la variable
fuente y que la variable puntero p queda vinculada a esa direccin de memoria.

Ahora hacemos una asignacin indirecta de datos utilizando el operador de


indireccin (*). Como se ve en la figura de abajo asignamos a la variable destino el valor
que tiene la variable fuente indirectamente a travs del puntero p.

Bueno ahora tena pensado hacer un ejemplo con los posibles errores que podemos
cometer a la hora de utilizar punteros, pero por hoy lo tengo que dejar, quizs en el
prximo artculo.

P.D.: si veis algn error algo que aportar al respecto sera de agradecer que lo
comentarais en el foro.