YCN-
Posts: 246
Joined: Fri Jun 10, 2016 3:18 pm

I2S - Clock trouble

Wed May 10, 2017 9:46 am

Hi,

I'm building an I2S sound driver. I'd like it to be 32bits/48kHz to be working with my DAC.
Well that's great, but here are my troubles, as I said in the I2S sound thread here : viewtopic.php?p=1159377#p1159377 , I just understood that I needed to provide my very own Clk for BCK and LRCLK. Okay sure no problem I was thinking, but the problem is that the datasheet seemed to be not complete enough for me to use.
I'm currently using a code I found online that is showing me how to use PCM_CLK. But here's the problem, I don't feel like I'm really setting the clocks, ie I can change the value of the clock with the integer part but can't with the fractional part.
Anyway that just part of the problem because the clock I'm struggling with is stuck at 19.2 MHz (I figured that out using an integer divider of 10). So I would need to set my divider to 19.2/3.072=6.25. (3.072 comes from 64*48kHz = 3.072MHz)
So the closest I can go without the divider part is 6 which give me a clock at 3.2MHz, which isn't close enough to what I'd like to have.

Here's the code I trying to understand in userspace before porting it to kernel space, it's a very simplified version of it but it's based on a userspace code done by Dom & Gert:

Code: Select all

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); 
  }               
      
  //printf("Disabling I2S clock\n");                                 
 *(clk+0x26) = 0x5A000000;                                            
 *(clk+0x27) = 0x5A000000;                                            
                                                                                
 usleep(10);                                                                     
                                                                            
 //printf("Confiure I2S clock\n");                                    
 *(clk+0x26) = 0x5A000000 | (1<<9);                                            
 *(clk+0x27) = 0x5A006004;                                            
                                                                      
 usleep(10);                                                          
 printf("Enabling I2S clock\n");                                      
 *(clk+0x26) = 0x5A000011;          
}


How do I enable the fractionnal part ? The datasheet seemed to say that it was by enabling the Mash which I did, they said that bit 9 will do Mash = 1 --> as p105 of bcm2835 says I will have clk_output(average) = source / ( DIVI + DIVF / 1024 ).
DIVI = 6, DIVF = 256 , I should then have clk_output(average) = 19.2 / ( 6 + 256/1024) = 19.2 / 6.25 = 3.072 MHz.
But I don't... I've seen on different topics that this was a problem, but it doesn't seemed to have been resolved... So what should I do ?

Many thanks for any leads on the matter.

Regards,

YCN-

EDIT : I can post you full code if needed but I'm not sure it would give you guys more info, by the way I'm measuring the freq output with a good oscilloscope !
EDIT2: I do not seem to be able to use the PLL (when I change my the source it doesn't seem to change a lot of stuff, so may be it's about addressing the right Clock Manager General Purpose Clocks Control but I can't find the adress on the datasheet. So I kept the define : #define CLOCK_BASE (BCM2835_PERI_BASE + 0x101000)

YCN-
Posts: 246
Joined: Fri Jun 10, 2016 3:18 pm

Re: I2S - Clock trouble

Wed May 10, 2017 11:49 am

It seems that finaly writing at *(clk+0x26) has no effect, I write a 0 in there but still had the exact same clock. So where should I write instead?

Edit : It has effect because it works when it come to kill the clock. ie : *(clk+0x26) = 0x5A000020;

Regards,

YCN-

YCN-
Posts: 246
Joined: Fri Jun 10, 2016 3:18 pm

Re: I2S - Clock trouble

Wed May 10, 2017 12:21 pm

Hi,

Well it was a stupid mistake :

*(clk+0x26) = 0x5A000011;

Of course when I add
*(clk+0x26) = 0x5A000011 | ( 1 << 9 );
It seem to work even if I don't have the correct frequency. But I will find it now that this is working ahah!

That enables the clock but doesn't enable the MASH.

Still there's this document that is weird when it comes to addressing, what is it about ?

https://www.scribd.com/doc/127599939/BC ... dio-clocks

Where is the proper datasheet of those matter?

Regards,

YCN-

YCN-
Posts: 246
Joined: Fri Jun 10, 2016 3:18 pm

Re: I2S - Clock trouble

Wed May 10, 2017 12:45 pm

Okay problem solved, if someday someone want to have a 3.072MHz clock on PCM clk here's the code he need to do so :
This code is has mostly been done by "Gert & Dom", I only tuned it for my purpose. ie having a 3.072MHz, which I do now. If someone came accross this problem he will find this solution. And be able to hopefully solve his problem.

Code: Select all

#define BCM2708_PERI_BASE        0x3F000000
#define GPIO_BASE                (BCM2708_PERI_BASE + 0x200000) /* GPIO controll
#define I2S_BASE                (BCM2708_PERI_BASE + 0x203000) /* GPIO controlle
#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_GPI
#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
                                
#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);
  }                
 printf("Disabling I2S clock\n");                                             
 *(clk+0x26) = 0x5A000000;                                                      
 *(clk+0x27) = 0x5A000000;                                                  
                                        
 usleep(10);                             
                                  
 printf("Configure I2S clock\n");                             
 *(clk+0x26) = 0x5A000000 | (1<<9)  ;                     
 *(clk+0x27) = 0x5A006400;                                                                                 
 usleep(10);                                                          
 printf("Enabling I2S clock\n");                                               
 *(clk+0x26) = 0x5A000011 | (1<<9)  ;                                                          

}

// 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    
Remaining quest (lol): how to generate the 48kHz clock on PCM_FS
Last edited by YCN- on Thu May 11, 2017 7:36 am, edited 1 time in total.

User avatar
Gert van Loo
Posts: 2484
Joined: Tue Aug 02, 2011 7:27 am
Contact: Website

Re: I2S - Clock trouble

Wed May 10, 2017 3:44 pm

YCN:

At that time of that document I was the person responsible for the BCM2835 product at Broadcom.
The document on scribd is an extract from the 'clocks and reset' section of the official datasheet.
I know nothing more then what is written there.

The data sheet of the BCM2835 which is out in the public domain is a heavily adapted version of the
official datasheet with only the most common peripherals. I removed everything which might
be interesting for the Broadcom competitors.

I no longer work at Broadcom thus I also have no longer access to more information.
You might try the Raspberry-Pi organisation as they have full access to all data.
I think the guy who designed the clocks now also works for them.

YCN-
Posts: 246
Joined: Fri Jun 10, 2016 3:18 pm

Re: I2S - Clock trouble

Thu May 11, 2017 12:52 pm

Thanks Gert,

Really cool to give my post a bit of time.
Thanks for the info about the datasheet, I have to admit that until I had to put my noze onto I2S it wasn't a problem that it was not complete enough.

It remain several point about the same stuff that I could not understand. How do I generate the PCM_FS ?
In my understanding it's generated by using the same osc that I used for my 3,072MHz clock (aka BCK or PCM_Clock). But I can't find how I have to do that since for the same reason I don't have the addresses needed.
The second point is about the fact that it's not clear enough to me about how it should be done manually by myself, or done by the RPI thanks to the channel settings?

EDIT : To answer all the mysteries that I came accross, in the end, PCM_FS is generated directly by setting FSLEN & FLEN, in my case I set FSLEN = 32 & FLEN = 64.

YCN-
Last edited by YCN- on Fri May 12, 2017 9:26 am, edited 1 time in total.

YCN-
Posts: 246
Joined: Fri Jun 10, 2016 3:18 pm

Re: I2S - Clock trouble

Fri May 12, 2017 9:02 am

Okay so I ended up solving all my problems, here's how you can generate PCM_CLK & FS_CLK.
In the code bellow PCM_CLK = 3.072MHz and FS_CLK = 48kHz.
For those interrested in the matter you can check the datasheet in order to see why every thing is done that way. If you have question please feel free to ask.

Code: Select all

#define BCM2708_PERI_BASE        0x3F000000
#define GPIO_BASE                (BCM2708_PERI_BASE + 0x200000) /* GPIO controll */
#define I2S_BASE                (BCM2708_PERI_BASE + 0x203000) /* GPIO controlle */
#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_GPI
#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);                   
  }                 
 *(clk+0x26) = 0x5A000000;                 
 *(clk+0x27) = 0x5A000000;                                                     
                                                                               
