Vai al contenuto
PLC Forum


Problema Ricezione Rs232 Pic


Edge

Messaggi consigliati

Vi chiedo un suggerimento se potete. Di seguito riporto la rutine di interrupt che utilizzo quando ricevuto una risposta dal modem che ho collegato al PIC (utilizzo il PIC18F458). Non capisco perchè a volte il PIC riesce a leggere la risposta e altre volte no.

Questa è la routine di interrupt:

void interrupt()

{

char dato_ricevuto;

for(cont=0;cont<MAX-1;cont++)

{

if (PIR1.RCIF) // Se ricevo qualcosa...

{

sms_ricevuto[cont] = UART1_Read();

cont++;

}

}

switch (STATE_GSM)

{

case 0:

{

risposta = -1; // clear response

for(cont=0; cont<MAX; cont++)

{

if(sms_ricevuto[cont]== 'K' && sms_ricevuto[cont-2]=='O')

{

risposta = GSM_OK;

Answer_received = 1; // Ricezione dato completata

risposta_ID = risposta;

PIR1.RCIF=0;

var_case=0;

}

else

var_case++;

}

if(var_case > 0)

STATE_GSM = 10;

else

sms_ricevuto==0;

break;

}

case 10:

{

for(cont=0; cont<MAX; cont++)

{

if(sms_ricevuto[cont]== 'R' && sms_ricevuto[cont-2]== 'R'

&& sms_ricevuto[cont-4] == 'E')

{

risposta = GSM_ERROR;

Answer_received = 1;

risposta_ID = risposta;

var_case = 0;

PIR1.RCIF=0;

sms_ricevuto==0;

break;

}

}

}

default:

{

STATE_GSM = 0;

break;

}

}

}

Il modem mi risponde con un OK oppure un ERROR a seconda del comando AT che gli invio, però a volte funziona tutto bene e a volte no, nel senso che a volte il PIC è come se si "incantasse" e ricevo sempre come risposta OK anche se dal modem ricevo ERROR o viceversa, ossia ricevo sempre ERROR anche se dal modem ricevo OK. Per sapere se ricevo OK ed ERROR come risposta, setto come vedete la variabile "risposta" a GSM_OK se il comando va a buon fine, altrimenti la setto a GSM_ERROR e poi visualizzo su display LCD la risposta.

Ho fatto anche in modo di ripetere per 3 volte il comando inviato nel caso la risposta del modem fosse ERROR. Anche in tal caso a volte mi funziona e a volte no, nel senso che a volte il PIC continua a leggere come risposta ERROR anche dopo i 3 tentativi, altre volte invece legge già dopo il secondo tentativo OK.

Non è un problema del modem perchè ho fatto in modo di collegare pic e modem alla seriale del PC per vedere tramite Hyperterminal che cosa si scambiavano effettivamente PIC e modem ed ho notato che a volte, anche se la risposta del modem era OK, il pic visualizzava sul display la risposta ERROR.

A questo punto o c'è un'errore che ho fatto nella routine di interrupt, oppure c'è un errore di sincronizzazione.

Nel main la velocità di trasmissione è così settata:

TXSTA.BRGH=1;

TXSTA.SYNC=0;

SPBRG = 22;

UART1_Init(19200); // Inizializzazione baud rate (bps) Seriale PIC

Utilizzando un quarzo da 8MHz ho un errore di Baud Rate dell'1,20% circa.

Premetto che ho provato anche altre combinazioni settando anche velocità più basse ma nulla. Sicuramente faccio un errore nella routine di interrupt.

Qualcuno riesce mica a darmi una dritta?

Saluti

Edge

Link al commento
Condividi su altri siti


Intanto ho visto un errore fatto nel primo ciclo for dell'interrupt.

C'è un cont++ che è in più!

char dato_ricevuto;

for(cont=0;cont<MAX-1;cont++)

{

if (PIR1.RCIF) // Se ricevo qualcosa...

{

sms_ricevuto[cont] = UART1_Read();

cont++; <--------- QUESTO LO DEVO TOGLIERE

}

}

Ho ricompilato e riprovato, ma si comporta ancora uguale a prima per ora.

Saluti

Edge

Link al commento
Condividi su altri siti

Purtroppo non conosco PIC a sensazione io proverei a spostare PIR1.RCIF = 0 dentro l'if del ciclo for

cioè

for(cont=0;cont<MAX-1;cont++)

{

if (PIR1.RCIF) // Se ricevo qualcosa...

{

sms_ricevuto[cont] = UART1_Read();

PIR1.RCIF = 0 ;

}

}

