Good evening, (I have a few questions which are at the bottom, so I hope you don't mind me having posted the full length of these two programs first)
I've managed to produce some great results in developing PPM (pulse position modulation; related to PWM pulse width modulation) for a head tracker project I'm working on. I've included two fundamentally different programs as described below, which I'm testing using my Nano 33 BLE via the Arduino development environment...
Method 1: Servo PPM using 2 timers (4 channels)
This method produces the best results (although method 2 does indeed work quite well). The servos move virtually perfectly smooth almost 100% of the time. Timer 3 CC[0] sets the initial high value of the waveform, followed by CC[1] to [4] for the 4 channels. CC[5] is used for the PPM frame length which is set to 22500 microseconds. Each timer 3 compare activates timer 4 which in turn controls the pulse length.
The GPIOTE tasks are controlled by timer events via PPI channels. One complete frame length is processed by timer 3 independent of timer 3 IRQ handler. After the frame is complete, timer 3 stops and clears, then the new servo values are transferred, and the timer is started again. The delay until the next frame starts is dependent on timer 3 prescaler which is set to 1MHz, which could be increased, and the PPM channel values scaled accordingly.
Note: I've omitted all Timer "_Pos" instructions except for TIMER_INTENSET (for GPIOTE I decided to just leave them all in place). _Pos is required for INTENSET even though it has only one field in its register (note "Write 1 to enable..." in the description. Without _Pos it doesn't work). Although SHORTS has 2 fields in its register, it doesn't need _Pos because I'm using its 1st field.
Method 2: Servo PPM using 1 timer (8 channels)
Unlike method 1, the entire PPM waveform is constructed inside the timer handler and therefore is dependent on its timing accuracy/consistency. You can have as many channels as PPM supports, even though only one timer is being used. I've added a demo option which requires setting the "demo_multiplier" value and "sigPin" to an LED.
Synchronisation seems like a key point. For example, by considering method 1, it's clear that all 4 channel values are transferred whilst the timer is stopped. However for this single timer method, the channel values are transferred without stopping the timer. **Refer to the 5th post for clarification about this**
Here are my questions
- When are timer CC values updated if the transfer is initiated whilst the timer is running?
- Commented in timer 3 handler for method 1 is "dummy" from here Nordic Timer Example. I thought that "NRF_TIMER3->EVENTS_COMPARE[n] = 0" took care of that, so please kindly explain to me what the two dummy lines do?
- How important is __WFE() and what does it do? (I have it commented, and everything seems fine)
- With respect to the findings as detailed in my "Note:" near the beginning of this post in relation to INTENSET for Timer, please explain why _Pos is required even though INTENSET's register only has one field?
- Please advise of any improvements for either method? Although both work well, method 1 as I already said is very smooth but limited to 4 channels via two timers, unless for example reusing the CC values by using a timer handler via EVENTS_COMPARE[4] to stop it, transfer the next set of PPM values, and restart again. Like it's already doing but two stages instead of one, but that would of course introduce a timer handler interruption part way in constructing the waveform.