Atmega32 Problems Recovered
Atmega32 Problems Recovered
ewcommand[1]1
ewcommand[1]1
ewcommand[1]1
ewcounterproblem
ewenvironmentproblem[1]
Problem : 1
ewenvironmentsolution[1]
Solution : 1
—1—
ATmega32 Register-Level
Programming in C
Register Purpose
DDRx Data Direction Register (0=Input, 1=Output)
PORTx Output / Pull-up control
PINx Read digital input
MCUCR INT0/INT1 sense control
GICR Global Interrupt Control Register
GIFR Global Interrupt Flag Register
TIMSK Timer Interrupt Mask
TCCR0/1/2 Timer/Counter Control
ADMUX ADC Multiplexer / Reference
ADCSRA ADC Control & Status
ADCH/L ADC Data Registers
SREG Status Register (bit 7 = Global IE)
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
Compiled for self-study purposes — ATmega32 Datasheet reference: Atmel/Microchip doc 2503
—2—
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
Contents
ewpage
1. Introduction
ewpage
LED Blink on PORTB Pin 0 Configure PB0 as output and blink an LED at approximately
1 Hz using a software delay loop. No external libraries; pure register-level delay.
Registers used: DDRB, PORTB
LED Blink on PORTB Pin 0
1 # define F_CPU 8000000 UL
2 # include < avr / io .h >
3
4 /* Software delay ~ 1 second at 8 MHz */
5 void delay_ms ( uint16_t ms ) {
6 uint32_t i ;
—1—
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
7 for (; ms > 0; ms - -)
8 for ( i = 0; i < 800; i ++) /* ~1 ms loop */
9 __asm__ __volatile__ ( " nop " ) ;
10
11 int main ( void ) {
12 DDRB |= (1 << PB0 ) ; /* PB0 = output */
13 PORTB &= ~(1 << PB0 ) ; /* LED off initially */
14
15 while (1) {
16 PORTB |= (1 << PB0 ) ; /* LED ON */
17 delay_ms (500) ;
18 PORTB &= ~(1 << PB0 ) ; /* LED OFF */
19 delay_ms (500) ;
20 }
21 }
Note: The inner loop count (800) is an approximation; calibrate with an oscilloscope for
exact timing. Use util/delay.h when precision matters.
Push-Button Controlled LED Read a push-button connected to PD2 (with internal pull-up
enabled). Light an LED on PB1 while the button is pressed.
Registers used: DDRD, PORTD, PIND, DDRB, PORTB
Push-Button Controlled LED
1 # include < avr / io .h >
2
3 int main ( void ) {
4 /* PD2 = input with internal pull - up */
5 DDRD &= ~(1 << PD2 ) ;
6 PORTD |= (1 << PD2 ) ;
7
8 /* PB1 = output */
9 DDRB |= (1 << PB1 ) ;
10
11 while (1) {
12 /* Button active - low : pressed = > PD2 reads 0 */
13 if (!( PIND & (1 << PD2 ) ) )
14 PORTB |= (1 << PB1 ) ; /* LED ON */
15 else
16 PORTB &= ~(1 << PB1 ) ; /* LED OFF */
17 }
8-bit Binary Counter on PORTA Display an incrementing 8-bit counter on all 8 pins of
PORTA (each pin drives an LED). The counter increments every 200 ms.
Registers used: DDRA, PORTA
8-bit Binary Counter on PORTA
1 # define F_CPU 8000000 UL
2 # include < avr / io .h >
3 # include < util / delay .h >
4
5 int main ( void ) {
6 DDRA = 0 xFF ; /* All PORTA pins = output */
—2—
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
7 PORTA = 0 x00 ;
8
9 uint8_t count = 0;
10
11 while (1) {
12 PORTA = count ++; /* Write counter value directly */
13 _delay_ms (200) ;
14 }
Note: After 255 the counter wraps back to 0 automatically due to uint8_t overflow —
perfect for a cycling display.
Shift Register: LED Chaser on PORTC Create a Knight-Rider style LED chaser on all 8
pins of PORTC. A single lit LED bounces left and right with a 100 ms step.
Registers used: DDRC, PORTC
Shift Register: LED Chaser on PORTC
1 # define F_CPU 8000000 UL
2 # include < avr / io .h >
3 # include < util / delay .h >
4
5 int main ( void ) {
6 DDRC = 0 xFF ;
7 PORTC = 0 x00 ;
8
9 uint8_t pos = 0;
10 int8_t dir = 1; /* +1 = left shift , -1 = right shift */
11
12 while (1) {
13 PORTC = (1 << pos ) ;
14 _delay_ms (100) ;
15
16 /* Bounce at edges */
17 if ( pos == 7) dir = -1;
18 if ( pos == 0) dir = 1;
19 pos += dir ;
20 }
ewpage
Toggle LED on INT0 Falling Edge Each time the button on INT0 (PD2) is pressed (falling
edge), toggle an LED on PB0.
Registers used: MCUCR, GICR, SREG, DDRB, PORTB
Toggle LED on INT0 Falling Edge
1 # include < avr / io .h >
2 # include < avr / interrupt .h >
—3—
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
3
4 ISR ( INT0_vect ) {
5 PORTB ^= (1 << PB0 ) ; /* Toggle LED */
6
7 int main ( void ) {
8 /* PB0 = output */
9 DDRB |= (1 << PB0 ) ;
10
11 /* INT0 = falling edge ( ISC01 =1 , ISC00 =0) */
12 MCUCR |= (1 << ISC01 ) ;
13 MCUCR &= ~(1 << ISC00 ) ;
14
Event Counter Using INT1 Count button presses on INT1 (PD3). Display the 8-bit count
on PORTA (LEDs). Reset the count when it reaches 10.
Registers used: MCUCR, GICR, DDRA, PORTA
Event Counter Using INT1
1 # include < avr / io .h >
2 # include < avr / interrupt .h >
3
—4—
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
Dual Interrupt Priority: INT0 and INT1 INT0 turns ON an LED on PB2; INT1 turns it
OFF. Both react on rising edge. Demonstrate that both interrupts operate independently.
Registers used: MCUCR, GICR
Dual Interrupt Priority: INT0 and INT1
1 # include < avr / io .h >
2 # include < avr / interrupt .h >
3
4 ISR ( INT0_vect ) {
5 PORTB |= (1 << PB2 ) ; /* LED ON */
6
7 ISR ( INT1_vect ) {
8 PORTB &= ~(1 << PB2 ) ; /* LED OFF */
9 }
10
11 int main ( void ) {
12 DDRB |= (1 << PB2 ) ; /* PB2 output */
13
14 /* PD2 , PD3 inputs with pull - ups */
15 DDRD &= ~((1 << PD2 ) | (1 << PD3 ) ) ;
16 PORTD |= ((1 << PD2 ) | (1 << PD3 ) ) ;
17
18 /* Both on rising edge : ISCx1 =1 , ISCx0 =1 */
19 MCUCR |= (1 << ISC01 ) | (1 << ISC00 ) /* INT0 */
20 | (1 << ISC11 ) | (1 << ISC10 ) ; /* INT1 */
21
22 GICR |= (1 << INT0 ) | (1 << INT1 ) ;
23 sei () ;
24
25 while (1) {}
26 }
ewpage
Timer0 Overflow — 1 Hz LED Blink Use Timer0 overflow interrupt (prescaler 1024) to
blink an LED on PB3 every 1 second. FCPU = 8 MHz.
Registers: TCCR0, TIMSK, TCNT0
256×1024
Calculation: Toverf low = 8 000 000
= 32.768 ms, so count 30 overflows ≈ 1 s.
Timer0 Overflow — 1 Hz LED Blink
1 # include < avr / io .h >
2 # include < avr / interrupt .h >
3
—5—
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
Timer0 CTC Mode — Precise 500 Hz Toggle Configure Timer0 in CTC mode to generate
an exact 500 Hz square wave on OC0 (PB3).
FCP U 8 000 000
Calculation: OCR0 = 2×prescaler×f
−1= 2×64×500
− 1 = 124
Timer0 CTC Mode — Precise 500 Hz Toggle
1 # include < avr / io .h >
2
In CTC mode the hardware toggles OC0 each compare match with no ISR needed — zero
CPU overhead.
Timer1 — Measure Pulse Width (Input Capture) Use Timer1 Input Capture to measure
the width of a pulse on ICP1 (PD6). Store rising-edge and falling-edge timestamps; output
result via a global variable.
Registers: TCCR1A, TCCR1B, TIMSK, ICR1
Timer1 — Measure Pulse Width (Input Capture)
1 # include < avr / io .h >
2 # include < avr / interrupt .h >
3
4 volatile uint16_t rise_time = 0;
5 volatile uint16_t pulse_width = 0;
—6—
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
ewpage Timer2 PWM — LED Brightness Control Generate an 8-bit Fast PWM signal
on OC2 (PD7) to control LED brightness. Fade from 0% to 100% and back continuously.
Registers: TCCR2, OCR2, DDRD
Timer2 PWM — LED Brightness Control
1 # define F_CPU 8000000 UL
2 # include < avr / io .h >
3 # include < util / delay .h >
4
—7—
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
18 while (1) {
19 OCR2 = duty ; /* Set duty cycle (0 -255) */
20 _delay_ms (10) ;
21
22 if ( duty == 255) step = -1;
23 if ( duty == 0) step = 1;
24 duty += step ;
25 }
ewpage
5. ADC Problems
Single-Channel ADC Read (Polling) Read a potentiometer on ADC0 (PA0) using polling
mode. Display the 8 MSBs of the 10-bit result on PORTB.
Registers: ADMUX, ADCSRA, ADCH, ADCL
Single-Channel ADC Read (Polling)
1 # include < avr / io .h >
2
3 /* Blocking ADC read on given channel (0 -7) ; returns 10 - bit result */
4 uint16_t adc_read ( uint8_t ch ) {
5 /* Select channel , AVCC reference , left - adjust off */
6 ADMUX = (1 << REFS0 ) | ( ch & 0 x07 ) ;
7
8 /* Start conversion */
9 ADCSRA |= (1 << ADSC ) ;
10
11 /* Wait until conversion complete ( ADSC goes low ) */
12 while ( ADCSRA & (1 << ADSC ) ) ;
13
ADC clock requirement: 50–200 kHz for full 10-bit accuracy. At 8 MHz: prescaler 64
gives 125 kHz — ideal.
ADC with Interrupt — Non-Blocking Read Trigger a new ADC conversion every 10 ms
using Timer0 overflow. Store results in a circular buffer of 8 samples using the ADC
—8—
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
complete interrupt.
Registers: ADCSRA, ADMUX, TIMSK
ADC with Interrupt — Non-Blocking Read
1 # include < avr / io .h >
2 # include < avr / interrupt .h >
3
4 # define BUF_SIZE 8
5 volatile uint16_t adc_buf [ BUF_SIZE ];
6 volatile uint8_t buf_idx = 0;
7
8 /* ADC conversion complete */
9 ISR ( ADC_vect ) {
10 adc_buf [ buf_idx ] = ADC ;
11 buf_idx = ( buf_idx + 1) % BUF_SIZE ;
12
13 /* Timer0 overflow -- triggers a new ADC conversion */
14 ISR ( TIMER0_OVF_vect ) {
15 ADCSRA |= (1 << ADSC ) ; /* Start next conversion */
16 }
17
4 # define NUM_CH 4
5 volatile uint16_t adc_result [ NUM_CH ];
6 volatile uint8_t cur_ch = 0;
7
8 ISR ( ADC_vect ) {
9 adc_result [ cur_ch ] = ADC ; /* Save result */
10 cur_ch = ( cur_ch + 1) % NUM_CH ; /* Next channel */
11
12 /* Select next channel */
—9—
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
ewpage Light-Dependent Resistor (LDR) Threshold Alert Read an LDR on ADC1. If the
ADC value falls below 200 (darkness threshold), light an LED on PB5; otherwise keep it
off.
Registers: ADMUX, ADCSRA, DDRB, PORTB
Light-Dependent Resistor (LDR) Threshold Alert
1 # include < avr / io .h >
2
ewpage
— 10 —
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
36 while (1) {}
37 }
Emergency Stop with INT0 and ADC Motor Simulation Simulate a motor: PB4 is the
“motor running” LED toggled by Timer0 overflow. Pressing INT0 immediately stops the
motor (disables Timer0). An ADC on ADC2 reads speed; if ADC > 900 (over-speed), also
stop.
Emergency Stop with INT0 and ADC Motor Simulation
1 # include < avr / io .h >
2 # include < avr / interrupt .h >
3
4 volatile uint8_t motor_on = 1;
5
6 /* INT0 emergency stop */
7 ISR ( INT0_vect ) {
— 11 —
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
8 motor_on = 0;
9 TCCR0 = 0; /* Stop Timer0 */
10 PORTB &= ~(1 << PB4 ) ; /* Motor LED off */
11
12 /* Timer0 overflow : simulate motor running */
13 ISR ( TIMER0_OVF_vect ) {
14 PORTB ^= (1 << PB4 ) ; /* Blink = motor pulse */
15 }
16
17 uint16_t adc_read ( uint8_t ch ) {
18 ADMUX = (1 << REFS0 ) | ch ;
19 ADCSRA |= (1 << ADSC ) ;
20 while ( ADCSRA & (1 << ADSC ) ) ;
21 return ADC ;
22 }
23
24 int main ( void ) {
25 DDRB |= (1 << PB4 ) ;
26
27 /* INT0 falling edge */
28 MCUCR |= (1 << ISC01 ) ;
29 GICR |= (1 << INT0 ) ;
30
31 /* Timer0 prescaler 1024 */
32 TCCR0 = (1 << CS02 ) | (1 << CS00 ) ;
33 TIMSK |= (1 << TOIE0 ) ;
34
35 /* ADC setup */
36 ADCSRA = (1 << ADEN ) | (1 << ADPS2 ) | (1 << ADPS1 ) ;
37
38 sei () ;
39
40 while (1) {
41 if ( motor_on ) {
42 uint16_t speed = adc_read (2) ;
43 if ( speed > 900) { /* Over - speed check */
44 motor_on = 0;
45 TCCR0 = 0;
46 PORTB &= ~(1 << PB4 ) ;
47 }
48 }
49 }
50 }
Temperature Alarm System Read an NTC thermistor ADC value on ADC3. Use a lookup
or threshold: if ADC value > 700 (hot), blink a red LED on PB6 at 2 Hz (Timer2
overflow); otherwise keep a green LED on PB7 steady. A button on INT1 silences the
blink for 5 timer ticks.
Temperature Alarm System
1 # include < avr / io .h >
2 # include < avr / interrupt .h >
3
4 volatile uint8_t alarm_active = 0;
5 volatile uint8_t silence_count = 0;
6 volatile uint16_t temp_adc = 0;
7
— 12 —
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
8 ISR ( TIMER2_OVF_vect ) {
9 /* ~122 Hz overflow at prescaler 1024. Blink every ~61 ticks =
0.5 s */
10 static uint8_t ticks = 0;
11 ticks ++;
12
13 if ( silence_count > 0) {
14 silence_count - -;
15 PORTB &= ~(1 << PB6 ) ; /* Alarm LED off while silenced */
16 return ;
17 }
18
— 13 —
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
ADC-Controlled Timer Period Read a potentiometer on ADC0. Use the result to dynami-
cally change Timer1 OCR1A compare value, making an LED on PB0 blink faster or slower
according to the potentiometer position.
ADC-Controlled Timer Period
1 # include < avr / io .h >
2 # include < avr / interrupt .h >
3
4 volatile uint16_t adc_val = 512;
5
30 while (1) {
31 adc_val = adc_read (0) ; /* Read pot continuously */
32 }
33 }
Debounced Button Counter with ADC Display Use INT0 with a 20 ms software debounce
(via Timer0) to count button presses. Display count proportionally on a 3-LED bar graph
(PB0–PB2): 0 presses = no LEDs, 1–3 = one LED, 4–6 = two LEDs, 7+ = all three LEDs.
Also read ADC0 and display raw MSB nibble on PORTA.
Debounced Button Counter with ADC Display
1 # include < avr / io .h >
2 # include < avr / interrupt .h >
3
4 volatile uint8_t press_count = 0;
5 volatile uint8_t debouncing = 0;
6
7 /* INT0 : record press , start debounce timer */
8 ISR ( INT0_vect ) {
9 if (! debouncing ) {
10 press_count ++;
11 debouncing = 1;
— 14 —
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
ADC Free-Running Mode with Watchdog Overflow Alert Configure the ADC in free-
running mode on ADC0. Use Timer1 overflow as a 1-second heartbeat: if no ADC
conversion has completed within the heartbeat window, blink an error LED on PB7 rapidly
(potential sensor failure detection).
ADC Free-Running Mode with Watchdog Overflow Alert
1 # include < avr / io .h >
2 # include < avr / interrupt .h >
3
4 volatile uint8_t adc_fresh = 0;
— 15 —
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
Servo Motor Control via ADC Control a servo motor using Timer1 in Fast PWM mode
(50 Hz, 20 ms period). Read ADC0 to set the servo angle. Map ADC (0–1023) to PWM
pulse width (1 ms to 2 ms ≡ 0° to 180°).
ICR1 sets the period; OCR1A sets the pulse width.
Servo Motor Control via ADC
1 # include < avr / io .h >
2 # include < avr / interrupt .h >
3
4 volatile uint16_t servo_pos = 1000; /* 1 ms default (0 deg ) */
5
6 ISR ( ADC_vect ) {
7 /* Map 0 -1023 -> 625 -1250 (1 ms -2 ms at prescaler 8 , 16 - bit )
— 16 —
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
Servo pulse widths: 1 ms = 0°, 1.5 ms = 90°, 2 ms = 180° (typical). Always verify with
your specific servo datasheet.
ewpage UART-less ADC Data Logger (store to EEPROM) Sample ADC0 every second
(Timer1 CTC) and write 8-bit samples to EEPROM starting at address 0x00. After 50
samples, stop. A button on INT0 can restart logging (clears index).
Registers: EEAR, EEDR, EECR
UART-less ADC Data Logger (store to EEPROM)
1 # include < avr / io .h >
2 # include < avr / interrupt .h >
3 # include < avr / eeprom .h >
4
5 # define MAX_SAMPLES 50
6 volatile uint8_t sample_idx = 0;
7 volatile uint8_t logging = 1;
8
9 void e e p r o m _ w r i t e _ b y t e _ r e g ( uint16_t addr , uint8_t data ) {
10 while ( EECR & (1 << EEWE ) ) ; /* Wait for previous write */
11 EEAR = addr ;
12 EEDR = data ;
13 EECR |= (1 << EEMWE ) ; /* Master write enable */
14 EECR |= (1 << EEWE ) ; /* Start write */
15
— 17 —
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
58 while (1) {
59 /* Low - power or other work can go here */
60 }
61 }
— 18 —
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
— 19 —
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
This capstone problem integrates: free-running ADC, dual timer interrupts, external interrupt
with override logic, PWM output, and multi-LED display — all operating concurrently
through the interrupt system.
ewpage
— 20 —
ATmega32 Register-Level Programming in C IO • Interrupts • ADC
— 21 —