Forum: Mikrocontroller und Digitale Elektronik ATMEGA16m1 CAN-Interrupt


von Der kleine Nils (Gast)


Lesenswert?

Moin,

ich habe hier ein kleines, selbstgebautes Testboard mit einem 
ATMEGA16m1. Ich möchte mit dem vorhandenen CAN-Controller Nachrichten 
versenden und auch empfangen. Zum Teil funktioniert das auch schon:

Ich kann Nachrichten in Form von MessageObjects erstellen und versenden. 
Die kommen bei meiner Gegenstation auch an (PEAK CAN/USB-Adapter). Der 
ATMEGA16m1 empfängt auch Nachrichten meiner Gegenstation und tütet diese 
korrekt in die MessageObjects ein. Die Nachrichten kann ich dann auch 
aus dem MOb herausholen, allerdings muss ich dazu das RXOK-Bit des 
CAN-Controllers anpollen. Das möchte ich nicht machen, ich will den 
Controller hiermit nicht blockieren.

Ich habe es bisher jedoch nicht geschafft, beim erfolgreichen Empfang 
einer Nachricht einen Interrupt auszulösen. Ich verstehe nicht warum, 
denn meiner Meinung nach habe ich alles getan, was dazu notwendig wäre.

Meinem Verständnis nach muss ich die Interrupts in den Registern CANIE2 
und CANIE1 für die MOb's aktivieren und zusätzlich noch die 
CAN-Interrupts generell aktivieren. Hierzu habe ich im CANGIE das ENIT 
und ENRX (für den Empfangsinterrupt) gesetzt.

Es passiert leider absolut gar nichts. Für den Anfang will ich mit dem 
Interrupt einfach nur mal einen globalen Boolean toggeln, mit dem ich 
dann eine simple LED einschalte. Es tut sich aber nichts. Über Polling 
funktioniert das Ganze, daher vermute ich mal, dass ich meinen Interrupt 
nicht richtig eingestellt habe (der Polling-Code ist auskommentiert).

Vielleicht hat ja jemand eine gute Idee und kann mir helfen.

Gruß
Nils
1
#define F_CPU 16000000UL
2
3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
#include <stdbool.h>
6
7
#include <util/delay.h>
8
9
bool led = false;
10
11
void can_init(void)
12
{
13
  uint8_t i,j;
14
15
  CANGCON = (1<<SWRES); // off and reset
16
17
  // Alle CAN-MOB's zurücksetzen
18
  for (i=0; i<6; i++)
19
  {
20
    CANPAGE = (i<<4); // MOb auswählen
21
    CANCDMOB = 0x00;  // MOb deaktivieren
22
    CANSTMOB = 0x00;  // Status zurücksetzen
23
    CANIDT1 = 0x00;    // ID zurücksetzen
24
    CANIDT2 = 0x00;
25
    CANIDT3 = 0x00;
26
    CANIDT4 = 0x00;
27
    CANIDM1 = 0x00;    // MASK zurücksetzen
28
    CANIDM2 = 0x00;
29
    CANIDM3 = 0x00;
30
    CANIDM4 = 0x00;
31
    for (j=0; j<8; j++)
32
    {
33
      CANMSG = 0x00; // Daten zurücksetzen
34
    } // for(..
35
  } // for(..
36
37
  // Einstellungen für 500 kBit/s mit 16 MHz Quarz
38
  /*
39
  CANBT1 = (1<<BRP0);
40
  CANBT2 = (1<<PRS2) | (1<<PRS1);
41
  CANBT3 = (1<<PHS21) | (1<<PHS20) | (1<<PHS11) | (1<<PHS10) | (1<<SMP);
42
*/
43
  // Einstellungen für 125 kBit/s mit 16 MHz Quarz
44
  CANBT1 = 0x1E;
45
  CANBT2 = 0x04;
46
  CANBT3 = 0x13;
47
  
48
  CANEN2 = 0x00;
49
  CANEN2 = 0x3F;              // Alle MObs aktivieren
50
  CANIE1 = 0x3F;              // Alle MOb Interrupts aktivieren
51
  
52
  CANGIE = (1<<ENIT) | (1<<ENRX);      // Interrupts aktivieren
53
  
54
  CANPAGE = (1<<MOBNB0);          // MOb0 auswählen
55
  
56
  
57
  CANGCON = (1<<ENASTB);          // CAN-Controller aktivieren
58
  CANSTMOB = 0x00;            // Statusregister zurücksetzen
59
  CANCDMOB = (1<<CONMOB1) | (8<<DLC0);  // CONMOB1 und DLC0 setzen, rest auf 0
60
}
61
62
int main(void)
63
{
64
  cli();
65
66
  can_init();
67
  
68
  DDRB |= (1<<DDB4);
69
  
70
  sei();
71
72
  while(1)
73
  {
74
    // Polling Methode: RXOK anpollen
75
    
76
    /*
77
    if (CANSTMOB & (1<<RXOK)) 
78
    { 
79
      CANSTMOB &= ~(1<<RXOK);
80
      CANCDMOB = (1<<CONMOB1) | (1<<RPLV);
81
      if (led)led = false;
82
      else led = true;
83
    } */
84
    
85
    
86
    if (led) PORTB |= (1<<PORTB4);
87
    else PORTB &= ~(1<<PORTB4);
88
    
89
  }
90
}
91
92
// ISR für CAN
93
ISR(CAN_INT_vect)
94
{
95
  
96
  if (led) led = false;  // LED-Bit Toggle
97
  else led = true;
98
  CANSTMOB &= ~(1<<RXOK); // RXOK zurücksetzen
99
}

von Rudolph (Gast)


Lesenswert?

Der kleine Nils schrieb:
> CANEN2 = 0x00;
>   CANEN2 = 0x3F;              // Alle MObs aktivieren
>   CANIE1 = 0x3F;              // Alle MOb Interrupts aktivieren
>
>   CANGIE = (1<<ENIT) | (1<<ENRX);      // Interrupts aktivieren
>
>   CANPAGE = (1<<MOBNB0);          // MOb0 auswählen
>
>
>   CANGCON = (1<<ENASTB);          // CAN-Controller aktivieren
>   CANSTMOB = 0x00;            // Statusregister zurücksetzen
>   CANCDMOB = (1<<CONMOB1) | (8<<DLC0);  // CONMOB1 und DLC0 setzen, rest
> auf 0

Das macht so noch wenig Sinn.
Warum werden alle MOBs ohne jede Konfiguration aktiviert?
Vor allem vor der Konfiguration?

"8<<DLC0" funktioniert so auch nicht.

Schau mal hier: Beitrag "CAN beim ATMega16M1 anders als bei 90CANxx?"

Da ist ein funktionierendes Init drin für den Versand einer Botschaft.

Mal eben auf Empfang angepasst:
1
...
2
  id = 0x444;   // Sende-ID
3
  CANPAGE = (0<<4); // select MOB0
4
  CANIDT1 = (id >> 3); // Put bits 3-11 of message ID into CANIDT1
5
  CANIDT2 = (id << 5); // Put bits 0-2 of message ID into CANIDT2
6
  CANIDM1 = 0xff;
7
  CANIDM2 = 0xe0;
8
  CANIDM4 = (1<<IDEMSK);
9
  CANCDMOB = (1<<CONMOB1) | (1<<DLC3); // set to receive, 8-bytes in message
10
11
  CANGIE = (1<<ENIT) | (1<<ENRX); // enable receive interrupt
12
  CANEN1 = 0x00;
13
  CANEN2 = (1<<ENMOB0); // enable MOB0
14
  CANIE1 = 0x00;
15
  CANIE2 = (1<<IEMOB0); // enable MOB0 IRQ
16
...

von Der kleine Nils (Gast)


Lesenswert?

Hallo Rudolph,
vielen Dank für die Antwort.

Rudolph schrieb:
> "8<<DLC0" funktioniert so auch nicht.

Da hat sich eine 8 verirrt, das sollte (1 werden. Aber das DLC brauche 
ich für's empfangen nicht, daher habe ich es entfernt.

Rudolph schrieb:
> Schau mal hier: Beitrag "CAN beim ATMega16M1 anders als bei 90CANxx?"
>
> Da ist ein funktionierendes Init drin für den Versand einer Botschaft.

Ja, das habe ich auch bereits gefunden. Der Versand funktioniert ja auch 
tadellos. Auch der Empfang. Aber der Interrupt nicht.

Trotzdem habe ich meinen Code mal abgeändert. Obwohl ich es eigentlich 
nicht will eine ID festgelegt - Ich möchte später auf erstmal generell 
jede CAN-Nachricht ansprechen. Aber das ist erstmal nebensächlich.

Allerdings leider wie gehabt: Beim Pollen des RXOK klappts, der 
Interrupt wird nicht ausgelöst...
1
#define F_CPU 16000000UL
2
3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
#include <stdbool.h>
6
7
#include <util/delay.h>
8
9
bool led = false;
10
11
void can_init(void)
12
{
13
  uint8_t i,j;
14
15
  CANGCON = (1<<SWRES); // off and reset
16
17
  // Alle CAN-MOB's zurücksetzen
18
  for (i=0; i<6; i++)
19
  {
20
    CANPAGE = (i<<4); // MOb auswählen
21
    CANCDMOB = 0x00;  // MOb deaktivieren
22
    CANSTMOB = 0x00;  // Status zurücksetzen
23
    CANIDT1 = 0x00;    // ID zurücksetzen
24
    CANIDT2 = 0x00;
25
    CANIDT3 = 0x00;
26
    CANIDT4 = 0x00;
27
    CANIDM1 = 0x00;    // MASK zurücksetzen
28
    CANIDM2 = 0x00;
29
    CANIDM3 = 0x00;
30
    CANIDM4 = 0x00;
31
    for (j=0; j<8; j++)
32
    {
33
      CANMSG = 0x00; // Daten zurücksetzen
34
    } // for(..
35
  } // for(..
36
37
  // Einstellungen für 500 kBit/s mit 16 MHz Quarz
38
  /*
39
  CANBT1 = (1<<BRP0);
40
  CANBT2 = (1<<PRS2) | (1<<PRS1);
41
  CANBT3 = (1<<PHS21) | (1<<PHS20) | (1<<PHS11) | (1<<PHS10) | (1<<SMP);
42
*/
43
  // Einstellungen für 125 kBit/s mit 16 MHz Quarz
44
  CANBT1 = 0x1E;
45
  CANBT2 = 0x04;
46
  CANBT3 = 0x13;
47
  
48
  
49
  
50
  CANGSTA = 0x00;              // Flags zurücksetzen
51
  CANGIT = 0x00;              // Interrupt Flags zurücksetzen
52
  
53
  CANSIT2 = 0x00;              // MOb-ISR-Status zurücksetzen
54
  CANSIT1 = 0x00;
55
  
56
// MOb0 initialisierung
57
  CANPAGE = (1<<MOBNB0);          // MOb0 auswählen
58
  
59
  CANSTMOB = 0x00;            // Statusregister zurücksetzen
60
  CANCDMOB = (1<<CONMOB1) | (1<<DLC3);  // CONMOB1 setzen, 8 Byte empfang
61
  
62
  CANIDT1 = 0xFF;              // ID = 7FF;
63
  CANIDT2 = 0xE0;
64
  CANIDT3 = 0x00;
65
  CANIDT4 = 0x00;
66
  
67
  CANIDM1 = 0xFF;              // Alle
68
  CANIDM2 = 0xE0;
69
  CANIDM3 = 0x00;
70
  CANIDM4 = (1<<IDEMSK);
71
  
72
  CANGCON = (1<<ENASTB);          // CAN-Controller aktivieren
73
    
74
  CANEN1 = 0x00;
75
  CANEN2 = (1<<ENMOB0);          // MOb0 aktivieren
76
    
77
  CANGIE = (1<<ENIT) | (1<<ENRX);      // Interrupts aktivieren
78
  
79
  CANIE1 = 0x00;              // MOb-Interrupt aktivieren
80
  CANIE2 = (1<<IEMOB0);          // Interrupt für Mob0 aktivieren
81
  
82
  
83
84
}
85
86
int main(void)
87
{
88
  cli();
89
90
  can_init();
91
  
92
  DDRB |= (1<<DDB4);
93
  
94
  sei();
95
96
  while(1)
97
  {
98
    // Polling Methode: RXOK anpollen
99
    
100
    /*
101
    if (CANSTMOB & (1<<RXOK)) 
102
    { 
103
      CANSTMOB &= ~(1<<RXOK);
104
      CANCDMOB = (1<<CONMOB1) | (1<<RPLV);
105
      if (led) led = false;
106
      else led = true;
107
    } */
108
    
109
    
110
    if (led) PORTB |= (1<<PORTB4);
111
    else PORTB &= ~(1<<PORTB4);
112
    
113
  }
114
}
115
116
// ISR für CAN
117
ISR(CAN_INT_vect)
118
{
119
  /*
120
  if (led) led = false;  // LED-Bit Toggle
121
  else led = true;*/
122
  led = true;
123
  CANSTMOB &= ~(1<<RXOK); // RXOK zurücksetzen
124
  CANCDMOB = (1<<CONMOB1) | (1<<RPLV);
125
}

von Rudolph (Gast)


Angehängte Dateien:

Lesenswert?

Ich habe das mal als Gelegenheit genutzt und Senden implementiert.
Wenn die Botschaft mit der ID 0x333 empfangen wird antwortet der 
Controller mit der Botschaft 0x444.

Das filtern macht doch:
  CANIDM1 = 0xff;
  CANIDM2 = 0xe0;
  CANIDM4 = (1<<IDEMSK);

Auf MOB1 alles empfangen müsste so aussehen:
  CANPAGE = (1<<4); // select MOB1
  CANCDMOB = (1<<CONMOB1); // set to receive

Natürlich davon ausgehend, dass in der Schleife drüber alle Register 
genullt wurden. :-)

