Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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
XNucleoCCA02M1.cpp
00001 /** 00002 ****************************************************************************** 00003 * @file XNucleoCCA02M1.cpp 00004 * @author AST / Software Platforms and Cloud 00005 * @version V1.0 00006 * @date October 17th, 2016 00007 * @brief Implementation file for the X_NUCLEO_CCA02M1 expansion board. 00008 ****************************************************************************** 00009 * @attention 00010 * 00011 * <h2><center>© COPYRIGHT(c) 2015 STMicroelectronics</center></h2> 00012 * 00013 * Redistribution and use in source and binary forms, with or without modification, 00014 * are permitted provided that the following conditions are met: 00015 * 1. Redistributions of source code must retain the above copyright notice, 00016 * this list of conditions and the following disclaimer. 00017 * 2. Redistributions in binary form must reproduce the above copyright notice, 00018 * this list of conditions and the following disclaimer in the documentation 00019 * and/or other materials provided with the distribution. 00020 * 3. Neither the name of STMicroelectronics nor the names of its contributors 00021 * may be used to endorse or promote products derived from this software 00022 * without specific prior written permission. 00023 * 00024 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00025 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00026 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 00027 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 00028 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 00029 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 00030 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00031 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 00032 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 00033 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00034 * 00035 ****************************************************************************** 00036 */ 00037 00038 00039 /* Includes ------------------------------------------------------------------*/ 00040 00041 /* ACTION 1 ------------------------------------------------------------------* 00042 * Include here platform specific header files. * 00043 *----------------------------------------------------------------------------*/ 00044 #include "mbed.h" 00045 /* ACTION 2 ------------------------------------------------------------------* 00046 * Include here expansion board specific header files. * 00047 *----------------------------------------------------------------------------*/ 00048 #include "XNucleoCCA02M1.h" 00049 00050 00051 /* Variables -----------------------------------------------------------------*/ 00052 00053 /* Indexes of the PCM buffer. */ 00054 uint32_t XNucleoCCA02M1::_PCM_buffer_read_index = 0; 00055 uint32_t XNucleoCCA02M1::_PCM_buffer_write_index = 0; 00056 00057 /* Number of expansion boards. */ 00058 uint8_t XNucleoCCA02M1::_number_of_boards = 0; 00059 00060 /* Accessing shared areas: the PCM buffer. */ 00061 SingletonPtr<PlatformMutex> XNucleoCCA02M1::_mutex; 00062 00063 00064 /* Methods -------------------------------------------------------------------*/ 00065 00066 /** 00067 * @brief Initializing the X_NUCLEO_CCA02M1 board. 00068 * @param init Pointer to device specific initalization structure. 00069 * @retval "0" in case of success, an error code otherwise. 00070 */ 00071 status_t XNucleoCCA02M1::init(void *init) 00072 { 00073 /* Storing data. */ 00074 if (init != NULL) 00075 { 00076 _frequency = (*((XNucleoCCA02M1_init_t *) init)).frequency; 00077 _channels = (*((XNucleoCCA02M1_init_t *) init)).channels; 00078 } 00079 00080 #ifdef USE_OPEN_PDM2PCM_LIBRARY 00081 /* Checking input parameters. */ 00082 if (!(((_frequency == 8000) && (_channels == 1)) || 00083 ((_frequency == 8000) && (_channels == 2)) || 00084 ((_frequency == 16000) && (_channels == 1)) || 00085 ((_frequency == 16000) && (_channels == 2)) || 00086 ((_frequency == 32000) && (_channels == 1)) || 00087 ((_frequency == 32000) && (_channels == 2)) || 00088 ((_frequency == 44100) && (_channels == 1)) || 00089 ((_frequency == 44100) && (_channels == 2)) || 00090 ((_frequency == 48000) && (_channels == 1)) || 00091 ((_frequency == 48000) && (_channels == 2)))) 00092 error("\r\nError: please set one of the following configurations: mono/stereo @ 8/16/32/44.1/48 KHz.\r\n"); 00093 #endif 00094 00095 /* 00096 * Starting the I2S frequency divider. 00097 * Note: put a jumper to connect PB_5 and PB_13 on the MORPHO connector when 00098 * running a mono configuration. 00099 */ 00100 if (_channels >= 2) 00101 { 00102 FrequencyDivider *divider = new FrequencyDivider(); 00103 divider->start(); 00104 } 00105 00106 /* Initializing the PDM to PCM conversion library. */ 00107 if ((_pdm2pcm = new PDM2PCMAudio(_frequency, _channels)) == NULL) 00108 return COMPONENT_ERROR; 00109 00110 /* Setting I2S parameters. */ 00111 dev_i2s.mode(MASTER_RX, true); 00112 dev_i2s.audio_frequency(_frequency == I2S_AUDIOFREQ_8K ? 4 * _frequency : 2 * _frequency); 00113 dev_i2s.protocol(MSB); 00114 dev_i2s.format(_channels == 1 ? 16 : 32, _channels == 1 ? 16 : 32, 1); 00115 00116 /* Buffer sizes in 16-bits samples. */ 00117 _PCM_samples_one_ms = ((_frequency * _channels) / 1000); 00118 _PDM_samples_one_ms = _pdm2pcm->pcm2pdm_samples(_PCM_samples_one_ms); 00119 _PDM_samples_two_ms = (_PDM_samples_one_ms << 1); 00120 00121 /* Allocating input and output buffers. */ 00122 _PDM_buffer_two_ms = (uint16_t *) calloc(_PDM_samples_two_ms, sizeof(uint16_t)); 00123 _PCM_buffer_n_ms = (int16_t *) calloc(_PCM_samples_one_ms * PCM_BUFFER_SIZE_ms, sizeof(uint16_t)); 00124 00125 /* Allocating support buffers. */ 00126 _PDM_buffer_one_ms = (uint16_t *) calloc(_PDM_samples_one_ms, sizeof(uint16_t)); 00127 00128 return COMPONENT_OK; 00129 } 00130 00131 /** 00132 * @brief Enabling transmission via USB. 00133 * @param None. 00134 * @retval "0" in case of success, an error code otherwise. 00135 */ 00136 status_t XNucleoCCA02M1::enable_usb(void) 00137 { 00138 /* Initializing the USBAudio object. */ 00139 if ((_usb_audio = new USBAudio(32000, 2, _frequency, _channels)) == NULL) 00140 return COMPONENT_ERROR; 00141 00142 /* Allocating support buffers. */ 00143 _USB_PCM_buffer_one_ms = (int16_t *) calloc(_PCM_samples_one_ms + _channels, sizeof(uint16_t)); 00144 00145 _usb_enabled = true; 00146 00147 return COMPONENT_OK; 00148 } 00149 00150 /** 00151 * @brief Disabling transmission via USB. 00152 * @param None. 00153 * @retval "0" in case of success, an error code otherwise. 00154 */ 00155 status_t XNucleoCCA02M1::disable_usb(void) 00156 { 00157 /* Freeing memory for the USBAudio object and support buffers. */ 00158 delete _usb_audio; 00159 free(_USB_PCM_buffer_one_ms); 00160 00161 _usb_enabled = false; 00162 00163 return COMPONENT_OK; 00164 } 00165 00166 /* 00167 * @brief Start recording audio. 00168 * @param None. 00169 * @retval "0" in case of success, an error code otherwise. 00170 */ 00171 status_t XNucleoCCA02M1::record(void) 00172 { 00173 /* Reading microphones via I2S. */ 00174 int res = dev_i2s.transfer( 00175 (void *) NULL, 0, 00176 (void *) _PDM_buffer_two_ms, _PDM_samples_two_ms * BYTES_PER_SAMPLE, 00177 event_callback_t(this, &XNucleoCCA02M1::i2s_callback), 00178 I2S_EVENT_ALL 00179 ); 00180 if (res != 0) 00181 return COMPONENT_ERROR; 00182 00183 /* Attaching a callback to send data through the USB at a standard frequency 00184 of 1KHz. */ 00185 if (_usb_enabled) 00186 _usb_audio->attachTx(this, &XNucleoCCA02M1::usb_handler); 00187 00188 return COMPONENT_OK; 00189 } 00190 00191 /** 00192 * @brief Attach a user-defined callback that will be executed whenever PCM 00193 * data are ready, i.e. once each millisecond. 00194 * The provided PCM buffer will be filled by the microphones. 00195 * @param fptr Callback to attach. 00196 * @retval None. 00197 */ 00198 void XNucleoCCA02M1::attach(void (*fptr) (int16_t *PCM_buffer, uint16_t PCM_buffer_bytes)) 00199 { 00200 /* Allocating support buffers. */ 00201 if ((_USER_PCM_buffer_one_ms = (int16_t *) calloc(_PCM_samples_one_ms, sizeof(uint16_t))) == NULL) 00202 error("Instantiation of support buffers failed.\r\n"); 00203 00204 /* Attaching the callback. */ 00205 _callback.attach(fptr); 00206 _callback_attached = true; 00207 } 00208 00209 /** 00210 * @brief Attach a user-defined non-static callback that will be executed 00211 * whenever PCM data are ready, i.e. once each millisecond. 00212 * The provided PCM buffer will be filled by the microphones. 00213 * @param tptr Pointer to an object. 00214 * @param mptr Pointer to an object's callback. 00215 * @retval None. 00216 */ 00217 template<typename T> 00218 void XNucleoCCA02M1::attach(T *tptr, void (T::*mptr) (int16_t *PCM_buffer, uint16_t PCM_buffer_bytes)) 00219 { 00220 /* Allocating support buffers. */ 00221 if ((_USER_PCM_buffer_one_ms = (int16_t *) calloc(_PCM_samples_one_ms, sizeof(uint16_t))) == NULL) 00222 error("Instantiation of support buffers failed.\r\n"); 00223 00224 /* Attaching the callback. */ 00225 _callback.attach(tptr, mptr); 00226 _callback_attached = true; 00227 } 00228 00229 /** 00230 * @brief I2S callback which is executed whenever PCM data are ready, i.e. once 00231 * each millisecond. 00232 * @param narg Narg flag. 00233 * @retval None. 00234 */ 00235 void XNucleoCCA02M1::i2s_callback(int narg) 00236 { 00237 /* Checking for errors. */ 00238 if (!(narg & (I2S_EVENT_RX_COMPLETE | I2S_EVENT_RX_HALF_COMPLETE))) 00239 error("Unexpected transmission event.\r\n"); 00240 00241 /* PDM to PCM Conversion. */ 00242 if (narg & (I2S_EVENT_RX_COMPLETE | I2S_EVENT_RX_HALF_COMPLETE)) 00243 { 00244 #ifdef X_NUCLEO_CCA02M1_DEBUG 00245 _i2s_signal = 1; 00246 #endif 00247 00248 uint32_t PDM_index = (narg & I2S_EVENT_RX_HALF_COMPLETE ? 0 : _PDM_samples_one_ms); 00249 switch (_channels) 00250 { 00251 case 1: 00252 /* Scrambling PDM audio data. */ 00253 _pdm2pcm->scramble(_PDM_buffer_one_ms, &_PDM_buffer_two_ms[PDM_index], _PDM_samples_one_ms); 00254 break; 00255 00256 case 2: 00257 /* Demuxing PDM audio data. */ 00258 _pdm2pcm->demux(_PDM_buffer_one_ms, &_PDM_buffer_two_ms[PDM_index], _PDM_samples_one_ms); 00259 break; 00260 } 00261 00262 /* Acquiring resources. */ 00263 _mutex->lock(); 00264 00265 /* Converting PDM to PCM audio data. */ 00266 _pdm2pcm->convert(&_PCM_buffer_n_ms[_PCM_buffer_write_index], _PDM_buffer_one_ms, _volume); 00267 00268 /* Copying PCM data to the user buffer. */ 00269 if (_callback_attached) 00270 memcpy(_USER_PCM_buffer_one_ms, &_PCM_buffer_n_ms[_PCM_buffer_write_index], _PCM_samples_one_ms * BYTES_PER_SAMPLE); 00271 00272 /* Updating write index. */ 00273 _PCM_buffer_write_index += _PCM_samples_one_ms; 00274 _PCM_buffer_write_index %= (PCM_BUFFER_SIZE_ms * _PCM_samples_one_ms); 00275 00276 /* Releasing resources. */ 00277 _mutex->unlock(); 00278 00279 /* Executing user-defined callback. */ 00280 static bool first_time = true; 00281 if (_callback_attached && first_time) 00282 { 00283 _callback.call(_USER_PCM_buffer_one_ms, _PCM_samples_one_ms * BYTES_PER_SAMPLE); 00284 first_time = !first_time; 00285 } 00286 00287 #ifdef X_NUCLEO_CCA02M1_DEBUG 00288 _i2s_signal = 0; 00289 #endif 00290 } 00291 00292 /* Start sending data through the USB whenever the PCM buffer has been 00293 filled up to the medium threshold. */ 00294 if (_usb_enabled) 00295 { 00296 static bool done = false; 00297 static uint32_t calls = 0; 00298 if (!done) 00299 if (calls++ >= PCM_BUFFER_TH_MED_ms) 00300 { 00301 usb_handler(); 00302 done = true; 00303 } 00304 } 00305 } 00306 00307 /** 00308 * @brief Sending PCM data via USB. 00309 * @param None. 00310 * @retval None. 00311 */ 00312 void XNucleoCCA02M1::usb_handler(void) 00313 { 00314 #ifdef X_NUCLEO_CCA02M1_DEBUG 00315 _usb_signal = 1; 00316 #endif 00317 00318 /* Acquiring resources. */ 00319 _mutex->lock(); 00320 00321 /* Computing the delta-data to send through the USB depending on the 00322 dis-alignment between the read and write index of the PCM buffer, 00323 which may happen whenever the I2S and the USB do not works at the 00324 same frequency (i.e. 1KHz). */ 00325 int32_t PCM_diff = _PCM_buffer_write_index - _PCM_buffer_read_index; 00326 PCM_diff = (PCM_diff >= 0 ? PCM_diff : PCM_diff + PCM_BUFFER_SIZE_ms * _PCM_samples_one_ms); 00327 int32_t PCM_buffer_high_index = PCM_BUFFER_TH_HIG_ms * _PCM_samples_one_ms; 00328 int32_t PCM_buffer_low_index = PCM_BUFFER_TH_LOW_ms * _PCM_samples_one_ms; 00329 USBAudio::AudioSampleCorrectType PCM_delta_samples = USBAudio::NoCorrection; 00330 00331 if (PCM_diff >= PCM_buffer_high_index) { 00332 PCM_delta_samples = USBAudio::AddOneSample; 00333 #ifdef X_NUCLEO_CCA02M1_DEBUG 00334 _buffer_overrun = 1; 00335 _buffer_underrun = 0; 00336 #endif 00337 } 00338 else if (PCM_diff <= PCM_buffer_low_index) { 00339 PCM_delta_samples = USBAudio::RemoveOneSample; 00340 #ifdef X_NUCLEO_CCA02M1_DEBUG 00341 _buffer_overrun = 0; 00342 _buffer_underrun = 1; 00343 } else { 00344 _buffer_overrun = _buffer_underrun = 0; 00345 #endif 00346 } 00347 00348 /* Writing data through the USB. */ 00349 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)) 00350 _USB_PCM_buffer_one_ms[i++] = _PCM_buffer_n_ms[j++]; 00351 _usb_audio->writeSync((uint8_t *) _USB_PCM_buffer_one_ms, PCM_delta_samples); 00352 00353 /* Updating read index. */ 00354 _PCM_buffer_read_index += _PCM_samples_one_ms + ((uint32_t) PCM_delta_samples) * _channels; 00355 _PCM_buffer_read_index %= (PCM_BUFFER_SIZE_ms * _PCM_samples_one_ms); 00356 00357 /* Releasing resources. */ 00358 _mutex->unlock(); 00359 00360 #ifdef X_NUCLEO_CCA02M1_DEBUG 00361 _usb_signal = 0; 00362 #endif 00363 }
Generated on Wed Jul 13 2022 12:32:42 by
1.7.2

X-NUCLEO-CCA02M1 Digital MEMS Microphones Expansion Board.