Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(590)

Side by Side Diff: pkg/gpio/lib/gpio.dart

Issue 1389573002: Refactor Raspberry Pi GPIO and add GPIO mock (Closed) Base URL: git@github.com:dart-lang/fletch.git@master
Patch Set: Removed unused code Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | pkg/gpio/lib/gpio_mock.dart » ('j') | pkg/gpio/lib/gpio_mock.dart » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2015, the Fletch project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Fletch project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE.md file. 3 // BSD-style license that can be found in the LICENSE.md file.
4 4
5 /// GPIO support. 5 /// GPIO support.
6 /// 6 ///
7 /// Provides access to controlling GPIO pins. 7 /// Provides access to controlling GPIO pins.
8 /// 8 ///
9 /// The library provide two ways of accessing the GPIO pins: direct access or 9 /// The library provide two ways of accessing the GPIO pins: direct access or
10 /// access through a Sysfs driver. 10 /// access through a Sysfs driver.
11 /// 11 ///
12 /// When direct access is used, the physical memory addresses, where the 12 /// When direct access is used, the physical memory addresses, where the
13 /// SoC registers for the GPIO pins are mapped, are accessed directly. This 13 /// SoC registers for the GPIO pins are mapped, are accessed directly. This
14 /// always require root access. 14 /// always require root access.
15 /// 15 ///
16 /// When the Sysfs driver is used the filesystem under `/sys/class/gpio` is 16 /// When the Sysfs driver is used the filesystem under `/sys/class/gpio` is
17 /// used; root access is also required by default. However this can be changed 17 /// used; root access is also required by default. However this can be changed
18 /// through udev rules, e.g. by adding a file to ` /etc/udev/rules.d`. 18 /// through udev rules, e.g. by adding a file to ` /etc/udev/rules.d`.
19 /// In addition the Sysfs driver supports state change notifications. 19 /// In addition the Sysfs driver supports state change notifications.
20 /// 20 ///
21 /// Currently this has only been tested with a Raspberry Pi 2. 21 /// Currently this has only been tested with a Raspberry Pi 2.
22 library gpio; 22 library gpio;
23 23
24 import 'dart:fletch.ffi'; 24 import 'dart:fletch.ffi';
25 import 'dart:typed_data'; 25 import 'dart:typed_data';
26 26
27 import 'package:file/file.dart'; 27 import 'package:file/file.dart';
28 28
29 // Foreign functions used. 29 // Foreign functions used.
30 final ForeignFunction _open = ForeignLibrary.main.lookup('open');
31 final ForeignFunction _lseek = ForeignLibrary.main.lookup('lseek'); 30 final ForeignFunction _lseek = ForeignLibrary.main.lookup('lseek');
32 final ForeignFunction _mmap = ForeignLibrary.main.lookup('mmap');
33 final ForeignFunction _poll = ForeignLibrary.main.lookup('poll'); 31 final ForeignFunction _poll = ForeignLibrary.main.lookup('poll');
34 32
35 /// GPIO modes. 33 /// GPIO modes.
36 enum Mode { 34 enum Mode {
37 /// GPIO pin input mode. 35 /// GPIO pin input mode.
38 input, 36 input,
39 /// GPIO pin output mode. 37 /// GPIO pin output mode.
40 output, 38 output,
41 /// GPIO has other functions than `input` and `output`. Most GPIO pins have 39 /// GPIO has other functions than `input` and `output`. Most GPIO pins have
42 /// several special functions, so when receiving the mode this value can be 40 /// several special functions, so when receiving the mode this value can be
43 /// returned. This value cannot be used when setting the mode. 41 /// returned. This value cannot be used when setting the mode.
44 other 42 other
45 } 43 }
46 44
47 /// Base GPIO interface supported by all GPIO implementations. 45 /// Base GPIO interface supported by all GPIO implementations.
48 abstract class GPIO { 46 abstract class GPIO {
47 /// The default number of pins for GPIO is 50.
48 static const int defaultPins = 50;
49
50 /// Number of pins exposed by this GPIO.
51 int get pins;
52
49 /// Set the mode of [pin] to [mode]. 53 /// Set the mode of [pin] to [mode].
50 void setMode(int pin, Mode mode); 54 void setMode(int pin, Mode mode);
51 55
52 /// Get the current mode of [pin]. 56 /// Get the current mode of [pin].
53 Mode getMode(int pin); 57 Mode getMode(int pin);
54 58
55 /// Set the value of the [pin] to [value]. The boolean value 59 /// Set the value of the [pin] to [value]. The boolean value
56 /// [true] represents high (1) and the boolean value [false] 60 /// [true] represents high (1) and the boolean value [false]
57 /// represents low (0). 61 /// represents low (0).
58 void setPin(int pin, bool value); 62 void setPin(int pin, bool value);
59 63
60 /// Get the value of the [pin]. The boolean value 64 /// Get the value of the [pin]. The boolean value
61 /// [true] represents high (1) and the boolean value [false] 65 /// [true] represents high (1) and the boolean value [false]
62 /// represents low (0). 66 /// represents low (0).
63 bool getPin(int pin); 67 bool getPin(int pin);
64 } 68 }
65 69
66 /// Pull-up/down resistor state. 70 // Internal base class.
67 enum PullUpDown { 71 abstract class GPIOBase implements GPIO {
68 floating, 72 // Number of GPIO pins.
69 pullDown, 73 final int _pins;
70 pullUp,
71 }
72 74
73 // Internal base class. 75 GPIOBase(this._pins);
74 class _GPIOBase {
75 // Number of GPIO pins.
76 final int _maxPins;
77 76
78 _GPIOBase(this._maxPins); 77 get pins => _pins;
79 78
80 void _checkPinRange(int pin) { 79 void checkPinRange(int pin) {
81 if (pin < 0 || _maxPins <= pin) { 80 if (pin < 0 || _pins <= pin) {
82 throw new RangeError.index(pin, this, 'pin', null, _maxPins); 81 throw new RangeError.index(pin, this, 'pin', null, _pins);
83 } 82 }
84 } 83 }
85 } 84 }
86 85
87 /// Provide GPIO access on Raspberry Pi using direct memory access.
88 ///
89 /// The following code shows how to turn on GPIO pin 4:
90 ///
91 /// ```
92 /// PiMemoryMappedGPIO gpio = new PiMemoryMappedGPIO();
93 /// gpio.setMode(4, Mode.output);
94 /// gpio.setPin(4, true));
95 /// ```
96 ///
97 /// The following code shows how to read GPIO pin 17:
98 ///
99 /// ```
100 /// PiMemoryMappedGPIO gpio = new PiMemoryMappedGPIO();
101 /// gpio.setMode(17, Mode.input);
102 /// print(gpio.getPin(17));
103 /// ```
104 class PiMemoryMappedGPIO extends _GPIOBase implements GPIO {
105 // See datasheet:
106 // https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripher als.pdf
107
108 // Raspberry Pi model 1 (A/A+/B)
109 // BCM2708 / BCM 2835
110
111 // Raspberry Pi model 2
112 // BCM2709 / BCM 2836
113
114 // Peripherals base address.
115 static const int _baseAddressModel1 = 0x20000000;
116 static const int _baseAddressModel2 = 0x3F000000;
117 static const int _baseAddressGPIOOffset = 0x00200000;
118
119 // Size of the peripherals area.
120 static const int _blockSize = 4096;
121
122 // Offsets (in bytes) to various areas.
123 static const int _gpioFunctionSelectBase = 0 << 2;
124 static const int _gpioOutputSetBase = 7 << 2;
125 static const int _gpioOutputClearBase = 10 << 2;
126 static const int _gpioPinLevelBase = 13 << 2;
127 static const int _gpioPullUpPullDown = 37 << 2;
128 static const int _gpioPullUpPullDownClockBase = 38 << 2;
129
130 // All alternative functions are mapped to `Mode.other` for now.
131 static const _functionToMode =
132 [Mode.input, Mode.output,
133 Mode.other, Mode.other, Mode.other, Mode.other, Mode.other];
134
135 int _fd; // File descriptor for /dev/mem.
136 ForeignPointer _addr;
137 ForeignMemory _mem;
138
139 PiMemoryMappedGPIO(): super(54) {
140 // From /usr/include/x86_64-linux-gnu/bits/fcntl-linux.h.
141 const int oRDWR = 02; // O_RDWR
142 // Found from C code 'printf("%x\n", O_SYNC);'.
143 const int oSync = 0x101000; // O_SYNC
144
145 // Open /dev/mem to get to the physical memory.
146 var devMem = new ForeignMemory.fromStringAsUTF8('/dev/mem');
147 _fd = _open.icall$2Retry(devMem, oRDWR | oSync);
148 if (_fd < 0) {
149 throw new GPIOException("Failed to open '/dev/mem'", Foreign.errno);
150 }
151 devMem.free();
152
153 // From /usr/include/x86_64-linux-gnu/bits/mman-linux.h.
154 const int protRead = 0x1; // PROT_READ.
155 const int protWrite = 0x2; // PROT_WRITE.
156 const int mapShared = 0x01; // MAP_SHARED.
157
158 _addr = _mmap.pcall$6(0, _blockSize, protRead | protWrite, mapShared,
159 _fd, _baseAddressModel2 + _baseAddressGPIOOffset);
160 _mem = new ForeignMemory.fromAddress(_addr.address, _blockSize);
161 }
162
163 void setMode(int pin, Mode mode) {
164 _checkPinRange(pin);
165 // GPIO function select registers each have 3 bits for 10 pins.
166 var fsel = (pin ~/ 10);
167 var shift = (pin % 10) * 3;
168 var function = mode == Mode.input ? 0 : 1;
169 var offset = _gpioFunctionSelectBase + (fsel << 2);
170 var value = _mem.getUint32(offset);
171 value = (value & ~(0x07 << shift)) | function << shift;
172 _mem.setUint32(offset, value);
173 }
174
175 Mode getMode(int pin) {
176 _checkPinRange(pin);
177 // GPIO function select registers each have 3 bits for 10 pins.
178 var fsel = (pin ~/ 10);
179 var shift = (pin % 10) * 3;
180 var offset = _gpioFunctionSelectBase + (fsel << 2);
181 var function = (_mem.getUint32(offset) >> shift) & 0x07;
182 return _functionToMode[function];
183 }
184
185 void setPin(int pin, bool value) {
186 _checkPinRange(pin);
187 // GPIO output set and output clear registers each have 1 bits for 32 pins.
188 int register = pin ~/ 32;
189 int shift = pin % 32;
190 if (value) {
191 _mem.setUint32(_gpioOutputSetBase + (register << 2), 1 << shift);
192 } else {
193 _mem.setUint32(_gpioOutputClearBase + (register << 2), 1 << shift);
194 }
195 }
196
197 bool getPin(int pin) {
198 _checkPinRange(pin);
199 // GPIO pin level registers each have 1 bits for 32 pins.
200 int register = pin ~/ 32;
201 int shift = pin % 32;
202 return
203 (_mem.getUint32(_gpioPinLevelBase + (register << 2)) & 1 << shift) != 0;
204 }
205
206 /// Set the floating/pull-up/pull-down state of [pin].
207 ///
208 /// Use `0` for floating, `1` for pull down and `2` for pull-up.
209 void setPullUpDown(int pin, PullUpDown pullUpDown) {
210 _checkPinRange(pin);
211 int register = pin ~/ 32;
212 int shift = pin % 32;
213 // First set the value in the update register.
214 _mem.setUint32(_gpioPullUpPullDown, pullUpDown.index);
215 sleep(1); // Datasheet says: "Wait for 150 cycles".
216 // Then set the clock bit.
217 _mem.setUint32(_gpioPullUpPullDownClockBase + (register << 2), 1 << shift);
218 sleep(1); // Datasheet says: "Wait for 150 cycles".
219 // Clear value and clock bit.
220 _mem.setUint32(_gpioPullUpPullDown, 0);
221 _mem.setUint32(_gpioPullUpPullDownClockBase + (register << 2), 0);
222 }
223 }
224
225 /// Interrupt triggers. 86 /// Interrupt triggers.
226 enum Trigger { 87 enum Trigger {
227 none, 88 none,
228 rising, 89 rising,
229 falling, 90 falling,
230 both, 91 both,
231 } 92 }
232 93
233 /// Provide GPIO access using the Sysfs Interface for Userspace. 94 /// Provide GPIO access using the Sysfs Interface for Userspace.
234 /// 95 ///
(...skipping 25 matching lines...) Expand all
260 /// gpio.exportPin(17); 121 /// gpio.exportPin(17);
261 /// gpio.setMode(17, Mode.input); 122 /// gpio.setMode(17, Mode.input);
262 /// gpio.setTrigger(17, Trigger.both); 123 /// gpio.setTrigger(17, Trigger.both);
263 /// bool value = gpio.getPin(17) 124 /// bool value = gpio.getPin(17)
264 /// gpio.setPin(4, value); 125 /// gpio.setPin(4, value);
265 /// while (true) { 126 /// while (true) {
266 /// var value = gpio.waitFor(17, !value, -1); 127 /// var value = gpio.waitFor(17, !value, -1);
267 /// gpio.setPin(4, value); 128 /// gpio.setPin(4, value);
268 /// } 129 /// }
269 /// ``` 130 /// ```
270 class SysfsGPIO extends _GPIOBase implements GPIO { 131 class SysfsGPIO extends GPIOBase {
271 // For documentation on the GPIO Sysfs Interface for Userspace see 132 // For documentation on the GPIO Sysfs Interface for Userspace see
272 // https://www.kernel.org/doc/Documentation/gpio/sysfs.txt. 133 // https://www.kernel.org/doc/Documentation/gpio/sysfs.txt.
273 static const String _basePath = '/sys/class/gpio/'; 134 static const String _basePath = '/sys/class/gpio/';
274 135
275 // Cached constants. 136 // Cached constants.
276 ByteBuffer _zero; 137 ByteBuffer _zero;
277 ByteBuffer _one; 138 ByteBuffer _one;
278 ByteBuffer _in; 139 ByteBuffer _in;
279 ByteBuffer _out; 140 ByteBuffer _out;
280 ByteBuffer _none; 141 ByteBuffer _none;
281 ByteBuffer _rising; 142 ByteBuffer _rising;
282 ByteBuffer _falling; 143 ByteBuffer _falling;
283 ByteBuffer _both; 144 ByteBuffer _both;
284 145
285 // Open value files for all tracked pins. Indexed by pin number. 146 // Open value files for all tracked pins. Indexed by pin number.
286 List<File> _tracked; 147 List<File> _tracked;
287 148
288 /// Create a GPIO controller using the GPIO Sysfs Interface. 149 /// Create a GPIO controller using the GPIO Sysfs Interface.
289 SysfsGPIO([int maxPins = 54]): super(maxPins) { 150 SysfsGPIO([int pins = GPIO.defaultPins]): super(pins) {
290 // Byte buffers for string constants. 151 // Byte buffers for string constants.
291 var data; 152 var data;
292 data = new Uint8List(1); 153 data = new Uint8List(1);
293 data.setRange(0, 1, '0'.codeUnits); 154 data.setRange(0, 1, '0'.codeUnits);
294 _zero = data.buffer; 155 _zero = data.buffer;
295 data = new Uint8List(1); 156 data = new Uint8List(1);
296 data.setRange(0, 1, '1'.codeUnits); 157 data.setRange(0, 1, '1'.codeUnits);
297 _one = data.buffer; 158 _one = data.buffer;
298 data = new Uint8List(2); 159 data = new Uint8List(2);
299 data.setRange(0, 2, 'in'.codeUnits); 160 data.setRange(0, 2, 'in'.codeUnits);
300 _in = data.buffer; 161 _in = data.buffer;
301 data = new Uint8List(3); 162 data = new Uint8List(3);
302 data.setRange(0, 3, 'out'.codeUnits); 163 data.setRange(0, 3, 'out'.codeUnits);
303 _out = data.buffer; 164 _out = data.buffer;
304 data = new Uint8List(4); 165 data = new Uint8List(4);
305 data.setRange(0, 4, 'none'.codeUnits); 166 data.setRange(0, 4, 'none'.codeUnits);
306 _none = data.buffer; 167 _none = data.buffer;
307 data = new Uint8List(6); 168 data = new Uint8List(6);
308 data.setRange(0, 6, 'rising'.codeUnits); 169 data.setRange(0, 6, 'rising'.codeUnits);
309 _rising = data.buffer; 170 _rising = data.buffer;
310 data = new Uint8List(7); 171 data = new Uint8List(7);
311 data.setRange(0, 7, 'falling'.codeUnits); 172 data.setRange(0, 7, 'falling'.codeUnits);
312 _falling = data.buffer; 173 _falling = data.buffer;
313 data = new Uint8List(4); 174 data = new Uint8List(4);
314 data.setRange(0, 4, 'both'.codeUnits); 175 data.setRange(0, 4, 'both'.codeUnits);
315 _both = data.buffer; 176 _both = data.buffer;
316 177
317 // Find the exported pins by just running through all trying to open 178 // Find the exported pins by just running through all trying to open
318 // the value file. 179 // the value file.
319 _tracked = new List<File>(_maxPins); 180 _tracked = new List<File>(pins);
320 for (int pin = 0; pin < _maxPins; pin++) { 181 for (int pin = 0; pin < pins; pin++) {
321 _track(pin); 182 _track(pin);
322 } 183 }
323 } 184 }
324 185
325 void _track(int pin) { 186 void _track(int pin) {
326 _untrack(pin); 187 _untrack(pin);
327 var f; 188 var f;
328 try { 189 try {
329 // Open the value file for this pin. 190 // Open the value file for this pin.
330 f = new File.open('${_basePath}gpio${pin}/value', mode: File.WRITE); 191 f = new File.open('${_basePath}gpio${pin}/value', mode: File.WRITE);
331 } catch (e) { 192 } catch (e) {
332 // Ignore pins which cannot be opened. 193 // Ignore pins which cannot be opened.
333 return; 194 return;
334 } 195 }
335 _tracked[pin] = f; 196 _tracked[pin] = f;
336 } 197 }
337 198
338 void _untrack(pin) { 199 void _untrack(pin) {
339 if (_tracked[pin] != null) { 200 if (_tracked[pin] != null) {
340 _tracked[pin].close(); 201 _tracked[pin].close();
341 _tracked[pin] = null; 202 _tracked[pin] = null;
342 } 203 }
343 } 204 }
344 205
345 void _checkTracked(int pin) { 206 void _checkTracked(int pin) {
346 _checkPinRange(pin); 207 checkPinRange(pin);
347 if (!isTracked(pin)) throw 'Pin $pin is not tracked'; 208 if (!isTracked(pin)) throw 'Pin $pin is not tracked';
348 } 209 }
349 210
350 /// Returns a list with the pins currently tracked. 211 /// Returns a list with the pins currently tracked.
351 List tracked() { 212 List tracked() {
352 var result = []; 213 var result = [];
353 for (int pin = 0; pin < _maxPins; pin++) { 214 for (int pin = 0; pin < pins; pin++) {
354 if (_tracked[pin] != null) result.add(pin); 215 if (_tracked[pin] != null) result.add(pin);
355 } 216 }
356 return result; 217 return result;
357 } 218 }
358 219
359 /// Checks if [pin] is tracked. 220 /// Checks if [pin] is tracked.
360 bool isTracked(int pin) { 221 bool isTracked(int pin) {
361 return _tracked[pin] != null; 222 return _tracked[pin] != null;
362 } 223 }
363 224
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
471 var f = new File.open('${_basePath}${exportOrUnexport}', 332 var f = new File.open('${_basePath}${exportOrUnexport}',
472 mode: File.WRITE_ONLY); 333 mode: File.WRITE_ONLY);
473 var value = '$pin'.codeUnits; 334 var value = '$pin'.codeUnits;
474 var bytes = new Uint8List(value.length); 335 var bytes = new Uint8List(value.length);
475 bytes.setRange(0, value.length, value); 336 bytes.setRange(0, value.length, value);
476 f.write(bytes.buffer); 337 f.write(bytes.buffer);
477 f.close(); 338 f.close();
478 } 339 }
479 340
480 void exportPin(int pin) { 341 void exportPin(int pin) {
481 _checkPinRange(pin); 342 checkPinRange(pin);
482 // If already exported do nothing. 343 // If already exported do nothing.
483 if (isTracked(pin)) return; 344 if (isTracked(pin)) return;
484 _exportUnexport(true, pin); 345 _exportUnexport(true, pin);
485 _track(pin); // This is now tracked. 346 _track(pin); // This is now tracked.
486 } 347 }
487 348
488 void unexportPin(int pin) { 349 void unexportPin(int pin) {
489 _checkPinRange(pin); 350 checkPinRange(pin);
490 // If not exported do nothing. 351 // If not exported do nothing.
491 if (!isTracked(pin)) return; 352 if (!isTracked(pin)) return;
492 _exportUnexport(false, pin); 353 _exportUnexport(false, pin);
493 _untrack(pin); // This is no longer tracked. 354 _untrack(pin); // This is no longer tracked.
494 } 355 }
495 } 356 }
496 357
497 /// Exceptions thrown by GPIO. 358 /// Exceptions thrown by GPIO.
498 class GPIOException implements Exception { 359 class GPIOException implements Exception {
499 /// Exception message. 360 /// Exception message.
500 final String message; 361 final String message;
501 /// OS error number if any. 362 /// OS error number if any.
502 final int errno; 363 final int errno;
503 const GPIOException(this.message, [this.errno]); 364 const GPIOException(this.message, [this.errno]);
504 String toString() { 365 String toString() {
505 if (errno == null) return message; 366 if (errno == null) return message;
506 return '$message (error ${errno})'; 367 return '$message (error ${errno})';
507 } 368 }
508 } 369 }
OLDNEW
« no previous file with comments | « no previous file | pkg/gpio/lib/gpio_mock.dart » ('j') | pkg/gpio/lib/gpio_mock.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698