In diesem Bereich versuche ich mich zu den Funktionen rund um die HS-CAN Anbindung des BCM vorzuarbeiten. Die gesamte Programmierung des BCM geschieht ausschließlich über den HS-CAN. Der MS-CAN sowie MM-CAN dienen nur der Kommunikation mit den Modulen. Daher ist der HS-CAN für mich erstmal der interessantere.
Welcher CAN-Controller bedient den HS-CAN?
"Wie immer" starte ich mit der Hardware. Aus dem Basis-Untersuchen ist ja schon bekannt das ein R32C auf dem BCM arbeitet. Dieser hat laut Datenblatt 3 CAN-Module (Controller)
Los geht es am Stecker. Der HS-CAN Bus liegt am "blauen" BCM-Stecker (Armaturenbrett-Kabelbaum) C3 and den Pins 6 und 7 an:
Auf der Platine des BCM wurde ordentlich mit Harz gekleckert. Das macht die Identifizierung der Bauteilbezeichnungen mitunter sehr schwer, weil man diesen erstmal vorsichtig runterkratzen muss. Ein CAN-Bus benötigt immer einen Transceiver (Pegelwandler). Hiervon finden sich gleich mehrere auf der Platine. Der vom HS-CAN ist ein TJA1042 (siehe Position (15) auf BCM Firmware Reverse-Engineering). Hier enden direkt die beiden Pins von der Buchse.
Auf der anderen Seite des TJA1042 geht es dann zu den Pins 1 und 2 vom R32C:
Die Pins von Microcontrollern wie dem R32C sind häufig nicht auf eine Funktion festgelegt, sondern abhängig von der Programmierung des Chips. Das Datenblatt zeigt dies durch mehrere Bezeichnungen mit "/" getrennt.
Ein physikalischer Pin am Gehäuse des Chips kann durch Programmierung mit verschiedenen Modulen innerhalb des Chips verbunden werden, wie hier an dem Schaubild zu erkennen:
Somit kann z.B. der "Pin 1" folgende Funktionen haben:
- "P9_6" => GPIO Port, ein digitaler IO-Port Nummer #6 in der Portgruppe #9
- "ANEX1" => Analoger, externer Watchdog #1
- "TXD4" => Sendeleitung des seriellen UART #4
- "CAN2OUT" => Sendeleitung des CAN-Controllers #2
- "CAN1OUT" => Sendeleitung des CAN-Controllers #1
Die Tatsache das der TXD des TJA1042 mit dem Pin 1 des R32C verbunden ist und der TJA1042 keinen Chip-Enable hat, lässt darauf schließen das es hier nur um die beiden CAN-Controller gehen kann. Welcher Controller nun am Ende die Arbeit macht, werden wir erst in der Initialisierungsroutine erkennen können.
Klar ist jedoch das es im Code Zugriffe auf die Konfiguration von Port P9_5 und P9_6 geben muss. Um diese zu finden und zuzuordnen muss man die Funktionsweise der CAN Controller etwas besser verstehen. Hilfe hierzu gibt das "R32C Users Manual" R32C-152 Users Manual.pdf im Abschnitt "CAN Controller", als auch die "R32C CAN Applicaiton Note" R32C CAN Application Note.pdf.
Im Schaubild eines CAN-Controllers erkannt man die Funktionsblöcke:
An (1) kommen die physikalischen Signale vom CAN-Transceiver zum Controller (Der Zusatz "/CANxWU" bedeutet das dieser Pin auch zum aufwecken der MCU aus einem Schlafmodus genutzt werden kann. Auf Deutsch: Kommt über den CAN-Bus eine Botschaft, kann diese das BCM aus dem Schlafmodus holen). Zur Takterzeugung wird an (8) der CPU-Takt (8 MHz) verwendet um die Baudrate des CAN-Bus (bei HS-CAN 500 MBit/s) einzustellen.
Hat der Protocoll Controller (2) z.B. ein eingehendes Datenpaket erkannt, leitet er es an den Filter und Priorisierer (3) weiter, welcher es ggf. in eine Mailbox (4) verschiebt. Die mit (5) gekennzeichneten SFRs sind zur Programmierung der Komponenten gedacht und für uns die interessanten.
Der Interrupt Generator (6) erzeugt je nach Betriebszustand verschiedene Ereignisse welche dann von der Software mittels Interrupt-Service-Routinen (ISRs) behandelt werden, so z.B. der Empfang einer Botschaft und die Verarbeitung der Daten darin.
Die Programmierung des Controllers (2) entscheidet also welche CAN I/O pins genutzt werden. Das schaue ich mir genauer an. Irgenwo muss es einen Zusammenhang zwischen den Pins 5+6 von Gruppe 9 und einem CAN-Controller geben. Hierzu finde ich das "Port P9_i Function Select Register". Das "i" wird dann mit "5" oder "6" substituiert. Es gibt also für jeden einzelnen Port ein eigenes 8-Bit Register im SFR2:
Die unteren 3 Bits geben die Konfiguration des Ports an, also bestimmen wohin dieser physikalische Pin intern verbunden wird. Die SFR2 Speicheradresse für Port P9_6 (CAN Ausgangsleitung) ist somit 0x400ed. Wird dort in die Bits 0-2 der Wert 0b101 geschrieben, ist er mit dem Controller CAN2 verbunden, beim Wert 0b110 mit dem von CAN1.
Mittels einer Rückwärtssuche auf den Schreibzugriff auf diese Adresse finde ich im Code:
Auch für nicht-Assembler geschädigte gut zu erkennen das der Wert 0b00000101 auf die Adresse geschrieben wird. Der Disassembler hat hier bereits die Adresse 0x400ed durch den Namen "p9_6s" ersetzt, weil er den Registeraufbau der R32C kennt. Der Wert verrät und nun das der Pin P9_6 mit der Sendeleitung von Controller CAN2 verbunden ist.
Somit ist klar das der HS-CAN vom Controller CAN2 bedient wird!
Der gesamte Code um diese Stelle herum enthält die Programmierung des Controllers u.a. mit der Baudrate von 500 MBit/s, sowie der Einstellung zu Interrupts und Mailboxen. Jeder Kanal verfügt über bis zu 32 solcher "Mailboxen" in die eingehende oder zu Sendung ausgehende CAN-Botschaften zwischengespeichert werden. Damit sich die Software nicht mit jedem Datagramm auf dem Bus beschäftigen muss gibt es zusätzliche "Acceptance Filter" die nur solche Botschaften in die Mailboxen einleiten, welche auch zum Zuständigkeitsbereich des BCM gehören.
Ich habe die Routine mit Kommentaren versehen. Wen das interessiert kann es ja mal durcharbeiten und mit dem Handbuch vergleichen:
CODE:FFFA1EDE hscan_init: ; CODE XREF: sub_FFFB04D4-E466↓p
CODE:FFFA1EDE PUSHM A1
CODE:FFFA1EE0 BSET prc2, prcr ; Recommendation of manual, set PRC2 to 1 just before changing direction register
CODE:FFFA1EE5 BSET pd9_6, pd9 ; Set port P9, pin 6 as output (connected to TXD of ext. CAN transceiver)
CODE:FFFA1EE9 BSET prc2, prcr ; Recommendation of manual, set PRC2 to 1 just before changing direction register
CODE:FFFA1EEE MOV.B #101b, p9_6s ; Set function of port P9, pin 6 as CAN2OUT (connected to CAN2 controller)
CODE:FFFA1EF3 BSET prc2, prcr ; Recommendation of manual, set PRC2 to 1 just before changing direction register
CODE:FFFA1EF8 BCLR pd9_5, pd9 ; Set port P9, pin 5 as input (connected to RXD of ext. CAN transceiver)
CODE:FFFA1EFC BSET #100b, ifs1 ; Set CAN2 input to be connected to port P9, pin 5 (P9_5)
CODE:FFFA1F01 BSET prc30, prcr3 ; Recommendation of manual, set PRC30 before changing port 3
CODE:FFFA1F06 MOV.B #0, p9_6s ; Set function port P9, pin 6 as GPIO
CODE:FFFA1F0B BSET pd3_7, pd3 ; Set pin 7 of port P3 as output
CODE:FFFA1F0F BCLR p3_7, p3 ; Set pin P3_7 to level "0"
CODE:FFFA1F13 BCLR prc30, prcr3 ; Recommendation of manual, set PRC30 before changing port 3
CODE:FFFA1F18 BTST #2, c2str ; if CAN2 is in sleep mode (SLPST=1) goto loc_FFFA1F2B
CODE:FFFA1F1D JEQ/Z loc_FFFA1F2B
CODE:FFFA1F1F BCLR #2, c2ctrl_b0_b7 ; Set "CAN2 Sleep Mode Bit (SLPM)" to "Mode other than CAN sleep mode" ("0")
CODE:FFFA1F24 loc_FFFA1F24: ; CODE XREF: hscan_init1+4B↓j
CODE:FFFA1F24 BTST #2, c2str
CODE:FFFA1F29 JNE/NZ loc_FFFA1F24
CODE:FFFA1F2B loc_FFFA1F2B: ; CODE XREF: hscan_init1+3F↑j
CODE:FFFA1F2B MOV.W #1, c2ctrl_b0_b7
CODE:FFFA1F30 loc_FFFA1F30: ; CODE XREF: hscan_init1+57↓j
CODE:FFFA1F30 BTST #0, c2str
CODE:FFFA1F35 JEQ/Z loc_FFFA1F30 ; b0 must be "0" (CAN2 not in reset state)
CODE:FFFA1F37 MOV.L #0, c2bcr
CODE:FFFA1F3C BSET #4, c2bcr+1 ; set TSEG1 to 0b1011 = 12 Tq
CODE:FFFA1F41 BSET #5, c2bcr+1
CODE:FFFA1F46 BCLR #6, c2bcr+1
CODE:FFFA1F4B BSET #7, c2bcr+1
CODE:FFFA1F50 BCLR #0, c2bcr+2 ; set TSEG2 to 0b010 = 3 Tq
CODE:FFFA1F50 ; set SJW to 0b01 = 2 Tq
CODE:FFFA1F55 BSET #1, c2bcr+2
CODE:FFFA1F5A BCLR #2, c2bcr+2
CODE:FFFA1F5F BSET #4, c2bcr+2
CODE:FFFA1F64 BCLR #5, c2bcr+2
CODE:FFFA1F69 BSET #0, c2bcr ; set prescaler (BPR) to 1
CODE:FFFA1F6E MOV.L #-1, dword_47228 ; ??? not an SFR
; -----------------------------------------------------
; Set data of mailboxes mb0-mb7 to 0x00
; -----------------------------------------------------
CODE:FFFA1F73 MOV.L #0FFFB1778h, A0 ; load A0 with base address of CAN2 mailbox 0
CODE:FFFA1F79 MOV.L A0, R2R0 ; R2R0 = A0
CODE:FFFA1F7B ADD.WL #80h, R2R0 ; R2R0+0x80 = base address of mailbox #8
CODE:FFFA1F7F
CODE:FFFA1F7F loc_FFFA1F7F: ; CODE XREF: hscan_init1+BC↓j
CODE:FFFA1F7F MOV.L #0, [[A0]]
CODE:FFFA1F82 MOV.L [A0], A1
CODE:FFFA1F84 ADD.L #4, A1
CODE:FFFA1F86 MOV.L #0, [A1]
CODE:FFFA1F88 MOV.L [A0], A1
CODE:FFFA1F8A ADD.BL #8, A1
CODE:FFFA1F8D MOV.L #0, [A1]
CODE:FFFA1F8F MOV.L [A0], A1
CODE:FFFA1F91 ADD.BL #0Ch, A1
CODE:FFFA1F94 MOV.L #0, [A1]
CODE:FFFA1F96 ADD.L #4, A0
CODE:FFFA1F98 CMP.L R2R0, A0
CODE:FFFA1F9A JNE/NZ loc_FFFA1F7F
; -----------------------------------------------------
; Set CAN-ID and DLC for mb0-mb3
; -----------------------------------------------------
CODE:FFFA1F9C MOV.L [off_FFFB1778], R2R0 ; load value of c2mb0 CAN-ID into R2R0
CODE:FFFA1FA2 AND.L #11100000000000111111111111111111b, R2R0 ; set SID bits 18-28 to all 0
CODE:FFFA1FA7 OR.L #11111011111000000000000000000b, R2R0 ; set SID bits 18-28 to 0b11111011111 (=0x7DF)
CODE:FFFA1FAD MOV.L R2R0, [off_FFFB1778] ; set CAN-ID of mailbox 0 to 0x7DF
CODE:FFFA1FB3 MOV.L off_FFFB1778, A0 ; A0 = 0x47400 = CAN2 mailbox 0 base address
CODE:FFFA1FB8 ADD.L #4, A0
CODE:FFFA1FBA MOV.W [A0], R0 ; R0 = 16 Bit value of DLC (only in upper 8 bits)
CODE:FFFA1FBC AND.W #1111000011111111b, R0 ; set DLC to 0
CODE:FFFA1FBF OR.W #100000000000b, R0 ; set DLC to 8
CODE:FFFA1FC3 MOV.L off_FFFB1778, A0 ; A0 = 0x47400 = CAN2 mailbox 0 base address
CODE:FFFA1FC8 ADD.L #4, A0
CODE:FFFA1FCA MOV.W R0, [A0] ; set DLC register to 8
CODE:FFFA1FCC MOV.L [off_FFFB177C], R2R0 ; load value of c2mb1
CODE:FFFA1FD2 AND.L #11100000000000111111111111111111b, R2R0 ; set SID bits 18-28 to all 0
CODE:FFFA1FD7 OR.L #11100100110000000000000000000b, R2R0 ; set SID bits 18-28 to 0b11100100110 (0x726)
CODE:FFFA1FDD MOV.L R2R0, [off_FFFB177C] ; set CAN-ID of mailbox 1 to 0x726
CODE:FFFA1FE3 MOV.L off_FFFB177C, A0 ; A0 = 0x47410
CODE:FFFA1FE8 ADD.L #4, A0
CODE:FFFA1FEA MOV.W [A0], R0
CODE:FFFA1FEC AND.W #1111000011111111b, R0
CODE:FFFA1FEF OR.W #100000000000b, R0 ; set DLC to 8
CODE:FFFA1FF3 MOV.L off_FFFB177C, A0 ; A0 = 0x47410 = CAN2 mailbox 1 base address
CODE:FFFA1FF8 ADD.L #4, A0 ; A0 0x47414
CODE:FFFA1FFA MOV.W R0, [A0]
CODE:FFFA1FFC MOV.L [off_FFFB1784], R2R0 ; load value from c2mb3 into R2R0
CODE:FFFA2002 AND.L #11100000000000111111111111111111b, R2R0 ; set SID bits 18-28 to all 0
CODE:FFFA2007 OR.L #11100101110000000000000000000b, R2R0 ; set 0b11100101110 = 0x72E
CODE:FFFA200D MOV.L R2R0, [off_FFFB1784] ; set CAN-ID of mailbox 3 to 0x72E
CODE:FFFA2013 MOV.L off_FFFB1784, A0
CODE:FFFA2018 ADD.L #4, A0
CODE:FFFA201A MOV.W [A0], R0
CODE:FFFA201C AND.W #1111000011111111b, R0
CODE:FFFA201F OR.W #100000000000b, R0 ; set DLC to 8
CODE:FFFA2023 MOV.L off_FFFB1784, A0
CODE:FFFA2028 ADD.L #4, A0
CODE:FFFA202A MOV.W R0, [A0]
; -----------------------------------------------------
; -----------------------------------------------------
CODE:FFFA202C MOV.L #11b, c2mier ; Enable CAN2 receive interrupt for mb0 and mb1
CODE:FFFA2031 MOV.B #0, byte_4774C
CODE:FFFA2036 AND.BW #-4, c2ctrl_b0_b7 ; -4 = 0b10000100 = 0x84
CODE:FFFA203C loc_FFFA203C: ; CODE XREF: hscan_init1+163↓j
CODE:FFFA203C BTST #0, c2str
CODE:FFFA2041 JNE/NZ loc_FFFA203C
CODE:FFFA2043 BCLR #3, c2ric ; CAN2 Receive Interrupt Control Register
CODE:FFFA2047 MOV.B c2ric, R0L ; CAN2 Receive Interrupt Control Register
CODE:FFFA204A AND.B #11111000b, R0L
CODE:FFFA204C OR.B #10b, R0L ; set IRQ Level 2
CODE:FFFA204F MOV.B R0L, c2ric ; CAN2 Receive Interrupt Control Register
CODE:FFFA2052 MOV.B #40h, c2mctl0 ; '@' ; set CAN2 mailbox 0 as receive mailbox
CODE:FFFA2058 MOV.B #40h, c2mctl1 ; '@' ; set CAN2 mailbox 1 as receive mailbox
CODE:FFFA205E POPM A1
CODE:FFFA2060 RTS
Alles anzeigen
Aus dieser geht u.a. noch hervor das für den Empfang von CAN-Botschaften die Mailboxen MB0 und MB1 im "Normal Mode" (also kein FIFO) verwendet werden. Diese sind jeweils mit unterschiedlichen Acceptance-Filtern ausgestattet. Auch die Interrupts werden hier aktiviert. Dazu mehr im folgenden Beitrag.
Die Filterungseinstellungen sind etwas komplex, ich versuche es so einfach wie möglich darzustellen.
"Acceptance Filter" wirken auf die ID der CAN-Botschaft und erlauben es das nur ein bestimmter Bereich von IDs zugelassen wird. Dabei können für die insgesamt 32 Mailboxen pro CAN-Controller unterschiedliche Filter hinterlegt werden. Diese sind in Gruppen eingeteilt, ein Filter bedient dabei jeweils 4 aufeinanderfolgende Mailboxen. Für die MB0 und MB1 des CAN2, welche oben konfiguriert wurden, gilt dabei nur der Filter C2MKR0. Dieser Filter wird über die SFR2 Speicheradressen 0x47600..0x47603 eingestellt:
Der dort hinterlegte 32-Bit Wert beschreibt mit 0 und 1 ob das jeweilige Bit mit der eingehenden CAN-ID verglichen wird oder nicht. Einfach ausgedrückt: wäre alles 0, dann würde der Filter jede CAN-ID akzeptieren und je mehr 1en man einbringt umso weniger IDs würden erkannt. EID/SID beschreibt die ID-Längen, wobei wir im Mondeo ausschließlich mit Standard-IDs (SID) arbeiten, die nur 11 Bit lang sind. Somit sind nur die Bits 18-28 ausschlaggebend.
Interessanterweise finde ich bislang im gesamten Code keinen Zugriff auf diese Register. Das bedeutet das keine Acceptance Filter für HS-CAN eingestellt werden. Da der Reset-Value "undefined" ist, macht mir das etwas zu denken, aber mal schaun.
Gefunden habe ich jedenfalls das die hscan_init Routine folgendes einstellt:
- mb0 => Empfängt CAN-Botschaften mit der ID 0x7DF (UDS Broadcast Adresse)
- mb1 => Empfängt CAN-Botschaften mit der ID 0x726 (BCM UDS Adresse für Anfragen)
- mb2 => Sendet CAN-Botschaften mit der ID 0x72E (BCM UDS Adresse für Antworten)