Electronic – How to use AVR ADC with multiplier

adcavr

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.

Best Answer

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);
}