Jump to content
PLC Forum


Sign in to follow this  
Grandegiove

Comunicazione Fra Pic - Master Modbus o alternativa

Recommended Posts

Grandegiove

Buongiorno,

devo realizzare un progetto in cui devo far comunicare due PIC remoti. . Devo quindi scegliere il livello fisico e il protocollo da utillzzare.

Fino ad ora ho sempre realizzato mediante PIC slave Modbus RTU su RS485 (il master era un PLC).

Ora però dovrei scrivere il firmware per implemenatere il Master Modbus RTU, assai più arduo che realizzare uno slave.

Qualcuno ha esperienza in merito e potrebbe dirmi come muovermi? (devo implementare logiche di ritrasmissione , di rilevazione d'errore e non sò come muovermi).

Consigliate un'alternativa all'utilizzo di ModBus su RS485?

Una prerogativa del sistema è che deve essere molto immune ai disturbi (Rs485 era una buona soluzione grazie alla sua robustezza)

Grazie mille.

Share this post


Link to post
Share on other sites

kappa47

Se hai già realizzato versioni slave, non vedo grossi problemi a sviluppare il protocollo lato master.

Merita più attenzione lo sviluppo dell’applicazione, ma in questo sviluppo sei tu che sai cosa devi fare (sistema operativo, scalabilità, ecc.)

Un consiglio che vale sempre e per tutte le applicazioni: dividi il problema.

Inizia con implementare il “main” con la struttura simile a quella finale; aggiungi solo uno o due comandi ModBus e verifichi che il tutto funzioni. Poi passi al controllo errori, alla eventuale ritrasmissione e alla verifica delle variazioni delle variabili (set point, temperature, ecc.).

La 485 è robusta, se fatta bene (terminazioni). Una valida alternativa è CAN, ma questo sistema ti costringe a modificare tutto l’approccio.

Saluti.

Share this post


Link to post
Share on other sites
Grandegiove

Grazie per la risposta,

ho provato a implemetare una semplice comunicazione fra due PIC 18F4620 mediante USART. Premesso che il sistema non funziona il princiape dubbio riguarda l'implementazione del MASTER: devo indicare in qualche modo l'inizio della trasmissione del pacchetto (per esempio un pacchetto MODBUS)?

Sono di corsa, per ora allego il codice del Master, così magari qualcuno può già dirmi cosa non va. Grazie!

(l'idea è implementare Modbus ma per ora non riconosce nessuno dei dati inviati, quindi devo prima superare questo scoglio).

MASTER

'TRASMISSIONE RS485 ------------------------------------------------------------

sub procedure InvioRs485(dim count as byte)

	dim i as byte

	DIR485=1
	for i=0 to (count-1)
                USART_Write(bufTx485[i])
	next i
	while (TXSTA.TRMT=0)           'attend
	wend
	delay_us(40)
	DIR485=0

end sub

'MAIN---------------------------------------------------------------------------

main:
USART_Init(9600)

RCSTA=%10010000
TRISC=%11000000     'RC3 output (RC6 e RC7) input (USART)

while(1)
        'Invio di un byte
        'Preparazione buffer da inviare

        bufTx485[0]=1 
        bufTx485[1]=2
        bufTx485[2]=3
        bufTx485[3]=4 
        bufTx485[4]=5
        bufTx485[5]=6
        bufTx485[6]=7
        bufTx485[7]=8

InvioRs485(8)

delay_ms(1000)

wend

end.

Share this post


Link to post
Share on other sites
kappa47

Se stato un po' troppo precipitoso.

Cerca in internet info sul protocollo ModBus: e' essenziale.

Prendiamo come esempio questo comando: preset multiple reg. (codice 0x10): crittura di word

Richiesta da master a slave:

bufTx485[0] = 0x30; // indirizzo dello slave

bufTx485[1] = 0x10; // codice di questo comando

bufTx485[2] = 0x1000; // indirizzo della prima variabile che si vuole modificare

bufTx485[4] = 0x0002; // numero di word da modificare (1 word = 16 BIT): in questo caso 2

bufTx485[6] = 0x04; // numero di byte (e' sempre il doppio del numero di word)

bufTx485[7] = 0x1234; // valore della prima word

bufTx485[9] = 0x5678; // valore della seconda word

bufTx485[11]= CRC16; // 2 byte

Risposta da slave a master:

bufRx485[0] = 0x30; // indirizzo dello slave

bufRx485[1] = 0x10; // codice del comando

bufRx485[2] = 0x1000; // indirizzo della prima variabile modificata

bufRx485[4] = 0x0002; // numero di word modificate

bufRx485[6] = CRC16;

Tieni presente che:

- il calcolo del CRC16 e' un polinomio (da calcolare in basic e' un po' ostico);in questa fase tienilo sempre ad un valore fisso

(tu semplicemente, non lo calcoli e in ricezioni controlli che sia uguale al valore che trasmetti)

- l'esempio fatto non prevede errori, quindi si suppone che lo slave abbia ricevuto e processato in modo corretto il comando

(per gli errori ti rimando ad un prossimo post, se ne hai bisogno)

- le word sono trasmesse come "big endian" (prima high e poi low)

- ho visto applicazioni in cui lo slave non ritrasmette tutte le word che il master a chiesto di modificare, ma solo quelle che ha effettivamente modificato (sempre un numero minore).

Tu, facendo sia il master che lo slave, puoi "giocartela" come vuoi.

- il master considera un comando NON andato a buon fine, se la risposta (corretta o meno) non arriva entro il tempo di trasmissione di 3,5 caratteri (a 9600 baud sono circa 3,5 mS.)

A questo punto e' libero di iniziare una nuova trasmissione (ad un'altro slave, per esempio)

Io modificherei cosi la procedura "InvioRs485":

for i=0 to (count-1)

USART_Write(bufTx485)

while (TXSTA.TRMT=0)

wend

next i

Prima di trasmettere il carattere successivo, devi aspettare la fine trasmissione del carattere precedente.

Non sono sicuro, ma credo che quando TRMT va a zero, l'UART ha liberato il suo registro, per cui tu puoi scrivere il nuovo carattere, ma il carattere che aveva "in pancia" non e' ancora stato serializzato completamente.

Io porterei il ritardo da 40 a 200 nella "delay_us" o un controllo con oscilloscopio, prima di cambiare la direzione da Tx a Rx

Ciao.

Share this post


Link to post
Share on other sites
Grandegiove

Ciao, grazie mille per la risposta.

Il fatto è che non riesco a passare alla parte di implementazione di protocollo Modbus in quanto non riesco a far comunicare in modo corretto i due PIC.

Ho ridimensionato i due programmi concentrandomi solo sulla trasmissione di un byte ma il comportamento che ottengo è molto strano!

Seguono i due semplici codici:

MASTER:

 program Master_Modbus

symbol DIR485 =PORTC.5
dim buf485     as byte[5]

'TRASMISSIONE RS485 ------------------------------------------------------------

sub procedure InvioRs485(dim count as byte)

	dim i as byte

	DIR485=1
	for i=0 to (count-1)
    USART_Write(buf485[i])
	next i
	while (TXSTA.TRMT=0)           'attend
	wend
	delay_us(200)
	DIR485=0

end sub

'MAIN---------------------------------------------------------------------------

main:
USART_Init(9600)

RCSTA=%10010000
TRISC=%11000000     'RC3 output (RC6 e RC7) input (USART)

     while(1)
     buf485[0]=62
     InvioRs485(1)
     delay_ms(200)
     wend

end
SLAVE
 program Master_Modbus

symbol DIR485 =PORTC.5
dim buf485     as byte[5]

'TRASMISSIONE RS485 ------------------------------------------------------------

sub procedure InvioRs485(dim count as byte)

	dim i as byte

	DIR485=1
	for i=0 to (count-1)
    USART_Write(buf485[i])
	next i
	while (TXSTA.TRMT=0)           'attend
	wend
	delay_us(200)
	DIR485=0

end sub

'MAIN---------------------------------------------------------------------------

main:
USART_Init(9600)

RCSTA=%10010000
TRISC=%11000000     'RC3 output (RC6 e RC7) input (USART)

     while(1)
     buf485[0]=62
     InvioRs485(1)
     delay_ms(200)
     wend

end

Il Master trasmette cicclicamente il dato. Lo slave riceve è stamapa su LCD.

Solo che dato trasmesso e ricevuto non coincidono!

La comunicazione credo vada a buon fine in quanto se trasmetto x ricevfe sempre y.

Ho trovato queste strane corrispondenze:

Trasmetto 55 ricevo 100 (LCD stampa d)

57 ricevo 99 (LCD stampa c)

59 ricevo 98 (LCD stampa b )

61 ricevo 97 (LCD stampa a)

63 ricevo 96 (LCD stampa ')

Trasmetto 50 ricevo 51 (LCD stampa 3)

54 ricevo 50 (LCD stampa 2)

58 ricevo 49 (LCD stampa 1)

62 ricevo 48 (LCD stampa 0)

Credo che l'errore sia quindi nell'interpretazione del dato.

Ho provato a ragionare sui binari in termini di "bit di parità" ma non ne ho ricavato nulla.

Il fatto poi che aumentando il valore trasmesso cali quello ricevuto mi fa pensare a qualche problema in termini di big/little endian.

TEmo di essere un po' annebbiato..

Se qualcuno riesce a risolvermi questo enigma mi toglierebbe dalla brace,

grazie mille

Trasmetto

Share this post


Link to post
Share on other sites
kappa47

Quello che visualizzi su LCD corrisponde esattamente a quello che gli passi da visualizzare, infatti:

100 (base decimale) = d (ASCII)

99 = c

98 = b

51 = 3

e cosi via…

Tutto quello che hai postato corrisponde, quindi l’errore e’ da cercare nella trasmissione o ricezione.

Quello che hai postato come codice SLAVE e’ uguale a quello del MASTER: errore o svista ?

Come detto al post precedente, correggi la procedura “InvioRs485”.

Dividi il problema: una volta messo a posto la Tx master, usa Hyper Terminal per testare la trasmissione del PIC e poi passa alla ricezione.

Ti aggiungo, come esempio, del codice scritto in C (non mi piace il basic per i micro di qualunque tipo, ma questo e’ un mio problema):

typedef	unsigned char	u_char;
typedef	unsigned int	u_int;

// Trasmissione in polling di un carattere
void tx_one_car (u_char car)
  {
  // aspetto Trasmit Shift Register vuoto
  while (TXSTA1bits.TRMT == 0);
  TXREG1 = car;
  }

// inizializzazione UART
// quanzo da 7,3 MHz: da tenerne conto nel valore per SPBRG1 !!!
void init_uart (void)
  {
  u_char	dummy;

  IPR1bits.RC1IP = 1;          // priority: high (se uso interrupt in ricezione)
  // sempre 1 STOP bit
  SPBRG1 = 46;                  //  9600 baud [at] 7,3 MHz
  RCSTA1 = 0x80;               // serial port enabled (SPEN=1)
  PIE1bits.TX1IE = 0;
  PIR1bits.RC1IF = 0;          // reset interrupt pending (flag)
  PIE1bits.RC1IE = 0;          // Rx Interrupt disabled
  //PIE1bits.RC1IE = 1;        // Rx Interrupt enabled
                                         // abilitare nel main o quando serve

  TXSTA1 = 0x04;               // TX disabled, 8-bit, Async, BRGH=1
  TXSTA1bits.TXEN = 1;       // Enable Tx UART 1
                                         // abilitare nel main o quando serve
  dummy  = RCREG1;          // pulisco reg. Rx (precauzione)
  }

Ciao.

Share this post


Link to post
Share on other sites
Grandegiove

Ciao!

Sono riuscito a far trasmettere i due PIC;

Il problema era hardware: ho realizzato l'interfaccia RS485 mediante un SN75176:

lo schema che ho utilizzato è quello che ho sempre utilizzato per realizzare periferiche Slave per PLC.

Questa struttura circuitale prevede che i segnali in uscita da Rx e Tx della USART vengano negati prima di entrare nell' SN75176. Non mi sono mai chiesto il perchè in quanto è sempre funzionato tutto.

Analizzando i segnali della trasmissione quando avevo lo strano comportamento ho notato che di fatto avevo un problema di negazione del segnale. Ho bypassato la porta NOT in ricezione dello Slave e ora ricevo in modo esatto.

Solo che ora ho le idee un po' confuse...

?_?

Share this post


Link to post
Share on other sites
GPmai

Buongiornoooo

Io solitamente utilizzo ADM485 senza alcun problema... diretto da RX e TX del micro...

Al limite prova a cambiare componente...

Share this post


Link to post
Share on other sites
Grandegiove

Buongiorno!

Problemi HW risolti! C'era una connessione errata. Idee snebbiate... Ora avanti con l'implemetazione di un semplice protocollo per la comunicazione. Per ora grazie a tutti! Naturalmente a lavoro finito lo condividerò con voi.. Ma prima di allora avrò sicuramente qualche altra domanda! Buona giornata a tutti!

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...