123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- /* Copyright (C) 2012 Andy Karpov <andy.karpov@gmail.com>
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- */
- #include <avr/pgmspace.h>
- #include <SPI.h>
- #include "VS1003.h"
- /****************************************************************************/
- // Audio FIFO is 8kB (2048 stereo samples)
- // SM_STREAM should be doable..
- #define VS_WRITE_COMMAND 0b00000010 // VS1003 SCI Write
- #define VS_READ_COMMAND 0b00000011 // VS1003 SCI Read
- const uint8_t vs1003_chunk_size = 32;
- // SCI Registers
- const uint8_t SCI_MODE = 0x0;
- const uint8_t SCI_STATUS = 0x1;
- const uint8_t SCI_BASS = 0x2;
- const uint8_t SCI_CLOCKF = 0x3;
- const uint8_t SCI_DECODE_TIME = 0x4;
- const uint8_t SCI_AUDATA = 0x5;
- const uint8_t SCI_WRAM = 0x6;
- const uint8_t SCI_WRAMADDR = 0x7;
- const uint8_t SCI_HDAT0 = 0x8;
- const uint8_t SCI_HDAT1 = 0x9;
- const uint8_t SCI_AIADDR = 0xa;
- const uint8_t SCI_VOL = 0xb;
- const uint8_t SCI_AICTRL0 = 0xc;
- const uint8_t SCI_AICTRL1 = 0xd;
- const uint8_t SCI_AICTRL2 = 0xe;
- const uint8_t SCI_AICTRL3 = 0xf;
- const uint8_t SCI_num_registers = 0xf;
- // SCI_MODE bits
- const uint8_t SM_DIFF = 0;
- const uint8_t SM_LAYER12 = 1;
- const uint8_t SM_RESET = 2;
- const uint8_t SM_OUTOFWAV = 3;
- const uint8_t SM_EARSPEAKER_LO = 4;
- const uint8_t SM_TESTS = 5;
- const uint8_t SM_STREAM = 6;
- const uint8_t SM_EARSPEAKER_HI = 7;
- const uint8_t SM_DACT = 8;
- const uint8_t SM_SDIORD = 9;
- const uint8_t SM_SDISHARE = 10;
- const uint8_t SM_SDINEW = 11;
- const uint8_t SM_ADPCM = 12;
- const uint8_t SM_ADPCM_HP = 13;
- const uint8_t SM_LINE_IN = 14;
- // Support RIFF WAv formats
- const uint8_t FMT_PCM = 0x01; // 8 & 16 bit, <= 48kHz
- const uint8_t FMT_IMA_ADPCM = 0x11; // <= 48kHz
- const uint8_t FMT_MPEGL3 = 0x55; // ..
- /****************************************************************************/
- struct spi_saver_t
- {
- uint8_t saved_SPCR;
- uint8_t saved_SPSR;
- spi_saver_t(void) { saved_SPCR = SPCR; saved_SPSR = SPSR; }
- ~spi_saver_t() { SPCR = saved_SPCR; SPSR = saved_SPSR; }
- };
- /****************************************************************************/
- uint16_t VS1003::read_register(uint8_t _reg) const
- {
- uint16_t result;
- control_mode_on();
- delayMicroseconds(1); // tXCSS
- SPI.transfer(VS_READ_COMMAND); // Read operation
- SPI.transfer(_reg); // Which register
- result = SPI.transfer(0xff) << 8; // read high byte
- result |= SPI.transfer(0xff); // read low byte
- delayMicroseconds(1); // tXCSH
- await_data_request();
- control_mode_off();
- return result;
- }
- /****************************************************************************/
- void VS1003::write_register(uint8_t _reg,uint16_t _value) const
- {
- control_mode_on();
- delayMicroseconds(1); // tXCSS
- SPI.transfer(VS_WRITE_COMMAND); // Write operation
- SPI.transfer(_reg); // Which register
- SPI.transfer(_value >> 8); // Send hi byte
- SPI.transfer(_value & 0xff); // Send lo byte
- delayMicroseconds(1); // tXCSH
- await_data_request();
- control_mode_off();
- }
- /****************************************************************************/
- void VS1003::sdi_send_buffer(const uint8_t* data, size_t len)
- {
- data_mode_on();
- while (len)
- {
- await_data_request();
- delayMicroseconds(3);
- size_t chunk_length = min(len,vs1003_chunk_size);
- len -= chunk_length;
- while (chunk_length--) SPI.transfer(*data++);
- }
- data_mode_off();
- }
- /****************************************************************************/
- void VS1003::sdi_send_zeroes(size_t len)
- {
- data_mode_on();
- while (len)
- {
- await_data_request();
- size_t chunk_length = min(len,vs1003_chunk_size);
- len -= chunk_length;
- while (chunk_length--) SPI.transfer(0);
- }
- data_mode_off();
- }
- /****************************************************************************/
- VS1003::VS1003(uint8_t _cs_pin,
- uint8_t _dcs_pin,
- uint8_t _dreq_pin,
- uint8_t _reset_pin):
- cs_pin(_cs_pin),
- dcs_pin(_dcs_pin),
- dreq_pin(_dreq_pin),
- reset_pin(_reset_pin)
- {
- }
- /****************************************************************************/
- void VS1003::begin(void)
- {
- spi_saver_t spi_saver;
- pinMode(reset_pin,OUTPUT); // Keep the chip in reset until we are ready
- digitalWrite(reset_pin,LOW);
- pinMode(cs_pin,OUTPUT); // The SCI and SDI will start deselected
- digitalWrite(cs_pin,HIGH);
- pinMode(dcs_pin,OUTPUT);
- digitalWrite(dcs_pin,HIGH);
- pinMode(dreq_pin,INPUT); // DREQ is an input
- delay(1);
- SPI.setClockDivider(SPI_CLOCK_DIV64); // init SPI slow mode
- digitalWrite(reset_pin,HIGH); // release from reset
- write_register(SCI_VOL,0xffff); // Declick: Immediately switch analog off
- write_register(SCI_AUDATA,10); // & Slow sample rate for analog part startup */
- delay(100);
- write_register(SCI_VOL,0xfefe); // switch on the analog parts
- write_register(SCI_AUDATA,8000); // 8kHz mono
- write_register(SCI_VOL,0x2020); // VOL
- write_register(SCI_MODE,_BV(SM_SDINEW)|_BV(SM_RESET)); // soft reset
- delay(1);
- await_data_request();
- write_register(SCI_CLOCKF,0xB800); // Experimenting with higher clock settings
- delay(1);
- await_data_request();
- SPI.setClockDivider(SPI_CLOCK_DIV4); // Fastest available SPI clock
- save_our_spi();
- }
- /****************************************************************************/
- void VS1003::setVolume(uint8_t vol) const
- {
- uint16_t value = vol;
- value <<= 8;
- value |= vol;
- write_register(SCI_VOL,value);
- }
- /****************************************************************************/
- void VS1003::playChunk(const uint8_t* data, size_t len)
- {
- spi_saver_t spi_saver;
- set_our_spi();
- sdi_send_buffer(data,len);
- }
- /****************************************************************************/
|