aspetta però il parere degli eserti

Altro dubbio tua spetti MAX-1 priam di sucire dal for ma come sai quanti caratteri ti arivano ? Nel modem di solito si controlloa la sequenza 0x0D 0x0A che chiude il messaggio

Modificato: da accacca
Link al commento
Condividi su altri siti

Intanto grazie per la risposta. Allora, per quanto riguarda PIR1.RCIF=0 dentro al for mi sembra di averlo già provato, ad ogni modo adesso ci riprovo. Per quanto riguarda il fatto di aver utilizzato MAX-1 è una bella domanda. In teoria mi servirebbe per il passo successivo, ossia leggere dati dagli sms della SIM card nel modem. Quando leggo un sms dal modem, questo mi risponde inviandomi non solo il testo del messaggio, ma anche altre informazioni come Numero di Cellulre, Data, Ora, etc...

Ti faccio vedere un esempio di risposta quando chiedo di leggere un SMS:

+CMGR: "REC READ","+393xxxxxxx",,"07/01/15,18:36:34+04"

TESTO: CIAO

OK

Come puoi vedere, (secondo me eh, poi forse sbaglierò), siccome non mi viene inviata una unica stringa su un'unica linea, ma ho anche dei CARRIAGE RETURN (== 0x0D) e dei LINE FEED (== 0x0A) prima di arrivare all'OK finale di conclusione messaggio, non posso mettere come termine del ciclo for 0x0D oppure 0x0A perchè in teoria il ciclo for si concluderebbe prima di aver letto tutto. Io lo ragiono così.

Poi una volta riuscito a leggere l'SMS ricevuto, devo fare in modo di leggere SOLO il testo e basta cercando di "filtrare" ciò che non mi serve. Comunque questo esempio del messaggio è un altro problema che verrà successivamente e vedrò come fare. Era per spiegarti come mai ho messo MAX-1 come conlusione del ciclo for.

Saluti

Edge

Link al commento
Condividi su altri siti

e' vero il modem mette diverse sequenze 0x0D 0x0A ma tu devi decodificare il messaggio innescando la procedura di decodifica ogni 0x0D0x0A

In altre parole ho ricevuto 0x0D0x0A provo a vedere se ho finito altrimenti agigungo ancora al buffer di ricezione

Nel tuo caso ricevendo OK0x0D0x0A hai concluso il messaggio

Così controlli facilmente tutti i casi possibili perchè il modem potrebbe anche rispondere ERRROR0x0D0x0A

Link al commento
Condividi su altri siti

Si, oh capito cosa vuoi dire. Allora intanto provo a realizzare quello che dici tu. Ho provato prima a inserire PIR1.RCIF=0 all'interno del for, ma per ora fa ancora uguale. Sembra quasi un errore durante durante la sincronizzazione. Sicuramente dovuto alla routine di interrupt non fatta bene.

Comunque intanto provo ad implementare quell'altro suggerimento che mi hai dato.

Sai qual è il problema maggiore che ho? Quando devo inserire il PIN della SIM. Se inserisco correttamente il PIN ma il PIC continua a leggermi ERROR come risposta, il firmware riprova 3 volte e poi la SIM si blocca e ci vuole il PUK.

Saluti

Edge

Link al commento
Condividi su altri siti

Infatti alla fine ho fatto così per ora.

Senti accacca ma se faccio in questo poco avrebbe poco senso vero?

for(cont=0;cont<MAX;cont++)

{

if (PIR1.RCIF) // Se ricevo qualcosa...

{

sms_ricevuto[cont] = UART1_Read();

PIR1.RCIF=0;

if(sms_ricevuto[cont]==0x0D && sms_ricevuto[cont-2]== 'K' || sms_ricevuto[cont-2] == 'R')

break;

}

}

Mi conviene fare un ciclo while e mettere delle condizioni all'interno. E' che un do-while all'interno dell' interrupt Handler l'avrei evitata.

Saluti

Marco

Link al commento
Condividi su altri siti

in teoria dentro l'interrupt non dovrebbe starci un ciclo

tu con l'interrupt dovresti solo leggere la seriale e mettere tutto in un buffer senza fare controlli se non quello di non andare oltre dimensione max del buffer

Poi fuori dall'interrupt fai i controlli di fine messaggio .

visto che il modem utilizza stringhe ascii potresti utilizzare

strstr per cercare il fine messagigo

Ad esempio

