Configuring GCC Projects in AVR Studio

Step-by-Step Instructions

  • Select New Project from the Project menu.
  • Select AVR GCC as the Project type, give the project a name (sampleC for this example) and select Next > >

  • Select AVR Simulator as the Debug platform, ATmega32 as the Device and select FINISH.

  • You should now have a project with one .c source file named after your project.
  • Select Configuration Options from the Project menu.
  • On the General panel: ensure that Generate Map File and Generate List File are checked. Your panel should look like this:

  • This tells the compiler to create .map and .lss files (that will appear, after compiling, under Other Files in the AVR GCC browser window).
  • On the Include Directories panel: add the directory where you unziped the stk200.zip file.

  • Note: You will probably have the same path except on drive D.
  • This will let us use
    #include <stk200/delay.c>

    instead of requiring us to type the entire path like this:

    #include "C:/Atmel/WinAVR/avr/include/stk200/delay.c"
  • On the Libraries panel: add libm.a to the Link with These Objects list.

  • This tells the compiler to make use the precompiled library file for code associated with system header files (e.g., inttypes.h, avr/io.h, etc…).
  • On the Custom Options panel: add -minit-stack=0x083F to the list of custom compilation options for [All files].

  • This tells the compiler to initialize the stack pointer to 0x083F instead of 0x085F so that our programs are compatible with the bootloader.

Example Program

The following program has two source files: sampleC.c and configB.s. The program turns every other LED on and then toggles all of the state of the LEDs every 0.5 seconds.

sampleC.c

This source file has two functions:

  • main – main program.
  • getPattern – returns 0b10101010 if passed a 1, otherwise it returns 0b01010101.
/* sampleC.c -- Sample program that causes every other LED to toggle on and
 *              off every half second.
 * Configuration: PORTB must be connected to eight LEDs.
 * Dependencies: configB.s and stk200/delay.c 
 *               (You do not need to include standard libraries in this list.)
 * Author: t a y l o r@msoe.edu
 * Date: 5-4-2007
 */
#include <avr/io.h>        /* Contains I/O port declarations */
#include <inttypes.h>      /* Contains C99 type declarations */
#include <avr/interrupt.h> /* Contains interrupt related functions */
#include <stk200/delay.c>  /* Contains delay function */
 
/* Configures PORTB for I/O based on the mask
 * Parameters:
 *     mask -- 8-bit mask with 1 indicating output and 0 indicating input
 * Assembly implementation in configB.s
 */
extern void configPortB(uint8_t mask);
 
/* Selects one of two bit patterns
 * Parameters:
 *     type -- selects the desired bit pattern
 * return:
 *     0b10101010 if type==1
 *     0b01010101 if type!=1
 * C implementation in this file.
 */
uint8_t getPattern(uint8_t type);
 
/* Causes every other LED to toggle on and off every half second.
 * return:
 *     never
 */
int main(void)
{
  sei();                        /* Sets global interrupt flag so ATmon works */
  configPortB(0xff);            /* Configure all pins on PORTB for output */
 
  while(1)                      /* Loop forever */
  {
    PORTB = getPattern(0);      /* Turn every other light on */
    delay_ms(500);              /* Pass for 0.5 seconds */
    PORTB = getPattern(1);      /* Toggle all the lights on/off */
    delay_ms(500);              /* Pass for 0.5 seconds */
  }
  return 0;                     /* We never get here */
}
 
uint8_t getPattern(uint8_t type)
{
  uint8_t pattern = 0b01010101;
  if(type==1)
  {
    pattern = 0b10101010;
  }
  return pattern;
}

configB.s

The assembly source file contains the confgPortB function.

#define _SFR_ASM_COMPAT 1
#define __SFR_OFFSET 0     /* Required for port labels to work as expected */
#include <avr/io.h>        /* Contains I/O port declarations */

#define temp r16           /* Associate temp label with R16 */

.global configPortB        ; Make configPortB function visible to other source files.

.section .text             ; Start program section

; Configures PORTB for I/O based on the mask
; Parameters:
;     mask -- 8-bit mask with 1 indicating output and 0 indicating input
; Implementation notes:
;  The implementation is more complicated than it needs to be.  We could
;  just send R24 directly out to DDRB.
configPortB:
     push  temp            ; Save temp register on stack

     mov   temp, r24       ; Copy parameter passed in to temp register
     out   DDRB, temp      ; Configure PORTB

     pop   temp            ; Restore temp register
     ret

Resulting Listing File: sampleC.lss

The results of compiling, assembling, and linking this project are stored in a default folder in the project directory. The listing file looks like this:

Sections

sampleC.elf:     file format elf32-avr

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         000001a8  00000000  00000000  00000094  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .data         00000000  00800060  000001a8  0000023c  2**0
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000000  00800060  000001a8  0000023c  2**0
                  ALLOC
  3 .noinit       00000000  00800060  00800060  0000023c  2**0
                  CONTENTS
  4 .eeprom       00000000  00810000  00810000  0000023c  2**0
                  CONTENTS
  5 .stab         0000036c  00000000  00000000  0000023c  2**2
                  CONTENTS, READONLY, DEBUGGING
  6 .stabstr      00000084  00000000  00000000  000005a8  2**0
                  CONTENTS, READONLY, DEBUGGING
  7 .debug_aranges 00000014  00000000  00000000  0000062c  2**0
                  CONTENTS, READONLY, DEBUGGING
  8 .debug_pubnames 00000044  00000000  00000000  00000640  2**0
                  CONTENTS, READONLY, DEBUGGING
  9 .debug_info   000001c5  00000000  00000000  00000684  2**0
                  CONTENTS, READONLY, DEBUGGING
 10 .debug_abbrev 00000096  00000000  00000000  00000849  2**0
                  CONTENTS, READONLY, DEBUGGING
 11 .debug_line   00000112  00000000  00000000  000008df  2**0
                  CONTENTS, READONLY, DEBUGGING

Interrupt Jump Table

Disassembly of section .text:

00000000 <__vectors>:
   0:	0c 94 2a 00 	jmp	0x54 <__ctors_end>
   4:	0c 94 45 00 	jmp	0x8a <__bad_interrupt>
   8:	0c 94 45 00 	jmp	0x8a <__bad_interrupt>
   c:	0c 94 45 00 	jmp	0x8a <__bad_interrupt>
  10:	0c 94 45 00 	jmp	0x8a <__bad_interrupt>
  14:	0c 94 45 00 	jmp	0x8a <__bad_interrupt>
  18:	0c 94 45 00 	jmp	0x8a <__bad_interrupt>
  1c:	0c 94 45 00 	jmp	0x8a <__bad_interrupt>
  20:	0c 94 45 00 	jmp	0x8a <__bad_interrupt>
  24:	0c 94 45 00 	jmp	0x8a <__bad_interrupt>
  28:	0c 94 45 00 	jmp	0x8a <__bad_interrupt>
  2c:	0c 94 45 00 	jmp	0x8a <__bad_interrupt>
  30:	0c 94 45 00 	jmp	0x8a <__bad_interrupt>
  34:	0c 94 45 00 	jmp	0x8a <__bad_interrupt>
  38:	0c 94 45 00 	jmp	0x8a <__bad_interrupt>
  3c:	0c 94 45 00 	jmp	0x8a <__bad_interrupt>
  40:	0c 94 45 00 	jmp	0x8a <__bad_interrupt>
  44:	0c 94 45 00 	jmp	0x8a <__bad_interrupt>
  48:	0c 94 45 00 	jmp	0x8a <__bad_interrupt>
  4c:	0c 94 45 00 	jmp	0x8a <__bad_interrupt>
  50:	0c 94 45 00 	jmp	0x8a <__bad_interrupt>

  • Note: the program memory addresses given in the listing file are actually double the values found in the listing files generated by the AVR Studio assembler.
    • Using this numbering system means that the interrupt jump table ends at 0×0052 instead of 0×0029.
  • Since we are not using any interrupts in our program, all but the reset jump vector are configured to call bad_interrupt (see 0x8a below).
  • Although we have used RJMP in the past, each jump vector is 4 bytes so that it can accommodate the JMP command.

Initialization Code

  • The following code is run immediately after a reset (see the reset jump vector at 0×00).
  • I've added comments on the right to explain what is going on.
00000054 <__ctors_end>:
  54:	11 24       	eor	r1, r1
  56:	1f be       	out	0x3f, r1	; 63   Ensure R1 = 0x00
  58:	cf e5       	ldi	r28, 0x5F	; 95   Initialize stack pointer
  5a:	d8 e0       	ldi	r29, 0x08	; 8    Initialize stack pointer
  5c:	de bf       	out	0x3e, r29	; 62   0x3e = SPH
  5e:	cd bf       	out	0x3d, r28	; 61   0x3d = SPL

00000060 <__do_copy_data>:
  60:	10 e0       	ldi	r17, 0x00	; 0    r17=0
  62:	a0 e6       	ldi	r26, 0x60	; 96   X=0x0060
  64:	b0 e0       	ldi	r27, 0x00	; 0     (start of free SRAM)
  66:	e8 ea       	ldi	r30, 0xA8	; 168  Z=0x01a8
  68:	f1 e0       	ldi	r31, 0x01	; 1     (end of program)
  6a:	02 c0       	rjmp	.+4      	; 0x70 <.do_copy_data_start>

0000006c <.do_copy_data_loop>:                  ; Doesn't get here in this case
  6c:	05 90       	lpm	r0, Z+          ; Move from program memory
  6e:	0d 92       	st	X+, r0          ;  to data memory

00000070 <.do_copy_data_start>:
  70:	a0 36       	cpi	r26, 0x60	; 96
  72:	b1 07       	cpc	r27, r17
  74:	d9 f7       	brne	.-10     	; 0x6c <.do_copy_data_loop>
  • Note: The address mappings for registers and I/O ports can be found on pp. 10-11 of the ATmega32 Reference Guide.
  • On 0×0070, R26 is loaded with 0×60 + the number of bytes to copy from program memory to data memory.
  • Since there is no data to copy, R26 gets 0×60 and the branch on 0×0074 does not occur.
  • The same type of thing happens with the BSS section below.
00000076 <__do_clear_bss>:
  76:	10 e0       	ldi	r17, 0x00	; 0
  78:	a0 e6       	ldi	r26, 0x60	; 96
  7a:	b0 e0       	ldi	r27, 0x00	; 0
  7c:	01 c0       	rjmp	.+2      	; 0x80 <.do_clear_bss_start>

0000007e <.do_clear_bss_loop>:
  7e:	1d 92       	st	X+, r1

00000080 <.do_clear_bss_start>:
  80:	a0 36       	cpi	r26, 0x60	; 96
  82:	b1 07       	cpc	r27, r17
  84:	e1 f7       	brne	.-8      	; 0x7e <.do_clear_bss_loop>
  86:	0c 94 97 00 	jmp	0x12e <main>

0000008a <__bad_interrupt>:                     ; Don't know how to deal with
  8a:	0c 94 00 00 	jmp	0x0 <__vectors> ; this, just start at reset vector

Assembled and then Disassembled delay.c Code

  • Since we included stk200/delay.c (not .h), this actually included the implementation of these C functions.
  • The functions were then compiled and assembled as part of the sampleC.c source file (and can be found in sampleC.o).
  • Since the include was before main and getPattern, the code for these functions is at the beginning of the user generated code.
  • Ideally the delay.c file (and the other .c files in stk200 would be pre-compiled into a library file, but I haven't looked into how to do this. As a result, we are stuck with either:
    • Including these files like I have done here, or
    • Adding the source files to our project. This would result in multiple copies of these files.
  • We won't do a detailed analysis of the code for these functions now.
0000008e <delay_ms>:

// delay for ms milli-seconds 
// delay times are only half-way accurate if optimization is turned on!
void delay_ms(uint16_t ms) 
{
  8e:	cf 93       	push	r28
  90:	df 93       	push	r29
  92:	cd b7       	in	r28, 0x3d	; 61
  94:	de b7       	in	r29, 0x3e	; 62
  96:	24 97       	sbiw	r28, 0x04	; 4
  98:	0f b6       	in	r0, 0x3f	; 63
  9a:	f8 94       	cli
  9c:	de bf       	out	0x3e, r29	; 62
  9e:	0f be       	out	0x3f, r0	; 63
  a0:	cd bf       	out	0x3d, r28	; 61
  a2:	9a 83       	std	Y+2, r25	; 0x02
  a4:	89 83       	std	Y+1, r24	; 0x01
	volatile uint16_t i;

	for(i=ms;i>0;i--)
  a6:	89 81       	ldd	r24, Y+1	; 0x01
  a8:	9a 81       	ldd	r25, Y+2	; 0x02
  aa:	9c 83       	std	Y+4, r25	; 0x04
  ac:	8b 83       	std	Y+3, r24	; 0x03
  ae:	8b 81       	ldd	r24, Y+3	; 0x03
  b0:	9c 81       	ldd	r25, Y+4	; 0x04
  b2:	00 97       	sbiw	r24, 0x00	; 0
  b4:	51 f0       	breq	.+20     	; 0xca <delay_ms+0x3c>
	{
		delay_us(1000);
  b6:	88 ee       	ldi	r24, 0xE8	; 232
  b8:	93 e0       	ldi	r25, 0x03	; 3
  ba:	0e 94 6e 00 	call	0xdc <delay_us>
  be:	8b 81       	ldd	r24, Y+3	; 0x03
  c0:	9c 81       	ldd	r25, Y+4	; 0x04
  c2:	01 97       	sbiw	r24, 0x01	; 1
  c4:	9c 83       	std	Y+4, r25	; 0x04
  c6:	8b 83       	std	Y+3, r24	; 0x03
  c8:	f2 cf       	rjmp	.-28     	; 0xae <delay_ms+0x20>
  ca:	24 96       	adiw	r28, 0x04	; 4
  cc:	0f b6       	in	r0, 0x3f	; 63
  ce:	f8 94       	cli
  d0:	de bf       	out	0x3e, r29	; 62
  d2:	0f be       	out	0x3f, r0	; 63
  d4:	cd bf       	out	0x3d, r28	; 61
  d6:	df 91       	pop	r29
  d8:	cf 91       	pop	r28
  da:	08 95       	ret

000000dc <delay_us>:
	}
} 

// delay for us micro-seconds 
// delay times are only half-way accurate if optimization is turned on to level 3!
// max value for us is 65535/4*CYCLES_PER_US
// which is app. 17777 for 14.7456MHZ
void delay_us(uint16_t us)
{
  dc:	cf 93       	push	r28
  de:	df 93       	push	r29
  e0:	cd b7       	in	r28, 0x3d	; 61
  e2:	de b7       	in	r29, 0x3e	; 62
  e4:	24 97       	sbiw	r28, 0x04	; 4
  e6:	0f b6       	in	r0, 0x3f	; 63
  e8:	f8 94       	cli
  ea:	de bf       	out	0x3e, r29	; 62
  ec:	0f be       	out	0x3f, r0	; 63
  ee:	cd bf       	out	0x3d, r28	; 61
  f0:	9a 83       	std	Y+2, r25	; 0x02
  f2:	89 83       	std	Y+1, r24	; 0x01
	uint16_t _count;
	_count=us/4*CYCLES_PER_US;
  f4:	89 81       	ldd	r24, Y+1	; 0x01
  f6:	9a 81       	ldd	r25, Y+2	; 0x02
  f8:	96 95       	lsr	r25
  fa:	87 95       	ror	r24
  fc:	96 95       	lsr	r25
  fe:	87 95       	ror	r24
 100:	88 0f       	add	r24, r24
 102:	99 1f       	adc	r25, r25
 104:	88 0f       	add	r24, r24
 106:	99 1f       	adc	r25, r25
 108:	88 0f       	add	r24, r24
 10a:	99 1f       	adc	r25, r25
 10c:	9c 83       	std	Y+4, r25	; 0x04
 10e:	8b 83       	std	Y+3, r24	; 0x03
	
	asm volatile (
 110:	8b 81       	ldd	r24, Y+3	; 0x03
 112:	9c 81       	ldd	r25, Y+4	; 0x04
 114:	01 97       	sbiw	r24, 0x01	; 1
 116:	f1 f7       	brne	.-4      	; 0x114 <delay_us+0x38>
 118:	9c 83       	std	Y+4, r25	; 0x04
 11a:	8b 83       	std	Y+3, r24	; 0x03
 11c:	24 96       	adiw	r28, 0x04	; 4
 11e:	0f b6       	in	r0, 0x3f	; 63
 120:	f8 94       	cli
 122:	de bf       	out	0x3e, r29	; 62
 124:	0f be       	out	0x3f, r0	; 63
 126:	cd bf       	out	0x3d, r28	; 61
 128:	df 91       	pop	r29
 12a:	cf 91       	pop	r28
 12c:	08 95       	ret

Assembled and then Disassembled sampleC.c Code

  • 0x012E:0134 – initialize the stack pointer to 0x083F (as instructed by the -minit-stack=0x083F custom option in the project configuration).
  • 0×0136 – enable the global interrupt flag.
  • 0×0138:013B – call the configPortB function that was written in assembly.
    • First we load the parameter to be passed (0xFF) into R24.
    • Then we call the function.
0000012e <main>:
 * return:
 *     never
 */
int main(void)
{
 12e:	cf e3       	ldi	r28, 0x3F	; 63
 130:	d8 e0       	ldi	r29, 0x08	; 8
 132:	de bf       	out	0x3e, r29	; 62
 134:	cd bf       	out	0x3d, r28	; 61
  sei();                        /* Sets global interrupt flag so ATmon works */
 136:	78 94       	sei
  configPortB(0xff);            /* Configure all pins on PORTB for output */
 138:	8f ef       	ldi	r24, 0xFF	; 255
 13a:	0e 94 cf 00 	call	0x19e <configPortB>

  • The rest of the program involves an infinite loop.
  • Calling getPattern(0) means:
    • 0x013e – loading R24 with 0×00,
    • 0×0140 – making the call, and then
    • 0×0144 – taking the value returned (stored in R24) and sending it to PORTB.
      • The C compiler sends the result to PORTB by writing to the actual address where port be is mapped (0×0038)
      • This is equivalent to OUT PORTB, R24.
  • Calling delay_ms(500) means:
    • 0×0148:014a – Loading R25:24 with 0x01F4 since 50010 = 1F416 and then
    • 0x014c – making the call.
  • Notice the compiler was smart enough to not even bother to generate the LDI R24, 0×00 and RET instructions since this the return 0 is never executed.
  while(1)                      /* Loop forever */
  {
    PORTB = getPattern(0);      /* Turn every other light on */
 13e:	80 e0       	ldi	r24, 0x00	; 0
 140:	0e 94 b2 00 	call	0x164 <getPattern>
 144:	80 93 38 00 	sts	0x0038, r24
    delay_ms(500);              /* Pass for 0.5 seconds */
 148:	84 ef       	ldi	r24, 0xF4	; 244
 14a:	91 e0       	ldi	r25, 0x01	; 1
 14c:	0e 94 47 00 	call	0x8e <delay_ms>
    PORTB = getPattern(1);      /* Toggle all the lights on/off */
 150:	81 e0       	ldi	r24, 0x01	; 1
 152:	0e 94 b2 00 	call	0x164 <getPattern>
 156:	80 93 38 00 	sts	0x0038, r24
    delay_ms(500);              /* Pass for 0.5 seconds */
 15a:	84 ef       	ldi	r24, 0xF4	; 244
 15c:	91 e0       	ldi	r25, 0x01	; 1
 15e:	0e 94 47 00 	call	0x8e <delay_ms>
 162:	ed cf       	rjmp	.-38     	; 0x13e <main+0x10>

00000164 <getPattern>:
  }
  return 0;                     /* We never get here */
}

Local Variables and the Stack

  • The C compiler uses the stack to store variables that are local to a function.
  • The getPattern function has two local variables: type and pattern
  • 0×0164:0176 – cause the stack to grow by two bytes so that pattern can be stored there.
    • 0×0164:0166 – Save the Y register on the stack
    • 0×0168:016a – Copy the SP to the Y register
    • 0x016c – Subtract two from the Y register (has the affect of growing the stack by 2 bytes when Y is written back out to SP).
    • 0x016e – Store the SREG in R0.
    • 0×0170 – Clear the interrupt flag (we don't want an interrupt to occur while we are messing while we are writing a new value to the SP).
    • 0×0172 – Write the new value to SPH.
    • 0×0174 – Restore SREG (we do this before writing SPL since this will ensure that the interrupts are enabled as soon after the stack has been written as possible).
    • 0×0176 – Write new value to SPL.
    • 0×0178 – Write the value of type into one location on the stack.
    • 0x017a:017c – Load the hardcoded value of pattern (0b01010101) into R24 and then store it on the stack.
    • 0x017e – Copy type from the stack into R24.
    • 0×0180:0182 – Jump to 0×0188 if type!=1.
    • 0×0184 – Load 0b10101010 into R24.
    • 0×0186 – Store new value of pattern on stack.
    • 0×0188:018a – Put the return value in R24 and set R25=0.
    • 0x018c:0196 – Take the two bytes for local variables off of the stack.
    • 0×0198:019a – Restore Y register.
    • 0x019c – Return from the function.
uint8_t getPattern(uint8_t type)
{
 164:	cf 93       	push	r28
 166:	df 93       	push	r29
 168:	cd b7       	in	r28, 0x3d	; 61
 16a:	de b7       	in	r29, 0x3e	; 62
 16c:	22 97       	sbiw	r28, 0x02	; 2
 16e:	0f b6       	in	r0, 0x3f	; 63
 170:	f8 94       	cli
 172:	de bf       	out	0x3e, r29	; 62
 174:	0f be       	out	0x3f, r0	; 63
 176:	cd bf       	out	0x3d, r28	; 61
 178:	89 83       	std	Y+1, r24	; 0x01
  uint8_t pattern = 0b01010101;
 17a:	85 e5       	ldi	r24, 0x55	; 85
 17c:	8a 83       	std	Y+2, r24	; 0x02
  if(type==1)
 17e:	89 81       	ldd	r24, Y+1	; 0x01
 180:	81 30       	cpi	r24, 0x01	; 1
 182:	11 f4       	brne	.+4      	; 0x188 <getPattern+0x24>
  {
    pattern = 0b10101010;
 184:	8a ea       	ldi	r24, 0xAA	; 170
 186:	8a 83       	std	Y+2, r24	; 0x02
  }
  return pattern;
 188:	8a 81       	ldd	r24, Y+2	; 0x02
 18a:	99 27       	eor	r25, r25
 18c:	22 96       	adiw	r28, 0x02	; 2
 18e:	0f b6       	in	r0, 0x3f	; 63
 190:	f8 94       	cli
 192:	de bf       	out	0x3e, r29	; 62
 194:	0f be       	out	0x3f, r0	; 63
 196:	cd bf       	out	0x3d, r28	; 61
 198:	df 91       	pop	r29
 19a:	cf 91       	pop	r28
 19c:	08 95       	ret

Assembled and then Disassembled configB.s Code

Nothing too spectacular happens here. The assembly code was:

  • Assembled into an object file (configB.o).
  • Linked
    • Linking determines where in program memory these instructions will be placed.
    • Linking results in the sampleC.hex file that can be downloaded to the ATmega32.
  • Disassembled to get the following listing:
0000019e <configPortB>:
 19e:	0f 93       	push	r16
 1a0:	08 2f       	mov	r16, r24
 1a2:	07 bb       	out	0x17, r16	; 23
 1a4:	0f 91       	pop	r16
 1a6:	08 95       	ret
cs280/gcc.txt · Last modified: 2009/06/03 11:22 (external edit)
 

This website is not owned or managed by the Milwaukee School of Engineering.

© 2003-2010 Dr. Christopher C. Taylor, et. al. • Office: L-343 • Phone: 277-7339 • npǝ˙ǝosɯ@ɹolʎɐʇ • -> RSS <-