Class: Denko::Display::SSD1306

Inherits:
Object
  • Object
show all
Includes:
I2C::Peripheral
Defined in:
lib/denko/display/ssd1306.rb

Constant Summary collapse

PIXELS_FROM_RAM =

Single byte (no need to OR with anything)

0xA4
PIXELS_ALL_ON =
0xA5
INVERT_OFF =
0xA6
INVERT_ON =
0xA7
DISPLAY_OFF =
0xAE
DISPLAY_ON =
0xAF
CONTRAST =

Double byte (following byte sets value)

0x81
COLUMN_START_LOWER =

Single byte. OR with value. These are for page addressing mode only.

0x00
COLUMN_START_UPPER =

lower 4 bytes of column

0x10
PAGE_START =

upper 4 bytes of column

0xB0
ADDRESSING_MODE =

Double byte. Following byte sets value.

0x20
COLUMN_ADDRESS_RANGE =

Triple byte. Following 2 bytes sets value. For H/V addressing modes only.

0x21
PAGE_ADDRESS_RANGE =
0x22
START_LINE =

Single byte. OR with value.

0x40
SEGMENT_REMAP =

Value: lowest 6 bits set RAM start line (default 0b000000)

0xA0
COM_DIRECTION =

Value: 0x00 = default, 0x01 = column draw order reversed (horizontal reflection)

0xC0
CHARGE_PUMP =

Double-byte commands. Following byte sets value.

0x8D
MULTIPLEX_RATIO =

Value: 0x10 = disable/external, 0x14 = enable/internal

0xA8
DISPLAY_OFFSET =

Value: rows of display - 1

0xD3
COM_PIN_CONFIG =

Value: lowest 5 bits. Vertically shifts COM by that amount.

0xDA
CLOCK =

Double-byte commands. Following byte sets value.

0xD5
PRECHARGE_PERIOD =

Lowest 4 bits = divider. Upper 4 bits = oscillator frequency.

0xD9
VCOM_DESELECT_LEVEL =

Lowest 4 bits = phase 1. Upper 4 bits = phase 2. 0xF1 for internal charge pump. 0x22 for external.

0xDB
WIDTHS =

Valid widths and heights for displays

[64,96,128]
HEIGHTS =
[16,32,48,64]

Instance Attribute Summary collapse

Attributes included from I2C::Peripheral

#i2c_frequency, #i2c_repeated_start

Attributes included from Behaviors::Callbacks

#callback_mutex

Attributes included from Behaviors::BusPeripheral

#address

Attributes included from Behaviors::Component

#board

Instance Method Summary collapse

Methods included from I2C::Peripheral

#i2c_read, #i2c_write

Methods included from Behaviors::Reader

#_read, #read, #read_using, #wait_for_read

Methods included from Behaviors::Callbacks

#add_callback, #callbacks, #initialize, #pre_callback_filter, #remove_callback, #update

Methods included from Behaviors::State

#initialize, #state

Methods included from Behaviors::BusPeripheral

#atomically

Methods included from Behaviors::Component

#initialize, #micro_delay

Instance Attribute Details

#canvasObject

Returns the value of attribute canvas.



116
117
118
# File 'lib/denko/display/ssd1306.rb', line 116

def canvas
  @canvas
end

Instance Method Details

#after_initialize(options = {}) ⇒ Object

Raises:

  • (ArgumentError)


70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/denko/display/ssd1306.rb', line 70

def after_initialize(options={})
  super(options)

  # Default to a 128x64 display.
  @columns = options[:columns] || options[:width]  || 128
  @rows    = options[:rows]    || options[:height] || 64

  # Validate known sizes.
  raise ArgumentError, "error in SSD1306 width: #{@columns}. Must be in: #{WIDTHS.inspect}" unless WIDTHS.include?(@columns)
  raise ArgumentError, "error in SSD1306 height: #{@rows}. Must be in: #{HEIGHTS.inspect}" unless HEIGHTS.include?(@rows)

  # Everything except 96x16 size uses clock 0x80.
  clock = 0x80
  clock = 0x60 if (@columns == 96 && @rows == 16)

  # 128x32 and 96x16 sizes use com pin config 0x02
  com_pin_config = 0x12
  com_pin_config = 0x02 if (@columns == 96 && @rows == 16) || (@columns == 128 && @rows == 32)

  # Reflecting horizontally and vertically to effectively rotate 180 degrees.
  seg_remap     = options[:rotate] ? 0x01 : 0x00
  com_direction = options[:rotate] ? 0x08 : 0x00

  # Startup sequence
  command [
    MULTIPLEX_RATIO,        @rows - 1,
    DISPLAY_OFFSET,         0x00,
    START_LINE            | 0x00,
    SEGMENT_REMAP         | seg_remap,
    COM_DIRECTION         | com_direction,
    COM_PIN_CONFIG,         com_pin_config,
    PIXELS_FROM_RAM,
    INVERT_OFF,
    CLOCK,                  clock,
    VCOM_DESELECT_LEVEL,    0x20,
    PRECHARGE_PERIOD,       0xF1,           # Charge period for internal charge pump
    CHARGE_PUMP,            0x14,           # Internal charge pump
    ADDRESSING_MODE,        0x00,           # Horizontal addressing mode so pages auto increment
    DISPLAY_ON
  ]
  
  # Create a new blank canvas and draw it.
  self.canvas = Canvas.new(@columns, @rows)
  draw
end

#before_initialize(options = {}) ⇒ Object



64
65
66
67
68
# File 'lib/denko/display/ssd1306.rb', line 64

def before_initialize(options={})
  @i2c_address   = 0x3C
  @i2c_frequency = 400000
  super(options)
end

#command(bytes) ⇒ Object

Commands are I2C messages prefixed with 0x00.



169
170
171
# File 'lib/denko/display/ssd1306.rb', line 169

def command(bytes)
  i2c_write([0x00] + bytes)
end

#contrast=(value) ⇒ Object

Raises:

  • (ArgumentError)


126
127
128
129
# File 'lib/denko/display/ssd1306.rb', line 126

def contrast=(value)
  raise ArgumentError, "contrast must be in range 0..255" if (value < 0 || value > 255)
  command [CONTRAST, value]
end

#data(bytes) ⇒ Object

Data are I2C messages prefixed with 0x40.



174
175
176
# File 'lib/denko/display/ssd1306.rb', line 174

def data(bytes)
  i2c_write([0x40] + bytes)
end

#draw(x_min = 0, x_max = (@columns-1), y_min = 0, y_max = (@rows-1)) ⇒ Object



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/denko/display/ssd1306.rb', line 131

def draw(x_min=0, x_max=(@columns-1), y_min=0, y_max=(@rows-1))
  # Convert y-coords to page coords.
  p_min = y_min / 8
  p_max = y_max / 8
  
  # If drawing the whole frame (default), bypass temp buffer to save time.
  if (x_min == 0) && (x_max == @columns-1) && (p_min == 0) && (p_max == @rows/8)
    draw_partial(canvas.framebuffer, x_min, x_max, p_min, p_max)
    
  # Copy bytes for the given rectangle into a temp buffer. 
  else
    temp_buffer = []
    (p_min..p_max).each do |page|
      src_start = (@columns * page) + x_min
      src_end   = (@columns * page) + x_max
      temp_buffer += canvas.framebuffer[src_start..src_end]
    end
    
    # And draw them.
    draw_partial(temp_buffer, x_min, x_max, p_min, p_max)
  end
end

#draw_partial(buffer, x_min, x_max, p_min, p_max) ⇒ Object



154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/denko/display/ssd1306.rb', line 154

def draw_partial(buffer, x_min, x_max, p_min, p_max)
  # Limit auto-incrementing GRAM address to the rectangle being drawn.
  command [ COLUMN_ADDRESS_RANGE, x_min, x_max, PAGE_ADDRESS_RANGE, p_min, p_max ]
  
  # Send all the bytes at once if within board I2C limit.
  if buffer.length < (bus.board.i2c_limit - 1)
    data(buffer)
  
  # Or split into chunks.
  else  
    buffer.each_slice(bus.board.i2c_limit - 1) { |slice| data(slice) }
  end
end

#offObject



118
119
120
# File 'lib/denko/display/ssd1306.rb', line 118

def off
  command(DISPLAY_OFF)
end

#onObject



122
123
124
# File 'lib/denko/display/ssd1306.rb', line 122

def on
  command(DISPLAY_ON)
end