It is currently 24 Oct 2017 02:10

All times are UTC + 1 hour




Post new topic Reply to topic  [ 6 posts ] 
Author Message
PostPosted: 22 Nov 2011 20:37 
Offline

Joined: 29 Sep 2007 14:35
Posts: 167
Location: France
Hi to all,

I actually try to make working a very simple wav player that is limited to 8 bits / 16 kHz / mono wave format files. Audio files are saved on a FAT16 formated MMC card. PIC 18F4520, MMC reader connected on PORTC, playing commands done via push buttons connected on some lines of PORTA, and DAC (simple R/2R ladder) connected to PORTB. PIC run at 20 MHz, but problem is exactly the same at 40 MHz (10 MHz with 4x PLL). MikroPascal V5.30.

Speed access for MMC card reading don't seem to be the problem, but I'm not totally sure.

Problem encountered : samples of the wav file are regulary read during 32 ms, and for some reason PORTB stay at a fixed value during about 8 ms. So, I got "dropout" every 40 ms, without any data loss. Every 40 ms, a value stay "locked" during 8 ms. Sound effect is similar to a chopper / AM modulator with 100% / 0% modulation on a 4/1 time ratio.

I tried to get sample from MMC card and send them to portB on two manners :
1 - byte by byte with a little delay added between each byte to play the file at correct sample rate;
2 - with Timer0 interrupt.
On both cases, problem is exactly the same.

To visually see the problem, i made a screenshot of what I get with a 1 kHz sinus :
http://www.sonelec-musique.com/images2/electronique_lecteur_audio_001_graph_000a.gif

And here the project schematic :
http://www.sonelec-musique.com/images2/electronique_lecteur_audio_001.gif

Full code is following :

{
program electronique_lecteur_audio_001_18f4520;
Wave audio files player
Only support Wav files Mono / 8 bits / 16 kHz
----------------------------------------------------------
Copyrights Remy Mallard - sonelec-musique.com - 2011
http://www.sonelec-musique.com/electronique_realisations_lecteur_audio_001.html
----------------------------------------------------------
Developped under MikroPascal V5.30
Tested on EasyPic4 dev board
Clock : ext 20 MHz crystal (PLL disabled)
}

program electronique_lecteur_audio_001_18f4520;

const
  Line_Len = 43;
  cFileExists = 1;     // or = 0 (MMC_FAT_EXISTS) ???
  cTMR0H = $FE;        // $FE for 16 kHz, $FF for 22 kHz - Osc 20 MHz
  cTMR0L = $D4;        // $D4 for 16 kHz, $1F for 22 kHz - Osc 20 MHz
  AudioFile0 = 'audio0.wav';
  AudioFile1 = 'audio1.wav';
  AudioFile2 = 'audio2.wav';
  AudioFile3 = 'audio3.wav';
  AudioFile4 = 'audio4.wav';
  AudioFile5 = 'audio5.wav';
  AudioFile6 = 'audio6.wav';
  AudioFile10 = 'sinus1k.wav';

var
  // for writing to output pin on PIC18 family, always use latch
  Mmc_Chip_Select: sbit at LATC0_bit;
  Mmc_Chip_Select_Direction: sbit at TRISC0_bit;
  Out_LED_Green: sbit at LATD0_bit;
  Out_LED_Red: sbit at LATD1_bit;
  AudioFile: string[14]; // audio file to play
  iSample: byte;
  size: longint;
  bTimerInt: boolean;
  //buffer: array[512] of byte;

procedure Main_Init;
begin
  AudioFile := AudioFile1;  // not really needed
  TRISA := $FF;
  TRISB := $00;
  PORTB := $7F;  // max audio voltage / 2
  TRISD := $00;
  INTCON := 0;
  INTCON2 := 0;
  INTCON3 := 0;
  T0CON := 0;
  T0CON.T08BIT := 0;         // Timer0 in 16 bits mode
 
  // Timer0 Registers:16-Bit Mode; Prescaler=1:1; TMRH Preset=$FE; TMRL Preset=$CA; Freq=16 129,03226Hz; Period=62,00 µs
  T0CON.TMR0ON := 1;  // Timer0 On/Off Control bit: 1=Enables Timer0 / 0=Stops Timer0
  T0CON.T08BIT := 0;  // Timer0 8-bit/16-bit Control bit: 1=8-bit timer/counter / 0=16-bit timer/counter
  T0CON.T0CS   := 0;  // TMR0 Clock Source Select bit: 0=Internal Clock (CLKO) / 1=Transition on T0CKI pin
  T0CON.T0SE   := 0;  // TMR0 Source Edge Select bit: 0=low/high / 1=high/low
  T0CON.PSA    := 1;  // Prescaler Assignment bit: 0=Prescaler is assigned; 1=NOT assigned/bypassed
  T0CON.T0PS2  := 0;  // bits 2-0  PS2:PS0: Prescaler Select bits
  T0CON.T0PS1  := 0;
  T0CON.T0PS0  := 0;
  TMR0H := cTMR0H;    // preset for Timer0 MSB register
  TMR0L := cTMR0L;    // preset for Timer0 LSB register

  ADCON1 := ADCON1 or 0x0F;  // Configure AN pins as digital
  CMCON := CMCON or 7;       // Turn off comparators
  Out_LED_Green := 1;
  Out_LED_Red := 1;
  Delay_ms(500);
  Out_LED_Green := 0;
  Out_LED_Red := 0;
  // SPI init and MMC init are done in main proc
