Repost: Mogplus8 PPM generator

Arduino projects on the go
Post Reply
User avatar
Phil_G
Posts: 601
Joined: 15 Feb 2018, 23:32
Contact:

Repost: Mogplus8 PPM generator

Post by Phil_G »

Heres a new idea for generating interrupt-driven PPM, from an RCG member:
https://www.rcgroups.com/forums/showthr ... st38439988

I've given it a try, and it is good - very stable, but for the moment I'm still favouring Hasi, Mogplus is forced by hardware to D9 or D10 only, with no option of using the more 'convenient' pins like we do on the DIY-More board. Its very small & neat code though.
The code as shown gives negative-going PPM (standing 1 pulsing to 0)
Studied the datasheet and if positive-going PPM is required, all thats needed is to set the COM1B0 bit of TCCR1A:

ie change this line:
TCCR1A |= (1 << COM1B1); // Toggle on B compare

to this:
TCCR1A |= (1 << COM1B1) | (1 << COM1B0); // Toggle on B compare, inverting mode

this gives a standing 0v pulsing to 5v
IMG_20171025_220401.jpg


Re: Mogplus8 PPM generator
Postby FBMinis » Thu Oct 26, 2017 4:04 pm


Thank you for your work, Phil. I'm following both topics.

Re: Mogplus8 PPM generator
Postby Phil_G » Thu Oct 26, 2017 4:20 pm


Hi Francesco, thanks but its not my work, its a clever guy in Australia :D


Re: Mogplus8 PPM generator
Postby Phil_G » Fri Nov 03, 2017 5:34 pm


I've been chatting at length with Ian (Mogplus8) and he came up with a slight variation on the idea which is simply brilliant.
The ISR that generates the PPM is now only two lines, the 300uS pulse is preset just once in setup, and within the encoder there is no need to take any account of the 300uS pulse width, channels are literally set to channel width (actually twice the width, and 600 for the pulse).
In a nutshell timer1 is set for "fast pwm" mode, its count ramping up from 0 to [2x channel width] at 0.5uS per step As the count passes 600 (thats 300uS), the PPM line (D10) is set. When the count terminates at [2x channel width] the PPM line is reset.
Its the most logical, neatest idea I've seen yet. The channels are stored as doubled timings in elements 0 to 7 of the array, element 8 is the sync time which of course is the 20ms frametime minus all the current channel widths, then doubled. The resulting PPM is perfectly timed, smooth, glitch-free and totally independent of your encoder loop.
Brilliant work, thanks Ian!

Cheers
Phil
generatePPMworks2.ino
(1.27 KiB) Downloaded 191 times
Re: Mogplus8 PPM generator
Postby ceptimus » Fri Nov 03, 2017 6:15 pm


That does look neat and simple. I think you're fixed with using Pin 10 for the output though - which of course shouldn't normally be a problem.

One thing to watch is that as the channel values array consists of 16-bit values, you should use an 'atomic' update when your main sketch changes them - otherwise an interrupt might occur when 8 bits of the value have been changed but not the other 8 bits which could cause a momentary glitch in the pulse position of up to 128 uS.

I'm not sure if declaring the values volatile forces the Arduino compiler to do an atomic update on the array values - if it doesn't then you just have to put cli(); and sei(); (or noInterrupts(); interrupts(); which is the same thing) around the update instruction so that an interrupt can't occur mid-update. Normally you would calculate the new value in a scratch variable so that the interrupts are switched off for the shortest time possible - like this:

int scratch = [calculation of new value for ch];
noInterrupts():
channelMap[ch] = scratch;
interrupts();

The only other issue - and it's trivial really - is that whenever your sketch modifies a channel value, it should also make a corresponding change to channelMap[8] so as to keep the whole frame length constant. But because your main sketch (probably) won't be synchronized with the frame output your sketch won't know whether or not the pulse for the currently-being-modified channel has started or not, so to keep the frame lengths identical it should sometimes change channelMap[8] straight away and sometimes change it only when the next frame begins.

Most all receivers and transmitter modules don't take a blind bit of notice of the frame length so this second point doesn't really matter - in fact you could just leave channelMap[8] set at some default value and accept that the frame might (worst case) get a few milliseconds longer or shorter when all the channels are changed at the same time.

If you want to keep the frame length rock-steady too, or you want to use a different output to Pin 10, you could use the code from my Reedy sketch: that keeps track of each channel pulse width as it sends them and pads the final delay at the end of the frame to match. The drawback is that the code is more complicated and takes up a fraction more program space.

Re: Mogplus8 PPM generator
Postby Phil_G » Fri Nov 03, 2017 8:32 pm

Thanks Martin, I've not played with timers much so this was an interesting experiment for me.
The Reeduino uses Hasi interrupt-driven PPM which works perfectly but I just liked the simplicity & economy of Ians code.I drew a diagram to help me understand how the timer was configured, for clarity it ignores the 'double values with 0.5uS clock' thing.
ppm.jpg
An easier way to avoid interrupts during variable updates is to set channelMap[0 to 7] during the sync, ie when ch==8 :D
Another even easier way might be to set each channelMap[] element inside the ISR:
ISR(TIMER1_OVF_vect) {
OCR1A = channelMap[ch];
channelMap[ch]=(whatevervalue for next frame)
ch = ( ch++ > 8) ? 0 : ch;
}

Re frame timing, totally agree, some old radios were fixed rate and some were fixed sync, but the modules really dont mind either Image
Phil_G wrote:...element 8 is the sync time which of course is the 20ms frametime minus all the current channel widths, then doubled

Image

As an Arduino noob, one thing I'm not sure of, with timer1 fully occupied, what 'Arduino' delays are to be avoided if any?

Cheers
Phil

Re: Mogplus8 PPM generator
Postby ceptimus » Fri Nov 03, 2017 9:33 pm

Interrupts are tricky with 8-bit processors and 16-bit values so that method you mentioned of updating the ch[] values inside the interrupt is still prone to glitches if the interrupt happens when the processor is midway through updating an element of the whateverValueForNextFrame[] array! ;)

The only way to avoid all glitches is to make the 16-bit modify instructions atomic (disable interrupts while they're happening) or somehow structure your code in such a way that it's synchronized with the interrupts so you know the interrupt will never happen mid-update.

As I mentioned, the compiler might be clever enough to think, "This variable is declared volatile, so I'll automatically compile atomic updates outside of interrupt routines" - but I've not checked whether it actually does that so I normally disable and re-enable interrupts myself for belt and braces. :)

Timer 1 isn't used by most of the standard Arduino stuff - delay, millis() and so on all use Timer 0, so you're safe to use it in this way. Timer 1 is used for analogWrite() on pins 9 and 10 (analogWrite is good for dimming LEDs and similar but, of course, it's not a true analogue output - it uses PWM). Timer 1 is also used by the Servo library if and when you use that - though of course the Servo library is designed just to put out one PWM signal on one of several pins so that each pin controls one servo - that Servo library doesn't have the functionality to drive a multi-channel PPM signal on a single pin.

So providing you don't want to analogWrite() to Pins 9 or 10 or use the servo library, you have no worries using Timer 1 however you wish.


Re: Mogplus8 PPM generator
Postby Phil_G » Fri Nov 03, 2017 10:31 pm


Ta v much Martin Image
Presumably, as with a PIC, interrupts flagged whilst disabled are still serviced when re-enabled,
ie the flag remains until the isr services it, or until its cleared manually?

Re: Mogplus8 PPM generator
Postby ceptimus » Sat Nov 04, 2017 12:51 am


Yes the interrupt still happens as soon as interrupts are re-enabled. If you just switch interrupts off while you copy one 16-bit variable to another it only delays the interrupt by about half a microsecond maximum - so it doesn't really affect the accuracy of the PPM signal.

That's why you should pre-calculate the value before disabling interrupts - you don't want to stop interrupts then do a whole lot of maths to work out a new value before allowing interrupts again. For the same reason don't alter all the Ch[] values together in one interrupt disabled section - do them one at a time in a loop so that the interrupt gets a chance to happen between each update. Fastest way to copy the value to the Ch array is to use a pointer - that way the array indexing calculation can be done before the interrupts are turned off. Instead of Ch[] = value; you just have *p = value; inside the interrupt disabled section.


Re: Mogplus8 PPM generator
Postby Mike_K » Sat Nov 18, 2017 3:17 pm

Sorry to jump unannounced into the middle of a thread, but I have been using a similar PWM output for the PPM output on my Arduino project, using Pin 9. I couldn’t use Pin10 as it was already in use for a LCD/OLED display with SPI interface. As soon as you use hardware SPI, Pin10 becomes the CS (chip select) and it overrides whatever you have set it for, including PWM.

I have cut and pasted the code I’m using into the program Phil posted earlier to demonstrate what to do, but in essence each timer used for PWM can use one of two output pins (9 and 10 for timer1). Which output is used is determined by which settings you use. To use pin 9 you use ICR1 instead of OCR1A to set the pulse length. I’ve also set all the bits in the TCCR1A and TCCR1B registers in one go with a hex value and not individually. I’ve also left the other functions (commented out) that my program use, all the joystick ADC and calculations are done within the ISR as it was the only way I could figure to ensure that each frame had up-to-date joystick positions. As a secondary benefit I found that it made the ADC readings more stable as they didn't get interrupted part way through by the PWM interrupt. Effectively the main loop runs the display and other non time critical things like the up and down timers and battery voltage and the ISR does all the adc, calculations and ppm in the background.

It may be of interest to somebody as it was implied earlier in the post that you can only use pin 10 and it may have stopped somebody trying the PWM method if they were already using pin10 and/or SPI in their project.

Unfortunately I can't recover Mike's sketch 'generatePPM_fastPWM_pin9.ino' please could Mike or anyone upload it?
Post Reply