adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.02k stars 1.19k forks source link

Feather Express M0 - pulseio.PulseIN - freezes at high frequency #516

Closed Apotessar closed 6 years ago

Apotessar commented 6 years ago

Test Case: Messure Light trough a light-to-frequency-coverter.

Test Setup: Feather Express M0 - Circuit Python 2.2.0 TSL235R-LF https://www.sparkfun.com/datasheets/Sensors/Imaging/TSL235R-LF.pdf connected 3V to VDD, GND to GND and Output to D12

> #Feather Express M0 connected 3V to VDD, GND to GND and Output to D12
 import board
 from board import*
 import digitalio
 import busio
 import adafruit_ssd1306
 import time
 import analogio
 import pulseio

 i2c = busio.I2C(SCL, SDA)
 display = adafruit_ssd1306.SSD1306_I2C(128, 32, i2c)
 pulses = pulseio.PulseIn(board.D12, maxlen=20)

 while True:
     time.sleep(1)
     pulse = 500000/float(pulses[0])
     display.fill(0)
     display.text(str(pulse),0,0)
     print(pulse)
     display.show()

Error: Board do not response, crash or disconnect while the input frequency is above 25 kHz. The specific freqency could not be determinated. While covering the Converter it is sometimes possible to recover the board. Outwise a reset is nessessary.

Source: https://forums.adafruit.com/viewtopic.php?f=60&t=129061

sommersoft commented 6 years ago

Now that PulseIn is in 3.0 (with updated tick_delay to boot), I decided to give this a run. I don't have a sensor available that will pulse >25kHz, so I used a Feather M0 Express with PWMOut.

Feather M0 Express was running this PWMOut script. 50% duty cycle, starting at 8kHz with incremental increases.

ItsyBitsy M0 Express (w/JLink) was running this PulseIn script. This version has a max_len=20, but also tested with default of 2 with the same results.

I got up to 64kHz before the REPL froze. After a few runs, I noticed that it always froze when the result was <20. But, after scrolling back through the terminal, 19's & 18's appeared with no effect.

I'm still narrowing down where to look specifically, but something interesting is showing up. I've had the Feather PWMOut script halted, and the jumper removed, for over 20 minutes. When I pause the ItsyBitsy and step-over in Atmel Studio, the callstack shows that it's cycling through instances of EIC_Handler. The REPL is also still unresponsive.

sommersoft commented 6 years ago

I'm still narrowing down where to look specifically, but something interesting is showing up. I've had the Feather PWMOut script halted, and the jumper removed, for over 20 minutes. When I pause the ItsyBitsy and step-over in Atmel Studio, the callstack shows that it's cycling through instances of EIC_Handler. The REPL is also still unresponsive.

CORRECTION: I thought I had the jumper removed. After actually removing it, the calls to EIC_Handler subsided. REPL became responsive....

tannewt commented 6 years ago

It sounds like we're hitting the limit for how fast we can execute the interrupt handler. Are the pulse lengths close enough that we can determine the input frequency?

We could add another C class specifically designed to measure an input frequency. It wouldn't need interrupts, we could have a counter measure input edges or something.

sommersoft commented 6 years ago

That's pretty much the conclusion I've come to today. I removed the pulsein.clear() from the script, thinking that the interrupt disabling was a culprit. That got me up to 128kHz before locking up.

Setting a breakpoint in Atmel Studio on pulsein_interrupt_handler proved interesting. With continuous output of total_diff, last_ms, and last_us, it shut down the serial comms to the terminal, but kept chugging right along past 128kHz. The only problem: total_diff never gets below 18 and tick_ms outputs a steady number (never increases). Using the same breakpoint output for SysTick_Handler->ticks_ms on a subsequent run resulted in a lockup at 128kHz.

So, yeah, I think we're getting stuck in an endless EIC interrupt loop.

Could we use the TC peripheral? During some searches I saw that it could be used for PWM In...but I didn't dig deep into it. Might also run into similar limitations with the EVSYS...

EDIT: TC peripheral...not TCC

sommersoft commented 6 years ago

I've got a FrequencyIn core module working!

Using the same PWMOut script as above, I'm now getting up to 512kHz before the REPL locks up. Using this script to capture the frequency in:

import board, pulseio
tc = pulseio.FrequencyIn(board.D7)
while True:
    if (tc.value > 0):
        freq = 48000000 / tc.value  // currently only working on SAMD21
        print(freq)

I have the TC prescaler set to 1 right now, and I think it may be hitting max COUNTER. Will continue to debug it. Will also look into a way to catch the upper bound and raise an exception (most likely the interrupt error flag).

Progress at least.

ladyada commented 6 years ago

awesome!