end;

procedure Interrupt;
begin
  // timer int occured ?
  if INTCON.TMR0IF then
  begin
    bTimerInt := true;
    TMR0H := cTMR0H;
    TMR0L := cTMR0L;
    INTCON.TMR0IF := false;
  end;
end;

procedure Timer_SetInterrupt(bState: boolean);
begin
  if bState then
  begin
    TMR0H := cTMR0H;
    TMR0L := cTMR0L;
    INTCON.TMR0IF := 0;
    INTCON.GIE := 1;
    INTCON.TMR0IE := 1;
    T0CON.TMR0ON := 1;
  end
  else
  begin
    T0CON.TMR0ON := 0;
    INTCON.TMR0IE := 0;
    INTCON.GIE := 0;
  end;
end;

procedure M_ReadAudioFile(idx: byte);
var
  iStatus: byte;
  bBadFile: boolean;
begin
  case idx of
    0 : AudioFile := AudioFile0;   // not exists (wanted, only for test)
    1 : AudioFile := AudioFile1;
    2 : AudioFile := AudioFile2;
    3 : AudioFile := AudioFile3;
    4 : AudioFile := AudioFile4;
    5 : AudioFile := AudioFile5;
    6 : AudioFile := AudioFile6
    else AudioFile := AudioFile10; // sinus 1 kHz file
  end;

  Out_LED_Green := 0;
  Out_LED_Red := 0;

  // verify audio file exists
  iStatus := Mmc_Fat_Exists(AudioFile);
 
  // verify audio file format (must be mono unsigned 8 bits / 16 kHz)
  // file format verification not yet implemented
  bBadFile := false;

  // if file exists then read it and light on green LED
  if (iStatus = cFileExists) and (not bBadFile) then
  begin
    Out_LED_Green := 1;

    Mmc_Fat_Assign(AudioFile, 0);
    Mmc_Fat_Reset(size);  // go to file start (procedure returns size of file)

    // for raw files, start reading at pos #0  (no header)
    // for wav files, start reading at pos #44 (previous bytes are header)
    Mmc_Fat_Seek(44);
    if size > 44 then
      size := size - 44;

    // use of timer0 to seq samples
    Timer_SetInterrupt(true);
    while size > 1 do
    begin
      if bTimerInt then      // wait next TMR0 int before take next sample
      begin
        Mmc_Fat_Read(iSample);
        PORTB := iSample;    // Write (send) audio sample data to PORTB
        Dec(size);
        bTimerInt := false;
      end;
    end;
    Timer_SetInterrupt(false);

    // no timer used, delay added to slow down sample rate
    {
    while size > 1 do
    begin
      Mmc_Fat_Read(iSample);
      PORTB := iSample;    // Write (send) audio sample data to PORTB
      Dec(size);
      Delay_us(40);
    end;
    }
   
    PORTB := $7F;  // max audio voltage / 2
    Out_LED_Green := 0;
  end
 
  // if file not exists then light on red LED
  else
  begin
    Out_LED_Red := 1;
    Delay_ms(500);
    Out_LED_Red := 0;
  end;

end;

