* 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
#include <stdio.h>
#include <stdint.h>
+#include <stdlib.h>
+#include <ctype.h>
#include <poll.h>
#include <unistd.h>
#include <errno.h>
void (*delayMicroseconds) (unsigned int howLong) ;
void (*pwmSetMode) (int mode) ;
void (*pwmSetRange) (unsigned int range) ;
+void (*pwmSetClock) (int divisor) ;
#ifndef TRUE
static volatile uint32_t *timerIrqRaw ;
+// Raspberry Pi board revision
+
+static int boardRevision = -1 ;
+
+// Debugging
+
+static int wiringPiDebug = FALSE ;
+
// The BCM2835 has 54 GPIO pins.
// BCM2835 data sheet, Page 90 onwards.
// There are 6 control registers, each control the functions of a block
// pinToGpio:
// Take a Wiring pin (0 through X) and re-map it to the BCM_GPIO pin
+// Cope for 2 different board revieions here
+
+static int *pinToGpio ;
-static int pinToGpio [64] =
+static int pinToGpioR1 [64] =
{
17, 18, 21, 22, 23, 24, 25, 4, // From the Original Wiki - GPIO 0 through 7
0, 1, // I2C - SDA0, SCL0
// Padding:
- -1, -1, -1,-1,-1,-1,-1, -1, -1, -1, -1, -1, -1, -1, -1, // ... 31
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ... 31
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ... 47
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ... 63
+} ;
+
+static int pinToGpioR2 [64] =
+{
+ 17, 18, 27, 22, 23, 24, 25, 4, // From the Original Wiki - GPIO 0 through 7
+ 2, 3, // I2C - SDA0, SCL0
+ 8, 7, // SPI - CE1, CE0
+ 10, 9, 11, // SPI - MOSI, MISO, SCLK
+ 14, 15, // UART - Tx, Rx
+ 28, 29, 30, 31, // New GPIOs 8 though 11
+
+// Padding:
+
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ... 31
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ... 47
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ... 63
} ;
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...
-
- if (!pwmRunning)
- {
+// Page 107 of the BCM Peripherals manual talks about the GPIO clocks,
+// but I'm assuming (hoping!) that this applies to other clocks too.
- *(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
+ *(pwm + PWM_CONTROL) = 0 ; // Stop PWM
+ *(clk + PWMCLK_CNTL) = BCM_PASSWORD | 0x01 ; // Stop PWM Clock
+ delayMicroseconds (110) ; // See comments in pwmSetClockWPi
- delayMicroseconds (10) ;
+ (void)*(pwm + PWM_CONTROL) ;
+ while ((*(pwm + PWM_CONTROL) & 0x80) != 0) // Wait for clock to be !BUSY
+ delayMicroseconds (1) ;
- *(pwm + PWM0_RANGE) = 0x400 ; delayMicroseconds (10) ;
- *(pwm + PWM1_RANGE) = 0x400 ; 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
-// Enable PWMs
+// Default range regsiter of 1024
- *(pwm + PWM0_DATA) = 512 ;
- *(pwm + PWM1_DATA) = 512 ;
+ *(pwm + PWM0_DATA) = 0 ; *(pwm + PWM0_RANGE) = 1024 ;
+ *(pwm + PWM1_DATA) = 0 ; *(pwm + PWM1_RANGE) = 1024 ;
-// 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
/*
int wiringPiSetup (void)
{
int fd ;
+ FILE *cpuFd ;
+ char line [80] ;
+ char *c ;
+ int revision = -1 ;
uint8_t *gpioMem, *pwmMem, *clkMem, *padsMem, *timerMem ;
struct timeval tv ;
+ if (getenv ("WIRINGPI_DEBUG") != NULL)
+ wiringPiDebug = TRUE ;
+
+ if (wiringPiDebug)
+ printf ("wiringPi: wiringPiSetup called\n") ;
+
pinMode = pinModeWPi ;
pullUpDnControl = pullUpDnControlWPi ;
digitalWrite = digitalWriteWPi ;
delayMicroseconds = delayMicrosecondsWPi ;
pwmSetMode = pwmSetModeWPi ;
pwmSetRange = pwmSetRangeWPi ;
+ pwmSetClock = pwmSetClockWPi ;
+// Find board revision
+
+ if ((cpuFd = fopen ("/proc/cpuinfo", "r")) == NULL)
+ {
+ fprintf (stderr, "wiringPiSetup: Unable to open /proc/cpuinfo: %s\n", strerror (errno)) ;
+ return -1 ;
+ }
+
+ while (fgets (line, 80, cpuFd) != NULL)
+ if (strncmp (line, "Revision", 8) == 0)
+ for (c = line ; *c ; ++c)
+ {
+ if (!isdigit (*c))
+ continue ;
+ revision = atoi (c) ;
+ break ;
+ }
+
+ fclose (cpuFd) ;
+ if (revision == -1)
+ {
+ fprintf (stderr, "wiringPiSetup: Unable to determine board revision\n") ;
+ errno = 0 ;
+ return -1 ;
+ }
+
+// If you have overvolted the Pi, then it appears that the revision
+// has 100000 added to it!
+
+ if (wiringPiDebug)
+ if (revision > 1000)
+ printf ("wiringPi: This Pi has/is overvolted!\n") ;
+
+ revision %= 100 ;
+
+ /**/ if ((revision == 2) || (revision == 3))
+ boardRevision = 1 ;
+ else if ((revision == 4) || (revision == 5) || (revision == 6))
+ boardRevision = 2 ;
+ else
+ {
+ fprintf (stderr, "wiringPiSetup: Unable to determine board revision: %d\n", revision) ;
+ errno = 0 ;
+ return -1 ;
+ }
+
+
+ if (boardRevision == 1)
+ pinToGpio = pinToGpioR1 ;
+ else
+ pinToGpio = pinToGpioR2 ;
+
+ if (wiringPiDebug)
+ printf ("wiringPi: Revision: %d, board revision: %d\n", revision, boardRevision) ;
+
// Open the master /dev/memory device
if ((fd = open ("/dev/mem", O_RDWR | O_SYNC) ) < 0)
int wiringPiSetupGpio (void)
{
- int x = wiringPiSetup () ;
+ int x ;
- if (x != 0)
+ if (wiringPiDebug)
+ printf ("wiringPi: wiringPiSetupGpio called\n") ;
+
+ if ((x = wiringPiSetup ()) < 0)
return x ;
pinMode = pinModeGpio ;
delayMicroseconds = delayMicrosecondsWPi ; // Same
pwmSetMode = pwmSetModeWPi ;
pwmSetRange = pwmSetRangeWPi ;
+ pwmSetClock = pwmSetClockWPi ;
return 0 ;
}
struct timeval tv ;
char fName [128] ;
+ if (wiringPiDebug)
+ printf ("wiringPi: wiringPiSetupSys called\n") ;
+
pinMode = pinModeSys ;
pullUpDnControl = pullUpDnControlSys ;
digitalWrite = digitalWriteSys ;
delayMicroseconds = delayMicrosecondsSys ;
pwmSetMode = pwmSetModeSys ;
pwmSetRange = pwmSetRangeSys ;
+ pwmSetClock = pwmSetClockSys ;
// Open and scan the directory, looking for exported GPIOs, and pre-open