Forum: Mikrocontroller und Digitale Elektronik ATTiny 45 hängt bei Interrupt anscheinend (ADC)


von Christian H. (christian_h)


Angehängte Dateien:

Lesenswert?

Abend,

ich hab an meinem ATTiny45 nur noch einen Port frei und wollte dort nun 
mehrere Taster anschließen und hab mir die Lösung mit dem ADC 
ausgesucht, wo mehrere Taster parallel geschaltet werden und dort 
Widerstände zwischen kommen, damit man die Taster unterscheiden kann.

Ports:

PB1:   LED (Vcc -> LED -> µC)
PB2:   LED (GND -> LED -> µC)
PB3:   Die Taster

Nun, ich aktiviere im Programm also den ADC mit der internen 
Referenzspannung. Das (1 << 5) für (1 << ADFR) musste ich schreiben, da 
mein avr-gcc meinte, es gäbe kein ADFR. So, eigentlich sollte das 
Programm nach dem ADC Init in die main-loop gehen und dort immer die 
Variable adc überprüfen. Ist diese kleiner als 250 soll PB2 5 mal die 
Sekunde blinken, bei 250-750 ebenfalls und über 750 soll PB2 1 mal die 
Sekunde blinken.

Das Problem ist, dass anscheinend, nach dem sei(); gemacht wird die LED 
nur dauerleuchtet. Mach ich kein sei() blinkt diese normal, wie sie 
soll. Aber ich brauch ja den Interrupt für die Auswertung von ADC.

Jemand eine Idee?

Christian

von rogger (Gast)


Lesenswert?

ich beschäftige mich schon seit einigen Jahren mit C/C++ Programmierung, 
aber ein
1
continue
 hab ich noch nicht gekannt. Man lernt eben doch nie aus....

Mal ganz davon abgesehen kommt mir das
>>(1 << 5) für (1 << ADFR)
etwas spanisch vor. Ich habe jetzt nicht im Datenblatt nachgeschaut, 
aber wenn der µC dieses Register wirklich hat, dann sollte der Compiler 
es auch kennen, vorausgestzt natürlich, dass das der richtige Controller 
ausgewählt ist und im Makefile oder eben deinem Buildporzess steht.

von Vlad T. (vlad_tepesch)


Lesenswert?

rogger schrieb:
> ich beschäftige mich schon seit einigen Jahren mit C/C++ Programmierung,
> aber eincontinue hab ich noch nicht gekannt. Man lernt eben doch nie aus....

soviele Schlüsselworte hat c ja nun wirklich nicht.
Aber das continue ist total überflüssig, da es alles else-ifs sind und 
nach dem letzten Zweig die Schleife sowieso zuende ist.

von Vlad T. (vlad_tepesch)


Lesenswert?

Christian H. schrieb:
> Nun, ich aktiviere im Programm also den ADC mit der internen
> Referenzspannung. Das (1 << 5) für (1 << ADFR) musste ich schreiben, da
> mein avr-gcc meinte, es gäbe kein ADFR.

gibts laut Datenblatt auch nicht. Das Bit heitß ADATE - 
AutoTriggerEnable
ADCSRB legt dabei den Trigger fest 0 ist der Free-Running-Mode.

Schaust du auch sicher ins richtige Datenblatt?


Edit:
Ich sehe kein sei()
ist est Absicht, das sin allen If-Blöcken das gleiche gemacht wird.

Edit Edit:
PORTB ^= x;
ist übrigens das gleiche wie
PINB = x;
nur das letzteres Schneller geht und weniger Programmspeicher braucht.

von Christian H. (christian_h)


Lesenswert?

Huch, dann hab ich wohl tatsächlich in ein falsches geschaut.

Das mit den if Sachen ist Absicht.

sei() ist bei adc_init()

Ich probiere nun mal das mit dem richtigen Begriff

EDIT: Auch mit (1 << ADATE) anstatt (1 << 5) bleibt alles gleich.

von Peter D. (peda)


Lesenswert?

Christian H. schrieb:
> ich hab an meinem ATTiny45 nur noch einen Port frei und wollte dort nun
> mehrere Taster anschließen und hab mir die Lösung mit dem ADC
> ausgesucht

Ja, das funktioniert ganz gut:

Beitrag "Tastenmatrix auslesen über nur 2 Leitungen"


Peter

von Christian H. (christian_h)


Angehängte Dateien:

Lesenswert?

Also meine Schaltung funktioniert, mit dem Multimeter nachgemessen. Und 
den Code dort kann ich nicht verwenden, möchte da auf ne andere Weise 
dran.

Mein aktueller Code ist nun im Anhang, hatte das MUX vergessen, nun ist 
die LED aus. Und bei Tastendruck passiert rein gar nichts (Spannung 
ändert sich aber am PIN)

von Christopher G. (cbg)


Lesenswert?

sei(); schreibt man nicht in Funktionen, das schreibt man nur in die 
main Funktion. Wenn man mehr als nur ein Modul verwendet kanns nämlich 
sonst zu nicht so tollen Nebeneffekten führen.

Deine Funktion adc_off() - und alles danach in main - wird nie 
aufgerufen. Du bleibst ewig in der ersten while-Schleife hängen.

Schau dir nochmal an wie man einzelne Bits manipuliert.
1
foo |= (1<<3); // setzt Bit 3 in foo
2
foo &= ~(1<<5); // löscht Bit 5 in foo
3
foo ^= (1<<7); // toggelt Bit 7 in foo
4
5
// äquivalent zu den oberen Zeilen aber mMn übersichtlicher
6
// _BV(x) als (1<<(x)) in avr/sfr_defs.h definiert und über avr/io.h eingebunden
7
foo |= _BV(3); // setzt Bit 3 in foo
8
foo &= ~_BV(5); // löscht Bit 5 in foo
9
foo ^= _BV(7); // toggelt Bit 7 in foo
Was willst du in deinem Programm also machen? adc_off() würde auch nicht 
das tun, was du vermutest, falls du die Funktion irgendwann einmal 
aufrufen solltest.

In ISR(ADC_vect) weist du deiner 16 Bit Variable adc nur die oberen 8 
Bit der ADC Wandlung zu. Dein Wert wird nie größer als 255 sein. Aber 
auch ein anderer Fehler ist dort zu finden. Du kannst bei den 8 Bit AVRs 
nur 8 Bit atomar lesen / schreiben, greifst aber in den If-Abfragen auf 
die gemeinsame 16 Bit Variable zu. Da können so wie du es jetzt hast 
ziemlich fiese Fehler auftreten. Du solltest SREG (da ist ja das I-Flag 
drin) sichern, Interrupts deaktivieren, den uint16_t Wert in eine lokale 
Variable umkopieren, dann SREG wieder zurückschreiben und nur mit der 
lokalen Variable weiterarbeiten. So z.B.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
...
4
volatile uint16_t adc;
5
...
6
void get_adcval(uint16_t *mem)
7
{
8
    uint8_t sreg;
9
    sreg = SREG;
10
    cli();
11
    *mem = adc;
12
    SREG = sreg;
13
}
14
...
15
int main(void)
16
{
17
    uint16_t adc_val;
18
    ...
19
    adc_init();
20
    sei();
21
22
    while(1)
23
    {
24
        get_adcval(&adc_val);
25
        if(adc_val < 250)
26
        {
27
            ...
28
        }
29
        ...
30
    }
31
    ...
32
33
    return 0;
34
}
35
36
ISR(ADC_vect)
37
{
38
    adc = ADC;
39
}

Vlad Tepesch schrieb:
> PORTB ^= x;
> ist übrigens das gleiche wie
> PINB = x;
> nur das letzteres Schneller geht und weniger Programmspeicher braucht.

Das ist schlicht falsch! PORTB ^= x toggelt jene Bits im Register PORTB, 
welche dem Wert der Variable x entsprechen.
PINB = x; ist eine einfache Zuweisung des Werts der Variable x an das 
Register PINB. Da die PINx Register read-only sind, macht diese 
Zuweisung ausserdem nicht viel Sinn.

