se interruttore alto attiva un uscita altrimenti me ne attivi un'altra

Progetti Arduino
Avatar utente
pgv
Messaggi: 484
Iscritto il: gio 17 set 2020, 13:16
Località: Ginevra

Re: se interruttore alto attiva un uscita altrimenti me ne attivi un'altra

Messaggio da pgv »

Nota Bene: il "pivello" proviene dalla citazione del film e NON fa riferimento a chicchessia sul forum (e tra l'altro e' una povera traduzione del "punk" originale). Mai mi permetterei di dare del pivello a qualcuno che non siauno dei miei colleghi colpevole di una bischerata galattica...
RoccoCostruzioni
Messaggi: 51
Iscritto il: mer 9 set 2020, 21:16

Re: se interruttore alto attiva un uscita altrimenti me ne attivi un'altra

Messaggio da RoccoCostruzioni »

stai tranquillo non bisognava specificare la questione del pivello, comunque accetto il tuo aiuto e andiamo avanti :D :D e grazie anticipatamente
Avatar utente
pgv
Messaggi: 484
Iscritto il: gio 17 set 2020, 13:16
Località: Ginevra

Re: se interruttore alto attiva un uscita altrimenti me ne attivi un'altra

Messaggio da pgv »

Oggi sono di corvee, sbuccio zucche e non ho accesso al mio sistema di sviluppo... Comunque la scatola pensante ce l'ho con me, ho pensato un pochino e si potrebbe fare una cosa efficace e perche' no anche didattica: agganciarci all'interrupt che "alimenta" la millis(). Ogni millisecondo (piu' o meno...) il Timer 0 dell'Arduino genera un interrupt che serve ad aggiornare il conteggio dei millisecondi (e anche altre cose). Possiamo sfruttare lo stesso interrupt per gestire le temporizzazioni in maniera trasparente al resto del programma.
<MODO DIDATTICO>
Abbiamo il modo di eseguire una (breve!) funzione tutti i millisecondi. Come facciamo per generare sull'uscita uscitaA un segnale di durata durataA millisecondi ogni volta che l'ingresso ingressoA passa da LOW a HIGH, e sull'uscita uscitaB un segnale di durata durataB ogni volta che lo stesso ingresso passa da HIGH a LOW, sotto il presupposto che una precisione di 1 millisecondo sia sul momento in cui partono i nostri segnalidi uscita che sulla loro durata sia adeguata allo scopo?
Possiamo servirci del modello di una Macchina a Stati Finiti per descrivere il sistema: dal punto di vista strettamente ridotto al nostro ingresso e alle nostre uscite, possiamo trovarci solamente in uno degli stati seguenti (da verificare con le specifiche del progetto se e' possibile, anche solo in via accidentale, che le transizioni dell'ingresso possano avvenire prima che il segnale sulla relativa uscita sia arrivato a termine):
S1 - 00000 - uscitaB LOW, uscitaA LOW, ingressoA LOW - lo stato "ozioso" (idle);
S2 - 00001 - uscitaB LOW, uscitaA LOW, ingressoA HIGH - e' appena arrivato il segnale dall'ingresso;
S3 - 00011 - uscitaB LOW, uscitaA HIGH, ingressoA HIGH - l'Arduino ha risposto al fronte di salita dell'ingresso "alzando" la prima uscita;
S4 - 00101 - l'ingresso e' ancora attivo ma la durata dell'impulso sull'uscitaA e' passata e l'uscitaA e' tornata bassa;
S5 - 00100 - l'ingresso e' finalmente tornato LOW;
S6 - 01100 - l'ingresso e' ancora LOW, l'uscitaB e' diventata a sua volta attiva;
S7 - 10100 - l'ingresso rimane LOW, la durata dell'uscitaB e' a sua volta passata, e anche uscitaB e' tornata bassa;

Ho usato piu' bit dello stretto necessario perche' bisogna mantenere memoria dell'avvenuta "scadenza" dei segnali alle uscite A e B. Cosi' abbiamo anche un certo numero di stati (combinazioni dei nostri 5 bit) teoricamente possibili ma che in pratica non si possono presentare, ed altri la cui possibilita' va verificata da specifiche del Timer esterno, per esempio

S8 - 00010 - il segnale in ingresso e' tornato LOW prima che la durata del segnale sull'uscitaA sia passata;
S9 - 01101 - il segnale in ingresso e' tornato HIGH prima che la durata del segnale sull'uscitaB sia passata;

che possono in effetti presentarsi, mentre altre non credo siano umanamente possibili.

