* 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>
int (*digitalRead) (int pin) ;
int (*waitForInterrupt) (int pin, int mS) ;
void (*delayMicroseconds) (unsigned int howLong) ;
+void (*pwmSetMode) (int mode) ;
+void (*pwmSetRange) (unsigned int range) ;
+void (*pwmSetClock) (int divisor) ;
#ifndef TRUE
static volatile uint32_t *clk ;
static volatile uint32_t *pads ;
static volatile uint32_t *timer ;
-
static volatile uint32_t *timerIrqRaw ;
+// 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: wpi 0 - 7
+ 2, 3, // I2C - SDA0, SCL0 wpi 8 - 9
+ 8, 7, // SPI - CE1, CE0 wpi 10 - 11
+ 10, 9, 11, // SPI - MOSI, MISO, SCLK wpi 12 - 14
+ 14, 15, // UART - Tx, Rx wpi 15 - 16
+ 28, 29, 30, 31, // New GPIOs 8 though 11 wpi 17 - 20
+
+// 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
+} ;
+
+
// gpioToGPFSEL:
// Map a BCM_GPIO pin to it's control port. (GPFSEL 0-5)
5,5,5,5,5,5,5,5,5,5,
} ;
+
// gpioToShift
// Define the shift up for the 3 bits per pin in each GPFSEL port
0,3,6,9,12,15,18,21,24,27,
} ;
+
// gpioToGPSET:
// (Word) offset to the GPIO Set registers for each GPIO pin
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
} ;
+
// gpioToGPCLR:
// (Word) offset to the GPIO Clear registers for each GPIO pin
11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
} ;
+
// gpioToGPLEV:
// (Word) offset to the GPIO Input level registers for each GPIO pin
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
} ;
+
#ifdef notYetReady
// gpioToEDS
// (Word) offset to the Event Detect Status
} ;
#endif
+
// gpioToPUDCLK
// (Word) offset to the Pull Up Down Clock regsiter
39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,
} ;
+
// gpioToPwmALT
// the ALT value to put a GPIO pin into PWM mode
static unsigned long long epoch ;
-//////////////////////////////////////////////////////////////////////////////////
+/*
+ * Functions
+ *********************************************************************************
+ */
+
+
+/*
+ * wpiPinToGpio:
+ * Translate a wiringPi Pin number to native GPIO pin number.
+ * (We don't use this here, prefering to just do the lookup directly,
+ * but it's been requested!)
+ *********************************************************************************
+ */
+
+int wpiPinToGpio (int wpiPin)
+{
+ return pinToGpio [wpiPin & 63] ;
+}
+
+
+/*
+ * piBoardRev:
+ * Return a number representing the hardware revision of the board.
+ * Revision is currently 1 or 2. -1 is returned on error.
+ *********************************************************************************
+ */
+
+int piBoardRev (void)
+{
+ FILE *cpuFd ;
+ char line [80] ;
+ char *c ;
+ int r = -1 ;
+ static int boardRev = -1 ;
+
+// No point checking twice...
+
+ if (boardRev != -1)
+ return boardRev ;
+
+ if ((cpuFd = fopen ("/proc/cpuinfo", "r")) == NULL)
+ return -1 ;
+
+ while (fgets (line, 80, cpuFd) != NULL)
+ if (strncmp (line, "Revision", 8) == 0)
+ for (c = line ; *c ; ++c)
+ {
+ if (!isdigit (*c))
+ continue ;
+ r = atoi (c) ;
+ break ;
+ }
+
+ fclose (cpuFd) ;
+ if (r == -1)
+ {
+ fprintf (stderr, "piBoardRev: Unable to determine board revision from /proc/cpuinfo\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 (r > 1000)
+ printf ("piboardRev: This Pi has/is overvolted!\n") ;
+
+ r %= 100 ;
+
+ /**/ if ((r == 2) || (r == 3))
+ boardRev = 1 ;
+ else if ((r == 4) || (r == 5) || (r == 6))
+ boardRev = 2 ;
+ else
+ {
+ fprintf (stderr, "piBoardRev: Unable to determine board revision from %d\n", r) ;
+ errno = 0 ;
+ return -1 ;
+ }
+
+ if (wiringPiDebug)
+ printf ("piboardRev: Revision: %d, board revision: %d\n", r, boardRev) ;
+
+ return boardRev ;
+}
+
/*
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
-// Gert/Doms Values
- *(clk + PWMCLK_DIV) = BCM_PASSWORD | (32<<12) ; // set pwm div to 32 (19.2/3 = 600KHz)
- *(clk + PWMCLK_CNTL) = BCM_PASSWORD | 0x11 ; // Source=osc and enable
- digitalWrite (pin, LOW) ;
- *(pwm + PWM_CONTROL) = 0 ; // Disable PWM
- delayMicroseconds (10) ;
- *(pwm + PWM0_RANGE) = 0x400 ;
- delayMicroseconds (10) ;
- *(pwm + PWM1_RANGE) = 0x400 ;
- delayMicroseconds (10) ;
+ (void)*(pwm + PWM_CONTROL) ;
+ while ((*(pwm + PWM_CONTROL) & 0x80) != 0) // Wait for clock to be !BUSY
+ delayMicroseconds (1) ;
-// Enable PWMs
+ *(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_DATA) = 512 ;
- *(pwm + PWM1_DATA) = 512 ;
+// Default range regsiter of 1024
- *(pwm + PWM_CONTROL) = PWM0_ENABLE | PWM1_ENABLE ;
+ *(pwm + PWM0_DATA) = 0 ; *(pwm + PWM0_RANGE) = 1024 ;
+ *(pwm + PWM1_DATA) = 0 ; *(pwm + PWM1_RANGE) = 1024 ;
- 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
-
-// delayMicroseconds (300) ;
+// Or we used to... Hm. Commented out now because for some wieird reason,
+// it seems to block subsequent attempts to set the pull up/downs and I've
+// not quite gotten to the bottom of why this happens
+// The down-side is that the pull up/downs are rememberd in the SoC between
+// power cycles, so it's going to be a good idea to explicitly set them in
+// any new code.
+//
// pullUpDnControl (pin, PUD_OFF) ;
+
}
void pinModeWPi (int pin, int mode)
}
+/*
+ * pwmControl:
+ * Allow the user to control some of the PWM functions
+ *********************************************************************************
+ */
+
+void pwmSetModeWPi (int mode)
+{
+ if (mode == PWM_MODE_MS)
+ *(pwm + PWM_CONTROL) = PWM0_ENABLE | PWM1_ENABLE | PWM0_MS_MODE | PWM1_MS_MODE ;
+ else
+ *(pwm + PWM_CONTROL) = PWM0_ENABLE | PWM1_ENABLE ;
+}
+
+void pwmSetModeSys (int mode)
+{
+ return ;
+}
+
+
+void pwmSetRangeWPi (unsigned int range)
+{
+ *(pwm + PWM0_RANGE) = range ; delayMicroseconds (10) ;
+ *(pwm + PWM1_RANGE) = range ; delayMicroseconds (10) ;
+}
+
+void pwmSetRangeSys (unsigned int range)
+{
+ 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
/*
* pinED01:
/*
- * pwnWrite:
+ * pwmWrite:
* Set an output PWM value
*********************************************************************************
*/
pin = pin & 63 ;
port = gpioToPwmPort [pin] ;
- *(pwm + port) = value & 0x3FF ;
+ *(pwm + port) = value ;
}
void pwmWriteWPi (int pin, int value)
}
-
-
/*
* delay:
* Wait for some number of milli seconds
int wiringPiSetup (void)
{
int fd ;
+ int boardRev ;
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 ;
digitalRead = digitalReadWPi ;
waitForInterrupt = waitForInterruptWPi ;
delayMicroseconds = delayMicrosecondsWPi ;
+ pwmSetMode = pwmSetModeWPi ;
+ pwmSetRange = pwmSetRangeWPi ;
+ pwmSetClock = pwmSetClockWPi ;
+ if ((boardRev = piBoardRev ()) < 0)
+ return -1 ;
+
+ if (boardRev == 1)
+ pinToGpio = pinToGpioR1 ;
+ else
+ pinToGpio = pinToGpioR2 ;
+
// 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 ;
digitalRead = digitalReadGpio ;
waitForInterrupt = waitForInterruptGpio ;
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 ;
digitalRead = digitalReadSys ;
waitForInterrupt = waitForInterruptSys ;
delayMicroseconds = delayMicrosecondsSys ;
+ pwmSetMode = pwmSetModeSys ;
+ pwmSetRange = pwmSetRangeSys ;
+ pwmSetClock = pwmSetClockSys ;
+
// Open and scan the directory, looking for exported GPIOs, and pre-open
// the 'value' interface to speed things up for later