Skip to content

Commit

Permalink
First pass at I2C bit bang implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
vickash committed Aug 11, 2024
1 parent 225d881 commit 35d6723
Show file tree
Hide file tree
Showing 11 changed files with 352 additions and 2 deletions.
2 changes: 1 addition & 1 deletion HARDWARE.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
| Servo/ESC PWM | :green_heart: | H | See Motor table | Uses PWM
| Tone Out (Square Wave)| :green_heart: | H | `PulseIO::Buzzer` | Except SAM3X. Uses PWM
| I2C | :green_heart: | H | `I2C::Bus` | Predetermined pins from IDE
| I2C Bit Bang | :heart: | S | `I2C::BitBang` | Any pins
| I2C Bit Bang | :yellow_heart: | S | `I2C::BitBang` | Any pins. No search yet. Maybe timing issues?
| SPI | :green_heart: | H | `SPI::Bus` | Predetermined pins from IDE
| SPI Bit Bang | :green_heart: | S | `SPI::BitBang` | Any pins
| UART | :green_heart: | H | `UART::Hardware` | Except Atmega328, ATmega168
Expand Down
5 changes: 5 additions & 0 deletions examples/display/ssd1306.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@
# Only give the SDA pin of the I2C bus. SCL (clock) pin must be
# connected for it to work, but we don't need to control it.
#

# Board's hardware I2C interface on predetermined pins.
bus = Denko::I2C::Bus.new(board: board, pin: :SDA)
# Bit-banged I2C on any pins.
# bus = Denko::I2C::BitBang.new(board: board, pins: {scl: 8, sda: 9})

oled = Denko::Display::SSD1306.new(bus: bus, rotate: true)
canvas = oled.canvas

Expand Down
7 changes: 6 additions & 1 deletion examples/sensor/aht10.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
#
# Example using AHT21 sensor over I2C, for temperature and humidity.
# Example using AHT10 sensor over I2C, for temperature and humidity.
#
require 'bundler/setup'
require 'denko'

board = Denko::Board.new(Denko::Connection::Serial.new)

# Board's hardware I2C interface on predetermined pins.
bus = Denko::I2C::Bus.new(board: board, pin: :SDA)
# Bit-banged I2C on any pins.
# bus = Denko::I2C::BitBang.new(board: board, pins: {scl: 8, sda: 9})

sensor = Denko::Sensor::AHT10.new(bus: bus) # address: 0x38 default

# Get the shared #print_tph_reading method to print readings neatly.
Expand Down
42 changes: 42 additions & 0 deletions lib/denko/board/i2c_bit_bang.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
module Denko
class Board
# CMD = 31
def i2c_bb_write(scl, sda, address, bytes, repeated_start=false)
bytes = [bytes] unless bytes.class == Array

# Use top bit of address to select stop condition (1), or repated start (0).
send_stop = repeated_start ? 0 : 1

write Message.encode command: 31,
pin: scl,
value: sda,
aux_message: pack(:uint8, 0x00) +
pack(:uint8, address | (send_stop << 7)) +
pack(:uint8, bytes.length) +
pack(:uint8, bytes)
end

# CMD = 35
def i2c_bb_read(scl, sda, address, register, read_length, repeated_start=false)
# Use top bit of address to select stop condition (1), or repated start (0).
send_stop = repeated_start ? 0 : 1

# A register address starting register address can be given (up to 4 bytes)
if register
register = [register].flatten
raise ArgumentError, 'maximum 4 byte register address for I2C read' if register.length > 4
register_packed = pack(:uint8, [register.length] + register)
else
register_packed = pack(:uint8, [0])
end

