INTRODUCCION AL CURSO

57
1 INTRODUCCION AL CURSO. El objetivo de este curso, es introducir al estudiante, en el área de la automatización y diseño de dispositivos utilizando el Microcontrolador PIC, como una opción para resolver problemas, tal como se hace normalmente a través de la programación de PLC, diseño de circuitos de control eléctrico, controles digitales o combinación de estos. El PIC al ser un dispositivo programable, puede ser usado en una gran variedad de formas, utilizando, dispositivos que permitan adecuar las señales de entrada y salida, es capaz de realizar funciones aritméticas, lógicas, secuenciales, hacer conversiones de datos, comunicarse con otros dispositivos, comparaciones, almacenar datos, etc. Utilizaremos el lenguaje C, para aprender resolver problemas utilizando programación. El PIC que se utilizará en este curso, es el PIC16F877(A). EL PIC16F877(A) Este potente pic de 200 nanosegundos ejecución x instrucción, fácil de programar, solamente 35 instrucciones, CMOS FLASH-basado en Micro controladores de paquetes de 8 bits, Microchip PIC de arquitectura de gran alcance, en un encapsulado de 40 o 44 pines. Es compatible con los dispositivos PIC16C5X, PIC12CXXX y PIC16C7X. El PIC16F877 tiene las siguientes características: 256 bytes de memoria EEPROM de datos, programación libre, 8 canales de 10 bits para conversiones analógicas a digital (A / D), 3 temporizadores, 2 puertos de captura, comparadores o de funciones PWM, el puerto serie síncrono puede estar configurado a 3 cables de interfaz periférica serial (SPI) o el de 2 cables bus (I ² C) y un transmisor receptor asíncrono universal (USART). La distribución de los puertos del PIC 16F877 se muestra en el siguiente esquema.

Transcript of INTRODUCCION AL CURSO

1

INTRODUCCION AL CURSO.

El objetivo de este curso, es introducir al estudiante, en el área de la

automatización y diseño de dispositivos utilizando el Microcontrolador PIC,

como una opción para resolver problemas, tal como se hace normalmente a

través de la programación de PLC, diseño de circuitos de control eléctrico,

controles digitales o combinación de estos. El PIC al ser un dispositivo

programable, puede ser usado en una gran variedad de formas, utilizando,

dispositivos que permitan adecuar las señales de entrada y salida, es capaz de

realizar funciones aritméticas, lógicas, secuenciales, hacer conversiones de

datos, comunicarse con otros dispositivos, comparaciones, almacenar datos,

etc. Utilizaremos el lenguaje C, para aprender resolver problemas utilizando

programación. El PIC que se utilizará en este curso, es el PIC16F877(A).

EL PIC16F877(A)

Este potente pic de 200 nanosegundos ejecución x instrucción, fácil de

programar, solamente 35 instrucciones, CMOS FLASH-basado en Micro

controladores de paquetes de 8 bits, Microchip PIC de arquitectura de gran

alcance, en un encapsulado de 40 o 44 pines. Es compatible con los

dispositivos PIC16C5X, PIC12CXXX y PIC16C7X. El PIC16F877 tiene las

siguientes características: 256 bytes de memoria EEPROM de datos,

programación libre, 8 canales de 10 bits para conversiones analógicas a digital

(A / D), 3 temporizadores, 2 puertos de captura, comparadores o de funciones

PWM, el puerto serie síncrono puede estar configurado a 3 cables de interfaz

periférica serial (SPI) o el de 2 cables bus (I ² C) y un transmisor receptor

asíncrono universal (USART). La distribución de los puertos del PIC 16F877 se

muestra en el siguiente esquema.

2

Características de los Periféricos del PIC16F877

• Timer0: 8-bit temporizador/contador con un prescalar de 8-bits

• Timer1: 16-bit temporizador/contador con prescaler, puede ser

incrementado durante el ciclo SLEEP por medio de un oscilador externo

• Timer2: 8-bit temporizador/contador con registro de periodo de 8-bit

prescalar y postcalar

• Dos módulos de Captura, Comparación y PWM

- Máxima resolución es de 12.5 ns, captura de 16 bit.

- Comparación de 16 bits, resolución máxima 200 ns

- PWM max. resolución es 10-bit

• Convertidor A/D multicanal de 10-bit

• Puerto Serial Síncrono con SPI (Modo Maestro) y I2C (Maestro/Esclavo)

• Transmisor Receptor Universal Síncrono y Asíncrono con detección de

dirección de 9 bits

3

LENGUAJE C

C es un lenguaje de programación de propósito general que ofrece

economía sintáctica, control de flujo y estructuras sencillas y un buen conjunto de operadores. No es un lenguaje de muy alto nivel y más bien un lenguaje pequeño, sencillo y no está especializado en ningún tipo de aplicación. Esto lo hace un lenguaje potente, con un campo de aplicación ilimitado y sobre todo, se aprende rápidamente. En poco tiempo, un programador puede utilizar la totalidad del lenguaje.

Este lenguaje ha sido estrechamente ligado al sistema operativo UNIX,

puesto que fueron desarrollados conjuntamente. Sin embargo, este lenguaje

no está ligado a ningún sistema operativo ni a ninguna máquina concreta. Se

le suele llamar lenguaje de programación de sistemas, debido a su utilidad para

escribir compiladores y sistemas operativos, aunque de igual forma se puede

desarrollar cualquier tipo de aplicación.

La base del C proviene del BCPL, escrito por Martin Richards, y del B

escrito por Ken Thompson en 1970 para el primer sistema UNIX en un DEC

PDP-7. Estos son lenguajes sin tipos, al contrario que el C que proporciona

varios tipos de datos. Los tipos que ofrece son caracteres, números enteros y

en coma flotante, de varios tamaños. Además se pueden crear tipos derivados

mediante la utilización de punteros, vectores, registros y uniones. El primer

compilador de C fue escrito por Dennis Ritchie para un DEC PDP-11 y escribió

el propio sistema operativo en C.

C trabaja con tipos de datos que son directamente tratables por el hardware de la mayoría de las computadoras actuales, como son los caracteres, números y direcciones. Estos tipos de datos pueden ser manipulados por las operaciones aritméticas que proporcionan las computadoras. No proporciona mecanismos para tratar tipos de datos que no sean los básicos, debiendo ser el programador el que los desarrolle. Esto permite que el código generado sea muy eficiente y de ahí el éxito que ha tenido como lenguaje de desarrollo de sistemas. No proporciona otros mecanismos de almacenamiento de datos que no sea el estático y no proporciona mecanismos de entrada ni salida. Ello permite que el lenguaje sea reducido y los compiladores de fácil implementación en distintos sistemas.

4

Estructura básica de un programa en C

La mejor forma de aprender un lenguaje es programando con él. El programa más sencillo que se puede escribir en C es el siguiente:

main( )

{

}

Como nos podemos imaginar, este programa no hace nada, pero contiene la parte más importante de cualquier programa C y además, es el más pequeño que se puede escribir y que se compile correctamente. En el se define la función main, que es la que ejecuta el sistema operativo al llamar a un programa C. El nombre de una función C siempre va seguida de paréntesis, tanto si tiene argumentos como si no. La definición de la función está formada por un bloque de sentencias, que esta encerrado entre llaves {}.

