Index: pkg/raspberry_pi/lib/raspberry_pi.dart |
diff --git a/pkg/raspberry_pi/lib/raspberry_pi.dart b/pkg/raspberry_pi/lib/raspberry_pi.dart |
index d0a8df0fdfe1734c3b51483b878361a8b2771fdc..9734327377fe91aaa21173298b66b207abe22677 100644 |
--- a/pkg/raspberry_pi/lib/raspberry_pi.dart |
+++ b/pkg/raspberry_pi/lib/raspberry_pi.dart |
@@ -5,9 +5,15 @@ |
/// Library for the basic Raspberry Pi features. |
library raspberry_pi; |
+import 'dart:fletch.ffi'; |
import 'dart:typed_data'; |
import 'package:file/file.dart'; |
+import 'package:gpio/gpio.dart' as gpio; |
+ |
+// Foreign functions used. |
+final ForeignFunction _open = ForeignLibrary.main.lookup('open'); |
+final ForeignFunction _mmap = ForeignLibrary.main.lookup('mmap'); |
/// Possible modes of the on-board LEDs |
enum OnboardLEDMode { |
@@ -23,6 +29,34 @@ enum OnboardLEDMode { |
/// Class for accessing Raspberry Pi features. |
/// |
+/// Use GPIO through the memory mapped interface: |
+/// |
+/// ``` |
+/// import 'package:raspberry_pi/raspberry_pi.dart'; |
+/// import 'package:gpio/gpio.dart'; |
+/// |
+/// main() { |
+/// RaspberryPi pi = new RaspberryPi(); |
+/// GPIO gpio = pi.memoryMappedGPIO; // Selecting memory mapped GPIO. |
+/// gpio.setMode(4, Mode.output); |
+/// gpio.setPin(4, true); |
+/// } |
+/// ``` |
+/// |
+/// Use GPIO through the sysfs interface: |
+/// |
+/// ``` |
+/// import 'package:raspberry_pi/raspberry_pi.dart'; |
+/// import 'package:gpio/gpio.dart'; |
+/// |
+/// main() { |
+/// RaspberryPi pi = new RaspberryPi(); |
+/// GPIO gpio = pi.sysfsGPIO; // Selecting sysfs GPIO. |
+/// gpio.setMode(4, Mode.output); |
+/// gpio.setPin(4, true); |
+/// } |
+/// ``` |
+/// |
/// Change the activity LED to heartbeat mode: |
/// |
/// ``` |
@@ -49,10 +83,176 @@ enum OnboardLEDMode { |
/// } |
/// ``` |
class RaspberryPi { |
+ /// The number of GPIO pins on the Raspberry Pi 2. |
+ static const int gpioPins = 54; |
+ var _memoryMappedGPIO; |
+ var _sysfsGPIO; |
+ |
/// Provide access to the on-board LEDs. |
final OnBoardLEDs leds = new OnBoardLEDs._(); |
RaspberryPi(); |
+ |
+ get memoryMappedGPIO { |
+ if (_memoryMappedGPIO == null) { |
+ _memoryMappedGPIO = new PiMemoryMappedGPIO(); |
+ } |
+ return _memoryMappedGPIO; |
+ } |
+ |
+ get sysfsGPIO { |
+ if (_sysfsGPIO == null) { |
+ _sysfsGPIO = new gpio.SysfsGPIO(gpioPins); |
+ } |
+ return _sysfsGPIO; |
+ |
+ } |
+} |
+ |
+/// Pull-up/down resistor state. |
+enum PullUpDown { |
+ floating, |
+ pullDown, |
+ pullUp, |
+} |
+ |
+/// Provide GPIO access on Raspberry Pi using direct memory access. |
+/// |
+/// The following code shows how to turn on GPIO pin 4: |
+/// |
+/// ``` |
+/// PiMemoryMappedGPIO gpio = new PiMemoryMappedGPIO(); |
+/// gpio.setMode(4, Mode.output); |
+/// gpio.setPin(4, true)); |
+/// ``` |
+/// |
+/// The following code shows how to read GPIO pin 17: |
+/// |
+/// ``` |
+/// PiMemoryMappedGPIO gpio = new PiMemoryMappedGPIO(); |
+/// gpio.setMode(17, Mode.input); |
+/// print(gpio.getPin(17)); |
+/// ``` |
+class PiMemoryMappedGPIO extends gpio.GPIOBase { |
+ // See datasheet: |
+ // https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf |
+ |
+ // Raspberry Pi model 1 (A/A+/B) |
+ // BCM2708 / BCM 2835 |
+ |
+ // Raspberry Pi model 2 |
+ // BCM2709 / BCM 2836 |
+ |
+ // Peripherals base address. |
+ static const int _baseAddressModel1 = 0x20000000; |
+ static const int _baseAddressModel2 = 0x3F000000; |
+ static const int _baseAddressGPIOOffset = 0x00200000; |
+ |
+ // Size of the peripherals area. |
+ static const int _blockSize = 4096; |
+ |
+ // Offsets (in bytes) to various areas. |
+ static const int _gpioFunctionSelectBase = 0 << 2; |
+ static const int _gpioOutputSetBase = 7 << 2; |
+ static const int _gpioOutputClearBase = 10 << 2; |
+ static const int _gpioPinLevelBase = 13 << 2; |
+ static const int _gpioPullUpPullDown = 37 << 2; |
+ static const int _gpioPullUpPullDownClockBase = 38 << 2; |
+ |
+ // All alternative functions are mapped to `Mode.other` for now. |
+ static const _functionToMode = |
+ [gpio.Mode.input, gpio.Mode.output, |
+ gpio.Mode.other, gpio.Mode.other, |
+ gpio.Mode.other, gpio.Mode.other, gpio.Mode.other]; |
+ |
+ int _fd; // File descriptor for /dev/mem. |
+ ForeignPointer _addr; |
+ ForeignMemory _mem; |
+ |
+ PiMemoryMappedGPIO(): super(RaspberryPi.gpioPins) { |
+ // From /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h. |
+ const int oRDWR = 02; // O_RDWR |
wibling
2015/10/05 07:43:37
NIT: Shouldn't we keep these as O_RDWR to make it
|
+ // Found from C code 'printf("%x\n", O_SYNC);'. |
+ const int oSync = 0x101000; // O_SYNC |
+ |
+ // Open /dev/mem to get to the physical memory. |
+ var devMem = new ForeignMemory.fromStringAsUTF8('/dev/mem'); |
+ _fd = _open.icall$2Retry(devMem, oRDWR | oSync); |
+ if (_fd < 0) { |
+ throw new gpio.GPIOException("Failed to open '/dev/mem'", Foreign.errno); |
+ } |
+ devMem.free(); |
+ |
+ // From /usr/include/x86_64-linux-gnu/bits/mman-linux.h. |
+ const int protRead = 0x1; // PROT_READ. |
wibling
2015/10/05 07:43:37
Ditto.
|
+ const int protWrite = 0x2; // PROT_WRITE. |
+ const int mapShared = 0x01; // MAP_SHARED. |
+ |
+ _addr = _mmap.pcall$6(0, _blockSize, protRead | protWrite, mapShared, |
+ _fd, _baseAddressModel2 + _baseAddressGPIOOffset); |
+ _mem = new ForeignMemory.fromAddress(_addr.address, _blockSize); |
+ } |
+ |
+ void setMode(int pin, gpio.Mode mode) { |
+ checkPinRange(pin); |
+ // GPIO function select registers each have 3 bits for 10 pins. |
+ var fsel = (pin ~/ 10); |
+ var shift = (pin % 10) * 3; |
+ var function = mode == gpio.Mode.input ? 0 : 1; |
+ var offset = _gpioFunctionSelectBase + (fsel << 2); |
+ var value = _mem.getUint32(offset); |
+ value = (value & ~(0x07 << shift)) | function << shift; |
+ _mem.setUint32(offset, value); |
+ } |
+ |
+ gpio.Mode getMode(int pin) { |
+ checkPinRange(pin); |
+ // GPIO function select registers each have 3 bits for 10 pins. |
+ var fsel = (pin ~/ 10); |
+ var shift = (pin % 10) * 3; |
+ var offset = _gpioFunctionSelectBase + (fsel << 2); |
+ var function = (_mem.getUint32(offset) >> shift) & 0x07; |
+ return _functionToMode[function]; |
+ } |
+ |
+ void setPin(int pin, bool value) { |
+ checkPinRange(pin); |
wibling
2015/10/05 07:43:37
NIT: Could enforce mode and fail on mismatch.
|
+ // GPIO output set and output clear registers each have 1 bits for 32 pins. |
wibling
2015/10/05 07:43:37
NIT: bits -> bit
|
+ int register = pin ~/ 32; |
+ int shift = pin % 32; |
+ if (value) { |
+ _mem.setUint32(_gpioOutputSetBase + (register << 2), 1 << shift); |
+ } else { |
+ _mem.setUint32(_gpioOutputClearBase + (register << 2), 1 << shift); |
+ } |
+ } |
+ |
+ bool getPin(int pin) { |
+ checkPinRange(pin); |
+ // GPIO pin level registers each have 1 bits for 32 pins. |
wibling
2015/10/05 07:43:37
NIT: bits -> bit
|
+ int register = pin ~/ 32; |
+ int shift = pin % 32; |
+ return |
+ (_mem.getUint32(_gpioPinLevelBase + (register << 2)) & 1 << shift) != 0; |
+ } |
+ |
+ /// Set the floating/pull-up/pull-down state of [pin]. |
+ /// |
+ /// Use `0` for floating, `1` for pull down and `2` for pull-up. |
+ void setPullUpDown(int pin, PullUpDown pullUpDown) { |
+ checkPinRange(pin); |
+ int register = pin ~/ 32; |
+ int shift = pin % 32; |
+ // First set the value in the update register. |
+ _mem.setUint32(_gpioPullUpPullDown, pullUpDown.index); |
+ sleep(1); // Datasheet says: "Wait for 150 cycles". |
+ // Then set the clock bit. |
+ _mem.setUint32(_gpioPullUpPullDownClockBase + (register << 2), 1 << shift); |
+ sleep(1); // Datasheet says: "Wait for 150 cycles". |
+ // Clear value and clock bit. |
+ _mem.setUint32(_gpioPullUpPullDown, 0); |
+ _mem.setUint32(_gpioPullUpPullDownClockBase + (register << 2), 0); |
+ } |
} |
// The on-board LEDs. |