write Message.encode command: 32,
pin: scl,
value: sda,
aux_message: pack(:uint8, 0x00) +
pack(:uint8, address | (send_stop << 7)) +
pack(:uint8, read_length) +
register_packed
end
end
end
1 change: 1 addition & 0 deletions lib/denko/i2c.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module Denko
module I2C
autoload :Bus, "#{__dir__}/i2c/bus"
autoload :BitBang, "#{__dir__}/i2c/bit_bang"
autoload :Peripheral, "#{__dir__}/i2c/peripheral"
end
end
59 changes: 59 additions & 0 deletions lib/denko/i2c/bit_bang.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
module Denko
module I2C
class BitBang
include Behaviors::MultiPin
include Behaviors::BusControllerAddressed
include Behaviors::Reader

def initialize_pins(options={})
require_pins :scl, :sda
end

attr_reader :found_devices

def after_initialize(options={})
super(options)
@found_devices = []

# Board will see we respond to #pin with pins[:sda], and provide updates
unregister
register
bubble_callbacks
end

# Receive data coming from the SDA pin.
def pin
pins[:sda]
end

# def search
# addresses = read_using -> { board.i2c_search }
# @found_devices = addresses.split(":").map(&:to_i) if addresses
# end

def write(address, bytes, frequency=nil, repeated_start=false)
board.i2c_bb_write(pins[:scl], pins[:sda], address, bytes, repeated_start)
end

def _read(address, register, num_bytes, frequency=nil, repeated_start=false)
board.i2c_bb_read(pins[:scl], pins[:sda], address, register, num_bytes, repeated_start)
end

def bubble_callbacks
add_callback(:bus_controller) do |str|
if str && str.match(/\A\d+-/)
address, data = str.split("-", 2)
address = address.to_i

data = data.split(",").map(&:to_i)
data = nil if data.empty?

components.each do |component|
component.update(data) if component.address == address
end
end
end
end
end
end
end
7 changes: 7 additions & 0 deletions lib/denko_cli/packages.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ class DenkoCLI::Generator
"DenkoOneWire.cpp",
]
},
i2c_bb: {
description: "Bit Bang I2C support",
directive: "DENKO_I2C_BB",
files: [
"DenkoI2CBB.cpp",
]
},
spi_bb: {
description: "Bit Bang SPI support",
directive: "DENKO_SPI_BB",
Expand Down
6 changes: 6 additions & 0 deletions src/lib/Denko.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,12 @@ void Denko::process() {
case 28: spiRemoveListener(); break;
#endif

// Implemented in DenkoI2CBB.cpp
#ifdef DENKO_I2C_BB
case 31: i2c_bb_write (); break;
case 32: i2c_bb_read (); break;
#endif

// Implemented in DenkoI2C.cpp
#ifdef DENKO_I2C
case 33: i2cSearch (); break;
Expand Down
26 changes: 26 additions & 0 deletions src/lib/Denko.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,4 +246,30 @@ class Denko {
};
SpiListener spiListeners[SPI_LISTENER_COUNT];
#endif

// I2C Bit Bang Interface
#if defined(DENKO_I2C_BB)
uint8_t i2c_bb_scl_pin;
uint8_t i2c_bb_sda_pin;
uint8_t i2c_bb_quarter_period = 1;

// Internal stuff
void i2c_bb_delay_quarter_period();
void i2c_bb_delay_half_period();
void i2c_bb_sda_high ();
void i2c_bb_sda_low ();
void i2c_bb_scl_high ();
void i2c_bb_scl_low ();
void i2c_bb_start ();
void i2c_bb_stop ();
uint8_t i2c_bb_read_bit ();
void i2c_bb_write_bit (uint8_t bit );
uint8_t i2c_bb_read_byte (bool back);
int i2c_bb_write_byte (uint8_t data);
void i2c_bb_init (uint8_t scl, uint8_t sda);

// Remote exposed interface
void i2c_bb_write ();
void i2c_bb_read ();
#endif
};
1 change: 1 addition & 0 deletions src/lib/DenkoDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// #define DENKO_EEPROM
// #define DENKO_ONE_WIRE
// #define DENKO_TONE
// #define DENKO_I2C_BB
// #define DENKO_SPI_BB
// #define DENKO_I2C
// #define DENKO_SPI
Expand Down
Loading

0 comments on commit 35d6723

Please sign in to comment.