equivalent to Arduinos pulseIn(), micros()

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
Turbinenreiter
Posts: 288
Joined: Sun May 04, 2014 8:54 am

equivalent to Arduinos pulseIn(), micros()

Post by Turbinenreiter » Mon Jul 07, 2014 1:56 pm

I'm trying to read signals from an RC-reciever (the ones going to the Servos) - they use PWM with pulses of somewhat around 2µs.

As there is no pulseIn() (yet?), I thought of measuring the time using interupts, however the pyb.millis() timer isn't fast enough for that. I will be tackling this problem over the next couple of days and am (again) happy for input.

Basically what I want to use this for is to connect the RC-reciever to the pyboard. A Servo is attached to the pyboard and triggered either by sensor data or, as a fallback, by the remote.

fma
Posts: 164
Joined: Wed Jan 01, 2014 5:38 pm
Location: France

Re: equivalent to Arduinos pulseIn(), micros()

Post by fma » Mon Jul 07, 2014 2:44 pm

I'm very interested by such feature too.

To emulate the micros() function, you can create a timer at freq=1 000 000 Hz (1MHz -> 1µs resolution), and get its current value with tim.counter(). You can reset it with tim.counter(0).

But I don't think a pure python interrupt callback will be fast enough...
Frédéric

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: equivalent to Arduinos pulseIn(), micros()

Post by dhylands » Mon Jul 07, 2014 4:36 pm

I expect to be exploring how to do exactly that (measure an input pulse) using a capture mode of the timers.

I want it for a couple of reasons:

1 - to support RC input (as you do)
2 - to support the SRF04 ultrasonic sensors.

What you can ddo today is to create a micros as per: https://github.com/micropython/micropython/issues/570 and use http://micropython.org/doc/module/pyb/ExtInt to capture the edges.

I expect that the results will be jittery though since your extint interrupt will occasionally be delayed by other interrupts.

Turbinenreiter
Posts: 288
Joined: Sun May 04, 2014 8:54 am

Re: equivalent to Arduinos pulseIn(), micros()

Post by Turbinenreiter » Tue Jul 08, 2014 7:33 am

the code from issue 570:

Code: Select all

import pyb

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

@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
this works!
except: pyb.Pin('X1', pyb.Pin.IN, pyb.Pin.PULL_DOWN) crashes my board. I updated to newest build of the firmware, still crashes.
but: pyb.Pin.board.X1 works.

also: @micropython.native works, @micropython.viper throws: ViperTypeError: local pin used before type known. Is this a bug in my code or in micropython? There isn't much to find about the different emitters but the two kickstarter updates yet. Should code work with viper without adjustment or do I need to state the types of the variables?

However, I think there should be an implementation of this in C as a method of the Pin-class. Like pyb.Pin().pulseIn().
I'm reading pin.c now, and I think I understand how to add a new method.
I imagine it looking somwhat like that:

Code: Select all

STATIC mp_obj_t pin_pulseIn(mp_obj_t *args) {
    /// create timer
    while (MP_OBJ_NEW_SMALL_INT(GPIO_read_pin(self->gpio, self->pin)) == 0) {
        /// reset timer
    }
    while ( ... != 0) {
        /// do not reset timer
    }
    return /// timer count
Now to figure out how to use timers from C.

Turbinenreiter
Posts: 288
Joined: Sun May 04, 2014 8:54 am

Re: equivalent to Arduinos pulseIn(), micros()

Post by Turbinenreiter » Wed Jul 09, 2014 12:28 pm

Code: Select all

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
Ok, I use this function in a while loop.
A script with only this function in a while loop runs forever.
Using @micropython.native() raises FATAL ERROR: MemManage after 3231 iterations.
@micropython.viper crashes silently after the same amount of iterations.

It seems like the board is running out of memory, however, I have no idea how to check memory (pyb.info() prints info I don't really get), and also no idea why it could run out of memory, because it uses the same variables each iteration and doesn't save anything else.

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: equivalent to Arduinos pulseIn(), micros()

Post by dhylands » Wed Jul 09, 2014 3:07 pm

What might be happening is that one of the ints could be overflowing from 31 to 32-bits.

A 31-bit int is stored without allocating any memory. As soon as you need 32 or more bits, then it has to allocate memory.

The largest positive 31-bit value is 0x3fffffff = 1073741823

Trying to allocate memory with interrupts disabled will cause a memory error.

In the output of pyb.info():

Code: Select all

GC:
  102208 total
  1568 : 100640
  1=34 2=9 m=6
the 1568 number is the amount of the heap currently in use, and the 100640 is the amount of the heap which is available.

I also tried:

Code: Select all

pyb.Pin('X1', pyb.Pin.IN, pyb.Pin.PULL_DOWN)
on recent firmware and its working fine for me. So I'm suspecting that you either didn't update your tree properly, or you didn't actually flash the updated firmware.

If you do:

Code: Select all

git rev-parse HEAD
from within the micropython tree, what does it report? (that will tell us which version of the tree you have).

Post Reply