Una volta costruita la tabella degli stati possibili, ci vuole la tabella delle transizioni, che in funzione delle condizioni (IN = ingressoA, UA = uscitaA, DA = durataA passata, UB = uscitaB, DB = durataB passata) e dello stato in cui si trova la MSF determina quale e' il prossimo stato:

Condizioni (IN, UA, DA, UB, DB) Stato Precedente
00000 S1 S2 S3 S4 S5 S6 S7 S8 S9 Qualsiasi altro
00001
...
11111

Anche le combinazioni di condizioni che ci sembrano impossibili vanno considerate, vuol dire che il Prossimo Stato che metteremo nella nostra tabellina sara' S1 (lo stato di "Attesa"). Lo stesso se ci troviamo in uno stato che non abbiamo previsto, "ci riconduciamo al caso precedente" come dicono i colleghi matematici...
Ora devo tornare a sbucciare zucche, deposito il malfatto e ti lascio pensare.
RoccoCostruzioni
Messaggi: 51
Iscritto il: mer 9 set 2020, 21:16

Re: se interruttore alto attiva un uscita altrimenti me ne attivi un'altra

Messaggio da RoccoCostruzioni »

perchè esisterebbe anche un modo professionale? sarei curioso di vederlo! gia quello didattico mi è difficile, figurati il professionale! :lol: :lol: comunque rifletterò
RoccoCostruzioni
Messaggi: 51
Iscritto il: mer 9 set 2020, 21:16

Re: se interruttore alto attiva un uscita altrimenti me ne attivi un'altra

Messaggio da RoccoCostruzioni »

allora ho letto con attenzione la risposta ma se devo usare l'interrupt per gestire millis non l'ho mai fatto, ho fatto invece la gestione di un led attivato da un pulsante, come faccio a gestire millis? servirebbe uno sketch di esempio, almeno per inquadrare la cosa!!!! help me!!!!
Avatar utente
pgv
Messaggi: 484
Iscritto il: gio 17 set 2020, 13:16
Località: Ginevra

Re: se interruttore alto attiva un uscita altrimenti me ne attivi un'altra

Messaggio da pgv »

