Hallo zusammen ,
möchte zwei siebensegmentanzeigen über einen atmel8 gemultiplext
ansteuern,
habe ich soweit auch schon mehr oder weniger hingekriegt mit
untenstehenden
Code, allerdings gibt es Probleme wenn sich die Zahl die ausgegeben
werden soll sehr schnell ändert, dann flackert die anzeige.
Habe leider kein Beispielcode in C gefunden, nur in assembler im
Tutorial, allerdings, krieg ich es nicht auf die reihe, den in c
umzuschreiben, wäre schön wenn mir jemand erklären würde wie ich
vorgehen muss wenn ich siebensegmentanzeigen gemultiplext in c ansteuern
will.
mfg crashdemon
Crashdemon wrote:
> Hallo zusammen ,>> möchte zwei siebensegmentanzeigen über einen atmel8 gemultiplext> ansteuern,> habe ich soweit auch schon mehr oder weniger hingekriegt mit> untenstehenden> Code, allerdings gibt es Probleme wenn sich die Zahl die ausgegeben> werden soll sehr schnell ändert, dann flackert die anzeige.>> Habe leider kein Beispielcode in C gefunden
Z.B.:
Beitrag "ADC mit Multiplexanzeige"
Peter
Naja hätte vllt. nicht schreiben sollen keinen Beispielcode gefunden,
das Beispiel hatte ich schon gefunden allerdings habe ich ein Problem
mit in den Code hineinzudenken, aber ich schau es mir nochmal an.
Hab es auch ein wenig nders gemacht und würde das auch gerne so
beibehalten nur halt mit einer ordentlichen multiplex funktion, hier
noch meine komplette multiplex funktion.
Komplett falscher Ansatz.
Lass einen Timerinterrupt laufen (1,2 oder 5ms).
-alle digits löschen
-statische Variable hochgezählen, je nach Zählerzustand das passende
Bitmuster
ausgeben
-entsprechendes Digit einschalten.
Dazu gibts ein Array, wo die anzuzeigenden Daten drinstehen. Das machst
du aus einem beliebigen Programmteil heraus, die Interruptroutine
schaufelt nur stur die Bits raus.
Was Du brauchst ist ein (in Deinem Fall) zweistelliges Array, wo immer
die aktuellen Ziffern (stellenweise) gespeichert sind.
Dann brauchst Du eine Routine, die periodisch aus diesem Array die
Zifferninformationen auf den Port umlegt. Am besten machst Du das
interrupt-gesteuert. Dann läuft das praktisch autonom und Du brauchst
nur noch in dem normalen Programmablauf Deine Zifferninformationen in
das Array zu schreiben.
Das erspart Dir nerviges Synchronisieren, was letztlich wahrscheinlich
Dein Problem (mit den flackenden Ziffern) darstellt.
Vorteil bei dieser Interruptsteuerung: Man kann auch relativ bequem eine
Dimmfunktion einbauen. Dabei benutzt man zB den T2-Overflow und den
Comparematch2. Beim Overflow setzt man die jeweils neue (!) Ziffer aus
dem Array auf die entsprechende 7-Seg-Anzeige/Port um und beim
Comparematch löscht man alle (!) Ziffern/ den entsprechenden Port. Dann
kannst Du über das Outputcompare-Register die Pulsweite (=Helligkeit)
einstellen. Wenn Du ein Beispiel dessen willst, schick mir eine Email -
nickname von oben und ein @gmx.de anhängen.
Keine Ahnung, wie Peter das in seinem Beispiel gelöst hat,
möglicherweise ja sogar genauso - ich hab's mir nicht durchgelesen.
Das ADC Beispiel ist wirklich nicht so besonders gut geeignet
um das Prinzip des Multiplexing isoliert zu verstehen. Die
eigentliche Multiplex-Funktionalität ist in die ADC-ISR
hineinverwoben. Ich kann mir schon vorstellen, dass jemand
der nicht weiss, worauf er achten soll, die nicht findet.
Dazu kommt, dass PeDa hier auch noch darauf achten muss,
den Port während der ADC Messung ruhig zu halten (das ist dann
auch der Grund warum das Multiplexing erfolgt, nachdem der ADC
eine Messung abgeschlossen hat und seine ISR ausführt).
Das Prinzip, wie man sowas im Allgemeinen macht wurde ja schon
angesprochen und zusammen mit der Erläuterung von Multiplexing
im Tutorial sollte es kein Problem mehr sein, sowas umzusetzen.
Nicht den Assembler Code umschreiben. Das hat wenig bis keinen
Sinn, da du dich in Assembler Details verlierst. Aber das Prinzip
aus dem Assembler Tutorial kann 1:1 übernommen werden (und ist
genau das, welches in diesem Fred schon angesprochen wurde)
danke für die vielen antworten,
ich fasse jetzt mal zusammen wie ich denke vorgehen zu sollen.
- timer basteln der in regelmäßigen abständen einen interrupt erzeugt
- die momentan erleuchtete Anzeige abgeschaltet (kling plausibel)
- das Muster für die nächste Anzeige am Port D ausgegeben
- die nächste Anzeige durch eine entsprechende Ausgabe am Port C
eingeschaltet
ich würde allerdings gerne mein array behalten und einer und
zehnerstellen getrennt voneinander ermitteln sprich
1
23=(23/10)=2=zehnerstelle(keinrest,dainteger)
2
23=23-(zehnerstelle(2)*10)=3=einerstelle
würde das auch gehen oder muss es umbedingt mit bitmasken gemacht werden
Crashdemon wrote:
> danke für die vielen antworten,>> ich fasse jetzt mal zusammen wie ich denke vorgehen zu sollen.>> - timer basteln der in regelmäßigen abständen einen interrupt erzeugt> - die momentan erleuchtete Anzeige abgeschaltet (kling plausibel)> - das Muster für die nächste Anzeige am Port D ausgegeben> - die nächste Anzeige durch eine entsprechende Ausgabe am Port C> eingeschaltet
Ganz genau.
Der springende Punkt:
Bei einem Aufruf der ISR wird nur zur nächsten Anzeige
umgeschaltet und die Ausgabe für diese Anzeigenstelle gemacht.
Nicht mehr.
Da die ISR aber sehr oft aufgerufen wird, bekommst du das
Umschalten nicht mehr mit und siehst beide Anzeigestellen
leuchten.
> ich würde allerdings gerne mein array behalten und einer und> zehnerstellen getrennt voneinander ermitteln sprich
Kannst du ja.
Ganz im Gegenteil: sollst du auch.
Du legst dir ein Array zurecht. In deinem Fall ist es ein
Array mit 2 Elementen. Nennen wir es mal Anzeige
In Anzeige[0] speicherst du das auszugebende Bitmuster für
die 'Hunderterstelle', in Array[1] speicherst du das
auszugebende Bitmuster für die 'Einerstelle'.
Die ISR sorgt dann dafür, dass diese Bitmuster abwechselnd auf
den beiden 7-Segment (natürlich auf den richtigen) ausgegeben
werden.
Dann brauchst du noch eine Funktion, der du eine Zahl
übergibst, und die aus dieser Zahl die den Ziffern
entsprechenden Bitmuster an die richtigen Positionen im
Array schreibt.
Und das wars dann auch schon.
Die ISR zeigt an, was auch immer sie im Array vorfindet.
Und wenn du eine Zahl ausgeben willst, dann rufst du
einfach diese Ausgabefunktion auf, die das Array entsprechend
befüllt. Die grundlegende Funktionalität, nämlich das Zerlegen
einer Zahl und das bestimmen des zugehörigen Bitmusters
hast du ja in deiner Funktion schon gemacht. Das ist also
nichts neues. Aber anders als in deiner Funktion wird nichts
ausgegeben, sondern die Bitmuster lediglich an die richtige
Stelle des Arrays geschrieben, von wo sich die ISR dann diese
Werte holt und die tatsächliche Ausgabe macht.
Das ganze sollte nicht wesentlich mehr als 10 bis 15 Zeilen
Code sein.
So ganz komme ich mit den Interrupts noch nicht klar, habe das Problem
sobald ich einmal in den Zweistelligen Bereich kommen bei der
Siebensegmentanzeige wird der Interrupt danach ständig aufgerufen auch
wenn ich nur eine einstellige zahl ausgeben will, hab hier anbei mal
meine funktion und isr mit den wichtigen variablen gapackt, wäre nett
wenn da mal einer drüber schauen könnte.
1
volatilechardigit[10]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};// Bitmaske für die Zahlen
interrupr Serviceroutinen schrieb man früher *SIGNAL()* jetzt wird
*ISR()* verwendet, ein paar Versionen vom GCC wird SIGNAL noch
funktionieren, doh ist als depricated markiert.
TIMSK und TCCR0 musst Du einmal bei der initialisierung setzten, dann
bleibt das eh im ConfigRgister stehen.
bei jedem Aufruf der ISR solltest Du EINEN Wert ausgeben und den Zähler
erhöhen, beim NÄCHSTEN Aufruf kommt dann das nächste Digit dran.
Wenn Du PORTC 0xFF haben willst nimm statt PORTC |= 0xFF PORTC = oxFF,
da sparst Du Dir das lesen des Ports, ausserdem reicht eine einfache
Zuweisung
PORTC = display[counter]; (Dir ist ja egal was vorher auf PORTCV war, es
soll lediglich display[counter] ausgegeben werden (wieder ein unnötiges
lesen gespart - und mehr lesbarkeit in den Code gebracht)
Crashdemon wrote:
> So ganz komme ich mit den Interrupts noch nicht klar, habe das Problem> sobald ich einmal in den Zweistelligen Bereich kommen bei der> Siebensegmentanzeige wird der Interrupt danach ständig aufgerufen auch> wenn ich nur eine einstellige zahl ausgeben will
Dem Multiplexen ists wurscht, ob die Zahl 2-, 1- oder 0-stellig ist,
gemultiplext wird immer.
Sonst würde Deine Zahl ja je nach Stellenzahl unterschiedlich hell
leuchten.
Du hast 2 Bytes für die Segmentmuster der beiden Anzeigen und der
Interrupt gibt diese Bytes abwechselnd aus mit dem zugehörigen
Stellenbit eingeschaltet.
Peter
Danke schonmal für die schnellen antworten, ja hab ich mit signal, jetzt
in ISR(TIMER0_OVF0_vect) geändert, werde die die isr jetzt nochmal
überarbeiten und dann schaune wir mal obs funzt.
Vielleicht verstehe ich dich falsch, aber ich habe den Eindruck du
willst die ISR nur aufrufen, wenn du zwei Stellen auszugeben hast. Wenn
nur eine ausgegeben werden soll, dann willst du die ISR anscheinend
nicht nutzen.
Du aktivierst in deinem Code den Overflow Interrupt, wenn eine Zahl >10
ausgegeben werden soll mit:
TIMSK = (1 << TOIE0); // Overflow Interrupt einschalten
Das heißt aber nicht, dass dieser Interrupt wieder deaktiviert wird,
wenn die ISR einmal durchlaufen wurde. Der Interrupt bleibt trotzdem
aktiv und wird somit immer wieder ausgeführt. Das ist genau das was du
beobachtest hast, einmal eine Zahl größer 10 und die Routine wird immer
wieder aufgerufen.
Ich würde es an deiner Stelle so machen, wie es oben auch schon
beschrieben wurde:
Nutze die ISR um deine Zahlen auf der 7-Segment-Anzeige auszugeben und
zwar immer, egal ob nur eine oder beide Stellen ausgegeben werden soll.
Lass den Timer einfach von Anfang an laufen, so wird die Anzeige
zyklisch aktualisiert.
In deiner main oder in der PrintNumber-Methode schreibst du einfach
immer die Variablen DIGIT1 und DIGIT2 wieder neu, aber nicht die Ports
selbst. Für die Ports ist nur die ISR zuständig. Meiner Meinung nach ist
das die sauberste Methode für deine Ausgabe, aber ich lass mich da auch
gerne eines besseren belehren.
Jo richtig ich wollte nur die zahlen die <9 sind über die ISR ausgeben,
das ist dann natürlich blöd das die interupt routine nach einmaligen
aufrufen immer wieder durchlaufen wird, kann man das unterbinden? wenn
nein das muss ich es wohl so machen wie du es beschrieben hast, indem
ich einstellige sowie zweistellige zahle per isr ausgebe.
mfg crashdemon
ich muss mich korrigieren. Ich hab das cli() am Ende deiner ISR
übersehen, das sollte alle Interrupts deaktivieren, so dass die Routine
nicht erneut aufgerufen wird. Ich hoffe, ich hab dich nun nicht
durcheinander gebracht damit.
Trotzdem würde ich dir empfehlen alle Ausgaben über die ISR laufen zu
lassen. Was spricht denn aus deiner Sicht dagegen das zu machen?
Das cli() scheint bei mir allerdings nicht zu funktionieren da er nicht
wieder aus der ISR rausspringt?, bin leider in interrupts nicht so fit
und dachte mir das ich es am besten so leicht wie möglich mache aber ich
werde da wohl nicht rumkommen, bei über eine routine abzuwickeln weiß
nur noch nicht wie diese aussehen soll, schade das es im avr-gcc
toutorial nicht so ein schönes beispiel in c gibt, wie im assembler
tutorial.
Wie ich jetzt auch erst lesen musste, darfst du kein cli() innerhalb
einer ISR nutzen. Du sperrst dich damit anscheinend selbst in der ISR
ein, da das nötige reti nicht ausgeführt werden kann, weil du den
Interrupt schon gesperrt hast durch das cli().
Falls du weiterhin bei deiner Idee bleiben willst, nur zweistellige
Zahlen per ISR auszugeben, ersetze mal das cli() durch:
TIMSK = 0;
Damit müsstest du eigentlich dafür sorgen, dass der Interrupt erst
wieder aufgerufen werden kann, wenn TIMSK wieder gesetzt wird. Das reti
sollte in diesem Fall aber ausgeführt werden, so dass er zurück in die
main springen kann.
Vielleicht klappt das, kannst ja mal ausprobieren und Bericht erstatten.
Hat nicht irgendjemand eine funktionierende Interrupt Routine für zwei
Siebensegmentanzeigen, mit dem Timer0 (8 Bit) und zusätzlichen
schnickschack, weil es bei rausschrieben aus fremden Code sehr schwer
ist die Funktionen nachzuvollziehen, erst recht wenn diese über zieg
headers verstreut sind, wäre dankbar wenn jemand der schonmal so eine
routine gebastelt hat diese hier posten könnte.
mfg crashdemon
Crashdemon wrote:
> Das cli() scheint bei mir allerdings nicht zu funktionieren da er nicht> wieder aus der ISR rausspringt?
Das CLI im Interrupt kannst Du Dir an die Backe schmieren, es bewirkt
garnichts.
Die globale Interruptfreigabe ist nur ein Bit und kein Zähler.
Du kannst 1000-mal CLI schreiben, das nächste RETI oder SEI gibt
trotzdem die Interrupts wieder frei.
Du darfst also ruhig im Interrupt CLI oder NOP schreiben, es macht
keinen Unterschied (also kannst Du es sein lassen).
Peter
Crashdemon wrote:
> weil es bei rausschrieben aus fremden Code sehr schwer> ist die Funktionen nachzuvollziehen,
Hast Du schonmal versucht, dem Autor Fragen zu stellen etwa in der Art,
"was macht Zeile X in File Y von Beispiel Z"?
> erst recht wenn diese über zieg> headers verstreut sind
Header enthalten normalerweise keinen Code.
Peter
Hab den Code ausprobiert und angepasst funzt aber irgendwie nicht, er
zeigt nichts auf der anzeige an, ich denke ich habe nich ein
zusätzliches problem was mir bis jetzt noch nicht so aufgefallen ist und
hatte ich bis jetzt immer schön ein while(1) laufen worin der die werte
des adc eingelesen hatte und mit in der schleife befand sich der aufruf
der funktion PrintNumber(ADC_Wert)
wenn ich das jetzt aber über Interupts machen würde würde er die
funktion immer wieder aufrufen, also wie eine art neuen thread, liege
ich damit richtig?
1
while(1)
2
{
3
ReadChannel(0);MesswertdesAD-Wandlers
4
PrintNumber(ADC_angle);// Wert ausgeben, ganzzahlig nicht negativ
1) Satzzeichen setzen! Sonst versteht man ja nichts.
2) Der Code muss, so wie er ist, erstmal eine 91 auf der ANzeige
darstellen.
Solange das nicht passiert, brauchst du auch nicht mit dem ADC
rumkaspern.
=> Poste mal die komplette Schaltung (µC + 7Seg Anbindung), dann kann
ich den COde anpassen.
3) Es ist sehr sinnvoll, sich auf Funktionen "ReadChannel(0)" zu
berufen, die
andere hier nicht kennen!
> das ist dann natürlich blöd das die interupt routine nach einmaligen> aufrufen immer wieder durchlaufen wird, kann man das unterbinden?
Das ist überhaupt nicht blöd. Ganz im Gegenteil. Die Funktion
soll immer wieder aufgerufen werden. Denn die ISR-Funktion
sorgt dafür, dass deine Anzeige richtig funktioniert, indem
sie abwechselnd die eine und dann die andere Stelle aktiviert.
Du hast eines immer noch nicht verinnerlicht:
Der eigentliche Anzeigevorgang ist unabhängig von dem Generieren
des Anzuzeigenden!
Das ist in wichtiges Prinzip! Denn du brauchst dich dadurch nicht
mehr um die Anzeige kümmern. Das macht die ISR ganz alleine.
Du schreibst lediglich die auszugebenden Bitmuster in die globalen
Variablen. Mehr hast du nicht zu tun, den Rest macht die ISR,
wenn sie das nächste mal drann ist.
Ich geh mal davon aus, dass deine Lösung von oben die
Hardware richtig anspricht.
Du machst das alles viel zu kompliziert! Ein Aufruf der ISR
gibt eine Steller aus. Beim nächsten Aufruf der ISR wird
die nächste Stelle ausgegeben. Und zwar ständig! Immer und
immer wieder!
1
unsignedchardigit[10]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};// Bitmaske für die Zahlen
2
3
volatileunsignedcharsegment[2];
4
unsignedchardisplay[2]={0x08,0x10};// Steuerleitungen für ein Digit
5
unsignedcharcounter;// Anzeige die Angeschaltet werden soll
6
7
ISR(TIMER0_OVF_vect)// Aufruf bei Interrupt von Timer0 (8 Bit)
8
{
9
// Die bisherige Anzeige ausschalten
10
PORTC|=0xFF;
11
12
// welches ist die nächste Stelle
13
counter=counter+1;
14
if(counter==2)
15
counter=0;
16
17
// Diese Stelle ausgeben
18
PORTD=segment[counter];
19
PORTC=display[counter];
20
}
21
22
voidPrintNumber(intnumber)
23
{
24
if(number<0)
25
number=-number;
26
27
segment[0]=digit[number/10];
28
segment[1]=digit[number%10];
29
}
30
31
intmain()
32
{
33
DDRC=0xFF;
34
DDRD=0xFF;
35
36
TCCR0|=(1<<CS00);// Vorteiler 1
37
TIMSK|=(1<<TOIE0);// Overflow Interrupt
38
39
PrintNumber(0);
40
41
sei();
42
43
while(1)
44
{
45
}
46
}
Wann immer du eine Zahl zum Ausgeben hast, dann rufst du die
Funktion PrintNumber auf. Diese zerlegt die Zahl und stellt
die richtigen Bitmuster in das globale Array 'segment'. Wenn
die ISR das nächste mal drann ist, dann gibt sie dann das
neue Bitmuster aus.
Ich hab mich an deine Vorgabe was Hardwareansteuerung geht
gehalten. Wenn da was nicht stimmen sollte, dann wirst du
wahrscheinlich nichts sehen. Versuch wenigstens zu verstehen,
was da passiert, häng dich im Simulator in den Debugger und finde
raus, welche Portzuweisung konkret nicht stimmt. Das ganze ist
viel einfacher als du denkst.
als du denkst.
Danke für das Programm, es funktioniert jetzt, erst hatte ich ein
Problem dass er nur den gleichen Wert angezeigt hatt, nachdem ich aber
das sei() vor PrintNumber(0); gesetzt habe funktionierte alles
einwandfrei.
thanx crashdemon
Hätte nicht gedacht das es so einfach ist, die Interrupt gibt quasi
pausenlos die Zahlen an die anzeigen und muss sich nicht um irgendwelche
timings vom adc kümmern, wie bei meinen alten programm, ändert sie die
auszugebende zahl, so wird das auch einfach von der ISR berücksichtig.
so noch zu allerletzt noch ein paar fragen zu code, dei eigentlich eher
die c systax selber betreffen, mir würde auch ein link zur eräuterung
reichen, leider weiß ich nicht nach was ich in google suchen soll.
Korrigiert mich bitte wenn ich falsch liege
1
if(number<0)// Vom Verständnis wenn Zahl negativ mit (-1) multipliziern
2
number=-number;
3
4
segment[0]=digit[number/10];// Zahl durch 10 teilen, Zahlen werden nach den Komma nich berücksichtigt, da Integer
5
segment[1]=digit[number%10];// Hmm?? 10% das sagt mir jetzt gar nichts
wenn du eine Zahl durch eine andere dividierst, dann ergibt das bei
Ganzzahlen ja häufig einen Rest, so dass man eine Division auch
folgendermaßen beschreiben kann:
a / b = c * b + d (alle Zahlen sind Ganzzahlen)
Beispiel: 36 / 10 = 3 * 10 + 6
In deinem Quellcode finden sich nun zwei Anweisungen wieder:
number / 10 ergibt, wie du selbst schon erkannt hast, das ganzzahlige
Ergebnis der Division ohne Nachkommastellen.
number % 10 ergibt jetzt einfach nur den Rest der Division number / 10.
Mit dem Beispiel von oben erklärt:
36 / 10 = 3
36 % 10 = 6
Für dich heißt das einfach nur number / 10 berechnet dir die
Zehnerstelle, number % 10 die Einerstelle.
warum kann ich die PrintNumber Funktion nicht in die while-Schleife
packen, sobald ich das mache, zeigt die Anzeige nur noch null an.
Würde gerne so etwas in der art Abarbeiten, muss ich dann auch wieder
zusäzlich mit einen Interrupt vom ADC arbeiten?
1
intmain()
2
{
3
uint8_tanalog_value;
4
5
DDRC=0xFF;
6
DDRD=0xFF;
7
8
TCCR0|=(1<<CS00);// Vorteiler 1
9
TIMSK|=(1<<TOIE0);// Overflow Interrupt
10
11
sei();
12
13
while(1)
14
{
15
analog_value=ReadChannel(0);// Ersten ADC auslesen
16
calculated=Calculator(analog_value);// Den Analogwert umrechnen
Crashdemon wrote:
> warum kann ich die PrintNumber Funktion nicht in die while-Schleife> packen,
Es gibt keinen Grund dafür, bzw. der Grund ist nicht in PrintNumber
zu suchen.
> sobald ich das mache, zeigt die Anzeige nur noch null an.
Wird wohl daran liegen, dass PrintNumber ständig mit 0
aufgerufen wird.
wenn das vorher alles richtig funktioniert hat als es noch außerhalb der
while-Schleife stand, dann sollte es nun nicht an der
PrintNumber-Funktion liegen.
Ich würde den Fehler dann eher mal in den Methoden ReadChannel und
Calcualator suchen.
Suche jetzt schon eine ganze weile nach einen Fehler finde aber leider
keinen ,ich glaube das sich die Interrupt ins gehäge kommen, der vom ADC
und der vom Timer. Ich poste hier mal meine Source vllt. findet ja
jemand einen Fehler?
1
#include<math.h>
2
#include<avr/io.h>
3
#include<avr/wdt.h>
4
#include<avr/iom8.h>
5
#include<util/delay.h>
6
#include<avr/sleep.h>
7
#include<avr/interrupt.h>
8
9
#define XTAL 4000000L // Systemtakt in Hz
10
11
unsignedcharstatus[2]={0x01,0x02};// Status LED's (Rot, Grün)
12
unsignedchardigit[10]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};// Bitmaske für die Zahlen
Habe eine volatile draus gemacht, seh gerade das ich noch gar nicht
geschrieben habe um was für einen Fehler es sich handelt. Das Problem
ist das die Siebensegmentanzeige immer Null anzeigt, das tut sie aber
rst seitdem ich die PrintNumber Funktion mit Interrupts habe, iegentlich
soll die den Winkel der Platine anzeigen auf der der Sensor
angeschlossen ist der über die Analog Eingänge eingelesen wird.
Mach' mal beim Start von 'main' eine Überprüfung der Resetquelle
(Power-On, Reset, Brown-Out oder Watchdog). Ich könnte mir denken, dass
der Watchdog zuschlägt, bevor 'Angle()' fertig ist. Ist aber nur eine
Vermutung, hab' mir jetzt nicht den gesamten Code genau angesehn.
Die Verwendung von 'volatile' hat nix geändert?
Ich bin mir nicht 100%ig sicher, aber meiner Meinung nach dürftest du
Probleme mit dem asin in der Angle-Methode bekommen, da soviel ich weiß
als Argument nur Werte zwischen -1 und 1 erlaubt sind. Deine Berechnung
(x-512.0)/102.4 mit x-Werten von 0 bis 1024 ergibt aber Werte zwischen
-5 und 5.
Eine andere Möglichkeit wäre der AD-Wandler. Hast du mal getestet, ob
die Werte überhaupt richtig eingelesen werden bzw. ob bei die Methode
ReagChannel wirklich Werte zwischen 0 und 1024 herauskommen?
-------------------------------------------------------
Zwei andere Sachen, die mir bei deinem Code noch aufgefallen sind, die
aber eigentlich nur kosmetischer bzw. optimierender Natur sind:
1. in deiner Methode PrintNumber kannst du die If-Anweisungen
verschachteln, denn wenn du weißt, dass eine Zahl zweistellig ist,
brauchst du ja nicht mehr kontrollieren, ob sie einstellig sein könnte.
2. in deiner ISR toggelst du counter immer zwischen 0 und 1 hin und her.
Statt
1
counter=counter+1;// Welches ist die nächste Stelle
2
3
if(counter==2)// Wenn Stelle 2 erreicht, zurücksetzen
4
{
5
counter=0;
6
}
kannst du den Code folgendermaßen einkürzen:
1
counter++;// oder wie bei dir: counter = counter + 1;
2
3
counter&=0x01;// bitweise Verknüpfung mit 1
Erklärung für die Bitverknüpfung:
hat counter den Wert 1, dann ergibt sich die Verknüfung:
0b00000001 & 0b00000001 zu 0b00000001 -> counter bleibt 1
hat counter den Wert 2, dann ergibt sich die Verknüfung:
0b00000010 & 0b00000001 zu 0b00000000 -> counter kippt auf 0 zurück
Thorsten wrote:
> 2. in deiner ISR toggelst du counter immer zwischen 0 und 1 hin und her.> Statt>
1
>counter=counter+1;// Welches ist die nächste Stelle
2
>
3
>if(counter==2)// Wenn Stelle 2 erreicht, zurücksetzen
4
>{
5
>counter=0;
6
>}
7
>
>> kannst du den Code folgendermaßen einkürzen:>
1
>counter++;// oder wie bei dir: counter = counter + 1;
2
>
3
>counter&=0x01;// bitweise Verknüpfung mit 1
4
>
Er könnte natürlich auch gleich
counter ^= 1 schreiben ;)
Möglicherweise kannst du es aber eh schreiben wie du willst, der
Optimierer könnte alle Varianten zum selben Ergebnis umwandeln.
Am WatchDog liegt es nicht, habe ihne auskommentiert und es funktioniert
trotzdem nicht, die sache mit dem asin stimmt es muss ein wert in dem
bereich von +/-1 übergeben werden, allerdings ist das nicht swo wichtig,
da der sensor sich nur im Breich der 410 - 614 Befindet, ((614,4 -512) /
102,4 = 1).
Denn Anwandler konnte ich leider noch nicht testen, da ich noch keinen
Quarz mit Usart Frequenz hab, mit einem 4Mhz Quarzoszillator scheint das
nicht zu funktionieren.
Die kosmetischen Fehler habe ich behoben, allerdings zeigt alles in
allem, immer noch nur eine Null an.
Impedance/buffering issues - This is by far the single most common source of problems in projects involving analog accelerometers, because so few people thoroughly read the required documentation. Both PIC and AVR datasheets specify that for A-D conversion to work properly, the connected device must have an output impedance under 10kΩ. Unfortunately, Analog Devices' analog accelerometers have an output impedance of 32kΩ. The solution to this is to use a low input offset rail to rail op amp as a buffer to lower the output impedance. As far as we know, the DE-ACCM is the only accelerometer solution that takes care of this problem for you.