/* * Copyright (C) 2003 by Pavel Chromy. All rights reserved. * Copyright (C) 2001-2006 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 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 THE * COPYRIGHT OWNER 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/ * - * * This software has been inspired by all the valuable work done by * Jesper Hansen . Many thanks for all his help. */ /*! * \file arch/avr/dev/vs1001k.c * \brief Legacy support for VS1001K. * * \verbatim * $Id: vs1001k.c 4937 2013-01-22 11:38:42Z haraldkipp $ * \endverbatim */ /* * This header file specifies the hardware port bits. You * need to change or replace it, if your hardware differs. */ #include #include #include #include #include #include #include #include #include /* NULL definition */ /*! * \addtogroup xgVs1001 */ /*@{*/ #ifndef VS_SCK_BIT /*! * \brief VS1001 serial control interface clock input bit. * * The first rising clock edge after XCS has gone low marks the first * bit to be written to the decoder. */ #define VS_SCK_BIT 0 #endif #if !defined(VS_SCK_AVRPORT) || (VS_SCK_AVRPORT == AVRPORTB) #define VS_SCK_PORT PORTB /*!< Port register of \ref VS_SCK_BIT. */ #define VS_SCK_DDR DDRB /*!< Data direction register of \ref VS_SCK_BIT. */ #elif (VS_SCK_AVRPORT == AVRPORTD) #define VS_SCK_PORT PORTD #define VS_SCK_DDR DDRD #elif (VS_SCK_AVRPORT == AVRPORTE) #define VS_SCK_PORT PORTE #define VS_SCK_DDR DDRE #elif (VS_SCK_AVRPORT == AVRPORTF) #define VS_SCK_PORT PORTF #define VS_SCK_DDR DDRF #else #warning "Bad SCK port specification" #endif #ifndef VS_SS_BIT /*! * \brief VS1001 serial data interface clock input bit. */ #define VS_SS_BIT 1 #endif #if !defined(VS_SS_AVRPORT) || (VS_SS_AVRPORT == AVRPORTB) #define VS_SS_PORT PORTB /*!< Port output register of \ref VS_SS_BIT. */ #define VS_SS_DDR DDRB /*!< Data direction register of \ref VS_SS_BIT. */ #elif (VS_SS_AVRPORT == AVRPORTD) #define VS_SS_PORT PORTD #define VS_SS_DDR DDRD #elif (VS_SS_AVRPORT == AVRPORTE) #define VS_SS_PORT PORTE #define VS_SS_DDR DDRE #elif (VS_SS_AVRPORT == AVRPORTF) #define VS_SS_PORT PORTF #define VS_SS_DDR DDRF #else #warning "Bad SS port specification" #endif #ifndef VS_SI_BIT /*! * \brief VS1001 serial control interface data input. * * The decoder samples this input on the rising edge of SCK if XCS is low. */ #define VS_SI_BIT 2 #endif #if !defined(VS_SI_AVRPORT) || (VS_SI_AVRPORT == AVRPORTB) #define VS_SI_PORT PORTB /*!< Port output register of \ref VS_SI_BIT. */ #define VS_SI_DDR DDRB /*!< Data direction register of \ref VS_SI_BIT. */ #elif (VS_SI_AVRPORT == AVRPORTD) #define VS_SI_PORT PORTD #define VS_SI_DDR DDRD #elif (VS_SI_AVRPORT == AVRPORTE) #define VS_SI_PORT PORTE #define VS_SI_DDR DDRE #elif (VS_SI_AVRPORT == AVRPORTF) #define VS_SI_PORT PORTF #define VS_SI_DDR DDRF #else #warning "Bad SI port specification" #endif #ifndef VS_SO_BIT /*! * \brief VS1001 serial control interface data output. * * If data is transfered from the decoder, bits are shifted out on the * falling SCK edge. If data is transfered to the decoder, SO is at a * high impedance state. */ #define VS_SO_BIT 3 #endif #if !defined(VS_SO_AVRPORT) || (VS_SO_AVRPORT == AVRPORTB) #define VS_SO_PIN PINB /*!< Port input register of \ref VS_SO_BIT. */ #define VS_SO_DDR DDRB /*!< Data direction register of \ref VS_SO_BIT. */ #elif (VS_SO_AVRPORT == AVRPORTD) #define VS_SO_PIN PIND #define VS_SO_DDR DDRD #elif (VS_SO_AVRPORT == AVRPORTE) #define VS_SO_PIN PINE #define VS_SO_DDR DDRE #elif (VS_SO_AVRPORT == AVRPORTF) #define VS_SO_PIN PINF #define VS_SO_DDR DDRF #else #warning "Bad SO port specification" #endif #ifndef VS_XCS_BIT /*! * \brief VS1001 active low chip select input. * * A high level forces the serial interface into standby mode, ending * the current operation. A high level also forces serial output (SO) * to high impedance state. */ #define VS_XCS_BIT 4 #endif #if !defined(VS_XCS_AVRPORT) || (VS_XCS_AVRPORT == AVRPORTB) #define VS_XCS_PORT PORTB /*!< Port output register of \ref VS_XCS_BIT. */ #define VS_XCS_DDR DDRB /*!< Data direction register of \ref VS_XCS_BIT. */ #elif (VS_XCS_AVRPORT == AVRPORTD) #define VS_XCS_PORT PORTD #define VS_XCS_DDR DDRD #elif (VS_XCS_AVRPORT == AVRPORTE) #define VS_XCS_PORT PORTE #define VS_XCS_DDR DDRE #elif (VS_XCS_AVRPORT == AVRPORTF) #define VS_XCS_PORT PORTF #define VS_XCS_DDR DDRF #else #warning "Bad XCS port specification" #endif #ifndef VS_BSYNC_BIT /*! * \brief VS1001 serial data interface bit sync. * * The first DCLK sampling edge, during which BSYNC is high, marks the * first bit of a data byte. */ #define VS_BSYNC_BIT 5 #endif #if !defined(VS_BSYNC_AVRPORT) || (VS_BSYNC_AVRPORT == AVRPORTB) #define VS_BSYNC_PORT PORTB /*!< Port output register of \ref VS_BSYNC_BIT. */ #define VS_BSYNC_DDR DDRB /*!< Data direction register of \ref VS_BSYNC_BIT. */ #elif (VS_BSYNC_AVRPORT == AVRPORTD) #define VS_BSYNC_PORT PORTD #define VS_BSYNC_DDR DDRD #elif (VS_BSYNC_AVRPORT == AVRPORTE) #define VS_BSYNC_PORT PORTE #define VS_BSYNC_DDR DDRE #elif (VS_BSYNC_AVRPORT == AVRPORTF) #define VS_BSYNC_PORT PORTF #define VS_BSYNC_DDR DDRF #else #warning "Bad BSYNC port specification" #endif #ifndef VS_RESET_BIT /*! * \brief VS1001 hardware reset input. */ #define VS_RESET_BIT 7 #endif #if !defined(VS_RESET_AVRPORT) || (VS_RESET_AVRPORT == AVRPORTB) #define VS_RESET_PORT PORTB /*!< Port output register of \ref VS_RESET_BIT. */ #define VS_RESET_DDR DDRB /*!< Data direction register of \ref VS_RESET_BIT. */ #elif (VS_RESET_AVRPORT == AVRPORTD) #define VS_RESET_PORT PORTD #define VS_RESET_DDR DDRD #elif (VS_RESET_AVRPORT == AVRPORTE) #define VS_RESET_PORT PORTE #define VS_RESET_DDR DDRE #elif (VS_RESET_AVRPORT == AVRPORTF) #define VS_RESET_PORT PORTF #define VS_RESET_DDR DDRF #else #warning "Bad RESET port specification" #endif #ifndef VS_SIGNAL_IRQ /*! * \brief VS1001 data request interrupt. */ #define VS_SIGNAL sig_INTERRUPT6 #define VS_DREQ_BIT 6 #define VS_DREQ_PORT PORTE /*!< Port output register of \ref VS_DREQ_BIT. */ #define VS_DREQ_PIN PINE /*!< Port input register of \ref VS_DREQ_BIT. */ #define VS_DREQ_DDR DDRE /*!< Data direction register of \ref VS_DREQ_BIT. */ #elif (VS_SIGNAL_IRQ == INT0) #define VS_SIGNAL sig_INTERRUPT0 #define VS_DREQ_BIT 0 #define VS_DREQ_PORT PORTD #define VS_DREQ_PIN PIND #define VS_DREQ_DDR DDRD #elif (VS_SIGNAL_IRQ == INT1) #define VS_SIGNAL sig_INTERRUPT1 #define VS_DREQ_BIT 1 #define VS_DREQ_PORT PORTD #define VS_DREQ_PIN PIND #define VS_DREQ_DDR DDRD #elif (VS_SIGNAL_IRQ == INT2) #define VS_SIGNAL sig_INTERRUPT2 #define VS_DREQ_BIT 2 #define VS_DREQ_PORT PORTD #define VS_DREQ_PIN PIND #define VS_DREQ_DDR DDRD #elif (VS_SIGNAL_IRQ == INT3) #define VS_SIGNAL sig_INTERRUPT3 #define VS_DREQ_BIT 3 #define VS_DREQ_PORT PORTD #define VS_DREQ_PIN PIND #define VS_DREQ_DDR DDRD #elif (VS_SIGNAL_IRQ == INT4) #define VS_SIGNAL sig_INTERRUPT4 #define VS_DREQ_BIT 4 #define VS_DREQ_PORT PORTE #define VS_DREQ_PIN PINE #define VS_DREQ_DDR DDRE #elif (VS_SIGNAL_IRQ == INT5) #define VS_SIGNAL sig_INTERRUPT5 #define VS_DREQ_BIT 5 #define VS_DREQ_PORT PORTE #define VS_DREQ_PIN PINE #define VS_DREQ_DDR DDRE #elif (VS_SIGNAL_IRQ == INT7) #define VS_SIGNAL sig_INTERRUPT7 #define VS_DREQ_BIT 7 #define VS_DREQ_PORT PORTE #define VS_DREQ_PIN PINE #define VS_DREQ_DDR DDRE #else #warning "Bad interrupt specification" #endif static volatile uint8_t vs_status = VS_STATUS_STOPPED; static volatile uint16_t vs_flush; /* * \brief Write a byte to the VS1001 data interface. * * The caller is responsible for checking the DREQ line. Also make sure, * that decoder interrupts are disabled. * * \param b Byte to be shifted to the decoder's data interface. */ static INLINE void VsSdiPutByte(uint8_t b) { #ifdef VS_NOSPI uint8_t mask = 0x80; sbi(VS_BSYNC_PORT, VS_BSYNC_BIT); while (mask) { if (b & mask) sbi(VS_SI_PORT, VS_SI_BIT); else cbi(VS_SI_PORT, VS_SI_BIT); sbi(VS_SS_PORT, VS_SS_BIT); mask >>= 1; cbi(VS_SS_PORT, VS_SS_BIT); cbi(VS_BSYNC_PORT, VS_BSYNC_BIT); } #else /* Wait for previous SPI transfer to finish. */ loop_until_bit_is_set(SPSR, SPIF); sbi(VS_BSYNC_PORT, VS_BSYNC_BIT); outb(SPDR, b); _NOP(); _NOP(); _NOP(); _NOP(); cbi(VS_BSYNC_PORT, VS_BSYNC_BIT); #endif } /*! * \brief Write a specified number of bytes to the VS1001 data interface. * * This function will check the DREQ line. Decoder interrupts must have * been disabled before calling this function. */ static int VsSdiWrite(const uint8_t * data, uint16_t len) { uint16_t try = 5000; while (len--) { while (try-- && bit_is_clear(VS_DREQ_PIN, VS_DREQ_BIT)); VsSdiPutByte(*data); data++; } return try ? 0 : -1; } /*! * \brief Write a specified number of bytes from program space to the * VS1001 data interface. * * This function is similar to VsSdiWrite() except that the data is * located in program space. */ static int VsSdiWrite_P(PGM_P data, uint16_t len) { uint16_t try = 5000; while (len--) { while (try-- && bit_is_clear(VS_DREQ_PIN, VS_DREQ_BIT)); VsSdiPutByte(PRG_RDB(data)); data++; } return try ? 0 : -1; } /*! * \brief Write a byte to the serial control interface. * * Decoder interrupts must have been disabled and the decoder chip * select must have been enabled before calling this function. */ static INLINE void VsSciPutByte(uint8_t data) { uint8_t mask = 0x80; /* * Loop until all 8 bits are processed. */ while (mask) { /* Set data line. */ if (data & mask) sbi(VS_SI_PORT, VS_SI_BIT); else cbi(VS_SI_PORT, VS_SI_BIT); /* Toggle clock and shift mask. */ sbi(VS_SCK_PORT, VS_SCK_BIT); mask >>= 1; cbi(VS_SCK_PORT, VS_SCK_BIT); } } /*! * \brief Read a byte from the serial control interface. * * Decoder interrupts must have been disabled and the decoder chip * select must have been enabled before calling this function. */ static INLINE uint8_t VsSciGetByte(void) { uint8_t mask = 0x80; uint8_t data = 0; /* * Loop until all 8 bits are processed. */ while (mask) { /* Toggle clock and get the data. */ sbi(VS_SCK_PORT, VS_SCK_BIT); if (bit_is_set(VS_SO_PIN, VS_SO_BIT)) data |= mask; mask >>= 1; cbi(VS_SCK_PORT, VS_SCK_BIT); } return data; } /*! * \brief Write to a decoder register. * * Decoder interrupts must have been disabled before calling this function. */ static void VsRegWrite(uint8_t reg, uint16_t data) { /* Select chip. */ cbi(VS_XCS_PORT, VS_XCS_BIT); #ifndef VS_NOSPI /* Disable SPI */ cbi(SPCR, SPE); #endif VsSciPutByte(VS_OPCODE_WRITE); VsSciPutByte(reg); VsSciPutByte((uint8_t) (data >> 8)); VsSciPutByte((uint8_t) data); #ifndef VS_NOSPI /* Re-enable SPI. Hint given by Jesper Hansen. */ outb(SPCR, BV(MSTR) | BV(SPE)); outb(SPSR, inb(SPSR)); #endif /* Deselect chip. */ sbi(VS_XCS_PORT, VS_XCS_BIT); } /* * \brief Read from a register. * * Decoder interrupts must have been disabled before calling this function. * * \return Register contents. */ static uint16_t VsRegRead(uint8_t reg) { uint16_t data; /* Disable interrupts and select chip. */ cbi(VS_XCS_PORT, VS_XCS_BIT); #ifndef VS_NOSPI /* Disable SPI. */ cbi(SPCR, SPE); #endif VsSciPutByte(VS_OPCODE_READ); VsSciPutByte(reg); data = (uint16_t)VsSciGetByte() << 8; data |= VsSciGetByte(); #ifndef VS_NOSPI /* Re-enable SPI. Changed due to a hint by Jesper. */ outb(SPCR, BV(MSTR) | BV(SPE)); outb(SPSR, inb(SPSR)); #endif /* Deselect chip and enable interrupts. */ sbi(VS_XCS_PORT, VS_XCS_BIT); return data; } /*! * \brief Enable or disable player interrupts. * * This routine is typically used by applications when dealing with * unprotected buffers. * * \param enable Disables interrupts when zero. Otherwise interrupts * are enabled. * * \return Zero if interrupts were disabled before this call. */ uint8_t VsPlayerInterrupts(uint8_t enable) { static uint8_t is_enabled = 0; uint8_t rc; rc = is_enabled; if(enable) { NutIrqEnable(&VS_SIGNAL); } else { NutIrqDisable(&VS_SIGNAL); } is_enabled = enable; return rc; } /* * \brief Feed the decoder with data. * * This function serves two purposes: * - It is called by VsPlayerKick() to initially fill the decoder buffer. * - It is used as an interrupt handler for the decoder. */ static void VsPlayerFeed(void *arg) { uint8_t ief; uint8_t j = 32; size_t total = 0; if (bit_is_clear(VS_DREQ_PIN, VS_DREQ_BIT)) { return; } /* * We are hanging around here some time and may block other important * interrupts. Disable decoder interrupts and enable global interrupts. */ ief = VsPlayerInterrupts(0); sei(); /* * Feed the decoder until its buffer is full or we ran out of data. */ if (vs_status == VS_STATUS_RUNNING) { char *bp = 0; size_t consumed = 0; size_t available = 0; do { if(consumed >= available) { /* Commit previously consumed bytes. */ if(consumed) { NutSegBufReadCommit(consumed); consumed = 0; } /* All bytes consumed, request new. */ bp = NutSegBufReadRequest(&available); if(available == 0) { /* End of stream. */ vs_status = VS_STATUS_EOF; break; } } /* We have some data in the buffer, feed it. */ VsSdiPutByte(*bp); bp++; consumed++; total++; if (total > 4096) { vs_status = VS_STATUS_EOF; break; } /* Allow 32 bytes to be sent as long as DREQ is set, This includes the one in progress. */ if (bit_is_set(VS_DREQ_PIN, VS_DREQ_BIT)) j = 32; } while(j--); /* Finally re-enable the producer buffer. */ NutSegBufReadLast(consumed); } /* * Flush the internal VS buffer. */ if(vs_status != VS_STATUS_RUNNING && vs_flush) { do { VsSdiPutByte(0); if (--vs_flush == 0) { /* Decoder internal buffer is flushed. */ vs_status = VS_STATUS_EMPTY; break; } /* Allow 32 bytes to be sent as long as DREQ is set, This includes the one in progress. */ if (bit_is_set(VS_DREQ_PIN, VS_DREQ_BIT)) j = 32; } while(j--); } VsPlayerInterrupts(ief); } /*! * \brief Start playback. * * This routine will send the first MP3 data bytes to the * decoder, until it is completely filled. The data buffer * should have been filled before calling this routine. * * Decoder interrupts will be enabled. * * \return 0 on success, -1 otherwise. */ int VsPlayerKick(void) { /* * Start feeding the decoder with data. */ VsPlayerInterrupts(0); vs_status = VS_STATUS_RUNNING; VsPlayerFeed(NULL); VsPlayerInterrupts(1); return 0; } /*! * \brief Stops the playback. * * This routine will stops the MP3 playback, VsPlayerKick() may be used * to resume the playback. * * \return 0 on success, -1 otherwise. */ int VsPlayerStop(void) { uint8_t ief; ief = VsPlayerInterrupts(0); /* Check whether we need to stop at all to not overwrite other than running status */ if (vs_status == VS_STATUS_RUNNING) { vs_status = VS_STATUS_STOPPED; } VsPlayerInterrupts(ief); return 0; } /*! * \brief Sets up decoder internal buffer flushing. * * This routine will set up internal VS buffer flushing, * unless the buffer is already empty and starts the playback * if necessary. The internal VS buffer is flushed in VsPlayerFeed() * at the end of the stream. * * Decoder interrupts will be enabled. * * \return 0 on success, -1 otherwise. */ int VsPlayerFlush(void) { VsPlayerInterrupts(0); /* Set up fluhing unless both buffers are empty. */ if (vs_status != VS_STATUS_EMPTY || NutSegBufUsed()) { if (vs_flush == 0) vs_flush = VS_FLUSH_BYTES; /* start the playback if necessary */ if (vs_status != VS_STATUS_RUNNING) VsPlayerKick(); } VsPlayerInterrupts(1); return 0; } /*! * \brief Initialize the VS1001 hardware interface. * * \note The interrupt handler for this device uses a significant amount * of stack space, which may require to increase thread stacks of * all running threads. Furthermore, it requires quite some time * to execute and may degrade overall system performance. * * \return 0 on success, -1 otherwise. */ int VsPlayerInit(void) { /* Disable decoder interrupts. */ VsPlayerInterrupts(0); /* Keep decoder in reset state. */ cbi(VS_RESET_PORT, VS_RESET_BIT); sbi(VS_RESET_DDR, VS_RESET_BIT); /* Set BSYNC output low. */ cbi(VS_BSYNC_PORT, VS_BSYNC_BIT); sbi(VS_BSYNC_DDR, VS_BSYNC_BIT); /* Set MP3 chip select output low. */ sbi(VS_XCS_PORT, VS_XCS_BIT); sbi(VS_XCS_DDR, VS_XCS_BIT); /* Set DREQ input with pullup. */ sbi(VS_DREQ_PORT, VS_DREQ_BIT); cbi(VS_DREQ_DDR, VS_DREQ_BIT); /* Init SPI Port. */ sbi(VS_SI_DDR, VS_SI_BIT); sbi(VS_SS_DDR, VS_SS_BIT); cbi(VS_SO_DDR, VS_SO_BIT); /* Set SCK output low. */ cbi(VS_SCK_PORT, VS_SCK_BIT); sbi(VS_SCK_DDR, VS_SCK_BIT); #ifndef VS_NOSPI { uint8_t dummy; /* Required by some compilers. */ /* * Init SPI mode to no interrupts, enabled, MSB first, master mode, * rising clock and fosc/4 clock speed. Send an initial zero byte to * make sure SPIF is set. Note, that the decoder reset line is still * active. */ outb(SPCR, BV(MSTR) | BV(SPE)); dummy = inb(SPSR); outb(SPDR, 0); } #endif /* Register the interrupt routine */ if (NutRegisterIrqHandler(&VS_SIGNAL, VsPlayerFeed, NULL)) { return -1; } /* Rising edge will generate interrupts. */ NutIrqSetMode(&VS_SIGNAL, NUT_IRQMODE_RISINGEDGE); /* Release decoder reset line. */ sbi(VS_RESET_PORT, VS_RESET_BIT); NutDelay(4); /* Force frequency change (see datasheet). */ VsRegWrite(VS_CLOCKF_REG, 0x9800); VsRegWrite(VS_INT_FCTLH_REG, 0x8008); NutDelay(200); /* Clear any spurious interrupt. */ outb(EIFR, BV(VS_DREQ_BIT)); return 0; } /*! * \brief Software reset the decoder. * * This function is typically called after VsPlayerInit() and at the end * of each track. * * \param mode Any of the following flags may be or'ed * - VS_SM_DIFF Left channel inverted. * - VS_SM_FFWD Fast forward. * - VS_SM_RESET Force hardware reset. * - VS_SM_PDOWN Switch to power down mode. * - VS_SM_BASS Bass/treble enhancer. * * \return 0 on success, -1 otherwise. */ int VsPlayerReset(uint16_t mode) { /* Disable decoder interrupts and feeding. */ VsPlayerInterrupts(0); vs_status = VS_STATUS_STOPPED; /* Software reset, set modes of decoder. */ VsRegWrite(VS_MODE_REG, VS_SM_RESET | mode); NutDelay(2); /* * Check for correct reset. */ if ((mode & VS_SM_RESET) != 0 || bit_is_clear(VS_DREQ_PIN, VS_DREQ_BIT)) { /* If not succeeded, give it one more chance and try hw reset, HW reset may also be forced by VS_SM_RESET mode bit. */ cbi(VS_RESET_PORT, VS_RESET_BIT); _NOP(); sbi(VS_RESET_PORT, VS_RESET_BIT); NutDelay(4); /* Set the requested modes. */ VsRegWrite(VS_MODE_REG, VS_SM_RESET | mode); NutDelay(2); if (bit_is_clear(VS_DREQ_PIN, VS_DREQ_BIT)) return -1; } /* Force frequency change (see datasheet). */ VsRegWrite(VS_CLOCKF_REG, 0x9800); VsRegWrite(VS_INT_FCTLH_REG, 0x8008); NutDelay(2); /* Clear any spurious interrupts. */ outb(EIFR, BV(VS_DREQ_BIT)); return 0; } /*! * \brief Set mode register of the decoder. * * \param mode Any of the following flags may be or'ed * - VS_SM_DIFF Left channel inverted. * - VS_SM_FFWD Fast forward. * - VS_SM_RESET Software reset. * - VS_SM_PDOWN Switch to power down mode. * - VS_SM_BASS Bass/treble enhancer. * * \return 0 on success, -1 otherwise. */ int VsPlayerSetMode(uint16_t mode) { uint8_t ief; ief = VsPlayerInterrupts(0); VsRegWrite(VS_MODE_REG, mode); VsPlayerInterrupts(ief); return 0; } /*! * \brief Returns play time since last reset. * * \return Play time since reset in seconds */ uint16_t VsPlayTime(void) { uint16_t rc; uint8_t ief; ief = VsPlayerInterrupts(0); rc = VsRegRead(VS_DECODE_TIME_REG); VsPlayerInterrupts(ief); return rc; } /*! * \brief Returns status of the player. * * \return Any of the following value: * - VS_STATUS_STOPPED Player is ready to be started by VsPlayerKick(). * - VS_STATUS_RUNNING Player is running. * - VS_STATUS_EOF Player has reached the end of a stream after VsPlayerFlush() has been called. * - VS_STATUS_EMPTY Player runs out of data. VsPlayerKick() will restart it. */ uint8_t VsGetStatus(void) { return vs_status; } #ifdef __GNUC__ /*! * \brief Query MP3 stream header information. * * \param vshi Pointer to VS_HEADERINFO structure. * * \return 0 on success, -1 otherwise. */ int VsGetHeaderInfo(VS_HEADERINFO * vshi) { uint16_t *usp = (uint16_t *) vshi; uint8_t ief; ief = VsPlayerInterrupts(0); *usp = VsRegRead(VS_HDAT1_REG); *++usp = VsRegRead(VS_HDAT0_REG); VsPlayerInterrupts(ief); return 0; } #endif /*! * \brief Initialize decoder memory test and return result. * * \return Memory test result. * - Bit 0: Good X ROM * - Bit 1: Good Y ROM (high) * - Bit 2: Good Y ROM (low) * - Bit 3: Good Y RAM * - Bit 4: Good X RAM * - Bit 5: Good Instruction RAM (high) * - Bit 6: Good Instruction RAM (low) */ uint16_t VsMemoryTest(void) { uint16_t rc; uint8_t ief; static const char mtcmd[] PROGMEM = { 0x4D, 0xEA, 0x6D, 0x54, 0x00, 0x00, 0x00, 0x00 }; ief = VsPlayerInterrupts(0); VsSdiWrite_P(mtcmd, sizeof(mtcmd)); NutDelay(40); rc = VsRegRead(VS_HDAT0_REG); VsPlayerInterrupts(ief); return rc; } /*! * \brief Set volume. * * \param left Left channel volume. * \param right Right channel volume. * * \return 0 on success, -1 otherwise. */ int VsSetVolume(uint8_t left, uint8_t right) { uint8_t ief; ief = VsPlayerInterrupts(0); VsRegWrite(VS_VOL_REG, (((uint16_t) left) << 8) | (uint16_t) right); VsPlayerInterrupts(ief); return 0; } /*! * \brief Sine wave beep. * * \param fsin Frequency. * \param ms Duration. * * \return 0 on success, -1 otherwise. */ int VsBeep(uint8_t fsin, uint8_t ms) { uint8_t ief; static const char on[] PROGMEM = { 0x53, 0xEF, 0x6E }; static const char off[] PROGMEM = { 0x45, 0x78, 0x69, 0x74 }; static const char end[] PROGMEM = { 0x00, 0x00, 0x00, 0x00 }; /* Disable decoder interrupts. */ ief = VsPlayerInterrupts(0); fsin = 56 + (fsin & 7) * 9; VsSdiWrite_P(on, sizeof(on)); VsSdiWrite(&fsin, 1); VsSdiWrite_P(end, sizeof(end)); NutDelay(ms); VsSdiWrite_P(off, sizeof(off)); VsSdiWrite_P(end, sizeof(end)); /* Enable decoder interrupts. */ VsPlayerInterrupts(ief); return 0; } /*@}*/