En un microcontrolador tendremos 16 entradas y una salida UART, en el otro microcontrolador tendremos una entrada UART y 16 salidas. De esta manera podremos controlar 16 salidas a distancia.
Si bien podríamos realizar el control de otra manera, en lugar de 16 entradas, podríamos usar una terminal serie, un terminal bluetooth desde el celular, botones, etc... Esta es la aplicación mas sencilla que servirá de base para otros proyectos, como podría ser el enlace vía bluetooth que es bastante tentador para encender y apagar cosas a distancia.
Tendremos que abordar dos programas, uno para el transmisor y otro para el receptor.
En este proyecto lo mostrare solamente en simulación ya que no tengo MCU con 16 salidas, por el momento..., pero el firmware esta realizado de forma simple para que se puedan emplear módulos inalámbricos UHF ASK de los que se venden en el mercado.
La Trama:
+----------+----------+----------+----------+
| Header | Payload | Checksum |
+----------+----------+----------+----------+
| Header | Valor1 | Valor2 | Checksum |
+----------+----------+----------+----------+
| 1 byte | 2 byte | 1 byte |
+----------+----------+----------+----------+
El Transmisor:
Para este programa realizaremos primero la lectura del puerto A y del puerto B, quedando así las 16 entradas que mencionamos anteriormente, luego de leer estos dos puertos, moveremos sus valores en dos variables.
Para la trama de transmisión estaremos realizando una trama muy básica pero eficiente, esta trama consta de un preámbulo, una información útil y una verificación, también conocido como Header, Payload y Checksum. Este es el método mas sencillo de realizar una trama completamente insegura pero si no nos importa la seguridad no sera un problema.
Entonces, tendremos un Header que sera un numero que enviaremos siempre como un ID, en este caso utilice el numero 200, pero podría ser cualquier otro entre 0 y 255. Luego el payload sera nuestra información útil, donde pondremos dos variables de 8 bit, y por ultimo el checksum sera la suma de estas.
Por Ejemplo:
Header: 200
Valor1: 120
Valor2 34
Checksum: 120+34=154
+----------+----------+----------+----------+
| Header | Payload | Checksum |
+----------+----------+----------+----------+
| 200 | 120 | 34 | 154 |
+----------+----------+----------+----------+
De esta manera vamos a enviar los 4 bytes por puerto serie a 600bps (velocidad empleada para que la señal pueda ser enviada correctamente por los módulos UHF ASK (ver mas en teorema de Shanon Hartley, Nyquist, Relación Señal/Ruido, etc...).
Luego de crear nuestra trama de datos, esta podría ser enviada por puerto serie, pero no la enviaremos de forma deliberada, la enviaremos solo cuando exista algún cambio en alguno de los 16 estados de entrada, sino no enviaremos nada, lo cual se hace para ahorrar energía y para que no se este enviando todo el tiempo, claro que esto requiere de una comunicación con baja ruido, ya que si nosotros enviamos el dato y no llega bien en el receptor, nunca se realizara un re-intento, no tenemos confirmación en una transmisión simplex como esta. Lo ideal seria realizar algo de redundancia para asegurarnos de que el dato ha llegado correctamente, pero esto lo dejare para otra nota con el fin de no complicar mucho esta.
- //*********************************************************************************************************
- // Programa para enviar 16 estados por UART con CRC
- // El enlace se realiza mediante una comunicación ASK OOK UHF sobre UART Invertido a 600bps
- //*********************************************************************************************************
- #include <16F883.h> //Biblioteca del microcontrolador
- #fuses NOMCLR, NOWDT, INTRC_IO //Configuración de fuses
- #use delay(int=4000000) //Configuración del clock interno a 4MHz
- #use rs232(baud=600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8) //Configuración de UART 600bps 8 N 1
- //*********************************************************************************************************
- // Función que realiza la lectura de puertos
- //*********************************************************************************************************
- int8 valor1=0, valor2=0, valor1Ant=0, valor2Ant=0; //Declaracion de variables de 8bit
- int1 cambio=FALSE; //Declaracion de variable boole
- int1 leePuertos(void){ //Declaración de función para enviar datos
- valor1=input_a(); //Lectura de puerto A
- delay_ms(50); //Delay de 50ms
- valor2=input_b(); //Lectura de puerto B
- delay_ms(50); //Delay de 50ms
- if(valor1!=valor1Ant || valor2!=valor2Ant) //Si hay cambio en entrada se actualiza
- cambio=TRUE; //Setea flag a TRUE
- else //Si no hay cambio en la entrada no actualiza
- cambio=FALSE; //Setea flag a FALSE
- valor1Ant=valor1; //Intercambio de valores
- valor2Ant=valor2; //Intercambio de valores
- return(cambio); //Retorno de funcion
- }
- //*********************************************************************************************************
- // Función que realiza el envio de datos por UART
- //*********************************************************************************************************
- #define HEADER 200 //Definición de valor Header para el payload
- static char trama[4]; //Variable donde se aloja el trama
- void enivaRF(void){ //Declaración de función para enviar datos
- trama[0]=HEADER; //Carga el Header en byte 0 del trama
- trama[3]=trama[1]+trama[2]; //Realiza suma de los dos datos y lo carga en el byte 3 del trama
- putc(trama[0]); //Envía el byte 0 del trama por UART
- delay_ms(50); //Delay de espera entre bytes enviados por UART
- putc(trama[1]); //Envía el byte 1 del trama por UART
- delay_ms(50); //Delay de espera entre bytes enviados por UART
- putc(trama[2]); //Envía el byte 2 del trama por UART
- delay_ms(50); //Delay de espera entre bytes enviados por UART
- putc(trama[3]); //Envía el byte 3 del trama por UART
- delay_ms(50); //Delay de espera entre bytes enviados por UART
- printf("\r"); //Envía caracter de retorno de linea como final de trama
- }
- //*********************************************************************************************************
- // Programa principal, Realiza el envio de datos por UART
- //*********************************************************************************************************
- void main(){ //Función principal
- while(true){ //Loop principal repetitivo
- if(leePuertos()){ //Funcion que lee los puertos para enviar datos
- trama[1]=valor1; //Carga el valor 1 en byte 1 del trama
- trama[2]=valor2; //Carga el valor 2 en byte 2 del trama
- enivaRF(); //Llamado a la función que envía datos
- }
- }
- }
El Receptor:
Este programa es similar al anterior, solo que en lugar de leer dos puertos, los escribiremos.
Para realizar la lectura de datos utilizaremos la interrupción de puerto serie, es decir, cuando exista un dato presente en el puerto serie, se realizara una interrupción y se guardaran los datos para ser procesados mas adelante.
En este caso realizaremos un pequeño y simple buffer, que consiste en un vector de 4 posiciones, donde guardaremos la trama (header, payload y checksum) de esta manera tendremos los datos disponibles para su procesado.
Primero vamos a mover los byte recibidos al vector con un indice incremental, luego preguntaremos si este indice es mayor que 4 (buffer lleno) y si es así, procesaremos los datos y pondremos el indice en 0 nuevamente.
Mientras el indice sea menor que 4 (de 0 a 3) entonces los datos son útiles, para ello tendremos otra consulta anidada que preguntara si el Header es el que nosotros dijimos en el programa transmisor "200", si esto es verdadero entonces tendremos otra consulta preguntando si los datos de valor1 mas valor2, menos el checksum es igual a cero.
Por Ejemplo:
Checksum: 200
Valor1: 120
Valor2 34
Checksum: 154
+----------+----------+----------+----------+
| Header | Payload | Checksum |
+----------+----------+----------+----------+
| 200 | 120 | 34 | 154 |
+----------+----------+----------+----------+
Si (120+34)-154=0?, en este caso podremos ver que los datos han llegado bien porque la suma de sus variables es igual al valor del checksum, esta es nuestra verificación, recién en este punto si los datos son correcto procedemos a guardar los valores en variables que luego serán mostradas en los puertos A y B.
- //*********************************************************************************************************
- // El programa recibe 16bit por UART y los muestra por puertos
- // El enlace se realiza mediante una comunicación ASK OOK UHF sobre UART Invertido a 600bps
- //*********************************************************************************************************
- #include <16F883.h> //Biblioteca del microcontrolador
- #fuses NOMCLR, NOWDT, INTRC_IO //Configuración de fuses
- #use delay(int=4000000) //Configuración del clock interno a 4MHz
- #use rs232(baud=600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8) //Configuración de UART 600bps 8 N 1
- //*********************************************************************************************************
- // Variables
- //*********************************************************************************************************
- #define HEADER 200 //Definición de valor Header para el payload
- #define STATUS PIN_C0 //Pin para analizar el status de la interrupt
- static int8 trama[4]; //Variable donde se aloja el trama
- static int8 valor1=0, valor2=0; //Variable donde se alojan los datos externos
- //*********************************************************************************************************
- // Función Interrupción UART que realiza el parse de datos, validación de Header y Checksum
- // Si validación y Checksum son validos, carga el vector DATO para ser utilizado
- //*********************************************************************************************************
- int8 i=0; //Variable para el contador de bytes de entrada
- #int_RDA //Interrupción por dato en UART
- void intser(){ //Función de servicio de interrupción
- trama[i++]=getc(); //Guarda byte de entrada en trama. incrementa indice
- if(i>4){ //Si el indice es mayor que 4 se asume que se completa el trama
- if(trama[0]==HEADER){ //Validación que el Header sea 200 (seteado en el transmisor)
- if(trama[1]+trama[2]-trama[3]==0){ //Validación de checksum, si datos leídos son igual a checksum
- valor1=trama[1]; //Cargamos los datos en el vector
- valor2=trama[2]; //Cargamos los datos en el vector
- output_high(STATUS); //Ponemos a 1 el LED de estado de dato presente
- delay_ms(100); //Delay de LED encendido
- output_low(STATUS); //Ponemos a 0 el LED
- }
- }
- i=0; //Una vez que se completan los 5 bytes, se reinicia el contador
- }
- }
- //*********************************************************************************************************
- // Programa principal, recibe los datos por UART y los muestra en los puertos
- //*********************************************************************************************************
- void main(){ //Función principal
- enable_interrupts(global); //Habilita interrupciones globales
- enable_interrupts(int_rda); //Habilita interrupción de dato en UART
- while(true){ //Loop principal infinito
- output_a(valor1); //Cargamos puerto A
- delay_us(100); //Delay de refresco
- output_b(valor2); //Cargamos puerto B
- delay_us(100); //Delay de refresco
- }
- }
"trama(i++)" graba apartir de 0 o de 1 ???
ResponderBorrar