Initial commit: MT firmware project
- BLE peripheral applications - dr_piezo and bladder_patch projects Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
445
modules/nrfx/drivers/src/nrfx_i2s.c
Normal file
445
modules/nrfx/drivers/src/nrfx_i2s.c
Normal file
@@ -0,0 +1,445 @@
|
||||
/**
|
||||
* Copyright (c) 2015 - 2021, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <nrfx.h>
|
||||
|
||||
#if NRFX_CHECK(NRFX_I2S_ENABLED)
|
||||
|
||||
#include <nrfx_i2s.h>
|
||||
#include <hal/nrf_gpio.h>
|
||||
|
||||
#define NRFX_LOG_MODULE I2S
|
||||
#include <nrfx_log.h>
|
||||
|
||||
#define EVT_TO_STR(event) \
|
||||
(event == NRF_I2S_EVENT_RXPTRUPD ? "NRF_I2S_EVENT_RXPTRUPD" : \
|
||||
(event == NRF_I2S_EVENT_TXPTRUPD ? "NRF_I2S_EVENT_TXPTRUPD" : \
|
||||
(event == NRF_I2S_EVENT_STOPPED ? "NRF_I2S_EVENT_STOPPED" : \
|
||||
"UNKNOWN EVENT")))
|
||||
|
||||
#if !defined(USE_WORKAROUND_FOR_I2S_STOP_ANOMALY) && \
|
||||
(defined(NRF52832_XXAA) || defined(NRF52832_XXAB) || defined(NRF52840_XXAA) || \
|
||||
defined(NRF9160_XXAA))
|
||||
// Enable workaround for nRF52832 and nRF52840 anomaly 194 / nrf9160 anomaly 1
|
||||
// (STOP task does not switch off all resources).
|
||||
#define USE_WORKAROUND_FOR_I2S_STOP_ANOMALY 1
|
||||
#endif
|
||||
|
||||
// Control block - driver instance local data.
|
||||
typedef struct
|
||||
{
|
||||
nrfx_i2s_data_handler_t handler;
|
||||
nrfx_drv_state_t state;
|
||||
|
||||
bool use_rx : 1;
|
||||
bool use_tx : 1;
|
||||
bool rx_ready : 1;
|
||||
bool tx_ready : 1;
|
||||
bool buffers_needed : 1;
|
||||
bool buffers_reused : 1;
|
||||
|
||||
uint16_t buffer_size;
|
||||
nrfx_i2s_buffers_t next_buffers;
|
||||
nrfx_i2s_buffers_t current_buffers;
|
||||
} i2s_control_block_t;
|
||||
static i2s_control_block_t m_cb;
|
||||
|
||||
|
||||
static void configure_pins(nrfx_i2s_config_t const * p_config)
|
||||
{
|
||||
uint32_t mck_pin, sdout_pin, sdin_pin;
|
||||
|
||||
// Configure pins used by the peripheral:
|
||||
|
||||
// - SCK and LRCK (required) - depending on the mode of operation these
|
||||
// pins are configured as outputs (in Master mode) or inputs (in Slave
|
||||
// mode).
|
||||
if (p_config->mode == NRF_I2S_MODE_MASTER)
|
||||
{
|
||||
nrf_gpio_cfg_output(p_config->sck_pin);
|
||||
nrf_gpio_cfg_output(p_config->lrck_pin);
|
||||
}
|
||||
else
|
||||
{
|
||||
nrf_gpio_cfg_input(p_config->sck_pin, NRF_GPIO_PIN_NOPULL);
|
||||
nrf_gpio_cfg_input(p_config->lrck_pin, NRF_GPIO_PIN_NOPULL);
|
||||
}
|
||||
|
||||
// - MCK (optional) - always output,
|
||||
if (p_config->mck_pin != NRFX_I2S_PIN_NOT_USED)
|
||||
{
|
||||
mck_pin = p_config->mck_pin;
|
||||
nrf_gpio_cfg_output(mck_pin);
|
||||
}
|
||||
else
|
||||
{
|
||||
mck_pin = NRF_I2S_PIN_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
// - SDOUT (optional) - always output,
|
||||
if (p_config->sdout_pin != NRFX_I2S_PIN_NOT_USED)
|
||||
{
|
||||
sdout_pin = p_config->sdout_pin;
|
||||
nrf_gpio_cfg_output(sdout_pin);
|
||||
}
|
||||
else
|
||||
{
|
||||
sdout_pin = NRF_I2S_PIN_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
// - SDIN (optional) - always input.
|
||||
if (p_config->sdin_pin != NRFX_I2S_PIN_NOT_USED)
|
||||
{
|
||||
sdin_pin = p_config->sdin_pin;
|
||||
nrf_gpio_cfg_input(sdin_pin, NRF_GPIO_PIN_NOPULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
sdin_pin = NRF_I2S_PIN_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
nrf_i2s_pins_set(NRF_I2S,
|
||||
p_config->sck_pin,
|
||||
p_config->lrck_pin,
|
||||
mck_pin,
|
||||
sdout_pin,
|
||||
sdin_pin);
|
||||
}
|
||||
|
||||
|
||||
nrfx_err_t nrfx_i2s_init(nrfx_i2s_config_t const * p_config,
|
||||
nrfx_i2s_data_handler_t handler)
|
||||
{
|
||||
NRFX_ASSERT(p_config);
|
||||
NRFX_ASSERT(handler);
|
||||
|
||||
nrfx_err_t err_code;
|
||||
|
||||
if (m_cb.state != NRFX_DRV_STATE_UNINITIALIZED)
|
||||
{
|
||||
err_code = NRFX_ERROR_INVALID_STATE;
|
||||
NRFX_LOG_WARNING("Function: %s, error code: %s.",
|
||||
__func__,
|
||||
NRFX_LOG_ERROR_STRING_GET(err_code));
|
||||
return err_code;
|
||||
}
|
||||
|
||||
if (!nrf_i2s_configure(NRF_I2S,
|
||||
p_config->mode,
|
||||
p_config->format,
|
||||
p_config->alignment,
|
||||
p_config->sample_width,
|
||||
p_config->channels,
|
||||
p_config->mck_setup,
|
||||
p_config->ratio))
|
||||
{
|
||||
err_code = NRFX_ERROR_INVALID_PARAM;
|
||||
NRFX_LOG_WARNING("Function: %s, error code: %s.",
|
||||
__func__,
|
||||
NRFX_LOG_ERROR_STRING_GET(err_code));
|
||||
return err_code;
|
||||
}
|
||||
configure_pins(p_config);
|
||||
|
||||
m_cb.handler = handler;
|
||||
|
||||
NRFX_IRQ_PRIORITY_SET(I2S_IRQn, p_config->irq_priority);
|
||||
NRFX_IRQ_ENABLE(I2S_IRQn);
|
||||
|
||||
m_cb.state = NRFX_DRV_STATE_INITIALIZED;
|
||||
|
||||
NRFX_LOG_INFO("Initialized.");
|
||||
return NRFX_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void nrfx_i2s_uninit(void)
|
||||
{
|
||||
NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
|
||||
|
||||
nrfx_i2s_stop();
|
||||
|
||||
NRFX_IRQ_DISABLE(I2S_IRQn);
|
||||
|
||||
nrf_i2s_pins_set(NRF_I2S,
|
||||
NRF_I2S_PIN_NOT_CONNECTED,
|
||||
NRF_I2S_PIN_NOT_CONNECTED,
|
||||
NRF_I2S_PIN_NOT_CONNECTED,
|
||||
NRF_I2S_PIN_NOT_CONNECTED,
|
||||
NRF_I2S_PIN_NOT_CONNECTED);
|
||||
|
||||
m_cb.state = NRFX_DRV_STATE_UNINITIALIZED;
|
||||
NRFX_LOG_INFO("Uninitialized.");
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
NRFX_ASSERT((p_initial_buffers->p_rx_buffer == NULL) ||
|
||||
(nrfx_is_in_ram(p_initial_buffers->p_rx_buffer) &&
|
||||
nrfx_is_word_aligned(p_initial_buffers->p_rx_buffer)));
|
||||
NRFX_ASSERT((p_initial_buffers->p_tx_buffer == NULL) ||
|
||||
(nrfx_is_in_ram(p_initial_buffers->p_tx_buffer) &&
|
||||
nrfx_is_word_aligned(p_initial_buffers->p_tx_buffer)));
|
||||
NRFX_ASSERT(buffer_size != 0);
|
||||
(void)(flags);
|
||||
|
||||
nrfx_err_t err_code;
|
||||
|
||||
if (m_cb.state != NRFX_DRV_STATE_INITIALIZED)
|
||||
{
|
||||
err_code = NRFX_ERROR_INVALID_STATE;
|
||||
NRFX_LOG_WARNING("Function: %s, error code: %s.",
|
||||
__func__,
|
||||
NRFX_LOG_ERROR_STRING_GET(err_code));
|
||||
return err_code;
|
||||
}
|
||||
|
||||
if (((p_initial_buffers->p_rx_buffer != NULL)
|
||||
&& !nrfx_is_in_ram(p_initial_buffers->p_rx_buffer))
|
||||
||
|
||||
((p_initial_buffers->p_tx_buffer != NULL)
|
||||
&& !nrfx_is_in_ram(p_initial_buffers->p_tx_buffer)))
|
||||
{
|
||||
err_code = NRFX_ERROR_INVALID_ADDR;
|
||||
NRFX_LOG_WARNING("Function: %s, error code: %s.",
|
||||
__func__,
|
||||
NRFX_LOG_ERROR_STRING_GET(err_code));
|
||||
return err_code;
|
||||
}
|
||||
|
||||
m_cb.use_rx = (p_initial_buffers->p_rx_buffer != NULL);
|
||||
m_cb.use_tx = (p_initial_buffers->p_tx_buffer != NULL);
|
||||
m_cb.rx_ready = false;
|
||||
m_cb.tx_ready = false;
|
||||
m_cb.buffers_needed = false;
|
||||
m_cb.buffer_size = buffer_size;
|
||||
|
||||
// Set the provided initial buffers as next, they will become the current
|
||||
// ones after the IRQ handler is called for the first time, what will occur
|
||||
// right after the START task is triggered.
|
||||
m_cb.next_buffers = *p_initial_buffers;
|
||||
m_cb.current_buffers.p_rx_buffer = NULL;
|
||||
m_cb.current_buffers.p_tx_buffer = NULL;
|
||||
|
||||
nrf_i2s_transfer_set(NRF_I2S,
|
||||
m_cb.buffer_size,
|
||||
m_cb.next_buffers.p_rx_buffer,
|
||||
m_cb.next_buffers.p_tx_buffer);
|
||||
|
||||
nrf_i2s_enable(NRF_I2S);
|
||||
|
||||
m_cb.state = NRFX_DRV_STATE_POWERED_ON;
|
||||
|
||||
nrf_i2s_event_clear(NRF_I2S, NRF_I2S_EVENT_RXPTRUPD);
|
||||
nrf_i2s_event_clear(NRF_I2S, NRF_I2S_EVENT_TXPTRUPD);
|
||||
nrf_i2s_event_clear(NRF_I2S, NRF_I2S_EVENT_STOPPED);
|
||||
nrf_i2s_int_enable(NRF_I2S, (m_cb.use_rx ? NRF_I2S_INT_RXPTRUPD_MASK : 0) |
|
||||
(m_cb.use_tx ? NRF_I2S_INT_TXPTRUPD_MASK : 0) |
|
||||
NRF_I2S_INT_STOPPED_MASK);
|
||||
nrf_i2s_task_trigger(NRF_I2S, NRF_I2S_TASK_START);
|
||||
|
||||
NRFX_LOG_INFO("Started.");
|
||||
return NRFX_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
nrfx_err_t nrfx_i2s_next_buffers_set(nrfx_i2s_buffers_t const * p_buffers)
|
||||
{
|
||||
NRFX_ASSERT(m_cb.state == NRFX_DRV_STATE_POWERED_ON);
|
||||
NRFX_ASSERT(p_buffers);
|
||||
NRFX_ASSERT((p_buffers->p_rx_buffer == NULL) ||
|
||||
(nrfx_is_in_ram(p_buffers->p_rx_buffer) &&
|
||||
nrfx_is_word_aligned(p_buffers->p_rx_buffer)));
|
||||
NRFX_ASSERT((p_buffers->p_tx_buffer == NULL) ||
|
||||
(nrfx_is_in_ram(p_buffers->p_tx_buffer) &&
|
||||
nrfx_is_word_aligned(p_buffers->p_tx_buffer)));
|
||||
|
||||
nrfx_err_t err_code;
|
||||
|
||||
if (!m_cb.buffers_needed)
|
||||
{
|
||||
err_code = NRFX_ERROR_INVALID_STATE;
|
||||
NRFX_LOG_WARNING("Function: %s, error code: %s.",
|
||||
__func__,
|
||||
NRFX_LOG_ERROR_STRING_GET(err_code));
|
||||
return err_code;
|
||||
}
|
||||
|
||||
if (((p_buffers->p_rx_buffer != NULL)
|
||||
&& !nrfx_is_in_ram(p_buffers->p_rx_buffer))
|
||||
||
|
||||
((p_buffers->p_tx_buffer != NULL)
|
||||
&& !nrfx_is_in_ram(p_buffers->p_tx_buffer)))
|
||||
{
|
||||
err_code = NRFX_ERROR_INVALID_ADDR;
|
||||
NRFX_LOG_WARNING("Function: %s, error code: %s.",
|
||||
__func__,
|
||||
NRFX_LOG_ERROR_STRING_GET(err_code));
|
||||
return err_code;
|
||||
}
|
||||
|
||||
if (m_cb.use_tx)
|
||||
{
|
||||
NRFX_ASSERT(p_buffers->p_tx_buffer != NULL);
|
||||
nrf_i2s_tx_buffer_set(NRF_I2S, p_buffers->p_tx_buffer);
|
||||
}
|
||||
if (m_cb.use_rx)
|
||||
{
|
||||
NRFX_ASSERT(p_buffers->p_rx_buffer != NULL);
|
||||
nrf_i2s_rx_buffer_set(NRF_I2S, p_buffers->p_rx_buffer);
|
||||
}
|
||||
|
||||
m_cb.next_buffers = *p_buffers;
|
||||
m_cb.buffers_needed = false;
|
||||
|
||||
return NRFX_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void nrfx_i2s_stop(void)
|
||||
{
|
||||
NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
|
||||
|
||||
m_cb.buffers_needed = false;
|
||||
|
||||
// First disable interrupts, then trigger the STOP task, so no spurious
|
||||
// RXPTRUPD and TXPTRUPD events (see nRF52 anomaly 55) are processed.
|
||||
nrf_i2s_int_disable(NRF_I2S, NRF_I2S_INT_RXPTRUPD_MASK |
|
||||
NRF_I2S_INT_TXPTRUPD_MASK);
|
||||
nrf_i2s_task_trigger(NRF_I2S, NRF_I2S_TASK_STOP);
|
||||
|
||||
#if NRFX_CHECK(USE_WORKAROUND_FOR_I2S_STOP_ANOMALY)
|
||||
*((volatile uint32_t *)(((uint32_t)NRF_I2S) + 0x38)) = 1;
|
||||
*((volatile uint32_t *)(((uint32_t)NRF_I2S) + 0x3C)) = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void nrfx_i2s_irq_handler(void)
|
||||
{
|
||||
if (nrf_i2s_event_check(NRF_I2S, NRF_I2S_EVENT_TXPTRUPD))
|
||||
{
|
||||
nrf_i2s_event_clear(NRF_I2S, NRF_I2S_EVENT_TXPTRUPD);
|
||||
m_cb.tx_ready = true;
|
||||
if (m_cb.use_tx && m_cb.buffers_needed)
|
||||
{
|
||||
m_cb.buffers_reused = true;
|
||||
}
|
||||
}
|
||||
if (nrf_i2s_event_check(NRF_I2S, NRF_I2S_EVENT_RXPTRUPD))
|
||||
{
|
||||
nrf_i2s_event_clear(NRF_I2S, NRF_I2S_EVENT_RXPTRUPD);
|
||||
m_cb.rx_ready = true;
|
||||
if (m_cb.use_rx && m_cb.buffers_needed)
|
||||
{
|
||||
m_cb.buffers_reused = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (nrf_i2s_event_check(NRF_I2S, NRF_I2S_EVENT_STOPPED))
|
||||
{
|
||||
nrf_i2s_event_clear(NRF_I2S, NRF_I2S_EVENT_STOPPED);
|
||||
nrf_i2s_int_disable(NRF_I2S, NRF_I2S_INT_STOPPED_MASK);
|
||||
nrf_i2s_disable(NRF_I2S);
|
||||
|
||||
// When stopped, release all buffers, including these scheduled for
|
||||
// the next transfer.
|
||||
m_cb.handler(&m_cb.current_buffers, 0);
|
||||
m_cb.handler(&m_cb.next_buffers, 0);
|
||||
|
||||
m_cb.state = NRFX_DRV_STATE_INITIALIZED;
|
||||
NRFX_LOG_INFO("Stopped.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if the requested transfer has been completed:
|
||||
// - full-duplex mode
|
||||
if ((m_cb.use_tx && m_cb.use_rx && m_cb.tx_ready && m_cb.rx_ready) ||
|
||||
// - TX only mode
|
||||
(!m_cb.use_rx && m_cb.tx_ready) ||
|
||||
// - RX only mode
|
||||
(!m_cb.use_tx && m_cb.rx_ready))
|
||||
{
|
||||
m_cb.tx_ready = false;
|
||||
m_cb.rx_ready = false;
|
||||
|
||||
// If the application did not supply the buffers for the next
|
||||
// part of the transfer until this moment, the current buffers
|
||||
// cannot be released, since the I2S peripheral already started
|
||||
// using them. Signal this situation to the application by
|
||||
// passing NULL instead of the structure with released buffers.
|
||||
if (m_cb.buffers_reused)
|
||||
{
|
||||
m_cb.buffers_reused = false;
|
||||
// This will most likely be set at this point. However, there is
|
||||
// a small time window between TXPTRUPD and RXPTRUPD events,
|
||||
// and it is theoretically possible that next buffers will be
|
||||
// set in this window, so to be sure this flag is set to true,
|
||||
// set it explicitly.
|
||||
m_cb.buffers_needed = true;
|
||||
m_cb.handler(NULL,
|
||||
NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Buffers that have been used by the I2S peripheral (current)
|
||||
// are now released and will be returned to the application,
|
||||
// and the ones scheduled to be used as next become the current
|
||||
// ones.
|
||||
nrfx_i2s_buffers_t released_buffers = m_cb.current_buffers;
|
||||
m_cb.current_buffers = m_cb.next_buffers;
|
||||
m_cb.next_buffers.p_rx_buffer = NULL;
|
||||
m_cb.next_buffers.p_tx_buffer = NULL;
|
||||
m_cb.buffers_needed = true;
|
||||
m_cb.handler(&released_buffers,
|
||||
NRFX_I2S_STATUS_NEXT_BUFFERS_NEEDED);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // NRFX_CHECK(NRFX_I2S_ENABLED)
|
||||
Reference in New Issue
Block a user