class Stm32

Public Class Methods

new(hash={}) click to toggle source
# File lib/stm32.rb, line 46
def initialize(hash={})
  @clist=@@Clist_def
  @debug=hash[:debug]
  if not hash[:dev]
    puts "Error: No serial Device??"
    return nil
  end
  if not File.chardev? hash[:dev]
    puts "Error: '#{hash[:dev]}'' is not serial Device??"
    return nil
  end
  begin
    @port = SerialPort.new hash[:dev],115200,8,1,SerialPort::NONE
    #$sp.read_timeout = 100
    @port.flow_control= SerialPort::NONE
    @port.binmode
    @port.sync = true
  rescue => e
    puts "Error: Cannot open serial device: #{e}"
    pp e.backtrace
    return nil
  end
  @dev=hash[:dev]
  puts "Open Serial OK!" if @debug
end

Public Instance Methods

boot() click to toggle source
# File lib/stm32.rb, line 180
def boot
  retries=0
  delay=0.001
  while retries<10
    #$sp.rts=1 #if retries>5 #power off --really cold boot
    #sleep 0.5
    @port.rts=0 #if retries>5 #power off --really cold boot
    @port.dtr=0
    sleep delay
    @port.flush_input
    @port.flush_output
    @port.rts=0 #power on
    sleep delay
    @port.dtr=1 #reset up -> start to run
    ch=wait_char delay

    if ch and ch==0
      printf("OK: [%02x]", ch )  if @debug
      sleep delay
    end
    send_buf [0x7f]
    if ch=wait_char
      if ch==0x79
        puts "Booted OK, retries=#{retries}\n"   if @debug
        @state=:booted
        return true
      else
        printf "Error:got strange ack: %02X '%c'\n",ch,ch
      end
    else
      puts "Error: no cmd ack"
    end
    retries+=1
    delay*=2
  end
  puts "Error:not booted, gave up\n"
  return false
end
cmd(c) click to toggle source
# File lib/stm32.rb, line 232
def cmd c
  boot if @state!=:booted
  puts "cmd: #{c}"  if @debug
  send_cmd c
  if c==:write #no reply expected
    return
  end
  if len=wait_char
    len+=1
    if buf=wait_chars(len)
      if @debug
        printf "len=#{len}:"
        buf.each do |b|
          printf "%02X ",b
        end
        printf "\n"
      end
      if ack=wait_char
        if ack==0x79
          return buf
        else
          puts "Error: no ack for cmd #{c} #{ack}"
        end
      else
        puts "Error: tout at ack for #{c}"
      end
    else
      puts "Error: timeout for #{c}"
    end
  end
end
erase(blocks) click to toggle source
# File lib/stm32.rb, line 334
def erase blocks
  return if not blocks or blocks==[]
  list=[blocks.length-1].pack("n").unpack("cc")
  blocks.each do |b|
    list+=[b].pack("n").unpack("cc")
  end
  if send_cmd(:erase)
    if ack=send_buf_with_check(list,3)
      #puts "Erase #{list.size} Pages Result: #{ack}"
      return ack
    end
  end
  return nil
end
flash(fn) click to toggle source
# File lib/stm32.rb, line 383
def flash fn
  s=Srec.new file: fn
  bsize=get_cpu(:flash_bsize)
  fs=get_cpu(:flash_s)
  fe=get_cpu(:flash_e)
  b= s.to_blocks fs,fe,bsize
  puts "#{b.size} blocks of #{bsize} bytes"
  list=[]
  b.each do |blk,data|
    list << blk
  end
  start=Time.now.to_i
  if erase list
    dur=Time.now.to_i-start
    puts "Erased in #{dur}s"
    cnt=0
    start=Time.now.to_i
    b.each do |blk,data|
      addr=blk*bsize+fs
      if write addr,data
        printf("\r#{cnt}/#{b.length} %.0f%%",100.0*cnt/b.length) if cnt%10==0
      else
        puts "Error: Write fails at #{addr}"
        break
      end
      cnt+=1
    end
    dur=Time.now.to_i-start
    puts "\nFlashed in #{dur}s"
  else
    puts "Error: Erase failed"
  end
end
flush_chars(tout=0.1) click to toggle source
# File lib/stm32.rb, line 84
def flush_chars tout=0.1
  while ch=wait_char(tout) do
    puts "\nWarning: Flushed #{ch.to_s(16)}\n"
  end
end
get_cpu(k) click to toggle source
# File lib/stm32.rb, line 80
def get_cpu(k)
  @cpu_info[k]
end
get_dev() click to toggle source
# File lib/stm32.rb, line 74
def get_dev()
  @dev
end
get_info() click to toggle source
# File lib/stm32.rb, line 265
def get_info
  if buf=cmd(:get)
    puts "BL ver: #{buf[0].to_s(16)}"
    @clist=buf[1..-1]
    #puts "Command list updated to #{@clist}"
  end
  if buf=cmd(:getid)
    @cpu=buf[0]*0x100 + buf[1]
    puts "Cpu ID: #{@cpu.to_s(16)}"
    if @@Cpu_ids[@cpu]
      @cpu_info=@@Cpu_ids[@cpu]
      printf "Family: %s\n",  @cpu_info[:family]
      printf "Ram:    %08X .. %08X %5.1fk\n",  @cpu_info[:ram_s],@cpu_info[:ram_e],(@cpu_info[:ram_e]-@cpu_info[:ram_s])/1024.0
      printf "Flash:  %08X .. %08X %5.1fk\n",  @cpu_info[:flash_s],@cpu_info[:flash_e],(@cpu_info[:flash_e]-@cpu_info[:flash_s])/1024.0
      addr=@cpu_info[:serno]
      base=addr&(0xffffff00)
      oset=addr&(0xff)
      buf=read base,oset+0x10
      serno=""
      10.times do |i|
        serno += sprintf("%02X",buf[oset+i])
      end
      @serno=serno
      puts "Serno:  '#{serno}'."
    end
  end
