⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 class-d.c

📁 最新版IAR FOR ARM(EWARM)5.11中的代码例子
💻 C
📖 第 1 页 / 共 2 页
字号:
//*****************************************************************************
//
// 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 + -