von Stefan E. (sternst)


Lesenswert?

Christopher G. schrieb:
> Das ist schlicht falsch! PORTB ^= x toggelt jene Bits im Register PORTB,
> welche dem Wert der Variable x entsprechen.
> PINB = x; ist eine einfache Zuweisung des Werts der Variable x an das
> Register PINB. Da die PINx Register read-only sind, macht diese
> Zuweisung ausserdem nicht viel Sinn.

Dann würde ich vorschlagen, dass du mal in das Datenblatt eines halbwegs 
aktuellen AVRs schaust.

von Vlad T. (vlad_tepesch)


Lesenswert?

Christopher G. schrieb:
> In ISR(ADC_vect) weist du deiner 16 Bit Variable adc nur die oberen 8
> Bit der ADC Wandlung zu. Dein Wert wird nie größer als 255 sein.

das ist falsch.
ADCW, wie auch ADC führen einen 16bit zugriff auf ADCH und ADCL aus.

den rest den du schreibst ist auch müll

von Vlad T. (vlad_tepesch)


Lesenswert?

Stefan Ernst schrieb:
> Christopher G. schrieb:
>> Das ist schlicht falsch! PORTB ^= x toggelt jene Bits im Register PORTB,
>> welche dem Wert der Variable x entsprechen.
>> PINB = x; ist eine einfache Zuweisung des Werts der Variable x an das
>> Register PINB. Da die PINx Register read-only sind, macht diese
>> Zuweisung ausserdem nicht viel Sinn.
>
> Dann würde ich vorschlagen, dass du mal in das Datenblatt eines halbwegs
> aktuellen AVRs schaust.

ATtiny25/45/85 Datasheet, 10.1 I/O Ports - Introduction
[...] Port Input Pins – PINx. The Port Input Pins I/O location is read 
only, while the Data Register and the Data Direction Register are 
read/write.
However, writing a logic one to a bit in the PINx Register, will result 
in a toggle in the corresponding bit in the Data Register. [...]

von Christian H. (christian_h)


Angehängte Dateien:

Lesenswert?

Gut, ich hab mal das mit dem get_adcval zum testen so eingesetzt, aber 
wie erwartet bleibt es bei dem Fehler, dass die LED einfach nicht an und 
ausgeht, wie die while-Schleife es eigentlich verlangt. Sie geht an und 
aus, wenn ich das den Start des ADC mit (1 << ADSC) auskommentiere, aber 
das ist ja nicht Sinn der Sache. Mein aktueller Code ist nun im Anhang.

Kann es sein, dass vielleicht nicht etwas falsch ist, sondern etwas gar 
fehlt? Also der Punkt ist ja, wenn ADC angemacht wird, passiert nichts 
mehr, der µC tritt anscheinend nicht mal mehr in die main-loop ein.

Christian

von Vlad T. (vlad_tepesch)


Lesenswert?

also laut meinem Datenblatt gibts die referenz, die du auswählst gar 
nicht (reserved)
Bitte besorge dir das richtige Datenblatt und schau da rein.
ISt vielleicht auch der ausgewählte Pin (ADC3 (PINB3)) falsch?

Bei der gelegenheit prüfe auch noch mal die Einstellungen bezüglich des 
Targets in der IDE.

füg doch mal an geeigneten Stellen des Programms einen BLinker ein. 
immer nur einen. bzw eine ein-Stelle und eine aus-Stelle.

von Christian H. (christian_h)


Lesenswert?

Stimmt tatsächlich meine Referenzspannung war falsch, aber das ist 
leider nicht die Lösung gewesen.  Es ist weiterhin so, dass die LED 
nicht blinkt, also die while-Schleife nicht ausgeführt wird, sobald 
irgendwie der ADC-Interrupt ins Spiel kommt, egal ob durch sei() oder 
durch anschalten des ADC, sobald der Interrupt möglich ist, passiert 
nichts mehr. Dabei wird im Interrupt ja nur ganz normal ein Wert 
zugewiesen.

