Menu

Asynchronous ServoIn -> algo -> Synchronous ServoOut

Features
Git Lang
2015-08-18
2019-02-25
  • Git Lang

    Git Lang - 2015-08-18

    Hi,

    This library looks great, thanks. I think I understand ServoIn and ServoOut examples ok, but I don'y know how to tie the two together, ie get a value with ServoIn and simply echo out to ServoOut. Ideally, I only need one channel, but I want to grab an input value when it changes, half the period, then output it with ServoOut at a fixed frequency. So, the output just runs at a fixed frequency and the output pulse width changes whenever the input pulse wodth changes, but at half the width. You huessed, I want to make a 1520uS to 760uS adapter :). Any hints appreciated, thanks.

    G

     
    • Anonymous

      Anonymous - 2015-08-19

      Hi Git,

      The first parameter passed to the constructor of ServoIn is an array which will contain the incoming signals in microseconds. The values in this array will be updated every time you call the update function. (see the ServoIn example)
      The second parameter of the ServoOut constructor is an array that contains the outgoing signals in microseconds. You'll need to fill this array with (modified) values based on the array used by ServoIn. After that, call the update function on ServoOut.

      You'll need a few arrays:

      uint16_t g_valuesIn[SERVOS];
      uint16_t g_valuesOut[SERVOS];
      uint8_t  g_workIn[SERVOIN_WORK_SIZE(SERVOS)];
      uint8_t  g_workOut[SERVOOUT_WORK_SIZE(SERVOS)]; 
      uint8_t  g_pinsOut[SERVOS] = {2, 3, 4, 5};
      

      call the constructors with the right parameters

      rc::ServoIn g_ServoIn(g_valuesIn, g_workIn, SERVOS);
      rc::ServoOut g_ServoOut(g_pinsOut, g_valuesOut, g_workOut, SERVOS);
      

      Initialize everything in the setup function (set up timer, interrupts pin modes and start both g_servoIn and g_servoOut.

      And your loop function will look something like this:

      void loop() {
          g_servoIn.update();
          for ( uint8_t i = 0; i < SERVOS; ++i )
              g_valuesOut[i] = g_valuesIn[i]; // perform extra processing here
          g_servoOut.update();
      }
      

      Don't forget to copy the ISR function from the servoIn example or the code won't register incoming signals.

      Good luck!

       
  • Git Lang

    Git Lang - 2015-08-19

    That's great, many thanks for such a quick and comprehensive reply. I assume the output does run at some fairly constant frquency, am I right in thinking I need to call ServoOut::setPauseLength() in setup() to set the frequency?

    G

     
    • dvdouden

      dvdouden - 2015-08-19

      I think the default length between two pulses (pause length) is 10000 microseconds by default, so that means 100 pulses per second on each output pin. Set it to 20000 to get 50 Hz or 5000 to get 200 Hz for example.

       
  • Git Lang

    Git Lang - 2015-08-19

    Thanks for that. I've started with a straight output = input test, but I'm getting no output at all. Is the library OK on a Nano or are some Nano pins different?. Thanks.

    G

    #include <ServoIn.h>
    #include <Timer1.h>
    #include <ServoOut.h>
    
    #define SERVOS 4
    
    uint16_t g_valuesIn[SERVOS];
    uint16_t g_valuesOut[SERVOS];
    uint8_t  g_workIn[SERVOIN_WORK_SIZE(SERVOS)];
    uint8_t  g_workOut[SERVOOUT_WORK_SIZE(SERVOS)]; 
    uint8_t  g_pinsOut[SERVOS] = {2, 3, 4, 5};
    uint8_t  g_pinsIn[SERVOS] = {8, 9, 10, 11};
    
    static uint8_t lastB = 0;
    
    rc::ServoIn g_ServoIn(g_valuesIn, g_workIn, SERVOS);
    rc::ServoOut g_ServoOut(g_pinsOut, g_valuesOut, g_workOut, SERVOS);
    
    void setup() 
    {
       // Initialize timer1, this is required for all features 
       // that use Timer1 (PPMIn/PPMOut/ServoIn/ServoOut)
       rc::Timer1::init();
    
       for (uint8_t i = 0;  i < SERVOS; ++i)
       {
          pinMode(g_pinsIn[i], INPUT);        // set up input pins    
          pinMode(g_pinsOut[i], OUTPUT);      // set up output pins
          digitalWrite(g_pinsOut[i], LOW);    // put them low
       }
    
       // only allow pin change interrupts 
       // for PB0-3 (digital pins 8-11)
       PCMSK0 = (1 << PCINT0) | (1 << PCINT1) | (1 << PCINT2) | (1 << PCINT3);
    
       // enable pin change interrupt 0
       PCICR = (1 << PCIE0);
    
       g_ServoIn.start();  
       g_ServoOut.start();   
    
    }
    
    void loop() 
    {
       // update incoming values
       g_ServoIn.update();
    
       // Half the incoming pulse width, ie 1000..2000 becomes 500..1000
       // We have 760uS at 200Hz. OK for tail servos.
       for ( uint8_t i = 0; i < SERVOS; ++i )
            g_valuesOut[i] = g_valuesIn[i];    
    
       // tell ServoOut there are new values available in the input buffer
       g_ServoOut.update();
    }
    
    // Pin change port 0 interrupt
    ISR(PCINT0_vect)
    {
       // we need to call the ServoIn ISR here, keep code in the ISR to a minimum!
       uint8_t newB = PINB;
       uint8_t chgB = newB ^ lastB; // bitwise XOR will set all bits that have changed
       lastB = newB;
    
       // has any of the pins changed?
       if (chgB)
       {
          // find out which pin has changed
          if (chgB & _BV(0))
          {
             g_ServoIn.pinChanged(0, newB & _BV(0));
          }
          if (chgB & _BV(1))
          {
             g_ServoIn.pinChanged(1, newB & _BV(1));
          }
          if (chgB & _BV(2))
          {
             g_ServoIn.pinChanged(2, newB & _BV(2));
          }
          if (chgB & _BV(3))
          {
             g_ServoIn.pinChanged(3, newB & _BV(3));
          }
       }
    }
    
     
    • dvdouden

      dvdouden - 2015-08-19

      The library was written for and tested on both Uno and Nano (Atmega 168/328). I'd start with putting a few Serial.println here and there, see if you get some input (set the serial speed to as high as possible to reduce the risk of messed up interrupts). Then check the output pins with either a scope or some LEDs (with current limiting resistor of course) to see if you get any output at all. This kind of problem can be hard to test, the best way is to start at the input stage and work your way to the output stage.

       
  • Git Lang

    Git Lang - 2015-08-19

    Yes, I should have said that Serial debugging showed no output. The cause of that was reading the nano pinout upside down and wiring the input and out reversed :). Having corrected that, I now have input, varying correctly as I vary the input pulse width, but I do not have output. I'm setting g_valuesOut[0,1,2,3] to g_valuesIn[0,1,2,3], calling g_ServoOut.update() in the loop, and getting no output. Serial print of input value, output value before out update, and output value after out update all show the same correct value, but the physical outputs get nothing, it just sits near ground voltage. Am I ok to put serial debug writes amongst ServoOut.cpp please ?

    Output pins 2, 3, 4, 5 are port D on the Nano and input pins 8,9,10,11 are port B. Is that definitely ok please?.

    G

     
  • Git Lang

    Git Lang - 2015-08-19

    AHA!.

    g_ServoOut.update();
    should be :
    g_ServoOut.update(true);

    Working ok now, thanks for all your help. I'll posts the final code when it's done.

    G

     
  • dvdouden

    dvdouden - 2015-08-19

    That's weird, update(true) only needs to be called when you've changed the pins. It recalculates the port and bit for each pin. Oh well, if it works... :)

     
  • Git Lang

    Git Lang - 2015-08-21

    Yes, it works, but the servo is very noisy and has a lot of jitter. There's definitely a fait bit more jitter at the output of nano than at the input, so something still isn't right. I did the updat(true) because I could see it was used after the pins changed. In my naiveity, I thought it was the input pins but it actually refers to the output pins, so I have no idea what is going on. Debugging inside interrupts sucks :(

    G

     
    • dvdouden

      dvdouden - 2015-08-21

      The jitter may be due to multiple interrupts interfering with each other. Should the timer1 interrupt (which is used by ServoOut) occur while a different interrupt (pin change (used by ServoIn), or timer 0 (used for micro/millisecond timing by some of the standard Arduino functions)) is being handled, the timer 1 interrupt will be queued and handled at a later time, messing up the ServoOut timing.
      The timer 0 overflow happens nearly 1000 times per second, the pin change interrupt depends on the frequency of your input signal and number of pins you're monitoring.
      You could try disabling Timer0 interrupts (TIMSK0 = 0;), see if that makes the output a bit more stable. This breaks delay(), delayMicros() and other time related functions though. ServoIn and ServoOut use Timer 1 so that should remain functioning.
      Another thing you can try is outputting the values of ServoIn to the console (and not using ServoOut) to see if it's reading stable values. Finally you could try using ServoOut with constant values (and disable ServoIn) to see if that gives any jitter. This may help you pinpoint the cause of the jitter.

      And yes, debugging interrupts sucks, but still easier than debugging an ATtiny without serial output ;)

       

      Last edit: dvdouden 2015-08-21
  • Anonymous

    Anonymous - 2016-05-14

    Hi! =)

    I've been implementing a pwm mixer using ServoIn and ServoOut in the last day.
    I did run exactly at the same problem of noisy due to (supposedly) manny interrupts.

    I'm currently reading 10 channels, and writing 6. When I don't read anything, and just output 6 fixed values (set in the code) using the ServoOut I still get almost the same noise.

    I did desable Timer0 and Timer2, and also left the ISR(PCINT0_vect) empty (the one that handles 6 of the 10 channels, I implemented another one to handle PCINT2_vect), still got the same noisy.

    Have you guys figured this out?

    By the way, I only got the output working calling g_ServoOut.update(true) too.

    p.s.: Great work! The library is awesome! =)

     
    • dvdouden

      dvdouden - 2016-05-17

      Sorry, never got around to investigate this further :/

       
  • Anonymous

    Anonymous - 2019-02-21

    Hi,

    How can I get hold of your code of the Esky transmitter? I want to use the input ppm signal from the transmitter main pcb and send it with the nrf24l01 module to my Esky receiver.

    M

     
    • dvdouden

      dvdouden - 2019-02-25

      I don't seem to have that code anymore; looks like I lost it when my previous laptop died :(

       

Anonymous
Anonymous

Add attachments
Cancel





Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.