I2S: Anyone got it running? (answer is yes!)


576 posts   Page 1 of 24   1, 2, 3, 4, 5 ... 24
by dariush » Fri Jun 15, 2012 7:52 pm
Hi,

I'm currently experimenting with the I2S/PCM interface.

(I know, PCM_FS isn't accessible as a GPIO on the RasberryPi, but PCM_CLK and PCM_DOUT are, which might be useful enough for certain use cases).

However I'm having trouble getting the I2S system to work. Maybe I misread the datasheet.
For starters I've configured the registers to 2x16bit Channels, enabled I2S and filled the FIFO with 64 words (see code below).

Unfortunately I don't see any output on either GPIO18 or 21.

Furthermore a couple of status bits aren't updated as I would expect after reading the datasheet.
Most troubling is that the SYNC register (p.126) is always reading 0, no matter what I'm writing into it.

Attached is a simple userspace program (based on the GPIO demo from the elinux wiki).
It configures and enables I2S and fills the FIFO in a loop.

I'm totally clueless at the moment. Either I made a mistake or the datasheet is missing some vital information. Did anyone else have a look into the I2S system and has some idea what I might have missed?

cheers
Dariush

(compile with "gcc -o i2s i2s.c")
Code: Select all
//
//  How to access GPIO registers from C-code on the Raspberry-Pi
//  Example program
//  15-January-2012
//  Dom and Gert
//  source: http://elinux.org/RPi_Low-level_peripherals

#define BCM2708_PERI_BASE        0x20000000
#define GPIO_BASE                (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */
#define I2S_BASE                (BCM2708_PERI_BASE + 0x203000) /* GPIO controller */


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <unistd.h>

#define PAGE_SIZE (4*1024)
#define BLOCK_SIZE (4*1024)

int  mem_fd;
char *gpio_mem, *gpio_map;
char *i2s_mem, *i2s_map;


// I/O access
volatile unsigned *gpio;
volatile unsigned *i2s;


// GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y)
#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
#define OUT_GPIO(g) *(gpio+((g)/10)) |=  (1<<(((g)%10)*3))
#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))

#define GPIO_SET *(gpio+7)  // sets   bits which are 1 ignores bits which are 0
#define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0

void setup_io();

int main(int argc, char **argv)
{ int g,rep;

  setup_io();

  printf("Setting GPIO regs to alt0\n");
  for (g=18; g<=21; g++)
  {
    INP_GPIO(g);
    SET_GPIO_ALT(g,0);
  }

 int i;
 printf("Memory dump\n");
 for(i=0; i<10;i++) {
    printf("GPIO memory address=0x%08x: 0x%08x\n", gpio+i, *(gpio+i));
 }

  // disable I2S so we can modify the regs
  printf("Disable I2S\n");
  *(i2s+0) = 0;
  usleep(10);
 
  // XXX: seems not to be working. FIFO still full, after this.
  printf("Clearing FIFOs\n");
  *(i2s+0) |= 1<<3 | 1<<4 | 11<5; // clear TX FIFO
  usleep(10);
 
  // set register settings
  // --> enable Channel1 and channel2
  // --> set channel both width to 16 bit
  printf("Setting TX channel settings\n");
  *(i2s+4) = 1<<30 | 8<<16 | 1<<14 | 16<<4 | 8<<0;
  // --> frame width 31+1 bit
  *(i2s+2) = 31<<10;

  // --> disable STBY
  printf("disabling standby\n");
  *(i2s+0) |= 1<<25;
  usleep(50);

  // --> ENABLE SYNC bit
  printf("setting sync bit high\n");
  *(i2s+0) |= 1<<24;
  usleep(50);
 
  if (*(i2s+0) & 1<<24) {
    printf("SYNC bit high, as expected.\n");
  } else {
    printf("SYNC bit low, strange.\n");
  }

  // enable I2S
  *(i2s+0) |= 0x01;

  usleep(10);

  // enable transmission
  *(i2s+0) |= 0x04;

  // fill FIFO in while loop
  int count=0;
  printf("going into loop\n");
  while (1) {
 
    while ((*i2s) & (1<<19)) {
      // FIFO accepts data
      *(i2s+1) = 0xAAAAAAAA;
     
      printf("Filling FIFO, count=%i\n", ++count);
    }
   
    // Toogle SYNC Bit
    //( XXX: do I have to deactivate I2S to do that? datasheet is unclear)
    *(i2s+0) &= ~(0x01);
    *(i2s+0) ^= 1<<24;
    *(i2s+0) |= 0x01;

    sleep(1);
   
    printf("Memory dump\n");
    for(i=0; i<9;i++) {
       printf("I2S memory address=0x%08x: 0x%08x\n", i2s+i, *(i2s+i));
    }

  }

  return 0;

} // main


//
// Set up a memory regions to access GPIO
//
void setup_io()
{
  printf("setup io\n");

   /* open /dev/mem */
   if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
      printf("can't open /dev/mem \n");
      exit (-1);
   }

   /* mmap GPIO */

   // Allocate MAP block
   if ((gpio_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) {
      printf("allocation error \n");
      exit (-1);
   }

   // Allocate MAP block
   if ((i2s_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) {
      printf("allocation error \n");
      exit (-1);
   }

   // Make sure pointer is on 4K boundary
   if ((unsigned long)gpio_mem % PAGE_SIZE)
     gpio_mem += PAGE_SIZE - ((unsigned long)gpio_mem % PAGE_SIZE);
   // Make sure pointer is on 4K boundary
   if ((unsigned long)i2s_mem % PAGE_SIZE)
     i2s_mem += PAGE_SIZE - ((unsigned long)i2s_mem % PAGE_SIZE);

   // Now map it
   gpio_map = (unsigned char *)mmap(
      (caddr_t)gpio_mem,
      BLOCK_SIZE,
      PROT_READ|PROT_WRITE,
      MAP_SHARED|MAP_FIXED,
      mem_fd,
      GPIO_BASE
   );

   // Now map it
   i2s_map = (unsigned char *)mmap(
      (caddr_t)i2s_mem,
      BLOCK_SIZE,
      PROT_READ|PROT_WRITE,
      MAP_SHARED|MAP_FIXED,
      mem_fd,
      I2S_BASE
   );

   if ((long)gpio_map < 0) {
      printf("mmap error %d\n", (int)gpio_map);
      exit (-1);
   }
   if ((long)i2s_map < 0) {
      printf("mmap error %d\n", (int)i2s_map);
      exit (-1);
   }

   // Always use volatile pointer!
   gpio = (volatile unsigned *)gpio_map;
   i2s = (volatile unsigned *)i2s_map;


} // setup_io
Posts: 9
Joined: Fri Jun 15, 2012 6:25 pm
by Gert van Loo » Fri Jun 15, 2012 11:04 pm
You probably need to enable the clock to the I2S module.
All clocks, power and reset are in a separate section.
There is an omission in the datasheet in that it does not mention those.
Do NOT experiment with trying to guess registers in that area.
Especially if you start 'playing' with, or accidentally hit a power register
you have the small, but distinct possibility of blowing up your PI.

I will see what I can find, but it will take a while as I do not have the
full datasheets here at home.
User avatar
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 1982
Joined: Tue Aug 02, 2011 7:27 am
by dariush » Sat Jun 16, 2012 6:34 am
Hi Gert,

thank you very much!

Yes, a disabled clock would probably explain this.

Looking forward hearing from you (don't worry, I'm not desperate enough to start fuzzing unknown registers ;)

cheers
Dariush
Posts: 9
Joined: Fri Jun 15, 2012 6:25 pm
by Gert van Loo » Sat Jun 16, 2012 5:11 pm
I assumed you did not want to wait until after the weekend.
So I did a remote login as soon as I came home.

There are two PCM/I2S clock control registers (Like with most peripherals)

CONTROL @ 0x7E101098
Enable bit = 4
Clock source is bits 3:0.
I assume you want the cleanest clock source which is the XTAL
(19.2MHz) crystal. (Clock source code = 0001b)

DIVIDE @ 0x7E10109C

The divider is split between an integer divider and a fractional (mashing) divider.
The mashing dividers are build such that clock artifacts should be pushed out of the audio frequency domain.
Bits 11:0 are the mashing divider
Bits 23:12 are the integer divider.
You must write the MS 8 bits as 0x5A.

You should always write both registers, then write again with the enable bit set as last action,.

Note that the addresses above need to be mapped to the ARM address space.
For example code see the PWM init code for the Gertboard demo.
(But I cheated with the enable bit to keep the code simple)
That sets up the PWM clock using identical registers but at a different address.

Let me know if it works as I could have made a mistake in the above text.
User avatar
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 1982
Joined: Tue Aug 02, 2011 7:27 am
by dariush » Sun Jun 17, 2012 3:00 pm
Hi Gert,

thank you very much! That was it!

Do you know how this clock relates to the clocks mentioned in the PCM/I2S chapter of the datasheet?

The datasheet mentions three clocks :

- The APB clock domain. (p.119)
- PCM_CLK (p.120)
- PCM_MCLK (p.120)

My interpretation:

-> PCM_CLK: seems to be the clock at which the I2S system is actually running (can be either PCM_MCLK or fed via GPIO18 from the outside).
-> PCM_MCLK: is a fixed clock at 3.072Mhz (really fixed?)
-> APB clock: The PCM/I2S clock we've just enabled.

Do you think this is correct?

Specifically I look for a way to modify the clock at which PCM_DOUT is driven. If I could run PCM_CLK at, say, 6.144MHz, that would be great.

(Tommorrow I'll check in the lab if the clock frequency of GPIO18 can be modified by playing with the divisor values you mentioned.)

cheers
Dariush
Posts: 9
Joined: Fri Jun 15, 2012 6:25 pm
by Gert van Loo » Sun Jun 17, 2012 8:47 pm
-> PCM_CLK: seems to be the clock at which the I2S system is actually running (can be either PCM_MCLK or fed via GPIO18 from the outside).

That is the PCM/I2S clock you just enabled. It controls the speed at which the PCM/I2S runs, provided you select the 'internal clock' mode

-> PCM_MCLK: is a fixed clock at 3.072Mhz (really fixed?)

I have to ask about that one. I am not familiar with it.

-> APB clock

That is an internal clock which is set by the GPU and can vary as the GPU adapts it's clock according to it's load.
User avatar
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 1982
Joined: Tue Aug 02, 2011 7:27 am
by DogEars » Mon Jun 18, 2012 10:16 am
dariush wrote:Hi Gert,

thank you very much! That was it!



HI Dariush, would there be any chance you could post your working code?

Cheers,
Ears.
Posts: 43
Joined: Sun Jun 17, 2012 1:16 pm
by DogEars » Mon Jun 18, 2012 10:23 am
dariush wrote:Hi Gert,

thank you very much! That was it!


Hi Dariush,

Would it be possible to post your working code?

Cheers,
Ears.
Posts: 43
Joined: Sun Jun 17, 2012 1:16 pm
by dariush » Mon Jun 18, 2012 8:29 pm
Gert van Loo wrote:
-> PCM_CLK: seems to be the clock at which the I2S system is actually running (can be either PCM_MCLK or fed via GPIO18 from the outside).

That is the PCM/I2S clock you just enabled. It controls the speed at which the PCM/I2S runs, provided you select the 'internal clock' mode


Yep, your right! Nice, thats even better than I initially hoped.

However configuring the I2S clock sometimes doesn't work (resulting in a clock of ~1-2 KHz). And I couldn't yet get the fractional divider to work (setting the bits seems to have no effect when measuring the frequency with a scope). However I didn't have time yet to in depth debug this, maybe I screwed up somewhere.

-> PCM_MCLK: is a fixed clock at 3.072Mhz (really fixed?)
I have to ask about that one. I am not familiar with it.


Maybe its a typo. ;)

cheers
Dariush
Posts: 9
Joined: Fri Jun 15, 2012 6:25 pm
by dariush » Mon Jun 18, 2012 8:42 pm
Hi,

DogEars wrote:Would it be possible to post your working code?


sure!

This code should set the frequency of PCM_CLK to 6.144MHz and output 0xA0A0A0A0
in a loop. The output on PCM_DOUT is correct, however the clock is somehow too high (about 6.4MHz).

cheers
Dariush

Code: Select all
//
// output A0A0A0 on PCM_DOUT with about 6Mhz
// based on source from: http://elinux.org/RPi_Low-level_peripherals

#define BCM2708_PERI_BASE        0x20000000
#define GPIO_BASE                (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */
#define I2S_BASE                (BCM2708_PERI_BASE + 0x203000) /* GPIO controller */
#define CLOCK_BASE               (BCM2708_PERI_BASE + 0x101000) /* Clocks */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <inttypes.h>

#include <unistd.h>

#define PAGE_SIZE (4*1024)
#define BLOCK_SIZE (4*1024)

int  mem_fd;
char *gpio_mem, *gpio_map;
char *i2s_mem, *i2s_map;
char *clk_mem, *clk_map;


// I/O access
volatile unsigned *gpio;
volatile unsigned *i2s;
volatile unsigned *clk;


// GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y)
#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
#define OUT_GPIO(g) *(gpio+((g)/10)) |=  (1<<(((g)%10)*3))
#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))

#define GPIO_SET *(gpio+7)  // sets   bits which are 1 ignores bits which are 0
#define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0

void setup_io();

int main(int argc, char **argv)
{ int g,rep;

  setup_io();

  printf("Setting GPIO regs to alt0\n");
  for (g=18; g<=21; g++)
  {
    INP_GPIO(g);
    SET_GPIO_ALT(g,0);
  }

 int i;
 printf("Memory dump\n");
 for(i=0; i<10;i++) {
    printf("GPIO memory address=0x%08x: 0x%08x\n", gpio+i, *(gpio+i));
 }

// for(i=0x26; i<=0x27;i++) {
//   printf("Clock memory address=0x%08x: 0x%08x\n", clk+i, *(clk+i));
// }

 printf("Disabling I2S clock\n");
 *(clk+0x26) = 0x5A000000;
 *(clk+0x27) = 0x5A000000;
 
 usleep(10);

 printf("Confiure I2S clock\n");
 *(clk+0x26) = 0x5A000001;
 *(clk+0x27) = 0x5A000000 | 3<<12 | 1<<9; // divider: 3.125==0b11.001

 usleep(10);
 printf("Enabling I2S clock\n");
 *(clk+0x26) = 0x5A000011;

  // disable I2S so we can modify the regs
  printf("Disable I2S\n");
  *(i2s+0) &= ~(1<<24);
  usleep(100);
  *(i2s+0) = 0;
  usleep(100);

  printf("Clearing FIFOs\n");
  *(i2s+0) |= 1<<3 | 1<<4 | 11<5; // clear TX FIFO
  usleep(10);

  // set register settings
  // --> enable Channel1 with 32bit width
  printf("Setting TX channel settings\n");
  *(i2s+4) = 1<<31 | 1<<30 | 8<<16;
  // --> frame width 31+1 bit
  *(i2s+2) = 31<<10;

  // --> disable STBY
  printf("disabling standby\n");
  *(i2s+0) |= 1<<25;
  usleep(50);

  // enable I2S
  *(i2s+0) |= 0x01;

  // enable transmission
  *(i2s+0) |= 0x04;
 
  // --> ENABLE SYNC bit
  printf("setting sync bit high\n");
  *(i2s+0) |= 1<<24;

  if (*(i2s+0) & 1<<24) {
    printf("SYNC bit high, strange.\n");
  } else {
    printf("SYNC bit low, as expected.\n");
  }

  usleep(1);
 
  if (*(i2s+0) & 1<<24) {
    printf("SYNC bit high, as expected.\n");
  } else {
    printf("SYNC bit low, strange.\n");
  }

    printf("Memory dump\n");
    for(i=0; i<9;i++) {
       printf("I2S memory address=0x%08x: 0x%08x\n", i2s+i, *(i2s+i));
    }

  // fill FIFO in while loop
  int count=0;
  printf("going into loop\n");

  while (1) {

    while ((*i2s) & (1<<19)) {
      // FIFO accepts data
      *(i2s+1) = 0xA0A0A0A0;
     
      if ((++count % 1000000)==0)
         printf("Filling FIFO, count=%i\n", count);
         
    }
   
  }

  return 0;

} // main


//
// Set up a memory regions to access GPIO
//
void setup_io()
{
  printf("setup io\n");

   /* open /dev/mem */
   if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
      printf("can't open /dev/mem \n");
      exit (-1);
   }

   /* mmap GPIO */

   // Allocate MAP block
   if ((gpio_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) {
      printf("allocation error \n");
      exit (-1);
   }

   // Allocate MAP block
   if ((i2s_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) {
      printf("allocation error \n");
      exit (-1);
   }

   // Allocate MAP block
   if ((clk_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) {
      printf("allocation error \n");
      exit (-1);
   }

   // Make sure pointer is on 4K boundary
   if ((unsigned long)gpio_mem % PAGE_SIZE)
     gpio_mem += PAGE_SIZE - ((unsigned long)gpio_mem % PAGE_SIZE);
   // Make sure pointer is on 4K boundary
   if ((unsigned long)i2s_mem % PAGE_SIZE)
     i2s_mem += PAGE_SIZE - ((unsigned long)i2s_mem % PAGE_SIZE);
   // Make sure pointer is on 4K boundary
   if ((unsigned long)clk_mem % PAGE_SIZE)
     clk_mem += PAGE_SIZE - ((unsigned long)clk_mem % PAGE_SIZE);

   // Now map it
   gpio_map = (unsigned char *)mmap(
      (caddr_t)gpio_mem,
      BLOCK_SIZE,
      PROT_READ|PROT_WRITE,
      MAP_SHARED|MAP_FIXED,
      mem_fd,
      GPIO_BASE
   );

   // Now map it
   i2s_map = (unsigned char *)mmap(
      (caddr_t)i2s_mem,
      BLOCK_SIZE,
      PROT_READ|PROT_WRITE,
      MAP_SHARED|MAP_FIXED,
      mem_fd,
      I2S_BASE
   );

   clk_map = (unsigned char *)mmap(
      (caddr_t)clk_mem,
      BLOCK_SIZE,
      PROT_READ|PROT_WRITE,
      MAP_SHARED|MAP_FIXED,
      mem_fd,
      CLOCK_BASE
   );

   if ((long)gpio_map < 0) {
      printf("mmap error %d\n", (int)gpio_map);
      exit (-1);
   }
   if ((long)i2s_map < 0) {
      printf("mmap error %d\n", (int)i2s_map);
      exit (-1);
   }
   if ((long)clk_map < 0) {
      printf("mmap error %d\n", (int)clk_map);
      exit (-1);
   }

   // Always use volatile pointer!
   gpio = (volatile unsigned *)gpio_map;
   i2s = (volatile unsigned *)i2s_map;
   clk = (volatile unsigned *)clk_map;


} // setup_io
Posts: 9
Joined: Fri Jun 15, 2012 6:25 pm
by DogEars » Mon Jun 18, 2012 10:06 pm
Could you just clarify; do we need to remove resistors and solder onto the bare tracks to get to the I2S outputs?

Cheers,
Ears.
Posts: 43
Joined: Sun Jun 17, 2012 1:16 pm
by dariush » Wed Jun 20, 2012 7:28 am
Hi,

DogEars wrote:Could you just clarify; do we need to remove resistors and solder onto the bare tracks to get to the I2S outputs?

Cheers,
Ears.


You can access PCM_DOUT and PCM_CLK directly via the P1 connector (that is GPIO21 and GPIO18). Not PCM_FS or PCM_DIN, though. Those ones are not available (to the best of my knowledge).

Whether you need PCM_FS and/or PCM_DIN depends on your application (i.e. what chip/circuit you want to connect).

A typical I2S would likely need at least PCM_FS, so you'd have to find that signal somewhere on the Pi.

However in case you only need something along the lines of an (uni-directional) SPI interface (like I do), PCM_CLK and PCM_DOUT work out just fine.

cheers
Dariush
Posts: 9
Joined: Fri Jun 15, 2012 6:25 pm
by DogEars » Thu Jun 21, 2012 10:17 pm
I don't quite understand how I'd set the PCM_CLK correctly? All this bit twiddling is confusing me :?

standard stereo audio 44.1 KHz @ 16bit would require 1,411,200Hz [is tht right?] any help would be appreciated!

Cheers,
Ears.
Posts: 43
Joined: Sun Jun 17, 2012 1:16 pm
by ceteras » Fri Jun 22, 2012 10:37 am
@DogEars: With the same arithmetic 6,144,000 Hz is for 192kHz (kilo samples per second actually), and 3072000 is for 96ks.
Posts: 192
Joined: Fri Jan 27, 2012 1:42 pm
Location: Romania
by DogEars » Fri Jun 22, 2012 12:09 pm
Now i'm more confused...

So was I correct with my calculation.. now how do i bit twiddle the registers to reflect what I want?

I guess it's this bit (from your code) but I can't quite figure out what it's doing:
Code: Select all
 printf("Confiure I2S clock\n");
 *(clk+0x26) = 0x5A000001;
 *(clk+0x27) = 0x5A000000 | 3<<12 | 1<<9; // divider: 3.125==0b11.001
Excuse my naive questions.. I'm been doing this C stuff for a matter of hours!

I sort of understand the bit shifting but don't know what the 12 and the 9 represent and what's this 0x26 & 0x27 where have you got them from?
Many thanks for your patients with this,
Cheers,
Graeme.
Posts: 43
Joined: Sun Jun 17, 2012 1:16 pm
by DogEars » Fri Jun 22, 2012 12:56 pm
DogEars wrote:what the 12 and the 9 represent

That was a typo, I meant 3 and 1.

G.
Posts: 43
Joined: Sun Jun 17, 2012 1:16 pm
by Gert van Loo » Fri Jun 22, 2012 12:59 pm
Look at the forth post from the beginning where I explain the registers and their bits:

Bits 11:0 are the mashing divider
Bits 23:12 are the integer divider.
User avatar
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 1982
Joined: Tue Aug 02, 2011 7:27 am
by ceteras » Fri Jun 22, 2012 1:01 pm
What I understand from the code:
*(clk+0x26) and *(clk+0x27) are the two registers given by Gert, 0x7E101098 and 0x7E10109C.

The value " 0x5A000000 | 3<<12 | 1<<9 " translates to 01011010.00000000.00110010.00000000 in binary.
So if we extract bits 23-12 we get the integer part: 00000000.0011 (3), and the fractional part 0010 which means 0.125.
You can check the conversions here: http://www.easysurf.cc/cnver17.htm#bf2tobf10 .

For 44100ks (1411200Hz), assuming 3.15 divider produces 6144000Hz, we can compute the required divider of 13.605 . This translates to a value of 0b01011010.00000000.11011001.10101110 or 0x5A00D9AE.

Can anyone confirm if my numbers are correct?
Also, regarding the required pins for I2S, I see two sets of pins in table 6-31 in the BCM2835 datasheet. One is for ALT0 alternative function assignments, at GPIO18-GPIO21 and not all 4 are available.
The other one is for ALT2 at GPIO28-GPIO31, physically available on the board.
Can we switch to these ones (ALT2) without affecting the behavior of other features on the GPIO connector?
Posts: 192
Joined: Fri Jan 27, 2012 1:42 pm
Location: Romania
by Gert van Loo » Fri Jun 22, 2012 3:22 pm
ceteras wrote:....
Also, regarding the required pins for I2S, I see two sets of pins in table 6-31 in the BCM2835 datasheet. One is for ALT0 alternative function assignments, at GPIO18-GPIO21 and not all 4 are available.
The other one is for ALT2 at GPIO28-GPIO31, physically available on the board.
Can we switch to these ones (ALT2) without affecting the behavior of other features on the GPIO connector?


You can always individually switch the ALT function of a GPIO pin. That will not effect any other GPIO pin.
Just make sure you really only change the pins you want to change as the ALT functions are common for every group of 10 pins.
User avatar
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 1982
Joined: Tue Aug 02, 2011 7:27 am
by ceteras » Fri Jun 22, 2012 4:26 pm
Thank you, Gert!
This means I have to read the registers, mask the bits I don't want to change then write the updated values.
I hope I'll learn how to actually do this soon, I haven't wrote a line of C code since... I can't remember.
Posts: 192
Joined: Fri Jan 27, 2012 1:42 pm
Location: Romania
by dariush » Fri Jun 22, 2012 6:01 pm
ceteras wrote:What I understand from the code:
*(clk+0x26) and *(clk+0x27) are the two registers given by Gert, 0x7E101098 and 0x7E10109C.

The value " 0x5A000000 | 3<<12 | 1<<9 " translates to 01011010.00000000.00110010.00000000 in binary.
So if we extract bits 23-12 we get the integer part: 00000000.0011 (3), and the fractional part 0010 which means 0.125.
You can check the conversions here: http://www.easysurf.cc/cnver17.htm#bf2tobf10 .

For 44100ks (1411200Hz), assuming 3.15 divider produces 6144000Hz, we can compute the required divider of 13.605 . This translates to a value of 0b01011010.00000000.11011001.10101110 or 0x5A00D9AE.

Can anyone confirm if my numbers are correct?



Yepp, that sounds about right.

(However, checking with a scope, I didn't read quite 6.144MHz on PCM_CLK. So maybe there is still something wrong with the fractional divider above.)

cheers
Dariush
Posts: 9
Joined: Fri Jun 15, 2012 6:25 pm
by dariush » Fri Jun 22, 2012 6:22 pm
Hi,

DogEars wrote:I don't quite understand how I'd set the PCM_CLK correctly? All this bit twiddling is confusing me :?

standard stereo audio 44.1 KHz @ 16bit would require 1,411,200Hz [is tht right?] any help would be appreciated!

Cheers,
Ears.


The frequency depends on what you are going to do with the signal, after it has left the Pi.
Sending out 2x16bit PCM would simple enough, but without any further signals or encoded information it will be impossible to know when one 16bit frame starts and which channel is which (that's what the PCM_SYNC signal is for in I2S).

I can see basically two to ways to go regarding audio output:

- you connect an I2S->SPDIF chip to the Pi (e.g. the CS8402). This requires modding of the Pi as PCM_SYNC is not easily available. The clock frequency of I2S would probably depend on what the I2S slave chip expects (I don't know much about I2S in that area).
- You produce a fully BMC-encoded SPDIF bit-stream on the Pi (at 6.144MHz), and then use a small circuit to convert it electrically to SPDIF (0.5V peak-to-peak). That signal could then be connected directly to e.g. an AV receiver. That's what I'm kind of trying to do.

cheers
Dariush
Posts: 9
Joined: Fri Jun 15, 2012 6:25 pm
by Gert van Loo » Fri Jun 22, 2012 8:15 pm
Or you could solder a wire to one of the PCM/I2S pins going to the 'revision' resistors.
User avatar
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 1982
Joined: Tue Aug 02, 2011 7:27 am
by DogEars » Fri Jun 22, 2012 9:21 pm
Gert van Loo wrote:Or you could solder a wire to one of the PCM/I2S pins going to the 'revision' resistors.


This is what I'm hoping to do, we should be able to solder a wire on to one of the pads (the one that's not 3.3V) of R7 (that's missing) that should give us access to GPIO29.

Will we still need to remove R9?

with ALT2 for just the GPIO29 will be to get to the PCM_FS via R7
and if what you've said above about ALT function of individual pins then we can leave the others at ALT0 and PCM_CLK & PCM_DOUT will be available on the header on pins 12 & 13 respectively.

I'm starting to understand what's going on :?

Graeme.
Posts: 43
Joined: Sun Jun 17, 2012 1:16 pm
by Gert van Loo » Fri Jun 22, 2012 9:31 pm
DogEars wrote:
Will we still need to remove R9?

......

Graeme.


Probably not but it would not hurt if you remove it. You have a slightly higher load on the pin but 10K is not really that much and there are no high frequencies so you do not have to worry about reflections.
User avatar
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 1982
Joined: Tue Aug 02, 2011 7:27 am