" gpio drive <group> <value>\n"
" gpio pwm-bal/pwm-ms \n"
" gpio pwmr <range> \n"
+ " gpio pwmc <divider> \n"
" gpio load spi/i2c\n"
" gpio gbr <channel>\n"
- " gpio gbw <channel> <value>\n" ;
+ " gpio gbw <channel> <value>" ; // No trailing newline needed here.
/*
/*
- * doPwmMode: doPwmRange:
- * Change the PWM mode and Range values
+ * doPwmMode: doPwmRange: doPwmClock:
+ * Change the PWM mode, range and clock divider values
*********************************************************************************
*/
pwmSetRange (range) ;
}
+static void doPwmClock (int argc, char *argv [])
+{
+ unsigned int clock ;
+
+ if (argc != 3)
+ {
+ fprintf (stderr, "Usage: %s pwmc <clock>\n", argv [0]) ;
+ exit (1) ;
+ }
+
+ clock = (unsigned int)strtoul (argv [2], NULL, 10) ;
+
+ if ((clock < 1) || (clock > 4095))
+ {
+ fprintf (stderr, "%s: clock must be between 0 and 4096\n", argv [0]) ;
+ exit (1) ;
+ }
+
+ pwmSetClock (clock) ;
+}
+
/*
* main:
if (strcasecmp (argv [1], "pwm-bal") == 0) { doPwmMode (PWM_MODE_BAL) ; return 0 ; }
if (strcasecmp (argv [1], "pwm-ms") == 0) { doPwmMode (PWM_MODE_MS) ; return 0 ; }
if (strcasecmp (argv [1], "pwmr") == 0) { doPwmRange (argc, argv) ; return 0 ; }
+ if (strcasecmp (argv [1], "pwmc") == 0) { doPwmClock (argc, argv) ; return 0 ; }
}
// Check for wiring commands
* wiringPi:
* Arduino compatable (ish) Wiring library for the Raspberry Pi
* Copyright (c) 2012 Gordon Henderson
+ * Additional code for pwmSetClock by Chris Hall <chris@kchall.plus.com>
*
* Thanks to code samples from Gert Jan van Loo and the
* BCM2835 ARM Peripherals manual, however it's missing
void (*delayMicroseconds) (unsigned int howLong) ;
void (*pwmSetMode) (int mode) ;
void (*pwmSetRange) (unsigned int range) ;
+void (*pwmSetClock) (int divisor) ;
#ifndef TRUE
void pinModeGpio (int pin, int mode)
{
- static int pwmRunning = FALSE ;
int fSel, shift, alt ;
pin &= 63 ;
*(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) | (alt << shift) ;
-// We didn't initialise the PWM hardware at setup time - because it's possible that
-// something else is using the PWM - e.g. the Audio systems! So if we use PWM
-// here, then we're assuming that nothing else is, otherwise things are going
-// to sound a bit funny...
+// Page 107 of the BCM Peripherals manual talks about the GPIO clocks,
+// but I'm assuming (hoping!) that this applies to other clocks too.
- if (!pwmRunning)
- {
+ *(pwm + PWM_CONTROL) = 0 ; // Stop PWM
+ *(clk + PWMCLK_CNTL) = BCM_PASSWORD | 0x01 ; // Stop PWM Clock
+ delayMicroseconds (110) ; // See comments in pwmSetClockWPi
- *(pwm + PWM_CONTROL) = 0 ; // Stop PWM
- delayMicroseconds (10) ;
-
-// Gert/Doms Values
- *(clk + PWMCLK_DIV) = BCM_PASSWORD | (32<<12) ; // set pwm div to 32 (19.2/32 = 600KHz)
- *(clk + PWMCLK_CNTL) = BCM_PASSWORD | 0x11 ; // Source=osc and enable
+ (void)*(pwm + PWM_CONTROL) ;
+ while ((*(pwm + PWM_CONTROL) & 0x80) != 0) // Wait for clock to be !BUSY
+ delayMicroseconds (1) ;
- delayMicroseconds (10) ;
+ *(clk + PWMCLK_DIV) = BCM_PASSWORD | (32 << 12) ; // set pwm div to 32 (19.2/32 = 600KHz)
+ *(clk + PWMCLK_CNTL) = BCM_PASSWORD | 0x11 ; // enable clk
- *(pwm + PWM0_RANGE) = 0x400 ; delayMicroseconds (10) ;
- *(pwm + PWM1_RANGE) = 0x400 ; delayMicroseconds (10) ;
+// Default range regsiter of 1024
-// Enable PWMs
+ *(pwm + PWM0_DATA) = 0 ; *(pwm + PWM0_RANGE) = 1024 ;
+ *(pwm + PWM1_DATA) = 0 ; *(pwm + PWM1_RANGE) = 1024 ;
- *(pwm + PWM0_DATA) = 512 ;
- *(pwm + PWM1_DATA) = 512 ;
-
-// Balanced mode (default)
-
- *(pwm + PWM_CONTROL) = PWM0_ENABLE | PWM1_ENABLE ;
-
- pwmRunning = TRUE ;
- }
+// Enable PWMs in balanced mode (default)
+ *(pwm + PWM_CONTROL) = PWM0_ENABLE | PWM1_ENABLE ;
}
// When we change mode of any pin, we remove the pull up/downs
return ;
}
+/*
+ * pwmSetClockWPi:
+ * Set/Change the PWM clock. Originally my code, but changed
+ * (for the better!) by Chris Hall, <chris@kchall.plus.com>
+ * after further study of the manual and testing with a 'scope
+ *********************************************************************************
+ */
+
+void pwmSetClockWPi (int divisor)
+{
+ unsigned int pwm_control ;
+ divisor &= 4095 ;
+
+ if (wiringPiDebug)
+ printf ("Setting to: %d. Current: 0x%08X\n", divisor, *(clk + PWMCLK_DIV)) ;
+
+ pwm_control = *(pwm + PWM_CONTROL) ; // preserve PWM_CONTROL
+
+// We need to stop PWM prior to stopping PWM clock in MS mode otherwise BUSY
+// stays high.
+
+ *(pwm + PWM_CONTROL) = 0 ; // Stop PWM
+
+// Stop PWM clock before changing divisor. The delay after this does need to
+// this big (95uS occasionally fails, 100uS OK), it's almost as though the BUSY
+// flag is not working properly in balanced mode. Without the delay when DIV is
+// adjusted the clock sometimes switches to very slow, once slow further DIV
+// adjustments do nothing and it's difficult to get out of this mode.
+
+ *(clk + PWMCLK_CNTL) = BCM_PASSWORD | 0x01 ; // Stop PWM Clock
+ delayMicroseconds (110) ; // prevents clock going sloooow
+
+ while ((*(pwm + PWM_CONTROL) & 0x80) != 0) // Wait for clock to be !BUSY
+ delayMicroseconds (1) ;
+
+ *(clk + PWMCLK_DIV) = BCM_PASSWORD | (divisor << 12) ;
+
+ *(clk + PWMCLK_CNTL) = BCM_PASSWORD | 0x11 ; // Start PWM clock
+ *(pwm + PWM_CONTROL) = pwm_control ; // restore PWM_CONTROL
+
+ if (wiringPiDebug)
+ printf ("Set to: %d. Now : 0x%08X\n", divisor, *(clk + PWMCLK_DIV)) ;
+}
+
+void pwmSetClockSys (int divisor)
+{
+ return ;
+}
+
+
+
#ifdef notYetReady
/*
delayMicroseconds = delayMicrosecondsWPi ;
pwmSetMode = pwmSetModeWPi ;
pwmSetRange = pwmSetRangeWPi ;
+ pwmSetClock = pwmSetClockWPi ;
// Find board revision
delayMicroseconds = delayMicrosecondsWPi ; // Same
pwmSetMode = pwmSetModeWPi ;
pwmSetRange = pwmSetRangeWPi ;
+ pwmSetClock = pwmSetClockWPi ;
return 0 ;
}
delayMicroseconds = delayMicrosecondsSys ;
pwmSetMode = pwmSetModeSys ;
pwmSetRange = pwmSetRangeSys ;
+ pwmSetClock = pwmSetClockSys ;
// Open and scan the directory, looking for exported GPIOs, and pre-open