Electrical Engineering 476
A simple keyboard monitor
for AVR microcontrollers
Debugging a program in a microcontroller can be frustrating because the internal state of the machine can be hard to determine. There are several approaches to debugging, all of which are useful:
This page describes a simple keyboard monitor for the Atmel AVR microcontrollers. The monitor has the following features:
keymon()function call inserted into the program under test.
esc', or by a break macro inserted into the program under test.
|'esc'||enter monitor (in ASM only)|
|g||return to program|
2 hex digits, 00 to 1f
|print register contents|
register number -- new value:
|modify register contents|
4 hex digits, 0060 to RAMEND
|print RAM contents|
|M||address -- new value:
4 hex digits -- 2 hex digits
|modify memory contents|
|i||i/o register number:
2 hex digits, 00 to 3f
|print i/o register|
|I||i/o reg number -- new value:
2 hex digits -- 2 hex digits
|modify i/o register|
|p||print program counter|
|P||new value: 4 hex digits||modify program counter|
|t||execute one instruction and print program counter|
|z||print ZH,ZL and m(Z)|
t command uses the transmit-complete
interrupt. When the command is used, the monitor code waits until the TXC bit
is set, enables the interrupt, then returns to the program under test. Since
an interrupt is guaranteed to be pending, exectly one instruction is executed
before re-entering the monitor. Note however, that the TXC interrupt is very
low priority, so any other ISR (e.g. a timer1) will execute before returning
to the monitor. The
P command modifies the program counter so that
the next single-step (
t) or go command (
from a new location. Setting the program counter to zero forces a mcu RESET.
Refer to the .LST file generated by the assembler (in ASM or C) for actual program
addresses. In C, you can get the addresses of variables from the .map file.
Using the monitor in C
A simple LED blinking program show how to include the monitor in a program. Experiment with the keyboard commands. Try reading/writing the count register, reading/writing PORTB, and single-stepping the program. To try this, you must also download the monitor .h file, and modify the include statement in the example program to point to it.
Using the monitor in ASM
A simple LED blinking program shows how you might use this monitor. Experiment with uncommenting the two indicated lines of code and experiment with the keyboard commands. Try reading/writing the count register, reading/writing PORTB, and single-stepping the program. To try this, you must also download the monitor include file, and modify the include statement in the example program to point to it. The monitor include statement should be at the end of the program under test.
Since the keyboard uses the UART, special attention needs to be directed to
an example program which also uses polled i/o to
get strings from the UART. To try this, you must also download the monitor
include file, and modify the include statement in the example program to
point to it. The monitor include statement should be at the end of the
program under test. The example program under test prompts for a string and
echos it to the PC terminal emulator. If the string='
g' is entered,
the program repeatedly sends a string to the PC and waits for a '
character. Note that this use of a '
g' command is not the monitor
command, but belongs to the program under test.
The monitor can be entered by a
break command or by pressing '
if the program is not waiting for input from the PC. When the monitor is active,
it displays a '
>' prompt. At this point any of the other monitor
commands may be used. Near the beginning of the program there is a short macro
defined which calls the UART Monitor ISR as a subroutine. This produces a software
interrupt to enter the monitor. The macro is shown below.
.macro break cli rcall Monitor .endmacro
Whenever the symbol
break is inserted into the program under test,
the monitor will be entered without a keystroke from the user. All of the monitor
commands can them be used.
As the example above shows, the monitor program requires two ISRs to be entered in the vector table. Also, the UART has to be enabled, and the receive-done ISR enabled. The code is duplicated here for easy reference. The code assumes a 4414/8515 mcu.
.org $0000 rjmp RESET ;reset entry vector reti reti reti reti reti reti reti reti rjmp Monitor ;entry point for the debugger reti rjmp Monitor ;hijack TXCisr for single step reti RESET: ldi temp, LOW(RAMEND) ;setup stack pointer out SPL, temp ldi temp, HIGH(RAMEND) out SPH, temp ;setup UART -- enable RX, TX pins without ISRs ldi temp, 0b00011000 out UCR, temp ;set baud rate to 9600 ldi temp, 25 out UBRR, temp ;enable receive ISR ONLY for keyboard monitor sbi UCR, RXCIE sei ;need sei ONLY for the Monitor
On the PC, you need to run hyperterm and connect it with the serial port to which you have attached the AVR mcu. configure the port to 9600 baud, 8 bits, no parity, 1 stop bit, no flow control. At the AVR end, you must configure jumpers and whatever else is necessary to connect the hardware UART to a serial port. The connecting cable should be 'straight through' and not a null-modem cable.
An Old example program in still available for use with interrupt-driven i/o.
Other Uses in ASM
Some of the subroutines within the monitor can be used by themselves for terminal output.
The following macro allows a running program to print a register on the fly without entering the monitor.
.macro printReg ;Use: printReg r19 or printReg temp push r28 push r29 mov r28,@0 rcall RXput2char _pwait: sbis USR, UDRE rjmp _pwait pop r29 pop r28 .endmacroThe following macro prints a memory location from a running program. As shown, symbolic names may be used to reference memory locations.
.macro printMem ;Use: printMem cntstr+1 push r28 push r29 push ZL push ZH ldi ZL, low(@0) ldi ZH, high(@0) ld r28, Z rcall RXput2char _pwaitm:sbis USR, UDRE rjmp _pwaitm push ZH push ZL pop r29 pop r28 .endmacro
Under some circumstances, it may be necessary to freeze the microcontroller timers when you enter the monitor so that you can debug time-dependent code. You can add code to:
An adapted version of an example from the program organization page shows one possible implementation to freeze the timers.
Copyright Cornell University Feb 2000