ADC on ATMEGA328

Hey guys, I am trying to read 2 sharp range sensors, and use the value from those sensors to independantly control the speed of the motors. Instead what happens is that the motors both go the same speed, the speed does change but they both go that same speed! No Independant control

My code:

#include <pololu/orangutan.h>

unsigned int CONV1;
unsigned int CONV2;
unsigned int sample;
unsigned int sample2;
unsigned char samples;
void set_mode();

int main()
{
  while(1)
    {
   set_mode();
  
	Start_Conversion(0);
	if(ADSC!=1){
	Start_Conversion(0);
	
	CONV1=ADCH;
            ADCH=0;
	}
	
 	Start_Conversion(1);
	set_mode();
	if(ADSC!=1){
	Start_Conversion(1);
	CONV2=ADCH;
	ADCH=0;
	}

set_m1_speed(CONV1);
set_m2_speed(CONV2);
	
    }
}

void set_mode()
{
		ADMUX |= 1 << ADLAR;	
}

void Start_Conversion(unsigned int channel)
{

 ADCSRA = 0x87;
 ADMUX |= 1 << 6;
 ADMUX &= ~(1 << 7);
 ADMUX &= 0xE0;
 ADMUX |= channel;  
 ADCSRA |= 1 << ADSC; // start the conversion
 
}

Hello,

You should really use the “Code” button when making a post so that your code is readable.

Anyway, it looks like you are very confused about how your code is supposed to work. How about you either try putting in detailed comments to explain what you think is going to happen or else just using the Analog Input Functions in the Pololu AVR Library?

-Paul

Hello.

You’re not actually waiting for the ADC conversion to finish before reading the result, which would explain the behavior you’re seeing. Also, ADSC is a constant, so I don’t know what you’re hoping to accomplish with “if (ADSC != 1)”. In general, your code implies you don’t quite yet understand how to use the ADC on the AVR.

If you use the analog functions in the Pololu AVR library to read the sensors, your code would then become something like:

int main()
{
  set_analog_mode(MODE_8_BIT);
  while (1)
  {
    set_motors(analog_read(0), analog_read(1));
  }
}

You’d have to do something slightly more fancy if you want the motors to spin in the other direction.

- Ben

Hey Ben i just copied that exact code, and I am getting the same behavior.

Also I get the ISP mode error so many times, infact most of the time it wont even program. Right now I cant get it to program. I just unistnalled and re-installed the driver and restarted my computer.

I had this same problem with the sv-168 and i had to buy this sv-328, I thought I screwed up the last one, apparently this issue is common?

I suspect that if you get the exact same behavior, you probably did not successfully load the new program, or you have electrical issues that are causing both analog input channels to see the same voltage.

What you’re describing is not a common issue. How are you powering your SV-328 and what programmer are you using?

- Ben

I’m using this package: pololu.com/catalog/product/1305

I’m powering it using 4 NIMH batteries

You are definitely underpowering your device, which would explain the behavior you’re seeing. The minimum operating voltage for the Orangutan SV-328 is 6V, and you are giving it a nominal voltage of 4.8V (possibly closer to 4V if your batteries are low). The Orangutan SV has is configured to brownout at around 4.3 V, and the programmer you are using will not allow you to program if your target voltage is too low since programming an underpowered AVR can permanently disable it. If you were powering your previous Orangutan SV-168 this way, that could explain why it stopped working.

I recommend you use at least six NiMH cells (though you could possibly get by with five if you don’t let them get too discharged). Please let me know if increasing your input voltage doesn’t fix your problems.

- Ben

Hey Ben, thanks that was the problem :).

I just wish I could understand what is so wrong about my code. I am trying to understand what is happening as this is for a project of mine, and if I just use the library functions I wont.

Thanks

Hello,

Understanding what you are doing is definitely a worthwhile goal. If you have everything working the way we recommend doing it, we would be happy to take a look at your own version of the code to help you find what is wrong with it.

You should start by following the advice we already gave you:

  • use the “code” button to post your code (after simplifying it as much as possible - e.g. no motor code)
  • add comments to explain exactly what you think should happen on each line
  • wait for the ADC conversion to finish
  • do not do “if (ADSC != 1)”

Also, if you are going to ask for more help, please also post the code (done our way) that worked for you, and tell us that it worked!

-Paul

If you want to understand how everything works, you should look at the Pololu AVR C/C++ Library source code. Most of it is pretty well commented and easy to follow (especially if you have the ATmega328p datasheet handy). You can compare the library code to your own code. If your own function is not working, try copying the analogous library function in to your project, so that you have one working function and one not-working function in your project. Then try changing the working function to be like your other function in small steps. Eventually you will find a step that makes the working function stop working, and you’ll learn something.

-David

Hey guys I’ve been reading through the Pololu functions and I believe I understand the PWM now but I have another problem.

I want to make a heartbeat using an Interrupt. I believe the PWM uses Timer0 and Timer2, so I am planning on using Timer1. My code is as follows:

ASSR=0x00; 
TCCR1A=0x0F; 
TCNT1=0x00; 
OCR1A=0xFF; 
TIMSK1=0x02;  

ISR(TIMER1_COMPA_vect)
{

red_led(1);

}

I found the concept online and modified it but still not working.

The code you’ve posted will not even compile, so it’s hard to give you feedback about why it’s not working for you. Can you post the simplest complete program that you think should work? Also, have you looked at all at the ATmega328P datasheet to verify that you’re setting these register values correctly, or are you just blindly modifying something you saw elsewhere? From the values you’re using, it really looks like the latter.

- Ben

#include <pololu/orangutan.h>
#include <avr/interrupt.h>
#include <avr/io.h>

int main()

{
TCCR1A=0x80; 
TCNT1=0x00;
OCR1A=0xFF;
TIMSK1=0x02;
}

ISR(TIMER1_COMPA_vect)
{
red_led(1);
}

I believe the TCNT1 will count until it reaches the 0xFF value in OCR1a, then go to the interrupt, and reset the TCNT1 value. I changed my values now. I believe 0x80 will enable the OC1A and the rest are zero’s to give CTC operation (count until match, then reset)

That looks a little better, but there are still some problems:

  1. As a general rule, you should never let program execution leave main(). You can accomplish this by putting an infinite loop at the bottom of main:
int main()
{
  // some code goes here
  while (1);  // loop forever
}
  1. You are not setting the timer for CTC mode. If you look at the register description for TCCR1A and TCCR1B in the datasheet you will see that these registers contain four bits split between them (WGM10:WGM13) that determine the timer mode, and CTC is not 0000. For CTC mode with OCR1A as the TOP timer value, you want WGM12 (in the TCCR1B register) set and the rest of the WGM1x bits cleared.

  2. You do not want to enable OC1A. You only want to enable OC1A or OC1B if you want to generate a hardware PWM output on pins OC1A and/or OC1B. If you just want to use the timer for internal timing purposes (which you do), then you want OC1A and OC1B disconnected from the timer (as you had in your original code).

  3. You haven’t selected a clock source for timer 1, and your code won’t do anything until you do. This is accomplished by configuring the CS12:CS10 bits in the TCCR1B register. These bits are 000 by default, which selects for no clock source (timer 1 is stopped). You should pick a prescaler that gives you the heartbeat timing you want when combined with your TOP value.

  4. Interrupts are globally disabled by default, so you need to set the global interrupt enable by calling sei(). You can clear the global interrupt enable with cli().

- Ben

Hello.

I just skimmed this so I’m not sure if someone has mentioned it, but it looks like you aren’t ever turning off the LED; you won’t get much of a heartbeat without that.

- Jan

Thanks for the help guys, I got it working in the morning but didn’t have time to post:

#include <pololu/orangutan.h>
#include <avr/interrupt.h>
#include <avr/io.h>

int main()

{
sei();

TCCR1B=0x0C;
TCCR1A=0x00; 
TCNT1=0x00;
OCR1A=0x00FF;
TIMSK1=0x02;

while (1)
{
red_led(1)
delay_ms(500);
}
}

ISR(TIMER1_COMPA_vect)
{
red_led(0);
}

I’m glad to hear you got it working, but the way you’re handling the LED seems a bit strange. Unless you get the timing just right, you’re going to get an uneven and varying heartbeat. Also, by handling half the heartbeat in your main loop, you completely defeat the purpose of using an interrupt (you could easily just make a heart beat in your main loop without using interrupts; the benefit of using an interrupt is that the heartbeat will occur automatically while the rest of your main loop runs). If you want to have an even heartbeat, you can do something as simple as:

int main()
{
  // code to setup timer
  while (1);
}

ISR(TIMER1_COMPA_vect)
{
  red_led(TOGGLE);
}

The TOGGLE argument makes the red LED change state (from off to on or on to off) every time the timer interrupt occurs.

- Ben