Uso eficiente de mas de 2 puertos serie en un PIC

Que tal amigos del foro, bueno como dice el título el tema se trata del uso eficiente de mas de dos puertos seriales en un solo PIC el cual obviamente tiene un solo puerto serial físico.

El punto es que mientras que usando el puerto físico no tengo ningún problema, la situación se malogra cuando declaro un nuevo puerto serie pues en el momento en el que simulo el programa con ISIS, la interrupción del puerto serie deja de funcionar y deja a mi programa en el aire sin hacer lo que deseo.

Aqui les explico un poco mas sobre el código.
Primero: Mi programa tiene un programa principal y otros secundarios, el que les pongo aqui es un "source code" secundario llamado "serial llave"

Código:
struct
{
   int8 address_rsp;                        //direccion del que envia el mensaje
   int8 len;                                //numero de bytes en el mensaje recivido
   int8 data[20]; 
}S_Llave_rx;

int8 PLlave;   //Puertos seriales, indica si esta libre, lleno u ocupado

int Llave_ind_trama=0;
char Llave_x_char;
int1 Llave_New_trama=0;  //Se pone a 1 cuando se ha terminado de recibir la trama
int Llave_step_trama=255;
int Llave_cks1=0xAA;
int Llave_tx_data[20];
int Llave_Bus_Act;

#int_RDA
void  RDA_isr(void) 
{
   Llave_x_char=fgetc(S_Llave);
   //x_char=getc();
   
   if(Llave_BUS_ACT==1)
   { Llave_BUS_ACT=0;}
   
   if(Llave_x_char=='#'&& Llave_step_trama==0xFF)
  {  Llave_step_trama=0;
     Llave_New_trama=0;
     Llave_ind_trama=0;
     Llave_cks1=0xAA;
     PLlave=Ocupado;
  }
  
  if(Llave_step_trama!=0xFF)
  {  
     switch(Llave_step_trama)
     {  case 0x00:               //para el #   
                   ++Llave_step_trama;
                   break;
        case 0x01: ++Llave_step_trama;
                   if(Llave_x_char!=Puerto_LCD)  
                   { Llave_step_trama=0xFF;}  //abandonar por que no coincide con el ID de este RTU
                   break;
                   
        case 0x02: ++Llave_step_trama;
                   S_Llave_rx.address_rsp=Llave_x_char;  //guardar el ID del que envia
                   break;
                   
        case 0x03: ++Llave_step_trama;
                   S_Llave_rx.len=Llave_x_char;   
                   break;
         
        default:  if(Llave_ind_trama<S_Llave_rx.len)
                  { 
                    S_Llave_rx.data[Llave_ind_trama]=Llave_x_char;
                    Llave_cks1^=Llave_x_char;
                  } 
                  if(Llave_ind_trama==S_Llave_rx.len)
                  { 
                     if(Llave_x_char==Llave_cks1)
                     {  
                        Llave_New_trama=1;
                        S_Llave_rx.data[Llave_ind_trama]='\0';
                     }
                     Llave_step_trama=0xFF;
                  }
                  ++Llave_ind_trama;
                  break;
     }
  }
}

int Verifica_PSerie()
{
   
   if(Llave_new_trama==1)
   {
      Llave_new_trama=0;
      PLlave=Lleno;
      return 1;
   }
   else
   {
      Llave_new_trama=0;
      PLlave=Libre;
      return 0;
   }
}
La parte mas importante es el del #INT_RDA ya que lo que sigue después de eso es la interrupción generada cuando un dato serial llega al puerto del PIC.

En el codigo ".h" de mi aplicación tengo la declaración del puerto serie

Código:
#include <16F876A.h>
#device adc=8

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES HS                       //High speed Osc (> 4mhz for PCM/PCH) (>10mhz for PCD)
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOPROTECT                //Code not protected from reading
#FUSES NODEBUG                  //No Debug mode for ICD
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD                    //No EE protection
#FUSES NOWRT                    //Program memory not write protected

