mirror of https://github.com/ARMmbed/mbed-os.git
162 lines
5.7 KiB
C
162 lines
5.7 KiB
C
/* mbed Microcontroller Library
|
|
* Copyright (c) 2006-2018 ARM Limited
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#if DEVICE_SAI
|
|
|
|
#include <string.h>
|
|
#include "stm_i2s_driver.h"
|
|
#include "stm32f4xx_ll_rcc.h"
|
|
#include "stm32f4xx_ll_spi.h"
|
|
#include "PeripheralPins.h"
|
|
|
|
static sai_result_t stm_i2s_init(sai_t *obj, sai_init_t *init);
|
|
static bool stm_i2s_transfer(sai_t *obj, uint32_t *sample);
|
|
static void stm_i2s_free(sai_t *obj);
|
|
|
|
const stm_sai_api_t stm_i2s_vtable = {
|
|
.init = stm_i2s_init,
|
|
.transfer = stm_i2s_transfer,
|
|
.free = stm_i2s_free
|
|
};
|
|
|
|
static sai_result_t stm_i2s_init(sai_t *obj, sai_init_t *init) {
|
|
if ((memcmp(&(init->format), &sai_mode_i2s16w32, sizeof(sai_format_t)) != 0) &&
|
|
(memcmp(&(init->format), &sai_mode_i2s16, sizeof(sai_format_t)) != 0)) {
|
|
// we only support 1 format so far
|
|
return SAI_RESULT_CONFIG_UNSUPPORTED;
|
|
}
|
|
if (init->sample_rate != 8000) {
|
|
return SAI_RESULT_CONFIG_UNSUPPORTED;
|
|
}
|
|
|
|
// validate pins & fetch base peripheral address
|
|
int32_t mclk = pinmap_peripheral(init->mclk, PinMap_SAI_MCLK);
|
|
int32_t sd = pinmap_peripheral(init->sd, PinMap_SAI_SD);
|
|
int32_t bclk = pinmap_peripheral(init->bclk, PinMap_SAI_BCLK);
|
|
int32_t wclk = pinmap_peripheral(init->wclk, PinMap_SAI_WCLK);
|
|
MBED_ASSERT((sd != NC) && (bclk != NC) && (wclk != NC));
|
|
|
|
// should probably handle here I2Sxext
|
|
|
|
int32_t base = pinmap_merge(pinmap_merge(pinmap_merge(sd, bclk), wclk), mclk);
|
|
MBED_ASSERT(base != NC);
|
|
MBED_ASSERT((obj != NULL) && (obj->base != NULL) && (obj->base->type == SAI_PERIPH_TYPE_I2S));
|
|
|
|
switch ((uint32_t)obj->base->u.spi) {
|
|
case (uint32_t)SPI_2:
|
|
case (uint32_t)I2S2ext:
|
|
__HAL_RCC_SPI2_CLK_ENABLE();
|
|
break;
|
|
case (uint32_t)SPI_3:
|
|
case (uint32_t)I2S3ext:
|
|
__HAL_RCC_SPI3_CLK_ENABLE();
|
|
break;
|
|
default:
|
|
return SAI_RESULT_CONFIG_UNSUPPORTED;
|
|
}
|
|
|
|
/* I2S GPIO Configuration --------------------------------------------------*/
|
|
if (init->mclk != (PinName)NC) {
|
|
pinmap_pinout(init->mclk, PinMap_SAI_MCLK);
|
|
}
|
|
pinmap_pinout(init->sd, PinMap_SAI_SD);
|
|
pinmap_pinout(init->bclk, PinMap_SAI_BCLK);
|
|
pinmap_pinout(init->wclk, PinMap_SAI_WCLK);
|
|
|
|
/* I2S configuration -------------------------------------------------------*/
|
|
if (LL_I2S_DeInit(obj->base->u.spi) != SUCCESS) {
|
|
return SAI_RESULT_GENERIC_FAILURE;
|
|
}
|
|
|
|
RCC_PeriphCLKInitTypeDef PeriphClkInit;
|
|
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_I2S;
|
|
PeriphClkInit.PLLI2S.PLLI2SN = 256; /*!< This parameter must be a number between Min_Data = 50 and Max_Data = 432. */ // VCO max 432MHz
|
|
PeriphClkInit.PLLI2S.PLLI2SR = 2; /*!< This parameter must be a number between Min_Data = 2 and Max_Data = 7. */
|
|
PeriphClkInit.PLLI2S.PLLI2SQ = 2; /*!< This parameter must be a number between Min_Data = 2 and Max_Data = 15. */ // output max 192 MHz
|
|
|
|
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) {
|
|
return SAI_RESULT_GENERIC_FAILURE;
|
|
}
|
|
|
|
LL_RCC_SetI2SClockSource(LL_RCC_I2S1_CLKSOURCE_PLLI2S);
|
|
|
|
/* Configure the Audio Frequency, Standard and the data format */
|
|
LL_I2S_InitTypeDef init_struct;
|
|
|
|
LL_I2S_StructInit(&init_struct);
|
|
init_struct.Mode = obj->is_receiver?LL_I2S_MODE_SLAVE_RX:LL_I2S_MODE_MASTER_TX;
|
|
init_struct.Standard = LL_I2S_STANDARD_PHILIPS;
|
|
init_struct.DataFormat = LL_I2S_DATAFORMAT_16B_EXTENDED;
|
|
init_struct.MCLKOutput = LL_I2S_MCLK_OUTPUT_ENABLE;
|
|
init_struct.AudioFreq = LL_I2S_AUDIOFREQ_8K;
|
|
init_struct.ClockPolarity = LL_I2S_POLARITY_LOW;
|
|
|
|
if (LL_I2S_Init(obj->base->u.spi, &init_struct) != SUCCESS) {
|
|
return SAI_RESULT_GENERIC_FAILURE;
|
|
}
|
|
|
|
LL_I2S_Enable(obj->base->u.spi);
|
|
|
|
return SAI_RESULT_OK;
|
|
}
|
|
|
|
static bool stm_i2s_transfer(sai_t *obj, uint32_t *psample) {
|
|
bool ret = false;
|
|
if (obj->is_receiver) {
|
|
if (LL_I2S_IsActiveFlag_FRE(obj->base->u.spi) && !LL_I2S_IsActiveFlag_CHSIDE(obj->base->u.spi)) {
|
|
uint32_t tmp = 0xFFF;
|
|
while (tmp-- && !LL_I2S_IsActiveFlag_CHSIDE(obj->base->u.spi));
|
|
if (LL_I2S_IsActiveFlag_CHSIDE(obj->base->u.spi)) {
|
|
LL_I2S_Disable(obj->base->u.spi);
|
|
LL_I2S_Enable(obj->base->u.spi);
|
|
}
|
|
}
|
|
|
|
if (LL_I2S_IsActiveFlag_OVR(obj->base->u.spi)) {
|
|
LL_I2S_ClearFlag_OVR(obj->base->u.spi);
|
|
}
|
|
|
|
/* Data Received through I2Sx SD pin */
|
|
ret = !LL_SPI_IsActiveFlag_RXNE(obj->base->u.spi);
|
|
if (ret) {
|
|
uint32_t sample = LL_I2S_ReceiveData16(obj->base->u.spi);
|
|
if (psample != NULL) {
|
|
*psample = sample;
|
|
}
|
|
}
|
|
} else {
|
|
if (!LL_I2S_IsEnabled(obj->base->u.spi)) {
|
|
LL_I2S_Enable(obj->base->u.spi);
|
|
}
|
|
|
|
uint32_t sample = 0;
|
|
if (psample != NULL) { sample = *psample; }
|
|
|
|
/* Data to transmitted through I2Sx_ext SD pin */
|
|
ret = LL_SPI_IsActiveFlag_TXE(obj->base->u.spi);
|
|
if (ret) {
|
|
LL_I2S_TransmitData16(obj->base->u.spi, sample);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void stm_i2s_free(sai_t *obj) {
|
|
while (LL_I2S_DeInit(obj->base->u.spi) != SUCCESS) ;
|
|
}
|
|
|
|
#endif // DEVICE_SAI
|