I2S output on STM32: small HOWTO
Posted: Tue Dec 17, 2019 10:03 am
While I have got to like ChibiOS and its HAL, setting up I2S to output data was not only fun. All I could find were some quite old threads in the forum, which were only of limited help.So I am sharing my notes how I achieved to get some sound from my STM32L152. Other STM32 devices are likely similar, refer to the reference manual for details.
I am using Sparkfun´s MAX98357A breakout board. The MAX98357 does not require an MCLK, so this part of the setup is not included.
1. Enable I2S in halconf.h
2. Assign a SPI channel and a DMA resource. Note that you need to choose a suitable DMA channel, "Summary of DMA1 requests for each channel" in the reference manual tells you which one. I chose SPI2 and therefore DMA1, channel 5.
mcuconf.h
Note that you actually need to add code to mcuconf.h (I was used to only modifying some TRUE/FALSE entries).
3. Configure the outputs in board.h. On the STM32L152, SPI is AF5, and SPI2 can be mapped to PB12 (LRCLK), PB13 (BCLK), PB15 (data).
Set up the I2S configuration. It is important to set IS2DIV (lowest 8 bits of I2SPR) to a proper value, as it defines the clock. I my case, 0x10 resulted in about 32 kHz on LRCLK or about 1 MHz on BCLK. This caused the MAX98357 to finally produce some noise.
In the hope that it will help someone,
Max
I am using Sparkfun´s MAX98357A breakout board. The MAX98357 does not require an MCLK, so this part of the setup is not included.
1. Enable I2S in halconf.h
Code: Select all
/**
* @brief Enables the I2S subsystem.
*/
#if !defined(HAL_USE_I2S) || defined(__DOXYGEN__)
#define HAL_USE_I2S TRUE
#endif
2. Assign a SPI channel and a DMA resource. Note that you need to choose a suitable DMA channel, "Summary of DMA1 requests for each channel" in the reference manual tells you which one. I chose SPI2 and therefore DMA1, channel 5.
mcuconf.h
Code: Select all
/*
* SPI driver system settings.
*/
#define STM32_SPI_USE_SPI1 TRUE
#define STM32_SPI_USE_SPI2 FALSE
#define STM32_I2S_USE_SPI1 FALSE
#define STM32_I2S_USE_SPI2 TRUE
#define STM32_I2S_SPI2_MODE STM32_I2S_MODE_MASTER | STM32_I2S_MODE_TX
#define STM32_SPI_SPI1_DMA_PRIORITY 1
#define STM32_SPI_SPI2_DMA_PRIORITY 1
#define STM32_SPI_SPI1_IRQ_PRIORITY 10
#define STM32_SPI_SPI2_IRQ_PRIORITY 10
#define STM32_I2S_SPI2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
#define STM32_I2S_SPI2_RX_DMA_STREAM 0
#define STM32_SPI_DMA_ERROR_HOOK(spip) osalSysHalt("DMA failure")
Note that you actually need to add code to mcuconf.h (I was used to only modifying some TRUE/FALSE entries).
3. Configure the outputs in board.h. On the STM32L152, SPI is AF5, and SPI2 can be mapped to PB12 (LRCLK), PB13 (BCLK), PB15 (data).
Code: Select all
#define GPIOB_I2S_LRCK 12U
#define GPIOB_I2S_CLK 13U
#define GPIOB_I2S_DATA 15U
...
#define VAL_GPIOB_MODER ... | \
PIN_MODE_ALTERNATE(GPIOB_I2S_LRCK) | \
PIN_MODE_ALTERNATE(GPIOB_I2S_CLK) | \
PIN_MODE_INPUT(GPIOB_PIN14) | \
PIN_MODE_ALTERNATE(GPIOB_I2S_DATA))
...
#define VAL_GPIOB_AFRH .... | \
PIN_AFIO_AF(GPIOB_I2S_LRCK, 5U) | \
PIN_AFIO_AF(GPIOB_I2S_CLK, 5U) | \
PIN_AFIO_AF(GPIOB_PIN14, 0U) | \
PIN_AFIO_AF(GPIOB_I2S_DATA, 5U))
Set up the I2S configuration. It is important to set IS2DIV (lowest 8 bits of I2SPR) to a proper value, as it defines the clock. I my case, 0x10 resulted in about 32 kHz on LRCLK or about 1 MHz on BCLK. This caused the MAX98357 to finally produce some noise.
Code: Select all
uint8_t tx_bfr[512];
void i2s_callback(I2SDriver *i2sp) {
return;
}
int main(void) {
halInit();
chSysInit();
for (uint16_t k=0; k < 512; k++) {
tx_bfr[k] = rx_bfr[k] = (uint8_t)k;
}
I2SConfig i2s_config = {
.tx_buffer = tx_bfr,
.rx_buffer = 0,
.size = sizeof(tx_bfr) >> 1,
.end_cb = i2s_callback,
.i2spr = 0x10
};
i2sStart(&I2SD2,&i2s_config);
i2sStartExchange(&I2SD2);
In the hope that it will help someone,
Max