if (strstr (miobuffer,"OK\r\n") ) fine

if (strstr (miobuffer,"ERROR\r\n") errore

e cos' via

Link al commento
Condividi su altri siti

Ah, ok. Allora provo a togliere i due "CASE" fatti nell'interrupt e lascio solo la memorizzazione nel buffer. Poi provo ad implementare le casistiche nel main.

Saluti

Edge

Link al commento
Condividi su altri siti

Macchè, niente di fatto. Non riesco a concludere. Non mi funziona.

Ho fatto una prima prova in questo modo:

ho tolto tutto dalla routine di interrupt e ho lasciato un ciclo for in cui ciò che arriva dalla seriale viene mandato sull'array che per esempio ho chiamato

dato_ricevuto[MAX];

Poi nel main ho fatto in questo modo per fare una prova:

string=(dato_ricevuto, "OK\r\n"), dove string è un puntatore del tipo char * string

if(string!=0) // Ossia se c'è OK

LCD_Out(1,1,"OK") // Mi scrive Ok sul display.

Fatto in questo modo è una porcata e non mi funziona nulla. Stavolta proprio non ho segni di vita.

Poi ho pensato un altra cosa se è giusta: siccome ricevo la stringa dal modem in ASCII, in teoria potrei anche visualizzarla sull'LCD. Ho provato allora a visualizzare la viaribile dato_ricevuto. Il risultato sono solo schifezze visualizzate e una K che una volta su 100 compariva.

Fatto in questo modo è peggio di prima. Proprio non capisco come devo fare.

Saluti

Edge

Link al commento
Condividi su altri siti

Salve a tutti.

Ho seguito da "lontano" (non per cattiveria, ma il lavoro ha la precedenza) i vostri post.

Adesso confesso di avere parecchia confusione. Provo a riordinarmi le idee.

Si conoscono le caratteristiche del modem ?

Se non ricordo male i modem hanno due modi di funzionamento: modo comandi e modo dati.

In generale, la stringa OK o ERROR indica che siamo passati dal modo dati al modo comandi.

Vorrei ribadire (scusatemi se sono noioso…) alcune cose:

1)Il flag di interrupt PIR1.RCIF e' settato quando l'UART riceve un (UNO) carattere, quindi è inutile controllarlo.

Se sono arrivato a questa routine sicuramente RCIF = 1) a meno che non abbia più interrupt sullo stesso handler.

2)Non bisogna resettate RCIF: si resetta da hardware quando si legge il carattere da UART. Se si resetta e nel frattempo è arrivato un altro carattere, si perde.

3)Nella routine di interrupt bisogna controllare se ci sono stati errori di ricezione.

La loro gestione può essere fatta sia nella routine di interrupt (se non è gravosa in termini di esecuzione) o in una routine a parte (che sarà chiamata dal main).

Questa routine che deve essere chiamata dall'handler di interrupt (fatta per un PIC18F6622) è sicuramente funzionante:

/* Ricezione ad interrupt USART 1 */

#define CHAR_ERR 0xff

#define CHAR_MAX 64

void rx1_int (void)

{

unsigned char tmp11_rx;

unsigned char tmp12_rx;

tmp11_rx = RCREG1;

tmp12_rx = RCSTA1 & 0x06;

if (tmp12_rx != 0x00)

{

tmp11_rx = RCREG1; // svuoto fifo usart

tmp11_rx = RCREG1;

tmp11_rx = RCREG1;

tmp11_rx = CHAR_ERR;

RCSTA1bits.CREN = 0; // reset errori

Nop ();

Nop ();

RCSTA1bits.CREN = 1; // riabilito usart

}

else

{

tmp11_rx = RCREG1;

}

bufferRx1[ptr] = tmp11_rx; // memorizzo

if (++ptr > MAX_CHAR)

ptr = MAX_CHAR; // perdo ulteriori caratteri

// devo dimensionare il buffer di Rx per non arrivare mai

// in questa condizione

// l’ideale sarebbe un buffer circolare

}

Nel mio caso gestisco gli errori (se trovo CHAR_ERR) quando leggo il buffer di ricezione.

Spero di non aver confuso le idee.

Link al commento
Condividi su altri siti

x kappa47

Intanto ti ringrazio per la risposta. Allora ti rispondo subito ai punti:

1) Sì, ho più interrupt nello stesso handler e quindi ho voluto suddividere le condizioni (Ah, il modem è un Telit GM862-GPS operante in modalità comandi).

2) Questo è un errore mio, hai ragione. Ho riletto il datasheet. Ero convinto di dover resettare via software anche il flag RCIF.

