This page looks best with JavaScript enabled

AVR Development

 ·  ☕ 4 min read  ·  ✍️ Ben Mason

AVR Notes


GitHub – buserror/simavr: simavr is a lean, mean and hackable AVR simulator for linux & OSX


/simduino.elf -d ~/Projects/Rhme-2016/challenges/binaries/casino/casino.hex

GCC flags

avr-gcc -c -mmcu=atmega328p -I. -gstabs -D__AVR_ATmega328P__ -Os -Wall -Wstrict-prototypes -std=gnu99 main.c -o main.o

avr-objcopy -O ihex -R .eeprom max7219.elf max7219.hex
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" \
	--change-section-lma .eeprom=0 -O ihex max7219.elf max7219.eep
avr-objcopy: --change-section-lma .eeprom=0x0000000000000000 never used
avrdude -p atmega328p  -c usbtiny  -v -v  -U flash:w:max7219.hex


<avr/io.h> – Aliases for IO register addresses

  • GPIO connections are set in groups of ports
  • Default to Input

DDRx – data direction register (in or out)

PORTx – Port output register

PINx – Port in put register

8bit in width

Bit operations

BV is a macro for the bit shift operator

_BV(x) = (1<<x)

Set a Bit (or)

BYTE |= (1 << i)

Clear a bit (not)

BYTE &= ~(1 << i)

Toggle a bit (xor)

BYTE ^= (1 << i)

Set first and forth bits

BTYE = (1 << 2) | (1<< 4)

Checking bits on ports

The following in avr/sfr_defs.h imported by in io.h

/* avr/sfr_defs.h - macros for accessing AVR special function registers */

 bit_is_set(sfr, bit)
 bit_is_clear(sfr, bit)
 loop_until_bit_is_set(sfr, bit)
 loop_until_bit_is_clear(sfr, bit)


Global variables should be declared as volatile ref: C Notes


Interrupt service routines are executed when a interrupt is triggered. You can not pass variables into an ISR, you will need to reference a global variable.


External Interrupts


  • Flexible can trigger on rising, falling, change, or continuously on low voltage
  • only on PD2 and PD3
  • high priority

PCINT – Pin Change Interrupt

  • Detects only change on a pin
  • Banked with the Port Groups

  • PCMSKx is used to determine which pins will trigger the interrupt

<span class="sf_code_syntax_project">void initPinChangeInterrupt18(<span class="sf_code_syntax_project">void){
	/* set pin-change interrupt for D pins */
  PCICR |= (<span class="sf_code_syntax_number">1 << PCIE2);
	/* set mask to look for PCINT18 / PD2 */     
  PCMSK2 |= (<span class="sf_code_syntax_number">1 << PCINT18);
	/* this will also work for the pin mask */  
  <span class="sf_code_syntax_comment">// PCMSK2 |= (1 << PD2);
	/* set (global) interrupt enable bit */   

/* ---------------------------------------------- */

<span class="sf_code_syntax_project">void initPinChangeInterrupt(<span class="sf_code_syntax_project">void) {
	/* enable Pin-change interrupts 1 (bank C) */
  PCICR |= (<span class="sf_code_syntax_number">1 << PCIE1);
	/* enable specific interrupt for our pin PC1 */    
  PCMSK1 |= (<span class="sf_code_syntax_number">1 << PC1);   

ISR(PCINT1_vect) {
	/* count this change */
	/* output mode */
  CAP_SENSOR_DDR |= (<span class="sf_code_syntax_number">1 << CAP_SENSOR);
	/* charging delay */                  
  _delay_us(<span class="sf_code_syntax_number">1);                                      

	/* set as input */
  CAP_SENSOR_DDR &= ~(<span class="sf_code_syntax_number">1 << CAP_SENSOR);  
	/* clear the pin-change interrupt */                
	PCIFR |= (<span class="sf_code_syntax_number">1 << PCIF1);             

sei() – Globally enable interrupts

cli() – Globally disable interrupts

Interrupts are disabled when inside of a ISR()

avr-libc: <avr/interrupt.h>: Interrupts

Timers / Counters

Counter can be connected to the system clock for and external pin. Value is stored in TCNTn register. This register can be read and written to, a write can be used to reset the register to 0.

Clock prescaller setting the TCCRnB register using the CSx0, CSx1, CSx2 Clock Select x bits

TCCR1B |= (1 << CS11) | (1 << CS10);

There are multiple modes for the Waveform

Normal Mode – counts until max value for the counter and stores value in TCNTn register.

CTC Mode – TCNTn is compared value to OCRnx register and triggers when they match


PORTC connected to ADC on atmega328p

To make full use of the ADC you can or must set:

  • The ADC clock prescaler (default: disabled, which means no conversions)
    • The ADC on the mega chips does not work at full clock rate and must be scaled down to between 50khz and 200 khz

  • The voltage reference that defines the full scale (default: an externally supplied voltage)
  • The analog channel to sample from (default: external pin PC0)
  • An ADC trigger source (default: free-running if specified)
  • Interrupts to call when an ADC conversion is complete (default: none)
  • Other miscellaneous options including an 8-bit mode, turning off the digital input circuitry to save power, and more

There are two modes:

  • Free running – Continually runs conversions.
  • Single Conversion – executes a single run to create a sample and will only start a next run when ADSC is set HIGH

When a conversion is complete, the result is written to the ADC Data Registers (ADCL and ADCH), and the ADC Interrupt Flag (ADCSRA.ADIF) is set. In Single Conversion mode, ADCSRA.ADSC is cleared simultaneously. The software may then set ADCSRA.ADSC again, and a new conversion will be initiated on the first rising ADC clock edge.

ADLAR – Left Adjust, Shift the bit up into the ADCH register. Limit is the top 8bits in resolution.

Mux Bits

|Bit 7 |Bit 6 |Bit 5 |Bit 4 |Bit 3 |Bit 2 |Bit 1 |Bit 0 |
|REFS1 |REFS0 |ADLAR |   -  | MUX3 | MUX2 | MUX1 | MUX0 |

#coding/c #microcontroller/avr #7-notes

Share on

Ben Mason
Ben Mason
Computer Security – Reverse Engineering – Malware – Electronics Hobbyist – Sometimes Photographer – Spaceflight – Cat Enthusiast