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

📄 tlv320dac.c

📁 qt91上实现的mp3播放机,支持sam9260,参考用
💻 C
字号:
/*!
 * Copyright (C) 2007 by egnite Software GmbH. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 * 3. Neither the name of the copyright holders nor the names of
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH 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 EGNITE
 * SOFTWARE GMBH OR 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.
 *
 * For additional information see http://www.ethernut.de/
 */

/*!
 * \file tlv320dac.c
 * \brief DAC routines.
 *
 * \verbatim
 *
 * $Log$
 *
 * \endverbatim
 */

#include <cfg/os.h>
#include <cfg/clock.h>
#include <dev/board.h>
#include <dev/irqreg.h>
#include <dev/twif.h>

#include <stdlib.h>
#include <string.h>

#include <sys/event.h>
#include <sys/timer.h>

#include "tlv320dac.h"

#include <stdio.h> //DEBUG

#ifndef TWI_SLA_DAC
#define TWI_SLA_DAC     0x1A
#endif

/*
 * Initial volume.
 */
#ifndef TLV320DAC_VOL
#define TLV320DAC_VOL   0x18
#endif

/*
 * Number of PCM buffers.
 */
#ifndef SAMPLE_BUFFERS
#if defined (AT91SAM9260_EK)
#define SAMPLE_BUFFERS  32
#else
#define SAMPLE_BUFFERS  3
#endif
#endif

/*
 * Select I2S pins.
 */
#if defined(AT91SAM7X_EK)   /* _EK */

#define DACI2S_PIO_ID   PIOA_ID
#define DACI2S_PINS_A   _BV(PA23_TD_A) | _BV(PA21_TF_A) | _BV(PA22_TK_A)
#define DACI2S_PINS_B   0

#elif defined (AT91SAM9260_EK) /* _EK */

#define DACI2S_PIO_ID   PIOB_ID
#define DACI2S_PINS_A   _BV(PB18_TD0_A) | _BV(PB17_TF0_A) | _BV(PB16_TK0_A)
#define DACI2S_PINS_B   0

#else                           /* _EK */
#warning "Unknown target board"
#endif                          /* _EK */

/*
 * Determine PIO used by I2S.
 */
#if DACI2S_PIO_ID == PIOA_ID    /* DACI2S_PIO_ID */
#define DACI2S_PDR  PIOA_PDR
#define DACI2S_ASR  PIOA_ASR
#define DACI2S_BSR  PIOA_BSR
#elif DACI2S_PIO_ID == PIOB_ID  /* DACI2S_PIO_ID */
#define DACI2S_PDR  PIOB_PDR
#define DACI2S_ASR  PIOB_ASR
#define DACI2S_BSR  PIOB_BSR
#endif                          /* DACI2S_PIO_ID */

/*!
 * \brief I2S event queue.
 */
static HANDLE i2s_que;

/*!
 * \brief Number of channels.
 */
#define PCM_CHANS   2

/*!
 * \brief Number of bits per sample.
 */
#define PCM_BITS    16

/*!
 * \brief PCM buffer type.
 */
typedef struct _PCM_BUFFER {
    u_short *wbf_dat;           /*!< Pointer to PCM data. */
    int wbf_siz;                /*!< Buffer size. */
    int wbf_len;                /*!< Number of valid bytes buffered. */
} PCM_BUFFER;

/* PCM buffer queue. */
static PCM_BUFFER pcm_bufq[SAMPLE_BUFFERS];
/* PCM buffer read index. */
static volatile u_int brd_idx;
/* PCM buffer write index. */
static u_int bwr_idx;

/*!
 * \brief Set PDC pointer and counter registers.
 *
 * SSC interrupts must have been disabled befor calling this function.
 */
static void I2sPdcFill(void)
{
    if (brd_idx != bwr_idx) {
        if (inr(SSC_TNCR) == 0) {
            if (++brd_idx >= SAMPLE_BUFFERS) {
                brd_idx = 0;
            }
            outr(SSC_TNPR, (u_int) pcm_bufq[brd_idx].wbf_dat);
            outr(SSC_TNCR, pcm_bufq[brd_idx].wbf_len);
        }
    }
}

/*!
 * \brief SSC interrupt handler.
 *
 * Called when PDC transmission done.
 */
static void I2sInterrupt(void *arg)
{
    I2sPdcFill();
    NutEventPostFromIrq(&i2s_que);
}

/*!
 * \brief Read value from specified DAC register.
 *
 * Not implemented, because the TLV320AIC23B is a write-only device.
 *
 * \param reg DAC register address.
 *
 * \return Always 0xFF.
 */
u_char Tlv320DacReadReg(u_int reg)
{
    return 0xFF;
}

/*!
 * \brief Write value to specified DAC register.
 *
 * Communicates with the DAC chip via TWI.
 *
 * \param reg DAC register address.
 * \param val Value to store in specified register.
 */
