module Beaglebone::GPIO
GPIO
¶ ↑
procedural methods for GPIO
control
Summary¶ ↑
pin_mode is called to initialize a pin. Further basic functionality is available with digital_read and digital_write
Constants
Public Class Methods
Resets all the GPIO
pins that we have used and unexport them
# File lib/beaglebone/gpio.rb, line 277 def cleanup get_gpio_pins.each { |x| disable_gpio_pin(x) } end
Reads a pin’s input state and return that value
@param pin should be a symbol representing the header pin, i.e. :P9_11
@return [Symbol] :HIGH or :LOW
@example
GPIO.digital_read(:P9_11) => :HIGH
# File lib/beaglebone/gpio.rb, line 166 def digital_read(pin) check_gpio_enabled(pin) raise StandardError, "PIN not in GPIO IN mode: #{pin}" unless get_gpio_mode(pin) == :IN fd = get_value_fd(pin) fd.rewind value = fd.read.to_s.strip state = STATES.key(value.to_i) Beaglebone::set_pin_status(pin, :state, state) end
Sets a pin’s output state
@param pin should be a symbol representing the header pin @param state should be a symbol representin the state, :HIGH or :LOW
@example
GPIO.digital_write(:P9_12, :HIGH) GPIO.digital_write(:P9_12, :LOW)
# File lib/beaglebone/gpio.rb, line 146 def digital_write(pin, state) check_valid_state(state) check_gpio_enabled(pin) raise StandardError, "PIN not in GPIO OUT mode: #{pin}" unless get_gpio_mode(pin) == :OUT fd = get_value_fd(pin) fd.write STATES[state.to_sym].to_s fd.flush Beaglebone::set_pin_status(pin, :state, state) end
Disable a GPIO
pin
@param pin should be a symbol representing the header pin
# File lib/beaglebone/gpio.rb, line 420 def disable_gpio_pin(pin) Beaglebone::check_valid_pin(pin, :gpio) pininfo = PINS[pin] close_value_fd(pin) #close any running threads stop_edge_wait(pin) #write to unexport to disable gpio begin File.open('/sys/class/gpio/unexport', 'w') { |f| f.write(pininfo[:gpio]) } rescue # end #unload device tree Beaglebone::device_tree_unload("#{TREES[:GPIO][:pin]}#{pin}_.*") unless pininfo[:led] #remove status from hash so following enabled? call checks actual system Beaglebone::delete_pin_status(pin) #check to see if pin is GPIO enabled in /sys/class/gpio/ raise StandardError, "GPIO was unable to uninitalize pin: #{pin.to_s}" if enabled?(pin) end
Returns true if specified pin is enabled in GPIO
mode, else false
# File lib/beaglebone/gpio.rb, line 282 def enabled?(pin) return true if Beaglebone::get_pin_status(pin, :type) == :gpio return false unless valid?(pin) if Dir.exists?(gpio_directory(pin)) Beaglebone::set_pin_status(pin, :type, :gpio) return true end false end
Returns the GPIO
edge trigger type on an initialized pin @return [Symbol] :NONE, :RISING, :FALLING, or :BOTH
# File lib/beaglebone/gpio.rb, line 398 def get_gpio_edge(pin) check_gpio_enabled(pin) edge = Beaglebone::get_pin_status(pin, :trigger) return edge if edge read_gpio_edge(pin) end
Returns mode from pin
, reads mode if unknown @returns [Symbol] :IN or :OUT
# File lib/beaglebone/gpio.rb, line 339 def get_gpio_mode(pin) check_gpio_enabled(pin) mode = Beaglebone::get_pin_status(pin, :mode) return mode if mode read_gpio_direction(pin) end
Return an array of GPIO
pins in use
@return [Array<Symbol>]
@example
GPIO.get_gpio_pins => [:P9_12, :P9_13]
# File lib/beaglebone/gpio.rb, line 413 def get_gpio_pins Beaglebone.pinstatus.clone.select { |x,y| x if y[:type] == :gpio && !PINS[x][:led] }.keys end
Returns last known state from pin
, reads state if unknown @returns [Symbol] :HIGH or :LOW
# File lib/beaglebone/gpio.rb, line 328 def get_gpio_state(pin) check_gpio_enabled(pin) state = Beaglebone::get_pin_status(pin, :state) return state if state digital_read(pin) end
Initialize a GPIO
pin
@param pin should be a symbol representing the header pin @param mode should specify the mode of the pin, either :IN or :OUT @param pullmode (optional) should specify the pull mode, :PULLUP, :PULLDOWN, or :NONE @param slewrate (optional) should specify the slew rate, :FAST or :SLOW @example
GPIO.pin_mode(:P9_12, :OUT) GPIO.pin_mode(:P9_11, :IN, :PULLUP, :FAST)
# File lib/beaglebone/gpio.rb, line 70 def pin_mode(pin, mode, pullmode = nil, slewrate = nil) #make sure a valid mode was passed check_valid_mode(mode) #make sure a valid pin was passed and that it supports GPIO Beaglebone::check_valid_pin(pin, :gpio) #get info from PINS hash pininfo = PINS[pin] #if pin is enabled for something else, disable it if Beaglebone::get_pin_status(pin) && Beaglebone::get_pin_status(pin, :type) != :gpio Beaglebone::disable_pin(pin) end pullmode = pullmode || :PULLUP slewrate = slewrate || :FAST raise ArgumentError, "Invalid Slew Rate (#{slewrate}) specified on: #{pin}" unless SLEWRATES.include?(slewrate) raise ArgumentError, "Invalid Pull Mode (#{pullmode}) specified on: #{pin}" unless PULLMODES.include?(pullmode) if mode == :IN and pullmode != :PULLUP and ( pininfo[:mmc] or pin == :P9_15 ) raise ArgumentError, "Invalid Pull mode specified for pin: #{pin} (#{pullmode})" end #export pin unless its an on board LED, if it isn't already exported if pininfo[:led] raise StandardError, "LEDs only support OUT mode: #{pin.to_s}" unless mode == :OUT File.open("#{gpio_directory(pin)}/trigger", 'w') { |f| f.write('gpio') } else # if pin is not an onboard LED # create device tree overlay for this pin, do not force rebuild pin_data = create_device_tree(pin, mode, pullmode, slewrate, false) # unload previous dtb if loaded begin Beaglebone::device_tree_unload("#{TREES[:GPIO][:pin]}#{pin}_.*") rescue # end # load device tree overlay Beaglebone::device_tree_load("#{TREES[:GPIO][:pin]}#{pin}_0x#{pin_data.to_s(16)}") # export gpio pin begin File.open('/sys/class/gpio/export', 'w') { |f| f.write pininfo[:gpio] } rescue # end #check to see if pin is GPIO enabled in /sys/class/gpio/ raise StandardError, "GPIO was unable to initalize pin: #{pin.to_s}" unless enabled?(pin) end unless Beaglebone::get_pin_status(pin, :type) == :gpio #set pin mode unless pininfo[:led] set_gpio_mode(pin, mode) dir = read_gpio_direction(pin) raise StandardError, "GPIO was unable to set mode: #{pin.to_s} to #{mode.to_s} (#{dir})" if mode != dir end Beaglebone::set_pin_status(pin, :mode, mode) end
Runs a callback on an edge trigger event. This creates a new thread that runs in the background
@param callback A method to call when the edge trigger is detected. This method should take 3 arguments, the pin, the edge, and the counter @param pin should be a symbol representing the header pin, i.e. :P9_11 @param edge should be a symbol representing the trigger type, e.g. :RISING, :FALLING, :BOTH @param timeout is optional and specifies a time window to wait @param repeats is optional and specifies the number of times the callback will be run
@example
GPIO.run_on_edge(lambda { |pin,edge,count| puts "[#{count}] #{pin} -- #{edge}" }, :P9_11, :RISING)
# File lib/beaglebone/gpio.rb, line 190 def run_on_edge(callback, pin, edge, timeout = nil, repeats=nil) raise StandardError, "Already waiting for trigger on pin: #{pin}" if Beaglebone::get_pin_status(pin, :trigger) raise StandardError, "Already waiting for trigger on pin: #{pin}" if Beaglebone::get_pin_status(pin, :thread) thread = Thread.new(callback, pin, edge, timeout, repeats) do |c, p, e, t, r| begin count = 0 loop do state = wait_for_edge(p, e, t, false) c.call(p, state, count) if c count += 1 break if r && count >= r end rescue => ex puts ex puts ex.backtrace ensure cleanup_edge_trigger(p) end end Beaglebone::set_pin_status(pin, :thread, thread) end
Runs a callback one time on an edge trigger event. This is a convenience method for run_on_edge
@see run_on_edge
# File lib/beaglebone/gpio.rb, line 220 def run_once_on_edge(callback, pin, edge, timeout = nil) run_on_edge(callback, pin, edge, timeout, 1) end
Set GPIO
edge trigger type on an initialized pin
@param pin should be a symbol representing the header pin @param edge should be a symbol representing the trigger type, e.g. :RISING, :FALLING, :BOTH @param force is optional, if set will set the mode even if already set
@example
GPIO.set_gpio_edge(:P9_11, :RISING)
# File lib/beaglebone/gpio.rb, line 373 def set_gpio_edge(pin, edge, force=nil) check_valid_edge(edge) Beaglebone::check_valid_pin(pin, :gpio) raise StandardError, "PIN not in GPIO IN mode: #{pin}" unless get_gpio_mode(pin) == :IN return if get_gpio_edge(pin) == edge && !force File.open("#{gpio_directory(pin)}/edge", 'w') { |f| f.write edge.to_s.downcase } testedge = read_gpio_edge(pin) if testedge != edge.to_s.downcase Beaglebone::delete_pin_status(pin, :trigger) raise StandardError, "GPIO was unable to set edge: #{pin.to_s} to #{edge.to_s}" end if edge.to_sym == :NONE Beaglebone::delete_pin_status(pin, :trigger) else Beaglebone::set_pin_status(pin, :trigger, edge.to_sym) end end
Set GPIO
mode on an initialized pin
@param pin should be a symbol representing the header pin @param mode should specify the mode of the pin, either :IN or :OUT
@example
GPIO.set_gpio_mode(:P9_12, :OUT) GPIO.set_gpio_mode(:P9_11, :IN)
# File lib/beaglebone/gpio.rb, line 356 def set_gpio_mode(pin, mode) Beaglebone::check_valid_pin(pin, :gpio) check_valid_mode(mode) check_gpio_enabled(pin) File.open("#{gpio_directory(pin)}/direction", 'w') { |f| f.write mode.to_s.downcase } Beaglebone::set_pin_status(pin, :mode, mode) end
Sends data to a shift register
@param latch_pin should be a symbol representing the header pin, i.e. :P9_12 @param clock_pin should be a symbol representing the header pin, i.e. :P9_13 @param data_pin should be a symbol representing the header pin, i.e. :P9_14 @param data Integer value to write to the shift register @param lsb optional, send least significant bit first if set
@example
GPIO.shift_out(:P9_11, :P9_12, :P9_13, 255)
# File lib/beaglebone/gpio.rb, line 306 def shift_out(latch_pin, clock_pin, data_pin, data, lsb=nil) raise ArgumentError, "data must be > 0 (#{data})" if data < 0 digital_write(latch_pin, :LOW) binary = data.to_s(2) pad = 8 - ( binary.size % 8 ) binary = ( '0' * pad ) + binary if pad.between?(1,7) binary.reverse! if lsb binary.each_char do |bit| digital_write(clock_pin, :LOW) digital_write(data_pin, bit == '0' ? :LOW : :HIGH) digital_write(clock_pin, :HIGH) end digital_write(latch_pin, :HIGH) data end
Stops any threads waiting for data on specified pin
@param pin should be a symbol representing the header pin, i.e. :P9_11
# File lib/beaglebone/gpio.rb, line 227 def stop_edge_wait(pin) thread = Beaglebone::get_pin_status(pin, :thread) thread.exit if thread thread.join if thread end
Wait for an edge trigger. Returns the type that triggered the event, e.g. :RISING, :FALLING, :BOTH
@returns [Symbol] :RISING, :FALLING, or :BOTH
@param pin should be a symbol representing the header pin, i.e. :P9_11 @param edge should be a symbol representing the trigger type, e.g. :RISING, :FALLING, :BOTH @param timeout is optional and specifies a time window to wait @param disable is optional. If set, edge trigger detection is cleared on return
@example
wait_for_edge(:P9_11, :RISING, 30) => :RISING
# File lib/beaglebone/gpio.rb, line 246 def wait_for_edge(pin, edge, timeout = nil, disable=true) check_valid_edge(edge) raise ArgumentError, "Cannot wait for edge trigger NONE: #{pin}" if edge.to_sym == :NONE check_gpio_enabled(pin) raise StandardError, "PIN not in GPIO IN mode: #{pin}" unless get_gpio_mode(pin) == :IN #ensure we're the only ones waiting for this trigger if Beaglebone::get_pin_status(pin, :thread) && Beaglebone::get_pin_status(pin, :thread) != Thread.current raise StandardError, "Already waiting for trigger on pin: #{pin}" end if Beaglebone::get_pin_status(pin, :trigger) && Beaglebone::get_pin_status(pin, :thread) != Thread.current raise StandardError, "Already waiting for trigger on pin: #{pin}" end set_gpio_edge(pin, edge) fd = get_value_fd(pin) fd.read #select will return fd into the error set "es" if it recieves an interrupt _, _, es = IO.select(nil, nil, [fd], timeout) set_gpio_edge(pin, :NONE) if disable es ? digital_read(pin) : nil end
Private Class Methods
ensure gpio pin is enabled
# File lib/beaglebone/gpio.rb, line 539 def check_gpio_enabled(pin) Beaglebone::check_valid_pin(pin, :gpio) raise StandardError, "PIN not GPIO enabled: #{pin}" unless enabled?(pin) end
ensure edge type is valid
# File lib/beaglebone/gpio.rb, line 452 def check_valid_edge(edge) raise ArgumentError, "No such edge: #{edge.to_s}" unless EDGES.include?(edge) end
ensure mode is valid
# File lib/beaglebone/gpio.rb, line 532 def check_valid_mode(mode) #check to see if mode is valid mode = mode.to_sym raise ArgumentError, "No such mode: #{mode.to_s}" unless MODES.include?(mode) end
ensure state is valid
# File lib/beaglebone/gpio.rb, line 525 def check_valid_state(state) #check to see if mode is valid state = state.to_sym raise ArgumentError, "No such state: #{state.to_s}" unless STATES.include?(state) end
set edge trigger to none
# File lib/beaglebone/gpio.rb, line 472 def cleanup_edge_trigger(pin) if Beaglebone::get_pin_status(pin, :thread) == Thread.current set_gpio_edge(pin, :NONE) Beaglebone::delete_pin_status(pin, :thread) end end
close value fd if open
# File lib/beaglebone/gpio.rb, line 518 def close_value_fd(pin) fd = Beaglebone::get_pin_status(pin, :fd_value) fd.close if fd Beaglebone::delete_pin_status(pin, :fd_value) end
# File lib/beaglebone/gpio.rb, line 544 def create_device_tree(pin, mode, pullmode, slewrate, force = false) #get info from PINS hash pininfo = PINS[pin] #generate data value for dts #Bit 15 PIN USAGE is an indicator and should be a 1 if the pin is used or 0 if it is unused. # Bits 14-7 RESERVED is not to be used and left as 0. # Bit 6 SLEW CONTROL 0=Fast 1=Slow # Bit 5 RX Enabled 0=Disabled 1=Enabled # Bit 4 PU/PD 0=Pulldown 1=Pullup. # Bit 3 PULLUP/DN 0=Pullup/pulldown enabled 1= Pullup/pulldown disabled # Bit 2-0 MUX MODE SELECT Mode 0-7. (refer to TRM) pin_data = 0 pin_data |= 0b1000000 if slewrate == :SLOW pin_data |= 0b100000 if mode == :IN case pullmode when :PULLUP pin_data |= 0b10000 when :NONE pin_data |= 0b1000 else # default is pulldown enabled end #set mux mode, 7 is gpio pin_data |= 7 dts = GPIOTEMPLATE.clone dts.gsub!('!PIN_KEY!', pin.to_s) dts.gsub!('!PIN_DOT_KEY!', pin.to_s.gsub('_', '.')) dts.gsub!('!PIN_FUNCTION!', pininfo[:gpiofunc]) dts.gsub!('!PIN_OFFSET!', pininfo[:muxoffset]) dts.gsub!('!DATA!', "0x#{pin_data.to_s(16)}") filename = "/lib/firmware/#{TREES[:GPIO][:pin]}#{pin}_0x#{pin_data.to_s(16)}-00A0" dts_fn = "#{filename}.dts" dtb_fn = "#{filename}.dtbo" # if we've already built this file, we don't need to do it again return pin_data if File.exists?(dtb_fn) && !force dts_file = File.open(dts_fn, 'w') dts_file.write(dts) dts_file.close system("dtc -O dtb -o #{dtb_fn} -b 0 -@ #{dts_fn}") pin_data end
return the open value fd, or open if needed
# File lib/beaglebone/gpio.rb, line 499 def get_value_fd(pin) check_gpio_enabled(pin) fd = Beaglebone::get_pin_status(pin, :fd_value) return fd if fd pininfo = PINS[pin] #leds aren't normal gpio pins, we can toggle them on and off however. if pininfo[:led] fd = File.open("#{gpio_directory(pin)}/brightness", 'w+') else fd = File.open("#{gpio_directory(pin)}/value", 'w+') end Beaglebone::set_pin_status(pin, :fd_value, fd) end
convenience method for getting gpio dir in /sys
# File lib/beaglebone/gpio.rb, line 480 def gpio_directory(pin) raise StandardError, 'Invalid Pin' unless valid?(pin) #led's are in a special place if PINS[pin][:led] "/sys/class/leds/beaglebone:green:#{pin.to_s.downcase}" else #normal gpio pins "/sys/class/gpio/gpio#{PINS[pin][:gpio]}" end end
read gpio direction file
# File lib/beaglebone/gpio.rb, line 492 def read_gpio_direction(pin) check_gpio_enabled(pin) Beaglebone::set_pin_status(pin, :mode, File.open("#{gpio_directory(pin)}/direction", 'r').read.to_s.strip.to_sym.upcase) end
read gpio edge file
# File lib/beaglebone/gpio.rb, line 457 def read_gpio_edge(pin) check_gpio_enabled(pin) File.open("#{gpio_directory(pin)}/edge", 'r').read.to_s.strip end
check if pin is valid to use as gpio pin
# File lib/beaglebone/gpio.rb, line 463 def valid?(pin) #check to see if pin exists return false unless PINS[pin] return false unless PINS[pin][:gpio] true end