Hallo zusammen, ich habe zwar schon etwas in C programmiert, aber das Thema hardwarenahe bzw. uC-Programmierung ist ziemlich neu für mich. Ich habe den folgenden Aufbau (Arduino Uno + CANdiy-Shield): https://github.com/watterott/CANdiy-Shield/blob/master/pcb/CANdiy-Shield_v13.pdf?raw=true Das CANdiy-Shield beinhaltet hauptsächlich einen MCP2515 CAN-Controller und MCP2551 CAN-Transceiver. Die Verbindung zum ATmega328 ist im oberen Link ersichtlicht. MCP2515-Datenblatt: http://ww1.microchip.com/downloads/en/DeviceDoc/21801G.pdf MCP2551-Datenblatt: http://ww1.microchip.com/downloads/en/DeviceDoc/21667f.pdf ATmega328-Datenblatt: http://www.atmel.com/Images/Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Summary.pdf Ich konnte über das Atmel Studio 6.1 ein simples Programm auf den Atmega328 übertragen, womit LEDs angesteuert wurden. Als nächstes möchte ich eine CAN-Botschaft in einem CAN-Bus absetzen und habe dazu folgendes Tutorial gefunden: http://www.kreatives-chaos.com/artikel/ansteuerung-eines-mcp2515#receive Jetzt kann ich aber schon die Initialisierung des SPI-Interface nicht ganz nachvollziehen. Hier der C-Code: void spi_init(void) { // Aktivieren der Pins für das SPI Interface DDR_SPI |= (1<<P_SCK)|(1<<P_MOSI); PORT_SPI &= ~((1<<P_SCK)|(1<<P_MOSI)|(1<<P_MISO)); DDR_CS |= (1<<P_CS); PORT_CS |= (1<<P_CS); // Aktivieren des SPI Master Interfaces, fosc = fclk / 2 SPCR = (1<<SPE)|(1<<MSTR); SPSR = (1<<SPI2X); } Ich könnte natürlich das Ganze Tutorial kopieren, aber ich würde diesen Code auch gerne nachvollziehen. DDR und PORT dient dazu die Datenrichtung bzw. den Ausgang zu definieren, auch Bitweise UND/ODER kann ich nachvollziehen, aber wozu das BIT KOMPLEMENT und wozu wird z.B. P_SCK/P_MOSI/P_CS um 1 digit nach links (<<) verschoben? Wenn im Laufe des Tutorials weitere Unklarheiten ergeben, würde ich gerne weiterhin diesen Thread nutzen. Danke. LG
irgendwo vorher steht noch was in der Art: #define DDR_SPI DDRB #define P_SCK 4 (keine Ahnung, welcher Wert da tatsächlich steht!) #define P_MOSI 3 Und das hier heisst dann: DDR_SPI |= (1<<P_SCK)|(1<<P_MOSI); Am gewählten Port (hier B) werden die Ausgänge 3 und 4 als Ausgang gesetzt. Man hätte auch schreiben können: DDRB |= 0x18; Damit hätte aber niemand was anfangen können. Und die Sache auf einen anderen Port/andere Pins umzuschreiben wäre auch nicht so einfach bzw. fehlerträchtig.
#define DDR_CS DDRB #define PORT_CS PORTB #define P_CS 2 #define DDR_SPI DDRB #define PORT_SPI PORTB #define P_MISO 4 #define P_MOSI 3 #define P_SCK 5 Ich verstehe es aber immer noch konkret. Nehmen wir mal folgende Zeile: DDR_SPI |= (1<<P_SCK)|(1<<P_MOSI); Was ist der Vorteil von "1<<" in diesem Fall? Lässt sich der Code dadurch einfacher (mit weniger Änderungen) auf verschiedenen AVR-uC integrieren? Lesbarer finde ich es zumindest für Anfänger nämlich nicht.
Ja, es ist einfacher auf andere übertragbarer. Und ja, es ist einfacher lesbar. Ich seh sofort, dass du die Pins SCK und MOSI auf Ausgang schalten willst, unabhängig davon, auf welchen Ports und Pins die tatsächlich liegen (was für die Logik des Programms auch egal ist) Und wenn nun SCK auf Port5 liegt, ergibt 1<<5 0b00100000. D.h. egal wo SCK tatsächlich liegt, das Programm arbeitet korrekt, wenn du bei define den tatsächlichen physikalischen Port angibst, je nach Prozessor. Es wird unabhängig vom Chip und jeder versteht es.
H.Joachim Seifert schrieb:0 > Und wenn nun SCK auf Port5 liegt, ergibt 1<<5 0b00100000. Es wird also um eine Stelle verschoben, da die Kanäle des jeweiligen Ports mit 0 beginnend gezählt werden und Port 5 damit tatsächlich auf Position 6 ist. Welche Bedeutung hat die Bitwise-OR-Verknüpfung zwischen den beiden Ports in der Zeile "DDR_SPI |= (1<<P_SCK)|(1<<P_MOSI);" ? Dadurch erkennt man dann letztlich doch gar nicht mehr, dass es Port 5 und 3 sein sollen. Und dann kommt noch |= dazu, oder heben die sich auf?
DDR_SPI |= () heisst ausgeschrieben: DDR_SPI=DDR_SPI | () Programmierer sind eben schreibfaul, Quelle und Ziel sind identisch, also brauch man sie nicht zweimal schreiben. Sinn dieses ORs ist, die anderen Bits des Ports nicht anzufassen, sondern nur die gewünschten (SCK und MOSI) auf 1 zu setzen. Also egal, ob die anderen auf 1 oder 0 standen - die bleiben so, wie sie waren. Es gab schon ganz viele Fragen hier in der Art: DDRB=0x20; //SCK als Output DDRB=0x08; //Mosi als Output Mit der zweiten Anweisung wird die erste hinfällig, ohne dass man das gleich sieht. Das arme Register bzw. der Compiler weiss natürlich nicht, dass das anders gedacht war :-) DDRB=DDRB | 0x20; //SCK als Output DDRB=DDRB | 0x08; //Mosi als Output so funktioniert das wie gewollt. Und jetzt noch die magic numbers weg (niemand, auch du selbst weisst nach kurzer Zeit nicht mehr, was gemeint war, falls kein Kommentar da. Richtig witzig wirds, wenn du das Bit mehrfach im Programm brauchst und es auf eine andere Position wandert (anderer Prozessor z.B), dann musst du alle Stellen im Programm suchen, wo das gebraucht wird. Bei grösseren Programmen eine Fehlerquelle 1.Grades. Also per #define, und schon wird es überall richtig ersetzt. #define P_MOSI 3 #define P_SCK 5 (1<<P_MOSI) ergibt 1<<3 = 0b00001000 (1<<SCK) ergibt 1<<5 = 0b00100000 Und (1<<P_MOSI) | (1<<SCK) ergibt folgerichtig 0b00101000. D.h. genau diese 2 Bits werden auf 1 gesetzt. Und das ist genau das, was der ganze Kram bewirken soll und es auch tut.
Danke. Jetzt hat es auch bei mir Klick gemacht. Ich habe nun die relevanten Teile des Tutorials in ein neues Projekt in Atmel Studio 6 transferiert. Mir geht es zunächst nur um das Senden von Dummy-Botschaften. Jedoch erhalte ich nach Auswahl von Build Solution eine Fehlermeldung. Tutorial: http://www.kreatives-chaos.com/artikel/ansteuerung-eines-mcp2515 Fehlermeldung: Error unknown type name 'CANMessage' CANdiy.c 169 23 in der Funktion:
1 | void can_send_message(CANMessage *p_message) |
CANMessage ist eine struct aus der main, dass die einzelnen teile der CAN-Botschaft enthält:
1 | int main(void) |
2 | {
|
3 | mpc2515_init(); |
4 | typedef struct{ |
5 | uint16_t id; |
6 | uint8_t rtr; |
7 | uint8_t length; |
8 | uint8_t data[8]; |
9 | } CANMessage; |
10 | |
11 | // Neue Nachricht erzeugen
|
12 | CANMessage message; |
13 | |
14 | // Daten eintragen
|
15 | message.id = 0x0123; |
16 | message.rtr = 0; |
17 | message.length = 2; |
18 | message.data[0] = 0x04; |
19 | message.data[1] = 0xf3; |
20 | |
21 | // Nachricht verschicken
|
22 | while(1) |
23 | {
|
24 | can_send_message(&message); |
25 | _delay_ms(5); |
26 | }
|
27 | }
|
Muss ich die einzelnen Variablen in struct VOR der function can_send_message() deklarieren? Oder muss in den () der Funktion can_send_message jede einzelne Variable von struct nacheinander deklariert werden? Über einen Tipp würde ich mich freuen.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.