Hallo Zusammen
ich habe hier ein Problem mit SPI.
Ich möchte einen LTC1865 mit einem Atmega16 der bei 12MHz läuft
auslesen.
Dazu habe ich vollgende Routine geschrieben, die ich per
volatileintadc_datah,adc_datal;//Variablen aus dem ADC
15
voidADC_get(intADC_CH)
16
{
17
// PORTB = PORTB & 0b11101111;
18
_delay_ms(1000);
19
PORTB=PORTB|0b00010000;
20
_delay_ms(10);
21
uart_putc(1);
22
23
uart_putc(2);
24
switch(ADC_CH)
25
{
26
case0:
27
uart_putc(3);
28
SPDR=0x80;//Daten anlegen für welchen Channel
29
PORTB=PORTB&0b11101111;
30
uart_putc(4);
31
//Bis hier her funktioniert es - allerdings bleibt er in der Schleife hängen
32
while(!(SPSR&(1<<SPIF)));//warten bis Daten da sind
33
uart_putc(5);
34
adc_datah=SPDR;//Daten aus Register und in datah speichern
35
SPDR=0x00;//Pseudodaten anlegen
36
while(!(SPSR&(1<<SPIF)));//warten bis Daten da sind
37
adc_datal=SPDR;//Daten aus Register lsen und in datal speichern
38
uart_putc(5);
39
break;
40
case1:
41
// kann zur zeit vernachlässig werden
42
uart_putc(3);
43
SPDR=0xC0;//Daten anlegen für welchen Channel
44
while(!(SPSR&(1<<SPIF)));//warten bis Daten da sind
45
adc_datah=SPDR;//Daten aus Register und in datah speichern
46
SPDR=0x00;//Pseudodaten anlegen
47
while(!(SPSR&!(1<<SPIF)));//warten bis Daten da sind
48
adc_datal=SPDR;//Daten aus Register lesen und in datal speichern
49
break;
50
51
uart_putc(6);
52
}
53
}
Die uart_putc() habe ich drinnen, dass ich erkenne wo er nicht mehr
weiterkommt, zudem konnte ich mit einem Dragon nachweißen, dass alle
Register nach meinem Vorgaben gesetzt werden.
Dazu habe ich auch schon im Internet gesucht und ein paar dinge gefunden
die ich falsch gemacht habe (zb. SPE zu spät gesetzt) doch diese sind
nun behoben.
Hat jemand eine Idee wo der Fehler liegt?
Wenn ihr noch mehr Infos braucht bitte sagen.
hat i ganz sicher den WErt 8. Von der vorher zugewiesenen 5 ist nichts
mehr übrig.
Genauso auch hier:
Von deinem SPE Bit ist nach der zweiten Zuweisung nichts mehr übrig.
Man könnte auch sagen: Gratuliere. Mit der 2-ten Zuweisung hast du das
SPI gerade wieder abgeschaltet.
> die ich falsch gemacht habe (zb. SPE zu spät gesetzt)
Was heißt "zu spät gesetzt". Mach eine einzige Zuweisung an SPCR, in der
alle Bits gesetzt sind, die du brauchst und gut ists.
verda.............................t
VIELEN DANK - habe wohl den Baum vor lauter Bäumen nicht mehr gesehen.
naja jetzt komme ich per JTAG Debuging über die while Schleife
DANKE
@ fuelre F. (fuelre)
>Falls mal jemand nach einer Lösung für den ADC sucht:>Ist zwar von den Timings noch nicht optimiert aber ansonsten>funktioniert es
Dafür muss man aber nicht identischen Code in mehrere case Zweige
reinschreiben. Es reicht, per Switch oder Array das unterschiedliche
Steuerwort zu dekodieren.
1
voidADC_get(intADC_CH)
2
{
3
PORTB=PORTB&0b11101111;
4
_delay_ms(10);
5
PORTB=PORTB|0b00010000;
6
_delay_ms(10);
7
8
switch(ADC_CH)
9
{
10
case0:
11
SPDR=0x80;//Kanal 1 auswählen
12
break;
13
case1:
14
SPDR=0x40;//Kanal 2 auswählen, NICHT GETESTET, nur Prinzip!
15
break;
16
}
17
PORTB=PORTB&0b11101111;
18
while(!(SPSR&(1<<SPIF)));//warten bis Daten da sind
19
adc_datah=SPDR;//Daten aus Register und in datah speichern
20
SPDR=0x00;//Pseudodaten anlegen
21
while(!(SPSR&(1<<SPIF)));//warten bis Daten da sind
22
adc_datal=SPDR;//Daten aus Register lsen und in datal speichern
23
PORTB=PORTB|0b00010000;
24
uart_putc(adc_datah);//Ausgabe der Daten adc_datah und adc_datal
while(!(SPSR&(1<<SPIF)));//warten bis Daten da sind
4
adc_datah=SPDR;//Daten aus Register und in datah speichern
dann muss dein ADC ein Hellseher sein, wenn er schon vorher weiss,
welche Daten welchen Kanales er als erstes Byte beim Byteaustausch
rübergeben muss, in dessen Zuge du ihm erst mal die Kanalnummer
mitteilst.
Warum blos glaube ich da nicht daran?
Aaaaah, weil du es nie mit 2 ADC-Kanälen probiert hast. Das könnte es
erkläeren. Aber ansonsten kann das gar nicht gehen. Wenn du dem ADC die
Kanalnummer geben musst, dann sind mindestens 3 SPI
Byte-Austausch-Operationen notwendig, um die Kanalnummer und dann die
darauf folgenden 2 Werte-Bytes von A nach B zu übertragen.
@ Karl Heinz (kbuchegg) (Moderator)
>erkläeren. Aber ansonsten kann das gar nicht gehen. Wenn du dem ADC die>Kanalnummer geben musst, dann sind mindestens 3 SPI>Byte-Austausch-Operationen notwendig, um die Kanalnummer und dann die>darauf folgenden 2 Werte-Bytes von A nach B zu übertragen.
Nein, es sind 2, weil der ADC etwas anders funktioniert. Man sendet nur
2 Steuerbits im 1. Byte, der Rest ist don't care. Danach noch ein Dummy
Byte. Parallel dazu wird das Ergebnis der VORHERIGEN Messung übertragen.
Ob der Code jetzt aber wirklich gut ist hab ich nicht geprüft ;-)
Falk Brunner schrieb:> @ Karl Heinz (kbuchegg) (Moderator)>>>erkläeren. Aber ansonsten kann das gar nicht gehen. Wenn du dem ADC die>>Kanalnummer geben musst, dann sind mindestens 3 SPI>>Byte-Austausch-Operationen notwendig, um die Kanalnummer und dann die>>darauf folgenden 2 Werte-Bytes von A nach B zu übertragen.>> Nein, es sind 2, weil der ADC etwas anders funktioniert. Man sendet nur> 2 Steuerbits im 1. Byte, der Rest ist don't care. Danach noch ein Dummy> Byte. Parallel dazu wird das Ergebnis der VORHERIGEN Messung übertragen.
OK. Das heist aber doch, dass sich dieses Steuerbyte dann erst auf die
nächste Messung auswirkt.
Solange er nur mit 1 Kanal misst, ist das ja ok.
Aber sobald er abwechselnd auf beiden Kanälen messen will, muss man das
berücksichtigen. Ich wollts nur noch mal explizit herausgestrichen
haben. Denn ohne dieses Wissen, kann ein Aufruf von
1
voidADC_get(intADC_CH)
2
...
3
4
5
channel0=ADC_get(0);
6
channel1=ADC_get(1);
zu bösen Überraschungen bei den Werten führen. Die 0 in
1
channel0=ADC_get(0);
bezieht sich nicht auf das Ergebnis, das man aus der Funktion
rauskriegt, sondern darauf, welches Ergebnis man beim nächsten Aufruf
von ADC_get kriegt.
Karl Heinz schrieb:> Solange er nur mit 1 Kanal misst, ist das ja ok.> Aber sobald er abwechselnd auf beiden Kanälen messen will, muss man das> berücksichtigen. Ich wollts nur noch mal explizit herausgestrichen> haben. Denn ohne dieses Wissen, kann ein Aufruf von void ADC_get(int> ADC_CH)> ...>> channel0 = ADC_get( 0 );> channel1 = ADC_get( 1 );>> zu bösen Überraschungen bei den Werten führen. Die 0 in channel0 => ADC_get( 0 );> bezieht sich nicht auf das Ergebnis, das man aus der Funktion> rauskriegt, sondern darauf, welches Ergebnis man beim nächsten Aufruf> von ADC_get kriegt.
Wie kann man denn das Problem softwaretechnisch lösen, dass man am Ende
über zwei Kanäle problemlos messen kann? Wie sieht der Aufruf bzw. wie
ist er strukturiert? Arbeite ich nur mit einem Kanal, läuft es. Sobald
ich wie beschrieben beide Kanäle nacheinander auswähle, kommt es zur
fehlerhaften Ausgabe.
Christian W. schrieb:> Wie kann man denn das Problem softwaretechnisch lösen, dass man am Ende> über zwei Kanäle problemlos messen kann? Wie sieht der Aufruf bzw. wie
a) Ein Dummy Aufruf genügt doch, oder ?
b) Bei 250Ks sind es 4uS, solange kann dein Programm warten ?
Hallo Christian
ich habe das gelöst indem ich in der Funktion abfrage welcher Channel
als letztes mal abgerufen wurde. Falls nun der gewünschte channel nicht
als letztes dran war, wird die konversation neu gestartet und solange
gewartet. Wenn schon der richtige channel abgefragt wurde gibt die
Funktion mir sofort den Wert zurück.
mfg fuelre
Hallo,
danke erstmal für die schnelle antwort. Also die ADC-Funktion mit einer
if-Anweisung erweitern?
Hättest du vielleicht ein Codebeispiel dazu?
Schöne Grüße.
Christian W. schrieb:> Hättest du vielleicht ein Codebeispiel dazu?
Wozu ?
Du hast es jetzt fast soweit.
Einfach eine variable z.B. 'LastChan' oder so verwenden.
Zuerst ein Dummy Aufruf mit Kanal 0, LastChan steht auf 0.
Wenn du abwechselnd die Kanäle aufrufst, brauchst du nichts mehr
zu machen, in LastChan steht von welchen Kanal die Werte sind. Nur
halt die LastChan fleissig toggeln.
Wenn nicht, wirst du die 4us warten müssen, falls LastChan ungleich...
Hallo Christian
ich habe gerade das Projekt geöffnet und musste feststellen, dass ich
den Teil nochmal anderst gelöst habe, da sich die Aufgabe geändert hat.
Hier musste ich die Channel abwechselnd abfragen, daher musste ich immer
ein "dummy Aufruf" machen.
Danach habe ich noch mit 10 Werten gemittelt.
Ich glaube auch, dass ich die wartezeiten nicht ausgereizt habe, da dies
bei mir nicht wichtig war.
Falls du doch noch interesse am Code hast:
1
#define ADC0 0
2
#define ADC1 1
3
#define set_CS_ADC PORTB = PORTB & 0b11110111;
4
#define cls_CS_ADC PORTB = PORTB | 0b00001000;
5
6
unsignedintADC_get(charADC_CH)
7
{
8
unsignedcharadc_datah,adc_datal;
9
unsignedlongtemp_adc=0;
10
switch(ADC_CH)
11
{
12
case1:
13
{
14
set_CS_ADC
15
SPDR=0x80;
16
while(!(SPSR&(1<<SPIF)));
17
SPDR=0x00;
18
while(!(SPSR&(1<<SPIF)));
19
cls_CS_ADC
20
21
for(chari=0;i<10;i++)
22
{
23
_delay_ms(10);
24
set_CS_ADC
25
SPDR=0x80;
26
while(!(SPSR&(1<<SPIF)));//warten bis Daten da sind
27
adc_datah=SPDR;//Daten aus Register und in datah speichern
28
SPDR=0x00;//Pseudodaten anlegen
29
while(!(SPSR&(1<<SPIF)));//warten bis Daten da sind
30
adc_datal=SPDR;//Daten aus Register lsen und in datal speichern
31
cls_CS_ADC
32
temp_adc=temp_adc+((adc_datah*256)+adc_datal);
33
}
34
break;
35
}
36
case2:
37
{
38
set_CS_ADC
39
SPDR=0xC0;
40
while(!(SPSR&(1<<SPIF)));
41
SPDR=0x00;
42
while(!(SPSR&(1<<SPIF)));
43
cls_CS_ADC
44
45
for(chari=0;i<10;i++)
46
{
47
_delay_ms(10);
48
set_CS_ADC
49
SPDR=0xC0;
50
while(!(SPSR&(1<<SPIF)));//warten bis Daten da sind
51
adc_datah=SPDR;//Daten aus Register und in datah speichern
52
SPDR=0x00;//Pseudodaten anlegen
53
while(!(SPSR&(1<<SPIF)));//warten bis Daten da sind
54
adc_datal=SPDR;//Daten aus Register lsen und in datal speichern
Okay also raten mir beide zu der Dummylösung :-)
Ich danke euch erstmal für die Hilfe. Ich werde mich nun mit dem
Beispielcode beschäftigen und ihn für meine Aufgabe anpassen.
Bei Fragen melde ich mich einfach nochmal.
Gruß.
Mit der Variante kann also der Aufruf der Funktion in der Main wie zu
Beginn beschrieben über
channel0 = ADC_get( 0 );
channel1 = ADC_get( 1 );
nacheinander erfolgen oder muss man dort auch noch was beachten?