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. 

  • Hi

    damo76 said:
    I have looked at ESB but I'm still having trouble working out how to successfully link the I2S buffer with the packet pointer and understanding how the code detects the buffer has changed so that i automatically triggers the raidio to send the new packet.

    When working on a project like this I would recommend breaking the development up into smaller blocks, and handle one task at a time. 

    First off you need to get the I2S transfer to/from your sensor working properly, which you can do based on the peripheral/i2s example in the SDK. Since you refer to block_words I assume you have looked at this example already?

    I would recommend setting up a stream with your sensor on the input side, and log the data coming in (or use the debugger to verify it) to check if the data looks ok. 

    Once you have good data coming in from the sensor you can start to pass it on to the ESB library in order to have it sent over the air. 

    I am not quite sure what you mean about the code detecting the buffer has changed?
    Whenever the i2s driver has emptied/filled a buffer, the data_handler callback will be triggered in your application to inform you of this. 

    The p_released argument is a pointer to the buffer that was just read and/or written, and in the case of incoming data you have to move the RX data from this buffer to the ESB library. 

    damo76 said:
    I have figured that the radio needs to stay on and not turn off between packets.

    This is not supported by the nrf_esb library. In most applications you want to receive an ACK after each TX packet, and as such the library will always disable the radio after each sent packet (so that it can be enabled in RX mode to receive the ACK). 

    It is possible to keep the radio active between packets and not use any ACK, but the library would need changes to support this. 

    damo76 said:
    Also I am unsure on the correct value of block_Words so that I2S doesnt glitch.

    In essence this is determined by how fast your application can respond to I2S interrupts, which depends on how many higher priority interrupts there are in the system and how long they could take to execute in the worst case. 

    Without having run any tests myself I would expect 64 or more to be plenty, but this is application dependent. 

    Have you had issues with glitches?

    damo76 said:
    Any coding help to link I2S with the radio directly, on both the TX and the RX will help greatly, Cheers

    Not sure what you mean about linking the I2S directly. While it is theoretically possible to assign the radio and the I2S to the same physical RAM buffer, this is not supported by any of the drivers, and I wouldn't recommend going down this path ;)

    You should have plenty of CPU time available to do a quick copy between buffers, so that you can move the I2S data into and out of the esb_payload_t buffers used by the esb library. Then you only need to add some code on top of the I2S driver and the nrf_esb library to pass data between them. 

    damo76 said:
    No compression. 2 MBIT should be ample throughput as 48k 16bit audio needs around 1,5 MBIT.

    2 Mbps is the on-air bitrate. The actual throughput is lower, mainly because of packet overhead (preamble, address, ESB header and CRC) and radio startup times. You might be able to push the ESB library up to about 1.5Mbps bi-directional bitrate, but this is assuming zero packet loss. You will have no overhead to do retransmission in case of lost packets, which means the link will not be very robust if you have interference from other 2.4GHz radios in the area (WiFi for instance). 

    damo76 said:
    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?

    Essentially you need to implement a FIFO (or at minimum a double buffer, which is essentially just a FIFO with 2 elements). Once the I2S driver has filled up one element of the FIFO you can pass this over to the ESB stack. The ESB stack won't do anything unless it has some data in its own internal buffer, so the timing will be controlled by how often the application passes data to it. 

    damo76 said:
    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.

    A 16-bit CRC doesn't add a lot of overhead to the packet, unless you send very short packets, and sending short packets is not good in terms of data throughput. 

    The advantage of having CRC included even if you don't plan to do any error correction is that you will know if there are any bit errors in the packet. From an application point of view it might be interesting to know whether or not you are receiving correct data. 

    damo76 said:
    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.

    The radio applies a CRC check on the whole packet, which means you don't know exactly which of the samples are affected. A faulty CRC could mean that all the samples in the packet are invalid, if you are unlucky. Unless you are talking about sending a single on air packet for every sample?

    In general I know that handling corrupted data for audio is not easy, as the human ear is very good at picking up certain kinds of distortions in the sound (such as a single sample with a huge offset in the value). 

    Best regards
    Torbjørn

  • Thanks for your help so far. 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?

  • 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. 

Reply
  • 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. 

Children
Related