lib: move pseudo-toplevel methods from Object
[nit.git] / lib / bcm2835 / bcm2835.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2013 Alexis Laferrière <alexis.laf@xymus.net>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Services to control the bcm2835 chipset used in the Raspberry Pi
18 #
19 # Uses the C library by Mike McCauley available at http://www.airspayce.com/mikem/bcm2835/
20 #
21 # This module targets the model B revision 1, it could be tweaked for other versions.
22 module bcm2835
23
24 in "C Header" `{
25 #include <bcm2835.h>
26 `}
27
28 fun bcm2835_init: Bool `{ return bcm2835_init(); `}
29 fun bcm2835_close `{ bcm2835_close(); `}
30 fun bcm2835_debug=(v: Bool) `{ bcm2835_set_debug(v); `}
31
32 # A physical binary pin
33 interface Pin
34 # Set the output of this pin
35 fun write(high: Bool) is abstract
36 end
37
38 extern class RPiPin `{ RPiGPIOPin `}
39 super Pin
40
41 new p1_03 `{ return RPI_GPIO_P1_03; `}
42
43 new p1_05 `{ return RPI_GPIO_P1_05; `}
44 new p1_07 `{ return RPI_GPIO_P1_07; `}
45
46 new p1_11 `{ return RPI_GPIO_P1_11; `}
47 new p1_12 `{ return RPI_GPIO_P1_12; `}
48 new p1_13 `{ return RPI_GPIO_P1_13; `}
49
50 new p1_15 `{ return RPI_GPIO_P1_15; `}
51 new p1_16 `{ return RPI_GPIO_P1_16; `}
52
53 new p1_18 `{ return RPI_GPIO_P1_18; `}
54 new p1_19 `{ return RPI_GPIO_P1_19; `}
55
56 new p1_21 `{ return RPI_GPIO_P1_21; `}
57 new p1_22 `{ return RPI_GPIO_P1_22; `}
58 new p1_23 `{ return RPI_GPIO_P1_23; `}
59 new p1_24 `{ return RPI_GPIO_P1_24; `}
60
61 new p1_26 `{ return RPI_GPIO_P1_26; `}
62
63 # Select mode: input, output or alts
64 fun fsel=(mode: FunctionSelect) `{ bcm2835_gpio_fsel(self, mode); `}
65
66 # Set output
67 redef fun write(high) `{ bcm2835_gpio_write(self, high? HIGH: LOW); `}
68
69 # Set pull up mode
70 fun pud=(pud: PUDControl) `{ bcm2835_gpio_set_pud(self, pud); `}
71
72 # Falling edge detect
73 # Do not use on raspbian, it is bugged!
74 fun fen `{ bcm2835_gpio_fen(self); `}
75 fun clr_fen `{ bcm2835_gpio_clr_fen(self); `}
76
77 # Raising edge detect
78 # Do not use on raspbian, it is bugged!
79 fun ren `{ bcm2835_gpio_ren(self); `}
80 fun clr_ren `{ bcm2835_gpio_clr_ren(self); `}
81
82 # High edge detect
83 # Do not use on raspbian, it is bugged!
84 fun hen `{ bcm2835_gpio_hen(self); `}
85 fun clr_hen `{ bcm2835_gpio_clr_hen(self); `}
86
87 # Low edge detect
88 # Do not use on raspbian, it is bugged!
89 fun len `{ bcm2835_gpio_len(self); `}
90 fun clr_len `{ bcm2835_gpio_clr_len(self); `}
91
92 fun set_eds `{ bcm2835_gpio_set_eds(self); `}
93 fun eds: Bool `{ return bcm2835_gpio_eds(self); `}
94
95 # Return input on pin, true for high and false for low
96 fun lev: Bool `{ return bcm2835_gpio_lev(self); `}
97 end
98
99 extern class FunctionSelect `{ bcm2835FunctionSelect `}
100 # Input function
101 new inpt `{ return BCM2835_GPIO_FSEL_INPT; `}
102
103 # Output function
104 new outp `{ return BCM2835_GPIO_FSEL_OUTP; `}
105
106 new alt0 `{ return BCM2835_GPIO_FSEL_ALT0; `}
107 new alt1 `{ return BCM2835_GPIO_FSEL_ALT1; `}
108 new alt2 `{ return BCM2835_GPIO_FSEL_ALT2; `}
109 new alt3 `{ return BCM2835_GPIO_FSEL_ALT3; `}
110 new alt4 `{ return BCM2835_GPIO_FSEL_ALT4; `}
111 new alt5 `{ return BCM2835_GPIO_FSEL_ALT5; `}
112 new mask `{ return BCM2835_GPIO_FSEL_MASK; `}
113 end
114
115 extern class PUDControl `{ bcm2835PUDControl `}
116 new off `{ return BCM2835_GPIO_PUD_OFF; `}
117 new down `{ return BCM2835_GPIO_PUD_DOWN; `}
118 new up `{ return BCM2835_GPIO_PUD_UP; `}
119 end
120
121 redef universal Int
122 fun bcm2835_delay `{ bcm2835_delay(self); `}
123 fun bcm2835_delay_micros `{ bcm2835_delayMicroseconds(self); `}
124 end
125
126 class RotaryEncoder
127 var pin_a: RPiPin
128 var pin_b: RPiPin
129 var old_a= false
130 var old_b= false
131
132 # returns '<', '>' or null accoring to rotation or lack thereof
133 fun update: nullable Char
134 do
135 var new_a = pin_a.lev
136 var new_b = pin_b.lev
137 var res = null
138
139 if new_a != old_a or new_b != old_b then
140 if not old_a and not old_b then
141 # everything was on
142 if not new_a and new_b then
143 res = '<'
144 else if new_a and not new_b then
145 res = '>'
146 end
147 else if old_a and old_b then
148 # everything was off
149 if not new_a and new_b then
150 res = '>'
151 else if new_a and not new_b then
152 res = '<'
153 end
154 end
155
156 old_a = new_a
157 old_b = new_b
158 end
159
160 return res
161 end
162 end
163
164 # Hitachi HD44780 or similar 2-4 lines LCD displays
165 class HD44780
166 var rs: RPiPin
167 var en: RPiPin
168 var d4: RPiPin
169 var d5: RPiPin
170 var d6: RPiPin
171 var d7: RPiPin
172
173 var ds = new Array[RPiPin]
174
175 # commands
176 fun flag_clear_display: Int do return 1
177 fun flag_return_home: Int do return 2
178 fun flag_entry_mode_set: Int do return 4
179 fun flag_display_control: Int do return 8
180 fun flag_cursor_shift: Int do return 16
181 fun flag_function_set: Int do return 32
182 fun flag_set_cgram_addr: Int do return 64
183 fun flag_set_ggram_addr: Int do return 128
184
185 # entry mode
186 fun flag_entry_right: Int do return 0
187 fun flag_entry_left: Int do return 2
188 fun flag_entry_shift_increment: Int do return 1
189 fun flag_entry_shift_decrement: Int do return 0
190
191 # display flags
192 fun flag_display_on: Int do return 4
193 fun flag_display_off: Int do return 0
194 fun flag_cursor_on: Int do return 2
195 fun flag_cursor_off: Int do return 0
196 fun flag_blink_on: Int do return 1
197 fun flag_blink_off: Int do return 0
198
199 # display/cursor shift
200 fun flag_display_move: Int do return 8
201 fun flag_cursor_move: Int do return 0
202 fun flag_move_right: Int do return 4
203 fun flag_move_left: Int do return 0
204
205 # function set
206 fun flag_8bit_mode: Int do return 16
207 fun flag_4bit_mode: Int do return 0
208 fun flag_2_lines: Int do return 8
209 fun flag_1_line: Int do return 0
210 fun flag_5x10_dots: Int do return 4
211 fun flag_5x8_dots: Int do return 0
212
213 # last text displayed
214 private var last_text: nullable String = null
215
216 fun function_set(bits, lines, dots_wide: Int)
217 do
218 var fs = flag_function_set
219 if bits == 8 then
220 fs = fs | 16
221 else if bits != 4 then abort
222
223 if lines == 2 then
224 fs = fs | 8
225 else if lines != 1 then abort
226
227 if dots_wide == 10 then
228 fs = fs | 4
229 else if dots_wide != 8 then abort
230
231 write(true, fs)
232 end
233
234 fun display_control(on, cursor, blink: Bool)
235 do
236 var fs = flag_display_control
237
238 fs |= if on then flag_display_on else flag_display_off
239
240 fs |= if cursor then flag_cursor_on else flag_cursor_off
241
242 fs |= if blink then flag_blink_on else flag_blink_off
243
244 write(true, fs)
245 end
246
247 fun entry_mode(left, incr: Bool)
248 do
249 var fs = flag_entry_mode_set
250
251 fs |= if left then flag_entry_left else flag_entry_right
252
253 fs |= if incr then flag_entry_shift_increment else flag_entry_shift_decrement
254
255 write(true, fs)
256 end
257
258 fun setup_alt
259 do
260 ds = [d4,d5,d6,d7]
261
262 rs.fsel = new FunctionSelect.outp
263 en.fsel = new FunctionSelect.outp
264 d4.fsel = new FunctionSelect.outp
265 d5.fsel = new FunctionSelect.outp
266 d6.fsel = new FunctionSelect.outp
267 d7.fsel = new FunctionSelect.outp
268
269 rs.write(false)
270 en.write(false)
271
272 # wait 20ms for power up
273 50.bcm2835_delay
274
275 write_4bits(true,true,false,false)
276 write_4_bits(3)
277
278 5.bcm2835_delay
279
280 write_4bits(true,true,false,false)
281 write_4_bits(3)
282
283 5.bcm2835_delay
284
285 write_4bits(true,true,false,false)
286 write_4_bits(3)
287
288 200.bcm2835_delay_micros
289
290 write_4bits(false,true,false,false)
291 write_4_bits(2)
292
293 # wait 5ms
294 5.bcm2835_delay
295
296 # set interface
297 # 4bits, 2 lines
298 function_set(4, 2, 8)
299
300 # cursor
301 # don't shift & hide
302 display_control(true, true, true)
303
304 # clear & home
305 clear
306
307 # set cursor move direction
308 # move right
309 write(true, 6)
310
311 # turn on display
312 write(true, 4)
313
314 # set entry mode
315 entry_mode(true, true)
316 end
317
318 fun setup
319 do
320 ds = [d4,d5,d6,d7]
321
322 rs.fsel = new FunctionSelect.outp
323 en.fsel = new FunctionSelect.outp
324 d4.fsel = new FunctionSelect.outp
325 d5.fsel = new FunctionSelect.outp
326 d6.fsel = new FunctionSelect.outp
327 d7.fsel = new FunctionSelect.outp
328
329 rs.write(false)
330 en.write(false)
331
332 write(true, "33".to_hex) # init
333 write(true, "32".to_hex) # init
334 write(true, "28".to_hex) # 2 lines, 5x7
335 write(true, "0C".to_hex) # hide cursor
336 write(true, "06".to_hex) # cursor move right
337 write(true, "04".to_hex) # turn on display
338 write(true, "01".to_hex) # clear display
339 end
340
341 fun write_4_bits(v: Int)
342 do
343 var lb = once [1,2,4,8]
344 for i in [0..4[ do
345 var b = lb[i]
346 var r = b & v != 0
347 var d = ds[i]
348 d.write(r)
349 end
350 pulse_enable
351 end
352
353 fun write_4bits(a,b,c,d:Bool)
354 do
355 d4.write(a)
356 d5.write(b)
357 d6.write(c)
358 d7.write(d)
359 pulse_enable
360 end
361
362 fun pulse_enable
363 do
364 en.write(false)
365 1.bcm2835_delay_micros
366 en.write(true)
367 100.bcm2835_delay_micros
368 en.write(false)
369 1.bcm2835_delay_micros
370 end
371
372 fun write(is_cmd: Bool, cmd: Int)
373 do
374 en.write(false)
375 rs.write(not is_cmd)
376
377 # high byte
378 var hb = once [16,32,64,128]
379 for i in [0..4[ do
380 var b = hb[i]
381 var r = b & cmd != 0
382 var d = ds[i]
383 d.write(r)
384 end
385
386 pulse_enable
387
388 if is_cmd then
389 # wait 5ms
390 5.bcm2835_delay
391 else
392 # wait 200us
393 200.bcm2835_delay_micros
394 end
395
396 # low byte
397 var lb = once [1,2,4,8]
398 for i in [0..4[ do
399 var b = lb[i]
400 var r = b & cmd != 0
401 var d = ds[i]
402 d.write(r)
403 end
404
405 pulse_enable
406
407 if is_cmd then
408 # wait 5ms
409 5.bcm2835_delay
410 else
411 # wait 200us
412 200.bcm2835_delay_micros
413 end
414 end
415
416 fun clear
417 do
418 write(true,1)
419 2.bcm2835_delay
420 end
421
422 fun return_home
423 do
424 write(true,2)
425 2.bcm2835_delay
426 end
427
428 fun text=(v: String)
429 do
430 # do not redraw the samething
431 var last_text = last_text
432 if last_text != null and last_text == v then return
433
434 clear
435 return_home
436 var count = 0
437 for c in v.chars do
438 if c == '\n' then
439 # FIXME, this should work
440 #write(true, "C0".to_hex)
441 # instead we use the following which may not be portable
442
443 for s in [count..40[ do write(false, ' '.code_point)
444 count = 0
445 else
446 write(false, c.code_point)
447 count += 1
448 end
449 end
450
451 self.last_text = v
452 end
453 end
454
455 # Component for any kind of buttons or switches
456 class Switch
457 var pin: RPiPin
458
459 init (pin: RPiPin, pud: PUDControl)
460 do
461 self.pin = pin
462 pin.fsel = new FunctionSelect.inpt
463 pin.pud = pud
464 end
465
466 fun is_down: Bool do return pin.lev
467
468 var last_down: nullable Bool = null
469
470 # Returns true is state (is_down) changed since last call to `changed`
471 fun changed: Bool
472 do
473 var now = is_down
474 var last_down = last_down
475 if last_down == null then
476 self.last_down = now
477 return false
478 else if last_down != now then
479 self.last_down = now
480 return true
481 else return false
482 end
483 end
484
485 class StepperMotor
486 var pins: Sequence[RPiPin]
487 var delay: Int
488
489 init (delay: Int, a, b, c, d: RPiPin)
490 do
491 pins = [a, b, c, d]
492 self.delay = delay
493
494 for p in pins do p.fsel = new FunctionSelect.outp
495 end
496
497 fun forward(steps: Int)
498 do
499 for s in [0..steps[ do
500 set(true, false, false, false)
501 delay.bcm2835_delay
502 set(true, true, false, false)
503 delay.bcm2835_delay
504 set(false, true, false, false)
505 delay.bcm2835_delay
506 set(false, true, true, false)
507 delay.bcm2835_delay
508 set(false, false, true, false)
509 delay.bcm2835_delay
510 set(false, false, true, true)
511 delay.bcm2835_delay
512 set(false, false, false, true)
513 delay.bcm2835_delay
514 set(true, false, false, true)
515 delay.bcm2835_delay
516 end
517 end
518
519 fun backwards(steps: Int)
520 do
521 for s in [0..steps[ do
522 set(true, false, false, true)
523 delay.bcm2835_delay
524 set(false, false, false, true)
525 delay.bcm2835_delay
526 set(false, false, true, true)
527 delay.bcm2835_delay
528 set(false, false, true, false)
529 delay.bcm2835_delay
530 set(false, true, true, false)
531 delay.bcm2835_delay
532 set(false, true, false, false)
533 delay.bcm2835_delay
534 set(true, true, false, false)
535 delay.bcm2835_delay
536 set(true, false, false, false)
537 delay.bcm2835_delay
538 end
539 end
540
541 fun release do set(false, false, false, false)
542
543 protected fun set(a, b, c, d: Bool)
544 do
545 var bits = new Array[Bool].with_items(a, b, c, d)
546
547 for i in [0..4[ do pins[i].write(bits[i])
548 end
549 end
550
551 class Buzzer
552 var pin: RPiPin
553
554 fun buzz(delay: Float, times: Int)
555 do
556 assert times > 0
557 assert delay > 0.0
558 var delay_i = (delay*1000.0).to_i
559 for i in [0..times[ do
560 pin.write(true)
561 delay_i.bcm2835_delay_micros
562 pin.write(false)
563 delay_i.bcm2835_delay_micros
564 end
565 end
566 end