end
get_port() click to toggle source
# File lib/stm32.rb, line 71
def get_port()
  @port
end
get_state() click to toggle source
# File lib/stm32.rb, line 77
def get_state()
  @state
end
read(addr,len) click to toggle source
# File lib/stm32.rb, line 293
def read addr,len
  if send_cmd(:read)
    if send_addr addr
      ch=send_buf [(len-1),0xff - (len-1)],true
      if buf=wait_chars(len,0.1)
        if @debug
          printf "len=#{len}:"
          buf.each do |b|
            printf "%02X ",b
          end
          printf "\n"
        end
        return buf
      end
    end
  end
  return nil
end
run(addr=0x08000000) click to toggle source
# File lib/stm32.rb, line 349
def run addr=0x08000000
  printf "try to Run @ %x",addr
  if @state!=:booted
    if not boot
      puts "Error: Cannot run, as cannot get booted"
      return nil
    end
  end
  retries=0
  while retries<4 do
    if send_cmd :go
      if send_addr addr
        if ch=wait_char
          puts "Started Running, retries: #{retries} got #{ch}\n"  if @debug
          if ch==0
            @state=:running
            return true
          end
        else
          puts "Started Running???, retries: #{retries} -- no start char\n"
        end
      end
    end
    puts "run failed, retry boot and run"
    if not boot
      puts "Error: Cannot run, as cannot get booted"
      return nil
    end
    retries+=1
  end
  boot #return to bootstrap mode
  return false
end
send_addr(a,ack=true) click to toggle source
# File lib/stm32.rb, line 118
def send_addr a,ack=true
  buf=[0,0,0,0]
  check=0
  4.times do |i|
    c=a&0xff
    a>>=8
    buf[3-i] = c
    check ^=c
  end
  buf << check
  send_buf buf,ack
end
send_buf(buf,ack=false,tout=0.1) click to toggle source
# File lib/stm32.rb, line 140
def send_buf buf,ack=false,tout=0.1
  bytes=buf.pack("c*")
  printf "> "  if @debug
  bytes.split("").each do |ch|
    @port.write ch
    printf("%02X ",ch.ord)  if @debug
    sleep 0.0001
  end
  puts ""  if @debug
  if ack
    ch=wait_char tout
    if ch
      if ch==0x1f
        printf("< NACK: %02X\n",ch)   if @debug
        return nil
      else
        printf("< ACK: %02X\n",ch)  if @debug
        return :ack
      end
      return ch
    else
      puts "< TOUT"
      return nil
    end
  end
  return true
end
send_buf_with_check(buf,tout=0.1) click to toggle source
# File lib/stm32.rb, line 131
def send_buf_with_check buf,tout=0.1
  check=0
  buf.each do |b|
    check ^=b
  end
  buf << check
  send_buf buf,true,tout
end
send_cmd(cmd,ack=true) click to toggle source
# File lib/stm32.rb, line 90
def send_cmd cmd,ack=true
  if not @@Commands[cmd] or not @@Commands[cmd][:index]
    puts "Error: Unknown command #{cmd}"
  end
  if not c=@clist[@@Commands[cmd][:index]]
    puts "Error: Unsupported command #{cmd}"
  end
  retries=0
  flush_chars 0.001
  while retries<2 do
    ch=send_buf [c,0xff-c],ack
    if ack
      if not ch
        return :tout
      elsif ch== :nack
        printf("SYNC\r\n") if @debug
        send_buf [32],false #synch!
        flush_chars 0.1
      else
        return ch
      end
    else
      return :ack
    end
  end
  :nack
end
wait_char(tout=1) click to toggle source
# File lib/stm32.rb, line 168
def wait_char tout=1
  cnt=0
  while cnt<tout*100
    if @port.ready_for_read?
      return @port.readbyte
    end
    sleep 0.01
    cnt+=1
  end
  return nil
end
wait_chars(len,tout=0.1) click to toggle source
# File lib/stm32.rb, line 219
def wait_chars len,tout=0.1
  ret=[]
  len.times do
    if ch=wait_char(tout)
      ret << ch
    else
      puts "Warning: Short Data! #{ret}"
      return nil
    end
  end
  return ret
end
write(addr,data) click to toggle source
# File lib/stm32.rb, line 312
def write addr,data
  return(nil) if not data or data==[]
  len=data.length
  if len>0x100
    puts "Too big block to write #{len}"
    return nil
  end
  if send_cmd(:write)
    if send_addr addr
      list=[data.length-1]
      list+=data
      if ack=send_buf_with_check(list,3)
        #puts "Write Result: #{ack}"
        return ack
      end
    end
  end
  puts "Error: Write fails!"
  flush_chars # failed write may have produced some nacks
  return nil
end