Source

maple-stm32-learn / stream_play / stream_play.pde

/* 
  stream_play.pde (MAPLE + AudioCodec Board) test sketch

  Additional it shows how to use Serial3 USART to communicate with the board during
  processing.

  Hardware connections
  --------------------
  From the the maple documentaiton the USART Pin Map
  Tx     Rx      Ck      cts     rts
  Serial3	D29	D30	D31	D32	D33
  (this also applies to the Maple RET6 edition)
  These pins are also listed as being ***5V tolerant***


  I chose Serial3 because these pins are on the 2x8 header "EXT" block which aren't
  connected to the audio codec shield.


  So I connected 3 wires to my FTDI basic board:
  D29 (Tx) to the Rx on the FTDI basic board
  D30 (Rx) to the Tx on the FTDI basic board
  GND to GND on on the FTDI basic board
*/

// setup codec parameters
// must be done before #includes
// see readme file in libraries folder for explanations
#define SAMPLE_RATE 44 // 44.1kHz sample rate
#define ADCS 2 // use both ADCs

// try faster 9600, 19200, 14400, 38400, 57600, 56000,
// 115200, 128000, 230400,460800, 153600, 256000,921600
// 2000000, 2500000
// the FTDI 232R datasheet says it can go to 3 Mbaud
// the stm32F103 data sheet mentioned a minimum of 2.25 Mbits/s or one at 4 Mbits/s

// #define BAUDRATE 9600    // works, safe
// #define BAUDRATE 921600  // works
// #define BAUDRATE 1500000 // works
// #define BAUDRATE  1000000   // works
#define BAUDRATE  2000000   // works
      // #define BAUDRATE  3000000   // doesn't work (as expected)
// #define BAUDRATE 115200
   
// include necessary libraries
// note the Maple library
#include <AudioCodec_Maple.h>



#define PBUFSIZE  1024
#define NPBUFS 2


#define BUFFER_NO_DATA 0
// #define BUFFER_OLD_DATA 1
#define BUFFER_FRESH_DATA 2

int16 _buf0[PBUFSIZE];
int16 _buf1[PBUFSIZE];

class Buffer {
public:
  int16* buf;
  uint8 status;
  int16 rpos; // read position
  Buffer();
  int16 readnext(void);
  bool atend(void) { return (rpos == PBUFSIZE ? 1 : 0); };
  void serial_request_data(void); // through this in to get it to compile
};

Buffer::Buffer(void)
{
  status = BUFFER_NO_DATA;
  rpos = 0;
}

inline int16 Buffer::readnext(void)
{
  return buf[rpos++];
}


void Buffer::serial_request_data(void)
{
  // request packet of data from server

  uint16 lsb,msb;
  
  Serial3.write('U');

  for (int ii =0; ii < PBUFSIZE; ii++) {
    while(!Serial3.available())
      ;
    lsb=Serial3.read();
    while(!Serial3.available())
      ;
    msb=Serial3.read();
    buf[ii] = (msb << 8) + lsb;
  }
  rpos = 0;
  status = BUFFER_FRESH_DATA;
}


/***************** global variables *******************/

int gPlaySound = 0;
int gCount =0;

// create data variables for audio transfer
// even though there is no input needed, the codec requires stereo data
// note the uint16 typedef, there is no "unsigned int16" type
int16 left_in = 0; // in from codec (LINE_IN)
int16 right_in = 0;
int16 left_out = 0; // out to codec (HP_OUT)
int16 right_out = 0;


// create variables for ADC results
// it only has positive values -> unsigned
uint16 mod0_value = 0;
uint16 mod1_value = 0;
uint16 gCurBufNum=0;

struct Buffer gBuffers[NPBUFS];


void setup()
{
  SerialUSB.end(); // usb conflicts with the codec in this mode
  // a different operating mode which allows both
  // is in the works
  Serial3.begin(BAUDRATE);
  gCurBufNum = 0;
  gBuffers[0].status = 0;
  gBuffers[0].rpos = 0;
  gBuffers[0].buf = _buf0;
 
  gBuffers[1].status = 0;
  gBuffers[1].rpos = 0;
  gBuffers[0].buf = _buf1;
  // setup codec and microcontroller registers
  AudioCodec_init(); // call this last if you are setting up other things
}


