feat(radio): I2S driver

This commit is contained in:
raphaelcoeffic
2025-01-18 11:31:07 +01:00
committed by Malte Langermann
parent 5fe02b7316
commit f22b9c0658
6 changed files with 195 additions and 4 deletions
@@ -47,6 +47,7 @@ if(NOT NATIVE_BUILD)
${STM32CUBE_SRC_PREFIX}_hal_rtc.c
${STM32CUBE_SRC_PREFIX}_hal_i2c.c
${STM32CUBE_SRC_PREFIX}_hal_i2c_ex.c
${STM32CUBE_SRC_PREFIX}_hal_i2s.c
${STM32CUBE_SRC_PREFIX}_hal_sd.c
${STM32CUBE_SRC_PREFIX}_hal_dma.c
${STM32CUBE_SRC_PREFIX}_hal_pcd.c
@@ -87,6 +88,7 @@ if(NOT NATIVE_BUILD)
${STM32CUBE_SRC_PREFIX}_hal_rtc.c
${STM32CUBE_SRC_PREFIX}_hal_i2c.c
${STM32CUBE_SRC_PREFIX}_hal_i2c_ex.c
${STM32CUBE_SRC_PREFIX}_hal_i2s.c
${STM32CUBE_SRC_PREFIX}_hal_sd.c
${STM32CUBE_SRC_PREFIX}_hal_dma.c
${STM32CUBE_SRC_PREFIX}_hal_mdma.c
@@ -34,12 +34,16 @@
inline static bool stm32_dma_check_tc_flag(DMA_TypeDef* DMAx, uint32_t DMA_Stream)
{
return LL_DMA_IsActiveFlag_TC(DMAx, DMA_Stream);
if (!LL_DMA_IsActiveFlag_TC(DMAx, DMA_Stream)) return false;
LL_DMA_ClearFlag_TC(DMAx, DMA_Stream);
return true;
}
inline static bool stm32_dma_check_ht_flag(DMA_TypeDef* DMAx, uint32_t DMA_Stream)
{
return LL_DMA_IsActiveFlag_HT(DMAx, DMA_Stream);
if (!LL_DMA_IsActiveFlag_HT(DMAx, DMA_Stream)) return false;
LL_DMA_ClearFlag_HT(DMAx, DMA_Stream);
return true;
}
#else // STM32H7RS
@@ -0,0 +1,136 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "stm32_i2s.h"
#include "stm32_gpio.h"
#include "stm32_spi.h"
#include "stm32_dma.h"
#include "hal/gpio.h"
#include "stm32_hal_ll.h"
static void i2s_gpio_init(const stm32_i2s_t* i2s)
{
stm32_i2s_pindef_t gpios[] = {
i2s->MCK, i2s->WS, i2s->CK, i2s->SDO, i2s->SDI,
};
for (unsigned i = 0; i < sizeof(gpios) / sizeof(gpios[0]); i++) {
gpio_init_af(gpios[i].pin, gpios[i].af, GPIO_PIN_SPEED_VERY_HIGH);
}
}
static void i2s_dma_init(const stm32_i2s_t* i2s)
{
stm32_dma_enable_clock(i2s->DMA);
LL_DMA_DeInit(i2s->DMA, i2s->DMA_Stream);
LL_DMA_InitTypeDef dmaInit;
LL_DMA_StructInit(&dmaInit);
#if defined(STM32H7)
dmaInit.Mode = LL_DMA_MODE_CIRCULAR;
dmaInit.PeriphRequest = i2s->DMA_PeriphRequest;
dmaInit.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
dmaInit.Priority = LL_DMA_PRIORITY_VERYHIGH;
dmaInit.PeriphOrM2MSrcAddress = LL_SPI_DMA_GetTxRegAddr(i2s->SPIx);
dmaInit.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD;
dmaInit.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
dmaInit.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD;
#elif defined(STM32H7RS)
dmaInit.Request = i2s->DMA_PeriphRequest;
dmaInit.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
dmaInit.Priority = LL_DMA_HIGH_PRIORITY;
dmaInit.DestAddress = LL_SPI_DMA_GetTxRegAddr(i2s->SPIx);
dmaInit.DestDataWidth = LL_DMA_DEST_DATAWIDTH_HALFWORD;
dmaInit.SrcIncMode = LL_DMA_SRC_INCREMENT;
dmaInit.SrcDataWidth = LL_DMA_SRC_DATAWIDTH_HALFWORD;
#endif
LL_DMA_Init(i2s->DMA, i2s->DMA_Stream, &dmaInit);
}
int stm32_i2s_init(const stm32_i2s_t* i2s)
{
if (!i2s) return -1;
i2s_gpio_init(i2s);
stm32_spi_enable_clock(i2s->SPIx);
LL_I2S_Disable(i2s->SPIx);
LL_I2S_InitTypeDef i2s_init;
LL_I2S_StructInit(&i2s_init);
i2s_init.Mode = LL_I2S_MODE_MASTER_TX;
i2s_init.Standard = LL_I2S_STANDARD_PHILIPS;
i2s_init.DataFormat = LL_I2S_DATAFORMAT_16B;
i2s_init.MCLKOutput = LL_I2S_MCLK_OUTPUT_ENABLE;
i2s_init.AudioFreq = LL_I2S_AUDIOFREQ_32K;
if (LL_I2S_Init(i2s->SPIx, &i2s_init) != SUCCESS) return -1;
LL_I2S_Enable(i2s->SPIx);
if (i2s->DMA) i2s_dma_init(i2s);
return 0;
}
void stm32_i2s_config_dma_stream(const stm32_i2s_t* i2s, void* buffer, uint32_t nb_xfer)
{
LL_I2S_Disable(i2s->SPIx);
// clear HT & TC flags
stm32_dma_check_ht_flag(i2s->DMA, i2s->DMA_Stream);
stm32_dma_check_tc_flag(i2s->DMA, i2s->DMA_Stream);
// setup DMA buffer address & length
#if defined(STM32H7)
LL_DMA_SetMemoryAddress(i2s->DMA, i2s->DMA_Stream, (uintptr_t)buffer);
LL_DMA_SetDataLength(i2s->DMA, i2s->DMA_Stream, nb_xfer);
#elif defined(STM32H7RS)
LL_DMA_SetSrcAddress(i2s->DMA, i2s->DMA_Stream, (uintptr_t)buffer);
LL_DMA_SetBlkDataLength(i2s->DMA, i2s->DMA_Stream, nb_xfer * 2); // bytes
#endif
}
void stm32_i2s_start_dma_stream(const stm32_i2s_t* i2s)
{
#if defined(STM32H7)
LL_DMA_EnableStream(i2s->DMA, i2s->DMA_Stream);
#elif defined(STM32H7RS)
LL_DMA_EnableChannel(i2s->DMA, i2s->DMA_Stream);
#endif
// enable DMA request
LL_SPI_EnableDMAReq_TX(i2s->SPIx);
// enable I2S
LL_I2S_Enable(i2s->SPIx);
// start transfer
LL_SPI_StartMasterTransfer(i2s->SPIx);
}
bool stm32_i2s_is_xfer_started(const stm32_i2s_t* i2s)
{
return READ_BIT(i2s->SPIx->CR1, SPI_CR1_CSTART);
}
@@ -0,0 +1,49 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#pragma once
#include "stm32_gpio.h"
struct stm32_i2s_pindef_t {
gpio_t pin;
gpio_af_t af;
};
struct stm32_i2s_t {
SPI_TypeDef* SPIx;
stm32_i2s_pindef_t MCK;
stm32_i2s_pindef_t WS;
stm32_i2s_pindef_t CK;
stm32_i2s_pindef_t SDO;
stm32_i2s_pindef_t SDI;
DMA_TypeDef* DMA;
uint32_t DMA_Stream;
uint32_t DMA_PeriphRequest;
};
int stm32_i2s_init(const stm32_i2s_t* i2s);
void stm32_i2s_config_dma_stream(const stm32_i2s_t* i2s, void* buffer, uint32_t nb_xfer);
void stm32_i2s_start_dma_stream(const stm32_i2s_t* i2s);
bool stm32_i2s_is_xfer_started(const stm32_i2s_t* i2s);
@@ -56,7 +56,7 @@ extern "C" {
// #define HAL_HASH_MODULE_ENABLED
// #define HAL_HCD_MODULE_ENABLED
#define HAL_I2C_MODULE_ENABLED
// #define HAL_I2S_MODULE_ENABLED
#define HAL_I2S_MODULE_ENABLED
// #define HAL_I3C_MODULE_ENABLED
// #define HAL_ICACHE_MODULE_ENABLED
// #define HAL_IRDA_MODULE_ENABLED
@@ -63,7 +63,7 @@
// #define HAL_HRTIM_MODULE_ENABLED
// #define HAL_HSEM_MODULE_ENABLED
#define HAL_I2C_MODULE_ENABLED
// #define HAL_I2S_MODULE_ENABLED
#define HAL_I2S_MODULE_ENABLED
// #define HAL_IRDA_MODULE_ENABLED
// #define HAL_IWDG_MODULE_ENABLED
//#define HAL_JPEG_MODULE_ENABLED