summaryrefslogtreecommitdiffstats
path: root/ruby
diff options
context:
space:
mode:
authorJenna Fox <a@creativepony.com>2012-09-30 14:22:04 +1000
committerJenna Fox <a@creativepony.com>2012-09-30 14:22:04 +1000
commit4ab39b1d0cdcbac838d2d15acd532493a33a3764 (patch)
tree972c70655fd58638eb487048981e6e3b4154b3e7 /ruby
parentd919861396dd343bcc7132b6358b3a286228cc89 (diff)
downloadmicronucleus-4ab39b1d0cdcbac838d2d15acd532493a33a3764.tar.gz
micronucleus-4ab39b1d0cdcbac838d2d15acd532493a33a3764.tar.bz2
micronucleus-4ab39b1d0cdcbac838d2d15acd532493a33a3764.zip
cleaning up directory structures
Diffstat (limited to 'ruby')
-rw-r--r--ruby/dump.rb3
-rw-r--r--ruby/micronucleus.rb166
-rw-r--r--ruby/upload.rb33
3 files changed, 202 insertions, 0 deletions
diff --git a/ruby/dump.rb b/ruby/dump.rb
new file mode 100644
index 0000000..471d6d4
--- /dev/null
+++ b/ruby/dump.rb
@@ -0,0 +1,3 @@
+`rm temp-dump.bin`
+`avrdude -c usbtiny -p t85 -U flash:r:temp-dump.bin:r`
+puts open('temp-dump.bin').read(30).bytes.to_a.map { |x| x.to_s(16) }.join(' ')
diff --git a/ruby/micronucleus.rb b/ruby/micronucleus.rb
new file mode 100644
index 0000000..3211d84
--- /dev/null
+++ b/ruby/micronucleus.rb
@@ -0,0 +1,166 @@
+require 'libusb'
+
+# Abstracts access to micronucleus avr tiny85 bootloader - can be used only to erase and upload bytes
+class Micronucleus
+ Functions = [
+ :get_info,
+ :write_page,
+ :erase_application,
+ :run_program
+ ]
+
+ # return all micronucleus devices connected to computer
+ def self.all
+ usb = LIBUSB::Context.new
+ usb.devices.select { |device|
+ device.idVendor == 0x16d0 && device.idProduct == 0x0753
+ }.map { |device|
+ self.new(device)
+ }
+ end
+
+ def initialize devref
+ @device = devref
+ end
+
+ def info
+ unless @info
+ result = control_transfer(function: :get_info, dataIn: 4)
+ flash_length, page_size, write_sleep = result.unpack('S>CC')
+
+ @info = {
+ flash_length: flash_length,
+ page_size: page_size,
+ pages: (flash_length.to_f / page_size).ceil,
+ write_sleep: write_sleep.to_f / 1000.0,
+ version: "#{@device.bcdDevice >> 8}.#{@device.bcdDevice & 0xFF}",
+ version_numeric: @device.bcdDevice
+ }
+ end
+
+ @info
+ end
+
+ def erase!
+ puts "erasing"
+ info = self.info
+ control_transfer(function: :erase_application)
+ info[:pages].times do
+ sleep(info[:write_sleep]) # sleep for as many pages as the chip has to erase
+ end
+ puts "erased chip"
+ end
+
+ # upload a new program
+ def program= bytestring
+ info = self.info
+ raise "Program too long!" if bytestring.bytesize > info[:flash_length]
+ bytes = bytestring.bytes.to_a
+
+ erase!
+
+ address = 0
+ bytes.each_slice(info[:page_size]) do |slice|
+ slice.push(0xFF) while slice.length < info[:page_size] # ensure every slice is one page_size long - pad out if needed
+
+ puts "uploading @ #{address} of #{bytes.length}"
+ control_transfer(function: :write_page, wIndex: address, wValue: slice.length, dataOut: slice.pack('C*'))
+ sleep(info[:write_sleep])
+ address += slice.length
+ end
+ end
+
+ def finished
+ info = self.info
+
+ puts "asking device to finish writing"
+ control_transfer(function: :run_program)
+ puts "waiting for device to finish"
+
+ # sleep for as many pages as the chip could potentially need to write - this could be smarter
+ info[:pages].times do
+ sleep(info[:write_sleep])
+ end
+
+ @io.close
+ @io = nil
+ end
+
+ def inspect
+ "<MicroBoot #{info[:version]}: #{(info[:flash_length] / 1024.0).round(1)} kb programmable>"
+ end
+
+ protected
+ # raw opened device
+ def io
+ unless @io
+ @io = @device.open
+ end
+
+ @io
+ end
+
+ def control_transfer(opts = {})
+ opts[:bRequest] = Functions.index(opts.delete(:function)) if opts[:function]
+ io.control_transfer({
+ wIndex: 0,
+ wValue: 0,
+ bmRequestType: usb_request_type(opts),
+ timeout: 5000
+ }.merge opts)
+ end
+
+ # calculate usb request type
+ def usb_request_type opts #:nodoc:
+ value = LIBUSB::REQUEST_TYPE_VENDOR | LIBUSB::RECIPIENT_DEVICE
+ value |= LIBUSB::ENDPOINT_OUT if opts.has_key? :dataOut
+ value |= LIBUSB::ENDPOINT_IN if opts.has_key? :dataIn
+ return value
+ end
+end
+
+class HexProgram
+ def initialize input
+ @bytes = Hash.new(0xFF)
+ input = input.read if input.is_a? IO
+ parse input
+ end
+
+ def binary
+ highest_address = @bytes.keys.max
+
+ bytestring = Array.new(highest_address + 1) { |index|
+ @bytes[index]
+ }.pack('C*')
+ end
+
+ protected
+
+ def parse input_text
+ input_text.each_line do |line|
+ next unless line.start_with? ':'
+ line.chomp!
+ length = line[1..2].to_i(16) # usually 16 or 32
+ address = line[3..6].to_i(16) # 16-bit start address
+ record_type = line[7..8].to_i(16)
+ data = line[9.. 9 + (length * 2)]
+ checksum = line[9 + (length * 2).. 10 + (length * 2)].to_i(16)
+ checksum_section = line[1...9 + (length * 2)]
+
+ checksum_calculated = checksum_section.chars.to_a.each_slice(2).map { |slice|
+ slice.join('').to_i(16)
+ }.reduce(0, &:+)
+
+ checksum_calculated = (((checksum_calculated % 256) ^ 0xFF) + 1) % 256
+
+ raise "Hex file checksum mismatch @ #{line}" unless checksum == checksum_calculated
+
+ if record_type == 0 # data record
+ data_bytes = data.chars.each_slice(2).map { |slice| slice.join('').to_i(16) }
+ data_bytes.each_with_index do |byte, index|
+ @bytes[address + index] = byte
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/ruby/upload.rb b/ruby/upload.rb
new file mode 100644
index 0000000..32aefa9
--- /dev/null
+++ b/ruby/upload.rb
@@ -0,0 +1,33 @@
+require_relative './micronucleus'
+
+if ARGV[0]
+ if ARGV[0].end_with? '.hex'
+ puts "parsing input file as intel hex"
+ test_data = HexProgram.new(open ARGV[0]).binary
+ else
+ puts "parsing input file as raw binary"
+ test_data = open(ARGV[0]).read
+ end
+else
+ raise "Pass intel hex or raw binary as argument to script"
+end
+
+#test_data += ("\xFF" * 64)
+
+puts "Plug in programmable device now: (waiting)"
+sleep 0.25 while Micronucleus.all.length == 0
+
+nucleus = Micronucleus.all.first
+puts "Attached to device: #{nucleus.inspect}"
+
+#puts "Attempting to write '#{test_data.inspect}' to first thinklet's program memory"
+#puts "Bytes: #{test_data.bytes.to_a.inspect}"
+sleep(0.25) # some time to think?
+puts "Attempting to write supplied program in to device's memory"
+nucleus.program = test_data
+
+puts "Great! Starting program..."
+
+
+nucleus.finished # let thinklet know it can go do other things now if it likes
+puts "All done!"