Forum: Mikrocontroller und Digitale Elektronik AVR: Timergesteuerter ADC Interrupt klemmt manchmal


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Dergute W. (derguteweka)


Lesenswert?

Moin,

Auf einem atmega16 verwende ich den ADC zum samplen von 1 Kanal. 
Samplingfrequenz ist 32kHz, CPU Takt 16MHz. Plan dahinter: Der ADC wird 
regelmaessig vom Timer1 getriggert; wenn er fertig gewandelt hat, gibts 
einen ADC interrupt und ich hol' mir das Sample ab.

Das funktioniert auch soweit prima. Zumindest zwischen ein paar Minuten 
und mehreren Stunden lang. So wie's aussieht, kanns dann bei mir dazu 
kommen, dass der ganze Apparat stehenbleibt. D.h. aus irgendeinem Grund 
wird nicht mehr in den ADC-Interrupthandler gesprungen (derzeit solange, 
bis ich einen Reset ausloese). Die "restliche" Software (nicht irq 
gesteuert) laeuft weiter.
Vom Fehlerbild her riecht das fuer mich ganz stark nach irgendeiner 
bloeden Racecondition, will sagen: Es koennte irgendwelche Zeitpunkte in 
meiner SW geben, in der z.b. das Interrupt-Ack nicht funktioniert oder 
sowas...Da fangen bei mir jetzt die Vermutungen an...bei mir gepaart mit 
keinerlei Erfahrung auf AVR.

Hier n' paar codefragmente - den kompletten source gibts da im File 
hb-0.1.0.zip:
Beitrag "Re: "LED-Spectrumanalyzer"software ohne Fouriertransformation"
Ich mach' hier mal einen separaten Thread auf, alldieweilen ich gesehen 
hab', wie andere Threads unter Projekte & Code durch die dort 
aufgetretenen Probleme doch ziemlich laenglich wurden.

Initialisierung:
1
...
2
    ldi r29,0x01
3
    ldi r28,0xf3
4
    out 0x2b,r29
5
    out 0x2a,r28; ocr1a = 499   ->   16MHz / 500 = 32kHz ADC sampling frequency
6
7
    ldi r28,0x00
8
    out 0x1f,r28; EEARH=0, the bar/dot lookuptable is just 256 bytes
9
    out 0x2f,r28; tccr1a = 0
10
    ldi r28,0x09
11
    out 0x2e,r28; tccr1b = 9
12
    ldi r28,0xed
13
    out 0x06,r28; adcsra=0xed
14
    ldi r28,0xe0
15
    out 0x07,r28; admux=0xe0
16
17
    in r28,0x30
18
    andi r28,0x0f
19
    ori  r28,0xa0
20
    out  0x30,r28; sfior upper nibble =0xa
21
    ...

Anfang irq-handler incl. irq-ack:
1
adc_irq:
2
    in r6,0x3f   ; save flag register
3
    ldi r25,0x08 ;
4
    out 0x38,r25 ; int ack
5
    in r25,0x05  ; read adc
6
    ;--uncomment next line for testsignal sawtooth
7
    ;mov r25,r30
8
    ;--uncomment next line for testsignal silence
9
    ;ldi r25,0x80
10
    ;--end testsignal
11
    subi r25,128 ; convert to signed 8 bit
12
    ;--uncomment next line for -6dB
13
    ;asr r25      ;  -6dB
14
    st z,r25     ; store in buffer
15
    inc r30      ; increment buffer write pointer
16
    mov r25,r30
17
    andi r25,0x0f;
18
    brne nodisplay
19
    ....
20
nodisplay:
21
    out 0x3f,r6  ; restore flag register
22
    reti

Ich denk' mal, regelmaessiges Samplen mit dem ADC ist nicht so exotisch, 
als dass das noch nie jemand in Assembler gemacht haette, daher die 
abschliessende Frage:
Was laeuft da manchmal schief?

Gruss
WK

von Karl M. (Gast)


Lesenswert?

Hallo,

was steht denn in Datenblatt in welchem Bereich sich der ADC-Takt 
befinden muss (soll) ?

Ich würde hier erstmal nachbessern und warum programmiert man so etwas 
nicht in C oder einer anderen Hochsprache ?

von Karl M. (Gast)


Lesenswert?

Ach ja,

in einer ISR muss man auch alle benutzen Register sichern, wenn man 
nicht ausschließlich diese nur dort verwendet !

von Peter II (Gast)


Lesenswert?

Noch nie etwas von Register namen gehört?

in r6,0x3f   ; save flag register
out 0x38,r25 ; int ack

wer soll denn mit 0x3F oder 0x38 etwas anfangen können?

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Karl M. schrieb:
> was steht denn in Datenblatt in welchem Bereich sich der ADC-Takt
> befinden muss (soll) ?
Wenn ich das Datenblatt richtig interpretiere, steht da:
Zwischen 50kHz und 200kHz, wenn man 10bit haben will, und oberhalb von 
200kHz, wenn einem 8bit reichen. Tut mir.
Also hoff' ich mal, dass ich den Vorteiler so eingestellt hab', dass 
16MHz/32=500kHz rauskommen. Damit sollte der ADC pro Wandlung 
500kHz/32kHz= 15.625 Zyklen Zeit haben. Damit sollte ich ueber den 13 
Zyklen liegen, die der ADC nach Datenblatt braucht. Die Ergebnisse des 
ADC sehen fuer mich auch OK aus, soweit ich's beurteilen kann. Haben 
also durchaus was mit dem Eingangssignal zu tun.


> Ich würde hier erstmal nachbessern und warum programmiert man so etwas
> nicht in C oder einer anderen Hochsprache ?
Das programmiert man deshalb nicht in C oder einer anderen Hochsprache, 
weil man gesehen hat, was ein in C programmierter Interrupt-Handler so 
an Ballast mit sich rumschleppt und weil man weiss, dass bei 32000 
Interrupts/sec. jeder Zyklus zaehlt, weil man weiss, dass zwischen 2 
Interrupts auch nur 500 Zyklen vergehen.

Karl M. schrieb:
> in einer ISR muss man auch alle benutzen Register sichern, wenn man
> nicht ausschließlich diese nur dort verwendet !

<Loriotmode>Ach</Loriotmode> Genau das ist einer der Gruende, warum ich 
das in Assembler gemacht hab'. Wenn man nicht alle von ausserhalb 
benutzten Register in einem IRQ Handler sichert und wiederherstellt, der 
32k mal pro Sekunde aufgerufen wird, dann ist's auch eher 
unwahrscheinlich dass das ueberhaupt ein paar Sekunden laueft. Bei mir 
tuts das aber schon auch stundenlang.

Peter II schrieb:
> Noch nie etwas von Register namen gehört?
Ja, schon. Nur eben der gcc nicht. Dessen asm output war vor Monaten mal 
die Basis des ganzen.

Gruss
WK

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Dergute W. schrieb:
> out 0x2a,r28; ocr1a = 499   ->   16MHz / 500 = 32kHz ADC sampling
> frequency

 Ohne mich jetzt im Detail durchzurechnen:
1
    out 0x06,r28; adcsra=0xed

 Das ist 16MHz / 32 = 500KHz = 2us * 13Cy = 26us = 38.4KHz.
 Wird aber on Timer/Counter1 Compare Match B gestartet ?

 TCCR1A = 0, das ist für mich Normal Mode, was da OCR1A zu suchen
 hat, ist mir unklar.

 Kurz, das ist so viel zu kompliziert, wenn du Hilfe erwartest, must
 du schon deinen Teil dazu beitragen.
 Also:
 Ich hab Timer1 so eingestellt...
 Und zwar hier...
 ADC ist so eingestellt...
 Und zwar hier...

 Und nicht Link auf 17 zipped Files und dann um Hilfe bitten.

von Einer K. (Gast)


Lesenswert?

Dergute W. schrieb:
> Das programmiert man deshalb nicht in C oder einer anderen Hochsprache,
> weil man gesehen hat, was ein in C programmierter Interrupt-Handler so
> an Ballast mit sich rumschleppt

Um genau dieses zu vermeiden, wurde das Attribut "nacked" erfunden.
(wenn ich mich nicht total irre)


Dergute W. schrieb:
> <Loriotmode>Ach</Loriotmode>
Ja.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Peter II schrieb:
> wer soll denn mit 0x3F oder 0x38 etwas anfangen können?

Marc V. schrieb:
> Kurz, das ist so viel zu kompliziert, wenn du Hilfe erwartest, must
>  du schon deinen Teil dazu beitragen.

 Deswegen verwendet auch ATMEL Register und Bitnamen.
1
    out 0x06,r28

 und:
1
    ldi r28, 1<<ADEN | 1<<ADSC | 1<<ADATE | 1<<ADIE | 1<<ADPS2 | 1<<ADPS0
2
    out ADCSRA, r28

 ist doch ein Unterschied, oder ?

: Bearbeitet durch User
von Dergute W. (derguteweka)


Lesenswert?

Moin,

Marc V. schrieb:
> Kurz, das ist so viel zu kompliziert, wenn du Hilfe erwartest, must
>  du schon deinen Teil dazu beitragen.
>  Also:
>  Ich hab Timer1 so eingestellt...
>  Und zwar hier...
>  ADC ist so eingestellt...
>  Und zwar hier...

Ja, da ist natuerlich was dran, das ist in der Form, in der ich's 
vorliegen hab', nicht so mundgerecht serviert, wie es sein koennte. 
Pardong.