Como estructura general utilizaremos los siguientes elementos al realizar un programa.

1. Comentarios. Los comentarios es texto que no se copilará, y ahí se

escribe la información del código, información del autor, procesador y cualquier dato que sea útil para quien lea el código. También se puede escribir comentarios para distinguir algunas rutinas o procesos, describiendo que función realiza.

2. Preprocesador, configuración. Aquí se selecciona el procesador para

el cual fue diseñado el programa, el registro de configuración, velocidad del oscilador y se configuran algunas funciones periféricas.

3. Definición de los Datos. Se definen variables, constantes, arreglos, etc.

4. Definición de Funciones. Aquí se definen las interrupciones, funciones, subrutinas y la función main.

Registro de Configuración

De alguna manera, los Microcontroladores PIC a diferencia de otros chips, tienen la capacidad de que ciertos parámetros pueden ser configurados cuando son energizados. Los parámetros son especificados cuando se escribe en un registro especial en la memoria de programa. Algunos de estos valores podrían causar que el PIC no encendiera o se reiniciara algunos minutos después.

De manera apropiada y sencilla iniciaremos un programa, con la siguiente estructura.

5

/*

EJEMPLO: AREA PARA COMENTARIOS

CODIGO DE EJEMPLO

UTILIZANDO EL PIC16F877(A)

*/

/* TAMBIEN PODEMOS COMENTAR EL TEXTO INICIANDO CON (/*)

Y CERRANDO LOS COMENTARIOS LINEAS DESPUES CON (*/)

*/

// AREA DE CONFIGURACIÓN //

#include <16F877.h> // CONFIGURA EL TIPO DE PIC A USAR

#fuses HS,NOWDT,NOPROTECT,LVP // REGISTRO DE CONFIGURACION

#use delay(clock=10000000) // OSCILADOR UTILIZADO

// SE VERA MAS A DETALLE

// DIRECCIONAMIENTO DE REGISTROS, DEFINICION DE CONSTANTES Y VARIABLES

#byte PORTD = 0x08

#byte TRISD = 0x88

#define VALOR 5 // ESCRIBIR VALOR ES IGUAL A UTILIZAR EL 5

#define FACT 120 // FACT ES IGUAL A UTILIZAR EL VALOR 120

int r;

char nombre;

float promedio;

// AREA PARA INTERRUPCIONES, SUBRUTINAS Y FUNCIONES

Void main ( )

{

………

}

Ejemplo 1

/*

6

El siguiente programa muestra como inicializar el puerto D

Y muestra la forma de cargar un dato por el puerto en binario, decimal y hexadecimal

*/

#include <16F877.h>

#fuses HS,NOWDT,NOPROTECT,LVP

#use delay(clock=10000000)

#byte PORTD = 0x08

#byte PORTD = 0x88

Void main () {

TRISD = 0; // configura todos los pines del puerto D como salida

PORTD = 0; // Puerto D = 0 en decimal

PORTD = 0b00000000; //Puerto D = 0 en binario

PORTD = 0x00; // Puerto D = 0 en Hexadecimal

}

EJERCICIO 1:

Utilizando el código del ejemplo anterior, probar igualando el puertoD con los siguientes datos, utilizando el siguiente formato Bit7 Bit 0, complete la tabla de abajo. Si el indicador enciende escriba 1 y si esta apagado escriba 0.

Decimal Puerto D Binario Puerto D Hexadecimal Puerto D

7 0b00000111 0x07

9 0b00001001 0x09

15 0b00001111 0x0F

6 0b00000110 0x06

128 0b10000000 0x80

7

Tipos básicos y variables

Los tipos de datos básicos definidos por C son caracteres, números enteros y números en coma flotante. Los caracteres son representados por char, los enteros por short, int, long y los números en coma flotante por float y double. Los tipos de variables básicas, disponibles cuando trabajamos con PIC´s, y su tamaño son:

VARIABLE DESCRIPCION

int1 Define un numero de 1 bit int8 Define un numero de 8 bits

int16 Define un numero de 16 bits int32 Define un numero de 32 bits char Define un carácter de 8 bits float Define un numero flotante a 32 bit short Define un numero igual que int1

Int Define un numero igual que int8 long Define un numero igual que int16

Las variables son definidas utilizando un identificador de tipo seguido del nombre de la variable. Veamos el siguiente programa:

void main()

{

float cels, farh;

farh = 35.0;

cels = 5.0 * ( farh - 32.0 ) / 9.0;

printf("-> %f F son %f C\n", farh, cels );

}

Expresiones y operadores

Los distintos operadores permiten formar expresiones tanto aritméticas como lógicas. Los operadores aritméticos y lógicos son:

8

+, - suma, resta

++, -- incremento, decremento

*, /, % multiplicación, división, módulo

>>, << rotación de bits a la derecha, izquierda.

& AND booleano

| OR booleano

^ EXOR u OREX booleano

! complemento, NOT lógico

==, != igualdad, desigualdad

&&, || AND, OR lógico

<, <= menor, menor o igual

>, >= mayor, mayor o igual

En estos operadores deben tenerse en cuenta la precedencia de operadores y las reglas asociativas, que son las normales en la mayoría de los lenguajes.

En la evaluación de expresiones lógicas, los compiladores normalmente utilizan técnicas de evaluación rápida. Para decidir si una expresión lógica es cierta o falsa muchas veces no es necesario evaluarla completamente. Por ejemplo una expresión formada <exp1> || <exp2>, el compilador evalúa primero <exp1> y si es cierta, no evalúa <exp2>. Por ello se deben evitar construcciones en las que se modifiquen valores de datos en la propia expresión, pues su comportamiento puede depender de la implementación del compilador o de la optimización utilizada en una compilación o en otra. Estos son errores que se pueden cometer fácilmente en C ya que una asignación es también una expresión.

Debemos evitar: if (( x++ > 3 ) || ( x < y ))

y escribir en su lugar: x++;

if (( x > 3 ) || ( x < y ))

9

Hay un tipo especial de expresión en C que se denomina expresión condicional y está representada por los operadores < ? : > . Su utilización es como sigue: <e> ? <x> : <y>. Se evalúa si e entonces x; si no, y.

int mayor ( int a, int b ) {

return ( a > b ) ? TRUE : FALSE;

}

waste_time () {

float a, b = 0.0;

( b > 0.0 ) ? a = 1 : a = 0;

}

Ejemplo 2: Operaciones Matemáticas

/*

PROGRAMA QUE UTILIZA UNA CONSTANTE, REALIZA UNA OPERACIÓN Y MUESTRA EL RESULTADO EN EL PUERTO D

*/

#include <16F877.h>

#fuses HS,NOWDT,NOPROTECT,LVP

#use delay(clock=10000000)

#byte PORTD = 0x08

#byte PORTD = 0x88

Int suma, resta, mult, div, modulo;

Void main() {

TRISD = 0x00; // El registro TRIS controla si el puerto es utilizado como

entrada o salida (I / O)

PORTD = 0;

10

Suma = 50 + 150;

Resta = 60 - 20;

Mult = 35 * 2;

Div = 120 / 2;

Modulo = 75 % 2;

PORTD = suma; // muestra por el puerto el valor de la variable suma

}

EJERCICIO 2. Modificar el programa para que realice las siguientes funciones y anote los resultados al final

