Conexion GPS-PIC

Hola compañeros, despues de mirar por los post del foro no me queda claro como he de conectar un GPS con salida serie a un PIC, el proyeto que estoy maquinando es para el estudio y desarrollo del protocolo NMEA, quiero comunicar un GPS (primer problemilla) con un PIC (18F6622).
La primera duda existencial es como adaptar los niveles de tension entre el GPS y el PIC, el GPS quiero que sea un Trimble Lassen IQ, este GPS tiene dos puertos serie al igual que el PIC ya que admite correciones diferenciales para mejorar la precision y me ha gustado mucho, pero trabaja a 3.3v y el PIC lo va ha hacer a 5.0v, por lo que tendre que intercalar un adaptador logico, en el foro he visto un esquema con dos transistores un BC558 y un BC548 y unas resistencias y en Sparkfun tienen uno con BSS138, este es el que mas me ha gustado pero con ambos esquemas me pasa lo mismo, aparte del circuito adaptador logico he de poner las resistencias pull-up o con el circuito solo vale, y si es hay que ponerlas ¿donde las coloco?, es que dado que el PIC es en formato TQFN y el GPS tiene una conexion propia y el unico conector que encuentro que es en formato SMD se me complica y encarece un poco las practicas ya que tengo que desarrollar la placa y las modificaciones posibles son bastante chapuceras y complicadas, y no me queda nada claro el tema.
Gracias por el interes.
 
PMB-648 ó PMB-688 de polstar.
Este trabaja a 5 voltios, NMEA 0183 v.2.2, salidas TTL/RS232 y el último hasta permite conectar antena externa para mejor ganancia.
 
Gracias hamser, pero el Trimble se ajusta mejor a mi interes ya que dispone de dos puertos serie a traves de los que puedo recibir las sentencias NMEA, enviar los comandos de configuracion y control del GPS, enviar las correcciones diferenciales, estas mejoran la precision hasta el metro de precision, tambien es un poco mas caro, pero poco. La cuestion es que quiero desarrollar una placa que externamente trabaje a 5v e internamente a 3.3v y me trae de cabeza lo de las resistencia Pull-up o Pull-down. Si visitais la pagina de Sparkfun tienen un conversor logico para dos puertos serie a base de BSS138, habia pensado pillar uno e integrarlo en mi placa, pero me surgen las puñeteras dudas, y no me aclaro.
 
En definitiva el Trimble Lassen IQ es un muy buen aparato, pero esta corrección diferencial si es obtenida por SBAS hasta donde se para C.A y America del sur no funciona, el WAAS solo para Norte america.
A menos que hablemos de estaciones de referencias, aunque creo que con usted hablamos de EGNOS.

Algo que no tengo muy claro de este dispositivo Trimble Lassen IQ, hay que soldarlo? Cuánto cuesta?
y con respecto a tu conversor de nivel, simulalo con ORCAD u otro simulador, Constrúyelo y pruébalo por separado.
Buena suerte golumx!!!
 
El formato de correciones diferenciales es el RTCM 104, es la version mas antigua del protocolo RTCM, dependera de la zona donde lo emplees, en España hay varias entidades que sirven estos datos a traves de internet para hacer RTK, pero eso vendra mas adelante. El circuito conversor de niveles logicos esta testado por que es un diseño comercial que vende sparkfun pero la duda es si tengo que poner las resistencias pull-up al intercalar este circuito, si hay que conectarlas a 3.3v a 5.0v o hay que ponerlas para ambas tensiones.
 
Hola, saludos, estoy trabajando con los módulos GPS Neo 6M de UBLOX y el modulo GSM SIM 900, he configurado el GPS para que me envié una sola trama, y funciona perfectamente si lo tengo conectado de forma individual al pic, para conectar los dos módulos al pic necesito de dos rs232, los cuales también los he configurado y es aquí donde tengo problemas por que los datos que llegan desde ambos dispositivos, se leen perfectamente, pero en el momento de trasmitir una parte del mensaje se esta trasmitiendo por un canal y otra parte por otro, lo cual me genera problemas, ya en teoría, lo que debe suceder es que el modulo GPS esta enviando cada 5s una trama, el modulo GSM en cual quier momento recibe un mensaje desde un teléfono móvil y realiza una acción (prender un led), luego cuando se envía la orden de apagado del led este debe responder "hecho" (lo esta haciendo) y también enviar la trama de datos del GPS como mensaje hacia el usuario, es en esta parte donde no hay buen funcionamiento. si alguien conoce o trabajado con estos dispositivos agradezco sus sugerencias.
Anexo código.

