Class: Stm32

Inherits:
Object
  • Object
show all
Defined in:
lib/stm32.rb

Constant Summary collapse

@@Clist_def =
[0x00,0x01,0x02,0x11,0x21,0x31,0x44,0x63,0x73,0x82,0x92]
@@Commands =
{
  get:    {index:0},
  getver: {index:1},
  getid:  {index:2},
  read:   {index:3},
  go:     {index:4},
  write:  {index:5},
  erase:  {index:6},
}
@@Cpu_ids =
{
0x416 => {
  family: :L1,
  ram_s:0x20000800,
  ram_e:0x20004000,
  flash_s:0x8000000,
  flash_e:0x08020000,
  flash_bsize:0x100,
  serno:0x1ff80050,
  flash_initial:0
  },
0x413 => {family: :F4,ram_s:0x20002000,ram_e:0x20020000,flash_s:0x8000000,flash_e:0x08100000,flash_blk:16384,serno:0x1fff7a10,flash_initial:0xff},
}

Instance Method Summary collapse

Constructor Details

#initialize(hash = {}) ⇒ Stm32

Returns a new instance of Stm32.



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# 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

Instance Method Details

#bootObject



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# 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) ⇒ Object



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# 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) ⇒ Object



334
335
336
337
338
339
340
341
342
343
344
345
346
347
# 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) ⇒ Object



383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
# 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) ⇒ Object



84
85
86
87
88
# 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) ⇒ Object



80
81
82
# File 'lib/stm32.rb', line 80

def get_cpu(k)
  @cpu_info[k]
end

#get_devObject



74
75
76
# File 'lib/stm32.rb', line 74

def get_dev()
  @dev
end

#get_infoObject



265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# 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_portObject



71
72
73
# File 'lib/stm32.rb', line 71

def get_port()
  @port
end

#get_stateObject



77
78
79
# File 'lib/stm32.rb', line 77

def get_state()
  @state
end

#read(addr, len) ⇒ Object



293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# 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) ⇒ Object



349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
# 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) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
129
# 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) ⇒ Object



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# 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) ⇒ Object



131
132
133
134
135
136
137
138
# 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) ⇒ Object



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
115
116
# 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) ⇒ Object



168
169
170
171
172
173
174
175
176
177
178
# 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) ⇒ Object



219
220
221
222
223
224
225
226
227
228
229
230
# 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) ⇒ Object



312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
# 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