1
0
This repository has been archived on 2024-09-17. You can view files and clone it, but cannot push or open issues or pull requests.
tor-heredero-tokenring/Audio_746G_Discovery.c

1163 lines
32 KiB
C
Raw Normal View History

2024-04-10 17:17:57 +00:00
/*-----------------------------------------------------------------------------
* Name: Audio_756G_EVAL.c
* Purpose: Audio interface for STM32756G-EVAL Board
* Rev.: 1.0.0
*----------------------------------------------------------------------------*/
/* Copyright (c) 2015 ARM LIMITED
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of ARM nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
*
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------*/
#include "stm32f7xx_hal.h"
#include "cmsis_os2.h"
#include "Driver_I2C.h"
#include "Driver_SAI.h"
#include "Board_Audio.h"
// WM8994 Outputs (Available: Headphone1 and speaker output)
#define WM8994_OUTPUT_NONE (0U)
#define WM8994_OUTPUT_HPOUT1 (1U)
#define WM8994_OUTPUT_SPKOUT (2U)
#ifndef WM8994_OUTPUT
#define WM8994_OUTPUT (WM8994_OUTPUT_HPOUT1 | WM8994_OUTPUT_SPKOUT)
#endif
// WM8994 Inputs (Available: Digital microphone 2 input and Line IN1)
#define WM8994_INPUT_NONE (0U)
#define WM8994_INPUT_DMIC2 (1U)
#define WM8994_INPUT_IN1 (2U)
#ifndef WM8994_INPUT
#define WM8994_INPUT (WM8994_INPUT_DMIC2)
#endif
#if (WM8994_INPUT == (WM8994_INPUT_DMIC2 | WM8994_INPUT_IN1))
#error "Digital microphone 2 and Line IN1, can not be used together. Please select just one!"
#endif
// I2C
#ifndef WM8994_I2C_PORT
#define WM8994_I2C_PORT 3 // I2C Port number
#endif
#define WM8994_I2C_ADDR 0x1A // WM8994 I2C address
// I2C Driver
#define _I2C_Driver_(n) Driver_I2C##n
#define I2C_Driver_(n) _I2C_Driver_(n)
extern ARM_DRIVER_I2C I2C_Driver_(WM8994_I2C_PORT);
#define ptrI2C (&I2C_Driver_(WM8994_I2C_PORT))
// SAI
#ifndef WM8994_SAI_PORT_OUT
#define WM8994_SAI_PORT_OUT 2 // SAI Port number
#endif
#ifndef WM8994_SAI_PORT_IN
#define WM8994_SAI_PORT_IN 2 // SAI Port number
#endif
// SAI Driver
#define _SAI_Driver_(n) Driver_SAI##n
#define SAI_Driver_(n) _SAI_Driver_(n)
extern ARM_DRIVER_SAI SAI_Driver_(WM8994_SAI_PORT_OUT);
#define ptrSAI_OUT (&SAI_Driver_(WM8994_SAI_PORT_OUT))
extern ARM_DRIVER_SAI SAI_Driver_(WM8994_SAI_PORT_IN);
#define ptrSAI_IN (&SAI_Driver_(WM8994_SAI_PORT_IN))
// Pointer to Stream info
#define STREAM_PTR(stream) ((stream & 0x80U) == 0U ? (&Audio.Out) : (&Audio.In))
// Audio Stream run-time information
typedef struct _STREAM_INFO {
void *pData;
uint32_t DataSize;
uint8_t Volume_L;
uint8_t Volume_R;
uint8_t Mono;
uint8_t Running;
uint8_t Paused;
uint8_t UnderflowPending;
uint8_t SendCompletePending;
} STREAM_INFO;
// Audio Stream
typedef struct _STREAM {
ARM_DRIVER_SAI *SAI;
STREAM_INFO *Info;
} STREAM;
// Audio run-time information
typedef const struct _AUDIO_RESOURCES {
STREAM Out;
STREAM In;
} AUDIO_RESOURCES;
static uint8_t DataBits = 16U;
static uint32_t SamplingFreq = 16000U;
static STREAM_INFO StreamInfoOut = { 0U };
static STREAM_INFO StreamInfoIn = { 0U };
static const AUDIO_RESOURCES Audio = {
ptrSAI_OUT,
&StreamInfoOut,
ptrSAI_IN,
&StreamInfoIn,
};
static Audio_SignalEvent_t CB_Event;
// Callback
/**
\fn void (uint16_t reg, uint16_t reg_val)
\brief SAI callback function
\param[in] event
*/
void SAI_callback (uint32_t event) {
if (event & ARM_SAI_EVENT_FRAME_ERROR) {
// Audio interface does not support FRAME ERRORS
event &= ~ARM_SAI_EVENT_FRAME_ERROR;
}
if (event & ARM_SAI_EVENT_TX_UNDERFLOW) {
if ((Audio.Out.Info->Running == 0U) ||
(Audio.Out.Info->Paused == 1U)) {
Audio.Out.Info->UnderflowPending = 1U;
// Invalid underflow: Transmitter is running to generate clock for codec
event &= ~ARM_SAI_EVENT_TX_UNDERFLOW;
}
}
if (event & ARM_SAI_EVENT_SEND_COMPLETE) {
if (Audio.Out.Info->Paused == 1U) {
Audio.Out.Info->SendCompletePending = 1U;
// Invalid SendComplete: Transmitter is running to generate clock for codec
event &= ~ARM_SAI_EVENT_SEND_COMPLETE;
}
}
if ((CB_Event != NULL) && (event != 0U)) {
CB_Event (event);
}
}
// Local functions
/**
\fn int32_t WM8994_RegRead (uint16_t reg, uint16_t * reg_val)
\brief Read value from WM8994 register
\param[in] reg WM8994 register address
\param[Out] reg_val Pointer to value read from WM8994 register
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
static int32_t WM8994_RegRead (uint16_t reg, uint16_t *reg_val) {
uint8_t buf[2], reg_addr[2];
reg_addr[0] = (uint8_t)(reg >> 8);
reg_addr[1] = (uint8_t)reg;
if (ptrI2C->MasterTransmit (WM8994_I2C_ADDR, reg_addr, 2U, true) != ARM_DRIVER_OK) {
return -1;
}
while (ptrI2C->GetStatus().busy);
if (ptrI2C->MasterReceive (WM8994_I2C_ADDR, buf, 2U, false) != ARM_DRIVER_OK) {
return -1;
}
while (ptrI2C->GetStatus().busy);
*reg_val = buf[0] << 8;
*reg_val |= buf[1];
return 0;
}
/**
\fn int32_t WM8994_RegWrite (uint8_16 reg, uint16_t reg_val)
\brief Write value to WM8994 register
\param[in] reg WM8994 register address
\param[in] reg_val Value to be written to WM8994 register
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
static int32_t WM8994_RegWrite (uint16_t reg, uint16_t reg_val) {
uint8_t buf[4];
buf[0] = (uint8_t)(reg >> 8);
buf[1] = (uint8_t)(reg );
buf[2] = (uint8_t)(reg_val >> 8);
buf[3] = (uint8_t)(reg_val );
if (ptrI2C->MasterTransmit (WM8994_I2C_ADDR, buf, 4U, false) != ARM_DRIVER_OK) {
return -1;
}
while (ptrI2C->GetStatus().busy);
return 0;
}
/**
\fn int32_t WM8994_RegModify (uint8_16 reg, uint16_t mask, uint16_t val)
\brief Write value to WM8994 register
\param[in] reg WM8994 register address
\param[in] mask Mask bits to be changed
\param[in] val Value of masked bits
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
static int32_t WM8994_RegModify (uint16_t reg, uint16_t mask, uint16_t val) {
uint8_t buf[4];
uint16_t rd_val;
WM8994_RegRead (reg, &rd_val);
val |= (rd_val & ~mask);
buf[0] = (uint8_t)(reg >> 8);
buf[1] = (uint8_t)(reg );
buf[2] = (uint8_t)(val >> 8);
buf[3] = (uint8_t)(val );
if (ptrI2C->MasterTransmit (WM8994_I2C_ADDR, buf, 4U, false) != ARM_DRIVER_OK) {
return -1;
}
while (ptrI2C->GetStatus().busy);
return 0;
}
/**
\fn int32_t SAI_Configure (STREAM *ptrStream)
\brief Configure SAI transmitter
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
static int32_t SAI_Configure (const STREAM *ptrStream) {
uint32_t cfg, arg1, arg2;
if (ptrStream == &Audio.Out) {
// Master transmitter, User protocol
cfg = ARM_SAI_CONFIGURE_TX |
ARM_SAI_MODE_MASTER |
ARM_SAI_PROTOCOL_USER |
ARM_SAI_CLOCK_POLARITY_0 |
ARM_SAI_MCLK_PIN_OUTPUT |
ARM_SAI_DATA_SIZE(DataBits);
arg2 = (SamplingFreq & ARM_SAI_AUDIO_FREQ_Msk) | ARM_SAI_MCLK_PRESCALER(256);
}
if (ptrStream == &Audio.In) {
// Slave receiver, User protocol
cfg = ARM_SAI_CONFIGURE_RX |
ARM_SAI_MODE_SLAVE |
ARM_SAI_PROTOCOL_USER |
ARM_SAI_CLOCK_POLARITY_0 |
ARM_SAI_SYNCHRONOUS |
ARM_SAI_DATA_SIZE(DataBits);
}
if (ptrStream->Info->Mono == 1U) {
cfg |= ARM_SAI_MONO_MODE;
}
arg1 = ARM_SAI_FRAME_LENGTH(DataBits * 4U) |
ARM_SAI_FRAME_SYNC_WIDTH(DataBits *2U) |
ARM_SAI_FRAME_SYNC_EARLY |
ARM_SAI_FRAME_SYNC_POLARITY_LOW |
ARM_SAI_SLOT_COUNT(4);
if (ptrStream->SAI->Control (cfg, arg1, arg2) != ARM_DRIVER_OK) {
return -1;
}
return 0;
}
// Board Audio Interface
/**
\fn int32_t Audio_Initialize (Audio_SignalEvent_t cb_event)
\brief Initialize Audio Interface
\param[in] cb_event pointer to event notification function
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
int32_t Audio_Initialize (Audio_SignalEvent_t cb_event) {
uint16_t reg_val;
CB_Event = cb_event;
// Clear Stream info
Audio.Out.Info->DataSize = 0U;
Audio.Out.Info->pData = NULL;
Audio.In.Info->DataSize = 0U;
Audio.In.Info->pData = NULL;
// Set Default values
DataBits = 16U;
SamplingFreq = 16000;
// SAI Initialization
Audio.Out.SAI->Initialize (SAI_callback);
Audio.In.SAI->Initialize (SAI_callback);
Audio.Out.SAI->PowerControl (ARM_POWER_FULL);
Audio.In.SAI->PowerControl (ARM_POWER_FULL);
// Set default TX SAI configuration
// Enable slot 0 and 2
if (Audio.Out.SAI->Control (ARM_SAI_MASK_SLOTS_TX, ~(0x05U), 0) != ARM_DRIVER_OK) {
return -1;
}
if (SAI_Configure (&Audio.Out) != 0U) return -1;
// Set default RX SAI configuration
#if (WM8994_INPUT == WM8994_INPUT_IN1)
// Enable slot 0 and 2
if (Audio.In.SAI->Control (ARM_SAI_MASK_SLOTS_RX, ~(0x05U), 0) != ARM_DRIVER_OK) {
return -1;
}
#endif
#if (WM8994_INPUT == WM8994_INPUT_DMIC2)
// Enable slot 1 and 3
if (Audio.In.SAI->Control (ARM_SAI_MASK_SLOTS_RX, ~(0x0AU), 0) != ARM_DRIVER_OK) {
return -1;
}
#endif
if (SAI_Configure (&Audio.In) != 0U) return -1;
// Start transmitter to run Codec clock
Audio.Out.SAI->Control (ARM_SAI_CONTROL_TX, 1U, 0U);
// I2C Initialization and configuration
ptrI2C->Initialize (NULL);
ptrI2C->PowerControl (ARM_POWER_FULL);
ptrI2C->Control (ARM_I2C_BUS_SPEED, ARM_I2C_BUS_SPEED_STANDARD);
ptrI2C->Control (ARM_I2C_BUS_CLEAR, 0U);
// WM8994 Reset
WM8994_RegWrite (0x0000, 0x0000);
// WM8994 ReadID
WM8994_RegRead (0x0000, &reg_val);
if (reg_val != 0x8994) { return -1; }
// WM8994 Codec Setup
// Errata Work-Arounds
WM8994_RegWrite(0x0102, 0x0003);
WM8994_RegWrite(0x0817, 0x0000);
WM8994_RegWrite(0x0102, 0x0000);
// VMID Soft fast start, VMID buffer enabled
WM8994_RegWrite(0x0039, 0x006C);
// Enable Bias Generator, Enable VMID
WM8994_RegWrite(0x0001, 0x0003);
osDelay (50);
#if ((WM8994_OUTPUT & WM8994_OUTPUT_HPOUT1) || \
(WM8994_OUTPUT & WM8994_INPUT_SPKOUT ))
// Configure AIF1 (Timeslot 0) to DAC1
// Enable AIF1DAC1(left and right) input path (Timeslot 0), Enable ADAC1(left and right)
WM8994_RegWrite(0x0005, 0x0303);
// Enable AIF1 (Timeslot 0, left) to DAC1L
WM8994_RegWrite(0x0601, 0x0001);
// Enable AIF1 (Timeslot 0, Right) to DAC1R
WM8994_RegWrite(0x0602, 0x0001);
#endif
#if (WM8994_INPUT == WM8994_INPUT_IN1)
// Enable ADCL and ADCR, Enable AIF1ADC1 (left) and AIF1ADC1 (right) path
WM8994_RegWrite (0x0004, 0x0303);
// Enable IN1L and IN1R, Enable Thermal sensor & shout down
WM8994_RegWrite (0x0002, 0x6350);
// Enable the ADCL(Left) to AIF1 Timeslot 0 (Left) mixer path
WM8994_RegWrite (0x0606, 0x0002);
// Enable the ADCR(Right) to AIF1 Timeslot 0 (Right) mixer path
WM8994_RegWrite (0x0607, 0x0002);
// GPIO1 Function = AIF1 DRC1
WM8994_RegWrite (0x0700, 0x000D);
// Enable Bias generator and VMID
WM8994_RegModify(0x0001, 0x0003, 0x0003);
WM8994_RegWrite (0x0018, 0x000B);
WM8994_RegWrite (0x001A, 0x000B);
WM8994_RegWrite (0x0029, 0x0025);
WM8994_RegWrite (0x002A, 0x0025);
// IN1L PGA Inverting Input Select, IN1R PGA Inverting Input Select
WM8994_RegWrite (0x0028, 0x0011);
// Enable AIF1ADC1 Digital HPF
WM8994_RegWrite(0x0410, 0x1800);
#endif
#if (WM8994_INPUT == WM8994_INPUT_DMIC2)
// MIC bias1 enable, VDIM divider enable, Normal bias current enable
WM8994_RegModify(0x0001, 0x0013, 0x0013);
osDelay (50);
// Configure DMIC2 to AIF1 (Timeslot 1)
// DMIC2 configuration
WM8994_RegWrite(0x0700, 0x8101);
// Enable AIF1ADC2(Left) and AIF1ADC2(Right) output path
WM8994_RegWrite(0x0004, 0x0C30);
// Enable DMIC2 (Left) to AIF1 (Timeslot 1, Left) output
WM8994_RegWrite(0x0608, 0x0002);
// Enable DMIC2 (Right) to AIF1 (Timeslot 1, Right) output
WM8994_RegWrite(0x0609, 0x0002);
// Enable AIF1ADC2 Digital HPF
WM8994_RegWrite(0x0411, 0x1800);
#endif
#if ((WM8994_INPUT != WM8994_INPUT_NONE ) && \
(WM8994_OUTPUT != WM8994_OUTPUT_NONE))
// default sample rate = 16kHz; AIF1CLK/fs ratio = 256
WM8994_RegWrite(0x0210, 0x0033);
// Right ADC data is output on right channel, Digital audio interface = I2S,
// Word Length = 16bits,
WM8994_RegWrite(0x0300, 0x4010);
// Slave mode
WM8994_RegWrite(0x0302, 0x0000);
// Enable AIF1 Processing clock and Digital mixing processor clock
WM8994_RegWrite(0x0208, 0x000A);
// AIF1CLK Source = MCLK1, Enable AIF1CLK
WM8994_RegWrite(0x0200, 0x0001);
#endif
#if (WM8994_OUTPUT & WM8994_OUTPUT_HPOUT1)
// Digital audio source for envelope tracking: AIF1, DAC Timeslot 0
// Enable Class W
WM8994_RegWrite(0x0051, 0x0005);
// Enable HPOUT1L and HPOUT1R input stage
WM8994_RegModify(0x0001, 0x0300, 0x0300);
// Enable HPOUT1L and HPOUT1R intermediate stage
WM8994_RegWrite(0x0060, 0x0022);
// Enable Charge pump
WM8994_RegWrite(0x004c, 0x9F25);
osDelay(15);
// DAC1L to Left HP Output PGA (HPOUT1LVOL)
WM8994_RegWrite(0x002D, 0x0001);
// DAC1R to Right HP Output PGA (HPOUT1RVOL)
WM8994_RegWrite(0x002E, 0x0001);
// Enable MIXOUTL and MIXOUTR output mixer
WM8994_RegModify(0x0003, 0x00F0, 0x00F0);
// Enable and Start-Up DC Servo for HPOUT1L and HPOUT1R
WM8994_RegWrite(0x0054, 0x0033);
osDelay(250);
// Remove HPOUT1L and HPOUT1R short
// Enable HPOUT1L and HPOUT1R output stage
WM8994_RegModify(0x0060, 0x00CC, 0x00CC);
#endif
#if (WM8994_OUTPUT & WM8994_OUTPUT_SPKOUT)
// Enable SPKMIXL and SPKMIXR output mixer
WM8994_RegModify(0x0003, 0x0300, 0x0300);
// Un-mute Left speaker mixer volume control
WM8994_RegWrite(0x0022, 0x0000);
// Un-mute Right speaker mixer volume control
WM8994_RegWrite(0x0023, 0x0000);
// UN-mute DAC1 to Speaker mixer
WM8994_RegWrite(0x0036, 0x0003);
// Enable Mixer, SPKRVOL PGA and SOKOUTR output
// Enable SPKMIXR Mixer, SPKLVOL PGA and SPKOUTL output
WM8994_RegModify(0x0001, 0x3000, 0x3000);
// Digital audio source for envelope tracking: AIF1, DAC Timeslot 0
// Enable Class W
WM8994_RegWrite(0x0051, 0x0005);
// Un-mute AIF1 Timeslot 0 path
#endif
#if ((WM8994_OUTPUT & WM8994_OUTPUT_HPOUT1) || \
(WM8994_OUTPUT & WM8994_INPUT_SPKOUT ))
// Un-mute DAC1L, DAC1L Digital Volume = 0dB
WM8994_RegWrite(0x0610, 0x00C0);
// Un-mute DAC1R, DAC1R Digital Volume = 0dB
WM8994_RegWrite(0x0611, 0x00C0);
// Un-mute AIF1DAC1 input path (AIF1 Timeslot0)
WM8994_RegWrite(0x0420, 0x0000);
#endif
return 0;
}
/**
\fn int32_t Audio_Uninitialize (void)
\brief De-initialize Audio Interface
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
int32_t Audio_Uninitialize (void) {
// Abort SAI Send and Receive
Audio.In.SAI->Control (ARM_SAI_ABORT_RECEIVE, 0U, 0U);
Audio.Out.SAI->Control (ARM_SAI_ABORT_SEND, 0U, 0U);
// WM8994 Reset
WM8994_RegWrite (0x0000, 0x0000);
// Disable SAI transmitter and receiver
Audio.In.SAI->Control (ARM_SAI_CONTROL_RX, 0U, 0U);
Audio.Out.SAI->Control (ARM_SAI_CONTROL_TX, 0U, 0U);
return 0;
}
/**
\fn int32_t Audio_SendData (const void *data, uint32_t num)
\brief Start sending data to Audio output stream
\param[in] data pointer to buffer with data to send
\param[in] num number of data items to send
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
int32_t Audio_SendData (const void *data, uint32_t num) {
if ((Audio.Out.Info->Running == 0U) ||
(Audio.Out.Info->Paused == 1U)) {
// Save data info
Audio.Out.Info->pData = (void *)data;
Audio.Out.Info->DataSize = num;
} else {
Audio.Out.Info->pData = (void *)data;
Audio.Out.Info->DataSize = num;
if (Audio.Out.SAI->Send (data, num) != ARM_DRIVER_OK) {
return -1;
}
}
return 0;
}
/**
\fn int32_t Audio_ReceiveData (void *data, uint32_t num)
\brief Start receiving data from Audio input stream
\param[out] data pointer to buffer for data to receive
\param[in] num number of data items to send
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
int32_t Audio_ReceiveData (void *data, uint32_t num) {
if (Audio.In.SAI->Receive (data, num) != ARM_DRIVER_OK) {
return -1;
}
return 0;
}
/**
\fn uint32_t Audio_GetDataTxCount (void)
\brief Get transmitted data count
\returns number of data items transmitted
*/
uint32_t Audio_GetDataTxCount (void) {
return (Audio.Out.SAI->GetTxCount());
}
/**
\fn uint32_t Audio_GetDataRxCount (void)
\brief Get received data count
\returns number of data items received
*/
uint32_t Audio_GetDataRxCount (void) {
return (Audio.Out.SAI->GetRxCount());
}
/**
\fn int32_t Audio_Start (uint8_t stream)
\brief Start Audio stream
\param[in] stream stream identifier
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
int32_t Audio_Start (uint8_t stream) {
void * data;
uint32_t num, evt = 0U;
switch (stream) {
case AUDIO_STREAM_OUT:
// Set Running flag
Audio.Out.Info->Running = 1U;
if (Audio.Out.Info->Paused == 0U) {
// Check for valid data to be send
if (Audio.Out.Info->DataSize != 0U) {
// Clear pending event flags
Audio.Out.Info->UnderflowPending = 0U;
Audio.Out.Info->SendCompletePending = 0U;
// Save data information, before clearing it
num = Audio.Out.Info->DataSize;
data = Audio.Out.Info->pData;
Audio.Out.Info->DataSize = 0U;
Audio.Out.Info->pData = NULL;
// Send data
if (Audio.Out.SAI->Send (data, num) != ARM_DRIVER_OK) {
return -1;
}
} else {
// Check for pending events
if (Audio.Out.Info->UnderflowPending == 1U) { evt |= AUDIO_EVENT_TX_UNDERFLOW; }
if (Audio.Out.Info->SendCompletePending == 1U) { evt |= AUDIO_EVENT_SEND_COMPLETE; }
// Clear pending event flags
Audio.Out.Info->UnderflowPending = 0U;
Audio.Out.Info->SendCompletePending = 0U;
if ((CB_Event != NULL) && (evt != 0U)) {
CB_Event (evt);
}
}
}
break;
case AUDIO_STREAM_IN:
Audio.In.SAI->Control (ARM_SAI_CONTROL_RX, 1U, 0U);
break;
default: return -1;
}
return 0;
}
/**
\fn int32_t Audio_Stop (uint8_t stream)
\brief Stop Audio stream
\param[in] stream stream identifier
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
int32_t Audio_Stop (uint8_t stream) {
switch (stream) {
case AUDIO_STREAM_OUT:
// Clear running flag
Audio.Out.Info->Running = 0U;
// Abort transfer
Audio.Out.SAI->Control (ARM_SAI_ABORT_SEND, 1U, 0U);
break;
case AUDIO_STREAM_IN:
// Clear running flag
Audio.In.Info->Running = 0U;
Audio.In.SAI->Control (ARM_SAI_CONTROL_RX, 0U, 0U);
// Abort transfer
Audio.In.SAI->Control (ARM_SAI_ABORT_RECEIVE, 1U, 0U);
break;
default: return (-1);
}
return 0;
}
/**
\fn int32_t Audio_Pause (uint8_t stream)
\brief Pause Audio stream
\param[in] stream stream identifier
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
int32_t Audio_Pause (uint8_t stream) {
switch (stream) {
case AUDIO_STREAM_OUT:
if (Audio.Out.Info->Running == 0U) { return 0U; }
// Clear pending event flags
Audio.Out.Info->UnderflowPending = 0U;
Audio.Out.Info->SendCompletePending = 0U;
// Set paused flag
Audio.Out.Info->Paused = 1U;
break;
case AUDIO_STREAM_IN:
if (Audio.In.Info->Running == 0U) { return 0U; }
// Disable SAI receiver
Audio.In.SAI->Control (ARM_SAI_CONTROL_RX, 0U, 0U);
break;
default: return (-1);
}
return 0;
}
/**
\fn int32_t Audio_Resume (uint8_t stream)
\brief Resume Audio stream
\param[in] stream stream identifier
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
int32_t Audio_Resume (uint8_t stream) {
void * data;
uint32_t num, evt = 0U;
switch (stream) {
case AUDIO_STREAM_OUT:
if ((Audio.Out.Info->Paused == 1U) &&
(Audio.Out.Info->Running == 1U)) {
// Clear Paused flag
Audio.Out.Info->Paused = 0U;
// Check for valid data to be send
if (Audio.Out.Info->DataSize != 0U) {
// Clear pending event flags
Audio.Out.Info->UnderflowPending = 0U;
Audio.Out.Info->SendCompletePending = 0U;
// Save data information, before clearing it
num = Audio.Out.Info->DataSize;
data = Audio.Out.Info->pData;
Audio.Out.Info->DataSize = 0U;
Audio.Out.Info->pData = NULL;
// Send data
if (Audio.Out.SAI->Send (data, num) != ARM_DRIVER_OK) {
return -1;
}
} else {
// Check for pending events
if (Audio.Out.Info->UnderflowPending == 1U) { evt |= AUDIO_EVENT_TX_UNDERFLOW; }
if (Audio.Out.Info->SendCompletePending == 1U) { evt |= AUDIO_EVENT_SEND_COMPLETE; }
// Clear pending event flags
Audio.Out.Info->UnderflowPending = 0U;
Audio.Out.Info->SendCompletePending = 0U;
if ((CB_Event != NULL) && (evt != 0U)) {
CB_Event (evt);
}
}
}
break;
case AUDIO_STREAM_IN:
if (Audio.In.Info->Paused == 1U) {
// Clear paused flag
Audio.In.Info->Paused = 0U;
// Enable Receiver
Audio.In.SAI->Control (ARM_SAI_CONTROL_RX, 1U, 0U);
}
break;
default: return -1;
}
return 0;
}
/**
\fn int32_t Audio_SetVolume (uint8_t stream, uint8_t channel, uint8_t volume)
\brief Set volume level for Audio stream
\param[in] stream stream identifier
\param[in] channel channel identifier
\param[in] volume volume level (0..100)
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
int32_t Audio_SetVolume (uint8_t stream, uint8_t channel, uint8_t volume) {
if (volume > 100) { return -1; }
switch (stream) {
case AUDIO_STREAM_OUT:
// Scale volume
volume = (volume * 63) / 100;
switch (channel) {
case AUDIO_CHANNEL_MASTER:
#if (WM8994_OUTPUT & WM8994_OUTPUT_HPOUT1)
// Update Left and Right channel volume
WM8994_RegWrite(0x001C, 0x0040 | volume);
WM8994_RegWrite(0x001D, 0x0140 | volume);
#endif
#if (WM8994_OUTPUT & WM8994_OUTPUT_SPKOUT)
// Update Left and Right channel volume
WM8994_RegWrite(0x0026, 0x0040 | volume);
WM8994_RegWrite(0x0027, 0x0140 | volume);
#endif
break;
case AUDIO_CHANNEL_LEFT:
#if (WM8994_OUTPUT & WM8994_OUTPUT_HPOUT1)
// Update Left channel volume
WM8994_RegWrite(0x001C, 0x0140 | volume);
#endif
#if (WM8994_OUTPUT & WM8994_OUTPUT_SPKOUT)
// Update Left channel volume
WM8994_RegWrite(0x0026, 0x0140 | volume);
#endif
break;
case AUDIO_CHANNEL_RIGHT:
#if (WM8994_OUTPUT & WM8994_OUTPUT_HPOUT1)
// Update Right channel volume
WM8994_RegWrite(0x001D, 0x0140 | volume);
#endif
#if (WM8994_OUTPUT & WM8994_OUTPUT_SPKOUT)
// Update Right channel volume
WM8994_RegWrite(0x0027, 0x0140 | volume);
#endif
break;
}
break;
case AUDIO_STREAM_IN:
// Scale Volume
volume = (volume * 239) / 100;
switch (channel) {
case AUDIO_CHANNEL_MASTER:
// Save Audio IN volume settings
Audio.In.Info->Volume_L = volume;
Audio.In.Info->Volume_R = volume;
// Update Left and Right channel volume
#if (WM8994_INPUT == WM8994_INPUT_DMIC2)
WM8994_RegWrite(0x0404, volume);
WM8994_RegWrite(0x0405, volume);
#endif
#if (WM8994_INPUT == WM8994_INPUT_IN1)
WM8994_RegWrite(0x0400, volume);
WM8994_RegWrite(0x0401, volume);
#endif
break;
case AUDIO_CHANNEL_LEFT:
// Save Audio IN volume settings
Audio.In.Info->Volume_L = volume;
// Update Left channel volume
#if (WM8994_INPUT == WM8994_INPUT_DMIC2)
WM8994_RegWrite(0x0404, volume);
#endif
#if (WM8994_INPUT == WM8994_INPUT_IN1)
WM8994_RegWrite(0x0400, volume);
#endif
break;
case AUDIO_CHANNEL_RIGHT:
// Save Audio IN volume settings
Audio.In.Info->Volume_R = volume;
// Update Right channel volume
#if (WM8994_INPUT == WM8994_INPUT_DMIC2)
WM8994_RegWrite(0x0405, volume);
#endif
#if (WM8994_INPUT == WM8994_INPUT_IN1)
WM8994_RegWrite(0x0401, volume);
#endif
break;
}
break;
default: return -1;
}
return 0;
}
/**
\fn int32_t Audio_SetMute (uint8_t stream, uint8_t channel, bool mute)
\brief Set mute state for Audio stream
\param[in] stream stream identifier
\param[in] channel channel identifier
\param[in] mute mute state
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
int32_t Audio_SetMute (uint8_t stream, uint8_t channel, bool mute) {
#if (WM8994_OUTPUT != WM8994_OUTPUT_NONE)
uint16_t unmute;
#endif
#if (WM8994_INPUT != WM8994_INPUT_NONE)
uint16_t vol_l, vol_r;
#endif
switch (stream) {
#if (WM8994_OUTPUT != WM8994_OUTPUT_NONE)
case AUDIO_STREAM_OUT:
if (mute == false) { unmute = 0x0100 | (1U << 6); }
else { unmute = 0x0100; }
switch (channel) {
case AUDIO_CHANNEL_MASTER:
#if (WM8994_OUTPUT & WM8994_OUTPUT_HPOUT1)
WM8994_RegModify(0x001C, 0x0040, unmute);
WM8994_RegModify(0x001D, 0x00140, unmute);
#endif
#if (WM8994_OUTPUT & WM8994_OUTPUT_SPKOUT)
WM8994_RegModify(0x0026, 0x0040, unmute);
WM8994_RegModify(0x0027, 0x00140, unmute);
#endif
break;
case AUDIO_CHANNEL_LEFT:
#if (WM8994_OUTPUT & WM8994_OUTPUT_HPOUT1)
WM8994_RegModify(0x001C, 0x00140, unmute);
#endif
#if (WM8994_OUTPUT & WM8994_OUTPUT_SPKOUT)
WM8994_RegModify(0x0026, 0x00140, unmute);
#endif
break;
case AUDIO_CHANNEL_RIGHT:
#if (WM8994_OUTPUT & WM8994_OUTPUT_HPOUT1)
WM8994_RegModify(0x001D, 0x0140, unmute);
#endif
#if (WM8994_OUTPUT & WM8994_OUTPUT_SPKOUT)
WM8994_RegModify(0x0027, 0x0140, unmute);
#endif
break;
}
break;
#endif
#if (WM8994_INPUT != WM8994_INPUT_NONE)
case AUDIO_STREAM_IN:
if (mute == true) {
vol_l = vol_r = 0U;
} else {
vol_l = Audio.In.Info->Volume_L;
vol_r = Audio.In.Info->Volume_R;
}
switch (channel) {
case AUDIO_CHANNEL_MASTER:
// Update Left and Right channel volume
#if (WM8994_INPUT == WM8994_INPUT_DMIC2)
WM8994_RegWrite(0x0404, vol_l);
WM8994_RegWrite(0x0405, vol_r);
#endif
#if (WM8994_INPUT == WM8994_INPUT_IN1)
WM8994_RegWrite(0x0400, vol_l);
WM8994_RegWrite(0x0401, vol_r);
#endif
break;
case AUDIO_CHANNEL_LEFT:
// Update Left channel volume
#if (WM8994_INPUT == WM8994_INPUT_DMIC2)
WM8994_RegWrite(0x0404, vol_l);
#endif
#if (WM8994_INPUT == WM8994_INPUT_IN1)
WM8994_RegWrite(0x0400, vol_l);
#endif
break;
case AUDIO_CHANNEL_RIGHT:
// Update Right channel volume
#if (WM8994_INPUT == WM8994_INPUT_DMIC2)
WM8994_RegWrite(0x0405, vol_r);
#endif
#if (WM8994_INPUT == WM8994_INPUT_IN1)
WM8994_RegWrite(0x0401, vol_r);
#endif
break;
}
break;
#endif
default: return -1;
}
return 0;
}
/**
\fn int32_t Audio_SetDataFormat (uint8_t stream, uint8_t format)
\brief Set Audio data format
\param[in] stream stream identifier
\param[in] format data format
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
int32_t Audio_SetDataFormat (uint8_t stream, uint8_t format) {
const STREAM *ptrStream;
ptrStream = STREAM_PTR(stream);
switch (format) {
case AUDIO_DATA_8_MONO:
// Not Supported
return -1;
case AUDIO_DATA_16_MONO:
DataBits = 16U;
ptrStream->Info->Mono = 1U;
break;
case AUDIO_DATA_32_MONO:
DataBits = 32U;
ptrStream->Info->Mono = 1U;
break;
case AUDIO_DATA_8_STEREO:
// Not supported
return -1;
case AUDIO_DATA_16_STEREO:
DataBits = 16U;
ptrStream->Info->Mono = 0U;
break;
case AUDIO_DATA_32_STEREO:
DataBits = 32U;
ptrStream->Info->Mono = 0U;
break;
default: return -1;
}
// Update SAI Data format configuration
if (SAI_Configure (ptrStream) == -1) { return -1; }
if (DataBits == 16U) {
// 16-bit data
WM8994_RegModify (0x0300, 0x0060, 0x0000);
}
if (DataBits == 32U) {
// 32-bit data
WM8994_RegModify (0x0300, 0x0060, 0x0060);
}
return 0;
}
/**
\fn int32_t Audio_SetFrequency (uint8_t stream, uint32_t frequency)
\brief Set Audio stream frequency
\param[in] stream stream identifier
\param[in] frequency Audio frequency in Hz
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
int32_t Audio_SetFrequency (uint8_t stream, uint32_t frequency) {
uint16_t val;
/* Clock Configurations */
switch (frequency){
case 8000:
// AIF1 Sample Rate = 8 kHz
val = 0x0003;
break;
case 16000:
// AIF1 Sample Rate = 16 kHz
val = 0x0033;
break;
case 32000:
// AIF1 Sample Rate = 32 kHz
val = 0x0063;
break;
case 48000:
// AIF1 Sample Rate = 48 kHz
val = 0x0083;
break;
case 96000:
// AIF1 Sample Rate = 96 kHz
val = 0x00A3;
break;
case 11000:
// AIF1 Sample Rate = 11 kHz
val = 0x0013;
break;
case 22000:
// AIF1 Sample Rate = 22 kHz
val = 0x0043;
break;
case 44000:
// AIF1 Sample Rate = 44 kHz
val = 0x0073;
break;
default: return -1;
}
// Save Audio frequency value
SamplingFreq = frequency;
// Update SAI Audio frequency configuration
SAI_Configure (&Audio.Out);
// Update WM8994 codec Audio frequency
WM8994_RegWrite (0x0210, val);
return 0;
}