Il y avait des problèmes dans la libraire...
Dependencies: ST_FREQUENCY_DIVIDER ST_I2S USBDEVICE
Fork of X_NUCLEO_CCA02M1 by
Diff: BSP/XNucleoCCA02M1.cpp
- Revision:
- 2:9f389fd8fb2e
- Child:
- 6:9b8bc842aeb3
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BSP/XNucleoCCA02M1.cpp Fri Apr 21 10:33:08 2017 +0200 @@ -0,0 +1,358 @@ +/** + ****************************************************************************** + * @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. +*/ +status_t XNucleoCCA02M1::init(void *init) +{ + /* Storing data. */ + if (init != NULL) + { + _frequency = (*((XNucleoCCA02M1_init_t *) init)).frequency; + _channels = (*((XNucleoCCA02M1_init_t *) init)).channels; + } + + /* 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, if needed. */ + if (_channels != 1) { + 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 +}