on 2022-07-01 10:06 AM
Here we will continue with the required firmware implementation to play the Audio files from external memory
Now, on the audio file, you can use the __attribute__((section(".myAudioFiles"))) to place it in the newly created section in the external memory. The reason we add the audio file array with the attribute is to create a binary with both the program, and the audio file the audio file binary will have different address based on the linker script settings and this will be used by the STM32CubeProgrammer external loader to program the audio file array portion in the external memory and the code in the MCU's memory. As the C vector file created can be edited, it is suggested to create a define for the size and add this define in the “adpcm.h”, thus adding these modifications:
In the adpcm.h, these small modifications were made to create the defines used previously and to create a structure that will allow multiple files to be loaded
#define ADPCMD_PI 6622 #define NUMBER_OF_AUDIO_FILES 1 /* Includes ------------------------------------------------------------------*/ #include "main.h" /* Exported types ------------------------------------------------------------*/ typedef union { uint8_t uBytes[2]; uint16_t uShort; } tTwoByte; typedef struct { uint32_t AudioFiles[NUMBER_OF_AUDIO_FILES]; uint32_t AudioSize[NUMBER_OF_AUDIO_FILES]; } AudioElement; /* Exported constants --------------------------------------------------------*/ extern const unsigned char Pure_Inspiration[ADPCMD_PI]; /* Exported macro ------------------------------------------------------------*/ #define NELEMS(x) (sizeof(x) / sizeof((x)[0]))
Note that the #define ADPCMD_PI 6622 should reflect your actual vector size and you can customize the name. As this implementation only deals with a single audio file, we use this: #define NUMBER_OF_AUDIO_FILES 1
Based on the automatically generated code, we need to add a few extra lines to make our application run. Starting with the main.c, you’ll need to:
Translating the steps above into actual code please locate it in the main.c we have it as shown below, please track the code position verifying the /* USER CODE BEGIN*/ and /* USER CODE END */
/* USER CODE BEGIN Includes */ #include "adpcm.h" /* USER CODE END Includes */ /* USER CODE BEGIN PD */ #define DEFAULT_STARTUP_VAL (0x80) /* USER CODE END PD */ /* USER CODE BEGIN WHILE */ #ifdef USE_DAC HAL_DAC_Start(&hdac1, DAC_CHANNEL_1); HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 0x7FF); #endif // capture/compare registers (CC1 PWM duty 50%) TIM3->CCR1 = DEFAULT_STARTUP_VAL; TIM3->CCR2 = DEFAULT_STARTUP_VAL; LL_TIM_EnableIT_UPDATE(TIM3); TIM3->CCER |= TIM_CCER_CC2E | TIM_CCER_CC1E; LL_TIM_EnableCounter(TIM3); while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ if(!HAL_GPIO_ReadPin(BT1_GPIO_Port,BT1_Pin)) { while(!HAL_GPIO_ReadPin(BT1_GPIO_Port,BT1_Pin)); if(AudioFileToPlay>=1) { AudioFileToPlay = 0; }else { AudioFileToPlay++; } /* Disable the TIM3 Interrupt */ NVIC_EnableIRQ(TIM3_IRQn); // stop the timer LL_TIM_EnableCounter(TIM3); } HAL_Delay(100); } /* USER CODE END 3 */
In the interrupt service routine, more specifically in the TIM3 periodic interrupt, we must execute these actions as well:
Translating the steps above into actual code please locate it in the stm32g0xx_it.c we have it as shown below, please track the code position by verifying the /* USER CODE BEGIN*/ and /* USER CODE END */
/* USER CODE BEGIN Includes */ #include "adpcm.h" #ifdef USE_DAC #include "dac.h" #endif /* USER CODE END Includes */ /* USER CODE BEGIN PV */ tTwoByte newSample; extern AudioElement AudioFile; extern uint8_t AudioFileToPlay; /* USER CODE END PV */ /** * @brief This function handles TIM3 global interrupt. */ void TIM3_IRQHandler(void) { /* USER CODE BEGIN TIM3_IRQn 0 */ uint8_t adpcmSample; static uint8_t one=1; static uint16_t pcmSample; static uint8_t nibble = 1; static uint8_t repetition = 0; static uint16_t sample_position = 0; extern uint32_t __myAudioFiles_START; extern uint32_t __myAudioFiles_END; uint32_t sizeofAudio= (uint32_t)(&__myAudioFiles_END)- (uint32_t (&__myAudioFiles_START); static uint8_t RawAudio[sizeofAudio]; uint32_t address=0x00000000; static uint8_t PrevAudioFileToPlay = 0xFF; // To make sure we read the Audio file just once if(one==1) { readData(address, RawAudio, sizeofAudio); one=0; } if(PrevAudioFileToPlay != AudioFileToPlay) { PrevAudioFileToPlay = AudioFileToPlay; nibble = 1; repetition = 0; sample_position = 0; } if (LL_TIM_IsActiveFlag_UPDATE(TIM3)) { LL_TIM_ClearFlag_UPDATE(TIM3); if ((repetition==0) & (sample_position < sizeofAudio)) { #ifndef USE_STM8 repetition = 7; #endif if (nibble) { // first 4 bits of the ADPCM byte decoded adpcmSample = (uint8_t)(RawAudio[sample_position] >>4); } else { // last 4 bits of the ADPCM byte decoded adpcmSample = (uint8_t)(RawAudio[sample_position] & 0x0F); sample_position++ ; } nibble = (uint8_t)(!nibble); pcmSample = ADPCM_Decode(adpcmSample); // update sample newSample.uShort = (uint16_t)32768 + pcmSample; TIM3->CCR2 = newSample.uBytes[0]; //LSB TIM3->CCR1 = newSample.uBytes[1]; //MSB #ifdef USE_DAC HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, (newSample.uShort)>>4); #endif } else if (sample_position < sizeofAudio) { // repetition 7 times of the PWM period before new sample repetition--; // reload Timer with the actual sample value newSample.uShort = (uint16_t)32768 + pcmSample; TIM3->CCR2 = newSample.uBytes[0]; //LSB TIM3->CCR1 = newSample.uBytes[1]; //MSB #ifdef USE_DAC HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, (newSample.uShort)>>4); #endif } else { // end of the audio clip /* Disable the TIM3 Interrupt */ NVIC_DisableIRQ(TIM3_IRQn); // stop the timer LL_TIM_DisableCounter(TIM3); } } return ; /* USER CODE END TIM3_IRQn 0 */ }
Conclusion:
This is a full-fledged tutorial which combines all the parts of several earlier tutorials with just a few additional steps such as configuring SPI2, TIM3 interrupt handler, linker file editing and combining the SPI Flash driver files to read the audio files from external flash. Now we have enhanced the storage with an external memory and have created our own audio player on STM32.