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