User avatar
MikeDB
Posts: 163
Joined: Sun Oct 12, 2014 8:27 am

PWM input clock (clk_pwm) frequency for Pi4

Tue Sep 17, 2019 6:20 am

The BCM2835 datasheet states "... clocked by clk_pwm which is nominally 100MHz but can be varied by the clock manager"

Can somebody point me to any information on the clock manager for the Pi4 please ?
Is it still 100MHz nominal even ? I assume the Pi3 was actually 5*19.2 MHz so is the Pi4 2*54Mhz or something else ?

Thank for any help

jdb
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 2160
Joined: Thu Jul 11, 2013 2:37 pm

Re: PWM input clock (clk_pwm) frequency for Pi4

Wed Sep 18, 2019 1:40 pm

Defaults to 107.14MHz in the firmware setup. The PLL channel that used to generate the 100MHz now runs at 750MHz instead of 500, so the fw uses 750/7.

Note that both the audio and GPIO PWM blocks use the same clock divider, so changing it after the audio jack has been used will likely lead to distorted (or nonfunctional) analogue audio.
Rockets are loud.
https://astro-pi.org

paulenuta
Posts: 44
Joined: Fri Oct 28, 2016 5:59 am
Location: Romania

Re: PWM input clock (clk_pwm) frequency for Pi4

Fri Sep 20, 2019 9:06 am

Hi,
on PI3/Pi3b+ we have oscillator clock of 19.2MHz, please confirm if for PI4 this clock is 54MHz.

Thanks a lot!
Paul

jdb
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 2160
Joined: Thu Jul 11, 2013 2:37 pm

Re: PWM input clock (clk_pwm) frequency for Pi4

Fri Sep 20, 2019 11:59 am

On pi 4 the oscillator frequency is 54MHz.
Rockets are loud.
https://astro-pi.org

paulenuta
Posts: 44
Joined: Fri Oct 28, 2016 5:59 am
Location: Romania

Re: PWM input clock (clk_pwm) frequency for Pi4

Sun Sep 22, 2019 12:51 pm

Thanks for confirmation!
I have a code to generate 1-3 beeps on a buzzer connected to GPIO18 via a MOSFET-N and after I adapted for both 19.2 MHz and 54 MHz, on RPI4 it is still a lot weaker than on RPI3B+.

Do you have any idea what could be the problem?

Paul

User avatar
jbeale
Posts: 3518
Joined: Tue Nov 22, 2011 11:51 pm
Contact: Website

Re: PWM input clock (clk_pwm) frequency for Pi4

Sun Sep 22, 2019 2:47 pm

An oscilloscope could show what the difference is. If you don't have one, but if you can post the code, then someone who does have one might be able to check what the signals look like. I've got a scope and a Pi3B but I don't have a Pi4.

Is your code generating the pulses using PWM hardware, or is it simply "bit-banging" the pin? If the latter, it's possible that different software optimizations on the newer CPU might believe a "do nothing" delay loop could be safely optimized away on the Pi4, causing a different frequency or duty cycle output.

paulenuta
Posts: 44
Joined: Fri Oct 28, 2016 5:59 am
Location: Romania

Re: PWM input clock (clk_pwm) frequency for Pi4

Mon Sep 23, 2019 5:11 pm

jbeale wrote:
Sun Sep 22, 2019 2:47 pm
An oscilloscope could show what the difference is. If you don't have one, but if you can post the code, then someone who does have one might be able to check what the signals look like. I've got a scope and a Pi3B but I don't have a Pi4.

Is your code generating the pulses using PWM hardware, or is it simply "bit-banging" the pin? If the latter, it's possible that different software optimizations on the newer CPU might believe a "do nothing" delay loop could be safely optimized away on the Pi4, causing a different frequency or duty cycle output.
It is hardware PWM.
Here is the code:

Code: Select all

/*********************************************************************
 * pwm.c - PWM HW program
 * sudo bash -c 'make clean; make all;'
 * ./pwm 1 or ./pwm 3 or ./pwm 20 100 5200  
 * DEPLOY:
 *		if different machine copy and
 *		sudo bash -c 'sudo chown root ./pwm; sudo chmod +x ./pwm; sudo chmod u+s ./pwm'
 *
 *********************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <bcm_host.h>

#define debug_print(fmt, ...) \
            do { if (DEBUG) fprintf(stderr, fmt, __VA_ARGS__); } while (0)
#define DEBUG 1

#define PWM_CLK_PASSWORD 0x5a000000
#define BCM2835_PWM_CONTROL 0
#define BCM2835_PWM_STATUS  1
#define BCM2835_PWM0_RANGE  4
#define BCM2835_PWM0_DATA   5

#define	PWMCLK_CNTL 40
#define	PWMCLK_DIV  41
#define BLOCK_SIZE 	(4*1024)

#define RPI3_PB		0x3F000000
#define RPI4_PB		0xFE000000

static volatile unsigned BCM2708_PERI_BASE, GPIO_BASE, PWM_BASE, CLK_BASE;
static volatile double clock_rate;

static volatile unsigned *ugpio = 0;
static volatile unsigned *ugpwm = 0;
static volatile unsigned *ugclk = 0;

static struct S_PWM_CTL {
	unsigned	PWEN1 : 1;
	unsigned	MODE1 : 1;
	unsigned	RPTL1 : 1;
	unsigned	SBIT1 : 1;
	unsigned	POLA1 : 1;
	unsigned	USEF1 : 1;
	unsigned	CLRF1 : 1;
	unsigned	MSEN1 : 1;
} volatile *pwm_ctl = 0;

static struct S_PWM_STA {
	unsigned	FULL1 : 1;
	unsigned	EMPT1 : 1;
	unsigned	WERR1 : 1;
	unsigned	RERR1 : 1;
	unsigned	GAP01 : 1;
	unsigned	GAP02 : 1;
	unsigned	GAP03 : 1;
	unsigned	GAP04 : 1;
	unsigned	BERR : 1;
	unsigned	STA1 : 1;
} volatile *pwm_sta = 0;

static volatile unsigned *pwm_rng1 = 0;
static volatile unsigned *pwm_dat1 = 0;

#define INP_GPIO(g) *(ugpio+((g)/10)) &= ~(7<<(((g)%10)*3))
#define SET_GPIO_ALT(g,a) \
    *(ugpio+(((g)/10))) |= (((a)<=3?(a)+4:((a)==4?3:2))<<(((g)%10)*3))

/*
 * Initialize GPIO/PWM/CLK Access
 */
static void
pwm_init() {
	int fd;	
	char *map;

	fd = open("/dev/mem",O_RDWR|O_SYNC);  /* Needs root access */
	if ( fd < 0 ) {
		perror("Opening /dev/mem");
		exit(1);
	}

	map = (char *) mmap(
		NULL,             /* Any address */
		BLOCK_SIZE,       /* # of bytes */
		PROT_READ|PROT_WRITE,
		MAP_SHARED,       /* Shared */
		fd,               /* /dev/mem */
		PWM_BASE          /* Offset to GPIO */
	);

	if ( (long)map == -1L ) {
		perror("mmap(/dev/mem)");    
		exit(1);
	}

	/* Access to PWM */
	ugpwm = (volatile unsigned *)map;
	pwm_ctl  = (struct S_PWM_CTL *) &ugpwm[BCM2835_PWM_CONTROL];
	pwm_sta  = (struct S_PWM_STA *) &ugpwm[BCM2835_PWM_STATUS];
	pwm_rng1 = &ugpwm[BCM2835_PWM0_RANGE];
	pwm_dat1 = &ugpwm[BCM2835_PWM0_DATA];

	map = (char *) mmap(
		NULL,             /* Any address */
		BLOCK_SIZE,       /* # of bytes */
		PROT_READ|PROT_WRITE,
		MAP_SHARED,       /* Shared */
		fd,               /* /dev/mem */
		CLK_BASE          /* Offset to GPIO */
	);

	if ( (long)map == -1L ) {
		perror("mmap(/dev/mem)");    
		exit(1);
	}

	/* Access to CLK */
	ugclk = (volatile unsigned *)map;

	map = (char *) mmap(
		NULL,             /* Any address */
		BLOCK_SIZE,       /* # of bytes */
		PROT_READ|PROT_WRITE,
		MAP_SHARED,       /* Shared */
		fd,               /* /dev/mem */
		GPIO_BASE         /* Offset to GPIO */
	);

	if ( (long)map == -1L ) {
		perror("mmap(/dev/mem)");    
		exit(1);
	}
	
	/* Access to GPIO */
	ugpio = (volatile unsigned *)map;

	close(fd);
}

/*
 * Establish the PWM frequency:
 */
static int
pwm_frequency(float freq) {
	debug_print(" Call pwm_frequency(%.lf) %s:%d\r\n",freq, __BASE_FILE__, __LINE__);
	long idiv;
	int rc = 0;

	/*
	 * Stop the clock:
	 */
	// ugclk[PWMCLK_CNTL] = 0x5A000020;	/* Kill clock */
	ugclk[PWMCLK_CNTL] = (ugclk[PWMCLK_CNTL]&~0x10)|PWM_CLK_PASSWORD;	/* Turn OFF enable flag */
	debug_print(" Turn OFF enable flag %s:%d\r\n",__BASE_FILE__, __LINE__);
	while( ugclk[PWMCLK_CNTL]&0x80 ) {
		debug_print(" Wait for Busy flag to turn OFF %s:%d\r\n",__BASE_FILE__, __LINE__);
	};															/* Wait for Busy flag */
	// usleep(10);
	pwm_ctl->PWEN1 = 0;     		/* Disable PWM */
	debug_print(" Disable PWM %s:%d\r\n",__BASE_FILE__, __LINE__);
	// usleep(10);  

	/*
	 * Compute and set the divisor :
	 */
	idiv = (long) ( clock_rate / (double) freq );
	if ( idiv < 1 ) {
		idiv = 1;			/* Lowest divisor */
		rc = -1;
	} else if ( idiv >= 0x1000 ) {
		idiv = 0xFFF;			/* Highest divisor */
		rc = +1;
	}

	ugclk[PWMCLK_DIV] = PWM_CLK_PASSWORD | ( idiv << 12 );
    
	/*
	 * Set source to oscillator and enable clock:
	 */
    ugclk[PWMCLK_CNTL] = 0x5A000011;
	while( !(ugclk[PWMCLK_CNTL]&0x80) ) {
		debug_print(" Wait for Busy flag to turn ON %s:%d\r\n",__BASE_FILE__, __LINE__);
	};
	pwm_ctl->PWEN1 = 1;     		/* Enable PWM */
	debug_print(" Enable PWM %s:%d\r\n",__BASE_FILE__, __LINE__);

	/*
 	 * GPIO 18 is PWM, when set to Alt Func 5 :
	 */
	INP_GPIO(18);		/* Set ALT = 0 */
	SET_GPIO_ALT(18,5);	/* Or in '5' */

	pwm_ctl->MODE1 = 0;     /* PWM mode */
	pwm_ctl->RPTL1 = 0;
	pwm_ctl->SBIT1 = 0;
	pwm_ctl->POLA1 = 0;
	pwm_ctl->USEF1 = 0;
	pwm_ctl->MSEN1 = 0;     /* PWM mode */
	pwm_ctl->CLRF1 = 1;
	return rc;
}

/*
 * Set PWM to ratio N/M, and enable it:
 */
static void
pwm_ratio(unsigned n,unsigned m) {

	debug_print(" Call pwm_ratio(%d, %d) %s:%d\n\r",n, m, __BASE_FILE__, __LINE__);
	// pwm_ctl->PWEN1 = 0;					/* Disable */
	// debug_print(" Disable PWM %s:%d\r\n",__BASE_FILE__, __LINE__);

	*pwm_rng1 = m;
	*pwm_dat1 = n;

	if ( !pwm_sta->STA1 ) {
		if ( pwm_sta->RERR1 )
			pwm_sta->RERR1 = 1;
		if ( pwm_sta->WERR1 )
			pwm_sta->WERR1 = 1;
		if ( pwm_sta->BERR )
			pwm_sta->BERR = 1;
	}

	pwm_ctl->PWEN1 = 1;					/* Enable */
	debug_print(" Enable PWM %s:%d\r\n",__BASE_FILE__, __LINE__);
}

/*
 * MicroSeconds sleep
 */ 
int msleep(long msec) {
	
    struct timespec ts;
    int res;

    if (msec < 0)
    {
        errno = EINVAL;
        return -1;
    }

    ts.tv_sec = msec / 1000;
    ts.tv_nsec = (msec % 1000) * 1000000;

    do {
        res = nanosleep(&ts, &ts);
    } while (res && errno == EINTR);

    return res;
}
/*
 * Main program:
 */
int
main(int argc,char **argv) {
	
	debug_print("< PWM start bcm_host_get_peripheral_address >%s\n\r",  __BASE_FILE__);
	/* Get peripheral addres via <bcm_host.h>
	* see: https://www.raspberrypi.org/documentation/hardware/raspberrypi/peripheral_addresses.md
	*/
	BCM2708_PERI_BASE	= bcm_host_get_peripheral_address();
	GPIO_BASE			= (BCM2708_PERI_BASE + 0x200000);
	PWM_BASE			= (BCM2708_PERI_BASE + 0x20C000);
	CLK_BASE			= (BCM2708_PERI_BASE + 0x101000);
	static volatile int n, m = 0;
	static volatile float f, delay = 0.0;

	if ( BCM2708_PERI_BASE == RPI4_PB ) {
		/* RPI4 = 54000000.0 */
		clock_rate = 54000000.0;
		n = 20;
		m = 100;
		f = 5200.0;
		delay = 100.0;
	} else if ( BCM2708_PERI_BASE == RPI3_PB ) {
		/* RPI3 = 19200000.0 */
		clock_rate = 19200000.0;
		n = 50;
		m = 100;
		f = 5200.0;
		delay = 100.0;
	}
	debug_print(" BCM2708_PERI_BASE:\t0x%X\r\n GPIO_BASE:\t\t0x%X\r\n PWM_BASE:\t\t0x%X\r\n CLK_BASE:\t\t0x%X\n\r clock_rate:\t\t%.lf\n\r",BCM2708_PERI_BASE,GPIO_BASE,PWM_BASE,CLK_BASE,clock_rate);

	pwm_init();

	if ( argc == 2 ) {
		int sw = atoi(argv[1]);
		/* Configure and enable PWM */
		pwm_frequency(f);
		pwm_ratio(n,m);
		
		/* Cycle for 1..3 */
		debug_print("Cycles: %d\n",sw);
		for (unsigned char z = 1; z <= sw; z += 1 ) {
			pwm_ctl->PWEN1 = 1;					/* Enable PWM */
			debug_print(" Enable PWM %s:%d\r\n",__BASE_FILE__, __LINE__);
			debug_print("%d - PWM set for %d/%d, frequency %.1f, delay %.1f\n",z,n,m,f,delay);
			msleep(delay);
			pwm_ctl->PWEN1 = 0;					/* Disable PWM */
			debug_print(" Disable PWM %s:%d\r\n",__BASE_FILE__, __LINE__);
			/* InterBeep delay */
			msleep(delay-25);	
		}
	} else	{    
		if ( argc > 1 ) {
			n = atoi(argv[1]); debug_print("argc: %d, argv[1]: %d\n",argc,n);
		}
		if ( argc > 2 ) {
			m = atoi(argv[2]); debug_print("argc: %d, argv[2]: %d\n",argc,m);
		}
		if ( argc > 3 ) {
			f = atof(argv[3]); debug_print("argc: %d, argv[3]: %.1f\n",argc,f);
		}
		if ( argc > 4 ) {
			delay = atof(argv[4]); debug_print("argc: %d, argv[4]: %.1f\n",argc,delay);
		}
		if ( argc > 1 ) {
			if ( n > m || n < 1 || m < 1 || f < 586.0 || f > clock_rate ) {
				fprintf(stderr,"Value error: N=%d, M=%d, F=%.1f\n",n,m,f);
				return 1;
			}
		}
		debug_print(" Start PWM set for %d/%d, frequency %.1f, delay %.1f\n",n,m,f,delay);
		pwm_frequency(f);
		pwm_ratio(n,m);
		msleep(delay);
		pwm_ctl->PWEN1 = 0;					/* Disable PWM */
		debug_print(" Disable PWM %s:%d\r\n",__BASE_FILE__, __LINE__);
	}
	return 0;
}

/*********************************************************************
 * End pwm.c
 *********************************************************************/
and Makefile:

Code: Select all

######################################################################
# PI 3B+, PI4 HW PWM GPIO18-ALT5 PWM1
# 
######################################################################

CC	= gcc
OPTS	= -Wall
DBG	= -O0 -g
CFLAGS	= $(OPTS) $(DBG)
BCMHOST	= -I/opt/vc/include -L/opt/vc/lib -lbcm_host
LDFLAGS	= -lm $(BCMHOST)

.c.o:
	$(CC) -c $(CFLAGS) $(BCMHOST) $< -o $*.o

all:	pwm

pwm:	pwm.o
	$(CC) pwm.o -o pwm $(LDFLAGS) 
	sudo chown root ./pwm
	sudo chmod u+s ./pwm

clean:
	rm -f *.o core errs.t

clobber: clean
	rm -f pwm

######################################################################
#  End Makefile.
######################################################################
and hardware connections:

Code: Select all

 *	- N-MOSFET connected to gpiopin, 2N7002-[https://en.wikipedia.org/wiki/2N7000];
 *	- PB-0927PQ Buzzer  connected to N-MOSFET Drain terminal;
 *	  [https://ro.mouser.com/datasheet/2/252/PB-0927PQ-1520569.pdf]
 *
 *                   ┌─────────────────────┐
 *                   │PB-0927PQ Buzzer     │
 *                   │  negative terminal  │
 *                   └┬────────────────────┘
 *                    │D
 *             G   │──┘
 * [GPIO18]──────┤ │<─┐  2N7002
 *                 │──┤
 *                    │S
 *                   ─┴─
 *                   GND
 *
Take care of https://github.com/raspberrypi/firmware/issues/1161 in case of RPI4.

Thanks!
Last edited by paulenuta on Tue Sep 24, 2019 10:44 am, edited 1 time in total.

paulenuta
Posts: 44
Joined: Fri Oct 28, 2016 5:59 am
Location: Romania

Re: PWM input clock (clk_pwm) frequency for Pi4

Mon Sep 23, 2019 5:13 pm

Should I move'it to a new post?

User avatar
MikeDB
Posts: 163
Joined: Sun Oct 12, 2014 8:27 am

Re: PWM input clock (clk_pwm) frequency for Pi4

Wed Sep 25, 2019 6:33 pm

Remember with a 2N7002 you'll only get 100mA drain current with a 3V drive signal max so the buzzer may be quieter than with a direct connection to the power supply

paulenuta
Posts: 44
Joined: Fri Oct 28, 2016 5:59 am
Location: Romania

Re: PWM input clock (clk_pwm) frequency for Pi4

Thu Sep 26, 2019 4:45 pm

That's for continuous current AFAIK, but PB-0927PQ buzzer has current consumption of max. 75mA, as per datasheet.

Return to “Interfacing (DSI, CSI, I2C, etc.)”