Testing Attiny45 ADC functionality in Proteus

Here I want to share simple Attiny45 ADC circuit. The Attiny has is 10-bit ADC hardware internally. To test this ADC functionality, I have connected a $10 k \Omega$ potentiometer to the pin PB4 and connected a LED to pin PB0. The Attiny45 microcontroller has 4 ADC pins which are PB2,PB3,PB4 and PB5.

ATtiny45 ADC Pin Mapping

ADC ChannelPhysical PinPort Pin
ADC0Pin 1PB5 (Also the Reset Pin*)
ADC1Pin 7PB2
ADC2Pin 3PB4
ADC3Pin 2PB3
This can be visualized using the ATtiny45 IC pinout explorer.

Attiny45 ADC pins

Remember that PB5 is also the Reset pin so usually it is not used. 

So the test is to use the potentiometer to input analog signal to the Attiny45 via PB4 and if the voltage is above 2.5V then the LED connected to pin PB0 is turned on. 

The following is the circuit diagram for testing the ADC functionality of ADC of Attiny45.

Testing Attiny45 ADC functionality in Proteus

The following is program code to test the functionality of Attiny45 ADC.

#include <avr/io.h>
#include <util/delay.h>

void adc_setup() {
    // 1. Set Voltage Reference to VCC (leave REFS0 at 0)
    // 2. Select Input Channel ADC2 (PB4) by setting MUX1 bit
    ADMUX |= (1 << MUX1);

    // 3. Set ADC Prescaler to 128 (ADPS0, ADPS1, ADPS2 = 1)
    // This scales the 9.6MHz or 1.2MHz clock down to a usable range (50-200kHz)
    ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);

    // 4. Enable the ADC
    ADCSRA |= (1 << ADEN);
}

uint16_t adc_read() {
    // Start the conversion
    ADCSRA |= (1 << ADSC);

    // Wait for conversion to finish (ADSC bit returns to 0)
    while (ADCSRA & (1 << ADSC));

    // Return the 10-bit result (ADCL + ADCH)
    return ADC;
}

int main(void) {
    // Set PB0 as an output (for an LED)
    DDRB |= (1 << DDB0);
    
    adc_setup();

    while (1) {
        uint16_t val = adc_read();

        // If input > ~2.5V (assuming 5V VCC, 512 is half of 1024)
        if (val > 512) {
            PORTB |= (1 << PB0);  // LED ON
        } else {
            PORTB &= ~(1 << PB0); // LED OFF
        }
        
        _delay_ms(50);
    }
}

This code is a classic "Threshold Switch." It translates an analog voltage into a digital action (turning an LED on or off). Because the ATtiny45 and ATtiny13 share very similar register structures for the ADC, this specific code works almost identically on both.

Here is the breakdown of what each section is doing:

1. The Configuration (adc_setup)

This function wakes up the ADC and tells it how to behave.

  • ADMUX |= (1 << MUX1); The ADMUX register (ADC Multiplexer Selection) acts like a rotary switch. Setting MUX1 to 1 connects the ADC hardware to PB4 (Pin 3). Note: By leaving the REFS bits at 0, you are telling the chip to use VCC as the "yardstick" for measurement.

  • ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); The ADC needs a clock speed between 50kHz and 200kHz to be accurate.

    • ADPS (Prescaler bits): Setting all three to 1 creates a division factor of 128.

    • If your chip is at 9.6MHz, the ADC runs at 75kHz.

    • If your chip is at 1.2MHz, the ADC runs at 9.3kHz (which is actually a bit slow for the ATtiny45, but it will still function).

  • ADCSRA |= (1 << ADEN); This is the "Power Button" for the ADC. Without setting the ADC Enable bit, the converter stays off to save power.


2. The Measurement (adc_read)

This function handles the "Sample and Hold" process.

  • ADCSRA |= (1 << ADSC); The Start Conversion bit tells the ADC to take a "snapshot" of the voltage on the pin right now.

  • while (ADCSRA & (1 << ADSC)); This is a "blocking" loop. The ADSC bit stays 1 as long as the chip is busy calculating. As soon as it finishes, the hardware flips the bit back to 0 and the code moves on.

  • return ADC; This returns a 10-bit number (0 to 1023). Even though the result is split into two 8-bit registers (ADCL and ADCH), the compiler allows you to just read ADC to get the full 10-bit value at once.


3. The Logic (main)

  • DDRB |= (1 << DDB0); Configures PB0 (Pin 5) as an Output so it can drive the LED.

  • if (val > 512) Since the maximum value is 1023, 512 is the halfway point.

    • If the pin sees more than half of the supply voltage (> 2.5V on a 5V system), the LED turns on.

    • If it sees less, the LED turns off.

  • _delay_ms(50); This prevents the chip from running at full speed, which saves power and reduces "jitter" if the voltage is hovering right at 2.5V.

I like to add that while this code works, the ATtiny45 has a feature the ATtiny13 lacks: Internal Voltage References. If your battery voltage drops, your "2.5V threshold" will actually shift because the reference (VCC) is changing. On the ATtiny45, you could change ADMUX to use the Internal 1.1V or 2.56V reference, which stays steady even if your power supply fluctuates.

The following is how to create and simulate the circuit.




Related


Post a Comment

Previous Post Next Post