How fast can 16 input pins be read in sequence?

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
bittware
Posts: 45
Joined: Mon Aug 18, 2014 3:27 am

How fast can 16 input pins be read in sequence?

Post by bittware » Sat Feb 21, 2015 12:56 pm

Hello,
As Pyboard lacks the parallel interface, I'd like to use GPI bit-bang mode to read the 16-bit register value stored in external FPGA.
Following steps need to done in sequence.
1.react to external I/O interrupt.
2.bit-bang sequential read on 16 GPI pins.
3.convert 16 single bit into two byte integer.
4.compare the integer with a predefined constant.
5.toggle an output pin based on comparison result.

I don't know how fast the whole process could be done?
Any chance this could be done within 100us?

Thanks in advance.

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

Re: How fast can 16 input pins be read in sequence?

Post by dhylands » Sat Feb 21, 2015 6:03 pm

You should be able to do it in C in 100 usec, not sure about python.

You would be better off to use SPI and send the data serially.

hansel
Posts: 13
Joined: Wed Feb 11, 2015 7:34 pm
Location: Rotterdam, NL
Contact:

Re: How fast can 16 input pins be read in sequence?

Post by hansel » Sat Feb 21, 2015 10:12 pm

I experimented with timer callback at 10Khz (= 100 us), but I'm not sure how much I/O you can put in it...

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

Re: How fast can 16 input pins be read in sequence?

Post by Damien » Sun Feb 22, 2015 11:21 pm

You can definitely run a callback at 10kHz. To read 16 pins at once, quickly, the best thing to do is read the GPIO input-data-register directly using the stm module. Eg:

Code: Select all

import pyb, stm

# callback to read pins X1-X8 at once, and store into the variable xvalue
xvalue = 0
def read_cb(tim):
    global xvalue
    xvalue = stm.mem32[stm.GPIOA + stm.GPIO_IDR] & 0xff # read pins X1-X8 directly

# init pins X1-X8 to input
for pin in range(1, 9):
    pyb.Pin('X%d' % i, pyb.Pin.IN)

# start the timer at 10kHz doing the read
tim = pyb.Timer(1, freq=10000, callback=read_cb)
Since pins X1-X8 map directly to PA0-PA7 it's easy to do this for 8 bits. For 16 bits at once you'll need to look at the pyboard pinout to find 8 other pins that you can use that are on a single port (probably port B). Then you can do 2 reads (one from port A, one from port B) then a few bit manipulations to construct the 16 bit value.

You can put @micropython.native before the callback function to make it run about 2 times faster, which might help in your case.

hansel
Posts: 13
Joined: Wed Feb 11, 2015 7:34 pm
Location: Rotterdam, NL
Contact:

Re: How fast can 16 input pins be read in sequence?

Post by hansel » Tue Feb 24, 2015 6:24 pm

Just did a small test: reading 8 bit samples into an array runs at 47 Khz. A bit slow for a logic analyzer...
Maybe a small assembler loop could bring this to 10 or 20 MHz? ;)

Code: Select all

  N=1000.0
  a=array.array('B')
  start = pyb.micros()
  for i in range(N):
    a.append(stm.mem32[stm.GPIOA + stm.GPIO_IDR])
  dt =  pyb.micros() - start

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

Re: How fast can 16 input pins be read in sequence?

Post by dhylands » Tue Feb 24, 2015 7:44 pm

For logic analyzer type functionality, I would code something up that uses timed DMA (off the top of my head - I haven't actually tried this).

Any SW solution is going to have jitter. The DMA solution will have none.

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

Re: How fast can 16 input pins be read in sequence?

Post by Damien » Tue Feb 24, 2015 11:20 pm

Using native Python code I get 110kHz, and assembler gives 12.9MHz:

Code: Select all

import pyb, stm

@micropython.native
def burst_read_native(buf):
    for i in range(len(buf)):
        buf[i] = stm.mem32[stm.GPIOA + stm.GPIO_IDR]

@micropython.asm_thumb
def burst_read_asm(r0, r1):
    movwt(r2, stm.GPIOA + stm.GPIO_IDR) # r2 points to GPIOA.IDR
    label(LOOP)
    ldr(r3, [r2, 0])    # r3 = GPIOA value
    strb(r3, [r0, 0])   # store r3 into bytearray
    add(r0, 1)          # increase bytearray pointer
    sub(r1, 1)          # decrease length counter
    bne(LOOP)           # keep going while length non-zero

a = bytearray(20000)

start = pyb.micros()
burst_read_native(a)
dt = pyb.elapsed_micros(start)
print('native reads per second:', len(a) / dt * 1e6)

start = pyb.micros()
burst_read_asm(a, len(a))
dt = pyb.elapsed_micros(start)
print('asm reads per second:', len(a) / dt * 1e6)
If you disable interrupts during the burst read then it'll be very stable (ie no jitter). But note that you can't time the function when interrupts are disabled because the systick won't tick :)

To disable in asm use cpsid(i) at the start of the function and to enable use cpsie(i) at the end.

Post Reply