gordon77
Posts: 5689
Joined: Sun Aug 05, 2012 3:12 pm

Re: Evaluate the PWM signal

Wed Apr 07, 2021 6:18 pm

I assume you are getting what I mentioned as "You'll notice some jitter".

I get similar when measuring the speed of a motor, I'd like to know why.

is it the time.ticks_us() function ?
Last edited by gordon77 on Wed Apr 07, 2021 7:24 pm, edited 1 time in total.

Nitro_fpv
Posts: 89
Joined: Tue Mar 30, 2021 11:56 am
Location: Switzerland

Re: Evaluate the PWM signal

Wed Apr 07, 2021 7:00 pm

Yeah exactly, like you mentioned, is jitter.
In the print mudus you can also see it in the numbers:
However, the feeding signal is absolutely precise.
This is measured with a very precise analyzer.
I don't understand why !?

Code: Select all

1489
1508
1504
1482
1498
1503
1486
1499
1487
1495
1510
1497
1485
1480
1490
1505
1503
1501
1506
1484
1493
1482
1507
1491
1482

Analyzer: Logic Pro 8! (From where I work)
https://www.saleae.com/

horuable
Posts: 121
Joined: Sat Mar 06, 2021 12:35 am

Re: Evaluate the PWM signal

Wed Apr 07, 2021 7:29 pm

Have you considered using PWM input as a counter? It seems to work quite well. For a signal that my oscilloscope shows to be high for 2290 us PWM counter measures 2288.9 us and is very consistent.

My PWMCounter module is here. And the program I've used for testing:

Code: Select all

from machine import Pin, PWM, freq
from PWMCounter import PWMCounter

# setup pwm output for test
pwm  = PWM(Pin(15))
pwm.freq(200)
pwm_duty = 30000 # 10550 = 800uS, 30000 = 2200uS 
pwm.duty_u16(pwm_duty)

in_pin = Pin(13)
counter = PWMCounter(13, "GATED")
# Set divider to 16 to avoid rollover
counter.set_div(1 << 8)
counter.start()

last_state = 1
while True:
    if ~(x := in_pin.value()) & last_state:
        print((counter.read_and_reset()) / 125 * 16) # Time in us
    last_state = x

Nitro_fpv
Posts: 89
Joined: Tue Mar 30, 2021 11:56 am
Location: Switzerland

Re: Evaluate the PWM signal

Wed Apr 07, 2021 8:20 pm

horuable wrote:
Wed Apr 07, 2021 7:29 pm
Have you considered using PWM input as a counter? It seems to work quite well. For a signal that my oscilloscope shows to be high for 2290 us PWM counter measures 2288.9 us and is very consistent.

My PWMCounter module is here. And the program I've used for testing:
...................................................
But hello!

I adjusted the program a bit.
The values are absolutely noise-free, perfect.
Thank you very much!
Unfortunately, this program also stops if there is no signal or no valid signal.
Can you do something so that the program continues to run anyway?
The value then doesn't matter, it can be 0 or an exotic number.

Image

Code: Select all

from machine import Pin, PWM, freq
from PWMCounter import PWMCounter

pwm  = PWM(Pin(15))
pwm.freq(200)

