Skip to main content
CLeo.1
Senior II
June 3, 2021
Question

Weirdest thing happening with STM32H7 I2S hardware

  • June 3, 2021
  • 11 replies
  • 3079 views

Hi guys! I am having the biggest issue with the I2S right now

What is happening: For whatever reason the I2S does not want to transmit any samples VIA DMA. Doesnt matter what I do what I try.

The weird thing happening: The I2S WS, SCLK wont work. Only if the DMA associated with the Tx part is enable (DMA1_Stream1) which I find so weird. All it does it set the WS pin to HIGH

What I have tried:

  • Verified the registers are correctly setup
  • Ensure with measuring tools the right signals are shooting out (WS, MCLK, SCLK) when the DMA1_Stream1 is enable
  • Swapped out the ADC/DAC for another one
  • Ensured the ADC is sending out its digital signal
  • Ensured the STM32 is receiving samples and transmitting to the Tx Buffer associated with the DMA

How the code works.

The code waits around till an UART signal is received VIA the DMA, once received in goes into a super loop where it process UART signals

Depending on what UART signal was received it will enable not only the I2S peripheral but setup the clock, Interrupt, anything to make the I2S running with DMA etc...

Then the stm32 should now be ready to take in I2S signals and spit them out

Code: (Using PasteBin as code is too long here)

Main.c

https://pastebin.com/GZCTs8Sd

I2S_Factory.c (Setups GPIO, and DMA)

https://pastebin.com/tDXjzVaX

Audio_Selector.c (Setups the I2S Clock, interrupt, and DMA)

https://pastebin.com/mHjDgZaX

DSP_FACTORY.c (Where in takes in the Rx Samples and transfer it to the Tx Buffer for transitmission)

https://pastebin.com/WNt9TSrA

Any help would be amazing, I am stumped right now its not even funny.

    This topic has been closed for replies.

    11 replies

    TDK
    June 4, 2021
    GPIOA->MODER |= (GPIO_MODER_MODE4_AF) |
     (GPIO_MODER_MODE5_AF) |
     (GPIO_MODER_MODE6_AF) |
     (GPIO_MODER_MODE7_AF);

    What is GPIO_MODER_MODE4_AF? It's not defined anywhere in the CubeH7 repo. Google also reports 0 hits.

    https://github.com/STMicroelectronics/STM32CubeH7/search?q=GPIO_MODER_MODE4_AF

    Do HAL I2S examples work?

    "If you feel a post has answered your question, please click ""Accept as Solution""."
    CLeo.1
    CLeo.1Author
    Senior II
    June 4, 2021

    Sorry thats my doing

    Here are the definitions:

    GPIOA->MODER |= (GPIO_MODER_MODE4_AF) | = (0x2UL << GPIO_MODER_MODE4_Pos) 

    (GPIO_MODER_MODE5_AF) | = (0x2UL << GPIO_MODER_MODE5_Pos) 

    (GPIO_MODER_MODE6_AF) | = (0x2UL << GPIO_MODER_MODE6_Pos) 

    (GPIO_MODER_MODE7_AF) ; = (0x2UL << GPIO_MODER_MODE7_Pos) 

    Never used HAL so I couldn't tell you. My guy feeling is telling me something is wrong with the DMA and the TxBuffer, does static put it in a piece of memory where the DMA can't reach?

    TDK
    June 4, 2021

    Depends on your linker script. Debug and find out the address of the data you're putting in there and cross reference with the memory map.

    "If you feel a post has answered your question, please click ""Accept as Solution""."
    TDK
    June 4, 2021

    > But for someone who didnt mess with the linker why is memory being put there while other memory isnt?

    You haven't even verified that this is the case, so who knows? I didn't write your linker and you didn't include it.

    > Also do you think it could be cause of the static keyword? Maybe when static is called it places in memory where the DMA can't reach it?

    Why do you think the static keyword is causing things to be placed somewhere else?

    Instead of grasping at straws, do a more systematic debugging process. When DMA fails to trigger, examine the registers for error codes. Find out where your data is located and ensure DMA can access that region. Create a minimal working example which displays the error or issue you're seeing so you can debug it easier.

    "If you feel a post has answered your question, please click ""Accept as Solution""."
    CLeo.1
    CLeo.1Author
    Senior II
    June 4, 2021

    That all make sense and Ill do it, but first can you kindly explain to me how does the DMA Tx work with the I2S peripheral. I got a few questions that's been bugging and can be integral for me solving this problem

    1. Unlike DMA Rx where data gets sent to, how does the DMA know to transferred out data from Memory to the peripheral ? Is it when you write into the Tx Buffer and it knows when the Tx Buffer gets full and its then it ships out to the shadow Register (SPI->TXDR) ?
    2. Would it make sense to have DMA_Tx for I2S on a "non-circ" mode and have it only execute when the Buffer is full?

    waclawek.jan
    Super User
    June 4, 2021

    The 'H7 SPI/I2S module is an overcomplicated beast and I don't use it so maybe I'm unaware of some detail, but generally in communication peripherals, there's a TXE (Tx Empty) flag/signal, which - when this signal is routed properly through DMAMUX - triggers a DMA transfer. This transfer is supposed to move a frame from memory to the Tx data register of I2S/SPI, thus clearing the TXE flag and starting transmission. After the frame is transmitted, TXE gets set and the cycle repeats.

    JW

    CLeo.1
    CLeo.1Author
    Senior II
    June 4, 2021

    Thank you king, would you mind taking a looking at my code and see if the DMA is setup correctly and its just a linker issue?

    CLeo.1
    CLeo.1Author
    Senior II
    June 4, 2021

    Kept digging and found this in my map file

    .bss.I2S1_TxBUFF
     0x00000000240004ac 0x10 Core/Src/driver/I2S_FACTORY.o
     .bss.I2S1_RxBUFF
     0x00000000240004bc 0x10 Core/Src/driver/I2S_FACTORY.o
     .bss.UART4_RxBUFF
     0x00000000240004cc 0x1 Core/Src/driver/UART_FACTORY.o
     
    .text.getUART4_RxBUFF
     0x0000000008000f60 0x14 Core/Src/driver/UART_FACTORY.o
     0x0000000008000f60 getUART4_RxBUFF
     .text.getI2S1_TxBUFF
     0x0000000008000cd0 0x14 Core/Src/driver/I2S_FACTORY.o
     0x0000000008000cd0 getI2S1_TxBUFF
     .text.getI2S1_RxBUFF
     0x0000000008000ce4 0x14 Core/Src/driver/I2S_FACTORY.o
     0x0000000008000ce4 getI2S1_RxBUFF

    All the DMA variables are located in the ITCM memory region, could this be the problem? If so how come the DMA doesnt return any errors?

    TDK
    June 4, 2021

    0x00000000240004ac is in the AXI SRAM.

    0x0000000008000f60 is in FLASH, but that's just code, not a buffer.

    Why do you think those are in ITCM?

    "If you feel a post has answered your question, please click ""Accept as Solution""."
    CLeo.1
    CLeo.1Author
    Senior II
    June 4, 2021

    Oh cause of this, weird 0693W00000Ba9BJQAZ.png

    CLeo.1
    CLeo.1Author
    Senior II
    June 4, 2021

    Right I see my mistake now correct its in the D1 Domain, what else could it be then

    CLeo.1
    CLeo.1Author
    Senior II
    June 4, 2021

    Alright @TDK​ I broke down the I2S module and now I am having issue just even getting the I2S working. All is happening the WS pin get set HIGH

    /* USER CODE BEGIN Header */
    /**
     ******************************************************************************
     * @file : main.c
     * @brief : Main program body
     ******************************************************************************
     * @attention
     *
     * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
     * All rights reserved.</center></h2>
     *
     * This software component is licensed by ST under BSD 3-Clause license,
     * the "License"; You may not use this file except in compliance with the
     * License. You may obtain a copy of the License at:
     * opensource.org/licenses/BSD-3-Clause
     *
     ******************************************************************************
     */
    /* USER CODE END Header */
    /* Includes ------------------------------------------------------------------*/
     
    #include "main.h"
    #include <stdint.h>
     
     int RxBuff_inline[4];
     int TxBuff_inline[4];
     
    uint8_t I2S1_TC = 0;
    uint8_t I2S1_HC = 0;
     
    void INIT_DMA(void);
     
    void INIT_DMA() {
     
    	//Setting Clock for 192kHz
    		//N = 122
    		//P = 4
    		//M = 10
    		//FRACT = 7209
     
    		// RCC_PLL2DIVR
    		// MASKING:
    		RCC -> PLL2DIVR &= ~RCC_PLL2DIVR_P2;
    		RCC -> PLL2DIVR &= ~RCC_PLL2DIVR_N2;
    		// WRITING:
    		RCC -> PLL2DIVR |= RCC_PLL2DIVR_P2_DIV8; // P
    		RCC -> PLL2DIVR |= RCC_PLL2DIVR_N2_MULT122; // N
     
    		// RCC_PLLCKSELR
    		// MASKING:
    		RCC -> PLLCKSELR &= ~RCC_PLLCKSELR_DIVM2;
    		// WRITING:
    		RCC -> PLLCKSELR |= RCC_PLLCKSELR_DIVM2_DIV10; // M
     
    		// RCC_PLL2FRACR
    		// MASKING:
    		RCC -> PLL2FRACR &= ~RCC_PLL2FRACR_FRACN2;
    		// WRITING:
    		RCC -> PLL2FRACR |= RCC_PLL2FRACR_FRACN_7209; // FRAC
     
    		// RCC_PLLCFGR
    		// MASKING:
    		RCC -> PLLCFGR &= ~RCC_PLLCFGR_DIVP2EN;
    		RCC -> PLLCFGR &= ~RCC_PLLCFGR_PLL2RGE;
    		RCC -> PLLCFGR &= ~RCC_PLLCFGR_PLL2VCOSEL;
    		RCC -> PLLCFGR &= ~RCC_PLLCFGR_PLL2FRACEN;
    		// WRITING:
    		RCC -> PLLCFGR |= RCC_PLLCFGR_DIVP2EN;
    		RCC -> PLLCFGR |= RCC_PLLCFGR_PLL2RGE_4_8;
    		RCC -> PLLCFGR |= RCC_PLLCFGR_PLL2VCOSEL_192_836;
    		RCC -> PLLCFGR |= RCC_PLLCFGR_PLL2FRACEN;
     
    		// RCC_CR
    		// MASKING:
    		RCC -> CR &= ~RCC_CR_PLL2ON;
    		// WRITING:
    		RCC -> CR |= RCC_CR_PLL2ON;
    		// WAITING:
    		while (((RCC -> CR) & (RCC_CR_PLL2RDY)) == 0){};
     
    		// ENALBING CLOCKS
     
    		// RCC_AHB4ENR
    		// MASKING:
    		RCC -> AHB4ENR &= ~ RCC_AHB4ENR_GPIOAEN;
    		RCC -> AHB4ENR &= ~ RCC_AHB4ENR_GPIOCEN;
    		// WRITING:
    		RCC -> AHB4ENR |= RCC_AHB4ENR_GPIOAEN;
    	 RCC -> AHB4ENR |= RCC_AHB4ENR_GPIOCEN;
     
    	 // RCC_APB2ENR
    	 // MASKING:
    	 RCC -> APB2ENR &= ~RCC_APB2ENR_SPI1EN;
    	 // WRITING:
    	 RCC -> APB2ENR |= RCC_APB2ENR_SPI1EN;
     
    	 // CHANGING CLOCKS OF PERIPHERALS
    	 // RCC_D2CCIP1R
    	 // MASKING;
    	 RCC -> D2CCIP1R &= ~RCC_D2CCIP1R_SPI123SEL;
    	 // WRITING:
    	 RCC -> D2CCIP1R |= RCC_D2CCIP1R_SPI123SEL_PLL2_P_CK;
     
    	 // CHANGING GPIO PINS TO ALETERNATIVE
    	 // GPIOx_MODER
    	 // MASKING
    	 GPIOA -> MODER &= ~GPIO_MODER_MODE4;
    	 GPIOA -> MODER &= ~GPIO_MODER_MODE5;
    	 GPIOA -> MODER &= ~GPIO_MODER_MODE6;
    	 GPIOA -> MODER &= ~GPIO_MODER_MODE7;
    	 GPIOC -> MODER &= ~GPIO_MODER_MODE4;
    	 // WRITING:
    	 GPIOA -> MODER |= GPIO_MODER_MODE4_ALT;
    	 GPIOA -> MODER |= GPIO_MODER_MODE5_ALT;
    	 GPIOA -> MODER |= GPIO_MODER_MODE6_ALT;
    	 GPIOA -> MODER |= GPIO_MODER_MODE7_ALT;
    	 GPIOC -> MODER |= GPIO_MODER_MODE4_ALT;
     
     
    	 // GPIOC -> OSPEEDR &= ~GPIO_OSPEEDR_OSPEED4;
    	 //GPIOC -> OSPEEDR |= GPIO_OSPEEDR_OSPEED4_VERY_HIGH;
     
    	 //SETTING ALT FUNCTIONS TO PINS
    	 // GPIOx_AFRL
    	 // MASKING:
    	 GPIOA -> AFR[0] &= ~GPIO_AFRL_AFSEL4;
    	 GPIOA -> AFR[0] &= ~GPIO_AFRL_AFSEL5;
    	 GPIOA -> AFR[0] &= ~GPIO_AFRL_AFSEL6;
    	 GPIOA -> AFR[0] &= ~GPIO_AFRL_AFSEL7;
    	 GPIOC -> AFR[0] &= ~GPIO_AFRL_AFSEL4;
    	 // WRITING;
    	 GPIOA -> AFR[0] |= GPIO_AFRL_AFSEL4_AF5;
    	 GPIOA -> AFR[0] |= GPIO_AFRL_AFSEL5_AF5;
    	 GPIOA -> AFR[0] |= GPIO_AFRL_AFSEL6_AF5;
    	 GPIOA -> AFR[0] |= GPIO_AFRL_AFSEL7_AF5;
    	 GPIOC -> AFR[0] |= GPIO_AFRL_AFSEL4_AF5;
     
    	 // ENABLING DMA1
    	 // RCC_AHB1ENR
    	 // MASKING:
     
    	 // DMA_SxCR
    	 // WRITING:
    	 // DMA1_Stream0 -> CR |= DMA_SxCR_EN;
    	 //DMA1_Stream1 -> CR |= DMA_SxCR_EN;
     
    	 // Setting up the SPI/I2S Peripheral
    	 // MASKING:
    	 SPI1->I2SCFGR = 0x00;
    	 		 SPI1->I2SCFGR |= (SPI_I2SCFGR_MCKOE) |
    	 							 (SPI_I2SCFGR_I2SDIV_2) |
    	 							 (SPI_I2SCFGR_I2SSTD_I2STAND) |
    											 (SPI_I2SCFGR_DATFMT_LAlign) |
    	 							 (SPI_I2SCFGR_I2SCFG_MASTER_FULLDUPLEX) |
    	 							 (SPI_I2SCFGR_I2SMOD_I2S_PCM_MODE);
    	 		 SPI1->I2SCFGR |= (SPI_I2SCFGR_CHNEL_32BIT_WIDE);
    	 		 SPI1->I2SCFGR |= (SPI_I2SCFGR_DATALEN_24BIT);
    	 		 		 SPI1->CR1 |= SPI_CR1_SPE;
    	 		 			 SPI1->CR1 |= SPI_CR1_CSTART;
     
    		NVIC_EnableIRQ(DMA1_Stream0_IRQn);
    		NVIC_SetPriority(DMA1_Stream0_IRQn,0);
    }
     
     
    int main (void) {
     
    INIT_DMA();
     
     
     
    while (1) {
     
     
    	if (I2S1_HC) {
    		 for (int i = 0; i < 2; i ++){
     
    			 TxBuff_inline[i] = (int)RxBuff_inline[i];
    					}
     
    					 //DMA1_Stream1->CR |= DMA_SxCR_EN;
    					 I2S1_HC = 0;
    	}
     
    	if (I2S1_TC) {
    		 for (int i = 2; i < 4; i ++){
     
     
     
    			 TxBuff_inline[i] = RxBuff_inline[i];
     
     
    						}
     
    						 //DMA1_Stream1->CR |= DMA_SxCR_EN;
    						 I2S1_TC = 0;
    	}
     
    }
     
     
    }
     
     
    void DMA1_Stream0_IRQHandler() {
     
    	if (((DMA1->LISR) & (DMA_LISR_HTIF0)) != 0) {
    	 DMA1->LIFCR |= DMA_LIFCR_CHTIF0;
     
    		 I2S1_HC = 1;
    	}
     
    	if (((DMA1->LISR) & (DMA_LISR_TCIF0)) != 0) {
    		 DMA1->LIFCR |= DMA_LIFCR_CTCIF0;
     
    		 I2S1_TC = 1;
    	}
    }

    Piranha
    Principal III
    June 5, 2021

    Why are you using the SPI peripheral, not the much more appropriate SAI peripheral?

    Here are the definitions:
    GPIOA->MODER |= (GPIO_MODER_MODE4_AF) | = (0x2UL << GPIO_MODER_MODE4_Pos) 
    (GPIO_MODER_MODE5_AF) | = (0x2UL << GPIO_MODER_MODE5_Pos) 
    (GPIO_MODER_MODE6_AF) | = (0x2UL << GPIO_MODER_MODE6_Pos) 
    (GPIO_MODER_MODE7_AF) ; = (0x2UL << GPIO_MODER_MODE7_Pos) 

    And what is this? That is not even a correct C syntax...

    CLeo.1
    CLeo.1Author
    Senior II
    June 5, 2021
    Piranha
    Principal III
    June 5, 2021

    For audio usage SAI is the preferred choice. Compared to SPI it wins in all aspects simultaneously:

    • More protocols, internal FIFO and other features.
    • More flexibility regarding channels, data bit number and position.
    • Not over-engineered and is simper to use.
    • Has less hardware bugs. Actually current errata still has none.
    CLeo.1
    CLeo.1Author
    Senior II
    June 5, 2021

    I mean you aint wrong and ill probably do it if I can get this working. Further more I still dont know why the MOSI line is still silent.

    TDK
    June 5, 2021

    > I broke down the I2S module and now I am having issue just even getting the I2S working

    You don't enable the DMA clock or set up DMA1_Stream0 anywhere in there.

    You don't enable the RXDMAEN or TXDMAEN bits anywhere.

    Possibly other things.

    Do you have it up and running without using DMA? That would be a smart first step IMO.

    "If you feel a post has answered your question, please click ""Accept as Solution""."
    CLeo.1
    CLeo.1Author
    Senior II
    June 5, 2021

    Oh sorry that was the old code, I can ensure you that RXDMAEN and TXDAMEN are set.

    I tried to get it running without the DMA and still cant get it working.

    TDK
    June 5, 2021
    It's frustrating to look through code that is posted and find several errors, only to then be told that the posted code wasn't actually the code you used. You are not the only one, it happens more often than I would like. That's a waste of my time, as well as yours.
    "If you feel a post has answered your question, please click ""Accept as Solution""."
    CLeo.1
    CLeo.1Author
    Senior II
    June 5, 2021

    @TDK​ Here you go king.

    https://pastebin.com/EFaLMLgR thats the code, currently using