[RISOLTO...MAGARI!!] MALEDETTI SERVOMOTORI

Salve raga... ho un piccolo problema...
ho realizzato un circuito "finito" con un 7805 per il 328 (16Mhz), un ultra LDO 3,3v a valle del 7805 per un xbee ed il tutto non deve far altro che muovere 2 servomotori (7,2v 2,5A di stallo, 30kg)...
alimento il circuito con un stabilizzato a 7v 3A (max) e il tutto funziona se non che ogni volta che ricevo i dati per girarli ai servo, questi ultimi scattano. mi spiego meglio.

TRALASCIAMO PER IL MOMENTO CHE I SERVO SONO 2 DATO CHE I TEST LI STO ESEGUENDO SOLO CON UN SERVO COLLEGATO.

I dati sono inviati da un altro xbee e 328 annesso al quale è collegato un encoder. i valori che vengono trasmessi ovviamente variano da 0 a 180 come accordato con la libreria servo preinstallata nell'ide.. La trasmissione dati avviene ogni 250ms in caso stiamo giochicchiando con l'encoder, diversamente la trasmissione passa a ogni 2000ms.
Il problema... se la trasmissione avviene ogni 250ms (ponendo ad esempio che andiamo con l'encoder da 0 verso 180 lentamente) sulla ricevente viene eseguito ovviamente il comando Servo.write(gradiRX) ogni 250ms... ma ad un certo punto (in modo randomico) il servo va a 0 per poi riprendere la giusta posizione (non avviene il reset del 328, lui continua a girare perfettamente, e i dati inviati sono corretti, il problema non è software!). alche quando lascio l'encoder, quindi il servo si ferma, e la trasmissione dati passa da 250ms a 2000ms, OGNI qualvolta si ricevono i dati (ho il led sul 13 che si accende quando c'è ricezione) e quindi viene di nuovo ritrasmesso l'ultimo valore memorizzato, il servo anziché stare fermo fa un piccolo scatto di circa 3/5mm... questo avviene proprio mentre riceve i dati... cosa può essere?

Io personalmente penso che sia un problema di eccessivo assorbimento dei servo che mi tira giù il pwm quando ricevo i dati... questo spiegherebbe il servo a 0 randomicamente quando giro lentamente e forse anche lo scatto durante lo stallo... cosa devo controllare? il pcb sulla Vin ha anche 2 elettrolitici da 2200uF appunto per quei servo energivori, posti a meno di 5mm dall'attacco dei servo stessi...
Idee?
Grazie!

Idee?

sí, errore nello Sketch, errore nella scheda, errore nel circuito.
Se la tensione di alimentazione si abbasserebbe troppo da fare problemi l' ATmega328 si resetterebbe. Il reset Tu lo hai escluso.
Ciao Uwe

uwefed:
Se la tensione di alimentazione si abbasserebbe troppo da fare problemi l' ATmega328 si resetterebbe. Il reset Tu lo hai escluso.
Ciao Uwe

Ho pensato anche io a quello che sostieni tu, ma avendo il led sul 13 ed il bootloader caricato se si resetta lo vedi dal led che è occorso un reset del 328... invece no. il led, che in loop è acceso quando ricevo i dati, quindi ogni 2 secondi oppure ogni 250ms, lampeggia come ci si aspetta... se riesco faccio un video e lo linko così fugo ogni dubbio sul funzionamento...
Codice fallato? altamente improbabile perché nel loop dopo la ricezione dati non fa altro che un semplice Servo.write(valoreRicevuto)...
ripeto lo sbalzo avviene quando sto ricevendo i dati, non dopo che si spegne il led...
in altre parole, poniamo che l'ultima posizione del servo sia Servo.write(80)
ritrasmetto 80 dopo due secondi... mentre leggo i dati dall'xbee il servo è come se va su 60 (o cerca di raggiungere una posizione diversa da quella che teneva)... a termine della lettura della seriale viene richiamato Servo.write(80) ed il servo ritorna subito in posizione creando appunto quel fastidiosissimo "effetto scossa".
2 precisazioni: l'xbee è alimentato da un rt9013 a valle del 7805 (d2pak) ed essendo un 5v tollerant non ho messo alcun level shifter tra lui ed il 328.
la seriale con l'xbee è inizializzata con la libreria SwSerial.
Alche mi sorge un dubbio... non è che è proprio il funzionamento di quest'ultima che crea problemi sul duty cicle del pwm quando viene richiamata in loop?

EDIT: devo tornare su breadboard per fugare questo atroce dubbio...

Non so se mi devo arrabbiare con Guglielmo, o gli devo esserne grato (solitamente lui è dappertutto) o se se devo ritenermi fortunato che non mi ha già sbattuto in faccia qualcosa tipo questa...
Faccio alcuni test e vi aggiorno... comunque per qualsiasi altra idea postate senza pietà...
PS: Guglielmo la mia affermazione è da intendersi di tono sarcastico. ci mancherebbe altro... :smiley:

miky_police:
Non so se mi devo arrabbiare con Guglielmo, o gli devo esserne grato (solitamente lui è dappertutto) ...

... purtroppo sono giorni che ho molto poco tempo a disposizione per controllare/rispondere alle varie discussioni e ... mi dedico proncipalmente hai problemi di "regolamento" :grin:

Guglielmo

Cosa devo dire? Non mi dici cosa hai di preciso e io dovrei dirti che il circuito elettrico é giusto o no?
Fai quello che vuoi. Se fai quello che Ti ho chiesto posso aiutarti.
Ciao Uwe

uwefed:
Cosa devo dire? Non mi dici cosa hai di preciso e io dovrei dirti che il circuito elettrico é giusto o no?

... Uwe, se leggi tutta la discussione che ha linkato, già a suo tempo avevo spiegato che è un problema con la SoftwareSerial e gli interrupts. Se ben ricordo avevo anche dato una soluzione ... ::slight_smile:

Guglielmo

Ciao uwe, non voglio polemizzare nel modo più assoluto anche perché dei tuoi post/consigli ne ho GRANDE RISPETTO. Si capisce che sei un esperto del settore, ma...

uwefed:
sí, errore nello Sketch, errore nella scheda, errore nel circuito.
Se la tensione di alimentazione si abbasserebbe troppo da fare problemi l' ATmega328 si resetterebbe. Il reset Tu lo hai escluso.
Ciao Uwe

da questo reply non mi hai chiesto di fare una specifica prova e restituirti l'effetto esordito, piuttosto hai detto che il problema può risiedere dappertutto. E già questo mi ha spinto a ragionare oltre e iniziare ad analizzare il problema con più oculatezza, tra l'altro riportando il ragionamento nel mio reply #3. Già per questo ti ringrazio... Se poi mangiando mangiando vien l'appetito... tanto meglio... Ho usato Guglielmo come capo espiatorio (scusa gpb01 :smiley: ) per celare la mia ignoranza :smiley: :smiley: .
Mi spiace che l'hai messa sul personale...
Rientrando in topic, sono ancora in fase di test (probabilmente lo faccio a breve, dopo cena).

EDIT: comunque da questo topic gpb01 identifica il problema al #29 e la soluzione la offre al #31.

miky_police:
... Ho usato Guglielmo come capo espiatorio (scusa gpb01 :smiley: ) per celare la mia ignoranza :smiley: :smiley: .

... hai presente la mia "firma" vero ? ? ? :smiling_imp: :smiling_imp: :smiling_imp:

Quindi ... hai fatto solo il tuo dovere :stuck_out_tongue_closed_eyes: :stuck_out_tongue_closed_eyes: :stuck_out_tongue_closed_eyes:

Guglielmo

Io ho del codice che a quanto sembra non usa ISR per gestire il servo ma solo il timer1, purtroppo non è arduinesco e l'ho scritto anni fa (quando il cervello funzionava) e ora non ho idea di come potesse lavorare senza ISR.

C'è anche il commento gli date uno sguardo per capire se è corretto (giuro funzionava alla grande)

// 16.000.000 / ((2 * 64) * 2500)) = 50Hz
// risoluzione 11,2 bit
/*
    Timer 1 in modalita PWM Phase and Frequency Correct (8).
    Il valore di ICR1 stabilisce la frequenza di oscillazione del PWM, OCR1x modifica il duty cycle.
    Per i servo abbiamo bisogno di un segnale PWM con periodo di 20ms (50Hz).

    Ricaviamo il valore di ICR1 con la seguente formula:

        (1) ICR1 = (F_CPU / ( 2 x pfactor )) / foc

    Per massimizzare la risoluzione, il valore di ICR1 deve essere il più alto possibile.
    Ricaviamo la risoluzione tramite la seguente formula:

        (2) BitResolution = log(ICR1+1) / log(2)

    Con F_CPU = 16.000.000 (16MHz) e pfactor = 1 otteniamo il valore di ICR1 con la (1):

        ICR1 = (16.000.000 / ( 2 x 1 )) / 50 = 160000

    ICR1 non può valere 160.000 perché il numero richiede più dei 16bit a disposizione del registro ICR1.
    Nel caso in cui F_CPU = 8.000.000 (8MHz) non possiamo ancora usare il prescaler di valore 1, ma è ancora valido
    il valore 8 e il valore di ICR1 si dimezza, cambia anche leggermente è la risoluzione in bit.
    Procediamo con un pfactor = 8:

        ICR1 = (16.000.000 / ( 2 x 8 )) / 50 = 20000

    Il valore 20.000 è minore di 65535 per cui con ICR1 = 20.000 ricaviamo la risoluzione in bit del timer 1
    con la (2):

        BitResolutin = log ( 20.000 + 1 ) / log (2) = 4,30105171 / 0,301029996 = 14,28

    Con questi valori, OCR1x è espresso in us, per cui il valore iniziale sarà OCR1x = 1.5ms x 1000 = 1500us.
    Mentre nel caso in cui F_CPU = 8.000.000, tutti i valori si dimezzano tranne la risoluzione che perde solp un bit.

    Secondo il datasheet la larghezza dell'impulso può variare da 1ms (-90°) a 2ms (+90°), per cui il valore
    di OCR1x deve potere variare nel range 1000 a 2000. In caso di F_CPU = 8.000.000 il range è di 500 a 1000.

    Per contenere il valore massimo di 2000, serve una variabile da 16 bit senza segno.

    La funzione setPulseWidth sarà inline e prende una variabile di tipo uint16_t:

        void inline setPulseWidth( uint16_t pw ) { OCR1A = pw; }
*/

/* Note:
        1) solo l'uscita OC1A viene inizializzata (da rimuovere, aggiunto canale B)
        2)  Ricorda di impostare il pin di uscita OC1A come output,
            stessa cosa per il pin OC1B se usato.


*/
void timer16_init() {
    // Imposta la modalità 8 (PWM Phase and Frequency Correct). Registri coinvolti 2 (TCCR1A e TCCR1B).
    // Per la modalità 8 occorre accendere solo il bit 4 (WGM13) di TCCR1B.
    TCCR1B  |=  _BV(WGM13);

    // Accende OC1A al match quando conta verso l'alto. Spegne OC1A al match quando conta verso il basso.
    // Accende OC1B al match quando conta verso l'alto. Spegne OC1B al match quando conta verso il basso.
    TCCR1A  |=  _BV(COM1A1) | _BV(COM1A0) | _BV(COM1B1) | _BV(COM1B0);


    ICR1 = (F_CPU / (2 * 8)) / 50;

    // impostare il prescaler a valore diverso da 0 significa anche avviare il timer
    // ** Attenzione ** il modulo prescaler è condiviso con il timer 0, ma la scelta del divisore
    // rimane indipendente per ogni timer. L'inconveniente può essere o meno rilevante in base al
    // divisore scelto. Visto che il modulo prescaler è sempre collegato con il clock principale, dopo
    // avere scelto il divisore il timer si avvia dopo n impulsi di clock, dove n è il valore del divisore scelto.
    // Con il divisore di 8 il problema è inrilevante.
    OCR1A = ICR1 - PWM_CENTER_POS_A;
    //OCR1B = ICR1 - PWM_CENTER_POS_B;    // aggiunto l'uscita B

    // TCCR1B |= _BV(CS11); // prescaler 8
}

Grazie Maurotec, lo sketch l'ho salvato, avrò modo di provarlo. Contestualmente ho risolto con i consigli di Guglielmo che riporto qui sotto.

La SoftwareSerial utilizza, per la ricezione di caratteri, un interrupt e relativa ISR, per cui, ogni volta che si riceve un carattere, per un certo periodo (durata della sua ISR) vengono disabilitati gli interrupts ...

La libreria Servo, utilizza anche essa una ISR associata ad un Timer ...

Non vorrei che, mentre la Servo sta lavorando, arriva un carattere, viene richiamata la ISR per riceverlo e ... magari per un brevissimo tempo non scatta l'ISR del Timer ...

E solo un'ipotesi, ma puoi fare una verifica ... il problema si presenta casualmente o quando invii caratteri sul BT ?

Guglielmo

Conferma quanto supponevo ... la ISR della SoftwareSerial, richiamata in continuazione, va ad interferire con quella della Servo.

Del resto ... basta fare una ricerca su Google per "arduino softwareserial servo conflict" per scoprire che il problema è noto e che NON c'è una diretta soluzione se non cambiando tutte le librerie usate o usando le vere seriali HW.

Quindi ...

  1. soluzione migliore, prendi una scheda, come la MEGA che ha più seriali HW, e connetti il modulo BT su una di esse. Sulle serilai HW la ricezione del carattere è fatta via HW e quindi non c'è questo "disturbo" alla libreria Servo.

  2. soluzione tutta da verificare, ma in altri thread pare funzionare, cambiare entrambe le librerie. Usare la AltSoftSerial e la ServoTimer2. La AltSoftSerial è più veloce e blocca meno gli interrupts, però usa Timer1 qundi va in conflitto con la classica Servo, motivo per cui occorre usare la ServoTimer2 che usa invece Timer2.

AltSoftSerial
ServoTimer2

Colgo l'occasione per fare 2 precisazioni.
La prima, ho risolto usando la seriale di Arduino su pin 0 ed 1 per evitare qualsiasi altro grattacapo.
La libreria AltSoftSerial emula la seriale (su Arduino UNO) solo su pin 9 (tx) e 8 (rx) senza possibilità di cambiare i pin e contestualmente non si può usare in PWM il pin 10...

OK, piccolo UPDATE... Ho spostato la comunicazione con l'XBEE sulla seriale nativa del 328 ovvero pin 0 e 1.
Il problema sopra citato è sparito, almeno in parte...

Il problema, in standalone, permane sui pin 4 e 5 del 328, ma sparisce sui pin 9 e 10... non ho avuto modo di provare gli altri pwm per mancanza di tempo.

Qualcuno sa darmi una spiegazione logica di questo?
Sicuramente riguarda i timer, ISR, ect ect, si, ma nello specifico? Grazie