a) Muestre el valor de la variable resta por el puerto D

b) Muestre el valor de la variable mult por el puerto D

c) Muestre el valor de la variable div + el valor de la variable modulo por el puerto D

d) Muestre el valor de cada variable, cada dos segundos por el puerto D.

e) Muestre el resultado de la división de las variables suma y resta, por el puerto D por 2 segundos y después muestre la suma de la variable Div y Mult por 5 segundos.

EJERCICIO 3. Utilizando la expresión condiciona ( <e>? <x>:<y>; ), haga las siguientes comparaciones

a) Compare cual de las variables suma y resta es mayor, y muestre el resultado por el puerto D

b) Compare cual de las variables Div y Mult es mayor, y muestre el resultado por el puerto D

c) Compare cual de las variables tienen el valor mayor y cual tiene el valor menor y muestre el valor mayor 2 seg y el valor menor por 2 seg.

Anote los resultados de los ejercicios 2 y 3, el nombre de la variable y el dato mostrado por el puerto binario siendo Bit7 Bit 0

11

Ejercicio 2

Variable dato mostrado

a) ______________ _______________

b)_______________ _______________

c)_______________ _______________

d)_______________ _______________

e)_______________ _______________

Ejercicio 3.

a)_______________ _________________

b)________________ ________________

c)________________ ________________

Ejemplo 3: Operaciones lógicas

/*

Este ejemplo muestra como capturar un dato a través de un puerto y utilizarlo para realizar la operación lógica And entre el bit 0 y el bit 1 del puerto D y mostrar el resultado en el bit 0 del puerto C, convirtiendo los datos de lógica negativa a lógica positiva

*/

#include <16F877.h>

#fuses HS,NOWDT,NOPROTECT,LVP

#use delay(clock=10000000)

#byte PORTC = 0x07

#byte TRISC = 0x87

#byte PORTD = 0x08

#byte TRISD = 0x88

char and, p_d0,p_d1,captura;

Void main() {

12

TRISC = 0x00;

TRISD = 0xFF; // configura al puerto D como entradas

PORTC = 0;

While (true) {

Captura = PORTD;

Captura = Captura ^ 0xff; // invierte el valor de la variable captura

P_d0 = Captura & 0x01; // hace cero los bits 7 1 sin alterar el bit 0

P_d1 = Captura & 0x02; // hace cero todos los bit, menos el bit 1

P_d1 = P_d1 >> 1 ; // Corre los bits 1 posición a la derecha

And = P_d0 & P_d1;

And = And ^ 0xff // invierte el valor para se mostrado correctamente

PORTC = And;

Delay_ms ( 100); }}

13

EJERCICIO 4. Utilizando el puerto C como salidas y el puerto D como entradas

defina las siguientes operaciones lógicas utilizando las siguientes entradas y salidas de los puertos, recuerde utilizar la operación corrimiento izq (<<) y corrimiento a la derecha (>>) para acomodar los resultados y realizar las operaciones, declare las variables que crea necesarias.

a) C0 = d0 and d1, c1 = d0 or d1, c2 = d0 orex d1; c3 = d0 nand d1, c4 = d0 nor d1, c5 = d0 norex d1, c6 = d0, c7 = d0

Complete la siguiente tabla de verdad con los resultados obtenidos del ejercicio

C7 C6 C5 C4 C3 C2 C1 C0

