#!/bin/bash # KandaFlash V0.1 # # A cross platform interface for programming the Philips 89C51RX series microcontrollers # # Copyright Ian Thompson-Bell 2005 # # This software is licensed under the GNU General Public Licence # # For details see http://www.gnu.org # # restart using local wish \ exec `which wish` "$0" "$@" # Globals set progname "KandaFlash" set connected 0 set serialfd 0 set reply "" set replyflag 0 # Procedures proc init {} { # initialisation } # # Capture received serial data # proc rxchar {} { global serialfd global serialdata global replyflag global reply global connected # # Get data and append to text widget # set serialdata [read $serialfd] .tx.textarea insert end $serialdata .tx.textarea see end # # remove white space and concatenate to form reply # append reply [string trim $serialdata] # # Device sends terminating dot for success, X for failure and single U to confirm connection # set dot [string first "." $reply] set exx [string first "X" $reply] set con [string first "U" $reply] if {$dot >= 0} {set replyflag "OK"} if {$exx >= 0} { set replyflag "FAIL" tmessage "Operation Failed" } if {$con == 0} { set connected 1 tmessage "Connected OK " set reply "" } } # # Misc Read ISP function 05 # proc miscreadbyte {request} { global serialfd global reply global replyflag # # Send request and wait for reply # set reply "" set replyflag "" puts $serialfd $request tkwait variable replyflag # # return last two hex digits # return [string range $reply end-2 end-1] } # # Calc CRC from base number and two addresses # proc crc {base addr1 addr2} { set base "0x$base" set loaddr1 "0x[string range $addr1 2 3]" set hiaddr1 "0x[string range $addr1 0 1]" set loaddr2 "0x[string range $addr2 2 3]" set hiaddr2 "0x[string range $addr2 0 1]" set sum [ expr $base + $loaddr1 + $hiaddr1 +$loaddr2 +$hiaddr2] set sum [expr 0 - [expr $sum & 0xFF]] return $sum } # # Standard message Box # proc tmessage {message} { global progname tk_messageBox -default ok -icon info -title $progname -message $message } # # Not Connected message # proc notconnected {} { tmessage "Not connected to 8051 \nSelect File/Connect first" } proc helpabout {} { global progname set a " $progname" puts $a set c "Copyright Ian Thompson-Bell 2005" set d " ISP Flash Programmer" tmessage "$a\n$c\n$d" } proc gui {} { # # Set window main window size, postion and prevent resizing # wm minsize . 480 320 wm maxsize . 480 320 wm geometry . +300+200 # # Set title # global progname wm title . $progname # # fix dialog font # option add *Dialog.msg.font {Helvetica 10} # # menu font # font create menufont -family Helvetica -size 10 # # menu # menu .topmenu # #attach to root window # . config -menu .topmenu # # create cascade menus # Note, window names cannot begin with an uppercase letter # foreach m {File Program Read Special Help} { set $m [menu .topmenu.m$m -tearoff 0] .topmenu add cascade -label $m -menu .topmenu.m$m -font menufont } # # populate menus # # File # global .tx.textarea $File add command -label Connect -font menufont -command filecomms $File add command -label {Clear Screen} -font menufont -command { .tx.textarea delete 0.1 end } $File add separator $File add command -label Quit -font menufont -command {cleanup quit} # # Also bind the exit button to cleanup # bind . {cleanup winquit} # # Program # $Program add command -label { Erase Block(s)} -font menufont -command eraseblocks $Program add command -label {Code (Hex File)} -font menufont -command programintelhexfile $Program add command -label {Boot/Status} -font menufont -command programbootstatus # # Read # $Read add command -label {Boot/Status} -font menufont -command readbootstatus $Read add command -label {Security Bits} -font menufont -command readsecuritybits $Read add command -label {Manufacturer ID} -font menufont -command readmanufacturerid $Read add command -label Code -font menufont -command {dumpmemory code} $Read add command -label {Blank Check} -font menufont -command {dumpmemory blank} # # Special # $Special add command -label {Full Chip Erase} -font menufont -command erasefullchip $Special add command -label {Program Security Bits} -font menufont -command programsecuritybits # # Help - Index/About # $Help add command -label {How To} -font menufont -command helpindex $Help add command -label About -font menufont -command helpabout # # Add text area beneath # frame .tx text .tx.textarea -yscrollcommand ".tx.scroll set" scrollbar .tx.scroll -command ".tx.textarea yview" pack .tx.scroll -side right -fill y pack .tx.textarea -side left pack .tx -side top focus .tx.textarea # # capture key events and send to stdout # bind .tx.textarea { puts -nonewline %A; break } } # # proc filecomms # # Set up serial comms with Kanda board # proc filecomms {} { # # global file descriptor # global serialfd global connected global replyflag global reply # # Build a dialog box with two frames # toplevel .portdlg -class Dialog wm title .portdlg "Port Settings" wm geometry .portdlg +400+300 frame .portdlg.tf -relief raised -borderwidth 1 frame .portdlg.bf -relief raised -borderwidth 1 pack .portdlg.tf pack .portdlg.bf # # Add device and params labels and entries with sensible defaults # label .portdlg.tf.device -text "Device:" entry .portdlg.tf.deventry -width 15 -textvariable device .portdlg.tf.deventry delete 0 end .portdlg.tf.deventry insert 0 "/dev/ttyUSB0" label .portdlg.tf.params -text "Params:" entry .portdlg.tf.paramentry -width 15 -textvariable params .portdlg.tf.paramentry delete 0 end .portdlg.tf.paramentry insert 0 "9600,n,8,1" label .portdlg.tf.osc -text "Osc. Freq. MHz:" entry .portdlg.tf.oscentry -width 15 -validate key \ -vcmd {expr {[string is digit %P] && [string length %P]<3}} .portdlg.tf.oscentry delete 0 end .portdlg.tf.oscentry insert 0 20 grid configure .portdlg.tf.device .portdlg.tf.deventry grid configure .portdlg.tf.params .portdlg.tf.paramentry grid configure .portdlg.tf.osc .portdlg.tf.oscentry # # Add cancel and OK buttons # button .portdlg.bf.cancel -text "Cancel" -command { destroy .portdlg } button .portdlg.bf.ok -text "OK" -command { if { $serialfd != 0 } { close $serialfd set serialfd 0 } if { [file exists $device ] } { if { [file readable $device ] && [file writable $device] } { set serialfd [open $device {RDWR NOCTTY} ] fconfigure $serialfd -mode $params -buffering none -blocking 0 -translation binary fileevent $serialfd readable rxchar set reply "" set connected 0 puts $serialfd "U" set cmd "01000002" set oscmhz [.portdlg.tf.oscentry get] if {$oscmhz > 33} {set oscmhz 33} if {$oscmhz < 1 } {set oscmhz 11} set oscmhz [format "%2X" $oscmhz] set check [crc 03 00$oscmhz 0000] set check [format "%2X" $check] append cmd $oscmhz [string range $check end-1 end] puts $serialfd $cmd } else { tmessage "You do not have permission to access $device" } } else { tmessage "Device $device does not exist" } destroy .portdlg #if {$connected == 1} {tmessage "Connected OK "} } grid configure .portdlg.bf.cancel .portdlg.bf.ok } proc readbootstatus {} { global progname global connected # # Check connected # if {!$connected} { notconnected return } # # Send get status and wait for result # set status "0x[miscreadbyte :020000050701F1]" # # send get boot flag and wait for result # set bootflag "0x[miscreadbyte :020000050702F0]" # # Report results to user # tk_messageBox -default ok -icon info -title $progname\ -message "Status Byte: $status \nBoot Flag: $bootflag " } proc readsecuritybits {} { global progname global connected # # Check connected # if {!$connected} { notconnected return } # # Send get security bits and wait for result # set sbits "0x[miscreadbyte :020000050700F2]" # # Report results to user # tmessage "Security Bits: $sbits " } proc readmanufacturerid {} { global progname global connected # # Check connected # if {!$connected} { notconnected return } # # Send get manufacturer and device id and wait for result # set manid "0x[miscreadbyte :020000050000F9]" set dvid1 "0x[miscreadbyte :020000050001F8]" set dvid2 "0x[miscreadbyte :020000050002F7]" # # Report results to user # tmessage "Manufacurer ID: $manid \n\ Device ID #1: $dvid1 \n\ Device ID #2: $dvid2 " } # # Dump selected range of memory or blank check # selected by type = blank/dump # ISp subfunction 04 # proc dumpmemory {type} { global serialfd global connected # # Check connected # if {!$connected} { notconnected return } # # Create dialog box with top and bottom frames # toplevel .dumpdlg -class Dialog if {$type == "blank"} { wm title .dumpdlg "-----Blank Check-----" } else { wm title .dumpdlg "-----Display Code-----" } wm geometry .dumpdlg +400+300 frame .dumpdlg.tf -relief raised -borderwidth 1 frame .dumpdlg.bf -relief raised -borderwidth 1 pack .dumpdlg.tf pack .dumpdlg.bf # # Add start and end labels and entries with sensible defaults # label .dumpdlg.tf.saddr -text "Start Address:" -font menufont entry .dumpdlg.tf.saddrentry -width 15 -validate key \ -vcmd {expr {[string is xdigit %P] && [string length %P]<5}} .dumpdlg.tf.saddrentry delete 0 end .dumpdlg.tf.saddrentry insert 0 "0000" label .dumpdlg.tf.eaddr -text "End Address:" -font menufont entry .dumpdlg.tf.eaddrentry -width 15 -validate key \ -vcmd {expr {[string is xdigit %P] && [string length %P]<5}} .dumpdlg.tf.eaddrentry delete 0 end .dumpdlg.tf.eaddrentry insert 0 "00FF" grid configure .dumpdlg.tf.saddr .dumpdlg.tf.saddrentry grid configure .dumpdlg.tf.eaddr .dumpdlg.tf.eaddrentry # # Add cancel and OK buttons # button .dumpdlg.bf.cancel -text "Cancel" -command { destroy .dumpdlg } button .dumpdlg.bf.ok -text "OK" -command "dumpmemoryokbutton \"$type\"" grid configure .dumpdlg.bf.cancel .dumpdlg.bf.ok } proc dumpmemoryokbutton {action} { global serialfd global replyflag global progname # # Get addresses and form start of ISP string # set saddr [.dumpdlg.tf.saddrentry get] set eaddr [.dumpdlg.tf.eaddrentry get] set cmd ":05000004$saddr$eaddr" # # Append command specific byte # if {$action == "code"} { append cmd "00" set base "09" } else { append cmd "01" set base "0A" } # # Get,format and append CRC # set check [crc $base $saddr $eaddr] set check [format "%2X" $check] append cmd [string range $check end-1 end] destroy .dumpdlg # # If dump just send command, if blank check report result to user # if {$action =="code"} { puts $serialfd $cmd } else { miscreadbyte $cmd set flag $replyflag tmessage "Blank Check $flag " } } # # Progam the Boot Vector and Status Byte # proc programbootstatus {} { global connected # # Check connected # if {!$connected} { notconnected return } # # Create Dialog with two frames # toplevel .pbvss -class Dialog wm title .pbvss "Program Boot Vector & Status Byte" wm geometry .pbvss +400+300 frame .pbvss.tf -relief raised -borderwidth 1 frame .pbvss.bf -relief raised -borderwidth 1 pack .pbvss.tf pack .pbvss.bf # # Add start and end labels and entries with sensible defaults # label .pbvss.tf.bootlabel -text "Boot Vector:" -font menufont entry .pbvss.tf.bootentry -width 15 -validate key \ -vcmd {expr {[string is xdigit %P] && [string length %P]<3}} .pbvss.tf.bootentry delete 0 end .pbvss.tf.bootentry insert 0 "FC" label .pbvss.tf.statuslabel -text "Status Byte:" -font menufont entry .pbvss.tf.statusentry -width 15 -validate key \ -vcmd {expr {[string is xdigit %P] && [string length %P]<3}} .pbvss.tf.statusentry delete 0 end .pbvss.tf.statusentry insert 0 "00" grid configure .pbvss.tf.bootlabel .pbvss.tf.bootentry grid configure .pbvss.tf.statuslabel .pbvss.tf.statusentry # # Add cancel and OK buttons # button .pbvss.bf.cancel -text "Cancel" -command { destroy .pbvss } button .pbvss.bf.ok -text "OK" -command { # # Must first erase boot vector and status bytes # set cmd ":020000030400F7" miscreadbyte $cmd # # The reprogram with new values # set basecmd ":0300000306" set bootv [.pbvss.tf.bootentry get] set bootcrc [crc 0C "01$bootv" 0000] set sbyte [.pbvss.tf.statusentry get] set statuscrc [crc 0C "00$sbyte" 0000] set cmd $basecmd append cmd 01 $bootv set bootcrc [format "%2X" $bootcrc] append cmd [string range $bootcrc end-1 end] miscreadbyte $cmd set cmd $basecmd append cmd 00 $sbyte set statuscrc [format "%2X" $statuscrc] append cmd [string range $statuscrc end-1 end] miscreadbyte $cmd destroy .pbvss # # Check by reading back # readbootstatus } grid configure .pbvss.bf.cancel .pbvss.bf.ok } # # Erase Blocks # proc eraseblocks {} { global connected global serialfd # # Check connected # if {!$connected} { notconnected return } # # Create Dialog Box with two frames # toplevel .ebdlg -class Dialog wm title .ebdlg "Erase Block(s)" wm geometry .ebdlg +400+300 frame .ebdlg.tf -relief raised -borderwidth 1 frame .ebdlg.bf -relief raised -borderwidth 1 pack .ebdlg.tf pack .ebdlg.bf # # Labels and check boxes # foreach m {0 1 2 3 4} { set c [checkbutton .ebdlg.tf.chk$m -text "Block $m" -variable var$m -padx 24] grid configure $c } # # Add Buttons # button .ebdlg.bf.cancel -text "Cancel" -command { destroy .ebdlg } button .ebdlg.bf.ok -text "OK" -command { destroy .ebdlg set cmds [list ":020000030100FA" ":020000030120DA" ":020000030140BA" ":0200000301807A" ":0200000301C03A"] foreach m {0 1 2 3 4} { if {[set var$m]} { miscreadbyte [lindex $cmds $m] tmessage "Block $m Erased " } } } grid configure .ebdlg.bf.cancel .ebdlg.bf.ok } # # Erase Blocks # proc erasefullchip {} { global connected # # Check connected # if {!$connected} { notconnected return } # # Confirm User really wants to do this # set answer [tk_messageBox -icon warning -title "Erase Full Chip" -type yesno -default no\ -message "This will erase the entire chip along with the security bits and the boot vector and status byte. Do you really want to do this?"] if {$answer == "yes"} { miscreadbyte ":0100000307F5" tmessage "Chip Erased " # # Reset boot/status??? # } } # # Program intel Hex File # proc programintelhexfile {} { global connected global serialfd # # Check connected # if {!$connected} { notconnected return } # # get file name and open it # set types { { {Intel Hex Files} {*.hex} } { {All Files} * } } set filename [tk_getOpenFile -title "Open Intel Hex File" -filetypes $types] if { [file exists $filename] } { set fd [open $filename r] # # Read and send one line at a time # while {1} { set inteldata [gets $fd] puts $inteldata miscreadbyte $inteldata if {[eof $fd]} { close $fd break } } } } # # Program Security bits # proc programsecuritybits {} { global connected global serialfd # # Check connected # if {!$connected} { notconnected return } # # Create Dialog Box with two frames # toplevel .psdlg -class Dialog wm title .psdlg "Erase Block(s)" wm geometry .psdlg +400+300 frame .psdlg.tf -relief raised -borderwidth 1 frame .psdlg.bf -relief raised -borderwidth 1 pack .psdlg.tf pack .psdlg.bf # # Labels and check boxes # foreach m {1 2 3} { set c [checkbutton .psdlg.tf.chk$m -text "Security Bit $m" -variable var$m -padx 16] grid configure $c } # # Add Buttons # button .psdlg.bf.cancel -text "Cancel" -padx 8 -command { destroy .psdlg } button .psdlg.bf.ok -text "OK" -padx 8 -command { destroy .psdlg set cmds [list "dummy" ":020000030500F6" ":020000030501F5" ":020000030502F4"] foreach m {1 2 3} { if {[set var$m]} { puts [lindex $cmds $m] miscreadbyte [lindex $cmds $m] tmessage "Security Bit $m Set " } } } grid configure .psdlg.bf.cancel .psdlg.bf.ok } # # Help Manual # proc helpindex {} { } # # Main # proc main {} { gui } proc cleanup {source} { global serialfd if {$serialfd != 0} { close $serialfd } puts "Source was $source" exit } # Prog init main