Contenido 1.
............................... ..................... ..................... ............................................... .................................... 4 Constantes.....................
2.
Depuración de programas con Proteus y CCS ..............................8
3.
Funciones Funciones de Entrada / Salida serie RS232 R S232.................................12
3.1.
La función printf!. ..................... ............................... ..................... ..................... .............................12 ...................12
3.2.
Funciones Funciones getc!" getc#! y getc#ar!. ......................................17
3.3.
Funciones gets! y puts!. ..................... ............................... ..................... ..................... .................20 .......20
4.
................................ ..................... ..................... ..................... ..................... ..................... ............ .. 24 $peradores......................
5.
Sentencias repetiti%as. ..................................................................30
5.1.
............................... ..................... ..................... ..................... ................................ ...................... 30 &ucle '#ile ....................
5.2.
&ucle for.................... ............................... ..................... ..................... ............................................... .................................... 33
5.3.
................................ ..................... .............................................. .................................... 37 &ucle do('#ile .....................
6.
Sentencias condicionales ..................... ................................ ...................... ..................... .................... .......... 38
6.1.
................................ ...................... ..................... ........................................ .............................. 38 Sentencia if .....................
6.2.
Sentencia if(else .................... ............................... ..................... ..................... ...................... ....................... ............ 39
6.3.
............................... ..................... ........................................... ................................ 43 Sentencia s'itc# .....................
7.
Funciones )!.................... ............................... ...................... .................................................... ......................................... 46
8.
............................... ...................... .................................................... ......................................... 54 Funciones 2!....................
9.
Punteros )!..................... ............................... ..................... ...................... ........................................... ................................ 56
10.
Sentencias goto" *rea+" continue.............................................61
1. Variables y Tipos de Datos.
¿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 !ue va a utili"ar nuestro pro#rama. $n C para poder utili"ar una variable primeramente %ay !ue declararla si#uiendo la si#uiente sinta&is' tipo nombre_variable tipo nombre_variable [=valor] [=valor];;
(o !ue va entre corc%etes es por!ue es opcional es decir) las variables se pueden iniciali"ar * no al al declararlas. declararlas. $+emplo de variable declarada' int i, $+emplo de variable declarada e iniciali"ada'
int i-, $n una misma lnea se puede declarar m/s de una variable si#uiendo el si#uiente formato' tipo nombre0variable1)nombre0 nombre0variable1)nombre0variable)...., variable)...., 2ay !ue tener en cuenta !ue la lnea tiene !ue acabar en punto y coma. $l tipo de datos es obli#atorio ponerlo y le dice al compilador cuantas celdillas de memoria tiene !ue reservar para almacenar el valor de la variable. (os tipos de datos pueden variar de un compilador a otro) vamos a ver los tipos de datos !ue podemos usar con nuestro compilador CC3. (os tipos de datos b/sicos !ue utili"a nuestro compilador son los si#uientes'
3in embar#o el compilador CC3 también admite los si#uientes tipos tipos de datos definidos definidos en el est/ndar C y !ue son los !ue normalmente se utili"an a la %ora de pro#ramar'
4odos los tipos e&cepto float son por defecto sin si#no) aun!ue pueden llevar el 4odos especificador unsi#ned * si#ned y su ran#o ran#o de valores ser/ el !ue corresponda corresponda a su tipo b/sico. $stos son los tipos b/sicos) también est/n los tipos de datos compuestos como $numeraciones) $structuras y 5niones !ue est/n formados por una combinaci*n de los b/sicos y !ue los veremos m/s adelante. $l nombre de la variable no puede ser una palabra clave 6reservada por el compilador para reali"ar unas funciones determinadas y los caracteres !ue podemos utili"ar son las letras' a7" y A78 6 9o+o: la ; o < no est/ permitida=) los n>meros' 7@ y el smbolo de subrayado 0. Adem/s %ay !ue tener en cuenta !ue el primer car/cter no puede ser un n>mero. ¿*nde se declaran las variables?
int i-, $n una misma lnea se puede declarar m/s de una variable si#uiendo el si#uiente formato' tipo nombre0variable1)nombre0 nombre0variable1)nombre0variable)...., variable)...., 2ay !ue tener en cuenta !ue la lnea tiene !ue acabar en punto y coma. $l tipo de datos es obli#atorio ponerlo y le dice al compilador cuantas celdillas de memoria tiene !ue reservar para almacenar el valor de la variable. (os tipos de datos pueden variar de un compilador a otro) vamos a ver los tipos de datos !ue podemos usar con nuestro compilador CC3. (os tipos de datos b/sicos !ue utili"a nuestro compilador son los si#uientes'
3in embar#o el compilador CC3 también admite los si#uientes tipos tipos de datos definidos definidos en el est/ndar C y !ue son los !ue normalmente se utili"an a la %ora de pro#ramar'
4odos los tipos e&cepto float son por defecto sin si#no) aun!ue pueden llevar el 4odos especificador unsi#ned * si#ned y su ran#o ran#o de valores ser/ el !ue corresponda corresponda a su tipo b/sico. $stos son los tipos b/sicos) también est/n los tipos de datos compuestos como $numeraciones) $structuras y 5niones !ue est/n formados por una combinaci*n de los b/sicos y !ue los veremos m/s adelante. $l nombre de la variable no puede ser una palabra clave 6reservada por el compilador para reali"ar unas funciones determinadas y los caracteres !ue podemos utili"ar son las letras' a7" y A78 6 9o+o: la ; o < no est/ permitida=) los n>meros' 7@ y el smbolo de subrayado 0. Adem/s %ay !ue tener en cuenta !ue el primer car/cter no puede ser un n>mero. ¿*nde se declaran las variables?
(as variables se#>n el lu#ar en !ue las declaremos pueden ser de dos tipos' #lobales o locales. (a variables #lobales se declaran fuera de las funciones y pueden ser utili"adas en cual!uier parte del pro#rama y se destruyen al finali"ar éste. (as variables locales se declaran en la funci*n en !ue van a ser utili"adas. 3*lo e&isten dentro de la funci*n en !ue se declara y se destruye al finali"ar dic%a funci*n. 3i una funci*n va a usar ar#umentos 6A4B3=) entonces debe declarar las variables !ue van a aceptar los valores de esos ar#umentos. $stas variables son los par/metros formales de la funci*n. 3e comportan como cual!uier otra variable local de la funci*n) cre/ndose al entrar en la funci*n y destruyéndose al salir. Cuando veamos el tema de las funciones veremos e+emplos de estas variables. •
•
ueno ya est/ bien de teora vamos %acer un e+emplo donde vamos a declarar y a usar varios tipos de variables'
$ste pro#rama #enerar/ la si#uiente salida'
Comentario del programa: •
•
•
•
•
•
•
•
$l compilador utili"a D bits para representar los n>meros enteros sin si#no con lo cual podemos representar desde el %asta el !ue corresponde en binario al n>mero' 11111111. Por lo !ue al asi#narle a la variable el valor E el compilador no #enerar/ un error pero el dato #uardado ser/ err*neo) nos mostrar/ !ue es el si#uiente valor a en binario. Para los n>meros enteros con si#no también se utili"an D bits pero el >ltimo bit se reserva para el si#no) con lo !ue se podr/n representar los n>meros desde' 71F al 1F. $l tipo s%ort se utili"ar/ para las variables de un bit y tendr/n como valor * 1. Para los n>meros tipo lon# int se reservan 1E bits sin si#no con lo !ue su ran#o va de a EG Para el tipo si#ned lon# se reservan también 1E bits pero se utili"a uno para el si#no) por lo !ue se tiene un ran#o !ue va desde 7GFEF a GFEF. $l tipo float define un n>mero de G bits en punto flotante. y con el podremos representar los n>meros reales. $l tipo c%ar se utili"a para almacenar los caracteres) utili"a D bits sin si#no suficientes para representar los E caracteres del c*di#o A3CI I. (os smbolos H) Hlu) Hld) Hc le indica a la funci*n printf en !ue formato tiene !ue representar el n>mero. $n la ayuda del compilador vienen los diferentes especificadores !ue %ay para los diferentes tipos de datos. A lo lar#o de los si#uientes e+emplos se ir/n mostrando al#unos m/s.
CONSIDERACIONES: 2ay !ue intentar siempre utili"ar el tipo de dato !ue menos
memoria ocupe dentro de los valores !ue pueda utili"ar la variable. 3i abusamos de los tipos #randes para almacenar valores pe!ue;os nos !uedaremos sin memoria y en los pro#ramas #randes es un dato !ue tenemos !ue tener en cuenta. Nota: en los e+emplos !ue ten#an poco c*di#o fuente como este y para !ue el formato
de te&to sal#a con los mismos colores !ue utili"a el compilador utili"aré im/#enes para mostrar el c*di#o y en la secci*n de descar#as iré incluyendo los e+emplos del curso para !ue todo el !ue no !uiera teclearlos a mano se los pueda descar#ar. Btra cosa no incluiré el circuito en Proteus ya !ue es el mismo para todos los e+emplos a e&cepci*n de !ue en al#unos e+emplos pueda ir cambiando el tipo de PIC.
1.
Constantes.
ntes de empe"ar con el tema de las constantes voy a comentar val#a la redundancia la forma de poner comentarios a nuestros pro#ramas. 2ay dos formas de poner comentarios en C' Poniendo doble barra 6la !ue %ay encima del F=) esta forma es pr/ctica para comentar una lnea. $+emplo' •
$ste te&to es un comentario. y este otro también. la otra forma es meter el te&to a comentar dentro de estos smbolos J mi comentarioJ. (a venta+a de este sistema es !ue podemos comentar blo!ues de te&tos enteros. $+emplo' •
JMi comentario empie"a a!u..... mas comentarios .. y termina a!u J $l comentar nuestro c*di#o es una buena costumbre !ue no debemos pasar por alto) ya !ue si pasado un tiempo !ueremos volver a un pro#rama y modificar al#una parte de él ayuda muc%o el !ue su c*di#o esté comentado. Btra forma en la !ue se utili"an los comentarios es a la %ora de depurar c*di#o) en ve" de estar borrando y escribiendo tro"os de c*di#o !ue no funcionan correctamente los comentamos) de est/ forma el compilador no los tratar/ como c*di#o fuente y podremos reali"ar a+ustes y pruebas de una manera m/s f/cil. Muc%as veces también vemos !ue revisando c*di#o !ue %an %ec%o otras personas %ay partes del c*di#o !ue est/n comentadas esto es para %acerlo mas funcional) es decir) por poner un e+emplo) si utili"as el PIC 1EKDFF des comenta esta parte y si utili"as otro PIC lo de+as comentado) de esta manera comentando o descomentando unas cuantas lneas podemos utili"ar el pro#rama en varias situaciones. ueno) todo esto para el !ue ten#a una idea de pro#ramaci*n se#uro !ue ya lo sabe) pero como di+e al principio voy %a intentar !ue este curso le sirva también al !ue no ten#a ni idea de pro#ramaci*n aun!ue) en este caso) %ay !ue decir también si se es %onesto) !ue aprender un len#ua+e de pro#ramaci*n al i#ual !ue aprender un idioma nuevo supone un esfuer"o considerable y no vasta con leerse un libro de C y decir 9ya soy un pro#ramador de C:) ba+o mi modesta opini*n lo !ue %ay !ue %acer es practicar muc%o) es decir teclear muc%o c*di#o compilarlo y comprobar !ue funciona como nosotros !ueremos !ue lo %a#a) al principio cometeremos muc%os 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 al#o no funcione. (a constancia y la perseverancia son las claves del é&ito para conse#uir cual!uier ob+etivo) no solo el aprender a pro#ramar PIC en C. L ya est/ bien por!ue menudo rollo estoy soltando) as !ue vamos a empe"ar con lo !ue era el tema de este capitulo' las constantes. (as constantes se refieren a valores fi+os !ue no se pueden alterar por medio del pro#rama. Pueden definirse constantes de cual!uiera de los tipos de datos simples !ue %emos
visto. 3e declaran colocando el modificador const delante del tipo de datos. $+em' cont int MIIMB-1)I4$RNA(B-1,
$sto definir/ dos constantes MIIMB con el valor de 1 e I4$RNA(B con el valor de 1. Btra forma de definir constantes es usando la directiva de compilaci*n Odefine. $+em. !de"ine MAIMB G
$sta orden se e+ecuta de la si#uiente forma' en la fase de compilaci*n al e+ecutar Odefine el compilador sustituye cada operaci*n de la primera cadena de caracteres por la se#unda) MAIMB por el valor G adem/s) no se permite asi#nar nin#>n valor a esa constante. $s decir si pusiéramos' Odefine MAIMB - G Al compilar tendramos un error. Nota: (a declaraci*n Odefine no acaba en ,
4ambién podemos tener en nuestro pro#rama Constantes de cadena' una cadena de te&to es una secuencia de caracteres encerrados entre dobles comillas. 3e usa para funciones de entrada y salida est/ndar) como funci*n de entrada y salida de te&to estamos utili"ando la funci*n printf !ue esta definida dentro de7 Ouse rsG) pero ya veremos !ue el compilador CC3 proporciona un n>mero considerable de funciones listas para usarse y !ue nos sirven para comunicarnos con el dispositivo de entrada y salida R37G. 2emos dic%o !ue podemos definir constantes pr/cticamente de cual!uier tipo de dato) pero CC3 nos permite también representar esas constantes en diferentes sistemas de numeraci*n como %e&adecimal) binario) octal) decimal y adem/s definir también constantes de caracteres especiales !ue el compilador utili"ar/ para reali"ar acciones concretas. e los sistemas de numeraci*n permitidos los !ue m/s se usan son los si#uientes'
Decimal
$+emplo' Hexadecimal empie"an
por &
$+emplo' &A Binario empie"an
$+emplo'
por b
b111 $ste >ltimo formato es muy >til) por e+emplo el P IC dispone de unos re#istros !ue sirven para confi#urar los puertos del PIC como entradas de datos o salida de datos) por defecto vienen confi#urados como entradas y si !uiero utili"ar al#>n pin como salida por!ue !uiero utili"arlo para encender un ($ o lo !ue sea) ten#o !ue poner a cero dic%o re#istro. $n el formato binario se ve f/cilmente !ue valores se le va asi#nar al re#istro) teniendo en cuenta !ue los re#istros empie"an por . Como siempre vamos %acer un e+emplo para ver si nuestro compilador se tra#a todo lo !ue %e dic%o'
ien si todo va bien obtendremos la si#uiente salida'
Comentario: Como di+e en la introducci*n de este curso la finalidad es aprender a
pro#ramar PIC en len#ua+e C eso conlleva saber el len#ua+e C) !ue se#uiremos viendo en esta parte del curso) pero también el saber utili"ar los recursos y funcionalidades !ue nos ofrecen los PIC como por e+emplo saber pro#ramar sus contadores) como enviar datos a un (C) el utili"ar los conversores A) etc. Para ello voy a iniciar pr*&imamente un se#undo artculo donde empe"aremos a estudiar e+emplos pr/cticos de los PIC. 2.
Depuración de programas con Proteus y CCS
$n la >ltima pr/ctica !ue %emos visto 6el #o del $%R& como contador = vimos !ue el entorno de Proteus nos proporciona una ventana de visuali"aci*n del estado de los re#istro 3KR de nuestro PIC ) muy >til cuando estamos depurando nuestro pro#rama) pero Proteus nos proporciona m/s ventanas para ver el estado de los re#istros de nuestro PIC !ue podemos acceder a ellas por medio del men> ebu# 77 PIC7CP5 cuando estamos e+ecutando nuestro pro#rama en el modo de simulaci*n paso a paso * cuando %emos pulsado el bot*n de pausa) una vista condensada de todas esas ventanas la tenemos en la fi#ura de aba+o'
Como vemos aparte de poder ver el estado de los re#istros 3KR del PIC podemos ver el estado de la memoria $PRBM del PIC) $l contenido de la memoria de pro#rama 6donde se encuentra #rabado de forma permanente nuestro pro#rama =) el estado de PI(A 6>til cuando se traba+a con interrupciones y funciones=) otra ventana nos muestra el estado de la memoria RAM reservada a los datos * re#istros de prop*sito #eneral 6SPR= en formato %e&adecimal y otra donde podemos ver el estado de las variables !ue tenemos activas en ese momento) recordar !ue si utili"amos variables locales por e+emplo dentro de una funci*n) est/s se destruir/n al salir de la funci*n. Pero todo esto como %e dic%o lo tenemos cuando estamos e+ecutando nuestro pro#rama en el modo paso a paso * tenemos nuestro pro#rama en pausa.
3i estamos en modo Run e intentamos acceder a estas ventanas vemos !ue est/n des%abilitadas'
¿Qué otro sistema tenemos para depurar nuestros pro#ramas? Pues bien una manera !ue siempre podemos utili"ar es utili"ar la funci*n printf como %erramienta de
depuraci*n) es decir) ponemos la funci*n printf en determinadas partes del pro#rama donde !ueramos saber el estado de una o varias variables y por medio de la terminal podemos saber el valor !ue van tomando) una ve" comprobado !ue nuestro pro#rama funciona como nosotros !ueremos borramos las funciones printf !ue %ayamos introducido con prop*sitos de depuraci*n. Pero Proteus nos proporciona otro método para ver el estado de las variables cuando estamos e+ecutando nuestro pro#rama ya sea en modo Run * en modo paso) es la ventana Tatc% TindoU y podemos acceder a ella por medio del men> ebu# 77 Tatc% TindoUs. Namos a ver c*mo podemos utili"arla. Para ello compilaremos el si#uiente e+emplo' 01.#include <16F84A.h> 02.#use delay(clock=4000000) 03.#fuses !"$!"%&'! 04.#use *232(+A$=,600" +-!*=8" A-!=%" /-!=-%+4" =-%+) 0.in 5 06.in y 07.oid 9ain() : 08.5"y=0 0,.;hile(!$) 10.: 11.if (5<=,) 12.: 13.5 14.delay9s(00) 1.?@inf(5 = Bd.C@"5) 16. 17.D 18.else 1,.: 20.if (y<=,) 21.: 22.y 23.delay9s(00) 24.?@inf(y = Bd.C@"y) 2.D 26.D 27.D 28.D Es un programa que lo único que hace es incrementar la variable X de 0 a 10 y después hace lo mismo con la variable Y, pero es suficiente para ver cómo utiliar la ventana !atch !indo"s para ver el valor que van tomando las variables X e Y#
Primeramente compilamos el e+emplo y después dentro del I$ del compilador %acemos clic en el icono 3ymbol Map se#>n se muestra en la fi#ura de aba+o'
$sto %ar/ !ue nos apare"ca el arc%ivo 3ymbol Map en modo lectura) en este arc%ivo podemos ver en !ué posici*n de memoria se #uardar/n las diferentes variables !ue
ten#amos declaradas en nuestro pro#rama) este arc%ivo se actuali"ar/ en cada compilaci*n !ue %a#amos.
Como vemos en la fi#ura de arriba las variable e L tienen asi#nadas las direcciones de memoria &11 y &1 en la memoria RAM de prop*sito #eneral 6SPR=) !ue como ya sabemos es la !ue el pro#ramador dispone para almacenar los valores de sus variables. ien) una ve" anotadas estas direcciones volvemos al entorno de Proteus y abrimos la ventana Tatc% TindoUs) dentro de ella %acemos clic con el bot*n derec%o del rat*n y seleccionamos Add Items 6y Address=V ) se#>n se muestra en la fi#ura de aba+o'
os aparecer/ una nueva ventana donde iremos a;adiendo las variables con su direcci*n correspondiente'
5na ve" a;adidas las variables podemos ver el valor !ue van tomando mientras e+ecutamos nuestro pro#rama en la ventana Tatc% TindoUs) se#>n se muestra en la fi#ura de aba+o'
Pero tenemos a>n mas opcciones) por e+emplo podemos establecer condiciones para ello %acemos clic en la variable con el bot*n derec%o y seleccionamos Tatc%point ConditionV os aparecer/ la ventana !ue se muestra aba+o'
Por e+emplo yo la %e confi#urado para !ue cuando la variable sea i#ual a cinco se pare la simulaci*n) pero admite m/s condiciones solo %ay !ue ponerse y e&perimentar con las diferentes opciones !ue tenemos) también decir !ue podemos %acer !ue la ventana Tatc% TindoUs nos muestre los re#istros 3KR !ue nos interesan +unto con las variables !ue nosotros %emos declarado) en fin muc%as posibilidades de depuraci*n. $l conocer estas %erramientas nos puede facilitar muc%o el aprendi"a+e por!ue vemos la secuencia real !ue si#ue nuestro pro#rama) !ue al#unas veces puede !ue no coincida con nuestra l*#ica de funcionamiento del pro#rama. Como siempre espero vuestros comentarios y os a#radecera !ue las pre#untas !ue ten#/is sobre estos temas) pues las %a#/is directamente en el foro !ue para eso est/. 3.
Funciones de Entrada / Salida serie RS232 3.1. La función printf!.
Aun!ue no %emos visto el tema de las funciones todava) pero ya !ue estamos utili"ando esta funci*n muy a menudo) vamos a ver al#una de las posibilidades !ue nos ofrece. $l !ue ten#a conocimientos del len#ua+e C sabr/ !ue para utili"ar esta funci*n !ue pertenece al est/ndar A3I de C %ay !ue incluir previamente el arc%ivo de cabecera Oinclude Wstdio.%) pero esto con el compilador PCT de CC3 no funciona) en este compilador esta funci*n est/ definida en la directiva'
!#e RS'(')*A+D=,-&&.*I$S=/.0ARI$1=N.2%I$=0IN_*3.RC4=0IN_*'5
$sto !uiere decir !ue cada ve" !ue !ueramos utili"ar la funci*n printf tenemos !ue %aber incluido previamente esta directiva) !ue posibilita la comunicaci*n del PIC con otro dispositivo utili"ando el protocolo de comunicaci*n serie R3G) adem/s de la funci*n printf esta directiva permite el uso de otras funciones para la entrada y salida de datos serie como' #etc) #etc%ar) #ets) puts y Xb%it !ue iremos viendo poco a poco) pero
la m/s importante para la salida de datos sin duda es printf) por!ue nos permite formatear la salida de esos datos de la forma !ue nosotros !ueramos.
Como vemos la directiva Ouse R3G admite una serie de par/metros !ue son los !ue van entre paréntesis separados por comas) estos son los si#uientes'
•
•
•
•
•
*A+D con este par/metro establecemos la velocidad en baudios a la !ue
!ueremos !ue se transmitan los datos por el puerto serie) @E es lo normal. *I$S n>mero de bits !ue utili"aremos en la transmisi*n) el est/ndar establece !ue pueden ser D * @) para la comunicaci*n con microcontroladores con D son suficientes. 0ARI$1 nos permite utili"ar un bit de paridad para la comprobaci*n de errores) est/ opci*n la de+amos a o. 2%I$ est/ opci*n nos confi#ura por!ue patilla del PIC saldr/n los datos) est/ opci*n +unto con la si#uiente s !ue la tendremos !ue cambiar a nuestras necesidades. RC4 nos confi#ura por!ue patilla del PIC se recibir/n los datos. $n el e+emplo) los datos se transmiten por el PI R1 y se reciben por R.
(a forma de %acer la llamada a la funci*n printf es la si#uiente'
print")Nombre 6#nci7n. Cadena de caractere . valore5;
Como vemos la funci*n printf también admite par/metros !ue podremos utili"ar para formatear el te&to de salida. Namos a ver cu/les son'
$l primero es opcional y es el nombre de una funci*n) si no lo ponemos los datos se transmitir/n va R3G a través de los pines !ue %ayamos confi#urado en la directiva Ouse R3G. $l se#undo par/metro es una cadena de caracteres encerrada entre comillas dobles. L el tercero son datos o nombres de variables cuyo valor !ueremos !ue se muestren. Namos a ver todo esto con e+emplos !ue es como me+or se ven las cosas'
1º Ejemplo:
#include <16F877.h> #use delay(clock=4000000) #include
#use *232(+A$=,600"+-!*=8"A-!=%"/-!=-%+1"=-%+2) oid 9ain() : in i1=, lcdini() funcin de inicialiGacin del E /os@a9os una cadena en la e@9inal ?@inf(so es una cadenaC@) /os@a9os una cadena de e5o Huno con el alo@ de una a@iaIle en la e@9inal. ?@inf(l alo@ de la a@iaIle i1 esJ Bd"i1) /os@a9os el alo@ de la a@iaIle ?o@ el E ?@inf (lcd?uc"l alo@ de i1 esJ Bd"i1) D
Comentario: $n este primer e+emplo vamos a ver el uso de la funci*n printf utili"ando diferentes par/metros. Como vamos a utili"ar la librera !ue incluye el compilador para el mane+o de un (C tenemos !ue incluir la directiva'
!incl#de 89CDC
eclaramos una variable i1 de tipo entero !ue nos va a servir para mostrar su valor en la terminal y en un (C. Cuando utilicemos la librera (C.C y antes de utili"ar cual!uier otra funci*n incluida en la librera tenemos !ue llamar a la si#uiente funci*n !ue sirve para iniciali"ar el (C.
lcd_init)5;
$n la primera llamada a la funci*n printf como par/metros solo incluimos una cadena de caracteres constante !ue termina en 6Yr=) esa barra invertida +unto con la r se le llama secuencia de escape y le est/ diciendo al compilador !ue al final de la cadena introdu"ca un retorno de carro 6tecla enter=. (as secuencias de escape se utili"an para representar caracteres o acciones especiales.
print")
$n la tabla de aba+o se muestran las secuencias de escape !ue tenemos disponibles para utili"ar con la funci*n printf'
$r
%etorno de carro
$t
&abulador
$'
(omilla simple
$)
(omillas dobles
$$
*arra invertida
$+
-mbolo de interrogación
$0
(aracter nulo
$.
-mbolo &anto por ciento
$b
%etroceder un caracter
Namos con la se#unda llamada a la funci*n'
print")d<.i35;
$n este caso tampoco est/ definido el primer par/metro) por tanto) al i#ual !ue en la primera llamada a la funci*n) los datos se enviaran por el puerto serie al pin !ue %ayamos definido en la directiva Ouse R3G) en esta llamada vemos !ue tenemos la cadena de caracteres limitada por las comillas dobles y separado por una coma) como tercer par/metro el nombre de la variable i1 !ue %abamos declarado previamente. $n la cadena de caracteres vemos !ue aparece el car/cter de H se#uido de la letra d) ese es un car/cter especial para la funci*n y lo !ue le indica a la funci*n es !ue en esa posici*n muestre el valor de la variable i1) la d le indica a la funci*n !ue represente ese valor en formato de n>mero entero. Podemos representar el valor de la variable en diferentes formatos se#>n se muestra en la tabla de aba+o'
c
(aracter
s
(adena ó caracter
u
Entero sin signo
d
Entero con signo
/u Entero largo sin signo /d Entero largo con signo
Entero eadecimal 2minúsculas3
X
Entero eadecimal 2mayúsculas3
/
Entero largo eadecimal 2minúsculas3
/X Entero largo eadecimal 2mayúsculas3 f
4úmero real en coma flotante con truncado
g
4úmero real en coma flotante con redondeo
e
4úmero real en formato eponencial
"
Entero sin signo con decimales insertados# Especifica dos números para n# /a 15 cifra indica el total y la 65 el número de decimales
3i !uisiésemos mostrar el valor de m/s de una variable lo %aramos de la si#uiente forma'
printf6$l valor i1 es' >d el de i' >d y el de iG' >d)i1)i)iG=,
Namos con la >ltima llamada a la funci*n del 1Z e+emplo'
print" )lcd_p#tc.d<.i35;
$n esta llamada %emos incluido el primer par/metro y %emos puesto el nombre de la funci*n lcd_p#tc) est/ funci*n est/ definida en la librera (C.C !ue trae el compilador para ayuda del mane+o de los dispositivos (C y !ue %emos incluido en nuestro pro#rama por medio de la directiva !incl#de 8lcdc) vemos !ue la librera est/ encerrada entre los smbolos de W esto le indica al compilador !ue bus!ue la librera en el directorio en !ue se instalo el compilador) si copi/ramos esa librera en otro directorio tendramos !ue indicarle la ruta completa) pero esta ve" encerrada entre comillas dobles.
$+emplo'
Oinclude [C'Y$+emplos de CYlcd.c\
Pues bien a%ora la funci*n printf no enviar/ los datos al puerto serie) sino a la funci*n lcd0puct !ue ser/ la encar#ada de envi/rselos al (C) esta funci*n por defecto enva los datos al puerto del PIC) pero accediendo a la librera se puede modificar el puerto f/cilmente.
A!u tenéis un video demostrativo del e+emplo'
2º Ejemplo
Salida del programa:
Comentario del programa: $l especificador de formato H& indica al sistema !ue escriba en %e&adecimal 6base 1E= el valor sustituido. $l e+emplo también escribe el car/cter ]A]) apoy/ndose en cuatro formas distintas de representaciones inciales. $n todos los casos se almacenar/ el mismo valor numérico) pero son diferentes las representaciones usadas. $l car/cter 6A= sale en la terminal en una lnea diferente cada ve" !ue se imprime) eso es debido a la secuencia de escape 6Yr= utili"ada. Bbservar !ue el e+emplo se %a %ec%o sobre el PIC 1EfD^ !ue no dispone de una 53AR4 %ardUare para la comunicaci*n serie ) pero sin embar#o el pro#rama se %a e+ecutado correctamente) eso es debido a !ue la comunicaci*n serie se %a establecido por softUare por medio de las libreras implementadas en el compilador PCT. •
•
•
•
3.2.
Funciones getc!" getc#! y getc#ar!.
Namos a continuar con las funciones disponibles en CC3 para la entrada y salida de datos a través del puerto serie R37G. 2asta a%ora solo %emos visto !ue con la funci*n printf6=) podemos enviar datos formateados a través del pin !ue %ayamos seleccionado en la directiva' Ouse R3G6A5-@E)I43-D)PARI4L-)2%I$=0IN_D3)RC4=0IN_D'= $n este caso los datos saldr/n por el pin R1 del PIC. Pero ¿de !ue funciones disponemos para recibir datos desde fuera %acia nuestro PIC?. $l !ue %aya pro#ramado en C ec%ar/ de menos la funci*n scanf6= definida en la librera stdio.% y perteneciente al est/ndar A3I C. Pero des#raciadamente esa funci*n tampoco est/ disponible en CC3. Pero tampoco %ay por !ué preocuparse muc%o) por!ue disponemos de otras. $n este caso vamos a ver las funciones' #etc6=) #etc%6= y #etc%ar6=. (as tres %acen lo mismo por lo !ue podemos usarlas indistintamente. $stas funciones esperan un car/cter por la patilla del PIC !ue %ayamos definido en la directiva Ouse R3G con el par/metro RCN. $n el caso del e+emplo de arriba) los datos ser/n recibidos por el pin R del PIC. Pues vamos a ver nuestro primer e+emplo acerca del uso de estas funciones'
Comentario: $l e+emplo lo !ue %ace es mostrar el valor de la tecla !ue pulsemos en el teclado y su e!uivalente en c*di#o ASCII Namos a e&plicar su funcionamiento paso a paso'
•
•
•
•
•
•
Primeramente) como siempre) incluimos por medio de la directiva Oinclude el arc%ivo de cabecera del PIC !ue vamos a utili"ar) en este caso el PIC1EKDFF. Por medio de Ouse delay le decimos al compilador la frecuencia de relo+ !ue vamos a utili"ar en nuestro circuito. Confi#uramos los par/metro de la directiva Ouse R3G) fi+aros !ue MI4-PI0 y !ue RCN-PI01. Con lo cual los datos saldr/n del PIC por el pin R y entrar/n por el pin R1. entro de la funci*n principal main6=) escribimos lo !ue !ueremos !ue %a#a nuestro pro#rama. (as instrucciones siempre empe"ar/n a e+ecutarse una a una a partir de esta funci*n y de arriba %acia aba+o. (o primero !ue %acemos es declarar una variable de tipo c%ar donde almacenaremos el valor de la tecla !ue pulsemos en el teclado. espués se nos mostrar/ un mensa+e en la terminal invit/ndonos a !ue introdu"camos un car/cter.
print")
•
espués se e+ecutar/ la sentencia'
c@=getc@)5
Que esperar/ %asta !ue pulsemos una tecla y almacenar/ su valor en la variable c%. •
(a si#uiente instrucci*n'
print")c tiene #n valor ASCII decimal de >dr<[email protected]@5;
muestra el valor del car/cter y su e!uivalente en c*di#o A3CII •
espués se repite el proceso dos veces m/s) pero esta ve" utili"ando las funciones #etc6= y #etc%ar6=
Al utili"ar solo la variable c%) el valor de la nueva tecla pulsada sobrescribir/ el valor anterior de la variable. (a salida de nuestro pro#rama ser/ el si#uiente'
ien) %ay !ue decir !ue el pro#rama finali"ar/ al lle#ar a la >ltima sentencia incluida en la funci*n main6=. Para !ue el pro#rama termine cuando nosotros !ueramos tenemos !ue incluir como mnimo un bucle y establecer una condici*n para !ue podamos salir de él) vamos a ver esto con otro e+emplo'
$n este e+emplo se ir/n mostrando en la terminal las teclas !ue vayamos pulsando por el teclado %asta !ue pulsemos la tecla _n` momento en el cual finali"ar/ el pro#rama.
3.3.
Funciones gets! y puts!.
5n par de funciones mas !ue se pueden utili"ar en la entrada y salida de datos serie R3G son las funciones #ets6= y puts6=.
get)tring5' esta funci*n lee los caracteres !ue se introducen por el teclado %asta !ue
encuentra un retorno de carro 6tecla $nter=. $l pin asi#nado para la lectura de los caracteres es el !ue %ayamos confi#urado en RCN. $n el e+emplo de aba+o el pin R.
p#t)tring5: esta funci*n enva la cadena de te&to contenida dentro de los paréntesis
al pin !ue %ayamos confi#urado en el par/metro MI4 de la directiva Ouse R3G) en el e+emplo de aba+o el pinR^. 5na ve" enviada la cadena a;ade un retorno de carro.
!#e RS'(')*A+D=,-&&. *I$S=/. 0ARI$1=N. 2%I$=0IN_D. RC4=0IN_DB5
Namos a ver un e+emplo sencillo !ue utilice estas dos funciones'
Comentario
$n este e+emplo se %a declarado un tipo de dato !ue todava no %emos visto) un array de caracteres'
c@ar nombre[,];
Aun!ue veremos los tipos de datos compuestos m/s adelante) podemos adelantar !ue un array es un con+unto 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. (os elementos del array se enumeran empe"ando por el ) 6es una caracterstica del len#ua+e C=. $n el e+emplo de arriba se %a declarado un array de caracteres 6tipo c%ar= y el n>mero m/&imo de elementos !ue podemos almacenar en el es de @. 3us elementos estar/n numerados del al D. L podemos acceder a ellos de la si#uiente forma'
valor = nombre[&];
= [];
valor = nombre[/];
$l e+emplo lo >nico !ue %ace es enviar un mensa+e a la terminal diciéndonos !ue introdu"camos nuestro nombre 6puede ser también una passUord o lo !ue !ueramos=. Cuando introdu"camos el nombre y pulsemos la tecla $nter) la cadena de caracteres ser/ #uardada en el array !ue %emos declarado previamente y lue#o con la primera funci*n prinf6= mostramos el valor de la cadena de te&to #uardada en el array) con la se#unda funci*n prinf6= mostramos el tercer car/cter del nombre introducido 6nombre=. $ste ser/ la salida de nuestro pro#rama'
3i intentamos introducir una cadena m/s lar#a) el valor se mostrar/ en la terminal pero truncado.
Por e+emplo si intentamos introducir la cadena'
%icrocontroladore 0IC
(a terminal nos mostrar/'
%icrocont
Como %e dic%o antes los elementos del array se almacenan en posiciones consecutivas de la memoria RAM del PIC. $sto lo #estiona autom/ticamente el compilador) lo mismo !ue cuando %aces un pro#rama en C * en otro len#ua+e de alto nivel para un PC de escritorio el pro#ramador no est/ preocupado de en !ué posici*n de la memoria RAM se almacenar/n las variables !ue declara. Pero si a pesar de ello !uieres saberlo %aces lo si#uiente'
espués de compilar el e+emplo) te vas al men> compile 3ymbol Map y nos aparecer/ la ventana de aba+o'
onde vemos las posiciones de memoria donde se %an mapeado nuestras variables. Como vemos nuestro array %a ocupado las posiciones de memoria de la &1 a la &@ de los re#istros SPR del PIC 6en total @ bytes=) ya !ue los elementos !ue componen el Array son de tipo c%ar !ue son de un byte 6D bits= cada uno.
3i !ueremos ver los valores !ue va tomando cada uno de los elementos del Array en tiempo de e+ecuci*n. (o podemos %acer por medio de la ventana Tatc% TindoUs en Proteus. 3i no te acuerdas de c*mo se %ace mralo aF#G
L obtendremos lo si#uiente'
Consideraciones:
$n C e&iste el concepto de memoria din/mica. (a memoria din/mica es a!uella !ue se puede reservar y liberar en tiempo de e+ecuci*n) es decir) durante la e+ecuci*n del pro#rama se liberar/ y se asi#nar/ memoria para optimi"ar los recursos de la CP5) para ello se dispone de funciones como malloc6= y free6=. $l compilador CC3 también admite este tipo de funciones) para utili"arlas debemos de incluir el arc%ivo de cabecera stdlibm.%) ya veremos un e+emplo sobre la asi#naci*n de memoria din/mica) si no utili"amos estas funciones la reserva de memoria es est/tica) es decir) si declaramos un array de nueve elementos el compilador le reservar/ memoria conti#ua a dic%o array al compilar el pro#rama en los re#istros de prop*sito #eneral 6SPR=. $sa memoria se reserva cuando el PIC empie"a a e+ecutar su pro#rama y permanece reservada %asta !ue desconectamos el PIC. Con todo esto !uiero decir !ue tenemos !ue tener siempre claro de la memoria RAM de !ue disponemos) se#>n el modelo de microcontrolador !ue utilicemos en nuestro proyecto) si no lo sabes consulta el data s%eet del PIC !ue estés utili"ando. Por e+emplo si en el PIC del e+emplo !ue %emos %ec%o 61EfDFF= en ve" de un array de @ elementos declaramos uno de 1 elementos el compilador nos mostrar/ el si#uiente error al compilar'
L como ya %e dic%o en al#una ocasi*n) utili"a el tipo de datos m/s pe!ue;o posible) en el e+emplo !ue %e puesto %e declarado un array de nueve elementos) para introducir el nombre [Antonio\ !ue tiene seis caracteres) con lo cual estoy desperdiciando dos bytes de memoria RAM) eso en un ordenador de escritorio es insi#nificante pero en un microcontrolador si !ue es importante y puede !ue nos !uedemos sin memoria suficiente para declarar todas las variables de nuestro pro#rama.
4.
$peradores.
$l len#ua+e C dispone de una #ran cantidad de operadores !ue nos sirven para operar con los datos dentro de nuestros pro#ramas) se pueden clasificar en varios apartados' aritméticos) relacionales) de asi#naci*n) de mane+o de un solo bit) etc. Pero lo importante no es saber a !ué #rupo pertenece cada operador) sino en conocer la operaci*n !ue se puede reali"ar con cada uno de ellos. Namos a ver los operadores !ue nos permite utili"ar nuestro compilador CC3.
•
Operadore AritmHtico: permiten la reali"aci*n de operaciones
matem/ticas en nuestros pro#ramas.
Operador
Descripción
7
uma
8
%esta
9
:ultiplicación
;
.
:ódulo 2%esto de una división entera3
77
=ncrementa en uno el valor del operando
88
•
Operadore relacionale: compara dos operandos y devuelve 1
6verdadero= * 6falso= se#>n el resultado de la e&presi*n. 3e utili"an principalmente para elaborar condiciones en las sentencias condicionales e iterativas !ue se ver/n m/s adelante.
Operador
Descripción
>
:enor que
?
:ayor que
>@
:enor o igual que
?@
:ayor o igual que
@@
=gual a
A@
•
Operadore de aignaci7n: permiten asi#nar valores a las variables.
4enemos las si#uientes.
Operador
Descripción
7@
Bsignación de suma# 7@y es lo mismo que @7y
8@
Bsignación de resta# 8@y es lo mismo que @8y
9@
Bsignación de multiplicación# 9@y es lo mismo que @9y
;@
Bsignación de división# ;@y es lo mismo que @;y
.@
Bsignación de resto de división# .@y es lo mismo que @.y
>>@
Bsignación de desplaamiento a la iquierda# >>@y es lo mismo que @>>y
??@
Bsignación de desplaamiento a la derecha# ??@y es lo mismo que @??y
C@
Bsignación de B4< de bits# C@y es lo mismo que @Cy
D@
Bsignación de % de bits# D@y es lo mismo que @Dy
F@
Bsignación de % eclusivo de bits 2X%3# F@y es lo mismo que @Fy
•
Operadore 97gico: Al i#ual !ue los operadores relacionales) éstos
devuelven 1 6verdadero=) 6falso= tras la evaluaci*n de sus operandos. (a tabla si#uiente ilustra estos operadores.
Operador
Descripción
G
4 lógico
CC
Y lógico
DD
lógico
•
Operadore de maneo de bit: $stos operadores permiten actuar sobre
los operandos para modificar un solo bit) los operandos s*lo pueden ser de tipo entero 6incluyendo el tipo c%ar=.
Operador
Descripción
H
4egación de bits 2complemento a 13
C
Y de bits 2B4<3
D
de bits 2%3
F
eclusivo de bits 2X%3
??
>>
•
Operadore para manear p#ntero: $n el len#ua+e C est/ muy difundido
el uso de punteros) este compilador permite su uso y los operadores !ue utili"a para ello son los si#uientes'
Operador
Descripción
C
perador de dirección
9
perador de inderección
8?
Bcceder a los miembros de una estructura por medio de punteros
(os !ue se inician en el mundo de la pro#ramaci*n suelen encontrar complicado el emplear punteros en sus pro#ramas pero) una ve" !ue se entiende el concepto se simplifica y optimi"a muc%o nuestro c*di#o. La di+imos !ue el P IC dispone de unos re#istros de prop*sito #eneral 6SPR= !ue el pro#ramador utili"a para almacenar all sus variables y poder utili"arlas a lo lar#o del pro#rama) pues bien un puntero es otra variable a la cual se le asi#na la direcci*n del re#istro * memoria de otra variable.
(a forma de utili"ar los punteros lo veremos en profundidad m/s adelante) pero a!u tienes un pe!ue;o e+emplo de c*mo utili"arlos.
Eemplo:
int y)", declaraci*n de las variables & e y de tipo entero'
int J&, declaraci*n de la variable puntero & !ue #uardar/ la direcci*n de memoria de una variable de tipo entero.
&-y, a través del operador de direcci*n 6= le asi#no al puntero & la direcci*n de memoria donde est/ #uardada la variable y.
"-J&, a través del operador de inderecci*n 6J= le asi#namos a " el valor de la variable cuya direcci*n est/ almacenada en la variable puntero &.
Nota: como vemos los smbolos de direcci*n 6= e inderecci*n 6J= son los mismos !ue
el A en el mane+o de bits 6= y el operador aritmético de multiplicaci*n) el compilador los diferencia se#>n los operandos !ue le preceden.
•
0recedencia de lo operadore:
Expresiones en orden descendente de precedencia
2epr3 Gepr
Hepr
77epr
epr77
2type3epr
9epr
Cvalue
sieof2type3
epr9epr
epr;epr
epr.epr
epr7epr
epr8epr
epr>>epr
epr??epr
epr>epr
epr>@epr
epr @ @ epr eprG@epr eprCepr eprFepr epr D epr
epr?epr
epr?@epr
88epr
epr 88
eprCC epr epr DD epr epr + eprI epr value @ epr
value7@epr
value8@epr
value9@epr
value;@epr
value.@epr
value??@epr value>>@epr valueC@epr valueF@epr
valueD@epr
epr,epr
(as operaciones con mayor precedencia se reali"an antes !ue las de menor precedencia. 3i en una operaci*n encontramos si#nos del mismo nivel de precedencia) dic%a operaci*n se reali"a de i"!uierda a derec%a. Eemplo:
aJbcd7e (as operaciones se reali"ar/n en el si#uiente orden' 1. aJb resultado - & . cd resultado - y G. &y resultado - " ^. "7e Nota: $s aconse+able el uso de paréntesis para evitar errores en la precedencia de
operadores) adem/s el c*di#o fuente !ueda m/s le#ible. Eemplo:
aJ6bc=d $n este caso el orden en reali"arse las operaciones ser/ el si#uiente' 1. bc resultado - & . aJ& resultado - y G. yd si"eof6type=77 nos da el tama;o en bytes del tipo de dato * variable !ue le pon#amos entre los paréntesis. Para conocer bien los resultados !ue se obtienen a l utili"ar cada uno de los operadores) lo me+or es practicar con ellos. Namos a ver un e+emplo donde se muestra el uso de al#unos de ellos'
Comentario del programa:
$n este e+emplo introducimos unos valores por el teclado del ordenador y se los enviamos al pic via serie por el dispositivo R37G) lue#o reali"aremos diferentes operaciones con ellos y mostraremos el resultado en la 4erminal) pero %ay !ue tener en cuenta !ue !ue esos valores !ue introducimos por el teclado son caracteres y por tanto no se los podemos asi#nar directamente a una variable de tipo entero para operar con ellos) primero tenemos !ue convertirlos. C33 nos proporciona las si#uientes funciones para ello'
7 atoi6cadena= 77 devuelve un valor entero de D bits de tama;o. 7 atol6cadena= 77 devuelve un valor entero de 1E bits 7 atoiG6cadena= 77 devuelve un valor entero de G bits
Para saber el ran#o de valores admisible por cada funci*n repasa los tipo de dato
$stas funciones est/n definidas en el fic%ero de cabecera stdlib.%) por tanto no %ay !ue olvidarse de incluirlo previamente por medio de la directiva' Oinclude Wstdlib.%.
Ki+aros en la instrucci*n de la lnea G1 !ue nos da el tama;o de la variable y'
print")d bKter<.i?eo")K55;
Y&a^ 77 es la secuencia de escape para representar la letra ;. $sto es por!ue el compilador no reconoce los caracteres en castellano.
$abla eF#ivalente de caractere en catellano:
J
$a0
é
$K6
-
$a1
ó
$a6
ú
$aL
M
$bN
O
$P0
Q
$dR
S
$e0
T
$eP
U
$aV
W
$aN
$aK
(a salida del pro#rama para los valores de J=B& e K=- es la si#uiente'
$l c*di#o fuente y el 3 de proteus lo tenéis aF#i
5na precauci*n !ue tenemos !ue tener en cuenta es !ue si utili"amos valores numéricos #randes y un PIC con poca memoria RAM) pronto la a#otaremos. $sto solo es
un e+emplo te*rico del uso de operadores aritméticos) aplicaciones pr/cticas puede tener muc%as) depende de lo !ue !uieras %acer.
5.
Sentencias repetiti%as. 5.1. &ucle '#ile
3on a!uellas !ue e+ecutan un blo!ue de sentencias mientras se cumpla una e&presi*n l*#ica. $ste blo!ue de sentencias !ue se e+ecuta repetidas veces) se denomina bucle) y cada e+ecuci*n se denomina iteraci*n.
e las diferentes sentencias repetitivas !ue %ay vamos a empe"ar con U%ile.
(a sentencia U%ile permite la e+ecuci*n de un blo!ue de sentencias si se eval>a como verdadera una e&presi*n l*#ica. (a e&presi*n l*#ica aparece al principio del blo!ue de sentencias. $n la fi#ura de aba+o se muestra el Pseudoc*di#o) el dia#rama de flu+o y la sinta&is de la sentencia U%ile.
$l Pseudoc*di#o es una forma informal de representar la secuencia del pro#rama) sin tener en cuenta la sinta&is particular del len#ua+e en !ue vayamos a pro#ramar y el dia#rama de flu+o es una representaci*n #r/fica del Pseudoc*di#o.
Cuando vayamos a crear un pro#rama el dibu+ar previamente un dia#rama de flu+o * el Pseudoc*di#o de la secuencia de nuestro pro#rama puede ayudarnos en la tarea de pro#ramaci*n) pero en nin#>n caso es un paso obli#atorio.
$l blo!ue delimitado por las llaves puede reducirse a una sentencia) y en este caso se suprimen las llaves.
(a e&presi*n l*#ica debe estar delimitada por paréntesis.
Cuando el pro#rama lle#a a una sentencia U%ile) si#ue los si#uientes pasos. • • • •
$val>a la e&presi*n. 3i es falsa) continua la e+ecuci*n tras el blo!ue de sentencias. 3i es verdadera entra en el blo!ue de sentencias asociado al U%ile. $+ecuta dic%o blo!ue de sentencias) evaluando de nuevo la e&presi*n y actuando en consecuencia.
3i la primera evaluaci*n resulta falsa) el blo!ue de sentencias no se e+ecuta nunca.
3i la e&presi*n es siempre cierta el bucle es infinito.
Namos con el primer e+emplo'
Comentario
$l e+emplo lo !ue %ace es mostrar en la terminal la tabla de multiplicar del n>mero cuatro) utili"ando un bucle U%ile. Para ello necesitamos declarar una variable au&iliar de tipo entero llamada i3) iniciali"ada con el valor de 1) en cada iteraci*n se comprueba el valor de la variable au&iliar) mientras el valor de i3 sea W-1 la evaluaci*n ser/
verdadera y se e+ecutar/n las instrucciones !ue %ay dentro del blo!ue U%ile) dentro de ese blo!ue tenemos !ue incrementar el valor de i1) de esta manera nos ase#uramos en al#>n momento la salida del bucle) cuando i3 lle#ue a 11 la condici*n ser/ falsa y la secuencia del pro#rama saltar/ a la lnea 1@ finali"ando el pro#rama.
(a salida del pro#rama ser/ el si#uiente'
3i !ueremos !ue el pro#rama este siempre e+ecut/ndose 6lo normal en un pro#rama para Microcontroladores=) %ay !ue colocar un bucle infinito) mira el si#uiente e+emplo'
Comentario tr#e es una constante booleana !ue e!uivale a 1 * verdadero. Por tanto la evaluaci*n
del bucle siempre ser/ cierta y no %abr/ manera del salir de él. $l pro#rama estar/ siempre esperando a !ue pulses una tecla y mostrar/ el valor de la tecla pulsada en la terminal.
5.2.
&ucle for
Bucle for() $n el e+emplo de la tabla de multiplicar utili"amos el bucle U%ile para obtener los die" valores de la tabla) y si record/is necesit/bamos una variable de control !ue tenamos !ue iniciali"ar antes de entrar en el bucle) comprobar el valor de la variable para la continuaci*n en el bucle y la modificaci*n posterior de la variable de control para poder salir del bucle en un momento determinado.
Pues bien) casi siempre !ue se %ace al#o) C proporciona frecuentemente un modo m/s compacto de %acer lo mismo.
$l bucle for permite indicar estos tres elementos en un solo lu#ar) al principio del bucle) facilitando as la obtenci*n de un c*di#o compacto) pero le#ible. Neamos cual es su sinta&is'
$n un bucle for) el paréntesis !ue acompa;a a la palabra reservada "or #eneralmente contiene tres e&presiones' EJprei7n 3; iniciali"a la variable * variables de control del bucle. EJprei7n ';representa la condici*n de continuaci*n en el bucle. EJprei7n (; modifica el valor de las variables de control en cada iteraci*n del bucle. (os puntos y comas !ue separan cada e&presi*n son obli#atorios.
Namos a ver un e+emplo donde se muestran las diferentes posibilidades de !ue disponemos cuando utilicemos el bucle for'
Comentario 3L b#cle "or:
indice-1) iniciali"a a la variable de control del bucle. (a se#unda e&presi*n) indiceW-NA(0MA) representa la condici*n de continuaci*n. Por >ltimo) la tercera e&presi*n) indice) utili"a el operador de incremento para modificar a la variable de control en cada iteraci*n del bucle. (os pasos !ue si#ue la sentencia for son los si#uientes'
1. indice es la variable de control. 3e iniciali"a a 1 . se testea la condici*n de e&presi*n0. G. se e+ecutan las sentencias ^. la variable de control indice se incrementa en uno . si se cumple !ue indiceW-NA(0MA va al paso G. 3i no va al paso E. E. Kinali"ar/ la e+ecuci*n cuando indice-
'L b#cle "or
ucle for con varias variables de control) las variables tienen !ue ir separadas por comas. $n este caso tenemos las variables & e y) aun!ue podemos poner todas las !ue !ueramos) ambas variables son iniciali"adas dentro de la sentencia for.
(L b#cle "or
Cuando el bucle for se escribe sin cuerpo sirve por e+emplo para #enerar retardos) esta posibilidad la utili"aremos poco con este compilador ya !ue incluye funciones especficas de retardo.
L b#cle "or
$l bucle for permite no incluir las e&presiones 1 y G) aun!ue los puntos y comas son obli#atorios ponerlos. $n este caso se aseme+a muc%o a un bucle U%ile.
BL b#cle "or
3e puede crear un bucle infinito por medio de la e&presi*n for6,,=.
Podemos salir de un bucle infinito por medio de la sentencia breaX) cuando se encuentra en cual!uier lu#ar dentro del cuerpo de un bucle da lu#ar a la terminaci*n inmediata de este) en el caso del e+emplo saldremos del bucle cuando pulsemos la letra _v`. (as sentencias de salto las veremos m/s adelante.
La salida del programa será la siguiente:
5.3.
&ucle do('#ile
Bucle do-while() A diferencia de los bucles for y U%ile) !ue anali"an la condici*n del bucle al principio del mismo) el bucle do7U%ile anali"a la condici*n al final del bucle. $sto si#nifica !ue el bucle do7U%ile siempre se e+ecuta al menos una ve". (a forma #eneral del bucle do7 U%ile es la !ue se muestra en la fi#ura de aba+o'
Namos a ver un e+emplo'
Comentario $ste e+emplo pide un valor entre 1 y 1) e+ecut/ndose repetidas veces %asta !ue se introduce un valor entre ambos lmites. Por >ltimo el pro#rama visuali"a el valor ledo.
$ste bucle se e+ecutar/ como mnimo una ve" por!ue el pro#rama no sabe cu/l es la condici*n de continuaci*n %asta !ue se encuentra el U%ile del final del cuerpo del bucle. 3i la condici*n si#ue siendo cierta 6es decir) si el valor ledo est/ fuera del intervalo deseado=) el pro#rama re#resa al principio del bucle do7U%ile y lo e+ecuta de nuevo.
Ki+aros en la orden i" !ue aparece dentro del cuerpo del bucle. $sto se permite por!ue las estructuras de control se pueden anidar unas dentro de otras.
REC+ERDA: $n el bucle U%ile la comprobaci*n de la condici*n de control del bucle se
encuentra al principio) por lo !ue dic%o bucle puede no e+ecutarse nunca, la comprobaci*n en el bucle do7U%ile est/ al final del bucle) por lo !ue al menos se e+ecutar/ una ve".
6.
Sentencias condicionales 6.1. Sentencia if
If Namos a empe"ar las sentencias condicionales) con la m/s simple de todas) la sentencia if. 3i se eval>a como cierta la e&presi*n !ue %ay entre paréntesis al principio de la sentencia if se e+ecuta el blo!ue de sentencias contenido entre las llaves y si se eval>a como falsa la condici*n) el pro#rama se salta ese blo!ue de instrucciones. $n la fi#ura de aba+o tenéis la sinta&is de esta sentencia.
3i s*lo %ay una sentencia se pueden suprimir las llaves) e+emplo'
i" 6&-1=
printf6[3in llaves solo una sentencia asociada al if \=,
6.2.
Sentencia if(else
Sentencia If…Else
Cuando el pro#rama lle#a a una sentencia condicional del tipo If V$lse) primero se eval>a una e&presi*n, si se cumple 6es cierta= se e+ecuta un blo!ue de sentencias y si es falsa se e+ecuta otro blo!ue.
$n la fi#ura de aba+o se muestra la sinta&is de esta sentencia condicional.
$+emplo'
Comentario
$ste e+emplo visuali"a en el display de c/todo com>n) conectado a la puerta del PIC) el [\ si el interruptor conectado a RA est/ abierto y [1\ si est/ cerrado) para ello utili"a la sentencia if7else) dentro de un bucle infinito para !ue el pro#rama esté siempre c%e!ueando el estado de la patilla RA.
$n este e+emplo %emos incluido una directiva nueva !#e "at_io)p#erto5. $sta directiva se utili"a para optimi"ar el c*di#o #enerado por el compilador cuando se utili"an funciones de mane+o de entrada y salida como [ inp#t)pin5\ definidas ya en CC3. 3i no se incluye esta directiva el compilador tomar/ por defecto la directiva !#e tandard_io)A5)!ue %ar/ !ue cada ve" !ue se utilicen estas funciones se repro#rame el pin correspondiente como entrada * salida) lo !ue %ar/ !ue el c*di#o A3M #enerado tras la compilaci*n sea mayor.
Podemos comprobar esto si después de compilar nuestro e+emplo) dentro del I$ de CC3 seleccionamos Compile77 CA3M (ist
Como se ve en la fi#ura la memoria de pro#rama 6RBM= ocupa 1 palabras.
A%ora se pueden %acer las si#uientes pruebas) la primera poner la directiva !#e tandard_io)A5. y la se#unda simplemente !uitar la directiva !#e "at_io)A5 y no poner nada) se#>n se muestra en la fi#ura de aba+o'
Nolvemos a compilar y en ambos casos obtendremos lo si#uiente'
$n ambos casos la memoria RBM utili"ada es de ^ palabras) tres m/s !ue cuando utili"/bamos la directiva Ouse fast0io6A=.
Btras funciones para el mane+o de bits de los puertos de entrada y salida !ue vienen definidas en CC3 y !ue dependen de la directiva OuseJ0io6= son'
• • •
o#tp#t_bit6ombre0pin)valor= 77 coloca el pin indicado a * 1. o#tp#t_@ig@6ombre0pin= 77 coloca el pin indicado a 1. o#tp#t_loM6ombre0pin= 77 coloca el pin indicado a
Ki+aros !ue no %e utili"ado la directiva !#e "at_io)*5 para el puerto ) ya !ue no se utili"an funciones del compilador para el mane+o de los bits de salida. $n este caso el puerto del PIC se controla mapeando la direcci*n de memoria del puerto como una variable m/s en la RAM del PIC) por medio del identificador port0b.
Circuito del ejemplo:
6.3.
Sentencia s'itc#
Sentencia switch
(a sentencia sUitc% se compone de las si#uientes palabras clave' Mitc@) cae) de"a#lt y brea.
(o !ue %ace est/ sentencia es comparar sucesivamente el valor de una e&presi*n 6dic%a e&presi*n tan solo puede ser de tipo entero o de tipo car/cter= con una lista de constantes enteras o de caracteres. Cuando la e&presi*n coincide con la constante) e+ecuta las sentencias asociadas a ésta.
(a estructura de la sentencia sUitc% es la si#uiente'
(a sentencia breaX %ace !ue el pro#rama salte a la lnea de c*di#o si#uiente a la sentencia sUitc%. 3i se omite se e+ecutar/ el resto de casos case %asta encontrar el pr*&imo breaX.
(a sentencia default se e+ecuta cuando no %a %abido nin#una coincidencia. (a parte default es opcional y) si no aparece) no se lleva a cabo nin#una acci*n al fallar todas las pruebas y el pro#rama se#uir/ a partir de la llave !ue cierra la sentencia sUitc%
Consideraciones a la %ora de usar esta sentencia'
•
• •
•
$n una sentencia sUitc% o puede %aber dos sentencias case con el mismo valor de constante. 5na constante c%ar se convierte autom/ticamente a sus valores enteros. 3Uitc% difiere del if en !ue sUitc% solo puede comprobar la i#ualdad mientras !ue if puede evaluar e&presiones relacionales o l*#icas. Adem/s cuando la comparaci*n se basa en variables o se traba+a con e&presiones !ue devuelven float deberemos usar el if7else. 2ay !ue decir !ue la secuencia de sentencias en un case no es un blo!ue 6no tiene por!ue ir entre llaves=. Por lo tanto no podramos definir una variable local en él. Mientras !ue la estructura sUit% #lobal s !ue es un blo!ue.
Namos a ver un e+emplo para ver todo esto'
Comentario
$n el e+emplo introducimos un car/cter numérico) lo almacenamos en el array llamado cadena1 y por medio de la funci*n atoi6= lo convertimos a un valor entero y #uardamos su valor en la variable de tipo entero num) no %ay !ue olvidarse de incluir el arc%ivo de cabecera [stdlib.%\ necesaria para la funci*n atoi6=.
A%ora introducimos valores para ver !ue obtenemos a la salida.
3i introducimos un [1\) coincidir/ con el valor de la constante asi#nada al primer case) por lo cual se e+ecutan las dos primeras sentencias y el pro#rama para de e+ecutar sentencias por!ue se %a encontrado con un breaX) después e+ecuta el >ltimo printf6= por estar esta sentencia fuera de las llaves !ue delimitan a sUitc%.
Ki+aros en el se#undo case) %e omitido su breaX correspondiente a posta 6el compilador no da error si se !uita=) para !ue ve/is el resultado cuando se introduce un [\'
Como veis en la fi#ura de arriba se e+ecutan las sentencias pertenecientes al se#undo case) pero al no encontrar la sentencia breaX) e+ecuta también la sentencia del tercer case. $sto %ay !ue tenerlo en cuenta para tener claro !ue lo !ue %ace salir de la sentencia sUitc% es el breaX correspondiente a cada case.
3i introducimos por e+emplo un [@\ al no a ver coincidencia con el valor de nin#>n case) se e+ecutar/ la sentencia perteneciente a default.
7.
Funciones )!
(as funciones son el pilar de los len#ua+es estructurados como es el C) cual!uier pro#rama medianamente #rande debe de estar formado por diferentes funciones) cada una de ellas %ar/ una tarea determinada y ser/n llamadas desde la funci*n principal 6main= o desde otras funciones se#>n vayan %aciendo falta a lo lar#o de la e+ecuci*n del pro#rama) por tanto es de vital importancia el conocer a fondo todas sus posibilidades) no solo para construir nuestras propias funciones) sino también para entender el c*di#o de pro#ramas y libreras %ec%os por otras personas. Namos %a empe"ar con al#unas definiciones y e+emplos sencillos aplicados al compilador CC3.
1. I!"#$%CCI& (as funciones son los elementos principales de un pro#rama en C. 3on blo!ues en los cuales se reali"a una tarea especfica. 5n pro#rama en C est/ formado por la funci*n main !ue es el blo!ue principal) por funciones propias del pro#ramador y por funciones de libreras propias del compilador. 5na caracterstica importante de las funciones) es !ue pueden recibir par/metros y !ue pueden devolver un valor. (a misma funci*n nos puede servir para varios casos) con tan solo variar el valor de los par/metros. $l compilador de CC3 incluye muc%as funciones b#iltin 6listas para usarse= en sus libreras para el control directo de muc%os de los recursos del PIC) para utili"arlas s*lo necesitamos saber los par/metros !ue reciben y los valores !ue devuelven. • • •
•
2. '"!E $E % *%CI& $n una funci*n %ay !ue distin#uir tres partes' • • •
(a declaraci*n 6también denominada prototipo=. (a definici*n 6o la propia funci*n=. (a llamada a la funci*n.
$l %ec%o de !ue nuestro pro#rama defina una funci*n) no !uiere decir !ue esa funci*n sea e+ecutada. A menos !ue se produ"ca una llamada a la funci*n) la funci*n no ser/ e+ecutada) sino tan solo definida. Por e+emplo) cuando nosotros incluimos la directiva O53$ R3G !ue es una directiva asociada a las bibliotecas precompiladas) incluimos los prototipos de muc%as funciones) pero s*lo se e+ecutan a!uellas funciones a las !ue llamamos) como printf. Namos a ver con m/s detalle cada una de estas partes.
2.1 $eclaraci+n de una funci+n (a declaraci*n de una funci*n se denomina prototipo de la funci*n. $l prototipo aparece antes del blo!ue main) o normalmente en los arc%ivos de cabecera 6.%= $l prototipo de una funci*n debe aparecer antes de su llamada) y le indica al compilador el n>mero de par/metros !ue utili"a una funci*n) y de !ue tipo son. • •
•
(a sinta&is del prototipo es' tipo nombre0funci*n6par/metros=, onde' tipo7 es el tipo de dato !ue va a devolver la funci*n. 3i no se indica nin#>n tipo de dato) por defecto se asume el tipo int. 3i la funci*n no va a devolver nin#>n dato %ay !ue poner void. nombre0funcion7 es el identificador de la funci*n) con el !ue va a ser referenciada. par/metros7 es la lista de par/metros 6valores= !ue recibe la funci*n, en la declaraci*n también sera correcto la si#uiente e&presi*n' tipo nombre0funci*n6tipo)tipo) tipo)....=, $n el cual tenemos tantos tipos como par/metros vaya a aceptar la funci*n. 3upon#amos una funci*n !ue define un par/metro de tipo float y !ue cuando es llamada en el pro#rama) se le pasa un dato de tipo int. Como en el prototipo se %a indicado !ue el par/metro es de tipo float) el compilador convertir/ primero el dato de tipo int a float) y después le pasar/ como par/metro a la funci*n ese dato convertido. (a declaraci*n de la funci*n también controla !ue el n>mero de ar#umentos usados en una llamada a una funci*n coincida con el n>mero de par/metros de la definici*n.
2.2 $efinici+n de una funci+n (a definici*n es la funci*n en s) el blo!ue de sentencias !ue va a componer esa funci*n. (a sinta&is de la definici*n de una funci*n es' tipo nombre0funci*n6par/metros= declaraci*n de datos de la funci*n. cuerpo de la funci*n g (a funci*n va encabe"ada por el prototipo) pero esta ve" sin finali"ar en punto y coma. espués) se incluye el blo!ue de sentencias de la funci*n. (a lista de par/metros puede ser vaca) sin par/metros) si bien los paréntesis de la funci*n deben colocarse de i#ual forma. (as definiciones de las funciones es aconse+able escribirlas a continuaci*n de la funci*n main6= 5n e+emplo de la definici*n de una funci*n sera' float division 6float &) float y=
float resultado, resultado-&y, return6resultado= g Nota: 5na funci*n C) no puede contener en su interior otras funciones.
2., lamada a una funci+n Para e+ecutar una funci*n %ay !ue llamarla. (a llamada a una funci*n consta del nombre de la misma y de una lista de ar#umentos o valores a pasar denominados par/metros actuales) separados por comas y encerrados entre paréntesis. Cuando el pro#rama llama a una funci*n) la e+ecuci*n del pro#rama se transfiere a dic%a funci*n. $l pro#rama retorna a la sentencia posterior a la llamada cuando acaba esa funci*n. (a sinta&is de una llamada a una funci*n es' nombre6par/metros=, donde' nombre7 es el identificador con !ue es definida la funci*n a la !ue !ueremos llamar. par/metros7 es la lista de valores !ue se asi#nan a cada par/metro de la funci*n 6en caso de !ue ten#a=) separados también por comas.
,. #"E $E%E!# 4odas las funciones) e&cepto a!uellas de tipo void) devuelven un valor. $ste valor se especifica e&plcitamente en la sentencia return y si no e&iste ésta) el valor es . (a forma #eneral de return es' return e&presi*n, 4res observaciones sobre la sentencia return' A5 (a sentencia return tiene dos usos importantes. Primero) fuer"a a una salida
inmediata de la funci*n) esto es) no espera a !ue se lle#ue a la >ltima sentencia de la funci*n para acabar. 3e#undo) se puede utili"ar para devolver un valor. *5 return no es una funci*n sino una palabra clave del C) por lo tanto no necesita
paréntesis como las funciones) aun!ue también es correcto' return6e&presi*n=, pero teniendo en cuenta !ue los paréntesis forman parte de la e&presi*n) no representa una llamada a una funci*n.
C5 $n las funciones de tipo void se puede %acer'
return, y de esta forma se provoca la salida inmediata de la funci*n. (a funci*n 6como %emos dic%o) en realidad es una sentencia de C= admite cual!uier e&presi*n valida en C. $+emplo. int multiplica 6int &) int y= return 6&Jy=, g 3i el tipo de datos de la e&presi*n de la sentencia return no coincide con el tipo de datos !ue debe devolver la funci*n autom/ticamente se convierte el tipo de datos para !ue %aya coincidencia. 5na funci*n puede contener varias sentencias return6= en su c*di#o. $+emplo. int compara 6int &) int y= if 6&Wy= return 6=, else return 61=, g 4odas las funciones pueden devolver variables de cual!uiera de los tipos de datos v/lidos en C. 3i no se especifica el tipo) la funci*n por defecto devuelve un entero. Por e+emplo) las si#uientes funciones son e!uivalentes'
int resta 6int &) int y= int ", " - y7&, return 6"=,
resta 6int &) int y= int ", " - y7&, return 6"=, g
g
5na funci*n !ue devuelva un tipo de datos v/lido en C se puede usar como operando en cual!uier e&presi*n v/lida en C. $+emplo. int resultado, resultado - resta6^)E=J1, 5na funci*n no puede usarse a la i"!uierda de una asi#naci*n. $+emplo. resta6a)b=-1, $sta asi#naci*n es incorrecta $l valor devuelto por una funci*n en la sentencia return6= puede no ser usado en una asi#naci*n) ni en una e&presi*n v/lida en C) con lo cual este valor devuelto se perder/. $n otras palabras) aun!ue todas las funciones) e&cepto las declaradas como void) devuelven valores) no se tiene !ue usar necesariamente ese valor de vuelta para al#o. 5na pre#unta muy com>n acerca de los valores devueltos por una funci*n es' ¿ya !ue se devuelve un valor?) ¿no se tiene !ue asi#nar ese valor de vuelta a al#una variable? . (a respuesta es no. 3i no %ay una asi#naci*n especificada) el valor devuelto simplemente se i#nora. Namos a ver al#unos e+emplos sencillos de utili"aci*n de funciones' 01.KHe9?lo de una funcin Lue deuele un alo@ de i?o ene@o y Lue no iene ?a@M9e@osK 02. 03.#include <16F877A.h> 04.#F$** %&'!" !" %&$!" %&&!!" %&+$N" %&+&'%&$!" %&E" %&" '!0B 0.#use delay(clock=4000000) 06.#use @s232(Iaud=,600"?a@iy=%"59i=-%6"@c=-%7"Iis=8) 07. 08.in ?@oduco() 0,. 10.oid 9ain() : 11.
K@ooi?o de la funcinK
12.in @esulado 13.@esulado=?@oduco()
Klla9ada a la funcin ?@oducoK
14.?@inf(l @esulado de 5Ky esJ
BdC@"@esulado)
1. 16.D 17. 18.KF-%--O% EA F$%-&%K 1,. 20.in ?@oduco() 21.: 22.in 5=4 23.in y= 24.in ?@oduco 2.?@inf(l alo@ de 5=4C@) 26.?@inf(l alo@ de y=C@) 27.?@oduco=5Ky 28.@eu@n(?@oduco)Ea funcin se eHecua y deuele el alo@ de la a@iaIle ?@oduco des?uPs el ?@oQ@a9a coninua en la funcin 9ain" en la senencia siQuiene a la lla9ada de la funcin. 2,.D
3i simulamos el e+emplo con Proteus obtendremos obtendremos la si#uiente salida en la terminal'
$n el si#uiente e+emplo se muestra diferentes formas de usar el valor devuelto por una funci*n' 01.#include <16F877A.h> 02.#F$** %&'!" !" %&$!" %&&!!" %&+$N" %&+&'%&$!" %&E" %&" '!0B 03.#use delay(clock=4000000) 04.#use @s232(Iaud=,600"?a@iy=%"59i=-%6"@c=-%7"Iis=8) 0. 06.9uli?lica(in a" in I)decla@acin de la funcin 07.oid 9ain(oid) 08.: 0,.in 5"y"G 10.5= 11.y=4 12.G=9uli?lica (5"y) en esa lRnea el alo@ de uela de 9uli?lica() es asiQnado a G 13.?@inf(Bd"9uli?lica(5"y)) en esa" el alo@ de uela no se asiQna" sino Lue se usa en la funcin ?@inf 14.9uli?lica (5"y) aLuR el alo@ de uela se ?ie@de ?o@Lue no se asiQna a o@a a@iaIle ni se usa co9o ?a@e de una e5?@esin.
1.D 16. 17.efinicin de la funcin 18.9uli?lica (in a" in I) 1,. 20.: 21.@eu@n aKI 22. 23.D
$n el si#uiente e+emplo se muestra lo !ue ocurre cuando no %ay coincidencia en el n>mero de par/metros declarados en el prototipo con el n>mero de ar#umentos en la llamada a la funci*n.
01.se ?@oQ@a9a no co9?ila@M ?o@Lue no coincide el nS9e@o de ?a@M9e@os es?ecificados en el ?@ooi?o de la funcin con el nS9e@o de a@Qu9enos usados en la lla9ada 02. 03.#include <16F877A.h> 04.#F$** %&'!" !" %&$!" %&&!!" %&+$N" %&+&'%&$!" %&E" %&" '!0B 0.#use delay(clock=4000000) 06.#use @s232(Iaud=,600"?a@iy=%"59i=-%6"@c=-%7"Iis=8) 07. 08.floa func(in 5" floa y) ?@ooi?o 0,. 10.9ain(oid) 11.: 12.func(2"2.0"4) 13.@eu@n 0 14.D 1. 16.floa func(in 5" floa y) 17.: 18.?@inf(Bf"y(floa)5)
1,.@eu@n y(floa)5 20. 21.D
8.
Funciones 2!
Continuamos con el tema de las funciones) en este caso vamos a ver a !ue nos referimos cuando %ablamos sobre el /mbito de las funciones y también profundi"aremos mas en el tema sobre el tipo de ar#umentos !ue pueden recibir las funciones) veremos !ue es eso de pasar un ar#umento por valor * por referencia y !ue diferencia %ay entre ambas formas) todo ello con e+emplos sencillos para entender el concepto y aplicados al compilador de CC3.
/. "E0 $E BI!# $E *%CI#E (as re#las de /mbito de un len#ua+e son las re#las !ue controlan si un fra#mento de c*di#o conoce o tiene acceso a otro fra#mento de c*di#o o de datos. $n C) cada funci*n es un blo!ue de c*di#o discreto. $l c*di#o de una funci*n es privado a esa funci*n) a menos !ue se %a#a a través de una llamada a esa funci*n. 6o es posible) por e+emplo) utili"ar un #oto para saltar en medio de otra funci*n=. $l c*di#o !ue comprende el cuerpo de una funci*n est/ oculto al resto del pro#rama y) a no ser !ue se usen datos o variables #lobales) no puede ser afectado por otras partes del pro#rama ni afectarlas. ic%o de otro modo) el c*di#o y los datos !ue est/n definidos dentro de una funci*n no pueden interactuar con el c*di#o o los datos definidos dentro de otra funci*n por!ue las dos funciones tienen un /mbito diferente. (as variables !ue est/n definidas dentro de una funci*n se llaman lla man variables locales. 5na variable local comien"a a e&istir cuando se entra en la funci*n y se destruye al salir de ella. As) las variables locales no pueden conservar sus valores entre distintas llamadas a la funci*n. (a >nica e&cepci*n a esta re#la se da cuando la variable se declara con el especificador de clase de almacenamiento tatic. $sto %ace !ue el compilador trate a la variable como si fuese una variable #lobal en cuanto a almacenamiento se refiere) pero !ue si#a limitando su /mbito al interior de la funci*n. $n C todas las funciones est/n al mismo nivel de /mbito. $s decir no se puede definir una funci*n dentro de otra funci*n. 3in embar#o si se puede llamar a funciones desde otras funciones. A%ora) Namos a profundi"ar un poco m/s sobre los valores 6ar#umentos= !ue le puedo pasar a la funci*n cuando la llamo.
3. "0%E!# $E *%CI#E
B3 6#ncione in arg#mento
Cuando una funci*n se declara con el tipo void como ar#umento) nos est/ indicando !ue esa funci*n no espera ar#umentos. $+emplo' int multiplica6void int multiplica6 void=, =, esta esta funci*n no espera nin#>n nin#>n ar#umento y devuelve un n>mero n>mero entero. B' 6#ncione con arg#mento
•
•
•
3i una funci*n va a usar ar#umentos) debe declarar variables !ue tomen los valores de esos ar#umentos. A estas variables !ue se declaran en la propia funci*n se les suele llamar par/metros formales de la funci*n. 3e comportan como otras variables locales dentro de la funci*n) cre/ndose al entrar en la funci*n y destruyéndose al salir salir.. 2ay !ue ase#urarse !ue el tipo de datos de los par/metros formales sea compatible con el tipo de datos usado para los ar#umentos de llamada a la funci*n. Aun!ue el compilador no muestre nin#>n error por esto) los resultados obtenidos seran imprevisibles. 3e pueden pasar ar#umentos a las funciones de dos formas' o Por valor o Por referencia
9lamada por valor
$ste método copia el valor de un ar#umento en el par/metro formal de la funci*n. e esta forma) los cambios en los par/metros de la subrutina no afectan a las variables !ue se usan en la llamada. $sto si#nifica) en #eneral !ue no se pueden alterar las variables usadas para llamar a la funci*n. 4oda esta serie de nombres y conceptos) puede parecer un poco confuso al principio 4oda sobre todo para el !ue empie"a por primera ve" a estudiar un len#ua+e de pro#ramaci*n. Personalmente) %e pro#ramado en varios len#ua+es de pro#ramaci*n y ba+o mi punto de vista la >nica forma de entender y avan"ar en el aprendi"a+e de un len#ua+e de pro#ramaci*n es' practicar. practicar.. K practicar. As !ue vamos %a empe"ar a ello) en el si#uiente e+emplo vamos a ver como se le pasan los ar#umentos a una funci*n por valor) los e+emplos de funciones !ue %emos visto anteriormente y !ue reciben par/metros utili"an este método) por lo !ue la sinta&is utili"ada nos ser/ familiar.
KHe9?lo Lue 9ues@a co9o se ?asan los a@Qu9enos a una funcin ?o@ alo@K #include <16F877A.h> #F$** %&'!" !" %&$!" %&&!!" %&+$N" %&+&'%&$!" %&E" %&" '!0B #use delay(clock=4000000) #use @s232(Iaud=,600"?a@iy=%"59i=-%6"@c=-%7"Iis=8) in cuad@ado(in 5) decla@acin de la funcin. oid 9ain() : in = ?@inf(Bd Bd" cuad@ado()")la cuad@ado()")la funcin ?@inf 9ues@a en ?analla 2 alo@esJ el alo@ deuelo ?o@ la funcin cuad@ado y el alo@ de la a@iaIle " Lue co9o e9os se ?asa co9o ?a@M9e@o a la funcin D in cuad@ado(in 5) in 5 es la decla@acin de la a@iaIle (?a@M9e@o fo@9al) Lue a a @eciIi@ el alo@ del a@Qu9eno de . : 5=5K5 @eu@n(5)la funcin deuele el cuad@ado de 5 D
3i simulamos el e+emplo con proteus) obtendremos la si#uiente salida'
Como vemos en la ima#en lo !ue se pasa a la funci*n es una copia del valor del ar#umento. (o !ue ocurra dentro de la funci*n no tiene efecto sobre la variable utili"ada en la llamada. $n este e+emplo) se copia el valor del ar#umento de cuadrado6t=) t- en el par/metro &. Cuando se reali"a la asi#naci*n &-&J&) el >nico elemento !ue se modifica es la variable local &. (a variable t) usada para llamar a cuadrado6t=) todava tiene el valor . REC+ERDA: lo !ue se pasa a la funci*n es una copia del valor del ar#umento. (o !ue
ocurra dentro de la funci*n no tiene efecto sobre la variable utili"ada en la llamada.
9.
Punteros )!
$mpe"amos %oy un tema muy importante en C como son los punteros) veremos !ue son) para !ue sirven y las precauciones !ue tenemos !ue tener en cuenta cuando los utilicemos.
45ue e6 un puntero7 5n puntero es una variable m/s) pero !ue almacena la direcci*n de memoria de otra variable.
4'ara 8ue 6ir9en lo6 puntero67 $l conocer la direcci*n de memoria de una variable es muy >til en C) como funciones principales !ue tienen los punteros podemos citar las si#uientes' •
• •
Pueden proporcionar una forma r/pida de acceder o referenciar a tipos de dados compuestos como arrays) estructuras y enumeraciones. 3irven para pasar variables por referencia a las funciones. 3e#>n los casos pueden optimi"ar el c*di#o y a%orrar recursos de memoria.
4$e 8ue operadore6 di6pongo para manipular lo6 puntero67 3 $l primer operador de punteros es P ) un operador monario !ue devuelve la direcci*n
de memoria del operando. 65n operador monario es a!uel !ue solo re!uiere un operando=. Por e+emplo' m-contador, Coloca en m la direcci*n de memoria de la variable contador) o sea la direcci*n del re#istro del PIC donde se %a #uardado la variable contador. Para comprenderlo me+or) supon#amos !ue la variable contador utili"a la posici*n de memoria &C 6primer re#istro de prop*sito #eneral del banco del PIC 1EKD^A= para #uardar su valor) también supon#amos !ue el valor de contador es 1. espués de la asi#naci*n) m tendr/ el valor de &C. 3e puede pensar en como la direcci*n de. Por tanto) la sentencia anterior de asi#naci*n si#nifica mrecibe la direcci*n de contador. ' $l se#undo operador de punteros es Q) !ue es el complementario de P . $s otro
operador monario !ue devuelve el valor de la variable ubicada en la direcci*n !ue se especifica. Por e+emplo) si m contiene la direcci*n de memoria de la variable contador) entonces' !-Jm, Colocar/ el valor de contador en !. 3i#uiendo con el e+emplo) ! tendr/ el valor 1) ya !ue 1 es lo #uardado en el re#istro &C) !ue es la direcci*n de memoria !ue indica m. Piensa en J como valor en la direcci*n. $n este caso) la sentencia de asi#naci*n si#nifica !ue recibe el valor en la direcci*n m. Como vemos y eso puede despistar un poco) el smbolo de estos operadores coincide con los operadores 6= A l*#ico y el operador matem/tico 6J= Multiplicaci*n. o %ay nin#>n problema en esto por!ue el compilador se encar#a de diferenciarlos se#>n el conte&to donde estén colocados ( $l tercer operador utili"ado es' 7 y se utili"a para acceder a tipo de datos
compuestos en C como las estructuras) lo veremos m/s adelante cuando veamos este tipo de datos.
4Como 6e declara una 9ariale puntero7 (as variables !ue vayan a contener direcciones de memoria) o punteros) como se llaman en C) deben declararse colocando un J delante del nombre de la variable. $sto indica al compilador !ue va a contener un puntero a ese tipo de variable. Por e+emplo) para declarar c como puntero a car/cter 6c%ar= escribiremos lo si#uiente' c%ar Jc, A!u) c no es un car/cter) sino un puntero a un car/cter) el tipo de dato al !ue apunta un puntero) en este caso c%ar) se denomina tipo base del puntero. ueno puede !ue al#uien se pre#unte lo si#uiente ¿de !ue tipo es la propia variable puntero? Pues esta claro !ue tiene !ue ser de un tipo de datos cuyo tama;o sea suficiente para #uardar una direcci*n de memoria tal y como esté definida por la ar!uitectura del microcontrolador !ue se utilice. Por e+emplo si utili"amos el PIC 1EfD^A) con un tipo de dato entero de D bits 6intD= sera suficiente para direccionar todos los re#istros de este PIC) pero ese tipo de datos sera insuficiente para direccionar toda la memoria de otro PIC con mayor memoria RAM. (o !ue %ace CC3 es establecer como tipo de dato por defecto para los punteros el entero de 1E bits 6int1E en el tipo de dato nativo de CC3= para !ue sea compatible con todos los PICs !ue actualmente se pueden pro#ramar con CC3) pero da la opci*n de modificar ese par/metro a través de la directiva !device' $sta directiva se encuentra incluida en el arc%ivo de cabecera y en la si#uiente instrucci*n se define para !ue el compilador le asi#ne un tipo de dato intD 61 byte= al PIC 1EfD^A para las variables puntero. Odevice PIC1EfD^A J-D ueno) es una manera de a%orrar recursos cuando utilicemos punteros en PICs con poca memoria RAM. Pero en la mayora de los casos la opci*n por defecto ser/ la correcta y adem/s de no tener !ue preocuparnos por ello) #anamos portabilidad en nuestro c*di#o) al saber !ue es compatible para todos los PICs. (o !ue si es responsabilidad del pro#ramador en cada momento es el tener en cuenta !ue un puntero s*lo debe ser usado para apuntar a datos !ue sean del tipo base del puntero declarado 6este tipo de cosas se ver/ mas adelante cuando veamos las precauciones !ue tenemos !ue tener en cuenta cuando usemos punteros=. Nota: se puede me"clar en una misma sentencia la declaraci*n de variables puntero
con variables normales) por e+emplo. int &) Jy) ", declara &) " como variables de tipo entero) e y como puntero a un tipo entero. Namos a ver un e+emplo del uso de punteros' #include 16F84A.h #use delay(clock=4000000) #fuses !"%&'! #use @s232(Iaud=,600"?a@iy=%"59i=-%+1"@c=-%+2"Iis=8) oid 9ain() : in fuene" desino in K? decla@acin de ? co9o un ?une@o de i?o ene@o
fuene=4asiQno el alo@ 4 a la a@iaIle fuene ?=Tfuene al ?une@o ? se le asiQna la di@eccin de 9e9o@ia de la a@iaIle fuene desino=K? Ee asiQno a la a@iaIle desino el alo@ de fuene a @aPs del ?une@o ? /os@a9os los @esulados en la e@9inal ?@inf(Ea a@iaIle fuene iene la di@eccionJ B5 (he5)C@"Tfuene) ?@inf(Ea a@iaIle desino iene la di@eccionJ B5 (he5)C@"Tdesino) ?@inf(Ea a@iaIle ?une@o ? iene la di@eccionJ B5 (he5)C@"T?) ?@inf(Ea a@iaIle fuene iene el alo@ deJ BdC@"fuene) ?@inf(desino iene el alo@ deJ Bd" asiQnacion hecha con el ?une@o ?C@"K?) /ues@o el nu9e@o de Iyes Lue ocu?a en 9e9o@ia una a@iaIle ?une@o ?@inf(l a9aC5a4o de la a@iaIle ?une@o es deJ BdCn Iye " siGeof(?)) D
(a salida del pro#rama as como el valor de los re#istros del PiC se pueden visuali"ar si simulamos nuestro e+emplo en Proteus.
Comentario $n este e+emplo se %a modificado el tipo de dato asi#nado por defecto por el compilador en la declaraci*n de las variables puntero. Para ello como %e dic%o ya) %ay !ue modificar la directiva !device en el arc%ivo de cabecera se#>n se muestra en la fi#ura de aba+o'
Podemos ver el tama;o !ue tiene la variable puntero por medio del operador i?eo")5) si no modific/is el par/metro de la directiva !device veréis !ue el tama;o en ese caso es de bytes.
Nota: cada ve" !ue se modifi!ue al#una librera perteneciente a CC3 es conveniente
%acer una copia de dic%a librera en la carpeta de nuestro proyecto y modificarla all) acordarse en este caso de incluir en el pro#rama principal el arc%ivo de cabecera entre comillas dobles y no entre los si#nos W . $n la declaraci*n de las variables el compilador reserva las posiciones de memoria RAM necesarias para poder contener los datos de las variables declaradas) como los re#istros de la RAM de prop*sito #eneral del PIC 1EK son de un byte 6D bits= de tama;o) las posiciones de memoria reservadas por el compilador depender/n del tipo de dato con el !ue se %a declarado la variable) por e+emplo una variable declarada como tipo entero 6intD= necesita un solo re#istro para almacenar su valor) una variable declarada como tipo float necesitar/ ^ re#istros 6G bits= para almacenar su valor.
$n el si#uiente paso se produce la asi#naci*n de datos a la variable fuente y a la variable puntero p) como veis en la fi#ura de aba+o) el dato almacenado en p es la direcci*n de memoria de la variable fuente) a partir de a!u se suele decir !ue p apunta a la variable fuente y !ue la variable puntero p !ueda vinculada a esa direcci*n de memoria.
A%ora %acemos una asi#naci*n indirecta de datos utili"ando el operador de indirecci*n 6J=. Como se ve en la fi#ura de aba+o asi#namos a la variable destino el valor !ue tiene la variable fuente indirectamente a través del puntero p.
10.
Sentencias goto" *rea+" continue.
%oy continuamos con el curso %ablando de las sentencias de control breaX) continue y #oto. 9a entencia brea: $sta sentencia tiene dos funciones) la primera es la !ue ya se %a
mencionado en este curso !ue nos permite salir de un case en un blo!ue sUitc%, la se#unda) de la cual %ablaremos %oy) es la de provocar la salida inmediata de cual!uier ciclo !ue se esté e+ecutando sin importar la condici*n de permanencia en el mismo. 3i tenemos dos o m/s ciclos iterativos anidados la sentencia breaX s*lo provocar/ la salida del ciclo en el !ue se encuentre. Para ilustrar el uso de esta sentencia veamos el si#uiente e+emplo'
Comentario: $ste pro#rama %ace !ue el PIC espere %asta !ue se pulse un bot*n
conectado en la patilla R) cuando esto ocurre se enciende un led conectado a la patilla R1 durante un se#undo para lue#o apar#arse y volver a esperar el bot*n sea pulsado nuevamente. *tese !ue) cuando ocurre el breaX se salta a la si#uiente instrucci*n fuera del U%ile m/s anidado o m/s pe!ue;o) es decir) salta a la instrucci*n' output0%i#%6PI01= . $l breaX nos permite interrumpir un ciclo a la ve". A!u tienen una captura de la simulaci*n en Proteus'
9a entencia contin#e: $sta otra sentencia provoca de forma for"ada una nueva
iteraci*n del ciclo en e+ecuci*n saltando a!uellas instrucciones !ue faltaban para el término normal de la iteraci*n en curso. $n el caso de un ciclo U%ile o do7U%ile se salta directamente a la verificaci*n de la condici*n del ciclo mientras !ue en un ciclo for se salta a la parte de incremento del ciclo y lue#o a la verificaci*n de la condici*n de ciclo.
Comentario: $ste pro#rama se basa en un ciclo for de iteraciones en la cuales se
espera a !ue se presione una tecla numérica y la muestra en pantalla) si se presiona otra tecla !ue no sea numérica se muestra en el terminal el mensa+e o presionaste un numero y se salta a la si#uiente iteraci*n. (ue#o de las cinco iteraciones) se termina el pro#rama mostrando el mensa+e Kin de pro#rama. (o ilustrativo de este e+emplo es !ue) aun!ue se presione otra tecla !ue no sea numérica 6lo !ue provocar/ un salto a la si#uiente iteraci*n con la sentencia continue=) la variable i se si#ue incrementando. A!u tienen una captura de una corrida del pro#rama'
Sentencia goto: $sta >ltima sentencia de la !ue %ablaremos %oy permite) en con+unto
con una eti!ueta) reali"ar un salto incondicional a cual!uier parte del pro#rama. (a forma de %acerlo sera la si#uiente' 1........... 2.in i= 3.cicloJ 4.iUU .if(i>0) Qoo ciclo 6.............
Como ver/n) es muy parecido a c*mo se %acen las cosas en assembler. $n el e+emplo anterior simplemente se decrementa la variable i desde %asta . $sto sera e!uivalente al si#uiente ciclo for' 1......... 2.fo@(i=i>0iUU) 3.: 4.D .........
(a sentencia #oto es) #eneralmente) la opci*n menos recomendada a utili"ar) esto se debe a !ue el len#ua+e C es un len#ua+e estructurado y el uso de esta sentencia puede) en al#unas ocasiones) %acer !ue la estructura del pro#rama sea inentendible pareciéndose as a un pro#rama %ec%o en assembler. Adem/s) muc%os autores ase#uran !ue con el uso de las sentencias breaX y continue se puede estruccturar cual!uier pro#rama para sin necesitar el uso de la funci*n #oto. o obstante) esta sentencia podra también %acernos la vida muc%o m/s f/cil a la %ora de pro#ramar) un caso tpico sera cuando !ueremos salir de varos ciclos anidados de una ve" cosa !ue) con la sentencia breaX) no sera tan sencillo. Para terminar de entender esto veamos el si#uiente y >ltimo e+emplo del da de %oy'