Il y avait des problèmes dans la libraire...
Fork of ST_I2S by
targets/TARGET_STM/stm_i2s_api.c
- Committer:
- Wolfgang Betz
- Date:
- 2017-01-20
- Revision:
- 14:0060a9850c5f
- Parent:
- 13:fa1b24df9025
- Child:
- 15:13ee1f813328
File content as of revision 14:0060a9850c5f:
#include "mbed_assert.h" #include "mbed_error.h" #include "stm_i2s_api.h" #include "stm_dma_caps.h" #if DEVICE_I2S #include <math.h> #include <string.h> #include "cmsis.h" #include "pinmap.h" #include "PeripheralPins.h" #include "StmI2sPeripheralPins.h" // #define DEBUG_STDIO 1 // betzw - TODO: temporarily enable debug printfs #ifndef DEBUG_STDIO # define DEBUG_STDIO 0 #endif #if DEBUG_STDIO # include <stdio.h> # define DEBUG_PRINTF(...) do { printf(__VA_ARGS__); } while(0) #else # define DEBUG_PRINTF(...) {} #endif typedef enum { I2S_TRANSFER_TYPE_TX = 1, I2S_TRANSFER_TYPE_RX = 2, I2S_TRANSFER_TYPE_TXRX = 3, } transfer_type_t; typedef struct { DMA_HandleTypeDef tx_dma_handle; DMA_HandleTypeDef rx_dma_handle; } dma_handles_t; #define I2S_NUM (5) // betzw: this approach wastes quite a bit of memory - TO BE IMPROVED!?!? static I2S_HandleTypeDef I2sHandle[I2S_NUM]; static DMA_HandleTypeDef DMaHandles[I2S_NUM][NUM_OF_DIRECTIONS]; static void init_i2s(i2s_t *obj) { I2S_HandleTypeDef *handle = &I2sHandle[obj->i2s.module]; __HAL_I2S_DISABLE(handle); HAL_I2S_Init(handle); __HAL_I2S_ENABLE(handle); } static void init_dmas(i2s_t *obj) { DMA_HandleTypeDef *primary_handle = NULL; DMA_HandleTypeDef *secondary_handle = NULL; DMA_HandleTypeDef *hdmatx = NULL; switch(obj->dma.dma_direction) { case DMA_TX: if(obj->dma.dma[DMA_TX] != NULL) { hdmatx = primary_handle = &DMaHandles[obj->i2s.module][DMA_TX]; } if(obj->dma.dma[DMA_RX] != NULL) { secondary_handle = &DMaHandles[obj->i2s.module][DMA_RX]; } break; case DMA_RX: default: if(obj->dma.dma[DMA_RX] != NULL) { primary_handle = &DMaHandles[obj->i2s.module][DMA_RX]; } if(obj->dma.dma[DMA_TX] != NULL) { hdmatx = secondary_handle = &DMaHandles[obj->i2s.module][DMA_TX]; } break; } if(primary_handle != NULL) { __HAL_DMA_DISABLE(primary_handle); HAL_DMA_Init(primary_handle); if(hdmatx == primary_handle) { __HAL_LINKDMA(&I2sHandle[obj->i2s.module], hdmatx, *primary_handle); } else { __HAL_LINKDMA(&I2sHandle[obj->i2s.module], hdmarx, *primary_handle); } } if(secondary_handle != NULL) { __HAL_DMA_DISABLE(secondary_handle); HAL_DMA_Init(secondary_handle); if(hdmatx == secondary_handle) { __HAL_LINKDMA(&I2sHandle[obj->i2s.module], hdmatx, *secondary_handle); } else { __HAL_LINKDMA(&I2sHandle[obj->i2s.module], hdmarx, *secondary_handle); } } } static inline uint32_t i2s_get_mode(i2s_mode_t mode, uint8_t *direction) { switch(mode) { case SLAVE_TX: *direction = DMA_TX; return I2S_MODE_SLAVE_TX; case SLAVE_RX: *direction = DMA_RX; return I2S_MODE_SLAVE_RX; case MASTER_TX: *direction = DMA_TX; return I2S_MODE_MASTER_TX; case MASTER_RX: default: *direction = DMA_RX; return I2S_MODE_MASTER_RX; } } static inline uint32_t i2s_get_priority(i2s_dma_prio_t priority) { switch(priority) { case LOW: return DMA_PRIORITY_LOW; case URGENT: return DMA_PRIORITY_VERY_HIGH; case HIGH: return DMA_PRIORITY_HIGH; default: return DMA_PRIORITY_MEDIUM; } } static void dma_i2s_init(i2s_t *obj, bool *use_tx, bool *use_rx, bool circular, i2s_dma_prio_t prio) { // DMA declarations DMA_HandleTypeDef *primary_handle = &DMaHandles[obj->i2s.module][obj->dma.dma_direction]; DMA_HandleTypeDef *secondary_handle = NULL; // DMA initialization & configuration stm_dma_init(); obj->dma.dma[DMA_TX] = obj->dma.dma[DMA_RX] = NULL; switch(obj->dma.dma_direction) { case DMA_TX: if(*use_tx) { obj->dma.dma[DMA_TX] = stm_dma_channel_allocate(MAKE_CAP(obj->dma.dma_device, DMA_TX)); MBED_ASSERT(obj->dma.dma[DMA_TX] != STM_DMA_ERROR_OUT_OF_CHANNELS); } break; case DMA_RX: default: if(*use_rx) { obj->dma.dma[DMA_RX] = stm_dma_channel_allocate(MAKE_CAP(obj->dma.dma_device, DMA_RX)); MBED_ASSERT(obj->dma.dma[DMA_RX] != STM_DMA_ERROR_OUT_OF_CHANNELS); } break; } // Primary DMA configuration if(obj->dma.dma[obj->dma.dma_direction] != NULL) { primary_handle->Instance = obj->dma.dma[obj->dma.dma_direction]->dma_stream; primary_handle->Init.Channel = obj->dma.dma[obj->dma.dma_direction]->channel_nr; primary_handle->Init.Direction = (obj->dma.dma_direction == DMA_TX) ? DMA_MEMORY_TO_PERIPH : DMA_PERIPH_TO_MEMORY; primary_handle->Init.FIFOMode = DMA_FIFOMODE_DISABLE; primary_handle->Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; primary_handle->Init.MemBurst = DMA_MBURST_SINGLE; primary_handle->Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; primary_handle->Init.MemInc = DMA_MINC_ENABLE; primary_handle->Init.Mode = (circular ? DMA_CIRCULAR : DMA_NORMAL); primary_handle->Init.PeriphBurst = DMA_PBURST_SINGLE; primary_handle->Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; primary_handle->Init.PeriphInc = DMA_PINC_DISABLE; primary_handle->Init.Priority = i2s_get_priority(prio); } // Allocate secondary DMA channel (if full-duplex) if(obj->i2s.pin_fdpx != NC) { switch(obj->dma.dma_direction) { case DMA_TX: if(*use_rx) { obj->dma.dma[DMA_RX] = stm_dma_channel_allocate(MAKE_CAP(obj->dma.dma_device, DMA_RX)); secondary_handle = &DMaHandles[obj->i2s.module][DMA_RX]; MBED_ASSERT(obj->dma.dma[DMA_RX] != STM_DMA_ERROR_OUT_OF_CHANNELS); } break; case DMA_RX: default: if(*use_tx) { obj->dma.dma[DMA_TX] = stm_dma_channel_allocate(MAKE_CAP(obj->dma.dma_device, DMA_TX)); secondary_handle = &DMaHandles[obj->i2s.module][DMA_TX]; MBED_ASSERT(obj->dma.dma[DMA_TX] != STM_DMA_ERROR_OUT_OF_CHANNELS); } break; } } // Secondary DMA configuration if(secondary_handle != NULL) { uint8_t secondary_dma_direction = (obj->dma.dma_direction == DMA_TX) ? DMA_RX : DMA_TX; secondary_handle->Instance = obj->dma.dma[secondary_dma_direction]->dma_stream; secondary_handle->Init.Channel = obj->dma.dma[secondary_dma_direction]->channel_nr_fd; secondary_handle->Init.Direction = (secondary_dma_direction == DMA_TX) ? DMA_MEMORY_TO_PERIPH : DMA_PERIPH_TO_MEMORY; secondary_handle->Init.FIFOMode = DMA_FIFOMODE_DISABLE; secondary_handle->Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; secondary_handle->Init.MemBurst = DMA_MBURST_SINGLE; secondary_handle->Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; secondary_handle->Init.MemInc = DMA_MINC_ENABLE; secondary_handle->Init.Mode = (circular ? DMA_CIRCULAR : DMA_NORMAL); secondary_handle->Init.PeriphBurst = DMA_PBURST_SINGLE; secondary_handle->Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; secondary_handle->Init.PeriphInc = DMA_PINC_DISABLE; secondary_handle->Init.Priority = i2s_get_priority(prio); } if(obj->dma.dma[DMA_TX] == NULL) *use_tx = false; if(obj->dma.dma[DMA_RX] == NULL) *use_rx = false; // don't do anything, if the buffers aren't valid if (!use_tx && !use_rx) { DEBUG_PRINTF("I2S%u: No DMAs to init\n", obj->i2s.module+1); return; } DEBUG_PRINTF("I2S%u: DMA(s) Init\n", obj->i2s.module+1); init_dmas(obj); } static void dma_i2s_free(i2s_t *obj, uint8_t direction) { const struct dma_stream_s *stream = obj->dma.dma[direction]; MBED_ASSERT(stream != NULL); // disable irq NVIC_DisableIRQ(stream->dma_stream_irq); // free channel stm_dma_channel_free((void*)stream); obj->dma.dma[direction] = NULL; } void i2s_init(i2s_t *obj, PinName data, PinName sclk, PinName wsel, PinName fdpx, PinName mclk, i2s_mode_t mode) { uint8_t dma_dev = 0, dma_direction = 0; // Determine the I2S/SPI to use SPIName i2s_data = (SPIName)pinmap_peripheral(data, PinMap_I2S_DATA); SPIName i2s_sclk = (SPIName)pinmap_peripheral(sclk, PinMap_I2S_SCLK); SPIName i2s_wsel = (SPIName)pinmap_peripheral(wsel, PinMap_I2S_WSEL); SPIName i2s_fdpx = (SPIName)pinmap_peripheral(fdpx, PinMap_I2S_FDPX); SPIName i2s_mclk = (SPIName)pinmap_peripheral(mclk, PinMap_I2S_MCLK); SPIName i2s_merge1 = (SPIName)pinmap_merge(i2s_data, i2s_sclk); SPIName i2s_merge2 = (SPIName)pinmap_merge(i2s_wsel, i2s_fdpx); SPIName i2s_merge3 = (SPIName)pinmap_merge(i2s_merge1, i2s_merge2); SPIName instance = (SPIName)pinmap_merge(i2s_merge3, i2s_mclk); MBED_ASSERT(instance != (SPIName)NC); // Enable I2S/SPI clock and set the right module number switch(instance) { #if defined(I2S1ext_BASE) case SPI_1: __SPI1_CLK_ENABLE(); obj->i2s.module = 0; dma_dev = DMA_SPI1; break; #endif #if defined(I2S2ext_BASE) case SPI_2: __SPI2_CLK_ENABLE(); obj->i2s.module = 1; dma_dev = DMA_SPI2; break; #endif #if defined(I2S3ext_BASE) case SPI_3: __SPI3_CLK_ENABLE(); obj->i2s.module = 2; dma_dev = DMA_SPI3; break; #endif #if defined(I2S4ext_BASE) case SPI_4: __SPI4_CLK_ENABLE(); obj->i2s.module = 3; dma_dev = DMA_SPI4; break; #endif #if defined(I2S5ext_BASE) case SPI_5: __SPI5_CLK_ENABLE(); obj->i2s.module = 4; dma_dev = DMA_SPI5; break; #endif default: MBED_ASSERT(0); break; } // Save DMA device obj->dma.dma_device = dma_dev; // Configure the I2S pins pinmap_pinout(data, PinMap_I2S_DATA); pinmap_pinout(wsel, PinMap_I2S_WSEL); pinmap_pinout(sclk, PinMap_I2S_SCLK); pinmap_pinout(fdpx, PinMap_I2S_FDPX); pinmap_pinout(mclk, PinMap_I2S_MCLK); obj->i2s.pin_wsel = wsel; obj->i2s.pin_data = data; obj->i2s.pin_sclk = sclk; obj->i2s.pin_fdpx = fdpx; obj->i2s.pin_mclk = mclk; /* Configure PLLI2S */ static bool first_time = true; if(first_time) { RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; /* Get RTCClockSelection */ HAL_RCCEx_GetPeriphCLKConfig(&PeriphClkInitStruct); /* Set default configuration. Default frequency is 44100Hz. */ PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S; PeriphClkInitStruct.PLLI2S.PLLI2SN = 271; // betzw: use values which are suggested in Table 91 of the PeriphClkInitStruct.PLLI2S.PLLI2SR = 2; // reference manual for master clock enabled & 44100Hz. #ifdef NDEBUG HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct); #else HAL_StatusTypeDef ret = HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct); #endif MBED_ASSERT(ret == HAL_OK); first_time = false; } // initialize the handle for this master! I2S_HandleTypeDef *handle = &I2sHandle[obj->i2s.module]; handle->Instance = (SPI_TypeDef *)(instance); handle->Init.Mode = i2s_get_mode(mode, &dma_direction); handle->Init.Standard = I2S_STANDARD_PCM_SHORT; handle->Init.DataFormat = I2S_DATAFORMAT_16B; handle->Init.MCLKOutput = (mclk == NC ? I2S_MCLKOUTPUT_DISABLE : I2S_MCLKOUTPUT_ENABLE); handle->Init.AudioFreq = I2S_AUDIOFREQ_44K; handle->Init.CPOL = I2S_CPOL_LOW; handle->Init.ClockSource = I2S_CLOCK_PLL; handle->Init.FullDuplexMode = (fdpx == NC) ? I2S_FULLDUPLEXMODE_DISABLE : I2S_FULLDUPLEXMODE_ENABLE; // Save primary DMA direction obj->dma.dma_direction = dma_direction; DEBUG_PRINTF("I2S%u: Init\n", obj->i2s.module+1); init_i2s(obj); } void i2s_free(i2s_t *obj) { // Reset I2S and disable clock switch(obj->i2s.module) { #if defined(I2S1ext_BASE) case 0: __SPI1_FORCE_RESET(); __SPI1_RELEASE_RESET(); __SPI1_CLK_DISABLE(); break; #endif #if defined(I2S2ext_BASE) case 1: __SPI2_FORCE_RESET(); __SPI2_RELEASE_RESET(); __SPI2_CLK_DISABLE(); break; #endif #if defined(I2S3ext_BASE) case 2: __SPI3_FORCE_RESET(); __SPI3_RELEASE_RESET(); __SPI3_CLK_DISABLE(); break; #endif #if defined(I2S4ext_BASE) case 3: __SPI4_FORCE_RESET(); __SPI4_RELEASE_RESET(); __SPI4_CLK_DISABLE(); break; #endif #if defined(I2S5ext_BASE) case 4: __SPI5_FORCE_RESET(); __SPI5_RELEASE_RESET(); __SPI5_CLK_DISABLE(); break; #endif default: MBED_ASSERT(0); break; } // betzw - TODO: what about 'PLLI2S'?!? // for the moment we leave it enabled! // Configure GPIOs pin_function(obj->i2s.pin_wsel, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0)); pin_function(obj->i2s.pin_data, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0)); pin_function(obj->i2s.pin_sclk, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0)); pin_function(obj->i2s.pin_fdpx, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0)); pin_function(obj->i2s.pin_mclk, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0)); DEBUG_PRINTF("I2S%u: Free\n", obj->i2s.module+1); } void i2s_format(i2s_t *obj, int dbits, int fbits, int polarity) { I2S_HandleTypeDef *handle = &I2sHandle[obj->i2s.module]; // Save new values if (fbits == 16) { // format MUST be 16B handle->Init.DataFormat = I2S_DATAFORMAT_16B; } else { // format may NOT be 16B switch (dbits) { case 16: handle->Init.DataFormat = I2S_DATAFORMAT_16B_EXTENDED; break; case 24: handle->Init.DataFormat = I2S_DATAFORMAT_24B; break; case 32: default: handle->Init.DataFormat = I2S_DATAFORMAT_32B; break; } } handle->Init.CPOL = (polarity == 0) ? I2S_CPOL_LOW : I2S_CPOL_HIGH; DEBUG_PRINTF("I2S%u: Format: %u (%u, %u), %u (%u)\n", obj->i2s.module+1, (unsigned int)handle->Init.DataFormat, (unsigned int)dbits, (unsigned int)fbits, (unsigned int)handle->Init.CPOL, (unsigned int)polarity); init_i2s(obj); } void i2s_set_mode(i2s_t *obj, i2s_mode_t mode) { uint8_t dma_direction; I2S_HandleTypeDef *handle = &I2sHandle[obj->i2s.module]; handle->Init.Mode = i2s_get_mode(mode, &dma_direction); // Save primary DMA direction obj->dma.dma_direction = dma_direction; DEBUG_PRINTF("I2S%u: Mode: %u (%u)\n", obj->i2s.module+1, (unsigned int)handle->Init.Mode, (unsigned int)mode); init_i2s(obj); } void i2s_set_protocol(i2s_t *obj, i2s_bitorder_t protocol) { I2S_HandleTypeDef *handle = &I2sHandle[obj->i2s.module]; switch (protocol) { case PHILIPS: handle->Init.Standard = I2S_STANDARD_PHILIPS; break; case MSB: handle->Init.Standard = I2S_STANDARD_MSB; break; case LSB: handle->Init.Standard = I2S_STANDARD_LSB; break; case PCM_SHORT: handle->Init.Standard = I2S_STANDARD_PCM_SHORT; break; case PCM_LONG: default: handle->Init.Standard = I2S_STANDARD_PCM_LONG; break; } DEBUG_PRINTF("I2S%u: Protocol: %u (%u)\n", obj->i2s.module+1, (unsigned int)handle->Init.Standard, (unsigned int)protocol); init_i2s(obj); } void i2s_audio_frequency(i2s_t *obj, uint32_t hz) { I2S_HandleTypeDef *handle = &I2sHandle[obj->i2s.module]; if (IS_I2S_AUDIO_FREQ(hz) && (hz != I2S_AUDIOFREQ_DEFAULT)) { handle->Init.AudioFreq = hz; } else if (hz < I2S_AUDIOFREQ_8K) { handle->Init.AudioFreq = I2S_AUDIOFREQ_8K; } else { handle->Init.AudioFreq = I2S_AUDIOFREQ_192K; } DEBUG_PRINTF("I2S%u: Audio frequency: %u (%u)\n", obj->i2s.module+1, (unsigned int)handle->Init.AudioFreq, (unsigned int)hz); init_i2s(obj); } uint8_t i2s_get_module(i2s_t *obj) { return obj->i2s.module; } static void i2s_start_asynch_transfer(i2s_t *obj, transfer_type_t transfer_type, void *tx, void *rx, int length) { I2S_HandleTypeDef *handle = &I2sHandle[obj->i2s.module]; obj->i2s.transfer_type = transfer_type; // the HAL expects number of transfers instead of number of bytes int words; switch(handle->Init.DataFormat) { case I2S_DATAFORMAT_16B: case I2S_DATAFORMAT_16B_EXTENDED: words = length / 2; if(words > 0xFFFC) words = 0xFFFC; // truncate in order to respect max DMA length break; case I2S_DATAFORMAT_24B: case I2S_DATAFORMAT_32B: default: words = length / 4; if(words > 0x7FFC) words = 0x7FFC; // truncate in order to respect max DMA length break; } // enable the right hal transfer int rc = 0; switch(transfer_type) { case I2S_TRANSFER_TYPE_TXRX: // enable the interrupts NVIC_EnableIRQ(obj->dma.dma[DMA_TX]->dma_stream_irq); NVIC_EnableIRQ(obj->dma.dma[DMA_RX]->dma_stream_irq); // trigger DMA transfers rc = HAL_I2SEx_TransmitReceive_DMA(handle, (uint16_t*)tx, (uint16_t*)rx, (uint16_t)words); break; case I2S_TRANSFER_TYPE_TX: // enable the interrupt NVIC_EnableIRQ(obj->dma.dma[DMA_TX]->dma_stream_irq); // trigger DMA transfer rc = HAL_I2S_Transmit_DMA(handle, (uint16_t*)tx, (uint16_t)words); break; case I2S_TRANSFER_TYPE_RX: // enable the interrupt NVIC_EnableIRQ(obj->dma.dma[DMA_RX]->dma_stream_irq); // trigger DMA transfer rc = HAL_I2S_Receive_DMA(handle, (uint16_t*)rx, (uint16_t)words); break; } if (rc) { DEBUG_PRINTF("I2S%u: RC=%d\n", obj->i2s.module+1, rc); } return; } // asynchronous API void i2s_transfer(i2s_t *obj, void *tx, int tx_length, void *rx, int rx_length, bool circular, i2s_dma_prio_t prio, uint32_t handler_tx, uint32_t handler_rx, uint32_t event) { // check which use-case we have bool use_tx = (tx != NULL && tx_length > 0); bool use_rx = (rx != NULL && rx_length > 0); // Init DMAs dma_i2s_init(obj, &use_tx, &use_rx, circular, prio); // don't do anything, if the buffers aren't valid if (!use_tx && !use_rx) return; // copy the buffers to the I2S object obj->tx_buff.buffer = tx; obj->tx_buff.length = tx_length; obj->tx_buff.pos = 0; obj->tx_buff.width = 16; obj->rx_buff.buffer = rx; obj->rx_buff.length = rx_length; obj->rx_buff.pos = 0; obj->rx_buff.width = obj->tx_buff.width; obj->i2s.event = event; DEBUG_PRINTF("I2S%u: Transfer: %u, %u\n", obj->i2s.module+1, tx_length, rx_length); // register the thunking handler if(use_tx) { NVIC_SetVector(obj->dma.dma[DMA_TX]->dma_stream_irq, handler_tx); } if(use_rx) { NVIC_SetVector(obj->dma.dma[DMA_RX]->dma_stream_irq, handler_rx); } // enable the right hal transfer if (use_tx && use_rx) { int size = (tx_length < rx_length)? tx_length : rx_length; i2s_start_asynch_transfer(obj, I2S_TRANSFER_TYPE_TXRX, tx, rx, size); } else if (use_tx) { i2s_start_asynch_transfer(obj, I2S_TRANSFER_TYPE_TX, tx, NULL, tx_length); } else if (use_rx) { i2s_start_asynch_transfer(obj, I2S_TRANSFER_TYPE_RX, NULL, rx, rx_length); } } uint32_t i2s_irq_handler_asynch(i2s_t *obj, uint8_t direction) { direction = (direction == I2S_TX_EVENT) ? DMA_TX : DMA_RX; // use the right instance I2S_HandleTypeDef *i2s_handle = &I2sHandle[obj->i2s.module]; DMA_HandleTypeDef *dma_handle = (direction == DMA_TX) ? i2s_handle->hdmatx : i2s_handle->hdmarx; MBED_ASSERT(dma_handle != NULL); int event = 0; // call the Cube handler, this will update the handle HAL_DMA_IRQHandler(dma_handle); switch(HAL_I2S_GetState(i2s_handle)) { case HAL_I2S_STATE_READY: { // adjust buffer positions (betzw - TODO: to be checked for DMA transfers!!!) int tx_size = (i2s_handle->TxXferSize - i2s_handle->TxXferCount); int rx_size = (i2s_handle->RxXferSize - i2s_handle->RxXferCount); // take data format into consideration switch(i2s_handle->Init.DataFormat) { case I2S_DATAFORMAT_16B: case I2S_DATAFORMAT_16B_EXTENDED: tx_size *= 2; rx_size *= 2; break; case I2S_DATAFORMAT_24B: case I2S_DATAFORMAT_32B: default: tx_size *= 4; rx_size *= 4; break; } // adjust buffer positions if (obj->i2s.transfer_type != I2S_TRANSFER_TYPE_RX) { obj->tx_buff.pos += tx_size; } if (obj->i2s.transfer_type != I2S_TRANSFER_TYPE_TX) { obj->rx_buff.pos += rx_size; } if (i2s_handle->TxXferCount > 0) { DEBUG_PRINTF("I2S%u: TxXferCount: %u\n", obj->i2s.module+1, i2s_handle->TxXferCount); } if (i2s_handle->RxXferCount > 0) { DEBUG_PRINTF("I2S%u: RxXferCount: %u\n", obj->i2s.module+1, i2s_handle->RxXferCount); } } /* no break */ case HAL_I2S_STATE_BUSY_TX: case HAL_I2S_STATE_BUSY_RX: case HAL_I2S_STATE_BUSY_TX_RX: { int error = HAL_I2S_GetError(i2s_handle); if(error != HAL_I2S_ERROR_NONE) { // something went wrong and the transfer has definitely completed event = ((direction == DMA_TX) ? I2S_EVENT_TX_ERROR : I2S_EVENT_RX_ERROR) | I2S_EVENT_INTERNAL_TRANSFER_COMPLETE; if (error & HAL_I2S_ERROR_OVR) { // buffer overrun event |= I2S_EVENT_RX_OVERFLOW; } if (error & HAL_I2S_ERROR_UDR) { // buffer underrun event |= I2S_EVENT_TX_UNDERRUN; } // cleanup DMA (after error) dma_i2s_free(obj, direction); } else { // no error detected HAL_DMA_StateTypeDef dma_state = HAL_DMA_GetState(dma_handle); switch(dma_state) { case HAL_DMA_STATE_READY_HALF_MEM0: case HAL_DMA_STATE_READY_HALF_MEM1: event = ((direction == DMA_TX) ? I2S_EVENT_TX_HALF_COMPLETE : I2S_EVENT_RX_HALF_COMPLETE); break; case HAL_DMA_STATE_READY_MEM0: case HAL_DMA_STATE_READY_MEM1: event = ((direction == DMA_TX) ? I2S_EVENT_TX_COMPLETE : I2S_EVENT_RX_COMPLETE); if(dma_handle->Init.Mode != DMA_CIRCULAR) { if (!i2s_active(obj)) { // Check for full-duplex transfer complete! event |= I2S_EVENT_INTERNAL_TRANSFER_COMPLETE; } // cleanup DMA (because we are done) dma_i2s_free(obj, direction); } break; default: printf("betzw(%s, %d): dma_state=0x%x\r\n", __func__, __LINE__, (int)dma_state); MBED_ASSERT(0); break; } } } break; default: // nothing to do?!? break; } if (event) DEBUG_PRINTF("I2S%u: Event: 0x%x\n", obj->i2s.module+1, event); return (event & (obj->i2s.event | I2S_EVENT_INTERNAL_TRANSFER_COMPLETE)); } uint8_t i2s_active(i2s_t *obj) { I2S_HandleTypeDef *handle = &I2sHandle[obj->i2s.module]; HAL_I2S_StateTypeDef state = HAL_I2S_GetState(handle); switch(state) { case HAL_I2S_STATE_RESET: case HAL_I2S_STATE_READY: case HAL_I2S_STATE_ERROR: return 0; default: return -1; } } void i2s_abort_asynch(i2s_t *obj) { I2S_HandleTypeDef *i2s_handle = &I2sHandle[obj->i2s.module]; // Stop transfer HAL_I2S_DMAStop(i2s_handle); if(obj->dma.dma[DMA_TX] != NULL) { DMA_HandleTypeDef *dma_handle_tx = &DMaHandles[obj->i2s.module][DMA_TX]; // disable interrupt & free resource dma_i2s_free(obj, DMA_TX); //clean up __HAL_DMA_DISABLE(dma_handle_tx); HAL_DMA_DeInit(dma_handle_tx); HAL_DMA_Init(dma_handle_tx); __HAL_DMA_ENABLE(dma_handle_tx); } if(obj->dma.dma[DMA_RX] != NULL) { DMA_HandleTypeDef *dma_handle_rx = &DMaHandles[obj->i2s.module][DMA_RX]; // disable interrupt & free resource dma_i2s_free(obj, DMA_RX); //clean up __HAL_DMA_DISABLE(dma_handle_rx); HAL_DMA_DeInit(dma_handle_rx); HAL_DMA_Init(dma_handle_rx); __HAL_DMA_ENABLE(dma_handle_rx); } // clean-up I2S __HAL_I2S_DISABLE(i2s_handle); HAL_I2S_DeInit(i2s_handle); HAL_I2S_Init(i2s_handle); __HAL_I2S_ENABLE(i2s_handle); } /*** Code for harmonizing frequencies ***/ static inline I2S_HandleTypeDef *i2s_get_handle(i2s_t *obj) { return (I2S_HandleTypeDef *) &I2sHandle[obj->i2s.module]; } /** Computes the two-div-plus-odd factor of a given I2S objects * on a desired frequency. * * @param dev_i2s reference to the I2S object. * @param frequency the desired frequency. * @return the computed two-div-plus-odd factor. */ static float i2s_compute_magic_factor(i2s_t *dev_i2s, float frequency) { float i2sclk; I2S_HandleTypeDef *hi2s; /* Get the I2S handle. */ hi2s = i2s_get_handle(dev_i2s); /* Get I2S source Clock frequency. */ i2sclk = (float)I2S_GetInputClock(hi2s); /* Compute the I2S frequencies. */ float format_factor = (float)(hi2s->Init.DataFormat == I2S_DATAFORMAT_16B ? 16 : 32); float mclk_factor = (float)(hi2s->Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE ? (format_factor == 16 ? 8 : 4) : 1); float mf = i2sclk / ((float)2.0 * format_factor * frequency * mclk_factor); return mf; } /** Computing the frequency of an I2S peripheral given a certain * two-div-plus-odd factor. * * @param obj reference to an i2s_t structure. * @param magic_factor the given two-div-plus-odd factor. * @return the computed frequency. */ static uint32_t i2s_compute_frequency(i2s_t *obj, int32_t magic_factor) { float i2sclk; I2S_HandleTypeDef *hi2s; /* Getting the I2S handle. */ hi2s = i2s_get_handle(obj); /* Getting the I2S source clock frequency. */ i2sclk = (float)I2S_GetInputClock(hi2s); /* Computing the frequencies. */ float format_factor = (float)(hi2s->Init.DataFormat == I2S_DATAFORMAT_16B ? 16 : 32); float mclk_factor = (float)(hi2s->Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE ? (format_factor == 16 ? 8 : 4) : 1); float f = i2sclk / ((float)2.0 * format_factor * (float)magic_factor * mclk_factor); //printf("f = %d / (2 * %d * %f * %d) = %f\r\n", i2sclk, format_factor, magic_factor, mclk_factor, f); /* Returning the computed frequency. */ return (uint32_t)f; } static uint32_t i2s_compute_closest_frequency(i2s_t *dev_i2s, uint32_t target_freq) { uint32_t i2sclk = 0U, i2sdiv = 2U, i2sodd = 0U, packetlength = 1U, tmp = 0U; I2S_HandleTypeDef *hi2s; /* Get the I2S handle. */ hi2s = i2s_get_handle(dev_i2s); /* Check the frame length (For the Prescaler computing). */ if (hi2s->Init.DataFormat != I2S_DATAFORMAT_16B) { /* Packet length is 32 bits */ packetlength = 2U; } /* Get I2S source Clock frequency. */ i2sclk = I2S_GetInputClock(hi2s); /* Compute the Real divider depending on the MCLK output state, with a floating point. */ if (hi2s->Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE) { /* MCLK output is enabled. */ tmp = (uint32_t)(((((i2sclk / 256U) * 10U) / target_freq)) + 5U); } else { /* MCLK output is disabled. */ tmp = (uint32_t)(((((i2sclk / (32U * packetlength)) * 10U) / target_freq)) + 5U); } /* Remove the flatting point. */ tmp = tmp / 10U; /* Check the parity of the divider. */ i2sodd = (uint32_t)(tmp & (uint32_t)1U); /* Compute the i2sdiv prescaler. */ i2sdiv = (uint32_t)((tmp - i2sodd) / 2U); /* Test if the divider is 1 or 0 or greater than 0xFF. */ if ((i2sdiv < 2U) || (i2sdiv > 0xFFU)) { /* Set the default values. */ i2sdiv = 2U; i2sodd = 0U; } /* Compute the I2S frequencies. */ uint32_t format_factor = (hi2s->Init.DataFormat == I2S_DATAFORMAT_16B ? 16 : 32); uint32_t mclk_factor = (hi2s->Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE ? (format_factor == 16 ? 8 : 4) : 1); uint32_t f = (uint32_t)((float)i2sclk / (float)(2 * format_factor * ((2 * i2sdiv) + i2sodd) * mclk_factor)); return f; } /** Compute the real frequency of a given I2S objects. * * @param dev_i2s reference to the I2S object. * @return the computed real frequency. */ static inline uint32_t i2s_compute_real_frequency(i2s_t *dev_i2s) { I2S_HandleTypeDef *hi2s; /* Get the I2S handle. */ hi2s = i2s_get_handle(dev_i2s); return i2s_compute_closest_frequency(dev_i2s, hi2s->Init.AudioFreq); } /** Computing the harmonized frequencies of two I2S peripherals. * * @param[in] i2s_t_l reference to the i2s_t structure with the lower frequency. * @param[in] i2s_t_h reference to the i2s_t structure with the higher frequency. * @param[in|out] ptr_low_freq pointer to lower frequency. * @param[in|out] ptr_high_freq pointer to higher frequency. * @param[in] real_low_freq real higher frequency. * @param[in] real_high_freq real lower frequency. * @return "0" if the frequencies have been harmonized, "-1" otherwise. */ static int8_t i2s_compute_harmonized_frequencies(i2s_t *i2s_t_l, i2s_t *i2s_t_h, uint32_t *ptr_low_freq, uint32_t *ptr_high_freq, uint32_t real_low_freq, uint32_t real_high_freq) { /* Returning if the two real frequencies are already multiple one of the other. */ float division = (float)real_high_freq / (float)real_low_freq; float rest = (division - floorf(division)); MBED_ASSERT(rest >= 0); if (rest == 0) { return 0; } /* Computing the harmonized frequencies so that they are multiple one of the other by a certain factor. */ uint32_t multiplier = ((rest >= 0.5) ? ((uint32_t)division + 1) : (uint32_t)division); float magic_factor = i2s_compute_magic_factor(i2s_t_l, (float)real_high_freq / (float)multiplier); uint32_t magic_factor_floor = (uint32_t)floorf(magic_factor); if(real_high_freq > (*ptr_high_freq)) { if(magic_factor_floor <= 1) { return -1; } } uint32_t new_low_freq = i2s_compute_frequency(i2s_t_l, magic_factor_floor + ((real_high_freq > (*ptr_high_freq)) ? 1 : -1)); uint32_t new_high_freq = multiplier * new_low_freq; // betzw: potentially this high frequency might not be achievable! uint32_t real_new_high_freq = i2s_compute_closest_frequency(i2s_t_h, new_high_freq); if(new_high_freq != real_new_high_freq) { return -1; } *ptr_low_freq = new_low_freq; *ptr_high_freq = new_high_freq; return 0; } #if 0 // betzw /** Compute the desired frequency of a given I2S objects, given the magic * factor. * * @param dev_i2s reference to the I2S object. * @param mf the two-div-plus-odd factor. * @return the computed desired frequency. */ static float i2s_compute_desired_frequency(i2s_t *dev_i2s, float mf) { uint32_t i2sclk = 0U; I2S_HandleTypeDef *hi2s; /* Get the I2S handle. */ hi2s = i2s_get_handle(dev_i2s); /* Get I2S source Clock frequency. */ i2sclk = I2S_GetInputClock(hi2s); /* Compute the I2S frequencies. */ uint32_t format_factor = (hi2s->Init.DataFormat == I2S_DATAFORMAT_16B ? 16 : 32); uint32_t mclk_factor = (hi2s->Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE ? (format_factor == 16 ? 8 : 4) : 1); float f = i2sclk / (2 * format_factor * mf * mclk_factor); return f; } static uint32_t i2s_compute_closest_frequency(i2s_t *dev_i2s, uint32_t target_freq, uint8_t *mclk_enabled) { uint32_t i2sclk = 0U, i2sdiv = 2U, i2sodd = 0U, packetlength = 1U, tmp = 0U; uint32_t f1, f2; I2S_HandleTypeDef *hi2s; /* init mclk_enabled */ *mclk_enabled = 0; /* Get the I2S handle. */ hi2s = i2s_get_handle(dev_i2s); /* Check the frame length (For the Prescaler computing). */ if (hi2s->Init.DataFormat != I2S_DATAFORMAT_16B) { /* Packet length is 32 bits */ packetlength = 2U; } /* Get I2S source Clock frequency. */ i2sclk = I2S_GetInputClock(hi2s); if(hi2s->Init.MCLKOutput == I2S_MCLKOUTPUT_DISABLE) { // betzw: one more degree of freedom, i.e. mclk can be enabled /* MCLK output is disabled. */ tmp = (uint32_t)(((((i2sclk / (32U * packetlength)) * 10U) / target_freq)) + 5U); /* Remove the flatting point. */ tmp = tmp / 10U; /* Check the parity of the divider. */ i2sodd = (uint32_t)(tmp & (uint32_t)1U); /* Compute the i2sdiv prescaler. */ i2sdiv = (uint32_t)((tmp - i2sodd) / 2U); /* Test if the divider is 1 or 0 or greater than 0xFF. */ if ((i2sdiv < 2U) || (i2sdiv > 0xFFU)) { /* Set the default values. */ i2sdiv = 2U; i2sodd = 0U; } /* Compute the I2S frequency */ uint32_t format_factor = (hi2s->Init.DataFormat == I2S_DATAFORMAT_16B ? 16 : 32); uint32_t mclk_factor = 1; f1 = (uint32_t)((float)i2sclk / (float)(2 * format_factor * ((2 * i2sdiv) + i2sodd) * mclk_factor)); if(f1 == target_freq) return f1; /* set mclk_enabled */ *mclk_enabled = 1; } /* Set mclk enabled */ hi2s->Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE; /* MCLK output is enabled. */ tmp = (uint32_t)(((((i2sclk / 256U) * 10U) / target_freq)) + 5U); /* Remove the flatting point. */ tmp = tmp / 10U; /* Check the parity of the divider. */ i2sodd = (uint32_t)(tmp & (uint32_t)1U); /* Compute the i2sdiv prescaler. */ i2sdiv = (uint32_t)((tmp - i2sodd) / 2U); /* Test if the divider is 1 or 0 or greater than 0xFF. */ if ((i2sdiv < 2U) || (i2sdiv > 0xFFU)) { /* Set the default values. */ i2sdiv = 2U; i2sodd = 0U; } /* Compute the I2S frequency */ uint32_t format_factor = (hi2s->Init.DataFormat == I2S_DATAFORMAT_16B ? 16 : 32); uint32_t mclk_factor = (format_factor == 16 ? 8 : 4); f2 = (uint32_t)((float)i2sclk / (float)(2 * format_factor * ((2 * i2sdiv) + i2sodd) * mclk_factor)); if(f2 == target_freq) return f2; /* return closest result */ uint32_t diff1, diff2; if(*mclk_enabled != 0) { if(target_freq > f1) diff1 = target_freq - f1; else diff1 = f1 - target_freq; } else { // no 'f1' available return f2; } if(target_freq > f2) diff2 = target_freq - f2; else diff2 = f2 - target_freq; if(diff1 < diff2) { /* reset mclk */ hi2s->Init.MCLKOutput = I2S_MCLKOUTPUT_DISABLE; /* reset mclk_enabled */ *mclk_enabled = 0; return f1; } return f2; } static inline void i2s_reset_mclk(i2s_t *dev_i2s) { I2S_HandleTypeDef *hi2s; /* Get the I2S handle. */ hi2s = i2s_get_handle(dev_i2s); /* rest mclk enabled */ hi2s->Init.MCLKOutput = I2S_MCLKOUTPUT_DISABLE; } #endif // 0 int8_t i2s_harmonize(i2s_t *dev_i2s_1, uint32_t *freq_i2s_1, i2s_t *dev_i2s_2, uint32_t *freq_i2s_2) { /* Returning if the two set frequencies are not multiple one of the other. */ if((*freq_i2s_1 < *freq_i2s_2) ? ((*freq_i2s_2 % *freq_i2s_1) != 0) : ((*freq_i2s_1 % *freq_i2s_2) != 0)) { return -1; } /* Compute the real frequencies. */ uint32_t real_f1 = i2s_compute_real_frequency(dev_i2s_1); uint32_t real_f2 = i2s_compute_real_frequency(dev_i2s_2); /* Returning if the two real frequencies are already equal. */ if(real_f1 == real_f2) { return 0; } /* Computing the harmonized frequencies. */ if(real_f1 < real_f2) { return i2s_compute_harmonized_frequencies(dev_i2s_1, dev_i2s_2, freq_i2s_1, freq_i2s_2, real_f1, real_f2); } else { return i2s_compute_harmonized_frequencies(dev_i2s_2, dev_i2s_1, freq_i2s_2, freq_i2s_1, real_f2, real_f1); } #if 0 // betzw //printf("REAL: %f %f\r\n", f1, f2); /* Compute the desired frequencies so that they are multiple one of the other. */ /* figure out multiply value and i2s to adapt and it's target freq */ uint32_t multiplier; i2s_t *target_dev; uint32_t target_freq; if(f1 > f2) { multiplier = (uint32_t)((float)f1 / (float)f2); target_freq = f2 * multiplier; target_dev = dev_i2s_1; } else { multiplier = (uint32_t)((float)f2 / (float)f1); target_freq = f1 * multiplier; target_dev = dev_i2s_2; } if(multiplier == 1) { // nothing that can be reasonably done return -1; } /* compute closest freq */ uint8_t mclk_enabled = 0; uint32_t closest_freq = i2s_compute_closest_frequency(target_dev, target_freq, &mclk_enabled); if(closest_freq == target_freq) { if(target_dev == dev_i2s_1) { /* Return the desired frequencies. */ *freq_i2s_1 = i2s_align_freq(closest_freq); *freq_i2s_2 = i2s_align_freq(f2); } else { /* Return the desired frequencies. */ *freq_i2s_1 = i2s_align_freq(f1); *freq_i2s_2 = i2s_align_freq(closest_freq); } return 0; } /* reset mclk */ if(mclk_enabled != 0) { i2s_reset_mclk(target_dev); } /* try other way round */ if(target_dev == dev_i2s_1) { target_dev = dev_i2s_2; target_freq = (uint32_t)((float)f1 / (float)multiplier); } else { target_dev = dev_i2s_1; target_freq = (uint32_t) ((float)f2 / (float)multiplier); } /* compute closest freq */ mclk_enabled = 0; closest_freq = i2s_compute_closest_frequency(target_dev, target_freq, &mclk_enabled); if(closest_freq == target_freq) { if(target_dev == dev_i2s_1) { /* Return the desired frequencies. */ *freq_i2s_1 = i2s_align_freq(closest_freq); *freq_i2s_2 = i2s_align_freq(f2); } else { /* Return the desired frequencies. */ *freq_i2s_1 = i2s_align_freq(f1); *freq_i2s_2 = i2s_align_freq(closest_freq); } return 0; } /* reset mclk */ if(mclk_enabled != 0) { i2s_reset_mclk(target_dev); } /* return failure */ return -1; #endif // 0 } #endif