"Irregular" non-integer timer interrupt values (e.g. 22.67us/44.1khz)

Post here first, or if you can't find a relevant section!
nicolas_soundforce
Posts: 22
Joined: Sat Jun 17, 2017 10:18 am

Re: "Irregular" non-integer timer interrupt values (e.g. 22.67us/44.1khz)

Post by nicolas_soundforce » Tue Aug 08, 2017 1:23 pm

Yes, maybe I'll try to know how it works. I'd like to try some audio DSP using the same board + ADC. But it does look complicated from DMA-noob point of view. I can find a lot of examples for the HAL library but not much with STM32duino. I got your example I need to go through it few times.

outgoingbot
Posts: 4
Joined: Wed Sep 06, 2017 9:31 pm

Re: "Irregular" non-integer timer interrupt values (e.g. 22.67us/44.1khz)

Post by outgoingbot » Thu Sep 07, 2017 7:42 pm

nicolas_soundforce wrote:
Sat Aug 05, 2017 1:17 pm

For anybody trying to do this:

If you are not overlock and running at 72mhz, just use 1633 as prescaler.
I am using the PT8211 cheap 16bits DAC, but a 12bits MCP4822 would be also do fine.

.....
sir,

Im trying to use the example you posted above but having a hard time compiling. are there certain #include statements I need to get your code running?

I have SD card on SPI 1, STMduino bootloader on a "blue pill", and speaker on TIM1_CH1 and TIM1_CH2.

dannyf
Posts: 140
Joined: Wed May 11, 2016 4:29 pm

Re: "Irregular" non-integer timer interrupt values (e.g. 22.67us/44.1khz)

Post by dannyf » Thu Sep 07, 2017 7:55 pm

Fairly easy.

1. Put the timer in output compare mode. Interrupt enabled.

2. Work out the math. For example, 22.67us is a combination of 1x 22us and 2x 23us.

3. In your isr, advance the output compare register by 22us, 23us, and 23us. And then back to 22us, ....

4. Done.

outgoingbot
Posts: 4
Joined: Wed Sep 06, 2017 9:31 pm

Re: "Irregular" non-integer timer interrupt values (e.g. 22.67us/44.1khz)

Post by outgoingbot » Thu Sep 07, 2017 8:39 pm

thanks for the reply. Below is a mess, but all Im trying to do it get your example to compile and play. im using the arduino IDE. This will now compile with the variable declare and #include statements I added. i have an uncompressed 8bit, 8khz, mono, wav called song0002.wav on the sd card. on spi 1. simple speaker on PA8 (TIM1_CH1). Im trying to get that wav to play.

Code: Select all

#include <SdFat.h>
#include <SPI.h>
#define WS PA8


File file1;
SdFat sd;
uint8_t buf[2];
byte buf1[13];
byte buf2[13];
uint8_t buffer_refill;
uint8_t flip_flop;
uint8_t play1;
int buffer_counter;
int counter1;
int length_audio1; //long?
int reading, reading_2, reading_3, reading_4;
int number_of_channels;
int sample_rate;
byte sample_rate_lsb, sample_rate_msb;
int bit_depth;
byte start_byte_1;

void setup() {  
  Timer2.pause();
  Timer2.setPrescaleFactor(1);
  Timer2.setOverflow(2175);
  Timer2.setChannel1Mode(TIMER_OUTPUT_COMPARE);
  Timer2.attachCompare1Interrupt(audio_int);
  Timer2.refresh();
  Timer2.resume();
   
   while (!sd.begin(PA4, SPI_CLOCK_DIV2)) {  // see if the card is present and can be initialized:
    Serial.println("SD fail. Holding on loop.");
    delay(100);
    }
    
  file1.read(buf1, 512);

}

void loop() {

  /*
    if (millis() - what_time > 100) {
      what_time = millis();
      Serial.println(after - before);
    }
  */

  if (buffer_refill) {
    if (flip_flop) {
      file1.read(buf2, 512);
    }

    else {
      file1.read(buf1, 512);
    }
    buffer_refill = 0;
  }
  /*
    if (rewind_1) {
      file1.seekSet(start_byte_1);
      rewind_1 = 0;
    }

    if (rewind_2) {
      file2.seekSet(start_byte_2);
      rewind_2 = 0;
    }*/
}

void audio_int(){
  if (play1) {
    if (flip_flop) {
      buf[0] = buf1[buffer_counter];
      buf[1] = buf1[buffer_counter + 1];
    }

    else {
      buf[0] = buf2[buffer_counter];
      buf[1] = buf2[buffer_counter + 1];
    }

    digitalWrite(WS, HIGH);  //Select RIGHT Audio channel

    SPI.transfer(buf[1]);  // Data bits 15-8
    SPI.transfer(buf[0]); // Data bits 7-0

    digitalWrite(WS, LOW);  //Select RIGHT Audio channel

    buffer_counter = buffer_counter + 2;

    if (buffer_counter == 512) {
      flip_flop = !flip_flop;
      buffer_counter = 0;
      buffer_refill = 1;
    }

    counter1++;

    if (counter1 > length_audio1) {
      play1 = 0;
    }
  }
}

void audio_info(){
sd.chvol();
  //-----------------//
  //---Open FILE1----//
  //-----------------//

  if (!file1.open("song0002.wav")) {
  }

  file1.printName(&Serial);
  Serial.println();

  for (int i = 0; i < 100; i++) {
    reading = file1.read();

    if (reading == 0x66) {
      reading_2 = file1.read();
      reading_3 = file1.read();
      reading_4 = file1.read();

      //search for Subchunk1ID, Contains the letters "fmt " (0x666d7420 big-endian form).
      if ((reading_2 == 0x6d) && (reading_3 == 0x74) && (reading_4 == 0x20)) {
        break;
      }
      else {
        file1.seekSet(i - 3);
      }
    }
  }

  Serial.print("Subchunk1ID start at : ");
  Serial.println(file1.curPosition() - 4, DEC);

  //after the Subchunk1ID, we know for sure that the next bytes define important information
  //skip Subchunk1Size
  for (int i = 0; i < 4; i++) {
    file1.read();
  }

  //skip AudioFormat
  for (int i = 0; i < 2; i++) {
    file1.read();
  }

  //get number of channels
  number_of_channels = file1.read();
  Serial.print("number of channels : ");
  Serial.println(number_of_channels, DEC);
  //byte 2 is empty
  file1.read();

  //get sample rate
  sample_rate_lsb = file1.read();
  sample_rate_msb = file1.read();
  sample_rate = sample_rate_lsb;
  sample_rate |= sample_rate_msb << 8;
  Serial.print("sample_rate : ");
  Serial.println(sample_rate, DEC);
  //byte 3 & 4 are empty
  file1.read();
  file1.read();

  //skip bytes until bitdepth
  for (int i = 0; i < 6; i++) {
    file1.read();
  }

  //bit depth
  bit_depth = file1.read();
  Serial.print("bitdepth : ");
  Serial.println(bit_depth, DEC);
  file1.read();

  //scroll through file until reaching the Subchunk2ID
  for (int i = 0; i < 1000; i++) {
    reading = file1.read();
    //Serial.print(i);
    // Serial.print("  -  ");
    // Serial.println(reading, HEX);

    if (reading == 0x64) {
      reading_2 = file1.read();
      reading_3 = file1.read();
      reading_4 = file1.read();

      //search for Subchunk2ID, Contains the letters "data" (0x64617461 big-endian form).
      if ((reading_2 == 0x61) && (reading_3 == 0x74) && (reading_4 == 0x61)) {
        break;
      }
      else {
        file1.seekSet(i - 3);
      }
    }
  }

  Serial.print("Subchunk2ID start at : ");
  Serial.println(file1.curPosition() - 4, DEC);


  //we then reached the Subchunk2Size, and can read the length of the file
  reading =  file1.read();
  length_audio1 = reading;
  reading =  file1.read();
  length_audio1 |= reading << 8;
  reading =  file1.read();
  length_audio1 |= reading << 16;
  reading =  file1.read();
  length_audio1 |= reading << 32;

  //length of audio in samples, it's the half of the amount of bytes in mono 16bits
  length_audio1 = length_audio1 / 2;

  Serial.println(length_audio1);

  start_byte_1 = file1.curPosition();


  Serial.print("audio starting start at : ");
  Serial.println(start_byte_1, DEC);
 // }
}


//}


dannyf
Posts: 140
Joined: Wed May 11, 2016 4:29 pm

Re: "Irregular" non-integer timer interrupt values (e.g. 22.67us/44.1khz)

Post by dannyf » Thu Sep 07, 2017 11:04 pm

way to complicated for me.

Go herehttps://github.com/dannyf00/Use-Output- ... er/STM32F1 and take the appropriate files, say tmr2oc.h, if you are using TIM2.

the code would be something like this:

Code: Select all

