STMicroelectronics' implementation of an I2S driver, also including DMA support.

Dependents:   temp X_NUCLEO_CCA01M1 X_NUCLEO_CCA01M1 X_NUCLEO_CCA02M1

Platform compatibility

This driver has been designed to support a wide range of the Nucleo F4 Family of platforms and MCUs, but not all members of this family support I2S and/or some of the members might require slight modifications to the sources of this driver in order to make it work on those.

This driver has for now been tested only with the following platforms:

Revision:
20:54b1a9b620c5
Parent:
19:ef6ef1795e30
Child:
21:7cf6b1538d29
--- a/targets/TARGET_STM/stm_i2s_api.c	Fri Jan 27 08:35:23 2017 +0100
+++ b/targets/TARGET_STM/stm_i2s_api.c	Mon Jan 30 10:17:44 2017 +0100
@@ -785,61 +785,7 @@
     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)roundf(f);
-}
-
-static uint32_t i2s_compute_closest_frequency(i2s_t *dev_i2s, uint32_t target_freq) {
+static float 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;
 
@@ -890,7 +836,7 @@
     uint32_t mclk_factor = (hi2s->Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE ? (format_factor == 16 ? 8 : 4) : 1);
     float f = ((float)i2sclk / (float)(2 * format_factor * ((2 * i2sdiv) + i2sodd) * mclk_factor));
 
-    return (uint32_t)roundf(f);
+    return f;
 }
 
 /** Compute the real frequency of a given I2S objects.
@@ -898,7 +844,7 @@
  *  @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)
+static inline float i2s_compute_real_frequency(i2s_t *dev_i2s)
 {
     I2S_HandleTypeDef *hi2s;
 
@@ -918,10 +864,11 @@
  *  @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)
+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,
+		float real_low_freq, float 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 division = real_high_freq / real_low_freq;
     float rest = (division - (uint32_t)division);
     MBED_ASSERT(rest >= 0);
     if (rest == 0) {
@@ -931,25 +878,42 @@
     /* 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)magic_factor;
+    float new_low_freq = real_low_freq;
+    uint32_t new_low_freq_int_ceil = (uint32_t)ceilf(new_low_freq);
+    uint32_t new_low_freq_int_floor = (uint32_t)new_low_freq;
 
-    if(real_high_freq > (*ptr_high_freq)) {
-    	if(magic_factor_floor <= 1) {
-	    return -1;
-        }
+    uint32_t new_low_freq_int;
+    float real_new_low_freq = i2s_compute_closest_frequency(i2s_t_l, new_low_freq_int_floor);
+    if(real_new_low_freq == new_low_freq) {
+    	new_low_freq_int = new_low_freq_int_floor;
+    } else {
+    	real_new_low_freq = i2s_compute_closest_frequency(i2s_t_l, new_low_freq_int_ceil);
+    	if(real_new_low_freq == new_low_freq) {
+    		new_low_freq_int = new_low_freq_int_ceil;
+    	} else {
+    		return -1; // should never happen!
+    	}
     }
 
-    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; // potentially this high frequency might not be achievable!
-    uint32_t real_new_high_freq = i2s_compute_closest_frequency(i2s_t_h, new_high_freq);
+    float new_high_freq = multiplier * new_low_freq;
+    uint32_t new_high_freq_int_ceil = (uint32_t)ceilf(new_high_freq);
+    uint32_t new_high_freq_int_floor = (uint32_t)new_high_freq;
 
-    if(new_high_freq != real_new_high_freq) {
-    	return -1;
+    uint32_t new_high_freq_int;
+    float real_new_high_freq = i2s_compute_closest_frequency(i2s_t_h, new_high_freq_int_ceil);
+    if(real_new_high_freq == new_high_freq) {
+    	new_high_freq_int = new_high_freq_int_ceil;
+    } else {
+    	real_new_high_freq = i2s_compute_closest_frequency(i2s_t_h, new_high_freq_int_floor);
+    	if(real_new_high_freq == new_high_freq) {
+        	new_high_freq_int = new_high_freq_int_floor;
+    	} else {
+    		return -1;
+    	}
     }
 
-    *ptr_low_freq = new_low_freq;
-    *ptr_high_freq = new_high_freq;
+    *ptr_low_freq = new_low_freq_int;
+    *ptr_high_freq = new_high_freq_int;
 
     return 0;
 }
@@ -962,8 +926,8 @@
     }
 
     /* 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);
+    float real_f1 = i2s_compute_real_frequency(dev_i2s_1);
+    float real_f2 = i2s_compute_real_frequency(dev_i2s_2);
 
     /* Returning if the two real frequencies are already equal. */
     if(real_f1 == real_f2) {