XIAO BLE Sense nRF52840 Support #984
-
I have a small 2K buffer that I am trying to stream out to I2S. For some reason, the following line of code in I2SNanoSenseBLE.h appears to be locking up the processor:
I broke up the line into a couple calls to narrow it down:
The lockup occurs when i2s_buffer.readEnd().address() is called. Here is the Arduino sketch I am using:
I put a Serial.println() just before the call to i2s_buffer.readEnd().address() and just after the call. I only see the first println() message. Using println() debugging in the readEnd() function of the BaseBuffer class (Buffers.h), I was able to narrow down the problem to when readEnd() returns. You get a null ptr crash because actual_read_buffer is null:
I am unsure why the actual_read_buffer is null as the logic for the buffers themselves is further than I wanted to dig for now. I hope I did not simply forget to set something up properly before calling i2s.begin() Any help would be appreciated. |
Beta Was this translation helpful? Give feedback.
Replies: 17 comments 32 replies
-
I started to work on the I2S Support for this procesor, but never found the time to complete it. |
Beta Was this translation helpful? Give feedback.
-
Thats's good news. Would the following maybe help ? Prevent potential NPE in IRQ const uin8_t i2s_empty_array[i2s_buffer_size] = {0};
extern "C" void I2S_IRQHandler(void) {
if(NRF_I2S->EVENTS_TXPTRUPD != 0) {
// writing to pins from buffer
NRF_I2S->TXD.PTR = (uint32_t) !i2s_buffer.isEmpty() ? i2s_buffer.readEnd().address() : i2s_empty_array; // last buffer was processed
NRF_I2S->EVENTS_TXPTRUPD = 0;
} else if(NRF_I2S->EVENTS_RXPTRUPD != 0) {
// reading from pins writing to buffer
NRF_I2S->RXD.PTR = (uint32_t) i2s_buffer.writeEnd().address(); // last buffer was processed
NRF_I2S->EVENTS_RXPTRUPD = 0;
}
} I have the feeling that the setupData() should be commented out in begin().
to the end of writeBytes(), so that the processing is only started when there is data available |
Beta Was this translation helpful? Give feedback.
-
I committed some DRAFT corrections. However I noticed that the I2S_IRQHandler is never called. |
Beta Was this translation helpful? Give feedback.
-
Strange that NRF_I2S->INTENSET is not mentioned in chapter 6.11.8 Module operation of the Product Specification |
Beta Was this translation helpful? Give feedback.
-
Calling NVIC_SetVector(I2S_IRQn, (uint32_t)I2S_IRQHandler); seems to make the difference.
I tried to add some corrections to the I2SNanoSenseBLE: It looks pretty promising... |
Beta Was this translation helpful? Give feedback.
-
I suggest to use the simplest possible sketch for testing as a starting point: https://github.com/pschatzmann/arduino-audio-tools/blob/main/examples/examples-stream/streams-generator-i2s/streams-generator-i2s.ino Your pins are wrong! Use Arduino Conventions! For me the above sketch was working. I did not test the signal though... |
Beta Was this translation helpful? Give feedback.
-
I found one minor bug, but not the cause of the lockup. The lines:
should be:
|
Beta Was this translation helpful? Give feedback.
-
I wrote something to just trace the NVIC and the NRF_I2S calls, and here is the output. It looks pretty much the same as the simple example:
|
Beta Was this translation helpful? Give feedback.
-
Strange: For me it is not hanging at all! My gut feeling is that this might be related with your pins or an issue with your specific processor. This was also the reason, why it did not work for me when I tried first, because I did not convert the Arduino Pin numbers. To support your pin convention, I extended the logic a bit so that you can do the following:
With the standard sine test, I am getting:
etc |
Beta Was this translation helpful? Give feedback.
-
Did you deactivate the Info log ? This might avoid any sound quality issues. You could also try to define a fixed i2s_buffer_size instead of updating it from the config.buffer_size. I suggest to try with smaller values then 512 to keep the time low |
Beta Was this translation helpful? Give feedback.
-
It would also be interresting to see if lower sample rates work better. |
Beta Was this translation helpful? Give feedback.
-
Did you try this: You could also try to define a fixed i2s_buffer_size instead of updating it from the config.buffer_size. I suggest to try with smaller values then 512 to keep the time low (e.g. with 64). |
Beta Was this translation helpful? Give feedback.
-
I tried the following experiment. I made my own non-streaming source of a sinewave int16 array. I then changed the I2S_IRQHandler() to copy from this array instead, and the ticks are completely gone. Just pure tone.
I am wondering if the problem might be upstream of the I2S buffer? |
Beta Was this translation helpful? Give feedback.
-
I was on a similar quest: I extended the I2SDriverNanoBLE to support the data directly from a Stream. I also added 2 alternative IRQ implementations: one with readArray and one using directly the addresses and provided different buffer implementations. I am confused as each alternative implementation behaves totally different. Your version should correspond to the following:
It is just replacing your fixed array with the input from sine stream. |
Beta Was this translation helpful? Give feedback.
-
Right, USE_FAST_READ or USE_RINGBUFFER is only relevant for the standard scenario (with the copy in the sketch) and has no impact on the above code. The ticking seems to come from the (small) delay that is added to provide the data. In the standard copy sceanrio, USE_FAST_READ should (in theory) add only a minimal delay because we just need to provide the available address with the already available data in the IRQ. I think the next step is to start to measure the time each step takes and how much time we have available... |
Beta Was this translation helpful? Give feedback.
-
I think, I found it: It seems that if the buffer is just updated but the address not changed in the IRQ, bad things happen! |
Beta Was this translation helpful? Give feedback.
-
This is a bad idea, since this will lead to compile errors im my framework on other platforms. In my latest commit, I have added a driver() method to give access to the underlying driver: so i2s.driver()->setStream(stream), should work, but I am not convinced if this is a good idea. Please note that this also gives a compile error, but it is in your sketch if you try to compile it on any other platfroms. So to highlight that this is only working on the "nano ble", it might be recommended to use the driver class directly, as I did in my example and not mix the approaches. |
Beta Was this translation helpful? Give feedback.
I started to work on the I2S Support for this procesor, but never found the time to complete it.
Maybe you can try to dig into this ?