123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- /**
- * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form, except as embedded into a Nordic
- * Semiconductor ASA integrated circuit in a product or a software update for
- * such product, must reproduce the above copyright notice, this list of
- * conditions and the following disclaimer in the documentation and/or other
- * materials provided with the distribution.
- *
- * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
- * contributors may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * 4. This software, with or without modification, must only be used with a
- * Nordic Semiconductor ASA integrated circuit.
- *
- * 5. Any software provided in binary form under this license must not be reverse
- * engineered, decompiled, modified and/or disassembled.
- *
- * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
- * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
- /** @file
- * @defgroup i2s_example_main main.c
- * @{
- * @ingroup i2s_example
- *
- * @brief I2S Example Application main file.
- *
- * This file contains the source code for a sample application using I2S.
- */
- #include <stdio.h>
- #include "nrf_drv_i2s.h"
- #include "nrf_delay.h"
- #include "app_util_platform.h"
- #include "app_error.h"
- #include "boards.h"
- #include "nrf_log.h"
- #include "nrf_log_ctrl.h"
- #include "nrf_log_default_backends.h"
- #define LED_OK BSP_BOARD_LED_0
- #define LED_ERROR BSP_BOARD_LED_1
- #define I2S_DATA_BLOCK_WORDS 512
- static uint32_t m_buffer_rx[2][I2S_DATA_BLOCK_WORDS];
- static uint32_t m_buffer_tx[2][I2S_DATA_BLOCK_WORDS];
- // Delay time between consecutive I2S transfers performed in the main loop
- // (in milliseconds).
- #define PAUSE_TIME 500
- // Number of blocks of data to be contained in each transfer.
- #define BLOCKS_TO_TRANSFER 20
- static uint8_t volatile m_blocks_transferred = 0;
- static uint8_t m_zero_samples_to_ignore = 0;
- static uint16_t m_sample_value_to_send;
- static uint16_t m_sample_value_expected;
- static bool m_error_encountered;
- static uint32_t * volatile mp_block_to_fill = NULL;
- static uint32_t const * volatile mp_block_to_check = NULL;
- static void prepare_tx_data(uint32_t * p_block)
- {
- // These variables will be both zero only at the very beginning of each
- // transfer, so we use them as the indication that the re-initialization
- // should be performed.
- if (m_blocks_transferred == 0 && m_zero_samples_to_ignore == 0)
- {
- // Number of initial samples (actually pairs of L/R samples) with zero
- // values that should be ignored - see the comment in 'check_samples'.
- m_zero_samples_to_ignore = 2;
- m_sample_value_to_send = 0xCAFE;
- m_sample_value_expected = 0xCAFE;
- m_error_encountered = false;
- }
- // [each data word contains two 16-bit samples]
- uint16_t i;
- for (i = 0; i < I2S_DATA_BLOCK_WORDS; ++i)
- {
- uint16_t sample_l = m_sample_value_to_send - 1;
- uint16_t sample_r = m_sample_value_to_send + 1;
- ++m_sample_value_to_send;
- uint32_t * p_word = &p_block[i];
- ((uint16_t *)p_word)[0] = sample_l;
- ((uint16_t *)p_word)[1] = sample_r;
- }
- }
- static bool check_samples(uint32_t const * p_block)
- {
- // [each data word contains two 16-bit samples]
- uint16_t i;
- for (i = 0; i < I2S_DATA_BLOCK_WORDS; ++i)
- {
- uint32_t const * p_word = &p_block[i];
- uint16_t actual_sample_l = ((uint16_t const *)p_word)[0];
- uint16_t actual_sample_r = ((uint16_t const *)p_word)[1];
- // Normally a couple of initial samples sent by the I2S peripheral
- // will have zero values, because it starts to output the clock
- // before the actual data is fetched by EasyDMA. As we are dealing
- // with streaming the initial zero samples can be simply ignored.
- if (m_zero_samples_to_ignore > 0 &&
- actual_sample_l == 0 &&
- actual_sample_r == 0)
- {
- --m_zero_samples_to_ignore;
- }
- else
- {
- m_zero_samples_to_ignore = 0;
- uint16_t expected_sample_l = m_sample_value_expected - 1;
- uint16_t expected_sample_r = m_sample_value_expected + 1;
- ++m_sample_value_expected;
- if (actual_sample_l != expected_sample_l ||
- actual_sample_r != expected_sample_r)
- {
- NRF_LOG_INFO("%3u: %04x/%04x, expected: %04x/%04x (i: %u)",
- m_blocks_transferred, actual_sample_l, actual_sample_r,
- expected_sample_l, expected_sample_r, i);
- return false;
- }
- }
- }
- NRF_LOG_INFO("%3u: OK", m_blocks_transferred);
- return true;
- }
- static void check_rx_data(uint32_t const * p_block)
- {
- ++m_blocks_transferred;
- if (!m_error_encountered)
- {
- m_error_encountered = !check_samples(p_block);
- }
- if (m_error_encountered)
- {
- bsp_board_led_off(LED_OK);
- bsp_board_led_invert(LED_ERROR);
- }
- else
- {
- bsp_board_led_off(LED_ERROR);
- bsp_board_led_invert(LED_OK);
- }
- }
- static void data_handler(nrf_drv_i2s_buffers_t const * p_released,
- uint32_t status)
- {
- // 'nrf_drv_i2s_next_buffers_set' is called directly from the handler
- // each time next buffers are requested, so data corruption is not
- // expected.
- ASSERT(p_released);
- // When the handler is called after the transfer has been stopped
- // (no next buffers are needed, only the used buffers are to be
- // released), there is nothing to do.
- if (!(status & NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED))
- {
- return;
- }
- // First call of this handler occurs right after the transfer is started.
- // No data has been transferred yet at this point, so there is nothing to
- // check. Only the buffers for the next part of the transfer should be
- // provided.
- if (!p_released->p_rx_buffer)
- {
- nrf_drv_i2s_buffers_t const next_buffers = {
- .p_rx_buffer = m_buffer_rx[1],
- .p_tx_buffer = m_buffer_tx[1],
- };
- APP_ERROR_CHECK(nrf_drv_i2s_next_buffers_set(&next_buffers));
- mp_block_to_fill = m_buffer_tx[1];
- }
- else
- {
- mp_block_to_check = p_released->p_rx_buffer;
- // The driver has just finished accessing the buffers pointed by
- // 'p_released'. They can be used for the next part of the transfer
- // that will be scheduled now.
- APP_ERROR_CHECK(nrf_drv_i2s_next_buffers_set(p_released));
- // The pointer needs to be typecasted here, so that it is possible to
- // modify the content it is pointing to (it is marked in the structure
- // as pointing to constant data because the driver is not supposed to
- // modify the provided data).
- mp_block_to_fill = (uint32_t *)p_released->p_tx_buffer;
- }
- }
- void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info)
- {
- bsp_board_leds_on();
- app_error_save_and_stop(id, pc, info);
- }
- int main(void)
- {
- uint32_t err_code = NRF_SUCCESS;
- bsp_board_init(BSP_INIT_LEDS);
- err_code = NRF_LOG_INIT(NULL);
- APP_ERROR_CHECK(err_code);
- NRF_LOG_DEFAULT_BACKENDS_INIT();
- NRF_LOG_INFO("I2S loopback example started.");
- nrf_drv_i2s_config_t config = NRF_DRV_I2S_DEFAULT_CONFIG;
- // In Master mode the MCK frequency and the MCK/LRCK ratio should be
- // set properly in order to achieve desired audio sample rate (which
- // is equivalent to the LRCK frequency).
- // For the following settings we'll get the LRCK frequency equal to
- // 15873 Hz (the closest one to 16 kHz that is possible to achieve).
- config.sdin_pin = I2S_SDIN_PIN;
- config.sdout_pin = I2S_SDOUT_PIN;
- config.mck_setup = NRF_I2S_MCK_32MDIV21;
- config.ratio = NRF_I2S_RATIO_96X;
- config.channels = NRF_I2S_CHANNELS_STEREO;
- err_code = nrf_drv_i2s_init(&config, data_handler);
- APP_ERROR_CHECK(err_code);
- for (;;)
- {
- m_blocks_transferred = 0;
- mp_block_to_fill = NULL;
- mp_block_to_check = NULL;
- prepare_tx_data(m_buffer_tx[0]);
- nrf_drv_i2s_buffers_t const initial_buffers = {
- .p_tx_buffer = m_buffer_tx[0],
- .p_rx_buffer = m_buffer_rx[0],
- };
- err_code = nrf_drv_i2s_start(&initial_buffers, I2S_DATA_BLOCK_WORDS, 0);
- APP_ERROR_CHECK(err_code);
- do {
- // Wait for an event.
- __WFE();
- // Clear the event register.
- __SEV();
- __WFE();
- if (mp_block_to_fill)
- {
- prepare_tx_data(mp_block_to_fill);
- mp_block_to_fill = NULL;
- }
- if (mp_block_to_check)
- {
- check_rx_data(mp_block_to_check);
- mp_block_to_check = NULL;
- }
- } while (m_blocks_transferred < BLOCKS_TO_TRANSFER);
- nrf_drv_i2s_stop();
- NRF_LOG_FLUSH();
- bsp_board_leds_off();
- nrf_delay_ms(PAUSE_TIME);
- }
- }
- /** @} */
|