樹莓派聲音︰頃聽者

登廬山望石門‧宋鮑照

訪世失隱淪,從山異靈士。
明發振雲冠,升嶠遠棲趾。
高岑隔半天,長崖斷千里。
氛霧承星辰,潭壑洞江汜。
嶄絕類虎牙,巑岏象熊耳。
埋冰或百年,韜樹必千祀。
雞鳴清澗中,猿嘯白雲裏。
瑤波逐穴開,霞石觸峰起。
迴互非一形,參差悉相似。
傾聽鳳管賓,緬望釣龍子。
松桂盈膝前,如何穢城市。

 

振羽 %e7%be%bd 騰空之時,音聲分明。發芽 %e8%8a%bd 伏藏之際,難得頃聽。

一則樹莓派探索者追尋更好聲音的故事,早已悄悄展開︰

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”)

    //
    //  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