diff --git a/FW/Leo_muziekdoos_fw/.vscode/settings.json b/FW/Leo_muziekdoos_fw/.vscode/settings.json new file mode 100644 index 0000000..9676423 --- /dev/null +++ b/FW/Leo_muziekdoos_fw/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "*.tcc": "cpp", + "random": "cpp" + } +} \ No newline at end of file diff --git a/FW/Leo_muziekdoos_fw/platformio.ini b/FW/Leo_muziekdoos_fw/platformio.ini index c16dc75..db95a57 100644 --- a/FW/Leo_muziekdoos_fw/platformio.ini +++ b/FW/Leo_muziekdoos_fw/platformio.ini @@ -10,14 +10,12 @@ [env:genericSTM32F411CE] platform = ststm32 -board = genericSTM32F411CE +board = blackpill_f411ce framework = arduino upload_protocol = stlink debug_tool = stlink -lib_deps = - ; stm32duino/STM32duino STM32SD@^1.2.3 - ; adafruit/Audio - Adafruit Fork@^1.3.1 - adafruit/Adafruit Zero I2S Library@^1.2.1 +lib_deps = stm32duino/STM32duino STM32SD@^1.2.3 build_flags = -D PIO_FRAMEWORK_ARDUINO_ENABLE_CDC -D PIO_FRAMEWORK_ARDUINO_USB_FULLSPEED_FULLMODE +;lib_ldf_mode = deep+ diff --git a/FW/Leo_muziekdoos_fw/src/audio.cpp b/FW/Leo_muziekdoos_fw/src/audio.cpp new file mode 100644 index 0000000..3443d04 --- /dev/null +++ b/FW/Leo_muziekdoos_fw/src/audio.cpp @@ -0,0 +1,211 @@ +#include "audio.h" + +/* generic I2S example for any STM32duino HAL core F4 +Original code by Rene Böllhoff +translated to STM32duino by Matthias Diro ("madias" -> STM32duino forum) +Features: Circular Buffer with DMA IRQ (half full, full), Master Clock enabled +This example uses the SPI3 port tested on STM32F407VET "black" board. On other boards please define LED0_BUILTIN and LED1_BUILTIN +*/ +#define I2S_BUFFER_SIZE 64 +I2S_HandleTypeDef hi2s3; +DMA_HandleTypeDef hdma_spi3_tx; +uint32_t dma_buffer[I2S_BUFFER_SIZE]; +// sinus oszillator +float osc_phi = 0; +float osc_phi_inc = 440.0f / 44100.0f; // generating 440HZ + +void MX_DMA_Init(void); + +extern "C" void DMA1_Stream5_IRQHandler(void) // this function must be included to avoid DMA to crash! +{ + HAL_DMA_IRQHandler(&hdma_spi3_tx); +} + +void Error_Handler2(byte errorcode) +{ // if something goes wrong counter the blinks for rudimentary debugging + digitalWrite(LED_BUILTIN, 1); + while (1) + { + for (int x = 0; x < errorcode; x++) + { + digitalWrite(LED_BUILTIN, 1); + delay(100); + digitalWrite(LED_BUILTIN, 0); + delay(100); + } + delay(1000); + } +} + +void FillBuffer(uint32_t *buffer, uint16_t len) +{ + float a; + int16_t y; + uint16_t c; + for (c = 0; c < len; c++) + { + // calculate sin + a = (float)sin(osc_phi * 6.2832f) * 0.20f; + osc_phi += osc_phi_inc; + osc_phi -= (float)((uint16_t)osc_phi); + // float to integer + y = (int16_t)(a * 32767.0f); + // auf beide kanäle + buffer[c] = ((uint32_t)(uint16_t)y) << 0 | + ((uint32_t)(uint16_t)y) << 16; + } +} +void StartAudioBuffers(I2S_HandleTypeDef *hi2s) +{ + // clear buffer + memset(dma_buffer, 0, sizeof(dma_buffer)); + // start circular dma + HAL_I2S_Transmit_DMA(hi2s, (uint16_t *)dma_buffer, I2S_BUFFER_SIZE << 1); +} +extern "C" void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s) +{ + // second half finished, filling it up again while first half is playing + FillBuffer(&(dma_buffer[I2S_BUFFER_SIZE >> 1]), I2S_BUFFER_SIZE >> 1); +} +extern "C" void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s) +{ + // first half finished, filling it up again while second half is playing + FillBuffer(&(dma_buffer[0]), I2S_BUFFER_SIZE >> 1); +} +// setting up I2S +extern "C" void MX_I2S3_Init(void) +{ + hi2s3.Instance = SPI3; + hi2s3.Init.Mode = I2S_MODE_MASTER_TX; + hi2s3.Init.Standard = I2S_STANDARD_PHILIPS; + hi2s3.Init.DataFormat = I2S_DATAFORMAT_16B; + //hi2s3.Init.MCLKOutput = I2S_MCLKOUTPUT_DISABLE; + hi2s3.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE; + hi2s3.Init.AudioFreq = I2S_AUDIOFREQ_44K; + hi2s3.Init.CPOL = I2S_CPOL_LOW; + hi2s3.Init.ClockSource = I2S_CLOCK_PLL; + hi2s3.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_DISABLE; + if (HAL_I2S_Init(&hi2s3) != HAL_OK) + { + Error_Handler2(1); // on error: one blink + } +} +// setting up DMA +void MX_DMA_Init(void) +{ + /* DMA controller clock enable */ + __HAL_RCC_DMA1_CLK_ENABLE(); + /* DMA interrupt init */ + /* DMA1_Stream5_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn); +} +// setting up pins and clocks; routing SPI3 to DMA +extern "C" void HAL_I2S_MspInit(I2S_HandleTypeDef *hi2s) +{ + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_SPI3_CLK_ENABLE(); + GPIO_InitTypeDef GPIO_InitStruct; + /* I2S standard configurations: + SPI2 + PB15 DIN + PB12 LRC + PB13 SCLK + PC6 MCK + SPI3 + PB5 DIN + PA4 LRC + PB3 SCLK + PC7 MCK +*/ + //I2S3 used GPIO configuration in this example: + // PB5 DIN / SD + // PA4 LRC /WD + // PB3 SCLK /CK + // PC7 MCK + GPIO_InitStruct.Pin = GPIO_PIN_4; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Alternate = GPIO_AF6_SPI3; + HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + GPIO_InitStruct.Pin = GPIO_PIN_3 | GPIO_PIN_5; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Alternate = GPIO_AF6_SPI3; + HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); + // master clock + GPIO_InitStruct.Pin = GPIO_PIN_7; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Alternate = GPIO_AF6_SPI3; + HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + // Peripheral DMA init + hdma_spi3_tx.Instance = DMA1_Stream5; + hdma_spi3_tx.Init.Channel = DMA_CHANNEL_0; + hdma_spi3_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; + hdma_spi3_tx.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_spi3_tx.Init.MemInc = DMA_MINC_ENABLE; + hdma_spi3_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; + hdma_spi3_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; + hdma_spi3_tx.Init.Mode = DMA_CIRCULAR; + hdma_spi3_tx.Init.Priority = DMA_PRIORITY_LOW; + hdma_spi3_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; + if (HAL_DMA_Init(&hdma_spi3_tx) != HAL_OK) + { + Error_Handler2(5); // on error: five blinks + } + __HAL_LINKDMA(hi2s, hdmatx, hdma_spi3_tx); +} +extern "C" void HAL_MspInit(void) // maybe useful, not included in this example +{ + HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); + /* System interrupt init*/ + /* MemoryManagement_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(MemoryManagement_IRQn, 0, 0); + /* BusFault_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(BusFault_IRQn, 0, 0); + /* UsageFault_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(UsageFault_IRQn, 0, 0); + /* SVCall_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(SVCall_IRQn, 0, 0); + /* DebugMonitor_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DebugMonitor_IRQn, 0, 0); + /* PendSV_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(PendSV_IRQn, 0, 0); + /* SysTick_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0); +} +// void initAudio(void) +// { + +// } + +// void handleAudio(void) +// { + +// } + +void initAudio() +{ + // HAL_MspInit(); // not important by default + HAL_I2S_MspInit(&hi2s3); // setting up pins and clocks; routing SPI3 to DMA + MX_DMA_Init(); + MX_I2S3_Init(); + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, 0); + StartAudioBuffers(&hi2s3); + digitalWrite(LED_BUILTIN, 1); +} +void handleAudio() +{ + // just a dummy code in loop, audio out is generated by ISR + digitalWrite(LED_BUILTIN, 1); + delay(250); + digitalWrite(LED_BUILTIN, 0); + delay(250); +} \ No newline at end of file diff --git a/FW/Leo_muziekdoos_fw/src/audio.h b/FW/Leo_muziekdoos_fw/src/audio.h new file mode 100644 index 0000000..70f77a5 --- /dev/null +++ b/FW/Leo_muziekdoos_fw/src/audio.h @@ -0,0 +1,7 @@ +#pragma once + +#include "Arduino.h" +#include "board.h" + +void initAudio(void); +void handleAudio(void); diff --git a/FW/Leo_muziekdoos_fw/src/board.h b/FW/Leo_muziekdoos_fw/src/board.h new file mode 100644 index 0000000..ba24e0b --- /dev/null +++ b/FW/Leo_muziekdoos_fw/src/board.h @@ -0,0 +1,7 @@ +#pragma once + + +// #define PIN_I2S_FS PB12 /* I2S4_WS, Frame Sync (LRclock) +// #define PIN_I2S_SCK PB13 /* I2S4_CLK, serial Clock */ +// #define PIN_I2S_SD PA1 /* I2S4_SD, Serial Data */ +// #define PIN_I2S_SDMODE PA13 /* DAC_SDMODE, SDmode HIGH = enabled (left channel), LOW = shutdown */ \ No newline at end of file diff --git a/FW/Leo_muziekdoos_fw/src/main.cpp b/FW/Leo_muziekdoos_fw/src/main.cpp index fca022f..121e7af 100644 --- a/FW/Leo_muziekdoos_fw/src/main.cpp +++ b/FW/Leo_muziekdoos_fw/src/main.cpp @@ -1,166 +1,18 @@ -// Arduino Zero / Feather M0 I2S audio tone generation example. -// Author: Tony DiCola -// -// Connect an I2S DAC or amp (like the UDA1334A) to the Arduino Zero -// and play back simple sine, sawtooth, triangle, and square waves. -// Makes your Zero sound like a NES! -// -// NOTE: The I2S signal generated by the Zero does NOT have a MCLK / -// master clock signal. You must use an I2S receiver that can operate -// without a MCLK signal (like the UDA1334A). -// -// For an Arduino Zero / Feather M0 connect it to you I2S hardware as follows: -// - Digital 0 -> I2S LRCLK / FS (left/right / frame select clock) -// - Digital 1 -> I2S BCLK / SCLK (bit / serial clock) -// - Digital 9 -> I2S DIN / SD (data output) -// - Ground -// -// Released under a MIT license: https://opensource.org/licenses/MIT -#include "Adafruit_ZeroI2S.h" +#include "Arduino.h" +#include "audio.h" -#define SAMPLERATE_HZ 44100 // The sample rate of the audio. Higher sample rates have better fidelity, - // but these tones are so simple it won't make a difference. 44.1khz is - // standard CD quality sound. +/* TODO: check hardware STM32L432KC (32pin QFN) -#define AMPLITUDE ((1<<29)-1) // Set the amplitude of generated waveforms. This controls how loud - // the signals are, and can be any value from 0 to 2**31 - 1. Start with - // a low value to prevent damaging speakers! +*/ -#define WAV_SIZE 256 // The size of each generated waveform. The larger the size the higher - // quality the signal. A size of 256 is more than enough for these simple - // waveforms. - - -// Define the frequency of music notes (from http://www.phy.mtu.edu/~suits/notefreqs.html): -#define C4_HZ 261.63 -#define D4_HZ 293.66 -#define E4_HZ 329.63 -#define F4_HZ 349.23 -#define G4_HZ 392.00 -#define A4_HZ 440.00 -#define B4_HZ 493.88 - -// Define a C-major scale to play all the notes up and down. -float scale[] = { C4_HZ, D4_HZ, E4_HZ, F4_HZ, G4_HZ, A4_HZ, B4_HZ, A4_HZ, G4_HZ, F4_HZ, E4_HZ, D4_HZ, C4_HZ }; - -// Store basic waveforms in memory. -int32_t sine[WAV_SIZE] = {0}; -int32_t sawtooth[WAV_SIZE] = {0}; -int32_t triangle[WAV_SIZE] = {0}; -int32_t square[WAV_SIZE] = {0}; - -// Create I2S audio transmitter object. -Adafruit_ZeroI2S i2s; - -#define Serial Serial - -void generateSine(int32_t amplitude, int32_t* buffer, uint16_t length) { - // Generate a sine wave signal with the provided amplitude and store it in - // the provided buffer of size length. - for (int i=0; i + +// If SD card slot has no detect pin then define it as SD_DETECT_NONE +// to ignore it. One other option is to call 'card.init()' without parameter. +#ifndef SD_DETECT_PIN +#define SD_DETECT_PIN PB9 +#endif + +void initStorage(void); +void handleStorage(void); \ No newline at end of file