barkest_lcd 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +112 -0
- data/Rakefile +12 -0
- data/barkest_lcd.gemspec +29 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/fonts/label.png +0 -0
- data/fonts/slkscr.ttf +0 -0
- data/fonts/slkscrb.ttf +0 -0
- data/lib/barkest_lcd.rb +10 -0
- data/lib/barkest_lcd/version.rb +3 -0
- data/lib/models/error_logger.rb +21 -0
- data/lib/models/font.rb +404 -0
- data/lib/models/font/silkscr.rb +1628 -0
- data/lib/models/font/silkscrb.rb +1627 -0
- data/lib/models/hash_enum.rb +91 -0
- data/lib/models/pico_lcd_graphic.rb +184 -0
- data/lib/models/pico_lcd_graphic/display.rb +97 -0
- data/lib/models/pico_lcd_graphic/enums.rb +107 -0
- data/lib/models/pico_lcd_graphic/flasher.rb +126 -0
- data/lib/models/pico_lcd_graphic/ir.rb +31 -0
- data/lib/models/pico_lcd_graphic/key.rb +92 -0
- data/lib/models/pico_lcd_graphic/splash.rb +40 -0
- data/lib/models/pico_lcd_graphic/version.rb +40 -0
- data/lib/models/simple_graphic.rb +467 -0
- metadata +128 -0
@@ -0,0 +1,91 @@
|
|
1
|
+
module BarkestLcd
|
2
|
+
##
|
3
|
+
# A simple enumeration class based on a hash of integers.
|
4
|
+
#
|
5
|
+
# enum = HashEnum.new({ :alpha => 0x01, :bravo => 0x02, :charlie => 0x04 })
|
6
|
+
# enum.alpha # 1
|
7
|
+
# enum.bravo # 2
|
8
|
+
# enum.CHARLIE # 4
|
9
|
+
# enum.alpha?(11) # true
|
10
|
+
# enum.BRAVO?(11) # true
|
11
|
+
# enum.charlie?(11) # false
|
12
|
+
#
|
13
|
+
class HashEnum
|
14
|
+
|
15
|
+
##
|
16
|
+
# Turns a hash into an enumeration.
|
17
|
+
def initialize(hash = {})
|
18
|
+
@hash = hash.inject({}){ |memo,(k,v)| memo[k.to_sym] = v.to_i; memo }.freeze
|
19
|
+
# values ordered highest to lowest.
|
20
|
+
@flags = @hash.to_a.sort{|a,b| b[1] <=> a[1]}
|
21
|
+
|
22
|
+
freeze
|
23
|
+
end
|
24
|
+
|
25
|
+
# :nodoc:
|
26
|
+
def method_missing(meth, *args, &block)
|
27
|
+
|
28
|
+
if @hash.respond_to?(meth)
|
29
|
+
return @hash.send(meth, *args, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
[ meth.to_s.downcase.to_sym, meth.to_s.upcase.to_sym ].each do |test_meth|
|
33
|
+
if @hash.keys.include?(test_meth)
|
34
|
+
return @hash[test_meth]
|
35
|
+
else
|
36
|
+
meth_name = test_meth.to_s
|
37
|
+
is_flag = meth_name[-1] == '?'
|
38
|
+
if is_flag
|
39
|
+
test_meth = meth_name[0..-2].to_sym
|
40
|
+
if @hash.keys.include?(test_meth)
|
41
|
+
valid = @hash[test_meth]
|
42
|
+
test = args && args.count > 0 ? args.first : nil
|
43
|
+
raise ArgumentError, 'Missing value to test.' unless test
|
44
|
+
raise ArgumentError, 'Test value must be an integer.' unless test.is_a?(Fixnum)
|
45
|
+
return (test & valid) == valid
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
super meth, *args, &block
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Determines what keys the value contains.
|
56
|
+
#
|
57
|
+
# If a remainder exists, then the remainder is added at the end of the returned array.
|
58
|
+
def flags(value)
|
59
|
+
ret = []
|
60
|
+
@flags.each do |(k,v)|
|
61
|
+
if (value & v) == v
|
62
|
+
value -= v
|
63
|
+
ret << k
|
64
|
+
end
|
65
|
+
end
|
66
|
+
ret << value if value != 0
|
67
|
+
ret
|
68
|
+
end
|
69
|
+
|
70
|
+
# :nodoc:
|
71
|
+
def inspect
|
72
|
+
@hash.inspect
|
73
|
+
end
|
74
|
+
|
75
|
+
# :nodoc:
|
76
|
+
def to_s
|
77
|
+
@hash.to_s
|
78
|
+
end
|
79
|
+
|
80
|
+
# :nodoc:
|
81
|
+
def ==(other)
|
82
|
+
@hash == other
|
83
|
+
end
|
84
|
+
|
85
|
+
# :nodoc:
|
86
|
+
def eql?(other)
|
87
|
+
@hash.eql?(other)
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
|
2
|
+
require 'models/error_logger'
|
3
|
+
|
4
|
+
module BarkestLcd
|
5
|
+
##
|
6
|
+
# A class to interface with the picoLCD 256x64 (aka picoLCD Graphic) from [www.mini-box.com](http://www.mini-box.com).
|
7
|
+
#
|
8
|
+
#
|
9
|
+
class PicoLcdGraphic < ::HIDAPI::Device
|
10
|
+
|
11
|
+
include BarkestLcd::ErrorLogger
|
12
|
+
|
13
|
+
##
|
14
|
+
# Any of the Pico LCD errors.
|
15
|
+
PicoLcdError = Class.new(StandardError)
|
16
|
+
|
17
|
+
##
|
18
|
+
# The device has already been opened.
|
19
|
+
AlreadyOpen = Class.new(PicoLcdError)
|
20
|
+
|
21
|
+
##
|
22
|
+
# The device is not currently open.
|
23
|
+
NotOpen = Class.new(PicoLcdError)
|
24
|
+
|
25
|
+
##
|
26
|
+
# The device failed to open (no address returned).
|
27
|
+
OpenFailed = Class.new(PicoLcdError)
|
28
|
+
|
29
|
+
##
|
30
|
+
# The operation has timed out.
|
31
|
+
Timeout = Class.new(PicoLcdError)
|
32
|
+
|
33
|
+
##
|
34
|
+
# USB Vendor ID
|
35
|
+
VENDOR_ID = 0x04d8
|
36
|
+
|
37
|
+
##
|
38
|
+
# USB Device ID
|
39
|
+
DEVICE_ID = 0xc002
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
##
|
44
|
+
# Enumerates the picoLCD devices attached to the system.
|
45
|
+
def self.devices(refresh = false)
|
46
|
+
@devices = nil if refresh
|
47
|
+
@devices ||= HIDAPI.enumerate(VENDOR_ID, DEVICE_ID, as: 'BarkestLcd::PicoLcdGraphic')
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Resets the device.
|
52
|
+
def reset
|
53
|
+
write [ OUT_REPORT.LCD_RESET, 0x01, 0x00 ]
|
54
|
+
|
55
|
+
(0...4).each do |csi|
|
56
|
+
cs = (csi << 2) & 0xFF
|
57
|
+
write [ OUT_REPORT.CMD, cs, 0x02, 0x00, 0x64, 0x3F, 0x00, 0x64, 0xC0 ]
|
58
|
+
end
|
59
|
+
|
60
|
+
reset_hook.each { |hook| hook.call(self) }
|
61
|
+
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
##
|
67
|
+
# Processes any waiting input from the device and paints the screen if it is dirty.
|
68
|
+
def loop
|
69
|
+
data = read
|
70
|
+
if data && data.length > 0
|
71
|
+
|
72
|
+
type = data.getbyte(0)
|
73
|
+
data = data[1..-1]
|
74
|
+
|
75
|
+
input_hook(type).call(self, type, data)
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
loop_hook.each { |hook| hook.call(self) }
|
80
|
+
|
81
|
+
self
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
# :nodoc:
|
87
|
+
def self.method_missing(meth, *args, &block)
|
88
|
+
# pass through methods like 'first', 'count', 'each', etc to the 'devices' list.
|
89
|
+
list = devices
|
90
|
+
if list.respond_to?(meth)
|
91
|
+
list.send meth, *args, &block
|
92
|
+
else
|
93
|
+
super meth, *args, &block
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
protected
|
100
|
+
|
101
|
+
|
102
|
+
##
|
103
|
+
# Hooks a block to run for a specific incoming type.
|
104
|
+
#
|
105
|
+
# Yields the device instance, type code, and the data to the block.
|
106
|
+
def input_hook(incoming_type, method_name = nil, &block)
|
107
|
+
@input_hook ||= {}
|
108
|
+
|
109
|
+
if block_given?
|
110
|
+
# set the hook.
|
111
|
+
@input_hook[incoming_type] = block
|
112
|
+
elsif method_name
|
113
|
+
@input_hook[incoming_type] = Proc.new { |dev, type, data| dev.send(method_name, dev, type, data) }
|
114
|
+
else
|
115
|
+
# get the hook
|
116
|
+
@input_hook[incoming_type] ||= Proc.new { |dev, type, data| HIDAPI.debug "no input hook for #{type} message type with #{data.length} bytes of data" }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
##
|
122
|
+
# Hooks a block to run during the loop method.
|
123
|
+
#
|
124
|
+
# Yields the device instance.
|
125
|
+
def loop_hook(method_name = nil, &block)
|
126
|
+
@loop_hook ||= []
|
127
|
+
if block_given?
|
128
|
+
@loop_hook << block
|
129
|
+
elsif method_name
|
130
|
+
@loop_hook << Proc.new { |dev| dev.send(method_name, dev) }
|
131
|
+
end
|
132
|
+
@loop_hook
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
##
|
137
|
+
# Hooks a block to run during the reset method.
|
138
|
+
#
|
139
|
+
# Yields the device instance.
|
140
|
+
def reset_hook(method_name = nil, &block)
|
141
|
+
@reset_hook ||= []
|
142
|
+
if block_given?
|
143
|
+
@reset_hook << block
|
144
|
+
elsif method_name
|
145
|
+
@reset_hook << Proc.new { |dev| dev.send(method_name, dev) }
|
146
|
+
end
|
147
|
+
@reset_hook
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
##
|
152
|
+
# Loops while the block returns true and the timeout hasn't expired.
|
153
|
+
def loop_while(options={}, &block)
|
154
|
+
timeout = options.delete(:timeout) || 2500
|
155
|
+
timeout = 10 if timeout < 10
|
156
|
+
|
157
|
+
result = block_given? ? block.call : true
|
158
|
+
while result
|
159
|
+
sleep 0.01
|
160
|
+
result = block_given? ? block.call : true
|
161
|
+
unless result
|
162
|
+
timeout -= 10
|
163
|
+
raise BarkestLcd::PicoLcdGraphic::Timeout if timeout < 0
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
true
|
168
|
+
end
|
169
|
+
|
170
|
+
# Turn off blocking for the PicoLcdGraphic.
|
171
|
+
# If the user decides, they can turn blocking back on.
|
172
|
+
init_hook do |dev|
|
173
|
+
dev.blocking = false
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# Include the components of the model.
|
182
|
+
Dir.glob(File.expand_path('../pico_lcd_graphic/*.rb', __FILE__)).each do |file|
|
183
|
+
require file
|
184
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'models/simple_graphic'
|
2
|
+
|
3
|
+
BarkestLcd::PicoLcdGraphic.class_eval do
|
4
|
+
include BarkestLcd::SimpleGraphic
|
5
|
+
|
6
|
+
init_hook :init_display
|
7
|
+
|
8
|
+
|
9
|
+
##
|
10
|
+
# Width of the screen in pixels.
|
11
|
+
SCREEN_W = 256
|
12
|
+
|
13
|
+
##
|
14
|
+
# Height of the screen in pixels.
|
15
|
+
SCREEN_H = 64
|
16
|
+
|
17
|
+
##
|
18
|
+
# Default contrast for the screen.
|
19
|
+
DEFAULT_CONTRAST = 0xE5
|
20
|
+
|
21
|
+
##
|
22
|
+
# Default brightness for the screen.
|
23
|
+
DEFAULT_BACKLIGHT = 0x7F
|
24
|
+
|
25
|
+
|
26
|
+
##
|
27
|
+
# Sets the backlight level.
|
28
|
+
def backlight(level = 0xFF)
|
29
|
+
level &= 0xFF
|
30
|
+
write [ OUT_REPORT.LCD_BACKLIGHT, level ]
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
##
|
36
|
+
# Sets the contrast level.
|
37
|
+
def contrast(level = 0xFF)
|
38
|
+
level &= 0xFF
|
39
|
+
write [ OUT_REPORT.LCD_CONTRAST, level ]
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
##
|
45
|
+
# Sends the screen contents to the device.
|
46
|
+
def paint(force = false)
|
47
|
+
if dirty? || force
|
48
|
+
# 4 chips each holding 64x64 of data.
|
49
|
+
(0..3).each do |csi|
|
50
|
+
cs = (csi << 2)
|
51
|
+
# each memory line holds 64 bytes, or 8 rows of data.
|
52
|
+
(0..7).each do |line|
|
53
|
+
# use dirty rectangles to avoid sending unnecessary data to the device.
|
54
|
+
if force || dirty_rect?(csi * 64, line * 8, 64, 8)
|
55
|
+
# send the data in two packets for each memory line.
|
56
|
+
packet_1 = [ OUT_REPORT.CMD_DATA, cs, 0x02, 0x00, 0x00, 0xb8 | line, 0x00, 0x00, 0x40, 0x00, 0x00, 32 ]
|
57
|
+
packet_2 = [ OUT_REPORT.DATA, cs | 0x01, 0x00, 0x00, 32 ]
|
58
|
+
|
59
|
+
(0..63).each do |index|
|
60
|
+
# each byte holds the data for 8 rows.
|
61
|
+
byte = 0x00
|
62
|
+
(0..7).each do |bit|
|
63
|
+
x = (csi * 64) + index
|
64
|
+
y = ((line * 8) + bit) % height
|
65
|
+
|
66
|
+
byte |= (1 << bit) if get_bit(x, y)
|
67
|
+
end
|
68
|
+
|
69
|
+
# add the byte to the correct packet.
|
70
|
+
(index < 32 ? packet_1 : packet_2) << byte
|
71
|
+
end
|
72
|
+
# send the packets.
|
73
|
+
write packet_1
|
74
|
+
write packet_2
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
clear_dirty
|
79
|
+
end
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def init_display(_)
|
87
|
+
init_graphic SCREEN_W, SCREEN_H
|
88
|
+
loop_hook { |_| paint }
|
89
|
+
reset_hook do |_|
|
90
|
+
clear.paint
|
91
|
+
contrast DEFAULT_CONTRAST
|
92
|
+
backlight DEFAULT_BACKLIGHT
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'models/hash_enum'
|
2
|
+
|
3
|
+
BarkestLcd::PicoLcdGraphic.class_eval do
|
4
|
+
|
5
|
+
##
|
6
|
+
# Status codes that may be returned from the device.
|
7
|
+
STATUS = BarkestLcd::HashEnum.new(
|
8
|
+
OK: 0x00,
|
9
|
+
ERASE: 0x01,
|
10
|
+
WRITE: 0x02,
|
11
|
+
READ: 0x03,
|
12
|
+
ERROR: 0xFF,
|
13
|
+
KEY: 0x10,
|
14
|
+
IR: 0x11,
|
15
|
+
VER: 0x12,
|
16
|
+
DISCONNECTED: 0x13,
|
17
|
+
)
|
18
|
+
|
19
|
+
##
|
20
|
+
# Reports received from the device.
|
21
|
+
IN_REPORT = BarkestLcd::HashEnum.new(
|
22
|
+
POWER_STATE: 0x01,
|
23
|
+
KEY_STATE: 0x11,
|
24
|
+
IR_DATA: 0x21,
|
25
|
+
EXT_EE_DATA: 0x31,
|
26
|
+
INT_EE_DATA: 0x32,
|
27
|
+
)
|
28
|
+
|
29
|
+
##
|
30
|
+
# Reports sent to the device.
|
31
|
+
OUT_REPORT = BarkestLcd::HashEnum.new(
|
32
|
+
LED_STATE: 0x81,
|
33
|
+
LCD_BACKLIGHT: 0x91,
|
34
|
+
LCD_CONTRAST: 0x92,
|
35
|
+
CMD: 0x94,
|
36
|
+
DATA: 0x95,
|
37
|
+
CMD_DATA: 0x96,
|
38
|
+
LCD_RESET: 0x93,
|
39
|
+
RELAY_ONOFF: 0xB1,
|
40
|
+
TESTSPLASH: 0xC1,
|
41
|
+
EXT_EE_READ: 0xA1,
|
42
|
+
EXT_EE_WRITE: 0xA2,
|
43
|
+
INT_EE_READ: 0xA3,
|
44
|
+
INT_EE_WRITE: 0xA4,
|
45
|
+
)
|
46
|
+
|
47
|
+
##
|
48
|
+
# Splash IDs.
|
49
|
+
ID_SPLASH = BarkestLcd::HashEnum.new(
|
50
|
+
TIMER: 0x72,
|
51
|
+
CYCLE_START: 0x72,
|
52
|
+
CYCLE_END: 0x73,
|
53
|
+
)
|
54
|
+
|
55
|
+
|
56
|
+
##
|
57
|
+
# Types for flash operations.
|
58
|
+
FLASH_TYPE = BarkestLcd::HashEnum.new(
|
59
|
+
CODE_MEMORY: 0x00,
|
60
|
+
EPROM_EXTERNAL: 0x01,
|
61
|
+
EPROM_INTERNAL: 0x02,
|
62
|
+
CODE_SPLASH: 0x03,
|
63
|
+
)
|
64
|
+
|
65
|
+
##
|
66
|
+
# HID reports for device.
|
67
|
+
HID_REPORT = BarkestLcd::HashEnum.new(
|
68
|
+
GET_VERSION_1: 0xF1,
|
69
|
+
GET_VERSION_2: 0xF7,
|
70
|
+
GET_MAX_STX_SIZE: 0xF6,
|
71
|
+
EXIT_FLASHER: 0xFF,
|
72
|
+
EXIT_KEYBOARD: 0xEF,
|
73
|
+
SET_SNOOZE_TIME: 0xF8,
|
74
|
+
ERROR: 0x10,
|
75
|
+
)
|
76
|
+
|
77
|
+
##
|
78
|
+
# Flash reports for the device.
|
79
|
+
FLASH_REPORT = BarkestLcd::HashEnum.new(
|
80
|
+
ERASE_MEMORY: 0xF2,
|
81
|
+
READ_MEMORY: 0xF3,
|
82
|
+
WRITE_MEMORY: 0xF4,
|
83
|
+
)
|
84
|
+
|
85
|
+
##
|
86
|
+
# Keyboard reports for the device.
|
87
|
+
KEYBD_REPORT = BarkestLcd::HashEnum.new(
|
88
|
+
ERASE_MEMORY: 0xB2,
|
89
|
+
READ_MEMORY: 0xB3,
|
90
|
+
WRITE_MEMORY: 0xB4,
|
91
|
+
MEMORY: 0x41,
|
92
|
+
)
|
93
|
+
|
94
|
+
##
|
95
|
+
# Request results.
|
96
|
+
RESULT = BarkestLcd::HashEnum.new(
|
97
|
+
OK: 0x00,
|
98
|
+
PARAM_MISSING: 0x01,
|
99
|
+
DATA_MISSING: 0x02,
|
100
|
+
BLOCK_READ_ONLY: 0x03,
|
101
|
+
BLOCK_NOT_ERASABLE: 0x04,
|
102
|
+
BLOCK_TOO_BIG: 0x05,
|
103
|
+
SECTION_OVERFLOW: 0x06,
|
104
|
+
|
105
|
+
)
|
106
|
+
|
107
|
+
end
|