#use delay(clock=20000000)

#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,stream=S_Llave)
Como verán estoy usando el pic 16f876a y los pines del puerto serial son los del puerto serial físico del micro.

Si el programa lo dejo alli todo funciona de maravilla, cuando llega un dato al puerto serie pues se genera la interrupción y el programa hace lo que debe hacer.

El problema es que yo necesito usar dos puertos seriales, 1 el de los pines del puerto físico y 2 un puerto serial con las siguientes características:

#use rs232(baud=9600,parity=N,xmit=PIN_C3,rcv=PIN_C4,bits=8,stream=RS_485)
pero cuando hago esa declaración, deja de funcionar todo, ya no se genera la interrupción por entrada al puerto serial y el programa no hace lo que quiero.

y el código del uso del otro puerto serial es el siguiente:

Código:
struct
{
   int8 address_rsp;                        //direccion del que envia el mensaje
   int8 len;                                //number de bytes en el mensaje recivido
   int8 data[20]; //data of the message received
}RS_485_rx;

int RS_485_ind_trama=0;
int RS_485_x_char;
int1 RS_485_New_trama=0;  //Se pone a 1 cuando se ha terminado de recibir la trama
int RS_485_step_trama=255;
int RS_485_cks1=0xAA;
int RS_485_tx_data[20];
int RS_485_Bus_Act;

void RS_485_RDA_isr() 
{
   RS_485_x_char=fgetc(RS_485);
   
  if((RS_485_x_char>0) && (RS_485_BUS_ACT==1))
  { RS_485_BUS_ACT=0; }
   
  if((RS_485_x_char=='#') && (RS_485_step_trama==0xFF)) //#=35
  {  
     RS_485_step_trama=0;
     RS_485_New_trama=0;
     RS_485_ind_trama=0;
     RS_485_cks1=0xAA;
  }
  
  if(RS_485_step_trama!=0xFF)
  {  
     switch(RS_485_step_trama)
     {  
        case 0x00:               //para el #   
                   ++RS_485_step_trama;
                   break;
        case 0x01: ++RS_485_step_trama;
                   if(RS_485_x_char!=Puerto_LCD)  
                   { RS_485_step_trama=0xFF;}  //abandonar por que no coincide con el ID de este RTU
                   break;
                   
        case 0x02: ++RS_485_step_trama;
                   Rs_485_rx.address_rsp=RS_485_x_char;  //guardar el ID del que envia
                   break;
                   
        case 0x03: ++RS_485_step_trama;
                   Rs_485_rx.len=RS_485_x_char;   
                   break;
         
        default:  if(RS_485_ind_trama<Rs_485_rx.len)
                  { 
                    Rs_485_rx.data[Rs_485_ind_trama]=RS_485_x_char;
                    RS_485_cks1^=RS_485_x_char;
                  } 
                  if(RS_485_ind_trama==Rs_485_rx.len)
                  { 
                     if(RS_485_x_char==RS_485_cks1)
                     {  
                        RS_485_New_trama=1;
                        Rs_485_rx.data[RS_485_ind_trama]='\0';
                     }
                     RS_485_step_trama=0xFF;
                  }
                  ++RS_485_ind_trama;
                  break;
     }
  }
}

int Verifica_RS_485()
{
   RS_485_RDA_isr();
   if(RS_485_New_trama==1)
   {
      RS_485_New_trama=0;
      return 1;
   }
   else
   {
      RS_485_New_trama=0;
      return 0;
   }
}
Finalmente el mi "source code" principal llamo a las dos verificaciones de los puertos de la siguiente manera:

Código:
if(Verifica_PSerie()==1)  //Detecta si se hay datos en el puerto serie 1
   {
      Com_Puerto=Puerto_Llave;
   }
   else if(Verifica_RS_485()==1)
   {
      Com_Puerto=Puerto_RS485;
   }
   else
   {
      Com_Puerto=Ninguno;
   }