Gracias...

PHP:
#include <18f452.h>
#fuses XT,NOWDT,PROTECT,NOCPD,NOLVP,PUT,BROWNOUT,BORV27,CPB
#use delay(clock=4000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C4,rcv=PIN_C5,stream=GSM,bits=8)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,stream=GPS,bits=8)
#include <string.h>
#include <lcd.c>
#use standard_io(C)
#use standard_io(B)

char trama[70];
char dato=0,dato2=0;
int  conta=0;
int1 rs232_ok=0;

void iniciobuf();
void lectura(char data);

#int_RDA
void serial_isr()
{ 
 dato=0;
 while(true)
 {
 if(kbhit(GSM))
   {
    dato=fgetc(GSM);
    //printf("%c",dato);
     if(dato=='3')
    { 
     //lcd_gotoxy(1,1);
     //printf(lcd_putc,"%c",dato);
     output_high(PIN_b0);
     dato=0;
    }
   if(dato=='b')
     {  conta=1;
      lcd_gotoxy(6,2);
      printf(lcd_putc,"%d",conta);
       output_low(PIN_b0);
       delay_ms(2000);
       printf("AT+CMGS=\"+573206622812\"\r");
      delay_ms(2000);
      printf("hecho\r");
      delay_ms(2000);
      putchar(0x1a);
      printf("AT+CSMS=1\r");
     
       //enviogsm();
      //dato=0;
     }
   }
   
  if(kbhit(GPS))
    {
     dato2=getchar(GPS);
     lectura(dato2);
     //printf("%c",dato2);
    }
  }

}


void main()
{

 int i=0;
 lcd_init();
 enable_interrupts(INT_RDA);
 enable_interrupts(GLOBAL);
 iniciobuf();
 rs232_ok=false;
 lcd_gotoxy(1,1);
 printf(lcd_putc," la:");
 lcd_gotoxy(1,2);
 printf(lcd_putc,"lo:");
 while(true)
  {
   if(rs232_ok==true)
    
   { 
   for(i=18;i<=29;i++)
            { 
             lcd_gotoxy(i-13,1);
             printf(lcd_putc,"%c",trama[i]);
            }
   for(i=31;i<=43;i++)
            { 
             lcd_gotoxy(i-27,2);
             printf(lcd_putc,"%c",trama[i]);
            }
         
    }
  //if( rs232_ok==false)
  if(trama[43]=='W')
     {delay_ms(3000);
      iniciobuf();
      }
    
   
  }


}

void iniciobuf()
{
 int k;
 //printf(lcd_putc,"\f");
  for(k=0;k<=70;k++)
   { 
    trama[k]=0;
   
   }
  conta=0;
}

void lectura(char data)
{ printf("%c",data);
  switch(data)
    {
     case '$' :  rs232_ok=true; break;  // asegura que solo se ponga en alto cuando llega una trama de datos del GPS, ya estas empizan con $    
     case '\n': rs232_ok=true; break;
     default: trama[conta++]=data; break;   // almacena el cararter en la posicion correspondendiente
              delay_ms(2000);
              printf("AT+CMGS=\"+573206622812\"\r");
              delay_ms(2000);
              printf("%c\r",data);
              delay_ms(2000);
              putchar(0x1a);   // comando de contol+z para envio de mensaje desde GSM
              printf("AT+CSMS=1\r"); 
  } 

}
 
Hola ingdirson.
No soy partidario del CCS y de implementar UART por software, además de que estoy reintegrándome a
Esto de los micros, así que no seré de mucha ayuda.

A lo que puedo entender el UART por hardware es para el GPS, y el de software es el del GSM.
Noto que usas enormes retardos de más de 2 segundos dentro de la interrupción lo que no es correcto.

Dado que desconozco CCS, quisiera hacerte una pregunta.
En CCS declaras ambos uart uno por hardware y el otro por software, pero al momento de
Transmitir información a un UART en particular como sabes a cual va dirigido el dato?
Es decir, cuando usas printf que te dice en ccs hacia dónde va el stream?
como puedes conmutar entre uno y otro?

