This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Need I2S help

Hi there,

I'm just trying to start to code my project. I want to stream 48k 16 bit audio from one nrf52840 dongle to the other one. I only need one way transfer.

I have looked at every example I could find but still dont seem to get how to send the packets to the radio and over the wireless connection.

Is there anyone that can help get me started with some simple no frills code.

I'm using Segger and sdk v17.

I would be most grateful if someone could assist me here.  Once I get the streaming part working successfully I think I can work out how to do the other things I need but this I2S is very tricky it seems to know what to do. It needs to be a steady stream.

Kind regards,

Dam076

Parents
  • Hi 

    Are you planning to do any compression of the audio stream, or do you want to send raw data?

    You should be able to handle the raw data stream if the link is good, but you won't have a lot of overhead to handle packet loss. 

    If you are planning to do something proprietary (ie not Bluetooth) I would recommend having a look at the nrf_esb library. Then you don't have to spend time on low level radio setup, but can instead focus on integrating the I2S sensor. 

    There are some examples in the SDK showing how to use the nrf_esb library, in the following folder:

    \nRF5_SDK_17.0.2_d674dde\examples\proprietary_rf

    Best regards
    Torbjørn

  • No compression. 2 MBIT should be ample throughput as 48k 16bit audio needs around 1,5 MBIT. Also I'm struggling to understand how the Queue works in the code because the audio stream is obviously slower than the RF-RF connection so, how does it pause and then start sending again when the buffer starts to empty? I will need a small buffer, no error correction and no ACK to achieve the lowest latency possible. Then if I have any headroom, I can possibly add CRC if needed but given the low latency specs I require and the likely possibily that any 'correction' will drastically slow down the stream, I dont think its going to be useful. It would be great if there was some type of interpolation of audio samples in the event that there is a CRC error but I fear that is going to be very complicated. But my theory would be to average out the 'before' error sample and the 'after' error sample, average the 2 samples and then replace the sample in the buffer that is detected as corrupt with the 'averaged' value,..  This should eliminate any glitch and cause minimal auditory changes. I guess mathematically this is not hard, I just need help working out what flag will be set to indicate that specific packet has a CRC error and then call the error correction function to replace the value in the buffer. Hope that makes sense. 

  • Also, is it possible to use EASYDMA to have the IS2 buffer auto copy to the Radio buffer using interupts when the I2S buffer fills up, and if so are you able to help with a simple example, keeping in mind that this I2S buffer will be constantly filling and needing to be copied and freed to allow the next I2S words to collect in the buffer. Would double buffering work in this case, and again if so, are you able to provide a very simple example? Thanks again. The learning curve here is enormous. All I want to do is send I2S over the radio link lol. cheers

  • Hi

    damo76 said:
    If I was to use the radio library directly and use ->PACKETPTR  will this pointer accept a pointer to an array or only a single 32bit variable? If it will accept an array, how do I code it correctly and how do I tell the radio how many 32bit words to send via RF?

    The PACKETPTR register assumes it is pointed to a byte array, and the length of the packet is set by a combination of the LENGTH field in the packet and the STATLEN field in the PCNF1 register

    The LENGTH field allows for dynamic packet length, where the packet length is sent along with the payload content so that the receiver knows how long each packet is. The critical thing when using the LENGTH field is that one of the first bytes in the buffer pointed to by PACKETPTR will be used to store the length, as described in this chapter of the RADIO documentation. 

    It shouldn't be a problem pointing PACKETPTR to an array of 32-bit values instead, you just have to multiply the length by 4 (and ensure the total packet length in bytes doesn't exceed 258 bytes, as mentioned in the chapter I linked to above). 

    damo76 said:
    Also, is it possible to use EASYDMA to have the IS2 buffer auto copy to the Radio buffer using interupts when the I2S buffer fills up, and if so are you able to help with a simple example, keeping in mind that this I2S buffer will be constantly filling and needing to be copied and freed to allow the next I2S words to collect in the buffer.

    You can't have the EasyDMA copy from one buffer to another, no, but you don't really need to. The trick is to assign the EasyDMA of both the radio and the I2S interface to the same buffer, then no copying is needed. 

    As I mentioned earlier you can implement a FIFO/ringbuffer in RAM (an array of buffers essentially), and start filling it up using the I2S interface. Once the first buffer is filled you point the EasyDMA of the I2S to the second buffer in the FIFO, and ask the RADIO to send the data stored in the first. 

    Then the I2S will be busy filling the second buffer, while the RADIO is busy transmitting the first buffer over the air. Once the radio operation is complete you can mark the first buffer as free for more I2S data. 

    damo76 said:
    Would double buffering work in this case, and again if so, are you able to provide a very simple example?

    If you don't plan to do any data acknowledge and retransmissions then a double buffer should be sufficient, but in this case you will be more susceptible to packet loss over the air. 

    Regarding the example, what specifically do you need demonstrated?

    As I mentioned earlier I would suggest starting on the I2S integration, and try to get data out of your I2S device. Until this is working there is no point trying to set up the rest. 

    Best regards
    Torbjørn

  • Now that was really helpful thankyou. One issue I have found when trying to copy and paste some code i found is that the function nrf_drv_i2s_start is different from nrfx_i2s_start even though the nrf_drv_i2s.h file links the 2 functions with a #define macro. Are you able to help me with specific code to 1) activate I2S in RX mode only, and 2) in TX mode only. It seems the new nrfx function requires both buffers to be passed as a structure in the first argument, yet the old driver allowed for them to be split. Is it possible I can pass the structure but give the value NULL in the rx (or tx) pointer where required or do I have to provide both buffers?  This would help me out because I only need I2S as a one way device and only want to deal with 1 set of buffers on each RF module, if possible.  and thanks again. 

  • "

    The trick is to assign the EasyDMA of both the radio and the I2S interface to the same buffer, then no copying is needed. 

    As I mentioned earlier you can implement a FIFO/ringbuffer in RAM (an array of buffers essentially), and start filling it up using the I2S interface. Once the first buffer is filled you point the EasyDMA of the I2S to the second buffer in the FIFO, and ask the RADIO to send the data stored in the first. 

    Then the I2S will be busy filling the second buffer, while the RADIO is busy transmitting the first buffer over the air. Once the radio operation is complete you can mark the first buffer as free for more I2S data. "

    This is exactly what i needed help to code. Is there any example of this or are you able to help me with a basic code structure for this? I am still getting my head around how I2S utilises the easyDMA.. tbh I am struggling to understand how it implements the alternating buffers in the data_handler

  • Hi

    The driver allows you to set one of the buffers to NULL in case you only need one directional data flow. 

    If you look at the start of the nrfx_i2s_start(..) function you can see that is checks to make sure one of these buffers is set, otherwise it will trigger an error. Having both set is not a requirement:

    nrfx_err_t nrfx_i2s_start(nrfx_i2s_buffers_t const * p_initial_buffers,
                              uint16_t                   buffer_size,
                              uint8_t                    flags)
    {
        NRFX_ASSERT(p_initial_buffers != NULL);
        NRFX_ASSERT(p_initial_buffers->p_rx_buffer != NULL ||
                    p_initial_buffers->p_tx_buffer != NULL);
        .
        .

    Once you  have this running let me know, and I will do my best to guide you in integrating the rest. 

    Best regards
    Torbjørn

Reply
  • Hi

    The driver allows you to set one of the buffers to NULL in case you only need one directional data flow. 

    If you look at the start of the nrfx_i2s_start(..) function you can see that is checks to make sure one of these buffers is set, otherwise it will trigger an error. Having both set is not a requirement:

    nrfx_err_t nrfx_i2s_start(nrfx_i2s_buffers_t const * p_initial_buffers,
                              uint16_t                   buffer_size,
                              uint8_t                    flags)
    {
        NRFX_ASSERT(p_initial_buffers != NULL);
        NRFX_ASSERT(p_initial_buffers->p_rx_buffer != NULL ||
                    p_initial_buffers->p_tx_buffer != NULL);
        .
        .

    Once you  have this running let me know, and I will do my best to guide you in integrating the rest. 

    Best regards
    Torbjørn

Children
Related