Showing results for 
Search instead for 
Did you mean: 

How do I configure an I2S on interface on the 32F746GDISCOVERY Board

Associate III

Hi, I'm new to the ST microcontroller, and I'm learning how to use it. I am working with the 32F746GDISCOVERY board and want to operate an S2S interface via the Arduino-IOs on the board.

From the data sheets I can see that I should be able to use the I2S2 resource for this:

  • I2S2_SD on the ST pin PB15 (R15), evaluation board CN7 pin 4 (D11)
  • I2S2_CK on the ST pin PI1 (D14), evaluation board CN7 pin 6 (D13)
  • I2S2_WS on the ST pin PB4 (A9). Eval board CN4 pin 4 (D3)

I also found an example (STM32Cube_FW_FW7_V1.12.0) that configures an I2S interface:

File: I2S/I2S_DataExchangeInterrupt/Src/main.c ->This example describes how to configure I2S using the STM32F7xx HAL API.

If I do this now analogous to this example, it does not work.

My first question: Where can I find more detailed documentation on the HAL drivers?

My second question: As I have understood this, the I2S2 resource can be assigned to different IOs. How is this configured?

  1. UM1905 (I'm not going to comment)
  2. Look into DS, there's a table where pins are assigned to various functions, the column is the "number" of function. Now look into the GPIO chapter of the RM, this "column number" goes into the AFRL/AFRH for the respective port/pin (while MODER for that pin is set to AF). I don't Cube so won't help with the particular incantation.


Associate III

OK, need some hours to understand a part of your answer, but so far an example for my Clock on PI1:

  1. Datatsheet In the Table 12. STM32F745xx and STM32F746xx alternate function mapping (continued): I see that I have to map PI1-Pin to AF5 (for I2S2_CK)
  2. So the "number"of function is 5
  3. In the RM on page 199 (GPIO) I read that all GPIOs have the alternate function selection registers GPIOx_AFRH and GPIOx_AFRL. But what exactly I have to write in to this registers was not clear for me. Do I have to write a five somewhere? In the AFR5-Section?0690X000006BvDtQAK.png
  4. In the MODER Register (GPIOI__MODER) i have to set the forth bit and clear the third, to say PI1 has an alternate function.

In addition I have also to setup the ouput states, speed selection etc. How do I change this registers in C? Which function do I have call, or which Macro do I have to use?

> In addition I have also to setup the ouput states, speed selection etc. How do I change this registers in C?

> Which function do I have call, or which Macro do I have to use?

As I've said I don't Cube, but if you look at the examples, you certainly notice that there is something like GPIO_Init() or similar function, and in there, a struct is filled with things like "mode is AF" and "number of alternate function" and then a initialization function (HAL_GPIO_Init()) is called, which does the "magic". They may be initialized also elsewhere, in some callback called from the I2S initialization, I don't quite get Cube's logic of doing things (that's why I dont' use it).

> Do I have to write a five somewhere? In the AFR5-Section?

Yes; but in Cube the "magic" in the init function as mentioned above does that. I do this using the usual bit manipulation routines, utiilizing macros defined in the standard CMSIS-mandated device header:


   & (~GPIO_MODER_MODER1)     // clear the field

 ) | (0

   | (GPIO_Mode_AlternateFunction * GPIO_MODER_MODER1_0) // and now set it to AF



   & (~GPIO_AFRL_AFRL1)     // clear the field

 ) | (0

   | (GPIO_AlternateFunction_I2S2 * GPIO_AFRL_AFRL1_0)  // I2S2_CK


(This, as it stands, wouldn't compile, as many constants are missing from the CMSIS-mandated device header, so I have my collection of header augmentations like

 // GPIOx_MODER - 2 bits per pin

#define GPIO_Mode_In                        0x00 // GPIO Input Mode

#define GPIO_Mode_Out                       0x01 // GPIO Output Mode

#define GPIO_Mode_AlternateFunction         0x02 // GPIO Alternate function Mode

#define GPIO_Mode_Analog                    0x03 // GPIO Analog Mode



You may also perhaps check out CubeMX - again something I don't use but that's a clicky program which might generate some code for you.


Associate III

Nice! Slowly it is getting clearer.. Also a nice example I found :

Unfortunately the macros GPIO_AFRL_AFRL1, GPIO_AlternateFunction_I2S2 and GPIO_AFRL_AFRL1_0 are not defined in my stm32f746xx.h What have you defined there?

Also for  GPIOI->AFRL I had to take GPIOI->AFR[0]

Yes I installed the CubeMX on my computer to see how it works. For some strange reason I could not find the 32F746GDISCOVERY board on it. But I also prefer understanding the GPIOs better, so I will try first to do it manually..


I don't use 'F7xx, but generally the scheme is the same. As I've said (and for the last few years I keep saying loudly and annoyingly wherever I meet ST employees, to no effect) ST doesn't define most of the needed constants in the device headers (they define them in the "libraries" headers, mostly in a non-systematic way, different in SPL and Cube) so I am augmenting the headers myself, trying to stick to a scheme used by ST where similar is existing, and sticking to my own scheme (with "final functionality" after double-underscore) where not.

In the headers I took from SPL and early Cube, GPIO_AFRL_AFRL1 was defined but GPIO_AFRL_AFRL1_0 wasn't so I defined them myself. However, looking into [STM32Cube_FW_F7_V1.7.0]\Drivers\CMSIS\Device\ST\STM32F7xx\Include\stm32f746xx.h , I see

/****************** Bit definition for GPIO_AFRL register *********************/
#define GPIO_AFRL_AFRL0_Pos              (0U)                                  
#define GPIO_AFRL_AFRL0_Msk              (0xFU << GPIO_AFRL_AFRL0_Pos)         /*!< 0x0000000F */
#define GPIO_AFRL_AFRL0                  GPIO_AFRL_AFRL0_Msk                   
#define GPIO_AFRL_AFRL0_0                (0x1U << GPIO_AFRL_AFRL0_Pos)         /*!< 0x00000001 */
#define GPIO_AFRL_AFRL0_1                (0x2U << GPIO_AFRL_AFRL0_Pos)         /*!< 0x00000002 */
#define GPIO_AFRL_AFRL0_2                (0x4U << GPIO_AFRL_AFRL0_Pos)         /*!< 0x00000004 */
#define GPIO_AFRL_AFRL0_3                (0x8U << GPIO_AFRL_AFRL0_Pos)         /*!< 0x00000008 */
#define GPIO_AFRL_AFRL1_Pos              (4U)                                  
#define GPIO_AFRL_AFRL1_Msk              (0xFU << GPIO_AFRL_AFRL1_Pos)         /*!< 0x000000F0 */
#define GPIO_AFRL_AFRL1                  GPIO_AFRL_AFRL1_Msk                   
#define GPIO_AFRL_AFRL1_0                (0x1U << GPIO_AFRL_AFRL1_Pos)         /*!< 0x00000010 */
#define GPIO_AFRL_AFRL1_1                (0x2U << GPIO_AFRL_AFRL1_Pos)         /*!< 0x00000020 */
#define GPIO_AFRL_AFRL1_2                (0x4U << GPIO_AFRL_AFRL1_Pos)         /*!< 0x00000040 */
#define GPIO_AFRL_AFRL1_3                (0x8U << GPIO_AFRL_AFRL1_Pos)         /*!< 0x00000080 */


I don't update Cuben regularly as I don't use them so this may be slightly outdated; please don't tell me ST omitted these constants from newer device headers...

> GPIO_AlternateFunction_I2S2

My own. Make it 5 if most of the I2S2 functionality is in the 5th column of the pin-AF table.

Also for GPIOI->AFRL I had to take GPIOI->AFR[0]

Yes, I have

#define AFRL AFR[0]

#define AFRH AFR[1]

I have a problem with the device headers not following the RM nomenclature, however failed it is.


Associate III

Hmm.. when I read this:

.. and combine it with your code it shoud be:

#define GPIO_AFRL_AFRL1                    ((uint32_t)0x000000F0)
#define GPIO_AFRL_AFRL1_0                  ((uint32_t)0x00000100)
#define GPIO_AlternateFunction_I2S2	   ((uint32_t)0x00000005)

is this correct?

One more thing - this is stylistic and my own again, so don't take it as a guideline or whatever, just one idea maybe worth some consideration: the vast majority of GPIO functionality tend to be assigned once for the whole life of program, and never or only seldom changed. So what I do is that I write the MODER and AFRx and also OTYPER and OSPEEDR and PUPDR registers at once, at the beginning of my program. In other words, I don't change only certain pins of a port, as it comes with a certain module's initialization. This avoids the read-mask-change-write process, as I described above (however, I take into account the non-standard reset values for the pins with debug functionality, and if appropriate, retain them, in the values written into the registers).

But the truth is, that I too group "functionality", through using my own preprocessor which reorders code; so my GPIO initialization for one pin then looks like:

PC9  I2S_CKIN             <table name="dxp GPIO"> <item name="PC"/><item name="pin">  9 </item> <item name="mode"> AF  </item> <item name="AF"> I2S1    </item> <item name="speed"> 25MHz </item> <_item name="PUPD"/> <_item name="type"/> </table>

from which (several lines like these, for the different pins) then my preprocessing framework generates something like

  GPIOC->ODR  = 0 
    OR (1 * GPIO_ODR_ODR_4);
  GPIOC->PUPDR   = 0 
    const bit64 a = { .l[0] = 0, .l[1] = 0,
  .n12 = GPIO_AlternateFunction_SPI3,
  .n10 = GPIO_AlternateFunction_SPI3,
  .n9 = GPIO_AlternateFunction_I2S1,
    GPIOC->AFR[0] = a.l[0];
    GPIOC->AFR[1] = a.l[1];
  GPIOC->OTYPER  = 0 ;
  GPIOC->MODER =  (0 
  OR (GPIO_Mode_Out * GPIO_MODER_MODER8_0));




    #define GPIO_AFRL_AFRL1                    ((uint32_t)0x000000F0)
    #define GPIO_AFRL_AFRL1_0                  ((uint32_t)0x00000010)
    #define GPIO_AlternateFunction_I2S2	   ((uint32_t)0x00000005)