Forum: Compiler & IDEs ATMEGA 644 Timer1 CTC Interrupt


von Uli (Gast)


Lesenswert?

Hallo in die Runde,

wahrscheinlich habe ich etwas vor den Augen und übersehen - ich habe 
folgenden Code und bin der Meinung, den Timer1 des ATMEGA 644 in den CTC 
Mode mit Interrupt on Compare Match (A) gesetzt zu haben. Zum Test soll 
in der ISR eine LED getoggelt werden. Hier der Code:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <avr/wdt.h>
4
5
#define CANIX_LED_YELLOW 1
6
#define LED_PORT PORTA
7
#define LED_PORT_DDR DDRA
8
9
ISR(TIMER1_COMPA_vect) {
10
  LED_PORT ^= (1 << CANIX_LED_YELLOW);
11
}
12
13
void timer_init(void) {
14
  TCNT1 = 0;
15
16
  OCR1A = 200;
17
  TIMSK1 = (1 << OCIE1A); /* Enable interrupt on compare match */
18
  TCCR1B = (1 << WGM12) | (1 << CS12) | (1 << CS10); /* Prescaler */
19
20
  sei();
21
}
22
23
int main(void) {
24
25
  /* Enable watchdog with 2 secs timeout */
26
  //wdt_enable(WDTO_2S);
27
  //wdt_reset();
28
29
  /* Yellow LED */
30
  LED_PORT_DDR |= (1 << CANIX_LED_YELLOW);
31
        LED_PORT &= ~(1 << CANIX_LED_YELLOW);
32
33
  timer_init();
34
35
  /* test */
36
  while(1) {
37
    wdt_reset();
38
  }
39
40
  return 0;
41
}

F_CPU ist 8MHz, Fuses sind E:FC, H:D2, L:E7, der Code wird an Stelle 
7800hex als Bootloader geschrieben.
Ich sollte bei dem eingestellten Prescaler von 1024 doch ein gut 
sichbares Blinken der LED erwarten können?!
Nach meinen Recherchen wird die ISR nicht ausgeführt. Der Zähler aber 
läuft, denn wenn ich z.B. in der Hauptschleife den Zählerstand abfrage 
und bei jeweils 0 bzw. 100 die LED an- und ausschalte habe ich mein 
Blinken.

Der WD ist nur testweise im Code, der dürfte doch keinen Einfluss haben, 
oder?

Irgendwelche Fallstricke? Etwas übersehen? Interrupts global noch aus 
trotz sei()?

Viele Grüße Uli

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Uli schrieb:
> Code wird an Stelle 7800hex als Bootloader geschrieben

Ich würde es erstmal mit dem Code auf der Basisadresse testen.

> 7800hex

Falls das eine Byteadresse ist (übliche Zählung bei allen Tools außer 
bei Atmel selbst), dann ist das falsch. Das Atmel-Datenblatt meint hier 
16-bit-Wortadressen. Als Byteadresse wäre das 0xf000.

von Uli (Gast)


Lesenswert?

Es ist die Boot start address, die mir der fuse-calculator bei einer 
boot-size von 2048 words vorgibt (https://www.engbedded.com/fusecalc/). 
Im Makefile habe ich diese 7800 dann als Beginn der Text-Section 
eingetragen. Was müsste ich denn sonst dem Linker mitgeben?

Der Code wird ja auch ausgeführt - die LED kann ich ja "händisch" über 
zB:
1
PORTA |= (1 << LED); //einschalten
2
PORTA &= ~(1 << LED); //ausschalten

Nur der Interrupt will nicht.
Ich hatte am Anfang meiner Versuche nicht den CTC mode sondern den 
normalen mode und einfach den Overflow-Interrupt nutzen wollen; das 
gleiche Ergebnis. Kein Interrupt.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Uli schrieb:
> Was müsste ich denn sonst dem Linker mitgeben?

Schrob ich doch: 0xf000.

Du hast ein 4 KiB-Page für den Bootloader reserviert. Dein Device hat 64 
KiB. Folglich muss der Bootloader bei 60 KiB beginnen. Das ist 0xf000.

Atmel zählt das halt alles in 16-bit-Worten (weil der Flash so 
organisiert ist). Ist nicht wirklich logisch, macht sonst niemand: auf 
einem 32-bit-ARM werden trotzdem Byteadressen benutzt, die müssen dann 
halt durch 4 teilbar sein.

Wenn du das mit der Bootloader-Adresse vergurkst, funktioniert der Code 
an sich schon, nur die Interrupts nicht, denn es gibt an der Stelle, wo 
der Controller sie sucht, keine Vektortabelle.

von Uli (Gast)


Lesenswert?

Okay, mein Fehler. Klingt logisch. Dennoch noch eine Verständnisfrage: 
Warum startet der Code dennoch, wenn er doch - zumindest bei dem, was 
ich dem Linker mitgebe, nämlich 7800 - an der falschen Stelle im Flash 
liegt (liegen müsste)? Ich nahm an, dass die Adresse, die ich der 
Text-Section mitgebe dem Programmiertool - bei mir avrdude - sagt, an 
welche Stelle im Flash der Anfang geschrieben wird?

Gruß Uli

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Uli schrieb:
> Warum startet der Code dennoch

Er startet auf (byteweise) 0xf000. Dort ist aber nichts, also 0xFFFF. 
Das wird (wenn auch nicht dokumentiert) wie ein NOP behandelt. Der PC 
tickt weiter, läuft bei 0xFFFE dann über auf 0x0000 (er zählt immer 
16-bit-weise). Dort stehen auch wieder 0xFFFF-NOPs … irgendwann erreicht 
er dann deinen Code auf 0x7800, auf dem steht der Sprung auf deinen 
eigentlichen Reset-Vektor, ab da "tickt" es normal – bis zum ersten 
Interrupt. Der findet keinen Vektor, sondern nur 0xFFFF, und es geht von 
vorn los …

von Uli (Gast)


Lesenswert?

Okay, danke. Hatte auch das Thema Interrupt-Vektor komplett unbeachtet 
gelassen. Das hier hat auch geholfen:

https://www.mikrocontroller.net/articles/AVR_Bootloader_in_C_-_eine_einfache_Anleitung

Danke nochmal!

Gruß Uli

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.