3) Non capisco bene questa sequenza:

tmp11_rx = RCREG1; // svuoto fifo usart

tmp11_rx = RCREG1;

tmp11_rx = RCREG1;

tmp11_rx = CHAR_ERR;

perchè ripeti tre volte il comando? Non ti basta svuotarlo una volta il buffer?

Saluti

Edge

Link al commento
Condividi su altri siti

Non conosco il modem che stai usando, ma se esegue e riconosce i comandi AT non dovrebbe essere dissimile da altri tipi. Tieni presente che se lo usi in modalità "verbose" le stringhe hanno il CRLF sia all'inizio sia alla fine della stringa.

Alcune famiglie di PIC hanno un FIFO sulla ricezione dei caratteri; per precauzione svuoto il buffer. In questo modo è vero che probabilmente elimino dei caratteri ricevuti correttamente, ma è anche vero che il messaggio che sto ricevendo è ormai corrotto (c’è stato errore).

Se il PIC che sto usando non ha FIFO, nulla di male: leggere più volte il registro del carattere ricevuto non comporta nulla.

Io sfrutto, non la routine, ma procedure simili anche con altri micro, per cui svuotare il buffer di rx è una (buona) norma di carattere generale.

Link al commento
Condividi su altri siti

Ah, ok, non lo sapevo che avessero anche il CRLF ad inizio stringa.

Il PIC ha una FIFO in ricezione, quindi ho svuotato il buffer di ricezione.

Stamattina ho fatto dei test sull'elettronica per paura che ci fossero problemi nell'adattamento dei segnali dai 5V del micro ai 2,8V del modem ma è tutto a posto a quanto pare.

A titolo informativo, tanto per avere in parere, a volte l'adattamento lo faccio con dei transceiver 74LVCC3245 con doppia alimentazione, oppure come in questo caso interfaccio il MAX232 connesso al PIC con un MAX3232 connesso al modem.

Saluti

Edge

Link al commento
Condividi su altri siti

Uhm Edge fai attenzione io ho usato il GM862 ma l'interfaccia almeno al tempo non era così semplice

Mi ricordo che la doc diceva di usare degli open collector sulle line in input al modme e di collegare questi input tramite resistenze di pullup ad un suo output sul connettore Non ricordo quale ma se ti serve vado a cercare.

Forse i nuovi modelli sono cambiati però controlla

Quindi a connessione con il max secondo me è a rischio perchè in uscita hai 3.3V

Link al commento
Condividi su altri siti

Personalmente preferisco utilizzare i MAX; se cambi applicazione, puoi riutilizzare l’hardware.

Il 74LVCC3245 è adatto per i bus locali; non è un line drivers.

Hai risolto i problemi con la ricezione ?

Link al commento
Condividi su altri siti

x acaca

Guarda, ti posso dire che con i max mi trovo bene e come dice kappa47 sono riutilizzabili in altre applicazione.

Ad ogni modo se alimenti a 3V ci stai dentro ed è difficile che arrivi ai 3,3 V. Se poi consideri anche dell'assorbimento del MAX hai anche una leggera caduta e in uscita hai 3V in pacca. A parte che il MAX3232CPE ti funziona anche a 2,8V.

x kappa47

Macché, non ci sono ancora riuscito. Ho provato anche ad implementare un buffer circolare abbastanza "scarno" :

#define BUFFER_SIZE 32

#define bkbhit (next_in != next_out)

char serbuf[BUFFER_SIZE];
char sms_ricevuto[BUFFER_SIZE];
char next_in = 0;
char next_out = 0;
char Flag_OVF = 0;
char carattere;

void interrupt()
  {

      if (PIR1.RCIF)    // Se ricevo qualcosa...
     {
        char t;
        serbuf[next_in] = UART1_Read();
        t = next_in;
        next_in = (next_in + 1) % BUFFER_SIZE;
        if (next_in == next_out)
        {
            // Buffer full !!
            next_in = t;
            Flag_OVF = 1;
        }

     }

  }


char get_char()
{
    char c;
    c = sms_ricevuto[next_out];
    next_out = (next_out + 1) % BUFFER_SIZE;
    return©;
}


main()