begin
  Main_Init;

  // Initialize SPI module at low speed
  SPI1_Init_Advanced(_SPI_MASTER_OSC_DIV64, _SPI_DATA_SAMPLE_MIDDLE, _SPI_CLK_IDLE_LOW, _SPI_LOW_2_HIGH);

  // Note: Mmc_Fat_Init tries to initialize a card more than once.
  if Mmc_Fat_Init = 0 then
  begin

    // reinitialize SPI module at higher speed
    SPI1_Init_Advanced(_SPI_MASTER_OSC_DIV4, _SPI_DATA_SAMPLE_MIDDLE, _SPI_CLK_IDLE_LOW, _SPI_LOW_2_HIGH);

    // debug
    M_ReadAudioFile(10);

    // play file related to button pressed
    while true do
    begin
      if button(PORTA, 0, 100, 0) then
        M_ReadAudioFile(1);
      if button(PORTA, 1, 100, 0) then
        M_ReadAudioFile(2);
      if button(PORTA, 2, 100, 0) then
        M_ReadAudioFile(3);
      if button(PORTA, 3, 100, 0) then
        M_ReadAudioFile(4);
      if button(PORTA, 4, 100, 0) then
        M_ReadAudioFile(5);
      if button(PORTA, 5, 100, 0) then
        M_ReadAudioFile(6);
    end;

  end
  else

  // MMC FAT init not OK
  begin
    // blink red LED in infinite loop  (need PIC reset / restart)
    while true do
    begin
      Out_Led_Red := 1;
      delay_ms(250);
      Out_Led_Red := 0;
      delay_ms(250);
    end;
  end;

end.


I think this is a stupid configuration of my own, but can't found it :D
Any idea ?

Thanks for any help or suggestion,
Remy

Edit 22/11/2011 : I know that circular buffer is a good method to avoid this type of problem, but for a so low sample rate, I think this is not really necessary...

Edit 23/11/2011 : also tried this code in ReadFile proc, where samples are read "in advance" and sent to DAC juste after Timer0 int occurs. But same problem :

    Timer_SetInterrupt(true);
    while size > 1 do
    begin
      Mmc_Fat_Read(iSample);    // take next sample now from MMC card
      while (not bTimerInt) do  // wait next TMR0 int before send it to DAC
        nop;
      PORTB := iSample;         // Write (send) audio sample data to PORTB
      Dec(size);
      bTimerInt := false;
    end;
    Timer_SetInterrupt(false);


And if I replace MMC reading part by a sawtooth, all is OK...

    while iSize > 1 do
    begin
      if iSample < 255 then inc(iSample) else iSample := 0;
      //Mmc_Fat_Read(iSample);    // take next sample now from MMC card
      while (not bTimerInt) do  // wait next TMR0 int before send it to DAC
        nop;
      PORTB := iSample;         // Write (send) audio sample data to PORTB
      Dec(iSize);
      bTimerInt := false;
    end;


So finally MMC reading seems to be the problem...


Top
 Profile  
 
PostPosted: 23 Nov 2011 16:00 
Offline
User avatar

Joined: 16 Jun 2011 13:48
Posts: 3565
Hello,

Please can you tell me which version of compiler are you using?

Best regards.


Top
 Profile  
 
PostPosted: 23 Nov 2011 17:15 
Offline

Joined: 29 Sep 2007 14:35
Posts: 167
Location: France
Hello,

thanks for your answer ! :D

As stated in my post, MP Pro V5.30.
First tests were made with V5.20 but SD access was too slow.
Found really better performance with V5.30 about SD/MMC access time.

Actually try to implement circular buffer to see if it change something ;-)

Remy


Top
 Profile  
 
PostPosted: 23 Nov 2011 18:10 
Offline

Joined: 18 Feb 2006 13:17
Posts: 4786
chimimic wrote:
Actually try to implement circular buffer to see if it change something ;-)
It surely will when you separate both processes. It'd be best to put the audio output in ISR to make it regular. You'll need a kind of semaphore signalling between both processes - so the part reading from SD card doesn't go too fast (obviously, it should not be slower than the audio output, but it should not overwrite data yet unused). And, if it happens that reading from SD card has a momentary hiccough, audio output better wait for some more data to accumulate than exactly reproduce the hiccough.

_________________
Replacement libraries for mP PRO and PIC18 processors, mP PRO tips & trics


Top
 Profile  
 
PostPosted: 23 Nov 2011 21:03 
Offline

Joined: 29 Sep 2007 14:35
Posts: 167
Location: France
Thanks Janni for your help.

I just finished to implement a circular buffer and all run fine now.
I'll post full code here if someone is interrested.

Remy

Edit 24/11/2011 : even if project now work fine with additionnal buffer, I would be interrested to know what happens with "standard" reading and what cause problem. I think 4:1 (32 ms OK / 8 ms locked) ratio is not due to hazard...


Top
 Profile  
 
PostPosted: 22 Nov 2014 03:40 
Offline

Joined: 22 Nov 2014 03:36
Posts: 2
if you complete this pls post full code bcz it is very useful for me


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 6 posts ] 

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron