NAME AVRmonitor: Provides access to AVR target hardware from a PC command line, using interpreted assembly language. The uC pins Miso, Mosi and Sck must be connected to the PC's parallel port using the same cable as your programmer, either the cable and `dongle' as supplied with the well-known STK200, or the cable used with sp12. A choice between these two options must be made at compile time. The AT90 uC must use the `AVRmon' routine as shown in AVRmnXX.asm. Copyright (C) 2000 Pitronics This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Pitronics can be reached by email: sbolt@xs4all.nl SYNOPSIS avrmon [-i path/xxxxdef.inc] <-l path/log> <-s path/source> DESCRIPTION AVRmonitor and its documentation can be useful only when you are familiar with the AVR instruction set and the datasheet of your AVR uC. The software consists of two parts: 1) A program running on a PC (avrmon.exe or simply avrmon, depending on the operating system), which we'll call avrMon(PC). 2) A set of three routines (sendByte, recvByte and avrMon) totalling 89 lines of code, which you add to the AVR uC software you are developing; to be called avrMon(uC). When this part is called by a `breakpoint' in your software (rcall avrMon), it makes contact with the waiting avrMon(PC). In most cases, an avrMon(uC) session takes only 7 bytes of stack space. All registers are restored on exit, unless you wanted their content changed. NOTE: avrMon is suitable for all AVR uCs except those which have no RAM, like the AT90S1200(A), the Tiny11 and the Tiny12. The uC containing avrMon(uC) should be part of your target hardware, and is connected to the PC parallel port using the same cable as your programmer. This may be the cable and `dongle' as supplied with the well-known Kanda STK200, or the cable used with SP12. In both cases, the same three uC pins are used: Miso, Mosi and Sck. But since SP12 and the STK200 use different parallel port pinouts, one or the other must be selected when compiling the avrMon source, by (not) defining `KANDA' in the makefile. As `LINUX' can also be defined (to select Linux instead of Dos/Windows-3/9X as the OS), there are four possible avrMon(PC) binaries, to be found in four packages: http://www.xs4all.nl/~sbolt/Packages/avrm_s12.exe http://www.xs4all.nl/~sbolt/Packages/avrm_kda.exe http://www.xs4all.nl/~sbolt/Packages/avrMon-Linux-sp12.tgz http://www.xs4all.nl/~sbolt/Packages/avrMon-Linux-Kanda.tgz In each of those you'll also find avrMon(uC), as part of demo assembly source files for various uCs. The Linux packages contain statically linked binaries, which should run on any Linux system. Starting the system: 1) Fire up your PC and make sure you are in your project's directory; avrMon(PC) must be in your PATH. 2) Make the connection between uC and PC parallel port. 3) Power up your target hardware. 4) If necessary, upload your uC software (including avrMon(uC) and one or more breakpoints) or the appropriate demo file (for instance avrmn13.hex) to the target uC, using your programmer. 4) Type avrMon -i 2313def.inc on the PC command line. (The actual .inc file used should of course be the one for your target uC). When you run avrMon for the first time, the runtime configuration file _avrcrc will be created in the directory as defined by the environment variable AVRCOM, or in the current directory if this variable is not set. This will take a few seconds, as a timing loop must be calibrated. At this time, the program also looks for parallel ports and selects the primary one for use. You can edit _avrcrc (with care, using a plain editor, *not* a word-processor) to select another port if necessary. The avrMon> prompt will appear as soon as the uC software reaches a breakpoint. If the uC has one of the demo files in its program area, the breakpoint is reached immediately. EXAMPLES In your source, you put breakpoints like: rcall avrMon You can add a breakpoint number if you like: ldi r16, 1 rcall avrMon When execution reaches the breakpoint, the content of r16 is reported as the breakpoint `token'. The PC screen will show something like: ----------------------------------------------------------------------- Waiting for uC to send break token; hit ESC to interrupt. Break token received: 1, 0x01, B00000001; program count 0x0085 ----------------------------------------------------------------------- avrMon> And then you have access to the uC's internal data space, by typing commands in an interpreted language which is very close to AVR assembly. For instance, typing `sbi PORTD, PD1' followed by a causes bit 1 of PORTD to be set, and brings the altered content of that port register in view: avrMon> sbi PORTD, PD1 result 0x02, B00000010 A special word allows you to put the content of any data space location on screen without changing anything. A look at an SRAM address: avrMon> cout $63 --> 0xf0, B11110000, decimal 240 Or a register: avrMon> cout r16 --> 0x01, B00000001, decimal 1 Or PORTD: avrMon> cout PORTD --> 0x02, B00000010, decimal 2 Line editing is limited to hitting and retyping. But if you want to repeat a line, you can use the up/down arrow keys to find it in the command line history, which is preserved between sessions in the file avrmon.his. Note that avrMon is not case sensitive, so you might as well type: avrMon> cout portd --> 0x02, B00000010, decimal 2 You can use labels like PORTD, TCNT0, TIMSK, TIFR, TOV0, PB6, XL, ZH; in short, any label which refers to either an address in i/o space, a register number (like r30) or a bit number. But not for instance RAMEND or OVF1addr. Of course operands may also be of the kind r12, 253, 0xfd, 0b11111101, or $021e (the latter would be an address in flash or SRAM). However, the operands must be plain addresses and constants - you can't use expressions. Another special word is used for writing something into the eeprom aera, in this case 0xab into address $21: avrMon> eepw $21, 0xab result 0xab, B10101011 You can read from the eeprom area as you would using normal AVR assembly: avrMon> ldi r16, 0x21 result 0x21, B00100001 avrMon> out EEAR, r16 result 0x21, B00100001 avrMon> sbi EECR, 0 result 0x01, B00000001 avrMon> in r16, EEDR result 0xab, B10101011 But there is a special word for doing it in one line: avrMon> eepr r9, $21 result 0xab, B10101011 Most words comply with the AVR instruction set, but provide access to the entire data space. For instance, you can `subi' something from any general purpose register, SRAM location or i/o register. As you can see, avrMon reports status register changes: avrMon> subi r9, 2 result 0xa9, B10101001 SREG B00010100 avrMon> subi $63, 0xa result 0xe6, B11100110 SREG B00110100 avrMon> subi PORTD, 1 result 0x01, B00000001 SREG B00000000 And you can do things like: avrMon> sbi $63, 3 result 0xee, B11101110 (Which sets bit number 3 in SRAM location $63.) There are two special words for reading word addresses in flash: avrMon> lpmh r16, $21e result 0x9a, B10011010 avrMon> lpml r16, $21e result 0x89, B10001001 Plain `lpm' reads byte addresses as per AVR instruction set, but into any register or data space location: avrMon> lpm r16, $43d result 0x9a, B10011010 avrMon> lpm r16, $43c result 0x89, B10001001 Starting and testing the A/D converter of an AT90S4433 is easy: avrMon> ldi admux, 0 result 0000, B00000000 avrMon> ldi adcsr, 0xf6 result 0xf6, B11110110 avrMon> cout adcl --> 0xc7, B11000111, decimal 199 avrMon> cout adch --> 0x03, B00000011, decimal 3 Change the voltage on the input pin (using for instance a potmeter) and read again: avrMon> cout adcl --> 0x24, B00100100, decimal 36 avrMon> cout adch --> 0x01, B00000001, decimal 1 The special word `go' allows the uC to continue program execution from the current breakpoint. AvrMon(PC) will wait for the next breakpoint, and again present its prompt: avrMon> go ----------------------------------------------------------------------- Waiting for uC to send break token; hit ESC to interrupt. Break token received: 2, 0x02, B00000010; program count 0x008a ----------------------------------------------------------------------- avrMon> `go ' may be used to set the program counter before releasing the uC. Note that in this case the Z register (r30 and r31) is used to perform an ijmp, and its original content is lost. avrMon> go $21e ----------------------------------------------------------------------- Waiting for uC to send break token; hit ESC to interrupt. Break token received: 1, 0x01, B00000001; program count 0x0085 ----------------------------------------------------------------------- avrMon> The above result would of course only be achieved if routine starting at $21e jumps to a breakpoint at $85. A list file provided by your assembler will show the address (program counter value) of each assembled instruction, to make the navigation of your software relatively easy. AvrMon(PC) exits when you type the word `bye' or hit the escape key: avrMon> bye Best use of the of avrMon involves running your uC software with well-placed breakpoints, where you can study and change data space locations as you wish. Alternatively (or in combination with breakpointed uC software), you can run short sources in avrMon(PC), which use the memory, status register and i/o of your target hardware. For instance, when avrmn13.hex is uploaded to an AT90S2313 on an STK200, this little avrMon(PC) program sequentially switches the LEDs connected to PORTB 0-4 on and off: reset: rjmp main .def temp =r16 .def LEDs =r17 .def on_off =r18 .equ waitVal =0x03 longDelay: ldi temp, waitVal delay: dec temp brne delay ret main: in LEDs, PORTB ori LEDs, 0x1f out PORTB, LEDs in LEDs, DDRB ori LEDs, 0x1f out DDRB, LEDs ldi on_off, 1 next: com on_off in LEDs, PORTB and LEDs, on_off out PORTB, LEDs rcall longDelay com on_off or LEDs, on_off out PORTB, LEDs lsl on_off sbrs on_off, 5 rjmp next end: bye Note that the example above takes care to avoid disturbing DDRB/PORTB 5-7, which are Mosi, Miso and Sck (on the AT90S2313). If these i/o bits are touched, the communication between avrMon(uC) and avrMon(PC) breaks down. Note also that the example is almost ready to be turned into code by an AVR assembler. All it needs to run directly in the uC is a line .include "2313def.inc" at the beginning, and two lines before the label `next:' to initialize the uC stack pointer: ldi temp, RAMEND out SPL, temp Finally, the line containing `bye' should be replaced by something like end: rjmp end And if you want to actually see the LEDs blink, the longDelay must of course be much increased, when the program runs directly on the target uC. A much shorter version is possible when using the increased scope of the avrMon(PC) instruction words: reset: rjmp main .def temp =r16 .def on_off =r18 .equ waitVal =0x03 longDelay: ldi temp, waitVal delay: dec temp brne delay ret main: ori PORTB, 0x1f ori DDRB, 0x1f ldi on_off, 1 next: com on_off and PORTB, on_off rcall longDelay com on_off or PORTB, on_off lsl on_off sbrs on_off, 5 rjmp next end: bye If you happen to own an STK200, you can run this example as follows: 1) Put an AT90S2313 in the STK200. 2) Upload avrmn13.hex to the uC using the STK200 programming software (I use a Kanda compatible version of sp12 :) 3) Open a DosBox, go to the avrmon directory and type avrmon -i 2313def.inc -l test.log -s kdemo.a The five LEDs will blink one after another, and then avrMon(PC) will end. When you open the log file `test.log', you see a complete, commented trace: AVRmonitor version 0.2. IncludePath: 2313def.inc ----------------------------------------------------------------------- Waiting for uC to send break token; hit ESC to interrupt. Break token received: 1, 0x01, B00000001; program count 0x0085 ----------------------------------------------------------------------- reset: rjmp main main: ori PORTB, 0x1f result 0x5f, B01011111 SREG B00000000 ori DDRB, 0x1f result 0x5f, B01011111 SREG B00000000 ldi on_off, 1 result 0x01, B00000001 next: com on_off result 0xfe, B11111110 SREG B00010101 and PORTB, on_off result 0x5e, B01011110 SREG B00000001 rcall longDelay longDelay: ldi temp, waitVal result 0x03, B00000011 delay: dec temp result 0x02, B00000010 SREG B00000001 brne delay SREG bit 1 clear; branch taken delay: dec temp result 0x01, B00000001 SREG B00000001 brne delay SREG bit 1 clear; branch taken delay: dec temp result 0000, B00000000 SREG B00000011 brne delay SREG bit 1 set; no branch ret com on_off result 0x01, B00000001 SREG B00000001 or PORTB, on_off result 0x5f, B01011111 SREG B00000001 lsl on_off result 0x02, B00000010 SREG B00000000 sbrs on_off, 5 Bit 5 clear; no skip rjmp next next: com on_off result 0xfd, B11111101 SREG B00010101 and PORTB, on_off result 0x5d, B01011101 SREG B00000001 rcall longDelay longDelay: ldi temp, waitVal result 0x03, B00000011 delay: dec temp result 0x02, B00000010 SREG B00000001 brne delay SREG bit 1 clear; branch taken delay: dec temp result 0x01, B00000001 SREG B00000001 brne delay SREG bit 1 clear; branch taken delay: dec temp result 0000, B00000000 SREG B00000011 brne delay SREG bit 1 set; no branch ret com on_off result 0x02, B00000010 SREG B00000001 or PORTB, on_off result 0x5f, B01011111 SREG B00000001 lsl on_off result 0x04, B00000100 SREG B00000000 sbrs on_off, 5 Bit 5 clear; no skip rjmp next next: com on_off result 0xfb, B11111011 SREG B00010101 and PORTB, on_off result 0x5b, B01011011 SREG B00000001 rcall longDelay longDelay: ldi temp, waitVal result 0x03, B00000011 delay: dec temp result 0x02, B00000010 SREG B00000001 brne delay SREG bit 1 clear; branch taken delay: dec temp result 0x01, B00000001 SREG B00000001 brne delay SREG bit 1 clear; branch taken delay: dec temp result 0000, B00000000 SREG B00000011 brne delay SREG bit 1 set; no branch ret com on_off result 0x04, B00000100 SREG B00000001 or PORTB, on_off result 0x5f, B01011111 SREG B00000001 lsl on_off result 0x08, B00001000 SREG B00000000 sbrs on_off, 5 Bit 5 clear; no skip rjmp next next: com on_off result 0xf7, B11110111 SREG B00010101 and PORTB, on_off result 0x57, B01010111 SREG B00000001 rcall longDelay longDelay: ldi temp, waitVal result 0x03, B00000011 delay: dec temp result 0x02, B00000010 SREG B00000001 brne delay SREG bit 1 clear; branch taken delay: dec temp result 0x01, B00000001 SREG B00000001 brne delay SREG bit 1 clear; branch taken delay: dec temp result 0000, B00000000 SREG B00000011 brne delay SREG bit 1 set; no branch ret com on_off result 0x08, B00001000 SREG B00000001 or PORTB, on_off result 0x5f, B01011111 SREG B00000001 lsl on_off result 0x10, B00010000 SREG B00100000 sbrs on_off, 5 Bit 5 clear; no skip rjmp next next: com on_off result 0xef, B11101111 SREG B00110101 and PORTB, on_off result 0x4f, B01001111 SREG B00100001 rcall longDelay longDelay: ldi temp, waitVal result 0x03, B00000011 delay: dec temp result 0x02, B00000010 SREG B00100001 brne delay SREG bit 1 clear; branch taken delay: dec temp result 0x01, B00000001 SREG B00100001 brne delay SREG bit 1 clear; branch taken delay: dec temp result 0000, B00000000 SREG B00100011 brne delay SREG bit 1 set; no branch ret com on_off result 0x10, B00010000 SREG B00100001 or PORTB, on_off result 0x5f, B01011111 SREG B00100001 lsl on_off result 0x20, B00100000 SREG B00000000 sbrs on_off, 5 Bit 5 set; performing skip end: bye Note: avrMon(PC) will only print the `result' and `SREG' columns if your instructions leave sufficient space for them. The above examples should make it easy to run some experiments of your own, to get a feel for what avrMon can do for you. For further details, look below in the paragraph `instruction set'. COMPATIBILITY The 32-bit protected mode executable found in the avrMon Dos/Windows-3/9X packages relies on MS-DOS and BIOS for file I/O and other basic functions such as keyboard input, screen cursor position, etc. It uses "DPMI" (the DOS Protected Mode Interface) to allow DOS/BIOS calls from protected mode. AvrMon is known to run in MS-DOS, in a Windows-3.X DosBox and in a Windows-9X DosBox or DosMode. Windows NT does not provide the access to the parallel port required by avrMon. The statically linked executable found in the avrMon Linux packages should run on any Linux system. If you have the right libraries installed, it's best to recompile the source to obtain much lighter dynamically linked binaries. I've used libncurses.so.3.0 and libc.so.5. INSTALLATION The avrMon executable (avrmon.exe or, in linux, just avrmon) should be put somewhere in your PATH. If you are using plain MS-DOS, Windows DosMode or something like it without a DPMI host, you must also put cwsdpmi.exe (found in csdpmi\bin) in your PATH. This is not required when using a Windows DosBox. Finally, it is advisable to set the environment variable AVRCOM by adding a line like "Set AVRCOM=C:\AVRCOM\" to your autoexec.bat file. (The name AVRCOM is used because the communications part of avrMon is also used in other tools.) When you run avrMon for the first time, the runtime configuration file _avrcrc will be created in the directory as defined by the environment variable AVRCOM, or in the current directory if this variable is not set. This will usually take a few seconds, as a timing loop must be calibrated. _avrcrc is in plain language and may be edited (with care), for instance to make avrMon use a different parallel port. NOTE: When running avrMon in a Windows DosBox (rather than DosMode) you should let avrMon generate _avrcrc while your computer is lightly loaded. If heavy multitasking is going on, the autocalibration will produce time constants which are too small. When you are actually running the program, heavy background loads may slow down the communication, but reliability won't suffer. OPTIONS avrMon(PC) takes three options; all three must be followed by an appropriate path/filename. -i include file For instance, -i 2313def.inc. This option must be used, or avrMon(PC) will not be able to deal with labels like SREG, PORTD, TCNT0 and so on. -l log file Such as -l test.log. When this option is used, what appears on screen is also written into the log file. -s source file This should be a valid source of instructions for avrMon(PC), as shown by the above examples. If you use -S rather than -s, most output to screen will be suppressed, but everything still goes into the log file, assuming that option -l was used. Note: avrMon(PC) will only print the `result' and `SREG' columns if your instructions leave sufficient space for them. INSTRUCTION SET These words have been implemented: adc add and andi asr clr bclr bld set bset bst cbi cbr clc clh cli cln cls clt clv clz com cp cpc cpi dec or eor in inc ldi lds lsl lsr mov neg ori out rol ror sbc sbci sbi sbr sec seh sei sen ser ses sev sez sts sub subi swap tst nop lpm lpml lpmh eepr eepw cout go bye cpse sbrc sbrs sbic sbis rjmp brbs brbc breq brne brcs brcc brsh brlo brmi brpl brge brlt brhs brhc brts brtc brvs brvc brie brid rcall ret Most words behave as you would expect, modifying registers, i/o locations, SRAM addresses and the status register of the uC as per the AVR instruction set. But since only direct addressing is used, they provide access to the entire data space (general purpose registers, SRAM and i/o registers). Operands may be suitably defined labels or numbers like r12, 253, 0xfd, 0b11111101, or $021e (the latter would be an address in flash or SRAM). However, they must be plain addresses and constants - you can't use expressions. As to labels; where the include files are concerned, you are limited to those like PORTD, TCNT0, TIMSK, TIFR, TOV0, PB6, XL, ZH; in short, any label which refers to either an address in i/o space, a register number (like r30) or a bit number. But not for instance RAMEND or OVF1addr. This is because label bindings may refer to addresses in various spaces, and avrMon needs pointers to the full data space - where the I/O group starts counting at 0x20, just above the register group. To convert from i/o space to data space, avrMon adds 0x20 to every label found in an include file. When a label is known to refer to a bit number (PD1, TOIE1 and so on), only the lower nibble is used, so the added 0x20 has no effect. Labels defined in your source file are presumed to refer to constants, register numbers (of the r16 kind) or adresses in data space. None of those require conversion, so the bindings are used as they are. Note: The branch and skip instructions are (obviously) only available when you are running a source file, not when you are using the command line to interact directly with the target hardware. Details of the special words: COUT [data space address or register] Brings the content of any register, i/o or sram location to your screen. The single operand may be a suitably defined label, a register number (like r9) or a data space address, best written as $. GO Restarts uC program execution from the current breakpoint. A program counter value (flash word address) may be used as operand, in which case execution continues at the location pointed to. AvrMon(uC) achieves this with an ijmp, so the original content of the Z register (r30 and r31) will be lost. EEPR [data space address or register] [eeprom address] Copies the content of an eeprom address into any register or dataspace location. EEPW [eeprom address] [constant] Writes the contstant into the eeprom address. LPM [data space address or register] [byte address in flash] Copies the content of a byte address in flash into any register or dataspace location. LPMH [data space address or register] [word address in flash] Copies the most significant byte of a word address in flash into any register or dataspace location. LPML [data space address or register] [word address in flash] Copies the least significant byte of a word address in flash into any register or dataspace location. BYE (no operands) Terminates avrMon(PC). When using the sp12 plain cable, all pins will be set to zero; if Ken Huntington's `dongle' is part of the cable, the pins become high-Z. Details of sp12 and its cable can be found on http://www.xs4all.nl/~sbolt/e-spider_prog.html When using the Kanda compatible avrMon(PC) version, the target end of the STK200 cable also ends in high-Z state. FILES avrmon.exe The avrMon(PC) executable found in the Dos/Windows-3/9X packages. avrmon The avrMon(PC) executable found in the Linux packages. avrmon.txt This file. XXXXdef.inc Include files for various AVR uCs. avrmnXX.hex avrMon(uC) demo files for various AVR uCs. kdemo.a avrMon source file mentioned in `examples' above. _avrcrc The runtime configuration file. avrmon.his The avrMon(PC) command line history file. avrmon.gif Shows the avrMon(uC)/(PC) comms protocol. sp12log.txt Checksums and time stamps for the avrMon demo's. licence.txt The GNU general public licence states the terms and conditions for copying, distribution and modification of this software SOURCE This directory contains the source files for both avrMon(uC) and avrMon(PC), as well as a makefile for use with gcc or djgpp. ACKNOWLEDGEMENTS The 32-bit Dos protected mode executable found in this version of AVRmonitor was compiled with gcc 2.8.1, as found in the DJGPP packages. You can obtain a copy of this excellent compiler from any SimTel.NET mirror in the pub/simtelnet/gnu/djgpp/ subdirectory, or from one of the CCT mirrors, world-wide. For instance: ftp://ftp.euro.net/d5/simtelnet/gnu/djgpp/ (in The Netherlands). The Dos version was linked with PDCurses, a public domain curses library; the ncurses library was used for the Linux version. This product includes software developed by the University of California, Berkeley and its contributors. BUGS & BOTHER Please report them to the author. AUTHORS Software: Steven Bolt (sbolt@xs4all.nl)