Come gestire la comunicazione RS485 con schede SYEL

Se la periferica è SYEL, si può utilizzare come protocollo seriale il SYEL BLOCK MODE 1.

Di seguito, un esempio sulla gestione della comunicazione RS485 in SYEL BLOCK MODE 1 in cui la centrale invia informazioni sulle operazioni che la periferica deve fare e la periferica risponde in base al pacchetto ricevuto.

In questo programma specifico la centrale comunica alla periferica:

  • quali uscite (1 - 10) attivare/disattivare
  • se attivare il pwm sull'uscita 1 (e quanto impostare la frequenza e duty cycle)
  • quanto potente (veloce) deve essere l'uscita chiamata pomp (gestita da uno slider grafico)
  • richiedere il valore degli ingressi In e Potenz

Nel common.h (o comunque in un file .h incluso nel common):

// Nel seguente esempio sono state omesse alcune definizioni e fuzioni (presenti nel progetto originale) che non erano
// necessarie al fine di spiegare la gestione RS485

#define PortaCOM COM2     // definisce PortaCOM (in questo caso è la COM2)
#define BAUD_RATE 115200  // deifnisce il baud rate della porta COM (in questo caso 115200)

// definizione comandi ralativi alla periferica del progetto
/***************************************
;Comando per settate singolo out
;0-0-0-3-1-3-'K'(H4B)-dato1=N°CH-dato2=freq.-dato3=pwm. Pwm =0-7 freq= 7sempre on, 6 512mSec, 5 256mSec, 4 128 mSec ecc
***************************************/
#define OUT_ON_OFF                  0x4B 

/***************************************
;Comando per settare velocità pompa
;0-0-0-3-1-2-'p'(H70)-valore%   
***************************************/
#define POMP_VEL                    0x70

/***************************************
;Comando per leggere digitale+analogico
;0-0-0-3-1-1-'i'  Risposta 1dato digitale+2dato analogico
***************************************/
#define READ_IN                     0x69


//---------------------funzioni----------------------
void init_serial_Rs485(void);     // funzione per inizializzare la seriale
void init_out(void);              // funzione per inizializzare lo stato iniziale delle uscite
void TaskCOM485(void);            // funzione principale per la comunicazione seriale 485
void tx485(void);                 // funzione per flag del tx della seriale
void subrx(void);                 // subroutine per la lettura dei messaggi ricevuti
void update_bufferTX_out(int);    // funzione che aggiorna il buffer con i dati da inviare (bufferTX) relativi alle uscite
void update_bufferTX_pomp(void);  // funzione che aggiorna il buffer con i dati da inviare relativi al settaggio dell' uscita pomp
void clear_bufferRX(void);        // funzione che svuota il buffer in cui vengono salvati i dati arrivati (bufferRX)

//--------------------costanti e variabili--------------------
const int num_centr = 128;         // numero della centrale (in questo caso 128)
const int num_perif = 1;           // numero della periferica (in questo caso 1)
const int dim_bufferTX = 129;      // dimensioni massime del buffer con i dati da inviare (bufferTX)
const int dim_bufferRX = 16;       // dimensioni massime del buffer in cui vengono salvati i dati ricevuti (bufferRX)
const int num_out = 10;            // numero totale di uscite digitali

volatile char bufferTX[dim_bufferTX];        // dichiara il buffer in cui prendere i dati da inviare
volatile char bufferRX[dim_bufferRX];        // dichiara il buffer in cui salvare i dati ricevuti

volatile int flag_tx485 = 0;                 // flag relativa al tx della seriale

volatile int flag_out_on_off[num_out];       // flag realtive allo stato delle uscite
volatile int flag_out_on_off_prec[num_out];  // flag previste realtive allo stato delle uscite
                                             // (utili per capire quando ha cambiato stato)

volatile int flag_pomp_on_off;               // flag realtive allo stato dell' uscita pomp
volatile int flag_pomp_on_off_prec;          // flag previste realtive allo stato dell' uscita pomp

volatile int flag_pwm_on_off;                // flag realtive all' attivazione/disattivazione del pwm sulla prima uscita
volatile int flag_pwm_on_off_prec;           // flag previste realtive all' attivazione/disattivazione del pwm sulla prima uscita

volatile short int TXX = 0;                  // variabile per il conteggio dei pacchetti inviati
volatile short int RXX = 0;                  // variabile per il conteggio dei pacchetti ricevuti

volatile int controllo_comando_uscite = 0;   // variabile per il controllo della corretta ricezione della periferica
                                             // 0 = fail, 1 = ok (uscite digitali)

volatile int controllo_comando_pomp = 0;     // variabile per il controllo della corrretta ricezione della periferica
                                             // 0 = fail, 1 = ok (uscita pomp)

