fossasia / pslab-python

Python Library for PSLab Desktop: https://pslab.io
GNU General Public License v3.0
1.63k stars 225 forks source link

Waveform Generator: Prescaler out of range #213

Closed marcnause closed 1 year ago

marcnause commented 1 year ago

I triggered the following error:

ValueError: Prescaler out of range. This should not happen. Please report this bug, including steps to trigger it, to https://github.com/fossasia/pslab-python/issues.

Steps to reproduce:

from pslab import ScienceLab psl = ScienceLab() psl.pwm_generator.generate(["SQ1"], 3, 0.5)

The error occurs for frequencies smaller than 3.81472637 Hz.

bessman commented 1 year ago

Thanks for the report. The reason this happens is the following:

64 MHz / (256 * (2^16 - 1)) = 3.8147554741741057 Hz, which is the value you found.

Supporting lower PWM frequencies than this would require using a 32-bit clock instead, which would make the new lower limit 58.21 µHz. This would require a firmware update.

May I ask what your use case is? PWM frequencies this low are not often used, and given that this is rather tricky to fix I'm inclined to instead make the low frequency limit explicit with an error message like:

ValueError: PWM frequencies lower than 4 Hz are not supported.
marcnause commented 1 year ago

I was just playing around and had a small piezo speaker connected for acoustic feedback while testing. I wanted to use a really low frequency since it is less annoying to my ears.

Not supporting frequencies below 4 Hz would probably not prevent any real world use cases. I already assumed that this is an edge case and 3 Hz was just a bad choice on my side. I only reported the issue since the error message asked me to.

I think adding an explicit low frequency limit would help users and you could always reference this issue if anybody complains about it. 😉

bessman commented 1 year ago

Sounds good, I'll clarify the error message :+1:

For the record, you could generate a reasonable approximation of a very low frequency PWM signal like this:

import time
import pslab

def slow_pwm(frequency, duty_cycle, pwm_generator):
    state = False
    period = frequency ** -1
    on_period = period * duty_cycle
    off_period = period * (1 - duty_cycle)
    while True:
        pwm_generator.set_states(sq1=state)
        state = not state
        time.sleep([on_period, off_period][state])

psl = pslab.ScienceLab()
slow_pwm(1, 0.5, psl.pwm_generator)  # 1 Hz PWM signal

The edge timing accuracy will depend on your operating system, typically 1 ms on Linux and 10 ms on Windows, unsure about Mac.