Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add micros command #570

Closed
tontonisback opened this issue May 5, 2014 · 15 comments
Closed

add micros command #570

tontonisback opened this issue May 5, 2014 · 15 comments
Labels

Comments

@tontonisback
Copy link

Hi,

Is it possible to add pyb.micros() to show the µs ellapsed since the board was last reset ?
There is pyb.millis() but when you work with signal of a few µs it doesn't help.

As example : http://arduino.cc/en/reference/micros

Thanks,

@dpgeorge
Copy link
Member

dpgeorge commented May 5, 2014

The pyb.millis() function uses the system tick functionality of the MCU, which is a millisecond counter.

To add a microsecond counter would require using up a precious hardware timer. I didn't want to include such a feature by default, because it takes away the use of a timer for other things.

But you can implement such a microsecond counter yourself:

micros_timer = pyb.Timer(2, prescaler=83, period=0x3ffffff)

Get the current micros using:

micros_timer.counter()

Set it with:

micros_timer.counter(0)

Technical notes: we use timer 2 because it's a 32-bit timer and so takes longer to wrap around. The prescaler is 83 which means the timer counts at a frequency 168MHz/2/(83+1)=1MHz. 168MHz is the CPU frequency. The period is large so that the timer counts up to a large number before wrapping back to 0. It will take about 17 minutes before it wraps around to 0.

@dpgeorge
Copy link
Member

dpgeorge commented May 5, 2014

Of course, you can change the prescaler to whatever you want to make a counter that counts faster or slower.

@tontonisback
Copy link
Author

Thanks for your answer. It's work but it's not precise. I suppose it's because of the time needed by the command to execute ?
For example:
print (micros_timer.counter())
pyb.delay(1)
print (micros_timer.counter())

I have got as answer:
2701601
2702900
So it's 1300µs instead of 1000.
It cause problem because i'm receiving signal on a pin 70µs mean 1 and 28µs mean 0.
With this timer I received this kind of period (41,68,120,70,41,64,120,194)

Thanks,

@dhylands
Copy link
Contributor

dhylands commented May 5, 2014

Can you provide more details about the sensor that you're connecting?

When you start getting into timings this precise you may need to consider using C or figuring out if there is a way to configure one of the timers to capture your timing information for you.

@tontonisback
Copy link
Author

Yes for sure. In fact I was trying as a first project to convert the dht22 (humidity sensor http://playground.arduino.cc/Main/DHTLib) library to python.
It's working well but not the end because of the delay.

Original c++:
unsigned long t = micros();
while(digitalRead(pin) == HIGH)
if ((micros() - t) > 40) bits[idx] |= mask;

micropython:
micros_timer.counter(0)
while (datapin.value() is 1):
if ((micros_timer.counter())>40): (I also tried to put higher value 70 for example)
data[idx]=data[idx] | mask

Thanks,

@dpgeorge
Copy link
Member

dpgeorge commented May 5, 2014

This is a very nice example of something that we should be able to get working in micropython without resorting to any special hardware features. Yes, the timers can most likey be used to capture a time period, but it would be great to have a way of doing this in pure Python.

There are many solutions. First, let's try to write the code more efficiently:

def time_pin():
    # get functions for efficiency
    micros = micros_timer.counter
    pin = datapin.value
    # wait for pin to go high
    while not pin():
        pass
    # reset micros counter
    micros(0)
    # wait for pin to go low
    while pin():
        pass
    # compute 1 or 0
    if micros() - start > 40:
        data[idx] |= mask

Give that a try. If it is still inaccurate, then we can try direct memory access for the pin and timer values. Or we could write this function in assembler. Or we could use an SPI bus (or UART maybe) to sample the pin and then count the number of samples that were high. There is also ADC.read_timed which may be useful here. Lots of solutions, which I can elaborate on if need be.

BTW, the case above where pyb.delay(1) gives 1300us is not so surprising: pyb.delay only has an accuracy of +/- 1 ms, so a 1ms delay will be between 1 and 2ms.

@dpgeorge
Copy link
Member

dpgeorge commented May 5, 2014

You can try also the above but using the native emitter (the code will run about 2x as fast, so should be more accurate):

@micropython.native
def time_pin(pin):
    m = micros_timer.counter
    p = pin.value
    while not p():
        pass
    m(0)
    while p():
        pass
    return m()

This function will return the number of microseconds that the pin was high.

@dpgeorge
Copy link
Member

dpgeorge commented May 5, 2014

Oh yeah, and you might find it is more robust if you disable interrupts while timing the pin:

pyb.disable_irq()
us = time_pin(datapin)
pyb.enable_irq()

@lurch
Copy link
Contributor

lurch commented May 6, 2014

Would it also help if you used interrupts (assuming you haven't disabled them of course!) to detect the rising/falling pin, rather than using functions and while loops? Or is the code here simple enough that using interrupts wouldn't give any benefit?

@dpgeorge
Copy link
Member

dpgeorge commented May 6, 2014

Probably easier to use simple polling. Interrupts would be possible, but would probably have more overhead than the polling example above. Still, might be nice to also write an interrupt version, and check the difference with some tests.

@tontonisback
Copy link
Author

Thanks for all these information. It's work better but I'm still missing some data in the begining of transmission so I will look at it to improve.
I try the pyb.disable_irq() and pyb.enable_irq() but It's make the board crash. It's not possible to mount the drive anymore. I try to update with a new dfu thinking it will reset everything but it doens't help.

I suppose I have to make a factory reset. I will follow the nice tuto !
Edit: Drive mount again thanks to factory reset !

@dpgeorge
Copy link
Member

dpgeorge commented May 6, 2014

Flashing with a new DFU image does not reset the filesystem. Filesystem reset is needed for that.

Interesting that disable_irq crashes the board... I'll look into it.

@dpgeorge
Copy link
Member

dpgeorge commented May 6, 2014

disable_irq and enable_irq are working for me. Try this:

import pyb

micros_timer = pyb.Timer(2, prescaler=83, period=0x3fffffff)
pin = pyb.Pin('X1', pyb.Pin.IN, pyb.Pin.PULL_DOWN)

@micropython.native
def time_pin(pin):
    m = micros_timer.counter
    p = pin.value
    pyb.disable_irq()
    while not p():
        pass
    m(0)
    while p():
        pass
    count = m()
    pyb.enable_irq()
    return count

That should give a fairly accurate measurement of the pin being held high. Note that it first waits while the pin is low, until it goes high, then starts timing.

If you run this at the REPL, eg with time_pin(pin), then it wont print any output until the function returns.

@pfalcon
Copy link
Contributor

pfalcon commented May 7, 2014

Ok, was original question re: micros() answered? Can this be closed now and further discussion perhaps taken to forum?

@tontonisback
Copy link
Author

Yes , thanks for the support I'm still working on it. The original question has been answered. I will post on the forum my result.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants