Solved: ATtiny 412 Multiple Analog Inputs using ADC0

Go To Last Post
5 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

 

You might have seen me post a few times before about the ATtiny 412, my apologizes again.I've been going through the tutorials for the series-1 ATtiny and in particular I'm on the ADC tutorial & I've been able to setup a single analog input, which was able to work great. However, when I need to add a second analog input I'm having trouble. From my understanding, the 412 has only the ADC0, but I should be able to grab multiple analog inputs in series vs. parallel (let me know if my thinking is wrong) as long as I'm okay with the slight delay, which isn't an issue for me. 

 

The pins I'm trying to get the analog input are from are PA7 & PA1. PA 7 will be used to measure the analog value from a mic & PA1 will be used to measure the voltage of an external battery via a voltage divider (9V to 24 V using 100k and 10k resistors). Both measurements are not continuous readings, values will be gathered during a specified time. 

 

The code I have is attached to this thread. Any help or guidance would be much appreciated. 

 

 

Thanks,

Ankit 

#define F_CPU 16000000UL
#include <avr/io.h>
#include <inttypes.h>
#include <avr/interrupt.h>
#include <time.h>
#include <util/delay.h>

unsigned int peakToPeak;
unsigned int signalMax;
unsigned int signalMin;
unsigned int sample;
unsigned int sample2;
float voltage;

uint16_t ms1 = 0;
uint64_t old_millis = 0;

volatile uint64_t millis1;           

void initTimerB(){
    TCB0.CCMP = 15999;
    TCB0.CTRLA = TCB_CLKSEL_CLKDIV1_gc | TCB_ENABLE_bm;
    TCB0.INTCTRL = TCB_CAPT_bm;     

}

ISR(TCB0_INT_vect){
    millis1++;
    TCB0.INTFLAGS = TCB_CAPT_bm;
}

uint64_t millis()
{
   uint64_t m;
   cli();
   m = millis1;
   sei();
   return m;
}

void initADC()
{

    ADC0.CTRLC = ADC_REFSEL_INTREF_gc | ADC_PRESC_DIV16_gc;
    ADC0.MUXPOS = ADC_MUXPOS_AIN7_gc  | ADC_MUXPOS_AIN1_gc;
    ADC0.CTRLA = ADC_RESSEL_10BIT_gc | ADC_ENABLE_bm;

}	

FUSES = {
    .WDTCFG     = FUSE_WDTCFG_DEFAULT,
    .BODCFG     = FUSE_BODCFG_DEFAULT,
    .OSCCFG     = FREQSEL_16MHZ_gc,     //Select 16MHz OSC
    .TCD0CFG    = FUSE_TCD0CFG_DEFAULT,
    .SYSCFG0    = FUSE_SYSCFG0_DEFAULT,
    .SYSCFG1    = FUSE_SYSCFG1_DEFAULT,
    .APPEND     = FUSE_APPEND_DEFAULT,
    .BOOTEND    = FUSE_BOOTEND_DEFAULT,
};

int main(void)
{   _PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, CLKCTRL_PDIV_2X_gc | !CLKCTRL_PEN_bm);
    _PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, !CLKCTRL_CLKOUT_bm | CLKCTRL_CLKSEL_EXTCLK_gc);

 	/* Set pins as i/o */
    PORTA.OUTCLR = PORTA.DIRCLR = PIN1_bm; // input.
//	PORTA.OUTCLR = PORTA.DIRSET = PIN2_bm; // input. not used
//  PORTA.OUTCLR = PORTA.DIRCLR = PIN3_bm; // input. not used
//	PORTA.OUTCLR = PORTA.DIRSET = PIN6_bm; // input. not used
	PORTA.OUTCLR = PORTA.DIRCLR = PIN7_bm; // input.

	initADC();
    initTimerB();

    while(1)
    {unsigned int peakToPeak = 0;   // peak-to-peak level
     unsigned long startMillis = millis();
	 unsigned int signalMax = 0;
	 unsigned int signalMin = 600;

       while (millis() - startMillis < 10)
       {
		ADC0.MUXPOS|= 0x07;                   // ADC input in pin 7.
	    ADC0.COMMAND |= 1; // start running ADC
        if (ADC0.INTFLAGS)                    // if an ADC result is ready
	    {sample = (ADC0.RES);}//reading from analog input

        _delay_ms(1); //increase this to 2 or 3 ms if problems occur
        if (sample < 600)  // toss out spurious readings
		 {
		  if (sample > signalMax)
		  { signalMax = sample;}  // save just the max levels

		  else if (sample < signalMin)
		   { signalMin = sample;
           }// save just the min levels

          peakToPeak = signalMax - signalMin; 

        }}

        while(millis() - startMillis > 10 && millis() - startMillis < 20)
        {
         ADC0.MUXPOS|= 0x01;                   // ADC input in pin 1.
         ADC0.COMMAND |= 1; // start running ADC
           if (ADC0.INTFLAGS)                    // if an ADC result is ready
	    {sample2 = (ADC0.RES);}
	    _delay_ms(2);
	    voltage = ((sample2*5)/1024)/.0909; //100k & 10K resistors for voltage divider

        }
    }}
    

 

This topic has a solution.
Last Edited: Tue. Dec 10, 2019 - 07:26 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The Tiny412 has ONE A-D converter but it has a 6-channel analog multiplexer so that one ADC can read 6 different sources.

 

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1
ADC0.MUXPOS|= 0x07;

This command sets 3 lowest bits in MUXPOS to 1 without touching other. The MUXPOS has value xxxxx111 (x - value don't changed by command).

ADC0.MUXPOS|= 0x01;

This command sets the lowest bit in MUXPOS to 1 without touching other. The MUXPOS value don't change and still is xxxxx111.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In old school AVRs the ADMUX register used to do three different things (select reference, possibly enable left adjust, pick MUX channel). As such if you wanted to change just the mux bits you had two options:

 

1) read current ADMUX, mask out the non-MUX bits, clear the MUX bits, OR in the new MUX setting, write back, or

 

2) do a plain write - but as well as the new MUX selection add to it the (fixed) reference and left-adjust bits

 

But these new style AVRs have control registers right, left and center. They tend to have a whole register for every setting.

 

awit already identified that the problem is you ORing (without first clearing any bits that might already be set using AND). The solution is remarkably simple in 412 because the MUXPOS register has nothing but MUX bits. Just change your |='s to be plain ='s.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks guys, that was the trick! Something so simple, but I'm glad it is working now. I'm now getting both analog signals using ADC0, & it's working beautifully.

 

Cheers,

Ankit