volatile int controllo_lettura_ingressi = 0; // variabile per il controllo della corretta ricezione della periferica
                                             // 0 = fail, 1 = ok (ingressi In e Potenz)
volatile U8 In;                              // prima variabile per lettura ingressi
volatile U8 Potenz;                          // seconda variabile per lettura ingressi
volatile char freq = 0;                      // variabile relativa alla frequanza del pwm
volatile char pwm = 0;                       // variabile relativa al duty cycle del pwm
volatile int p;                              // variabile ralativa al valore % della velocità dell'uscita pomp

//-------------------------------------------
// funzione di inizializzazione seriale RS485
void init_serial_Rs485(void)
{
    com_open(PortaCOM, BAUD_RATE);         // apre la porta specificando il baud rate
    com_disable(PortaCOM);                 // disabilita la porta
    protocol_mode(PortaCOM, 1);            // imposta il protocollo della seriale su 1 (SYEL BLOCK MODE 1)
    PortaCOM->centr = num_centr;           // imposta il numero della centrale
    PortaCOM->perif = num_perif;           // imposta il numero della periferica
    onrx(PortaCOM, subrx);                 // funzione che richiama la subroutine subrx quando ci sono dei dati ricevuti
    com_enable(PortaCOM);                  // riabilita la porta
}

// subroutine che viene chiamata da onrx quando è arrivato un pacchetto dati
void subrx(void)
{
    int n = bload(PortaCOM, bufferRX, 8);  // la funzione bload salva il messaggio arrivato, sul bufferRX
    if (n != -1)                           // se bload restituisce un valore diverso da -1 ha letto correttemente il messaggio
    {
        if (bufferRX[0] == (char)OUT_ON_OFF)     // se la periferica ha risposto con il primo byte OUT_ON_OFF
            controllo_comando_uscita = 1;        // lo scambio dati ha avutp successo, la variabile di controllo viene messa a 1
                                                 
        else if (bufferRX[0] == (char)POMP_VEL)  // se la periferica ha risposto con il primo byte POMP_VEL
            controllo_comando_pomp = 1;          // lo scambio dati ha avuto successso, la variabile di controllo viene messa a 1
            
        else
        {
            controllo_lettura_ingressi = 1; // la lettura degli ingressi ha avuto successo, la variabile di controllo viene messa a 1
            In = bufferRX[0];               // il valore della variabile In diventa il valore del primo byte del pacchetto
            Potenz = bufferRX[1];           // il valore della variabile Potenz diventa il valore del secondo byte del pacchetto
        }
        RXX++;                              // incremento del contatore dei pacchetti ricevuti
    }
    clear_bufferRX();                       // svuotamento del bufferRX
}

// funzione che inizializza lo stato iniziale delle uscite all' avvio
void init_out(void)
{
    // inizializzazione uscite digitali
    for (int i = 0; i < num_out; i++)
    {
        flag_out_on_off[i] = 0;        // le flag delle uscite vengono messe tutte a 0 perchè all'avvio sono tutte spente
        flag_out_on_off_prec[i] = 0;
        update_bufferTX_out(i);        // agiorna il bufferTX per ogni uscita
        bsave(PortaCOM, bufferTX, 4);  // il comando bsave invia i dati contenuti nel bufferTX (comunicando che ogni uscita è spenta)
        wait(10);                      // aspetta 10 ms per dar tempo di eseguire lo scambio dati
        TXX++;                         // incremento del contatore dei pacchetti inviati
    }
    flag_pwm_on_off = 0;               // la flag del pwm viene messsa a 0 perchè all' avvio il pwm è disattivo
    flag_pwm_on_off_prec = 0;
    
    // inizializzazione dell'uscita pomp
    flag_pomp_on_off = 0;                 // la flag dell' uscita pomp è 0 perchè all' avvio è spenta
    flag_pomp_on_off_prec = 0;
    update_bufferTX_pomp();               // aggiorna il bufferTX con i dati relativi all' uscita pomp (comunicando che è spenta)
    bsave(PortaCOM, bufferTX, 2);         // invia i dati del bufferTX
    wait(10);
    TXX++;
}

// funzione principale per la gestione della comunicazione RS485 che verrà richiamata nello start.c
void TaskCOM485(void)
{
    while(1)
    {
        if (flag_tx485)
        {
            flag_tx485 = 0;                       // ogni 100 ms (vedi start.c)
            
            // controlllo uscite digitali
            for (int i = 0; i < num_out; i++)
                if (flag_out_on_off[i] != flag_out_on_off_prec[i])  // se un' uscita ha cambiato stato
                {
                    flag_out_on_off_prec[i] = flag_out_on_off[i];
                    update_bufferTX_out(i);                         // aggiorna il bufferTX
                    TXX++;
                    bsave(PortaCOM, bufferTX, 4);                   // invia il bufferTX
                                                                    // (comunicando attivazione/disattivazione dell'uscita i)
                    wait(10);
                }
            
            // controllo uscita pomp
            if (flag_pomp_on_off != flag_pomp_on_off_prec)    // se ha cambiato stato
            {
                flag_pomp_on_off_prec = flag_pomp_on_off;
                update_bufferTX_pomp();                       // aggiorna il bufferTX con i dati sul valore di pomp
                TXX++;
                bsave(PortaCOM, bufferTX, 2);                 // invia il bufferTX
                wait(10);
            }
            if (flag_pomp_on_off)                             // se comunque pomp è accesa
            {
                update_bufferTX_pomp();                       // aggiorna il bufferTX con il valore di pomp
                TXX++;
                bsave(PortaCOM, bufferTX, 2);                 // invia il bufferTX
                wait(10);
            }

            // controllo pwm sull' uscita 1
            if (flag_pwm_on_off != flag_pwm_on_off_prec)     // se ha cambiato stato
            {
                flag_pwm_on_off_prec = flag_pwm_on_off;
                update_bufferTX_out(0);                      // aggiorna il bufferTX con l' uscita 1 (0)
                TXX++;
                bsave(PortaCOM, bufferTX, 4);                // invia il bufferTX
                wait(10);
            }

            // lettura ingressi
            bufferTX[0] = READ_IN;         // il primo byte del bufferTX viene impostato col comando READ_IN per comunicare che la
                                           // centrale vuole ricevere come risposta il valore di In e Potenz
            TXX++;
            bsave(PortaCOM, bufferTX, 1);  // invia il bufferTX
            wait(10);
        }
    }
}

// ogni 100 ms imposta la flag del tx della seriale a 1 (vedi start.c)
void tx485(void)
{
    flag_tx485 = 1;
}

// funzione che aggiorna il bufferTX con i dati relativi allo stato delle uscite
// il parametro in ingresso è il numero dell'uscita che ha cambiato stato
void update_bufferTX_out(int x)
{
    bufferTX[0] = OUT_ON_OFF;         // imposta il primo byte del bufferTX con il comando OUT_ON_OFF
    bufferTX[1] = x;                  // imposta il secondo byte del bufferTX con il numero dell'uscita
    if (flag_out_on_off[x] == 0)      // se l' uscita ha cambiato stato spengendosi
    {                                 // il terzo e quarto byte del bufferTX vengono impostati a 0 (off)
        bufferTX[2] = 0;
        bufferTX[3] = 0;
    }
    else                                  // se ha cambiato stato accendendosi
    { 
        if (x == 0 && flag_pwm_on_off)    // se l' uscita è la 1 (x == 0) e se il pwm è attivo
        {                                 // il terzo e quarto byte del bufferTX vengono impostati con freq e pwm
            bufferTX[2] = freq;           // freq e pwm possono essere modificati tramite dei pulsanti grafici su una pagina
            bufferTX[3] = pwm;
        }
        else                              // se l' uscita non è la 1 o se il pwm è disattivo
        {                                 // il terzo e quarto byte del bufferTX vengono impostati a 7 (on)
            bufferTX[2] = 7;
            bufferTX[3] = 7;
        }
    }
}

// funzione che aggiorna il bufferTX con il dati per settare l' uscita pomp;
void update_bufferTX_pomp(void)
{
    bufferTX[0] = POMP_VEL;    // imposta il primo byte del bufferTX con il comando POMP_VEL
    if (flag_pomp_on_off)      // se la pomp cambia stato accendendosi
        bufferTX[1] = p;       // imposta il secondo byte del bufferTX con il valore della variabile relativa alla velocità di pomp
                               // p dipende dal vaolore dello slider sl1 che si trova su una pagina grafica del progetto
                               // p = sl1->val;
    else                       // se la pomp cambia stato spengendosi
        bufferTX[1] = 0;       // imposta il secondo byte del bufferTX con il valore 0 (off)
                               // p dipende dal vaolore dello slider sl1 che si trova su una pagina grafica del progetto
                               // p = sl1->val;
}

// funzione che svuota il bufferRX
void clear_bufferRX(void)
{
    for (int i = 0; i < dim_bufferRX; i++)    // tutti i valori del bufferRX vengono messi a 0
        bufferRX[i] = 0;
}

Nello start.c:

init_serial_RS485();          // inizializza la seriale RS485
init_out();                   // inizializza le uscite

exec_timer(tx485, 100, 0);    // esegue ogni 100 ms tx485
exec_task(TaskCOM485, 0, 4);  // esegue la TaskCOM485