From c00ee1e9ddcafd5b5fcca45873e50384bc9d6b5e Mon Sep 17 00:00:00 2001 From: raphaelcoeffic <1050031+raphaelcoeffic@users.noreply.github.com> Date: Wed, 15 Jan 2025 12:20:28 +0100 Subject: [PATCH] feat(stm32h7s78-dk): sound support --- .../src/targets/stm32h7s78-dk/CMakeLists.txt | 4 + .../targets/stm32h7s78-dk/audio_driver.cpp | 181 ++++++++++++++++++ radio/src/targets/stm32h7s78-dk/board.cpp | 2 +- radio/src/targets/stm32h7s78-dk/board.h | 33 +--- radio/src/targets/stm32h7s78-dk/hal.h | 2 + .../src/targets/stm32h7s78-dk/lcd_driver.cpp | 28 +-- .../targets/stm32h7s78-dk/system_clock.cpp | 39 ++-- 7 files changed, 213 insertions(+), 76 deletions(-) create mode 100644 radio/src/targets/stm32h7s78-dk/audio_driver.cpp diff --git a/radio/src/targets/stm32h7s78-dk/CMakeLists.txt b/radio/src/targets/stm32h7s78-dk/CMakeLists.txt index 19a820252..cff8d7c98 100644 --- a/radio/src/targets/stm32h7s78-dk/CMakeLists.txt +++ b/radio/src/targets/stm32h7s78-dk/CMakeLists.txt @@ -46,6 +46,7 @@ add_definitions( -DSTM32H7S7xx -DCORE_CM7 -DFIRMWARE_QSPI -DSDRAM -DCOLORLCD -DLIBOPENUI -DPCBHORUS -DPCBX10 -DPCBFRSKY -DHARDWARE_TOUCH -DSOFTWARE_KEYBOARD + -DAUDIO -DVOICE -DSOFTWARE_VOLUME ) # Make malloc() thread-safe @@ -96,9 +97,12 @@ set(BOOTLOADER_SRC ${BOOTLOADER_SRC} $) add_library(board OBJECT EXCLUDE_FROM_ALL ${BOARD_COMMON_SRC} ${TARGET_SRC_DIR}/tp_gt911.cpp + ${TARGET_SRC_DIR}/audio_driver.cpp + drivers/wm8904.cpp targets/common/arm/stm32/lsm6ds_driver.cpp targets/common/arm/stm32/mixer_scheduler_driver.cpp targets/common/arm/stm32/stm32_switch_driver.cpp + targets/common/arm/stm32/stm32_i2s.cpp ) set(FIRMWARE_SRC ${FIRMWARE_SRC} $) diff --git a/radio/src/targets/stm32h7s78-dk/audio_driver.cpp b/radio/src/targets/stm32h7s78-dk/audio_driver.cpp new file mode 100644 index 000000000..33b95294d --- /dev/null +++ b/radio/src/targets/stm32h7s78-dk/audio_driver.cpp @@ -0,0 +1,181 @@ +#include "audio.h" +#include "delays_driver.h" +#include "drivers/wm8904.h" + +#include "hal/gpio.h" +#include "stm32_dma.h" +#include "stm32_i2s.h" +#include "stm32_i2c_driver.h" + +#include "stm32_hal_ll.h" + +#include "debug.h" +#include "timers_driver.h" + +static wm8904_t _wm8904 = {0}; + +// +// STM32H7S78-DK pinout +// +// Pin MCU WM8904 +// --------------------------------- +// PA3 (I2S2_MCK): MCLK <-> MCLK +// PA4 (I2S2_WS): WCLK <-> WCLK +// PA5 (I2S2_CK): BCLK <-> BCLK +// PB5 (I2S2_SDO): DOUT <-> DIN +// PG12 (I2S2_SDI): DIN <-> DOUT + +#define I2S_DMA GPDMA1 +#define I2S_DMA_Stream LL_DMA_CHANNEL_2 +#define I2S_DMA_Stream_Request LL_GPDMA1_REQUEST_SPI6_TX +#define I2S_DMA_Stream_IRQn GPDMA1_Channel2_IRQn +#define I2S_DMA_Stream_IRQHandler GPDMA1_Channel2_IRQHandler + +const stm32_i2s_t _i2s_dev = { + .SPIx = AUDIO_SPI, + .MCK = {GPIO_PIN(GPIOA, 3), GPIO_AF5}, + .WS = {GPIO_PIN(GPIOA, 4), GPIO_AF8}, + .CK = {GPIO_PIN(GPIOA, 5), GPIO_AF8}, + .SDO = {GPIO_PIN(GPIOB, 5), GPIO_AF8}, + .SDI = {GPIO_PIN(GPIOG, 12), GPIO_AF5}, + + .DMA = I2S_DMA, + .DMA_Stream = I2S_DMA_Stream, + .DMA_PeriphRequest = I2S_DMA_Stream_Request, +}; + +// 16 bit, 2 channels +#define DMA_BUFFER_HALF_LEN AUDIO_BUFFER_SIZE + +// 2 buffers +#define DMA_BUFFER_LEN (DMA_BUFFER_HALF_LEN * 2) + +static uint32_t _dma_buffer[DMA_BUFFER_LEN] __DMA_NO_CACHE; +static uintptr_t _ll_addr_reset __DMA_NO_CACHE; + +static inline uint32_t _conv_sample(int16_t sample) +{ + return ((uint32_t)(uint16_t)sample << 16) | (uint16_t)sample; +} + +#define PI 3.14159265358979323846f + +static void generate_sine_wave(const uint32_t num_samples) +{ + const uint32_t sample_rate = 32000; + const float amplitude = 0.8f; + const float frequency = 440.0f; + + // Generate and write samples + for (uint32_t i = 0; i < num_samples; i++) { + float time = (float)i / sample_rate; + // Calculate sample value (-32768 to 32767) + int16_t sample = (int16_t)(amplitude * 32767.0f * + sinf(2.0f * PI * frequency * time)); + + // write into dma buffer + _dma_buffer[i] = _conv_sample(sample); + } +} + +static int enable_dma_irqs() +{ + // enable DMA channel IRQ + LL_DMA_EnableIT_HT(I2S_DMA, I2S_DMA_Stream); + LL_DMA_EnableIT_TC(I2S_DMA, I2S_DMA_Stream); + + NVIC_SetPriority(GPDMA1_Channel2_IRQn, 7); + NVIC_EnableIRQ(GPDMA1_Channel2_IRQn); + + return 0; +} + +int audioInit() +{ + memset(_dma_buffer, 0, sizeof(_dma_buffer)); + + if (wm8904_probe(&_wm8904, 0, 0x34 >> 1) < 0) { + return -1; + } + + if (stm32_i2s_init(&_i2s_dev)) { + return -1; + } + + if (wm8904_init(&_wm8904) < 0) { + return -1; + } + + enable_dma_irqs(); + + return 0; +} + +static volatile uint32_t _dma_buffer_offset = 0; + +static void copy_into_dma_buffer(const AudioBuffer* buffer) +{ + unsigned idx = 0; + uint32_t offset = _dma_buffer_offset; + + if (buffer) { + while (idx < buffer->size) { + _dma_buffer[offset++] = _conv_sample(buffer->data[idx++]); + } + audioQueue.buffersFifo.freeNextFilledBuffer(); + } + + for (;idx < DMA_BUFFER_HALF_LEN; idx++) { + _dma_buffer[offset++] = 0; + } +} + +void audioConsumeCurrentBuffer() +{ + if (!stm32_i2s_is_xfer_started(&_i2s_dev)) { + + auto buffer = audioQueue.buffersFifo.getNextFilledBuffer(); + if (!buffer) return; + + copy_into_dma_buffer(buffer); + + // basic DMA configuration + stm32_i2s_config_dma_stream(&_i2s_dev, _dma_buffer, DMA_BUFFER_LEN * 2); + + // configure source address reset (circular buffer) + _ll_addr_reset = (uintptr_t)_dma_buffer; + + LL_DMA_SetLinkedListBaseAddr(_i2s_dev.DMA, _i2s_dev.DMA_Stream, + (uintptr_t)&_ll_addr_reset & 0xFFFF0000); + + LL_DMA_ConfigLinkUpdate(_i2s_dev.DMA, _i2s_dev.DMA_Stream, + LL_DMA_UPDATE_CSAR, + (uintptr_t)&_ll_addr_reset & 0xFFFF); + + // and finally start it! + stm32_i2s_start_dma_stream(&_i2s_dev); + } +} + +static inline uint32_t _calc_offset(uint8_t tc) +{ + return tc * DMA_BUFFER_HALF_LEN; +} + +static void audio_update_dma_buffer(uint8_t tc) +{ + _dma_buffer_offset = _calc_offset(tc); + copy_into_dma_buffer(audioQueue.buffersFifo.getNextFilledBuffer()); +} + +extern "C" void GPDMA1_Channel2_IRQHandler() +{ + if (stm32_dma_check_ht_flag(GPDMA1, LL_DMA_CHANNEL_2)) { + audio_update_dma_buffer(0); + } + + if (stm32_dma_check_tc_flag(GPDMA1, LL_DMA_CHANNEL_2)) { + audio_update_dma_buffer(1); + } +} + diff --git a/radio/src/targets/stm32h7s78-dk/board.cpp b/radio/src/targets/stm32h7s78-dk/board.cpp index 0e7f79ac1..fcf925fab 100644 --- a/radio/src/targets/stm32h7s78-dk/board.cpp +++ b/radio/src/targets/stm32h7s78-dk/board.cpp @@ -112,7 +112,7 @@ void boardInit() keysInit(); switchInit(); - // audioInit(); + audioInit(); adcInit(&_adc_driver); setAnalogValue(adcGetInputOffset(ADC_INPUT_VBAT), 840 * 2); diff --git a/radio/src/targets/stm32h7s78-dk/board.h b/radio/src/targets/stm32h7s78-dk/board.h index 69d120a77..215bd2139 100644 --- a/radio/src/targets/stm32h7s78-dk/board.h +++ b/radio/src/targets/stm32h7s78-dk/board.h @@ -181,39 +181,8 @@ bool isBacklightEnabled(); #endif // Audio driver -void audioInit(); +int audioInit(); void audioConsumeCurrentBuffer(); -void audioSpiWriteBuffer(const uint8_t * buffer, uint32_t size); -void audioSpiSetSpeed(uint8_t speed); -uint8_t audioHardReset(); -uint8_t audioSoftReset(); -void audioSendRiffHeader(); -void audioOn(); -void audioOff(); -bool isAudioReady(); -bool audioChipReset(); - -#define SPI_SPEED_2 0 -#define SPI_SPEED_4 1 -#define SPI_SPEED_8 2 -#define SPI_SPEED_16 3 -#define SPI_SPEED_32 4 -#define SPI_SPEED_64 5 -#define SPI_SPEED_128 6 -#define SPI_SPEED_256 7 - -#define audioDisableIrq() // interrupts must stay enabled on Horus -#define audioEnableIrq() // interrupts must stay enabled on Horus -#if defined(PCBNV14) -#define setSampleRate(freq) -#else -void setSampleRate(uint32_t frequency); -#endif -void setScaledVolume(uint8_t volume); -void setVolume(uint8_t volume); -static inline int32_t getVolume() { return 0; } -#define VOLUME_LEVEL_MAX 23 -#define VOLUME_LEVEL_DEF 12 // Telemetry driver #define INTMODULE_FIFO_SIZE 512 diff --git a/radio/src/targets/stm32h7s78-dk/hal.h b/radio/src/targets/stm32h7s78-dk/hal.h index dd8f316d1..8e83cdf17 100644 --- a/radio/src/targets/stm32h7s78-dk/hal.h +++ b/radio/src/targets/stm32h7s78-dk/hal.h @@ -227,6 +227,8 @@ // #define AUDIO_TIMER TIM6 // #define AUDIO_DMA DMA1 +#define AUDIO_SPI SPI6 + // I2C Bus #define I2C_B1 I2C1 #define I2C_B1_SDA_GPIO GPIO_PIN(GPIOB, 9) // PB.09 diff --git a/radio/src/targets/stm32h7s78-dk/lcd_driver.cpp b/radio/src/targets/stm32h7s78-dk/lcd_driver.cpp index fe6b35544..4139306c5 100644 --- a/radio/src/targets/stm32h7s78-dk/lcd_driver.cpp +++ b/radio/src/targets/stm32h7s78-dk/lcd_driver.cpp @@ -214,30 +214,6 @@ static void LTDC_MspInit() __HAL_RCC_LTDC_RELEASE_RESET(); } -/** - * @brief LTDC Clock Config for LCD TFT display. - * @param hltdc LTDC Handle - * Being __weak it can be overwritten by the application - * @retval HAL_status - */ -static HAL_StatusTypeDef LTDC_ClockConfig(LTDC_HandleTypeDef *hltdc) -{ - LL_RCC_PLL3R_Enable(); - LL_RCC_PLL3FRACN_Disable(); - LL_RCC_PLL3_SetVCOInputRange(LL_RCC_PLLINPUTRANGE_4_8); - LL_RCC_PLL3_SetVCOOutputRange(LL_RCC_PLLVCORANGE_WIDE); - LL_RCC_PLL3_SetM(2); - LL_RCC_PLL3_SetN(50); - LL_RCC_PLL3_SetP(2); - LL_RCC_PLL3_SetQ(2); - LL_RCC_PLL3_SetR(24); - LL_RCC_PLL3_Enable(); - while (LL_RCC_PLL3_IsReady() != 1) { - } - - return HAL_OK; -} - /** * @brief Initializes the LTDC. * @param hltdc LTDC handle @@ -340,9 +316,7 @@ static int32_t LCD_InitEx(uint32_t Orientation, uint32_t PixelFormat, /* Assert display enable LCD_DISP_CTRL pin */ HAL_GPIO_WritePin(LCD_DISP_EN_GPIO_PORT, LCD_DISP_EN_PIN, GPIO_PIN_SET); - if (LTDC_ClockConfig(&hlcd_ltdc) != HAL_OK) { - ret = LCD_ERROR_PERIPH_FAILURE; - } else if (LTDC_Init(&hlcd_ltdc, Width, Height) != HAL_OK) { + if (LTDC_Init(&hlcd_ltdc, Width, Height) != HAL_OK) { ret = LCD_ERROR_PERIPH_FAILURE; } diff --git a/radio/src/targets/stm32h7s78-dk/system_clock.cpp b/radio/src/targets/stm32h7s78-dk/system_clock.cpp index bead1b55b..432992948 100644 --- a/radio/src/targets/stm32h7s78-dk/system_clock.cpp +++ b/radio/src/targets/stm32h7s78-dk/system_clock.cpp @@ -98,31 +98,34 @@ void SystemClock_Config() } /* PLL2 configuration and activation */ - // LL_RCC_PLL2P_Enable(); - // LL_RCC_PLL2Q_Enable(); - // LL_RCC_PLL2R_Enable(); LL_RCC_PLL2S_Enable(); + LL_RCC_PLL2Q_Enable(); LL_RCC_PLL2FRACN_Disable(); LL_RCC_PLL2_SetVCOInputRange(LL_RCC_PLLINPUTRANGE_2_4); LL_RCC_PLL2_SetVCOOutputRange(LL_RCC_PLLVCORANGE_WIDE); LL_RCC_PLL2_SetM(2); - LL_RCC_PLL2_SetN(50); - // LL_RCC_PLL2_SetP(1); - // LL_RCC_PLL2_SetQ(2); - // LL_RCC_PLL2_SetR(2); - LL_RCC_PLL2_SetS(3); + LL_RCC_PLL2_SetN(64); + LL_RCC_PLL2_SetP(2); // disabled + LL_RCC_PLL2_SetQ(47); // 16.340 MHz + LL_RCC_PLL2_SetR(2); // disabled + LL_RCC_PLL2_SetS(4); // 192 MHz LL_RCC_PLL2_Enable(); while (LL_RCC_PLL2_IsReady() != 1) { } - /* Enable SRAM1, SRAM2 & SRAM3 */ - // LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_D2SRAM1 | - // LL_AHB2_GRP1_PERIPH_D2SRAM2 | - // LL_AHB2_GRP1_PERIPH_D2SRAM3); - - /* Set periph clock sources */ - // LL_RCC_SetFMCClockSource(LL_RCC_FMC_CLKSOURCE_HCLK); - // LL_RCC_SetSPIClockSource(LL_RCC_SPI123_CLKSOURCE_PLL3P); + /* PLL2 configuration and activation */ + LL_RCC_PLL3R_Enable(); + LL_RCC_PLL3FRACN_Disable(); + LL_RCC_PLL3_SetVCOInputRange(LL_RCC_PLLINPUTRANGE_4_8); + LL_RCC_PLL3_SetVCOOutputRange(LL_RCC_PLLVCORANGE_WIDE); + LL_RCC_PLL3_SetM(2); + LL_RCC_PLL3_SetN(50); + LL_RCC_PLL3_SetP(2); + LL_RCC_PLL3_SetQ(2); + LL_RCC_PLL3_SetR(24); + LL_RCC_PLL3_Enable(); + while (LL_RCC_PLL3_IsReady() != 1) { + } #if defined(USE_USB_HS) LL_RCC_SetUSBPHYCClockSource(LL_RCC_USBPHYC_CLKSOURCE_HSE); @@ -134,9 +137,13 @@ void SystemClock_Config() LL_RCC_SetOTGFSClockSource(LL_RCC_OTGFS_CLKSOURCE_HSI48); #endif + // 192 MHz LL_RCC_SetXSPIClockSource(LL_RCC_XSPI1_CLKSOURCE_PLL2S); LL_RCC_SetXSPIClockSource(LL_RCC_XSPI2_CLKSOURCE_PLL2S); + // 16.340 MHz + LL_RCC_SetSPIClockSource(LL_RCC_SPI6_CLKSOURCE_PLL2Q); + // Only required if using Async ADC clock ??? LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_CLKP); }