-
Notifications
You must be signed in to change notification settings - Fork 190
Added I2S (native) output support for RP2040 #163
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, we really want to have this! Looks good to me on a first glance (no actual testing done). Found some minor details, and also don't forget about the documentation (readme and news).
(Also, the RP2040 is just crying for an implementation of bare chip PDM output via PIO+DMA. Just saying ;-) ).
Hei!
Thanks for you comments, the lack of doc and testing explain the draft state of this, I wanted to have comments before going to deep in! I added a few comments where I would be happy to have an external point of view if you have time!
Edit: that being said, I see a very fresh update (three days ago) on the arduino-pico core that should allow to use the same structure for PWM output (via DMA and internal buffers), so it might be easy. |
I don't seem to see those. Am I overlooking them?
I mean both, actually. But in fact, I was skipping over some bits, here: The PIO mechanism does not allow enough programming to perform PD-modulation inside the state machine. However, the RP2040 certainly has enough processing power for the encoding. DMA transfer is an extra bit, really, but will certainly be useful for handling the required signal rates. For PWM modulation, the PIO state machines should be quite capable of performing that all by itself, and using DMA would offload (essentially) all output handling to hardware.
Yes, there is a reason, why I did not use DMA in my initial RP2040-port...
Ah, great, that will be a significant enhancement to the current solution in itself, then. |
Oh, you probably mean these:
Sounds reasonable to me. On a more general note, I guess we should have an option to skip over the mozziAnalogRead() setup (not just for RP2040), for conflicts such as this one, but also, because to save some resources, where analog reads are not needed.
Having them configurable via a define sounds benign to me. Thinking of this, it may also be a good idea to initialize the buffer size, explicitly, in any case. The default used in the i2s implementation could possibly be subject to change, which might in turn lead to mysterious differences. |
Damn, attaching a screenshot with them, if they are only displaying for me there is very little point of commenting the code :/ For the PWM, I think this might turn useful: earlephilhower/arduino-pico@08d37de , the PDM might be on my list in the future. Thanks for your comments! |
@tfry-git Actually meant the ones in the screenshot above. Will also allow configuration of the buffers/DMA via define when I get back to it! |
I think you need to click "Submit review" near the bottom of the page. Ah, the PT8211 issue. I actually had my share of that in a different context (esp8266/Arduino#6940), before. I suppose, the only proper fix would have to be one inside the i2s implementation itself (and there, it would have to be a new option). -- Oh, and looking at the sources to see, where it would be done, I see it has just been added a few hours ago: setLSBJFormat(). Adding AUDIO_BIAS as a workaround is not without drawbacks: It removes the sign-bit, but at the same time, of course, it halves the available output range (and I believe, we'd also need to shift out one bit, too, to avoid overflow). Either way, on the Mozzi-side, yes, I think we'll need an option / define for that. It should be labeled something like I2S_LSB_FORMAT, with a comment right next to it, that it is needed for PT8211. Just how that support is implemented should not be something for the user to worry about. |
As for an option to invert left and right, I guess that may be (minor) overkill, as it seems easy enough to just switch left and right in hardware. If at all, though, I'd suggest to make it a global option right next to AUDIO_CHANNELS. The implementation could simply switch l() and r() in AudioOutput.h ( Line 100 in baca7e0
|
Hi,
Yes, we can say that this library is changing fast, my issue did not stay long in there (the PR was submitted when I was opening the source file). |
All good here! Best, |
AudioConfigRP2040.h
Outdated
// Configuration of the I2S port, especially DMA. Set in stone here as default of the library when this want written. | ||
// Probably do not change if you are not sure of what you are doing | ||
#define BUFFERS 8 // number of DMA buffers used | ||
#if (AUDIO_BITS == 32) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if AUDIO_BITS > 16?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not think so, at least the default values in the I2S libraries are only different for 32 bits, cf line 153 of I2S.cpp:
_bufferWords = 16 * (_bps == 32 ? 2 : 1);
I prefer to stick on what is implemented there as I think he has a better understanding of the hardware behind than me, even though I agree that it would be logical to have 24 and 32 in the same batch. So let me know what you prefer ;)
AudioConfigRP2040.h
Outdated
// Probably do not change if you are not sure of what you are doing | ||
#define BUFFERS 8 // number of DMA buffers used | ||
#if (AUDIO_BITS == 32) | ||
#define BUFFER_WORDS 32 // number of words in the buffers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest to put total buffer sample size, here (i.e. 256 samples), and move the calculation what that means in terms of words to the implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure because of the previous comment actually, the case of 24bits being ill-posed. This is the closest to the library for sure but I agree that would be more understandable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the end I agree. I think the case of 24 samples is a small mistake in the core I was inspired by.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(see latest commit)
README.md
Outdated
- 16 bits resolution by default (configurable in AudioConfigRP2040.h), 8, 16, 24 (left aligned) and 32 resolution are available. | ||
- Both plain I2S and LSBJ_FORMAT (for the PT8211 for instance) are available (configurable in AudioConfigRP2040.h), default is LSBJ. | ||
- Outputs pins can be configured in AudioConfigRP2040.h. Default is BCK: 20, WS: 21, DATA: 22. | ||
- Two hardware alarms and two DMA channels are claimed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the i2s-library claim the hardware alarms?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It claims IRQ0, isn't that an hardware alarm :3 ?
AudioConfigRP2040.h
Outdated
// Configuration of the I2S port, especially DMA. Set in stone here as default of the library when this want written. | ||
// Probably do not change if you are not sure of what you are doing | ||
#define BUFFERS 8 // number of DMA buffers used | ||
#if (AUDIO_BITS == 32) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not think so, at least the default values in the I2S libraries are only different for 32 bits, cf line 153 of I2S.cpp:
_bufferWords = 16 * (_bps == 32 ? 2 : 1);
I prefer to stick on what is implemented there as I think he has a better understanding of the hardware behind than me, even though I agree that it would be logical to have 24 and 32 in the same batch. So let me know what you prefer ;)
AudioConfigRP2040.h
Outdated
// Probably do not change if you are not sure of what you are doing | ||
#define BUFFERS 8 // number of DMA buffers used | ||
#if (AUDIO_BITS == 32) | ||
#define BUFFER_WORDS 32 // number of words in the buffers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure because of the previous comment actually, the case of 24bits being ill-posed. This is the closest to the library for sure but I agree that would be more understandable.
README.md
Outdated
- 16 bits resolution by default (configurable in AudioConfigRP2040.h), 8, 16, 24 (left aligned) and 32 resolution are available. | ||
- Both plain I2S and LSBJ_FORMAT (for the PT8211 for instance) are available (configurable in AudioConfigRP2040.h), default is LSBJ. | ||
- Outputs pins can be configured in AudioConfigRP2040.h. Default is BCK: 20, WS: 21, DATA: 22. | ||
- Two hardware alarms and two DMA channels are claimed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It claims IRQ0, isn't that an hardware alarm :3 ?
I can see how one could read it that way, but it's not what I had meant to write. Perhaps change my "hardware alarm" to "timer interrupt" for more clarity. (Interrupts come in different flavors, and the ones used for hardware timers are not the same ones used for DMA). That said, after thinking about this some more, I believe we can probably do better than hogging up one DMA IRQ exclusivley (out of only two that are available!). In short (I hope to get around to trying something myself in a day or two, but you may be faster): Mozzi/MozziGuts_impl_RP2040.hpp Line 105 in 5a4a17a
Using Mozzi/MozziGuts_impl_RP2040.hpp Line 85 in 5a4a17a
|
Indeed, I did not see before this |
…l fix in DMA channels attribution for ADC readings
Hei, |
I assumed the same about irq_add_shared_handler(). What symptoms do you see when you try to actually share DMA_IRQ_0? I did not take the time to connect an I2S chip, but I compiled with EXTERNAL_DAC_VIA_I2S, changed the implementation code to DMA_IRQ_0 (lines 106 and 107), and tried the Volume_Knob example. I got good readings from mozziAnalogRead(), and there is at least something coming out of pins 20-22. |
I guess we will also need to patch
for shared irqs. That's still based on theory, alone, not testing, though. |
Ah, so my initial understanding was the same… When I tried it worked in a non-reliable way:
Will try your patch and report back! |
Good catch, works like a charm on IRQ0 now! |
Great! I'll merge, then! |
The end of a long story! |
Hello and happy new year!
I have started fiddling with the RP2040 (what a nice chip!) and as it does possess I2S support, found a bit sad not to use it in order to interface it with some external DAC. Having a native I2S support allows not to have two buffers in row (Mozzi and the I2S output buffer). Also, it uses DMAs.
A few details:
As a draft for now, it seems to work but I want to test it a bit further.
Best,