?? class-d.c
字號:
//*****************************************************************************
//
// class-d.c - Audio driver for the Class-D amplifier on the EK-LM3S1968.
//
// Copyright (c) 2007 Luminary Micro, Inc. All rights reserved.
//
// Software License Agreement
//
// Luminary Micro, Inc. (LMI) is supplying this software for use solely and
// exclusively on LMI's microcontroller products.
//
// The software is owned by LMI and/or its suppliers, and is protected under
// applicable copyright laws. All rights are reserved. You may not combine
// this software with "viral" open-source software in order to form a larger
// program. Any use in violation of the foregoing restrictions may subject
// the user to criminal sanctions under applicable laws, as well as to civil
// liability for the breach of the terms and conditions of this license.
//
// THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED
// OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
// LMI SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR
// CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
//
// This is part of revision 1952 of the Stellaris Peripheral Driver Library.
//
//*****************************************************************************
//*****************************************************************************
//
//! \addtogroup ek_lm3s1968_api
//! @{
//
//*****************************************************************************
#include "../../hw_ints.h"
#include "../../hw_memmap.h"
#include "../../hw_types.h"
#include "../../src/gpio.h"
#include "../../src/interrupt.h"
#include "../../src/pwm.h"
#include "../../src/sysctl.h"
#include "class-d.h"
//*****************************************************************************
//
// The number of clocks per PWM period.
//
//*****************************************************************************
static unsigned long g_ulClassDPeriod;
//*****************************************************************************
//
// A set of flags indicating the mode of the Class-D audio driver.
//
//*****************************************************************************
static volatile unsigned long g_ulClassDFlags;
#define CLASSD_FLAG_STARTUP 0
#define CLASSD_FLAG_ADPCM 1
#define CLASSD_FLAG_PCM 2
//*****************************************************************************
//
// A pointer to the audio buffer being played. The validity of this pointer,
// and the expected contents of this buffer, are defined by the flag set in
// g_ulClassDFlags.
//
//*****************************************************************************
static const unsigned char *g_pucClassDBuffer;
//*****************************************************************************
//
// The length of the audio buffer, in bytes. When performing the startup ramp,
// this is the number of steps left in the ramp.
//
//*****************************************************************************
static unsigned long g_ulClassDLength;
//*****************************************************************************
//
// The volume to playback the audio stream. This will be a value between 0
// (for silence) and 256 (for full volume).
//
//*****************************************************************************
static long g_lClassDVolume = 256;
//*****************************************************************************
//
// The previous and current audio samples, used for interpolating from 8 KHz
// to 64 KHz audio.
//
//*****************************************************************************
static unsigned short g_pusClassDSamples[2];
//*****************************************************************************
//
// The audio step, which corresponds to the current interpolation point between
// the previous and current audio samples. The upper bits (that is, 31 through
// 3) are used to determine the sub-sample within a byte of input (for ADPCM
// and DPCM).
//
//*****************************************************************************
static unsigned long g_ulClassDStep;
//*****************************************************************************
//
// The current step index for the ADPCM decoder. This selects a differential
// value from g_pusADPCMStep.
//
//*****************************************************************************
static long g_lClassDADPCMStepIndex;
//*****************************************************************************
//
// The adjustment to the step index based on the value of an encoded sample.
// The sign bit is ignored when using this table (that is, only the lower three
// bits are used).
//
//*****************************************************************************
static const signed char g_pcADPCMIndex[8] =
{
-1, -1, -1, -1, 2, 4, 6, 8
};
//*****************************************************************************
//
// The differential values for the ADPCM decoder. One of these is selected
// based on the step index.
//
//*****************************************************************************
static const unsigned short g_pusADPCMStep[89] =
{
7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41,
45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209,
230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876,
963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749,
3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630,
9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623,
27086, 29794, 32767
};
//*****************************************************************************
//
//! Handles the PWM1 interrupt.
//!
//! This function responds to the PWM1 interrupt, updating the duty cycle of
//! the output waveform in order to produce sound. It is the application's
//! responsibility to ensure that this function is called in response to the
//! PWM1 interrupt, typically by installing it in the vector table as the
//! handler for the PWM1 interrupt.
//!
//! This function is contained in <tt>class-d.c</tt>, with <tt>class-d.h</tt>
//! containing the API definition for use by applications.
//!
//! \return None.
//
//*****************************************************************************
void
ClassDPWMHandler(void)
{
long lStep, lNibble, lDutyCycle;
//
// Clear the PWM interrupt.
//
PWMGenIntClear(PWM_BASE, PWM_GEN_1, PWM_INT_CNT_ZERO);
//
// See if the startup ramp is in progress.
//
if(HWREGBITW(&g_ulClassDFlags, CLASSD_FLAG_STARTUP))
{
//
// Decrement the ramp count.
//
g_ulClassDLength--;
//
// Increase the pulse width of the two outputs by one clock.
//
PWMDeadBandEnable(PWM_BASE, PWM_GEN_1, 0, g_ulClassDLength);
PWMPulseWidthSet(PWM_BASE, PWM_OUT_2,
(g_ulClassDPeriod - g_ulClassDLength) / 2);
//
// See if this was the last step of the ramp.
//
if(g_ulClassDLength == 0)
{
//
// Indicate that the startup ramp has completed.
//
HWREGBITW(&g_ulClassDFlags, CLASSD_FLAG_STARTUP) = 0;
}
//
// There is nothing further to be done.
//
return;
}
//
// See if there is any audio playing.
//
if(g_ulClassDFlags == 0)
{
//
// Set the duty cycle to 50%, producing silence.
//
PWMPulseWidthSet(PWM_BASE, PWM_OUT_2, g_ulClassDPeriod / 2);
//
// There is nothing further to be done.
//
return;
}
//
// Compute the value of the PCM sample based on the blended average of the
// previous and current samples. It should be noted that linear
// interpolation does not produce the best results with audio (it produces
// a significant amount of harmonic aliasing) but it is fast.
//
lDutyCycle = (((g_pusClassDSamples[0] * (8 - (g_ulClassDStep & 7))) +
(g_pusClassDSamples[1] * (g_ulClassDStep & 7))) / 8);
//
// Adjust the magnitude of the sample based on the current volume. Since a
// multiplicative volume control is implemented, the volume value will
// result in nearly linear volume adjustment if it is squared.
//
lDutyCycle = (((lDutyCycle - 32768) * g_lClassDVolume * g_lClassDVolume) /
65536) + 32768;
//
// Set the PWM duty cycle based on this PCM sample.
//
lDutyCycle = (g_ulClassDPeriod * lDutyCycle) / 65536;
if(lDutyCycle > (g_ulClassDPeriod - 2))
{
lDutyCycle = g_ulClassDPeriod - 2;
}
if(lDutyCycle < 2)
{
lDutyCycle = 2;
}
PWMPulseWidthSet(PWM_BASE, PWM_OUT_2, lDutyCycle);
//
// Increment the audio step.
//
g_ulClassDStep++;
//
// See if the next sample has been reached.
//
if((g_ulClassDStep & 7) == 0)
{
//
// Copy the current sample to the previous sample.
//
g_pusClassDSamples[0] = g_pusClassDSamples[1];
//
// See if there is more input data.
//
if(g_ulClassDLength == 0)
{
//
// All input data has been played, so indicate that playback has
// completed.
//
g_ulClassDFlags = 0;
}
//
// See if an ADPCM stream is being played.
//
else if(HWREGBITW(&g_ulClassDFlags, CLASSD_FLAG_ADPCM))
{
//
// See which nibble should be decoded.
//
if((g_ulClassDStep & 8) == 0)
{
//
// Extract the lower nibble from the current byte, and skip to
// the next byte.
//
lNibble = *g_pucClassDBuffer++;
//
// Decrement the count of bytes to be decoded.
//
g_ulClassDLength--;
}
else
{
//
// Extract the upper nibble from the current byte.
//
lNibble = *g_pucClassDBuffer >> 4;
}
//
// Compute the sample delta based on the current nibble and step
// size.
//
lStep = ((((2 * (lNibble & 7)) + 1) *
g_pusADPCMStep[g_lClassDADPCMStepIndex]) / 16);
//
// Add or subtract the delta to the previous sample value, clipping
// if necessary.
//
if(lNibble & 8)
{
lStep = g_pusClassDSamples[0] - lStep;
if(lStep < 0)
{
lStep = 0;
}
}
else
{
lStep = g_pusClassDSamples[0] + lStep;
if(lStep > 65535)
{
lStep = 65535;
}
}
//
// Store the generated sample.
//
g_pusClassDSamples[1] = lStep;
//
// Adjust the step size index based on the current nibble, clipping
// the value if required.
//
g_lClassDADPCMStepIndex += g_pcADPCMIndex[lNibble & 7];
if(g_lClassDADPCMStepIndex < 0)
{
g_lClassDADPCMStepIndex = 0;
}
if(g_lClassDADPCMStepIndex > 88)
{
g_lClassDADPCMStepIndex = 88;
}
}
//
// See if a 8-bit PCM stream is being played.
//
else if(HWREGBITW(&g_ulClassDFlags, CLASSD_FLAG_PCM))
{
//
// Read the next sample from the input stream.
//
g_pusClassDSamples[1] = *g_pucClassDBuffer++ * 256;
//
// Decrement the count of samples to be played.
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -