Library to handle the X-NUCLEO-CCA02M1 MEMS Microphones Expansion Board.

Dependencies:   ST_I2S ST_FREQUENCY_DIVIDER USBDEVICE

Dependents:   HelloWorld_CCA02M1 HelloWorld_CCA02M1_mbedOS HelloWorld_CCA02M1 Karaoke_CCA01M1_CCA02M1_mbedOS

Fork of X_NUCLEO_CCA02M1 by ST Expansion SW Team

MEMS Microphones Library

Library to handle the X-NUCLEO-CCA02M1 MEMS Microphones Expansion Board. A single board allows to record a standard 2-channel stereo signal as an array of PCM samples (16 bit/sample); in principle, it could make use of six additional MEMS microphones to realize a 8-channel audio system.


Microphones configuration

Currently the configurations supported are the following:

  • Stereo@48KHz
  • Stereo@44.1KHz (CD audio quality)
  • Stereo@32KHz
  • Stereo@16KHz
  • Stereo@8KHz
  • Mono@48KHz
  • Mono@44.1KHz
  • Mono@32KHz
  • Mono@16KHz
  • Mono@8KHz

Mono configurations need a Jumper connecting PB_5 and PB_13 on the Morpho connector to properly work.


Platform compatibility

  • This board can be currently used with the Nucleo F4 Family only, please see the ST_I2S library compatibility for further information.
  • The library is compatible both with mbed OS 5.x and mbed classic 2.x (to work with mbed classic, the main application has to import the "events" library, which is not included into the "mbed" library).


I2S Peripheral Usage

By default this board makes use of the I2S peripheral available on Nucleo boards.


Acquiring through the USB

In order to acquire the recorded PCM audio channel with an audio SW on a PC, please connect the expansion board to a USB port of the PC, and the Nucleo board to a USB power supply.

