📄 sta013.c
字号:
//++
//sta013.c - STA013 MP3 decoder functions
//
// Copyright (C) 2005 by Spare Time Gizmos. All rights reserved.
//
// This file is part of the Spare Time Gizmos' MP3 Player firmware.
//
// This firmware is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 59 Temple
// Place, Suite 330, Boston, MA 02111-1307 USA.
//
//DESCRIPTION:
//
//
//REFERENCES:
// http://hubbard.engr.scu.edu/embedded/avr/avrlib/docs/html/sta013_8h.html
// http://hubbard.engr.scu.edu/embedded/avr/avrlib/docs/html/sta013_8c.html
// http://www.pjrc.com/tech/mp3/sta013.html
//
//REVISION HISTORY:
// dd-mmm-yy who description
// 15-May-05 RLA New file.
// 15-Oct-05 RLA Add VOLUME conditional.
// 19-OCT-05 RLA Add ResetMP3Decoder(). Split up the P02_0609 data
// into the UpdateData[] and ConfigData[] arrays. This
// cleans up the noise burst (part of the previous song,
// actually) when turning the knob.
//--
// Include files...
#include <stdio.h> // needed so DBGOUT(()) can find printf!
#include "standard.h" // standard types - BYTE, WORD, BOOL, etc
#include "reg66x.h" // declarations for Philips 89C66x processors
#include "player.h" // project wide (hardware configuration) declarations
#include "debug.h" // debugging stuff and ResetWDT()
#include "post.h" // POST codes for Fail()
#include "timer.h" // timer and time functions
#include "i2c.h" // low level STA013 I2C communcations routines
#include "buffer.h" // disk sector buffer functions
#include "sta013.h" // declarations for this module
// It turns out that the STA013 isn't good for much of anything right after a
// reset. In fact, it can't even decode MP3 files until it has been properly
// "updated" first; according to ST this process is mandatory! ST provides an
// update file on their web page; this file is really nothing more than a list of
// register addresses, register value pairs.
PRIVATE const BYTE CODE m_abUpdateData[] = {
#include "p02_0609.h"
};
// After the STA013 has been updated, it must be configured to work with the
// rest of our hardware. This includes things like programming the PLL dividers
// to work with our 14.31818MHz clock (the STA013 clock, NOT the MCU clock!),
// programming the output data format to be compatible with the CS4334, and
// setting the DRQ and IRQ polarities. ST originally included a lot of this
// information in their P02_0609 patch file, but it doesn't really belong there
// since it is specific to our hardware. Note also that, unlike the update data,
// this information does not survive a STA013 soft reset!
//
// WARNING! The _order_ in which these registers are written is important!
// If you're tempted to re-arrange the order of the lines in this table just
// for cosmetic purposes, go lie down until the urge goes away...
PRIVATE const BYTE CODE m_abConfigData[] = {
58, 0, // undocumented
STA_REG_PCMDIVIDER, 1, // 256X oversample, 32 bit words
STA_REG_PCMCONF, 33, // I2S format for CS4334
// The following section gives the PPL configration for various crystal
// frequencies. You can generate your own for almost any crystal by using
// the CPLL.EXE program from the ST web site (see the URL given above)...
#if STA013_CLOCK == 14745600L
STA_REG_PLLCTL_N, 0, // PLLCTL (N)
STA_REG_PLLCTL_M, 12, // PLLCTL (M)
11, 3, // undocumented (but CPLL generates it!)
STA_REG_MFSDF_441, 16, // MFSDF_441
STA_REG_PLLFRAC_441_L, 0, // PLLFRAC_441_L
STA_REG_PLLFRAC_441_H, 4, // PLLFRAC_441_H
STA_REG_MFSDF, 15, // MFSDF (X)
STA_REG_PLLFRAC_L, 85, // PLLFRAC_L
STA_REG_PLLFRAC_H, 85, // PLLFRAC_H
#elif STA013_CLOCK == 14318180L
STA_REG_PLLCTL_N, 0, // PLLCTL (N)
STA_REG_PLLCTL_M, 12, // PLLCTL (M)
11, 3, // undocumented
STA_REG_MFSDF_441, 16, // MFSDF_441
STA_REG_PLLFRAC_441_L, 119, // PLLFRAC_441_L
STA_REG_PLLFRAC_441_H, 103, // PLLFRAC_441_H
STA_REG_MFSDF, 15, // MFSDF (X)
STA_REG_PLLFRAC_L, 58, // PLLFRAC_L
STA_REG_PLLFRAC_H, 187, // PLLFRAC_H
#else
#error UNKNOWN STA013 CLOCK FREQUENCY!!
#endif
STA_REG_PLLCTL, 161, // enable OCLK/DRQ and update PLL FRAC
//STA_REG_SCLK_POL, 4, // sample SDI on the falling edge of SCKR
STA_REG_DATA_REQ_ENABLE, 4, // enable data request pin
STA_REG_REQ_POL, 5 // send data when DRQ pin is low
};
// These arrays translate the binary codes returned by the STA013 into actual
// bit rate and sampling frequencies. Note that there are two sets of bit rates
// - one for MPEG1 and the other for MPEG2 and 2.5. There are _three_ sets of
// sampling frequences, one each for MPEG 1, 2 and 2.5...
PRIVATE const WORD code m_awMP3BitRates[2][16] = {
{0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0}, // MPEG 1 rates
{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0} // MPEG2 and 2.5
};
PRIVATE const BYTE code m_abMP3SampleFrequencies[4][4] = {
{11, 12, 8, 0}, // MPEG 2.5 rates
{ 0, 0, 0, 0}, // (unused)
{22, 24, 16, 0}, // MPEG 2 rates
{44, 48, 32, 0} // MPEG 1 rates
};
//++
// This routine will send a list of data to the STA013. The first byte in
// every pair specifies the STA013 register address, and the second byte is
// the data to be sent. cbData specifies the number of _bytes_ (not words!)
// in the table.
//--
PRIVATE void SendSTA013 (const BYTE CODE *pcbData, WORD cbData)
{
WORD n;
//DBGOUT(("SendSTA013: Configuring STA013 ... "));
for (n = 0; n < cbData; n += 2) {
//DBGOUT(("SendSTA013: register 0x%02bx data 0x%02bx\n", pcbData[n], pcbData[n+1]));
if (!Write013(pcbData[n], pcbData[n+1])) POST(PC_STA013, FALSE);
}
//DBGOUT((" done (%u bytes)\n", cbData));
}
//++
// InitializeSTA013
//--
PUBLIC void InitializeSTA013 (void)
{
BYTE bVersion;
// Reset the STA013...
STA013_RESET = 0; DelayMS(20); STA013_RESET = 1; DelayMS(20);
// Read the ident register and make sure it's 0xAC...
if (!Read013(STA_REG_IDENT, &bVersion)) POST(PC_STA013, FALSE);
if (bVersion != STA_IDENT) POST(PC_STA013, FALSE);
// Set the DRQ polarity (move this to Configuration File ???)
DBGOUT(("STA013 initialized (chip version 0x%02bX) ...\n", (BYTE) bVersion));
SendSTA013(m_abUpdateData, sizeof(m_abUpdateData));
ResetMP3Decoder();
}
//++
// This routine will do a soft reset of the STA013 - the main advantage to
// doing this is that it flushes all the internal buffers and pipeline in the
// decoder so that, when we start playing the next song, we don't get a blast
// of noise that's left over data from the previous song!
//
// This is easily done with the SOFT_RESET register of the STA013, but it
// it's not as simple as that. According to the documentation, writing this
// register "clears the current command and interrupt registers and puts the
// decoder in idle mode." Sounds great, but IMHO it's a lie. Turns out that
// a soft reset also erases all of the PLL, PCM DIVISOR, interface type and
// polarity, and even the volume registers! If you use soft reset, all these
// have to be re-programmed before STA013 will play anything again!
//
// One more thing - this function leaves the STA013 muted on return; it's
// up to the caller to unmute...
//--
PUBLIC void ResetMP3Decoder (void)
{
// This combination causes a soft reset and the STA013 may need a few
// microseconds to recover before it can continue. Many thanks to Paul
// for pointing this out, http://www.pjrc.com/tech/mp3/sta013.html .
if (!Write013(STA_REG_SOFT_RESET, 1)) POST(PC_STA013, FALSE);
//DELAYUS(100);
if (!Write013(STA_REG_MUTE, 1)) POST(PC_STA013, FALSE);
// And reconfigure the STA013 for our hardware...
SendSTA013(m_abConfigData, sizeof(m_abConfigData));
//DBGOUT(("ResetMP3Decoder: ...\n"));
}
//++
// This routine sets the MP3 volume, which is specified by a value from zero
// (absolute silence) to 100 (deafening). In principle these registers can also
// be used to set the balance, but currently that's not implemented.
//--
#ifdef VOLUME
PRIVATE void SetMP3Volume (BYTE bVolume)
{
// Conveniently, the STA013 also wants a value from 0..100 for the volume
// setting. The catch, however, is that the STA013 registers control an
// _attenuator_, so the sense of the values is reversed (i.e. zero is maximum
// volume; 100 is silence). That's easy enough to fix.
ASSERT((bVolume <= 100), ("SetMP3Volume: illegal value %bd\n", bVolume));
if (!Write013(STA_REG_DLA, 100-bVolume)) POST(PC_STA013, FALSE);
if (!Write013(STA_REG_DRA, 100-bVolume)) POST(PC_STA013, FALSE);
// The STA013 also contains two channel attenuators which would allow you
// to reverse the left and right channels, mix them both together for a mono
// output, or reduce the stereo separation by any amount you desire. Currently
// we don't use these, so we'll turn them off by setting them to maximum
// attenuation.
if (!Write013(STA_REG_DLB, MAX_VOLUME_ATTENUATION)) POST(PC_STA013, FALSE);
if (!Write013(STA_REG_DRB, MAX_VOLUME_ATTENUATION)) POST(PC_STA013, FALSE);
//DBGOUT(("SetMP3Volume: STA013 volume set to %bd%%\n", bVolume));
}
#endif
//++
// This routine will start the MP3 decoder running, sets the PLAY register
// to one, and then unmutes the output. Immediately after this (most likely
// before we even have time to return!) the STA013 will assert DRQ and, provided
// that we have data ready to feed it, music will appear.
//--
PUBLIC void StartMP3Decoder (void)
{
#ifdef VOLUME
SetMP3Volume (VOLUME);
#endif
if (!Write013(STA_REG_RUN , 1)) POST(PC_STA013, FALSE);
if (!Write013(STA_REG_PLAY, 1)) POST(PC_STA013, FALSE);
if (!Write013(STA_REG_MUTE, 0)) POST(PC_STA013, FALSE);
//DBGOUT(("StartMP3Decoder: ...\n"));
}
//++
// This routine will interrupt the currently playing song and stop the MP3
// decoder. Unlike the PauseMP3(), this is a destructive stop - it's not
// possible to continue where we left off. This function is normally used
// to interrupt the current song when the user turns the dial...
//--
PUBLIC void StopMP3Decoder (void)
{
if (!Write013(STA_REG_MUTE, 1)) POST(PC_STA013, FALSE);
if (!Write013(STA_REG_PLAY, 0)) POST(PC_STA013, FALSE);
if (!Write013(STA_REG_RUN , 0)) POST(PC_STA013, FALSE);
//DBGOUT(("StopMP3Decoder: ...\n"));
}
//++
// This function pauses the current MP3 playback. This is a clean pause -
// the STA013 stops asserting DRQ and simply idles until playback is resumed
// by calling (what else?) ResumeMP3()...
//--
PUBLIC void PauseMP3 (void)
{
// Mute the audio and then stop the decoder...
if (!Write013(STA_REG_MUTE, 1)) POST(PC_STA013, FALSE);
if (!Write013(STA_REG_PLAY, 0)) POST(PC_STA013, FALSE);
}
//++
// Resume playback after a PauseMP3(). WARNING - calling this routine when
// playback is not paused (i.e. when the decoder is stopped will have all sorts
// of unhappy effects.
//--
PUBLIC void ResumeMP3 (void)
{
// Restart the decoder and unmute the audio...
if (!Write013(STA_REG_PLAY, 1)) POST(PC_STA013, FALSE);
if (!Write013(STA_REG_MUTE, 0)) POST(PC_STA013, FALSE);
}
//++
// Return the current MPEG ID from the STA013...
//--
PUBLIC MPEG_ID GetMPEGID (void)
{
BYTE bHeadH;
// This value is kept in bits 19..20 of the HEAD[er] register...
if (!Read013(STA_REG_HEAD_H, &bHeadH)) POST(PC_STA013, FALSE);
return (bHeadH & 0x18) >> 3;
}
//++
// Return the bit rate used by the currently playing MP3 file. Note that
// the return value is in kbps (e.g. 128 for a CD quality sound, 320 for the
// maximum possible MPEG bit rate).
//--
PUBLIC WORD GetMP3BitRate (void)
{
BYTE bBitRateIndex, bID, bHeadM;
// The bitrate is in bits 12..15 of the STA013 HEAD[er] register. This
// value is just an index, and unfortunately the meaning of this index
// depends on whether a MPEG1 or MPEG2/2.5 file is being played. Once we
// know these things, we can use the m_awMP3BitRates[][] table to convert
// to a real k bits per second value.
if (!Read013(STA_REG_HEAD_M, &bHeadM)) POST(PC_STA013, FALSE);
bID = GetMPEGID(); bBitRateIndex = (bHeadM & 0xF0) >> 4;
//DBGOUT(("GetMP3BitRate: bBitRateIndex=%bd, bID=%bd\n", bBitRateIndex, bID));
return m_awMP3BitRates[(bID==STA_MPEG_1) ? 0 : 1][bBitRateIndex];
}
//++
// Return the sampling rate used by the currently playing MP3. The return
// valus is always an integer - e.g 44, 22, etc., for 44.1kHz, 22.05kHz, etc.
//--
PUBLIC BYTE GetMP3SampleFrequency (void)
{
BYTE bSampleIndex, bID, bHeadM;
// The sampling frequency is stored in bits 11..10 of the STA013 HEAD[er]
// register. Other than that, it's pretty much the same as the bit rate...
if (!Read013(STA_REG_HEAD_M, &bHeadM)) POST(PC_STA013, FALSE);
bID = GetMPEGID(); bSampleIndex = (bHeadM & 0x0C) >> 2;
//DBGOUT(("GetMP3SampleFrequency: bSampleIndex=%bd, bID=%bd\n", bSampleIndex, bID));
return m_abMP3SampleFrequencies[bID][bSampleIndex];
}
#if 0
PUBLIC WORD GetMP3AverageBitRate (void)
{
BYTE bBitRate;
if (!Read013(STA_REG_AVERAGE_BITRATE, &bBitRate)) POST(PC_STA013, FALSE);
return bBitRate<<1;
}
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -