2
\$\begingroup\$

I am learning about AVR microcontroller programming and currently have a simple LED blinker set up on an ATtiny26.

A 10k potentiometer is connected to ADC7 (pin 7) as per the example in part 1 of this tutorial. The output pins of port B are connected to LEDs via transistors in a multiplexed array. (The circuit works just fine without the ADC involved.)

To test/learn, I am blinking a single LED and waiting for a number of milliseconds equal to what is read from the ADC. I am having a problem when I try to multiply the value to implement longer delays.

I thought the problem was that the variable I store the ADCH in was an int but needs to be something else because this is an 8-bit microcontroller and an int can only be 0-255. Substituting long in did not work, however.

Here is the code that works:

#define F_CPU 1000000UL
#define ADC_VREF_TYPE = 0x40
int SD = 1;
int main()
{
    // Set port A to output (1)
    DDRA = 0b11111111;
    // Set port B, pin 3, to input (0)
    DDRB &= ~(1 << PB3);
    // Set control and status register
    ADCSR = 0b11100111;
    // Set ADC multiplexer selection register
    ADMUX  = 0b00100111;

    while (1)
    {
        // Set SD (step delay) to value from ADC
        SD = ADCH;
        // Turn on LED (it is a multiplexer, so two pins are used)
        PORTA = 0b00010001;
        // Wait for SD milliseconds
        milli_delay(SD);
        // Turn off LED
        PORTA = 0b00000000;
        // Wait for SD milliseconds
        milli_delay(SD);
    }
}

The problem occurs when I try to use a multiplier like this:

SD = ADCH * 10;

When doing this, the LED will flash quickly at one end of the potentiometer sweep, but will light solid and stay that way at some point while turning the pot to slower speeds (larger ADCH values); essentially "locking up" and requiring a reset while the potentiometer is at the original position again. My guess is that I'm trying to store a value greater than the numeric type holds, but I can't seem to overcome this by changing the variable type. I've tried long and uint32_t but I don't really understand what the numeric types hold on this micro.

Q: What do I need to do so that I can implement delays ranging from 0 to say 2000 milliseconds?

FYI I have the following libraries included:

#include <inttypes.h>
#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
#include <math.h>

Edit:

I had placed _delay_ms(x) directly in my example to avoid posting the functions I am actually using which call _delay_ms(1) inside a loop. My thought was it would be more concise, but as one answer pointed out, it actually appeared to be a mistake.

\$\endgroup\$
2
  • \$\begingroup\$ can you post a schematic of your transistor/mux setup as well? Who knows, someone might see a prob there. \$\endgroup\$
    – justing
    Aug 21, 2012 at 16:32
  • \$\begingroup\$ I did the schematic first on graph paper by hand, and just yesterday redid it in Eagle. If you think it's useful I'll include it; though when I ignore the ADC and use a hard coded value, everything works as expected. Additionally if I use the ADC value without trying to multiply it, I get good results (0-255 millisecond delays). \$\endgroup\$
    – JYelton
    Aug 23, 2012 at 16:17

2 Answers 2

7
\$\begingroup\$

int can only be 0-255

That's not correct. In the C programming language, an int is always at least 16 bits wide, even on 8-bit micros. A char is 8 bits wide.

But when using inttypes.h, I strongly recommend using the explicit types like uint8_t,int16_t or uint32_t.

Your real error is using _delay_ms() with a variable parameter. This is not allowed, as it is a macro involving floating point calculations - at least in avr gcc. You are supposed to use something like this:

void DelayMs(int delay) {
  while(delay--) _delay_ms(1);
}
\$\endgroup\$
5
  • \$\begingroup\$ Heh, the funny thing is I modified the _delay_ms() lines for posting my question, as they are actually function calls that do exactly as you specify for avr gcc. I modified them thinking it would be less code to post. \$\endgroup\$
    – JYelton
    Aug 21, 2012 at 15:22
  • \$\begingroup\$ If the int value is 16 bits, then I would think ADCH * 10 would work flawlessly. Any additional insight as to why it might not? \$\endgroup\$
    – JYelton
    Aug 21, 2012 at 15:27
  • \$\begingroup\$ Guys, are you sure about this macro thing?! The util/delay.h I'm looking at, defines _delay_ms() as a regular function. \$\endgroup\$
    – DimKo
    Aug 21, 2012 at 18:47
  • \$\begingroup\$ I am thinking that it's something to do with language usage or something. See stackoverflow.com/q/12095890/161052 \$\endgroup\$
    – JYelton
    Aug 23, 2012 at 16:12
  • \$\begingroup\$ The solution to my problem turned out to be optimization settings on the compiler. Still, your information was very helpful, and kept me on the right track. Thanks! \$\endgroup\$
    – JYelton
    Dec 19, 2012 at 8:39
1
\$\begingroup\$

I think the problem is that you have the ADC result as left justified in the results register:

enter image description here

Your code below shows that you have set ADLAR to 1 (left justified):

 // Set ADC multiplexer selection register
    ADMUX  = 0b00100111;

This means that when you are reading the ADC value in ADCH, you are completly skipping the two least significant bits (LSB's). This means you are reading the number in as if it is 4 times smaller (two bit shifts right).

What you want to do is set it to RIGHT justified:

 // Set ADC multiplexer selection register
    ADMUX  = 0b00000111;

and read your ADC value from ADCL (the lower ADC result register):

while (1)
{
    // Set SD (step delay) to value from ADC
    SD = ADCL;
    // Turn on LED (it is a multiplexer, so two pins are used)
    PORTA = 0b00010001;
    // Wait for SD milliseconds
    milli_delay(SD);
    // Turn off LED
    PORTA = 0b00000000;
    // Wait for SD milliseconds
    milli_delay(SD);
}
\$\endgroup\$
1
  • \$\begingroup\$ This didn't help, sorry. If I use the ADCH value and left-justify the result, I get my expected values (0-255). I tested this by writing a bit of code that lights a number of LED's equal to 1/16th of the value. Turning the potentiometer yields the expected lit LEDs. I am suspecting a code/language/usage issue, see stackoverflow.com/q/12095890/161052 \$\endgroup\$
    – JYelton
    Aug 23, 2012 at 16:14

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.