Class: LGPIO::I2CBitBang

Inherits:
Object
  • Object
show all
Defined in:
lib/lgpio/i2c_bitbang.rb

Constant Summary collapse

VALID_ADDRESSES =
(0x08..0x77).to_a

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ I2CBitBang

Returns a new instance of I2CBitBang.

Raises:

  • (ArgumentError)


7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/lgpio/i2c_bitbang.rb', line 7

def initialize(options={})
  scl  = options[:scl]
  sda  = options[:sda]

  @scl_handle = scl[:handle] if scl
  @scl_line   = scl[:line]   if scl
  raise ArgumentError, ":scl pin required as Hash, with :handle and :line required" unless (@scl_handle && @scl_line)

  @sda_handle = sda[:handle] if sda
  @sda_line   = sda[:line]   if sda
  raise ArgumentError, ":sda pin required as Hash, with :handle and :line required" unless (@sda_handle && @sda_line)

  @sda_state  = nil
  initialize_pins
end

Instance Attribute Details

#scl_handleObject (readonly)

Returns the value of attribute scl_handle.



5
6
7
# File 'lib/lgpio/i2c_bitbang.rb', line 5

def scl_handle
  @scl_handle
end

#scl_lineObject (readonly)

Returns the value of attribute scl_line.



5
6
7
# File 'lib/lgpio/i2c_bitbang.rb', line 5

def scl_line
  @scl_line
end

#sda_handleObject (readonly)

Returns the value of attribute sda_handle.



5
6
7
# File 'lib/lgpio/i2c_bitbang.rb', line 5

def sda_handle
  @sda_handle
end

#sda_lineObject (readonly)

Returns the value of attribute sda_line.



5
6
7
# File 'lib/lgpio/i2c_bitbang.rb', line 5

def sda_line
  @sda_line
end

Instance Method Details

#configObject



23
24
25
# File 'lib/lgpio/i2c_bitbang.rb', line 23

def config
  @config ||= [scl_handle, scl_line, sda_handle, scl_line]
end

#initialize_pinsObject



27
28
29
30
# File 'lib/lgpio/i2c_bitbang.rb', line 27

def initialize_pins
  LGPIO.gpio_claim_output(scl_handle, scl_line, LGPIO::SET_PULL_NONE, LGPIO::HIGH)
  LGPIO.gpio_claim_output(sda_handle, sda_line, LGPIO::SET_OPEN_DRAIN | LGPIO::SET_PULL_UP, LGPIO::HIGH)
end

#read(address, length) ⇒ Object

Raises:

  • (ArgumentError)


99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/lgpio/i2c_bitbang.rb', line 99

def read(address, length)
  raise ArgumentError, "invalid I2C address: #{address}. Range is 0x08..0x77" unless VALID_ADDRESSES.include?(address)
  raise ArgumentError, "invalid Integer for read length: #{length}" unless length.kind_of?(Integer)

  start
  ack = write_byte(read_form(address))
  return nil unless ack

  # Read length bytes, and ACK for all but the last one.
  bytes = []

  # Bit-bang per-byte in Ruby.
  # (length-1).times { bytes << read_byte(true) }
  # bytes << read_byte(false)

  # Bit-bang per-byte in C.
  (length-1).times { bytes << read_byte_c(true) }
  bytes << read_byte_c(false)

  stop
  bytes
end

#read_bitObject



56
57
58
59
60
61
62
# File 'lib/lgpio/i2c_bitbang.rb', line 56

def read_bit
  set_sda(1)
  LGPIO.gpio_write(scl_handle, scl_line, 1)
  bit = LGPIO.gpio_read(sda_handle, sda_line)
  LGPIO.gpio_write(scl_handle, scl_line, 0)
  bit
end

#read_byte(ack) ⇒ Object



70
71
72
73
74
75
76
77
78
79
# File 'lib/lgpio/i2c_bitbang.rb', line 70

def read_byte(ack)
  byte = 0
  i    = 0
  while i < 8
    byte = (byte << 1) | read_bit
    i = i + 1
  end
  write_bit(ack ? 0 : 1)
  byte
end

#read_byte_c(ack) ⇒ Object



91
92
93
# File 'lib/lgpio/i2c_bitbang.rb', line 91

def read_byte_c(ack)
  LGPIO.i2c_bb_read_byte(@scl_handle, @scl_line, @sda_handle, @sda_line, ack)
end

#read_form(address) ⇒ Object



41
42
43
# File 'lib/lgpio/i2c_bitbang.rb', line 41

def read_form(address)
  (address << 1) | 0b00000001
end

#searchObject



138
139
140
141
142
143
144
145
146
147
# File 'lib/lgpio/i2c_bitbang.rb', line 138

def search
  found = []
  VALID_ADDRESSES.each do |address|
    start
    # Device present if ACK received when we write its address to the bus.
    found << address if write_byte(write_form(address))
    stop
  end
  found
end

#set_sda(value) ⇒ Object



32
33
34
35
# File 'lib/lgpio/i2c_bitbang.rb', line 32

def set_sda(value)
  return if (@sda_state == value)
  LGPIO.gpio_write(sda_handle, sda_line, @sda_state = value)
end

#startObject



45
46
47
48
# File 'lib/lgpio/i2c_bitbang.rb', line 45

def start
  LGPIO.gpio_write(sda_handle, sda_line, 0)
  LGPIO.gpio_write(scl_handle, scl_line, 0)
end

#stopObject



50
51
52
53
54
# File 'lib/lgpio/i2c_bitbang.rb', line 50

def stop
  LGPIO.gpio_write(sda_handle, sda_line, 0)
  LGPIO.gpio_write(scl_handle, scl_line, 1)
  LGPIO.gpio_write(sda_handle, sda_line, 1)
end

#write(address, bytes) ⇒ Object

Raises:

  • (ArgumentError)


122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/lgpio/i2c_bitbang.rb', line 122

def write(address, bytes)
  raise ArgumentError, "invalid I2C address: #{address}. Range is 0x08..0x77" unless VALID_ADDRESSES.include?(address)
  raise ArgumentError, "invalid byte Array to write: #{bytes}" unless bytes.kind_of?(Array)

  start
  write_byte(write_form(address))

  # Bit-bang per-byte in Ruby.
  # bytes.each { |byte| write_byte(byte) }

  # Bit-bang per-byte in C.
  bytes.each { |byte| write_byte_c(byte) }

  stop
end

#write_bit(bit) ⇒ Object



64
65
66
67
68
# File 'lib/lgpio/i2c_bitbang.rb', line 64

def write_bit(bit)
  set_sda(bit)
  LGPIO.gpio_write(scl_handle, scl_line, 1)
  LGPIO.gpio_write(scl_handle, scl_line, 0)
end

#write_byte(byte) ⇒ Object



81
82
83
84
85
86
87
88
89
# File 'lib/lgpio/i2c_bitbang.rb', line 81

def write_byte(byte)
  i = 7
  while i >= 0
    write_bit (byte >> i) & 0b1
    i = i - 1
  end
  # Return ACK (SDA pulled low) or NACK (SDA stayed high).
  (read_bit == 0)
end

#write_byte_c(byte) ⇒ Object



95
96
97
# File 'lib/lgpio/i2c_bitbang.rb', line 95

def write_byte_c(byte)
  LGPIO.i2c_bb_write_byte(@scl_handle, @scl_line, @sda_handle, @sda_line, byte)
end

#write_form(address) ⇒ Object



37
38
39
# File 'lib/lgpio/i2c_bitbang.rb', line 37

def write_form(address)
  (address << 1)
end