La pregunta es si ¿alguien de ustedes tiene alguna experiencia positiva al usar mas de 1 puerto serial con un pic? o si me puede indicar como mejorar mi código para no tener problemas con la lectura de los dos puertos?
 
Hola como estás.

Primero que nada: No uses el ISIS para hacer las pruebas, muchisimas veces me ha pasado que en el simulador no me andaba y en la realidad funciona correctamente.

Ahora yo te comento que usé 2 puertos series en una aplicación GSM. El problema generalmente lo tenía en la interrupción, acá tenés que hacer una gestión eficiente. Lo que hice yo fue tomar los datos y luego procesarlos en el bucle principal de programa, nunca tomaba decisiones dentro de la interrupción, tampoco me fijaba el estado de otros puertos, solo seteaba banderas, asignaba valores a variables y nada más...pero siempre lo más simple posible.

En el otro puerto serie (por software) lo usaba para enviar datos de debug al hyperterminal de la PC. Lo que NO podés, es tener 2 interrupciones por puerto serie cuando tenés 1 puerto serie por hardware.

El problema que a vos se te presenta es que tenés que estudiar como trabaja la librería RS485 de CCS. A mi personalmente nunca me funcionó correctamente. Fijate que en dentro de las opciones que trae dicha librería, es la de usar la interrupción por el puerto B0, simulando una USART por hardware...

Ahora si tenés un microcontrolador, por ejemplo PIC18F, que tenga 2 USART hardware, vas a tener que declarar cuál interrupción va a tener más peso. Eso se explica en la hoja de datos del PIC y en la ayuda de CCS también hay una referencia.

Otra recomendación es que te escribas tu propia librería para trabajar con RS485, la de CCS nunca me funcionó..o trabajó con muchos errores.

Un saludo !
 
Hola moyano, gracias por la respuesta, bueno en realidad no me imaginaba que me daria problemas el usar el ISIS para las simulaciones ya que siempre he usado ese programa aun con códigos mas elaborados y pues nunca tube problemas, aunque hace un tiempo "actualice" mi ISIS a la version 7.8, quiza debería trabajar con la versión que siempre usé o en todo caso tu conoces algun simulador mas poderoso?.

Sobre el driver RS_485 que uso no es del CCS PICC, ese driver es propio en realidad, y como dije, mientras no declare ese segundo puerto serial con el #use todo funciona bien, la cosa se complica cuando lo declaro e intento utilizar los dos puertos seriales. Ahora aunque los códigos de los dos puertos son casi idénticos, solo uso la interrupción para el puerto serial en los pines físicos del micro, el otro si te das cuenta, para entrar a la seudo-interrupción, lo hago con una llamada que es:

