Digispark as multiple servo driver.

Arduino projects on the go
MaxZ
Posts: 130
Joined: 31 Jan 2019, 11:48
Location: Boskoop, Netherlands

Re: Digispark as multiple servo driver.

Post by MaxZ » 06 Aug 2019, 17:50

Thanks Martin,

I have to study that, but I think the result is more or less the same to what I did. I stuffed four readings in an array, and then update those on a rotational basis with the latest sample. After that I run a for-loop summing the contents of the array and divide that by 4:

Code: Select all

// Servo signal reverser by Max Zuijdendorp, based on
// 50:50 Vtail mixer for ATTiny85 such as the 16mhz DigiSpark board - Phil_G
// august 2019
#define Rxch 2 // receiver channel connected to P0 (ATTiny physical pin 5)
#define servo1 3    // servo 1 connected to P1 ATTiny physical pin 6
#define servo2 3    // servo 2 connected to P2 ATTiny physical pin 7
const int midpulse = 1500; // midpulse = pulse at zero trim
const int avg = 4; // number of pulses in running average
volatile unsigned long timer_mark; // all timer variables are unsigned long
volatile int Rx_pulse = midpulse;
int inpulse = midpulse;
int oldpulse[avg], x; // previous pulses array and increment variable
int servopulse1 = midpulse, servopulse2 = midpulse;
volatile byte sync = 0;

void setup()
{
  pinMode(servo1, OUTPUT);
  pinMode(servo2, OUTPUT);
  pinMode(Rxch, INPUT);

  for (int i=0;i<avg;i++) oldpulse[i] = midpulse; // set initial values for previous pulses array
  x=0;

  timer_mark = 0;
  GIMSK = (1 << PCIE);   // Enable Pin Change Interrupts
  PCMSK = (1 << Rxch); // Mask interrupts for Rx channel input only, use bitwise-OR for multiple inputs
  interrupts(); // enable interrupts
}

void loop()
{
  while (sync == 0); // wait until new pulse info arrives (output timing set by Rx pulse timing)
  sync = 0;
  delay(6);  // do the output pulses mid-frame, reduces jitter...

  noInterrupts(); // disable interrupts
  inpulse = Rx_pulse; // do an atomic copy in the quickest way
  interrupts(); // enable interrupts
  // then do arithmetic on the copies...
  
  // replace previous pulses with latest pulse on a rotational basis
  oldpulse[x] = inpulse;
  x += 1;
  if (x==avg) x=0;

  inpulse = 0;
  for (int i=0;i<avg;i++) inpulse += oldpulse[i]; // totalize previous pulses
  servopulse1 = inpulse / avg;  // output pulse for servo 1 is averaged previous pulses
  servopulse2 = midpulse*2 - servopulse1;
  constrain(servopulse1, 850, 2150);
  constrain(servopulse2, 850, 2150);
  digitalWrite(servo1, 1); delayMicroseconds(servopulse1); digitalWrite(servo1, 0); // servo 1 out = Rx out
  digitalWrite(servo2, 1); delayMicroseconds(servopulse2); digitalWrite(servo2, 0); // servo 2 out = Rx out, reversed
}

ISR(PCINT0_vect)
{ // Interrupt on Rx pin state change
  if (PINB & (1 << Rxch)) { // Check if input pin has changed state 0 > 1
     timer_mark = micros();
  }
  else { // state change must be 1 > 0
     Rx_pulse = ((volatile int)micros() - timer_mark);
     sync = 1; // triggers output loop
  }
}
So, should I conclude that there are no further tricks that will reduce the jitter in the first place?
Cheers,
Max.

Martin
Posts: 296
Joined: 16 Feb 2018, 14:11
Location: Warwickshire

Re: Digispark as multiple servo driver.

Post by Martin » 06 Aug 2019, 18:42

Yes the rotating buffer should work just as fast. There are advantages both ways, but the main advantage of the method I outlined is that you don't need a buffer - only a total. So if you had a very noisy signal, didn't need a fast output and wanted to average say the last 1000 readings, you could do that without needing a 1000-element array. You'd probably choose 1024 rather than 1000 to allow the quick divide, and then you'd just need a total variable 'wide' enough to cope with up to 1024 times the highest possible reading.

Strictly the method doesn't average over the number (like 1024) of samples and actually takes more samples than that to reach a new steady state after a step change of the input value. The output follows a sort of exponential curve with the limit at the new steady input value - so the output changes faster to begin with and then changes slower and slower as it approaches the new level. Your rotary buffer method produces a straight line ramp response to a step input - and achieves the new stable value exactly after one buffer rotation.

MaxZ
Posts: 130
Joined: 31 Jan 2019, 11:48
Location: Boskoop, Netherlands

Re: Digispark as multiple servo driver.

Post by MaxZ » 06 Aug 2019, 19:19

Interesting stuff this, thanks guys. The trick will be to remember it when I need it :roll: , difficult for me as an occasional coder :?

Max.

Post Reply