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:
Davidroid
Date:
Thu May 04 10:39:39 2017 +0000
Revision:
19:1a061e306cc9
Parent:
15:17bdadc6aa9c
Child:
20:9952bef19da1
Enabled 44.1 and 48 KHz configurations.

Who changed what in which revision?

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