ComunicaciĆ³n serial RS232 half duplex, Pic C Compiler

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
   }
}
Scroll al inicio