>  Und nicht Link auf 17 zipped Files und dann um Hilfe bitten.
Tja, wie man's macht, isses verkehrt. Wenn da nur die .s Files und ein 
Makefile drinnen ist, motzen wieder welche, weil sie nur die hex-Files 
wollen; weil sie nicht wissen, wie's zum Inhalt des eeproms kommt, etc.
Daher hatte ich auch hier direkt signifikante Teile der sourcen in den 
Post uebernommen.

Marc V. schrieb:
> TCCR1A = 0, das ist für mich Normal Mode, was da OCR1A zu suchen
>  hat, ist mir unklar.

Mir momentan auch, wahrscheinlich war das in irgendeinem 
C-Codeschnipsel, den ich ausm www gefischt hatte, vielleicht um 
zusaetzlich einen Pin wackeln zu lassen.
Merci schonmal fuer die Anregung, ich werd' mir die Timerinitialisierung 
noch paarmal angucken.

Aber mal unter uns: Wie wahrscheinlich ist es, dass ein Effekt, der 
irgendwann mal nach Stunden auftritt und natuerlich nicht, wenn man 
drauf wartet, mit einer falschen Initialisierung zusammenhaengt?
Ich mein - klar, wenn das Ding noch nie gelaufen waere, oder nach genau 
1x ADC wandlung haengen wuerde - dann isses wohl was mit der 
Initialisierung. Aber der Effekt tritt halt wirklich selten auf. Hab's, 
waehrend ich programmiert hab', nicht bemerkt; wahrscheinlich, weil die 
Software nie lang genug gelaufen ist.

U. F. schrieb:
> Um genau dieses zu vermeiden, wurde das Attribut "nacked" erfunden.
> (wenn ich mich nicht total irre)

OMG, "naggisch" als Attribut hab' ich noch nicht gekannt. OK, 
wahrscheinlich kriegt man mit solchen Sauereien mit Gewalt irgendwelchen 
Sourcecode  zustande, der, wenn durch einen gcc gepruegelt wird, 
irgendwie sowas aehnliches ergibt, wie das, was ich direkt in Assembler 
haben wollte.
Aber ob das dann besser leserlich ist?

Marc V. schrieb:
> ist doch ein Unterschied, oder ?

Ja klar - wie Tag und Nacht. Mit beiden kann ich ohne Datenblatt als pdf 
fuer erleichterte Suche oder meinetwegen Headerfile gleich wenig 
anfangen.

Gruss
WK

von S. Landolt (Gast)


Lesenswert?

Dergute W. schrieb:
> Marc V. schrieb:
>> TCCR1A = 0, das ist für mich Normal Mode, was da OCR1A zu suchen
>>  hat, ist mir unklar.
>
> Mir momentan auch ...

?
Mit WGM12 (alias CTC1) in TCCR1B wird CTC auf OCR1A eingeschaltet, OCR1B 
steht auf 0, löst also dort den ADC aus, stimmt doch alles. Der beste 
Beweis ist ja auch, dass es im Prinzip läuft.
  Ich vermute ein ungewolltes Überschreiben von Registern und/oder SRAM.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

S. Landolt schrieb:
> Mit WGM12 (alias CTC1) in TCCR1B wird CTC auf OCR1A eingeschaltet, OCR1B
> steht auf 0, löst also dort den ADC aus, stimmt doch alles. Der beste
> Beweis ist ja auch, dass es im Prinzip läuft.

 Korrekt.
 Aber auch wiederum der beste Beweis dafür, dass man es mit Bitnamen,
 Registernamen und direkt hintereinander im Code machen soll... ;)

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

Ganz Ihrer Meinung.
Als Anregung für Weka:
1
    ldi     tmp0,(1<<ADEN)+(1<<ADATE)+(1<<ADIE)+(1<<ADPS2)+(1<<ADPS0)   ; /32 (-> 500 kHz)
2
    out     ADCSRA,tmp0
3
    ldi     tmp0,(1<<REFS1)+(1<<REFS0)+(1<<ADLAR) ; intern 2.56 V
4
    out     ADMUX,tmp0
5
    ldi     tmp0,(1<<ADTS2)+(1<<ADTS0)            ; timer1 compare match B
6
    out     SFIOR,tmp0
7
    ldi     tmp0,high(500-1)                      ; 16 MHz -> 32 kHz
8
    out     OCR1AH,tmp0
9
    ldi     tmp0,low (500-1)
10
    out     OCR1AL,tmp0                           ; OCR1B bleibt auf 0
11
    ldi     tmp0,(1<<WGM12)+(1<<CS10)             ; CTC on OCR1A, /1
12
    out     TCCR1B,tmp0

von Carl D. (jcw2)


Lesenswert?

Man kann auch C und ASM Code mischen, wenn man tatsächlich ein Problem 
mit einer ISR haben sollte. Und ob man hat, "erfühlt" man am besten 
durch messen.
".s" bedeutet doch GNU-Toolchain, oder nicht?

: Bearbeitet durch User
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
Noch kein Account? Hier anmelden.