b) Hacer un programa que corra un bit del puerto c desde el bit 0 al bit 7 y viceversa, con un retardo de 1 s por posición. Utilice la función de comparación `? para determinar cuando llego al bit 7 o al bit 0

c) Hacer un programa que muestre por el puerto C los valores del 0 al 20, con incrementos de 1 cada segundo.

d) Hacer un programa que genere una onda cuadrada con las siguientes frecuencias utilizando el pin 0 del puerto C como salidas. Hacer los Cálculos para la frecuencia, señale la unidad de tiempo. ( F = 1 / P)

a. 5 Hz, P =_______________

b. 50 Hz, P =_______________

c. 100 Hz P =_______________

d. 500 Hz P =_______________

e. 1 KHz P =_______________

14

Estructuras de Control

Sentencia if

La sentencia de control básica es if (<e>) then <s> else <t>. En ella se evalúa una expresión condicional y si se cumple, se ejecuta la sentencia s; si no, se ejecuta la sentencia t. La segunda parte de la condición, else <t>, es opcional.

int cero ( double a )

{

if ( a == 0 )

return (TRUE);

else

return (FALSE);

}

En el caso que <e> no sea una expresión condicional y sea aritmética, se considera falso si vale 0; y si no, verdadero. Hay casos en los que se deben evaluar múltiples condiciones y únicamente se debe evaluar una de ellas.

Ejemplo 4:

/*

Este ejemplo muestra como utilizar la sentencia if, de la siguiente manera. Cuando se presiona el botón 0, se enciende la salida 0, cuando se presiona el botón 1 se enciende la salida 1, cuando se presionan ambos, se activa la salida 3 y mientras no se presione ningún botón todas las salida se mantienen apagadas. Utiliza el puerto D como entradas y el puerto C como salidas.

*/

#include <16F877.h>

#fuses HS,NOWDT,NOPROTECT,LVP

#use delay(clock=10000000)

#byte PORTC = 0x07

#byte PORTD = 0x08

#byte TRISC = 0x87

15

#byte TRISD = 0x88

int entrada,salidas;

void main (){

TRISC = 0x00;

TRISD = 0xFF;

PORTC = 0;

while (TRUE){

entrada = PORTD;

entrada = entrada ^ 0xff;

if ((entrada & 0x01) && (!(entrada & 0x02)))

salidas = 0x01;

else if ((entrada & 0x02) && (!(entrada & 0x01)))

salidas = 0x02;

else if (entrada & 0x03)

salidas = 0x04;

else

salidas = 0;

salidas = salidas ^ 0xff;

PORTC = salidas;

}

}

EJERCICIO 5. Modifique el ejemplo anterior para que además realice las siguientes funciones. Si se presiona el botón 3, botón 4 o botón 5, se activen las salidas 0, 2, 1.

EJERCICIO 6. Haga un programa utilizando las sentencias If, else, que realice las siguientes funciones. Botón 0, envía la secuencia 0x01,… al 0x0f en intervalos de 1 segundo; Botón 1, envía la secuencia 0x01, 0x03, 0x02, 0x04, 0x00 en intervalos de 500 ms. Botón 2. Envía la secuencia por el puerto C, 0x0E, 0x0C, 0x0A, 0x09, 0x07, 0x00. Determinar los valores para que se visualice correctamente los datos por el puerto C.

16

EJERCICIO 7. Haga un programa que envíe una onda cuadrada de 10 Hz, al

presionarse el Botón 0, al soltarse, continuara la onda durante 10 segundos.

17

Sentencia switch

Se puede programar con un grupo de sentencias if then else anidadas,

aunque ello puede ser tedioso y de complicada lectura. Para evitarlo, nos puede ayudar la sentencia switch.

Su utilización es:

switch (valor) {

case valor1: <sentencias>

case valor2: <sentencias>

...

default: <sentencias>

}

Cuando se encuentra una sentencia case que concuerda con el valor del switch se ejecutan las sentencias que le siguen y todas las demás a partir de ahí, a no ser que se introduzca una sentencia break para salir de la sentencia switch. Por ejemplo:

Ejemplo 5:

/* Este ejemplo muestra como utilizar la sentencia switch, case, default. Por

cada botón de entrada, se activará una sola salida. Si B0 se activa S7. Las

salidas estarán apagadas. Si no se activa alguna entrada o si se activan mas

de 1

*/

#include <16F877.h>

#fuses HS,NOWDT,NOPROTECT,LVP

#use delay(clock=10000000)

#byte PORTC = 0x07

#byte PORTD = 0x08

#byte TRISC = 0x87

#byte TRISD = 0x88

18

int entrada,salidas;

void main (){

TRISC = 0x00;

TRISD = 0xFF;

PORTC = 0;

while (TRUE){

entrada = PORTD;

entrada = entrada ^ 0xff;

. switch (entrada){

case 0x01: salidas = 0x80;

break;

case 0x02: salidas = 0x40;

break;

case 0x04: salidas = 0x20;

break;

case 0x08: salidas = 0x10;

break;

case 0x10: salidas = 0x08;

break;

case 0x20: salidas = 0x04;

break;

case 0x40: salidas = 0x02;

break;

case 0x80: salidas = 0x01;

break;

default: salidas = 0;

break;

19

}

salidas = salidas ^ 0xff;

PORTC = salidas;

}

}

EJERCICIOS 8 y 9. Modifique los ejercicios 1 y 2, de la sección sentencia IF,

utilizando sentencia switch.

20

Sentencia while

Otras sentencias de control de flujo son las que nos permiten realizar iteraciones sobre un conjunto de sentencias. En C tenemos tres formas principales de realizar iteraciones. La sentencia while (<e>) <s> es seguramente la más utilizada. La sentencia, o grupo de sentencias <s> se ejecuta mientras la evaluación de la expresión <e> sea verdadera.

long raiz ( long valor )

{

long r = 1;

while ( r * r <= valor )

r++;

return r;

}

Una variación de la sentencia while es: do <s> while ( <e> ); En ella la sentencia se ejecuta al menos una vez, antes de que se evalúe la expresión condicional.

EJEMPLO 6

/*

Utilizando la sentencia while, el programa genera una

onda cuadrada de 10Hz por el PIN 0 del puerto C,

mientras se presiona el Boton 0

*/

#include <16F877.h>

#fuses HS,NOWDT,NOPROTECT,LVP

#use delay(clock=10000000)

#byte PORTC = 0x07

#byte PORTD = 0x08

#byte TRISC = 0x87

#byte TRISD = 0x88

21

int entrada,salidas;

void main (){

TRISC = 0x00;

TRISD = 0xFF;

PORTC = 0xFF;

while (TRUE){

entrada = PORTD;

entrada = entrada ^ 0xff;

while (entrada & 0x01)

{

PORTC = PORTC ^ 0x01;

delay_ms(50);

entrada = PORTD ^ 0xff;

}

PORTC = 0xff;

}

}

EJERCICIO 10. Hacer un programa que genere una onda cuadrada con las

siguientes frecuencias utilizando el pin 0 del puerto C como salidas y las siguientes entradas. Hacer los Cálculos para la frecuencia, señale la unidad de tiempo. Boton 0 = 5Hz, Boton 1 = 50Hz, Boton 2 = 100 Hz, Boton 3 = 500 Hz Boton 4 = 1 KHz

EJERCICIO 11. Modificar el ejercicio anterior utilizando la sentencia do

22

Sentencia for

Otra sentencia iterativa, que permite inicializar los controles del bucle es la sentencia for ( <i>; <e>; <p> ) <s>. La sentencia for se puede escribir también como:

long raiz ( long valor )

{

long r;

for ( r = 1; r * r <= valor; ++r ) ;

return r;

}

EJEMPLO 7

/*

Este programa muestra los números del 0 al 255 en intervalos de 1 segundo y vuelve a iniciar

*/

#include <16F877.h>

#fuses HS,NOWDT,NOPROTECT,LVP

#use delay(clock=10000000)

#byte PORTC = 0x07

#byte TRISC = 0x87

int i,salida;

void main (){

TRISC = 0x00;

PORTC = 0xFF;

23

while (TRUE){

i=0x00;

for ( i = 0; i <= 0xff; ++i)

{

salida = i ^ 0xff;

PORTC = salida;

delay_ms(1000);

}

}

}

EJERCICIO 12. Modifique el ejemplo 7, para que muestre los números del 255 al 0 por el puerto C y vuelva a iniciar.

24

Break y Continue

Otras sentencias interesantes, aunque menos utilizadas son break y continue. break provoca que se termine la ejecución de una iteración o para salir de la sentencia switch, como ya hemos visto. En cambio, continue provoca que se comience una nueva iteración, evaluándose la expresión de control. Veamos dos ejemplos:

void final_countdown (void)

{

int count = 10;

while ( count--> 1 )

{

if ( count == 4 )

start_engines();

if ( status() == WARNING )

break;

printf("%d ", count );

}

if ( count == 0 ){

launch();

printf("Shuttle launched\n");

}

else

{

printf("WARNING condition received.\n");

printf("Count held at T - %d\n", count );

}

}

25

d2 ()

{

int f;

for ( f = 1; f <= 50; ++f ) {

if ( f == 10 )

continue;

printf("%d",f );

}

}

EJEMPLO 8.

/*

Este Programa cuenta de i = 0 a i = 100 si se presiona el boton 0 el programa se reinicia, si se presiona el boton 1 termina el conteo y enciende todas las lámparas del puerto de salida.

*/

#include <16F877.h>

#fuses HS,NOWDT,NOPROTECT,LVP

#use delay(clock=10000000)

#byte PORTC = 0x07

#byte PORTD = 0x08

#byte TRISC = 0x87

#byte TRISD = 0x88

int entrada;

int16 i;

void main (){

TRISC = 0x00;

TRISD = 0xFF;

26

PORTC = 0xFF;

entrada = PORTD;

entrada = entrada ^ 0xff;

i = 0;

for(i = 0; i<= 100; ++i)

{

delay_ms(500);

entrada = PORTD ^ 0xff;

if (entrada & 0x01)

{

i = 0;

PORTC = 0xff;

continue;

}

else if (entrada & 0x02)

break;

else

PORTC = i ^ 0xff;

}

PORTC = 0x00;

}

EJERCICIO 13. Utilizando la sentencia break y continue, iniciar una cuenta

regresiva de 10 segundos al presionar el botón 0, presionando el botón 1

apaga la cuenta regresiva, presionando el botón 2 reinicia la cuenta regresiva

siempre que no halla terminado.

Utilice las salidas 5 para indicar que se ha iniciado la cuenta regresiva, la

salida 6 para indicar que se apago o que aun no se ha iniciado la cuenta

regresiva.

27

Punteros

Cada variable de un programa tiene una dirección en la memoria del ordenador. Esta dirección indica la posición del primer byte que la variable ocupa. En el caso de una estructura es la dirección del primer campo. En los ordenadores actuales la dirección de inicio se considera la dirección baja de memoria. Como en cualquier caso las variables son almacenadas ordenadamente y de una forma predecible, es posible acceder a estas y manipularlas mediante otra variable que contenga su dirección. A este tipo de variables se les denomina punteros.

Los punteros C son el tipo más potente y seguramente la otra clave del éxito del lenguaje. La primera ventaja que obtenemos de los punteros es la posibilidad que nos dan de poder tratar con datos de un tamaño arbitrario sin tener que moverlos por la memoria. Esto puede ahorrar un tiempo de computación muy importante en algunos tipos de aplicaciones. También permiten que una función reciba y cambie el valor de una variable. Recordemos que todas las funciones C únicamente aceptan parámetros por valor. Mediante un puntero a una variable podemos modificarla indirectamente desde una función cualquiera.

Un puntero se declara de la forma: tipo *nombre;

float *pf;

char *pc;

Para manipular un puntero, como variable que es, se utiliza su nombre; pero para acceder a la variable a la que apunta se le debe preceder de *. A este proceso se le llama indirección. Accedemos indirectamente a una variable. Para trabajar con punteros existe un operador, &, que indica 'dirección de'. Con él se puede asignar a un puntero la dirección de una variable, o pasar como parámetro a una función.

void prueba_puntero ( void ) {

long edad;

long *p;

p = &edad;

edad = 50;

printf("La edad es %ld\n", edad );

*p = *p / 2;

printf("La edad es %ld\n", edad );

28

}

De forma similar se pueden utilizar funciones que tengan como parámetros punteros, para cambiar el valor de una variable. Veamos:

void intercambio ( void ) {

int a, b;

a = 1;

b = 2;

swap( &a, &b );

printf(" a = %d b = %d\n", a, b );

}

void swap ( int *x, int *y ) {

int tmp;

tmp = *x;

*x = *y;

*y = tmp;

}

EJEMPLO 9

/*

Este Programa cuenta de i = 0 a i = 100 si se presiona el boton 0 el conteo se reinicia, si se presiona el boton 1 termina el conteo enciende todas las lámparas del puerto de salida, utiliza punteros para invertir los datos a cargar en los puertos de entrada y salida.

*/

#include <16F877.h>

#fuses HS,NOWDT,NOPROTECT,LVP

#use delay(clock=10000000)

29

#byte PORTC = 0x07

#byte PORTD = 0x08

#byte TRISC = 0x87

#byte TRISD = 0x88

int entrada,salida;

int16 i;

char *c_entrada,*c_salida;

void main (){

TRISC = 0x00;

TRISD = 0xFF;

c_entrada = &entrada;

c_salida = &salida;

*c_entrada = PORTD ^0xff;

*c_salida = 0xff;

PORTC = salida;

i = 0;

for( i = 0; i<= 100; ++I )

{

PORTC = salida;

*c_salida = i ^0xff;

delay_ms(500);

*c_entrada = PORTD ^ 0xff;

if (entrada & 0x01)

{

i = 0;

*c_salida = 0xff;

30

continue;

}

else if (entrada & 0x02)

break;

}

*c_salida = 0x00;

PORTC = salida;

}

EJERCICIO 14. Haga un programa que utilice punteros para realizar operaciones lógicas entre los pines de entrada 0 y 1 del puerto D y el resultado es mostrado en los pines 0,1,2,3,4,5 del puerto C para las funciones lógicas And, Or, Orex, Nand, Nor, Norex.

31

Funciones

Un programa C está formado por un conjunto de funciones que al menos contiene la función main. Una función se declara con el nombre de la función precedido del tipo de valor que retorna y una lista de argumentos encerrados entre paréntesis. El cuerpo de la función está formado por un conjunto de declaraciones y de sentencias comprendidas entre llaves. Veamos un ejemplo de utilización de funciones:

#include <stdio.h>

#define VALORR 5

#define FACT 120

int fact_i ( int v )

{

int r = 1, i = 0;

while ( i <= v )

{

r = r * i;

i = i + 1;

}

return r;

}

main() {

int r, valor = VALORR;

r = fact_i(VALORR);

if ( r == FACT )

printf("Codificación correcta.\n");

else

printf("Algo falla!!.\n");

}

32

Se define la función, fact_i, además de la función main. Toman como parámetro un valor entero y devuelven otro entero. Calcula el factorial de un número de forma iterativa.

Todas las líneas que comienzan con el símbolo # indican una directiva del precompilador. Antes de realizar la compilación en C se llama a un precompilador cuya misión es procesar el texto y realizar ciertas sustituciones textuales. Hemos visto que la directiva #include incluye el texto contenido en un fichero en el fuente que estamos compilando. De forma parecida, #define nombre texto sustituye todas las apariciones de nombre por texto. Así, en el fuente, la palabra VALORR se sustituye por el número 5.

El valor que debe devolver una función se indica con la palabra return. La evaluación de la expresión debe dar un valor del mismo tipo de dato que el que se ha definido como resultado. La declaración de una variable puede incluir una inicialización en la misma declaración.

Se debe tener muy en cuenta que en C todos los argumentos son pasados 'por valor'. No existe el concepto de paso de parámetros 'por variable' o 'por referencia'. Veamos un ejemplo:

int incr ( int v ) { return v + 1; }

main() {

int a, b;

b = 3;

a = incr(b);

/* a = 4 mientras que b = 3. No ha cambiado después de la llamada. */

}

En el ejemplo anterior el valor del parámetro de la función incr, aunque se modifique dentro de la función, no cambia el valor de la variable b de la función main. Todo el texto comprendido entre los caracteres /* y */ son comentarios al programa y son ignorados por el compilador. En un fuente C los comentarios no se pueden anidar.

33

EJEMPLO 10.

/*El siguiente programa utiliza el puerto D como entradas y el puerto C como salidas y realiza las siguientes funciones

Puerto D

Botón D0 entrada de dato 0

Botón D1 entrada de dato 1

Puerto C

C0 muestra el resultado And entre D0 y D1

C1 muestra el resultado OR entre D0 y D1

Utiliza la función llamada salida y regresará un valor tipo carácter de acuerdo al valor introducido char x

*/

#include <16F877.h>

#fuses HS,NOWDT,NOPROTECT,LVP

#use delay(clock=10000000)

#byte PORTC = 0x07

#byte PORTD = 0x08

#byte TRISC = 0x87

#byte TRISD = 0x88

char salida(char x){

char d0,d1,c_and,c_or,c_salida;

x = x ^ 0xff;

d0 = x & 0x01;

d1 = x & 0x02;

d1 = d1 >> 1;

c_and = d0 & d1;

c_or = d0 | d1;

c_or = c_or << 1;

34

c_salida = c_or + c_and;

c_salida = c_salida ^ 0xff;

return c_salida; // regresa el valor en la variable c_salida

}

void main (){

TRISC = 0;

TRISD = 0xff;

PORTC = 0xff;

while(true){

delay_ms(25);

PORTC = salida(PORTD);

}

}

35

EJEMPLO 11

/*El siguiente programa utiliza una función para realizar las siguientes operaciones utilizando apuntadores

Puerto D

D0 entrada de dato 0

D1 entrada de dato 1

Puerto C

C0 muestra el resultado And entre D0 y D1

C1 muestra el resultado OR entre D0 y D1

Utiliza la función salida apuntando a los registros del puerto C y puerto D directamente

*/

#include <16F877.h>

#fuses HS,NOWDT,NOPROTECT,LVP

#use delay(clock=10000000)

#byte PORTC = 0x07

#byte PORTD = 0x08

#byte TRISC = 0x87

#byte TRISD = 0x88

void salida(char *in,char *out){

char boton,d0,d1,c_and,c_or,c_salida;

boton = *in ^ 0xff; // guarda e invierte el valor depositado en el apuntador in

// en la variable botón

d0 = boton & 0x01;

d1 = boton & 0x02;

d1 = d1 >> 1;

36

c_and = d0 & d1;

c_or = d0 | d1;

c_or = c_or << 1;

*out = (c_and + c_or) ^ 0xff;; // carga la operación en la variable que apunta

// *out

}

void main (){

TRISC = 0;

TRISD = 0xff;

PORTC = 0xff;

while(true){

delay_ms(25);

salida(&PORTD,&PORTC);

}

}

EJERCICIO 15. Modifique el ejemplo anterior creando una función que invierta el valor de cualquier registro de 8 bits, y otra función que pueda realizar las siguientes operaciones lógicas.

Puerto D configurarlo como entradas D0: entrada dato 0

D1: entrada dato 1

D2: entrada dato 2

D4: Muestra el resultado de la operación AND entre D0,D1,D2

D5: Muestra el resultado de la operación OR entre D0,D1,D2

D6: Muestra el resultado de la operación NAND entre D0, D1, D2

D7: Muestra el resultado de la operación NOR entre D0, D1, D2

Muestra el resultado por el puerto C

C0: Muestra el resultado de las operaciones lógicas

C4: Indica que se realizó una operación AND

37

C5: Indica que se realizó una operación OR

C6: Indica que se realizó una operación D0 NAND D2 AND D1

C7: Indica que se realizó una operación D0 NOR D1 AND D2

EJERCICIO 16. Haga el problema anterior para que las operaciones seleccionadas se mantengan en memoria al presionar el botón correspondiente a la función y muestre cualquier cambio en el resultado de acuerdo a los valores de los botones 0, 1 y 2, utilice apuntadores para resolver el problema.

38

Definición y prototipos de funciones

Los programas sencillos, como los ejemplo planteados hasta ahora, normalmente no necesitan un nivel de estructuración elevado. Pero cuando éstos crecen un poco necesitamos estructurarlos adecuadamente para mantenerlos legibles, facilitar su mantenimiento y para poder reutilizar ciertas porciones de código. El mecanismo C que nos permite esto son las funciones. Con los compiladores, los fabricantes nos proporcionan un conjunto importante de funciones de librería. A veces, nos puede interesar construir nuestras propias librerías. Ya hemos utilizado funciones, pero veamos cómo debemos definirlas.

Los prototipos de funciones son una característica clave de la recomendación ANSI del C. Un prototipo es una declaración que toma la forma:

tipo_resultado nombre_función ( tipo_parámetro nombre_parámetro ... );

int fact_i ( int v );

int mayor ( int a, int b );

int cero ( double a );

long raiz ( long valor );

void final_countdown ( void );

int main ( int argc, char **argv );

Observando el prototipo de una función podemos decir exactamente que tipo de parámetros necesita y que resultado devuelve. Si una función tiene como argumento void, quiere decir que no tiene argumentos, al igual que si el resultado es void, no devuelve ningún valor.

En la vieja definición de Kernighan y Ritchie el tipo que devolvía una función se declaraba únicamente si era distinto de int. Similarmente, los parámetros eran declarados en el cuerpo de la función, en lugar de utilizar la lista de parámetros. Por ejemplo:

mayor ( a, b )

int a;

int b;

{

...

}

39

Las funciones al viejo estilo se compilan correctamente en muchos compiladores actuales. Por contra, proporcionan menos información sobre sus parámetros y errores que afecten al tipo de parámetros de llamada a las funciones no pueden ser detectados automáticamente. Por tanto, la declaración de una función debe escribirse igual que su prototipo pero sin el punto y coma final. El cuerpo de la función le sigue encerrado entre llaves.

En un programa que esté formado por distintas partes bien diferenciadas es conveniente utilizar múltiples ficheros fuente. Cada fuente agrupa las funciones semejantes, como por ejemplo en un compilador podríamos tener un fuente para el análisis léxico, otro para el sintáctico y otro para la generación de código. Pero en un fuente necesitaremos funciones que se han definido en otro. Para ello, escribiremos, un fichero de cabecera (header), que contendrá las declaraciones que podemos necesitar en otros fuente. Así, en el fuente que implementa el analizador sintáctico pondremos una línea #include "lexic.h". De esta forma al compilar el módulo sintáctico tendremos todos los prototipos de las funciones del léxico y el compilador podrá detectar malas utilizaciones de las funciones allí definidas.

EJERCICIO 17. Escriba un archivo que contenga las funciones de los ejercicios

15, 16 y modifique ambos ejercicios.

EJEMPLO. Utilizando la librería LCD.c, realice lo siguiente:

Configure para utilizar el puerto B, Inicialice la pantalla y escriba el mensaje Hola Mundo y en el siguiente renglón Primer Mensaje

/*Ejemplo LCD

Inicializa el LCD y escribe: Hola Mundo

Primer Mensaje */

#include <16F877.h>

#fuses HS,NOWDT,NOPROTECT,LVP

#use delay(clock=10000000)

#include <LCD.c>

#byte PORTC = 0x07

#byte PORTD = 0x08

#byte TRISC = 0x87

#byte TRISD = 0x88

void main (){

40

char opcion;

TRISC = 0;

TRISD = 0xff;

PORTC = 0xff;

opcion = 1;

lcd_init();

lcd_putc("H");

lcd_putc("o");

lcd_putc("l");

lcd_putc("a");

lcd_putc(" ");

lcd_putc("M");

lcd_putc("u");

lcd_putc("n");

lcd_putc("d");

lcd_putc("o");

lcd_putc("\n");

lcd_putc("P");

lcd_putc("r");

lcd_putc("i");

lcd_putc("m");

lcd_putc("e");

lcd_putc("r");

lcd_putc(" ");

lcd_putc("M");

lcd_putc("e");

lcd_putc("n");

lcd_putc("s");

lcd_putc("a");

41

lcd_putc("j");

lcd_putc("e");

while (true) {

}

}

42

Construcción de tipos

Los datos del mundo real, normalmente no están formados por variables escalares de tipos los tipos básicos. Por ejemplo, nos puede interesar saber cuántos módulos en C hemos escrito cada semana, a lo largo del año. O también nos interesa tener los datos de cada planeta del Sistema Solar, masa, posición, velocidad y aceleración, para un programa de simulación de la ley de gravitación de Newton. Para resolver el primer caso, C nos permite declarar una variable que sea de tipo vector. Para el segundo, podemos definir un registro para cada elemento.

Vectores

Un vector es una porción de memoria que es utilizada para almacenar un grupo de elementos del mismo tipo Un vector se declara: tipo nombre [tamaño];. Por ejemplo, int modulo[52];. Aquí 'modulo' es un vector de 52 elementos enteros.

main()

{

int f, modulo[52];

for ( f = 0; f< 52; f++ )

modulo[f] = 0;

...

}

Cada elemento de un vector es accedido mediante un número de índice y se comporta como una variable del tipo base del vector. Los elementos de un vector son accedidos por índices que van desde 0 hasta N-1 para un vector de N elementos. Los elementos de un vector pueden ser inicializados en la misma declaración:

char vocal[5] = {'a', 'e', 'i', 'o', 'u' };

float n_Bode[5] = { 0.4, 0.7, 1, 1.6, 2.8 };

También podemos definir vectores multidimensionales. C no impone ninguna limitación al número de dimensiones de un vector. Existe, en cambio, la limitación del tamaño de memoria que podamos utilizar en nuestro ordenador. Por ejemplo, para la declaración de un vector multidimensional podemos escribir: Hay que considerar que cada procesador tiene, una capacidad limitada de memoria RAM, al momento de definir el vector con el que trabajara el nuestra aplicación.

43

int video[25][80][2];

Un tipo vector muy utilizado es la cadena de caracteres (string). Si queremos asignar espacio para un string podemos hacer:

char nombre[60], direccion[80];

Es un vector C pero con la particularidad de que el propio lenguaje utiliza un carácter especial como marca de final de string. Así en un vector de caracteres de tamaño N podremos almacenar una cadena de N-1 caracteres, cuyo último carácter estará en la posición N-2 y la marca de final de string en la N-1. Veamos un ejemplo:

char servei[6] = "SCI";

La posición 0 contiene el carácter 'S'; la 1 el 'C'; la 2 el 'I'; la 3 el '\0', marca de final de string. El resto de componentes no están definidas. En la inicialización de strings no se debe indicar el final; ya lo hace el compilador. Para la manipulación de cadenas de caracteres ANSI proporciona el fichero string.h que contiene las declaraciones de un conjunto de funciones proporcionadas con la librería del compilador.

EJEMPLO 1: Escribe Hola Mundo utilizando un ciclo For y un vector que

contiene la cadena “HOLA MUNDO”

/*EJEMPLO 1 Vectores

*/

#include <16F877.h>

#fuses HS,NOWDT,NOPROTECT,LVP

#use delay(clock=10000000)

#include <lcd.c>

#byte PORTC = 0x07

44

#byte PORTD = 0x08

#byte TRISC = 0x87

#byte TRISD = 0x88

void main (){

int i;

char cadena[12] = "HOLA MUNDO\n";

TRISC = 0;

TRISD = 0xff;

PORTC = 0xff;

lcd_init();

for(i = 0; i <= 10; ++i)

{

lcd_putc(cadena[i]);

}

}

45

Estructuras

Un registro agrupa distintos tipos de datos en una misma estructura. Los registros son definidos de la forma:

struct nombre{ lista de declaraciones };

Los campos de cada registro pueden ser tipos básicos u otros registros. Por ejemplo:

struct planeta {

struct 3D r, v, a;

double masa;

char nom[10];

};

struct 3D {

double x,y,z;

};

Los campos de cada registro son accesibles mediante el nombre del registro seguido de punto y el nombre del campo, como por ejemplo venus.r.x = 1.0; . Cada campo se comporta como lo hace su tipo básico. C no proporciona mecanismos de inicialización, ni copia de registros, por lo que debe ser el programador el que los implemente.

EJEMPLO:

/*Ejemplo

Crea una estructura que asigna los bit 0 al 7 de la variable entrada

y una estructura que asigna los bit del 0 al 7 a la variable salida que

a la vez direcciona al puerto de salida C

*/

46

#include <16F877.h>

#fuses HS,NOWDT,NOPROTECT,LVP

#use delay(clock=10000000)

#byte PORTC = 0x07

#byte PORTD = 0x08

#byte TRISC = 0x87

#byte TRISD = 0x88

void main (){

char opcion;

struct input_p {

BOOLEAN i0;

BOOLEAN i1;

BOOLEAN i2;

BOOLEAN i3;

BOOLEAN i4;

BOOLEAN i5;

BOOLEAN i6;

BOOLEAN i7;

}entrada;

struct out_p{

BOOLEAN out0;

BOOLEAN out1;

BOOLEAN out2;

BOOLEAN out3;

BOOLEAN out4;

47

BOOLEAN out5;

BOOLEAN out6;

BOOLEAN out7;

}salida;

TRISC = 0;

TRISD = 0xff;

PORTC = 0xff;

while(true){

entrada = PORTD ^ 0xff;

salida.out1 = entrada.i0 & entrada.i1;

opcion = salida;

PORTC = opcion ^ 0xff;

delay_ms(10);

}

}

EJERCICIO 18. Haga el ejercicio 4(a) utilizando estructuras.

EJEMPLO 2

/*Ejemplo

El siguiente programa, controla la siguiente estación de mecanizado

DESCRIPCIÓN DE LA ESTACION

--> Al accionar un pulsador, se alimenta y se fija un bloque desde un almacén por gravedad a una estación de mecanizado, por medio de un cilindro simple efecto (cilindro A).

--> Un segundo cilindro de doble efecto, con la presión reducida, (cilindro B) sujeta entonces el bloque en sentido perpendicular al primero.

--> Una vez finalizada la mecanización, se acciona un segundo pulsador. Esto hace que ambos cilindros retrocedan en secuencia inversa.

48

--> Ambos cilindros alcanzan en un tiempo de T1 = T2 = 1 segundo

--> El sistema utiliza valvulas 3/2

Pulsadores (Puerto D)

Botón 0 = pulsador de inicio

Botón 1 = pulsador de paro

Salidas

Puerto C0 = activa la válvula del cilindro A para que avance

Puerto C1 = activa la válvula del cilindro B para que avance

*/

#include <16F877.h>

#fuses HS,NOWDT,NOPROTECT,LVP

#use delay(clock=10000000)

#byte PORTC = 0x07

#byte PORTD = 0x08

#byte TRISC = 0x87

#byte TRISD = 0x88

#define v_cil 1000 //1000 ms = 1s

void main (){

char opcion,t0,t1,t2;

struct input_p {

BOOLEAN s0;

BOOLEAN s1;

BOOLEAN s2;

BOOLEAN s3;

49

BOOLEAN s4;

BOOLEAN s5;

BOOLEAN s6;

BOOLEAN s7;

}boton;

struct out_p{

BOOLEAN A;

BOOLEAN B;

BOOLEAN b0;

BOOLEAN b1;

BOOLEAN c0;

BOOLEAN c1;

BOOLEAN d0;

BOOLEAN d1;

}cilindro;

struct pasos{

BOOLEAN p1;

BOOLEAN p2;

BOOLEAN p3;

BOOLEAN p4;

BOOLEAN p5;

}paso;

TRISC = 0;

TRISD = 0xff;

PORTC = 0xff;

50

delay_ms(v_cil);

while(true){

boton = PORTD ^ 0xff;

paso.p1 = ( boton.s0 | paso.p1) & (!(paso.p5));

paso.p2 = (( paso.p1 & t0 ) | (paso.p2)) & (!(paso.p5));

paso.p3 = ((paso.p2 & boton.s1) | (paso.p3)) &(!(paso.p5));

paso.p4 = ((paso.p3 & t1)|(paso.p4)) & (!(paso.p5));

paso.p5 = (paso.p4 & t2);

cilindro.A = paso.p1 & (!(paso.p4));

cilindro.B = paso.p2 & (!(paso.p3));

opcion = cilindro;

PORTC = opcion ^ 0xff;

if ((paso.p1 == 1) && (paso.p3 == 0) && (t0 == 0))

{

delay_ms(v_cil);

t0 = 1;

}

if((paso.p3 == 1) && (paso.p4 == 0) && (t1 == 0))

{

delay_ms(v_cil);

t1 = 1;

}

if((paso.p4 == 1) && (paso.p5 == 0) && (t2 == 0))

{

delay_ms(v_cil);

t2 = 1;

}

51

if (paso.p5 == 1)

t0 = t1 = t2 = 0;

}

}

EJERCICIO 19. Haga una función que realice la operación de un temporizador

a encendido con los siguientes parámetros.

El valor será asignado igualando la variable del timer a la función.

El tiempo se establecerá en milisegundos, tendrá una entrada de activación, una entrada de reset y una bandera que verifique el estado del temporizador, si ya esta activada la salida o no.

Utilice esta función en el programa del ejemplo anterior.

52

Convertidor Analógico a Digital

Para la conversión analógica digital, el PIC16f877 cuenta con 8 canales, 5 en el puerto A y 3 en el puerto E. Para activarlos insertaremos en la función main las siguientes instrucciones.

EJEMPLO

/*

Este ejemplo muestra como capturar un dato a través de un puerto y

utilizarlo para realizar la operación lógica And entre el bit 0 y

el bit 1 del puerto D y mostrar el resultado en el bit 0 del puerto C,

convirtiendo los datos de lógica negativa a lógica positiva

*/

#include <16F877.h>

#fuses HS,NOWDT,NOPROTECT,LVP

#use delay(clock=10000000)

#byte PORTC = 0x07

#byte TRISC = 0x87

#byte PORTD = 0x08

#byte TRISD = 0x88

int and, p_d0,p_d1,captura;

long value;

Void main() {

TRISD = 0x00; // configura al puerto D como salidas

setup_adc(ADC_CLOCK_INTERNAL); //Habilita el modulo a/d

// y configura el reloj interno del

//convertidor analógico a digital

53

setup_adc_ports(ALL_ANALOG); //configura los canales del convertidr

//analógico digital como todos //analógicos

set_adc_channel(0); //selecciona el canal a leer

//en este ejemplo el canal 0

delay_us(10); //un retardo de 10 microsegundos hasta

//para ajustar el canal antes de leerlo

While (true) {

value = read_adc(); // Captura el valor del canal 0 en la variable value

captura = value;

PORTD = captura;

delay_ms (100);

}

}

EJERCICIO 20.

Realizar un programa que compare el valor de la señal analógica del cana 0 con la del canal 1 y escriba un mensaje en la pantalla LCD los siguientes estados lógicos.

Mensaje 1.- C0 menor que

C1

Mensaje 2.- C0 mayor que

C1

Mensaje 3.- C0 igual a C1

EJERCICIO 21.

Realizar un programa que al variar la señal en el canal 0, se mueva un motor a pasos siguiendo la dirección que se ha movido el potenciómetro del canal 0. De tal manera que al aumentar el voltaje girará en una dirección y al disminuir el voltaje en la entrada analógica este girará en sentido contrario teniendo como límite girar 180º solamente.

54

Comunicación RS232.

Para la comunicación serial, el PIC 16f877 cuenta con una UART síncrona y asíncrona. En el caso de la comunicación serial RS232, se lleva acabo por los pines 7 y 6 del puerto C, el pin 7 recibirá la información y el pin 6 transmitirá la información por el puerto serie.

Para configurar el puerto RS232 debemos agregar la siguiente línea en la sección de configuración

#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)

Donde baud es la velocidad de transmisión que debe de ser igual en ambas terminales para que pueda establecerse la configuración. Xmit es el pin por el cual el PIC transmitirá la información y RCV es el pin por el cual el Pic recibirá la información.

Cuando se trate de un microcontrolador a otro podemos usar los voltajes de 0 y 5 volts para establecer la comunicación serial asíncrona, conectando el pin de transmisión con el de receptor y viceversa en el otro controlador, pero cuando, queremos transmitir datos a una PC es necesario convertir los valores de 0 y 5 volts a valores de 12 y -12 V respectivamente. Para eso el módulo que estamos utilizando cuenta con un integrado llamado MAX232 que se encarga de convertir a los valores de voltajes necesarios para establecer la comunicación con una PC, facilitándonos el tener que utilizar interfaces externas.

EJEMPLO

/*

Este Ejemplo muestra como transmitir una cadena de datos por el puerto RS232.*/

#include <16F877.h>

#fuses HS,NOWDT,NOPROTECT,NOLVP

#use delay(clock=10000000)

#use rs232( baud=9600, xmit=PIN_C6, rcv=PIN_C7)

void main (){

while(true){

printf("Hola Mundo \r");

delay_ms(500);

}

}

55

EJEMPLO

/*

Este Ejemplo muestra como transmitir una cadena de datos por el puerto RS232 y un dato almacenado en una variable.*/

#include <16F877.h>

#fuses HS,NOWDT,NOPROTECT,NOLVP

#use delay(clock=10000000)

#use rs232( baud=9600, xmit=PIN_C6, rcv=PIN_C7)

void main (){

int edad;

edad = 30;

while(true){

printf("Mi edad es %u años \r",edad);

delay_ms(500);

}

}

EJEMPLO

/*En el siguiente ejemplo el programa vía RS232 le pide que envíe una tecla por el RS232, y recibe de respuesta la tecla que presiono*/

#include <16F877.h>

#fuses HS,NOWDT,NOPROTECT,NOLVP

#use delay(clock=10000000)

#use rs232( baud=9600, xmit=PIN_C6, rcv=PIN_C7)

void main (){

char edad;

while(true){

printf("Presiona una tecla\r");

edad = getchar();

printf("La tecla presionada fue %c \r",edad);

56

delay_ms(500);

}

}

MEMORIA EEPROM

Esta memoria eeprom, nos permite escribir datos en ella, pero a diferencia de la memoria RAM, esta no se pierde al reiniciar el sistema. Los datos permanecen ahí hasta que se borran por medio de instrucciones en dentro del programa del pic o hasta que se borra el PIC mediante el programador. Podemos borrar, grabar y leer datos cuantas veces se desee o sea necesario.

#include <16F877.h>

#fuses HS,NOWDT,NOPROTECT,NOLVP

#use delay(clock=10000000)

#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)

#include <input.c>

// Inicializa las primeras 4 posiciones de la memoria EEPROM con los valores

// 1,2,3,4

// Usando la instrucción ROM

// #rom 0x2100={1,2,3,4}

void main() {

BYTE i, j, address, value;

for(i=0; i<=15; ++i) { // Graba 0 en las primeras 64 posiciones

// de la memoria eeprom

for(j=0; j<=15; ++j) {

write_eeprom( i*16+j , 0 );

}

}

57

do {

printf("\r\n\nEEPROM:\r\n");

for(i=0; i<=15; ++i) { // Lee el contenido de las primeras 64

// posiciones de la eeprom

for(j=0; j<=15; ++j) {

printf( "%2x ", read_eeprom( i*16+j ) );

}

printf("\n\r");

}

printf("\r\nLocation to change: ");

address = gethex();

printf("\r\nNew value: ");

value = gethex();

write_eeprom( address, value ); // escribe valores en la memoria eeprom,

// address es la dirección donde se desea

// escribir, disponible desde la 0 a la 255

// value el valor en exadecimal

} while (TRUE);

}