Documente Academic
Documente Profesional
Documente Cultură
1.
Constantes......................................................................................... 4
2.
3.
3.1.
La funcin printf()........................................................................12
3.2.
3.3.
4.
Operadores....................................................................................... 24
5.
Sentencias repetitivas...................................................................30
5.1.
Bucle while.................................................................................... 30
5.2.
Bucle for........................................................................................ 33
5.3.
Bucle do-while.............................................................................. 37
6.
Sentencias condicionales...............................................................38
6.1.
Sentencia if................................................................................... 38
6.2.
Sentencia if-else...........................................................................39
6.3.
Sentencia switch..........................................................................43
7.
Funciones (1)................................................................................... 46
8.
Funciones (2)................................................................................... 54
9.
Punteros (1)..................................................................................... 56
10.
1.
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];
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:
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.
1. Constantes.
ntes 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:
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
01.#include <16F84A.h>
02.#use delay(clock=4000000)
03.#fuses XT,PUT,NOWDT
04.#use RS232(BAUD=9600, BITS=8, PARITY=N, XMIT=PIN_B4, RCV=PIN_B5)
05.int x;
06.int y;
07.void main() {
08.x,y=0;
09.while(TRUE)
10.{
11.if (x<=9)
12.{
13.x++;
14.delay_ms(500);
15.//printf("x = %d.\r",x);
16.
17.}
18.else
19.{
20.if (y<=9)
21.{
22.y++;
23.delay_ms(500);
24.//printf("y = %d.\r",y);
25.}
26.}
27.}
28.}
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
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.
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.
#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:
Como vemos la funcin printf tambin admite parmetros que podremos utilizar para
formatear el texto de salida. Vamos a ver cules son:
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.
\r
Retorno de carro
\t
Tabulador
\'
Comilla simple
\"
Comillas dobles
\\
Barra invertida
\?
Smbolo de interrogacin
\0
Caracter nulo
\%
\b
Retroceder un caracter
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
Lu
Ld
Lx
LX
Ejemplo:
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.
2 Ejemplo
3.2.
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
ch=getch()
Que esperar hasta que pulsemos una tecla y almacenar su valor en la variable ch.
La siguiente instruccin:
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.
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.
3.3.
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 pinRD4. Una vez enviada la cadena aade un retorno de carro.
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];
Microcontroladores PIC
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:
4. Operadores.
El lenguaje C dispone de una gran cantidad de operadores que nos sirven para operar
con los datos dentro de nuestros programas, se pueden clasificar en varios apartados:
aritmticos, relacionales, de asignacin, de manejo de un solo bit, etc. Pero lo
importante no es saber a qu grupo pertenece cada operador, sino en conocer la
operacin que se puede realizar con cada uno de ellos. Vamos a ver los operadores que
nos permite utilizar nuestro compilador CCS.
Operador
Descripcin
Suma
Resta
Multiplicacin
Divisin
++
--
Operador
Descripcin
<
Menor que
>
Mayor que
<=
>=
==
Igual a
Distinto de
Operador
Descripcin
+=
-=
*=
/=
%=
<<=
>>=
&=
|=
^=
Operador
Descripcin
NO lgico
&&
Y lgico
||
O lgico
Operador
Descripcin
&
Y de bits (AND)
O de bits (OR)
>>
Desplazamiento a la derecha
<<
Desplazamiento a la izquierda
Operador
Descripcin
&
Operador de direccin
Operador de indereccin
->
Ejemplo:
z=*x; //a travs del operador de indereccin (*) le asignamos a z el valor de la variable
cuya direccin est almacenada en la variable puntero x.
Nota: como vemos los smbolos de direccin (&) e indereccin (*) son los mismos que
el AND en el manejo de bits (&) y el operador aritmtico de multiplicacin, el compilador
los diferencia segn los operandos que le preceden.
~expr
++expr
expr++
(type)expr
*expr
&value
sizeof(type)
expr*expr
expr/expr
expr%expr
expr+expr
expr-expr
expr<<expr
expr>>expr
expr<expr
expr<=expr
expr>expr
expr>=expr
--expr
expr --
expr&& expr
expr || expr
expr ? expr: expr
value = expr
value+=expr
value-=expr
value*=expr
value/=expr
value%=expr
value|=expr
expr,expr
Las operaciones con mayor precedencia se realizan antes que las de menor precedencia.
Si en una operacin encontramos signos del mismo nivel de precedencia, dicha
operacin se realiza de izquierda a derecha.
Ejemplo:
a*b+c/d-e
Las operaciones se realizarn en el siguiente orden:
1.
2.
3.
4.
a*b resultado = x
c/d resultado = y
x+y resultado = z
z-e
Para saber el rango de valores admisible por cada funcin repasa los tipos de datos
Estas funciones estn definidas en el fichero de cabecera stdlib.h, por tanto no hay que
olvidarse de incluirlo previamente por medio de la directiva: #include <stdlib.h>.
\xa0
\x82
\xa1
\xa2
\xa3
\xb5
\x90
\xd6
\xe0
\xe9
\xa4
\xa5
\xa8
Una precaucin que tenemos que tener en cuenta es que si utilizamos valores
numricos grandes y un PIC con poca memoria RAM, pronto la agotaremos. Esto solo es
5. Sentencias repetitivas.
5.1.
Bucle while
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.
El bloque delimitado por las llaves puede reducirse a una sentencia, y en este caso se
suprimen las llaves.
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.
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 dei1 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.
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.
5.2.
Bucle for
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:
Comentario
1 bucle for:
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
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.
5.3.
Bucle do-while
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 dowhile es la que se muestra en la figura de abajo:
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.
6. Sentencias condicionales
6.1.
Sentencia if
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.
if (x=1)
printf(Sin llaves solo una sentencia asociada al if);
6.2.
Sentencia if-else
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.
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
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:
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:
6.3.
Sentencia switch
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.
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.
Comentario
7. Funciones (1)
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
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.
{
float resultado;
resultado=x/y;
return(resultado)
}
Nota: Una funcin C, no puede contener en su interior otras funciones.
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.
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:
/*Prototipo de la funcin*/
12.int resultado;
13.resultado=producto();
%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:
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.}
19.return y/(float)x;
20.
21.}
8. Funciones (2)
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.
5. ARGUMENTOS DE FUNCIONES
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:
o Por valor
o Por referencia
/*Ejemplo que muestra como se pasan los argumentos a una funcin por
valor*/
#include <16F877A.h>
#FUSES NOWDT, XT, NOPUT, NOPROTECT, NODEBUG, NOBROWNOUT, NOLVP, NOCPD,
WRT_50%
#use delay(clock=4000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
int cuadrado(int x); //declaracin de la funcin.
void main() {
int t=5;
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
}
int cuadrado(int x) //int x es la declaracin de la variable
(parmetro formal) que va a recibir el valor del argumento de t.
{
x=x*x;
return(x);//la funcin devuelve el cuadrado de x
}
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.
9. Punteros (1)
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.
#include "16F84A.h"
#use delay(clock=4000000)
#fuses XT,NOWDT
#use rs232(baud=9600,parity=N,xmit=PIN_B1,rcv=PIN_B2,bits=8)
void main() {
int fuente, destino;
int *p; //declaracin de p como un puntero de tipo entero
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.
10.
Comentario: Este programa hace que el PIC espere hasta que se pulse un botn
conectado en la patilla RB0, cuando esto ocurre se enciende un led conectado a la
patilla RB1 durante un segundo para luego apargarse y volver a esperar el botn sea
pulsado nuevamente.
Ntese que, cuando ocurre el break se salta a la siguiente instruccin fuera
del while ms anidado o ms "pequeo", es decir, salta a la
instruccin: output_high(PIN_B1) . El break nos permite interrumpir un ciclo a la vez.
Aqu tienen una captura de la simulacin en Proteus:
La sentencia continue: Esta otra sentencia provoca de forma forzada una nueva
iteracin del ciclo en ejecucin saltando aquellas instrucciones que faltaban para el
trmino normal de la iteracin en curso. En el caso de un ciclo while o do-while se salta
directamente a la verificacin de la condicin del ciclo mientras que en un ciclo for se
salta a la parte de incremento del ciclo y luego a la verificacin de la condicin de ciclo.
Sentencia goto: Esta ltima sentencia de la que hablaremos hoy permite, en conjunto
con una etiqueta, realizar un salto incondicional a cualquier parte del programa. La
forma de hacerlo sera la siguiente:
1...........
2.int i=5;
3.ciclo:
4.i--;
5.if(i>0) goto ciclo;
6.............
Como vern, es muy parecido a cmo se hacen las cosas en assembler. En el ejemplo
anterior simplemente se decrementa la variable i desde 5 hasta 0. Esto sera
equivalente al siguiente ciclo for:
1.........
2.for(i=5;i>0;i--)
3.{
4.}
5.........
La sentencia goto es, generalmente, la opcin menos recomendada a utilizar, esto se
debe a que el lenguaje C es un lenguaje estructurado y el uso de esta sentencia puede,
en algunas ocasiones, hacer que la estructura del programa sea "inentendible"
parecindose as a un programa hecho en assembler. Adems, muchos autores
aseguran que con el uso de las sentencias break y continue se puede estruccturar
cualquier programa para sin necesitar el uso de la funcin goto. No obstante, esta
sentencia podra tambin hacernos la "vida" mucho ms fcil a la hora de programar, un
caso tpico sera cuando queremos salir de varos ciclos anidados de una vez cosa que,
con la sentencia break, no sera tan sencillo. Para terminar de entender esto veamos el
siguiente y ltimo ejemplo del da de hoy: