¿Cómo usar AVR ADC con multiplicador?

2

Estoy aprendiendo acerca de la programación del microcontrolador AVR y actualmente tengo un simple indicador de luz LED configurado en un ATtiny26.

Un potenciómetro de 10k está conectado a ADC7 (pin 7) según el ejemplo en la parte 1 de este tutorial . Los pines de salida del puerto B están conectados a los LED a través de transistores en una matriz multiplexada. (El circuito funciona bien sin el ADC involucrado).

Para probar / aprender, estoy parpadeando un solo LED y esperando una cantidad de milisegundos igual a lo que se lee del ADC. Tengo un problema cuando intento multiplicar el valor para implementar retrasos más largos.

Pensé que el problema era que la variable en la que almacenaba el ADCH era un int pero necesita ser otra cosa porque es un microcontrolador de 8 bits y un int solo puede ser 0-255. Sin embargo, la sustitución de long en no funcionó.

Aquí está el código que funciona:

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

El problema se produce cuando intento usar un multiplicador como este:

SD = ADCH * 10;

Al hacer esto, el LED destellará rápidamente en un extremo del barrido del potenciómetro, pero se iluminará de forma continua y permanecerá así en algún punto mientras se gira la olla a velocidades más lentas (valores ADCH más altos); esencialmente "bloqueando" y requiriendo un reinicio mientras el potenciómetro se encuentra nuevamente en la posición original. Supongo que estoy tratando de almacenar un valor mayor que el tipo numérico, pero parece que no puedo superar esto cambiando el tipo de variable. He intentado long y uint32_t pero no entiendo muy bien qué tienen los tipos numéricos en este micro.

P: ¿Qué debo hacer para poder implementar retrasos que van de 0 a 2000 milisegundos?

Para tu información, tengo las siguientes bibliotecas incluidas:

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

Editar:

Había colocado _delay_ms(x) directamente en mi ejemplo para evitar publicar las funciones que realmente estoy usando, que llama _delay_ms(1) dentro de un bucle. Mi pensamiento fue que sería más conciso, pero como señaló una respuesta, en realidad parecía ser un error.

    
pregunta JYelton

2 respuestas

7
  

int solo puede ser 0-255

Eso no es correcto. En el lenguaje de programación C , un int siempre tiene un ancho de al menos 16 bits, incluso en micros de 8 bits. Un char tiene 8 bits de ancho.

Pero al usar inttypes.h , recomiendo encarecidamente usar los tipos explícitos como uint8_t , int16_t o uint32_t .

Su error real es usar _delay_ms() con un parámetro variable. Esto no está permitido, ya que es una macro que implica cálculos de punto flotante, al menos en avr gcc. Se supone que debes usar algo como esto:

void DelayMs(int delay) {
  while(delay--) _delay_ms(1);
}
    
respondido por el Turbo J
1

Creo que el problema es que tienes el resultado de ADC como justificado a la izquierda en el registro de resultados:

Su código a continuación muestra que ha configurado ADLAR en 1 (justificado a la izquierda):

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

Esto significa que cuando estás leyendo el valor de ADC en ADCH, estás omitiendo completamente los dos bits menos significativos (LSB). Esto significa que está leyendo el número como si fuera 4 veces más pequeño (dos bits se desplaza a la derecha).

Lo que quieres hacer es configurarlo en RIGHT justified:

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

y lea su valor de ADC en ADCL (el registro de resultados de ADC más bajo):

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);
}
    
respondido por el justing

Lea otras preguntas en las etiquetas