Services to control the bcm2835 chipset used in the Raspberry Pi

Uses the C library by Mike McCauley available at http://www.airspayce.com/mikem/bcm2835/

This module targets the model B revision 1, it could be tweaked for other versions.

Introduced classes

class Buzzer

bcm2835 :: Buzzer

class HD44780

bcm2835 :: HD44780

Hitachi HD44780 or similar 2-4 lines LCD displays
extern class PUDControl

bcm2835 :: PUDControl

interface Pin

bcm2835 :: Pin

A physical binary pin
extern class RPiPin

bcm2835 :: RPiPin

class Switch

bcm2835 :: Switch

Component for any kind of buttons or switches

Redefined classes

redef enum Int

bcm2835 :: bcm2835 $ Int

Native integer numbers.
redef class Sys

bcm2835 :: bcm2835 $ Sys

The main class of the program.

All class definitions

class Buzzer

bcm2835 $ Buzzer

class HD44780

bcm2835 $ HD44780

Hitachi HD44780 or similar 2-4 lines LCD displays
redef enum Int

bcm2835 :: bcm2835 $ Int

Native integer numbers.
extern class PUDControl

bcm2835 $ PUDControl

interface Pin

bcm2835 $ Pin

A physical binary pin
extern class RPiPin

bcm2835 $ RPiPin

class Switch

bcm2835 $ Switch

Component for any kind of buttons or switches
redef class Sys

bcm2835 :: bcm2835 $ Sys

The main class of the program.
package_diagram bcm2835::bcm2835 bcm2835 core core bcm2835::bcm2835->core bcm2835::blink blink bcm2835::blink->bcm2835::bcm2835 bcm2835::input input bcm2835::input->bcm2835::bcm2835 a_star-m a_star-m a_star-m->bcm2835::blink a_star-m->bcm2835::input a_star-m... ... a_star-m...->a_star-m

Ancestors

module abstract_collection

core :: abstract_collection

Abstract collection classes and services.
module abstract_text

core :: abstract_text

Abstract class for manipulation of sequences of characters
module array

core :: array

This module introduces the standard array structure.
module bitset

core :: bitset

Services to handle BitSet
module bytes

core :: bytes

Services for byte streams and arrays
module circular_array

core :: circular_array

Efficient data structure to access both end of the sequence.
module codec_base

core :: codec_base

Base for codecs to use with streams
module codecs

core :: codecs

Group module for all codec-related manipulations
module collection

core :: collection

This module define several collection classes.
module environ

core :: environ

Access to the environment variables of the process
module error

core :: error

Standard error-management infrastructure.
module exec

core :: exec

Invocation and management of operating system sub-processes.
module file

core :: file

File manipulations (create, read, write, etc.)
module fixed_ints

core :: fixed_ints

Basic integers of fixed-precision
module fixed_ints_text

core :: fixed_ints_text

Text services to complement fixed_ints
module flat

core :: flat

All the array-based text representations
module gc

core :: gc

Access to the Nit internal garbage collection mechanism
module hash_collection

core :: hash_collection

Introduce HashMap and HashSet.
module iso8859_1

core :: iso8859_1

Codec for ISO8859-1 I/O
module kernel

core :: kernel

Most basic classes and methods.
module list

core :: list

This module handle double linked lists
module math

core :: math

Mathematical operations
module native

core :: native

Native structures for text and bytes
module numeric

core :: numeric

Advanced services for Numeric types
module protocol

core :: protocol

module queue

core :: queue

Queuing data structures and wrappers
module range

core :: range

Module for range of discrete objects.
module re

core :: re

Regular expression support for all services based on Pattern
module ropes

core :: ropes

Tree-based representation of a String.
module sorter

core :: sorter

This module contains classes used to compare things and sorts arrays.
module stream

core :: stream

Input and output streams of characters
module text

core :: text

All the classes and methods related to the manipulation of text entities
module time

core :: time

Management of time and dates
module union_find

core :: union_find

union–find algorithm using an efficient disjoint-set data structure
module utf8

core :: utf8

Codec for UTF-8 I/O

Parents

module core

core :: core

Standard classes and methods used by default by Nit programs and libraries.

Children

module input

bcm2835 :: input

Descendants

module a_star-m

a_star-m

# Services to control the bcm2835 chipset used in the Raspberry Pi
#
# Uses the C library by Mike McCauley available at http://www.airspayce.com/mikem/bcm2835/
#
# This module targets the model B revision 1, it could be tweaked for other versions.
module bcm2835

in "C Header" `{
	#include <bcm2835.h>
`}

fun bcm2835_init: Bool `{ return bcm2835_init(); `}
fun bcm2835_close `{ bcm2835_close(); `}
fun bcm2835_debug=(v: Bool) `{ bcm2835_set_debug(v); `}

# A physical binary pin
interface Pin
	# Set the output of this pin
	fun write(high: Bool) is abstract
end

extern class RPiPin `{ RPiGPIOPin `}
	super Pin

	new p1_03 `{ return RPI_GPIO_P1_03; `}

	new p1_05 `{ return RPI_GPIO_P1_05; `}
	new p1_07 `{ return RPI_GPIO_P1_07; `}

	new p1_11 `{ return RPI_GPIO_P1_11; `}
	new p1_12 `{ return RPI_GPIO_P1_12; `}
	new p1_13 `{ return RPI_GPIO_P1_13; `}

	new p1_15 `{ return RPI_GPIO_P1_15; `}
	new p1_16 `{ return RPI_GPIO_P1_16; `}

	new p1_18 `{ return RPI_GPIO_P1_18; `}
	new p1_19 `{ return RPI_GPIO_P1_19; `}

	new p1_21 `{ return RPI_GPIO_P1_21; `}
	new p1_22 `{ return RPI_GPIO_P1_22; `}
	new p1_23 `{ return RPI_GPIO_P1_23; `}
	new p1_24 `{ return RPI_GPIO_P1_24; `}

	new p1_26 `{ return RPI_GPIO_P1_26; `}

	# Select mode: input, output or alts
	fun fsel=(mode: FunctionSelect) `{ bcm2835_gpio_fsel(self, mode); `}

	# Set output
	redef fun write(high) `{ bcm2835_gpio_write(self, high? HIGH: LOW); `}

	# Set pull up mode
	fun pud=(pud: PUDControl) `{ bcm2835_gpio_set_pud(self, pud); `}

	# Falling edge detect
	# Do not use on raspbian, it is bugged!
	fun fen `{ bcm2835_gpio_fen(self); `}
	fun clr_fen `{ bcm2835_gpio_clr_fen(self); `}

	# Raising edge detect
	# Do not use on raspbian, it is bugged!
	fun ren `{ bcm2835_gpio_ren(self); `}
	fun clr_ren `{ bcm2835_gpio_clr_ren(self); `}

	# High edge detect
	# Do not use on raspbian, it is bugged!
	fun hen `{ bcm2835_gpio_hen(self); `}
	fun clr_hen `{ bcm2835_gpio_clr_hen(self); `}

	# Low edge detect
	# Do not use on raspbian, it is bugged!
	fun len `{ bcm2835_gpio_len(self); `}
	fun clr_len `{ bcm2835_gpio_clr_len(self); `}

	fun set_eds `{ bcm2835_gpio_set_eds(self); `}
	fun eds: Bool `{ return bcm2835_gpio_eds(self); `}

	# Return input on pin, true for high and false for low
	fun lev: Bool `{ return bcm2835_gpio_lev(self); `}
end

extern class FunctionSelect `{ bcm2835FunctionSelect `}
	# Input function
	new inpt `{ return BCM2835_GPIO_FSEL_INPT; `}

	# Output function
	new outp `{ return BCM2835_GPIO_FSEL_OUTP; `}

	new alt0 `{ return BCM2835_GPIO_FSEL_ALT0; `}
	new alt1 `{ return BCM2835_GPIO_FSEL_ALT1; `}
	new alt2 `{ return BCM2835_GPIO_FSEL_ALT2; `}
	new alt3 `{ return BCM2835_GPIO_FSEL_ALT3; `}
	new alt4 `{ return BCM2835_GPIO_FSEL_ALT4; `}
	new alt5 `{ return BCM2835_GPIO_FSEL_ALT5; `}
	new mask `{ return BCM2835_GPIO_FSEL_MASK; `}
end

extern class PUDControl `{ bcm2835PUDControl `}
	new off `{ return BCM2835_GPIO_PUD_OFF; `}
	new down `{ return BCM2835_GPIO_PUD_DOWN; `}
	new up `{ return BCM2835_GPIO_PUD_UP; `}
end

redef universal Int
	fun bcm2835_delay `{ bcm2835_delay(self); `}
	fun bcm2835_delay_micros `{ bcm2835_delayMicroseconds(self); `}
end

class RotaryEncoder
	var pin_a: RPiPin
	var pin_b: RPiPin
	var old_a= false
	var old_b= false

	# returns '<', '>' or null accoring to rotation or lack thereof
	fun update: nullable Char
	do
		var new_a = pin_a.lev
		var new_b = pin_b.lev
		var res = null

		if new_a != old_a or new_b != old_b then
			if not old_a and not old_b then
				# everything was on
				if not new_a and new_b then
					res = '<'
				else if new_a and not new_b then
					res = '>'
				end
			else if old_a and old_b then
				# everything was off
				if not new_a and new_b then
					res = '>'
				else if new_a and not new_b then
					res = '<'
				end
			end

			old_a = new_a
			old_b = new_b
		end

		return res
	end
end

# Hitachi HD44780 or similar 2-4 lines LCD displays
class HD44780
	var rs: RPiPin
	var en: RPiPin
	var d4: RPiPin
	var d5: RPiPin
	var d6: RPiPin
	var d7: RPiPin

	var ds = new Array[RPiPin]

	# commands
	fun flag_clear_display: Int do return 1
	fun flag_return_home: Int do return 2
	fun flag_entry_mode_set: Int do return 4
	fun flag_display_control: Int do return 8
	fun flag_cursor_shift: Int do return 16
	fun flag_function_set: Int do return 32
	fun flag_set_cgram_addr: Int do return 64
	fun flag_set_ggram_addr: Int do return 128

	# entry mode
	fun flag_entry_right: Int do return 0
	fun flag_entry_left: Int do return 2
	fun flag_entry_shift_increment: Int do return 1
	fun flag_entry_shift_decrement: Int do return 0

	# display flags
	fun flag_display_on: Int do return 4
	fun flag_display_off: Int do return 0
	fun flag_cursor_on: Int do return 2
	fun flag_cursor_off: Int do return 0
	fun flag_blink_on: Int do return 1
	fun flag_blink_off: Int do return 0

	# display/cursor shift
	fun flag_display_move: Int do return 8
	fun flag_cursor_move: Int do return 0
	fun flag_move_right: Int do return 4
	fun flag_move_left: Int do return 0

	# function set
	fun flag_8bit_mode: Int do return 16
	fun flag_4bit_mode: Int do return 0
	fun flag_2_lines: Int do return 8
	fun flag_1_line: Int do return 0
	fun flag_5x10_dots: Int do return 4
	fun flag_5x8_dots: Int do return 0

	# last text displayed
	private var last_text: nullable String = null

	fun function_set(bits, lines, dots_wide: Int)
	do
		var fs = flag_function_set
		if bits == 8 then
			fs = fs | 16
		else if bits != 4 then abort

		if lines == 2 then
			fs = fs | 8
		else if lines != 1 then abort

		if dots_wide == 10 then
			fs = fs | 4
		else if dots_wide != 8 then abort

		write(true, fs)
	end

	fun display_control(on, cursor, blink: Bool)
	do
		var fs = flag_display_control

		fs |= if on then flag_display_on else flag_display_off

		fs |= if cursor then flag_cursor_on else flag_cursor_off

		fs |= if blink then flag_blink_on else flag_blink_off

		write(true, fs)
	end

	fun entry_mode(left, incr: Bool)
	do
		var fs = flag_entry_mode_set

		fs |= if left then flag_entry_left else flag_entry_right

		fs |= if incr then flag_entry_shift_increment else flag_entry_shift_decrement

		write(true, fs)
	end

	fun setup_alt
	do
		ds = [d4,d5,d6,d7]

		rs.fsel = new FunctionSelect.outp
		en.fsel = new FunctionSelect.outp
		d4.fsel = new FunctionSelect.outp
		d5.fsel = new FunctionSelect.outp
		d6.fsel = new FunctionSelect.outp
		d7.fsel = new FunctionSelect.outp

		rs.write(false)
		en.write(false)

		# wait 20ms for power up
		50.bcm2835_delay

		write_4bits(true,true,false,false)
		write_4_bits(3)

		5.bcm2835_delay

		write_4bits(true,true,false,false)
		write_4_bits(3)

		5.bcm2835_delay

		write_4bits(true,true,false,false)
		write_4_bits(3)

		200.bcm2835_delay_micros

		write_4bits(false,true,false,false)
		write_4_bits(2)

		# wait 5ms
		5.bcm2835_delay

		# set interface
		# 4bits, 2 lines
		function_set(4, 2, 8)

		# cursor
		# don't shift & hide
		display_control(true, true, true)

		# clear & home
		clear

		# set cursor move direction
		# move right
		write(true, 6)

		# turn on display
		write(true, 4)

		# set entry mode
		entry_mode(true, true)
	end

	fun setup
	do
		ds = [d4,d5,d6,d7]

		rs.fsel = new FunctionSelect.outp
		en.fsel = new FunctionSelect.outp
		d4.fsel = new FunctionSelect.outp
		d5.fsel = new FunctionSelect.outp
		d6.fsel = new FunctionSelect.outp
		d7.fsel = new FunctionSelect.outp

		rs.write(false)
		en.write(false)

		write(true, "33".to_hex) # init
		write(true, "32".to_hex) # init
		write(true, "28".to_hex) # 2 lines, 5x7
		write(true, "0C".to_hex) # hide cursor
		write(true, "06".to_hex) # cursor move right
		write(true, "04".to_hex) # turn on display
		write(true, "01".to_hex) # clear display
	end

	fun write_4_bits(v: Int)
	do
		var lb = once [1,2,4,8]
		for i in [0..4[ do
			var b = lb[i]
			var r = b & v != 0
			var d = ds[i]
			d.write(r)
		end
		pulse_enable
	end

	fun write_4bits(a,b,c,d:Bool)
	do
		d4.write(a)
		d5.write(b)
		d6.write(c)
		d7.write(d)
		pulse_enable
	end

	fun pulse_enable
	do
		en.write(false)
		1.bcm2835_delay_micros
		en.write(true)
		100.bcm2835_delay_micros
		en.write(false)
		1.bcm2835_delay_micros
	end

	fun write(is_cmd: Bool, cmd: Int)
	do
		en.write(false)
		rs.write(not is_cmd)

		# high byte
		var hb = once [16,32,64,128]
		for i in [0..4[ do
			var b = hb[i]
			var r = b & cmd != 0
			var d = ds[i]
			d.write(r)
		end

		pulse_enable

		if is_cmd then
			# wait 5ms
			5.bcm2835_delay
		else
			# wait 200us
			200.bcm2835_delay_micros
		end

		# low byte
		var lb = once [1,2,4,8]
		for i in [0..4[ do
			var b = lb[i]
			var r = b & cmd != 0
			var d = ds[i]
			d.write(r)
		end

		pulse_enable

		if is_cmd then
			# wait 5ms
			5.bcm2835_delay
		else
			# wait 200us
			200.bcm2835_delay_micros
		end
	end

	fun clear
	do
		write(true,1)
		2.bcm2835_delay
	end

	fun return_home
	do
		write(true,2)
		2.bcm2835_delay
	end

	fun text=(v: String)
	do
		# do not redraw the samething
		var last_text = last_text
		if last_text != null and last_text == v then return

		clear
		return_home
		var count = 0
		for c in v.chars do
			if c == '\n' then
				# FIXME, this should work
				#write(true, "C0".to_hex)
				# instead we use the following which may not be portable

				for s in [count..40[ do write(false, ' '.code_point)
				count = 0
			else
				write(false, c.code_point)
				count += 1
			end
		end

		self.last_text = v
	end
end

# Component for any kind of buttons or switches
class Switch
	var pin: RPiPin

	init (pin: RPiPin, pud: PUDControl)
	do
		self.pin = pin
		pin.fsel = new FunctionSelect.inpt
		pin.pud = pud
	end

	fun is_down: Bool do return pin.lev

	var last_down: nullable Bool = null

	# Returns true is state (is_down) changed since last call to `changed`
	fun changed: Bool
	do
		var now = is_down
		var last_down = last_down
		if last_down == null then
			self.last_down = now
			return false
		else if last_down != now then
			self.last_down = now
			return true
		else return false
	end
end

class StepperMotor
	var pins: Sequence[RPiPin]
	var delay: Int

	init (delay: Int, a, b, c, d: RPiPin)
	do
		pins = [a, b, c, d]
		self.delay = delay

		for p in pins do p.fsel = new FunctionSelect.outp
	end

	fun forward(steps: Int)
	do
		for s in [0..steps[ do
			set(true, false, false, false)
			delay.bcm2835_delay
			set(true, true, false, false)
			delay.bcm2835_delay
			set(false, true, false, false)
			delay.bcm2835_delay
			set(false, true, true, false)
			delay.bcm2835_delay
			set(false, false, true, false)
			delay.bcm2835_delay
			set(false, false, true, true)
			delay.bcm2835_delay
			set(false, false, false, true)
			delay.bcm2835_delay
			set(true, false, false, true)
			delay.bcm2835_delay
		end
	end

	fun backwards(steps: Int)
	do
		for s in [0..steps[ do
			set(true, false, false, true)
			delay.bcm2835_delay
			set(false, false, false, true)
			delay.bcm2835_delay
			set(false, false, true, true)
			delay.bcm2835_delay
			set(false, false, true, false)
			delay.bcm2835_delay
			set(false, true, true, false)
			delay.bcm2835_delay
			set(false, true, false, false)
			delay.bcm2835_delay
			set(true, true, false, false)
			delay.bcm2835_delay
			set(true, false, false, false)
			delay.bcm2835_delay
		end
	end

	fun release do set(false, false, false, false)

	protected fun set(a, b, c, d: Bool)
	do
		var bits = new Array[Bool].with_items(a, b, c, d)

		for i in [0..4[ do pins[i].write(bits[i])
	end
end

class Buzzer
	var pin: RPiPin

	fun buzz(delay: Float, times: Int)
	do
		assert times > 0
		assert delay > 0.0
		var delay_i = (delay*1000.0).to_i
		for i in [0..times[ do
			pin.write(true)
			delay_i.bcm2835_delay_micros
			pin.write(false)
			delay_i.bcm2835_delay_micros
		end
	end
end
lib/bcm2835/bcm2835.nit:17,1--566,3