Código:
int Verifica_RS_485()
{
   RS_485_RDA_isr();    //Esta es la llamada a la subrutina que se encarga de leer el puerto serial x soft
....

el tema es que este código es una pequeña parte del proyecto y ese pic el 876A en realidad solo lo uso para leer los puertos seriales y tener un LCD ya que el pic principal es un 18F4550 y para comuncarme con éste pic principal es que uso el protocolo 485, ya que ambos chips estarán a una distancia larga y pues estoy tratando de hacer un código que funcionne casi completamente en simulación antes de mandarme a imprimir el diseño de la tarjeta ya que seria costoso desecharla.

Lo de usar la interrupción del B0 es una buena sujerencia, la tendré en cuenta. De verdad, de verdad necesito tener una manera de comunicarme con mas de un puerto serial ... de eso depende el proyecto, si tuvieras una parte del código que usaste, mas que todo en el tema de la lectura de los puertos que es lo que me interesa me ayudaria mucho.

Gracias
 
Supongo que a estas alturas ya resolvieron el problema de los dos puertos serie en un PIC.

De todos modos aquí dejo mi aportación.

Hablando de puerto USART virtual, el mejor ejemplo está en un PIC16F84, porque este PIC no tiene puerto serie.

Lo primero para implementar el puerto serie es definir la velocidad en baudios a la que se va a comunicar el microcontrolador con la PC.
Después de esto se aprovecha la interrupción por cambio de estado en el PIC, esto es en los puertos RB<4-7>

RSI BTFSS INTCON,0 ;CHECA INTERRUPCION DE CAMBIO DE ESTADO

Si la interrupción es válida, entonces se ejecuta el código. (Está en ensamblador.)

---Para transmitir un dato por el puerto serie----

Código:
ENVIAR  MOVLW   H'08'      ;8 datos
        MOVWF   CONTA       ;carga al registro conta
        BCF     PORTB,TX        ;pone la linea de TX en bajo
        CALL    DELAY           ;llama al la duracion del pulso
XNEXT   BCF     PORTB,TX
        BCF     STATUS,C        ;carry =0
        RRF     RS232,F         ;rota primer bit (atraves de carry)
        BTFSC   STATUS,C        ;checa el estado de carry
        BSF     PORTB,TX        ;si carry=1 entonces linea Tx=1
        CALL    DELAY           ; y marca duracion del pulso ( si pulso=0 => carry=0)
        DECFSZ  CONTA,F         ;descuenta uno al contador de datos 8 en este caso
        GOTO    XNEXT           ;regresa y continua con el siguiente bit del dato a transmitir
        BSF     PORTB,TX        ;cuando terminan los 8 bits del dato linea TX =1
        CALL    DELAY           ;duracion del pulso
        RETLW   0      ; y regresa.

DELAY   MOVLW   H'20'           ;RETARDO DE 52 microseg APROX
        MOVWF   RETAR0
REDO    DECFSZ  RETAR0,F
        GOTO    REDO
        RETLW   0
Esto funciona muy bien en el PIC16F84.

Para el PIC16F877 y/o el PIC16F876 que ya cuenta con un módulo USART, se puede implementar otro.
Pero he aquí el interés de mi participación en el foro.

Para la transmisión en PICBASIC tengo el siguiente código y si funciona.

Código:
'--------------------TRANSMITE 9600  XTAL=20MHz--------------------------------
'RX= RB<4>, TX= RB<5>

'-------------------------MI VERSION--------------------------------------
OUTTX:  @NOP
  PORTC.1=1
  RS485 = RECEP
  CONTX = 8
  PORTB.5=0               'BIT DE ARRANQUE
  DELAYUS 104
XNEXT:  STATUS.0=0              'CARRY =0
  @ RRF   RS485,F         'ROTA A LA DERECHA DATO
  IF STATUS.0=1 THEN
  PORTB.5=1
  ELSE
  PORTB.5=0
  ENDIF
  DELAYUS 104
  CONTX = CONTX - 1       'DECREMENTA CONTADOR
  IF CONTX > 0 THEN
  GOTO XNEXT
  ENDIF
  PORTB.5=1               'BIT DE PARADA
  DELAYUS 104
  PORTC.1=0
  RETURN

;-------------------------------------------------------------------------
Pero para la recepción no me funciona el código correspondiente, claro está.

¿Alguien me puede ayudar con este tema?

De preferencia en PICBasic. Gracias.
 
Última edición por un moderador:
En PICBasic Pro tienes SerOut, SerIn, SerOut2, SerIn2, HSerOut, HSerIn, HSerOut2 y HSerIn2.
A cualquiera de esas funciones les puedes poner el pin y la velocidad que quieras para enviar o recibir.
Por lo tanto, puedes disponer de varios puertos seriales por software y uno por módulo USART.

Hacer funciones para emular RS-232 en un programa de PICBasic, no le veo sentido.
 
Lo malo de los puertos serie por soft es que suelen tumbar el sistema. Por lo tanto su uso es muy restringido (por no decir directamente que son muy bonitos y muy inútiles)
Dependerá de como esté hecho el código pero normalmente mientras usas un soft serial no puedes hacer nada mas.
Si puedes hacer un uso "consecutivo" no tendrás problemas. Si es simultaneo será complicado hacerlo andar.
 
Atrás
Arriba