STM32F4Discovery I2C

Discussion and questions about boards that can run MicroPython but don't have a dedicated forum.
Target audience: Everyone interested in running MicroPython on other hardware.
Post Reply
kjarke
Posts: 5
Joined: Wed Jun 17, 2015 12:28 am

STM32F4Discovery I2C

Post by kjarke » Wed Jun 24, 2015 4:22 am

I want to read the VL6180 proximity sensor every time I press the user button on the discovery board.

I verified that I2C is working, configuring and reading the VL6180 works and the interrupt works as well. I every time I press the button I get "Uncaught exception in ExtInt interrupt handler line 0 MemoryError". I traced this back to

Code: Select all

self.i2c1.mem_read(1, self.addr, 0x0062, addr_size=16)
in the read_dist function. Why can't I read the I2C bus from the interrupt handler but I can write to I2C? The call to read_dist in the last line of Main.py returns the distance as expected. Here is my code:
Main.py

Code: Select all

import pyb
from pyb import Pin, ExtInt
from vl6180 import VL6180

dist = VL6180()

def range_it(t):
    global dist
    print('Distance: ', dist.read_dist()[0])

d_it = ExtInt(Pin.cpu.A0, ExtInt.IRQ_RISING, Pin.PULL_NONE, range_it)

print('Distance: ', dist.read_dist()[0])
Parts of vl6180.py:

Code: Select all

def read_dist(self):
        self.i2c1.mem_write(0x01, self.addr, 0x0018, addr_size=16)       #start one shot distance measurement
        pyb.delay(10)
        self.i2c1.mem_write(0x07, self.addr, 0x0015, addr_size=16)       #clear interrupt flags
        return self.i2c1.mem_read(1, self.addr, 0x0062, addr_size=16)   #return distance result

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

Re: STM32F4Discovery I2C

Post by dhylands » Wed Jun 24, 2015 4:47 pm

You're not allowed to allocate memory from within an interrupt handler.

Read tries to allocate a buffer to put the results in.

Performing an i2c read also isn't an instantaneous operation so isn't really appropriate for use from within an irq

We really. Need an asynchronous read where the irq initiates the read and a callback is called when the read completes.

In the meantime you probably need to set a flag in the irq that triggers the main thread to perform the read.

Damien
Site Admin
Posts: 614
Joined: Mon Dec 09, 2013 5:02 pm

Re: STM32F4Discovery I2C

Post by Damien » Thu Jun 25, 2015 5:58 pm

You can read into a preallocated buffer. Create the buffer outside the irq handler as buf=bytearray(1) for a 1 byte buffer and then use buf as the first argument to mem_read.

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

Re: STM32F4Discovery I2C

Post by dhylands » Fri Jun 26, 2015 5:55 am

Yes - but you still shouldn't be doing an i2c read from within an interrupt handler, because it basically blocks until the read is complete.

I'm not saying it isn't possible to do the read, just that its a bad design choice (in my experience), and I would go out of my way to avoid doing it. You'll also be blocking any lower priority interrupts from happening for the duration of the read.

User avatar
pythoncoder
Posts: 3538
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: STM32F4Discovery I2C

Post by pythoncoder » Thu Jul 09, 2015 5:04 am

@dhylands While I entirely agree with your point about doing i2C activities in an interrupt handler, I'm unsure about having the handler set a flag which is tested by the main code. Why not discard the handler and have the main code poll the hardware? The technique seems to discard the one advantage of interrupt driven code over polling: a response to events which occurs in a predictable, short period of time.
Peter Hinch

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

Re: STM32F4Discovery I2C

Post by dhylands » Thu Jul 09, 2015 3:57 pm

The proper way to do it from an interrupt handler is that you'd initiate the read, and have a transfer-complete interrupt which fires when the read is finished.

User avatar
pythoncoder
Posts: 3538
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: STM32F4Discovery I2C

Post by pythoncoder » Fri Jul 10, 2015 6:31 am

That would be ideal if the MicroPython implementation supported it, especially for long transfers. But for typical short transfers I'd question the timings. You can clock the Pyboard I2C at just over 5MHz which is about 1.6uS per byte. If you're only reading a few bytes would doing the read in an interrupt handler really be a problem?
Peter Hinch

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

Re: STM32F4Discovery I2C

Post by dhylands » Fri Jul 10, 2015 3:49 pm

pythoncoder wrote:That would be ideal if the MicroPython implementation supported it, especially for long transfers. But for typical short transfers I'd question the timings. You can clock the Pyboard I2C at just over 5MHz which is about 1.6uS per byte. If you're only reading a few bytes would doing the read in an interrupt handler really be a problem?
It all boils down to your tolerance for having interrupts disabled.

If disabling interrupts (well I guess it's not all interrupts - just this level and lower priority interrupts) for the length of time that it takes isn't a problem for your system, then its fine.

User avatar
pythoncoder
Posts: 3538
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: STM32F4Discovery I2C

Post by pythoncoder » Fri Jul 10, 2015 5:07 pm

I've been working on a system where most I2C transfers are of one or two bytes. I've today upped the clock rte to 21MHz so the longest transfer will take 800nS. As it happens I'm not doing any of this in an ISR. But, if I were, we have two cases. In the first and interrupt occurs, I kick off the transfer, wait (800nS) for the result, and handle it. In the second I kick off the transfer and return, then a second interrupt occurs and I handle the result in that. Instead of a single period where lower priority interrupts are disabled I now have two. But given the general speed of MicroPython on the Pyboard I'm doubtful whether the second case would be significantly better: I imagine the second interrupt would occur by the time the first handler has passed control to the main code.

For long transfers the second option would, of course, be vastly superior. I wonder there are any plans to modify the SPI (and I2C) drivers to offer an "Interrupt when complete" feature (or other means of implementing nonblocking I/O)? It would be very useful.
Peter Hinch

Post Reply