Hello, I am using the nRF5340 Audio DK with Zephyr and want to use I2S to send audio to an I2S device. So far I have had little success with the microcontroller halting in the arch_system_halt() function.
So far I have managed to get a similar code working on the nrf52840 in Arduino/platformio. As IDE I use clion. (the debugger for the nRF5340 Audio DK is working)
Here is the concrete situation of what I want to achieve:
I have the nRF5340 Audio DK and want to send an I2S signal from the pins P0.14 (HW_CODEC_BCLK), P0.15 (HW_CODEC_DOUT), P0.16 (HW_CODEC_FSYNC). (As labled on the board and in https://infocenter.nordicsemi.com/pdf/nRF5340_Audio_UG_v1.0.0.pdf)
This I2S signal has the specifications:
- Sample Rate 44100Hz
- Left alignedLeft
- 16 Bit sample width
- Left channel
- MCK setup 32MDIV23
- Ratio 32
I have used 2 approaches so far but both end up halting in arch_system_halt() at one point or another.
I assume something with the interrupts is not working correctly.
I will detail both approaches and what errors I encountered in hopes it can help with finding a solution.
The difference between the approaches was that in the first I ported the code from the nrf52840 working project (it is based on the old API in nrf_i2s.h)
The second approach uses the new API from nrfx_i2s.h.
As for the setup done similarly in both:
1) In the prj.conf the following flags have been set so far
CONFIG_GPIO=y CONFIG_CPLUSPLUS=y
(I need the C++ support for later on)
2) In the nrfx_config_nrf5340_application.h I have set the following flags:
- NRFX_I2S_ENABLED 1
- NRFX_I2S0_ENABLED 1
Approach 1
As said, I have used a ported version of the code that I have used with the nrf52840.
Here are the relevant snippets:
#include <stdio.h> #include <zephyr/kernel.h> #include <zephyr/drivers/gpio.h> #include "nrfx.h" #include "nrfx_i2s.h" #include "nrf5340_application.h" #include <stddef.h> uint32_t _sckPin = 14; // Pin P0.14 uint32_t _lrckPin = 16; // Pin P0.16 uint32_t _sdoutPin = 15; // Pin P0.15 #define WORD_SIZE 4 // 1 word = 4 bytes uint8_t * buffer; uint32_t buffer_size = 1024; // 1 KB void i2s_irq_handler(); void setup(); void loop(); int main(void) { setup(); while (1) { // Do something // refill buffer etc. } } void setup() { // Setup buffer // Pin setup nrf_i2s_pins_t pins = { .sck_pin = _sckPin, .lrck_pin = _lrckPin, .mck_pin = NRF_I2S_PIN_NOT_CONNECTED, .sdout_pin = _sdoutPin, .sdin_pin = NRF_I2S_PIN_NOT_CONNECTED }; nrf_i2s_pins_set(NRF_I2S0_S, &pins); // Configure I2S nrf_i2s_config_t config = { .mode = NRF_I2S_MODE_MASTER, .format = NRF_I2S_FORMAT_I2S, .alignment = NRF_I2S_ALIGN_LEFT, .sample_width = NRF_I2S_SWIDTH_16BIT, .channels = NRF_I2S_CHANNELS_LEFT, .mck_setup = NRF_I2S_MCK_32MDIV23, .ratio = NRF_I2S_RATIO_32X }; nrf_i2s_configure(NRF_I2S0_S, &config); //setting up the I2S transfer nrf_i2s_transfer_set(NRF_I2S0_S, buffer_size/WORD_SIZE, NULL, (uint32_t const *)buffer); //Enable i2s peripheral nrf_i2s_enable(NRF_I2S0_S); //Enable i2s tx event interrupt nrf_i2s_int_enable(NRF_I2S0_S, NRF_I2S_INT_TXPTRUPD_MASK); //Assigning i2s_irq_handler as i2s interrupt handler __NVIC_SetVector(I2S0_IRQn, (uint32_t)i2s_irq_handler); //Setting I2S interrupt priority //Could not find NRFX_I2S_CONFIG_IRQ_PRIORITY thus setting it to 7 NRFX_IRQ_PRIORITY_SET(I2S_IRQn, 7); //Enabling I2S interrupt in NVIC NRFX_IRQ_ENABLE(I2S0_IRQn); //Start I2S nrf_i2s_task_trigger(NRF_I2S0_S, NRF_I2S_TASK_START); } void i2s_irq_handler(void) { //Checking TXPTRUPD event if (nrf_i2s_event_check(NRF_I2S0_S, NRF_I2S_EVENT_TXPTRUPD)) { //clear TXPTRUPD event nrf_i2s_event_clear(NRF_I2S0_S, NRF_I2S_EVENT_TXPTRUPD); //Give I2S the next buffer; assume that buffer is already filled correctly nrf_i2s_tx_buffer_set(NRF_I2S0_S, (uint32_t const *)buffer); } }
(It can be assumed that the buffer is setup already with the right values)
One aspect that I was unsure of was whether to use NRF_I2S0_S or NRF_I2S0_NS. What is the difference between them?
This code as it is halts as soon as it calls the __NVIC_SetVector.
It seems to break as soon as the address of the handler function is written into the vector table.
Approach 2
For this I have used the newer API from nrfx_i2s.h.
Here are the relevant snippets:
#include <stdio.h> #include <zephyr/kernel.h> #include <zephyr/drivers/gpio.h> #include "nrfx.h" #include "nrfx_i2s.h" #include "nrf5340_application.h" uint32_t _sckPin = 14; // Pin P0.14 uint32_t _lrckPin = 16; // Pin P0.16 uint32_t _sdoutPin = 15; // Pin P0.15 #define WORD_SIZE 4 // 1 word = 4 bytes uint8_t * buffer; uint32_t buffer_size = 1024; // 1 KB // I2S Instance nrfx_i2s_t i2s_instance = NRFX_I2S_INSTANCE(0); void setup(); void i2s_irq_handler(nrfx_i2s_buffers_t const *p_released, uint32_t status); int main(void) { setup(); while (1) { // Do something // refill buffer etc. } } void setup() { // Make default config with pins nrfx_i2s_config_t config = NRFX_I2S_DEFAULT_CONFIG( _sckPin, _lrckPin, NRF_I2S_PIN_NOT_CONNECTED, _sdoutPin, NRF_I2S_PIN_NOT_CONNECTED); // Change mck_setup and ratio config.mck_setup = NRF_I2S_MCK_32MDIV23; config.ratio = NRF_I2S_RATIO_32X; // Initialize I2S nrfx_err_t ret = nrfx_i2s_init(&i2s_instance, &config, i2s_irq_handler); // This returns NRFX_SUCCESS // Setup buffers nrfx_i2s_buffers_t buffers = { .p_rx_buffer = nullptr, .p_tx_buffer = (uint32_t*)buffer, .buffer_size = static_cast<uint16_t>(buffer_size / WORD_SIZE) }; // Start I2S with buffers and 0 flags nrfx_i2s_start(&i2s_instance, &buffers, 0); } void i2s_irq_handler(nrfx_i2s_buffers_t const *p_released, uint32_t status) { if (status == NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED) { nrfx_i2s_buffers_t buffers = { .p_rx_buffer = nullptr, .p_tx_buffer = (uint32_t*)buffer, .buffer_size = static_cast<uint16_t>(buffer_size / WORD_SIZE) }; // assume buffer has been refilled nrfx_i2s_next_buffers_set(&i2s_instance, &buffers); } }
(It can be assumed that the buffer is setup already with the right values)
As soon as nrfx_i2s_start is called the microcontroller halts.
Additionally, there was an issue when compiling. Initially the nrfx_i2s_init was not found. This could be solved by adding the nrfx_i2s.c to the target sources specifically.
Conclusion:
I am stuck on how to continue and how to debug this.
Does anyone have a clue what I have been missing?
Foot note:
It has generally been quite difficult to search for good guides regarding I2S and the nrf series. Especially examples/guides regarding development with Arduino/platformio that actually work.