Ecco un esempio semplice semplice (nell'applicazione finale): contiamo i millisecondi senza ricorrere alla funzione millis().

<----- Tagliare qui ----->
volatile unsigned long milliSecondi; // Deve essere "volatile" per avvertire il compilatore di NON ottimizzare l'accesso alla variabile
// altrimenti rischia che se ha gia' letto il valore in un registro non lo rilegge e quello e' cambiato

void setup()
{
// Timer0 e' gia' usato per la funzione millis()
// ma noi ci agganciamo da qualche parte nel mezzo
// e chiamiamo la nostra funzione
OCR0A = 0x80; // Questo e' l'Output Compare Register A del Timer 0
// Quando il valure contato dal Timer 0 raggiunge il valore da noi scelto, part un interrupt

TIMSK0 |= _BV(OCIE0A); // Questa invece e' la maschera degli Interrupt del Timer 0, e questa strana istruzione
// _BV() e' una macro offertaci da Arduino che vuol dire "Bit Value", quindi _BV(2) = 4
// (il valore del Bit 2). E' definita come uno shift a sinistra di N posizioni:
// #define _BV(bit) (1 << (bit))
// Facciamo l'OR bita a bit del contenuto del registro con _BV(OCIE0A) per mettere
// A "1" il bit che abilita le interruzioni quando il Timer 0 raggiunge il valore di sopra
// Finalmente, azzeriamo il nostro contatore di millisecondi
milliSecondi = 0;
// E predisponiamo la porta seriale per vedere che cosa succede
Serial.begin(9600);
}

// Ora dichiariamo la funzione che vogliamo sia eseguita in risposta all'interrupt, ossia ogni millisecondo
SIGNAL(TIMER0_COMPA_vect)
{
milliSecondi++; // Aggiorniamo il nostro contatore di millisecondi
}

void loop()
{
while(1) {
Serial.print("Sono passati ");
Serial.print(milliSecondi);
Serial.println(" millisecondi.");
delay(100);
}
}
<----- Tagliare qui ----->

L'estensione dell'esempio e' lasciata al Gentile Lettore. Per esempio, controllare se e' aumentato il numero di SECONDI e stampare qualcosa, sempre all'interno di loop().
Avatar utente
pgv
Messaggi: 484
Iscritto il: gio 17 set 2020, 13:16
Località: Ginevra

Re: se interruttore alto attiva un uscita altrimenti me ne attivi un'altra

Messaggio da pgv »

Mi sono accorto che non e' preciso cosi' come lo ho scritto, perde qualche colpo. Sto studiando...
Avatar utente
pgv
Messaggi: 484
Iscritto il: gio 17 set 2020, 13:16
Località: Ginevra

Re: se interruttore alto attiva un uscita altrimenti me ne attivi un'altra

Messaggio da pgv »

Mi rispondo da me come i matti... Comunque, dopo un po' di ricerca, ho scoperto che in Arduino il Timer 0 (Timer il quale viene utilizzato per contare i millisecondi) genera un interrupt NON tutti i microsecondi, o meglio tutte le volde che ha contato 250 colpi del Clock a 16 MHz di sistema, ma dopo che ha contato 256 colpi, e quindi ogni 1024 microsecondi! All'interno della routine che aggiorna il valore dei millisecondi restituito da millis() c'e' una astuta correzione per la leggera differenza tra l'intervallo tra due interruzioni e un millisecondo "vero" (da notare che poi tanto vero non e' e occorrerebbe avere un real Time Clock - RTC - per temporizzazioni estremamente precise). Ho scavato il codice e ricavato la mia correzioncina che a questo punto permette di tenere conto dei millisecondi che passano. Perche' fare cio'? Per esempio, perche' leggendo un pin di ingresso e andando a vedere se ha cambiato valore rispetto alla volta precedente (o abilitando una routine di interrupt attivata dal cambiamento di livello che aggiorna un flag visibile dal "temporizzatore") diventa possibile attivare un'uscita e contare a ritroso 100 millisecondi (anche 473 se uno cosi' fosse inclinato, o 1234...), ossia ogni volta che viene chiamata la funzione sottrarre uno da un contatore inizializzato a 100 e quando questo arriva a zero disattivare di nuovo l'uscita.

Ho qui il codice corretto per quanto riguarda il conteggio dei millisecondi. Per il momento non riesco a testare la logica che gestisce gli ingressi e le uscite, che e' presente ma commentata. C'e' qualcosa che fa si' che il LED si accende ma non si spegne piu'

<----- tagliare qui ----->

volatile unsigned long milliSecondi; // Deve essere "volatile" per avvertire il compilatore di NON ottimizzare l'accesso alla variabile
// altrimenti rischia che se ha gia' letto il valore in un registro non lo rilegge e quello e' cambiato
volatile uint8_t erroreMilliSecondi; // Per tenere conto del fatto che il Timer 0 genera un interrupt tutti i 1024 e non 1000
// millisecondi, accumuliamo l'errore fatto fino a che non e' tanto da farci aggiungere
// un millisecondo intero.
volatile int contatore; // il contatore per i 100 "millisecondi"
volatile bool ingressoVecchio; // vogliamo far partire il LED quando l'ingresso cambia da 0 a 1, per ora
volatile bool stoContando; // E non vogliamo ripartire sui rimbalzi del contatto
const int ingresso = 8; // pin di ingresso che andiamo a monitorare
const int uscita = 13; // pin di uscita guardacaso quello col LED sull'Arduino Uno

void setup()
{
// Dichiariamo i nostri pin
pinMode(ingresso, INPUT);
pinMode(uscita, OUTPUT);
digitalWrite(uscita, LOW);
ingressoVecchio = (digitalRead(ingresso) == LOW);
stoContando = false;

// Timer0 e' gia' usato per la funzione millis()
// ma noi ci agganciamo da qualche parte nel mezzo
// e chiamiamo la nostra funzione
TIMSK0 |= _BV(OCIE0A); // Questa e' la maschera degli Interrupt del Timer 0, e questa strana istruzione
// _BV() e' una macro offertaci da Arduino che vuol dire "Bit Value", quindi _BV(2) = 4
// (il valore del Bit 2). E' definita come uno shift a sinistra di N posizioni:
// #define _BV(bit) (1 << (bit))
// Facciamo l'OR bita a bit del contenuto del registro con _BV(OCIE0A) per mettere
// A "1" il bit che abilita le interruzioni quando il Timer 0 raggiunge il valore di sopra
// Azzeriamo il nostro contatore di millisecondi e il contatore di errore sui millisecondi
milliSecondi = 0L;
erroreMilliSecondi = 0;
// E predisponiamo la porta seriale per vedere che cosa succede
Serial.begin(9600);
}

// Ora dichiariamo la funzione che vogliamo sia eseguita in risposta all'interrupt, ossia ogni millisecondo
SIGNAL(TIMER0_COMPA_vect)
{
// Ahinoi, il Timer 0 dell'Arduino non va in overflow tutti i 1000 microsecondi come ci farebbe comodo, ma
// tutti i 1024 microsecondi. Siccome mettersi a "ciacciare" nel funzionamento interno del Timer 0 e' volere
// male a se' e all'Arduino, per evitare di fare cose di cui ci potremmo pentire copiamo il trucchetto che
// viene usato all'interno della funzione millis() delle librerie di Arduino.
bool ingressoNuovo;

ingressoNuovo = (digitalRead(ingresso) == HIGH);

milliSecondi += 1; // Aggiorniamo provvisoriamente il nostro contatore di millisecondi
erroreMilliSecondi += 3; // Valore empirico necessario per compensare il rapporto 1024/1000 millisecondi
if (erroreMilliSecondi >= 125) { // Altro valore empirico che ci dice quanto deve essere l'errore cumulativo
// per dover incrementare di uno il nostro computo dei millisecondi
erroreMilliSecondi -= 125; // Sottraiamo il numero magico, potrebbe avanzare qualcosina
milliSecondi += 1; // Aggiungiamo il millisecondo mancante, quasi fosse bisestile.
}
// if ((ingressoVecchio == false) && (ingressoNuovo == true)) {
// // Abbiamo avuto una transizione da LOW a HIGH all'ingresso che ci interessa, quindi attiviamo il LED
// digitalWrite(uscita, HIGH);
// Facciamo partire il conteggio dei 100 "millisecondi"
// contatore = 100;
// stoContando = true;
// }
// // Teniamo conto dei cambiamenti
// ingressoVecchio = ingressoNuovo;
// if (stoContando) {
// contatore--;
// if (contatore <= 0) {
// digitalWrite(uscita, LOW);
// stoContando = false;
// }
// }
}

void loop()
{
unsigned long myMs;

while(1) {
// if (stoContando) {
// Serial.println("BEEEP!");
// Serial.print("Sono passati ");
// Serial.print(milliSecondi);
// Serial.println(" milliSecondi.");
// }

// Stampiamo su due colonne i millisecondi contati da noi e quelli contati dal sistema
Serial.print("Sono passati ");
Serial.print(milliSecondi);
Serial.print(" ");
Serial.print(myMs = millis());
Serial.println(" millisecondi.");
delay(1000); // Faccio finta di fare dell'altro
}
}

<----- Tagliare qui ----->
RoccoCostruzioni
Messaggi: 51
Iscritto il: mer 9 set 2020, 21:16

Re: se interruttore alto attiva un uscita altrimenti me ne attivi un'altra

Messaggio da RoccoCostruzioni »

io la ringrazio PGV ma mi rendo conto che stiamo andando oltre le mie capacità attuali non riesco a buttar giù niente lei è stato gentilissimo ma mi fermo qui non so proprio andare avanti, GRAZIE
Dispiace solamente che il mio progetto rimarrà in sospeso, pazienza Un saluto a tutti
Avatar utente
pgv
Messaggi: 484
Iscritto il: gio 17 set 2020, 13:16
Località: Ginevra

Re: se interruttore alto attiva un uscita altrimenti me ne attivi un'altra

Messaggio da pgv »

Questa settimana ho lavorato come una bestia, compreso oggi. Domani la moglie vuole essere portata fuori, ma ho sulla scrivania un Arduino Nano Cinese con un timer 555 che gli genera un segnale alt-basso ogni qualche secondo e potro' simulare l'oggettino. Se scrivo la funzione di risposta all'interrupt (e la tengo semplice) che:
1. controlla il livello presente su un dato pin di ingresso (definito globalmente quindi facile da cambiare) ogni millisecondo;
2. se rivela un cambiamento da LOW a HIGH fa partire un conteggio di 100 millisecondi e porta HIGH un pin di uscita (durata e numero del pin definiti esternamente e quindi modificabili con agio);
3. arrivato alla fine del conteggio riporta LOW il pin di uscita, e continua a monitorare l'ingresso di controllo;
4. quando rivela un passaggio da HIGH a LOW all'ingresso di controllo ripete il passo 3 (con una durata e un pin di uscita definiti esternamente e potenzialmente diversi);
5. finito anche questo conteggio, riporta a LOW anche la seconda uscita e si riporta al punto 1.
dovremmo avere un sistema indipendente dal resto del programma e che funziona "da solo" una volta inizializzato, a patto che:

il segnale di ingresso che avvia le uscite resti HIGH almeno 100 millisecondi (se torna LOW prima della fine del primo segnale in uscita occorre definire altri casi) e LOW almeno 100 millisecondi (ide per il secondo segnale di uscita), ossia non ci sono ripartenze mentre le uscite sono ancora attive.

Risolto il problema pratico, possiamo provare a capire come e perche' fa quello che fa. OK?
Rispondi