Documente Academic
Documente Profesional
Documente Cultură
INTRODUCCION A LA
PROGRAMACION DE
MICROCONTROLADORES
PIC EN CCS C
Por John Caipa Roldan
Este material es una traduccin y actualizacin del libro original de Nigel Gardner
titulado "An introduction to programming the Microchip PIC in CCS C", copyright
Bluebird Electronics 2002.
1
DECLARACION CLASICA Y MODERNA DE FUNCIONES .................................................................. 30
PASANDO CADENAS CONSTANTES A FUNCIONES ........................................................................ 31
4. OPERADORES ............................................................................................................................ 32
OPERADORES ARITMETICOS ......................................................................................................... 32
OPERADORES RELACIONALES ....................................................................................................... 33
OPERADORES LOGICOS ................................................................................................................. 34
OPERADORES DE BIT A BIT ............................................................................................................ 34
OPERADORES INCREMENTALES Y DECREMENTALES .................................................................... 36
PRECEDENCIA DE OPERADORES .................................................................................................... 37
5. DECLARACIONES DE CONTROL DEL PROGRAMA ..................................................................... 39
DECLARACION if .......................................................................................................................... 39
DECLARACION if-else .............................................................................................................. 40
OPERADOR ? ................................................................................................................................. 42
DECLARACION for ....................................................................................................................... 42
DECLARACION while................................................................................................................... 43
DECLARACION do-while ........................................................................................................... 44
DECLARACIONES DE CONTROL ANIDADAS.................................................................................... 45
DECLARACION break................................................................................................................... 46
DECLARACION continue ........................................................................................................... 46
DECLARACION switch ................................................................................................................ 47
DECLARACION null(;) .............................................................................................................. 49
DECLARACION return ................................................................................................................ 50
6. ARREGLOS Y CADENAS ............................................................................................................. 51
ARREGLOS UNIDIMENSIONAL ...................................................................................................... 51
CADENA DE CARACTERES .............................................................................................................. 53
ARREGLOS MULTIDIMENSIONALES ............................................................................................... 54
INICIALIZANDO ARREGLOS ............................................................................................................ 55
ARREGLOS DE CADENAS ................................................................................................................ 56
FUNCIONES PARA MANIPULAR CADENAS .................................................................................... 56
7. PUNTEROS................................................................................................................................. 58
INTRODUCCION A LOS PUNTEROS ................................................................................................ 58
2
RESTRICCIONES PARA PUNTEROS ................................................................................................. 59
PUNTEROS Y ARREGLOS ................................................................................................................ 61
PASANDO PUNTEROS A FUNCIONES ............................................................................................. 62
8. ESTRUCTURAS Y UNIONES........................................................................................................ 64
INTRODUCCION A LAS ESTRUCTURAS ........................................................................................... 64
PUNTEROS A ESTRUCTURAS.......................................................................................................... 67
INTRODUCCION A LAS UNIONES ................................................................................................... 69
9. LENGUAJE C ESPECIFICO PARA PIC ........................................................................................... 71
ENTRADAS Y SALIDAS .................................................................................................................... 71
MEZCLANDO C Y ASSEMBLER ........................................................................................................ 73
MANIPULACION AVANZADA DE BIT .............................................................................................. 75
TEMPORIZADORES ........................................................................................................................ 76
3
INTRODUCCION
Para el desarrollo de los ejemplos y ejercicios que se muestran en el transcurso del libro se
recomienda sean compilados en Microsoft Visual C++ Express, el cual puede descargarse
gratuitamente de la pgina de Microsoft. El Microcontrolador empleado en este material es
el PIC16F84A de Microchip, adems los programas ejemplo que utilizan este
Microcontrolador se escribieron y compilaron en PIC C Compiler de CCS.
PORQUE USAR C?
El lenguaje C fue desarrollado en los laboratorios Bell a finales de los 60s por Dennis Ritchie y Brian
Kernighan. Una de las primeras plataformas para su implementacin fue PDP-11 que corra bajo el
ambiente UNIX. Desde su introduccin, este ha evolucionado, estandarizado y se ha establecido
en toda la industria de software como el lenguaje de programacin ms empleado.
El uso del lenguaje C en las aplicaciones con Microcontroladores es debido a que provee
programas ms complejos con un desarrollo ms fcil y tiempo de diseo ms corto comparado
con el lenguaje Assembler, pero con el inconveniente que no es tan eficiente en lo relacionado
con memoria de programa.
4
TERMINOLOGIA
I/O Pin de conexin con el mundo exterior que puede ser configurado como entrada o salida. I/O
es necesario en la mayora de los casos para permitirle al Microcontrolador comunicarse, controlar
y leer informacin.
Software La informacin que el Microcontrolador necesita para operar o correr. Este tiene que
estar libre errores para una aplicacin exitosa. Puede ser escrito en una variedad de lenguajes
como C, Pascal o Assembler.
Simulador Aplicacin en la que puede probar y depurar sus diseos de manera interactiva y
rpida evitando la programacin del dispositivo real, ejemplo: ISIS PROTEUS .
Programador Unidad que permite al programa ser cargado dentro de la memoria del
Microcontrolador. Estos vienen en diferentes formas, protocolos de comunicacin y precios,
ejemplos: PICSTART PLUS, PICKIT de Microchip.
Archivo Fuente Programa escrito en un lenguaje como Assembler o C que usted puede
entender. Este archivo tiene que procesarse antes de que el Microcontrolador lo reciba.
Archivo Objeto Archivo que se produce despus de compilar el archivo fuente. La extensin es
.OBJ o .HEX, y es el archivo que necesita el Simulador y el Microcontrolador para funcionar.
5
(a)
(b)
6
RECOMENDACIONES EN LA ESCRITURA DE CODIGO C
La escritura de un programa es como construir una casa, si la base es firme, el resto del cdigo
permanecer solido. Pero si la base es dbil, el cdigo perder su firmeza en algn punto. Las
siguientes recomendaciones tomadas del estndar C++ han sido adaptadas para el PIC.
if (condicin)
{
}
Tabulacin e Indentacin
Use espaciado en lugar de tabulacin (8 espacios), recuerde que la configuracin de tabulacin de
un editor no es la misma en otro, haga el cdigo portable, utilice Indentacin para que su cdigo
luzca organizado.
Longitud de lnea
Mantenga una longitud de lnea de mximo 78 caracteres para mantener compatibilidad entre
monitor e impresora.
Comentarios
Los comentarios permiten descifrar la historia que usted est escribiendo. Usted sabe como su
programa est funcionando hoy pero en dos semanas o dos aos podr recordar, o puede alguien
diferente entender su programa como lo tiene ahora?
Use comentarios para marcar reas donde hay que hacer alguna modificacin, errores a depurar o
complementos futuros de su cdigo.
7
1. FUNDAMENTOS DE C
Este captulo presenta algunos de los aspecto clave del lenguaje de programacin C. Se entregar
una vista rpida de cada aspecto. El objetivo es proporcionar al lector un conocimiento bsico de C
tal que puede comprender los ejemplos de los siguientes captulos.
Declaraciones
Una declaracin establece el nombre y atributos de las variables, funciones y tipos usados en el
programa. Las variables globales son declaradas fuera de las funciones y son visibles desde el final
de la declaracin hasta el final del archivo. Una variable local es declarada dentro de una funcin y
es visible desde el final de la declaracin hasta el final de la funcin.
Definiciones
Una definicin establece el contenido de una variable o funcin. Una definicin tambin asigna el
espacio de almacenaje necesario para las variables y funciones.
Expresiones
Una expresin es una combinacin de operadores y operandos que producen un nico valor
Instrucciones
Controlan el flujo y orden en la ejecucin del programa en un cdigo C.
Funciones
Una funcin es una coleccin de declaraciones, definiciones, expresiones e instrucciones que
desempean una tarea especfica. Los corchetes encierran el cuerpo de una funcin. Las funciones
no se pueden anidar en C.
Funcin main
Todos los programas hechos en C deben incluir una funcin llamada main donde la ejecucin del
programa comienza. Los corchetes que encierran la funcin main definen el comienzo y punto de
finalizacin del programa.
8
float area; /* declaracin variable global */
int cuadrado (int r);
void main()
{ /* Comienza la funcin main */
int radio_alCuadrado; /* declaracin variable local */
int radio = 3; /*declaracin e inicializacin*/
radio_alCuadrado = cuadrado(radio); /*funcin de usuario*/
area = PI * radio_alCuadrado;
printf("El area es %6.4f unidades cuadradas\n",area);
getchar(); /* funcin de terminacin*/
} /*fin de la funcin main */
int cuadrado(int r) /* encabezado de la funcin*/
{
int r_alCuadrado;
r_alCuadrado = r * r;
return(r_alCuadrado);
}
Todos los programas escritos en C contienen ciertos componentes esenciales tales como
instruccin y funciones. Las instrucciones son la parte del programa que realiza las operaciones.
Todos los programas en C contienen una o ms funciones. Las funciones son subrutinas, cada una
de las cuales contienen una o ms instrucciones y pueden ser llamada por otras partes del
programa. Cuando escribe programas con lneas en blanco, usar indentacin y comentarios mejora
la visualizacin, no solo para usted en el futuro, sino tambin para aquellas personas interesadas
en su trabajo. El siguiente ejemplo muestra algunas de las partes requeridas de un programa en C.
#include <stdio.h>
/* mi 1er codigo en C */
void main()
{
printf("Hola Mundo!");
getchar(); /* termina el programa oprimiendo ENTER */
}
Todos los programas escritos en C deben tener la funcin main(). Esta funcin es el punto de
entrada del programa. Todas las funciones tienen el mismo formato, el cual es el siguiente:
9
TipoFuncion NombreFuncion()
{
Cdigo;
}
Las instrucciones dentro de una funcin son ejecutadas secuencialmente, comenzando con el
corchete abierto y terminando con el corchete cerrado.
Los corchetes {} muestran el comienzo y el fin de bloques de cdigo en C.
Finalmente, printf("Hola Mundo"); representa una instruccin tpica en C. Casi todas las
instruccin en C terminan con punto y coma (;). El carcter final de lnea no es reconocido por C
como un terminador de lnea. En consecuencia, no hay restricciones sobre la posicin de las
instrucciones en una lnea ni del nmero de estas.
Todas las lneas tienen un punto y coma (;) al final para informar al compilador que ha alcanzado el
final de la instruccin. El error comn de no incluirlo es sealado como error en la lnea siguiente.
La excepcin a esta regla es con el comando if donde el punto y coma necesita estar al final de
la siguiente lnea, ejemplo:
if (EstoEsVerdad)
HagaEstaFuncion();
#PRAGMA
El comando pragma le informa al compilador que desempee una accin particular al momento
de realizar la compilacin como el PIC especifico usado en la aplicacin. Ejemplo:
#device PIC16F84A
MAIN()
Como ya se ha dicho anteriormente todo programa debe incluir una funcin main la cual puede
aparecer solo una vez en el programa. Ningn parmetro es necesario dentro de los parntesis
(). Como main est clasificada como funcin, todo el cdigo siguiente a esta funcin debe estar
dentro de un par de corchetes {}.
void main()
{
Cuerpo del programa
}
#INCLUDE
El archivo de encabezado, (denotado por la extensin .h) contiene informacin acerca libreras de
funciones tales como registros especiales de un PIC especfico, etc. Ejemplo:
#include <16F84A.h>
10
Esta informacin la utiliza el compilador para conectar detalles especficos de hardware y el
programa fuente. El siguiente ejemplo es hecho en el compilador C de CCS.
void main()
{
Printf("Escriba caracteres:");
while(TRUE)
putc(toupper(getc())); //toupper convierte a maysculas
}
Habr notado probablemente que la directiva #include no termina con punto y coma. La razn
para esto es que esta directiva no es una palabra clave de C, pero en cambio es una instruccin del
compilador. Todo el contenido de la librera incluida es insertado en el archivo fuente en la etapa
de compilacin.
FUNCION PRINTF
printf("Hola Mundo!");
printf("Microchip es el #%d!",1);
%d da el formato para la constante que se est imprimiendo. La Tabla 1 muestra todos los
especificadores de formato de C y el tipo de datos que afectan.
11
Tabla 1. Especificadores de formato en C.
Especificador de formato Tipo de dato que afecta
%c Carcter sencillo
%uc Carcter sin signo
%s Cadena de caracteres
%d Enteros decimales con signo
%f Punto flotante (notacin decimal)
%e Punto flotante (notacin exponencial o cientfica)
%g Punto flotante (%f o %e, cualquiera en su formato
corto)
%u Enteros decimales sin signo
%x, %X Enteros hexadecimales sin signo
%p Apuntador
%o Enteros octales sin signo
l Prefijo usado con %d, %u, %x, %o para especificar
entero largo
NOTA: Un 0 (cero) seguido del smbolo % dentro de un formato de cadena obliga a imprimir los
ceros que estn al comienzo. El siguiente nmero especifica el tamao del campo a imprimir.
Ejemplo:
Esta instruccin imprime en pantalla el valor de area en un espacio con ancho de 6 y precisin
decimal de 4 posiciones.
VARIABLES
Una variable es un nombre que se le da a una localizacin especfica de memoria. Esta localizacin
de memoria puede guardar varios valores dependiendo de cmo haya sido declarada la variable.
En C, todas las variables deben declararse antes de ser usadas. Una declaracin de variables le
12
informa al compilador que tipo de variable est siendo usada. Todas las declaraciones de variables
son instrucciones en C y por consiguiente deben finalizar con un punto y coma.
Los cinco tipos bsicos de datos que soporta C son: char, int, float, double, void. El
formato general para declarar una variable es el siguiente:
TipoVariable NombreVariable;
CONSTANTES
Una constante es un valor fijo que no puede ser alterado por el programa. Por ejemplo, 25 es una
constante. Las constantes enteras son especificadas sin ninguna componente fraccional como -100
o 40. Constantes en punto flotante requieren punto decimal seguido por su componente
fraccional. El nmero 456.75 es una constante en punto flotante. Los caracteres tambin son
constantes y deben estar encerradas por comillas sencillas como 'A' o '&'.
Cuando el compilador encuentra una constante en el programa, este decide qu tipo de constante
es. El compilador C podr, por defecto, acomodar la constante dentro del tipo de dato ms
pequeo que lo pueda contener. As que 15 es un tipo int, 64000 es un tipo long y 105020 es
un tipo int32.
Una constante es declarada usando la sentencia #define.
<etiqueta> define el nombre que usar a travs del programa, valor es el valor que se le
asignara a <etiqueta>. Ejemplos:
#define VERDADERO 1
#define pi 3.14159265359
Las constantes no son almacenadas en memoria, son resueltas en tiempo de compilacin. Para
guardar constantes en la memoria ROM del chip, use la palabra clave const en la declaracin de
una variable. Por ejemplo:
13
Se estn usando cinco localizaciones de memoria para guardar la cadena porque esta debe
terminar con el carcter null (\0) como se explicar ms adelante.
COMENTARIOS
Los comentarios son empleados para documentar el significado y operacin del cdigo fuente.
Todos los comentarios son ignorados por el compilador. Un comentario puede ser puesto en
cualquier parte del programa excepto en la mitad de alguna instruccin, nombre de funcin o
nombre de variable. Los comentarios pueden abarcar muchas lneas y pueden ser usados para
remover temporalmente una lnea de cdigo. Finalmente, los comentarios no se pueden anidar.
Los comentarios tienen dos formatos. El primer formato es usado por todos los compiladores de C
y es el siguiente:
/* Este es un comentario */
// Este es un comentario
FUNCIONES
Las funciones son los bloques de construccin bsicos de un programa en C. todos los programas
en C contienen al menos una funcin, main(). La mayora de los programas que usted escribe
contendr muchas funciones. El formato para un programa en C con muchas funciones es el
siguiente:
void main()
{
}
funcion1()
{
}
funcion2()
{
}
main() es la primera funcin llamada cuando el programa se est ejecutando. Las otras
funciones, funcion1() y funcion2(), pueden ser llamadas por cualquier otra funcin en el
programa.
Tradicionalmente main() no es llamada por ninguna otra funcin, sin embargo, no hay
restricciones en C con respecto a esto.
#include <stdio.h>
void main()
{
printf("Me ");
14
funcion1();
printf("C.");
}
funcion1()
{
printf("gusta programar en");
}
Una cuestin que hay que tener en cuenta al escribe sus propias funciones es que cuando el
corchete de cierre es alcanzado, el programa continuar ejecutndose una lnea despus del
punto en el cual la funcin ha sido originalmente llamada.
El compilador necesita saber acerca del hardware para que el cdigo pueda ser compilado
correctamente. Un programa tpico en el compilador C de CCS puede comenzar as:
#include <16F84A.h>
#fuses XT,NOWDT, PUT, NOPROTECT
#use delay(clock=4000000)
La primera lnea incluye las definiciones especficas del dispositivo tales como nombre de pines,
registros especiales, etc. La segunda lnea establece la palabra de configuracin del PIC, para este
caso se deja al oscilador de cristal como oscilador del dispositivo, se desactiva el Watch Dog Timer,
se habilita el Power-up Timer y se desactiva la proteccin de cdigo. La ltima lnea le informa al
compilador la velocidad del oscilador. Las siguientes lneas son otro ejemplo:
En adicin, las variables de C pueden ser creadas y mapeadas a registros del hardware. Estas
variables pueden ser bits o bytes. Despus que son definidas, pueden ser utilizadas en un
programa como cualquier otra variable. Ejemplo:
#bit carry=3.0
#byte portb=6
#byte intcon=11
PALABRAS CLAVES DE C
El estndar C ANSI define 32 palabras clave para usar en el lenguaje C. En C, ciertas palabras son
reservadas del compilador para definir tipos de datos y para su uso en loops (bucles o lazos). Todas
las palabras claves de C deben escribirse en minscula para que el compilador las reconozca.
Tpicamente, la mayora de los compiladores de C agregan varias palabras adicionales que toman
ventaja de la arquitectura del procesador.
15
auto double int struct
break else long switch
case enum register typedef
char extern return union
const float short unsigned
continue for signed void
default goto sizeof volatile
do if static while
Ejercicios
El ao es 2011.
16
2. VARIABLES
Un aspecto importante del lenguaje C es como este almacena datos. Este captulo examinar ms
en detalle cmo se usan las variables en C para almacenar datos.
TIPOS DE DATOS
El lenguaje de programacin C soporta cinco tipos de datos bsicos (como se haba comentado en
el capitulo anterior) y cuatro tipos de modificadores. La Tabla 3 muestra la definicin de los tipos
de datos bsicos y el tipo de modificador.
Cada tipo de dato representa un rango particular de nmeros, que pueden cambiar dependiendo
del modificador usado. La Tabla 4 muestra los posibles rangos de valores para algunos tipos de
datos bsicos y los modificadores en CCS C.
NOTA: revisar la documentacin del compilador C para tipos de datos y rangos numricos
actualizados. Todos los tipos, excepto float, por defecto son unsigned.
C permite una notacin ms corta para los tipos unsigned int, short int, y long
int. Simplemente use las palabras unsigned, short o long sin la palabra int. Para realizar
las operaciones aritmticas de una forma sencillas para la CPU, C representa todos los nmeros
negativos en el formato complemento a 2. Para encontrar el complemento a 2 de un nmero
simplemente invierta todos los bits y sume 1 al resultado. Por ejemplo, para convertir el nmero
con signo +29 en complemento a 2 se procede as:
17
00011101= 29
11100010 se invierten todos los bits
+ 1 se suma 1
________
11100011= -29
Ejercicios
2. Para entender la diferencia entre un nmero con signo y uno sin signo, escriba el siguiente
programa en Microsoft Visual C++ Express. El entero sin signo 35000 es representado por
-30536 en el formato entero con signo.
#include <stdio.h>
void main()
{
short int i; // entero con signo equiv. a signed int16 en CCS
unsigned short int u; // entero sin signo equiv. a int16 en CCS
u = 35000;
i = u;
printf("%d %u\n",i,u);
getchar();//detiene la ejecucin hasta oprimir ENTER
}
DECLARACION DE VARIABLES
Las variables pueden ser declaradas en dos lugares bsicos: dentro de una funcin o por fuera de
todas las funciones. Las variables son llamadas locales o globales respectivamente. Las variables
son declaradas de la siguiente forma:
TipoVariable NombreVariable;
El valor de una variable local no puede ser accesado por otras funciones por fuera de la funcin de
origen. La cosa ms importante a recordar acerca de las variables locales es que son creadas al
momento de entrar a la funcin y destruidas cuando la funcin ha finalizado. Las variables locales
deben declararse al comienzo de la funcin antes de las instrucciones.
Es aceptable para variables locales que estas tengan nombres iguales en diferentes funciones.
Considere el siguiente ejemplo.
#include <stdio.h>
void f2()
{
int cont;
for (cont=0; cont<10; cont++)
18
printf("%d \n",cont);
}
void f1()
{
int cont;
for (cont=0; cont<10; cont++)
f2();
}
int main()
{
f1();
getchar();
return 0;
}
Este programa imprime los nmeros del 0 al 9 en pantalla 10 veces. La operacin del programa no
es afectada por la variable cont que est definida en ambas funciones.
Las variables globales, por otra parte, pueden ser usadas por todas las funciones definidas en el
programa. Las variables globales deben ser declaradas antes que cualquier funcin que las vaya a
usar. Lo ms importante, las variables locales no son destruidas hasta que la ejecucin del
programa haya sido completada.
#include <stdio.h>
int max;
void f1()
{
int i;
for (i=0; i<max; i++)
printf("%d ",i);
}
int main()
{
max = 10;
f1();
return 0;
}
En este ejemplo, ambas funciones main() y f1() hacen referencia a la variable max. La funcin
main() asigna un valor a max y la funcin f1() usa el valor de max para controlar el bucle del
for.
Ejercicios
1. Cules son las diferencias principales entre variables locales y variables globales?
2. Ambos tipos de variables podran compartir el mismo nombre en C?. Escriba el siguiente
programa.
#include <stdio.h>
int cont;
void f1()
19
{
int cont;
cont = 100;
printf("contador en f1(): %d\n ",cont);
}
int main()
{
cont = 10;
f1();
printf("contador en main(): %d\n ",cont);
return 0;
}
En la funcin main() la referencia a cont es una variable global. En la funcin f1() la variable
local cont domina sobre el uso de la variable global.
Hasta el momento se ha discutido solo como declarar una variable en el programa y no realmente
como se asignan valores a esta. La asignacin de valores a variables es sencilla, as:
NombreVariable = valor;
Debido a que la asignacin de valores a variables se hace con una instruccin, se debe incluir el
punto y coma al final. Un ejemplo de cmo asignar el valor 100 a la variable tipo entera cont es:
cont = 100;
El valor 100 es una constante. Diferentes tipos de constantes existen en C. una constante tipo
carcter esta especificado porque debe estar encerrada en comillas sencillas, como 'M'. Todos los
nmeros enteros se emplean cuan se asignan valores a enteros. Nmeros en punto flotante deben
usar valores con punto decimal. Por ejemplo, para decirle a C que el valor 100 est en punto
flotante, use 100.0.
A una variable tambin se le puede asignar el valor que tiene otra variable diferente. El siguiente
programa ilustra esta asignacin.
void main()
{
int i;
int j;
i=0;
j=i;
}
Ejercicios
1. Escriba un programa en Microsoft Visual C++ Express que declare una variable tipo
entero llamada cont. De un valor de 100 a cont y use la funcin printf() para
imprimir el valor en pantalla. La salida debe lucir as:
20
2. Escriba un programa en Microsoft Visual C++ Express que declare tres variables, char,
float, y double con los nombres ch, f, y d. Asignar el valor 'R' a ch, 50.5 a f, y
156.007 a d. Imprima el valor de todas las variables en pantalla. La salida debe lucir como
la siguiente:
ENUMERACION
En C, es posible crear una lista de constantes enteras. Este tipo de declaracin es llamada
enumeracin. La lista de constantes creadas con enumeracin puede ser usada en cualquier lugar.
La forma general para crear una enumeracin es la siguiente:
A la variable color solo se le pueden asignar los valores rojo, verde o amarillo (esto es,
color = rojo;).
Una vez la enumeracin es definida, el nombre puede usarse para crear variables adicionales en
otros puntos del programa. Por ejemplo, la variable MiColor puede crearse con la enumeracin
TipoColor asi:
if (color==fruta)
//haga esto
21
Ejercicios
TYPEDEF
La declaracin typedef es usada para definir nuevos tipos de datos por medio de tipos
existentes. Su formato es el siguiente:
El nuevo nombre puede usarse para declarar variables. Por ejemplo, el siguiente programa usa el
nombre smallint para el tipo signed char.
#include <stdio.h>
typedef signed char smallint;
void main()
{
smallint i;
for(i=0;i<10;i++)
printf("%d ",i);
}
Cuando se usa typedef, usted debe recordar dos puntos clave: La declaracin typedef no
desactiva el nombre original o el tipo, en el ejemplo anterior signed char todava es un tipo
valido; varias declaraciones typedef pueden usarse para crear nuevos nombres para el mismo
tipo original.
Typedef es tpicamente usado por dos razones. La primera para crear un programa portable. Si
el programa que se est escribiendo va a ser usando en maquinas con enteros de 16-bits y de 32-
bits, usted querra asegurar que solo los enteros de 16-bits van a ser empleados. La segunda razn
para usar la declaracin typedef es para ayudarle a documentar su cdigo.
Ejercicios
1. Crear un nuevo nombre para el tipo unsigned long que se llame UL. Use este
typedef en un programa corto que declare una variable usando UL, asigne un valor a
est y muestre el valor en pantalla.
22
CONVERSION DE TIPO
C le permite combinar diferentes tipos de datos en una sola expresin. Por ejemplo, el siguiente
ejemplo es un fragmento de cdigo valido:
char ch = '0';
int i = 15;
float f = 25.6
Ahora que la promocin de tipo automtica ha sido completada, el compilador C convertir todas
las variables en una expresin sobre el tipo de la variable ms larga. Esta tarea se completa sobre
una operacin por medio de operaciones bsica. El siguiente fragmento de cdigo muestra como
imprimir la porcin entera de un nmero en punto flotante:
float f;
f = 100.2;
printf("%d",(int)f);
El numero 100 se imprimir en pantalla al ejecutarse el cdigo. Si dos enteros de 8-bits son
multiplicados, el resultado ser un valor de 8-bits. Si el resultado es asignado a un tipo long, el
resultado todava seguir siendo un entero de 8-bits, debido a que la aritmtica es desarrollada
antes de que el resultado sea asignado a la nueva variable.
El resultado ser 196 el cual es errneo. Entonces si necesita un valor tipo long como respuesta,
entonces al menos un valor necesita definirse inicialmente como long o escribir lo siguiente.
c = (long) a * b;
El resultado ser 2500 porque a primero ha sido convertido a tipo long antes que la
multiplicacin se haya completado.
Cada variable y funcin en C tiene dos atributos, el tipo y clase de almacenamiento. El tipo ya ha
sido discutido como char, int, etc. Hay cuatro clases de almacenamiento: automtica, externa,
esttica y registro. Estas clases tienen los siguientes nombres en C:
23
Auto
Las variables declaradas dentro de una funcin son por defecto auto, tal que.
{
char c;
int a,b,e;
}
Es lo mismo que:
{
auto char c;
auto int a,b,e;
}
Cuando un bloque de cdigo es introducido, el compilador asigna espacio RAM para las variables
declaradas. Las localizaciones RAM son utilizadas en ese bloque local de cdigo y puede ser
usado por otros bloques de cdigo.
Extern
La palabra clave extern declara una variable o una funcin y especfica que esta variable tiene
conexin externa. Esto significa que su nombre es visible en otros archivos diferentes en donde
fue definida. Extern no est definida en el compilador C de CCS.
Static
La clase static define variables globales activas que son inicializadas a cero, a menos que se
defina lo contrario.
void test()
{
char x,y,z;
static int cont = 4;
printf("cont = %d\n",++cont);
}
La variable cont es inicializada una vez, y despus se incrementa cada vez que la funcin test
es llamada.
Register
La clase register se origina de aplicaciones de sistemas donde esta puede ser usada para
reservar memoria de alta velocidad para variables usadas frecuentemente. Register no est
definida en el compilador C de CCS.
24
3. FUNCIONES
Las funciones son los bloques bsicos del lenguaje C. todas las instrucciones deben estar dentro de
funciones. En este captulo se discutir como pasar argumentos a funciones y como recibir un
argumento de una funcin.
FUNCIONES
void main()
{
f1();
}
int f1()
{
return 1;
}
En realidad, este programa producir un error. La razn es que la funcin f1() debe declararse o
definirse antes de usarse, tal como las variables. Si est empleando una funcin estndar de C, el
archivo de encabezado que incluye en el comienzo del programa ya ha informado al compilador
acerca de la funcin. Si est empleando una funcin que usted ha creado, hay dos caminos para
corregir este error. Una forma es usar funcin prototipo, que sern explicadas en la siguiente
seccin. La otra forma es reorganizar el programa de la siguiente manera:
int f1()
{
return 1;
}
void main()
{
f1();
}
El error ya no se genera porque la funcin f1() es definida antes que sea llamada en el main().
FUNCION PROTOTIPO
Hay dos mtodos empleados para informarle al compilador que tipo de valor retorna una funcin.
La forma general es la siguiente:
tipo NombreFuncion();
Por ejemplo, la declaracin en sum() podra decirle al compilador que la funcin sum() retorno
un entero. La segunda forma para informarle al compilador acerca del valor que retorna una
funcin es la funcin prototipo. Una funcin prototipo no solo entrega el valor a retornar de la
funcin, sino que tambin declara el nmero y tipo de los argumentos que la funcin acepta. El
prototipo debe corresponder con la declaracin de la funcin exactamente.
25
Los prototipos le ayudan a identificar errores en el programa reportando cualquier conversin
ilegal de tipos entre los argumentos que pasan a una funcin y en su declaracin. Adems reporta
si el nmero de argumentos enviados a una funcin no concuerdan con los especificados en la
declaracin de la funcin.
En el ejemplo anterior, el tipo de cada variable puede ser diferente. Un ejemplo de una funcin
prototipo se entrega en el siguiente programa. La funcin calcula el volumen definido por largo,
ancho y alto.
void main()
{
int vol;
vol = volumen(5,7,12);
printf("El volumen es: %d\n",vol);
}
int volumen(int s1, int s2, int s3)
{
return s1*s2*s3;
}
Note que return usa una expresin en lugar de una constante o variable.
La importancia de los prototipos no es significativa en programas cortos, como los que se han
realizado hasta ahora. Sin embargo, cuando el tamao de los programas crezca de unas pocas
lneas a cientos de stas, la importancia de los prototipos en la depuracin de errores ser
evidente.
Ejercicios
1. Para mostrar como los errores son capturados por el compilador, cambie el programa
anterior para mandar cuatro parmetros a la funcin volumen:
vol = volumen(5,7,12,15);
double myfunc(void);
void main()
{
printf("%f\n",myfunc(10.2));
}
double myfunc(double num)
{
return num/2.0;
}
26
VOID
Se hace una excepcin cuando una funcin no tiene ningn parmetro de entrada ni de salida.
Esta funcin podra declararse como sigue:
void NombreFuncion(void)
Un argumento de funcin es un valor que se le pasa a una funcin cuando se le hace un llamado.
C permite de ningn a varios argumentos en la declaracin de una funcin. El nmero de
argumentos que una funcin puede aceptar depende del compilador, pero el estndar C ANSI
especifica que una funcin debe ser capaz de aceptar al menos 31 argumentos.
Cuando una funcin es definida, deben ser declaradas variables especiales para recibir
parmetros. Estas variables especiales son definidas como parmetros formales. Los parmetros
son declarados entre los parntesis que siguen al nombre de la funcin. Por ejemplo, la funcin
que sigue calcula e imprime la suma de dos enteros que son enviados a la funcin cuando esta es
llamada.
void main()
{
sum(1,10);
sum(15,6);
sum(100,25);
}
void sum(int a, int b)
{
printf("%d\n",a+b);
}
27
Cuando sum() es llamada, el compilador copiar el valor de cada argumento dentro de las
variables a y b. es importante recordar que los valores pasados a una funcin (1, 10, 15, 6,
100, 25) son llamadas argumentos y las variables a y b son los parmetros formales.
Las funciones pueden pasar argumentos en dos formas. La primera forma es nombrada como
llamado por valor (call by value). Este mtodo copia el valor de un argumento dentro de un
parmetro formal en una funcin. Cualquier cambio hecho a un parmetro formal no afecta el
valor original de la rutina de llamado. El segundo mtodo es nombrado como llamado por
referencia (call by reference). En este mtodo, la direccin del argumento es copiada dentro del
parmetro formal de una funcin. Dentro de esta funcin, el parmetro formal se usa para
acceder a la variable actual en la rutina de llamado. Esto significa que se pueden realizar cambios a
la variable usando el parmetro formal. Este mtodo se discutir en el captulo sobre apuntadores.
Por ahora, solo se usar el primer mtodo cuando se pasen argumentos a una funcin.
Ejercicios
1. Escriba una funcin que tome un argumento entero e imprima su valor en pantalla.
2. Cul es el error en el siguiente programa?
imprimirEsto(int num)
{
printf("%d\n",num);
}
void main()
{
imprimirEsto (156.7);
}
Cualquier funcin en C puede retornar un valor a la rutina que realiza el llamado. Tpicamente, la
funcin es puesta al lado derecho del signo igual (=). El valor de retorno no necesariamente
necesita usarse en una instruccin asignada, pero puede usarse en una funcin printf(). El
formato general para informarle al compilador que una funcin retorna un valor es el siguiente:
Donde tipo especifica el tipo de dato del valor que retorna la funcin. Una funcin puede
retornar cualquier tipo de dato excepto un arreglo (array). Si no se especifica un tipo de dato,
entonces el compilador C asume que la funcin retorna un entero (int). Si su funcin no retorna
un valor, el estndar C ANSI especifica que la funcin debe retornar nada (void). Esto
explcitamente le informa al compilador que la funcin no retorna un valor. El siguiente ejemplo
muestra un uso tpico para una funcin que retorna un valor.
#include <stdio.h>
#include <math.h>
28
void main()
{
double result;
result = sqrt(16.0);
printf("%f\n",result);
}
Este programa hace un llamado a la funcin sqrt() la cual retorna un nmero en punto flotante.
Este nmero es asignado a la variable result. Note que el archivo encabezado math.h es
incluido debido a que contiene la informacin acerca sqrt() usada por el compilador.
Es importante que el tipo de dato que retorna la funcin sea el mismo tipo de dato de la variable a
la cual se le asignar el valor de retorno. Lo mismo se tiene que garantizar para los argumentos
que se entregan a la funcin.
Entonces, Cmo se debe retorna un valor desde una funcin? La forma general es la siguiente:
return NombreVariable;
Donde NombreVariable es una constante, variable o cualquier expresin valida en C que tenga
el mismo tipo de dato que el valor de retorno. El siguiente ejemplo muestra los dos tipos de
funciones.
int func();
int sum(int a, int b);
void main()
{
int num;
num = func();
printf("%d\n", num);
num = sum(5,127);
printf("%d\n",num);
}
int func()
{
return 6;
}
int sum(int a, int b)
{
int result;
result = a + b;
return result;
}
Una cosa importante para resaltar, es cuando una declaracin return es encontrada, la funcin
retorna inmediatamente a la rutina de llamado. Cualquier instruccin despus del return no
ser ejecutada. El valor retornado de una funcin no requiere ser asignado a una variable o usado
en alguna expresin, sin embargo, el valor se pierde de esta manera.
Ejercicio
29
void main()
{
double result;
result = f1();
printf("%f\n",result);
}
int f1()
{
return 60;
}
tipo NombreFuncion(var1,var2,,varn)
tipo var1;
tipo var2;
tipo varn;
{
<instrucciones>
}
Note que la declaracin est dividida en dos partes. Solo los nombres de los parmetros son
incluidos dentro de los parntesis. Por fuera de los parntesis el tipo de datos y los nombres
de los parmetros formales son especificados.
En este tipo de declaracin de funcin, tanto el tipo de dato como los nombres de los
parmetros formales son especificados dentro de los parntesis.
Ejercicios
void main(void)
{
printf("area = %d\n", area(10,15));
30
}
area(l,w)
int l,w;
{
return 1*w;
}
Debido a que los microcontroladores PIC tienen limitaciones en cuanto a acceso ROM, cadenas de
caracteres constantes (constant strings) no pueden ser pasadas a funciones de la manera
ordinaria.
El compilador C de CCS maneja esta situacin de una manera no estndar. Si una cadena de
caracteres es pasada a una funcin que permita solo un parmetro carcter, entones la funcin es
llamada para cada carcter en la cadena de caracteres. Por ejemplo:
void lcd_putc(char c)
{
. . . . .
}
lcd_putc("abcd");
Es lo mismo que:
lcd_putc("a");
lcd_putc("b");
lcd_putc("c");
lcd_putc("d");
31
4. OPERADORES
OPERADORES ARITMETICOS
El lenguaje C define cinco operadores aritmticos para la suma, resta, multiplicacin, divisin y
modulo.
+ suma
- resta
* multiplicacin
/ divisin
% modulo
Los operadores +,-,* y / pueden ser usados con cualquier tipo de dato. El operador modulo, %,
puede usarse solo con nmeros enteros. El operador modulo entrega residuo de una divisin
entre enteros. Por consiguiente, este operador no tiene significado cuando se aplica a nmeros en
punto flotante.
El operador puede usarse de dos formas, la primera siendo el operador de resta. La segunda
forma es para invertir el signo de un nmero. El siguiente ejemplo ilustra las dos formas de
manejar el signo menos:
a = a b; //resta
a = -a; //inversin de signo
Los operadores aritmticos pueden usarse con cualquier combinacin de constantes y/o variables.
Por ejemplo, la siguiente expresin es una instruccin valida en C.
C adems permite algunas abreviaturas cuando usa operadores aritmticos. Uno de los ejemplos
anteriores, a = a b; puede escribirse a-=b;. Este mtodo puede usarse con los operadores
+,-,* y /. El siguiente ejemplo muestra varias abreviaturas.
32
int a,b,c,;
a = b + c;
Se convierte en:
0007: MOVF b,W ;carga b
0008: ADDWF c,W ;suma c con b
0009: MOVWF a ;guarda en a
a = b - c;
Se convierte en:
0007: MOVF b,W ;carga b
0008: MOVWF a ;guarda en a
0009: MOVF c,W ;carga c
000A: SUBWF a,F ;resta c de a
Ejercicios
1. Escriba un programa en Microsoft Visual C++ Express que encuentre el residuo de 5/5,
5/4, 5/3, 5/2, y 5/1.
2. Escriba un programa en Microsoft Visual C++ Express que calcule el nmero de segundos
de un ao.
OPERADORES RELACIONALES
Una cosa a resaltar acerca de los operadores relacionales es que el resultado de la comparacin es
siempre 0 o 1, aun cuando C defina verdad como cualquier valor diferente de cero. Falso siempre
es definido como cero.
var > 15; //si var es menor o igual a 15, el resultado es 0 (falso)
var != 15; //si var es mayor o menor a 15, el resultado es 1 (verdadero)
Ejercicios
cont != 0;
33
OPERADORES LOGICOS
Los operadores lgicos soportan las operaciones lgicas bsicas AND, OR, y NOT. De nuevo, estos
operadores retornan 0 para falso o 1 para verdadero. Los operadores lgicos y la tabla de verdad
para estas operaciones se muestran a continuacin:
AND OR NOT
p q p && q p || q !p
0 0 0 0 1
0 1 0 1 1
1 0 0 1 0
1 1 1 1 0
Los operadores lgicos y relacionales estn estrechamente acoplados cuando se evala una
expresin. Un ejemplo de juntar estos operadores es el siguiente:
Otra parte de C que emplea los operadores relacionales y lgicos son las instrucciones de control
de programa que se estudiarn en el siguiente captulo.
Ejercicios
cont == 0;
result <= 5;
2. Dado que C no entrega explcitamente una funcin OR exclusiva, escriba una funcin XOR
basada en la siguiente tabla de verdad.
p q XOR
0 0 0
0 1 1
1 0 1
1 1 0
OPERADORES DE BIT A BIT
C contiene seis operadores especiales que desarrollan operaciones bit a bit sobre nmeros. Estas
operaciones de bit pueden usarse solo sobre tipos de datos enteros y caracteres. El resultado de
usar cualquiera de estas operaciones es una operacin bit a bit de los operandos. Los operadores
de bit a bit son los siguientes:
34
variable >> expresin
La nica situacin a resaltar acerca del uso de los desplazamientos es que un desplazamiento hacia
la izquierda es equivalente a multiplicar un nmero por 2 y un desplazamiento hacia la derecha es
equivalente a dividir un nmero por 2. Los operadores de desplazamiento son casi siempre ms
rpidos que su equivalente operacin aritmtica debido a cmo trabaja la CPU.
AND OR
00000101 (5) 00000101 (5)
& 00000110 (6) | 00000110 (6)
------------ ------------
00000100 (4) 00000111 (7)
NOTA: no realice desplazamientos mas all de los bits que el operando posea, debido a que se
obtiene un resultado indefinido.
a = b | c;
Se convierte en:
0007: MOVF b,W ;carga b
0008: IORWF c,W ;opera b OR c
0009: MOVWF a ;guarda en a
a = b & c;
Se convierte en:
0007: MOVF b,W ;carga b
0008: ANDWF c,W ;opera b AND c
0009: MOVWF a ;guarda en a
a = b >> 3;
Se convierte en:
0007: MOVF b,W ;carga b
0008: MOVWF a ;guarda en a
0009: RRF a,F ;rota el contenido
000A: RRF a,F ;hacia la derecha
000B: RRF a,F ;tres veces
000C: MOVLW 1F ;enmascara el contenido
000D: ANDWF a,F ;del registro para a
j = ~a;
35
Se convierte en:
0007: MOVF a,W ;carga a
0008: MOVWF j ;guarda en j
0009: COMF j,F ;complementa j
Ejercicios
1. Escriba un programa que invierta los bits ms significativos (MSB) de un dato tipo signed
char.
2. Escriba un programa que imprima la representacin binaria de un nmero tipo char.
Cmo podra usted incrementar o decrementar en una unidad una variable? Probablemente una
de dos formas le vendra a la mente. Tal vez las siguientes:
a = a + 1; o a = a - 1;
Una vez ms, los creadores de C han llegado con una notacin abreviada para incrementar o
decrementar un nmero. Los formatos generales son los siguientes:
int j, a = 3;
0007: MOVLW 03
0008: MOVWF a ;registro asignado a a
j = ++a;
0009: INCF a,F ;a = 4
000A: MOVF a,W ;carga a en w
000B: MOVWF j ;almacena w en j
j = a++;
000C: MOVF a,W ;carga el valor de a en w
000D: INCF a,F ;a = 5
000E: MOVWF j ;almacena w en j, j = 4
a = a++;
36
El siguiente ejemplo ilustra los dos usos.
#include <stdio.h>
void main(void)
{
int i,j;
i = 10;
j = i++;
printf("i = %d, j = %d\n",i,j);
i = 10;
j = ++i;
printf("i = %d, j = %d\n",i,j);
}
Mezclando operadores
Escribiendo Se obtine
sum = a+b++ sum = a+b b = b+1
sum = a+b-- sum = a+b b = b-1
sum = a+ ++b b = b+1 sum = a+b
sum = a+ --b b = b-1 sum = a+b
Ejercicios
void main(void)
{
int a, b;
a = 1;
a = a+1;
b = a;
b = b-1;
printf("a=%d, b=%d\n", a,b);
}
2. Cules son los valores de a y b despus de que el siguiente segmento de cdigo se ha
ejecutado?
a = 0;
b = 0;
a = ++a + b++;
a++;
b++;
b = --a + ++b;
PRECEDENCIA DE OPERADORES
La precedencia se refiere al orden en el cual los operadores son procesados por el compilador C.
por ejemplo, si la expresin a+b*c se encontrar en su programa, Qu operacin ocurrira en
primer lugar, la suma o la multiplicacin? El lenguaje C mantiene precedencia para todos los
operadores. La siguiente lista muestra la precedencia desde la ms alta a la ms baja.
37
Prioridad Operador Ejemplo
1 () ++ -- (a+b)/c parentesis
2 sizeof & * + - ~ ! ++ -- a=-b mas/menos/NOT/complemento
incremento/decremento/sizeof
3 * / % a%b multi/dividir/modulo
4 + - a+b suma/resta
5 << >> a=b>>c Desp izg. o derecho
6 < > <= >= a>=b mayor/menor/igual que
7 == != a==b
8 & a=b&c AND bit a bit
9 ^ a=b^c XOR bit a bit
10 | a=b|c OR bit a bit
11 && a=b&&c AND lgico
12 || a=b||c OR lgico
13 = *= /= %= += -= <<= >>= $= a+=b asignacin
^= |=
Algunos de estos operadores todava no los hemos estudiado, pero no se preocupe, los
estudiaremos ms adelante. Los parntesis pueden usarse para fijar el orden especfico en el cual
las operaciones se lleven a cabo.
Un par de ejemplos del uso del parntesis para clarificar o cambiar la precedencia de una
declaracin son los siguientes:
10-2*5 = 0
(10-2)*5 = 40
cont*sum+88/val-19%cont
(cont*sum) + (88/val) (19%cont)
38
5. DECLARACIONES DE CONTROL DEL PROGRAMA
En este captulo aprender acerca de las instrucciones que C usa para controlar el flujo de
ejecucin en un programa. Adems aprender como los operadores relacionales y lgicos se usan
con estas declaraciones de control.
DECLARACION if
La declaracin if es una instruccin del tipo condicional. El bloque de cdigo asociado con la
instruccin if es ejecutado basado en el resultado de una condicin. Nuevamente, cualquier valor
diferente de cero es verdadero y cualquier valor cero es falso. El formato ms sencillo es el
siguiente:
if (expresion)
{
.
instruccones;
.
}
Los corchetes {} se usan para encerrar el boque de cdigo. Esto le informa al compilador que si la
expresin es verdadera, ejecute el cdigo dentro de los corchetes. Ejemplo:
if (cont < 0)
{
Cont = 0;
printf("cuenta descendente\n");
}
if (TestMode == 1)
{
... Haga esto
}
x == y x igual a y
x != y x es diferente a y
x > y x es mayor que y
x < y x es menor que y
x <= y x es menor o igual que y
x >= y x es mayor o igual que y
x && y AND lgica
x || y OR lgica
int j, a = 3;
0007: MOVLW 03 ;carga a con 3
0008: MOVWF a
if (j == 2)
0009: MOVLW 02 ;carga w con 2
000A: SUBWF j,W ;testeo entre w y j
000B: BTFSS STATUS,Z ;si es cero salta
000C: GOTO 00F ;j no es igual a 2
{
j = a;
39
000D: MOVF a,W ;como es cero
000E: MOVWF j ;carga a en j
}
000F:
Ejercicios
a. 0
b. 1
c. -1
d. 5*5<25
e. 1==1
2. Escriba una funcin que le informe cuando un nmero sea par o impar. La funcin retorna
0 cuando el nmero sea par y 1 cuando el nmero sea impar.
DECLARACION if-else
Qu hara si se tienen dos bloques de cdigo que se ejecutan basados en el resultado de una
misma expresin? Si la expresin es verdadera, el primer bloque de cdigo se ejecuta, si la
expresin es falsa el segundo bloque de cdigo se ejecuta. Entonces, probablemente usted
empleara una declaracin if en combinacin con una declaracin else. El formato general para
la instruccin if-else es el siguiente:
if (expresion)
intruccion1;
else
intruccion2;
El formato para una declaracin if-else que use boques de cdigo (ms de una lnea) es el
siguiente:
if (expresion)
{
.
instruccones;
.
}
else
{
.
instruccones;
.
}
Tenga en cuenta que tanta la instruccin if como else pueden tener tantas instrucciones como
necesiten. Adems los corchetes pueden descartarse cuando solo haya una instruccin en
cualquiera de los dos casos. Un ejemplo de nica instruccin if-else es el siguiente:
if (num<0)
printf("Nmero negativo.\n");
else
printf("Nmero positivo.\n");
40
La adicin de la declaracin else proporciona dos opciones en la toma de decisiones. Pero qu
pasa si quiere combinar varias declaraciones if y else para realizar ms decisiones?
Coincidencia o no, C provee un mtodo con el cual usted puede combinar varios if con else
para suministrar diversos niveles de decisin. El formato general es el siguiente:
if (expresion1)
{
.
instruccones;
.
}
else if (expresion2)
{
.
instruccones;
.
}
else
{
.
instruccones;
.
}
Aqu se puede apreciar que se pueden evaluar las diferentes expresiones para elegir un bloque de
cdigo a ejecutar. Antes de explica ms acerca de este mtodo, aqu hay un ejemplo sencillo.
if(num == 1)
printf("lleva 1\n");
else if(num == 2)
printf("lleva 2\n");
else if(num == 3)
printf("lleva 3\n");
else
printf("no lleva nada\n");
NOTA: dentro de la declaracin if, hay que asegurar el correcto uso de los comparadores
sencillos y dobles, por ejemplo, los operadores sencillos &, = o | tiene el efecto de causar que la
funcin actu sobre la variable, opuesto a los operadores dobles &&, == o || que actan como
comparadores de la variable bajo prueba. Este es un error comn y no es fcil de encontrar en
cdigos extensos, debido a que el compilador no lo toma como error.
Ejercicios
if (cont>20)
printf("cont es mayor de 20");
count-- ;
}
2. Escriba un programa que imprima 1 peso, 5 pesos, 10 pesos, 20 pesos, 50 pesos o 100
pesos dependiendo del valor de una variable. Los nicos valores validos de la variable son
1, 5, 10, 20, 50 y 100.
41
OPERADOR ?
Donde cada expr es una declaracin C vlida. Primero, se evala expr1. Si el resultado es
VERDADERO (recuerde este es cualquier valor diferente de cero), entonces se evala expr2. Pero
si el resultado es FALSO (cero), entonces se evala expr3. Lo siguiente es un ejemplo del
operador ?
int i,j;
i = j;
i ? j=0 : j=1; o j=i ? 0 : 1;
Puesto que i es 1 o diferente de cero, ser evaluada la expresin j=0. Distinto a la declaracin if
el operador ? retorna un valor. Por ejemplo:
Donde i se incrementar a menos que este sea igual o mayor a 20, entonces se le asignar el valor
10.
DECLARACION for
Uno de los tres bucles o lazos (loops) que suministra C es el lazo for. Si tiene una instruccin o un
conjunto de instrucciones que necesitan repetirse, con un lazo for puede fcilmente
implementar esto. El formato bsico para un lazo for es similar en todos los lenguajes, como
BASIC o Pascal. La forma ms comn de un lazo for es la siguiente:
void main(void)
{
int i;
for(i=0; i<10; i++)
42
printf("%d ",i);
printf("hecho");
}
Este programa imprimir los nmeros 0-9 en pantalla. El programa trabaja de la siguiente manera:
primero la variable de cuenta del lazo, i, es puesta a cero; la siguiente expresin i<10 se evala.
Si esta declaracin es verdadera la instruccin printf("%d ",i); es ejecutada. Cada vez
despus que la instruccin printf("%d ",i); se ejecuta, la variable de cuenta del lazo es
incrementada. Este proceso continua hasta que la expresin i<10 se convierte en falsa. En este
punto, el lazo for termina su ejecucin y la instruccin printf("hecho"); es ejecutada
dando fin al programa.
int h,a;
for (h=0;h!=10;h++)
0007: CLRF h ;borra h
0008: MOVLW 0A ;carga 10 en w
0009: SUBWF h,W ;resta w de h
000A: BTFSC STATUS,Z ;y testeo por cero
000B: GOTO 00F ;si i=10, sale del lazo
a++;
000C: INCF a,F ;incrementa a
000D: INCF h,F ;increment h
000E: GOTO 008 ;regresa
000F:
Ejercicios
for(i=1; ;i++)
for( ; ; )
for(num=1; num; num++)
DECLARACION while
Otro bucle en C es el lazo while. Cuando una expresin es verdadera, el lazo while repite una
declaracin o un bloque de cdigo. El formato general es el siguiente:
while (expresion)
instruccion;
o
43
while (expresion)
{
instrucciones;
}
#include <16F84A.h>
#fuses XT,NOWDT, PUT, NOPROTECT
#use delay(clock=4000000)
#use RS232 (Baud=9600, xmit=pin_a3, RCV=pin_a4)
void main(void)
{
char ch;
printf("Escribe la letra q\n");
ch=getch();
while(ch!='q')
ch=getch();
printf("Escribiste la letra correcta!\n");
}
Habr notado que la primera declaracin pide un carcter desde el teclado. Luego la expresin es
evaluada. Siempre y cuando el valor de ch no sea igual a q, el programa continuar solicitando
otro carcter desde el teclado. Una vez una q se reciba, la funcin printf es ejecutado y el
programa termina.
Ejercicios
DECLARACION do-while
El ultimo tipo de bucle en C es el lazo do. Este se puede combinar con la declaracin while de la
siguiente manera:
do
{
instrucciones;
}
while(expresion)
44
En este caso las instrucciones siempre se ejecutan antes que expresin es evaluada (al menos
una sola vez). El parmetro expresin puede ser cualquier expresin vlida en C. un ejemplo de
un lazo do-while se muestra a continuacin:
#include <16F84A.h>
#fuses XT,NOWDT, PUT, NOPROTECT
#use delay(clock=4000000)
#use RS232 (Baud=9600, xmit=pin_a3, RCV=pin_a4)
void main(void)
{
char ch;
do
{
ch = getch();
}
while(ch != 'q');
printf("Escribiste la letra q!\n");
}
Este programa es equivalente al ejemplo estudiado en la seccin anterior.
Ejercicios
1. Reescriba los puntos a y b del ejercicio 1 de la seccin anterior usando el lazo do-while.
2. Reescriba el ejercicio 2 de la seccin anterior usando el lazo do-while.
Cuando el cuerpo de un lazo contiene a otro, el segundo lazo se dice que esta anidado dentro del
primero. Cualquier bucle o lazo de C u otra declaracin de control pueden anidarse dentro de
cualquier otra. El estndar C ANSI especifica que los compiladores debe tener al menos 15 niveles
de anidado.
i = 0;
while(i < 10)
{
for(j=0;j<10;j++)
printf("%d ",i*10+j);
i++;
}
Ejercicio
45
DECLARACION break
La declaracin break permite terminar cualquier lazo en cualquier punto dentro de su cuerpo.
Esta declaracin se desva de la terminacin normal de una expresin. Cuando la declaracin
break es encontrada en la ejecucin de un lazo, el programa salta a la siguiente lnea despus del
lazo. Por ejemplo:
void main(void)
{
int i;
for(i=0;i<50;i++)
{
printf("%d ",i);
if(i==15)
break;
}
}
Este programa imprime los nmeros 0-15 en pantalla. La declaracin break trabaja con todos los
lazos de C.
Ejercicios
DECLARACION continue
Vamos a asumir que cuando una cierta condicin ocurre en un lazo, usted quiere saltar al final del
lazo pero sin salir de este. C suministra la declaracin continue. Cuando el programa se
encuentra con esta declaracin, este salta todas las otras instrucciones entre continue y la
condicin de terminacin del lazo. Por ejemplo:
#include <16F84A.h>
#fuses XT,NOWDT, PUT, NOPROTECT
#use delay(clock=4000000)
#use RS232 (Baud=9600, xmit=pin_a3, RCV=pin_a4)
void main(void)
{
int i;
for(i=0;i<100;i++)
{
continue;
printf("%d ",i);
}
}
46
Este lazo nunca ejecutar la declaracin printf(). Cada vez que continue es alcanzado, el
programa salta el printf() y evala la expresin i<100 despus de incrementar i.
Una declaracin continue puede causar que el programa vaya directamente a la condicin de
terminacin para los lazos while y do-while, un continue puede causar que se ejecute la
parte incremental de un lazo y que se evale la condicin de terminacin.
DECLARACION switch
switch (variable)
{
case constante1:
instrucciones;
break;
case constante2:
instrucciones;
break;
case constanteN:
instrucciones;
break;
default:
instrucciones;
}
#include <stdio.h>
int main()
{
char ch;
for(;;)
{
ch = getchar();
if(ch=='x')
return 0;
switch(ch)
{
case '0':
printf("Domindo\n");
break;
case '1':
printf("Lunes\n");
break;
case '2':
printf("Martes\n");
break;
47
case '3':
printf("Miercoles\n");
break;
case '4':
printf("Jueves\n");
break;
case '5':
printf("Viernes\n");
break;
case '6':
printf("Sbado\n");
break;
default:
printf("Entrada Invalida\n");
}
}
}
Este ejemplo lee un nmero entre 0 y 6. Si el nmero esta fuera de este rango, el mensaje
Entrada Invalida se imprime. Valores dentro del rango se convierten en un da de la
semana.
El estndar ANSI declara que un compilador C debe soportar al menos 257 declaraciones case.
Dos declaraciones case no pueden tener el mismo valor dentro del mismo switch. Tambin las
declaraciones switch pueden anidarse, siempre y cuando los switch interiores no tengan
conflicto con valores de switch exteriores. Un compilador ANSI debe suministrar al menos 15
niveles de anidamiento para las declaraciones switch. A continuacin se muestra un ejemplo de
switches anidados.
switch (a)
{
case 1:
switch (b)
{
case 0:
printf("b es falso");
break;
case 1:
printf("b es verdadero");
break;
}
break;
case 2:
.
.
La declaracin break dentro de un switch tambin es opcional. Esto significa que dos
declaraciones case pueden compartir la mismo porcin de cdigo. Un ejemplo de esto se
muestra a continuacin.
void main(void)
{
int a=6,b=3;
char ch;
printf("S = Suma\n");
printf("R = Resta\n");
printf("M = Multiplicacion\n");
printf("D = Division\n");
48
printf("Escoja una Opcion:\n");
ch=getch();
switch (ch)
{
case 'R':
b=-b;
case 'S':
printf("\t\t%d",a+b);
break;
case 'M':
printf("\t\t%d",a*b);
break;
case 'D':
printf("\t\t%d",a/b);
break;
default:
printf("Opcion Incorrecta");
}
}
Ejercicios
float f;
switch(f)
{
case 10.05:
.
.
2. Use una declaracin switch para imprimir el valor de una moneda. El valor de la moneda
est contenido en la variable moneda. Las fases para describir las monedas son: de 20, de
50, de 100, de 200 y de 500.
3. Cules son las ventajas del uso de la declaracin switch sobre la declaracin if-
else?
DECLARACION null(;)
La declaracin null es una instruccin que contiene sola el smbolo punto y coma (;). Este puede
aparecer dondequiera que una declaracin sea esperada. Nada pasa cuando la declaracin null
es ejecutada, a diferencia de la instruccin NOP de Assembler, la cual introduce un ciclo de
retardo.
Declaraciones como do, for y while requieren que una instruccin ejecutable aparezca como el
cuerpo de la declaracin. La declaracin null satisface la sintaxis para esos casos.
for (i=0;i<10;linea[i++]=0)
;
En este ejemplo, la expresin del lazo for linea[i++]=0 inicializa los 10 primeros elementos
de linea a 0. La instruccin del cuerpo es un null, entonces no se requieren ms instrucciones.
49
DECLARACION return
La declaracin return termina la ejecucin de una funcin y retorna el control a la rutina que
hizo el llamado. Un valor puede retornarse a la funcin que realiza el llamado si es requerido pero
si este es omitido, el valor retornado es indefinido. Si no se incluye un return en la funcin, el
control es aun pasado a la funcin que hizo el llamado despus de ejecutar la ltima lnea de
cdigo. Si no se necesita retornar un valor, declare la funcin para ser del tipo void.
GetValue(c)
int c;
{
c++;
return c;
}
void GetNothing(c)
int c;
{
c++;
return;
}
void main()
{
int x;
x = GetValue();
GetNothing();
}
50
6. ARREGLOS Y CADENAS
En este captulo se discutirn los arreglos (array) y cadenas de caracteres (strings). Un arreglo es
simplemente una lista de variables relacionadas del mismo tipo de dato. Una cadena es definida
como un arreglo de caracteres, y tambin es conocido como el arreglo ms comn de una sola
dimensin (unidimensional).
ARREGLOS UNIDIMENSIONAL
Un arreglo es una lista de variables que son todas del mismo tipo de dato y que pueden
referenciarse a travs del mismo nombre. Una variable individual en un arreglo es llamada un
elemento del arreglo. Este es la manera sencilla de manejar grupos de datos relacionados.
int altura[50];
Cuando se declara un arreglo, C define el primer elemento con un ndice 0. Si el arreglo tiene 50
elementos, el ltimo elemento tiene ndice 49. Usando el ejemplo anterior, digamos que quiero
indexar el elemento 25 del arreglo altura y asgnale un valor de 60. El siguiente ejemplo
muestra cmo hacer esto.
altura[24] = 60;
int num[10];
int i;
for(i=0;i<10;i++)
0007: CLRF i ;borra i
0008: MOVLW 0A
0009: SUBWF i,W ;testea si i<10
000A: BTFSC STATUS,C
000B: GOTO 013 ;si no es as, deja la rutina
num[i] = i;
000C: MOVLW 0E ;carga el comienzo de num
000D: ADDWF i,W
000E: MOVWF 04
000F: MOVF i,W
0010: MOVWF 00
0011: INCF i,F
0012: GOTO 008
0013:
51
elemento 1 2 3 4 5 6 7 8 9 10
0 1 2 3 4 5 6 7 8 9
Cualquier elemento del arreglo puede usarse en cualquier lugar donde usted quiera emplear una
variable o una constante. A continuacin se muestra otro ejemplo hecho en el compilador C de
CCS, en este simplemente se le asigna el cuadrado del ndice al elemento del arreglo, luego se
imprimen todos los elementos.
#include <16F84A.h>
#fuses XT,NOWDT, PUT, NOPROTECT
#use delay(clock=4000000)
#use RS232 (Baud=9600, xmit=pin_a3, RCV=pin_a4)
void main(void)
{
int num[10];
int i;
for(i=0;i<10;i++)
num[i] = i * i;
for(i=0;i<10;i++)
printf("%d ",num[i]);
}
El ejemplo anterior es incorrecto. Si usted quiere copiar el contenido de un arreglo dentro de otro,
usted debe copiar cada elemento individual desde el primer arreglo dentro del segundo arreglo. El
siguiente ejemplo muestra como copiar el arreglo a[] dentro del b[] asumiendo que cada
arreglo tiene 20 elementos.
for(i=0;i<20;i++)
b[i] = a[i];
Ejercicios
int i;
char cont[10];
for(i=0;i<100;i++)
cont[i]=getch();
52
CADENA DE CARACTERES
#include <stdio.h>
void main(void)
{
char str[20];
int i;
printf("Escriba una cadena (<20 caracteres):\n");
gets(str);
for(i=0;str[i];i++)
printf("%c",str[i]);
printf("\n%s",str);
getchar();//termina el programa oprimiendo ENTER
}
En el ejemplo anterior se observa que la cadena puede imprimirse de dos maneras: como un
arreglo de caracteres usando %c o como una cadena usando %s.
Ejercicios
#include <string.h>
void main(void)
{
char str[10];
strcpy(str, "Microcontrolador");
printf(str);
}
2. Escriba un programa que lea una cadena de caracteres desde el teclado y los imprima en
orden inverso en pantalla.
53
ARREGLOS MULTIDIMENSIONALES
C no est limitado a los arreglos unidimensionales. Usted puede crear arreglos de dos o ms
dimensiones. Por ejemplo, para crear un arreglo entero llamado numero de dimensin 5x5,
puede hacerlo de la siguiente manera:
Los arreglos de dos dimensiones se usan de la misma manera que se usan los arreglos
unidimensionales. Por ejemplo, el siguiente programa carga un arreglo de 5x4 con el producto de
los ndices, entonces imprime el contenido del arreglo en el formato fila/columna.
#include <stdio.h>
void main(void)
{
int arreglo[5][4];
int i,j;
for(i=0;i<5;i++)
for(j=0;j<4;j++)
arreglo[i][j]=i*j;
for(i=0;i<5;i++)
{
for(j=0;j<4;j++)
printf("%d ",arreglo[i][j]);
printf("\n");
}
getchar(); // termina el programa oprimiendo ENTER
}
0 0 0 0
0 1 2 3
0 2 4 6
0 3 6 9
0 4 8 12
54
Como se puede dar cuenta, cuando se usa un arreglo multidimensional al nmero de variables
necesarias para acceder a cada elemento individual del arreglo s incrementan. Debido a la
capacidad de memoria del PIC16F84A, arreglos de 100 o 10x10 no son posibles. Sin embargo, un
arreglo de 50 elementos podra implementarse o podra utilizar un Microcontrolador con ms
capacidad de memoria como por ejemplo el PIC16F877A.
Ejercicios
1. Escriba un programa que declare un arreglo de 3x3x3 y lo cargue con los nmeros del 1 al
27. Imprima el arreglo en pantalla.
2. Empleando el programa del ejercicio 1, imprima la suma de cada fila y de cada columna.
INICIALIZANDO ARREGLOS
El parmetro ListaValores es una lista de constantes separadas por comas que son
compatibles con el tipo de arreglo. La primera constante ser asignada al primer elemento del
arreglo, la segunda constante al segundo elemento y as. El siguiente ejemplo muestra como se
inicializa un arreglo tipo entero de 5 elementos.
El Segundo mtodo consiste en usar una cadena entre comillas, como se muestra a continuacin:
Abra notado que no se utilizan corchetes para encerar la cadena, debido a que no son necesarios
en este tipo de inicializacin debido a que las cadenas en C deben terminar con un vacio. El
compilador automticamente aade un vacio al final de "John".
55
Ejercicios
2. Escriba un programa que defina una tabla con el valor al cuadrado y al cubo de un rango
de nmeros. Cada fila debe tener los nmeros, su valor al cuadrado y al cubo. Cree un
arreglo de 9x3 que mantenga esta informacin para los nmeros del 1 al 9. Debe
preguntarle al usuario por un nmero usando la funcin scanf("%d",&num);.
entonces imprime al nmero y su valor al cuadrado y al cubo.
ARREGLOS DE CADENAS
char nombres[10][40];
Esta instruccin especifica que el arreglo nombres contiene 10 nombres, de hasta 40 caracteres
de largo cada uno (incluyendo el vacio). Para acceder a una cadena de esta tabla, hay que definir
solo el primer ndice. Por ejemplo, para imprimir el quinto nombre de este arreglo, puede usar el
siguiente comando.
printf("%s",nombres[4]);
Lo mismo pasa para arreglos con dimensiones mayores a dos. Por ejemplo, si el arreglo
animales se declarara de la siguiente manera:
char animales[5][4][80];
Para acceder a una cadena especifica, debe usar las dos primeras dimensiones. Por ejemplo, para
acceder a la segunda cadena de la tercera lista, se escribe animales[2][1].
Ejercicio
1. Escriba un programa que cree una tabla que contenga las palabras para los nmeros del 0
al 9. Permita que el usuario escriba un digito para que entonces su programa imprima en
pantalla la palabra respectiva. Para obtener un ndice dentro de la tabla, reste cero al
carcter tecleado.
56
#include <string.h> //la librera de funciones string
char cadena[10]; //define un arreglo string
.
.
strcpy (cadena, "Hi There");//disposicin de caracteres dentro del arreglo
Tenga en cuenta que apuntadores a ROM no son validos en las microcontroladores PIC por lo que
usted no puede pasar una cadena constante a ninguna de estas funciones. strlen("Hola") no
es vlida. A continuacin se presenta otro ejemplo.
#include <stdio.h>
#include <string.h>
void main()
{
char s1[10], s2[10];
strcpy(s1,"abc");
strcpy(s2,"def");
strcat(s1,s2);
printf("%u\n",strlen(s1)); //imprime el nmero 6
printf(s1); //imprime abcdef
if(strcmp(s1,s2)!=0)
printf("\n No son Iguales");
getchar();
}
NOTA: Asegrese de que el tamao de los arreglos de cadenas sean acordes con el tamao de las
cadenas que se manipulen.
57
7. PUNTEROS
Un puntero es una posicin de memoria (variable) que guarda la direccin de otra posicin de
memoria. Por ejemplo, si una variable puntero a contiene la direccin de la variable b, entonces a
apunta a b. si b es una variable que est en la posicin 100 de la memoria. Entonces a debera
contener el valor 100.
La forma general para declarar una variable puntero es la siguiente:
type *NombreVariable;
En un puntero type es uno de los tipos de datos validos en C. Este especifica el tipo de variables a
las cuales NombreVariable puede apuntar. Usted habr notado que NombreVariable va
precedido por un asterisco *. Este le informa al compilador que NombreVariable es una
variable puntero. Por ejemplo, la siguiente declaracin crea un puntero a un tipo entero.
int *ptr;
Los dos operadores especiales que estn asociados con los punteros son *, &. La direccin de una
variable puede ser accesada colocando antes de la variable el operador &. El operador * retorna
el valor de la direccin apuntada por la variable. Por ejemplo:
#include <16F84A.h>
#fuses XT,NOWDT, PUT, NOPROTECT
#use delay(clock=4000000)
#use RS232 (Baud=9600, xmit=pin_a3, RCV=pin_a4)
void main(void)
{
int *a,b; //ms de 1 byte puede ser asignado a a
b = 6;
a = &b;
printf("%d",*a);
}
NOTA: por defecto, el compilador emplea 1 byte para punteros. Esto significa que solo las
posiciones de 0 hasta 255 pueden ser apuntadas. Para partes con una memoria ms extensa es
necesario usar un puntero de 2 bytes (16 bits). Para seleccionar un puntero de 16 bits, use la
siguiente directiva:
#device *=16
Sea consciente de que ms cdigo ROM se generara debido a que la aritmtica de 16 bits es
menos eficiente.
La primera instruccin declara dos variables: a, que es un puntero a entero y b, que es un entero.
La siguiente instruccin asigna el valor 6 a b. Lugo la direccin de b (&b) es asignada a la variable
puntero a. Esta lnea puede entenderse como la asignacin de a a la direccin de b. Finalmente, el
58
valor de b se imprime en pantalla usando el operador * con la variable puntero a. Esta lnea
puede imprimir el valor de la direccin apuntada por a. este proceso de referenciar un valor a
travs de un puntero es llamado direccionamiento indirecto. Un ejemplo grafico se muestra a
continuacin:
int i, j, k;
int *ptr;
Inicialmente, i es 3, &i es 100 (la direccin de i). Como ptr contiene el valor 102, *ptr es 5 (el
contenido de la direccin 102).
Es adems posible asignar un valor de posicin de memoria usando un puntero. Por ejemplo,
reestructuremos el programa anterior de la siguiente manera:
#include <16F84A.h>
#fuses XT,NOWDT, PUT, NOPROTECT
#use delay(clock=4000000)
#use RS232 (Baud=9600, xmit=pin_a3, RCV=pin_a4)
void main(void)
{
int *a,b;
a = &b;
*a=6;
printf("%d",b);
}
Ejercicio
1. Escriba un programa en Microsoft Visual C++ Express donde con un lazo for realice una
cuenta de 0 a 9 e imprima los nmeros en pantalla. Imprima los nmeros empleando un
puntero.
En general, los punteros pueden tratarse como variables. Sin embargo, existen algunas pocas
reglas y excepciones que se deben tener en cuenta. En adicin de los operadores *, &, hay solo
otros cuatro operadores que se pueden aplicar a punteros: +, ++, -, --. Solo cantidades enteras
pueden sumarse o restarse de variables puntero.
Cuando es incrementada una variable puntero, este apunta a la siguiente posicin de memoria. Si
por ejemplo asumimos que la variable puntero p contiene la direccin 100, despus de que la
instruccin p++; se ejecuta, p tendr el valor 102 asumiendo que los enteros tienen un tamao
de 2 bytes. Si p hubiera sido un puntero tipo float, p contendra el valor 104 despus del
59
incremento asumiendo que los nmeros en punto flotante tienen un tamao de 4 bytes. El nico
puntero que aparece como se espera es el de tipo char, debido a que los caracteres tienen un
tamao de 1 byte.
Usted puede sumarle o restarle cualquier valor entero que quiera, a un puntero o desde un
puntero. Por ejemplo, la siguiente instruccin:
int *p;
.
p = p+200;
Causa que p apunte desde la posicin de memoria actual 200 posiciones ms adelante.
Es posible incrementa o decrementar tanto el puntero como el objeto al cual apunta. Debe ser
cuidadoso cuando incrementa o decrementa el objeto apuntado por el puntero. Qu cree usted
que haga la siguiente instruccin si el valor de p es 1 antes de que la instruccin se ejecute?
*p++;
Esta instruccin obtiene al valor apuntado por p, luego incrementa p. para incrementar el objeto
que es apuntada por un puntero, use la siguiente instruccin:
(*p)++;
El parntesis causa que el valor apuntado por p se incremente. Esto debido a la precedencia de *
versus ++.
Los punteros tambin pueden usarse en operaciones relacionales. Sin embargo, estos solo tienen
sentido si el puntero si los punteros estn relacionados los unos con los otros, por ejemplo, si
apuntan al mismo objeto.
Los punteros no se pueden crear en ROM. Por ejemplo, la siguiente instruccin es ilegal:
Ejercicios
1. Declare las siguientes variables y asigne sus direcciones a la variable puntero. Imprima el
valor de cada variable usando %p. Luego incremente cada puntero e imprima el valor de la
variable nuevamente. Cules son los tamaos de cada tipo de dato en su ordenador?
char *cp,ch;
int *ip,i;
float *fp,f;
double *dp,d;
60
int *p,i;
p = &i;
p = p/2;
PUNTEROS Y ARREGLOS
Si usted emplea el nombre de un arreglo sin el ndice, entonces est usando un puntero que
direcciona al comienzo del arreglo. En el capitulo anterior, se uso la funcin gets(), con la cual
nosotros solo pasamos el nombre de la cadena. Lo que actualmente se pasa a una funcin, es un
puntero al primer elemento de la cadena. Nota importante: cuando un arreglo se entrega a una
funcin, solo un puntero que direcciona al primer elemento es entregado; estos no pueden
crearse para usarse con arreglos constantes o estructuras.
Debido a que el nombre de un arreglo sin un ndice es un puntero, puede asignar ese valor a otro
puntero. Esto te permitir acceder al arreglo usando la aritmtica puntero. Por ejemplo:
#include <stdio.h>
int a[5]={1,2,3,4,5};
void main(void)
{
int *p,i;
p=a;
for(i=0;i<5;i++)
printf("%d",*(p+i));
getchar();
}
Este es un programa claramente vlido en C. Abra notado que en la funcin printf() usamos
*(p+i), donde i es el ndice del arreglo. Adems se habr sorprendido del hecho de que
podemos colocar un ndice al puntero como si este fuera un arreglo. El siguiente programa
tambin es vlido.
#include <stdio.h>
int a[5]={1,2,3,4,5};
void main(void)
{
int *p,i;
p=a;
for(i=0;i<5;i++)
printf("%d",p[i]);
getchar();
}
Una cuestin a recordar es que un puntero solo podr indexarse cuando este apunte a un arreglo.
Debido a que los punteros a arreglos direccionan solo al primer elemento o base de la cadena, es
invalido incrementar el puntero. Por lo tanto, la instruccin p++; ser invalida utilizndola en el
programa anterior.
61
Ejercicios
int cont[10];
.
cont = cont+2;
int valor[5]={5,10,15,20,25};
int *p;
p = valor;
printf("%d",*p+3);
En el captulo 3, hablamos acerca de las dos formas en las que los argumentos podan pasar a las
funciones, llamado por valor y llamado por referencia. El segundo mtodo pasa la direccin a la
funcin, o en otras palabras un puntero es pasado a la funcin. En este punto cualquier cambio
hecho a las variables empleando el puntero cambiara el valor de la variable desde la rutina de
llamado. Los punteros pueden pasarse a funciones al igual que otras variables. El siguiente
ejemplo muestra como pasar una cadena a una funcin usando punteros.
#include <16F84A.h>
#fuses XT,NOWDT, PUT, NOPROTECT
#use delay(clock=4000000)
#use RS232 (Baud=9600, xmit=pin_a3, RCV=pin_a4)
62
{
int i=0;
IncBy10(i);
}
El ejemplo anterior puede reescribirse para mejorar la legibilidad, usando una clase especial de
parmetros de puntero llamado parmetro de referencia.
Ambos ejemplos muestran como retornar un valor desde una funcin por medio de la lista de
parmetros.
Ejercicios
1. Escriba un programa que pase un valor tipo float a una funcin. Dentro de esta
funcin, el valor -1 se asigna al parmetro de la funcin. Despus que la funcin retorna al
main, imprime el valor de la variable float.
2. Escriba un programa que el puntero fl a una funcin. Dentro de la funcin, el valor -1 se
asigna a la variable. Despus que la funcin retorna al main, imprime el valor de la
variable.
63
8. ESTRUCTURAS Y UNIONES
Las estructuras y uniones representan dos de los ms importantes tipos de datos definidos por
usuario que tiene C. Las estructuras son un grupo de variables relacionadas que pueden tener
diferentes tipos de datos. Las uniones son un grupo de variables que comparten el mismo especio
de memoria.
Una estructura es un grupo relacionado de elementos o tems que pueden ser accesados a travs
de un nombre comn. Cada tem dentro de una estructura tiene su propio tipo de dato, y pueden
ser diferentes entre ellos. C define las estructuras de la siguiente manera:
struct NombreEstructura
{
tipo elemento1;
tipo elemento2;
.
tipo elementoN;
} ListaVariables;
La palabra clave struct le informa al compilador que una estructura est por definirse. Dentro
de la estructura cada type es uno de los tipos de datos validos en C. estos tipos no tienen que ser
los mismos. NombreEstructura es el nombre con el cual vamos a identificar la estructura.
ListaVariables declara algunas variables que tienen un tipo de dato de la estructura.
ListaVariables es opcional. Cada uno de los elementos en la estructura es referenciado
comnmente como un campo o miembro. Nosotros nos referiremos a estos como miembros.
En general, la informacin almacenada en una estructura lgicamente debe estar relacionada. Por
ejemplo, usted podra emplear una estructura para guardar el nombre, direccin y nmero
telefnico de todos sus clientes o compaeros. El siguiente ejemplo es para una referencia
bibliogrfica en una biblioteca.
struct ReferenciaBib
{
char autor[40];
char titulo[40];
char editorial[40];
unsigned int pag;
unsigned char rev;
} tarjeta;
64
El operador es empleado para acceder a miembros de una estructura. Para imprimir el miembro
autor de la estructura ReferenciaBib, usted debe escribir lo siguiente:
autor 40 bytes
titulo 40 bytes
editorial 40 bytes
pag 2 bytes
rev 1 byte
Si usted quiere obtener la direccin del miembro pag de la estructura debe usar
&tarjeta.pag. Si quiere imprimir el nombre de la editorial, debe usar
printf("%s",tarjeta.editorial). Qu pasa si usted quiere acceder a un elemento
especifico del ttulo, por ejemplo la tercera letra de la cadena?, entonces debe usar:
Tarjeta.titulo[2];
El primer elemento del ttulo esta en 0, el segundo en 1 y, finalmente el tercero esta en 2. Una vez
usted ha definido una estructura, puede crear ms variables de estructura en cualquier lugar del
programa de la siguiente manera:
C le permite declarar arreglos de estructuras de la misma manera que cualquier otro tipo de dato.
El siguiente ejemplo declara un arreglo de 50 elementos para la estructura ReferenciaBib.
Si usted quiere acceder a una estructura individual dentro del arreglo, debe indexar la variable
estructura (i.e grande[10]). Cmo puede acceder al miembro titulo del elemento 10 del
arreglo estructura grande?, de la siguiente manera:
Grande[9].titulo;
Las estructuras tambin pueden pasarse a funciones. Una funcin puede retornar una estructura
de la misma manera que cualquier otro tipo de dato. Tambin puede asignar valores de una
estructura a otra simplemente usando una asignacin. El siguiente fragmento es perfectamente
vlido:
struct temp
{
int a;
float b;
char c;
65
} var1,var2;
var1.a=37;
var2.b=53.65;
var2 = var1;
Despus que este fragmento de cdigo ejecuta la estructura, la variable var2 tendr el mismo
contenido de la variable var1.
El siguiente es un ejemplo de cmo inicializar una estructura.
struct ejemplo
{
char nombre[50];
char ch;
int i;
} var1[2]={"Rodger", 'Y',27,"Jack",'N',30};
NOTA: Cuando pasa una estructura a una funcin, toda la estructura pasa empleando el mtodo
llamado por valor. Por consiguiente, cualquier modificacin hecha a la estructura en la funcin no
afectara el valor de la estructura en la rutuna de llamado. El nmero de elementos tampoco
afecta la forma en la cual es pasada a una funcin.
Un ejemplo de estructura emplendola sobre un PIC para configurar una interfaz con LCD podra
ser el siguiente:
struct cont_pins
{
boolean en1; //habilita para todos los displays
boolean en2; //habilita para displays de 40x4
boolean rs; //selector de registro
int data:4;
} cont;
#byte cont = 8; //control sobre el puerto d
Este pone la estructura para cont_pins para luego ser manejada dentro del programa
NOTA: La notacin :4 en data indica que se van a utilizar 4 bits par ese elemento. En este caso
D0 ser en1, y D3-D6 sern los datos.
void LcdSendNibble(byte n)
{
cont.data=n; //dato actual
delay_cycles(1); //retardo
cont.en1=1; //pone la linea en1 en ALTO
delay_us(2); //retardo de tiempo
cont.en1=0; //pone la linea en1 en BAJO
}
Ejercicios
1. Escriba un programa que tenga una estructura con un variable carcter y una cadena de
40 caracteres. Lea un carcter desde el teclado y gurdelo en la primera variable usando
getch(). Lea una cadena y gurdela en la segunda variable usando gets(). Por ltimo
imprima los valores de cada miembro en pantalla.
66
2. En el siguiente fragmento de cdigo cual es el error?
struct tipo
{
int i;
long l;
char str[50];
} s;
.
.
i = 10;
PUNTEROS A ESTRUCTURAS
Algunas veces es til poder acceder a una estructura a travs de un puntero. Punteros a
estructuras son declarados de la misma manera que punteros a otros tipos de datos. Por ejemplo,
el siguiente fragmento de cdigo declara una variable del tipo estructura p y un puntero a
estructura q con el tipo de estructura temp.
struct temp
{
int i;
char ch;
} p,q;
q->i=1;
Esta instruccin asigna el valor de 1 al nmero i de la variable p. Note que el operador flecha es
un signo menos seguido del signo mayor que sin ningn espacio entre estos.
Debido a que C pasa toda la estructura a una funcin, estructuras largas pueden reducir la
velocidad de ejecucin del programa debido a la gran cantidad de datos transferidos. Por esta
razn, es fcil pasar un apuntador a estructura a la funcin.
NOTA: Cuando se accese a un miembro de una estructura empleando una variable, use el punto.
Cuando accese a un miembro de una estructura empleando un puntero a estructura, usted debe
usar el operador flecha. El siguiente ejemplo muestra como un puntero a estructura debe
inicializase:
#include <16F84A.h>
#include <string.h>
#fuses XT,NOWDT, PUT, NOPROTECT
#use delay(clock=4000000)
#use RS232 (Baud=9600, xmit=pin_a3, RCV=pin_a4)
struct s_type
{
int i;
char str[40];
} s,*p;
67
void main(void)
{
p=&s;
s.i=10;
p->i=10;
strcpy(p->str,"Utilizo estructuras");
printf("%d %d %s",s.i,p->i,p->str);
}
Ejercicios
struct s_type
{
int a;
int b;
} s, *p;
void main(void)
{
p=&s;
p.a=100;
}
2. Escriba un programa que cree un arreglo de estructuras de tamao 3 para las familias de
PIC bsicas. Necesitara cargar la estructura con un dispositivo PIC12, PIC16 Y PIC 18. El
usuario seleccionara la estructura a imprimir usando el teclado para entrar 1, 2, o 3. El
formato de la estructura ser el siguiente:
struct PIC
{
char nombre[20];
unsigned char progmem;
unsigned char datamem;
char rasgo[80];
};
ESTRUCTURAS ANIDADAS
Hasta ahora, usted solo ha visto que los miembros de una estructura son uno de los tipos de datos
de C. sin embargo, los miembros de las estructuras tambin pueden ser otras estructuras. A esto
se le llama estructuras anidadas. Por ejemplos:
#define NumerodePICS 25
struct PIC
{
char nombre[40];
unsigned char progmem;
unsigned char datamem;
char rasgo[40];
};
struct productos
{
struct PIC dispositivos[NumerodePICS];
char tipoEncapsulado[40];
68
float precio;
} lista1;
Una unin es definida como una posicin de memoria sencilla que es compartida por dos o ms
variables. Las variables que comparten la misma posicin de memoria pueden tener diferentes
tipos de datos. Sin embargo, solo podemos usar una variable a la vez. Una unin se parece mucho
a una estructura. La forma general de una unin es la siguiente:
union NombreUnion
{
tipo elemento1;
tipo elemento2;
.
.
tipo elementoN
} ListaVaribles;
union u_type
{
int i;
char c[3];
double d;
} temp;
-----------------------------tipo double----------------------------
----c[2]---- ----c[1]---- ----c[0]----
---------entero(int)---------
Elemento0 Elemento1 Elemento2 Elemento3
Accesar los miembros de la unin se realiza de la misma manera que como en las estructuras, se
emplea el punto (.). La instruccin temp.i accesa al miembro tipo entero de dos bytes. Si quiere
acceder a una unin a travs de un puntero, debe usar el operador flecha (->) como se hizo para
las estructuras.
69
Un buen ejemplo de la aplicacin de las uniones es cuando un Microcontrolador de 8-bits tiene un
conversor A/D externo de de 12-bits conectado a un puerto serie. El Microcontrolador lee el A/D
en dos bytes. Entonces nosotros podramos configurar una unin que tenga dos unsigned
char y un signed short como miembros.
union muestra
{
unsigned char bytes[2];
signed short palabra;
}
Cuando quiera leer al A/D, leer dos bytes desde el A/D y los almacenara en el arreglo bytes.
Entonces, cuando quiera usar la muestra de 12-bits deber usar el miembro palabra para
acceder al nmero de 12-bits.
Ejercicios
1. Cules son las diferencias entre una estructura y una unin? Cules son las similitudes?
2. Escriba un programa que tenga una unin con un miembro long int y un arreglo de
caracteres de cuatro bytes. Su programa debe imprimir el miembro long int en
pantalla un byte a la vez.
70
9. LENGUAJE C ESPECIFICO PARA PIC
Habiendo estudiado las bases de C, es tiempo para continuar con las instrucciones, funciones y
operaciones relacionadas con el PIC. El compilador C de CCS tiene un extenso set de funciones
que ahorran tiempo, y aceleran el proceso de aprendizaje para los estudiantes y programadores
principiantes.
ENTRADAS Y SALIDAS
Los puertos de entrada/salida del PIC estn compuestos por dos registros: PORT y TRIS. Estos son
designados dependiendo de la disponibilidad de puertos del PIC en cuestin: PORTA, B, C, D, E y
TRISA, B, C, D, E. Un PIC de 8 pines tiene un solo registro GPIO y un TRIS para 6 lneas I/O. el
PIC16F84A posee 2 puertos (13 lneas I/O) por lo que tiene los siguientes registros: PORTA, PORTB,
TRISA, TRISB.
El puerto A tiene de 5 a 6 lneas dependiendo del PIC, las cuales pueden configurarse como
entradas o como salidas. Esto se hace por medio del registro TRISA, las entradas son configuradas
con un 1 y las salidas con un 0.
En Assembler para configurar individualmente los pines como entrada o salida se utilizan las
instrucciones BSF o BCF. Adems para leer el puerto se emplea la siguiente instruccin MOVF
PORTA,W.
NOTA: en dispositivos con conversores A/D, asegrese que el registro ADCON1 est configurado
correctamente, la configuracin I/O por defecto es ANALOGICO.
El compilador C puede interrumpir entradas y salidas de muchas maneras: fija, rpida, o estndar.
En el modo estndar, el registro TRIS es configurado antes de cada operacin I/O. esto aade
lneas a un programa y por lo tanto disminuye la velocidad, pero mejora la parte de seguridad del
cdigo asegurando que las lneas I/O estn siempre como se especificaron.
El modo I/O rpido habilita al usuario para configurar la direccin del puerto y este permanece as
hasta que sea redefinido. El compilador no aade lneas de cdigo para configurar la direccin del
puerto antes de cada operacin I/O.
El valor almacenado en b_valor puede usarse para establecer un valor de retorno a una funcin.
Lo siguiente es el conjunto de una funcin empleada para leer algunos dip switches y establecer
una velocidad de transmisin (baudios) para una rutina comn.
71
byte bd_sw_get() //seleccin de velocidad de transmisin
{
byte b_rate;
b_rate = portb & 0b00000011; //enmascara los bits no deseados
switch(b_rate)
{
case 0: set_uart_speed(1200);
break;
case 1: set_uart_speed(2400);
break;
case 2: set_uart_speed(4800);
break;
case 3: set_uart_speed(9600);
break;
}
}
Cuando configuramos el puerto B, es aconsejable establecer las condiciones del puerto antes que
el registro TRIS. Esto previene que el puerto genere una condicin no requerida antes de que sea
configurado. Cuando configure los bits en el registro o puertos, trabaje en forma binaria, esto har
que su cdigo fuente sea ms fcil de escribir para usted y ms fcil de entender para otros.
La manipulacin de datos desde y hacia los puertos I/O se realiza de forma sencilla con el uso de
las numerosas funciones construidas. Sobre el nivel de bit se tienen las siguientes:
port_b_pullups(true/false);
set_tris_a(value);
Establece la combinacin de entradas y salidas para un puerto dado (ponga 1 para entradas y 0
para salidas). Esta funcin aplica para todos los puertos.
El registro de direccin de puerto (TRIS) es configurado cada vez que un puerto es accesado a
menos que las siguientes directivas del preprocesador se usen:
#use fast_io(port)
72
Permanentemente configura el registro TRIS para el puerto
#use standard_io(port)
Por defecto para configurar el puerto cada vez que este se use.
MEZCLANDO C Y ASSEMBLER
Habr momentos en los cuales cdigo en Assembler ser requerido en nuestro programa escrito
en C. Buscando cdigo compacto, limitaciones en temporizaciones, o simplemente porque se
necesita alguna rutina especial. El siguiente ejemplo encuentra la paridad de un valor d pasado a
una rutina cuyo valor retornado es asignado a la variable a al momento de realizar el llamado.
BuscaParidad(byte d)
{
byte cont;
#asm
Movlw 8
Movwf cont
clrw
bucle:
xorwf d,w
rrf d,f
decfsz cont,f
goto bucle
movwf _return_
#endasm
}
void main(void)
{
byte a,d=7;
a=BuscaParidad(d);
}
BuscaParidad(byte d)
{
byte cont;
#asm
0005: MOVLW 8
0006: MOVWF cont
0007: CLRW
0008: XORWF 27,W
0009: RRF 27,F
000A: DECFSZ 28,F
000B: GOTO 008
#endasm
000C: MOVWF 21
000E: GOTO 016
}
void main(void)
{
0011: MOVLW 07
0012: MOVWF 26
byte a,d=7;
a=BuscaParidad(d);
73
0013: MOVF 26,W
0014: MOVWF 27
0015: GOTO 005
0016: MOVF 21,W
0017: MOVWF 25
}
Las 35 instrucciones fundamentalmente se dividen en tres tipos. Esta divisin viene dada por el
tipo de datos con los que trabajan:
Instrucciones orientadas a los registros o bytes (byte-oriented operations).
Instrucciones orientadas a los bits (bit-oriented operations).
Operaciones con literales y de control (literal and control operations).
74
Tabla 6. Instrucciones orientadas a bit
Instrucciones orientadas a bit
MNEMNICO
DESCRIPCIN CDIGO OP BANDERAS NCIC NOTAS
OPERANDOS
BCF f,b Pone a 0 bit b de registro f 01 00bb bfff ffff Ninguna 1 1,2
BSF f,b Pone a 1 bit b de registro f 01 01bb bfff ffff Ninguna 1 1,2
BTFSC f,b Salto si bit b de reg. f es 0 01 10bb bfff ffff Ninguna 1(2) 3
BTFSS f,b Salto si bit b de reg. f es 1 01 11bb bfff ffff Ninguna 1(2) 3
Fuente: http://perso.wanadoo.es/pictob/instrucciones.htm
75
estas funciones consideran el byte menos significativo en memoria como LSB. El segundo
parmetro es el nmero de bytes y el ltimo parmetro es el nuevo bit. Ejemplo:
long y;
struct { int a,b,c} z;
shitf_left(&y,2,0);
shitf_right(&z,3,0);
La funcin swap intercambia los 4 bits ms significativos con los 4 bits menos significativos de un
byte. Por ejemplo:
int x;
x = 0b10010110;
swap(x); //x es ahora 01101001
TEMPORIZADORES
Todas las gamas de PICs tienen un temporizador (timer) de 8-bits y adems algunos PICs tienen
dos temporizadores avanzados. Las capacidades se presentan a continuacin:
timer1 = 16Bit.
76
En el modo captura, la cuenta del timer1 puede guardarse en otro registro cuando el
estado de un pin de entrada cambia, una interrupcin puede generarse
En el modo captura, un pin de salida cambia su estado cuando la cuenta alcanza un valor
preestablecido, y una interrupcin puede generarse
Este temporizador se emplea como parte de la generacin de PWM (Modulacin por
anchura de pulsos)
timer2 = 8Bit.
En el siguiente ejemplo se usa rtcc (timer0) para temporizar cuanto tiempo dura un pulso en
estado ALTO.
#include <16F84A.h>
#fuses XT,NOWDT, PUT, NOPROTECT
#use delay(clock=1024000)
#use RS232 (Baud=9600, xmit=pin_a3, RCV=pin_a4)
void main(void) {
int time;
setup_counters(rtcc_internal, rtcc_div_256);
//incrementa 1024000/4*256 veces por segundo
//o cada milisegundo
while(input(PIN_B0)); //espera por estado ALTO
set_rtcc(0);
while(!input(PIN_B0)); //espera por estado BAJO
time = get_rtcc();
printf("tiempo en ALTO = %u ms.",time);
}
El siguiente ejemplo emplea el timer1 en el modo captura para temporizar cuanto tiempo le toma
al pin C2 cambiar al estado ALTO despus de que el pin B0
#include <16F877A.h>
#fuses XT,NOWDT, PUT, NOPROTECT
#use delay(clock=8000000)
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)
#bit capture_1 = 0x0c.2 //registro pir1
//bit 2 = la captura se ha realizado
void main(void)
{
long time;
setup_timer_1(t1_internal | t1_div_by_2);
//incrementa cada 1 us
setup_ccp1(ccp_capture_re);
//configura CCP1 para captura en flanco subida
capture_1=0;
set_timer1(0);
output_high(PIN_B0);
while(!capture_1);
77
time = ccp_1;
printf("Tiempo de reaccion = %1u us.",time);
}
COMVERSION A/D
Pag117
78