Improved the PWM functions with help from Chris Hall.

pull/6/head
Gordon Henderson 13 years ago
parent 30d79dab76
commit 8cb493937f

@ -52,9 +52,10 @@ char *usage = "Usage: gpio -v\n"
" gpio drive <group> <value>\n" " gpio drive <group> <value>\n"
" gpio pwm-bal/pwm-ms \n" " gpio pwm-bal/pwm-ms \n"
" gpio pwmr <range> \n" " gpio pwmr <range> \n"
" gpio pwmc <divider> \n"
" gpio load spi/i2c\n" " gpio load spi/i2c\n"
" gpio gbr <channel>\n" " gpio gbr <channel>\n"
" gpio gbw <channel> <value>\n" ; " gpio gbw <channel> <value>" ; // No trailing newline needed here.
/* /*
@ -687,8 +688,8 @@ void doPwm (int argc, char *argv [])
/* /*
* doPwmMode: doPwmRange: * doPwmMode: doPwmRange: doPwmClock:
* Change the PWM mode and Range values * Change the PWM mode, range and clock divider values
********************************************************************************* *********************************************************************************
*/ */
@ -718,6 +719,27 @@ static void doPwmRange (int argc, char *argv [])
pwmSetRange (range) ; 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: * main:
@ -846,6 +868,7 @@ int main (int argc, char *argv [])
if (strcasecmp (argv [1], "pwm-bal") == 0) { doPwmMode (PWM_MODE_BAL) ; return 0 ; } 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], "pwm-ms") == 0) { doPwmMode (PWM_MODE_MS) ; return 0 ; }
if (strcasecmp (argv [1], "pwmr") == 0) { doPwmRange (argc, argv) ; 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 // Check for wiring commands

@ -2,6 +2,7 @@
* wiringPi: * wiringPi:
* Arduino compatable (ish) Wiring library for the Raspberry Pi * Arduino compatable (ish) Wiring library for the Raspberry Pi
* Copyright (c) 2012 Gordon Henderson * 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 * Thanks to code samples from Gert Jan van Loo and the
* BCM2835 ARM Peripherals manual, however it's missing * BCM2835 ARM Peripherals manual, however it's missing
@ -83,6 +84,7 @@ int (*waitForInterrupt) (int pin, int mS) ;
void (*delayMicroseconds) (unsigned int howLong) ; void (*delayMicroseconds) (unsigned int howLong) ;
void (*pwmSetMode) (int mode) ; void (*pwmSetMode) (int mode) ;
void (*pwmSetRange) (unsigned int range) ; void (*pwmSetRange) (unsigned int range) ;
void (*pwmSetClock) (int divisor) ;
#ifndef TRUE #ifndef TRUE
@ -379,7 +381,6 @@ static unsigned long long epoch ;
void pinModeGpio (int pin, int mode) void pinModeGpio (int pin, int mode)
{ {
static int pwmRunning = FALSE ;
int fSel, shift, alt ; int fSel, shift, alt ;
pin &= 63 ; pin &= 63 ;
@ -400,38 +401,28 @@ void pinModeGpio (int pin, int mode)
*(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) | (alt << shift) ; *(gpio + fSel) = (*(gpio + fSel) & ~(7 << shift)) | (alt << shift) ;
// We didn't initialise the PWM hardware at setup time - because it's possible that // Page 107 of the BCM Peripherals manual talks about the GPIO clocks,
// something else is using the PWM - e.g. the Audio systems! So if we use PWM // but I'm assuming (hoping!) that this applies to other clocks too.
// here, then we're assuming that nothing else is, otherwise things are going
// to sound a bit funny...
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 (void)*(pwm + PWM_CONTROL) ;
delayMicroseconds (10) ; while ((*(pwm + PWM_CONTROL) & 0x80) != 0) // Wait for clock to be !BUSY
delayMicroseconds (1) ;
// 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
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) ; // Default range regsiter of 1024
*(pwm + PWM1_RANGE) = 0x400 ; delayMicroseconds (10) ;
// Enable PWMs *(pwm + PWM0_DATA) = 0 ; *(pwm + PWM0_RANGE) = 1024 ;
*(pwm + PWM1_DATA) = 0 ; *(pwm + PWM1_RANGE) = 1024 ;
*(pwm + PWM0_DATA) = 512 ; // Enable PWMs in balanced mode (default)
*(pwm + PWM1_DATA) = 512 ;
// Balanced mode (default)
*(pwm + PWM_CONTROL) = PWM0_ENABLE | PWM1_ENABLE ;
pwmRunning = TRUE ;
}
*(pwm + PWM_CONTROL) = PWM0_ENABLE | PWM1_ENABLE ;
} }
// When we change mode of any pin, we remove the pull up/downs // When we change mode of any pin, we remove the pull up/downs
@ -488,6 +479,57 @@ void pwmSetRangeSys (unsigned int range)
return ; 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 #ifdef notYetReady
/* /*
@ -852,6 +894,7 @@ int wiringPiSetup (void)
delayMicroseconds = delayMicrosecondsWPi ; delayMicroseconds = delayMicrosecondsWPi ;
pwmSetMode = pwmSetModeWPi ; pwmSetMode = pwmSetModeWPi ;
pwmSetRange = pwmSetRangeWPi ; pwmSetRange = pwmSetRangeWPi ;
pwmSetClock = pwmSetClockWPi ;
// Find board revision // Find board revision
@ -1066,6 +1109,7 @@ int wiringPiSetupGpio (void)
delayMicroseconds = delayMicrosecondsWPi ; // Same delayMicroseconds = delayMicrosecondsWPi ; // Same
pwmSetMode = pwmSetModeWPi ; pwmSetMode = pwmSetModeWPi ;
pwmSetRange = pwmSetRangeWPi ; pwmSetRange = pwmSetRangeWPi ;
pwmSetClock = pwmSetClockWPi ;
return 0 ; return 0 ;
} }
@ -1099,6 +1143,7 @@ int wiringPiSetupSys (void)
delayMicroseconds = delayMicrosecondsSys ; delayMicroseconds = delayMicrosecondsSys ;
pwmSetMode = pwmSetModeSys ; pwmSetMode = pwmSetModeSys ;
pwmSetRange = pwmSetRangeSys ; pwmSetRange = pwmSetRangeSys ;
pwmSetClock = pwmSetClockSys ;
// Open and scan the directory, looking for exported GPIOs, and pre-open // Open and scan the directory, looking for exported GPIOs, and pre-open

@ -73,6 +73,7 @@ extern int (*digitalRead) (int pin) ;
extern void (*delayMicroseconds) (unsigned int howLong) ; extern void (*delayMicroseconds) (unsigned int howLong) ;
extern void (*pwmSetMode) (int mode) ; extern void (*pwmSetMode) (int mode) ;
extern void (*pwmSetRange) (unsigned int range) ; extern void (*pwmSetRange) (unsigned int range) ;
extern void (*pwmSetClock) (int divisor) ;
// Interrupts // Interrupts

Loading…
Cancel
Save