Go Down

Topic: ChibiOS and Audio? (Read 1 time) previous topic - next topic

tms8c8

I could use some guidance implementing the Audio library. Currently, I've got several tasks that are time critical. I am using the RTOS "Chibi" with success. There are two primary tasks that need to happen at 500 uS and 500 mS intervals. The 500 mS task updates an LCD screen, which does use up some resources. However, my back-of-the-envelope calculations suggest that I should still have plenty of processing power to play a sound file.

I created another thread to play the audio file. I set a time interval for this thread as well - it should be called every 2 uS. However, even changing around the thread priorities, the sound is very choppy. Is there a software solution to this problem or do I need a separate audio chip?

Code: [Select]

//ChibiOS Tests
#include <SD.h>
#include <SPI.h>
#include <Audio.h>
#include <ChibiOS_ARM.h>
#include <LiquidCrystal.h>

//declare variables
File myFile;
const int S= 1024;
short buffer[S];

Thread* tp1;
Thread* tp2;
Thread* tp3;

LiquidCrystal lcd(47,48,49,50,51,52);

static WORKING_AREA(waTask1, 1024);
static msg_t Task1(void *arg){
  systime_t displayCheckTime = chTimeNow();
  while (!chThdShouldTerminate()){
    task1CheckTime += MS2ST(500);
    doTask1();
    chThdSleepUntil(task1CheckTime);
  }
  return 0;
}

static WORKING_AREA(waTask2, 14336);
static msg_t Task2(void *arg){
  systime_t task2CheckTime = chTimeNow();
  while(!chThdShouldTerminate()){
    task2CheckTime += US2ST(500);
    doTask2();
    chThdSleepUntil(task2CheckTime);
  }
}

static WORKING_AREA(waMusic, 10240); 
static msg_t Music(void *arg){
  systime_t musicCheckTime = chTimeNow();
  while(!chThdShouldTerminate()){
    musicCheckTime += US2ST(2);
    playMusic();
    chThdSleepUntil(musicCheckTime);
  }
}

void setup() {
  Serial.begin(9600);
  // wait for USB Serial
  while (!Serial) {}
 
  lcd.begin(20,4);

  //do some stuff

  Serial.println("Initializing SD card...");
  lcd.print("Initializing SD");
  pinMode(4, OUTPUT);
  if (!SD.begin(4)) {
    Serial.println(" failed!");
    lcd.print("OOPS!");
    return;
  }
  Serial.println(" done.");
  // hi-speed SPI transfers
  SPI.setClockDivider(4);
 
  // 44100Khz stereo => 88200 sample rate
  // 100 mSec of prebuffering.
  Audio.begin(88200, 100);

  chBegin(mainThread);
  // chBegin never returns, main thread continues with mainThread()
  while(1) {}
}
//------------------------------------------------------------------------------
// main thread runs at NORMALPRIO
void mainThread() {
 
      myFile = SD.open("test.wav");
  if (!myFile) {
    // if the file didn't open, print an error and stop
    Serial.println("error opening test.wav");
    while (true);
  }
  if (myFile) Serial.print("Loaded file!");

  // start thread
  tp1 = chThdCreateStatic(waTask1, sizeof(waTask1),
                          NORMALPRIO, Task1, NULL);

  // start thread
  tp2 = chThdCreateStatic(waTask2, sizeof(waTask2),
                          NORMALPRIO + 1, Firing, NULL);
               
   // start music thread
   tp3 = chThdCreateStatic(waMusic, sizeof(waMusic),
                          NORMALPRIO, Music, NULL);
   
  while(1){ 
 
    if (finished){
    // print memory use
    chThdTerminate(tp1);
    chThdTerminate(tp2);

    }
  }
                   
}
//------------------------------------------------------------------------------
void loop() {
// not used
}

void doTask1() {
//do stuff
}

void doTask2(){
//print a couple lines to LCD screen
}

void playMusic(){
  //Serial.println("Playing");
  // until the file is not finished                       
  while(1){                       
  //int count=0;

  if (myFile.available()) {
    // read from the file into buffer
    myFile.read(buffer, sizeof(buffer));
    //Serial.println("reading file");

    // Prepare samples
    int volume = 1023;
    Audio.prepare(buffer, S, volume);
    // Feed samples to audio
    //Serial.println("prepared samples");
    Audio.write(buffer, S);

    // Every 100 block print a '.'
    //count++;
    //if (count == 100) {
      //Serial.print(".");
      //count = 0;
    //}
  }
  if (!myFile.available()) myFile.close();

  //Serial.println("End of file. Thank you for listening!");
  }
}

fat16lib

#1
Feb 02, 2013, 03:55 pm Last Edit: Feb 02, 2013, 04:22 pm by fat16lib Reason: 1
You are running all threads at the same priority so the scheduling is round robin with a 20 millisecond quantum.

playMusic has a while(1) loop so no other thread will run if you raise the priority of the Music thread.

Also the system tick is 1 millisecond so you can't sleep for less that that. US2ST() will return one tick for arguments less than 1000.
Code: [Select]

#define US2ST(usec) ((systime_t)(((((usec) - 1L) * CH_FREQUENCY) / 1000000L) + 1L))

CH_FREQUENCY is 1000.

Edit: I looked at the Due Audio library and it just isn't designed for muti-threading.  

It has stuff like this:

Code: [Select]

while (next == running)
;


This will cause a high priority thread to starve low priority threads.

A proper library for muti-threading would wake the high priority thread when the the DACC_ISR_HANDLER interrupt happened.

cmaglie


It has stuff like this:

Code: [Select]

while (next == running)
;


This will cause a high priority thread to starve low priority threads.


This is an interesting point, what can we do to make the library more thread-friendly?
The most obvious answer for a cooperative scheduler is to put a yield() inside the loop, but my question is: is this solution compatible with an RTOS? Can we find a solution that satisfy both?
C.

fat16lib

This is the kind of problem that preemptive RTOSs were designed for.  Coop scheduling really isn't very good.

You would need to add a hook to ISR functions.  Generally the RTOS thread wakeup is simple.

Here is my pin change example for ChibiOS:

Code: [Select]

void isrFcn() {

  // on ARM CH_IRQ_PROLOGUE is void
  CH_IRQ_PROLOGUE();

  // Need this because of nested interrupts on Cortex M
  chSysLockFromIsr();

  // wake the waiting task
  chBSemSignalI(&isrSem);
  chSysUnlockFromIsr();

  // Perform rescheduling if required.
  CH_IRQ_EPILOGUE();
}


Here is the call to connect it to pin change.
Code: [Select]

  // attach interrupt function
  attachInterrupt(INPUT_PIN, isrFcn, RISING);

tms8c8

Thanks Fat16lib! I fixed the while(1) loop yesterday and I had been playing with priorities but I never could eliminate the problems for the reasons you pointed out. I don't have time to work on the audio library right now, but in a couple of months I may start trying to tune it up for use with Chibi.

Also, thanks for pointing out that the system tick is only 1 mS. Somehow, I missed that important detail in the literature.  :smiley-red:

Go Up