The Achatz ICSP dongle

My friend Franz, founder of
http://www.achatz.nl,
developped an ICSP programming dongle when I was still in my diapers. Or he copied a good and standard design
from a great manufacturer. I'm not sure. Bu I do know, Franz saw the quality and the potentials of the design
and made a version that fits inside a DB25 hood. Above you see his AVR ICSP programming dongle.
I happen to be the proud owner of such a device. See if Franz still sells his hooded dongles. It's cheaper to
buy it from Franz, and he can use the money. His chimney has to smoke as well.
My mission
The ICSP dongle works with PonyProg. There is a version for PonyProg for followers of the penguin. So that
would mean I am home free. Not. I have a tendency to reinvent wheels. For study. And that's exactly why I am
going to remake another wheel. Not to improve on Ponyprog. I never used PonyProg so I wouldn't know if it can
be improved.
Still, that's hardly an issue. I want to create my own ICSP programming for the AVR. Just to gain experience
with making such an adapter. So that, later, I can make a similar program for my Okapi programmer.
I set out to do this project in Tcl/Tk, supported by some Unix filters, written in Modula-2. Look for 'IOrd',
'IOwr' and 'findPort' in the mocka section.
AVR01 : get it onscreen

This is what you see when you run the source below. The source is still way too big. Only one of the proc's is actually used. It's just the user interface that is working in version 0.1 and it's liable to be changed.
#! /usr/bin/wish
#
# Control the Achatz AVR programming dongle
# Version
# 0.1 : See if we get it on screen : 13 May 2008
wm title . "Achatz AVR programming dongle"
set Mosi 0
set Led 0
set Reset 0
set Clock 0
set Miso 0
set Presence 0
set Data0 0 ; # These bits are used for presence detection
set Data1 0
set Data2 1 ; # These bits control the '244 tristate outputs
set Data3 1
set IOport EFF0 ; # IO port address (Base)
set outWord 0
proc rdIn {} { ; # read the inputs and process the data
global Miso Presence
set ins [exec IOrd EFF1] ; # get the values of the IO port
set Miso [expr ($ins >> 6) % 2] ; # extract the value of MISO
set Presence [expr 2 * ($ins >> 7) + ($ins >> 5)]
; # extract the value of the presence detect pins
}
proc Sync {} {
rdIn
}
frame .top
frame .mid
frame .bottom
frame .mid.presence -relief ridge -borderwidth 4
frame .mid.icsp -relief groove -borderwidth 4
frame .mid.icsp.top
frame .mid.icsp.bottom
frame .mid.reset -relief ridge -borderwidth 4
button .bottom.sync -text Sync -command Sync
button .bottom.exit -text Exit -command exit
checkbutton .mid.presence.d0 -text "Presence 0" -variable Data0
checkbutton .mid.presence.d1 -text "Presence 1" -variable Data1
checkbutton .mid.icsp.top.d2 -text "Tristated" -variable !Data2
checkbutton .mid.reset.d3 -text "Tristated" -variable !Data3
checkbutton .mid.icsp.bottom.d4 -text "Clock" -variable Clock
checkbutton .mid.icsp.bottom.d5 -text "MOSI" -variable Mosi
checkbutton .mid.icsp.bottom.d6 -text "LED" -variable Led
checkbutton .mid.reset.d7 -text "Reset" -variable Reset
checkbutton .top.miso -text "MISO" -variable Miso
label .top.status1 -text "IOport \t\t: $IOport"
pack .top.status1 -side left
pack .top.miso -side right -pady 5 -padx 10
pack .mid.presence.d0 .mid.presence.d1 -side top -pady 5
pack .mid.icsp.top.d2
pack .mid.icsp.bottom.d4 .mid.icsp.bottom.d5 .mid.icsp.bottom.d6 -side left -padx 5
pack .mid.icsp.top .mid.icsp.bottom -side top -pady 5
pack .mid.reset.d3 .mid.reset.d7 -side top -fill x -pady 5
pack .mid.presence .mid.icsp .mid.reset -side left -padx 5
pack .bottom.sync -side left -padx 20 -pady 5
pack .bottom.exit -side right -padx 20 -pady 5
pack .top .mid .bottom -side top -fill x
Adding some Mocka to tickle
Now that we have a userinterface that interfaces to the user, it's time to get some content in the lot. Since I'm used to writing small programs in Modula-2, I will stick to this and make small Tcl procedures. These are listed below and some explanation is added.
proc setMosi {state} { ; # change the state of the MOSI bit
global outWord
if {$state == 0} { set outWord [expr $outWord & 223] }
if {$state == 1} { set outWord [expr $outWord | 32] }
}
"setMosi x" with "x" being either zero or one, controls the Mosi bit. We start out with the "global" keyword
to tell tickel that the 'outWord' variable we are using is the same as the one available globally, outside
this proc.
wrong # args: extra words after "else" clause in "if" commandwhich is rather confusing. There is no 'else'. So what does the interpreter really mean? It means that the curly braces are missing... If only Tcl was invented by Professor Wirth....
The magic numbers 32 and 223 , mentioned above, are binary bitmasks:
| 32 | = | 0010 0000 |
| 223 | = | 1101 1111 |
One view is enough to see that both seemingly random decimal numbers in fact are closely related binary
numbers. Each one is the complement of the other.
The importance of 32/223 is that bit 5 is the only bit in the word that is different from the other bits. You
can use this in cunning AND and OR operation, thereby manipulating (in this case) bit 5 but leaving all other
bits untouched. Below are the similar procedures to set or clear bits:
proc setLed {state} { ; # change the state of the LED
global outWord
if {$state == 0} { set outWord [expr $outWord & 191] }
if {$state == 1} { set outWord [expr $outWord | 64] }
}
proc setClock {state} { ; # change the state of the Clock bit
global outWord
if {$state == 0} { set outWord [expr $outWord & 239] }
if {$state == 1} { set outWord [expr $outWord | 16] }
}
proc setReset {state} { ; # change the state of the RESET bit
global outWord
if {$state == 0} { set outWord [expr $outWord & 127] }
if {$state == 1} { set outWord [expr $outWord | 128] }
}
proc enable {group} { ; # Enable the group of pins on the '244
global outWord Data3 Data2
if {$group == "Reset"} {
set Data3 0 ; # reset bit 3
} elseif {$group == "Control"} {
set Data2 0 ; # reset bit 2
}
Compose
wrOut $outWord
}
proc disable {group} { ; # Disable the group of pins on the '244
global outWord Data2 Data3
if {$group == "Reset"} {
set Data3 1 ; # set bit 3
} elseif {$group == "Control"} {
set Data2 1 ; # set bit 2
}
Compose
wrOut $outWord
}
As you can see I used another method in the enable/disable functions. Insstead of cunning AND and OR
operations I use rather blunt assignment instructions. This works as well, but now you need to recompose the
outWord.
Debugging Tickle
When debugging or checking tcl sources you can use tclsh, the TCL shell. Just start it in a console and use
some magic commands to see if your source runs and how it reacts to your actions. You can load your source
with the 'source' command. When loaded (without error messages) you can run your procedures and test your
variables.
When you find an error, just fix the source (in another console) and reload the source with the 'source'
command again. No need to exit tclsh first. Just reload the source and do your next set of tests and checks.
% source avr02 % Compose 12 % setClock 1 28 % setLed 1 92 % setLed 0 28 % enable Reset syntax error in expression "$group="Control"": extra tokens at end of expression % source avr02 % Compose 12 % enable Reset wrong # args: should be "wrOut value" % source avr02 % Compose 12 % enable Reset -This is not a number. % source avr02 % Compose 12 % enable Reset % set outWord 12 % set Data3 1 % disable Reset % set outWord 12 % source avr02 % Compose 12 % enable Reset % set outWord 4 % disable Reset % set outWord 12 % enable Control % set outWord 8 % source avr02 % setMosi 0 wrong # args: extra words after "else" clause in "if" command % source avr02 % setMosi 0
Some more procedures
Now that we have a way of testing the sources, it gets a lot easier to remove errors from Tickel procedures and algorithms. So I made a few more. I will show them below and in each stage I will confess in which pits I fell. Tcl is a nice language but it has some queer constructs.
proc Compose {} { ; # compose the value for the outWord
global Data0 Data1 Data2 Data3 Clock Mosi Led Reset outWord
set outWord [expr $Data0 + ($Data1 << 1) + ($Data2 << 2) + ($Data3 << 3) \
+ ($Clock << 4) + ($Mosi << 5) + ($Led << 6) + ($Reset << 7)]
# puts "Outword = $outWord"
}
What I learned here (and keep on forgetting, occasionally):
original : [expr $Data0 + $Data1 << 1 + $Data2 << 2 + $Data3 << 3 etc current : [expr $Data0 + ($Data1 << 1) + ($Data2 << 2) + ($Data3 << 3) etcThe original line translated to something like
[expr (((((((($Data0 + $Data1) << 1) + Data2) << 2 ) + Data3) << 3) + Data4) << 4) etcwhich always ends up at zero. So take care if you need to do something similar.
proc rdIn {} { ; # read the inputs and process the data
global Miso Presence IOport1
set ins [exec IOrd $IOport1] ; # get the values of the IO port
set Miso [expr ($ins >> 6) & 1] ; # extract the value of MISO
set Presence [expr ($ins >> 7) * 2 + (($ins >> 5) & 1 )]
; # extract the value of the presence detect pins
}
Proc 'rdIn' is a shortcut to read what's on the status port of the LPT port and automatically derive the
values of Miso (Master In, Slave Out) and the Presence Detect inputs.
Now it's fine for all good men to use rdIn. We do so in the procedure 'detectPresence'. The Achatz dongle has presence detect circuitry. So we need to use it.
proc detectPresence {Port} {
global Presence
exec IOwr $Port 3 ; # D0 = 1, D1 = 1 => Presence = 1
rdIn ; # get value of PresenceDetect pins
if {$Presence != 1} { return 0 }
exec IOwr $Port 0 ; # D0 = 0, D1 = 0 => Presence = 2
rdIn
if {$Presence != 2} { return 0 }
return $Port
}
Apart from a few dollar sign omissions this procedure ran out of the box. It puts values on the presence
detect inputs dynamically. Then it tries to read back those values. Beware, that the value present on D1 is
inverted by the corresponding input line.
proc findLpt {name} { ; # try to autodetect the LPT port
global IOport IOport1 IOport2
set result [exec findPort $name] ; # is this port present in hardware?
if {[string first "-Error" $result] == 0} {
return 0 ; # if not, report it
}
set IOport [string map {a A b B c C d D e E f F} $result] ; # Convert to uppercase Hex
set char [string index $IOport end] ; # Isolate last char of address
if { $char == "9" } { ; # If it is a '9'
set char "A" ; # increment to 'A'
} else {
incr char ; # else increment to the next
}
set IOport1 [string replace $IOport 3 3 $char] ; # change the last character and store
if { $char == "9" } {
set char "A"
} else {
incr char
}
set IOport2 [string replace $IOport 3 3 $char]
# puts "IOport = $IOport" ; # debugging code, commented out
# puts "Base + 1 = $IOport1"
# puts "Base + 2 = $IOport2"
if {[detectPresence $IOport] == 0} { ; # how about the presence detect inputs?
return 0 ; # report 'not there'
} else {
return 1 ; # report 'found!'
}
}
The main pitfall here was the dollar sign. I forgot some in front of a variable. But not when I coded the incr
functions. And right there, dollar signs are a nono! It may be logical, but it isn't, for a guy who is used to
program in a logical language, made in Switzerland.
% source avr02 ; findLpt parport1 IOport = EFF0 Base + 1 = EFF1 Base + 2 = EFF2Time to hit the sack.
AVR03.tcl : it works
After the sack, several days of painstakingly deep thinking (Tcl/Tk is not Modula-2, not by far!) yielded the following source. It runs stable. It's main properties, so far:
#! /usr/bin/wish
#
# Control the Achatz AVR programming dongle
# Version
# 0.1 : See if we get it on screen : 13 May 2008
# 0.2 : Try to get the functions going : 22 May 2008
# 0.3 : Clean up and streamline the source : 23 May 2008
set Mosi 0
set Led 0
set Reset 0
set Clock 0
set Miso 0
set Detect1 0
set Detect2 0
set Presence 0
set Data0 0 ; # These bits are used for presence detection
set Data1 0
set Data2 1 ; # These bits control the '244 tristate outputs
set Data3 1
set IOport 0 ; # IO port address (Base)
set IOport1 0 ; # Base + 1
set IOport2 0 ; # Base + 2
set outWord 0
set Device 1
proc wrOut {} {
global IOport outWord
exec IOwr $IOport $outWord
}
proc Compose {} { ; # compose the value for the outWord
global Data0 Data1 Data2 Data3 Clock Mosi Led Reset outWord IOport
set outWord [expr $Data0 + ($Data1 << 1) + ($Data2 << 2) + ($Data3 << 3) \
+ ($Clock << 4) + ($Mosi << 5) + ($Led << 6) + ($Reset << 7)]
wrOut
}
proc rdIn {} { ; # read the inputs and process the data
global Miso Presence IOport1 Detect1 Detect2
set ins [exec IOrd $IOport1] ; # get the values of the IO port
set Miso [expr ($ins >> 6) & 1] ; # extract the value of MISO
set Detect1 [expr ($ins >> 5) & 1] ; # extract the value of the presence detect pins
set Detect2 [expr ($ins >> 7) & 1]
set Presence [expr $Detect2 * 2 + $Detect1]
}
proc toggleClk {} {
global Clock
if {$Clock == 1} {
set Clock 0
} else {
set Clock 1
}
Compose
}
proc toggleMosi {} {
global Mosi
if {$Mosi == 1} {
set Mosi 0
} else {
set Mosi 1
}
Compose
}
proc toggleLed {} {
global Led
if {$Led == 1} {
set Led 0
} else {
set Led 1
}
Compose
}
proc toggleReset {} {
global Reset
if {$Reset == 1} {
set Reset 0
} else {
set Reset 1
}
Compose
}
proc tristateReset {} {
global Data2
if {$Data2 == 0} {
set Data2 1
} else {
set Data2 0
}
Compose
}
proc tristateIcsp {} {
global Data3
if {$Data3 == 0} {
set Data3 1
} else {
set Data3 0
}
Compose
}
proc detectPresence {} {
global Presence IOport
exec IOwr $IOport 3 ; # D0 = 1, D1 = 1 => Presence = 1
rdIn ; # get value of PresenceDetect pins
if {$Presence != 1} { return 0 }
exec IOwr $IOport 0 ; # D0 = 0, D1 = 0 => Presence = 2
rdIn
if {$Presence != 2} { return 0 }
return 1
}
proc findLpt {name} { ; # try to autodetect the LPT port
global IOport IOport1 IOport2
set result [exec findPort $name] ; # is this port present in hardware?
if {[string first "-Error" $result] == 0} { return 0 } ; # if not, report it
set IOport [string map {a A b B c C d D e E f F} $result] ; # Convert to uppercase Hex
set char [string index $IOport end] ; # extract last hex digit
if { $char == "9" } { ; # is it a '9'?
set char "A" ; # if so, make it an 'A'
} else {
incr char ; # else increment it
}
set IOport1 [string replace $IOport 3 3 $char] ; # Store the new character
if { $char == "9" } { ; # Once more for Base + 2
set char "A"
} else {
incr char
}
set IOport2 [string replace $IOport 3 3 $char]
puts "IOport = $IOport"
puts "Base + 1 = $IOport1"
puts "Base + 2 = $IOport2"
if {[detectPresence] == 0} { ; # how about the presence detect inputs?
puts "Nothing here... is the cable connected?"
return 0
} else {
puts "Achatz dongle detected"
return 1
}
}
proc Sync {} {
Compose
rdIn
}
proc Detect {} {
global Device
if {[findLpt parport0] == 1} {
set Device parport0
return
}
if {[findLpt parport1] == 1} {
set Device parport1
return
}
if {[findLpt parport2] == 1} {
set Device parport2
return
}
puts "No parallel ports with attached dongle detected"
exit
}
Detect
Sync
wm title . "Achatz AVR programming dongle, version 0.3"
frame .top
frame .top.status
frame .middle1
frame .middle2
frame .bottom
frame .middle1.presence -relief ridge -borderwidth 4
frame .middle1.presence.top
frame .middle1.presence.bot
frame .middle2.icsp -relief groove -borderwidth 4
frame .middle2.icsp.top
frame .middle2.icsp.bottom
frame .middle2.reset -relief groove -borderwidth 4
frame .middle2.reset.top
frame .middle2.reset.bot
button .middle1.sync -text Sync -command Sync
button .bottom.exit -text Exit -command exit
button .middle2.icsp.top.dd2 -text "Change" -command tristateIcsp
button .middle2.reset.top.button -text "Change" -command tristateReset
button .middle2.reset.bot.tReset -text "Reset" -command toggleReset
button .middle2.icsp.bottom.tClk -text "Clock" -command toggleClk
button .middle2.icsp.bottom.tMosi -text "Mosi" -command toggleMosi
button .middle2.icsp.bottom.tLed -text "LED" -command toggleLed
checkbutton .middle1.presence.top.d0 -text "Presence 0 ->--" -variable Data0
checkbutton .middle1.presence.bot.d1 -text "Presence 1 ->o-" -variable Data1
checkbutton .middle1.presence.top.c1 -text "true" -variable Detect1
checkbutton .middle1.presence.bot.c2 -text "inverted" -variable Detect2
checkbutton .middle2.reset.top.d3 -text "Tristated" -variable Data2
checkbutton .middle2.icsp.top.d2 -text "Tristated" -variable Data3
checkbutton .middle2.icsp.bottom.clk -variable Clock
checkbutton .middle2.icsp.bottom.mosi -variable Mosi
checkbutton .middle2.icsp.bottom.led -variable Led
checkbutton .middle2.reset.bot.d7 -variable Reset
checkbutton .top.miso -text "MISO" -variable Miso
label .top.status.l1 -text "IOport \t\t: $IOport" -anchor w
label .top.status.l2 -text "Device \t\t: $Device" -anchor w
label .middle1.presence.txt -text "Presence detect"
label .middle2.icsp.text -text "ICSP Controls"
label .middle2.reset.text -text "Target RESET"
pack .top.status.l1 .top.status.l2 -side top -fill x
pack .top.status -side left -pady 5 -padx 10
pack .top.miso -side right -pady 5 -padx 10
pack .middle1.presence.top.d0 .middle1.presence.top.c1 -side left -pady 5 -padx 5
pack .middle1.presence.bot.d1 .middle1.presence.bot.c2 -side left -pady 5 -padx 5
pack .middle1.presence.txt .middle1.presence.top .middle1.presence.bot -side top -fill x
pack .middle1.presence -side left -padx 5
pack .middle1.sync -side right -padx 5
pack .middle2.icsp.top.d2 .middle2.icsp.top.dd2 -side left -padx 10
pack .middle2.icsp.bottom.clk .middle2.icsp.bottom.tClk \
.middle2.icsp.bottom.mosi .middle2.icsp.bottom.tMosi \
.middle2.icsp.bottom.led .middle2.icsp.bottom.tLed -side left -padx 5
pack .middle2.icsp.text .middle2.icsp.top .middle2.icsp.bottom -side top -pady 5
pack .middle2.reset.top.d3 .middle2.reset.top.button -side left -padx 10
pack .middle2.reset.bot.d7 .middle2.reset.bot.tReset -side left -padx 5
pack .middle2.reset.text .middle2.reset.top .middle2.reset.bot -side top -fill x -pady 5
pack .middle2.icsp .middle2.reset -side left -padx 5
pack .bottom.exit -padx 20 -pady 5
pack .top .middle1 .middle2 .bottom -side top -fill x -pady 5 -padx 5
As long as the '244 buffers are tristated, they do nothing. No reset, no ICSP signals. But as soon as you
toggle the tristate button, things start working. Do some experiments with the LED and the Reset group.

Page created on 13 May 2008 and
Page equipped with FroogleBuster technology