{

  while(1)
   {

     sendAT_command("AT");    // Invio comando 'AT' 
     Lcd_Cmd(_LCD_CLEAR);


if(bkbhit)
 {
     carattere=get_char();
     
     switch(carattere)
      
        {
             case 'K'

               .........
               .........

               .........

                      

    }

Saluti

Edge

Link al commento
Condividi su altri siti

Se funzia non metto in discussione dico solo che il max3232 a 3V lavora al limite inferiore del range ammesso per la V di alimentazione e questa la giudico una scelta non felice ma ripeto è una mia oopinione.

Forse se ne produci N qualcuno potrebbe non funzionare

Comunque l'interfaccia non è in discussione purtroppo sul codice non ti posso aiutare perchè non conosco pic ma credo che kappa sia un valido aiuto

Link al commento
Condividi su altri siti

No no, ma hai ragione, e molto probabilmente come dici se ne produci N è probabile che qualcuno non vada. Era solo un mio modo di operare. Anzi, ho provato un giorno per caso a farla come interfaccia perchè ero scarso di componenti e avevo dei MAX. Allora ho provato a fare un accrocchio interfacciando i due MAX e ho visto che funzionava. Poi ho scoperto che c'erano anche altre persone che avevano avuto la stessa idea e alla fine ho scelto quella. Tutto qui.

Spero solo di arrivare ad una soluzione.

Saluti

Edge

Link al commento
Condividi su altri siti

Probabilmente ti ho confuso le idee parlandoti di buffer circolari. Ho dato uno sguardo veloce al software sul post: non vorrei salire in cattedra, ma mi sembra farraginoso…

Ritorneremo su questo aspetto più avanti; ora vorrei concentrarmi sulla ricezione da interrupt (se non siamo sicuri di questo aspetto è inutile andare avanti).

A costo di essere pesante (non scuso in anticipo) vorrei che tu facessi questa prova:

1) fai un main ridotto all’osso del tipo:

void main (void)

{

inizializzo I/O;

azzero variabili che mi interessano;

abilito interrurt seriale (solo Rx)

while (1);

}

2) Collega il PIC tramite seriale al PC

3) Esegui il run del programma

4) Tramite hyper terminal invia una stringa (es. 1234567890)

5) Stop del programma (ovviamente si ferma sul “while”)

6) Controlla che nel buffer di ricezione ci sia la stringa trasmessa.

Ci deve essere tutta la stringa !!!

Se tutto funziona prova (sempre con un main ridotto all’osso) a inviare da PIC una stringa al PC che contenga solo caratteri visualizzabili (i caratteri inferiori allo spazio, 0x20, sono interpretati male da hyper terminal) . CR e LF sono ammessi.

I problemi hardware sono stati risolti ?

Spero di risentirti con buone notizie.

Link al commento
Condividi su altri siti

Non ho letto tutto il codice, ma quello che fai nella routine di interrupt è sicuramente sbagliato.

Tu stai cercando di ricevere n caratteri con un ciclo dentro l'interrupt, è sbagliatissimo.

Quando avviene l'interrupt devi copiare il carattere ricevuto nel buffer di ricezione, incrementare il puntatore del buffer e BASTA... devi uscire dall'interrupt, il prossimo carattere che arriverà ti genererà un altro interrupt e così via...

La gestione del buffer deve essere fatta esternamente all'interrupt.

Link al commento
Condividi su altri siti

Eccomi.

X Kappa47

Immagiono che sia farraginoso, non ho mai provato ad implementarlo in C, ho solo provato. Tutto qui.

Allora, quello che mi hai detto sono i primi test che in genere faccio quando progetto le schede. Ad ogni modo ho fatto esattamene quello che mi dici tu e funziona egregiamente. Da PC a PIC e viceversa tutto funziona! Da PC a Modem e viceversa (perchè ho fatto anche questa prova) tutto funziona egregiamente.

Il problema è tra Modem e PIC.

X Nikiki

Ok, non avevo dubbi che fosse sbagliato un ciclo dentro l'interrupt, però non capisco, che limite metto all'incremento del buffer?

#define MAX ??

char BUFFER[MAX],

int cont=0;

void interrupt()

{

if(PIR1.RCIF) // <--- Siccome ho anche un interrupt esterno da gestire, posso

metterla questa condizione o non serve?

{

buffer[cont]=UART1_Read();

cont++;

} STOP???

E' così che intendi Nikiki?

Saluti

Edge

Link al commento
Condividi su altri siti

Crea un account o accedi per commentare

Devi essere un utente per poter lasciare un commento

Crea un account

Registrati per un nuovo account nella nostra comunità. è facile!

Registra un nuovo account

Accedi

Hai già un account? Accedi qui.

Accedi ora
×
×
  • Crea nuovo/a...