/* General Notes about this document : This file contains three parts. The first part is composed mainly of #define commands. The seconds contains the low-level routine to acces the MAS chip (or even every I2C chip, using directly the I2C subroutines) plus some general purpose simple function, like active wait, enabling a led etc The last part contains a basic program that allows to receives Datas by the Bluetooth Channel, and to send it to the MAS This file, masDriver_Demand.c, is very similar to an other one : masDriver_Streaming.c At this time, masDriver_Demand contains a "handshake" protocol for the retrieving of the Datas on the Btnode. The other file contain a trial for a another sort of protocol. This code should be useable like this, using the corresponding C++ PC program. /* ------------------------------------------------------------------------- * includes * ------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include /* ------------------------------------------------------------------------- * defines * ------------------------------------------------------------------------- */ #define USE_SERIAL #define DEBUG_MODE /* ----------- atmel128 I2C response codes : values that can take the register TWSR after sending a I2C command ----------- */ #define I2C_START_TRANSMITTED 0x08 #define I2C_REP_START_TRANSMITTED 0x10 #define I2C_SLA_W_TRANSMITTED_W_ACK 0x18 #define I2C_SLA_W_TRANSMITTED_NO_ACK 0x20 #define I2C_DATA_BYTE_TRANSMITTED_W_ACK 0x28 #define I2C_DATA_BYTE_TRANSMITTED_NO_ACK 0x30 #define I2C_ARBITRATION_LOST 0x38 // not used #define I2C_SLA_R_TRANSMITTED_W_ACK 0x40 #define I2C_SLA_R_TRANSMITTED_NO_ACK 0x48 #define I2C_DATA_BYTE_RECEIVED_W_ACK 0x50 #define I2C_DATA_BYTE_RECEIVED_NO_ACK 0x58 /* ----------- MAS 3587F dependent data : mostly I2C codes ----------- */ // main I2C device adress #define DW 0x3c #define DR 0x3d // adresses to write or read the DSP memory and registers #define DATA_WRITE 0x68 #define DATA_READ 0x69 // adresses to write or read the codec register #define CODEC_WRITE 0x6c #define CODEC_READ 0x6d // adresses of the direct configuration registers #define CONTROL_REG 0x6a #define DCCF_REG 0x76 #define FIRST_DCDC_CONFIGURATION_REGISTER DCCF_REG #define DCFR_REG 0x77 #define SECOND_DCDC_CONFIGURATION_REGISTER DCFR_REG // commands for DCDC configuration register #define DCCF_REG_RESET 0x5050 #define DCFR_REG_RESET 0x0000 // commands for control register #define CTRL_REG_RESET_NODC 0x0000 #define CTRL_REG_DSP_ON_NODC 0x0400 #define CTRL_REG_BOTH_ON_NODC 0x0C00 // adresses of the differents memory cells #define APPSELECT 0x07F6 #define APPRUNNING 0x07F7 #define ENCODERCONTROL 0x07F0 #define IOCONTROLMAIN 0x07F1 #define INTERFACECONTROL 0x07F2 #define OUTCLKCONFIG 0x07F4 #define SPDOUTBITS 0x07F8 #define SOFTMUTE 0x07F9 #define OUT_LL 0x07FC #define OUT_LR 0x07FD #define OUT_RL 0x07FE #define OUT_RR 0x07FF // values for the AppSelect memory cell (D0:7F6) #define APPSELECT_MEM_BOTH_DECODE 0x0c #define APPSELECT_MEM_LAYER2_DECODE 0x04 #define APPSELECT_MEM_LAYER3_DECODE 0x03 #define APPSELECT_MEM_ENCODE 0x20 // values for the IOControlMain memory cell (DO:7F1) #define IOCTRL_MEM_INVERT_SOC (1 << 14) #define IOCTRL_MEM_INSERT_TIMECODE (1 << 12) #define IOCTRL_MEM_OUTPUT_DELAY (1 << 11) #define IOCTRL_MEM_LOOP_THROUGH (1 << 10) #define IOCTRL_MEM_ENCODE_SDI_INPUT_NOPLL (1 << 8) #define IOCTRL_MEM_ENCODE_SPDIF_INPUT (1 << 9) #define IOCTRL_MEM_DECODE_INPUT_PIO (1 << 8) #define IOCTRL_MEM_DECODE_INPUT_SIDB 0 #define IOCTRL_MEM_INVERT_SIC (1 << 7) #define IOCTRL_MEM_INPUT_DELAY (1 << 6) #define IOCTRL_MEM_WORD_STROBE_SDO_INVERT (1 << 5) #define IOCTRL_MEM_SDO_16BITS_SAMPLE (1 << 4) #define IOCTRL_MEM_CLOCK_SETTING_MPEG2 (1 << 3) #define IOCTRL_MEM_DECODE_SIBC_INVERT (1 << 2) #define IOCTRL_MEM_ENCODE_SDI_STROBE_INVERT (1 << 2) #define IOCTRL_MEM_BROADCASTMODE (1 << 1) #define IOCTRL_MEM_VALIDATE 1 #define IOCTRL_MEM_STANDARD_IN_PIO 0x125 #define IOCTRL_MEM_STANDARD_IN_SERIAL 0x25 // values fot the InterfaceControl memory cell (DO:7F2) #define INTCTRL_MEM_SPDIF2 (1 << 6) #define INTCTRL_MEM_SPDIF_DISABLE (1 << 5) #define INTCTRL_MEM_SDO_DISABLE (1 << 3) #define INTCTRL_MEM_CLOCK_HIGH_IMPEDANCE (1 << 2) #define INTCTRL_MEM_SDI_SOURCE_AD 1 #define INTCTRL_MEM_STANDARD 0x0004 // values for the OutClkCOnfig memory cell (D0:7F4) #define OUTCLKCONFIG_MEM_STANDARD 0x00080000 // values for the SoftMute memory cell (D0:7F9) #define MUTE_MEM_BITRESERVOIR_EMPTY (1 << 2) #define MUTE_MEM_PAUSE (1 << 1) #define MUTE_MEM_MUTE 1 // values for the OUT_LL - OUT_RR - OUT_RL - OUTLR register #define OUT_RR_STEREO 0x00080000 #define OUT_LL_STEREO 0x00080000 #define OUT_RL_STEREO 0x00000000 #define OUT_LR_STEREO 0x00000000 #define OUT_RR_LEFTMONO 0x00000000 #define OUT_LL_LEFTMONO 0x00080000 #define OUT_RL_LEFTMONO 0x00000000 #define OUT_LR_LEFTMONO 0x00080000 #define OUT_RR_RIGHTMONO 0x00080000 #define OUT_LL_RIGHTMONO 0x00000000 #define OUT_RL_RIGHTMONO 0x00080000 #define OUT_LR_RIGHTMONO 0x00000000 // adresses for the codec configuration registers #define CONV_CONF 0x0000 #define ADC_IN_MODE 0x0008 #define DAC_IN_ADC 0x0006 #define DAC_IN_DSP 0x0007 #define DAC_OUT_MODE 0x000e #define BASS 0x0014 #define TREBLE 0x0015 #define LOUDNESS 0x001e #define BALANCE 0x0011 #define VOLUME 0x0010 // standard values for the codec configuration registers #define CONV_CONF_LINE_IN 0x2207 #define CONV_CONF_MIC_IN 0x220F #define ADC_IN_MONO 0x8000 #define ADC_IN_STEREO 0x0000 #define DAC_IN_NO_VOLUME 0x0000 #define DAC_IN_50_PERCENT 0x2000 #define DAC_IN_100_PERCENT 0x4000 #define DAC_IN_MAX 0x7f00 #define BALANCE_FULL_RIGHT 0x7f00 #define BALANCE_HALF_RIGHT 0x3f00 #define BALANCE_FULL_LEFT 0x8000 #define BALANCE_HALF_LEFT 0x4000 #define BALANCE_MEDIUM 0x0000 #define VOLUME_MAX 0x7f00 #define VOLUME_STANDARD 0x6000 #define VOLUME_HALF 0x3f00 #define VOLUME_MUTE 0x0000 /* ----------- various defines : ----------- */ #define LED_PORT PORTB #define LED_PORT_DDR DDRB #define CONTROL_PORT PORTB #define CONTROL_PORT_DDR DDRB #define MSB_PIO_PORT PORTE #define MSB_PIO_PORT_DDR DDRE #define LSB_PIO_PORT PORTF #define LSB_PIO_PORT_DDR DDRF #define EOD PIN0 // PB0 #define PR PIN3 // PB3 #define RTR PIN1 // PB1 #define PCS PIN2 // PB2 #define PI19 PIN7 // PE7 #define PI18 PIN6 // PE6 #define PI17 PIN5 // PE5 #define PI16 PIN3 // PE3 #define PI15 PIN4 // PF4 #define PI14 PIN5 // PF5 #define PI13 PIN6 // PF6 #define PI12 PIN7 // PF7 #define LED0 4 #define LED1 5 #define LED2 6 #define LED3 7 #define L2CAP_BUF_LEN 4 // very important : this value musst be the same than in the C program // if not, either the btnode will wait infinitly for data which wanna come // or discard the some of the incoming datas #define PACKET_SIZE 1024 #define MP3_BUFFER_START_ADDR 0x1100 #define MP3_BUFFER_END_ADDR 0xFFFF #define MP3_BUFFER_LENGTH 0xEEFF #define DATA_ACK_EVENT 192 #define FILE_TRANSFER_DONE_EVENT 193 #define PLAY_EVENT 194 #define PROCEED_COMMAND_EVENT 195 #define BUFFER_PLACE_THRESHOLD 0x1800 // in byte : 6KB #define BUFFER_CRITICAL_THRESHOLD 0x800 // in byte = 2KB #define BUFFER_LOW_THRESHOLD 0x3000 // in bytes = 12 KB #define NMORE "nMORE" struct Demand_struct { u8 tag; u32 time; struct Demand_struct* next; struct Demand_struct* previous; }; typedef struct Demand_struct demand; /* ------------------------------------------------------------------------- * globals * ------------------------------------------------------------------------- */ l2cap_data_buffer_elem_t data_buf[L2CAP_BUF_LEN]; u8 * read_pointer; u8 * write_pointer; u16 quantity_received; // says how many packet will normally arrive u8 more[3] = "nM"; // The M holds for "MORE", the n (more[0] for the num // ber of packet that one wants #ifdef USE_SERIAL u8 serial_mask; #endif u32 microsec; u8 bufferFilling; // used as boolean u8 cid; u8 endOfFile; // used as boolean u8 firstTransfer; // used as boolean u8 demandOnProcess; u8 bufferLow = 0; // used as boolean u8 bufferCritical = 0; // used as boolean /* ------------------------------------------------------------------------- * methods * ------------------------------------------------------------------------- */ void wait(u16 microseconds) { u16 j; for (j = 0 ; j < microseconds; j++) { asm volatile("nop"); } } /*-----------------------------------------------------------------------*/ void wait32(u32 microseconds) { u32 j; for (j = 0 ; j < microseconds; j++) { asm volatile("nop"); } } /*-----------------------------------------------------------------------*/ void waitForPup(void) { while(!(inp(PIND) & PIN6)); } /*-----------------------------------------------------------------------*/ void resetMAS(void) { // reset MAS3587 sbi(DDRD, PIN7); cbi(PORTD, PIN7); wait(200); sbi(PORTD, PIN7); wait(1000); } /*-----------------------------------------------------------------------*/ void setLed(int i) { i = i + 3; sbi(LED_PORT, i); } /*-----------------------------------------------------------------------*/ void unsetLed(int i) { i = i + 3; cbi(LED_PORT, i); } /*-----------------------------------------------------------------------*/ void led_init(void) { sbi( LED_PORT_DDR, LED0 ); sbi( LED_PORT_DDR, LED1 ); sbi( LED_PORT_DDR, LED2 ); sbi( LED_PORT_DDR, LED3 ); cbi( LED_PORT, LED0 ); cbi( LED_PORT, LED1 ); cbi( LED_PORT, LED2 ); cbi( LED_PORT, LED3 ); } /*-----------------------------------------------------------------------*/ void I2C_init(void) { printf("Configuring I2C channel..."); // spec page 196: i2c speed // init SCL frequency // 100 kHz regular, 400 kHz fast // clock speed: 7.3728 MHz // 100 kHz = 7.3728 MHz / (16 + 2 x TWBR) x 4 ^ TWPS // regular: TWSR = 1, TWBR = 7 // set i2c speed // it's written in doc that TWBR >= 10 outp( (u8)10, TWBR ); outp( BV(TWPS0), TWSR ); // outp(SLAVE_ADDRESS << 1 | 1, TWAR); // enable TWI outp(BV(TWEN), TWCR); printf("done" NL); } /*-----------------------------------------------------------------------*/ int I2C_startCond(void) { unsigned char status; // write start condition outp(BV(TWINT) | BV(TWSTA) | BV(TWEN), TWCR); // wait until TWINT flag is set while (!(inp(TWCR) & BV(TWINT))); status = inp(TWSR); if ((status & 0xF8) != I2C_START_TRANSMITTED) { return -1; } return 0; } /*-----------------------------------------------------------------------*/ int I2C_repStartCond() { unsigned char status; // write start condition outp(BV(TWINT) | BV(TWSTA) | BV(TWEN), TWCR); // wait until TWINT flag is set while (!(inp(TWCR) & BV(TWINT))); status = inp(TWSR); if ((status & 0xF8) != I2C_REP_START_TRANSMITTED) { return -1; } return 0; } /*-----------------------------------------------------------------------*/ int I2C_stopCond() { // write stop condition outp(BV(TWINT) | BV(TWSTO) | BV(TWEN), TWCR); while (!(inp(TWCR) & BV(TWSTO))); return 0; } /*-----------------------------------------------------------------------*/ int I2C_writeByte(u8 byte) { unsigned char status; outp(byte, TWDR); // load the data that we want to transmit outp(BV(TWINT) | BV(TWEN), TWCR); // start transmission // wait until TWINT flag is set while (!(inp(TWCR) & BV(TWINT))); status = inp(TWSR); status &= 0xF8; if (!(status == I2C_SLA_W_TRANSMITTED_W_ACK || I2C_DATA_BYTE_TRANSMITTED_W_ACK || status == I2C_SLA_R_TRANSMITTED_W_ACK)) { return -1; } return 0; } /*-----------------------------------------------------------------------*/ int I2C_readByte(u8 * result, bool last) { unsigned char status; // for the last byte to read, a NOACK must be send if (last) { outp(BV(TWINT) | BV(TWEN), TWCR); while (!(inp(TWCR) & BV(TWINT))); *result = inp(TWDR); status = inp(TWSR); status &= 0xF8; if (status != I2C_DATA_BYTE_RECEIVED_NO_ACK) { return -1; } return 0; } else { outp(BV(TWINT) | BV(TWEN) | BV(TWEA), TWCR); while (!(inp(TWCR) & BV(TWINT))); *result = inp(TWDR); status = inp(TWSR); status &= 0xF8; if (status != I2C_DATA_BYTE_RECEIVED_W_ACK) { return -1; } return 0; } } /*-----------------------------------------------------------------------*/ int writeControlRegister(u16 data) { // OK int ret; ret = ( I2C_startCond() || I2C_writeByte(DW) || I2C_writeByte(CONTROL_REG) || I2C_writeByte((u8)(data >> 8)) || // left part I2C_writeByte((u8)(data & 0xFF)) || // right part I2C_stopCond() ); wait(1000); return ret; } /*-----------------------------------------------------------------------*/ int readControlRegister(u16 * data) { // OK u8 result1, result2; int ret; ret = ( I2C_startCond() || I2C_writeByte(DW) || I2C_writeByte(CONTROL_REG) || I2C_repStartCond() || I2C_writeByte(DR) || I2C_readByte(&result1,0) || I2C_readByte(&result2,1) || I2C_stopCond() ); *data = result2; *data |= ((u32)result1 << 8); wait(500); return ret; } /*-----------------------------------------------------------------------*/ int writeFirstDCDCconfigurationRegister(u16 data) { // OK int ret; ret = ( I2C_startCond() || I2C_writeByte(DW) || I2C_writeByte(FIRST_DCDC_CONFIGURATION_REGISTER) || I2C_writeByte((u8)(data >> 8)) || // left part I2C_writeByte((u8)(data & 0xFF)) || // right part I2C_stopCond() ); wait(1000); return ret; } /*-----------------------------------------------------------------------*/ int readFirstDCDCconfigurationRegister(u16 * data) { // OK u8 result1, result2; int ret; ret = ( I2C_startCond() || I2C_writeByte(DW) || I2C_writeByte(FIRST_DCDC_CONFIGURATION_REGISTER) || I2C_repStartCond() || I2C_writeByte(DR) || I2C_readByte(&result1,0) || I2C_readByte(&result2,1) || I2C_stopCond() ); *data = result2; *data |= ((u32)result1 << 8); wait(500); return ret; } /*-----------------------------------------------------------------------*/ int writeSecondDCDCconfigurationRegister(u16 data) { // OK int ret; ret = ( I2C_startCond() || I2C_writeByte(DW) || I2C_writeByte(SECOND_DCDC_CONFIGURATION_REGISTER) || I2C_writeByte((u8)(data >> 8)) || // left part I2C_writeByte((u8)(data & 0xFF)) || // right part I2C_stopCond() ); wait(1000); return ret; } /*-----------------------------------------------------------------------*/ int readSecondDCDCconfigurationRegister(u16 * data) { // OK u8 result1, result2; int ret; ret = ( I2C_startCond() || I2C_writeByte(DW) || I2C_writeByte(SECOND_DCDC_CONFIGURATION_REGISTER) || I2C_repStartCond() || I2C_writeByte(DR) || I2C_readByte(&result1,0) || I2C_readByte(&result2,1) || I2C_stopCond() ); *data = result2; *data |= ((u32)result1 << 8); wait(500); return ret; } /*-----------------------------------------------------------------------*/ int writeMemoryD0(u16 adress, u32 data) { // OK int ret; ret = ( I2C_startCond() || I2C_writeByte(DW) || // I2C_writeByte(DATA_WRITE) || // I2C_writeByte(0xe0) || // I2C_writeByte(0x00) || // --> write in D1 Memory I2C_writeByte(0x00) || // I2C_writeByte(0x01) || // --> 1 word to write I2C_writeByte((u8)(adress >> 8)) || // I2C_writeByte((u8)(adress & 0xFF)) || // --> adress I2C_writeByte((u8)(data >> 24)) || // I2C_writeByte((u8)(data >> 16)) || // I2C_writeByte((u8)(data >> 8)) || // I2C_writeByte((u8)(data & 0xFF)) || // --> data I2C_stopCond() ); wait(500); return ret; } /*-----------------------------------------------------------------------*/ int writeMemoryD1(u16 adress, u32 data) { int ret; ret = ( I2C_startCond() || I2C_writeByte(DW) || // I2C_writeByte(DATA_WRITE) || // I2C_writeByte(0xf0) || // I2C_writeByte(0x00) || // --> write in D1 Memory I2C_writeByte(0x00) || // I2C_writeByte(0x01) || // --> 1 word to write I2C_writeByte((u8)(adress >> 8)) || // I2C_writeByte((u8)(adress & 0xFF)) || // --> adress I2C_writeByte((u8)(data >> 24)) || // I2C_writeByte((u8)(data >> 16)) || // I2C_writeByte((u8)(data >> 8)) || // I2C_writeByte((u8)(data & 0xFF)) || // --> data I2C_stopCond() ); wait(500); return ret; } /*-----------------------------------------------------------------------*/ int readMemoryD0(u16 adress, u32 * data) { // OK u8 result1, result2, result3, result4; int ret; ret = ( I2C_startCond() || I2C_writeByte(DW) || // I2C_writeByte(DATA_WRITE) || // I2C_writeByte(0xc0) || // I2C_writeByte(0x00) || // --> write in D0 Memory I2C_writeByte(0x00) || // I2C_writeByte(0x01) || // --> 1 word to write I2C_writeByte((u8)(adress >> 8)) || // I2C_writeByte((u8)(adress & 0xFF)) || // --> adress I2C_stopCond() ); wait(10); ret |= ( I2C_startCond() || I2C_writeByte(DW) || I2C_writeByte(DATA_READ) || I2C_repStartCond() || I2C_writeByte(DR) || I2C_readByte(&result1,0) || I2C_readByte(&result2,0) || I2C_readByte(&result3,0) || I2C_readByte(&result4,1) || I2C_stopCond() ); *data = result4; *data |= ((u32)result3 << 8); *data |= ((u32)result2 << 16); *data |= ((u32)result1 << 24); wait(500); return ret; } /*-----------------------------------------------------------------------*/ int readMemoryD1(u16 adress, u32 * data) { u8 result1, result2, result3, result4; int ret; ret = ( I2C_startCond() || I2C_writeByte(DW) || // I2C_writeByte(DATA_WRITE) || // I2C_writeByte(0xd0) || // I2C_writeByte(0x00) || // --> write in D1 Memory I2C_writeByte(0x00) || // I2C_writeByte(0x01) || // --> 1 word to write I2C_writeByte((u8)(adress >> 8)) || // I2C_writeByte((u8)(adress & 0xFF)) || // --> adress I2C_stopCond() ); wait(10); ret |= ( I2C_startCond() || I2C_writeByte(DW) || I2C_writeByte(DATA_READ) || I2C_repStartCond() || I2C_writeByte(DR) || I2C_readByte(&result1,0) || I2C_readByte(&result2,0) || I2C_readByte(&result3,0) || I2C_readByte(&result4,1) || I2C_stopCond() ); *data = result4; *data |= ((u32)result3 << 8); *data |= ((u32)result2 << 16); *data |= ((u32)result1 << 24); wait(500); return ret; } /*-----------------------------------------------------------------------*/ int readIcVersion(u32 * data) { // OK u8 result1, result2, result3, result4; int ret; ret = ( I2C_startCond() || I2C_writeByte(DW) || I2C_writeByte(0x68) || I2C_writeByte(0x70) || I2C_writeByte(0x00) || I2C_stopCond() ); wait(10); ret |= ( I2C_startCond() || I2C_writeByte(DW) || I2C_writeByte(0x69) || I2C_repStartCond() || I2C_writeByte(DR) || I2C_readByte(&result1,0) || I2C_readByte(&result2,0) || I2C_readByte(&result3,0) || I2C_readByte(&result4,1) || I2C_stopCond() ); *data = (u32)result4; *data |= ((u32)result3 << 8); *data |= ((u32)result2 << 16); *data |= ((u32)result1 << 24); wait(500); return ret; } /*-----------------------------------------------------------------------*/ int writeCodec(u8 addr, u16 data) { int ret; ret = ( I2C_startCond() || I2C_writeByte(DW) || I2C_writeByte(CODEC_WRITE) || I2C_writeByte(00) || I2C_writeByte(addr) || I2C_writeByte((u8)(data >> 8)) || // left part I2C_writeByte((u8)(data & 0xFF)) || // right part I2C_stopCond() ); wait(500); return ret; } /*-----------------------------------------------------------------------*/ int readCodec(u8 addr, u16 * data) { u8 result1, result2; int ret; ret = ( I2C_startCond() || I2C_writeByte(DW) || I2C_writeByte(CODEC_WRITE) || I2C_writeByte(0x00) || I2C_writeByte(addr) || I2C_stopCond() ); wait(10); ret |= ( I2C_startCond() || I2C_writeByte(DW) || I2C_writeByte(CODEC_READ) || I2C_repStartCond() || I2C_writeByte(DR) || I2C_readByte(&result1,0) || I2C_readByte(&result2,1) || I2C_stopCond() ); *data = (u16)result2; *data |= ((u16)result1 << 8); wait(500); return ret; } /*-----------------------------------------------------------------------*/ void printMemory(int D, char * s, u16 address) { u32 * data; if (D == 0) { readMemoryD0(address, data); } else { readMemoryD1(address, data); } printf("%s : UP %X DOWN %X" NL, s, (u16)(*data >> 16), (u16)(*data)); } /*-----------------------------------------------------------------------*/ void printCodec(char * s, u16 address, u16 * data) { readCodec(address, data); printf("%s : %X" NL,s,(u16)(*data)); } /*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/ void bt_cb( call_data_t call_data, cb_data_t cb_data ) { // printf("IN METHOD bt_cb"NL); demandOnProcess = 1; } /*-----------------------------------------------------------------------*/ void send_data_request(call_data_t call_data /* cid */, cb_data_t cb_data) { u8 error_code; more[0] = 1; demandOnProcess += 1; btn_bt_data_send(getChan((u16)cid), more, 3, &error_code); return; } /*-----------------------------------------------------------------------*/ void feedMas(void) { /* This method will look at the EOD signal. If the MAS is ready to receive some datas, this method will send data until the EOD signal will be down. A call to this method will take between 1 us (EOD down) and 2 ms (MAS buffer completly empty, in particular at startup), with an average value of about 1 ms (depending on the bitrate). Because this method goes pretty quickly, it's possible to call it very often, for example after each other process (if the MAS needs Data, it's always a good thing to send some to him, if it does'nt need, 2us is not a too big waste of CPU time One has the choice between serial or parallel sending, but as explained in the report, serial goes quicker */ #ifdef USE_PIO if (!(bufferCritical)) { while (inp(PINB) & 0x1) { // EOD HIGH // set PIO if (*read_pointer & 0x1) sbi(PORTF, PIN7); else cbi(PORTF, PIN7); // PI19 PIN7 // PE7 if (*read_pointer & 0x2) sbi(PORTF, PIN6); else cbi(PORTF, PIN6); // PI18 PIN6 // PE6 if (*read_pointer & 0x4) sbi(PORTF, PIN5); else cbi(PORTF, PIN5); // PI17 PIN5 // PE5 if (*read_pointer & 0x8) sbi(PORTF, PIN4); else cbi(PORTF, PIN4); // PI16 PIN3 // PE3 if (*read_pointer & 0x10) sbi(PORTE, PIN3); else cbi(PORTE, PIN3); // PI15 PIN4 // PF4 if (*read_pointer & 0x20) sbi(PORTE, PIN5); else cbi(PORTE, PIN5); // PI14 PIN5 // PF5 if (*read_pointer & 0x40) sbi(PORTE, PIN6); else cbi(PORTE, PIN6); // PI13 PIN6 // PF6 if (*read_pointer & 0x80) sbi(PORTE, PIN7); else cbi(PORTE, PIN7); // PI12 PIN7 // PF7 sbi(CONTROL_PORT, PR); // raise PR if (read_pointer == MP3_BUFFER_END_ADDR) { // active wait read_pointer = MP3_BUFFER_START_ADDR; // active wait } // active wait else { // active wait read_pointer++; // active wait } // active wait while(!(inp(PINB) & 0x2)); // wait for RTR up cbi(CONTROL_PORT, PR); // low PR } } #endif sbi(PORTF, PIN4); #ifdef USE_SERIAL if (!(bufferCritical)) { outp(BV(SPE) | BV (MSTR) | BV(CPHA) | BV(SPR0), SPCR); // enable SPI, master mode, clock rate = f/4 while (inp(PINE) & 0x8) { // EOD HIGH (EOD = E3 --> 0x8) outp(*read_pointer, SPDR); if (read_pointer == MP3_BUFFER_END_ADDR) { read_pointer = MP3_BUFFER_START_ADDR; } else { read_pointer++; } while(!(inp(SPSR) & (BV(SPIF)))); } } cbi(PORTF, PIN4); #endif } /*-----------------------------------------------------------------------*/ void data_received_cb( call_data_t call_data, cb_data_t cb_data ) { /* this method is called each time the bluetooth module receive something. It mainly takes the incoming data and copy them to the buffer. Then it calls some other action (in the demand protocol, for example, it will look if there's place in buffer, and if its the case, ask the PC for new datas. This method is also very important, because : - Normally, the BTnode is "idle" (no events, no running method) - When a packet comes, this method is called. This method has then to trigger all the action that we have to do (calling event). A the end, when this method is finished, and all his triggered action also, the Btnode system software will then return to idle mode. If the actions are not correctly done in this method, there will be probably a failure (this method, for example, sould not trigger more then one EVENTS per call. If each time, this method register 3 Events to the dispatcher, the Event queue will very quickly suffer an overflow Dont forget also to call at least once the method "feedMas". Remember that this method is called after an idle time, during that the MAS has received nothing. */ u16 i; u16 length; u8 * data; u16 diff; // extracting the information about the reception u8 data_idx = (u8)((call_data & 0x0000FF) >> 0); length = l2cap_data_buffer[data_idx].payload_length; data = l2cap_data_buffer[data_idx].data; // looking if program in filling mode. If in filling mode, the MAS isnt playing, // so there's no real time contraints, and new datas may be ordered directly until // the buffer will be full if (bufferFilling) { if (firstTransfer) { printf("Filling buffer..."); firstTransfer = 0; read_pointer = MP3_BUFFER_END_ADDR; // assign once the global variable "cid" cid = l2cap_data_buffer[data_idx].local_cid; } // MAS not playing yet, filling the buffer for (i = 0 ; i < length ; i++) { *write_pointer = *(data + i); if (write_pointer == MP3_BUFFER_END_ADDR) { write_pointer = MP3_BUFFER_START_ADDR; } else { write_pointer++; } } quantity_received += length; // free the buffer for incoming l2cap/rfcomm data packets btn_bt_data_free( data_idx ); if (quantity_received == PACKET_SIZE) { // End of the 1kb packet quantity_received = 0; // a full packet has been received, one can then decrement // the "demandOnProcess" flag demandOnProcess--; // computing the difference between the two pointers if (write_pointer >= read_pointer) { diff = MP3_BUFFER_LENGTH - (u16)(write_pointer - read_pointer); } else { diff = read_pointer - write_pointer; } // testing if buffer quasi full if (diff <= PACKET_SIZE) { // full - start playing bufferFilling = FALSE; printf("done"NL); demandOnProcess = 0; // register an PLAY_EVENT, that will call the play method // where real-time tests for buffer handling an data ordering // are done btn_disp_put_event(PLAY_EVENT, (call_data_t)cid); } else { // not full - ask for more if (demandOnProcess == 0) { // register a "DATA_ACK_EVENT" correspond to order new data btn_disp_put_event(DATA_ACK_EVENT, (call_data_t)cid); } } } } else { // feed MAS feedMas(); quantity_received += length; // this loop takes about 225 us to be executed for (i = 0 ; i < length ; i++) { *write_pointer = *(data + i); if (write_pointer == MP3_BUFFER_END_ADDR) { write_pointer = MP3_BUFFER_START_ADDR; } else { write_pointer++; } } btn_bt_data_free( data_idx ); if (quantity_received == PACKET_SIZE) { // End of the 1kb packet // if the transmission of one PACKET_SIZE is finished, one can // launch the Play method quantity_received = 0; demandOnProcess--; if (demandOnProcess == 0) { btn_disp_put_event(PLAY_EVENT, (call_data_t)cid); } } } if ((length >= 4) && (strncmp(data, "END", 3) == 0)) { endOfFile = 1; if (bufferFilling) { printf("done"NL); } } } /*-----------------------------------------------------------------------*/ void buffer_init(void) { // because the buffer is "handly" adressed, one can eventually place // here a mechanism of checksum that will see if the memory exists // (for example : detecting non-patched Btnode) u16 i = 0; printf("Initializing buffer..."); write_pointer = MP3_BUFFER_START_ADDR; for (i = 0 ; i < MP3_BUFFER_LENGTH ; i++) { *write_pointer = 0; write_pointer++; } // place pointers at the beginning read_pointer = MP3_BUFFER_START_ADDR; write_pointer = MP3_BUFFER_START_ADDR; printf("done"NL); } /*-----------------------------------------------------------------------*/ void play( call_data_t call_data, cb_data_t cb_data ) { /* This method is build to manage the balance between entering and exiting data in and out the buffer. Using three different level of bufferfilling (NORMAL, LOW, CRITICAL), one can take different actions, for example stoping the send of data to the MAS when buffer is CRITICAL, and then let the buffer being full again, before restart the play The further idea is to modulate the throwput of the Bluetooth with the state of the Buffer. If the Buffer is going low, the idea is to demand more datas to the PC. One has tried many things to get this, but without sucess. See also file masDriver_Streaming.c about this problem */ u8 error_code; feedMas(); if (bufferLow) { // testing if buffer is going low if (write_pointer >= read_pointer) { // testing if buffer critical if ((u16)(write_pointer - read_pointer) < (u16)BUFFER_CRITICAL_THRESHOLD) { // buffer empty, stop the sending of data to MAS. Music will stop printf("buffer underrun, filling it again..."); bufferLow = FALSE; bufferFilling = TRUE; // order one packet to the PC (with more[0] = 1) demandOnProcess++; more[0] = 1; btn_bt_data_send(getChan((u16)cid), more, 3, &error_code); return; } // testing if buffer always low if ((u16)(write_pointer - read_pointer) >= (u16)BUFFER_LOW_THRESHOLD) { bufferLow = FALSE; printf("now OK again"NL); } } else { // testing if buffer critical if ( (u16)(read_pointer - write_pointer) > (u16)(MP3_BUFFER_LENGTH - BUFFER_CRITICAL_THRESHOLD) ) { printf("buffer underrun, filling it again..."NL); bufferLow = FALSE; bufferFilling = TRUE; // order one packet to the PC (with more[0] = 1) demandOnProcess++; more[0] = 1; btn_bt_data_send(getChan((u16)cid), more, 3, &error_code); return; } // testing if buffer always low if ( (u16)(read_pointer - write_pointer) <= (u16)(MP3_BUFFER_LENGTH - BUFFER_LOW_THRESHOLD) ) { bufferLow = FALSE; printf("now OK again"NL); } } } else { if (write_pointer >= read_pointer) { if ((u16)(write_pointer - read_pointer) < (u16)BUFFER_LOW_THRESHOLD) { printf("buffer going low..."); bufferLow = TRUE; } } else { if ( (u16)(read_pointer - write_pointer) > (u16)(MP3_BUFFER_LENGTH - BUFFER_LOW_THRESHOLD) ) { printf("buffer going low..."); bufferLow = TRUE; } } } if (read_pointer >= write_pointer) { if ( ( (u16)(read_pointer - write_pointer) > BUFFER_PLACE_THRESHOLD) && (!(endOfFile)) ) { if (!(demandOnProcess)) { // ask for data if (bufferLow) { more[0] = 1; demandOnProcess += 1; btn_bt_data_send(getChan((u16)cid), more, 3, &error_code); } else { more[0] = 1; demandOnProcess += 1; btn_bt_data_send(getChan((u16)cid), more, 3, &error_code); } return; } } } else { if ( ((u16)(read_pointer - MP3_BUFFER_START_ADDR + MP3_BUFFER_END_ADDR - write_pointer) > (u16)(BUFFER_PLACE_THRESHOLD)) && (!(endOfFile)) ) { if (!(demandOnProcess)) { // ask for data if (bufferLow) { more[0] = 1; demandOnProcess += 1; btn_bt_data_send(getChan((u16)cid), more, 3, &error_code); } else { more[0] = 1; demandOnProcess += 1; btn_bt_data_send(getChan((u16)cid), more, 3, &error_code); } return; } } } btn_disp_put_event(PROCEED_COMMAND_EVENT, (call_data_t)cid); } /*-----------------------------------------------------------------------*/ void commands( call_data_t call_data, cb_data_t cb_data ) { // place here what you want to do when system has a bit of idle time // // this method is typically the designed place where one shall look // if user is pushing on a button to, e.g change the volume or so // // but WARNING :this method shall to take too long to be executed. // // After some test, the execution times of this methods shall be bigger than // // | bitrate | recommended | critical | // ---------------------------------------------------- // 56 kbps < 55 ms 90 ms // 64 kbps < 50 ms 70 ms // 80 kbps < 45 ms 70 ms // 96 kbps < 20 ms ? // // Note that at 96 kbps (with the current version), the buffer will also // need some data, so this method will be executed very rarely. It's // eventually possible to decrease the BUFFER_SPACE_THRESHOLD this method // more often executed // // Do also not forget to call again the play method, using fot that // the event mechanism btn_disp_put_event(PLAY_EVENT, (call_data_t)cid); } /*-----------------------------------------------------------------------*/ void stop( call_data_t call_data, cb_data_t cb_data ) { } /*-----------------------------------------------------------------------*/ void transfer_init(void) { /* init flags */ quantity_received = 0; bufferFilling = 1; endOfFile = 0; firstTransfer = 1; demandOnProcess = 0; bufferLow = 0; bufferCritical = 0; #ifdef USE_SERIAL serial_mask = 0x80; // 1 << 7 #endif microsec = 50; } /*-----------------------------------------------------------------------*/ u8 mas_init(void) { /* this method prepares the Microcontroller and the MAS for the operations. First one has to configure the I/O Pins of the micro controller (all sbi and cbi instructions)...*/ u8 boolean = 0; printf("Preparing IO to talk with MAS..."); // configure input pin PD7 / PUP cbi(DDRD, PIN6); cbi(PORTD, PIN6); #ifdef USE_PIO // configure input pin PB0 / EOD cbi(CONTROL_PORT, EOD); cbi(CONTROL_PORT_DDR, EOD); // configure input pin PB1 / RTR cbi(CONTROL_PORT, RTR /*works with EOD*/); cbi(CONTROL_PORT_DDR, RTR /*works with EOD*/); // configure output pin PB2 / PCS sbi(CONTROL_PORT_DDR, PCS); cbi(CONTROL_PORT, PCS); // configure output pin PB3 / PR sbi(CONTROL_PORT_DDR, PR); cbi(CONTROL_PORT, PR); // confiure output PIO sbi(MSB_PIO_PORT_DDR, PI19); sbi(MSB_PIO_PORT_DDR, PI18); sbi(MSB_PIO_PORT_DDR, PI17); sbi(MSB_PIO_PORT_DDR, PI16); sbi(LSB_PIO_PORT_DDR, PI15); sbi(LSB_PIO_PORT_DDR, PI14); sbi(LSB_PIO_PORT_DDR, PI13); sbi(LSB_PIO_PORT_DDR, PI12); cbi(MSB_PIO_PORT, PI19); cbi(MSB_PIO_PORT, PI18); cbi(MSB_PIO_PORT, PI17); cbi(MSB_PIO_PORT, PI16); cbi(LSB_PIO_PORT, PI15); cbi(LSB_PIO_PORT, PI14); cbi(LSB_PIO_PORT, PI13); cbi(LSB_PIO_PORT, PI12); #endif #ifdef USE_SERIAL // configure built-in SPI outp(BV(PIN2) | BV(PIN1) | BV(PIN0), DDRB); // set MOSI and SCK output, SS and MISO inputs // configure input pin PE3 / EOD cbi(PORTE, PIN3); cbi(DDRE, PIN3); outp(BV(SPI2X), SPSR); #endif #ifdef DEBUG_MODE // configure output pin PF4 sbi(DDRF, PIN4); cbi(PORTF, PIN4); sbi(DDRF, PIN5); cbi(PORTF, PIN5); sbi(DDRF, PIN6); cbi(PORTF, PIN6); sbi(DDRF, PIN7); cbi(PORTF, PIN7); #endif printf("done"NL); /* All the pins are correctly set, now one can directly talk with the MAS using the recently configured pins. Before all one have to reset the MAS. Then write to the direct configurtion register to enable the MAS and disable its Power converter (not use with the PCB rev3). Then write to memory to set some parameters (input interface, clockconfig, etc) Finally write to codec register to set up the volume and the mixing coefficients. */ printf("Configuring MAS..."); resetMAS(); writeControlRegister(CTRL_REG_DSP_ON_NODC); wait(1000); writeFirstDCDCconfigurationRegister(DCCF_REG_RESET); writeSecondDCDCconfigurationRegister(DCFR_REG_RESET); boolean |= writeMemoryD0(INTERFACECONTROL, INTCTRL_MEM_STANDARD); boolean |= writeMemoryD0(OUT_LL, OUT_LL_STEREO); boolean |= writeMemoryD0(OUT_LR, OUT_LR_STEREO); boolean |= writeMemoryD0(OUT_RL, OUT_RL_STEREO); boolean |= writeMemoryD0(OUT_RR, OUT_RR_STEREO); boolean |= writeMemoryD0(OUTCLKCONFIG, OUTCLKCONFIG_MEM_STANDARD); // One writes to the IOCONTROLMAIN register at the end, because // it's first bit (0) is use as validate bit for the other registers #ifdef USE_PIO boolean |= writeMemoryD0(IOCONTROLMAIN, IOCTRL_MEM_STANDARD_IN_PIO); #endif #ifdef USE_SERIAL boolean |= writeMemoryD0(IOCONTROLMAIN, IOCTRL_MEM_STANDARD_IN_SERIAL); #endif boolean |= writeControlRegister(CTRL_REG_BOTH_ON_NODC); wait(2000); // Set the volume for the stream coming for the Analogical input (ADC) boolean |= writeCodec(DAC_IN_ADC, DAC_IN_50_PERCENT); boolean |= writeCodec(CONV_CONF, CONV_CONF_LINE_IN); boolean |= writeCodec(ADC_IN_MODE, ADC_IN_STEREO); boolean |= writeCodec(VOLUME, VOLUME_MAX); // Set the volume for the stream coming out the DSP (mp3 stream) boolean |= writeCodec(DAC_IN_DSP, DAC_IN_100_PERCENT); wait(200); // Enable the decoding. After that, EOD shall go up and the operating // software may start boolean |= writeMemoryD0(APPSELECT, APPSELECT_MEM_BOTH_DECODE); if (boolean) { printf("NOT DONE" NL); printf("Please start again. If problem persist, just cry" NL); } else { printf("done"NL); } return boolean; } /* ------------------------------------------------------------------------- * main() * ------------------------------------------------------------------------- */ int main( int argc, char *argv[] ) { u8 error_code; btn_init( argc, argv ); btn_system_init( argc, argv, data_buf, L2CAP_BUF_LEN ); btn_rtc_init(); btn_uart1_init(); printf(NL); printf("+------------------------------------------------+"NL); printf("| MAS_DRIVER 1.3 |"NL); printf("+------------------------------------------------+"NL NL); led_init(); I2C_init(); buffer_init(); transfer_init(); mas_init(); btn_bt_change_local_name( "btnode 2.2", &error_code ); btn_bt_psm_add( 8197 ); // register function "cb" for event "event". when "event" occurs, // the callback function "cb" is called with "cb_data" as its second // argument // event cb cb_data btn_disp_ev_reg( BT_CONNECTION_EV, bt_cb, BT_CONNECTION_EV ); btn_disp_ev_reg( BT_DISCONNECT_EV, bt_cb, BT_DISCONNECT_EV ); btn_disp_ev_reg( BT_ROLE_CHANGE_EV, bt_cb, BT_ROLE_CHANGE_EV ); btn_disp_ev_reg( BT_DATA_RCV_EV, data_received_cb, BT_DATA_RCV_EV ); btn_disp_ev_reg( BT_RFCOMM_RCV_EV, data_received_cb, BT_RFCOMM_RCV_EV ); btn_disp_ev_reg( DATA_ACK_EVENT, send_data_request, DATA_ACK_EVENT ); btn_disp_ev_reg( PLAY_EVENT, play, PLAY_EVENT); btn_disp_ev_reg( FILE_TRANSFER_DONE_EVENT, stop, FILE_TRANSFER_DONE_EVENT); btn_disp_ev_reg( PROCEED_COMMAND_EVENT, commands, PROCEED_COMMAND_EVENT); btn_disp_run(); return 0; }