Wie verwende ich AVR ADC mit Multiplikator?

Ich lerne etwas über die Programmierung von AVR-Mikrocontrollern und habe derzeit einen einfachen LED-Blinker auf einem ATtiny26 eingerichtet.

Ein 10k-Potentiometer wird gemäß dem Beispiel in Teil 1 dieses Tutorials an ADC7 (Pin 7) angeschlossen . Die Ausgangspins von Port B sind über Transistoren in einem Multiplex-Array mit LEDs verbunden. (Die Schaltung funktioniert ohne den beteiligten ADC einwandfrei.)

Zum Testen/Lernen lasse ich eine einzelne LED blinken und warte auf eine Anzahl von Millisekunden, die dem entspricht, was vom ADC gelesen wird. Ich habe ein Problem, wenn ich versuche, den Wert zu multiplizieren, um längere Verzögerungen zu implementieren.

Ich dachte, das Problem sei, dass die Variable, in der ich den ADCH speichere, eine war, intaber etwas anderes sein muss, da dies ein 8-Bit-Mikrocontroller ist und intnur 0-255 sein kann. Das Ersetzen longhat jedoch nicht funktioniert.

Hier ist der Code, der funktioniert:

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

Das Problem tritt auf, wenn ich versuche, einen Multiplikator wie diesen zu verwenden:

SD = ADCH * 10;

Wenn Sie dies tun, blinkt die LED schnell an einem Ende des Potentiometer-Sweeps, leuchtet aber dauerhaft und bleibt irgendwann so, während Sie das Poti auf langsamere Geschwindigkeiten drehen (größere ADCH-Werte); im Wesentlichen "einrasten" und einen Reset erfordern, während sich das Potentiometer wieder in der ursprünglichen Position befindet. Ich vermute, dass ich versuche, einen Wert zu speichern, der größer ist als der numerische Typ, aber ich kann dies anscheinend nicht überwinden, indem ich den Variablentyp ändere. Ich habe es versucht longund uint32_tverstehe nicht wirklich, was die numerischen Typen auf diesem Mikro beinhalten.

F: Was muss ich tun, damit ich Verzögerungen im Bereich von 0 bis sagen wir 2000 Millisekunden implementieren kann?

FYI Ich habe die folgenden Bibliotheken enthalten:

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

Bearbeiten:

Ich hatte _delay_ms(x)direkt in meinem Beispiel platziert, um zu vermeiden, dass die Funktionen, die ich tatsächlich verwende, welche Aufrufe _delay_ms(1)innerhalb einer Schleife posten. Mein Gedanke war, dass es prägnanter wäre, aber wie eine Antwort darauf hinwies, schien es tatsächlich ein Fehler zu sein.

Können Sie auch einen Schaltplan Ihres Transistor- / Mux-Setups posten? Wer weiß, vielleicht sieht jemand dort ein Problem.
Ich habe den Schaltplan zuerst auf Millimeterpapier von Hand gemacht und ihn erst gestern in Eagle überarbeitet. Wenn Sie denken, dass es nützlich ist, füge ich es hinzu; Wenn ich jedoch den ADC ignoriere und einen fest codierten Wert verwende, funktioniert alles wie erwartet. Wenn ich außerdem den ADC-Wert verwende, ohne zu versuchen, ihn zu multiplizieren, erhalte ich gute Ergebnisse (0-255 Millisekunden Verzögerungen).

Antworten (2)

intkann nur 0-255 sein

Das ist nicht richtig. In der CProgrammiersprache intist an immer mindestens 16 Bit breit, auch auf 8-Bit-Mikros. A charist 8 Bit breit.

Aber bei der Verwendung inttypes.hempfehle ich dringend, die expliziten Typen wie uint8_t, int16_toder zu verwenden uint32_t.

Ihr echter Fehler ist die Verwendung _delay_ms()mit einem variablen Parameter. Dies ist nicht zulässig, da es sich um ein Makro mit Gleitkommaberechnungen handelt - zumindest in avr gcc. Sie sollten so etwas verwenden:

void DelayMs(int delay) {
  while(delay--) _delay_ms(1);
}
Heh, das Lustige ist, dass ich die Zeilen _delay_ms() für das Posten meiner Frage geändert habe, da es sich tatsächlich um Funktionsaufrufe handelt, die genau das tun, was Sie für avr gcc angeben. Ich habe sie geändert, weil ich dachte, es wäre weniger Code zum Posten.
Wenn der int-Wert 16 Bit beträgt, würde ich denken, dass es ADCH * 10einwandfrei funktionieren würde. Gibt es zusätzliche Erkenntnisse darüber, warum dies möglicherweise nicht der Fall ist?
Leute, seid ihr euch bei dieser Makro-Sache sicher?! Die util/delay.h, die ich mir ansehe, definiert _delay_ms() als reguläre Funktion.
Ich denke, dass es etwas mit Sprachgebrauch oder so etwas zu tun hat. Siehe stackoverflow.com/q/12095890/161052
Die Lösung für mein Problem stellten sich als Optimierungseinstellungen am Compiler heraus. Trotzdem waren Ihre Informationen sehr hilfreich und haben mich auf dem richtigen Weg gehalten. Danke!

Ich denke, das Problem ist, dass Sie das ADC-Ergebnis linksbündig im Ergebnisregister haben:

Geben Sie hier die Bildbeschreibung ein

Ihr Code unten zeigt, dass Sie ADLAR auf 1 gesetzt haben (linksbündig):

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

Dies bedeutet, dass Sie beim Lesen des ADC-Werts in ADCH die beiden niederwertigsten Bits (LSBs) vollständig überspringen. Dies bedeutet, dass Sie die Zahl einlesen, als ob sie 4-mal kleiner wäre (zwei Bitverschiebungen nach rechts).

Was Sie tun möchten, ist es auf RECHTS ausgerichtet zu setzen:

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

und lesen Sie Ihren ADC-Wert aus ADCL (dem unteren ADC-Ergebnisregister):

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);
}
Das hat nicht geholfen, tut mir leid. Wenn ich den ADCH-Wert verwende und das Ergebnis linksbündig ausrichte, erhalte ich meine erwarteten Werte (0-255). Ich habe dies getestet, indem ich ein bisschen Code geschrieben habe, der eine Anzahl von LEDs gleich 1/16 des Wertes aufleuchtet. Das Drehen des Potentiometers ergibt die erwarteten leuchtenden LEDs. Ich vermute ein Code-/Sprach-/Verwendungsproblem, siehe stackoverflow.com/q/12095890/161052