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
RFC: PWM HIL #1139
Comments
We have a DAC HIL. Are you describing merging that with the PWM HIL? A lot of Arudino boards seem to have motors or some other actuator, and I'm impressed with their PWM API and that it supports the development that users want to do: // pin: the pin to write to. Allowed data types: int.
// value: the duty cycle: between 0 (always off) and 255 (always on). Allowed data types: int.
analogWrite(pin, value) Doesn't get much simpler than that. Now I don't think we should make that our HIL, but supporting an interface that simple will probably get us pretty far. |
The SAM4L has a DAC controller, but no PWM, the NRF52 has a PWM controller but no DAC. The CC26x2 has a PWM (well a timer that can be in PWM mode, which I don't totally understand), and separately a DAC (well they call it a reference DAC, and I also don't totally understand it). Are these controllers interchangeable, and thus we should have one HIL for both? Is one a superset of the other (e.g. PWMs can also implement a DAC HIL)? In the cases @phil-levis mentions (motors, DACs, buzzers): Do you typically want to generate some wave-form continuously and, if you stop it, stop it out of band? or do you generally want to generate it for a fixed period (of time/iterations/whatever)? Similarly, what does hardware typically provide? The NRF52 seems to support continuously generating a particular wave, as well |
Doesn't get much simpler than that. Now I don't think we should make that our HIL, but supporting an interface that simple will probably get us pretty far. Yeah, sorta, if that's the level of stuff people want to do. But I don't think we should limit ourselves to Arduino-level applications. The issue is that if the HIL is too simple, then people will have to work around it. This can then play havoc with things like power management. Basically, PWM without a notion of time in it then requires that the higher layers control timing, which can be much easier and more accurate when close to the hardware (e.g., interrupt handlers on samples complete). E.g., a good 25% of final projects in CS107E use PWM for audio on the Raspberry Pi, and they would not be able to do so with the Arduino API. There are basically four knobs/parameters you have:
We can probably cut 4) -- the cases when you don't want to spread them (want to lower the frequency) is pretty rare. But to give you a counterpoint to the Arduino approach, this is the Atmel API to the SAM4 PWM: It defines 42 functions! I'd much rather be closer to Arduino, but we want to aim higher. Among other things, PWM can have FIFO or DMA, which enable removing jitter (e.g., as on the Raspberry PI BCM2835). The SAM4 seems to have some pretty weird PWM mechanisms, but it would be fine to fit into an API like the one above. |
I have no experience with PWMs, so I can't agree or disagree about 1-4, but they seem like a good balance between control and simplicity. It certainly seems like one could expose the Arduino API in a library call that wraps the 1-4 things @phil-levis suggests. |
Hm -- one trick with cutting 4 is that the SAM4L (and Atmel chips generally) don't really support spreading. E.g., if you want to do a 60% duty cycle, then their hardware is well suited to have the line high for 6 ticks and low for 4, so 1111110000, rather than spreading like so 1010110101. The place I encountered PWM spreading was on the Raspberry Pi: I'm looking around for MCUs that do it. I'm therefore going to suggest that we add 4 back in. |
How about something like this. I swapped it such that the time you specify is length of a pulse, since that can be in terms of underlying clock ticks.
Another approach might be to couple the three configuration parameters together. I think we can then layer a simpler interface on top of this, which computes number of pulses, etc.
|
Could we combine some of these functions? E.g. does it make sense to change the duty cycle while the PWM is currently running or start it without having set the duty cycle first? If not, maybe just pass the duty cycle as a parameter to Similarly, a lot of state changing functions in the advanced trait seem like they could just be parameters to |
For the advanced/low-level trait, the challenge then is the caller needs to keep all of those parameters, in order to re-invoke them. For example, suppose you want a buzzer at a particular frequency. What you'd like to be able to do is configure the buzzer to be at that frequency, then turn it on and off. If start() takes all of the parameters, then they need to be stored for when start() is re-invoked. Furthermore, it means re-configuring the PWM each time you want to start it, even if the configuration hasn't changed. I'm more ambivalent about the simpler trait -- I could see it making sense to put the duty cycle in start(). Although then it shouldn't be start(), because chances you'll want to call it while is_running() is true; on() and off() might make more sense. |
Here's a revised version of the simple trait:
|
I had to bang out my own HIL and driver for a deliverable this week. Here's where I ended up (with some extra affordances thrown in for 8 bit vs 16 bit timers) At the HIL level:
My userland interface looks like this:
What I like about this is it gives you varying degrees of control & ease. Set frequency and set duty cycle assume you don't care about the other. Meanwhile, if you care about controlling both accurately, the pwm_configure is there. I only did the 16 bit implementation so far because that's what my timer is, but here's what the capsule/pwm.rs looks like (in broad strokes)
What I'd like to point out in contrast to what it seemed like Phil was converging on is that we can get frequency conversions done at the capsule-driver level so that each HIL needs to provide much less functionality. Also, I would urge a "lowest level possible" type interface to exist along with "simple ones". That's what "configure" is in my scheme and maps directly to the timers. I will admit though, that the user might use the interface without checking for the bit-size of the PWM driver. IMO, we could catch those errors and convert them to the best supported resolution at the driver level. |
@phil-levis BTW, the DAC on the CC26x2 is exclusively as an input to the analog comparator. It's useful because I believe you can setup a low power analog alarm by putting some threshold voltage out on the DAC and then setting up the comparator to give you an interrupt. Much cheaper then running continuous ADC and comparing the values to a threshold in software. And also, it's worth being explicit about the differences of DAC vs PWM since I don't think this was made clear in this thread. DAC outputs a constant voltage. PWM outputs switching signals that can help you simulate a voltage. Having separate and clear interfaces is important as that's a significantly different property. For example, I would suggest extending the current DAC interface to provide an "easy" voltage control command similar to the way PWM provides an "easy" frequency control command. |
We currently don't have a HIL for PWM. @bradjc has started the conversation in #1003. Let's start with use cases. The major PWM uses I'm aware of are motors, DACs (with supporting circuitry), and situations when you want high frequency square waves (e.g., @bradjc's buzzer).
Motors have the property that you want to control the width of the pulses but don't want to spread them. With DACs, you want to drive at a much higher frequency and spread the pulses to create analog signals. Square waves are pretty simple, just a 50% duty cycle and adjusting the frequency.
When you're driving an analog signal, often you need to clock out changes as sample points of the wave. So, for example, if you want to clock out 32 data points on a 1kHz analog wave, you need to update PWM settings at 32kHz.
So this seems like there are 3 different knobs: the underlying frequency, the pulse width, and the pulse spreading. Furthermore, we probably want separate APIs for high frequency streaming (e.g., DAC) and lower frequency.
The text was updated successfully, but these errors were encountered: