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.
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
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 | 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.
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:
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
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.
Das mit SW_UART bekomme ich jetzt so nicht hin. Gibt es vielleicht noch einen anderen Lösungsansatz?
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.