En esta secciĆ³n aprenderĆ”s a realizar una comunicaciĆ³n serial RS232 half duplex con dos microcontroladores PIC. El objetivo es enviar un comando al PIC esclavo para pedir el valor de diferentes sensores y mostrarlos en una pantalla OLED.
ComunicaciĆ³n serial RS232
La comunicaciĆ³n RS232 es un protocolo de comunicaciĆ³n asĆncrono que define cĆ³mo los dispositivos deben transmitir y recibir datos y seƱales de control. Establece normas para la transmisiĆ³n de seƱales de control, como el inicio y el fin de los datos, y tambiĆ©n define los parĆ”metros de los cables y conectores utilizados para la conexiĆ³n.
AdemĆ”s, laĀ comunicaciĆ³nĀ RS232 permite una amplia gama de velocidades de transmisiĆ³n de datos, lo que la hace adecuada para una amplia variedad de aplicaciones. La velocidad de transmisiĆ³n de datos se puede ajustar segĆŗn sea necesario para adaptarse a las necesidades especĆficas de una aplicaciĆ³n.
ComunicaciĆ³n half duplex
La comunicaciĆ³n half duplex es una forma de comunicaciĆ³n en la que dos dispositivos sĆ³lo pueden transmitir informaciĆ³n en un solo sentido a la vez. Es decir, en cualquier momento dado, sĆ³lo uno de los dispositivos puede enviar informaciĆ³n mientras que el otro recibe.
Este tipo de comunicaciĆ³n es comĆŗn en radios walkie-talkies, en los que un usuario presiona un botĆ³n para hablar y suelta el botĆ³n para escuchar. TambiĆ©n se utiliza en algunos dispositivos de red y en sistemas de control de trĆ”fico aĆ©reo.
En comparaciĆ³n con la comunicaciĆ³n full duplex, la comunicaciĆ³n half duplex es menos eficiente ya que requiere un tiempo adicional para alternar entre el envĆo y la recepciĆ³n de informaciĆ³n. Sin embargo, puede ser Ćŗtil en situaciones en las que se requiere un control mĆ”s estricto sobre quiĆ©n puede hablar en cualquier momento dado.
En resumen, la comunicaciĆ³n half duplex es una forma de comunicaciĆ³n en la que dos dispositivos pueden transmitir informaciĆ³n en un solo sentido a la vez, alternando entre el envĆo y la recepciĆ³n de informaciĆ³n.
Circuito de conexiĆ³n
ProgramaciĆ³n PIC de Maestro
Inicialmente se establecen los parĆ”metros de la comunicaciĆ³n RS232.
- baud = Velocidad de transmisiĆ³n.
- parity = Bit de paridad.Ā
- xmit = Pin de transmisiĆ³n.
- rcv = Pin de recepciĆ³n.Ā
- bits = Datos de transmisiĆ³n.
- stream = identificaciĆ³n de transmisiĆ³n.
#use rs232(baud=9600, parity=N, xmit=PIN_C6, rcv=PIN_C7, bits=8)
Se envĆan los datos al PIC esclavo para pedir el valor de la mediciĆ³n del puerto analĆ³gico AN0.
Los primeros 4 caracteres Ā«PIC1Ā» corresponden a la direcciĆ³n del PIC esclavo y los siguientes 4 caracteres Ā«PAN0Ā» corresponden al comando para pedir el valor del pin analĆ³gico AN0.
printf("PIC1PAN0\r");
Cuando se envĆan los datos al PIC esclavo debemos esperar su respuesta con el valor del puerto que solicitamos, la funciĆ³n Ā«kbhit()Ā» espera la recepciĆ³n del primer caracterĀ por el puerto RS232, los caracteres recibidos se guardan en la variable Ā«recibido[]Ā» y termina hasta recibir el salto de linea Ā«/rĀ».
void recive()
{
memset(recibido, 0, sizeof(recibido)); //limpia los registros de la varible
while(true)
{
if (kbhit())//espera el primer caracter
{
int8 i=0;
while(true)
{
recibido[i]=getc();//RECIBE EL CARACTER
if(recibido[i]=='\r')//busca el salto de linea \r
{
recibido[i]='\0';//fin de la cadena = caracter nulo \0
return;
}
i++;
}
}
}
}
Se convierte la cadena de caracteres de la variable Ā«recibidoĀ» en un entero de 16bits y el valor se guarda en la variable Ā«PAN0Ā».
PAN0 = (int16)atol(recibido); //convierte la cadena en un numero de 16bits
Se realiza el mismo procedimiento para obtener el valor del pin analĆ³gico AN1 y los pines digitales RD0 y RD1.
printf("PIC1PAN1\r");
recive();
PAN1 = (int16)atol(recibido); //convierte la cadena en un numero de 16bits
printf("PIC1PRD0\r");
recive();
PRD0 = (int1)atoi(recibido); //convierte la cadena en un numero de 8bits
printf("PIC1PRD1\r");
recive();
PRD1 = (int1)atoi(recibido); //convierte la cadena en un numero de 8bits
Finalmente hace el llamado a la subrutina Ā«mostrar()Ā» para mostrar los valores obtenido de los diferentes sensores y actuadores en el display OLED.
void mostrar()
{
char texto[20];
sprintf(texto,"PAN0=%04lu",PAN0);
OLED_DrawText(1,1,texto,1);
sprintf(texto,"PAN1=%04lu",PAN1);
OLED_DrawText(1,10,texto,1);
sprintf(texto,"PRD0=%u",PRD0);
OLED_DrawText(1,20,texto,1);
sprintf(texto,"PRD1=%u",PRD1);
OLED_DrawText(1,30,texto,1);
OLED_Display();//Muestra la informaciĆ³n en pantalla;
}
CĆ³digo completo PIC de Maestro
#include "string.h"
#include "stdlib.h"
#FUSES NOMCLR
#use i2c(Master,Fast, sda=PIN_B0, scl=PIN_B1, force_sw, stream=OLED_stream)//parametros I2C
#use rs232(baud=9600, parity=N, xmit=PIN_C6, rcv=PIN_C7, bits=8)
#define SH1106_128_64 //DEFINE EL MODELO DE LA PANTALLA OLED
//#define SSD1306_128_64
#include "OLED_I2C.c" //libreria oled I2C
char recibido[20];
int16 PAN0=0;
int16 PAN1=0;
int1 PRD0=0;
int1 PRD1=0;
void recive()
{
memset(recibido, 0, sizeof(recibido)); //limpia los registros de la varible
while(true)
{
if (kbhit())//espera el primer caracter
{
int8 i=0;
while(true)
{
recibido[i]=getc();//RECIBE EL CARACTER
if(recibido[i]=='\r')//busca el salto de linea \r
{
recibido[i]='\0';//fin de la cadena = caracter nulo \0
return;
}
i++;
}
}
}
}
void mostrar()
{
char texto[20];
sprintf(texto,"PAN0=%04lu",PAN0);
OLED_DrawText(1,1,texto,1);
sprintf(texto,"PAN1=%04lu",PAN1);
OLED_DrawText(1,10,texto,1);
sprintf(texto,"PRD0=%u",PRD0);
OLED_DrawText(1,20,texto,1);
sprintf(texto,"PRD1=%u",PRD1);
OLED_DrawText(1,30,texto,1);
OLED_Display();//Muestra la informaciĆ³n en pantalla;
}
void main()
{
OLED_Begin();
OLED_ClearDisplay();
while(TRUE)
{
printf("PIC1PAN0\r");
recive();
PAN0 = (int16)atol(recibido); //convierte la cadena en un numero de 16bits
printf("PIC1PAN1\r");
recive();
PAN1 = (int16)atol(recibido); //convierte la cadena en un numero de 16bits
printf("PIC1PRD0\r");
recive();
PRD0 = (int1)atoi(recibido); //convierte la cadena en un numero de 8bits
printf("PIC1PRD1\r");
recive();
PRD1 = (int1)atoi(recibido); //convierte la cadena en un numero de 8bits
mostrar();
}
}
ProgramaciĆ³n PIC de Esclavo
Inicialmente se establecen los parĆ”metros de la comunicaciĆ³n RS232.
- baud = Velocidad de transmisiĆ³n.
- parity = Bit de paridad.Ā
- xmit = Pin de transmisiĆ³n.
- rcv = Pin de recepciĆ³n.Ā
- bits = Datos de transmisiĆ³n.
- stream = identificaciĆ³n de transmisiĆ³n.
#use rs232(baud=9600, parity=N, xmit=PIN_C6, rcv=PIN_C7, bits=8)
Se habilita la conversiĆ³n analĆ³gico digital para hacer la mediciĆ³n en los pines analĆ³gicos.
setup_adc(ADC_CLOCK_INTERNAL);
En la funciĆ³n Ā«main()Ā» se realizan la mediciones de los componentes conectados a los pines analĆ³gico AN0, analogico AN1 y los pines digitales RD0 y RD1.
set_adc_channel (0); //Habilita el canal AN0
delay_us(10);
PAN0 = read_adc(); //lectura del potenciometro
set_adc_channel (1); //Habilita el canal AN1
delay_us(10);
PAN1 = read_adc(); //lectura del sensor de gas
PRD0 = input(pin_d0); // lectura del boton
PRD1 = input(pin_d1); // lectura del sensor de presencia
Se habilitan las interrupciĆ³nes para la recepciĆ³n de datos por el puerto RS232.
enable_interrupts(int_rda);
enable_interrupts(global);
Cuando se recibe un dato por el puerto Rs232 se activa la interrupciĆ³n y se introduce en la subrutina Ā«rda_isr()Ā», donde la variable Ā«recibido[]Ā» guarda la cadena de caracteres recibidos hasta recibir el carĆ”cter ‘\r’ y terminar la recepciĆ³n de los datos.
#int_rda
void rda_isr()
{
memset(recibido, 0, sizeof(recibido)); //limpia los registros de la varible
int8 i=0;
while(true)
{
recibido[i]=getc();//RECIBE EL CARACTER
if(recibido[i]=='\r')//busca el salto de linea \r
{
recibido[i]='\0';//fin de la cadena = caracter nulo \0
enviar();
return;
}
i++;
}
}
Cuando termina de recibir datos se hace el llamado a la subrutina Ā«enviar()Ā» donde inicialmente identificamos la direcciĆ³n y el comando.
for (int i=0; i<=3; ++i)
{
address[i] = recibido[i]; //identifica el PIC
}
for (int i=0; i<=3; ++i)
{
com[i] = recibido[i+4]; //identifica el COMANDO
}
Se compara la direcciĆ³n del PIC esclavo con la direcciĆ³n recibida, si son iguales entrara a la condiciĆ³n de lo contrario retornara a la funciĆ³n Ā«main()Ā».
result = strcmp(address, PIC1);//compara, si son iguales (0), no (+-1)
if (result==0)///si recibe la direccion
{
}
Si la direcciĆ³n es correcta, se procede a comparar el comando recibido con los comandos del pic esclavo, para identificar cual es el dato que se enviara devuelta el PIC maestro, una vez identificado el comando, se envĆa el dato por el puerto rs232 al pic maestro y se retorna a la funciĆ³n Ā«main()Ā».
result = strcmp(com, CPAN0);//compara, si son iguales (0), no (+-1)
if (result==0)
{
printf("%04lu\r",PAN0);//envia el valor del pin AN0
return;
}
result = strcmp(com, CPAN1);//compara, si son iguales (0), no (+-1)
if (result==0)
{
printf("%04lu\r",PAN1);//envia el valor del pin AN1
return;
}
CĆ³digo completo PIC de Esclavo
#include "string.h"
#include "stdlib.h"
#FUSES NOMCLR
#use rs232(baud=9600, parity=N, xmit=PIN_C6, rcv=PIN_C7, bits=8)
char recibido[20];
int16 PAN0=0;
int16 PAN1=0;
int1 PRD0=0;
int1 PRD1=0;
void enviar();
#int_rda
void rda_isr()
{
memset(recibido, 0, sizeof(recibido)); //limpia los registros de la varible
int8 i=0;
while(true)
{
recibido[i]=getc();//RECIBE EL CARACTER
if(recibido[i]=='\r')//busca el salto de linea \r
{
recibido[i]='\0';//fin de la cadena = caracter nulo \0
enviar();
return;
}
i++;
}
}
void enviar()
{
int8 address[10];
int8 com[10];
int8 PIC1[10]="PIC1";
int8 CPAN0[10] = "PAN0";
int8 CPAN1[10] = "PAN1";
int8 CPRD0[10] = "PRD0";
int8 CPRD1[10] = "PRD1";
signed int8 result;
memset(address, 0, sizeof(address)); //limpia los registros de la varible
memset(com, 0, sizeof(com)); //limpia los registros de la varible
while (true)
{
for (int i=0; i<=3; ++i)
{
address[i] = recibido[i]; //identifica el PIC
}
for (int i=0; i<=3; ++i)
{
com[i] = recibido[i+4]; //identifica el COMANDO
}
result = strcmp(address, PIC1);//compara, si son iguales (0), no (+-1)
if (result==0)///si recibe la direccion
{
result = strcmp(com, CPAN0);//compara, si son iguales (0), no (+-1)
if (result==0)
{
printf("%04lu\r",PAN0);//envia el valor del pin AN0
return;
}
result = strcmp(com, CPAN1);//compara, si son iguales (0), no (+-1)
if (result==0)
{
printf("%04lu\r",PAN1);//envia el valor del pin AN1
return;
}
result = strcmp(com, CPRD0);//compara, si son iguales (0), no (+-1)
if (result==0)
{
printf("%u\r",PRD0);//envia el valor del pin RD0
return;
}
result = strcmp(com, CPRD1);//compara, si son iguales (0), no (+-1)
if (result==0)
{
printf("%u\r",PRD1);//envia el valor del pin RD1
return;
}
}
return;
}
}
void main()
{
enable_interrupts(int_rda);
enable_interrupts(global);
setup_adc(ADC_CLOCK_INTERNAL);
while(TRUE)
{
set_adc_channel (0); //Habilita el canal AN0
delay_us(10);
PAN0 = read_adc(); //lectura del potenciometro
set_adc_channel (1); //Habilita el canal AN1
delay_us(10);
PAN1 = read_adc(); //lectura del sensor de gas
PRD0 = input(pin_d0); // lectura del boton
PRD1 = input(pin_d1); // lectura del sensor de presencia
}
}