Das DLC ist fürs Empfangen fester IDs schon interessant, wenn die Größe 
nämlich von der eingestellten abweicht, erhält man eine 
Data-Length-Code-Warning im CANSTMOB Register.

von Der kleine Nils (Gast)


Lesenswert?

Ein paar Sachen, die ich an deinem Quellcode nicht verstehe:

Warum wird das MOb1 nicht aktiviert, der Interrupt dafür aber 
freigegeben?

Nunja, seisdrum. Ich habe meine Initialisierungsroutine mal angepasst, 
so dass die Reihenfolge, in der die Register gesetzt werden deiner 
entspricht. Jetzt funktioniert der Interrupt - Vielen Dank schon einmal 
an dieser Stelle.


Mir ist noch nicht ganz klar, warum offenbar genau diese Reihenfolge 
eingehalten werden muss. Im Datenblatt zum µC habe ich auch nichts dazu 
gefunden. Komisch.

Anbei noch mal mein Code für Interessierte
1
#define F_CPU 16000000UL
2
3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
#include <stdbool.h>
6
7
#include <util/delay.h>
8
9
bool led = false;
10
11
void can_init(void)
12
{
13
  uint8_t i,j;
14
  uint32_t id;
15
16
  CANGCON = (1<<SWRES); // off and reset
17
18
  // Alle CAN-MOB's zurücksetzen
19
  for (i=0; i<6; i++)
20
  {
21
    CANPAGE = (i<<4); // MOb auswählen
22
    CANCDMOB = 0x00;  // MOb deaktivieren
23
    CANSTMOB = 0x00;  // Status zurücksetzen
24
    CANIDT1 = 0x00;    // ID zurücksetzen
25
    CANIDT2 = 0x00;
26
    CANIDT3 = 0x00;
27
    CANIDT4 = 0x00;
28
    CANIDM1 = 0x00;    // MASK zurücksetzen
29
    CANIDM2 = 0x00;
30
    CANIDM3 = 0x00;
31
    CANIDM4 = 0x00;
32
    for (j=0; j<8; j++)
33
    {
34
      CANMSG = 0x00; // Daten zurücksetzen
35
    } // for(..
36
  } // for(..
37
38
  // Einstellungen für 500 kBit/s mit 16 MHz Quarz
39
  /*
40
  CANBT1 = (1<<BRP0);
41
  CANBT2 = (1<<PRS2) | (1<<PRS1);
42
  CANBT3 = (1<<PHS21) | (1<<PHS20) | (1<<PHS11) | (1<<PHS10) | (1<<SMP);
43
*/
44
  // Einstellungen für 125 kBit/s mit 16 MHz Quarz
45
  CANBT1 = 0x1E;
46
  CANBT2 = 0x04;
47
  CANBT3 = 0x13;
48
  
49
    
50
//  CANGSTA = 0x00;              // Flags zurücksetzen
51
//  CANGIT = 0x00;              // Interrupt Flags zurücksetzen
52
  
53
54
  CANGCON = (1<<ENASTB);          // CAN-Controller aktivieren
55
56
  id = 0x444;   // Empfangs-ID
57
  CANPAGE = (1<<4); // select MOB1
58
  CANIDT1 = (id >> 3); // Put bits 3-11 of message ID into CANIDT1
59
  CANIDT2 = (id << 5); // Put bits 0-2 of message ID into CANIDT2
60
  CANIDM1 = 0xff;
61
  CANIDM2 = 0xe0;
62
  CANIDM4 = (1<<IDEMSK);
63
  CANCDMOB = (1<<CONMOB1) | (1<<DLC3); // set to receive, 8-bytes in message
64
65
  CANGIE = (1<<ENIT) | (1<<ENRX); // enable receive interrupt
66
  CANEN1 = 0x00;
67
  CANEN2 = (1<<ENMOB0);// | (1<<ENMOB1); // enable MOB0, MOB1
68
  CANIE1 = 0x00;
69
  CANIE2 = (1<<IEMOB1); // enable MOB0 IRQ
70
  CANSIT1 = 0x00;
71
  CANSIT2 = 0x00;
72
}
73
74
int main(void)
75
{
76
  uint8_t data[8];
77
  uint8_t i;
78
  cli();
79
80
  can_init();
81
  
82
  DDRB |= (1<<DDB4);
83
  
84
  sei();
85
86
  while(1)
87
  {
88
    // Polling Methode: RXOK anpollen
89
    
90
    /*
91
    if (CANSTMOB & (1<<RXOK)) 
92
    { 
93
      CANSTMOB &= ~(1<<RXOK);
94
      CANCDMOB = (1<<CONMOB1) | (1<<RPLV);
95
      for (i=0;i<8;i++)
96
      {
97
        data[i] = CANMSG;
98
      }
99
      
100
      if (data[0] == 0x01) led = true;
101
      else led = false;
102
    } */
103
    
104
    
105
    if (led) PORTB |= (1<<PORTB4);
106
    else PORTB &= ~(1<<PORTB4);
107
    
108
  }
109
}
110
111
// ISR für CAN
112
ISR(CAN_INT_vect)
113
{
114
  if (led) led = false;  // LED-Bit Toggle
115
  else led = true;
116
  CANSTMOB &= ~(1<<RXOK); // RXOK zurücksetzen
117
  CANCDMOB = (1<<CONMOB1) | (1<<DLC3); // set to receive, 8-bytes in message
118
}

von Rudolph (Gast)


Lesenswert?

Der kleine Nils schrieb:
> Warum wird das MOb1 nicht aktiviert, der Interrupt dafür aber
> freigegeben?

Das hatte ich übersehen.
Das funktioniert dennoch weil CANEN1 und CANEN2 garnicht die MOBs 
aktivieren, das sind Read-Only Register.
Die beiden Zeilen wirken sich also garnicht aus und können gelöscht 
werden.

Irgendwie habe ich das verpeilt als ich um 2006 den Code für den 90CAN 
geschrieben habe.
Und da das so funktioniert, war es seitdem eben weitgehend Copy&Paste 
für den Teil. :-)

Manmachmal sollte man vielleicht auch funktionierenden Code nochmal mit 
einem Blicks ins Datenblatt reviewen. :-)

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.