feat(stm32h7s78-dk): sound support
This commit is contained in:
committed by
Malte Langermann
parent
1b0cde96ed
commit
c00ee1e9dd
@@ -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} $<TARGET_OBJECTS:board_bl>)
|
||||
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} $<TARGET_OBJECTS:board>)
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ void boardInit()
|
||||
|
||||
keysInit();
|
||||
switchInit();
|
||||
// audioInit();
|
||||
audioInit();
|
||||
|
||||
adcInit(&_adc_driver);
|
||||
setAnalogValue(adcGetInputOffset(ADC_INPUT_VBAT), 840 * 2);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user