WAHa06x36
Posts: 1
Joined: Wed Mar 20, 2013 1:58 pm

Correctly configuring clocks for PWM or GPIO [obsolete]

Wed Mar 20, 2013 2:09 pm

I have been fighting for a while now with the clock generator hardware on the Pi, trying to get PWM working right. And I seem to have it working now, so I figured I would make a post for the benefit of anyone else who is looking for information about this. All the available information I could find seemed quite confused and partly wrong.

This is still partly based on guesswork, as not all of this is properly documented in the data sheets, so if anyone has any suggestions for corrections, do post them.

This is the code I have for starting up PWM. It is written for bare metal mode, thus the direct memory accesses, but it should be easy enough to adapt to Linux if you just mmap() /dev/mem as usual:

Code: Select all

	#define PWMCTL (*(volatile uint32_t *)0x2020c000)
	#define PWMSTA (*(volatile uint32_t *)0x2020c004)
	#define PWMDMAC (*(volatile uint32_t *)0x2020c008)
	#define PWMRNG1 (*(volatile uint32_t *)0x2020c010)
	#define PWMDAT1 (*(volatile uint32_t *)0x2020c014)
	#define PWMFIF1 (*(volatile uint32_t *)0x2020c018)
	#define PWMRNG2 (*(volatile uint32_t *)0x2020c020)
	#define PWMDAT2 (*(volatile uint32_t *)0x2020c024)

	#define CM_PWMCTL (*(volatile uint32_t *)0x201010a0)
	#define CM_PWMDIV (*(volatile uint32_t *)0x201010a4)

	PWMCTL=0; // Turn off PWM.

	CM_PWMCTL=(CM_PWMCTL&~0x10)|0x5a000000; // Turn off enable flag.
	while(CM_PWMCTL&0x80); // Wait for busy flag to turn off.
	CM_PWMDIV=0x5a000000|(5<<12); // Configure divider.
	CM_PWMCTL=0x5a000206; // Source=PLLD (500 MHz), 1-stage MASH.
	CM_PWMCTL=0x5a000216; // Enable clock.
	while(!(CM_PWMCTL&0x80)); // Wait for busy flag to turn on.

	PWMRNG1=100;
	PWMDAT1=10;

	PWMCTL=0x0081; // Channel 1 M/S mode, no FIFO, PWM mode, enabled.
This configures the PWM for a 100 MHz clock, sets up a range of 100 to give an output frequency of 1 MHz and a duty cycle of 1/10. Unlike most of the other code I've seen, it does not use the kill flag on the clock, which is documented to be for debug purposes only and should not be used, and it does not use any usleeps(), but instead checks the busy flag in the proper way. Some code examples I have seen claim that this does not work, but it seems to work fine for me. The code is quite careful to not change things in the wrong order, according to the hints in the data sheet.

This seems to me to be the correct way to configure the clock, but if anyone has any problems with it, do tell.

Relatedly, here is how to set up the GPIO clock to output a clock signal on one of the GPIO pins, if you want to play with the FM transmission tricks or something:

Code: Select all

	#define CM_GP0CTL (*(volatile uint32_t *)0x20101070)
	#define CM_GP0DIV (*(volatile uint32_t *)0x20101074)

	CM_GP0CTL=CM_GP0CTL&~0x10; // Turn off enable flag.
	while(CM_GP0CTL&0x80); // Wait for busy flag to turn off.
	CM_GP0DIV=0x5a000000|(10<<12); // Configure divider.
	CM_GP0CTL=0x5a000206; // Source=PLLD (500 MHz), 1-stage MASH.
	CM_GP0CTL=0x5a000216; // Enable clock.
	while(!(CM_GP0CTL&0x80)); // Wait for busy flag to turn on.

	SetGPIOAlternateMode(4,0);
This code sets the clock to 50 MHz. Adjust the divider as needed.

Edit: Fixed a line in the "Turn off enable flag" line to properly include the password.

DrPinball
Posts: 57
Joined: Fri May 04, 2012 6:44 pm

Re: Correctly configuring clocks for PWM or GPIO

Sun May 26, 2013 9:24 pm

Many thanks - I wanted to control the brightness of an LVDS panel using the PWM of the Pi. This code helped me get it sorted, cheers!! :D

dragondead9
Posts: 1
Joined: Tue Jun 18, 2013 7:28 pm

Re: Correctly configuring clocks for PWM or GPIO