Dices que lo único que no te funciona es esto de enviar msg con printf porque conmuta entre uno y otro,

Pienso que te sería más fácil de solucionar cualquier problema en el C18 compiler.

Estoy trabajando en algo parecido con un sim900, PMB688 / L10 de Quectel usando un pic24fj(32/64)ga002
y dsPIC33FJ128GP202 el cual tiene 2 UART, 2 SPI y pins mapeables. Te aconsejaría que migraras a los
PIC24FJ son baratos sencillos y a mi parecer más fáciles de programar en C que los PIC16/PIC18.
 
hola hamster

Gracias por contestar.
Bueno cuando implemento los uart, efectivamente es uno por hardware y otro por software, en el momento en que los defino, indico que stream van a recibir cada uno, así que cuando uso la interrupción hago uso tambien de kbhit(stream), que lo que hace es devolver un true si ha llegado algo al bufer, asumo que por estar dentro de este condicional la impresión se hace en el bufer correspondiente a dicha stream ya sea GPS o GSM, pero es hay donde como dices no hay seguridad de que se envie por el canal deseado. Ademas de que la interrupción según tengo entendido solo es para la uart de hardware.
respecto a pasarme a la familia de pic que me sugieres, lo he pensado, solo que las tiendas de electrónica en mi ciudad no manejan esas referencias, sin embargo tratare de buscarlo en las ciudades aledañas.
Saludos...
gracias...
 
Yo creo que el problema no es de hardware sino del software, de la estructura del programa.
Pienso que debería ser posible tener un módulo UART por hardware y otro por software a la vez, quizás siendo necesario reducir la velocidad del módulo de software a 4800 bps o 2400 bps.

Programa:
Nunca utilices un while(1) dentro de una rutina de interrupción, ni delays. Muchos aconsejan marcar flags y atenderlos en el loop de main, a mí a veces me gusta procesar algo que no requiera muchos ciclos y permita agilizar y hacer más eficiente el programa.

Además estás utilizando delay_ms(2000); lo que es hacer que el micro sea ciego sordo y mudo durante 2 segundos (!!!) a cualquier cosa que pueda estar pasando (que llegue un caracter por gsm, o un caracter por gps).

Usar las rutinas de delay es un comienzo para aprender, pero ni bien se precisa que el micro haga múltiples tareas o atienda eventos que no sabemos cuando van a ocurrir quedan claras las limitaciones que impone.

Es más, ni siquiera haría falta utilizar interrupciones (esto suponiendo que la librería de software uart trabaje por sí sola y no requiera que nosotros programemos nada).
¿Como sería una posible estructura de programa secuencial (sin interrupciones, para hacerlo más fácil de entender) que pueda manejar eso?:

Podemos hacer una estructura switch-case, que es fácil de entender sobre todo si uno recién esta tratando de abandonar los benditos delay:

PHP:
enum{inactivo, datoRecibido}estadoGSM;

char datoGSM = 0;

char tramaGPS[100]; //no se la longitud maxima de las tramas NMEA, reemplazar
                    //100 por lo que corresponda
unsigned int tamanoTrama = 0;

enum{recibiendoTrama, tramaCompleta}estadoGPS;

void main(void)
{

    //Inicialización, bla bla bla

    estadoGSM = inactivo;
    estadoGPS = recibiendoTrama;
    while(1)
    {
        switch(estadoGSM)
        {
            case inactivo:
                if( kbhit(GSM) )
                {
                    datoGSM = fgetc(GSM);
                    estadoGSM = datoRecibido;
                }
            break;
            case datoRecibido:
                //Procesar dato
                switch(dato)
                {
                    case '3':
                        //ejecutar acciones en respuesta a recibir '3'
                    break;

                    case 'b':
                        //ejecutar acciones en respuesta a recibir 'b'
                    break;
                }
               
                estadoGSM = inactivo;
            break;
        }

        switch (estadoGPS)
        {
            case recibiendoTrama:
                if( kbhit(GSM) )
                {   
                    tramaGPS[tamanoTrama] = getchar(GPS);
                    if(tramaGPS[tamanoTrama] == '\r')    //\r o \n no se cual es
                    {
                        estadoGPS = tramaCompleta;
                    }
                    tamanoTrama++;
                   
                }
            break;

            case tramaCompleta:
                //empezar transmisión GSM
                //mostrar en LCD
                //etc.
                //No sobrecargar esto de instrucciones que pronto podemos
                //recibir un nuevo caracter del GPS y nos lo vamos a perder
                tamanoTrama = 0;
                estadoGPS = recibiendoTrama;
            break;
        }

    }//fin while(1)
}//fin main

