0
\$\begingroup\$

As a test I'm trying to set up the ADC of an ATtiny404 to read from pin B0, then cause a delay in blinking an LED on pin A1.

However, it seems to blink at the same rate (~1 Hz) regardless of what I do to pin B0, so something seems to be missing, but I'm not sure what. Various tutorials that I've found for ATtiny ADCs reference the ATtiny85 and rely on other pin definitions than the ATtiny404 uses, so it's a little confusing trying to translate.

Per the ATtiny404 datasheet:

To use pin n as an input, bit n in the PORTx.DIR register must be written to '0' to disable the output driver

The analog input channel is selected by writing to the MUXPOS bits in the MUXPOS register (ADC.MUXPOS). Any of the ADC input pins, GND, can be selected as single-ended input to the ADC. The ADC is enabled by writing a ‘1’ to the ADC ENABLE bit in the Control A register (ADC.CTRLA). Voltage reference and input channel selections will not go into effect before the ADC is enabled. The ADC does not consume power when the ENABLE bit in ADC.CTRLA is ‘0’. The ADC generates a 10-bit result that can be read from the Result Register (ADC.RES). The result is presented right adjusted.

Once the input channel is selected by writing to the MUXPOS register (ADCn.MUXPOS), a conversion is triggered by writing a '1' to the ADC Start Conversion bit (STCONV) in the Command register (ADCn.COMMAND). This bit is '1' as long as the conversion is in progress. In Single Conversion mode, STCONV is cleared by hardware when the conversion is completed.

#define F_CPU 3660000UL
#include <avr/io.h>
#define __DELAY_BACKWARD_COMPATIBLE__
#include <util/delay.h>

void initADC(void)
{
    // Disable digital input buffer
    PORTB.PIN0CTRL &= ~PORT_ISC_gm;
    PORTB.PIN0CTRL |= PORT_ISC_INPUT_DISABLE_gc;

    // Disable pull-up resistor
    PORTB.PIN0CTRL &= ~PORT_PULLUPEN_bm;

    ADC0.CTRLC = ADC_PRESC_DIV4_gc /* CLK_PER divided by 4 */
      | ADC_REFSEL_INTREF_gc; /* Internal reference */
    ADC0.CTRLA = ADC_ENABLE_bm /* ADC Enable: enabled */
      | ADC_RESSEL_10BIT_gc; /* 10-bit mode */

    /* Select ADC channel */
    ADC0.MUXPOS = ADC_MUXPOS_AIN9_gc;
}

uint16_t read_ADC(void)
{
    /* Start ADC conversion */
    ADC0.COMMAND = ADC_STCONV_bm;

    /* Wait until ADC conversion done */
    while ( !(ADC0.INTFLAGS & ADC_RESRDY_bm) );

    /* Clear the interrupt flag by writing 1: */
    ADC0.INTFLAGS = ADC_RESRDY_bm;

    return ADC0.RES; // return 10-bit result as 16-bit number
}

int main(void)
{
    uint16_t reading = 0;

    //PA1 is LED
    PORTA.DIR = 0b11111110; //Reserve pin A0 for UPDI, but other A pins are outputs
    PORTB.DIR = 0b1110; //Read from pin B0

    initADC();
    
    while (1) 
    {
        reading = read_ADC();

        PORTA.OUT = 0b10; // set HIGH;
        _delay_ms(reading+10);
        PORTA.OUT = 0b00; // set LOW;
        _delay_ms(reading+10);
        
    }
}

Updated to reflect a good example doc for programming ADC on ATtinys, but the issue persists that the pin doesn't respond to different inputs.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Maybe the problem lies in the fact that you have "inserted" a variable in the delay loop. Try this version instead:

#define F_CPU 3660000UL
#include <avr/io.h>
#include <util/delay.h>

void initADC(void)
{
    // Disable digital input buffer
    PORTB.PIN0CTRL &= ~PORT_ISC_gm;
    PORTB.PIN0CTRL |= PORT_ISC_INPUT_DISABLE_gc;

    // Disable pull-up resistor
    PORTB.PIN0CTRL &= ~PORT_PULLUPEN_bm;

    ADC0.CTRLC = ADC_PRESC_DIV4_gc /* CLK_PER divided by 4 */
    | ADC_REFSEL_INTREF_gc; /* Internal reference */
    ADC0.CTRLA = ADC_ENABLE_bm /* ADC Enable: enabled */
    | ADC_RESSEL_10BIT_gc; /* 10-bit mode */

    /* Select ADC channel PB0 */
    ADC0.MUXPOS = ADC_MUXPOS_AIN11_gc;
}

void Delay_ms( uint16_t ms ) 
{
    while ( ms-- ) 
    {
      _delay_ms( 1 );   
    }
}

uint16_t read_ADC(void)
{
    /* Start ADC conversion */
    ADC0.COMMAND = ADC_STCONV_bm;

    /* Wait until ADC conversion done */
    while ( !(ADC0.INTFLAGS & ADC_RESRDY_bm) );

    /* Clear the interrupt flag by writing 1: */
    ADC0.INTFLAGS = ADC_RESRDY_bm;

    return ADC0.RES; // return 10-bit result as 16-bit number
}

int main(void)
{
    uint16_t reading = 0;

    //PA1 is LED
    PORTA.DIR = 0b11111110; //Reserve pin A0 for UPDI, but other A pins are outputs
    PORTB.DIR = 0b1110; //Read from pin B0

    initADC();
    
    while (1)
    {
        reading = read_ADC();

        PORTA.OUT = 0b10; // set HIGH;
        Delay_ms(reading+10);
        PORTA.OUT = 0b00; // set LOW;
        Delay_ms(reading+10);
        
    }
}

I also noticed the PB0 is AIN11, not AIN9 (AIN9 is not available). So I changed it in the code as well.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.