void Tlv320DacWriteReg(u_int reg, u_int val)
{
    u_char txdata[2];

    txdata[0] = (u_char)(reg << 1) | (u_char)(val >> 8);
    txdata[1] = (u_char)val;
    TwMasterTransact(TWI_SLA_DAC, txdata, 2, NULL, 0, 0);
}

/*!
 * \brief Enable I2S interface with a given rate.
 *
 * \param rate PCM sample rate in Hertz.
 *
 * \return Always 0.
 */
static int Tlv320I2sEnable(u_int rate)
{
    /* Enable SSC clock. */
    outr(PMC_PCER, _BV(SSC_ID));

    /* Select SSC peripheral functions. */
    outr(DACI2S_ASR, DACI2S_PINS_A);
    outr(DACI2S_BSR, DACI2S_PINS_B);

    /* Enable SSC peripheral pins. */
    outr(DACI2S_PDR, DACI2S_PINS_A | DACI2S_PINS_B);

    /* Configure 16-bit stereo I2S transmit format. */
    outr(SSC_CMR, 0);
    outr(SSC_TCMR,              /* Set transmit clock mode. */
        SSC_CKS_TK |            /* Use external clock at TK. */
        SSC_START_EDGE_RF |     /* Start transmission on any edge. */
        (1 << SSC_STTDLY_LSB)); /* Delay start by 1 cycle. */
    outr(SSC_TFMR,              /* Set transmit frame mode. */
        ((PCM_BITS - 1) << SSC_DATLEN_LSB) |   /* Transmit 16 bits. */
        SSC_MSBF);              /* Most significant bit first. */

    /* Enable transmitter in PDC mode. */
    outr(SSC_PTCR, PDC_TXTEN);
    outr(SSC_CR, SSC_TXEN);

    return 0;
}

/*!
 * \brief Disable I2S interface.
 *
 * \return Always 0.
 */
static int Tlv320I2sDisable(void)
{
    /* Disable all interrupts. */
    outr(SSC_IDR, 0xFFFFFFFF);

    /* Disable SSC interrupt. */
    NutIrqDisable(&sig_SSC);

    /* Disable SSC clock. */
    outr(PMC_PCDR, _BV(SSC_ID));

    /* Reset receiver and transmitter. */
    outr(SSC_CR, SSC_SWRST | SSC_RXDIS | SSC_TXDIS);
    outr(SSC_RCMR, 0);
    outr(SSC_RFMR, 0);
    outr(SSC_PTCR, PDC_RXTDIS);
    outr(SSC_PTCR, PDC_TXTDIS);
    outr(SSC_TNCR, 0);
    outr(SSC_TCR, 0);

    return 0;
}

/*!
 * \brief Configure the SSC for I2S mode.
 *
 * \param rate Sampling rate in Hertz.
 *
 * \return Always 0.
 */
static int Tlv320I2sInit(u_int rate)
{
    /* Register SSC interrupt handler. */
    NutRegisterIrqHandler(&sig_SSC, I2sInterrupt, 0);

    Tlv320I2sDisable();
    Tlv320I2sEnable(rate);

    /* Enable SSC interrupt. */
    NutIrqEnable(&sig_SSC);

    return 0;
}

int Tlv320DacSetRate(u_int rate)
{
    switch(rate) {
    case 8000:
#ifdef AT91SAM7X_EK
        Tlv320DacWriteReg(DAC_SRATE, (3 << DAC_SRATE_SR_LSB));
#else
        Tlv320DacWriteReg(DAC_SRATE, (3 << DAC_SRATE_SR_LSB) | DAC_SRATE_USB);
#endif
        break;
    case 8021:      
        Tlv320DacWriteReg(DAC_SRATE, (11 << DAC_SRATE_SR_LSB) | DAC_SRATE_BOSR | DAC_SRATE_USB);
        break;
    case 44100:     
        Tlv320DacWriteReg(DAC_SRATE, (8 << DAC_SRATE_SR_LSB) | DAC_SRATE_BOSR | DAC_SRATE_USB);
        break;
    case 48000:     
        Tlv320DacWriteReg(DAC_SRATE, (0 << DAC_SRATE_SR_LSB) | DAC_SRATE_USB);
        break;
    case 88200:     
        Tlv320DacWriteReg(DAC_SRATE, (15 << DAC_SRATE_SR_LSB) | DAC_SRATE_BOSR | DAC_SRATE_USB);
        break;
    case 96000:     
        Tlv320DacWriteReg(DAC_SRATE, (7 << DAC_SRATE_SR_LSB) | DAC_SRATE_USB);
        break;
    default:        
        return -1;
    }
    return 0;
}

