Gestione delle priorità e precedenze

Buongiorno a tutti sono nuovo nel quartiere e spero di aver rispettato le formalità e non aver sbagliato il posto .
è da giorni che sto lavorando su un progetto per convertire un plc ( meglio scheda relè intelligente ) in una scheda che venga gestita da un Atmega , ho fatto lo schema su esayeda ho creato il pcb , da quel progetto ho fatto un prototipo su breadboard .
e ho verificato il funzionamento di tutti i componenti , ho fatto un programmino stupido e tutte le periferiche funzionano ora è il momento della scrittura programma ... sono in altissimo mare !!!
il programma è davvero semplice 2 ingressi digitali "erog1" "erog2" che attivano 2 relè , 2 ingressi digitali ( sensore 1 e sensore 2 ) che attivano un caricamento( 3 elettrovalvole) , poi ho una sirena , 3 relè e un allarme .
ho letto decine di pagine nel forum per capire l' istruzione da usare ( while , for o if ) ma non riesco a capire un dettaglio.

verifico lo stato di sensore 1
se vuoto parte la sequenza di carico
attiva carico 1 e mescolatore
aspetta 1000
disattiva carico 1 mescolatore
aspetta 1000

Se parte erogazione erog1 in un qualsiasi momento devo fermare la sequenza di carico ed eseguire la sequenza erogazione .
come faccio ? devo verificare lo stato di erog 1 prima di ogni delay ?
oltretutto dovrei avere una copia identica di questo programma ma che controlla
carico 2 ed erogazione 2 come possono coesistere in un programma che viene eseguito riga dopo riga ??

Grazie mille in anticipo a chi avrà la pazienza di leggere sto papiro .

( premetto che simultaneamente sto provando a gestire la programmazione su "openplc" per programmare in ladder che uso gia per il plc ma ci tengo a risolvere il problema anche in c++ )



Il programma viene certamente eseguito riga per riga. E questo non si può fisicamente cambiare. Quello che si può cambiare invece è che la logica non sia eseguita riga per riga, ma in più passaggi (senza delay o for/while che bloccano li l'esecuzione). In questo modo porti anche in C il concetto di ciclo di scansione, e ad ogni ciclo puoi valutare tutte le condizioni necessarie anche per processi indipendenti. Il controllo del flusso in questo caso si fa con variabili flag/merker da impostare opportunamente.

( premetto che simultaneamente sto provando a gestire la programmazione su "openplc" per programmare in ladder

...dove appunto ogni rung viene valutato ad ogni ciclo, e rung diversi si possono occupare di processi indipendenti.

Anche in C si può ragionare allo stesso modo (ogni rung un'espressione logica o un if). Certo è il livello più basso in cui si può scrivere un programma, anche meno leggibile se vogliamo (perché si tratta di programmazione funzionale e non algoritmica/imperativa), ma in casi come questo semplifica tantissimo.

Se parte erogazione erog1 in un qualsiasi momento devo fermare la sequenza di carico ed eseguire la sequenza erogazione .
come faccio ? devo verificare lo stato di erog 1 prima di ogni delay ?

Naturalmente no, perché se il delay è già iniziato si resta bloccati li.
La via maestra per ogni progetto di automazione è sempre modellare (proprio su carta) il comportamento del sistema sotto forma di stati espliciti in cui si può trovare, di eventi che possono accadere durante quegli stati, e di azioni in loro risposta.

Qui discussioni sulla "contemporaneità" e sull'implementazione pratica:
https://forum.arduino.cc/t/un-tasto-due-led-indipendenti/1225770
https://forum.arduino.cc/t/gestire-2-led-con-millis-ed-interruttore/1242236

Una bella cosa da realizzare con un motore di macchina a stati come avevamo discusso a febbraio

Concordo con Salvorhardin.

PS: Ma aggiungo anche che vedo dei trasformatori che non mi ispirano molta fiducia circa le tensioni che questo aggeggio dovrebbe gestire...

Dallo schema direi solo 24Vca, 24Vcc, 5Vcc. Il trasformatore è solo l'alimentazione a monte di tutto.

Piuttosto ci sono altri piccoli dettagli che mi perplimono:

  • Mancano le resistenze di pull-down sugli ingressi comandati dai relé erog1 erog2
  • Manca un condensatore tra Vcc-GND del micro
  • Manca un collegamento GND del micro
  • Manca il collegamento a AVCC del micro
  • I transistor 2n3904 basteranno per le elettrovalvole?
  • Ci saranno i diodi di protezione in antiparallelo sulle valvole?
  • È previsto il connettore ICSP per la programmazione del micro? Altrimenti come si programma?

Ciao claudio mi hai gia dato un bel po di spunti , grazie mille .
Sono contento che un esempio reale abbia suscitato tanto interesse !

Grazie per aver spiegato il circuito e per i suggerimenti , pe elettrovalvole sono da 3 w ( adesso aggiungo i diodi ) e sono pilotati da dei 2n222 e sembrano tenere con una resistenza da 1/2 w , per i collegamenti del micro ( microcontrollore pensavo di averli fatti tutti )
Adesso ricontrollo i collegamenti micro .

Sono una persona un po approssimativa quindi spero mi perdoniate le inmprecisioni ,
Apprezzo e faccio tesoro di critiche e consigli !!!

Per la programmazione pensavo di usare un usb to TTL ( non previsto nello schema )
Non ero a conoscenza del ICSP ora vado a studiarlo

Dove posso approfondire sul ciclo di scansione , ce qualcosa nel forum ?
Volevo provare a fare una mega scaletta di if ma ho dubbi sulla riuscita .

Ho provato anche a creare uno schema a blocchi ma credo sia impossibile viste le caratteristiche dello schema che stoppa e rilancia sequenze

Grazie mille ancora Claudio_FF

Tutto si spiega con la semplice considerazione che se anche il programma prosegue e le righe scorrono, i pin accesi rimangono accesi, quelli spenti rimangono spenti

Da li discende tutto

Vai a vedere una macchina a stati

Posso solo consigliarti di fare una ricerca sul forum con:

  1. macchina a stati maurotec
  2. macchina a stati Claudio_FF

Ciao.

1 Like

I 2n2222 vanno bene, sono circa 130 mA.
I 2n3904 potrebbero anche andare ma per me sarebbero un po' troppo tirati.

e sembrano tenere con una resistenza da 1/2 w

che resistenza???

spero mi perdoniate le inmprecisioni

Quello che facciamo noi è abbastanza irrilevante, sono i circuiti e il software a non perdonare NIENTE e a richiedere precisione assoluta.

Per la programmazione pensavo di usare un usb to TTL ( non previsto nello schema ) Non ero a conoscenza del ICSP ora vado a studiarlo

La programmazione seriale (via IDE Arduino e convertitore USB) va bene solo per un micro a cui sia già stato precaricato il bootloader (come viene fatto ai micro montati sulle schede Arduino già pronte).

Con un micro nuovo di fabbrica non funziona, bisogna prima caricare il bootloader attraverso il bus SPI. Sulle schede già pronte è previsto per questo scopo il connettore ICSP a 6 pin, a cui collegare un apposito programmatore esterno (che può essere anche un altro Arduino programmato per funzionare da programmatore).

Piuttosto, visto che dallo schema intendi usare un ATMEGA328-PU, non so se è utilizzabile, credo che per poterlo programmare via Arduino IDE sia obbligatoria la versione 328P. In pratica credo si possano usare solo quelli che su LCSC si ottengono inserendo nel campo ricerca esattamente ATMEGA328P-

Dove posso approfondire sul ciclo di scansione , ce qualcosa nel forum ?

Non è un argomento Arduino, ma PLC. Facendo una ricerca "PLC ciclo di scansione" dovrebbe saltare fuori qualcosa. In C noi possiamo simulare il ciclo di scansione dei PLC semplicemente facendo ripetere continuamente alla funzione loop le tre seguenti cose in sequenza:

  • lettura ingressi
  • elaborazione (non bloccante)
  • aggiornamento uscite

Volevo provare a fare una mega scaletta di if ma ho dubbi sulla riuscita .
Ho provato anche a creare uno schema a blocchi ma credo sia impossibile viste le caratteristiche dello schema che stoppa e rilancia sequenze

L'importante è che per te quello che deve accadere nelle varie situazioni sia assolutamente chiaro, almeno a livello discorsivo. Poi si tratta di schematizzarlo in modo preciso e non ambiguo. E una volta che lo schema funziona su carta si può pensare alla trasformazione in codice. Partire subito dal codice sperando di ottenere il risultato desiderato attraverso qualche istruzione automagica porta solo ad aggrovigliarsi.

Se sei in grado di scriverlo totalmente in ladder, è già un ottimo punto di partenza, perché vuol dire che la logica da eseguire è chiara, e anche perché il ladder (LD, ma anche uno schema FBD) si può convertire facilmente in espressioni C (discorso un po' a parte per i temporizzatori, che richiedono l'equivalende di function o function block parlando in gergo openPLC).

Ma pensarlo a stati già dall'inizio (l'equivalente dell' SFC) semplifica molto, perché è ancora più vicino alla descrizione discorsiva. Parti disegnando il primo cerchio: stato riposo iniziale del sistema.
Da li cosa può accadere? Quali segnali sono rilevanti?
Cosa deve essere fatto se vengono rilevati?
A quale altra situazione/stato devono portare?
E descrivi/costruisci così il funzionamento dettagliato dell'intero sistema. Una sequenza da stoppare diventa semplicemente il salto ad un altro stato di funzionamento. Ogni pausa sarà anch'essa uno stato in cui rimanere per un certo tempo (e quindi un certo numero di cicli di scansione/loop).

Tutto questo è da fare per ogni singolo processo indipendente che dovrà funzionare sul sistema. Se saranno da gestire due sistemi identici, basterà duplicare la logica e cambiare le variabili di lavoro e quelle di ingresso/uscita.

1 Like

Realizzarla come macchina a stati finiti, magari utilizzando la AFSD Rhodense seconda di qualche mese fa è di una semplicità disarmante:

serve definire ingressi e uscite

// dichiarazione dei pin usati dalla macchina a stati

// gli ingressi
#define SVuoto1 2
#define SPieno1 3
#define SButton1 4
#define SVuoto2 7
#define SPieno2 8
#define SButton2 9

byte ingresso[]{SVuoto1, SPieno1, SButton1, SVuoto2, SPieno2, SButton2};


// le uscite
#define UEroga1 5
#define UPompa1 6
#define UEroga2 10
#define UPompa2 11
// led lampeggiante (servisse)
byte uscita[] = {UEroga1, UEroga2, UPompa1, UPompa2, LED_BUILTIN};

poi stabilire le varie azioni che la macchina deve fare


// la azioni
void OffPompa1()
{
   // spegne la pompa 1
   digitalWrite(UPompa1, LOW);
}
void OffPompa2()
{
   // spegne la pompa 2
   digitalWrite(UPompa2, LOW);
}

void OnPompa1()
{
   // accende la pompa 1
   digitalWrite(UPompa1, HIGH);
}

void OnPompa2()
{
   // accende la pompa 2
   digitalWrite(UPompa2, HIGH);
}



void Eroga1()
{
   // eroga 1
   digitalWrite(UEroga1, HIGH);
   // e spegne la sua pompa
   OffPompa1();
}


void Eroga2()
{
   // eroga 2
   digitalWrite(UEroga2, HIGH);
   // e spegne la sua pompa
   OffPompa2();
}

void NonEroga1()
{
   // non eroga 1
   digitalWrite(UEroga1, LOW);
}
void NonEroga2()
{
   // non eroga 2
   digitalWrite(UEroga2, LOW);
}

le condizioni che provocano i passaggi di stato

// le condizioni
bool Vuoto1()
{
   // sensore serbatoio vuoto 1
   return digitalRead(SVuoto1);
}

bool Vuoto2()
{
   // sensore serbatoio vuoto 2
   return digitalRead(SVuoto2);
}

bool Pieno1()
{
   // sensore serbatoio pieno 1
   return digitalRead(SPieno1);
}

bool Pieno2()
{
   // sensore serbatoio pieno 2
   return digitalRead(SPieno2);
}



bool Bottone1()
{
   // Bottone erogazione 1
   return digitalRead(SButton1);
}


bool Bottone2()
{
   // Bottone erogazione 1
   return digitalRead(SButton2);
}

infine descrivere gli stati in funzione di condizioni, tempi ed azioni

//gli stati

stato_t stato1[] =
{
   {OffPompa1},        // stato 0 spegne pompa
   {OnPompa1},         // stato 1 accende pompa
   {Eroga1, NonEroga1}, // stato 2, eroga e spegne pompa
};
stato_t stato2[] =
{
   {OffPompa2},        // stato 0 spegne pompa
   {OnPompa2},         // stato 1 accende pompa
   {Eroga1, NonEroga2}, // stato 2, eroga e spegne pompa
};

a questo punto si definiscono le due macchine (due differenti set di pompa erogatori e similia)


regola_t regola1[] =
{
   {0, 1, Vuoto1, 0}, // stato 0, attesa, reagisce al sensore di vuoto
   {0, 2, Bottone1},    // stato 0, attesa, reagisce al pulsante
   {1, 0, Pieno1},    // stato 1, sta riempiendo, reagisce al pieno
   {1, 2, Bottone1},    // stato 1, sta riempiendo, reagisce al pulsante
   {2, 0, 0, 3000}    // stato 2, eroga, attende fine erogazione
};

regola_t regola2[] =
{
   {0, 1, Vuoto2, 0}, // stato 0, attesa, reagisce al sensore di vuoto
   {0, 2, Bottone2},    // stato 0, attesa, reagisce al pulsante
   {1, 0, Pieno2},    // stato 1, sta riempiendo, reagisce al pieno
   {1, 2, Bottone2},    // stato 1, sta riempiendo, reagisce al pulsante
   {2, 0, 0, 3000}    // stato 2, eroga, attende fine erogazione
};




fsm_t macchinaA = {stato1, sizeof stato1 / sizeof stato1[0], regola1, sizeof regola1 / sizeof regola1[0]}; // * stato, numero stati, * regole, numero regole, stato iniziale
fsm_t macchinaB = {stato2, sizeof stato2 / sizeof stato2[0], regola2, sizeof regola2 / sizeof regola2[0]}; // * stato, numero stati, * regole, numero regole, stato iniziale

e nella loop rimangono da scrivere ben 4 righe

   fsm(macchinaA);
   // richiama il motore della FSM, sulla macchina A
   fsm(macchinaB);
   // sulla macchina B

fatto e finito, un pomeriggio di pioggia, anzi, il tempo di una digestione

PS: forse l'ordine di spiegazione non corrisponde con l'ordine di compilazione, verificate di dichiarare le variabili prima di usarle

e con questo chiudo

3 Likes

Mi pungola il fratello:

Il programma è lungo
Ma è composto di tante righe semplici e di tante funzioni corte

Ogni singolo elemento è semplice ed auto esplicativo
Con una attenta scelta (e io qui un po' cado) dei nomi è pure facile da scrivere e da comprendere

Una maniera alternativa sarebbe passare per delle strutture che contengono tutto al loro interno e che permettono anche l'esecuzione del codice della fsm, insomma una classe di C++

Ma non mettiamo troppa carne al fuoco

1 Like

grazie mille salvor gentilissimo , mi risulta molto complicato capire le istruzioni che usi perchè sono un novizio ma adesso vedo di approfondire bene tutte le risposte che gentilmente mi avete dato


stavo leggendo i link che mi avete girato e prima per fare mente locale ho fatto lo schema a blocchi scusate se non rispetto le forme ufficiali ma canva è un po limitato .

Ok, quello che hai disegnato è un diagramma di flusso, che mostra cosa deve fare il processo (salvo dettagli sottintesi che nel disegno non compaiono, e noi non possiamo sapere se sono sottintesi o semplicemente non considerati).

Il diagramma potrebbe essere codificato con i criteri della programmazione strutturata tramite if e cicli. Tuttavia non potrebbero "vivere" contemporaneamente due processi codificati in questo modo, per quello che si diceva nei primi post.

Tecnicamente è necessario trasformare il processo descritto dal diagramma di flusso, in un equivalente diagramma di stato che svolga le stesse identiche funzioni, nella stessa identica sequenza, con le stesse identiche temporizzazioni.

In pratica significa partire da: accendo, sono nello stato di riposo (un bel cerchio che indica lo stato di riposo), quali segnali/eventi/condizioni devo attendere? Cosa devo fare se accadono? In quale altro stato di funzionamento mi devo portare? (un altro bel cerchio con una freccia in arrivo dal primo).
In questo stato, di nuovo: quali segnali/eventi/condizioni devo attendere? Cosa devo fare se accadono? In quale altro stato di funzionamento mi devo portare?
Ogni stato è una situazione di funzionamento in cui il sistema può permanere per un certo tempo (anche una pausa è uno stato).

La codifica di un processo descritto tramite stati, permette in modo naturale e automatico la presenza di più processi contemporanei indipendenti.

Ora dal diagramma di flusso io non capisco bene i diversi stati possibili.

3 Likes

Gran lavorone

E mi copio la macchina rhodense
La porto a Varese

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.