I2S example code

Post your cool example code here.
madias
Posts: 812
Joined: Mon Apr 27, 2015 11:26 am
Location: Vienna, Austria

Re: I2S example code

Postby madias » Mon Sep 07, 2015 5:49 pm

If you are using the saleae logic analyser, then you can add the "I2s protocol" with following settings:
Image
So I proofed that the values are right (I removed the <<8 from the saw up, so it counts only from 0 to 255 and this are the values I got for the channel #1)

madias
Posts: 812
Joined: Mon Apr 27, 2015 11:26 am
Location: Vienna, Austria

Re: I2S example code

Postby madias » Mon Sep 07, 2015 8:39 pm

Just a note:
A necessary step to get glitch free audio is to insert:

Code: Select all

 dma_set_priority(DMA2, DMA_CH2, DMA_PRIORITY_VERY_HIGH);

( if SPI3 is used)
See my attached (strange) sequencer example :)
All in all I can say sound is really good and a big step forward to my old "8-bit AVR synth" experience. Even with lack of FPU four independent voices (incl. FM/AM modulation and funny LFO stuff) should be possible.
Attachments
I2s-PT8211-strange_test_seq.zip
(3.28 KiB) Downloaded 29 times

User avatar
Vassilis
Posts: 294
Joined: Thu May 21, 2015 6:42 am
Location: Thessaloniki, Greece
Contact:

Re: I2S example code

Postby Vassilis » Mon Sep 07, 2015 8:55 pm

I downloaded a fresh copy of stm32duino and finally the DMA is working now! The problem was in the repository I was using. Maybe in the meantime (from the day I downloaded the repository I used) Victor or Roger made some changes to the DMA files.
Anyway, now it works ok!

I get the rectangle pulse with 47.88 kHz on PD2.
Also I get almost 186Hz * 256 = 47872 samples /second on Sine and Sawtooth waves.

Everything is ok now!

madias
Posts: 812
Joined: Mon Apr 27, 2015 11:26 am
Location: Vienna, Austria

Re: I2S example code

Postby madias » Mon Sep 07, 2015 9:17 pm

Congrats Vassilis!
I think the main work is done and from all datasheets, code examples and websites the init code is now very comprehensible.
The big "thank you" goes to victor, who helped me out with DMA and for his patience with me ;)
If done some further tests: Sound stability with the high DMA priority is now perfect, I changed the sine table to 1024 steps (in my last code this can be done very easily) and tested a frequency sweep loop: With 1024 steps there is virtually no antialiasing hearable anymore. Further I got it working with my code that the 440hz reference is REALLY 440hz, amazing.
Vassilis: What are you plans with I2S and what should be in a library (if we need one)?

User avatar
Vassilis
Posts: 294
Joined: Thu May 21, 2015 6:42 am
Location: Thessaloniki, Greece
Contact:

Re: I2S example code

Postby Vassilis » Mon Sep 07, 2015 9:33 pm

Well, the fresh repository works ok with DMA but it doesn't work with my VS1003 library.

Tested on:
Arduino IDE 1.6.3, 1.6.4 and 1.6.5

I use the maple mini and from arduino IDE I choose:

Board: "Generic STM32F103C series"
Variant: "STM32F103CB (20k RAM 128k Flash)"
Upload method: "STLink"

After compiling + burning the maple with the "hello_STM" example the blue led started flashing. The example code is not executed :(

victor_pv
Posts: 1077
Joined: Mon Apr 27, 2015 12:12 pm

Re: I2S example code

Postby victor_pv » Mon Sep 07, 2015 11:34 pm

Good cath with the DMA priority, I didn't think on that, but may have been what was causing the frequencies to not match from the start.
I would still recommend setting a bigger buffer, at least a few bytes. That will help reduce the overhead a lot, as each ISR call needs something like 15 cycles, and we are doing one per byte sent.

Another option is to remove the half transfer flash, and remove the "if..." sentence in the ISR, and just reload both values in the buffer each time is called.
That will reduce the interrupts to half. The point of having a half transfer interrupt was more for filling larger buffers, there is no advantage with such a small buffer, and a speed disadvantage on servicing interrupts so frequently.

madias
Posts: 812
Joined: Mon Apr 27, 2015 11:26 am
Location: Vienna, Austria

Re: I2S example code

Postby madias » Tue Sep 08, 2015 6:02 am

Vassilis wrote:Well, the fresh repository works ok with DMA but it doesn't work with my VS1003 library.

Maybe I've got time this week, I'll try it out with different repos -> I also own a VS1003 module!

madias
Posts: 812
Joined: Mon Apr 27, 2015 11:26 am
Location: Vienna, Austria

Re: I2S example code

Postby madias » Tue Sep 08, 2015 6:10 am

victor_pv wrote:Good cath with the DMA priority, I didn't think on that, but may have been what was causing the frequencies to not match from the start.
I would still recommend setting a bigger buffer, at least a few bytes. That will help reduce the overhead a lot, as each ISR call needs something like 15 cycles, and we are doing one per byte sent.

Another option is to remove the half transfer flash, and remove the "if..." sentence in the ISR, and just reload both values in the buffer each time is called.
That will reduce the interrupts to half. The point of having a half transfer interrupt was more for filling larger buffers, there is no advantage with such a small buffer, and a speed disadvantage on servicing interrupts so frequently.

Victor: There is enough place for optimizing my "audio-oscillator" code. I've done the whole "table sweeping" within the DMA ISR, the big advantage is, that the timing is total exact. But you are right, maybe I can get the code to "forward thinking" at least for 16,32 or 64 values. But I think, I would need another ISR/timer ISR doing this.

This is my code in the DMA ISR: It uses 2 different oscillators each channel (waveoutA and waveoutB) , second channel is only dummy.
ulPhaseIncrement_A[0] = pre calculated frequency increment (how fast the wavetable is read) so with a simple for (x=0;x<15;x++) I can calculate forward, but I have to change the SAMPLES_PER_CYCLE_FIXEDPOINT (easy to do)

Code: Select all

void DMAEvent() {
  if (dma_get_isr_bits( DMA2, DMA_CH2) == 3)
  {
    ulPhaseAccumulator_A[0] += ulPhaseIncrement_A[0] ;
    if (ulPhaseAccumulator_A[0]  > SAMPLES_PER_CYCLE_FIXEDPOINT)
    {
      ulPhaseAccumulator_A[0]  -= SAMPLES_PER_CYCLE_FIXEDPOINT;
    }
    ulPhaseAccumulator_B[0] += ulPhaseIncrement_B[0] ;
    if (ulPhaseAccumulator_B[0]  > SAMPLES_PER_CYCLE_FIXEDPOINT)
    {
      ulPhaseAccumulator_B[0]  -= SAMPLES_PER_CYCLE_FIXEDPOINT;
    }
    waveout_A[0] = (wavetable_A[ulPhaseAccumulator_A[0] >> 20] + wavetable_A[ulPhaseAccumulator_B[0] >> 20]) / 2;
    waveout_A[0] = ( (waveout_A[0] * testvol) / 127);
    i2s_buffer[0]  = waveout_A[0]; // half transfer
  }
  else
  {
      i2s_buffer[1] = wavecounter2 << 8 ; // nothing to do with this channel --> dummy
    wavecounter2++;
  }

  dma_clear_isr_bits(DMA2, DMA_CH2);
}

victor_pv
Posts: 1077
Joined: Mon Apr 27, 2015 12:12 pm

Re: I2S example code

Postby victor_pv » Tue Sep 08, 2015 1:35 pm

If you can deal with a bit less precision, I would change:
(waveout_A[0] * testvol) / 127)
to
(waveout_A[0] * testvol) / 128)

The compiler should optimize that to a right shift 7 bits, rather than a division that takes a bunch more cycles.
But in general, yeah, the rest is a bit too much for an ISR.
I would use an index to know where I am in the wave portion, and then a buffer of at least 128 bytes, that gives me 64 per channel, 32 words if using 16bits per word, 128 bytes should not leave you out of memory, and would provide a peed up, since I bet calling the ISR causes as much wasted time as calculating one of your output values, big waste.

If you post your whole sketch somewhere I can try turning it to a CoOS example that shows you CPU usage in the corner of a display, if you use a display, or send it thru serialusb if you dont have a display connected, that way you can see how much CPU time you have left for other things, and how much you save with changes.

madias
Posts: 812
Joined: Mon Apr 27, 2015 11:26 am
Location: Vienna, Austria

Re: I2S example code

Postby madias » Tue Sep 08, 2015 2:29 pm

Code: Select all

(waveout_A[0] * testvol) / 127)
to
(waveout_A[0] * testvol) / 128)3

yes, no problem with it, but I measured nothing better (but the mass of such thing makes the differences!)


Meanwhile I played a little bit with the buffer and tried to found the pros and cons:
Bigger buffer: Less MCU time, but less precision.
So for example: If I use a buffer of 256 (=128 for each channel) and I change the volume (fast fade in, slow fade out, like an ADSR enveleope does) I hear much hiss in the volume change.
So a buffer of 16 or 32 is a good mix for me, but this depends on how the final oscillator code works. The only real gauge for this task are my ears :)

I'm not on my working PC, so I'll send you some test code later, thank you for that (I have many ILI9341 or ILI9163 (both SPI) at home)


Return to “Code snipplets”

Who is online

Users browsing this forum: No registered users and 2 guests