La siguiente figura muestra el diagrama de flujo del programa realizado para el microcontrolador
El funcionamiento del sistema en terminos generales es así:
• El sistema envía un mensaje de bienvenida en el LCD
• Por medio de un switch se elige el modo de programación o el modo de monitoreo
o El modo de monitoreo sirve para leer las etiquetas y activar o desactivar la cerradura
electrónica, en caso de recibir una etiqueta autorizada o no.
o El modo de programación almacena las etiquetas autorizadas en la memoria del PIC.
Componentes del software.
El código fuente está compuesto por funciones, estas realizan una determinada acción sobre el sistema de hardware, de la misma manera se conforma de una función principal (main), que es la que gobierna todas las funciones que conforman el programa, y las cuales se describe en el presente capitulo. El software desarrollado para el PIC fue realizado en el entorno SourceBoost usando como base el lenguaje C, no se usaron ninguna librería para el LCD todo fue programado.
Programa principal (main)
El programa principal está en el listado 4.1; se comentan algunas líneas de código para mejor comprensión del programa.
Listado 4.1 Función para seleccionar opciones.
1 void main()
2 {
3 int u,c,i,opcion;
4 char tag[11];
5 char DatosDeEtiqueta[11];
6 char string[33];
7 adcon1=0x06; // puerto A como I/O digitales
8 trisb=0x00; // Se configura el Puerto B como salida
9 trisa=0x00; // Se configura el Puerto A como salida
10 porta=0x00; // Pone al Puerto A apagado
11 trisc=0x87; // 1000 0111 puerto C como entradas y salidas
12 def(); // Se manda llamar la subrutina de default
13 iniLCD(); // Se manda llamar la subrutina de inicialización
14 configserial(); // Llama la funcion configurar serial
15 set_bit(portc,5); // Led VERDE desactivado lector RFID
16 strcpy(string, " BIENVENIDOS CONTROL ACCESO");
17 escribeCadenaEnLCD(string);
18 delay_s(3);
19 inst(0x01); // borrar LCD
20 while (1)
21 {
22 opcion=menu();
23 switch (opcion)
24 {
25 case 1:
26 leerEtiquetaDeRF(DatosDeEtiqueta);
27 if (portc.5==1)
28 {
29 strcpy(string, " ETIQUETA # ");
30 escribeCadenaEnLCD(string);
31 mostrarEtiquetaEnLCD(DatosDeEtiqueta);
32 delay_s(3);
33 inst(0x01); // instrucción borra LCD
34 }
35 c=0;
36 for (i=0; i<11; i++)
37 {
38 if (tag[i]==DatosDeEtiqueta[i]) // verifica tag valido
39 {
40 c++;
41 }
42 }
43 if (c<=10)
44 {
45 porta.5=1;
46 strcpy(string, " ACCESO AUTORIZADO");
47 escribeCadenaEnLCD(string);
48 delay_s(10);
49 inst(0x01); // instrucción borra LCD
50 porta.5=0;
51 }
52 else
53 {
54 strcpy(string, " ACCESO DENEGADO");
55 escribeCadenaEnLCD(string);
56 delay_s(3);
57 inst(0x01); // instrucción borra LCD
58 }
59 break;
60 case 2:
61 if (portc.1==0) //push botton # 1
62 {
63 for (i=0; i<11; i++)
64 {
65 tag[i]=0x00; // borra un tag(etiqueta) grabada
66 }
67 strcpy(string, " ETIQUETA BORRADA");
68 escribeCadenaEnLCD(string);
69 delay_s(3);
70 inst(0x01); // instrucción borra LCD
71 }
72 if ((portc.1==1) && (portc.2==0)) // push botton 1 y 2
73 {
74 strcpy(string, " GRABANDO ETIQUETA");
75 escribeCadenaEnLCD(string);
76 leerEtiquetaDeRF(DatosDeEtiqueta);
77 u=0;
78 for (i=0; i<12; i++)
79 {
80 tag[u]=DatosDeEtiqueta[i]; //grabar tag en arreglo
81 u++;
82 }
83 inst(0x01); // instrucción borra LCD
84 strcpy(string, "ETIQUETA GRABADA ES ");
85 escribeCadenaEnLCD(string);
86 mostrarEtiquetaEnLCD(DatosDeEtiqueta);
87 delay_s(3);
88 inst(0x01); // instrucción borra LCD
89 }
90 if ((portc.1==1) && (portc.2==1))
91 {
92 menu();
93 delay_s(3);
94 }
95 break;
96 } // fin de switch
97 } // fin de while
98 } // fin de función main
99
Funciones para el LCD.
Las funciones necesarias para trabajar con la pantalla LCD son tres esencialmente, y son la inicialización, enviar instrucción y enviar dato, estas funciones fueron programadas usando los diagramas de tiempo pueden verse en cualquier hoja de datos para el LCD.
Función para inicializar LCD.
Esta función es la encargada de la inicialización de la pantalla LCD; En el listado 4.2 de escribe una función llamada def, descrita en el programa como función default , esta es necesaria para controlar los estados de E (enable del LCD ) , por el hecho de que enable del LCD debe cambiar a “0” valor de “inicial”, para que al interpretar la función iniLCD (descrita posteriormente en el listado 4.4) sea única para cada instrucción enviada.
Listado 4.2 función default devuelve E=0.
01 void def() // Condiciones default del LCD.
02 {
03 clear_bit(porta,0); // Señal E = 0
04 set_bit(porta,1); // Señal RW = 1
05 set_bit(porta,2); // Señal RS = 1
06 }
07
El listado 4.3 muestra una función que carga un valor de inicialización y consta de una retardo de 15ms y las instrucciones que se carga una a una a la función llamada inst (instrucción al LCD); estos valores se toman de la tabla 3.7 y tabla 3.8 expuestas en el capitulo III.
Listado 4.3 función para carga el valor de inicialización.
01 void iniLCD() // Condiciones para inicializar el LCD
02 {
03 delay_ms(15); // Espera 15 milisegundos
04 inst(0x01); // 0000 0001 limpia LCD
05 inst(0X3F); // 0011 1111 Funtion Set: 8bit, activa 2 líneas, 5x10 puntos.
06 inst(0X0F); // 0000 1111 on/off pantalla y cursor, parpadea el cursor.
07 inst(0X06); // 0000 0110 Modo de Entrada de caracteres
08 }
09
Función para escribir instrucciones
Para enviar una instrucción al LCD y inicializarlo es necesario cumplir con los tiempos requeridos y expuestos en el capitulo III, en la sección 3.5.2, con uso del diagrama de tiempo expuesto en la figura 3.10 de dicho capitulo, por lo tanto, es necesario crear una función que lo realice; el siguiente listado 4.4 describe una función llamada inst() que en conjunto con las función def y la función iniLCD descritas anteriormente cumplen con ese diagrama de tiempo.
Listado 4.4 función para enviar instrucción al LCD.
01 void inst(int i) // Secuencia para enviar las instrucciones al LCD
02 {
03 clear_bit(porta,2); // RS = 0
04 clear_bit(porta,1); // RW = 0
05 portb=i; // carga el valor de i al puerto B
06 set_bit(porta,0); // E = 0
07 delay_ms(50); // retardo de 50 milisegundos
08 def(); // llama a la función def
09 }
10
Función para enviar dato al LCD.
Al igual que la función para enviar instrucciones al LCD, la función de enviar datos en esta caso la función se llama datos(); Hace uso también de la función def, para regresar el valor E=0; La función “enviar datos” se muestra en el listado 4.5, la cual cumple con el diagrama de tiempo de una LCD estándar. Esta función es usada para escribir los datos en LCD, tanto los obtenidos por las etiquetas RFID como los letreros de información de estado del sistema; La descripción de esta función es tan simple como la función de enviar instrucción, solamente que en esta función cambian los valores RS, E y RW , lo cual se describe en las hojas de daos de una LCD
Listado 4.5 función para enviar dato al LCD .
01 void datos(int j) // Instrucciones para enviar los datos al LCD
02 {
03 set_bit(porta,2); // RS = 1
04 clear_bit(porta,1); // RW = 0
05 portb=j; // carga el valor de j al puerto B
06 set_bit(porta,0); // E = 1
07 delay_ms(20); // retardo de 50 milisegundos
08 def(); // llama a la función def
09 }
10
Función para escribir el número de etiqueta en el LCD.
Esta función llamada en el programa principal así: mostrarEtiquetaEnLCD, requiere que otras funciones sean ejecutadas con anterioridad, las cuales obtiene el numero de etiqueta, para posteriormente, poder mostrar su valor en el LCD, pero por razones de orden y descripción del programa es mencionada primero.
Esta función es mostrada en el listado 4.6, la función opera de la siguiente manera, los datos cargados en el arreglo etiqueta[ ] , son enviados a la función datos() para qué esta función los escrita en el LCD.
Al comenzar la función inicializamos una variable llamada u donde esta variable tomó valores desde 1 hasta 10, que representan los datos obtenidos del etiqueta; Que son los que se pueden mostrar, esto se describe en la sección anterior donde se habla del protocolo de cserial del lector, lafunción sólo mostrará los 10 bytes donde está escrito él ID del etiqueta leída.
Es de notar el arreglo etiqueta[] ese tipo char, el cual devuelve un valor hexa-decimal representativo de tipo ASCII, que se reciben del etiqueta en cuestión. El primer byte de start 0x0A se ubica en la posición etiqueta[0] y el byte de stop 0x0D se ubica en la posición etiqueta[11], es por eso que u comienza en uno y terminar en 10 el byte start y stop de la etiqueta no es necesario mostrarlos.
Listado 4.6. Función muestra datos de etiqueta en el LCD .
01 void mostrarEtiquetaEnLCD(char etiqueta[])
02 {
03 int u; // declaramos una variable
04 for (u=1; u<11; u++) // ciclo for con u =1 hasta U=11
05 {
06 datos(etiqueta[u]); // llama a la función datos()
07 delay_ms(100); // retardo 100 milisegundos
08 }
09 }
10
Función para escribir una cadena de texto en el LCD
Al usar esta función llamada escribeCadenaEnLCD y que se muestra en el listado 4.7, se puede mandar una cadena de caracteres “texto” al LCD, sin la necesidad de escribir cada letra del texto en un código hexa-decimal, ya que como se menciona una cadena devuelve el valor en tipo ASCII al arreglo, para este caso, se usa el arreglo llamado str[ ] , que además es tipo char.
El proceso es el siguiente, los datos almacenados en el arreglo str[ ], son enviados uno a uno desde str[0] hasta str[a], donde a es valorada con la función strlen incluida en la librería string.h, y es devuelta como un entero el cual tiene la medida de longitud de la cadena; Aquí podemos intuir que una cadena de mas de 32 caracteres no podrá ser mostrada, pues el LCD tiene precisamente esos espacios 2x16, en todo caso si se escribiera una cadena de mayor longitud no se podrá visualizar con esta función.
Listado 4.7. función para escribir texto en el LCD.
01 void escribeCadenaEnLCD(char str[ ])
02 {
03 int a, u; // declaramos variables usadas
04 a = strlen(str); // mide la longitud de una cadena.
05 for (u=0; u < a; u++) // ciclo for; u=0 hasta u < a
06 {
07 if (u==16) // el if para verificar el renglón del LCD
08 {
09 inst(0xc0); // para cambiar a renglón 2
10 }
11 datos(str[u]); // manda toda la cadena a la función datos
12 }
13 }
14
Dentro de esta misma función esta un if que es el encargado de verificar que renglón del LCD se esta visualizando, es decir, los primeros 16 caracteres, sobrepasando este valor el LCD comenzara ha escribir en el renglón 2 y con esto se da una visualización legible.
Por ultimo se vuelve a llamar la función datos() que es la encargada de enviar la información al LCD para que sea posible visualizarla, como se menciona al principio envía uno a uno los datos en el arreglo str[u].
Función para la comunicación serial
Para establecer una comunicación serial primero se debe configurar el microcontrolador, para que se establezca los pines de comunicación y esto se logra con la función del listado 4.8 llamada configserial() en este código.
Listado 4.8 Configuración del PIC para recepción serial.
01 void configserial() // Configuración PIC para recepción Serial
02 {
03 trisc.6= 0; // configura el pin 6 puerto C como salida
04 trisc.5= 0; // configura el pin 5 puerto C como salida
05 trisc.7= 1; // configura el pin 7 puerto C como entrada
06 spbrg = 25; // frecuencia en baudios 2400bps
07 txsta = 0x00; // valor requerido en el registro txsta
08 rcsta = 0x80; // valor requerido en el registro rcsta
09 }
10
Lo que realiza esta función es configurar los registros TXSTA y RCSTA descritos en la hoja de datos del PIC16F73 para establecer la recepción serial.
Función para leer un dato serial
La función esta en el listado 4.9 y se llama leer1DatoSerial(), este proceso de obtener un dato serial esta descrito en la sección 3.3.4.2. En esta función se toma en cuenta los bit star y stop de cada byte recibido, es posible realizar esto porque RCIF (pir1<5>) se coloca a 1 lógico solo cuando recibe un dato completo; Puesto que ya se sabe que la etiqueta RFID se obtienen 12 bytes de su ID, entonces esta función se ejecuta 12 veces u once si no se toma en cuenta el byte stop de la etiqueta tal como se ve en el listado 4.10 con el while.
Listado 4.9 Función leer un dato del puerto Serie
01 int leer1DatoSerial()
02 {
03 int x=0; // Declaramos una variable y la iniciamos en 0
04 while (pir1.5 == 0); // Espera a que llegue el un Dato x el puerto serial
05 x = rcreg; // Guarda el dato
06 while (pir1.5 == 1); // Terminada la recepción de un dato (8bits)
07 return x; // Regresa el valor de x
08 }
09
El dato recibido se guarda en una variable x, la cual regresa el valor a otra función que se encarga de ir almacenado en un arreglo llamado etiqueta [u] esto byte a byte hasta los 12 bytes de la etiqueta RFID.
Función para leer los datos de una etiqueta.
La función encargada de la lectura de la etiqueta, esta en el listado 4.10, la función es llamada leerEtiquetaDeRF, esta función almacena cada byte de la etiqueta RFID en un arreglo de 10 pociones llamado etiqueta[u], los bytes son obtenidos a través de la función anteriormente descrita llamada leer1DatoSerial().
Listado 4.10 Lee datos de etiqueta RFID.
01 void leerEtiquetaDeRF(char etiqueta[])
02 {
03 int u = 0; //Declara una variable u=0 para el control
04 clear_bit(portc,5); //Activa el Lector
05 rcsta.4 = 1; //Activa recepcion Continua
06 do
07 {
08 etiqueta[u]=leer1DatoSerial(); //Uno a uno byte recibido al arreglo
09 u++;
10 } // incrementamos u para las posiciones del arreglo
11 while (u<11); //Repite el ciclo hasta que es falsa la condición
12 set_bit(portc,5); //Desactiva el Lector
13 rcsta.4 = 0; //Desactiva recepción Continua
14 }
15
En esta función, como primer instrucción se activa el lector RFID, mandando un “0” al pin 5 de puerto C, puesto que en el se encuentra conectado la terminal /enable del lector RFID.
Seguido se activa la recepción continua colocando un “1”lógico en el bit CREN, ubicado en el registro RCSTA del microcontrolador descrito en la sección 3.3.4.2. entra en un ciclo do-while que se repetirá hasta haber obtenido los 10 bytes de ID de la etiqueta RFID, dentro del ciclo se manda llamar la función leer1DatoSerial() que obtiene un byte y lo almacena en el arreglo; Terminando el ciclo do-while se desactiva el lector RFID al igual que la recepción continua del Pic.
Función para mostrar el menú de opciones
La función menú() nos permite seleccionar una entre dos opciones que son: CONTROL ACCESO ***ACTIVADO*** y MODO DE PROGRAMACIÓN.
Esta función solamente muestra en el lcd los dos letreros anteriores, y mediante un if se selecciona la opción deseada, lo que hay que resaltar es la función strcpy que pertenece a la librería string.h, dicha función strcpy, se encarga de copiar el letrero a la cadena de caracteres llamada string[ ], que es de 33 posiciones, después es mandada a la función escribeCadenaEnLCD() que se encarga de mandar la cadena al LCD y que es descrita en el listado 4.7.
Además esta función devuelve la opción seleccionada op para que mediante un switch en el programa principal (main), ejecute lo que concierne a la opción seleccionada.
Listado 4.10 Función para seleccionar opciones.
01 int menu()
02 {
03 char string[33]; // cadena de 32 posiciones, segmentos tiene el LCD
04 int op;
05 if (portc.0==1)
06 {
07 strcpy(string, " CONTROL ACCESO ***ACTIVADO***");
08 escribeCadenaEnLCD(string);
09 delay_s(3);
10 inst(0x01);
11 op=1;
12 }
13 else
14 {
15 strcpy(string, " MODO DE PROGRAMACION");
16 escribeCadenaEnLCD(string);
17 delay_s(3);
18 inst(0x01);
19 op=2;
20 }
21 return op;
22 }
23
Ups, fue basante código, espero se haya explicado y si hay duda del código no duden en preguntar.