Meinst du mit Target in der IDE, wo hin ich mein Programm flashe? Also 
alles andere, was nicht mit dem Programm zutun hat stimmt eigentlich. 
Oder welches Target meinst du genau?

Das mit den Blink der LEDs zum gucken, wie weit der kommt brauch ich 
nicht, hab ja bereits herausgefunden, dass er nicht in die main-loop 
kommt, sobald ich in adc_init oder in der main verhindere, dass der 
ADC-Interrupt ausgelöst werden kann, wird die main-loop ausgeführt. Ich 
hab sogar im Interrupt ein Toggle gemacht mit und einmal ohne Zeit 
dazwischen, auch da macht die LED gar nichts.

Mit dem PB3 (ADC3) bin ich übrigens zu 100% sicher.

von Peter D. (peda)


Lesenswert?

Christian H. schrieb:
> Und
> den Code dort kann ich nicht verwenden, möchte da auf ne andere Weise
> dran.

Besser gesagt, Du möchtest nicht. Kann ich verstehen, selber entwickeln 
macht eben mehr Spaß.

Die ausgereifte Lösung rennt einem ja nicht weg. Wenn man das Prinzip 
erstmal verstanden hat (AD-Wandlung, Schwellwerttabelle, 
Störunterdrückung, Flankenerkennung), kann man sie immer noch nehmen.


Anstatt Stochern im Nebel, gibt doch erstmal die ADC-Werte über die UART 
aus und gucke sie Dir am PC an.
SW-UART:
1
void sputchar( uint8_t c )
2
{
3
  c = ~c;
4
  STX_PORT &= ~(1<<STX_BIT);            // start bit
5
  for( uint8_t i = 10; i; i-- ){        // 10 bits
6
    _delay_us( 1e6 / BAUD );            // bit duration
7
    if( c & 1 )
8
      STX_PORT &= ~(1<<STX_BIT);        // data bit 0
9
    else
10
      STX_PORT |= 1<<STX_BIT;           // data bit 1 or stop bit
11
    c >>= 1;
12
  }
13
}


Peter

von Christopher G. (cbg)


Lesenswert?

Vlad Tepesch schrieb:
> Christopher G. schrieb:
>> In ISR(ADC_vect) weist du deiner 16 Bit Variable adc nur die oberen 8
>> Bit der ADC Wandlung zu. Dein Wert wird nie größer als 255 sein.
>
> das ist falsch.
> ADCW, wie auch ADC führen einen 16bit zugriff auf ADCH und ADCL aus.
>
> den rest den du schreibst ist auch müll

In der Version direkt über meinem Post wird aber ADCH und nicht ADCW 
zugewiesen.

Das mit der alternativen Funktion der Register ist mir wohl um die 
Uhrzeit einfach nicht in den Sinn gekommen, sorry. Mein hirn hat sich 
halt mehr an "Zuweisung ist toggeln" gestossen.
Wenn du meinst, dass ich sonst nur Müll schreibe, dann lösche deine Bits 
ruhig mit foo |= ~(1<<Bit) und spiele dich mit schönen Glitch-Fehler 
herum wenn du nicht 8Bit Werte in ISRs und normalem Programmablauf 
verwendest.

von Christian H. (christian_h)


Lesenswert?

Das mit SW_UART bekomme ich jetzt so nicht hin. Gibt es vielleicht noch 
einen anderen Lösungsansatz?

von Peter D. (peda)


Lesenswert?

Christian H. schrieb:
> Das mit SW_UART bekomme ich jetzt so nicht hin.
1
#define BAUD 9600
2
#define STX_PORT PORTB
3
#define STX_BIT  PB0
4
...
5
  char s[10];
6
  DDRB |= 1<<STX_BIT;
7
  itoa( s, ADC, 10 );
8
  for( uint8_t i = 0; s[i]; i++ )
9
    sputchar( s[i] );


Peter

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.