Committer:
davide.aliprandi@st.com
Date:
Tue Feb 28 11:20:53 2017 +0100
Revision:
0:d5552d432108
Library to handle the X-NUCLEO-CCA02M1 MEMS Microphones Expansion Board.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
davide.aliprandi@st.com 0:d5552d432108 1 /**
davide.aliprandi@st.com 0:d5552d432108 2 ******************************************************************************
davide.aliprandi@st.com 0:d5552d432108 3 * @file x_nucleo_cca02m1_class.cpp
davide.aliprandi@st.com 0:d5552d432108 4 * @author AST / Software Platforms and Cloud
davide.aliprandi@st.com 0:d5552d432108 5 * @version V1.0
davide.aliprandi@st.com 0:d5552d432108 6 * @date October 17th, 2016
davide.aliprandi@st.com 0:d5552d432108 7 * @brief Implementation file for the X_NUCLEO_CCA02M1 expansion board.
davide.aliprandi@st.com 0:d5552d432108 8 ******************************************************************************
davide.aliprandi@st.com 0:d5552d432108 9 * @attention
davide.aliprandi@st.com 0:d5552d432108 10 *
davide.aliprandi@st.com 0:d5552d432108 11 * <h2><center>&copy; COPYRIGHT(c) 2015 STMicroelectronics</center></h2>
davide.aliprandi@st.com 0:d5552d432108 12 *
davide.aliprandi@st.com 0:d5552d432108 13 * Redistribution and use in source and binary forms, with or without modification,
davide.aliprandi@st.com 0:d5552d432108 14 * are permitted provided that the following conditions are met:
davide.aliprandi@st.com 0:d5552d432108 15 * 1. Redistributions of source code must retain the above copyright notice,
davide.aliprandi@st.com 0:d5552d432108 16 * this list of conditions and the following disclaimer.
davide.aliprandi@st.com 0:d5552d432108 17 * 2. Redistributions in binary form must reproduce the above copyright notice,
davide.aliprandi@st.com 0:d5552d432108 18 * this list of conditions and the following disclaimer in the documentation
davide.aliprandi@st.com 0:d5552d432108 19 * and/or other materials provided with the distribution.
davide.aliprandi@st.com 0:d5552d432108 20 * 3. Neither the name of STMicroelectronics nor the names of its contributors
davide.aliprandi@st.com 0:d5552d432108 21 * may be used to endorse or promote products derived from this software
davide.aliprandi@st.com 0:d5552d432108 22 * without specific prior written permission.
davide.aliprandi@st.com 0:d5552d432108 23 *
davide.aliprandi@st.com 0:d5552d432108 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
davide.aliprandi@st.com 0:d5552d432108 25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
davide.aliprandi@st.com 0:d5552d432108 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
davide.aliprandi@st.com 0:d5552d432108 27 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
davide.aliprandi@st.com 0:d5552d432108 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
davide.aliprandi@st.com 0:d5552d432108 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
davide.aliprandi@st.com 0:d5552d432108 30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
davide.aliprandi@st.com 0:d5552d432108 31 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
davide.aliprandi@st.com 0:d5552d432108 32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
davide.aliprandi@st.com 0:d5552d432108 33 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
davide.aliprandi@st.com 0:d5552d432108 34 *
davide.aliprandi@st.com 0:d5552d432108 35 ******************************************************************************
davide.aliprandi@st.com 0:d5552d432108 36 */
davide.aliprandi@st.com 0:d5552d432108 37
davide.aliprandi@st.com 0:d5552d432108 38
davide.aliprandi@st.com 0:d5552d432108 39 /* Includes ------------------------------------------------------------------*/
davide.aliprandi@st.com 0:d5552d432108 40
davide.aliprandi@st.com 0:d5552d432108 41 /* ACTION 1 ------------------------------------------------------------------*
davide.aliprandi@st.com 0:d5552d432108 42 * Include here platform specific header files. *
davide.aliprandi@st.com 0:d5552d432108 43 *----------------------------------------------------------------------------*/
davide.aliprandi@st.com 0:d5552d432108 44 #include "mbed.h"
davide.aliprandi@st.com 0:d5552d432108 45 /* ACTION 2 ------------------------------------------------------------------*
davide.aliprandi@st.com 0:d5552d432108 46 * Include here expansion board specific header files. *
davide.aliprandi@st.com 0:d5552d432108 47 *----------------------------------------------------------------------------*/
davide.aliprandi@st.com 0:d5552d432108 48 #include "x_nucleo_cca02m1_class.h"
davide.aliprandi@st.com 0:d5552d432108 49
davide.aliprandi@st.com 0:d5552d432108 50
davide.aliprandi@st.com 0:d5552d432108 51 /* Variables -----------------------------------------------------------------*/
davide.aliprandi@st.com 0:d5552d432108 52
davide.aliprandi@st.com 0:d5552d432108 53 /* Indexes of the PCM buffer. */
davide.aliprandi@st.com 0:d5552d432108 54 uint32_t X_NUCLEO_CCA02M1::_PCM_buffer_read_index = 0;
davide.aliprandi@st.com 0:d5552d432108 55 uint32_t X_NUCLEO_CCA02M1::_PCM_buffer_write_index = 0;
davide.aliprandi@st.com 0:d5552d432108 56
davide.aliprandi@st.com 0:d5552d432108 57 /* Number of expansion boards. */
davide.aliprandi@st.com 0:d5552d432108 58 uint8_t X_NUCLEO_CCA02M1::_number_of_boards = 0;
davide.aliprandi@st.com 0:d5552d432108 59
davide.aliprandi@st.com 0:d5552d432108 60 /* Accessing shared areas: the PCM buffer. */
davide.aliprandi@st.com 0:d5552d432108 61 SingletonPtr<PlatformMutex> X_NUCLEO_CCA02M1::_mutex;
davide.aliprandi@st.com 0:d5552d432108 62
davide.aliprandi@st.com 0:d5552d432108 63
davide.aliprandi@st.com 0:d5552d432108 64 /* Methods -------------------------------------------------------------------*/
davide.aliprandi@st.com 0:d5552d432108 65
davide.aliprandi@st.com 0:d5552d432108 66 /**
davide.aliprandi@st.com 0:d5552d432108 67 * @brief Initializing the X_NUCLEO_CCA02M1 board.
davide.aliprandi@st.com 0:d5552d432108 68 * @param init Pointer to device specific initalization structure.
davide.aliprandi@st.com 0:d5552d432108 69 * @retval "0" in case of success, an error code otherwise.
davide.aliprandi@st.com 0:d5552d432108 70 */
davide.aliprandi@st.com 0:d5552d432108 71 Status_t X_NUCLEO_CCA02M1::Init(void *init)
davide.aliprandi@st.com 0:d5552d432108 72 {
davide.aliprandi@st.com 0:d5552d432108 73 /* Storing data. */
davide.aliprandi@st.com 0:d5552d432108 74 if (init != NULL)
davide.aliprandi@st.com 0:d5552d432108 75 {
davide.aliprandi@st.com 0:d5552d432108 76 _frequency = (*((X_NUCLEO_CCA02M1_Init_t *) init)).frequency;
davide.aliprandi@st.com 0:d5552d432108 77 _channels = (*((X_NUCLEO_CCA02M1_Init_t *) init)).channels;
davide.aliprandi@st.com 0:d5552d432108 78 }
davide.aliprandi@st.com 0:d5552d432108 79
davide.aliprandi@st.com 0:d5552d432108 80 /* Setting configuration. */
davide.aliprandi@st.com 0:d5552d432108 81 switch (_frequency)
davide.aliprandi@st.com 0:d5552d432108 82 {
davide.aliprandi@st.com 0:d5552d432108 83 case I2S_AUDIOFREQ_8K:
davide.aliprandi@st.com 0:d5552d432108 84 _decimation_factor = 128;
davide.aliprandi@st.com 0:d5552d432108 85 break;
davide.aliprandi@st.com 0:d5552d432108 86
davide.aliprandi@st.com 0:d5552d432108 87 case I2S_AUDIOFREQ_16K:
davide.aliprandi@st.com 0:d5552d432108 88 case I2S_AUDIOFREQ_32K:
davide.aliprandi@st.com 0:d5552d432108 89 case I2S_AUDIOFREQ_48K:
davide.aliprandi@st.com 0:d5552d432108 90 default:
davide.aliprandi@st.com 0:d5552d432108 91 _decimation_factor = 64;
davide.aliprandi@st.com 0:d5552d432108 92 break;
davide.aliprandi@st.com 0:d5552d432108 93 }
davide.aliprandi@st.com 0:d5552d432108 94
davide.aliprandi@st.com 0:d5552d432108 95 /* Buffer sizes in 16-bits samples. */
davide.aliprandi@st.com 0:d5552d432108 96 _PCM_samples_one_ms = ((_frequency * _channels) / 1000);
davide.aliprandi@st.com 0:d5552d432108 97 _PDM_samples_one_ms = ((_PCM_samples_one_ms * _decimation_factor) / 16);
davide.aliprandi@st.com 0:d5552d432108 98 _PDM_samples_two_ms = (_PDM_samples_one_ms << 1);
davide.aliprandi@st.com 0:d5552d432108 99
davide.aliprandi@st.com 0:d5552d432108 100 /* Allocating input and output buffers. */
davide.aliprandi@st.com 0:d5552d432108 101 _PDM_buffer_two_ms = (uint16_t *) calloc(_PDM_samples_two_ms, sizeof(uint16_t));
davide.aliprandi@st.com 0:d5552d432108 102 _PCM_buffer_n_ms = (int16_t *) calloc(_PCM_samples_one_ms * PCM_BUFFER_SIZE_ms, sizeof(uint16_t));
davide.aliprandi@st.com 0:d5552d432108 103
davide.aliprandi@st.com 0:d5552d432108 104 /* Allocating support buffers. */
davide.aliprandi@st.com 0:d5552d432108 105 _PDM_buffer_one_ms = (uint16_t *) calloc(_PDM_samples_one_ms, sizeof(uint16_t));
davide.aliprandi@st.com 0:d5552d432108 106
davide.aliprandi@st.com 0:d5552d432108 107 /* Starting the I2S timer, if needed. */
davide.aliprandi@st.com 0:d5552d432108 108 if (_channels != 1)
davide.aliprandi@st.com 0:d5552d432108 109 if (InitI2STimer() != COMPONENT_OK)
davide.aliprandi@st.com 0:d5552d432108 110 return COMPONENT_ERROR;
davide.aliprandi@st.com 0:d5552d432108 111
davide.aliprandi@st.com 0:d5552d432108 112 /* Initializing the PDM to PCM conversion library. */
davide.aliprandi@st.com 0:d5552d432108 113 if ((_pdm2pcm = new PDM2PCMAudio(_frequency, _channels)) == NULL)
davide.aliprandi@st.com 0:d5552d432108 114 return COMPONENT_ERROR;
davide.aliprandi@st.com 0:d5552d432108 115
davide.aliprandi@st.com 0:d5552d432108 116 /* Setting I2S parameters. */
davide.aliprandi@st.com 0:d5552d432108 117 dev_i2s.mode(MASTER_RX, true);
davide.aliprandi@st.com 0:d5552d432108 118 dev_i2s.audio_frequency(_frequency == I2S_AUDIOFREQ_8K ? 4 * _frequency : 2 * _frequency);
davide.aliprandi@st.com 0:d5552d432108 119 dev_i2s.protocol(MSB);
davide.aliprandi@st.com 0:d5552d432108 120 dev_i2s.format(_channels == 1 ? 16 : 32, _channels == 1 ? 16 : 32, 1);
davide.aliprandi@st.com 0:d5552d432108 121
davide.aliprandi@st.com 0:d5552d432108 122 return COMPONENT_OK;
davide.aliprandi@st.com 0:d5552d432108 123 }
davide.aliprandi@st.com 0:d5552d432108 124
davide.aliprandi@st.com 0:d5552d432108 125 /**
davide.aliprandi@st.com 0:d5552d432108 126 * @brief Enabling transmission via USB.
davide.aliprandi@st.com 0:d5552d432108 127 * @param None.
davide.aliprandi@st.com 0:d5552d432108 128 * @retval "0" in case of success, an error code otherwise.
davide.aliprandi@st.com 0:d5552d432108 129 */
davide.aliprandi@st.com 0:d5552d432108 130 Status_t X_NUCLEO_CCA02M1::EnableUSB(void)
davide.aliprandi@st.com 0:d5552d432108 131 {
davide.aliprandi@st.com 0:d5552d432108 132 /* Initializing the USBAudio object. */
davide.aliprandi@st.com 0:d5552d432108 133 if ((_usb_audio = new USBAudio(32000, 2, _frequency, _channels)) == NULL)
davide.aliprandi@st.com 0:d5552d432108 134 return COMPONENT_ERROR;
davide.aliprandi@st.com 0:d5552d432108 135
davide.aliprandi@st.com 0:d5552d432108 136 /* Allocating support buffers. */
davide.aliprandi@st.com 0:d5552d432108 137 _USB_PCM_buffer_one_ms = (int16_t *) calloc(_PCM_samples_one_ms + _channels, sizeof(uint16_t));
davide.aliprandi@st.com 0:d5552d432108 138
davide.aliprandi@st.com 0:d5552d432108 139 _usb_enabled = true;
davide.aliprandi@st.com 0:d5552d432108 140
davide.aliprandi@st.com 0:d5552d432108 141 return COMPONENT_OK;
davide.aliprandi@st.com 0:d5552d432108 142 }
davide.aliprandi@st.com 0:d5552d432108 143
davide.aliprandi@st.com 0:d5552d432108 144 /**
davide.aliprandi@st.com 0:d5552d432108 145 * @brief Disabling transmission via USB.
davide.aliprandi@st.com 0:d5552d432108 146 * @param None.
davide.aliprandi@st.com 0:d5552d432108 147 * @retval "0" in case of success, an error code otherwise.
davide.aliprandi@st.com 0:d5552d432108 148 */
davide.aliprandi@st.com 0:d5552d432108 149 Status_t X_NUCLEO_CCA02M1::DisableUSB(void)
davide.aliprandi@st.com 0:d5552d432108 150 {
davide.aliprandi@st.com 0:d5552d432108 151 /* Freeing memory for the USBAudio object and support buffers. */
davide.aliprandi@st.com 0:d5552d432108 152 delete _usb_audio;
davide.aliprandi@st.com 0:d5552d432108 153 free(_USB_PCM_buffer_one_ms);
davide.aliprandi@st.com 0:d5552d432108 154
davide.aliprandi@st.com 0:d5552d432108 155 _usb_enabled = false;
davide.aliprandi@st.com 0:d5552d432108 156
davide.aliprandi@st.com 0:d5552d432108 157 return COMPONENT_OK;
davide.aliprandi@st.com 0:d5552d432108 158 }
davide.aliprandi@st.com 0:d5552d432108 159
davide.aliprandi@st.com 0:d5552d432108 160 /*
davide.aliprandi@st.com 0:d5552d432108 161 * @brief Start recording audio.
davide.aliprandi@st.com 0:d5552d432108 162 * @param None.
davide.aliprandi@st.com 0:d5552d432108 163 * @retval "0" in case of success, an error code otherwise.
davide.aliprandi@st.com 0:d5552d432108 164 */
davide.aliprandi@st.com 0:d5552d432108 165 Status_t X_NUCLEO_CCA02M1::Record(void)
davide.aliprandi@st.com 0:d5552d432108 166 {
davide.aliprandi@st.com 0:d5552d432108 167 /* Reading microphones via I2S. */
davide.aliprandi@st.com 0:d5552d432108 168 int res = dev_i2s.transfer(
davide.aliprandi@st.com 0:d5552d432108 169 (void *) NULL, 0,
davide.aliprandi@st.com 0:d5552d432108 170 (void *) _PDM_buffer_two_ms, _PDM_samples_two_ms * BYTES_PER_SAMPLE,
davide.aliprandi@st.com 0:d5552d432108 171 event_callback_t(this, &X_NUCLEO_CCA02M1::I2SCallback),
davide.aliprandi@st.com 0:d5552d432108 172 I2S_EVENT_ALL
davide.aliprandi@st.com 0:d5552d432108 173 );
davide.aliprandi@st.com 0:d5552d432108 174 if (res != 0)
davide.aliprandi@st.com 0:d5552d432108 175 return COMPONENT_ERROR;
davide.aliprandi@st.com 0:d5552d432108 176
davide.aliprandi@st.com 0:d5552d432108 177 /* Attaching a callback to send data through the USB at a standard frequency
davide.aliprandi@st.com 0:d5552d432108 178 of 1KHz. */
davide.aliprandi@st.com 0:d5552d432108 179 if (_usb_enabled)
davide.aliprandi@st.com 0:d5552d432108 180 _usb_audio->attachTx(this, &X_NUCLEO_CCA02M1::USBHandler);
davide.aliprandi@st.com 0:d5552d432108 181
davide.aliprandi@st.com 0:d5552d432108 182 return COMPONENT_OK;
davide.aliprandi@st.com 0:d5552d432108 183 }
davide.aliprandi@st.com 0:d5552d432108 184
davide.aliprandi@st.com 0:d5552d432108 185 /**
davide.aliprandi@st.com 0:d5552d432108 186 * @brief Attach a user-defined callback that will be executed whenever PCM
davide.aliprandi@st.com 0:d5552d432108 187 * data are ready, i.e. once each millisecond.
davide.aliprandi@st.com 0:d5552d432108 188 * The provided PCM buffer will be filled by the microphones.
davide.aliprandi@st.com 0:d5552d432108 189 * @param fptr Callback to attach.
davide.aliprandi@st.com 0:d5552d432108 190 * @retval None.
davide.aliprandi@st.com 0:d5552d432108 191 */
davide.aliprandi@st.com 0:d5552d432108 192 void X_NUCLEO_CCA02M1::Attach(void (*fptr) (int16_t *PCM_buffer, uint16_t PCM_buffer_bytes))
davide.aliprandi@st.com 0:d5552d432108 193 {
davide.aliprandi@st.com 0:d5552d432108 194 /* Allocating support buffers. */
davide.aliprandi@st.com 0:d5552d432108 195 if ((_USER_PCM_buffer_one_ms = (int16_t *) calloc(_PCM_samples_one_ms, sizeof(uint16_t))) == NULL)
davide.aliprandi@st.com 0:d5552d432108 196 error("Instantiation of support buffers failed.\r\n");
davide.aliprandi@st.com 0:d5552d432108 197
davide.aliprandi@st.com 0:d5552d432108 198 /* Attaching the callback. */
davide.aliprandi@st.com 0:d5552d432108 199 _callback.attach(fptr);
davide.aliprandi@st.com 0:d5552d432108 200 _callback_attached = true;
davide.aliprandi@st.com 0:d5552d432108 201 }
davide.aliprandi@st.com 0:d5552d432108 202
davide.aliprandi@st.com 0:d5552d432108 203 /**
davide.aliprandi@st.com 0:d5552d432108 204 * @brief Attach a user-defined non-static callback that will be executed
davide.aliprandi@st.com 0:d5552d432108 205 * whenever PCM data are ready, i.e. once each millisecond.
davide.aliprandi@st.com 0:d5552d432108 206 * The provided PCM buffer will be filled by the microphones.
davide.aliprandi@st.com 0:d5552d432108 207 * @param tptr Pointer to an object.
davide.aliprandi@st.com 0:d5552d432108 208 * @param mptr Pointer to an object's callback.
davide.aliprandi@st.com 0:d5552d432108 209 * @retval None.
davide.aliprandi@st.com 0:d5552d432108 210 */
davide.aliprandi@st.com 0:d5552d432108 211 template<typename T>
davide.aliprandi@st.com 0:d5552d432108 212 void X_NUCLEO_CCA02M1::Attach(T *tptr, void (T::*mptr) (int16_t *PCM_buffer, uint16_t PCM_buffer_bytes))
davide.aliprandi@st.com 0:d5552d432108 213 {
davide.aliprandi@st.com 0:d5552d432108 214 /* Allocating support buffers. */
davide.aliprandi@st.com 0:d5552d432108 215 if ((_USER_PCM_buffer_one_ms = (int16_t *) calloc(_PCM_samples_one_ms, sizeof(uint16_t))) == NULL)
davide.aliprandi@st.com 0:d5552d432108 216 error("Instantiation of support buffers failed.\r\n");
davide.aliprandi@st.com 0:d5552d432108 217
davide.aliprandi@st.com 0:d5552d432108 218 /* Attaching the callback. */
davide.aliprandi@st.com 0:d5552d432108 219 _callback.attach(tptr, mptr);
davide.aliprandi@st.com 0:d5552d432108 220 _callback_attached = true;
davide.aliprandi@st.com 0:d5552d432108 221 }
davide.aliprandi@st.com 0:d5552d432108 222
davide.aliprandi@st.com 0:d5552d432108 223 /**
davide.aliprandi@st.com 0:d5552d432108 224 * @brief Initializing the I2S timer.
davide.aliprandi@st.com 0:d5552d432108 225 * @param None.
davide.aliprandi@st.com 0:d5552d432108 226 * @retval COMPONENT_OK in case of success, COMPONENT_ERROR otherwise.
davide.aliprandi@st.com 0:d5552d432108 227 */
davide.aliprandi@st.com 0:d5552d432108 228 Status_t X_NUCLEO_CCA02M1::InitI2STimer(void)
davide.aliprandi@st.com 0:d5552d432108 229 {
davide.aliprandi@st.com 0:d5552d432108 230 if (AUDIO_IN_Timer_Init() != COMPONENT_OK)
davide.aliprandi@st.com 0:d5552d432108 231 return COMPONENT_ERROR;
davide.aliprandi@st.com 0:d5552d432108 232
davide.aliprandi@st.com 0:d5552d432108 233 if (AUDIO_IN_Timer_Start() != COMPONENT_OK)
davide.aliprandi@st.com 0:d5552d432108 234 return COMPONENT_ERROR;
davide.aliprandi@st.com 0:d5552d432108 235
davide.aliprandi@st.com 0:d5552d432108 236 return COMPONENT_OK;
davide.aliprandi@st.com 0:d5552d432108 237 }
davide.aliprandi@st.com 0:d5552d432108 238
davide.aliprandi@st.com 0:d5552d432108 239 /**
davide.aliprandi@st.com 0:d5552d432108 240 * @brief I2S callback which is executed whenever PCM data are ready, i.e. once
davide.aliprandi@st.com 0:d5552d432108 241 * each millisecond.
davide.aliprandi@st.com 0:d5552d432108 242 * @param narg Narg flag.
davide.aliprandi@st.com 0:d5552d432108 243 * @retval None.
davide.aliprandi@st.com 0:d5552d432108 244 */
davide.aliprandi@st.com 0:d5552d432108 245 void X_NUCLEO_CCA02M1::I2SCallback(int narg)
davide.aliprandi@st.com 0:d5552d432108 246 {
davide.aliprandi@st.com 0:d5552d432108 247 /* Checking for errors. */
davide.aliprandi@st.com 0:d5552d432108 248 if (!(narg & (I2S_EVENT_RX_COMPLETE | I2S_EVENT_RX_HALF_COMPLETE)))
davide.aliprandi@st.com 0:d5552d432108 249 error("Unexpected transmission event.\r\n");
davide.aliprandi@st.com 0:d5552d432108 250
davide.aliprandi@st.com 0:d5552d432108 251 /* PDM to PCM Conversion. */
davide.aliprandi@st.com 0:d5552d432108 252 if (narg & (I2S_EVENT_RX_COMPLETE | I2S_EVENT_RX_HALF_COMPLETE))
davide.aliprandi@st.com 0:d5552d432108 253 {
davide.aliprandi@st.com 0:d5552d432108 254 #ifdef X_NUCLEO_CCA02M1_DEBUG
davide.aliprandi@st.com 0:d5552d432108 255 _i2s_signal = 1;
davide.aliprandi@st.com 0:d5552d432108 256 #endif
davide.aliprandi@st.com 0:d5552d432108 257
davide.aliprandi@st.com 0:d5552d432108 258 uint32_t PDM_index = (narg & I2S_EVENT_RX_HALF_COMPLETE ? 0 : _PDM_samples_one_ms);
davide.aliprandi@st.com 0:d5552d432108 259 switch (_channels)
davide.aliprandi@st.com 0:d5552d432108 260 {
davide.aliprandi@st.com 0:d5552d432108 261 case 1:
davide.aliprandi@st.com 0:d5552d432108 262 /* Scrambling PDM audio data. */
davide.aliprandi@st.com 0:d5552d432108 263 _pdm2pcm->Scramble(_PDM_buffer_one_ms, &_PDM_buffer_two_ms[PDM_index], _PDM_samples_one_ms);
davide.aliprandi@st.com 0:d5552d432108 264 break;
davide.aliprandi@st.com 0:d5552d432108 265
davide.aliprandi@st.com 0:d5552d432108 266 case 2:
davide.aliprandi@st.com 0:d5552d432108 267 /* Demuxing PDM audio data. */
davide.aliprandi@st.com 0:d5552d432108 268 _pdm2pcm->Demux(_PDM_buffer_one_ms, &_PDM_buffer_two_ms[PDM_index], _PDM_samples_one_ms);
davide.aliprandi@st.com 0:d5552d432108 269 break;
davide.aliprandi@st.com 0:d5552d432108 270 }
davide.aliprandi@st.com 0:d5552d432108 271
davide.aliprandi@st.com 0:d5552d432108 272 /* Acquiring resources. */
davide.aliprandi@st.com 0:d5552d432108 273 _mutex->lock();
davide.aliprandi@st.com 0:d5552d432108 274
davide.aliprandi@st.com 0:d5552d432108 275 /* Converting PDM to PCM audio data. */
davide.aliprandi@st.com 0:d5552d432108 276 _pdm2pcm->Convert(&_PCM_buffer_n_ms[_PCM_buffer_write_index], _PDM_buffer_one_ms, _volume, _decimation_factor);
davide.aliprandi@st.com 0:d5552d432108 277
davide.aliprandi@st.com 0:d5552d432108 278 /* Copying PCM data to the user buffer. */
davide.aliprandi@st.com 0:d5552d432108 279 if (_callback_attached)
davide.aliprandi@st.com 0:d5552d432108 280 memcpy(_USER_PCM_buffer_one_ms, &_PCM_buffer_n_ms[_PCM_buffer_write_index], _PCM_samples_one_ms * BYTES_PER_SAMPLE);
davide.aliprandi@st.com 0:d5552d432108 281
davide.aliprandi@st.com 0:d5552d432108 282 /* Updating write index. */
davide.aliprandi@st.com 0:d5552d432108 283 _PCM_buffer_write_index += _PCM_samples_one_ms;
davide.aliprandi@st.com 0:d5552d432108 284 _PCM_buffer_write_index %= (PCM_BUFFER_SIZE_ms * _PCM_samples_one_ms);
davide.aliprandi@st.com 0:d5552d432108 285
davide.aliprandi@st.com 0:d5552d432108 286 /* Releasing resources. */
davide.aliprandi@st.com 0:d5552d432108 287 _mutex->unlock();
davide.aliprandi@st.com 0:d5552d432108 288
davide.aliprandi@st.com 0:d5552d432108 289 /* Executing user-defined callback. */
davide.aliprandi@st.com 0:d5552d432108 290 static bool first_time = true;
davide.aliprandi@st.com 0:d5552d432108 291 if (_callback_attached && first_time)
davide.aliprandi@st.com 0:d5552d432108 292 {
davide.aliprandi@st.com 0:d5552d432108 293 _callback.call(_USER_PCM_buffer_one_ms, _PCM_samples_one_ms * BYTES_PER_SAMPLE);
davide.aliprandi@st.com 0:d5552d432108 294 first_time = !first_time;
davide.aliprandi@st.com 0:d5552d432108 295 }
davide.aliprandi@st.com 0:d5552d432108 296
davide.aliprandi@st.com 0:d5552d432108 297 #ifdef X_NUCLEO_CCA02M1_DEBUG
davide.aliprandi@st.com 0:d5552d432108 298 _i2s_signal = 0;
davide.aliprandi@st.com 0:d5552d432108 299 #endif
davide.aliprandi@st.com 0:d5552d432108 300 }
davide.aliprandi@st.com 0:d5552d432108 301
davide.aliprandi@st.com 0:d5552d432108 302 /* Start sending data through the USB whenever the PCM buffer has been
davide.aliprandi@st.com 0:d5552d432108 303 filled up to the medium threshold. */
davide.aliprandi@st.com 0:d5552d432108 304 if (_usb_enabled)
davide.aliprandi@st.com 0:d5552d432108 305 {
davide.aliprandi@st.com 0:d5552d432108 306 static bool done = false;
davide.aliprandi@st.com 0:d5552d432108 307 static uint32_t calls = 0;
davide.aliprandi@st.com 0:d5552d432108 308 if (!done)
davide.aliprandi@st.com 0:d5552d432108 309 if (calls++ >= PCM_BUFFER_TH_MED_ms)
davide.aliprandi@st.com 0:d5552d432108 310 {
davide.aliprandi@st.com 0:d5552d432108 311 USBHandler();
davide.aliprandi@st.com 0:d5552d432108 312 done = true;
davide.aliprandi@st.com 0:d5552d432108 313 }
davide.aliprandi@st.com 0:d5552d432108 314 }
davide.aliprandi@st.com 0:d5552d432108 315 }
davide.aliprandi@st.com 0:d5552d432108 316
davide.aliprandi@st.com 0:d5552d432108 317 /**
davide.aliprandi@st.com 0:d5552d432108 318 * @brief Sending PCM data via USB.
davide.aliprandi@st.com 0:d5552d432108 319 * @param None.
davide.aliprandi@st.com 0:d5552d432108 320 * @retval None.
davide.aliprandi@st.com 0:d5552d432108 321 */
davide.aliprandi@st.com 0:d5552d432108 322 void X_NUCLEO_CCA02M1::USBHandler(void)
davide.aliprandi@st.com 0:d5552d432108 323 {
davide.aliprandi@st.com 0:d5552d432108 324 #ifdef X_NUCLEO_CCA02M1_DEBUG
davide.aliprandi@st.com 0:d5552d432108 325 _usb_signal = 1;
davide.aliprandi@st.com 0:d5552d432108 326 #endif
davide.aliprandi@st.com 0:d5552d432108 327
davide.aliprandi@st.com 0:d5552d432108 328 /* Acquiring resources. */
davide.aliprandi@st.com 0:d5552d432108 329 _mutex->lock();
davide.aliprandi@st.com 0:d5552d432108 330
davide.aliprandi@st.com 0:d5552d432108 331 /* Computing the delta-data to send through the USB depending on the
davide.aliprandi@st.com 0:d5552d432108 332 dis-alignment between the read and write index of the PCM buffer,
davide.aliprandi@st.com 0:d5552d432108 333 which may happen whenever the I2S and the USB do not works at the
davide.aliprandi@st.com 0:d5552d432108 334 same frequency (i.e. 1KHz). */
davide.aliprandi@st.com 0:d5552d432108 335 int32_t PCM_diff = _PCM_buffer_write_index - _PCM_buffer_read_index;
davide.aliprandi@st.com 0:d5552d432108 336 PCM_diff = (PCM_diff >= 0 ? PCM_diff : PCM_diff + PCM_BUFFER_SIZE_ms * _PCM_samples_one_ms);
davide.aliprandi@st.com 0:d5552d432108 337 int32_t PCM_buffer_high_index = PCM_BUFFER_TH_HIG_ms * _PCM_samples_one_ms;
davide.aliprandi@st.com 0:d5552d432108 338 int32_t PCM_buffer_low_index = PCM_BUFFER_TH_LOW_ms * _PCM_samples_one_ms;
davide.aliprandi@st.com 0:d5552d432108 339 int32_t PCM_delta_samples = 0;
davide.aliprandi@st.com 0:d5552d432108 340
davide.aliprandi@st.com 0:d5552d432108 341 if (PCM_diff >= PCM_buffer_high_index) {
davide.aliprandi@st.com 0:d5552d432108 342 PCM_delta_samples = 1;
davide.aliprandi@st.com 0:d5552d432108 343 #ifdef X_NUCLEO_CCA02M1_DEBUG
davide.aliprandi@st.com 0:d5552d432108 344 _buffer_overrun = 1;
davide.aliprandi@st.com 0:d5552d432108 345 _buffer_underrun = 0;
davide.aliprandi@st.com 0:d5552d432108 346 #endif
davide.aliprandi@st.com 0:d5552d432108 347 }
davide.aliprandi@st.com 0:d5552d432108 348 else if (PCM_diff <= PCM_buffer_low_index) {
davide.aliprandi@st.com 0:d5552d432108 349 PCM_delta_samples = -1;
davide.aliprandi@st.com 0:d5552d432108 350 #ifdef X_NUCLEO_CCA02M1_DEBUG
davide.aliprandi@st.com 0:d5552d432108 351 _buffer_overrun = 0;
davide.aliprandi@st.com 0:d5552d432108 352 _buffer_underrun = 1;
davide.aliprandi@st.com 0:d5552d432108 353 } else {
davide.aliprandi@st.com 0:d5552d432108 354 _buffer_overrun = _buffer_underrun = 0;
davide.aliprandi@st.com 0:d5552d432108 355 #endif
davide.aliprandi@st.com 0:d5552d432108 356 }
davide.aliprandi@st.com 0:d5552d432108 357
davide.aliprandi@st.com 0:d5552d432108 358 /* Writing data through the USB. */
davide.aliprandi@st.com 0:d5552d432108 359 for (uint32_t i = 0, j = _PCM_buffer_read_index; i < _PCM_samples_one_ms + PCM_delta_samples * _channels; j %= (PCM_BUFFER_SIZE_ms * _PCM_samples_one_ms))
davide.aliprandi@st.com 0:d5552d432108 360 _USB_PCM_buffer_one_ms[i++] = _PCM_buffer_n_ms[j++];
davide.aliprandi@st.com 0:d5552d432108 361 _usb_audio->writeSync((uint8_t *) _USB_PCM_buffer_one_ms, PCM_delta_samples);
davide.aliprandi@st.com 0:d5552d432108 362
davide.aliprandi@st.com 0:d5552d432108 363 /* Updating read index. */
davide.aliprandi@st.com 0:d5552d432108 364 _PCM_buffer_read_index += _PCM_samples_one_ms + PCM_delta_samples * _channels;
davide.aliprandi@st.com 0:d5552d432108 365 _PCM_buffer_read_index %= (PCM_BUFFER_SIZE_ms * _PCM_samples_one_ms);
davide.aliprandi@st.com 0:d5552d432108 366
davide.aliprandi@st.com 0:d5552d432108 367 /* Releasing resources. */
davide.aliprandi@st.com 0:d5552d432108 368 _mutex->unlock();
davide.aliprandi@st.com 0:d5552d432108 369
davide.aliprandi@st.com 0:d5552d432108 370 #ifdef X_NUCLEO_CCA02M1_DEBUG
davide.aliprandi@st.com 0:d5552d432108 371 _usb_signal = 0;
davide.aliprandi@st.com 0:d5552d432108 372 #endif
davide.aliprandi@st.com 0:d5552d432108 373 }