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
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.
BSP/XNucleoCCA02M1.cpp@20:9952bef19da1, 2017-05-05 (annotated)
- Committer:
- Davidroid
- Date:
- Fri May 05 11:34:10 2017 +0000
- Revision:
- 20:9952bef19da1
- Parent:
- 19:1a061e306cc9
Simplifying code.
Who changed what in which revision?
User | Revision | Line number | New 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>© 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 | 2:9f389fd8fb2e | 70 | */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 71 | status_t XNucleoCCA02M1::init(void *init) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 72 | { |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 73 | /* Storing data. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 74 | if (init != NULL) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 75 | { |
davide.aliprandi@st.com | 9:4c3d94b67a6b | 76 | _frequency = (*((XNucleoCCA02M1_init_t *) init)).frequency; |
davide.aliprandi@st.com | 9:4c3d94b67a6b | 77 | _channels = (*((XNucleoCCA02M1_init_t *) init)).channels; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 78 | } |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 79 | |
davide.aliprandi@st.com | 9:4c3d94b67a6b | 80 | #ifdef USE_OPEN_PDM2PCM_LIBRARY |
davide.aliprandi@st.com | 9:4c3d94b67a6b | 81 | /* Checking input parameters. */ |
Davidroid | 20:9952bef19da1 | 82 | if (!(((_frequency == 8000) && (_channels == 1)) || |
Davidroid | 20:9952bef19da1 | 83 | ((_frequency == 8000) && (_channels == 2)) || |
Davidroid | 20:9952bef19da1 | 84 | ((_frequency == 16000) && (_channels == 1)) || |
davide.aliprandi@st.com | 11:b2f7f79026e4 | 85 | ((_frequency == 16000) && (_channels == 2)) || |
davide.aliprandi@st.com | 15:17bdadc6aa9c | 86 | ((_frequency == 32000) && (_channels == 1)) || |
Davidroid | 19:1a061e306cc9 | 87 | ((_frequency == 32000) && (_channels == 2)) || |
Davidroid | 19:1a061e306cc9 | 88 | ((_frequency == 44100) && (_channels == 1)) || |
Davidroid | 19:1a061e306cc9 | 89 | ((_frequency == 44100) && (_channels == 2)) || |
Davidroid | 19:1a061e306cc9 | 90 | ((_frequency == 48000) && (_channels == 1)) || |
Davidroid | 19:1a061e306cc9 | 91 | ((_frequency == 48000) && (_channels == 2)))) |
Davidroid | 20:9952bef19da1 | 92 | error("\r\nError: please set one of the following configurations: mono/stereo @ 8/16/32/44.1/48 KHz.\r\n"); |
davide.aliprandi@st.com | 9:4c3d94b67a6b | 93 | #endif |
davide.aliprandi@st.com | 9:4c3d94b67a6b | 94 | |
Davidroid | 14:377677cca2e9 | 95 | /* |
Davidroid | 14:377677cca2e9 | 96 | * Starting the I2S frequency divider. |
Davidroid | 14:377677cca2e9 | 97 | * Note: put a jumper to connect PB_5 and PB_13 on the MORPHO connector when |
Davidroid | 14:377677cca2e9 | 98 | * running a mono configuration. |
Davidroid | 14:377677cca2e9 | 99 | */ |
Davidroid | 14:377677cca2e9 | 100 | if (_channels >= 2) |
Davidroid | 14:377677cca2e9 | 101 | { |
Davidroid | 14:377677cca2e9 | 102 | FrequencyDivider *divider = new FrequencyDivider(); |
Davidroid | 14:377677cca2e9 | 103 | divider->start(); |
Davidroid | 14:377677cca2e9 | 104 | } |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 105 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 106 | /* Initializing the PDM to PCM conversion library. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 107 | if ((_pdm2pcm = new PDM2PCMAudio(_frequency, _channels)) == NULL) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 108 | return COMPONENT_ERROR; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 109 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 110 | /* Setting I2S parameters. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 111 | dev_i2s.mode(MASTER_RX, true); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 112 | dev_i2s.audio_frequency(_frequency == I2S_AUDIOFREQ_8K ? 4 * _frequency : 2 * _frequency); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 113 | dev_i2s.protocol(MSB); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 114 | dev_i2s.format(_channels == 1 ? 16 : 32, _channels == 1 ? 16 : 32, 1); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 115 | |
Davidroid | 20:9952bef19da1 | 116 | /* Buffer sizes in 16-bits samples. */ |
Davidroid | 20:9952bef19da1 | 117 | _PCM_samples_one_ms = ((_frequency * _channels) / 1000); |
Davidroid | 20:9952bef19da1 | 118 | _PDM_samples_one_ms = _pdm2pcm->pcm2pdm_samples(_PCM_samples_one_ms); |
Davidroid | 20:9952bef19da1 | 119 | _PDM_samples_two_ms = (_PDM_samples_one_ms << 1); |
Davidroid | 20:9952bef19da1 | 120 | |
Davidroid | 20:9952bef19da1 | 121 | /* Allocating input and output buffers. */ |
Davidroid | 20:9952bef19da1 | 122 | _PDM_buffer_two_ms = (uint16_t *) calloc(_PDM_samples_two_ms, sizeof(uint16_t)); |
Davidroid | 20:9952bef19da1 | 123 | _PCM_buffer_n_ms = (int16_t *) calloc(_PCM_samples_one_ms * PCM_BUFFER_SIZE_ms, sizeof(uint16_t)); |
Davidroid | 20:9952bef19da1 | 124 | |
Davidroid | 20:9952bef19da1 | 125 | /* Allocating support buffers. */ |
Davidroid | 20:9952bef19da1 | 126 | _PDM_buffer_one_ms = (uint16_t *) calloc(_PDM_samples_one_ms, sizeof(uint16_t)); |
Davidroid | 20:9952bef19da1 | 127 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 128 | return COMPONENT_OK; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 129 | } |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 130 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 131 | /** |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 132 | * @brief Enabling transmission via USB. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 133 | * @param None. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 134 | * @retval "0" in case of success, an error code otherwise. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 135 | */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 136 | status_t XNucleoCCA02M1::enable_usb(void) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 137 | { |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 138 | /* Initializing the USBAudio object. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 139 | if ((_usb_audio = new USBAudio(32000, 2, _frequency, _channels)) == NULL) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 140 | return COMPONENT_ERROR; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 141 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 142 | /* Allocating support buffers. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 143 | _USB_PCM_buffer_one_ms = (int16_t *) calloc(_PCM_samples_one_ms + _channels, sizeof(uint16_t)); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 144 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 145 | _usb_enabled = true; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 146 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 147 | return COMPONENT_OK; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 148 | } |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 149 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 150 | /** |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 151 | * @brief Disabling transmission via USB. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 152 | * @param None. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 153 | * @retval "0" in case of success, an error code otherwise. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 154 | */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 155 | status_t XNucleoCCA02M1::disable_usb(void) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 156 | { |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 157 | /* Freeing memory for the USBAudio object and support buffers. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 158 | delete _usb_audio; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 159 | free(_USB_PCM_buffer_one_ms); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 160 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 161 | _usb_enabled = false; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 162 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 163 | return COMPONENT_OK; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 164 | } |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 165 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 166 | /* |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 167 | * @brief Start recording audio. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 168 | * @param None. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 169 | * @retval "0" in case of success, an error code otherwise. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 170 | */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 171 | status_t XNucleoCCA02M1::record(void) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 172 | { |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 173 | /* Reading microphones via I2S. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 174 | int res = dev_i2s.transfer( |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 175 | (void *) NULL, 0, |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 176 | (void *) _PDM_buffer_two_ms, _PDM_samples_two_ms * BYTES_PER_SAMPLE, |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 177 | event_callback_t(this, &XNucleoCCA02M1::i2s_callback), |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 178 | I2S_EVENT_ALL |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 179 | ); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 180 | if (res != 0) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 181 | return COMPONENT_ERROR; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 182 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 183 | /* Attaching a callback to send data through the USB at a standard frequency |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 184 | of 1KHz. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 185 | if (_usb_enabled) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 186 | _usb_audio->attachTx(this, &XNucleoCCA02M1::usb_handler); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 187 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 188 | return COMPONENT_OK; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 189 | } |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 190 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 191 | /** |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 192 | * @brief Attach a user-defined callback that will be executed whenever PCM |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 193 | * data are ready, i.e. once each millisecond. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 194 | * The provided PCM buffer will be filled by the microphones. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 195 | * @param fptr Callback to attach. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 196 | * @retval None. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 197 | */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 198 | void XNucleoCCA02M1::attach(void (*fptr) (int16_t *PCM_buffer, uint16_t PCM_buffer_bytes)) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 199 | { |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 200 | /* Allocating support buffers. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 201 | if ((_USER_PCM_buffer_one_ms = (int16_t *) calloc(_PCM_samples_one_ms, sizeof(uint16_t))) == NULL) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 202 | error("Instantiation of support buffers failed.\r\n"); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 203 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 204 | /* Attaching the callback. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 205 | _callback.attach(fptr); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 206 | _callback_attached = true; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 207 | } |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 208 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 209 | /** |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 210 | * @brief Attach a user-defined non-static callback that will be executed |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 211 | * whenever PCM data are ready, i.e. once each millisecond. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 212 | * The provided PCM buffer will be filled by the microphones. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 213 | * @param tptr Pointer to an object. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 214 | * @param mptr Pointer to an object's callback. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 215 | * @retval None. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 216 | */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 217 | template<typename T> |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 218 | void XNucleoCCA02M1::attach(T *tptr, void (T::*mptr) (int16_t *PCM_buffer, uint16_t PCM_buffer_bytes)) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 219 | { |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 220 | /* Allocating support buffers. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 221 | if ((_USER_PCM_buffer_one_ms = (int16_t *) calloc(_PCM_samples_one_ms, sizeof(uint16_t))) == NULL) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 222 | error("Instantiation of support buffers failed.\r\n"); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 223 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 224 | /* Attaching the callback. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 225 | _callback.attach(tptr, mptr); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 226 | _callback_attached = true; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 227 | } |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 228 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 229 | /** |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 230 | * @brief I2S callback which is executed whenever PCM data are ready, i.e. once |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 231 | * each millisecond. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 232 | * @param narg Narg flag. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 233 | * @retval None. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 234 | */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 235 | void XNucleoCCA02M1::i2s_callback(int narg) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 236 | { |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 237 | /* Checking for errors. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 238 | if (!(narg & (I2S_EVENT_RX_COMPLETE | I2S_EVENT_RX_HALF_COMPLETE))) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 239 | error("Unexpected transmission event.\r\n"); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 240 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 241 | /* PDM to PCM Conversion. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 242 | if (narg & (I2S_EVENT_RX_COMPLETE | I2S_EVENT_RX_HALF_COMPLETE)) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 243 | { |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 244 | #ifdef X_NUCLEO_CCA02M1_DEBUG |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 245 | _i2s_signal = 1; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 246 | #endif |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 247 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 248 | uint32_t PDM_index = (narg & I2S_EVENT_RX_HALF_COMPLETE ? 0 : _PDM_samples_one_ms); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 249 | switch (_channels) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 250 | { |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 251 | case 1: |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 252 | /* Scrambling PDM audio data. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 253 | _pdm2pcm->scramble(_PDM_buffer_one_ms, &_PDM_buffer_two_ms[PDM_index], _PDM_samples_one_ms); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 254 | break; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 255 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 256 | case 2: |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 257 | /* Demuxing PDM audio data. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 258 | _pdm2pcm->demux(_PDM_buffer_one_ms, &_PDM_buffer_two_ms[PDM_index], _PDM_samples_one_ms); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 259 | break; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 260 | } |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 261 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 262 | /* Acquiring resources. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 263 | _mutex->lock(); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 264 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 265 | /* Converting PDM to PCM audio data. */ |
Davidroid | 20:9952bef19da1 | 266 | _pdm2pcm->convert(&_PCM_buffer_n_ms[_PCM_buffer_write_index], _PDM_buffer_one_ms, _volume); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 267 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 268 | /* Copying PCM data to the user buffer. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 269 | if (_callback_attached) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 270 | 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 | 271 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 272 | /* Updating write index. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 273 | _PCM_buffer_write_index += _PCM_samples_one_ms; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 274 | _PCM_buffer_write_index %= (PCM_BUFFER_SIZE_ms * _PCM_samples_one_ms); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 275 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 276 | /* Releasing resources. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 277 | _mutex->unlock(); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 278 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 279 | /* Executing user-defined callback. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 280 | static bool first_time = true; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 281 | if (_callback_attached && first_time) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 282 | { |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 283 | _callback.call(_USER_PCM_buffer_one_ms, _PCM_samples_one_ms * BYTES_PER_SAMPLE); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 284 | first_time = !first_time; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 285 | } |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 286 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 287 | #ifdef X_NUCLEO_CCA02M1_DEBUG |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 288 | _i2s_signal = 0; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 289 | #endif |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 290 | } |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 291 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 292 | /* Start sending data through the USB whenever the PCM buffer has been |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 293 | filled up to the medium threshold. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 294 | if (_usb_enabled) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 295 | { |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 296 | static bool done = false; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 297 | static uint32_t calls = 0; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 298 | if (!done) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 299 | if (calls++ >= PCM_BUFFER_TH_MED_ms) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 300 | { |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 301 | usb_handler(); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 302 | done = true; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 303 | } |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 304 | } |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 305 | } |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 306 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 307 | /** |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 308 | * @brief Sending PCM data via USB. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 309 | * @param None. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 310 | * @retval None. |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 311 | */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 312 | void XNucleoCCA02M1::usb_handler(void) |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 313 | { |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 314 | #ifdef X_NUCLEO_CCA02M1_DEBUG |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 315 | _usb_signal = 1; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 316 | #endif |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 317 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 318 | /* Acquiring resources. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 319 | _mutex->lock(); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 320 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 321 | /* Computing the delta-data to send through the USB depending on the |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 322 | dis-alignment between the read and write index of the PCM buffer, |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 323 | which may happen whenever the I2S and the USB do not works at the |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 324 | same frequency (i.e. 1KHz). */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 325 | int32_t PCM_diff = _PCM_buffer_write_index - _PCM_buffer_read_index; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 326 | PCM_diff = (PCM_diff >= 0 ? PCM_diff : PCM_diff + PCM_BUFFER_SIZE_ms * _PCM_samples_one_ms); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 327 | int32_t PCM_buffer_high_index = PCM_BUFFER_TH_HIG_ms * _PCM_samples_one_ms; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 328 | int32_t PCM_buffer_low_index = PCM_BUFFER_TH_LOW_ms * _PCM_samples_one_ms; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 329 | USBAudio::AudioSampleCorrectType PCM_delta_samples = USBAudio::NoCorrection; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 330 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 331 | if (PCM_diff >= PCM_buffer_high_index) { |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 332 | PCM_delta_samples = USBAudio::AddOneSample; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 333 | #ifdef X_NUCLEO_CCA02M1_DEBUG |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 334 | _buffer_overrun = 1; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 335 | _buffer_underrun = 0; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 336 | #endif |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 337 | } |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 338 | else if (PCM_diff <= PCM_buffer_low_index) { |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 339 | PCM_delta_samples = USBAudio::RemoveOneSample; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 340 | #ifdef X_NUCLEO_CCA02M1_DEBUG |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 341 | _buffer_overrun = 0; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 342 | _buffer_underrun = 1; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 343 | } else { |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 344 | _buffer_overrun = _buffer_underrun = 0; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 345 | #endif |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 346 | } |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 347 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 348 | /* Writing data through the USB. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 349 | 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 | 350 | _USB_PCM_buffer_one_ms[i++] = _PCM_buffer_n_ms[j++]; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 351 | _usb_audio->writeSync((uint8_t *) _USB_PCM_buffer_one_ms, PCM_delta_samples); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 352 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 353 | /* Updating read index. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 354 | _PCM_buffer_read_index += _PCM_samples_one_ms + ((uint32_t) PCM_delta_samples) * _channels; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 355 | _PCM_buffer_read_index %= (PCM_BUFFER_SIZE_ms * _PCM_samples_one_ms); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 356 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 357 | /* Releasing resources. */ |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 358 | _mutex->unlock(); |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 359 | |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 360 | #ifdef X_NUCLEO_CCA02M1_DEBUG |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 361 | _usb_signal = 0; |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 362 | #endif |
davide.aliprandi@st.com | 2:9f389fd8fb2e | 363 | } |