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
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.
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.
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.
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.
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)
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
volatileuint16_tadc;
5
...
6
voidget_adcval(uint16_t*mem)
7
{
8
uint8_tsreg;
9
sreg=SREG;
10
cli();
11
*mem=adc;
12
SREG=sreg;
13
}
14
...
15
intmain(void)
16
{
17
uint16_tadc_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
return0;
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.
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.
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
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. [...]
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
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.
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.
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:
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.