Cornell University
Electrical Engineering 476
A simple keyboard monitor
for AVR microcontrollers

Introduction

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:

Command summary

Command Parameter(s) Effect
'esc'   enter monitor (in ASM only)
g   return to program
r register number:
2 hex digits, 00 to 1f
print register contents
R

register number -- new value:
2 hex digits -- 2 hex digits

modify register contents
m memory address:
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
s   print SREG
t   execute one instruction and print program counter
z   print ZH,ZL and m(Z)

The single-step 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 (g) executes 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 's' 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 'esc' 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
.endmacro
The 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:

  1. On entry:
    1. push two more registers on the stack. These must be pushed in the order r26 then r25.
    2. save TCCR0 and TCCR1B
    3. clear TCCR0 and TCCR1B
  2. On exit:
    1. restore TCCR0 and TCCR1B
    2. pop two more registers off the stack

An adapted version of an example from the program organization page shows one possible implementation to freeze the timers.


Copyright Cornell University Feb 2000