0
Offline
Sr. Member
Karma: 1
Posts: 412
Arduino rocks
|
 |
« Reply #15 on: September 22, 2012, 05:19:37 am » |
Mi viene il dubbio e chiedo una conferma a voi che il radiocomando invii il segnale solo quando ci sono delle variazioni sui valori (movimento stick) mentre quando non ci sono variazioni, non invia nulla o invia solo un segnale di conferma che secondo me potrebbe essere un segnale alto che dura 22.1ms + una pausa di 0.4ms.
Il segnale viene trasmesso di continuo, il tremolio dei servi vuol dire che il tuo segnale non è stabile nel tempo, ovvero non vengono generate sequenze di impulsi perfettamente identiche a parità di condizioni. Come generi il ppm e in base a cosa vari le durate dei singoli impulsi ? Eccomi, scusate il ritardo nella risposta ma nel frattempo ho provato a vedere se ne capivo qualcosa di più e riuscivo a cavarmela da solo............. ovviamente non ci sono riuscito  Questo è lo sketch che genera il segnale PPM: #define Durata 22000 #define Pausa 400 #define PPMPin 8
void setup() { Serial.begin(9600); pinMode(PPMPin, OUTPUT); }
void loop() { int ch[8] = {720, 1120, 1120, 1120, 1120, 1120, 1120, 1120}; long timeframe; timeframe=0;
//1 digitalWrite(PPMPin, HIGH); delayMicroseconds(ch[0]); digitalWrite(PPMPin, LOW); delayMicroseconds(Pausa); timeframe=timeframe+ch[0]+Pausa; //2 digitalWrite(PPMPin, HIGH); delayMicroseconds(ch[1]); digitalWrite(PPMPin, LOW); delayMicroseconds(Pausa); timeframe=timeframe+ch[1]+Pausa; //3 digitalWrite(PPMPin, HIGH); delayMicroseconds(ch[2]); digitalWrite(PPMPin, LOW); delayMicroseconds(Pausa); timeframe=timeframe+ch[2]+Pausa;
//4 digitalWrite(PPMPin, HIGH); delayMicroseconds(ch[3]); digitalWrite(PPMPin, LOW); delayMicroseconds(Pausa); timeframe=timeframe+ch[3]+Pausa;
//5 digitalWrite(PPMPin, HIGH); delayMicroseconds(ch[4]); digitalWrite(PPMPin, LOW); delayMicroseconds(Pausa); timeframe=timeframe+ch[4]+Pausa; //6 digitalWrite(PPMPin, HIGH); delayMicroseconds(ch[5]); digitalWrite(PPMPin, LOW); delayMicroseconds(Pausa); timeframe=timeframe+ch[5]+Pausa;
//7 digitalWrite(PPMPin, HIGH); delayMicroseconds(ch[6]); digitalWrite(PPMPin, LOW); delayMicroseconds(Pausa); timeframe=timeframe+ch[6]+Pausa;
//8 digitalWrite(PPMPin, HIGH); delayMicroseconds(ch[7]); digitalWrite(PPMPin, LOW); delayMicroseconds(Pausa); timeframe=timeframe+ch[7]+Pausa;
//Completa il frame 20ms digitalWrite(PPMPin, HIGH); delayMicroseconds(Durata -timeframe); digitalWrite(PPMPin, LOW); delayMicroseconds(Pausa); }
Questo sketch genera un segnale PPM che dovrebbe lasciare tutti i servocomandi fermi nella posizione impostata. Provando a commentare le righe inerenti a tutti i canali e lasciando solo quella del primo canale, con l'oscilloscopio ho effettivamente notato una cosa che non riuscivo a vedere analizzando l'onda di tutti gli 8 canali. Il segnale non è precisissimo, ha delle piccole oscillazioni che sono effettivamente quelle che generano il tremolio dei servocomandi. Nel frattempo ho fatto qualche ricerca per capire se magari dipendesse dal fatto che la serie di istruzioni faceva perdere del tempo prezioso di elaborazione ma non credo sia quello il problema. Infatti ho provato a scrivere un piccolo sketch che genera un'onda quadra direttamente tramite il timer ma anche con questo vedo che la lettura non è sempre uguale, ci sono delle piccole variazioni: void setup() { pinMode(3, OUTPUT); pinMode(11, OUTPUT); TCCR2A = _BV (COM2A1) | _BV (COM2B1) |_BV (WGM21) | _BV (WGM20); TCCR2B = _BV (CS22); OCR2A = 200; OCR2B = 100; } void loop() {}
Quindi a questo punto la domanda è: qual'è il trucco per avere un segnale pulito e stabile? Grazie dell'aiuto. Ciao.
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Sr. Member
Karma: 1
Posts: 412
Arduino rocks
|
 |
« Reply #16 on: September 22, 2012, 05:24:19 am » |
Thanks for your help. I will study it as soon as possible  Bye.
|
|
|
|
|
Logged
|
|
|
|
|
Rome (Italy)
Offline
Tesla Member
Karma: 74
Posts: 7382
"Il Vero Programmatore ha imparato il C sul K&R, qualunque altro testo è inutile e deviante."
|
 |
« Reply #17 on: September 22, 2012, 05:50:08 am » |
Eccomi, scusate il ritardo nella risposta ma nel frattempo ho provato a vedere se ne capivo qualcosa di più e riuscivo a cavarmela da solo............. ovviamente non ci sono riuscito  Se non mi ricordo male la delayMicroseconds() non è molto precisa come ripetibilità dei tempi, ovvero soffre di jitter, dopo pranzo provo il tuo sketch e ti dico di quanto oscillano esattamente i vari impulsi (misura con DSO) e vediamo come fare per risolvere il problema.
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Sr. Member
Karma: 1
Posts: 412
Arduino rocks
|
 |
« Reply #18 on: September 22, 2012, 06:48:23 am » |
Eccomi, scusate il ritardo nella risposta ma nel frattempo ho provato a vedere se ne capivo qualcosa di più e riuscivo a cavarmela da solo............. ovviamente non ci sono riuscito  Se non mi ricordo male la delayMicroseconds() non è molto precisa come ripetibilità dei tempi, ovvero soffre di jitter, dopo pranzo provo il tuo sketch e ti dico di quanto oscillano esattamente i vari impulsi (misura con DSO) e vediamo come fare per risolvere il problema. Grazie 
|
|
|
|
|
Logged
|
|
|
|
|
Rome (Italy)
Offline
Tesla Member
Karma: 74
Posts: 7382
"Il Vero Programmatore ha imparato il C sul K&R, qualunque altro testo è inutile e deviante."
|
 |
« Reply #19 on: September 22, 2012, 07:23:23 am » |
Ho fatto un primo test col tuo sketch e ti confermo quanto avevo sospettato, e tu che tu avevi già visto con l'oscilloscopio, i segnali dei servo soffrono tutti di un jitter compreso tra 5 e 20 us e questo causa il tremolio dei servo che lavorano in continuazione per aggiornare la posizione. Per me la soluzione può essere solo una, prima di tutto eliminare la digitalwrite che è lentissima di suo e poi usare direttamente un timer per le temporizzazioni invece della delaymicroseconds che per questo genere di applicazioni non è abbastanza precisa/stabile. Adesso faccio una modifica al volo allo sketch per renderlo preciso e tra non molto te lo posto.
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Sr. Member
Karma: 1
Posts: 412
Arduino rocks
|
 |
« Reply #20 on: September 22, 2012, 07:34:35 am » |
Ho fatto un primo test col tuo sketch e ti confermo quanto avevo sospettato, e tu che tu avevi già visto con l'oscilloscopio, i segnali dei servo soffrono tutti di un jitter compreso tra 5 e 20 us e questo causa il tremolio dei servo che lavorano in continuazione per aggiornare la posizione. Per me la soluzione può essere solo una, prima di tutto eliminare la digitalwrite che è lentissima di suo e poi usare direttamente un timer per le temporizzazioni invece della delaymicroseconds che per questo genere di applicazioni non è abbastanza precisa/stabile. Adesso faccio una modifica al volo allo sketch per renderlo preciso e tra non molto te lo posto.
Ti ringrazio. Effettivamente stavo pensando anch'io di usare un timer ma è da questa mattina che ci penso ma non riesco a tirare fuori un'idea su come implementare lo stesso sketch con l'uso del timer.
|
|
|
|
|
Logged
|
|
|
|
|
Rome (Italy)
Offline
Tesla Member
Karma: 74
Posts: 7382
"Il Vero Programmatore ha imparato il C sul K&R, qualunque altro testo è inutile e deviante."
|
 |
« Reply #21 on: September 22, 2012, 10:32:02 am » |
Adesso dovrebbe andare bene, è scritto in fretta e sicuramente si possono fare varie migliorie al codice, uso il timer1 in modo normale caricando di volta in volta il conteggio per raggiungere il tempo previsto dall'impulso, quando si verifica l'interrupt di overflow del timer1 resetto il pin 8 e il ciclo continua con il prossimo evento. Volendo è possibile rendere il tutto indipendente dalle attese semplicemente utilizzando uno scheduler su evento, basta incrementare all'interno dell'interrupt una variabile che dice su quale canale si deve operare e usare il flag per la while d'attesa come flag per dare il via al prossimo ciclo, il loop gira normalmente permettendo di svolgere altri compiti nel frattempo. #define Durata 20000 #define Pausa 400 #define PPMPin 8
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
volatile char attesa;
ISR(TIMER1_OVF_vect) { cbi(TCCR1B,CS10); // stop timer 1 cbi (PORTB,0); // pin 8 a 0 attesa = 0; }
void setup() { Serial.begin(9600); pinMode(PPMPin, OUTPUT); pinMode(13, OUTPUT); digitalWrite(13,HIGH); // init timer 1 TCCR1A = 0x00; // modo normale TCCR1B = 0x00; // sbi(TCCR1B,CS12); // sbi(TCCR1B,CS11); // sbi(TCCR1B,CS10); TIMSK1=0x01; // enabled timer overflow interrupt; }
void loop() { int ch[8] = {720, 1000, 1200, 1500, 1700, 1900, 2000, 900}; long timeframe; timeframe=0; //1 TCNT1 = 65536- ch[0] * 16; // calcola valore da caricare nel timer sbi(TCCR1B,CS10); // avvia il timer (16 MHz) sbi (PORTB,0); // setta il pin 8 a 1 attesa = 1; // setta la variabile per il ciclo d'attesa dell'interrupt. while (attesa == 1); // attesa interrupt TCNT1 = 65536- Pausa * 16; // pausa tra gli impulsi. sbi(TCCR1B,CS10); attesa = 1; while (attesa == 1); timeframe=timeframe+ch[0]+Pausa;
//2 TCNT1 = 65536- ch[1] * 16; sbi(TCCR1B,CS10); attesa = 1; sbi (PORTB,0); while (attesa == 1); TCNT1 = 65536- Pausa * 16; sbi(TCCR1B,CS10); attesa = 1; while (attesa == 1); timeframe=timeframe+ch[1]+Pausa; //3 TCNT1 = 65536- ch[2] * 16; sbi(TCCR1B,CS10); attesa = 1; sbi (PORTB,0); while (attesa == 1);
TCNT1 = 65536- Pausa * 16; sbi(TCCR1B,CS10); attesa = 1; while (attesa == 1); timeframe=timeframe+ch[2]+Pausa;
//4 TCNT1 = 65536- ch[3] * 16; sbi(TCCR1B,CS10); attesa = 1; sbi (PORTB,0); while (attesa == 1); TCNT1 = 65536- Pausa * 16; sbi(TCCR1B,CS10); attesa = 1; while (attesa == 1); timeframe=timeframe+ch[3]+Pausa;
//5 TCNT1 = 65536- ch[4] * 16; sbi(TCCR1B,CS10); attesa = 1; sbi (PORTB,0); while (attesa == 1); TCNT1 = 65536- Pausa * 16; sbi(TCCR1B,CS10); attesa = 1; while (attesa == 1); timeframe=timeframe+ch[4]+Pausa; //6 TCNT1 = 65536- ch[5] * 16; sbi(TCCR1B,CS10); attesa = 1; sbi (PORTB,0); while (attesa == 1); TCNT1 = 65536- Pausa * 16; sbi(TCCR1B,CS10); attesa = 1; while (attesa == 1); timeframe=timeframe+ch[5]+Pausa; //7 TCNT1 = 65536- ch[6] * 16; sbi(TCCR1B,CS10); attesa = 1; sbi (PORTB,0); while (attesa == 1);
TCNT1 = 65536- Pausa * 16; sbi(TCCR1B,CS10); attesa = 1; while (attesa == 1); timeframe=timeframe+ch[6]+Pausa; //8 TCNT1 = 65536- ch[7] * 16; sbi(TCCR1B,CS10); attesa = 1; sbi (PORTB,0); while (attesa == 1); TCNT1 = 65536- Pausa * 16; sbi(TCCR1B,CS10); attesa = 1; while (attesa == 1); timeframe=timeframe+ch[7]+Pausa; //Completa il frame 20ms digitalWrite(PPMPin, HIGH); delayMicroseconds(Durata -timeframe); digitalWrite(PPMPin, LOW);
delayMicroseconds(Pausa); }
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Tesla Member
Karma: 83
Posts: 8244
:(){:|:&};:
|
 |
« Reply #22 on: September 22, 2012, 11:39:01 am » |
qualcosa non mi torna, tu dai una pausa fissa di 400 tra un canale e l'altro, ma non dovrebbe essere di 2000-ch[ i ], ovvero ogni canale occupare sempre 2000 e il segnale totale 20000, fissando quindi il limite di canali massimi a 10? comuqnue ho rielaborato il codice per essere un poco più comprensibile (odio le ripetizioni  ), c'è un salto a funzione in più, verò che aggiunge un (microscopico) delay ma il tempo è fisso, quindi anche se fosse un delay sensibile sarebbe aggirabile sottraendo al valore wait il "tempo di salto" #define Durata 20000 #define Pausa 400 #define PPMPin 8 #define NUMERO_CANALI 8
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
volatile char attesa;
int ch[NUMERO_CANALI] = {720, 1000, 1200, 1500, 1700, 1900, 2000, 900};
ISR(TIMER1_OVF_vect) { cbi(TCCR1B,CS10); // stop timer 1 cbi (PORTB,0); // pin 8 a 0 attesa = 0; }
void setup() { Serial.begin(9600); pinMode(PPMPin, OUTPUT); pinMode(13, OUTPUT); digitalWrite(13,HIGH); // init timer 1 TCCR1A = 0x00; // modo normale TCCR1B = 0x00; TIMSK1=0x01; // enabled timer overflow interrupt; }
void loop() { long timeframe; timeframe=0;
//All int i; for (i=0;i<NUMERO_CANALI;i++){ sbi (PORTB,0); wait(ch[i]); wait(pausa); timeframe=timeframe+ch[i]+Pausa; } //Completa il frame 20ms digitalWrite(PPMPin, HIGH); delayMicroseconds(Durata -timeframe); digitalWrite(PPMPin, LOW);
delayMicroseconds(Pausa); }
void wait(int tempo){ TCNT1 = 65536- tempo * 16; sbi(TCCR1B,CS10); attesa = 1; while (attesa == 1); }
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Sr. Member
Karma: 1
Posts: 412
Arduino rocks
|
 |
« Reply #23 on: September 22, 2012, 11:58:10 am » |
Adesso dovrebbe andare bene, è scritto in fretta e sicuramente si possono fare varie migliorie al codice, uso il timer1 in modo normale caricando di volta in volta il conteggio per raggiungere il tempo previsto dall'impulso, quando si verifica l'interrupt di overflow del timer1 resetto il pin 8 e il ciclo continua con il prossimo evento. Volendo è possibile rendere il tutto indipendente dalle attese semplicemente utilizzando uno scheduler su evento, basta incrementare all'interno dell'interrupt una variabile che dice su quale canale si deve operare e usare il flag per la while d'attesa come flag per dare il via al prossimo ciclo, il loop gira normalmente permettendo di svolgere altri compiti nel frattempo. #define Durata 20000 #define Pausa 400 #define PPMPin 8
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
volatile char attesa;
ISR(TIMER1_OVF_vect) { cbi(TCCR1B,CS10); // stop timer 1 cbi (PORTB,0); // pin 8 a 0 attesa = 0; }
void setup() { Serial.begin(9600); pinMode(PPMPin, OUTPUT); pinMode(13, OUTPUT); digitalWrite(13,HIGH); // init timer 1 TCCR1A = 0x00; // modo normale TCCR1B = 0x00; // sbi(TCCR1B,CS12); // sbi(TCCR1B,CS11); // sbi(TCCR1B,CS10); TIMSK1=0x01; // enabled timer overflow interrupt; }
void loop() { int ch[8] = {720, 1000, 1200, 1500, 1700, 1900, 2000, 900}; long timeframe; timeframe=0; //1 TCNT1 = 65536- ch[0] * 16; // calcola valore da caricare nel timer sbi(TCCR1B,CS10); // avvia il timer (16 MHz) sbi (PORTB,0); // setta il pin 8 a 1 attesa = 1; // setta la variabile per il ciclo d'attesa dell'interrupt. while (attesa == 1); // attesa interrupt TCNT1 = 65536- Pausa * 16; // pausa tra gli impulsi. sbi(TCCR1B,CS10); attesa = 1; while (attesa == 1); timeframe=timeframe+ch[0]+Pausa;
//2 TCNT1 = 65536- ch[1] * 16; sbi(TCCR1B,CS10); attesa = 1; sbi (PORTB,0); while (attesa == 1); TCNT1 = 65536- Pausa * 16; sbi(TCCR1B,CS10); attesa = 1; while (attesa == 1); timeframe=timeframe+ch[1]+Pausa; //3 TCNT1 = 65536- ch[2] * 16; sbi(TCCR1B,CS10); attesa = 1; sbi (PORTB,0); while (attesa == 1);
TCNT1 = 65536- Pausa * 16; sbi(TCCR1B,CS10); attesa = 1; while (attesa == 1); timeframe=timeframe+ch[2]+Pausa;
//4 TCNT1 = 65536- ch[3] * 16; sbi(TCCR1B,CS10); attesa = 1; sbi (PORTB,0); while (attesa == 1); TCNT1 = 65536- Pausa * 16; sbi(TCCR1B,CS10); attesa = 1; while (attesa == 1); timeframe=timeframe+ch[3]+Pausa;
//5 TCNT1 = 65536- ch[4] * 16; sbi(TCCR1B,CS10); attesa = 1; sbi (PORTB,0); while (attesa == 1); TCNT1 = 65536- Pausa * 16; sbi(TCCR1B,CS10); attesa = 1; while (attesa == 1); timeframe=timeframe+ch[4]+Pausa; //6 TCNT1 = 65536- ch[5] * 16; sbi(TCCR1B,CS10); attesa = 1; sbi (PORTB,0); while (attesa == 1); TCNT1 = 65536- Pausa * 16; sbi(TCCR1B,CS10); attesa = 1; while (attesa == 1); timeframe=timeframe+ch[5]+Pausa; //7 TCNT1 = 65536- ch[6] * 16; sbi(TCCR1B,CS10); attesa = 1; sbi (PORTB,0); while (attesa == 1);
TCNT1 = 65536- Pausa * 16; sbi(TCCR1B,CS10); attesa = 1; while (attesa == 1); timeframe=timeframe+ch[6]+Pausa; //8 TCNT1 = 65536- ch[7] * 16; sbi(TCCR1B,CS10); attesa = 1; sbi (PORTB,0); while (attesa == 1); TCNT1 = 65536- Pausa * 16; sbi(TCCR1B,CS10); attesa = 1; while (attesa == 1); timeframe=timeframe+ch[7]+Pausa; //Completa il frame 20ms digitalWrite(PPMPin, HIGH); delayMicroseconds(Durata -timeframe); digitalWrite(PPMPin, LOW);
delayMicroseconds(Pausa); }
Perfetto, funziona! Astro: SANTO SUBITO  Ora i servi stanno belli fermi solo che non riesco più a passare i dati da pc ad arduino attraverso la seriale probabilmente perchè softwareserial usa lo stesso timer per funzionare. Comunque avevo già intenzione di escogitare qualche altro sistema perchè la seriale o la faccio lavorare a bassa velocità oppure si perde dati per strada. Il dubbio è se trovare qualche interfaccia i2c per pc o usare la parallela.
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Sr. Member
Karma: 1
Posts: 412
Arduino rocks
|
 |
« Reply #24 on: September 22, 2012, 11:59:27 am » |
qualcosa non mi torna, tu dai una pausa fissa di 400 tra un canale e l'altro, ma non dovrebbe essere di 2000-ch[ i ], ovvero ogni canale occupare sempre 2000 e il segnale totale 20000, fissando quindi il limite di canali massimi a 10? comuqnue ho rielaborato il codice per essere un poco più comprensibile (odio le ripetizioni  ), c'è un salto a funzione in più, verò che aggiunge un (microscopico) delay ma il tempo è fisso, quindi anche se fosse un delay sensibile sarebbe aggirabile sottraendo al valore wait il "tempo di salto" #define Durata 20000 #define Pausa 400 #define PPMPin 8 #define NUMERO_CANALI 8
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
volatile char attesa;
int ch[NUMERO_CANALI] = {720, 1000, 1200, 1500, 1700, 1900, 2000, 900};
ISR(TIMER1_OVF_vect) { cbi(TCCR1B,CS10); // stop timer 1 cbi (PORTB,0); // pin 8 a 0 attesa = 0; }
void setup() { Serial.begin(9600); pinMode(PPMPin, OUTPUT); pinMode(13, OUTPUT); digitalWrite(13,HIGH); // init timer 1 TCCR1A = 0x00; // modo normale TCCR1B = 0x00; TIMSK1=0x01; // enabled timer overflow interrupt; }
void loop() { long timeframe; timeframe=0;
//All int i; for (i=0;i<NUMERO_CANALI;i++){ sbi (PORTB,0); wait(ch[i]); wait(pausa); timeframe=timeframe+ch[i]+Pausa; } //Completa il frame 20ms digitalWrite(PPMPin, HIGH); delayMicroseconds(Durata -timeframe); digitalWrite(PPMPin, LOW);
delayMicroseconds(Pausa); }
void wait(int tempo){ TCNT1 = 65536- tempo * 16; sbi(TCCR1B,CS10); attesa = 1; while (attesa == 1); } No, la pausa tra canale e canale è sempre uguale, è il segnale di start che va a completare la lunghezza del pacchetto.
|
|
|
|
« Last Edit: September 22, 2012, 06:36:38 pm by Stefanoxjx »
|
Logged
|
|
|
|
|
|