Il y avait des problèmes dans la libraire...

Dependents:   X_NUCLEO_CCA02M1

Fork of ST_I2S by ST

Revision:
1:f90318e0923b
Child:
5:74da3773bf43
Child:
2:0c9ce59aee25
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/TARGET_STM/stm_i2s_api.c	Tue Nov 29 14:43:26 2016 +0100
@@ -0,0 +1,792 @@
+#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"
+
+// #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
+    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] = dma_channel_allocate(MAKE_CAP(obj->dma.dma_device, DMA_TX));
+	    MBED_ASSERT(obj->dma.dma[DMA_TX] != DMA_ERROR_OUT_OF_CHANNELS);
+	}
+	break;
+    case DMA_RX:
+    default:
+	if(*use_rx) {
+	    obj->dma.dma[DMA_RX] = dma_channel_allocate(MAKE_CAP(obj->dma.dma_device, DMA_RX));
+	    MBED_ASSERT(obj->dma.dma[DMA_RX] != 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] = 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] != DMA_ERROR_OUT_OF_CHANNELS);
+	    }
+	    break;
+	case DMA_RX:
+	default:
+	    if(*use_tx) {
+		obj->dma.dma[DMA_TX] = 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] != 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
+    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 */
+	PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S;
+	PeriphClkInitStruct.PLLI2S.PLLI2SN = 271; // betzw: use values which are suggested in Table 90. 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        = I2S_MCLKOUTPUT_ENABLE; // betzw: always enable master clock to avoid frequency dis-alignment between I2S devices
+    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);
+}
+
+#endif