Bueno, con eso creo que se ve la idea, el programa no esta completo ni mucho menos (no es la idea del foro dar el trabajo hecho, sino ver que soluciones hay, evaluarlas, etc).
Habría que hacer algo similar para atender el GSM (cuales estados utilizarías?), y enviar 2 cosas diferentes por el mismo canal se puede hacer empleando colas fifo, o más fácil, usando 2 buffers por separado (si hay algo para enviar en cualquiera de los 2 buffers se envía, y se llama a la rutina de transmisión dentro del while(1) ).
 
Gracias Ardogan...
Implementare tu código y tendré en cuenta tus sugerencias, hago las pruebas y les cuento el resultado, también he conseguido un micro con dos uart, haré pruebas con ambos y les cuento...
Gracias...
saludos desde Colombia
 
Hola. Que pena no haber escrito antes, pero estoy en exámenes finales de la universidad y me tocó suspender el proyecto por una semana.

He hecho las pruebas correspondientes tanto al código de Ardogan, como a uno que implementé con un PIC que tiene dos UART, el 18F26K22, pero tengo el mismo problema en ambos.

Tengo una bandera global que no se está actualizando, es decir, en una parte de mi código, yo genero el cambio de estado de esta, pero sólo lo hace dentro de la función en que se genera el cambio.
Cuando quiero verificar el estado de esta desde otra función, esta no se ha actualizado y conserva el valor con el que se declaró inicialmente.

Agradezco su pronta respuesta, pues el tiempo de entrega se agota y llevo varios días en este problema.

Saludos.
Anexo el código...
PHP:
#include <18F26K22.h>
#fuses NOWDT,WDT128,BORV29,NOLVP,NOXINST
#use delay(internal = 64000000)
#use rs232(baud = 9600,parity = N,xmit=PIN_C6,rcv=PIN_C7,bits=8,stream = GSM)
#use rs232(baud = 9600,parity = N,xmit=PIN_B6,rcv=PIN_B7,bits=8,stream = GPS)
#include <string.h>
#use standard_io(B)

char datoGSM=0,datoGPS=0;
int cont=0;
int8 ban = 0;
//extern int ban;
char trama[70];
int rs232_gps=false;


void recibeGSM(char dato);
void recibeGPS(char trama);
void confiGSM();
void bandera(int band);


//gsm
#INT_RDA
void RDA_isr(void) 
{
    //confiGSM();
    datoGSM = fgetc(GSM);
    recibeGSM(datoGSM);
    fprintf(GSM, "%c",datoGSM);
}
   
   
 
 //gps
#INT_RDA2
void RDA2_isr()
{
     datoGPS = fgetc(GPS); 
     recibeGPS(datoGPS);
     fprintf(GPS, "%C" "%u",datoGPS,ban);
}

void main()
{
   int bandera;
   //ban = 0;
   enable_interrupts(INT_RDA);  /* Habilita interrupcion UART1 */
   enable_interrupts(INT_RDA2); /* habilita interrupcion UART 2 */
   enable_interrupts(GLOBAL);   /* Habilitador Global de interrupciones */
  // while(true){
   
   
   //}


}

void recibeGSM(dato)
{ 
  switch(dato)
  {
   case '3':
             output_high(PIN_b0);

             break;
  case 'b':
            output_low(PIN_b0);
            confiGSM();
           delay_ms(500);
            fprintf(GSM,"AT+CMGS=\"+573206622812\"\r");
           delay_ms(200);
            fprintf(GSM,"hecho\r");
            delay_ms(500);
            putchar(0x1a);
            fprintf(GSM,"AT+CSMS=1\r");
           
            break;
  case 'p':
          
           confiGSM();
           ban=1;
           fprintf(GSM,"Aqui%d",ban);
           
           // (1);
           break;
  }



}

void confiGSM()
{
  fprintf(GSM,"AT+CMGF=1\r");
  delay_ms(500);
  fprintf(GSM,"AT+CNMI=2,2,0,0,0\r");
  
}