/*!
 * \brief Initialize TLV320AIC23B DAC interface.
 *
 * \param rate Sample rate.
 *
 * \return 0 on success, -1 otherwise.
 */
int Tlv320DacInit(u_int rate)
{
    /* Initialize TWI. */
    TwInit(0);

    Tlv320DacWriteReg(DAC_RESET, 0);
    /* Power down line in. */
    Tlv320DacWriteReg(DAC_PWRDN, DAC_PWRDN_LINE);
    Tlv320DacWriteReg(DAC_PWRDN, 0);
    /* Set sampling rate. */
    if (Tlv320DacSetRate(rate)) {
        Tlv320DacWriteReg(DAC_RESET, 0);
        return -1;
    }
    Tlv320DacWriteReg(DAC_ANA_PATH, DAC_ANA_PATH_DAC | DAC_ANA_PATH_INSEL | DAC_ANA_PATH_MICB);
    Tlv320DacWriteReg(DAC_DIG_PATH, 0);
    /* I2S master. */
    Tlv320DacWriteReg(DAC_DAI_FMT, DAC_DAI_FMT_MS | DAC_DAI_FMT_FOR_I2S);
    Tlv320DacWriteReg(DAC_DI_ACT, DAC_DI_ACT_ACT);

    Tlv320DacWriteReg(DAC_LHP_VOL, DAC_LHP_VOL_LRS | (0x60 << DAC_LHP_VOL_LHV_LSB));

    /* Initialize I2S. */
    return Tlv320I2sInit(rate);
}

/*!
 * \brief Start transmitting audio samples.
 *
 * \return Always 0.
 */
static int Tlv320DacStart(void)
{
    NutIrqDisable(&sig_SSC);
    I2sPdcFill();
    outr(SSC_IER, SSC_ENDTX);
    outr(SSC_CR, SSC_TXEN);
    NutIrqEnable(&sig_SSC);

    return 0;
}

/*!
 * \brief Wait until all buffered samples have been transmitted.
 *
 * \return Always 0.
 */
int Tlv320DacFlush(void)
{
    int rc = 0;

    while (bwr_idx != brd_idx) {
        Tlv320DacStart();
        if ((rc = NutEventWait(&i2s_que, 500)) != 0) {
            break;
        }
    }
    return rc;
}

/*!
 * \brief Add audio samples to the TLV320AIC23B transmit queue.
 *
 * \param buf Points to PCM data.
 * \param len Number of samples.
 *
 * \return 0 on success or -1 if out of memory.
 */
int Tlv320DacWrite(void *buf, int len)
{
    u_int idx;

    /* Move to the next buffer to write to. */
    idx = bwr_idx + 1;
    if (idx >= SAMPLE_BUFFERS) {
        idx = 0;
    }

    /* If all buffers are filled, wait for an event posted by the
       interrupt routine. */
    while (idx == brd_idx) {
        if (NutEventWait(&i2s_que, 100)) {
            Tlv320DacStart();
        }
    }

    /*
     * Check, if the current buffer size is too small or not allocated.
     */
    if (pcm_bufq[idx].wbf_siz < len) {
        if (pcm_bufq[idx].wbf_siz) {
            free(pcm_bufq[idx].wbf_dat);
            pcm_bufq[idx].wbf_siz = 0;
        }
        pcm_bufq[idx].wbf_dat = malloc(len * 2);
        if (pcm_bufq[idx].wbf_dat == NULL) {
            /* Out of memory. */
            return -1;
        }
        pcm_bufq[idx].wbf_siz = len;
    }

    /*
     * At this point we got an available buffer with sufficient size.
     * Move the data to it, set the number of valid bytes and update
     * the write (producer) index.
     */
    memcpy(pcm_bufq[idx].wbf_dat, buf, len * 2);
    pcm_bufq[idx].wbf_len = len;
    bwr_idx = idx;

    return 0;
}

/*!
 * \brief Set volume.
 *
 * Sets the master playback gain. Range is +6..-73 dB.
 *
 * \param left  Left channel gain in dB.
 * \param right Right channel gain in dB.
 *
 * \return 0 on success, -1 otherwise.
 */
int Tlv320DacSetVolume(int left, int right)
{
    /* Cut to limits. */
    if (left > DAC_MAX_VOLUME) {
        left = DAC_MAX_VOLUME;
    }
    else if (left < DAC_MIN_VOLUME) {
        left = DAC_MIN_VOLUME;
    }
    if (right > DAC_MAX_VOLUME) {
        right = DAC_MAX_VOLUME;
    }
    else if (right < DAC_MIN_VOLUME) {
        right = DAC_MIN_VOLUME;
    }
    Tlv320DacWriteReg(DAC_LHP_VOL, (u_int)(left + 121));
    Tlv320DacWriteReg(DAC_RHP_VOL, (u_int)(right + 121));

    return 0;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -