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