Tue Jun 18, 2013 7:37 pm

I'm attempting to build a current pulse circuit that uses the RasPi for generating square waves (PWM). I need to be able to generate a PWM signal up to ~100MHz. I noticed the RasPi CPU can operate around 300 MHz, but obviously not all of this speed is able to be directly accessed. After reading your post, you make it seem like it is possible to generate a 100MHz PWM signal (I'm not sure at what duty cycle). On all the other forums I read about this topic, people stated the fastest they could generate was a 1MHz signal.

I haven't purchased the RasPi yet because I want to make sure it is possible to produce a PWM signal at the frequency I need. Could you please confirm what is the upper limit on PWM frequencies and how to modify the frequency and duty cycle of the signal.

Your help is greatly appreciated.

Pieter-Jan5000
Posts: 40
Joined: Tue Jun 12, 2012 7:20 pm
Contact: Website

Re: Correctly configuring clocks for PWM or GPIO

Wed Jun 19, 2013 2:16 pm

Cool! Thanks for this!
http://www.pieter-jan.com/

Poofjunior
Posts: 35
Joined: Tue May 15, 2012 5:27 pm
Location: Claremont California

Re: Correctly configuring clocks for PWM or GPIO

Thu Jan 16, 2014 5:50 pm

Many thanks as well!

One note: I can't seem to find

Code: Select all

   #define CM_PWMDIV (*(volatile uint32_t *)0x201010a4)
in the peripherals datasheet. Is there another datasheet that has this elsewhere?

(I also double-checked, looking for it with the 0x7Exxxxxx prefix instead.)

Thanks in advance for posting this up!

Poofjunior
Posts: 35
Joined: Tue May 15, 2012 5:27 pm
Location: Claremont California

Re: Correctly configuring clocks for PWM or GPIO

Thu Jan 16, 2014 7:00 pm

Ah, to answer part of my own question, those registers exist in an addendum to the datasheet which I could only find here below:
http://www.scribd.com/doc/127599939/BCM ... dio-clocks

Is that datasheet available anywhere else?

PiyushS
Posts: 1
Joined: Tue Oct 07, 2014 9:29 am

Re: Correctly configuring clocks for PWM or GPIO

Tue Oct 07, 2014 10:02 am

I am trying to get PWM up and running but somehow I can't. I used the same setting as provided in this discussion.... :(
Any I am pasting my code... it compiles and runs without error but don't produce anything on CRO..
I am pasting my code please try running it and help me to debug it.

Code: Select all

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

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

int  mem_fd, i2cfd;
void *gpio_map, *i2cmap;
volatile unsigned *gpio;

//*((gpio+0x205000))
   #define PWMCTL *((gpio + 0x20c000)+0)
   #define PWMSTA *((gpio + 0x20c000)+1)
   #define PWMDMAC *((gpio + 0x20c000)+2)
   #define PWMRNG1 *((gpio + 0x20c000)+4)

   #define PWMDAT1 *((gpio + 0x20c000)+5)

   #define PWMFIF1 *((gpio + 0x20c000)+6)

   #define PWMRNG2 *((gpio + 0x20c000)+8)

   #define PWMDAT2 *((gpio +0x20c000)+9)


   #define CM_PWMCTL *((gpio +0x1010a0)+0)

  #define CM_PWMDIV *((gpio +0x1010a0)+4)

// GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y)

#define INP_GPIO(g) *((gpio+0x200000)+((g)/10)) &= ~(7<<(((g)%10)*3))
#define OUT_GPIO(g) *((gpio+0x200000)+((g)/10)) |=  (1<<(((g)%10)*3))
#define SET_GPIO_ALT(g,a) *((gpio+0x200000)+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))

void setup_io();
void setup_PWM();

void main(int argc, char **argv)
{
 setup_io();
 setup_PWM();

 PWMCTL=0; // Turn off PWM.

   CM_PWMCTL=(CM_PWMCTL&~0x10)|0x5a000000; // Turn off enable flag.
   while(CM_PWMCTL&0x80); // Wait for busy flag to turn off.
   CM_PWMDIV=0x5a000000|(5<<12); // Configure divider.
   CM_PWMCTL=0x5a000206; // Source=PLLD (500 MHz), 1-stage MASH.
   CM_PWMCTL=0x5a000216; // Enable clock.
   while(!(CM_PWMCTL&0x80)); // Wait for busy flag to turn on.

   PWMRNG1=100;
   PWMDAT1=10;

   PWMCTL=0x0081; // Channel 1 M/S mode, no FIFO, PWM mode, enabled.
}