//TIM2_IRQHandler() needs to be changed by commenting out "TIMx->CCR1 += _tim_oc1;"
//it looks like this:
/*
void TIM2_IRQHandler(void) {
	//oc1 portion
	if (TIMx->SR & TIM_SR_CC1IF) {		//output compare 1 flag is set
		TIMx->SR &=~TIM_SR_CC1IF;		//clear the flag
		//TIMx->CCR1 += _tim_oc1;			//update the output compare register - needs to be commented out
		_tim_oc1isrptr();				//execute user handler
	}
*/

//my isr
void myisr(void) {
  static pr_index=0;  //period index
  
  switch (pr_index) {
    case 0: TIM2_CCR1 += PR0; pr_index=1; break;
    case 1: TIM2_CCR1 += PR1; pr_index=2; break;
    case 2: TIM2_CCR1 += PR2; pr_index=3; break;
    case 3: TIM2_CCR1 += PR3; pr_index=0; break;
    //add additional periods here...
  }
  do_your_thing_here();
}

  tmr2_init(1);  //set up tmr2 at 1:1 prescaler
  tmr2_setpr1(PR0);  //use compare channel 1, set period to PR0
  tmr2_act1(myisr);  //install my isr

The period is (PR0+PR1+PR2+PR3)/4, or something like that depends on how you customize the isr.

once you do that, the code runs in the background without your worrying about it.

all the code does is to keep advancing the output compare register to the next match point -> in this case, the advancements can vary, to produce fractional periods.

outgoingbot
Posts: 4
Joined: Wed Sep 06, 2017 9:31 pm

Re: "Irregular" non-integer timer interrupt values (e.g. 22.67us/44.1khz)

Post by outgoingbot » Fri Sep 08, 2017 8:39 pm

thanks for the help. im just getting started with this device and I'm a little lost because of how this great information is spread out in this forum.

I was thinking that the example nicolas posted was related to the TMRpcm library that Victor has been working on porting. I believe now I understand that the example is using an External DAC. so basically the main loop() is responsible for refilling each 512 byte dual buffers, while the interrupt send out 2 8-bit PCM data (using SPI_2.transfer) to the PT8211 DAC. This is done at a rate that is proportional to the sample rate of the WAV file from the file header info.


would it be possible to use a DAC that supports I2S to have a DMA 'link' between the SDcard and the DAC to load the PCM data directly? I believe victor said the new SdFat library supports this and has also posted about the PT8211.
(viewtopic.php?f=3&t=2328&start=20#p32520)
(http://www.stm32duino.com/viewtopic.php ... 211#p33300)

The information is spread all out in the forum. But it seams like Victor has written a DMA function in the most recent TMRpcm lib update. This function could be extracted from the TMRpcm.cpp and used in the example nicolas posted?

I trying to the understand the data signal flow from Wav file to the DAC with and without DMA. Does both the PT8211 and the SD card use DMA? or somehow the PT8211 uses i2s and the sd card uses DMA? or just do DMA on SD card and SPI_2.(transfer); to the DAC?

the code below is from the example posted on page 3 of this thread.

Code: Select all

digitalWrite(WS, HIGH);  //Select RIGHT Audio channel
    SPI_2.transfer(buf[1]);  // Data bits 15-8
    SPI_2.transfer(buf[0]); // Data bits 7-0
    digitalWrite(WS, LOW);  //Select RIGHT Audio channel
It seems like this version does neither i2s or dma. the flipflop buffer read is just interrupted on each TIM2 interrupt. if this is true then it seems that there could be significant performance increase if DMA or I2s were used.

The end goal is a PCM WAV player running on a blue pill.

*page 19 of this PDF has some excellent info
http://www.st.com/resource/en/applicati ... 259245.pdf
ST_DMA_WAVplayer.png
ST_DMA_WAVplayer.png (47.34 KiB) Viewed 95 times

nicolas_soundforce
Posts: 22
Joined: Sat Jun 17, 2017 10:18 am

Re: "Irregular" non-integer timer interrupt values (e.g. 22.67us/44.1khz)

Post by nicolas_soundforce » Fri Sep 29, 2017 10:44 am

Dear "outgoingbot",

My program is not relying on TMRpcm. It is simply reading a wave file from the sd card and sending the bytes to the pt8211.
The program is made for 16bits 44.1khz files. You would need to modify it to make play back 8 bits 8khz files.

Maybe at first forget about DMA, you really don't need it, especially for 8khz files.

Good luck!

Post Reply