Il y avait des problèmes dans la libraire...
Dependencies: ST_FREQUENCY_DIVIDER ST_I2S USBDEVICE
Fork of X_NUCLEO_CCA02M1 by
BSP/XNucleoCCA02M1.cpp
- Committer:
- Davidroid
- Date:
- 2017-05-02
- Revision:
- 14:377677cca2e9
- Parent:
- 11:b2f7f79026e4
- Child:
- 15:17bdadc6aa9c
File content as of revision 14:377677cca2e9:
/** ****************************************************************************** * @file XNucleoCCA02M1.cpp * @author AST / Software Platforms and Cloud * @version V1.0 * @date October 17th, 2016 * @brief Implementation file for the X_NUCLEO_CCA02M1 expansion board. ****************************************************************************** * @attention * * <h2><center>© COPYRIGHT(c) 2015 STMicroelectronics</center></h2> * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of STMicroelectronics nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ /* ACTION 1 ------------------------------------------------------------------* * Include here platform specific header files. * *----------------------------------------------------------------------------*/ #include "mbed.h" /* ACTION 2 ------------------------------------------------------------------* * Include here expansion board specific header files. * *----------------------------------------------------------------------------*/ #include "XNucleoCCA02M1.h" /* Variables -----------------------------------------------------------------*/ /* Indexes of the PCM buffer. */ uint32_t XNucleoCCA02M1::_PCM_buffer_read_index = 0; uint32_t XNucleoCCA02M1::_PCM_buffer_write_index = 0; /* Number of expansion boards. */ uint8_t XNucleoCCA02M1::_number_of_boards = 0; /* Accessing shared areas: the PCM buffer. */ SingletonPtr<PlatformMutex> XNucleoCCA02M1::_mutex; /* Methods -------------------------------------------------------------------*/ /** * @brief Initializing the X_NUCLEO_CCA02M1 board. * @param init Pointer to device specific initalization structure. * @retval "0" in case of success, an error code otherwise. * @note Currently only two channels at 16KHz are supported. */ status_t XNucleoCCA02M1::init(void *init) { /* Storing data. */ if (init != NULL) { _frequency = (*((XNucleoCCA02M1_init_t *) init)).frequency; _channels = (*((XNucleoCCA02M1_init_t *) init)).channels; } #ifdef USE_OPEN_PDM2PCM_LIBRARY /* Checking input parameters. */ if (!(((_frequency == 16000) && (_channels == 1)) || ((_frequency == 16000) && (_channels == 2)) || ((_frequency == 32000) && (_channels == 1)))) error("\r\nConfiguration error: Currently only mono@16KHz, stero@16KHz or mono@32KHz are supported.\n\r"); #endif /* Setting configuration. */ switch (_frequency) { case I2S_AUDIOFREQ_8K: _decimation_factor = 128; break; case I2S_AUDIOFREQ_16K: case I2S_AUDIOFREQ_32K: case I2S_AUDIOFREQ_48K: default: _decimation_factor = 64; break; } /* Buffer sizes in 16-bits samples. */ _PCM_samples_one_ms = ((_frequency * _channels) / 1000); _PDM_samples_one_ms = ((_PCM_samples_one_ms * _decimation_factor) / 16); _PDM_samples_two_ms = (_PDM_samples_one_ms << 1); /* Allocating input and output buffers. */ _PDM_buffer_two_ms = (uint16_t *) calloc(_PDM_samples_two_ms, sizeof(uint16_t)); _PCM_buffer_n_ms = (int16_t *) calloc(_PCM_samples_one_ms * PCM_BUFFER_SIZE_ms, sizeof(uint16_t)); /* Allocating support buffers. */ _PDM_buffer_one_ms = (uint16_t *) calloc(_PDM_samples_one_ms, sizeof(uint16_t)); /* * Starting the I2S frequency divider. * Note: put a jumper to connect PB_5 and PB_13 on the MORPHO connector when * running a mono configuration. */ if (_channels >= 2) { FrequencyDivider *divider = new FrequencyDivider(); divider->start(); } /* Initializing the PDM to PCM conversion library. */ if ((_pdm2pcm = new PDM2PCMAudio(_frequency, _channels)) == NULL) return COMPONENT_ERROR; /* Setting I2S parameters. */ dev_i2s.mode(MASTER_RX, true); dev_i2s.audio_frequency(_frequency == I2S_AUDIOFREQ_8K ? 4 * _frequency : 2 * _frequency); dev_i2s.protocol(MSB); dev_i2s.format(_channels == 1 ? 16 : 32, _channels == 1 ? 16 : 32, 1); return COMPONENT_OK; } /** * @brief Enabling transmission via USB. * @param None. * @retval "0" in case of success, an error code otherwise. */ status_t XNucleoCCA02M1::enable_usb(void) { /* Initializing the USBAudio object. */ if ((_usb_audio = new USBAudio(32000, 2, _frequency, _channels)) == NULL) return COMPONENT_ERROR; /* Allocating support buffers. */ _USB_PCM_buffer_one_ms = (int16_t *) calloc(_PCM_samples_one_ms + _channels, sizeof(uint16_t)); _usb_enabled = true; return COMPONENT_OK; } /** * @brief Disabling transmission via USB. * @param None. * @retval "0" in case of success, an error code otherwise. */ status_t XNucleoCCA02M1::disable_usb(void) { /* Freeing memory for the USBAudio object and support buffers. */ delete _usb_audio; free(_USB_PCM_buffer_one_ms); _usb_enabled = false; return COMPONENT_OK; } /* * @brief Start recording audio. * @param None. * @retval "0" in case of success, an error code otherwise. */ status_t XNucleoCCA02M1::record(void) { /* Reading microphones via I2S. */ int res = dev_i2s.transfer( (void *) NULL, 0, (void *) _PDM_buffer_two_ms, _PDM_samples_two_ms * BYTES_PER_SAMPLE, event_callback_t(this, &XNucleoCCA02M1::i2s_callback), I2S_EVENT_ALL ); if (res != 0) return COMPONENT_ERROR; /* Attaching a callback to send data through the USB at a standard frequency of 1KHz. */ if (_usb_enabled) _usb_audio->attachTx(this, &XNucleoCCA02M1::usb_handler); return COMPONENT_OK; } /** * @brief Attach a user-defined callback that will be executed whenever PCM * data are ready, i.e. once each millisecond. * The provided PCM buffer will be filled by the microphones. * @param fptr Callback to attach. * @retval None. */ void XNucleoCCA02M1::attach(void (*fptr) (int16_t *PCM_buffer, uint16_t PCM_buffer_bytes)) { /* Allocating support buffers. */ if ((_USER_PCM_buffer_one_ms = (int16_t *) calloc(_PCM_samples_one_ms, sizeof(uint16_t))) == NULL) error("Instantiation of support buffers failed.\r\n"); /* Attaching the callback. */ _callback.attach(fptr); _callback_attached = true; } /** * @brief Attach a user-defined non-static callback that will be executed * whenever PCM data are ready, i.e. once each millisecond. * The provided PCM buffer will be filled by the microphones. * @param tptr Pointer to an object. * @param mptr Pointer to an object's callback. * @retval None. */ template<typename T> void XNucleoCCA02M1::attach(T *tptr, void (T::*mptr) (int16_t *PCM_buffer, uint16_t PCM_buffer_bytes)) { /* Allocating support buffers. */ if ((_USER_PCM_buffer_one_ms = (int16_t *) calloc(_PCM_samples_one_ms, sizeof(uint16_t))) == NULL) error("Instantiation of support buffers failed.\r\n"); /* Attaching the callback. */ _callback.attach(tptr, mptr); _callback_attached = true; } /** * @brief I2S callback which is executed whenever PCM data are ready, i.e. once * each millisecond. * @param narg Narg flag. * @retval None. */ void XNucleoCCA02M1::i2s_callback(int narg) { /* Checking for errors. */ if (!(narg & (I2S_EVENT_RX_COMPLETE | I2S_EVENT_RX_HALF_COMPLETE))) error("Unexpected transmission event.\r\n"); /* PDM to PCM Conversion. */ if (narg & (I2S_EVENT_RX_COMPLETE | I2S_EVENT_RX_HALF_COMPLETE)) { #ifdef X_NUCLEO_CCA02M1_DEBUG _i2s_signal = 1; #endif uint32_t PDM_index = (narg & I2S_EVENT_RX_HALF_COMPLETE ? 0 : _PDM_samples_one_ms); switch (_channels) { case 1: /* Scrambling PDM audio data. */ _pdm2pcm->scramble(_PDM_buffer_one_ms, &_PDM_buffer_two_ms[PDM_index], _PDM_samples_one_ms); break; case 2: /* Demuxing PDM audio data. */ _pdm2pcm->demux(_PDM_buffer_one_ms, &_PDM_buffer_two_ms[PDM_index], _PDM_samples_one_ms); break; } /* Acquiring resources. */ _mutex->lock(); /* Converting PDM to PCM audio data. */ _pdm2pcm->convert(&_PCM_buffer_n_ms[_PCM_buffer_write_index], _PDM_buffer_one_ms, _volume, _decimation_factor); /* Copying PCM data to the user buffer. */ if (_callback_attached) memcpy(_USER_PCM_buffer_one_ms, &_PCM_buffer_n_ms[_PCM_buffer_write_index], _PCM_samples_one_ms * BYTES_PER_SAMPLE); /* Updating write index. */ _PCM_buffer_write_index += _PCM_samples_one_ms; _PCM_buffer_write_index %= (PCM_BUFFER_SIZE_ms * _PCM_samples_one_ms); /* Releasing resources. */ _mutex->unlock(); /* Executing user-defined callback. */ static bool first_time = true; if (_callback_attached && first_time) { _callback.call(_USER_PCM_buffer_one_ms, _PCM_samples_one_ms * BYTES_PER_SAMPLE); first_time = !first_time; } #ifdef X_NUCLEO_CCA02M1_DEBUG _i2s_signal = 0; #endif } /* Start sending data through the USB whenever the PCM buffer has been filled up to the medium threshold. */ if (_usb_enabled) { static bool done = false; static uint32_t calls = 0; if (!done) if (calls++ >= PCM_BUFFER_TH_MED_ms) { usb_handler(); done = true; } } } /** * @brief Sending PCM data via USB. * @param None. * @retval None. */ void XNucleoCCA02M1::usb_handler(void) { #ifdef X_NUCLEO_CCA02M1_DEBUG _usb_signal = 1; #endif /* Acquiring resources. */ _mutex->lock(); /* Computing the delta-data to send through the USB depending on the dis-alignment between the read and write index of the PCM buffer, which may happen whenever the I2S and the USB do not works at the same frequency (i.e. 1KHz). */ int32_t PCM_diff = _PCM_buffer_write_index - _PCM_buffer_read_index; PCM_diff = (PCM_diff >= 0 ? PCM_diff : PCM_diff + PCM_BUFFER_SIZE_ms * _PCM_samples_one_ms); int32_t PCM_buffer_high_index = PCM_BUFFER_TH_HIG_ms * _PCM_samples_one_ms; int32_t PCM_buffer_low_index = PCM_BUFFER_TH_LOW_ms * _PCM_samples_one_ms; USBAudio::AudioSampleCorrectType PCM_delta_samples = USBAudio::NoCorrection; if (PCM_diff >= PCM_buffer_high_index) { PCM_delta_samples = USBAudio::AddOneSample; #ifdef X_NUCLEO_CCA02M1_DEBUG _buffer_overrun = 1; _buffer_underrun = 0; #endif } else if (PCM_diff <= PCM_buffer_low_index) { PCM_delta_samples = USBAudio::RemoveOneSample; #ifdef X_NUCLEO_CCA02M1_DEBUG _buffer_overrun = 0; _buffer_underrun = 1; } else { _buffer_overrun = _buffer_underrun = 0; #endif } /* Writing data through the USB. */ 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)) _USB_PCM_buffer_one_ms[i++] = _PCM_buffer_n_ms[j++]; _usb_audio->writeSync((uint8_t *) _USB_PCM_buffer_one_ms, PCM_delta_samples); /* Updating read index. */ _PCM_buffer_read_index += _PCM_samples_one_ms + ((uint32_t) PCM_delta_samples) * _channels; _PCM_buffer_read_index %= (PCM_BUFFER_SIZE_ms * _PCM_samples_one_ms); /* Releasing resources. */ _mutex->unlock(); #ifdef X_NUCLEO_CCA02M1_DEBUG _usb_signal = 0; #endif }