def interpoliert(x, i_m, i_M, o_m, o_M):
    return max(min(o_M, (x - i_m) * (o_M - o_m) // (i_M - i_m) + o_m), o_m)

in_pin = Pin(17)
counter = PWMCounter(17, "GATED")
# Set divider to 16 to avoid rollover
counter.set_div(1 << 8)
counter.start()

last_state = 1
while True:
    if ~(x := in_pin.value()) & last_state:
        test = ((counter.read_and_reset()) / 125 * 16) # Time in us
        test = int(test)
        print(test)
    last_state = x
    pwm.duty_u16(interpoliert(test, 1000, 2000, 13100, 26500))

horuable
Posts: 121
Joined: Sat Mar 06, 2021 12:35 am

Re: Evaluate the PWM signal

Wed Apr 07, 2021 8:59 pm

I'm not sure what you mean. There's nothing in the code that could make the program stop with or without valid input. It's literally just an infinite loop without any blocking functions inside.

Nitro_fpv
Posts: 89
Joined: Tue Mar 30, 2021 11:56 am
Location: Switzerland

Re: Evaluate the PWM signal

Thu Apr 08, 2021 6:29 am

horuable wrote:
Wed Apr 07, 2021 8:59 pm
I'm not sure what you mean. There's nothing in the code that could make the program stop with or without valid input. It's literally just an infinite loop without any blocking functions inside.

Hello horuable

You are right, the program continues.
However, it is like this:
If no valid PWM signal is received, the program outputs the last valid PWM signal.
So I don't see any failure or failsave.
Here is a picture of it.
To recognize a failsave, however, I need a utobian value such as <500us or> 2500us.


Image

Here I interrupted the PWM signal, but the program continues to output the last valid PWM signal.
Unfortunately, I do not recognize a failsave.
Is there a possibility?

gordon77
Posts: 5689
Joined: Sun Aug 05, 2012 3:12 pm

Re: Evaluate the PWM signal

Thu Apr 08, 2021 4:23 pm

horuable wrote:
Wed Apr 07, 2021 7:29 pm
Have you considered using PWM input as a counter? It seems to work quite well. For a signal that my oscilloscope shows to be high for 2290 us PWM counter measures 2288.9 us and is very consistent.

My PWMCounter module is here. And the program I've used for testing:

Code: Select all

from machine import Pin, PWM, freq
from PWMCounter import PWMCounter

# setup pwm output for test
pwm  = PWM(Pin(15))
pwm.freq(200)
pwm_duty = 30000 # 10550 = 800uS, 30000 = 2200uS 
pwm.duty_u16(pwm_duty)

in_pin = Pin(13)
counter = PWMCounter(13, "GATED")
# Set divider to 16 to avoid rollover
counter.set_div(1 << 8)
counter.start()

last_state = 1
while True:
    if ~(x := in_pin.value()) & last_state:
        print((counter.read_and_reset()) / 125 * 16) # Time in us
    last_state = x
Could you give us more details on PWMCounter, eg GATED, FREE, RISING etc and how to use them ?

horuable
Posts: 121
Joined: Sat Mar 06, 2021 12:35 am

Re: Evaluate the PWM signal

Thu Apr 08, 2021 5:11 pm

I guess a simple timeout should be enough, much like @gordon77 did in his program.
I think something like this is what you want. Of course, you have to set timeout_period to be greater than the signal period. I have also added a timeout flag to avoid resetting the counter needlessly. On timeout test value is set to 0.

Code: Select all

from machine import Pin, PWM, freq
from PWMCounter import PWMCounter
from time import ticks_us, ticks_diff

pwm  = PWM(Pin(15))
pwm.freq(200)

def interpoliert(x, i_m, i_M, o_m, o_M):
    return max(min(o_M, (x - i_m) * (o_M - o_m) // (i_M - i_m) + o_m), o_m)

in_pin = Pin(17)
counter = PWMCounter(17, "GATED")
# Set divider to 16 to avoid rollover
counter.set_div(1 << 8)
counter.start()

test = 0
last_state = 0
last_update = ticks_us()
timeout_period = 20000
timeout = False
while True:
    if ~(x := in_pin.value()) & last_state:
        test = ((counter.read_and_reset()) / 125 * 16) # Time in us
        test = int(test)
        last_update = ticks_us()
        timeout = False
        print(test)
    if ticks_diff(ticks_us(), last_update) > timeout_period and timeout is not True:
        print("Timeout")
        timeout = True
        counter.reset()
        test = 0
    last_state = x
    pwm.duty_u16(interpoliert(test, 1000, 2000, 13100, 26500))

Nitro_fpv
Posts: 89
Joined: Tue Mar 30, 2021 11:56 am
Location: Switzerland

Re: Evaluate the PWM signal

Thu Apr 08, 2021 7:19 pm

horuable wrote:
Thu Apr 08, 2021 5:11 pm
I guess a simple timeout should be enough, much like @gordon77 did in his program.
I think something like this is what you want. Of course, you have to set timeout_period to be greater than the signal period. I have also added a timeout flag to avoid resetting the counter needlessly. On timeout test value is set to 0.
........
It works perfectly now.
With this output I can recognize a failsave.
Thanks a lot for this!

Image

Best regards

Nitro_fpv
Posts: 89
Joined: Tue Mar 30, 2021 11:56 am
Location: Switzerland

Re: Evaluate the PWM signal

Thu Apr 08, 2021 8:26 pm

My goodness

I have now put together the program.
The mapping works as it should, unfortunately the whole thing has a problem: The servo twitches briefly about every 1 second ?!
Now I wonder why?
Is there a timer biting itself?

Code:

Code: Select all

from machine import Pin, PWM, freq
from PWMCounter import PWMCounter
from time import ticks_us, ticks_diff

pwm  = PWM(Pin(15))
pwm.freq(200)

# mapping PWM in / PWM out
def interpoliert(x, i_m, i_M, o_m, o_M):
    return max(min(o_M, (x - i_m) * (o_M - o_m) // (i_M - i_m) + o_m), o_m)

#             __input___   __Output__
mapping = [                      1500  , # Failsafe - For any input value not in any range
            [  500, 1000,  1000, 1000 ],
            [ 1000, 1485,  1000, 1400 ],
            [ 1515, 2000,  1600, 2000 ],
            [ 2000, 2500,  2000, 2000 ],
          ]
          
def map(n, mapping):
    for idx in range(1, len(mapping)):
        inmin, inmax, outmin, outmax = mapping[idx]
        if n >= inmin and n <= inmax:
            i = (n - inmin) / (inmax - inmin)
            o = ((outmax - outmin) * i ) + outmin 
            return int(o)
    return mapping[0]

in_pin = Pin(17)
counter = PWMCounter(17, "GATED")
# Set divider to 16 to avoid rollover
counter.set_div(1 << 8)
counter.start()

test = 0
last_state = 0
last_update = ticks_us()
timeout_period = 20000
timeout = False
while True:
    if ~(x := in_pin.value()) & last_state:
        test = ((counter.read_and_reset()) / 125 * 16) # Time in us
        test = int(test)
        last_update = ticks_us()
        timeout = False
        #print(test)
    if ticks_diff(ticks_us(), last_update) > timeout_period and timeout is not True:
        print("Timeout")
        timeout = True
        counter.reset()
        test = 0
    last_state = x
    out = map(test, mapping)
    #print(test, out)
    pwm.duty_u16(interpoliert(out, 500, 2500, 6555, 32777))

horuable
Posts: 121
Joined: Sat Mar 06, 2021 12:35 am

Re: Evaluate the PWM signal

Thu Apr 08, 2021 11:07 pm

There's no obvious reason for this behaviour. The timer shouldn't be a problem, the whole point of using ticks_diff() rather than simple subtraction is avoiding the possibility of error due to timer overflow.
I guess your best bet is to hook up your analyzer and try to catch what happens with the signal when the servo twitches. That may help with further troubleshooting.

gordon77
Posts: 5689
Joined: Sun Aug 05, 2012 3:12 pm

Re: Evaluate the PWM signal

Fri Apr 09, 2021 8:50 am

horuable,

Could you help me with measuring the speed of a motor using your PWMCounter ?

I'm started a new thread viewtopic.php?f=28&t=309117

Nitro_fpv
Posts: 89
Joined: Tue Mar 30, 2021 11:56 am
Location: Switzerland

Re: Evaluate the PWM signal

Fri Apr 09, 2021 2:44 pm

I've been looking for the error for several hours.
With servo, without servo, with mapping, without mapping, etc.
Now I noticed the smallest extension in the main program (an additional print command, mapping, etc.) is sufficient for the PWM reading to start pulsing.

Here is a code, the print command is inactive, PWM output with mapping is active.
This is enough and the PWM signal starts to output errors every second, which is acknowledged with jerks in the servo.
If I use the other PWM output without mapping (see code) I have no jerks and the signal looks good.

Code: Select all

from machine import Pin, PWM, freq
from PWMCounter import PWMCounter
from time import ticks_us, ticks_diff

#             __input___   __Output__
mapping = [                      1500  , # Failsafe - For any input value not in any range
            [  500, 1000,  1000, 1000 ],
            [ 1000, 1485,  1000, 1400 ],
            [ 1515, 2000,  1600, 2000 ],
            [ 2000, 2500,  2000, 2000 ],
          ]
          
def map(n, mapping):
    for idx in range(1, len(mapping)):
        inmin, inmax, outmin, outmax = mapping[idx]
        if n >= inmin and n <= inmax:
            i = (n - inmin) / (inmax - inmin)
            o = ((outmax - outmin) * i ) + outmin 
            return int(o)
    return mapping[0]



pwm  = PWM(Pin(15))
pwm.freq(200)

def interpoliert(x, i_m, i_M, o_m, o_M):
    return max(min(o_M, (x - i_m) * (o_M - o_m) // (i_M - i_m) + o_m), o_m)

in_pin = Pin(17)
counter = PWMCounter(17, "GATED")
# Set divider to 16 to avoid rollover
counter.set_div(1 << 8)
counter.start()

test = 0
last_state = 0
last_update = ticks_us()
timeout_period = 2000000
timeout = False

while True:
    if ~(x := in_pin.value()) & last_state:
        test = ((counter.read_and_reset()) / 125 * 16) # Time in us
        test = int(test)
        last_update = ticks_us()
        timeout = False
        #print(test, map(test, mapping))
    if ticks_diff(ticks_us(), last_update) > timeout_period and timeout is not True:
        print("Timeout")
        timeout = True
        counter.reset()
        test = 0
    last_state = x
    #pwm.duty_u16(interpoliert(test,500, 2500, 6555, 32777 ))
    pwm.duty_u16(interpoliert(map(test, mapping),500, 2500, 6555, 32777 ))
Here are pictures of what the disorder looks like:
Channel 1 is the PWM input
Channel 0 is the PWM output

Image

Image

Can it be that the PWMCounter.py is very time-critical?

hippy
Posts: 9991
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: Evaluate the PWM signal

Fri Apr 09, 2021 6:53 pm

Nitro_fpv wrote:
Fri Apr 09, 2021 2:44 pm
If I use the other PWM output without mapping (see code) I have no jerks and the signal looks good.

Code: Select all

    pwm.duty_u16(interpoliert(map(test, mapping),500, 2500, 6555, 32777 ))
I am not sure what that's intended to achieve but it doesn't make sense to me to apply interpolation to a mapping function which provides the necessary interpolation.

Noting 'test = 0' in your code I can't help but suspect that is causing things to not be correct.

I'm also rather concerned that your lower 'channel 0' seems to have some rather odd voltages, as if there's a short to that pin somewhere.

horuable
Posts: 121
Joined: Sat Mar 06, 2021 12:35 am

Re: Evaluate the PWM signal

Fri Apr 09, 2021 7:20 pm

I have no idea what could cause that. Maybe someone else would be able to help you, sorry.
gordon77 wrote:Could you give us more details on PWMCounter, eg GATED, FREE, RISING etc and how to use them ?
Sorry for not responding earlier, I've somehow not seen your post before.

PWMCounter is a simple class to use hardware counting capabilities of PWM block of Pico. It can work in three modes (FREE shouldn't really be here):
  1. GATED - Count continuously when a high level is detected on the B pin
  2. EDGE_RISING - Count once with each rising edge detected on the B pin
  3. EDGE_FALLING - Count once with each falling edge detected on the B pin
(descriptions are taken from datasheet)
Full description can be found in datasheet here. Unfortunately I don't have any sort of documentation or examples as of yet. I'll try and do something about it over the weekend :)
hippy wrote:Noting 'test = 0' in your code I can't help but suspect that is causing things to not be correct.
That shouldn't be a problem, since it gets called only on timeout, which in this case occurs 2 s after the last falling edge is detected, so as long as there's a valid signal present it won't get called at all. And even if it happens duty gets set to 1500 us and it's not what is visible on traces.

gordon77
Posts: 5689
Joined: Sun Aug 05, 2012 3:12 pm

Re: Evaluate the PWM signal

Fri Apr 09, 2021 7:42 pm

Thank you.

Nitro_fpv
Posts: 89
Joined: Tue Mar 30, 2021 11:56 am
Location: Switzerland

Re: Evaluate the PWM signal

Sat Apr 10, 2021 12:51 am

hippy wrote:
Fri Apr 09, 2021 6:53 pm

I am not sure what that's intended to achieve but it doesn't make sense to me to apply interpolation to a mapping function which provides the necessary interpolation.
.......
That's exactly why I'm doing the project.
To add a dead zone to the PWM signal, 1485us - 1515us, starting with 1400us or 1600us after the dead zone.

Code: Select all

  	    [  500, 1000,  1000, 1000 ],
            [ 1000, 1485,  1000, 1400 ],
            [ 1515, 2000,  1600, 2000 ],
            [ 2000, 2500,  2000, 2000 ],
I don't know any other way I can do it differently.

hippy
Posts: 9991
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: Evaluate the PWM signal

Sat Apr 10, 2021 12:02 pm

Okay; I can see what you are doing - You are converting the raw 'test' value to an appropriate duty, thus have to convert the mapped 'test' value to determine the actual duty.

I can't explain why it doesn't work for the second, why what you see for the mapped version is so different to without it. It sill looks like a hardware issue to me from the traces.
Nitro_fpv wrote:
Fri Apr 09, 2021 2:44 pm
Now I noticed the smallest extension in the main program (an additional print command, mapping, etc.) is sufficient for the PWM reading to start pulsing.
Barring hardware issues it seem it could be four things, perhaps a combination -

Reading the input isn't working
Mapping the input to output isn't working
Converting the output value to duty isn't working
PWM pulse generation isn't as rock-solid as it needs to be

Nitro_fpv
Posts: 89
Joined: Tue Mar 30, 2021 11:56 am
Location: Switzerland

Re: Evaluate the PWM signal

Sat Apr 10, 2021 4:33 pm

I tested again for a couple of hours.

I wrote a code to test the modules in which all functions are activated except for PWM reading.
I simulate the PWM input with a counting loop.
It works perfectly without any interference.
I noticed that the PWM reading (PWM counter) is very time-critical as soon as something is additionally executed in the main program, for example the mapping function or the print output.
Can this be defused a bit, possibly in the PWM counter program?

I didn't think this project would be so difficult!

- PWM reading
- Add special dead zone
- Output PWM

Here is the test code:

Code: Select all

from machine import Pin, PWM

pwm  = PWM(Pin(15))
pwm.freq(200)

def interpoliert(x, i_m, i_M, o_m, o_M):
    return max(min(o_M, (x - i_m) * (o_M - o_m) // (i_M - i_m) + o_m), o_m)

#             __input___   __Output__
mapping = [                      1500  , # Failsafe - For any input value not in any range
            [  500, 1000,  1000, 1000 ],
            [ 1000, 1470,  1000, 1400 ],
            [ 1530, 2000,  1600, 2000 ],
            [ 2000, 2500,  2000, 2000 ],
          ]
          
def map(n, mapping):
    for inmin, inmax, outmin, outmax in mapping[1:]:
        if n >= inmin and n <= inmax:
            i = (n - inmin) / (inmax - inmin)
            o = ((outmax - outmin) * i ) + outmin 
            return int(o)
    return mapping[0]

while True:
    for n in range(200, 2700, 3):
        print("{:>4} -> {}".format(n, map(n, mapping)))
        pwm.duty_u16(interpoliert(map(n, mapping),500, 2500, 6555, 32777 ))
    for n in range(2700, 200, -3):
        print("{:>4} -> {}".format(n, map(n, mapping)))
        pwm.duty_u16(interpoliert(map(n, mapping),500, 2500, 6555, 32777 ))

hippy
Posts: 9991
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: Evaluate the PWM signal

Sat Apr 10, 2021 7:53 pm

I am not overly familiar with PWM as a Gated Counter on the RP2040 but the theory is simple enough; provide a clock to the counter at some rate, it counts when the gating signal is asserted and, once it isn't, the count it reached tells you how long it was asserted for. For example a 1MHz clock will give 1us timing from the count value read.

The practice is easy enough on a PICmicro when I have done that, but the RP2040 doesn't seem to have the 'synchronise gating to start of gate assertion' nor a 'one-shot mode' so it only counts a single pulse which makes it a doddle on a PICmicro.

The same can be implemented by ensuring the gate isn't asserted before zeroing and enabling the counter - best done after a 'no longer asserted' edge of the gate, and reading the counted value on the next 'no longer asserted' edge.

That should work and very accurately. And should be easy enough to implement on a slow signal like from a servo, a 1-2ms pulse every 20ms or so - it may also be the solution for gordon77's challenge elsewhere.

I presume that's what 'the PWMcounter module from 'horuable' implements. I don't want to criticise that code because I haven't studied it but if there is an unintended flaw in synchronisation it can throw things off quite badly. Without hardware 'gate assertion synchronisation' one has to ensure the count is zeroed before the gate is asserted, without a hardware 'one-shot' one has to ensure the count is read before a further gate is asserted,

A 'wait for deasserted, read the count and reset it to zero, ready for the next' should work, as should an interrupt on deassertion.

Having not used Gated Counters on the RP2040 I have put that on the 'to do' list and will report back.

horuable
Posts: 121
Joined: Sat Mar 06, 2021 12:35 am

Re: Evaluate the PWM signal

Sat Apr 10, 2021 9:30 pm

PWMCounter is just a simple class that has a few methods for setting up counters and (reading their values) using PWM slices on Pico. There's really nothing that can go wrong here in terms of counting since that's done by hardware.
hippy wrote:theory is simple enough; provide a clock to the counter at some rate, it counts when the gating signal is asserted and, once it isn't, the count it reached tells you how long it was asserted for. For example a 1MHz clock will give 1us timing from the count value read..
You're absolutely right, that's exactly how "Gated" mode works. It's supposed to be used to measure pulse width or duty cycle.
hippy wrote:RP2040 doesn't seem to have the 'synchronise gating to start of gate assertion' nor a 'one-shot mode'
Yup, no such thing on the Pico, which is a bit of a shame really as it would be very helpful here.
hippy wrote:A 'wait for deasserted, read the count and reset it to zero, ready for the next' should work, as should an interrupt on deassertion.
Since there's no hardware synchronization I've tried to achieve that in the main loop by waiting for the falling edge of the signal and then immediately reading and resetting the counter so it's ready for the next cycle. It seems to work quite well by itself even with much faster signals (at least 2 kHz) than the one used here (200 Hz). That's what makes me think there might be some other problem in the main program loop and once in a while, the counter catches a part of the next pulse, which briefly throws it off.
Another possibility is that there's some noise getting into the input because I'm not able to see anything weird going on when I'm using clean PWM generated by Pico on another pin to feed the counter and the output after mapping is rock steady. Or at least steady enough for my scope to not show anything.

Oh, and I've also updated the code on github, written a short readme and added examples, so feel free to take look. All feedback is welcome :)

hippy
Posts: 9991
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: Evaluate the PWM signal

Sun Apr 11, 2021 12:28 am

horuable wrote:
Sat Apr 10, 2021 9:30 pm
Since there's no hardware synchronization I've tried to achieve that in the main loop by waiting for the falling edge of the signal and then immediately reading and resetting the counter so it's ready for the next cycle. It seems to work quite well by itself even with much faster signals (at least 2 kHz) than the one used here (200 Hz). That's what makes me think there might be some other problem in the main program loop and once in a while, the counter catches a part of the next pulse, which briefly throws it off.
I'm glad I wrote what I did - even if it may have seemed like 'teaching the sucking of eggs'. Because, when I went back to your PWMCounter class it was entirely understandable this time round, and looks perfectly correct to me, assuming the mem32[] and 'magic numbers' are right, and I have no reason to suspect they aren't.

The issue may lie with -

Code: Select all

if ~(x := in_pin.value()) & last_state:
Not that it's wrong per se, not that it's not falling edge detecting, just that it's not necessarily reading only one pulse worth of counts.

Consider an input signal and hitting the above code on every bottom mark -

Code: Select all

   __    __    __    __    __    __
__|  |__|  |__|  |__|  |__|  |__|  |__
   |        |        |        |
   1        0        1        0                       
At each '0', that's a 1 -> 0 falling edge, you read the counter, reset it, loop again. But between those '0' any number of pulses might have occurred, will have been counted.

It works so long as those bottom marks are hit at a higher frequency than the input signal, but if not, because of a print for instance and a lot of other processing such as a long mapping operation, it could be prone to errors.

I think you need to have; detect falling edge, discard that count, zero, wait for next falling edge, read the count, then continue.

I might be wrong, but that's my gut feeling.

Nitro_fpv
Posts: 89
Joined: Tue Mar 30, 2021 11:56 am
Location: Switzerland

Re: Evaluate the PWM signal

Sun Apr 11, 2021 1:17 am

I'll give up soon!

I have now made the code with the new PWMCounter.
Again the same twitches as soon as all modules are activated together.

I really do not know anymore.

Code: Select all

from machine import Pin, PWM
from time import ticks_us, ticks_diff
from PWMCounter import PWMCounter

pwm  = PWM(Pin(14))
pwm.freq(200)

def interpoliert(x, i_m, i_M, o_m, o_M):
    return max(min(o_M, (x - i_m) * (o_M - o_m) // (i_M - i_m) + o_m), o_m)

#             __input___   __Output__
mapping = [
                                1500  , # Failsafe - For any input value not in any range
            [  500, 1000,  1000, 1000 ],
            [ 1000, 1485,  1000, 1400 ],
            [ 1515, 2000,  1600, 2000 ],
            [ 2000, 2500,  2000, 2000 ],
          ]
          
def map(n, mapping):
    for idx in range(1, len(mapping)):
        inmin, inmax, outmin, outmax = mapping[idx]
        if n >= inmin and n <= inmax:
            i = (n - inmin) / (inmax - inmin)
            o = ((outmax - outmin) * i ) + outmin 
            return int(o)
    return mapping[0]


# We'll use counter pin for triggering, so set it up.
in_pin = Pin(19, Pin.IN)
# Configure counter to count rising edges on GP15
counter = PWMCounter(19, PWMCounter.LEVEL_HIGH)
# Set divisor to 16 (helps avoid counter overflow)
counter.set_div(16)
# Start counter
counter.start()

last_state = 0
last_update = ticks_us()
pwmout = 0


while True:
    start = ticks_us()
    if ~(x := in_pin.value()) & last_state:
        # Print pulse width in us - should show 250 with default setup
        pwmout = ((counter.read_and_reset() * 16) / 125)
    last_state = x
    pwmout = int(pwmout)
    deadzone = (map(pwmout, mapping))
    convertoutput = (interpoliert(deadzone,500, 2500, 6555, 32777 ))
    pwm.duty_u16(convertoutput)

horuable
Posts: 121
Joined: Sat Mar 06, 2021 12:35 am

Re: Evaluate the PWM signal

Sun Apr 11, 2021 1:02 pm

hippy wrote: just that it's not necessarily reading only one pulse worth of counts.
That was my concern as well, but I've dismissed it because OP said:
Nitro_fpv wrote:Period duration 5.0ms (200Hz), Duty Cycle is between 800us and 2200us.
that gives anywhere from 2800 to 4200 us to read and clear the counter which should be more than enough. The processing can go even longer since it can be done in parallel with the counter measuring the next pulse.
To check this I've devised a little test. I'm setting GP2 low after the edge is detected and high again after reading and clearing the counter and calculating the pulse width. When I feed it into the scope along with input PWM I can see the delay between the falling edge and its detection by Pico. I can also measure how much time the whole read-clear-calculate operation takes. That's how it looks:
DS1Z_QuickPrint4.png
DS1Z_QuickPrint4.png (40.47 KiB) Viewed 219 times
(Keep in mind that all timings are with the overhead of actually flipping additional output, so in reality they're slightly better)
The time between input edge and detection averages at around 50 us, and read-clear-calculate is about 100 us. After that, the program can do whatever processing it needs as long as it finishes before the next falling edge, which is not a problem. I've timed the whole loop including all calculations, mapping, and setting the new PWM duty at around 500 us max after 10000 iterations. So unless there is something else going on there should be plenty of time to spare before this becomes a problem. For reference, I had to artificially extend the mapping time by almost 3 ms to see the program miss every other edge.
If you think my testing method is flawed, please do tell. I really want to know.

While doing this I've also slightly modified the code because there's no need to make all the calculations and mapping on every loop, even if no new data was read. Also map shouldn't be used for function name because there's already a built-in function called map.

Code: Select all

from machine import Pin, PWM
from time import ticks_us, ticks_diff
from PWMCounter import PWMCounter

pwm  = PWM(Pin(14))
pwm.freq(200)

def interpoliert(x, i_m, i_M, o_m, o_M):
    return max(min(o_M, (x - i_m) * (o_M - o_m) // (i_M - i_m) + o_m), o_m)

#             __input___   __Output__
mapping = [
                                1500  , # Failsafe - For any input value not in any range
            [  500, 1000,  1000, 1000 ],
            [ 1000, 1485,  1000, 1400 ],
            [ 1515, 2000,  1600, 2000 ],
            [ 2000, 2500,  2000, 2000 ],
          ]
          
def map_(n, mapping):
    for idx in range(1, len(mapping)):
        inmin, inmax, outmin, outmax = mapping[idx]
        if n >= inmin and n <= inmax:
            i = (n - inmin) / (inmax - inmin)
            o = ((outmax - outmin) * i ) + outmin 
            return int(o)
    return mapping[0]


# We'll use counter pin for triggering, so set it up.
in_pin = Pin(19, Pin.IN)
# Configure counter to count rising edges on GP15
counter = PWMCounter(19, PWMCounter.LEVEL_HIGH)
# Set divisor to 16 (helps avoid counter overflow)
counter.set_div(16)
# Start counter
counter.start()

last_state = 0
pwmout = 0

while True:
    if ~(x := in_pin.value()) & last_state:
        pwmout = ((counter.read_and_reset() * 16) // 125)
        deadzone = (map_(pwmout, mapping))
        convertoutput = (interpoliert(deadzone,500, 2500, 6555, 32777 ))
        pwm.duty_u16(convertoutput)
    last_state = x

Nitro_fpv
Posts: 89
Joined: Tue Mar 30, 2021 11:56 am
Location: Switzerland

Re: Evaluate the PWM signal

Sun Apr 11, 2021 1:11 pm

I found the cause!
I now know what is causing the twitches.
But I don't know why ?!

If I change line 1 of the mapping, 95% of the ticks are gone when reading PWM.

of:

Code: Select all

mapping = [
                                 1500  , # Failsafe - For any input value not in any range
            [  500, 1000,  1000, 1000 ],
            [ 1000, 1485,  1000, 1400 ],
            [ 1515, 2000,  1600, 2000 ],
            [ 2000, 2500,  2000, 2000 ],
          ]
to:

Code: Select all

mapping = [
                                    0  , # Failsafe - For any input value not in any range
            [  500, 1000,  1000, 1000 ],
            [ 1000, 1485,  1000, 1400 ],
            [ 1515, 2000,  1600, 2000 ],
            [ 2000, 2500,  2000, 2000 ],
          ]
How do you delete this first line completely?
If I delete this completely, I get an error message.

If I delete another two lines from the mapping, all jerks are gone and the servo runs as smoothly as a Swiss watch.

to:

Code: Select all

mapping = [                         0  , # Failsafe - For any input value not in any range
            [ 1000, 1485,  1000, 1400 ],
            [ 1515, 2000,  1600, 2000 ],
          ]
horuable:

The only thing missing now is the failsave.
Is there a way to tell the PWMCounter that a false or missing signal is not 0 but 1500us?
Neither may <1000us and> 2000us be output.
I am so glad that it might work!

Return to “MicroPython”