void recibeGPS(char data)
{
  int b;
 
  
   fprintf(GPS,"holo%d",ban);
  
 // fprintf(GSM,"BAN:%c",b);
 switch(data)
       {
       case '$':
               rs232_gps=true;
              
       
       }


}
 

Adjuntos

  • GPS_GSM_2UART.zip
    125 KB · Visitas: 14
Última edición por un moderador:
...Tengo una bandera global que no se está actualizando, es decir, en una parte de mi código, yo genero el cambio de estado de esta, pero sólo lo hace dentro de la función en que se genera el cambio.

Toda variable que vaya a ser escrita dentro de una rutina de interrupción debe ser declarada como volatile.

Debe ser:
volatile char datoGSM=0;
volatile datoGPS=0;

¿Por qué?, porque el compilador optimiza, si tengo el siguiente código:

PHP:
int a = 2;

void main(void)
{
    switch(a)
    {
        case 0:
        break;
        
        case 1:
        break;

        case 2:
        //siempre va a entrar acá, porque el compilador no ve ninguna
        //función que está modificando el valor de a.
        //Más aún, el optimizador puede eliminar completamente el switch(a)
        //y dejar en su lugar el código que hay entre el case 2: y el break
        break;

        default:
        break;
    }
}
En tu caso para el programa siempre va a ser datoGPS = 0 y datoGSM = 0;
Podés pensar que el compilador es ciego a lo que se hace dentro de rutinas de interrupción. O mejor dicho, que el procesador ejecuta en paralelo lo que hay en rutinas de interrupción y en el programa principal.
Algunos se refieren a esto como background/foreground, sin embargo es confuso porque por foreground algunos se refieren a código ejecutado dentro de rutina de interrupción y otros a código ejecuta por fuera del contexto de interrupción.
Por eso es más claro decir código de interrupción y código normal o de no-interrupción.

De ahí la necesidad de poner volatile, para informar que esa variable puede ser alterada por algo más que el código normal (por una rutina de interrupción, o por algún periférico del micro como por ejemplo un puerto de entrada, el registro que recibe datos de la uart, el resultado de una conversión AD, etc).
---------------------------------------------------------
Otra cosa que me preocupa, seguís llamando a funciones complejas (fprintf es un mastodonte) desde rutinas de interrupción (n)(n)(n). Pero bueno, ya respondí antes como me parece a mí (destaco con negrita porque hay infinitas formas de hacerlo, y lo que yo propongo no deja de ser en cierto punto arbitrario) que el programa podría funcionar, de ahí en más es tu decisión.

Agradezco su pronta respuesta, pues el tiempo de entrega se agota y llevo varios días en este problema.
No tengas miedo de preguntar, cuando mucho alguno podrá responder de mala manera pero es preferible eso (hay que filtrar algunos malos modos y quedarse con la información útil) a perder días y frustrarse.
 
Toda variable que vaya a ser escrita dentro de una rutina de interrupción debe ser declarada como volatile.

Debe ser:
volatile char datoGSM=0;
volatile datoGPS=0;
.
Hola Ardogan, el problema de la bandera, hacia referencia a la variable "ban", problema que ya corregí, al parecer los fuses no estaban bien declarados, asi que los añadi usando el Wizard y la ban funciono correctamente; los fprintf que ponía en las interrupciones era solo un eco para usar de forma simulada y saber si en realidad los datos eran resividos y enviados desde un rx a un tx y viceversa, al momento de montar son comentados.
Por otro lado hare uso del volatile para las variables que me sugieres, para garantizar mayor efectividad del proceso y de las interrupciones. El proyecto ya funciona, no a la perfeccion pero funciona, solo tengo un problema en modulo GSM con los mensajes, que no le estan llegando en algunas oportunidades, al parecer parte de la configuracion se pierde al reiniciar el modulo..., sin embargo pues ya el semestre finalizo y bueno aunque no fue la mejor nota, gracias a Dios pude pasar, sin embargo debo mejorar este proyecto por que el siguiente semestre debo exponerlo en una feria de la ciencia, asi que escucho sugerencias para solucionar este ultimo problema.
Saludos desde Colombia, gracias por sus recomendaciones...
 
Atrás
Arriba