Skip to content

Adds an LED_pulse feature using ISR to devices/grid_maze.py #150

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

mathias-sm
Copy link

Hi everyone,

Thanks for maintaining this great ecosystem!

For an experiment we're running these days, I needed a feature to dim LEDs in a grid_maze setup. I'm doing this with PWM using a MicroPython timer and a callback that turns the LED on and off at the required duty_cycle (fraction of the time on), at the required freqency. The code is heavily inspired from

def pulse(self, freq, duty_cycle=50, n_pulses=False, load_warning=True):
# Turn on pulsed output with specified frequency and duty cycle.
assert duty_cycle % 5 == 0, "duty cycle must be a multiple of 5 between 5 and 95%"
if not self.timer:
self.timer = pyb.Timer(available_timers.pop())
self.fm = int(100 / next(x for x in (50, 25, 20, 10, 5) if duty_cycle % x == 0))
self.off_ind = int(duty_cycle / 100 * self.fm)
self.i = 0
self.n_pulses = n_pulses
if self.n_pulses:
self.pulse_n = 0
int_freq = freq * self.fm
if load_warning and int_freq > 2000:
warning("This pulse freq and duty_cycle will use > 10% of pyboard processor resources.")
self.timer.init(freq=int_freq)
self.timer.callback(self._ISR)
self.on()
@micropython.native
def _ISR(self, t):
self.i += 1
if self.i == self.off_ind:
if self.n_pulses:
self.pulse_n += 1
if self.pulse_n == self.n_pulses:
self.off()
return
self.toggle()
elif self.i == self.fm:
self.i = 0
self.toggle()
but as they are currently written, this maze's LED_on and LED_off are allocating new memory with to_byte
self.I2C[i2c].mem_write(values.to_bytes(n_bytes, "little"), self.addr[exp_n], self.reg_addr[register], timeout=5)
which is incompatible with ISRs (see https://docs.micropython.org/en/latest/reference/isr_rules.html).

Instead, I pre-allocate buffers for on and off, and write those in a tight loop: this works, is as computationally light as I could think of, but makes pulsing incompatible with turning on/off other LEDs or delivering rewards. Feel free to merge, or otherwise leave here should anyone else need this feature! Happy to take the maintainers' input of course.

This is currently being tested in an experiment, so far we have not had any trouble but I will keep upstreaming fixes if need be. Thanks to @ThomasAkam for his input; some of this code was developed together with Arya Bhomick.

Cheers,
Mathias.

@mathias-sm
Copy link
Author

Update: it turns out that the race condition between the pulse timer and other LED_on/LED_off/SOL_on/SOL_off can, in fact, sometimes occur. Let me fix this before merging.

I now think that the cleanest version of this would be to have a single buffer for the maze object, and when we want to communicate with I2C we read to the buffer, update it, and write from it. That way we only ever allocate it once, it can be used in the tight loop, and if the value gets updated outside of the pulse callback it's not going to mess up with the rest of the code.

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

Successfully merging this pull request may close these issues.

1 participant