void serial_term(void)
{
  uint8 notcurbuf_num = gCurBufNum^0x1; // probably more efficient to do this with xor

  if (Serial3.available()) {
    uint8 input = Serial3.read();
    switch(input) {

    case 'h':
      Serial3.println("Hello!");
      break;

    case 's': // stop
      gPlaySound = 0;
      break;

    case 'p': 
      {
	gPlaySound = 1;
      }
      break;

    case 'w': // wip
      gBuffers[0].status = BUFFER_NO_DATA;
      gBuffers[1].status = BUFFER_NO_DATA;
      break;

    case 't':
      gBuffers[0].serial_request_data();
      break;

    case '1':
      gBuffers[1].serial_request_data();
      break;
      
    case 'd': //debug
      Serial3.print("debug::");
      Serial3.println(gCount);
      Serial3.print("gCurBufNum::");
      Serial3.println(gCurBufNum);
      Serial3.print("gPlaySound::");
      Serial3.println(gPlaySound);
      Serial3.print("gBuffers[0]::status,rpos");
      Serial3.print(gBuffers[0].status);
      Serial3.println(gBuffers[0].rpos);
      Serial3.print("gBuffers[1]::status,rpos");
      Serial3.print(gBuffers[1].status);
      Serial3.println(gBuffers[1].rpos);
      Serial3.print("mod0_value::");
      Serial3.println(mod0_value);
      break;
	
      
    default: // -------------------------------
      // do nothing but really something is wrong!
      Serial3.write('e'); // signal error, unrecognized byte read
    }
  }

  if (!gPlaySound) {
    return;
  }
  
  // request data
  if (!(gBuffers[gCurBufNum].status == BUFFER_FRESH_DATA)) {
    gBuffers[gCurBufNum].serial_request_data();
  }

  if (!(gBuffers[notcurbuf_num].status == BUFFER_FRESH_DATA)) {
    gBuffers[notcurbuf_num].serial_request_data();
  }
  
}


void loop() {
  
  while (1) { // reduces clock jitter (? maybe because no function call?)
    // delay(1);
    serial_term();
    gCount++;
  }
}

// timer1 interrupt routine - all data processed here
void AudioCodec_interrupt() {
  Buffer* curbuf = &(gBuffers[gCurBufNum]);
  int16 * buf = curbuf->buf;
  // &'s are necessary on data_in variables

  AudioCodec_data(&left_in, &right_in, left_out, right_out);

  if (gPlaySound && (curbuf->status == BUFFER_FRESH_DATA)) {
      // create some temporary variables
      // these tend to work faster than using the main data variables
      // as they arent fetched and stored all the time
    int16 temp1;
  
    // create a variable frequency and amplitude sinewave
    // fetch a sample from the lookup table
    // temp1 = curbuf->readnext();  // advances rpos
    temp1 = *(buf+curbuf->rpos);
    curbuf->rpos++;
    // step through table at rate determined by mod1
    // use upper byte of mod1 value to set the rate
    // and have an offset of 1 so there is always an increment.
      // if weve gone over the table boundary -> loop back
      // around to the other side.
    if (curbuf->rpos >= PBUFSIZE ) {
      curbuf->status = BUFFER_NO_DATA;
      curbuf->rpos=0;
      // flip the current buffer number and start reading from the other buffer
      gCurBufNum = (gCurBufNum ? 0 : 1);
    }
  
    // set amplitude with mod0
    // multiply our sinewave by the mod0 value
    // since it returns a 32bit value it needs to be scaled with ">> 16"
    // temp1 = (temp1 * mod0_value) >> 16;
    left_out = temp1; // put sinusoid out on left channel
    right_out = -temp1; // put inverted version out on right chanel

    // get ADC values
    // & is required before adc variables
  }
  AudioCodec_ADC(&mod0_value, &mod1_value);


  // you dont need to reti() with Maple
}