Output in PPMsum, è possibile?

astrobeed:
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.

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);
}

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 :slight_smile: ), 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);
}

astrobeed:
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 :smiley:
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.

lesto:
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 :slight_smile: ), 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.