Use the TLV320 with in-built I2S object to stream audio data from an SD Card and send it to the TLV320 CODEC for audio playback
Dependencies: I2SSlave mbed TLV320
Revision 0:3d6892f6384f, committed 2011-08-05
- Comitter:
- d_worrall
- Date:
- Fri Aug 05 10:07:47 2011 +0000
- Child:
- 1:9ea0cc2fa567
- Commit message:
- version 2.0
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FATFileSystem.lib Fri Aug 05 10:07:47 2011 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_unsupported/code/fatfilesystem/ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/I2SSlave/I2SSlave.cpp Fri Aug 05 10:07:47 2011 +0000 @@ -0,0 +1,306 @@ +/****************************************************** + * I2SSlave.cpp: I2S port abstraction library cpp file for NXP LPC1768 + * + * Written by: + * mbed Team - 15th July 2011 + * Daniel Worrall + * + * History + * version 1.31 +******************************************************/ +#include "I2SSlave.h" +/*Global Functions*/ +FunctionPointer akjnh3489v8ncv; + +extern "C" void I2S_IRQHandler(void){ //this is a very special function so can remain outside + akjnh3489v8ncv.call(); +} +/****************************************************** + * Function name: I2SSlave + * + * Description: class constructor + * + * Parameters: PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws + * Returns: none +******************************************************/ +//Constructor +I2SSlave::I2SSlave(PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws){ + storePins_(tx_sda, tx_ws, clk, rx_sda, rx_ws); + format(16, STEREO); //this also invokes initialize so no need to call it twice +} +//Public functions +/****************************************************** + * Function name: format() + * + * Description: sets the bit length for writing and stereo or mono mode + * + * Parameters: int bit, bool mode + * Returns: none +******************************************************/ +void I2SSlave::format(int bit, bool mode){ + bit_ = bit; + mode_ = mode; + initialize_(tx_sda_, tx_ws_, clk_, rx_sda_, rx_ws_); +} +/****************************************************** + * Function name: write() + * + * Description: load buffer to write to I2S port + * + * Parameters: long *buffer + * Returns: none +******************************************************/ +void I2SSlave::write(int* buffer, int from, int length){ + int to = from + length; + for(int i = from; i < to; ++i){ + LPC_I2S->I2STXFIFO = buffer[i]; + } +} +/****************************************************** + * Function name: start() + * + * Description: attach streamOut_ function to ticker interrupt + * + * Parameters: none + * Returns: none +******************************************************/ +void I2SSlave::start(int mode){ + switch(mode){ + case(0): + LPC_I2S->I2SIRQ |= (0 << 0); //disable receive interrupt + LPC_I2S->I2SIRQ |= (0 << 1); //disable transmit interrupt + break; + case(1): + LPC_I2S->I2SIRQ |= (0 << 0); //disable receive interrupt + LPC_I2S->I2SIRQ |= (1 << 1); //enable transmit interrupt + LPC_I2S->I2SIRQ |= (0 << 16); //set I2STXFIFO depth to 0 words + break; + case(2): + LPC_I2S->I2SIRQ |= (1 << 0); //enable receive interrupt + LPC_I2S->I2SIRQ |= (0 << 1); //disable transmit interrupt + LPC_I2S->I2SIRQ |= (4 << 8); //set I2SRXFIFO depth to 4 words + break; + case(3): + LPC_I2S->I2SIRQ |= (1 << 0); //enable receive interrupt + LPC_I2S->I2SIRQ |= (4 << 8); //set I2SRXFIFO depth to 4 words + LPC_I2S->I2SIRQ |= (1 << 1); //enable transmit interrupt + LPC_I2S->I2SIRQ |= (0 << 16); //set I2STXFIFO depth to 0 words + break; + default: + break; + } + NVIC_SetPriority(I2S_IRQn, 0); + NVIC_EnableIRQ(I2S_IRQn); //enable I2S interrupt in the NVIC +} +/****************************************************** + * Function name: stop() + * + * Description: detach streamOut_ from ticker + * + * Parameters: none + * Returns: none +******************************************************/ +void I2SSlave::stop(void){ + NVIC_DisableIRQ(I2S_IRQn); +} +/****************************************************** + * Function name: read() + * + * Description: reads FIFORX buffer into [int32_t rxBuffer[8]] + * + * Parameters: none + * Returns: none +******************************************************/ +void I2SSlave::read(void){ + rxBuffer[0] = LPC_I2S->I2SRXFIFO; + rxBuffer[1] = LPC_I2S->I2SRXFIFO; + rxBuffer[2] = LPC_I2S->I2SRXFIFO; + rxBuffer[3] = LPC_I2S->I2SRXFIFO; +} +/****************************************************** + * Function name: status_() + * + * Description: Read I2SSTATE register + * + * Parameters: none + * Returns: int +******************************************************/ +int I2SSlave::status(void){ + return LPC_I2S->I2SSTATE; +} +//Private functions +/****************************************************** + * Function name: initialize() + * + * Description: initialises I2S port + * + * Parameters: PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws + * Returns: 0 = successful initialisation + -1 = initialisation failure +******************************************************/ +int I2SSlave::initialize_(PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws){ + setPins_(tx_sda, tx_ws, clk, rx_sda, rx_ws); //designate pins + LPC_SC->PCONP |= (1 << 27); + //configure input/output register + format_(bit_, mode_); + //set mbed as SLAVE + LPC_I2S->I2SDAO |= (1 << 5); + LPC_I2S->I2SDAI |= (1 << 5); + //clock mode + setClocks_(4); + //set slave mode + modeConfig_(); + //set receiver mode + LPC_I2S->I2SRXMODE |= (1 << 1); + //slave mode + LPC_I2S->I2STXRATE = 0; + LPC_I2S->I2SRXRATE = 0; + //Start + LPC_I2S->I2SDAO |= (0 << 3); + LPC_I2S->I2SDAI |= (0 << 3); + LPC_I2S->I2SDAO |= (0 << 4); + LPC_I2S->I2SDAI |= (0 << 4); + LPC_I2S->I2SDAO |= (0 << 15); + return 0; +} +/****************************************************** + * Function name: setClocks_() + * + * Description: Set the division setting on the internal clocks + * + * Parameters: int divideBy + * Returns: nothing +******************************************************/ +void I2SSlave::setClocks_(int divideBy){ + switch(divideBy){ + case 1: + LPC_SC->PCLKSEL1 |= (1 << 22); + LPC_SC->PCLKSEL1 |= (0 << 23); + break; + case 2: + LPC_SC->PCLKSEL1 |= (0 << 22); + LPC_SC->PCLKSEL1 |= (1 << 23); + break; + case 4: + LPC_SC->PCLKSEL1 |= (0 << 22); + LPC_SC->PCLKSEL1 |= (0 << 23); + break; + case 8: + LPC_SC->PCLKSEL1 |= (1 << 22); + LPC_SC->PCLKSEL1 |= (1 << 23); + break; + default: + break; + } +} +/****************************************************** + * Function name: setPins_() + * + * Description: set external pin configuration + * + * Parameters: PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws + * Returns: none +******************************************************/ +void I2SSlave::setPins_(PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws){ + if(rx_ws == p29){ + LPC_PINCON->PINSEL0 |= (1 << 10); //set p29 as receive word select line + } else { + LPC_PINCON->PINSEL1 |= (2 << 16); //set p16 as receive word select line + } + if(rx_sda == p8){ + LPC_PINCON->PINSEL0 |= (1 << 12); //set p8 as receive serial data line + } else { + LPC_PINCON->PINSEL1 |= (2 << 18); //set p17 as receive serial data line + } + LPC_PINCON->PINSEL0 |= (1 << 14); //set p7 as transmit clock line (only one of these) + LPC_PINCON->PINSEL0 |= (1 << 16); //set p6 as word select line (only one of these) + LPC_PINCON->PINSEL0 |= (1 << 18); //set p5 as transmit serial data line (only one of these) + LPC_PINCON->PINSEL0 |= (0 << 8); //clear rx_clk +} +/****************************************************** + * Function name: format_() + * + * Description: sets the bit length for writing and stereo or mono mode + * + * Parameters: int bit, bool mode + * Returns: none +******************************************************/ +void I2SSlave::format_(int bit, bool mode){ + uint32_t bps= ((bit+1)*8)-1; + LPC_I2S->I2SDAO &= (0x00 << 6); + LPC_I2S->I2SDAO |= (bps << 6); + //set bit length + switch(bit){ + case 8: + LPC_I2S->I2SDAO &= 0xfffffffc; + break; + case 16: + LPC_I2S->I2SDAO &= (0 << 1); + LPC_I2S->I2SDAO |= (1 << 0); + break; + case 32: + LPC_I2S->I2SDAO &= (0 << 1); + LPC_I2S->I2SDAO |= (3 << 0); + break; + default: + break; + } + //set audio mode + if(mode == STEREO){ + LPC_I2S->I2SDAO |= (0 << 2); + } else { + LPC_I2S->I2SDAO |= (1 << 2); + } + //set transmitter and receiver setting to be the same + LPC_I2S->I2SDAI &= (0x00 << 6); + LPC_I2S->I2SDAI |= (bps << 6); + //set bit length + switch(bit){ + case 8: + LPC_I2S->I2SDAI &= 0xfffffffc; + break; + case 16: + LPC_I2S->I2SDAI &= (0 << 1); + LPC_I2S->I2SDAI |= (1 << 0); + break; + case 32: + LPC_I2S->I2SDAI &= (0 << 1); + LPC_I2S->I2SDAI |= (3 << 0); + break; + default: + break; + } + //set audio mode + if(mode == STEREO){ + LPC_I2S->I2SDAI |= (0 << 2); + } else { + LPC_I2S->I2SDAI |= (1 << 2); + } +} +/****************************************************** + * Function name: modeConfig_() + * + * Description: Set slave mode + * + * Parameters: none + * Returns: none +******************************************************/ +void I2SSlave::modeConfig_(void){ + LPC_I2S->I2STXMODE |= (0x0 << 0); + LPC_I2S->I2SRXMODE |= (0x0 << 0); +} +/****************************************************** + * Function name: storePins_() + * + * Description: set external pin configuration + * + * Parameters: PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws + * Returns: none +******************************************************/ +void I2SSlave::storePins_(PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws){ + tx_sda_ = tx_sda; + tx_ws_ = tx_ws; + clk_ = clk; + rx_sda_ = rx_sda; + rx_ws_ = rx_ws; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/I2SSlave/I2SSlave.h Fri Aug 05 10:07:47 2011 +0000 @@ -0,0 +1,170 @@ +/****************************************************** + * I2SSlave.h: I2S port abstraction library header file for NXP LPC1768 + * + * Written by: + * mbed Team - 15th July 2011 + * Daniel Worrall + * + * History + * version 1.31 +******************************************************/ + +#include "mbed.h" +#include "math.h" + +#ifndef __MBED_I2SSLAVE_H__ +#define __MBED_I2SSLAVE_H__ + +/** I2S class defined on the LPC1768 port + * + */ +extern FunctionPointer akjnh3489v8ncv; +class I2SSlave{ + + public: + //Constructor + /** Create an I2S object + * + * @param tx_sda Transmitter serial data line + * @param tx_ws Transmitter word select line + * @param clk Shared transmitter/receiver clock line + * @param rx_sda Receiver serial data line + * @param rx_ws Receiver word select line + */ + I2SSlave(PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws); + /** Set the data transmission format + * + * @param bit Set the number of bits per write + * @param mode Set STEREO (0) or MONO (1) mode + */ + void format(int bit, bool mode); + /** Write a buffer to the I2S port + * + * @param buffer Address of buffer to pass to I2S port + * @param from Start position in buffer to read from + * @param length Length of buffer (MUST not exceed 8 words, each 32 bits long) + * + * Note: sending 8 words to the TXFIFO will trigger an interrupt! + */ + void write(int* buffer, int from, int length); + /** Activate I2S port for data streaming + * + * @param mode Mode to enable - NONE, TRANSMIT only, RECEIVE only, BOTH + * Enables tx/rx interrupts + */ + void start(int mode); + /** Deactivate I2S port from data streaming + * + * Disable all interrupts + */ + void stop(void); + /** Load receive FIFO data into receiver buffer + * + */ + void read(void); + /** Attach a void/void function or void/void static memeber function to an interrupt generated by the I2SxxFIFOs + * + * @param function Function to attach + * + * e.g. myI2sObject.attach(&myfunction); + * OR myI2sObject.attach(&myClass::myStaticMemberFunction); + */ + void attach(void(*fptr)(void)){ + akjnh3489v8ncv.attach(fptr); + } + /** Attach a nonstatic void/void member function to an interrupt generated by the I2SxxFIFOs + * + * @param tptr Object pointer + * @param mptr Member function pointer + * + * e.g. myI2sObject.attach(&myObject, &myClass::myNonstaticMemberFunction); where myObject is an object of myClass + */ + template<typename T> + void attach(T *tptr, void(T::*mptr)(void)){ + akjnh3489v8ncv.attach(tptr, mptr); + } + /** Return contents of I2S status register + * + * @returns Content of I2SSTATE register + * + * bit0: receive/transmit interrupt active + * bit1: receive/transmit DMA request 1 + * bit2: receive/transmit DMA request 2 + * bit[11:8]: receive FIFO level + * bit[19:16]: transmit FIFO level + */ + int status(void); + + //Receiver buffer + int rxBuffer[4]; + //defines + #define STEREO 0 + #define MONO 1 + + #define I2SFIFO_EMPTY 0 + #define I2SFIFO_FULL 8 + + #define RAM_LENGTH 1024 + #define RAM_LIMIT (RAM_LENGTH - 1) + #define PTR_MAX ((RAM_LENGTH / 8) - 1) + + #define NONE 0 + #define TRANSMIT 1 + #define RECEIVE 2 + #define BOTH 3 + + private: + /** I2S intitalize function + * + * @param tx_sda Transmitter serial data line + * @param tx_ws Transmitter word select line + * @param clk Shared transmitter/receiver clock line + * @param rx_sda Receiver serial data line + * @param rx_ws Receiver word select line + * @return Returns 0 for successful initialisation, -1 for an error + */ + int initialize_(PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws); + /** Set internal clock divide by rate + * + * @param divideBy Divide by 1, 2, 4 or 8 + */ + void setClocks_(int divideBy); + /** Set up the pins on the processor itself + * + * @param tx_sda Transmitter serial data line + * @param tx_ws Transmitter word select line + * @param clk Shared transmitter/receiver clock line + * @param rx_sda Receiver serial data line + * @param rx_ws Receiver word select line + */ + void setPins_(PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws); + /** Set the data transmission format + * + * @param bit Set the number of bits per write + * @param mode Set STEREO (0) or MONO (1) mode + */ + void format_(int bit, bool mode); + /** Set slave mode + * + */ + void modeConfig_(void); + /** Store PinName values + * + * @param tx_sda Transmitter serial data line + * @param tx_ws Transmitter word select line + * @param clk Shared transmitter/receiver clock line + * @param rx_sda Receiver serial data line + * @param rx_ws Receiver word select line + */ + void storePins_(PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws); + //variables + int bit_; + bool mode_; + PinName tx_sda_; + PinName tx_ws_; + PinName clk_; + PinName rx_sda_; + PinName rx_ws_; +}; + +#endif /*__MBED_I2S_H__*/ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SDHCFileSystem/SDHCFileSystem.cpp Fri Aug 05 10:07:47 2011 +0000 @@ -0,0 +1,495 @@ +/* mbed SDFileSystem Library, for providing file access to SD cards + * Copyright (c) 2008-2010, sford + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* Introduction + * ------------ + * SD and MMC cards support a number of interfaces, but common to them all + * is one based on SPI. This is the one I'm implmenting because it means + * it is much more portable even though not so performant, and we already + * have the mbed SPI Interface! + * + * The main reference I'm using is Chapter 7, "SPI Mode" of: + * http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf + * + * SPI Startup + * ----------- + * The SD card powers up in SD mode. The SPI interface mode is selected by + * asserting CS low and sending the reset command (CMD0). The card will + * respond with a (R1) response. + * + * CMD8 is optionally sent to determine the voltage range supported, and + * indirectly determine whether it is a version 1.x SD/non-SD card or + * version 2.x. I'll just ignore this for now. + * + * ACMD41 is repeatedly issued to initialise the card, until "in idle" + * (bit 0) of the R1 response goes to '0', indicating it is initialised. + * + * You should also indicate whether the host supports High Capicity cards, + * and check whether the card is high capacity - i'll also ignore this + * + * SPI Protocol + * ------------ + * The SD SPI protocol is based on transactions made up of 8-bit words, with + * the host starting every bus transaction by asserting the CS signal low. The + * card always responds to commands, data blocks and errors. + * + * The protocol supports a CRC, but by default it is off (except for the + * first reset CMD0, where the CRC can just be pre-calculated, and CMD8) + * I'll leave the CRC off I think! + * + * Standard capacity cards have variable data block sizes, whereas High + * Capacity cards fix the size of data block to 512 bytes. I'll therefore + * just always use the Standard Capacity cards with a block size of 512 bytes. + * This is set with CMD16. + * + * You can read and write single blocks (CMD17, CMD25) or multiple blocks + * (CMD18, CMD25). For simplicity, I'll just use single block accesses. When + * the card gets a read command, it responds with a response token, and then + * a data token or an error. + * + * SPI Command Format + * ------------------ + * Commands are 6-bytes long, containing the command, 32-bit argument, and CRC. + * + * +---------------+------------+------------+-----------+----------+--------------+ + * | 01 | cmd[5:0] | arg[31:24] | arg[23:16] | arg[15:8] | arg[7:0] | crc[6:0] | 1 | + * +---------------+------------+------------+-----------+----------+--------------+ + * + * As I'm not using CRC, I can fix that byte to what is needed for CMD0 (0x95) + * + * All Application Specific commands shall be preceded with APP_CMD (CMD55). + * + * SPI Response Format + * ------------------- + * The main response format (R1) is a status byte (normally zero). Key flags: + * idle - 1 if the card is in an idle state/initialising + * cmd - 1 if an illegal command code was detected + * + * +-------------------------------------------------+ + * R1 | 0 | arg | addr | seq | crc | cmd | erase | idle | + * +-------------------------------------------------+ + * + * R1b is the same, except it is followed by a busy signal (zeros) until + * the first non-zero byte when it is ready again. + * + * Data Response Token + * ------------------- + * Every data block written to the card is acknowledged by a byte + * response token + * + * +----------------------+ + * | xxx | 0 | status | 1 | + * +----------------------+ + * 010 - OK! + * 101 - CRC Error + * 110 - Write Error + * + * Single Block Read and Write + * --------------------------- + * + * Block transfers have a byte header, followed by the data, followed + * by a 16-bit CRC. In our case, the data will always be 512 bytes. + * + * +------+---------+---------+- - - -+---------+-----------+----------+ + * | 0xFE | data[0] | data[1] | | data[n] | crc[15:8] | crc[7:0] | + * +------+---------+---------+- - - -+---------+-----------+----------+ + */ + + /* + * Comment: Changes for SDHC support till 32GB + * Name: KB + * Date: 07/24/2010 + * Release: 0.1 + */ + +#include "SDHCFileSystem.h" + +#define DEBUG +#define SD_COMMAND_TIMEOUT 5000 + + +SDFileSystem::SDFileSystem(PinName mosi, PinName miso, PinName sclk, PinName cs, const char* name) : + FATFileSystem(name), _spi(mosi, miso, sclk), _cs(cs) { + _cs = 1; +} + +#define R1_IDLE_STATE (1 << 0) +#define R1_ERASE_RESET (1 << 1) +#define R1_ILLEGAL_COMMAND (1 << 2) +#define R1_COM_CRC_ERROR (1 << 3) +#define R1_ERASE_SEQUENCE_ERROR (1 << 4) +#define R1_ADDRESS_ERROR (1 << 5) +#define R1_PARAMETER_ERROR (1 << 6) + +// Types +// - v1.x Standard Capacity +// - v2.x Standard Capacity +// - v2.x High Capacity +// - Not recognised as an SD Card + +#define SDCARD_FAIL 0 +#define SDCARD_V1 1 +#define SDCARD_V2 2 +#define SDCARD_V2HC 3 + +int SDFileSystem::initialise_card() { + // Set to 100kHz for initialisation, and clock card with cs = 1 + _spi.frequency(100000); + _cs = 1; + for(int i=0; i<16; i++) { + _spi.write(0xFF); + } + + // send CMD0, should return with all zeros except IDLE STATE set (bit 0) + if(_cmd(0, 0) != R1_IDLE_STATE) { + fprintf(stderr, "No disk, or could not put SD card in to SPI idle state\n"); + return SDCARD_FAIL; + } + + // send CMD8 to determine whther it is ver 2.x + int r = _cmd8(); + if(r == R1_IDLE_STATE) { + return initialise_card_v2(); + } else if(r == (R1_IDLE_STATE | R1_ILLEGAL_COMMAND)) { + return initialise_card_v1(); + } else { + fprintf(stderr, "Not in idle state after sending CMD8 (not an SD card?)\n"); + return SDCARD_FAIL; + } +} + +int SDFileSystem::initialise_card_v1() { + for(int i=0; i<SD_COMMAND_TIMEOUT; i++) { + _cmd(55, 0); + if(_cmd(41, 0) == 0) { + cdv = 512; + #ifdef DEBUG + printf("\n\rInit: SEDCARD_V1\n\r"); + #endif + return SDCARD_V1; + } + } + + fprintf(stderr, "Timeout waiting for v1.x card\n"); + return SDCARD_FAIL; +} + +int SDFileSystem::initialise_card_v2() { + + for(int i=0; i<SD_COMMAND_TIMEOUT; i++) { + wait_ms(50); + _cmd58(); + _cmd(55, 0); + if(_cmd(41, 0x40000000) == 0) { + _cmd58(); + #ifdef DEBUG + printf("\n\rInit: SDCARD_V2\n\r"); + #endif + cdv = 1; + return SDCARD_V2; + } + } + + fprintf(stderr, "Timeout waiting for v2.x card\n"); + return SDCARD_FAIL; +} + +int SDFileSystem::disk_initialize() { + + int i = initialise_card(); + #ifdef DEBUG + printf("init card = %d\n", i); + #endif + _sectors = _sd_sectors(); + + // Set block length to 512 (CMD16) + if(_cmd(16, 512) != 0) { + fprintf(stderr, "Set 512-byte block timed out\n"); + return 1; + } + + _spi.frequency(20000000); // Set to 1MHz for data transfer + return 0; +} + +int SDFileSystem::disk_write(const char *buffer, int block_number) { + // set write address for single block (CMD24) + if(_cmd(24, block_number * cdv) != 0) { + return 1; + } + + // send the data block + _write(buffer, 512); + return 0; +} + +int SDFileSystem::disk_read(char *buffer, int block_number) { + // set read address for single block (CMD17) + if(_cmd(17, block_number * cdv) != 0) { + return 1; + } + + // receive the data + _read(buffer, 512); + return 0; +} + +int SDFileSystem::disk_status() { return 0; } +int SDFileSystem::disk_sync() { return 0; } +int SDFileSystem::disk_sectors() { return _sectors; } + +// PRIVATE FUNCTIONS + +int SDFileSystem::_cmd(int cmd, int arg) { + _cs = 0; + + // send a command + _spi.write(0x40 | cmd); + _spi.write(arg >> 24); + _spi.write(arg >> 16); + _spi.write(arg >> 8); + _spi.write(arg >> 0); + _spi.write(0x95); + + // wait for the repsonse (response[7] == 0) + for(int i=0; i<SD_COMMAND_TIMEOUT; i++) { + int response = _spi.write(0xFF); + if(!(response & 0x80)) { + _cs = 1; + _spi.write(0xFF); + return response; + } + } + _cs = 1; + _spi.write(0xFF); + return -1; // timeout +} +int SDFileSystem::_cmdx(int cmd, int arg) { + _cs = 0; + + // send a command + _spi.write(0x40 | cmd); + _spi.write(arg >> 24); + _spi.write(arg >> 16); + _spi.write(arg >> 8); + _spi.write(arg >> 0); + _spi.write(0x95); + + // wait for the repsonse (response[7] == 0) + for(int i=0; i<SD_COMMAND_TIMEOUT; i++) { + int response = _spi.write(0xFF); + if(!(response & 0x80)) { + return response; + } + } + _cs = 1; + _spi.write(0xFF); + return -1; // timeout +} + + +int SDFileSystem::_cmd58() { + _cs = 0; + int arg = 0; + + // send a command + _spi.write(0x40 | 58); + _spi.write(arg >> 24); + _spi.write(arg >> 16); + _spi.write(arg >> 8); + _spi.write(arg >> 0); + _spi.write(0x95); + + // wait for the repsonse (response[7] == 0) + for(int i=0; i<SD_COMMAND_TIMEOUT; i++) { + int response = _spi.write(0xFF); + if(!(response & 0x80)) { + int ocr = _spi.write(0xFF) << 24; + ocr |= _spi.write(0xFF) << 16; + ocr |= _spi.write(0xFF) << 8; + ocr |= _spi.write(0xFF) << 0; +// printf("OCR = 0x%08X\n", ocr); + _cs = 1; + _spi.write(0xFF); + return response; + } + } + _cs = 1; + _spi.write(0xFF); + return -1; // timeout +} + +int SDFileSystem::_cmd8() { + _cs = 0; + + // send a command + _spi.write(0x40 | 8); // CMD8 + _spi.write(0x00); // reserved + _spi.write(0x00); // reserved + _spi.write(0x01); // 3.3v + _spi.write(0xAA); // check pattern + _spi.write(0x87); // crc + + // wait for the repsonse (response[7] == 0) + for(int i=0; i<SD_COMMAND_TIMEOUT * 1000; i++) { + char response[5]; + response[0] = _spi.write(0xFF); + if(!(response[0] & 0x80)) { + for(int j=1; j<5; j++) { + response[i] = _spi.write(0xFF); + } + _cs = 1; + _spi.write(0xFF); + return response[0]; + } + } + _cs = 1; + _spi.write(0xFF); + return -1; // timeout +} + +int SDFileSystem::_read(char *buffer, int length) { + _cs = 0; + + // read until start byte (0xFF) + while(_spi.write(0xFF) != 0xFE); + + // read data + for(int i=0; i<length; i++) { + buffer[i] = _spi.write(0xFF); + } + _spi.write(0xFF); // checksum + _spi.write(0xFF); + + _cs = 1; + _spi.write(0xFF); + return 0; +} + +int SDFileSystem::_write(const char *buffer, int length) { + _cs = 0; + + // indicate start of block + _spi.write(0xFE); + + // write the data + for(int i=0; i<length; i++) { + _spi.write(buffer[i]); + } + + // write the checksum + _spi.write(0xFF); + _spi.write(0xFF); + + // check the repsonse token + if((_spi.write(0xFF) & 0x1F) != 0x05) { + _cs = 1; + _spi.write(0xFF); + return 1; + } + + // wait for write to finish + while(_spi.write(0xFF) == 0); + + _cs = 1; + _spi.write(0xFF); + return 0; +} + +static int ext_bits(char *data, int msb, int lsb) { + int bits = 0; + int size = 1 + msb - lsb; + for(int i=0; i<size; i++) { + int position = lsb + i; + int byte = 15 - (position >> 3); + int bit = position & 0x7; + int value = (data[byte] >> bit) & 1; + bits |= value << i; + } + return bits; +} + +int SDFileSystem::_sd_sectors() { + + int c_size, c_size_mult, read_bl_len; + int block_len, mult, blocknr, capacity; + int blocks, hc_c_size; + uint64_t hc_capacity; + + // CMD9, Response R2 (R1 byte + 16-byte block read) + if(_cmdx(9, 0) != 0) { + fprintf(stderr, "Didn't get a response from the disk\n"); + return 0; + } + + char csd[16]; + if(_read(csd, 16) != 0) { + fprintf(stderr, "Couldn't read csd response from disk\n"); + return 0; + } + + // csd_structure : csd[127:126] + // c_size : csd[73:62] + // c_size_mult : csd[49:47] + // read_bl_len : csd[83:80] - the *maximum* read block length + + int csd_structure = ext_bits(csd, 127, 126); + + #ifdef DEBUG + printf("\n\rCSD_STRUCT = %d\n", csd_structure); + #endif + + switch (csd_structure){ + case 0: + cdv = 512; + c_size = ext_bits(csd, 73, 62); + c_size_mult = ext_bits(csd, 49, 47); + read_bl_len = ext_bits(csd, 83, 80); + + block_len = 1 << read_bl_len; + mult = 1 << (c_size_mult + 2); + blocknr = (c_size + 1) * mult; + capacity = blocknr * block_len; + blocks = capacity / 512; + #ifdef DEBUG + printf("\n\rSDCard\n\rc_size: %.4X \n\rcapacity: %.ld \n\rsectors: %d\r\n", c_size, capacity, blocks); + #endif + break; + + case 1: + cdv = 1; + hc_c_size = ext_bits(csd, 63, 48); + int hc_read_bl_len = ext_bits(csd, 83, 80); + hc_capacity = hc_c_size+1; + blocks = (hc_c_size+1)*1024; + #ifdef DEBUG + printf("\n\rSDHC Card \n\rhc_c_size: %.4X \n\rcapacity: %.lld \n\rsectors: %d\r\n", hc_c_size, hc_capacity*512*1024, blocks); + #endif + break; + + default: + fprintf(stderr, "This disk tastes funny! I only know about type 0 CSD structures\n"); + return 0; + //break; + }; + return blocks; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SDHCFileSystem/SDHCFileSystem.h Fri Aug 05 10:07:47 2011 +0000 @@ -0,0 +1,86 @@ +/* mbed SDFileSystem Library, for providing file access to SD cards + * Copyright (c) 2008-2010, sford + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MBED_SDHCFILESYSTEM_H +#define MBED_SDHCFILESYSTEM_H + +#include "mbed.h" +#include "FATFileSystem.h" + +/* Double Words */ +typedef unsigned long long uint64_t; +typedef long long sint64_t; + +/** Access the filesystem on an SD Card using SPI + * + * @code + * #include "mbed.h" + * #include "SDFileSystem.h" + * + * SDFileSystem sd(p5, p6, p7, p12, "sd"); // mosi, miso, sclk, cs + * + * int main() { + * FILE *fp = fopen("/sd/myfile.txt", "w"); + * fprintf(fp, "Hello World!\n"); + * fclose(fp); + * } + */ +class SDFileSystem : public FATFileSystem { +public: + + /** Create the File System for accessing an SD Card using SPI + * + * @param mosi SPI mosi pin connected to SD Card + * @param miso SPI miso pin conencted to SD Card + * @param sclk SPI sclk pin connected to SD Card + * @param cs DigitalOut pin used as SD Card chip select + * @param name The name used to access the virtual filesystem + */ + SDFileSystem(PinName mosi, PinName miso, PinName sclk, PinName cs, const char* name); + virtual int disk_initialize(); + virtual int disk_write(const char *buffer, int block_number); + virtual int disk_read(char *buffer, int block_number); + virtual int disk_status(); + virtual int disk_sync(); + virtual int disk_sectors(); + +protected: + + int _cmd(int cmd, int arg); + int _cmdx(int cmd, int arg); + int _cmd8(); + int _cmd58(); + int initialise_card(); + int initialise_card_v1(); + int initialise_card_v2(); + + int _read(char *buffer, int length); + int _write(const char *buffer, int length); + int _sd_sectors(); + int _sectors; + + SPI _spi; + DigitalOut _cs; + int cdv; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TLV320/TLV320.cpp Fri Aug 05 10:07:47 2011 +0000 @@ -0,0 +1,344 @@ + /****************************************************** + * TLV320.cpp: Texas instruments TLV320AIC23B library NXP LPC1768 + * + * Written by: + * mbed Team + * Ioannis Kedros - 10th January 2011 + * (basic functionality incurred) + * Daniel Worrall - 15th July 2011 + * (I2SSlave implementation and API restructuring) + * + * History + * version 2.29 +******************************************************/ +#include "mbed.h" +#include "TLV320.h" + +TLV320::TLV320(PinName sda, PinName scl, int addr, PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws) + : mAddr(addr), mI2c_(sda, scl), mI2s_(tx_sda, tx_ws, clk, rx_sda, rx_ws){ + mI2c_.frequency(150000); + reset(); //TLV resets + power(0x07); //Power Up the TLV320, but not the MIC, ADC and LINE + format(16, STEREO); //16Bit I2S protocol format, STEREO + frequency(44100); //Default sample frequency is 44.1kHz + bypass(false); //Do not bypass device + mute(false); //Not muted + activateDigitalInterface_(); //The digital part of the chip is active + outputVolume(0.6, 0.6); //Headphone volume to the default state + rxBuffer = &mI2s_.rxBuffer[0]; +} +//Public Functions +/****************************************************** + * Function name: inputVolume() + * + * Description: set line in volume for left and right channels + * + * Parameters: float leftVolumeIn, float rightVolumeIn + * Returns: int 0 (success), -1 (value out of range) +******************************************************/ +int TLV320::inputVolume(float leftVolumeIn, float rightVolumeIn){ + //check values are in range + if((leftVolumeIn < 0.0)||leftVolumeIn > 1.0) return -1; + if((rightVolumeIn < 0.0)||rightVolumeIn > 1.0) return -1; + //convert float to encoded char + char left = (char)31*leftVolumeIn; + char right = (char)31*rightVolumeIn; + //Left Channel + cmd[1] = left | (0 << 7); //set volume + cmd[0] = LEFT_LINE_INPUT_CHANNEL_VOLUME_CONTROL; //set address + mI2c_.write(mAddr, cmd, 2); //send + //Right Channel + cmd[1] = right | (0 << 7); //set volume + cmd[0] = RIGHT_LINE_INPUT_CHANNEL_VOLUME_CONTROL; //set address + mI2c_.write(mAddr, cmd, 2); //send + return 0; +} +/****************************************************** + * Function name: outputVolume() + * + * Description: Set headphone (line out) volume for left an right channels + * + * Parameters: float leftVolumeOut, float rightVolumeOut + * Returns: int 0 (success), -1 (value out of range) +******************************************************/ +int TLV320::outputVolume(float leftVolumeOut, float rightVolumeOut){ + //check values are in range + if((leftVolumeOut < 0.0)||leftVolumeOut > 1.0) return -1; + if((rightVolumeOut < 0.0)||rightVolumeOut > 1.0) return -1; + //convert float to encoded char + char left = (char)(79*leftVolumeOut)+0x30; + char right = (char)(79*rightVolumeOut)+0x30; + //Left Channel + cmd[1] = left | (1 << 7); //set volume + cmd[0] = LEFT_CHANNEL_HEADPHONE_VOLUME_CONTROL; //set address + mI2c_.write(mAddr, cmd, 2); //send + //Right Channel + cmd[1] = right | (1 << 7); //set volume + cmd[0] = RIGHT_CHANNEL_HEADPHONE_VOLUME_CONTROL; //set address + mI2c_.write(mAddr, cmd, 2); //send + return 0; +} +/****************************************************** + * Function name: bypass() + * + * Description: Send TLV320 into bypass mode, i.e. connect input to output + * + * Parameters: bool bypassVar + * Returns: none +******************************************************/ +void TLV320::bypass(bool bypassVar){ + if(bypassVar == true) + cmd[1] = (1 << 3) | (0 << 4) | (0 << 5);//bypass enabled, DAC disabled, sidetone insertion disabled + else + cmd[1] = (0 << 3) | (1 << 4); //bypass disabled, DAC enabled + cmd[1] |= (0 << 2); + cmd[0] = ANALOG_AUDIO_PATH_CONTROL; //set address + mI2c_.write(mAddr, cmd, 2); //send +} +/****************************************************** + * Function name: mute() + * + * Description: Send TLV320 into mute mode + * + * Parameters: bool softMute + * Returns: none +******************************************************/ +void TLV320::mute(bool softMute){ + if(softMute == true) cmd[1] = 0x08; //set instruction to mute + else cmd[1] = 0x00; //set instruction to NOT mute + + cmd[0] = DIGITAL_AUDIO_PATH_CONTROL; //set address + mI2c_.write(mAddr, cmd, 2); //send +} +/****************************************************** + * Function name: power() + * + * Description: Switch TLV320 on/off + * + * Parameters: bool powerUp + * Returns: none +******************************************************/ +void TLV320::power(bool powerUp){ + if(powerUp == true) cmd[1] = 0x00; //everything on + else cmd[1] = 0xFF; //everything off + + cmd[0] = POWER_DOWN_CONTROL; //set address + mI2c_.write(mAddr, cmd, 2); //send +} +/****************************************************** + * Function name: power() + * + * Description: Switch on individual devices on TLV320 + * + * Parameters: int device + * Returns: none +******************************************************/ +void TLV320::power(int device){ + cmd[1] = (char)device; //set user defined commands + cmd[0] = POWER_DOWN_CONTROL; //set address + mI2c_.write(mAddr, cmd, 2); //send +} +/****************************************************** + * Function name: format() + * + * Description: Set interface format + * + * Parameters: char length, bool mode + * Returns: none +******************************************************/ +void TLV320::format(char length, bool mode){ + char modeSet = (1 << 6); + modeSet |= (1 << 5); //swap left and right channels + + switch (length) //input data into instruction byte + { + case 16: + cmd[1] = modeSet | 0x02; + break; + case 20: + cmd[1] = modeSet | 0x06; + break; + case 24: + cmd[1] = modeSet | 0x0A; + break; + case 32: + cmd[1] = modeSet | 0x0E; + break; + default: + break; + } + mI2s_.format(length, mode); + cmd[0] = DIGITAL_AUDIO_INTERFACE_FORMAT; //set address + mI2c_.write(mAddr, cmd, 2); //send +} +/****************************************************** + * Function name: frequency() + * + * Description: Set sample frequency + * + * Parameters: int hz + * Returns: int 0 (success), -1 (value not recognised) +******************************************************/ +int TLV320::frequency(int hz){ + char rate; + switch(hz){ + case 8000: + rate = 0x03; + break; + case 8021: + rate = 0x0B; + break; + case 32000: + rate = 0x06; + break; + case 44100: + rate = 0x08; + break; + case 48000: + rate = 0x00; + break; + case 88200: + rate = 0x0F; + break; + case 96000: + rate = 0x07; + break; + default: + return -1; + } + char clockInChar = (0 << 6); + char clockModeChar = (1 << 0); + + cmd[1] = (rate << 2) | clockInChar | clockModeChar; //input data into instruciton byte + cmd[0] = SAMPLE_RATE_CONTROL; //set address + mI2c_.write(mAddr, cmd, 2); //send + return 0; +} +/****************************************************** + * Function name: reset() + * + * Description: Reset TLV320 + * + * Parameters: none + * Returns: none +******************************************************/ +void TLV320::reset(void){ + cmd[0] = RESET_REGISTER; //set address + cmd[1] = 0x00; //this resets the entire device + mI2c_.write(mAddr, cmd, 2); +} +/****************************************************** + * Function name: start() + * + * Description: Enable interrupts on the I2S port + * + * Parameters: int mode + * Returns: none +******************************************************/ +void TLV320::start(int mode){ + mI2s_.start(mode); +} +/****************************************************** + * Function name: stop() + * + * Description: Disable interrupts on the I2S port + * + * Parameters: none + * Returns: none +******************************************************/ +void TLV320::stop(void){ + mI2s_.stop(); +} +/****************************************************** + * Function name: write() + * + * Description: Write (part of) a buffer to the I2S port + * + * Parameters: int *buffer, int from, int length + * Returns: none +******************************************************/ +void TLV320::write(int *buffer, int from, int length){ + mI2s_.write(buffer, from, length); +} +/****************************************************** + * Function name: read() + * + * Description: Place I2SRXFIFO in rxBuffer + * + * Parameters: none + * Returns: none +******************************************************/ +void TLV320::read(void){ + mI2s_.read(); +} +/****************************************************** + * Function name: attach() + * + * Description: Attach a void/void function or void/void static member function to IRQHandler + * + * Parameters: none + * Returns: none +******************************************************/ +void TLV320::attach(void(*fptr)(void)){ + mI2s_.attach(fptr); +} +//Private Functions +/****************************************************** + * Function name: setSampleRate_() + * + * Description: Clocking control + * + * Parameters: char rate, bool clockIn, bool clockMode, bool bOSR + * Returns: none +******************************************************/ +void TLV320::setSampleRate_(char rate, bool clockIn, bool clockMode, bool bOSR){ + char clockInChar; + char clockModeChar; + char baseOverSamplingRate; + if(bOSR){ + baseOverSamplingRate = (1 << 0); + } else { + baseOverSamplingRate = (0 << 0); + } + if(clockIn){ + clockInChar = (1 << 6); + } else { + clockInChar = (0 << 6); + } + if(clockMode){ + clockModeChar = 0x01; + } else { + clockModeChar = 0x00; + } + cmd[1] = (rate << 2) | clockInChar | clockModeChar | baseOverSamplingRate; //input data into instruciton byte + cmd[0] = SAMPLE_RATE_CONTROL; //set address + mI2c_.write(mAddr, cmd, 2); //send +} +/****************************************************** + * Function name: activateDigitalInterface_() + * + * Description: Activate digital part of chip + * + * Parameters: none + * Returns: none +******************************************************/ +void TLV320::activateDigitalInterface_(void){ + cmd[1] = 0x01; //Activate + cmd[0] = DIGITAL_INTERFACE_ACTIVATION; //set address + mI2c_.write(mAddr, cmd, 2); //send +} +/****************************************************** + * Function name: deactivateDigitalInterface_ + * + * Description: Deactivate digital part of chip + * + * Parameters: none + * Returns: none +******************************************************/ +//Digital interface deactivation +void TLV320::deactivateDigitalInterface_(void){ + cmd[1] = 0x00; //Deactivate + cmd[0] = DIGITAL_INTERFACE_ACTIVATION; //set address + mI2c_.write(mAddr, cmd, 2); //send +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TLV320/TLV320.h Fri Aug 05 10:07:47 2011 +0000 @@ -0,0 +1,187 @@ + /****************************************************** + * TLV320.h: Texas instruments TLV320AIC23B library NXP LPC1768 + * + * Written by: + * mbed Team + * Ioannis Kedros - 10th January 2011 + * (basic functionality incurred) + * Daniel Worrall - 15th July 2011 + * (I2SSlave implementation and API restructuring) + * + * History + * version 2.29 +******************************************************/ + +#ifndef MBED_TLV320_H +#define MBED_TLV320_H + +#include "mbed.h" +#include "I2SSlave.h" + +/** TLV320 class, defined on the I2C master bus +* +*/ + +class TLV320 +{ + public: + //constructor + /** Create a TLV320 object defined on the I2C port + * + * @param sda Serial data pin (p9 or p28) + * @param scl Serial clock pin (p10 or p27) + * @param addr Object address + */ + TLV320(PinName sda, PinName scl, int addr, PinName tx_sda, PinName tx_ws, PinName clk, PinName rx_sda, PinName rx_ws); + /** Power up/down + * + * @param powerUp 0 = power down, 1 = power up + */ + void power(bool powerUp); + /** Overloaded power() function + * + * @param device Call individual devices to power up/down + * Device power 0x00 = On 0x80 = Off + * Clock 0x00 = On 0x40 = Off + * Oscillator 0x00 = On 0x20 = Off + * Outputs 0x00 = On 0x10 = Off + * DAC 0x00 = On 0x08 = Off + * ADC 0x00 = On 0x04 = Off + * Microphone input 0x00 = On 0x02 = Off + * Line input 0x00 = On 0x01 = Off + */ + void power(int device); + /** Set I2S interface bit length and mode + * + * @param length Set bit length to 16, 20, 24 or 32 bits + * @param mode Set STEREO (0), MONO (1) + */ + void format(char length, bool mode); + /** Set sample frequency + * + * @param frequency Sample frequency of data in Hz + * @return Returns an integer 0 = success, -1 = unrecognnised frequency + * + * The TLV320 supports the following frequencies: 8kHz, 8.021kHz, 32kHz, 44.1kHz, 48kHz, 88.2kHz, 96kHz + * Default is 44.1kHz + */ + int frequency(int hz); + /** Reset TLV320 + * + */ + void reset(void); + /** Start streaming i.e. enable interrupts + * + * @param mode Enable interrupts for NONE, TRANSMIT only, RECEIVE only, BOTH + */ + void start(int mode); + /** Stop streaming i.e. disable all interrupts + * + */ + void stop(void); + /** Write [length] 32 bit words in buffer to I2S port + * + * @param *buffer Address of buffer to be written + * @param from Start position in buffer to read from + * @param length Number of words to be written (MUST not exceed 4) + */ + void write(int *buffer, int from, int length); + /** Read 4 x (32bit) words into rxBuffer + * + */ + void read(void); + /** Attach a void/void function or void/void static member funciton to an interrupt generated by the I2SxxFIFOs + * + * @param function Function to attach + * + * e.g. myTlv320Object.attach(&myfunction); + * OR myTlv320Object.attach(&myClass::myStaticMemberFunction); + */ + void attach(void(*fptr)(void)); + /** Attach a nonstatic void/void member function to an interrupt generated by the I2SxxFIFOs + * + * @param tptr Object pointer + * @param mptr Member function pointer + * + * e.g. myTlv320Object.attach(&myObject, &myClass::myNonstaticMemberFunction); where myObject is an object of myClass + */ + template<typename T> + void attach(T *tptr, void(T::*mptr)(void)){ + mI2s_.attach(tptr, mptr); + } + /** Line in volume control i.e. record volume + * + * @param leftVolumeIn Left line-in volume + * @param rightVolumeIn Right line-in volume + * @return Returns 0 for success, -1 if parameters are out of range + * Parameters accept a value, where 0.0 < parameter < 1.0 and where 0.0 maps to -34.5dB + * and 1.0 maps to +12dB (0.74 = 0 dB default). + */ + int inputVolume(float leftVolumeIn, float rightVolumeIn); + /** Headphone out volume control + * + * @param leftVolumeOut Left line-out volume + * @param rightVolumeOut Right line-out volume + * @return Returns 0 for success, -1 if parameters are out of range + * Parameters accept a value, where 0.0 < parameter < 1.0 and where 0.0 maps to -73dB (mute) + * and 1.0 maps to +6dB (0.5 = default) + */ + int outputVolume(float leftVolumeOut, float rightVolumeOut); + /** Analog audio path control + * + * @param bypassVar Route analogue audio direct from line in to headphone out + */ + void bypass(bool bypassVar); + /**Digital audio path control + * + * @param softMute Mute output + */ + void mute(bool softMute); + //Receive buffer + + int *rxBuffer; + + protected: + char cmd[2]; //the address and command for TLV320 internal registers + int mAddr; //register write address + private: + I2C mI2c_; //MUST use the I2C port + I2SSlave mI2s_; + Ticker I2sTick; + void io(void); + /** Sample rate control + * + * @param rate Set the sampling rate as per datasheet section 3.3.2 + * @param clockIn Set the clock in divider MCLK, MCLK_DIV2 + * @param clockMode Set clock mode CLOCK_NORMAL, CLOCK_USB + */ + void setSampleRate_(char rate, bool clockIn, bool mode, bool bOSR); + /** Digital interface activation + * + */ + void activateDigitalInterface_(void); + /** Digital interface deactivation + * + */ + void deactivateDigitalInterface_(void); + + //TLV320AIC23B register addresses as defined in the TLV320AIC23B datasheet + #define LEFT_LINE_INPUT_CHANNEL_VOLUME_CONTROL (0x00 << 1) + #define RIGHT_LINE_INPUT_CHANNEL_VOLUME_CONTROL (0x01 << 1) + #define LEFT_CHANNEL_HEADPHONE_VOLUME_CONTROL (0x02 << 1) + #define RIGHT_CHANNEL_HEADPHONE_VOLUME_CONTROL (0x03 << 1) + #define ANALOG_AUDIO_PATH_CONTROL (0x04 << 1) + #define DIGITAL_AUDIO_PATH_CONTROL (0x05 << 1) + #define POWER_DOWN_CONTROL (0x06 << 1) + #define DIGITAL_AUDIO_INTERFACE_FORMAT (0x07 << 1) + #define SAMPLE_RATE_CONTROL (0x08 << 1) + #define DIGITAL_INTERFACE_ACTIVATION (0x09 << 1) + #define RESET_REGISTER (0x0F << 1) + + #define CLOCK_NORMAL 0 + #define CLOCK_USB 1 + #define MCLK 0 + #define MCLK_DIV2 1 +}; + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Fri Aug 05 10:07:47 2011 +0000 @@ -0,0 +1,68 @@ +#include "mbed.h" +#include "SDHCFileSystem.h" +#include "TLV320.h" + +TLV320 audio(p9, p10, 52, p5, p6, p7, p8, p29); //TLV320 object +SDFileSystem sd(p11, p12, p13, p14, "sd"); //SD Card object +InterruptIn volumeSet(p17); +AnalogIn aIn(p19); +FILE *infp; //File pointer object +/* Buffers */ +int circularBuffer[4096]; +volatile int readPointer = 0; +volatile int theta = 0; +/* Wav file header data, for setting up the transfer protocol */ +short channels; +long sampleRate; +short wordWidth; +/* Function to set volume*/ +void setVolume(void){ + audio.outputVolume(aIn, aIn); +} +/* Function to read from circular buffer and send data to TLV320 */ +void play(void){ + audio.write(circularBuffer, readPointer, 8); + //Pointer fun :-) + readPointer += 8; + readPointer &= 0xfff; + theta -= 8; +} +/* Function to load circular buffer from SD Card */ +void fillBuffer(void){ + while(!feof(infp)){ //fill the circular buffer until the end of the file + static volatile int writePointer = 0; + if(theta < 4096){ + fread(&circularBuffer[writePointer], 4, 4, infp); //read 4 integers into the circular buffer at a time + //More pointer fun :D + theta+=4; + writePointer+=4; + writePointer &= 0xfff; + } + } +} +/* main */ +int main(){ + infp = fopen("/sd/test.wav", "r"); //open file + if(infp == NULL){ //make sure it's been opened + perror("Error opening file!"); + exit(1); + } + /* Parse wav file header */ + fseek(infp, 22, SEEK_SET); + fread(&channels, 2, 1, infp); + fseek(infp, 24, SEEK_SET); + fread(&sampleRate, 4, 1, infp); + fseek(infp, 34, SEEK_SET); + fread(&wordWidth, 2, 1, infp); + + volumeSet.rise(&setVolume); //attach set volume function to digital input + audio.power(0x07); //power up TLV apart from analogue input + audio.frequency(sampleRate); //set sample frequency + audio.format(wordWidth, (2-channels)); //set transfer protocol + audio.attach(&play); //attach interrupt handler to send data to TLV320 + for(int j = 0; j < 4096; ++j){ //upon interrupt generation + circularBuffer[j] = 0; //clear circular buffer + } + audio.start(TRANSMIT); //interrupt come from the I2STXFIFO only + fillBuffer(); //continually fill circular buffer +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Fri Aug 05 10:07:47 2011 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/63bcd7ba4912