Kip wrote: ↑Mon Jun 03, 2019 11:02 am
I guess I could use Timer.OC_TOGGLE and half the period to get the same frequency. Then set the counters to 0 each loop (Thanks for that tip btw)
I'm not quite sure I follow sorry...
However, that thread gave me a possible idea of how you could solve both problems (synchronising the ADC loop with the PWM, _and_ ensuring that you don't start sampling until the pulse goes high).
First of all, synchronising the two timers. Extending @dhyland's example at
https://github.com/dhylands/upy-example ... r/gated.py
Code: Select all
import pyb
import stm
# Using TIM2 and TIM5 because they correspond to AFs on X1 (TIM2 CH1) & X2 (TIM5 CH2)
# Choose prescaler/period values make sense for sample rate etc.
tim2 = pyb.Timer(2, prescaler=209, period=19) # 20 kHz for ADC
ch2_1 = tim2.channel(1, pyb.Timer.IC, pin=pyb.Pin.board.X1, polarity=pyb.Timer.FALLING)
tim5 = pyb.Timer(5, prescaler=209, period=399) # 1 kHz for PWM
ch5_2 = tim5.channel(2, pyb.Timer.IC, pin=pyb.Pin.board.X2, polarity=pyb.Timer.FALLING)
# Additional config to make TIM2 gated on TI1 (i.e. channel 1)
smcr = stm.mem16[stm.TIM2 + stm.TIM_SMCR]
smcr &= 0b1111111110001000
smcr |= 0b0000000001010101 # TS = 101 (TI1), SMS = 101 (Gated)
stm.mem16[stm.TIM2 + stm.TIM_SMCR] = smcr
# Additional config to make TIM5 gated on TI2 (i.e. channel 2)
smcr = stm.mem16[stm.TIM5 + stm.TIM_SMCR]
smcr &= 0b1111111110001000
smcr |= 0b0000000001100101 # TS = 110 (TI2), SMS = 101 (Gated)
stm.mem16[stm.TIM5 + stm.TIM_SMCR] = smcr
So at this point if were to wire up X1 and X2 to X17 (the user switch) then the timers would only run when the button is held down.
Then enable the PWM output on TIM5's other channel (e.g. channel 3, available on X3), so it will be syncronized to the ADC.
Code: Select all
ch5_3 = tim5.channel(3, pyb.Timer.PWM, pin=pyb.Pin.board.X3, polarity=pyb.Timer.FALLING)
ch5_3.pulse_width(200) # 50% duty cycle
Then reset both the (stopped) timers. Note I've chosen to set it to the end of the range because setting it to zero will turn X3 on. (But you could change your PWM config to match, or even play with these values to introduce a phase delay).
Code: Select all
tim2.counter(19)
tim5.counter(399)
So now you have two timers at different integer-multiple frequencies, synchronised on the USER button. If you call adc.read_timed, it will block until you press the button because the timers aren't running. So your very first sample will be exactly when the PWM goes high, and N samples to follow at 20x the sample rate of the PWM.
Code: Select all
>>> adc = pyb.ADC(pyb.Pin.board.X4)
>>> buf = bytearray(20)
>>> adc.read_timed(buf, tim2) # Blocks on the USER button
Exercise for the reader: set up a third timer on another pin that replaces the USER button.
Also I'm sure this can be done without having to externally connect the pins (i.e. you can probably route the timer channels internally) but this is as far as I've tested.
(Thanks for the excuse to dive into this
)
(Note: it's worth reading the implementation of read_timed just to understand exactly what it gives you. In particular note that you will still have jitter due to interrupts, and there is a very small (but constant) phase delay between the detection of the timer trigger and sampling the ADC).