Moin moin
jetzt hänge ich schon Stunden an einem Problem und suche Hilfe.
Was ich möchte:
Auf Analogeingang am ATtiny45 eine Spannung messen (bewegt sich so
zwischen 0V und 4V DC). Wenn die Spannung 2,5V überschreitet dann eine
LED einschalten. Fällt die Spannung wieder unterhalb 2,5V, dann die LED
verzögert ausschalten. Das mit der Verzögerung klappt, nur schaltet die
LED immer bei etwa 1,2V. Das ist es was ich nicht hinbekomme. Ist in
meiner Rechnung etwas falsch? Oder sind REFS0, REFS1 und REFS2 falsch
gesetzt.
Möchte die Versorgungsspannung, also 5V als Referenz nehmen. PB0 also
unbeschaltet ?!
Hier mein Quellcode:
1
#include<avr/io.h>
2
#include<inttypes.h>
3
#include<util/delay.h> /* bei alter avr-libc: #include <avr/delay.h> */
4
#include<avr/interrupt.h>
5
6
# define ADC2_PB4 0b00000010; // Analogwert auf ADC2 PB4
7
8
volatilefloatspannung;// Messwert
9
volatileuint16_tzeit;// Ausschaltverzögerung
10
11
ISR(TIMER0_OVF_vect)
12
{
13
if(spannung<=2.0)zeit++;// Wenn Messwert unter 2.0V Zeit heraufzählen
14
if(zeit>=2)PORTB&=~(1<<PB1);// wenn zeitverzögerung erreicht ausschalten. //Origwert=480
15
}
16
17
intmain(void)
18
{
19
TIMSK|=(1<<TOIE0);// Interrupt bei Überlauf auf 0 (TOIE0)
20
TCCR0B|=(1<<CS00)|(1<<CS02);// Vorteiler 1024
21
DDRB|=(1<<PB1);// Augang LED
22
PORTB&=~(1<<PB1);// aus
23
24
ADCSRA=(1<<ADEN)|(1<<ADPS1)|(1<<ADPS0);// Prescaler und Enable
Endlich mal ein vernünftiges Bildchen!
Wenn PB0 der Referenz-Pin ist gehört da ein kleiner C dran. Würde sagen
100nF.
Edit:
Referenzspannung überhaupt korrekt ausgewählt? Muß ich doch gleich mal
ins Datenblatt schauen, was da der Standard-Wert ist.
Nochmal Edit:
Müßte stimmen und PB0 braucht dabei keinen C. Kenne mich aber nicht so
gut mit C aus, bin Assembler-Freak.
Hi
>Wenn PB0 der Referenz-Pin ist gehört da ein kleiner C dran. Würde sagen>100nF.
Bringt nichts:
VCC used as Voltage Reference, disconnected from PB0 (AREF).
MfG Spess
Also auf hochdeutsch: PB0 unbeschaltet lassen.
(Bisher fühlte ich mich immer nicht so wohl mit unbeschalteten Pins)
Falls funktionierender AssemblerCode vorhanden ist, schaue ich mir gerne
mal an.
Thx, Ingolf
Bitte auch die Edits beachten... :) Mußte das Datenblatt erst
durchforsten.
Abblock-C an Vcc entgegen dem Schaltplan vorhanden? Bist auch sicher,
daß Du dich nicht vermessen hast? Hast überhaupt was gemessen? :)
Ja, edits beachtet.
Fasse zusammen:
PB0 wird gar nicht beschaltet.
Abblockkondensator an VCC ist vorhanden.
Ich messe am Mittelabgriff des Poti am PB4 die Spannung gegen GND, so
bei etwa 1,1V leuchtet LED, drehe ich Poti zurück geht LED bei 0,95V
aus.
Der interne Osci läuft mit 8Mhz, über Fuse ist Div8 enabled.
Scheint so richtig zu sein, denn wenn ich eine LED zum Test über
_delay_ms(1000) blinken lasse dann blinkt sie im Sekundentakt.
Den Vorteiler hatte ich mit ADPS0 und ADPS1 auf 8,
jetzt mal mit ADPS2 auf 16 gesetzt, keine Besserung.
Ja, und ADMUX = ADC2_PB4 macht man normalerweise nicht.
Da hast du Recht. Aber ich ändere ja nichts, ADLAR und die REFSx bleiben
wie sie sind. Habe aber trotzdem mal mit ADMUX |= (1 << MUX1); probiert.
Grr, immer noch bei 1,1V ...
Ich richte meine Vermutung jetzt mal auf die Rechnung.
Ob der Wert aus ADCW so einfach in einen "volatile float spannung"
geschrieben werden kann? Vorzeichen? Bitverschiebung?
Im Moment ist mir schwindelig ;-)
Edit: Bevor ich jetzt verhauen werde ...
ADCSRA = (1<<ADEN) | (1<<ADPS2); // | (1<<ADPS0);
ADCSRA |= (1<<ADSC)
Jetzt muss ich (Anfänger) mich nochmal kurz einlesen über die Bedeutung
von = und |=
Falls das jetzt von Bedeutung ist.
Ingolf Grilec schrieb:> Jetzt muss ich (Anfänger) mich nochmal kurz einlesen über die Bedeutung> von = und |=
Das geht schon klar. Wo und wie definierst du ADCW? Zeig ma alles an
Code.
Warum setzt du dir überhaupt den Rucksack mit float auf? Man könnte
alles so schön über die Auflösung des ADC ausrechnen, ohne punkt und
komma.
Hi
nach ADCW habe ich gerade in den includes gesucht, aber noch nicht
gefunden.
Nehme an das dort (nur wo?) ADCL + ADCH zu einem Wort verknüpft werden.
Bisher noch nicht gefunden.
Naja, mit dem float ...
Ich habe Messpunkte, an denen ich Spannungen von z.B. 2.35V messe.
Somit möchte ich das Programm so anpassen, das ich im Header nur
"#define Schaltwert 2.35" schreibe.
Ja, stimmt, über die Auflösung von 1023 = 5V und 0 = 0V muss ich mir
jetzt was stricken, nur wie?
5V / 1024 = ca. 4,88mV pro Stufe. Damit kannst du dir jeden
"Schwellwert" als ganzzahl speichern.
z.b. deine 2,5V dürften so bei genau 512 liegen.
Naja, mit dem ADCW solltest du nochmal nachschauen. Ich vermute dort den
Knackpunkt.
ADCW gefunden im include von include ... ;-)
genau genommen in der iotnx5.h
#define ADCW _SFR_IO16(0x04)
#define ADCL _SFR_IO8(0x04)
#define ADCH _SFR_IO8(0x05)
Übersteigt aber meine Kenntnisse was das jetzt bedeutet.
Vielen Dank für die Tips!
ahhh, das is ein virtuelles Register aus der avr-lib. Kannte ich nicht
bisher.
Also scheint trotzdem was am Typecast schief zu laufen. Probiers einfach
mal mit Ganzzahligen Werten.
Ingolf Grilec schrieb:> ADCW gefunden im include von include ... ;-)> genau genommen in der iotnx5.h>> #define ADCW _SFR_IO16(0x04)> #define ADCL _SFR_IO8(0x04)> #define ADCH _SFR_IO8(0x05)>> Übersteigt aber meine Kenntnisse was das jetzt bedeutet.
Brauchst du auch nicht wissen.
Das macht der Compiler schon richtig.
Die float würde ich auch rauswerfen. Ob du jetzt jeden Messwert auf Volt
umrechnest oder ob du einmalig (im Idealfall beim Compilieren) den
Grenzwert von Volt auf ADC Einheiten umrechnest, kommt sich logisch aufs
gleiche raus. Nur für den µC ist die 2.te Version viel einfacher.
Ich würde auch erst mal mit einer Einfachversion des Programms dem
Spannungsproblem auf den Grund gehen. Dazu brauchst du den ganze
Verzögerungskram nicht. Dafür fällt vieles weg, was vom Problem ablenkt
und was selber fehlerhaft sein könnte.
1
#include<avr/io.h>
2
#include<inttypes.h>
3
#include<util/delay.h> /* bei alter avr-libc: #include <avr/delay.h> */
4
#include<avr/interrupt.h>
5
6
# define ADC2_PB4 0b00000010; // Analogwert auf ADC2 PB4
7
8
intmain(void)
9
{
10
DDRB|=(1<<PB1);// Augang LED
11
PORTB&=~(1<<PB1);// aus
12
13
ADCSRA=(1<<ADEN)|(1<<ADPS1)|(1<<ADPS0);// Prescaler und Enable
Guter Tip das mit dem überflüssigen rausschmeissen, vielen Dank,
hat mir sehr geholfen. So weit das es jetzt klappt. :)
Habe das Test-Programm, nachdem es mit einem Eingang funktionierte, auf
zwei Eingänge/Ausgänge umgestrickt. So jetzt auch gut. Sicherlich, auch
von der Logik her, noch verbesserungswürdig (auch kürzere Namen,
Kommentare in eigene Zeile etc), trotzdem hänge ich mal den
funktionierenden Aufbau nochmal in aktueller Fassung an.
So, jetzt in den Keller und löten ...
Thx, Ingolf
1
/*
2
ATTINY45
3
Zwei mal ADC, misst zwei Spannungen und
4
gibt, wenn Schaltpunkt_xx überschritten, dieses auf zwei LED aus
5
6
ADC3 auf PB3 -> LED PB2
7
ADC2 auf PB4 -> LED PB1
8
9
FUSES:
10
SPIEN
11
CKDIV8 daher Projekt auf 1Mhz
12
Int. RC Osc. 8 MHz; Start-up time PWRDWN/RESET: 6 CK/14 CK + 64 ms
13
14
AVR-Studio-Konfig Frequency = 1 MHz
15
*/
16
17
#include<avr/io.h>
18
#include<inttypes.h>
19
#include<util/delay.h> /* bei alter avr-libc: #include <avr/delay.h> */
20
#include<avr/interrupt.h>
21
22
# define ADC3_PB3 0b00000011; // Analogwert auf ADC3 PB3
23
# define ADC2_PB4 0b00000010; // Analogwert auf ADC2 PB4
24
25
#define Ausverzoegerung 2 // Im Einsatz hochsetzen auf 480
26
#define Schaltunkt1_PB3_PB2 400 // Im Einsatz anpassen (400 * 0,00488V = 1,95V)
27
#define Schaltunkt2_PB4_PB1 800 // Im Einsatz anpassen (800 * 0,00488V = 3,90V)
28
29
volatileuint16_tspannung1;// Messwert1
30
volatileuint16_tspannung2;// Messwert2
31
volatileuint16_tzeit1;// Ausschaltverzögerung
32
volatileuint16_tzeit2;// Ausschaltverzögerung
33
34
35
ISR(TIMER0_OVF_vect)
36
{
37
if(spannung1<=Schaltunkt1_PB3_PB2)zeit1++;// Wenn Messwert unter xxV Zeit heraufzählen
38
if(zeit1>=2)PORTB&=~(1<<PB2);// wenn zeitverzögerung erreicht ausschalten.
39
if(spannung2<=Schaltunkt2_PB4_PB1)zeit2++;// Wenn Messwert unter xxV Zeit heraufzählen
40
if(zeit2>=2)PORTB&=~(1<<PB1);// wenn zeitverzögerung erreicht ausschalten.
41
}
42
43
intmain(void)
44
{
45
TIMSK|=(1<<TOIE0);// Interrupt bei Überlauf auf 0 (TOIE0)
46
TCCR0B|=(1<<CS00)|(1<<CS02);// Vorteiler 1024
47
DDRB|=(1<<PB1)|(1<<PB2);// Augang LED
48
49
PORTB&=~(1<<PB1);// aus
50
PORTB&=~(1<<PB2);// aus
51
52
_delay_ms(500);// Pause
53
PORTB|=(1<<PB2);// LED an
54
_delay_ms(500);// Pause
55
PORTB|=(1<<PB1);// LED an
56
_delay_ms(500);// Pause
57
PORTB&=~(1<<PB2);// aus
58
_delay_ms(500);// Pause
59
PORTB&=~(1<<PB1);// aus
60
61
62
ADCSRA=(1<<ADEN)|(1<<ADPS2);// Prescaler und Enable
63
ADMUX=ADC2_PB4;// select channel
64
65
ADCSRA|=(1<<ADSC);// single conversion
66
while(ADCSRA&(1<<ADSC));// warten bis fertig
67
68
69
zeit1=0;
70
zeit2=0;
71
spannung1=0;// Init
72
spannung2=0;// Init
73
_delay_ms(500);// Pause
74
sei();// global Interr. aktiv.
75
76
while(1)
77
{
78
79
ADMUX=ADC3_PB3;// select channel
80
ADCSRA|=(1<<ADSC);// single conversion
81
while(ADCSRA&(1<<ADSC));// warten bis fertig
82
spannung1=ADCW;// spannung auslesen
83
if(spannung1>Schaltunkt1_PB3_PB2)// Spannung erreicht
84
{
85
zeit1=0;// Ausschaltzeitverzögerung nullen
86
PORTB|=(1<<PB2);// LED an
87
}
88
89
_delay_ms(300);// Pause
90
91
92
ADMUX=ADC2_PB4;// select channel
93
ADCSRA|=(1<<ADSC);// single conversion
94
while(ADCSRA&(1<<ADSC));// warten bis fertig
95
spannung2=ADCW;// spannung auslesen
96
if(spannung2>Schaltunkt2_PB4_PB1)// Spannung erreicht
97
{
98
zeit2=0;// Ausschaltzeitverzögerung nullen
99
PORTB|=(1<<PB1);// LED an
100
}
101
_delay_ms(300);// Pause
102
103
}
104
}
BTW:
Längeren Code soll man ja als Anhang senden. Ist das hier schon "lang"?
Ingolf Grilec schrieb:> von der Logik her, noch verbesserungswürdig (auch kürzere Namen,> Kommentare in eigene Zeile etc)
Wenn ich lästern darf?
Schmeiss die Kommentare raus.
Die meisten deiner Kommentare sind beim drüberschauen sowieso sinnlose
Kommentare, weil sie mir nichts erzählen, was ich nicht sowieso im Code
auch sehe.
Karl Heinz Buchegger schrieb:>> Wenn ich lästern darf?>
Gerne :D
> Schmeiss die Kommentare raus.
Ist für mich, weil Neuland. In meinen Delphi-Codefiles stehen kaum
noch Kommentare.
Aber hast Recht, zuviel ist zuviel!
Karl Heinz Buchegger schrieb:> Ingolf Grilec schrieb:>>> von der Logik her, noch verbesserungswürdig (auch kürzere Namen,>> Kommentare in eigene Zeile etc)>> Wenn ich lästern darf?>> Schmeiss die Kommentare raus.> Die meisten deiner Kommentare sind beim drüberschauen sowieso sinnlose> Kommentare, weil sie mir nichts erzählen, was ich nicht sowieso im Code> auch sehe.
Ich nehm mir jetzt nur einen Teil raus
1
PORTB&=~(1<<PB1);// aus
2
PORTB&=~(1<<PB2);// aus
3
4
_delay_ms(500);// Pause
5
PORTB|=(1<<PB2);// LED an
6
_delay_ms(500);// Pause
7
PORTB|=(1<<PB1);// LED an
8
_delay_ms(500);// Pause
9
PORTB&=~(1<<PB2);// aus
10
_delay_ms(500);// Pause
11
PORTB&=~(1<<PB1);// aus
das ein _delay_ms eine Pause macht, weiß ich auch so. Das musst du nicht
kommentieren. Das du dazu geschrieben hast, was die Portoperation macht,
ist zwar löblich, aber wenn du versucht bist so etwas zu kommentieren,
dann solltest du dir sofort überlegen, wie du genau dasselbe auch im
Code formulieren kannst.
Der erste Schritt
1
#define LED_PORT PORTB
2
#define LED_1_BIT PB1
3
#define LED_2_BIT PB2
4
5
#define BLINK_TIME 500
6
7
...
8
9
LED_PORT&=~(1<<LED_1_BIT);
10
LED_PORT&=~(1<<LED_2_BIT);
11
12
_delay_ms(BLINK_TIME);
13
LED_PORT|=(1<<LED_2_BIT);
14
_delay_ms(BLINK_TIME);
15
LED_PORT|=(1<<LED_1_BIT);
16
_delay_ms(BLINK_TIME);
17
LED_PORT&=~(1<<LED_2_BIT);
18
_delay_ms(BLINK_TIME);
19
LED_PORT&=~(1<<LED_1_BIT);
Wenn dir bessere Namen einfallen als LED_1_BIT und LED_2_BIT dann ist
das noch besser. Ich weiß ja nicht wofür deine LED stehen. Aber wenn die
eine zb die Fehler-LED ist während die andere das Eintreffen einer
Nachricht auf der UART anzeigt, dann nennt man die eben zb
#define ERROR_LED_BIT PB1
#define UART_RECEIVE_BIT PB2
und im Code dann natürlich auch mit entsprechenden Änderungen.
Damit wird der Code gleich mal in eine ganz andere Ebene gehievt. Zum
einen wird er selbsterklärender. Denn ein
LED_PORT |= ( 1 << ERROR_LED_BIT );
erzählt mir als Leser schon viel mehr, als ein
PORTB |= ( 1 << PB1 );
Ich kann im Code sehen, dass es hier offenbar um die Fehler Led geht! An
welchem Pin diese LED angeschlossen ist, interessiert mich eher weniger.
VIel wichtiger ist aber, dass ich schnell sehe, dass da offenbar
irgendwelche Lichtspiele mit der Fehler Led gemacht werden. Das ist für
zum Verständnis der logischen Aufbaus viel wichtiger.
Und zum anderen habe ich dadurch automatisch die Gewissheit, dass ich
bei der ERROR_LED immer mit demselben Pin operiere und nicht irgendwo
irrtümlich mal zb den PB4 angesprochen habe, anstatt PB1.
Und wenn die LED wegen Hardware-Änderungen von PB1 an PB3 'umziehen'
muss, dann ändere ich an EINER Stelle diese Zuordnung, nämlich hier
1
#define ERROR_LED_BIT PB3
und ich kann mich darauf verlassen, dass auch wirklich an allen Stellen
die entsprechende Änderung vom Compiler durchgeführt wird.
Aber man kann auch noch weiter gehen.
Du willst dir vielleicht nicht merken müssen, ob Pin auf 0 setzen die
LED jetzt ein oder aus schaltet.
Der zweite Schritt:
Kein Problem. Mach dir ein Makro dafür
1
#define LED_PORT PORTB
2
#define LED_1_BIT PB1
3
#define LED_2_BIT PB2
4
5
#define BLINK_TIME 500
6
7
#define LED_ON(x) LED_PORT |= ( 1 << (x) )
8
#define LED_OFF(x) LED_PORT &= ~( 1 << (x) )
9
...
10
11
LED_OFF(LED_1_BIT);
12
LED_OFF(LED_2_BIT);
13
14
_delay_ms(BLINK_TIME);
15
LED_ON(LED_2_BIT);
16
_delay_ms(BLINK_TIME);
17
LED_ON(LED_1_BIT);
18
19
_delay_ms(BLINK_TIME);
20
LED_OFF(LED_2_BIT);
21
_delay_ms(BLINK_TIME);
22
LED_OFF(LED_1_BIT);
und jetzt hast du im Code EXAKT genau die gleiche Information, die du
vorher durch die Kommentare hattest. Nur mit einem Unterschied: Da du
jetzt keine Kommentare mehr brauchst, können die auch NIE falsch sein!
Wenn du ein LED einschalten gegen ein ausschalten austauscht, dann kann
es dir nie passieren, dass du vergisst den entsprechenden Kommentar
anzupassen. Denn hier ist der Code sein eigener Kommentar.
PS: LED_1_BIT bzw. LED_2_BIT sind natürlich scheusliche Namen. Beim
Lesen sind sie nur schwer zu unterscheiden, weil der UNterschied von 1
auf 2 nur schlecht ins Auge springt. WEnn du daher kannst, dann wähle
dafür andere Namen. Irgendetwas, was mit der genauen Funktion der LED zu
tun hat und nicht zu lange ist.
ist ein guter Kommentar. Denn das diese beiden Bits den Vorteiler 1024
ergeben, kann ich tatsächlich nicht im Code sehen.
Der hier
1
TIMSK|=(1<<TOIE0);// Interrupt bei Überlauf auf 0 (TOIE0)
ist so an der Kippe. TOIE bedeutet
-T-imer -O-verflow -I-nterrupt -E-nable (vom Timer 0)
d.h. da steckt im Bitnamen schon der Zweck des BIts drinnen. Aber ist
ok. Das kann man wissen, muss man aber nicht wissen. Daher auch: an der
Kippe.
Die Grundregel lautet:
Kommentiere nicht das WIE, sondern kommentiere das WARUM.
Das WIE steht im Code. Und wenn ich den Code so ändern kann, dass damit
auch das WARUM klar wird, dann ist das noch besser.
Der Hauptfehler war meine umständliche Umrechnung des Analogwert in eine
Gleitkommazahl als Spannungswert. Jetzt nehme ich den Wert vom ADC
direkt, also 0-1023 und werte diesen aus.
Da verstehe ich das Problem trotzdem nicht. Kann C sowas nicht
vernünftig? Eigentlich nimmt man eine Hochsprache ja gerade weil man
solche Probleme vermeiden möchte...
Ben _ schrieb:> Da verstehe ich das Problem trotzdem nicht.
man sollte z.B. nicht auf "spannung" rumrechnen während gleichzeitig der
IRQ drauf zugreift