void setup_io()
{
   /* 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 */
   gpio_map = mmap(
      NULL,             //Any adddress in our space will do
      0x01000000,       //Map length
      PROT_READ|PROT_WRITE,// Enable reading & writting to mapped memory
      MAP_SHARED,       //Shared with other processes
      mem_fd,           //File to map
      0x20000000 //GPIO_BASE         //Offset to GPIO peripheral
   );

   close(mem_fd); //No need to keep mem_fd open after mmap

   if (gpio_map == MAP_FAILED) {
      printf("mmap error %d\n", (int)gpio_map);//errno also set!
      exit(-1);
   }

   // Always use volatile pointer!
   gpio = (volatile unsigned *)gpio_map;
} // setup_io

void setup_PWM()
{
  INP_GPIO(18); // must use INP_GPIO before we can use OUT_GPIO
  SET_GPIO_ALT(18, 5);  //PWM alt fun 5
}

EnglishmanInNY
Posts: 4
Joined: Thu Mar 31, 2016 1:33 pm

Re: Correctly configuring clocks for PWM or GPIO

Tue Apr 05, 2016 3:40 pm

I don't know if it's authorized to post on old topics but here it is.

I followed what says the author of the post and it's working perfectly when I generate PWM signals, thanks a lot to the author! :D But I don't understand some of the parts about the clock :

1°) How did he find that the registers to access the CTL and DIV register for the pwm are the base address of the clock register (0x20101000) with an offset of 0xA0 for PWMCTL and of 0xA4 for PWMDIV? I didn't find those offset in the datasheet of the BCM2835 (page 105).

2°) How does he know that the frequency of the PLL is 500 MHz?

phil123456
Posts: 39
Joined: Fri Jun 26, 2020 5:29 pm

Re: Correctly configuring clocks for PWM or GPIO

Thu Jul 09, 2020 9:03 pm

#define CM_PWMCTL (*(volatile uint32_t *)0x201010a0)
#define CM_PWMDIV (*(volatile uint32_t *)0x201010a4)

what are these register ?

they are nowhere to be found in the datasheet

cleverca22
Posts: 581
Joined: Sat Aug 18, 2012 2:33 pm

Re: Correctly configuring clocks for PWM or GPIO

Thu Jul 09, 2020 10:19 pm

phil123456 wrote:
Thu Jul 09, 2020 9:03 pm
#define CM_PWMCTL (*(volatile uint32_t *)0x201010a0)
#define CM_PWMDIV (*(volatile uint32_t *)0x201010a4)

what are these register ?

they are nowhere to be found in the datasheet
a: this thread is 4 years old, and a new thread should be started instead
b: https://elinux.org/BCM2835_registers there are a lot of undocumented registers that got revealed when the 3d driver stack got some source released

phil123456
Posts: 39
Joined: Fri Jun 26, 2020 5:29 pm

Re: Correctly configuring clocks for PWM or GPIO

Fri Jul 10, 2020 3:54 pm

I also found this

https://groups.google.com/forum/#!topic ... foWTDSwF8k

also I don't see any mold around this page, people will still find it in google and read it anyway

these 'thread too old' reflex is not constructive, I mean who cares

cleverca22
Posts: 581
Joined: Sat Aug 18, 2012 2:33 pm

Re: Correctly configuring clocks for PWM or GPIO

Fri Jul 10, 2020 6:09 pm

phil123456 wrote:
Fri Jul 10, 2020 3:54 pm
these 'thread too old' reflex is not constructive, I mean who cares
forum moderators that keep locking threads

User avatar
mahjongg
Forum Moderator
Forum Moderator
Posts: 13009
Joined: Sun Mar 11, 2012 12:19 am
Location: South Holland, The Netherlands

Re: Correctly configuring clocks for PWM or GPIO

Fri Jul 10, 2020 7:58 pm

Indeed now locking useless thread, seven years ago the (unspecified) hardware was probably different on register levels.

Using old outdated hardware info only wastes peoples time, google should not point people to here, (or to other obsolte info) but to newer info, so locking, and marking obsolete . Hoping to prevent other people from falling into such traps. Thats why necroing seven year old posts doesn't do anybody a service.

Return to “General programming discussion”