mirror of
https://github.com/STMicroelectronics/STM32CubeF4.git
synced 2025-05-02 22:17:06 +08:00
463 lines
14 KiB
C
463 lines
14 KiB
C
/**
|
|
******************************************************************************
|
|
* @file BSP/Src/audio.c
|
|
* @author MCD Application Team
|
|
* @brief This example code shows how to use the audio feature in the
|
|
* stm32f413h_discovery driver
|
|
******************************************************************************
|
|
* @attention
|
|
*
|
|
* Copyright (c) 2017 STMicroelectronics.
|
|
* All rights reserved.
|
|
*
|
|
* This software is licensed under terms that can be found in the LICENSE file
|
|
* in the root directory of this software component.
|
|
* If no LICENSE file comes with this software, it is provided AS-IS.
|
|
*
|
|
******************************************************************************
|
|
*/
|
|
|
|
/* Includes ------------------------------------------------------------------*/
|
|
#include "main.h"
|
|
#include "stm32f413h_discovery_audio.h"
|
|
#include <stdio.h>
|
|
|
|
|
|
/** @addtogroup STM32F4xx_HAL_Examples
|
|
* @{
|
|
*/
|
|
|
|
/** @addtogroup BSP
|
|
* @{
|
|
*/
|
|
|
|
/* Private define ------------------------------------------------------------*/
|
|
|
|
/*Since SysTick is set to 1ms (unless to set it quicker) */
|
|
/* to run up to 48khz, a buffer around 1000 (or more) is requested*/
|
|
/* to run up to 96khz, a buffer around 2000 (or more) is requested*/
|
|
#define AUDIO_BUFFER_SIZE 2048
|
|
#define AUDIO_DEFAULT_VOLUME 70
|
|
|
|
/* Audio file size and start address are defined here since the audio file is
|
|
stored in Flash memory as a constant table of 16-bit data */
|
|
#define AUDIO_FILE_SIZE 524288
|
|
#define AUDIO_START_OFFSET_ADDRESS 0 /* Offset relative to audio file header size */
|
|
#define AUDIO_FILE_ADDRESS 0x08040000 /* Audio file address */
|
|
|
|
#define HEADBAND_HEIGHT 64
|
|
|
|
/* Private typedef -----------------------------------------------------------*/
|
|
typedef enum {
|
|
AUDIO_STATE_IDLE = 0,
|
|
AUDIO_STATE_INIT,
|
|
AUDIO_STATE_PLAYING,
|
|
}AUDIO_PLAYBACK_StateTypeDef;
|
|
|
|
typedef enum {
|
|
BUFFER_OFFSET_NONE = 0,
|
|
BUFFER_OFFSET_HALF,
|
|
BUFFER_OFFSET_FULL,
|
|
}BUFFER_StateTypeDef;
|
|
|
|
typedef struct {
|
|
uint8_t buff[AUDIO_BUFFER_SIZE];
|
|
uint32_t fptr;
|
|
BUFFER_StateTypeDef state;
|
|
uint32_t AudioFileSize;
|
|
uint32_t *SrcAddress;
|
|
}AUDIO_BufferTypeDef;
|
|
|
|
typedef enum {
|
|
TS_ACT_NONE = 0,
|
|
TS_ACT_FREQ_DOWN,
|
|
TS_ACT_FREQ_UP,
|
|
TS_ACT_VOLUME_DOWN,
|
|
TS_ACT_VOLUME_UP,
|
|
TS_ACT_PAUSE = 0xFE
|
|
}TS_ActionTypeDef;
|
|
|
|
/* Private macro -------------------------------------------------------------*/
|
|
/* Private variables ---------------------------------------------------------*/
|
|
static TS_ActionTypeDef ts_action = TS_ACT_NONE;
|
|
extern TS_StateTypeDef TS_State;
|
|
|
|
static AUDIO_BufferTypeDef buffer_ctl;
|
|
static AUDIO_PLAYBACK_StateTypeDef audio_state;
|
|
static uint32_t AudioStartAddress;
|
|
static uint32_t AudioFileSize;
|
|
__IO uint32_t uwVolume = 20;
|
|
__IO uint32_t uwPauseEnabledStatus = 0; /* 0 when audio is running, 1 when Pause is on */
|
|
|
|
static uint32_t AudioFreq[8] = {8000 ,11025, 16000, 22050, 32000, 44100, 48000, 96000};
|
|
|
|
/* Private function prototypes -----------------------------------------------*/
|
|
static void Audio_SetHint(void);
|
|
static uint32_t GetData(void *pdata, uint32_t offset, uint8_t *pbuf, uint32_t NbrOfData);
|
|
AUDIO_ErrorTypeDef AUDIO_Start(uint32_t audio_start_address, uint32_t audio_file_size);
|
|
|
|
/* Private functions ---------------------------------------------------------*/
|
|
|
|
/**
|
|
* @brief Audio Play demo
|
|
* @param None
|
|
* @retval None
|
|
*/
|
|
void AudioPlay_demo (void)
|
|
{
|
|
uint32_t *AudioFreq_ptr;
|
|
AudioFreq_ptr = AudioFreq+6; /*AF_48K*/
|
|
uint8_t frequency_str[256] = {0};
|
|
uint8_t volume_str[256] = {0};
|
|
uint8_t ts_status = TS_OK;
|
|
|
|
uwVolume = 40;
|
|
|
|
/* If calibration is not yet done, proceed with calibration */
|
|
if (TouchScreen_IsCalibrationDone() == 0)
|
|
{
|
|
ts_status = Touchscreen_Calibration();
|
|
if(ts_status == TS_OK)
|
|
{
|
|
BSP_LCD_SetTextColor(LCD_COLOR_BLACK);
|
|
BSP_LCD_DisplayStringAt(0, BSP_LCD_GetYSize() - 65, (uint8_t *)"Touchscreen calibration success.", CENTER_MODE);
|
|
}
|
|
} /* of if (TouchScreen_IsCalibrationDone() == 0) */
|
|
|
|
Audio_SetHint();
|
|
BSP_LCD_SetFont(&Font12);
|
|
|
|
if(BSP_AUDIO_OUT_Init(OUTPUT_DEVICE_HEADPHONE, uwVolume, *AudioFreq_ptr) == 0)
|
|
{
|
|
BSP_LCD_SetBackColor(LCD_COLOR_WHITE);
|
|
BSP_LCD_SetTextColor(LCD_COLOR_GREEN);
|
|
BSP_LCD_DisplayStringAt(0, BSP_LCD_GetYSize() - 120, (uint8_t *)" AUDIO CODEC OK ", CENTER_MODE);
|
|
}
|
|
else
|
|
{
|
|
BSP_LCD_SetBackColor(LCD_COLOR_WHITE);
|
|
BSP_LCD_SetTextColor(LCD_COLOR_RED);
|
|
BSP_LCD_DisplayStringAt(0, BSP_LCD_GetYSize() - 120, (uint8_t *)" AUDIO CODEC FAIL ", CENTER_MODE);
|
|
BSP_LCD_DisplayStringAt(0, BSP_LCD_GetYSize() - 105, (uint8_t *)" Try to reset board ", CENTER_MODE);
|
|
}
|
|
|
|
/*
|
|
Start playing the file from a circular buffer, once the DMA is enabled, it is
|
|
always in running state. Application has to fill the buffer with the audio data
|
|
using Transfer complete and/or half transfer complete interrupts callbacks
|
|
(BSP_AUDIO_OUT_TransferComplete_CallBack() or BSP_AUDIO_OUT_HalfTransfer_CallBack()...
|
|
*/
|
|
AUDIO_Start(AUDIO_FILE_ADDRESS, AUDIO_FILE_SIZE);
|
|
|
|
/* Display the state on the screen */
|
|
BSP_LCD_SetBackColor(LCD_COLOR_WHITE);
|
|
BSP_LCD_SetTextColor(LCD_COLOR_BLUE);
|
|
BSP_LCD_DisplayStringAt(0, BSP_LCD_GetYSize()- 105, (uint8_t *)" PLAYING... ", CENTER_MODE);
|
|
|
|
sprintf((char*)volume_str," VOL: %lu ",uwVolume);
|
|
BSP_LCD_DisplayStringAt(0, 90, (uint8_t *)volume_str, CENTER_MODE);
|
|
|
|
sprintf((char*)frequency_str," FREQ: %lu ",*AudioFreq_ptr);
|
|
BSP_LCD_DisplayStringAt(0, 215, (uint8_t *)frequency_str, CENTER_MODE);
|
|
|
|
if(ts_status == TS_OK)
|
|
{
|
|
/* Set touchscreen in Interrupt mode and program EXTI accordingly on falling edge of TS_INT pin */
|
|
ts_status = BSP_TS_ITConfig();
|
|
Touchscreen_DrawControl_Buttons();
|
|
}
|
|
|
|
BSP_LCD_SetFont(&Font12);
|
|
|
|
/* Infinite loop */
|
|
while (1)
|
|
{
|
|
/* IMPORTANT: AUDIO_Process() should be called within a periodic process */
|
|
AUDIO_Process();
|
|
|
|
/* Get the TouchScreen State */
|
|
ts_action = (TS_ActionTypeDef) TouchScreen_GetTouchButtonPosition();
|
|
|
|
switch (ts_action)
|
|
{
|
|
case TS_ACT_VOLUME_UP:
|
|
TS_State.touchDetected = 0;
|
|
if(uwPauseEnabledStatus != 1)
|
|
{
|
|
/* Increase volume by 5% */
|
|
if (uwVolume < 95)
|
|
uwVolume += 5;
|
|
else
|
|
uwVolume = 100;
|
|
|
|
BSP_AUDIO_OUT_SetVolume(uwVolume);
|
|
sprintf((char*)volume_str," VOL: %lu ",uwVolume);
|
|
BSP_LCD_DisplayStringAt(0, 90, (uint8_t *)volume_str, CENTER_MODE);
|
|
}
|
|
break;
|
|
|
|
case TS_ACT_VOLUME_DOWN:
|
|
if(uwPauseEnabledStatus != 1)
|
|
{
|
|
/* Decrease volume by 5% */
|
|
if (uwVolume > 5)
|
|
uwVolume -= 5;
|
|
else
|
|
uwVolume = 0;
|
|
|
|
BSP_AUDIO_OUT_SetVolume(uwVolume);
|
|
sprintf((char*)volume_str," VOL: %lu ",uwVolume);
|
|
BSP_LCD_DisplayStringAt(0, 90, (uint8_t *)volume_str, CENTER_MODE);
|
|
}
|
|
break;
|
|
|
|
case TS_ACT_FREQ_DOWN:
|
|
/*Decrease Frequency */
|
|
if ((*AudioFreq_ptr != 8000) && (uwPauseEnabledStatus != 1))
|
|
{
|
|
AudioFreq_ptr--;
|
|
BSP_AUDIO_OUT_SetFrequency(*AudioFreq_ptr);
|
|
sprintf((char*)frequency_str, " FREQ: %6lu ", *AudioFreq_ptr);
|
|
BSP_LCD_DisplayStringAt(0, 215, (uint8_t *)frequency_str, CENTER_MODE);
|
|
}
|
|
|
|
break;
|
|
case TS_ACT_FREQ_UP:
|
|
/* Increase Frequency */
|
|
if ((*AudioFreq_ptr != 96000) && (uwPauseEnabledStatus != 1))
|
|
{
|
|
AudioFreq_ptr++;
|
|
|
|
BSP_AUDIO_OUT_SetFrequency(*AudioFreq_ptr);
|
|
sprintf((char*)frequency_str, " FREQ: %6lu ", *AudioFreq_ptr);
|
|
BSP_LCD_DisplayStringAt(0, 215, (uint8_t *)frequency_str, CENTER_MODE);
|
|
}
|
|
break;
|
|
case TS_ACT_PAUSE:
|
|
TS_State.touchDetected = 0;
|
|
/* Set Pause / Resume */
|
|
if (uwPauseEnabledStatus == 1)
|
|
{ /* Pause is enabled, call Resume */
|
|
BSP_AUDIO_OUT_Resume();
|
|
uwPauseEnabledStatus = 0;
|
|
BSP_LCD_DisplayStringAt(0, BSP_LCD_GetYSize()- 105, (uint8_t *)" PLAYING... ", CENTER_MODE);
|
|
|
|
BSP_LCD_SetTextColor(LCD_COLOR_BLUE);
|
|
BSP_LCD_DisplayStringAt(105, 165, (uint8_t *)"Pause", LEFT_MODE);
|
|
}
|
|
else
|
|
{ /* Pause the playback */
|
|
BSP_AUDIO_OUT_Pause();
|
|
uwPauseEnabledStatus = 1;
|
|
BSP_LCD_DisplayStringAt(0, BSP_LCD_GetYSize()- 105, (uint8_t *)" PAUSE ... ", CENTER_MODE);
|
|
BSP_LCD_SetTextColor(LCD_COLOR_BLUE);
|
|
BSP_LCD_DisplayStringAt(105, 165, (uint8_t *)"Play ", LEFT_MODE);
|
|
}
|
|
HAL_Delay(200);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
if (CheckForUserInput() > 0)
|
|
{
|
|
BSP_AUDIO_OUT_Stop(CODEC_PDWN_SW);
|
|
BSP_AUDIO_OUT_DeInit();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Display Audio demo hint
|
|
* @param None
|
|
* @retval None
|
|
*/
|
|
static void Audio_SetHint(void)
|
|
{
|
|
/* Clear the LCD */
|
|
BSP_LCD_Clear(LCD_COLOR_WHITE);
|
|
|
|
/* Set Audio Demo description */
|
|
BSP_LCD_SetTextColor(LCD_COLOR_BLUE);
|
|
BSP_LCD_FillRect(0, 0, BSP_LCD_GetXSize(), HEADBAND_HEIGHT);
|
|
BSP_LCD_SetTextColor(LCD_COLOR_WHITE);
|
|
BSP_LCD_SetBackColor(LCD_COLOR_BLUE);
|
|
BSP_LCD_SetFont(&Font16);
|
|
BSP_LCD_DisplayStringAt(0, 0, (uint8_t *)"AUDIO PLAY", CENTER_MODE);
|
|
BSP_LCD_SetFont(&Font12);
|
|
BSP_LCD_DisplayStringAt(0, 20, (uint8_t *)" Touch | pause / resume ", LEFT_MODE);
|
|
BSP_LCD_DisplayStringAt(0, 35, (uint8_t *)" Touch | change Volume/Freq", LEFT_MODE);
|
|
BSP_LCD_DisplayStringAt(0, 50, (uint8_t *)" User Button | Exit Test ", LEFT_MODE);
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Starts Audio streaming.
|
|
* @param audio_start_address : buffer start address
|
|
* @param audio_file_size : buffer size in bytes
|
|
* @retval Audio error
|
|
*/
|
|
AUDIO_ErrorTypeDef AUDIO_Start(uint32_t audio_start_address, uint32_t audio_file_size)
|
|
{
|
|
uint32_t bytesread;
|
|
|
|
buffer_ctl.state = BUFFER_OFFSET_NONE;
|
|
AudioStartAddress = audio_start_address;
|
|
AudioFileSize = audio_file_size;
|
|
bytesread = GetData( (void *)AudioStartAddress,
|
|
0,
|
|
&buffer_ctl.buff[0],
|
|
AUDIO_BUFFER_SIZE);
|
|
if(bytesread > 0)
|
|
{
|
|
BSP_AUDIO_OUT_Play((uint16_t*)&buffer_ctl.buff[0], AUDIO_BUFFER_SIZE);
|
|
audio_state = AUDIO_STATE_PLAYING;
|
|
buffer_ctl.fptr = bytesread;
|
|
return AUDIO_ERROR_NONE;
|
|
}
|
|
return AUDIO_ERROR_IO;
|
|
}
|
|
|
|
/**
|
|
* @brief Manages Audio process.
|
|
* @param None
|
|
* @retval Audio error
|
|
*/
|
|
uint8_t AUDIO_Process(void)
|
|
{
|
|
uint32_t bytesread;
|
|
AUDIO_ErrorTypeDef error_state = AUDIO_ERROR_NONE;
|
|
|
|
switch(audio_state)
|
|
{
|
|
case AUDIO_STATE_PLAYING:
|
|
|
|
if(buffer_ctl.fptr >= AudioFileSize)
|
|
{
|
|
/* Play audio sample again ... */
|
|
buffer_ctl.fptr = 0;
|
|
error_state = AUDIO_ERROR_EOF;
|
|
}
|
|
|
|
/* 1st half buffer played; so fill it and continue playing from bottom*/
|
|
if(buffer_ctl.state == BUFFER_OFFSET_HALF)
|
|
{
|
|
bytesread = GetData((void *)AudioStartAddress,
|
|
buffer_ctl.fptr,
|
|
&buffer_ctl.buff[0],
|
|
AUDIO_BUFFER_SIZE /2);
|
|
|
|
if( bytesread >0)
|
|
{
|
|
buffer_ctl.state = BUFFER_OFFSET_NONE;
|
|
buffer_ctl.fptr += bytesread;
|
|
}
|
|
}
|
|
|
|
/* 2nd half buffer played; so fill it and continue playing from top */
|
|
if(buffer_ctl.state == BUFFER_OFFSET_FULL)
|
|
{
|
|
bytesread = GetData((void *)AudioStartAddress,
|
|
buffer_ctl.fptr,
|
|
&buffer_ctl.buff[AUDIO_BUFFER_SIZE /2],
|
|
AUDIO_BUFFER_SIZE /2);
|
|
if( bytesread > 0)
|
|
{
|
|
buffer_ctl.state = BUFFER_OFFSET_NONE;
|
|
buffer_ctl.fptr += bytesread;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
error_state = AUDIO_ERROR_NOTREADY;
|
|
break;
|
|
}
|
|
|
|
return (uint8_t) error_state;
|
|
}
|
|
|
|
/**
|
|
* @brief Gets Data from storage unit.
|
|
* @param None
|
|
* @retval None
|
|
*/
|
|
static uint32_t GetData(void *pdata, uint32_t offset, uint8_t *pbuf, uint32_t NbrOfData)
|
|
{
|
|
uint8_t *lptr = pdata;
|
|
uint32_t ReadDataNbr;
|
|
|
|
ReadDataNbr = 0;
|
|
while(((offset + ReadDataNbr) < AudioFileSize) && (ReadDataNbr < NbrOfData))
|
|
{
|
|
pbuf[ReadDataNbr]= lptr [offset + ReadDataNbr];
|
|
ReadDataNbr++;
|
|
}
|
|
return ReadDataNbr;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------
|
|
Callbacks implementation:
|
|
the callbacks API are defined __weak in the stm32f413h_discovery_audio.c file
|
|
and their implementation should be done the user code if they are needed.
|
|
Below some examples of callback implementations.
|
|
----------------------------------------------------------------------------*/
|
|
/**
|
|
* @brief Manages the full Transfer complete event.
|
|
* @param None
|
|
* @retval None
|
|
*/
|
|
void BSP_AUDIO_OUT_TransferComplete_CallBack(void)
|
|
{
|
|
if(audio_state == AUDIO_STATE_PLAYING)
|
|
{
|
|
/* allows AUDIO_Process() to refill 2nd part of the buffer */
|
|
buffer_ctl.state = BUFFER_OFFSET_FULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Manages the DMA Half Transfer complete event.
|
|
* @param None
|
|
* @retval None
|
|
*/
|
|
void BSP_AUDIO_OUT_HalfTransfer_CallBack(void)
|
|
{
|
|
if(audio_state == AUDIO_STATE_PLAYING)
|
|
{
|
|
/* allows AUDIO_Process() to refill 1st part of the buffer */
|
|
buffer_ctl.state = BUFFER_OFFSET_HALF;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Manages the DMA FIFO error event.
|
|
* @param None
|
|
* @retval None
|
|
*/
|
|
void BSP_AUDIO_OUT_Error_CallBack(void)
|
|
{
|
|
/* Display message on the LCD screen */
|
|
BSP_LCD_SetBackColor(LCD_COLOR_RED);
|
|
BSP_LCD_DisplayStringAt(0, LINE(14), (uint8_t *)" DMA ERROR ", CENTER_MODE);
|
|
BSP_LCD_SetBackColor(LCD_COLOR_WHITE);
|
|
|
|
/* Stop the program with an infinite loop */
|
|
while (BSP_PB_GetState(BUTTON_WAKEUP) != RESET)
|
|
{ return;}
|
|
|
|
/* could also generate a system reset to recover from the error */
|
|
/* .... */
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
/**
|
|
* @}
|
|
*/
|