usleep(10);                                                                 
                               
 printf("Configure I2S clock\n");       
 *(clk+0x26) = 0x5A000000 | (1<<9)  ;
 *(clk+0x27) = 0x5A006400;                                                     
                                                                     
 usleep(10);                                                         
 printf("Enabling I2S clock\n");                                               
 *(clk+0x26) = 0x5A000011 | (1<<9)  ;     
 usleep(1000000);  
  //Master mode                                                       
 *(i2s+2)&=~ ( 1 << 23);                   
 *(i2s+2)&=~ ( 1 << 21);                                                        
 //FLEN & FSLEN set : 
 *(i2s+2)|= 0x8020;                                                             
                       
 return 0;       
}

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;                                                                             
}                      
YCN-

felipeduque
Posts: 2
Joined: Thu Aug 31, 2017 5:20 pm

Re: I2S - Clock trouble

Thu Aug 31, 2017 5:29 pm

That's almost exactly what I'm trying to achieve :)

However, something has been bugging me for hours: where does those 0x26 and 0x27 come from? They're being added to 0x20101000, aren't they? This would result in addresses 0x20101026 and 0x20101027 being modified, is that right? But from what I've gathered so far, the registers of interest are in 0x7e101098 (PCMCTL) and 0x7e10109c (PCMDIV), which, translated to ARM address space, would be located at 0x20101098 and 0x2010109c.
Obviously they're different from above mentioned 0x20101026 and 0x20101027.

What am I doing wrong?

YCN-
Posts: 246
Joined: Fri Jun 10, 2016 3:18 pm

Re: I2S - Clock trouble

Fri Sep 01, 2017 8:03 am

Oh I though you were confused with the difference between adresses and content. Well I can't answer your question because obviously the datasheet isn't agreed with the 0x26 or 0x27 indeed the datasheet pages 107 and 108 doesn't give those adresses.
There's a lot of mysteries around this datasheet. In the end the answer I mostly get about my problems was that the datasheet was incomplete for commercial matters, and that you just have to deal with it...
I find those adresses on other codes from "Dom & Gert" which are the holy grail of people trying to do low level stuff ^^.

(But if you're looking for the two clocks of the PCM block, those are the adresses even if I can not tell you why!)

Good luck,

YCN-

User avatar
Gert van Loo
Posts: 2484
Joined: Tue Aug 02, 2011 7:27 am
Contact: Website

Re: I2S - Clock trouble

Fri Sep 01, 2017 1:48 pm

This would result in addresses 0x20101026 and 0x20101027 being modified, is that right?
No, that is wrong (This is where you find out if you know C-code a bit or a bit more :D ).
clk is a (volatile) pointer to unsigned (int is implied). sizeof(unsigned) is four, thus adding 0x26 or 0x27 to a pointer pointing to objects of size four adds 26*4=104 (0x68) and 27*4=108 (0x6C).

(For the Dutch still remembering the old insurance adverts: "gelukkig heb ik meer verstand van hardware").

User avatar
rpdom
Posts: 15173
Joined: Sun May 06, 2012 5:17 am
Location: Chelmsford, Essex, UK

Re: I2S - Clock trouble

Fri Sep 01, 2017 8:22 pm

Gert van Loo wrote:
Fri Sep 01, 2017 1:48 pm
clk is a (volatile) pointer to unsigned (int is implied). sizeof(unsigned) is four, thus adding 0x26 or 0x27 to a pointer pointing to objects of size four adds 26*4=104 (0x68) and 27*4=108 (0x6C).
0x26 * 4 = 0x98 (152) and 0x27 * 4 = 0x9c (156)

Or did I get it wrong?

User avatar
Gert van Loo
Posts: 2484
Joined: Tue Aug 02, 2011 7:27 am
Contact: Website

Re: I2S - Clock trouble

Sun Sep 03, 2017 5:32 pm

Yep! Sorry I used 26 and 27 not 0x26 and 0x27. My fault.

Return to “Advanced users”