Backgound. I am doing a STM32L4 Arduino Core for a few STM32L4xx platforms (github: https://github.com/GrumpyOldPizza/arduino-STM32L4, kickstarter (our newest creations): https://www.kickstarter.com/projects/2109048010/arduino-programmable-cortex-m4f-development-boards). Of course I need to add I2S/SAI support. Ran across the newly checked in I2S library and since I don't see a good reason to have yet another I2S library that is incompatible I have a few design questions and requests for extensions.First off congrats to Sandeep coming up with this excellent concept.
Off we go to the questions:(1) I need to add MCK output. Some CODECs do not have their own clocks, and hence need to derive that from MCK. That of course is only relevant to master mode. The various CODECs document the configuration of MCK as ratio between MCK and LRCK (I2S_FS/I2S_WS, pick your name for the same pin ;-)). Hence I would propose the following API modification:int begin(int mode, long sampleRate, int bitsPerSample, int ratio = 0);If "ratio" is 0, then MCK is disabled, if it's non-zero, the MCK output is enabled. Typical values for "ratio" are 128, 192, 256, 384, 512. STM32L4 supports 128, 256 & 512. SAMD would support anything between 64 and 1024. nRF52 does 128, 192, 256, 384, 512. Hence I2S.begin() would return an error if a "ratio" cannot be supported by hardware.
(2) On STM32L4 on can link 2 SAI peripherals together to form a composite peripheral that supports I2S_MCK/I2S_SCK/I2S_FS/I2S_SDO/I2S_SDI, basically switching from halfduplex to fullduplex. nRF52 has a similar setup as well. This needed to be selectable for.both master and slave mode. Hence I would propose similar to the HardwareSerial "config" idea to merge that into "mode" via:I2S_MODE_DUPLEX
(3) "bitsPerSample" seems to describe right now only the memory layout for the DMA engine. However even the example code dealing with an I2S microphone runs into the issue that the real data is 24 bits only. I'd suggest to adopt the following semantics (based upon what common CODECs do):32 32 bit data, MSB is bit 3124 32 bit data, MSB is bit 2320 32 bit data, MSB is bit 1916 16 bit data, MSB is bit 1510 16 bit data, MSB is bit 98 8 bit data, MSB is bit 7It's then the task of any given implementation to setup the internal shifting/reformatting logic so that "mode" + "bitsPerSample" get mapped to the proper output stream. However a single sample on the I2S port still takes up 64 SCK cycles. There does not seem to be any good reason to add a more convoluted scheme.
(4) I kind of like and do not like the idea of a doubled buffered scheme over a ringbuffer scheme. If each buffer has 512 bytes, you can store 128 24 bit samples. So there is some latency before you can process the first sample on the receive path, as you have to wait for your buffer to be filled. For one that latency needs to be exposed to the user, which means I2S_BUFFER_SIZE should be moved into I2S.h for visibility. However if you need a smaller buffer size for reducing the read latency, you'd need a knob for that. Hence I would propose the following new API:I2S.threshold(int numSamples);This would force the code to swap buffers after "numSamples" on the read path, if "numSamples" is larger than a MCU dependent minimum values and smaller than the I2S_BUFFER_SIZE allows.
(5) Right now the library is defined in terms if half-duplex. However other than throu the callback there is no way for the application to determine whether a transmit is fully done, and a switch to receive mode would not block. I would suggest to add this API:I2S.done();This would return "false" if there is any unsent sample (other DMA or in the HW FIFO), and hence a switch to receive mode would block. N.b I2S.flush() is then really a wait till I2S.done() is true.
Hi Thomas,Thanks for the initial feedback and for getting the discussion going!See inline responses below:On Mon, Dec 12, 2016 at 10:41 AM, Thomas Roell <grumpyo...@gmail.com> wrote:Backgound. I am doing a STM32L4 Arduino Core for a few STM32L4xx platforms (github: https://github.com/GrumpyOldPizza/arduino-STM32L4, kickstarter (our newest creations): https://www.kickstarter.com/projects/2109048010/arduino-programmable-cortex-m4f-development-boards). Of course I need to add I2S/SAI support. Ran across the newly checked in I2S library and since I don't see a good reason to have yet another I2S library that is incompatible I have a few design questions and requests for extensions.First off congrats to Sandeep coming up with this excellent concept.It was great team effort! Cristian Maglie, Tom Igoe, and Arturo Guadalupi all provided their valuable input!Off we go to the questions:(1) I need to add MCK output. Some CODECs do not have their own clocks, and hence need to derive that from MCK. That of course is only relevant to master mode. The various CODECs document the configuration of MCK as ratio between MCK and LRCK (I2S_FS/I2S_WS, pick your name for the same pin ;-)). Hence I would propose the following API modification:int begin(int mode, long sampleRate, int bitsPerSample, int ratio = 0);If "ratio" is 0, then MCK is disabled, if it's non-zero, the MCK output is enabled. Typical values for "ratio" are 128, 192, 256, 384, 512. STM32L4 supports 128, 256 & 512. SAMD would support anything between 64 and 1024. nRF52 does 128, 192, 256, 384, 512. Hence I2S.begin() would return an error if a "ratio" cannot be supported by hardware.That proposal sounds good for now, let's see if anyone else on the mailing list has any other ideas. We will also need to consider the case for using the MCK as an input signal in slave mode.
May I ask which specific codecs you are looking to support?
The "maker friendly" I2S devices we tested with for the initial release include:Both did not need the MCK pin.
(2) On STM32L4 on can link 2 SAI peripherals together to form a composite peripheral that supports I2S_MCK/I2S_SCK/I2S_FS/I2S_SDO/I2S_SDI, basically switching from halfduplex to fullduplex. nRF52 has a similar setup as well. This needed to be selectable for.both master and slave mode. Hence I would propose similar to the HardwareSerial "config" idea to merge that into "mode" via:I2S_MODE_DUPLEXJust to confirm, there is only a single set of MCK, SCK and FS pins? Do both peripherals need to have the same rate and bits per sample? Could duplex mode be entered when I2S.read(...) and I2S.write(...) are both called?
Do you have any specific use cases in mind for this setup? Also, a rough idea for user sketch in this scenario would be great.
32 32 bit data, MSB is bit 3124 32 bit data, MSB is bit 2320 32 bit data, MSB is bit 1916 16 bit data, MSB is bit 1510 16 bit data, MSB is bit 98 8 bit data, MSB is bit 7It's then the task of any given implementation to setup the internal shifting/reformatting logic so that "mode" + "bitsPerSample" get mapped to the proper output stream. However a single sample on the I2S port still takes up 64 SCK cycles. There does not seem to be any good reason to add a more convoluted scheme.For now, we've decided to keep things simple and only support 32, 16, and 8 bits per sample.
The microphone is nice enough to shift in 0's for the LSB which makes dealing with basic audio analysis with signed math much easier.Again, outside of the mic I mentioned that uses 24-bit are you targeting any particular devices that need to use those bits per sample?
(4) I kind of like and do not like the idea of a doubled buffered scheme over a ringbuffer scheme. If each buffer has 512 bytes, you can store 128 24 bit samples. So there is some latency before you can process the first sample on the receive path, as you have to wait for your buffer to be filled. For one that latency needs to be exposed to the user, which means I2S_BUFFER_SIZE should be moved into I2S.h for visibility. However if you need a smaller buffer size for reducing the read latency, you'd need a knob for that. Hence I would propose the following new API:I2S.threshold(int numSamples);This would force the code to swap buffers after "numSamples" on the read path, if "numSamples" is larger than a MCU dependent minimum values and smaller than the I2S_BUFFER_SIZE allows.Yes, I agree something like this would be useful.Do you think we could use the size argument of I2S.read(buffer, size) to determine the sample count threshold? Currently receive mode is not started until I2S.read(...) or I2S.available() are read.
(5) Right now the library is defined in terms if half-duplex. However other than throu the callback there is no way for the application to determine whether a transmit is fully done, and a switch to receive mode would not block. I would suggest to add this API:I2S.done();This would return "false" if there is any unsent sample (other DMA or in the HW FIFO), and hence a switch to receive mode would block. N.b I2S.flush() is then really a wait till I2S.done() is true.It would be useful for me to see a sketch of what you have in mind for this. Also, how would the hardware setup work - would someone be plugging and unplugging wires or toggling a switch?
The SAMD version currently does not block on I2S.flush(), something we should definitely consider changing.Thomas, again these are an excellent set of questions. It would be really helpful for us to get a better picture of how you see users using the various features you mentioned above (including user sketches and hardware setups).There is also a higher level ArduinoSound library available via the library manager now, that builds upon the new SAMD's I2S library. Tutorials and documentation for I2S and ArduinoSound should be up on the arduino.cc website later this week.
Sandeep
Pretty much all the hardware I had checked can do 16/24/32 bit (packed as 16bit and 32bit respectively). 8 bit seems to be rarely supported. Is there a specific use case for 8 bit that was the driving force ?
(1) Add "ratio" to I2S.begin() in master mode to expose a MCK pin (which SAMD may or may not use for now). With a "ratio = 0" default that should be fairly transparent.
(2) Add 24 bit, so right justified 24 bit data can be expressed. Should also be transparent. SAMD doesn't support that yet, so it would just return 0.
Anyway, let me ask the other way around, how would you support the WM8731 use case, where you have a real CODEC with audio output as well as audio output, but a shared SCK ?
So it seems "bitsPerSample" in the I2S API is really "bitsPerSlot" and the application needs to then reformat ? My comments where assuming that one would always use "bitsPerSlot == 32" and then bitsPerSample would indicate how many data bits were transferred on this 32 bit slot. Also for DMA the byte count per sample would be rounded up to the next 8bit, 16bit & 32bit value. Perhaps this clears up some confusion.
Ideally the API would expose "bitsPerSample" and "bitsPerSlot", but that does buy little over just assuming what I outlined above for majority of cases.
What I am worried about after sifting throu all those datasheets is whether "bitsPerSample == 16" (CD Audio playback) works with "bitsPerSlot == 16" across the board or not.
--
You received this message because you are subscribed to the Google Groups "Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to developers+unsubscribe@arduino.cc.
Pretty much all the hardware I had checked can do 16/24/32 bit (packed as 16bit and 32bit respectively). 8 bit seems to be rarely supported. Is there a specific use case for 8 bit that was the driving force ?No specific use case, we tested with SAMD board to board with 8-bits. It was a bit of a freebie to add 8-bit.
(1) Add "ratio" to I2S.begin() in master mode to expose a MCK pin (which SAMD may or may not use for now). With a "ratio = 0" default that should be fairly transparent.Yes, that works.(2) Add 24 bit, so right justified 24 bit data can be expressed. Should also be transparent. SAMD doesn't support that yet, so it would just return 0.Would the data be "packed" in memory when reading writing (3 bytes per sample) - DMA transfers would remain 32-bit though? This might tie in the the bits per slot topic below.
Anyway, let me ask the other way around, how would you support the WM8731 use case, where you have a real CODEC with audio output as well as audio output, but a shared SCK ?Do you have link to the data sheet for the WM8731? I can only find a device that uses I2C and SPI ...
Instead of having a half and full duplex mode, maybe I2S.begin(...) could just mux both the input and output pins?
So it seems "bitsPerSample" in the I2S API is really "bitsPerSlot" and the application needs to then reformat ? My comments where assuming that one would always use "bitsPerSlot == 32" and then bitsPerSample would indicate how many data bits were transferred on this 32 bit slot. Also for DMA the byte count per sample would be rounded up to the next 8bit, 16bit & 32bit value. Perhaps this clears up some confusion.
Ideally the API would expose "bitsPerSample" and "bitsPerSlot", but that does buy little over just assuming what I outlined above for majority of cases.It could help distinguish how the data is packed in memory?
What I am worried about after sifting throu all those datasheets is whether "bitsPerSample == 16" (CD Audio playback) works with "bitsPerSlot == 16" across the board or not.It really depends, we tried to keep things simple and stick to the "maker friendly" boards out there for the initial release. The Tindie I2S mic we tested needs a bits per slot of 32 to work, right now as you noticed we must use 32-bits per sample. Adding bits per slot would allow people to get 8-bit or 16-bit data from it - which is pretty nice.
To unsubscribe from this group and stop receiving emails from it, send an email to developers+...@arduino.cc.
To unsubscribe from this group and stop receiving emails from it, send an email to developers+...@arduino.cc.
(1) Added "masterClock = false" to I2S.begin(). No need for ratio, as all chips do 256.
Is there such a list for SAMD as well ?
Correct, although I was more commenting on how within a 32bit word the 24 bits of data payload are fetched from. I am not suggesting a 3 byte data type.
I would recommend against exposing "bitsPerSlot" to the user. It's another thing to understand and of course to get wrong (why can I not do 20 for "bitsPerSlot" ?). Sifting throu all the relevant datasheets from NXP/Freescale/STM/TI/Novuton/Atmel, it seems all the Cortex-M based systems do support 16 & 32 for bitsPerSlot. Many support 8 bits for the slot size. But rarely any do 24 bit. So this is why I would not want to expose another parameter that introduces hardware dependencies.
To unsubscribe from this group and stop receiving emails from it, send an email to developers+unsubscribe@arduino.cc.
To unsubscribe from this group and stop receiving emails from it, send an email to developers+...@arduino.cc.