Der AVR-/ARDUINO-Faden
Moderatoren: Heaterman, Finger, Sven, TDI, Marsupilami72, duese
Re: Der AVR-/ARDUINO-Faden
Re: Der AVR-/ARDUINO-Faden
Probiere doch mal zu deinen 100n noch mal 2.2u oder 4.7u hin zuzufügen. Nur um mal auszuschließen das da noch Power-Glitches mit reinspielen.
Kann sich einfach sein das die alten eine alte DIE haben. Ich würde mir sonst mal das passende Dabla zum Datecode holen.
EDIT: Deine Masse ist auch arg dünn angebunden.
Wenn du CS8 nach innen legst kannst du den Masse-PIN direkt mit der GND-Plane verbinden.
Grüße Jan
Re: Der AVR-/ARDUINO-Faden
Re: Der AVR-/ARDUINO-Faden
Bei einem solchen Board, kann man da den deep-sleep Modus überhaupt sinnvoll nutzen:
https://www.az-delivery.de/collections/ ... pmentboard#
Wenn ich den Chip in den Deep-Sleep schicke, zieht das Board trotzdem 16mA.
Isso oder? Wegen dem Spannungsregler und dem CP2102.
Re: Der AVR-/ARDUINO-Faden
Okay dann wird das wohl normal sein bei deiner Revision. Siehe TI Datenblatt -> Invalid data.
Da hilft dann nur einen aktuellen Chip zu verwenden oder RC-Glieder nach den Port-Pins, die die Spikes weg bügeln.
Der Deep-Sleep macht nur Sinn wenn du das nackte ESP32-Modul an einer 3V-Lithium Batterie oder eine Spannungsregler wie MCP1702.
Hier muss dann auch mit 2 Reglern gefahren werden.
Einmal der MCP und dann muss man erst einen kräftigeren Regler zuschalten bevor man das Wifi aktiviert.
Ich verwende immer den Light-Sleep Mode mit einem LF33CV oder LM1117-3.3. Das ist dann meistens auch ein Bleiaquarium mit dran.
Der Deep-sleep hat auch so seine Tücken.
Die MCU kann in einigen Silizium Revision durch Power-Glitches stehen bleiben. Und wacht da nie mehr auf. Da hilft dann nur noch der Reset.
Grüße Jan
Re: Der AVR-/ARDUINO-Faden
Ich habe schon ein paar andere Module zum Laufen bekommen, ein SPI Display auf das ich Daten Schreiben konnte, einen PWM gesteuerten Motorcontroller für 220V Motoren, ein Lichtschrankenmodul, und ich habe auch eine LED Blinken lassen. Bin aber sonst was Arduinos angeht noch recht grün und hab auch elektrotechnisch nur oberflächlich Ahnung.
Sensor scheint optisch baugleich mit diversen anderen Anbietern von AHT10 Modulen: https://www.aliexpress.com/item/1005001722606573.html
Die Pins sind wie folgt Verbunden:
AHT10 - Arduino
GND - GND
VIN - 5V
SDA - A4
SCL - A5
Ich habe zuerst die Code Sketch von hier probiert, und auch vorher die Libary importiert: https://elektro.turanis.de/html/prj349/index.html
Da kommt zunächst eine Fehlermeldung das der Init gescheitert ist. Wenn ich die Adresse auf 0x39 ändere kommen auf dem Serial Monitor in der Arduino IDE dann Fantasiewerte von mehreren 10.000°C und negativ 1.000de % Feuchtigkeit die stark schwanken, egal ob Modul angesteckt oder nicht.
Ich hab dann noch dieses Codebeispiel aus dieser Libary (mit dieser Libary importiert) Verwendet: https://github.com/enjoyneering/AHTxx/b ... Serial.ino
Auf dem Serial Monitor erscheint dann das hier: ������ ��������������������������������������������������������������
Bin ein bisschen ratlos, vielleicht weiß ja hier Jemand woran es liegen könnte oder wie man Diagnostizieren könnte.
- Später Gast
- Beiträge: 1699
- Registriert: Di 5. Apr 2016, 22:03
- Wohnort: Karlsruhe
- Kontaktdaten:
Re: Der AVR-/ARDUINO-Faden
Re: Der AVR-/ARDUINO-Faden
Danke!
Re: Der AVR-/ARDUINO-Faden
Oder die Spannung reich nicht.Später Gast hat geschrieben: ↑So 1. Jan 2023, 19:20 Wenn der Serial Monitor Hieroglyphen anzeigt, ist meist die falsche Baudrate im Sketch/im Monitor eingestellt.
Re: Der AVR-/ARDUINO-Faden
Ich hab nun einen 100n am Ausgang. das reicht nicht...
Ich muss wohl mal neue Kaufen.
- Bastelbruder
- Beiträge: 11550
- Registriert: Mi 14. Aug 2013, 18:28
Re: Der AVR-/ARDUINO-Faden
Das falsche high kann mit dem internen "Write Pulse" nach (Zeichning 8.2.2) garnicht erzeugt werden, weil das D-Flipflop schon vorher den Zustand L gespeichert hatte. Die t_pv von max. 4 µs hat mit dem Fehler nichts zu tun, das ist eher die Durchlaufverzögerung. Aber komisch ist diese Angabe im Datenblatt schon.
4 µs ist auch für Standard-CMOS eine lange Zeit.
Ich hätte jetzt erstmal andere Hersteller durchprobiert.
Den "Glitch" auszublenden geht sauber bloß mit einer Diode, die nur den "Open Drain" durchläßt.
Ein Serienwiderstand mit zusätzlicher Lastkapazität macht m.E. keinen Sinn.
Re: Der AVR-/ARDUINO-Faden
Hast du die mal nackt auf den Steckbrett probiert?
Gleiche Verhalten?
Und mal welche von NXP probiert?
Ich denke langsam das ist m.E. ein TI spezifisches Verhalten. Was auffällt ist das der Spike und der ACK Impuls genau gleich lang sind. Zudem fehlt die Passage "Invalid data" im NXP Datenblatt.
Grüße Jan
Re: Der AVR-/ARDUINO-Faden
Hat doch direkt mit den Daten zu tun(Daten low, Clock low-> switch), also kein Spike.
Edith:kein zusammenhang mit der Ausgabe.
offene Eingänge kann man ausschließen? Is ja bei hoher Eingangsimpendanz relevant.
Programmierung saubär? Was sagt der SPI Datenstring was die cpz tun soll?
Re: Der AVR-/ARDUINO-Faden
Anscheinend gehen die Ports von selber auf High, wenn ich irgendwelche Befehle Sende
Der D0 wird auf 1 gesetzt
Der D3 sollte eigentlich immer 0 sein
Im Code ist 0 und 3 vertauscht
PCF8574(0x20).begin();
//i2c_datasend(0x26,0,0);
i2c_datasend(0x20,3,0);
while(1==1){
i2c_datasend(0x20,3,0);
i2c_datasend(0x20,0,0);
delay(10);
i2c_datasend(0x20,0,0);
;}
Re: Der AVR-/ARDUINO-Faden
Jetzt sieht es ja so aus wie es sein sollte!?
Grüße Jan
Re: Der AVR-/ARDUINO-Faden
Re: Der AVR-/ARDUINO-Faden
Noch eine Frage:
Code: Alles auswählen
i2c_datasend(0x20,3,0);
Insgesamt hat das Datagram irgendwie nichts mit der Ausgabe der Pins gemein. Kann dein LA auch das Protokoll dekodieren? Und sind die analog gemessenen Pegel auch noch digital?
Re: Der AVR-/ARDUINO-Faden
Den LOW Befehl sende ich mehrfach, um zu schauen, was beim senden eines Befehls passiert.
Ich denke ich habe eine Ursache gefunden:
Alle Ports sind auf High, wenn der Chip nicht initialisiert ist. Man muss ihn initialisieren und dann erst ein mal alle Ports auf 0 ziehen.
In der i2cdatasend(addr, pin, state) wird aber mit
if (!PCF8574(addr).begin)
{
Serial.print("init fail");
PCF8574(addr).begin;
}
nicht nur gefragt, ob der Chip initialisiert wurde, sonder direkt in der Abfrage der Init bei if (!PCF8574(addr).begin)) neu initialisiert.
Bei jedem benutzen der i2cdatasend.
Da gehen dann alle Pins wieder auf High
Das ist für die Ansteuerung des CS-Pins des MCP2515 egal, der arbeitet ja eh als invertierter Eingang, aber der ULN2308 arbeitet mit Highlevel.
So hab ich am Ausgang des ULN immer ein Signal, wenn der PCF initialisiert wird.
Das zweite Problem sind heftige Spikes, die muss ich aber noch untersuchen, wo und wann die auftreten.
Re: Der AVR-/ARDUINO-Faden
Re: Der AVR-/ARDUINO-Faden
Re: Der AVR-/ARDUINO-Faden
Wo denn? Auf SDA? Das wäre normal und (bei korrektem Timing) egal. Deshalb fragte ich nach nem LA der das Protokoll dekodieren kann.Das zweite Problem sind heftige Spikes, die muss ich aber noch untersuchen, wo und wann die auftreten.
Re: Der AVR-/ARDUINO-Faden
Okay das Wissen hatte ich als gegeben angenommen, Sorry.
Jetzt verstehe ich langsam wo der Wind her weht.Hightech hat geschrieben: ↑Do 5. Jan 2023, 08:03 Das ist für die Ansteuerung des CS-Pins des MCP2515 egal, der arbeitet ja eh als invertierter Eingang, aber der ULN2308 arbeitet mit Highlevel.
So hab ich am Ausgang des ULN immer ein Signal, wenn der PCF initialisiert wird.
Das zweite Problem sind heftige Spikes, die muss ich aber noch untersuchen, wo und wann die auftreten.
Der PCF8574 ist auch eher als Low-Active LED-/Motor-Driver bzw. zum einlesen von Tastern erdacht worden. Überall wo es billig sein soll. Ich verwende die sehr gerne in Frontpanels von DIN-Rail Gehäuse.
Für deine Anwendung ist ein MCP23017 wohl besser geeignet, das ist ein vollwertiger Port-Expander.
Grüße Jan
Re: Der AVR-/ARDUINO-Faden
Kanal 1 ist Pin3
Kanal 4 ist Pin 0
Warum zum Geier geht der immer wieder nach High wenn ich den Pin 3 beschreibe?
void loop()
{
PCF8574(0x26).write(0, 0);
_delay_us(100);
PCF8574(0x26).write(3, 0);
_delay_us(100);
PCF8574(0x26).write(3, 1);
_delay_us(100);
}
0xff sollte dann auch alle Pins auf 0 bedeuten oder?
Re: Der AVR-/ARDUINO-Faden
Code: Alles auswählen
void PCF8574::write(const uint8_t pin, const uint8_t value)
{
if (pin > 7)
{
_error = PCF8574_PIN_ERROR;
return;
}
if (value == LOW)
{
_dataOut &= ~(1 << pin);
}
else
{
_dataOut |= (1 << pin);
}
write8(_dataOut);
}
void PCF8574::write8(const uint8_t value)
{
_dataOut = value;
_wire->beginTransmission(_address);
_wire->write(_dataOut);
_error = _wire->endTransmission();
}
Re: Der AVR-/ARDUINO-Faden
Wenn ich direkt mit
setup:
PCF8574(0x26).begin()
loop:
PCF8574(0x26).write(3, 1);
schreibe, macht es irgendwo einen neuen Init
Wenn ich es vorher deklariere
PCF8574 pcf(0x26);
setup:
pcf.begin(0x26);
Und dann
loop:
pcf.write(0,0);
pcf.write(3,1);
pcf.write(3,0);
schreibe klappt es.
WTF?
hier fehlt auch das 0xff dazwischen
Aber wie bekomme ich den CS-Pin jetzt in der conti_mpc2515 Lib geschriben?? Darum gings es ja eigentlich.
Re: Der AVR-/ARDUINO-Faden
Aktuelles Ziel ist ein bisschen Heimautomatisierung, automatische Belüftung, Heizung, Klimatisierung, Beleuchtung basierend auf Uhrzeit, Temperatur und gewählten Profilen.
Später noch einen Auf- und Abwickelautomat für 3D Drucker Filament mit Sensor zur Vermessung (Qualitätskontrolle von zweifelhaftem Filament das mir mal günstig zugelaufen ist), ein Rudermaschinencomputer und mir fällt sicher noch mehr Dummes ein.
Sensoren, Display, Tastatur und Relaiskarte hab ich schon Gekauft und zum Laufen gebracht.
Was ich sonst schon habe ist ein Sortiment Keramikkondensatoren und Metallfilmwiderstände, eine handvoll Arduino Nanos, und Breadboards.
Ich denke da insbesondere an NPN und PNP Transistoren (verstehe ungefähr wofür man beide Typen braucht, aber keine Ahnung welchen Typ man da am besten genau wählt) und vielleicht ein paar lineare Spannungsregler?
Bei MOSFETs habe ich als Empfehlung IRF520 Gelesen.
Die Pins am Nano werden mir nicht reichen, aber ich brauche fast nur langsam geschaltete Pins um Relaiskarten an/aus zu Schalten die mit 4mA Schaltstrom zufrieden sind. Ich glaube da kann ich einen Multiplexer nehmen? Empfiehlt sich da ein bestimmter?
Ich werde auch sicher aus Versehen was Kaufen das 3.3V oder 5V Signale braucht oder umgekehrt, empfiehlt sich ein bestimmter logic level converter?
Gibt es eine Drahtlostechnologie die besonders zuverlässig ist? Benötigte Datenraten sind sehr niedrig, wichtig wäre das es in der Wohnung zuverlässig auch mal durch ne Wand geht. Ansonsten brauche ich wohl mehrere Meter Kabel und muss irgendeinen Bus basteln, sind die empfindlich und brauchen geschirmte Kabel oder spezielle Bauteile um sauber zu funktionieren?
Hab bisher das meiste bei Aliexpress Gekauft und bin damit gut Gefahren. Hab auch noch genug zu tun das mich die Versandzeit nicht stört. Würde also vermutlich was möglich dort wieder Kaufen. Da ich noch keine Ahnung habe wie genau das Endkonstrukt aussehen wird würde ich erstmal Breadboards zustammenstecken bis es mir auf die Füsse fällt und mich dann an Finger'scher Fädeltechnik versuchen. Daher gefallen mir die fertigen Module mit 2.54mm Pinheadern gut, insbesondere wenn da SMD Teile schon fertig Gelötet drauf sind. Btw, wo bekomme ich den "richtigen" Fädeldraht bei dem die Isolierung auch bei normaler Löttemperatur gut Abbrennt?
Was mir auch noch etwas unklar ist ist z.B. wie ich die Stromversorgung von Bauteilen gestalte wie z.B. Modellbauservos. Die haben ja 3 Pins, einmal GND, VCC und dann den Steuerpin. Der Steuerpin kommt an den Microcontroller, und dessen 20mA Dauerstrom sind dafür ja auch sicher ausreichend. Bei kleinen Servos reicht ja vermutlich auch noch der Spannungsregler auf dem Ardunio der wohl so um die 800mA kann. Was ich mich frage ist wie ich mit größeren Servos oder einfach vielen Servos umgehe die die 800mA des Arduino übersteigen. Bei einer dummen Last wie einem Motor oder einer Glühbirne wo ich nur An/Aus benötige, klar, da kann ich einen MOSFET Schalten oder ein Relais, aber bei dem Servo hängt ja ein Pin direkt am MC. Kann ich GND vom Arduinoboard und dem externen Netzteil was ich dann noch brauche einfach zusammenlegen? Muss da eine Trennung Erfolgen? Gibt es da bestimmte geeignete und ungeeignete Netzteilarten? Wenn es da Stichwörter gibt die ich einfach in Google Füttern muss für eine Erkärung würde mir auch schon helfen.
Re: Der AVR-/ARDUINO-Faden
Ich muss nun die PCF8574 vorher anlegen.
Also
PCF8574 pcf_01(0x26);
PCF8574 pcf_02(0x25);
PCF8574 pcf_03(0x24);
PCF8574 pcf_04(0x23);
PCF8574 pcf_05(0x22);
PCF8574 pcf_06(0x21);
Jetzt muss ich die aber in die Funktionen und Klassen weiterleiten, bisher hab ich es mit
i2c_datasend ( (akku[a].addr, akku[a].toggle_pin,state) oder
MCP2515 mcp2515(akku[a].addr, akku[a].cs_pin)
int i2c_datasend(int addr, int pin, bool state) //i2c Adresse , Pin, Low/High
{
PCF8574(addr).write(pin,state)
}
einfach manchen können.
Aber wie bekomme ich die pcf_01 - pcf_06 in die Klassen und Funktionen rein.
Wahrscheinlich brauche ich dafür eine eigene Klasse, aber da fehlt mir das Verständnis.....
- ProgBernie
- Beiträge: 593
- Registriert: Fr 16. Sep 2022, 21:59
- Wohnort: Zwischen Hamburg und Haiti ^W Lübeck
Re: Der AVR-/ARDUINO-Faden
Das ist nun nicht so überraschend. Mit jedem PCF8574(0x26) wird eine neue Instanz erstellt, die nach dem write sofort wieder verworfen wird. Die Klasse initialisiert sich vor dem write() intern, wenn nicht zuvor aufgerufen. Du benutzt die lib von Rob Tillaart?Hightech hat geschrieben: ↑Fr 6. Jan 2023, 00:45 So, jetzt kommt Licht ins Dunkel!
Wenn ich direkt mit
setup:
PCF8574(0x26).begin()
loop:
PCF8574(0x26).write(3, 1);
schreibe, macht es irgendwo einen neuen Init
Wenn ich es vorher deklariere
PCF8574 pcf(0x26);
setup:
pcf.begin(0x26);
Und dann
loop:
pcf.write(0,0);
pcf.write(3,1);
pcf.write(3,0);
schreibe klappt es.
WTF?
Bernd
-
- Beiträge: 173
- Registriert: Fr 1. Jan 2016, 20:43
- Wohnort: Freie Feldlage (Ja, da wo das Treffen ist))
Re: Der AVR-/ARDUINO-Faden
Oh uff, mir schwappen da gerade Erinnerungen an meine letzten Experimente mit C++ hoch...
Also prinzipiell sollte
Code: Alles auswählen
int i2c_datasend(PCF8574 *pcf, int pin, bool state) //i2c Adresse , Pin, Low/High
{
pcf->write(pin,state)
}
(Das ist jetzt aus dem Stegreif kann also Syntaxfehler enthalten)
Erklärung dazu:
Das
Code: Alles auswählen
PCF8574 *pcf
Mit
Code: Alles auswählen
pcf->write(pin,state)
Du speicherst also in deiner liste nicht die Adresse sondern die komplette Instanz, oder einen Pointer, je nach dem wie der Rest vom Code aussieht.
- Phiona
Re: Der AVR-/ARDUINO-Faden
ExaktProgBernie hat geschrieben: ↑Fr 6. Jan 2023, 11:51 Das ist nun nicht so überraschend. Mit jedem PCF8574(0x26) wird eine neue Instanz erstellt, die nach dem write sofort wieder verworfen wird. Die Klasse initialisiert sich vor dem write() intern, wenn nicht zuvor aufgerufen. Du benutzt die lib von Rob Tillaart?
Bernd
Re: Der AVR-/ARDUINO-Faden
Wie rufe ich das dann auf?
Ich muss es ja auch initialisieren.
PCF8574 *pcf(0x26);
pcf->begin();
i2c_datasend(??,1,0);
Lg
Boris
Kenakapheus hat geschrieben: ↑Fr 6. Jan 2023, 12:34Oh uff, mir schwappen da gerade Erinnerungen an meine letzten Experimente mit C++ hoch...
Also prinzipiell solltefunktionieren.Code: Alles auswählen
int i2c_datasend(PCF8574 *pcf, int pin, bool state) //i2c Adresse , Pin, Low/High { pcf->write(pin,state) }
(Das ist jetzt aus dem Stegreif kann also Syntaxfehler enthalten)
Erklärung dazu:
Dasbedeutet das die Funktion einen Pointer zu einer PCF8574 Instanz übergeben bekommt.Code: Alles auswählen
PCF8574 *pcf
Mitwird die Funktion write der übergebenen Instanz aufgerufen.Code: Alles auswählen
pcf->write(pin,state)
Du speicherst also in deiner liste nicht die Adresse sondern die komplette Instanz, oder einen Pointer, je nach dem wie der Rest vom Code aussieht.
- Phiona
-
- Beiträge: 173
- Registriert: Fr 1. Jan 2016, 20:43
- Wohnort: Freie Feldlage (Ja, da wo das Treffen ist))
Re: Der AVR-/ARDUINO-Faden
Code: Alles auswählen
PCF8574 pcf(0x26);
void setup() {
...
pcf.begin();
...
}
Code: Alles auswählen
i2c_datasend(&pcf,1,0);
Da du ja mehrere Instanzen für unterschiedliche PCF8574 haben willst und die in einem Array hast wäre mein vorschlage das Array so anzupassen das der pointer direkt darin gespeichert wird.
Soweit ich das sehe hast du den Code mit er Initialisierung vom Array noch nicht gepostet, daher schätze ich mal wie der grob aussieht:
Code: Alles auswählen
typedef struct akku_s {
int addr,
int toggle_pin
} akku_t;
akku_t akku[8];
Code: Alles auswählen
typedef struct akku_s {
PCF8574 pcf,
int toggle_pin
} akku_t;
Code: Alles auswählen
akku_t akku[8] = [
{.pcf = pcf(0x26), .toggle_pin = 2},
{.pcf = pcf(0x27), .toggle_pin = 3},
...
];
edit:
Damit wird der Aufruf dann zu:
Code: Alles auswählen
i2c_datasend(&akku[a].pcf, akku[a].toggle_pin, 0);
edit2:
Und die Initialisierung zu:
Code: Alles auswählen
akku[a].pcf.begin();
- Phiona
-
- Beiträge: 173
- Registriert: Fr 1. Jan 2016, 20:43
- Wohnort: Freie Feldlage (Ja, da wo das Treffen ist))
Re: Der AVR-/ARDUINO-Faden
Re: Der AVR-/ARDUINO-Faden
dann bräuchst nur eine Instanz?
nur ne idee ich weis ja nicht was du da vorhast/ ob die Instanzen nötig sind.
Re: Der AVR-/ARDUINO-Faden
Re: Der AVR-/ARDUINO-Faden
Das klappt schon mal.
Jetzt nur noch die Kleinigkeit mit dem cs pin für den MCP2515.
Der soll ja mit dem Pin 3 des PCF8574 gesteuert werden, das hatte ich in der MCP2515.cpp so gelöst:
void MCP2515::startSPI() {
SPI.beginTransaction(SPISettings(SPI_CLOCK, MSBFIRST, SPI_MODE0));
PCF8574(CsAddr).write(SPICS, 0); //I2C Addresse des CS-Pin, Pinnummer des CS Pin
}
Wie bekomme ich in der MCP2515.cpp den CS Pin über den PCF gesteuert?
pcf init
Code: Alles auswählen
PCF8574 pcf_01(0x26);
PCF8574 pcf_02(0x25);
PCF8574 pcf_03(0x24);
PCF8574 pcf_04(0x23);
PCF8574 pcf_05(0x22);
PCF8574 pcf_06(0x21);
Code: Alles auswählen
typedef struct
{
PCF8574 pcf;
int i2c_addr;
int cs_pin;
int toggle_pin;
int intr_pin;
} akku_t;
int Akku_Nr = 0;
akku_t akku[24] = {
{.pcf = pcf_01, 0x26, 3, 0, 49},
{.pcf = pcf_01, 0x26, 2, 1, 48},
{.pcf = pcf_01, 0x26, 5, 6, 47},
{.pcf = pcf_01, 0x26, 4, 7, 46},
{.pcf = pcf_02, 0x25, 1, 4, 45},
{.pcf = pcf_02, 0x25, 3, 5, 44},
{.pcf = pcf_02, 0x25, 2, 6, 43},
{.pcf = pcf_02, 0x25, 0, 7, 42},
{.pcf = pcf_03, 0x24, 3, 0, 30},
{.pcf = pcf_03, 0x24, 2, 1, 31},
{.pcf = pcf_03, 0x24, 5, 6, 32},
{.pcf = pcf_03, 0x24, 4, 7, 33},
{.pcf = pcf_04, 0x23, 1, 4, 34},
{.pcf = pcf_04, 0x23, 3, 5, 35},
{.pcf = pcf_04, 0x23, 2, 6, 36},
{.pcf = pcf_04, 0x23, 0, 7, 37},
{.pcf = pcf_05, 0x22, 3, 0, 22},
{.pcf = pcf_05, 0x22, 2, 1, 23},
{.pcf = pcf_05, 0x22, 5, 6, 24},
{.pcf = pcf_05, 0x22, 4, 7, 25},
{.pcf = pcf_06, 0x21, 1, 4, 26},
{.pcf = pcf_06, 0x21, 3, 5, 27},
{.pcf = pcf_06, 0x21, 2, 6, 28},
{.pcf = pcf_06, 0x21, 0, 7, 29}};
setup
Code: Alles auswählen
for (int a = 0; a < 24; a += 4)
{
akku[a].pcf.begin();
Serial.print("Start PCF 0x");
Serial.println(akku[a].i2c_addr, 16);
delay(100);
}
Code: Alles auswählen
void i2c_datasend(PCF8574 *pcf, int pin, bool state) // i2c Adresse , Pin, Low/High
{
bool status;
if (!(status = pcf->isConnected()))
{
Serial.print("Not connected ");
}
if (pcf->lastError())
{
Serial.print("I2C Fehler Adresse 0x");
Serial.println(pcf->getAddress());
}
pcf->write(pin, state);
}
Code: Alles auswählen
i2c_datasend(&pcf_01, 0, 0);
_delay_us(100);
i2c_datasend(&pcf_01, 3, 1);
_delay_us(100);
i2c_datasend(&pcf_01, 3, 0);
_delay_us(100);
Re: Der AVR-/ARDUINO-Faden
Das ist in C++ sehr einfach.
In dem du eine neue Class erstellst die von der MCP2515 Lib abgeleitet wurde.
Hier überschreibst du die Methoden startSPI(), endSPI(), reset() und erstellst einen zusätzlichen Konstructor.
Der Konstruktor wird un die I2C Initialisierung erweitert.
Die Methoden startSPI() und endSPI() behommen deinen neuen Code.
Die Methode reset() setzt den Portenxpander zurück und ruft anschließend die Methode MCP2515::reset() auf.
So die grobe Zusammenfassung.
Grüße Jan
Re: Der AVR-/ARDUINO-Faden
Vor dem Aufruf von bsp MCP2515.write ect
sende ich schlicht ein
i2c_datasend(akku[1].pcf, 3, 0);
und danach ein
i2c_datasend(akku[1].pcf, 3, 1);
quasi
Deklaration
MCP2515 mpc2515(1);
loop:
for (int a=0 ; a<24; a++){
i2c_datasend(akku[a].pcf, 3, 0);
mcp2515.reset() ect
i2c_datasend(akku[a].pcf, 3, 1);
}
Wobei, kann ich alle 24 MCP2515 gleichzeitig initialisieren, wenn ich alle CS pins LOW ziehe und dann das Init sende, bzw den Reset?
- ProgBernie
- Beiträge: 593
- Registriert: Fr 16. Sep 2022, 21:59
- Wohnort: Zwischen Hamburg und Haiti ^W Lübeck
Re: Der AVR-/ARDUINO-Faden
Das Verständnis kommt noch. Probiere einfach mal zuerste eine leere von Klasse von MCP2515 abzuleiten. Dadrin ist zunächst alles so wie im Original. Dann kannst Du zusätzliche Methoden dafür schreiben.
Leere Klasse die erstmal alles von MCP2515 erbt:
class HighTechMCP2515 : public MCP2515 {
}
Methoden (über)schreiben geht dann innerhalb der geschweiften Klammern:
... {
void startSPI() {
SPIn->beginTransaction(SPISettings(SPI_CLOCK, MSBFIRST, SPI_MODE0));
i2c_datasend(..., 3, 0);
}
void endSPI() {
i2c_datasend(..., 3, 1);
SPIn->endTransaction();
}
}
Der zusätzliche Kram muß dann in einem neuen Konstruktor übergeben werden.
Ich habe leider gerade zu wenig Zeit mir das im Detail anzusehen, aber die Idee eine abgeleitete Klasse zu schreiben ist gut, aber ich vermute es wäre sogar noch besser einen Decorator zu schreiben der vor MCP2515 gesetzt wird (und irgendeinen Fake-CS für die Basisklasse setzen, aber das hast Du jetzt ja auch).
Das sollte sogar funktionieren, allerdings arbeiten dann alle Slave-Out der MCP gegeneinander. Da aber beim reset nichts gelesen wird, sollte das egal sein.
Ich überlege gerade ob ich meine MCP letztlich mit SPI daisy chain betreiben sollte.
Bernd
Re: Der AVR-/ARDUINO-Faden
Aktuell programmiere ich wie Folgt:
- Vorbereitung: Bootloader mit Arduino-IDE + Arduino Uno einmalig auf ATTINY13A schieben
- Assembler-Programm mit Gerd's AVR-Simulator assemblieren -> .HEX Datei wird erzeugt
- .HEX mit AVRDUDE über USBasp auf ATTINY schieben
Das funktioniert gut, und ist für mich OK so. Eine andere funktionierende Methode habe ich eh' noch nicht gefunden
Der AVRDUDE zeigt die aktuellen FUSE-Werte an.
Wie kann ich die FUSE-Werte ändern? Mir geht es darum, auf externen Systemclock umzuschalten.
Wenn ich es richtig gelesen habe, braucht es dazu einen HV-Programmer, das kann ja weder USBasp noch der A-Uno.
Oder können die FUSES gesetzt, aber ohne HV nicht mehr gelöscht werden?
Das Problem ist ja, dass bei richtig verpfuschten FUSES das Moped nicht mehr ansprechbar ist -> gibt es da eine RESET-Methode mit Hausmitteln (z.B. USBasp + 12V auf Reset-Pin)?
Anderes Thema (oder auch Fuses verpfuscht??): aufgrund Problemen mit kack-Windoofrechner (mit Linux-Rechner gab's da nie Probleme...) wurden die Bootloader bei einigen Tinys verpfuscht, und die Dinger sind jetzt nicht mehr über Arduino-IDE + Uno und USBasp ansprechbar.
Sind die ATTINYS jetzt noch zu retten?
Noch ein anderes Thema: ich hatte versucht im Programm einen Systemclock-Vorteiler mit 256 einzustellen. Danach war der Tiny nicht mehr über USBasp programmierbar. Ich konnte allerdings den Bootloader über den Uno neu aufspielen, danach funktionierte es wieder. Ist das normal?
Re: Der AVR-/ARDUINO-Faden
Hier findest du die Antworten auf deine Fragen
https://www.mikrocontroller.net/articles/AVR_Fuses
Grüße Jan
- Bastelbruder
- Beiträge: 11550
- Registriert: Mi 14. Aug 2013, 18:28
Re: Der AVR-/ARDUINO-Faden
Re: Der AVR-/ARDUINO-Faden
Auf der Seite ist der Burn-O-Mat genannt, der unkryptisch den AVRDUDE bedient, und FUSES setzen kann. Braucht es dazu einen HV-Programmer, oder reicht auch der USBasp?
Defi mit externem Takt wäre kein Problem
Re: Der AVR-/ARDUINO-Faden
Für meine Projekte mit ATMegas hab ich mit dem myAVR ProgTool gute Erfahrungen gemacht, das sollte auch mit den Tinys funktionieren:
https://shop.myavr.de/index.php?sp=down ... wort=DL112
Damit lassen sich sowohl hex-Files laden als auch Fuses lesen / schreiben. Solange der Controller noch tickt auch ohne HVProg.
Programmer ist bei mir der der mySmartUSB light, auch von myAVR.
Re: Der AVR-/ARDUINO-Faden
ausser es wurde SPI EN(able) abgewählt.
das kann nur mit HV wieder gesetzt werden.
Um dann wieder mit SPI zu programmieren.
(glaub ich. RST DIS(able)? weis ich grad nicht, aber steht ja im Datenblatt) einfach Finger weg von den Roten, dann passiert nix schlimmes.
Re: Der AVR-/ARDUINO-Faden
Mein Probem mit dem Clock-Vorteiler ist auf der MC Seite auch explizit beschrieben:
"Reaktivieren beim CLKPR-Problem (Attiny13)
Beim Attiny13 (und anderen) kann man aus dem Programm heraus die Taktquelle mit dem Vorteilerregister CLKPR heruntersetzen. Beim nächsten ISP-Programmieren kann das zu Problemen führen, wenn das Programm anläuft, die Taktquelle sehr langsam schaltet und dann erst in den ISP-Programmiermodus geschaltet wird. Travel Rec. beschreibt wie man mit einem Pull-Down-Widerstand an RESET Anlaufen des problematischen Programms verhindert [1]."
Muss man halt wissen dass es so ist. Für mich ließ sich das lösen, indem ich nochmal ein Bootloader aufgebraten hatte, und den Vorteiler vermied.
Beim fehlerhaften Brennen des Bootloaders wurden vermutlich Fuses verpfuscht, da krame ich mal nach einem externen Quarzoszillator.
...dann versuche ich demnächst mal vorsichtig über AVRDUDE harmlose Fuses zu ändern.
Danke euch
Re: Der AVR-/ARDUINO-Faden
Vielleicht schaut jemand mal rein?
Es sollen 24 MCP2515 CAN-BUS Module über SPI bedient werden.
Die CS-Pins der MCP2515 Module hängen an einem I2C Portexpander und sollen über I2C bedient werden.
Im Prinzip ja ganz Simpel, man biegt in der mcp2515.cpp den SPICS einfach auf den PCF8574 um.
Man darf aber immer nur eine Instanz je PCF8574 haben, denn wenn man den immer neu initialisiert, gehen alle Pins des PCF8574 auf High.
Am Ende soll es im Prinzip so aussehen:
for (int a = 0, a<24 , a++) {
mcp2515 (akku[a].cs-pin).sendMessage(MCPCONTI::TXB1, &canMsg1);
}
Kann man mehrere Instanzen irgenwie so erzeugen, mit verschiedenen Parametern?
for (int a = 0, a<24 , a++) {
MCP2515 mcp2515[a](akku[a].cs_pin);
}
main.cpp
Code: Alles auswählen
#include <Arduino.h>
#include "main.h"
#include <SPI.h>
#include <mcp2515.h> // https://github.com/atc1441/arduino-mcp2515
#include <PCF8574.h>
#include "conti.h"
void setup()
{
Serial.begin(115200);
pcf_01.begin();
i2c_datasend(&pcf_01,3, 0);
mcp2515.setBitrate(CAN_250KBPS);
i2c_datasend(&pcf_01,3, 1);
i2c_datasend(&pcf_01,3, 0);
mcp2515.setNormalMode();
i2c_datasend(&pcf_01,3, 1);
i2c_datasend(&pcf_01,3, 0);
mcp2515.reset();
i2c_datasend(&pcf_01,3, 1);
for (int a = 0; a < 24; a += 4)
{
akku[a].pcf.begin();
Serial.print("Start PCF 0x");
Serial.println(akku[a].i2c_addr, 16);
i2c_datasend(&akku[a].pcf,akku[a].cs_pin, 0);
mcp2515.setBitrate(CAN_250KBPS);
i2c_datasend(&akku[a].pcf,akku[a].cs_pin, 1);
i2c_datasend(&akku[a].pcf,akku[a].cs_pin, 0);
mcp2515.setNormalMode();
i2c_datasend(&akku[a].pcf,akku[a].cs_pin, 1);
i2c_datasend(&akku[a].pcf,akku[a].cs_pin, 0);
mcp2515.reset();
i2c_datasend(&akku[a].pcf,akku[a].cs_pin, 1);
}
}
boolean light_status = 0;
uint8_t power_setting = 0;
char out_string[100];
char test;
uint32_t lastsend = 0;
uint32_t lastmsg = 0;
void loop()
{
i2c_datasend(&pcf_03, 3, 1);
_delay_us(100);
i2c_datasend(&pcf_03, 3, 0);
_delay_us(100);
if (millis() - lastsend >= 100)
{ // Keeps the Battery alive needs to be send periodically
lastsend = millis();
sendCAN(0x201, 4, 0, 1, 0, 0); //I2c Adresse des SPI CS und der Pin wird hier mit übergeben
}
if (1==0)//(mcp2515.getErrorFlags() == 0x15)
{ // On Can Error 0x15 restart CAN & Toggle Akku-DATA+
Serial.println("CAN-Error 0x15, CAN-RESET & Toggle Data+");
mcp2515.reset();
mcp2515.setBitrate(CAN_250KBPS);
mcp2515.setNormalMode();
// i2c_datasend(0x26, 3, HIGH);
delay(3000);
// i2c_datasend(0x26, 3, LOW);
delay(3000);
}
if (1==0)//MCP2515(0x26, 0).readMessage(&canMsg)) // == MCP2515::ERROR_OK)
{
if (1 == 1)
{ // Turn 0 into a 1 to enable debug prints
Serial.print(canMsg.can_id, HEX); // print ID
Serial.print(" ");
Serial.print(canMsg.can_dlc, HEX); // print DLC
Serial.print(" ");
for (int i = 0; i < canMsg.can_dlc; i++)
{ // print the data
Serial.print(canMsg.data[i], HEX);
Serial.print(" ");
}
Serial.println();
}
switch (canMsg.can_id)
{
case 0x300:
{
power_setting = canMsg.data[0];
if (canMsg.data[2] == 0x64)
light_status = 1;
else
light_status = 0;
break;
}
case 0x404:
{
uint16_t voltage = ((uint16_t)canMsg.data[2] | canMsg.data[3] << 8);
int16_t ampere = ((uint16_t)canMsg.data[0] | canMsg.data[1] << 8);
uint8_t percent = canMsg.data[4];
sprintf(out_string, "%u%% %i Light:%i ", percent, power_setting, light_status);
Serial.println(out_string);
if (ampere < 0)
sprintf(out_string, "%u,%02uV -%i,%02iA ", voltage / 1000, (voltage % 999) / 10, abs(ampere) / 1000, (abs(ampere) % 999) / 10);
else
sprintf(out_string, "%u,%02uV %i,%02iA ", voltage / 1000, (voltage % 999) / 10, abs(ampere) / 1000, (abs(ampere) % 999) / 10);
Serial.println(out_string);
break;
}
}
}
}
Code: Alles auswählen
#include <mcp2515.h>
#include <PCF8574.h>
PCF8574 pcf_01(0x26);
PCF8574 pcf_02(0x25);
PCF8574 pcf_03(0x24);
PCF8574 pcf_04(0x23);
PCF8574 pcf_05(0x22);
PCF8574 pcf_06(0x21);
struct can_frame canMsg;
void sendCAN(uint32_t id, uint8_t length, uint8_t data0 = 0x00, uint8_t data1 = 0x00, uint8_t data2 = 0x00, uint8_t data3 = 0x00, uint8_t data4 = 0x00, uint8_t data5 = 0x00, uint8_t data6 = 0x00, uint8_t data7 = 0x00);
typedef struct
{
PCF8574 pcf;
int i2c_addr;
int cs_pin;
int toggle_pin;
int intr_pin;
} akku_t;
int Akku_Nr = 0;
akku_t akku[24] = {
{.pcf = pcf_01, 0x26, 3, 0, 49},
{.pcf = pcf_01, 0x26, 2, 1, 48},
{.pcf = pcf_01, 0x26, 5, 6, 47},
{.pcf = pcf_01, 0x26, 4, 7, 46},
{.pcf = pcf_02, 0x25, 1, 4, 45},
{.pcf = pcf_02, 0x25, 3, 5, 44},
{.pcf = pcf_02, 0x25, 2, 6, 43},
{.pcf = pcf_02, 0x25, 0, 7, 42},
{.pcf = pcf_03, 0x24, 3, 0, 30},
{.pcf = pcf_03, 0x24, 2, 1, 31},
{.pcf = pcf_03, 0x24, 5, 6, 32},
{.pcf = pcf_03, 0x24, 4, 7, 33},
{.pcf = pcf_04, 0x23, 1, 4, 34},
{.pcf = pcf_04, 0x23, 3, 5, 35},
{.pcf = pcf_04, 0x23, 2, 6, 36},
{.pcf = pcf_04, 0x23, 0, 7, 37},
{.pcf = pcf_05, 0x22, 3, 0, 22},
{.pcf = pcf_05, 0x22, 2, 1, 23},
{.pcf = pcf_05, 0x22, 5, 6, 24},
{.pcf = pcf_05, 0x22, 4, 7, 25},
{.pcf = pcf_06, 0x21, 1, 4, 26},
{.pcf = pcf_06, 0x21, 3, 5, 27},
{.pcf = pcf_06, 0x21, 2, 6, 28},
{.pcf = pcf_06, 0x21, 0, 7, 29}};
void i2c_datasend(PCF8574 *pcf, int pin, bool state) // i2c Adresse , Pin, Low/High
{
bool status;
if (!(status = pcf->isConnected()))
{
Serial.print("Not connected ");
}
if (pcf->lastError())
{
Serial.print("I2C Fehler Adresse 0x");
Serial.println(pcf->getAddress());
}
pcf->write(pin, state);
}
class MCPCONTI:public MCP2515{
void startSPI() {
SPI.beginTransaction(SPISettings(SPI_CLOCK, MSBFIRST, SPI_MODE0));
i2c_datasend(&akku[0].pcf,akku[0].cs_pin, 0);}
void endSPI() {
i2c_datasend(&akku[0].pcf,akku[0].cs_pin, 1);
SPI.endTransaction();
}
};
MCPCONTI mcp2515(5);
void sendCAN(uint32_t id, uint8_t length, uint8_t data0, uint8_t data1, uint8_t data2, uint8_t data3, uint8_t data4, uint8_t data5, uint8_t data6, uint8_t data7)
{
struct can_frame canMsg1;
canMsg1.can_id = id;
canMsg1.can_dlc = length;
canMsg1.data[0] = data0;
canMsg1.data[1] = data1;
canMsg1.data[2] = data2;
canMsg1.data[3] = data3;
canMsg1.data[4] = data4;
canMsg1.data[5] = data5;
canMsg1.data[6] = data6;
canMsg1.data[7] = data7;
mcp2515.sendMessage(MCPCONTI::TXB1, &canMsg1);
}
Code: Alles auswählen
#include "mcp2515.h"
const struct MCP2515::TXBn_REGS MCP2515::TXB[MCP2515::N_TXBUFFERS] = {
{MCP_TXB0CTRL, MCP_TXB0SIDH, MCP_TXB0DATA},
{MCP_TXB1CTRL, MCP_TXB1SIDH, MCP_TXB1DATA},
{MCP_TXB2CTRL, MCP_TXB2SIDH, MCP_TXB2DATA}
};
const struct MCP2515::RXBn_REGS MCP2515::RXB[N_RXBUFFERS] = {
{MCP_RXB0CTRL, MCP_RXB0SIDH, MCP_RXB0DATA, CANINTF_RX0IF},
{MCP_RXB1CTRL, MCP_RXB1SIDH, MCP_RXB1DATA, CANINTF_RX1IF}
};
MCP2515::MCP2515(const uint8_t _CS)
{
SPI.begin();
SPICS = _CS;
pinMode(SPICS, OUTPUT);
endSPI();
}
void MCP2515::startSPI() {
SPI.beginTransaction(SPISettings(SPI_CLOCK, MSBFIRST, SPI_MODE0));
digitalWrite(SPICS, LOW);
}
void MCP2515::endSPI() {
digitalWrite(SPICS, HIGH);
SPI.endTransaction();
}
void MCP2515::setPin(uint8_t pinstate) {
setRegister(MCP_PinControl, pinstate);
}
MCP2515::ERROR MCP2515::reset(void)
{
startSPI();
SPI.transfer(INSTRUCTION_RESET);
endSPI();
delay(10);
uint8_t zeros[14];
memset(zeros, 0, sizeof(zeros));
setRegisters(MCP_TXB0CTRL, zeros, 14);
setRegisters(MCP_TXB1CTRL, zeros, 14);
setRegisters(MCP_TXB2CTRL, zeros, 14);
setRegister(MCP_RXB0CTRL, 0);
setRegister(MCP_RXB1CTRL, 0);
setRegister(MCP_CANINTE, CANINTF_WAKIF | CANINTF_RX0IF | CANINTF_RX1IF | CANINTF_ERRIF | CANINTF_MERRF);
modifyRegister(MCP_RXB0CTRL, RXBnCTRL_RXM_MASK | RXB0CTRL_BUKT, RXBnCTRL_RXM_STDEXT | RXB0CTRL_BUKT);
modifyRegister(MCP_RXB1CTRL, RXBnCTRL_RXM_MASK, RXBnCTRL_RXM_STDEXT);
return ERROR_OK;
}
uint8_t MCP2515::readRegister(const REGISTER reg)
{
startSPI();
SPI.transfer(INSTRUCTION_READ);
SPI.transfer(reg);
uint8_t ret = SPI.transfer(0x00);
endSPI();
return ret;
}
void MCP2515::readRegisters(const REGISTER reg, uint8_t values[], const uint8_t n)
{
startSPI();
SPI.transfer(INSTRUCTION_READ);
SPI.transfer(reg);
// mcp2515 has auto-increment of address-pointer
for (uint8_t i = 0; i < n; i++) {
values[i] = SPI.transfer(0x00);
}
endSPI();
}
void MCP2515::setRegister(const REGISTER reg, const uint8_t value)
{
startSPI();
SPI.transfer(INSTRUCTION_WRITE);
SPI.transfer(reg);
SPI.transfer(value);
endSPI();
}
void MCP2515::setRegisters(const REGISTER reg, const uint8_t values[], const uint8_t n)
{
startSPI();
SPI.transfer(INSTRUCTION_WRITE);
SPI.transfer(reg);
for (uint8_t i = 0; i < n; i++) {
SPI.transfer(values[i]);
}
endSPI();
}
void MCP2515::modifyRegister(const REGISTER reg, const uint8_t mask, const uint8_t data)
{
startSPI();
SPI.transfer(INSTRUCTION_BITMOD);
SPI.transfer(reg);
SPI.transfer(mask);
SPI.transfer(data);
endSPI();
}
uint8_t MCP2515::getStatus(void)
{
startSPI();
SPI.transfer(INSTRUCTION_READ_STATUS);
uint8_t i = SPI.transfer(0x00);
endSPI();
return i;
}
MCP2515::ERROR MCP2515::setConfigMode()
{
return setMode(CANCTRL_REQOP_CONFIG);
}
MCP2515::ERROR MCP2515::setListenOnlyMode()
{
return setMode(CANCTRL_REQOP_LISTENONLY);
}
MCP2515::ERROR MCP2515::setSleepMode()
{
return setMode(CANCTRL_REQOP_SLEEP);
}
MCP2515::ERROR MCP2515::setLoopbackMode()
{
return setMode(CANCTRL_REQOP_LOOPBACK);
}
MCP2515::ERROR MCP2515::setNormalMode()
{
return setMode(CANCTRL_REQOP_NORMAL);
}
MCP2515::ERROR MCP2515::setMode(const CANCTRL_REQOP_MODE mode)
{
unsigned long endTime = millis() + 200;
bool modeMatch = false;
while (millis() < endTime) {
modifyRegister(MCP_CANCTRL, CANCTRL_REQOP, mode);
uint8_t newmode = readRegister(MCP_CANSTAT);
newmode &= CANSTAT_OPMOD;
modeMatch = newmode == mode;
if (modeMatch) {
break;
}
}
return modeMatch ? ERROR_OK : ERROR_FAIL;
}
MCP2515::ERROR MCP2515::setBitrate(const CAN_SPEED canSpeed)
{
return setBitrate(canSpeed, MCP_8MHZ);
}
MCP2515::ERROR MCP2515::setBitrate(const CAN_SPEED canSpeed, CAN_CLOCK canClock)
{
ERROR error = setConfigMode();
if (error != ERROR_OK) {
return error;
}
uint8_t set, cfg1, cfg2, cfg3;
set = 1;
switch (canClock)
{
case (MCP_8MHZ):
switch (canSpeed)
{
case (CAN_5KBPS): // 5KBPS
cfg1 = MCP_8MHz_5kBPS_CFG1;
cfg2 = MCP_8MHz_5kBPS_CFG2;
cfg3 = MCP_8MHz_5kBPS_CFG3;
break;
case (CAN_10KBPS): // 10KBPS
cfg1 = MCP_8MHz_10kBPS_CFG1;
cfg2 = MCP_8MHz_10kBPS_CFG2;
cfg3 = MCP_8MHz_10kBPS_CFG3;
break;
case (CAN_20KBPS): // 20KBPS
cfg1 = MCP_8MHz_20kBPS_CFG1;
cfg2 = MCP_8MHz_20kBPS_CFG2;
cfg3 = MCP_8MHz_20kBPS_CFG3;
break;
case (CAN_31K25BPS): // 31.25KBPS
cfg1 = MCP_8MHz_31k25BPS_CFG1;
cfg2 = MCP_8MHz_31k25BPS_CFG2;
cfg3 = MCP_8MHz_31k25BPS_CFG3;
break;
case (CAN_33KBPS): // 33.333KBPS
cfg1 = MCP_8MHz_33k3BPS_CFG1;
cfg2 = MCP_8MHz_33k3BPS_CFG2;
cfg3 = MCP_8MHz_33k3BPS_CFG3;
break;
case (CAN_40KBPS): // 40Kbps
cfg1 = MCP_8MHz_40kBPS_CFG1;
cfg2 = MCP_8MHz_40kBPS_CFG2;
cfg3 = MCP_8MHz_40kBPS_CFG3;
break;
case (CAN_50KBPS): // 50Kbps
cfg1 = MCP_8MHz_50kBPS_CFG1;
cfg2 = MCP_8MHz_50kBPS_CFG2;
cfg3 = MCP_8MHz_50kBPS_CFG3;
break;
case (CAN_80KBPS): // 80Kbps
cfg1 = MCP_8MHz_80kBPS_CFG1;
cfg2 = MCP_8MHz_80kBPS_CFG2;
cfg3 = MCP_8MHz_80kBPS_CFG3;
break;
case (CAN_100KBPS): // 100Kbps
cfg1 = MCP_8MHz_100kBPS_CFG1;
cfg2 = MCP_8MHz_100kBPS_CFG2;
cfg3 = MCP_8MHz_100kBPS_CFG3;
break;
case (CAN_125KBPS): // 125Kbps
cfg1 = MCP_8MHz_125kBPS_CFG1;
cfg2 = MCP_8MHz_125kBPS_CFG2;
cfg3 = MCP_8MHz_125kBPS_CFG3;
break;
case (CAN_200KBPS): // 200Kbps
cfg1 = MCP_8MHz_200kBPS_CFG1;
cfg2 = MCP_8MHz_200kBPS_CFG2;
cfg3 = MCP_8MHz_200kBPS_CFG3;
break;
case (CAN_250KBPS): // 250Kbps
cfg1 = MCP_8MHz_250kBPS_CFG1;
cfg2 = MCP_8MHz_250kBPS_CFG2;
cfg3 = MCP_8MHz_250kBPS_CFG3;
break;
case (CAN_500KBPS): // 500Kbps
cfg1 = MCP_8MHz_500kBPS_CFG1;
cfg2 = MCP_8MHz_500kBPS_CFG2;
cfg3 = MCP_8MHz_500kBPS_CFG3;
break;
case (CAN_1000KBPS): // 1Mbps
cfg1 = MCP_8MHz_1000kBPS_CFG1;
cfg2 = MCP_8MHz_1000kBPS_CFG2;
cfg3 = MCP_8MHz_1000kBPS_CFG3;
break;
default:
set = 0;
break;
}
break;
case (MCP_16MHZ):
switch (canSpeed)
{
case (CAN_5KBPS): // 5Kbps
cfg1 = MCP_16MHz_5kBPS_CFG1;
cfg2 = MCP_16MHz_5kBPS_CFG2;
cfg3 = MCP_16MHz_5kBPS_CFG3;
break;
case (CAN_10KBPS): // 10Kbps
cfg1 = MCP_16MHz_10kBPS_CFG1;
cfg2 = MCP_16MHz_10kBPS_CFG2;
cfg3 = MCP_16MHz_10kBPS_CFG3;
break;
case (CAN_20KBPS): // 20Kbps
cfg1 = MCP_16MHz_20kBPS_CFG1;
cfg2 = MCP_16MHz_20kBPS_CFG2;
cfg3 = MCP_16MHz_20kBPS_CFG3;
break;
case (CAN_33KBPS): // 33.333Kbps
cfg1 = MCP_16MHz_33k3BPS_CFG1;
cfg2 = MCP_16MHz_33k3BPS_CFG2;
cfg3 = MCP_16MHz_33k3BPS_CFG3;
break;
case (CAN_40KBPS): // 40Kbps
cfg1 = MCP_16MHz_40kBPS_CFG1;
cfg2 = MCP_16MHz_40kBPS_CFG2;
cfg3 = MCP_16MHz_40kBPS_CFG3;
break;
case (CAN_50KBPS): // 50Kbps
cfg1 = MCP_16MHz_50kBPS_CFG1;
cfg2 = MCP_16MHz_50kBPS_CFG2;
cfg3 = MCP_16MHz_50kBPS_CFG3;
break;
case (CAN_80KBPS): // 80Kbps
cfg1 = MCP_16MHz_80kBPS_CFG1;
cfg2 = MCP_16MHz_80kBPS_CFG2;
cfg3 = MCP_16MHz_80kBPS_CFG3;
break;
case (CAN_83K3BPS): // 83.333Kbps
cfg1 = MCP_16MHz_83k3BPS_CFG1;
cfg2 = MCP_16MHz_83k3BPS_CFG2;
cfg3 = MCP_16MHz_83k3BPS_CFG3;
break;
case (CAN_100KBPS): // 100Kbps
cfg1 = MCP_16MHz_100kBPS_CFG1;
cfg2 = MCP_16MHz_100kBPS_CFG2;
cfg3 = MCP_16MHz_100kBPS_CFG3;
break;
case (CAN_125KBPS): // 125Kbps
cfg1 = MCP_16MHz_125kBPS_CFG1;
cfg2 = MCP_16MHz_125kBPS_CFG2;
cfg3 = MCP_16MHz_125kBPS_CFG3;
break;
case (CAN_200KBPS): // 200Kbps
cfg1 = MCP_16MHz_200kBPS_CFG1;
cfg2 = MCP_16MHz_200kBPS_CFG2;
cfg3 = MCP_16MHz_200kBPS_CFG3;
break;
case (CAN_250KBPS): // 250Kbps
cfg1 = MCP_16MHz_250kBPS_CFG1;
cfg2 = MCP_16MHz_250kBPS_CFG2;
cfg3 = MCP_16MHz_250kBPS_CFG3;
break;
case (CAN_500KBPS): // 500Kbps
cfg1 = MCP_16MHz_500kBPS_CFG1;
cfg2 = MCP_16MHz_500kBPS_CFG2;
cfg3 = MCP_16MHz_500kBPS_CFG3;
break;
case (CAN_1000KBPS): // 1Mbps
cfg1 = MCP_16MHz_1000kBPS_CFG1;
cfg2 = MCP_16MHz_1000kBPS_CFG2;
cfg3 = MCP_16MHz_1000kBPS_CFG3;
break;
default:
set = 0;
break;
}
break;
case (MCP_20MHZ):
switch (canSpeed)
{
case (CAN_33KBPS): // 33.333Kbps
cfg1 = MCP_20MHz_33k3BPS_CFG1;
cfg2 = MCP_20MHz_33k3BPS_CFG2;
cfg3 = MCP_20MHz_33k3BPS_CFG3;
break;
case (CAN_40KBPS): // 40Kbps
cfg1 = MCP_20MHz_40kBPS_CFG1;
cfg2 = MCP_20MHz_40kBPS_CFG2;
cfg3 = MCP_20MHz_40kBPS_CFG3;
break;
case (CAN_50KBPS): // 50Kbps
cfg1 = MCP_20MHz_50kBPS_CFG1;
cfg2 = MCP_20MHz_50kBPS_CFG2;
cfg3 = MCP_20MHz_50kBPS_CFG3;
break;
case (CAN_80KBPS): // 80Kbps
cfg1 = MCP_20MHz_80kBPS_CFG1;
cfg2 = MCP_20MHz_80kBPS_CFG2;
cfg3 = MCP_20MHz_80kBPS_CFG3;
break;
case (CAN_83K3BPS): // 83.333Kbps
cfg1 = MCP_20MHz_83k3BPS_CFG1;
cfg2 = MCP_20MHz_83k3BPS_CFG2;
cfg3 = MCP_20MHz_83k3BPS_CFG3;
break;
case (CAN_100KBPS): // 100Kbps
cfg1 = MCP_20MHz_100kBPS_CFG1;
cfg2 = MCP_20MHz_100kBPS_CFG2;
cfg3 = MCP_20MHz_100kBPS_CFG3;
break;
case (CAN_125KBPS): // 125Kbps
cfg1 = MCP_20MHz_125kBPS_CFG1;
cfg2 = MCP_20MHz_125kBPS_CFG2;
cfg3 = MCP_20MHz_125kBPS_CFG3;
break;
case (CAN_200KBPS): // 200Kbps
cfg1 = MCP_20MHz_200kBPS_CFG1;
cfg2 = MCP_20MHz_200kBPS_CFG2;
cfg3 = MCP_20MHz_200kBPS_CFG3;
break;
case (CAN_250KBPS): // 250Kbps
cfg1 = MCP_20MHz_250kBPS_CFG1;
cfg2 = MCP_20MHz_250kBPS_CFG2;
cfg3 = MCP_20MHz_250kBPS_CFG3;
break;
case (CAN_500KBPS): // 500Kbps
cfg1 = MCP_20MHz_500kBPS_CFG1;
cfg2 = MCP_20MHz_500kBPS_CFG2;
cfg3 = MCP_20MHz_500kBPS_CFG3;
break;
case (CAN_1000KBPS): // 1Mbps
cfg1 = MCP_20MHz_1000kBPS_CFG1;
cfg2 = MCP_20MHz_1000kBPS_CFG2;
cfg3 = MCP_20MHz_1000kBPS_CFG3;
break;
default:
set = 0;
break;
}
break;
default:
set = 0;
break;
}
if (set) {
setRegister(MCP_CNF1, cfg1);
setRegister(MCP_CNF2, cfg2);
setRegister(MCP_CNF3, cfg3);
return ERROR_OK;
}
else {
return ERROR_FAIL;
}
}
MCP2515::ERROR MCP2515::setClkOut(const CAN_CLKOUT divisor)
{
if (divisor == CLKOUT_DISABLE) {
/* Turn off CLKEN */
modifyRegister(MCP_CANCTRL, CANCTRL_CLKEN, 0x00);
/* Turn on CLKOUT for SOF */
modifyRegister(MCP_CNF3, CNF3_SOF, CNF3_SOF);
return ERROR_OK;
}
/* Set the prescaler (CLKPRE) */
modifyRegister(MCP_CANCTRL, CANCTRL_CLKPRE, divisor);
/* Turn on CLKEN */
modifyRegister(MCP_CANCTRL, CANCTRL_CLKEN, CANCTRL_CLKEN);
/* Turn off CLKOUT for SOF */
modifyRegister(MCP_CNF3, CNF3_SOF, 0x00);
return ERROR_OK;
}
void MCP2515::prepareId(uint8_t *buffer, const bool ext, const uint32_t id)
{
uint16_t canid = (uint16_t)(id & 0x0FFFF);
if (ext) {
buffer[MCP_EID0] = (uint8_t) (canid & 0xFF);
buffer[MCP_EID8] = (uint8_t) (canid >> 8);
canid = (uint16_t)(id >> 16);
buffer[MCP_SIDL] = (uint8_t) (canid & 0x03);
buffer[MCP_SIDL] += (uint8_t) ((canid & 0x1C) << 3);
buffer[MCP_SIDL] |= TXB_EXIDE_MASK;
buffer[MCP_SIDH] = (uint8_t) (canid >> 5);
} else {
buffer[MCP_SIDH] = (uint8_t) (canid >> 3);
buffer[MCP_SIDL] = (uint8_t) ((canid & 0x07 ) << 5);
buffer[MCP_EID0] = 0;
buffer[MCP_EID8] = 0;
}
}
MCP2515::ERROR MCP2515::setFilterMask(const MASK mask, const bool ext, const uint32_t ulData)
{
ERROR res = setConfigMode();
if (res != ERROR_OK) {
return res;
}
uint8_t tbufdata[4];
prepareId(tbufdata, ext, ulData);
REGISTER reg;
switch (mask) {
case MASK0: reg = MCP_RXM0SIDH; break;
case MASK1: reg = MCP_RXM1SIDH; break;
default:
return ERROR_FAIL;
}
setRegisters(reg, tbufdata, 4);
return ERROR_OK;
}
MCP2515::ERROR MCP2515::setFilter(const RXF num, const bool ext, const uint32_t ulData)
{
ERROR res = setConfigMode();
if (res != ERROR_OK) {
return res;
}
REGISTER reg;
switch (num) {
case RXF0: reg = MCP_RXF0SIDH; break;
case RXF1: reg = MCP_RXF1SIDH; break;
case RXF2: reg = MCP_RXF2SIDH; break;
case RXF3: reg = MCP_RXF3SIDH; break;
case RXF4: reg = MCP_RXF4SIDH; break;
case RXF5: reg = MCP_RXF5SIDH; break;
default:
return ERROR_FAIL;
}
uint8_t tbufdata[4];
prepareId(tbufdata, ext, ulData);
setRegisters(reg, tbufdata, 4);
return ERROR_OK;
}
MCP2515::ERROR MCP2515::sendMessage(const TXBn txbn, const struct can_frame *frame)
{
if (frame->can_dlc > CAN_MAX_DLEN) {
return ERROR_FAILTX;
}
const struct TXBn_REGS *txbuf = &TXB[txbn];
uint8_t data[13];
bool ext = (frame->can_id & CAN_EFF_FLAG);
bool rtr = (frame->can_id & CAN_RTR_FLAG);
uint32_t id = (frame->can_id & (ext ? CAN_EFF_MASK : CAN_SFF_MASK));
prepareId(data, ext, id);
data[MCP_DLC] = rtr ? (frame->can_dlc | RTR_MASK) : frame->can_dlc;
memcpy(&data[MCP_DATA], frame->data, frame->can_dlc);
setRegisters(txbuf->SIDH, data, 5 + frame->can_dlc);
modifyRegister(txbuf->CTRL, TXB_TXREQ, TXB_TXREQ);
return ERROR_OK;
}
MCP2515::ERROR MCP2515::sendMessage(const struct can_frame *frame)
{
if (frame->can_dlc > CAN_MAX_DLEN) {
return ERROR_FAILTX;
}
TXBn txBuffers[N_TXBUFFERS] = {TXB0, TXB1, TXB2};
for (int i = 0; i < N_TXBUFFERS; i++) {
const struct TXBn_REGS *txbuf = &TXB[txBuffers[i]];
uint8_t ctrlval = readRegister(txbuf->CTRL);
if ( (ctrlval & TXB_TXREQ) == 0 ) {
return sendMessage(txBuffers[i], frame);
}
}
return ERROR_FAILTX;
}
MCP2515::ERROR MCP2515::readMessage(const RXBn rxbn, struct can_frame *frame)
{
const struct RXBn_REGS *rxb = &RXB[rxbn];
uint8_t tbufdata[5];
readRegisters(rxb->SIDH, tbufdata, 5);
uint32_t id = (tbufdata[MCP_SIDH] << 3) + (tbufdata[MCP_SIDL] >> 5);
if ( (tbufdata[MCP_SIDL] & TXB_EXIDE_MASK) == TXB_EXIDE_MASK ) {
id = (id << 2) + (tbufdata[MCP_SIDL] & 0x03);
id = (id << 8) + tbufdata[MCP_EID8];
id = (id << 8) + tbufdata[MCP_EID0];
id |= CAN_EFF_FLAG;
}
uint8_t dlc = (tbufdata[MCP_DLC] & DLC_MASK);
if (dlc > CAN_MAX_DLEN) {
return ERROR_FAIL;
}
uint8_t ctrl = readRegister(rxb->CTRL);
if (ctrl & RXBnCTRL_RTR) {
id |= CAN_RTR_FLAG;
}
frame->can_id = id;
frame->can_dlc = dlc;
readRegisters(rxb->DATA, frame->data, dlc);
modifyRegister(MCP_CANINTF, rxb->CANINTF_RXnIF, 0);
return ERROR_OK;
}
MCP2515::ERROR MCP2515::readMessage(struct can_frame *frame)
{
ERROR rc;
uint8_t stat = getStatus();
if ( stat & STAT_RX0IF ) {
rc = readMessage(RXB0, frame);
} else if ( stat & STAT_RX1IF ) {
rc = readMessage(RXB1, frame);
} else {
rc = ERROR_NOMSG;
}
return rc;
}
bool MCP2515::checkReceive(void)
{
uint8_t res = getStatus();
if ( res & STAT_RXIF_MASK ) {
return true;
} else {
return false;
}
}
bool MCP2515::checkError(void)
{
uint8_t eflg = getErrorFlags();
if ( eflg & EFLG_ERRORMASK ) {
return true;
} else {
return false;
}
}
uint8_t MCP2515::getErrorFlags(void)
{
return readRegister(MCP_EFLG);
}
void MCP2515::clearRXnOVRFlags(void)
{
modifyRegister(MCP_EFLG, EFLG_RX0OVR | EFLG_RX1OVR, 0);
}
uint8_t MCP2515::getInterrupts(void)
{
return readRegister(MCP_CANINTF);
}
void MCP2515::clearInterrupts(void)
{
setRegister(MCP_CANINTF, 0);
}
uint8_t MCP2515::getInterruptMask(void)
{
return readRegister(MCP_CANINTE);
}
void MCP2515::clearTXInterrupts(void)
{
modifyRegister(MCP_CANINTF, (CANINTF_TX0IF | CANINTF_TX1IF | CANINTF_TX2IF), 0);
}
void MCP2515::clearRXnOVR(void)
{
uint8_t eflg = getErrorFlags();
if (eflg != 0) {
clearRXnOVRFlags();
clearInterrupts();
//modifyRegister(MCP_CANINTF, CANINTF_ERRIF, 0);
}
}
void MCP2515::clearMERR()
{
//modifyRegister(MCP_EFLG, EFLG_RX0OVR | EFLG_RX1OVR, 0);
//clearInterrupts();
modifyRegister(MCP_CANINTF, CANINTF_MERRF, 0);
}
void MCP2515::clearERRIF()
{
//modifyRegister(MCP_EFLG, EFLG_RX0OVR | EFLG_RX1OVR, 0);
//clearInterrupts();
modifyRegister(MCP_CANINTF, CANINTF_ERRIF, 0);
}
mcp2515.h
[code]
#ifndef _MCP2515_H_
#define _MCP2515_H_
#include <SPI.h>
#include "can.h"
/*
Speed 8M
*/
#define MCP_8MHz_1000kBPS_CFG1 (0x00)
#define MCP_8MHz_1000kBPS_CFG2 (0x80)
#define MCP_8MHz_1000kBPS_CFG3 (0x80)
#define MCP_8MHz_500kBPS_CFG1 (0x00)
#define MCP_8MHz_500kBPS_CFG2 (0x90)
#define MCP_8MHz_500kBPS_CFG3 (0x82)
#define MCP_8MHz_250kBPS_CFG1 (0x00)
#define MCP_8MHz_250kBPS_CFG2 (0xB1)
#define MCP_8MHz_250kBPS_CFG3 (0x85)
#define MCP_8MHz_200kBPS_CFG1 (0x00)
#define MCP_8MHz_200kBPS_CFG2 (0xB4)
#define MCP_8MHz_200kBPS_CFG3 (0x86)
#define MCP_8MHz_125kBPS_CFG1 (0x01)
#define MCP_8MHz_125kBPS_CFG2 (0xB1)
#define MCP_8MHz_125kBPS_CFG3 (0x85)
#define MCP_8MHz_100kBPS_CFG1 (0x01)
#define MCP_8MHz_100kBPS_CFG2 (0xB4)
#define MCP_8MHz_100kBPS_CFG3 (0x86)
#define MCP_8MHz_80kBPS_CFG1 (0x01)
#define MCP_8MHz_80kBPS_CFG2 (0xBF)
#define MCP_8MHz_80kBPS_CFG3 (0x87)
#define MCP_8MHz_50kBPS_CFG1 (0x03)
#define MCP_8MHz_50kBPS_CFG2 (0xB4)
#define MCP_8MHz_50kBPS_CFG3 (0x86)
#define MCP_8MHz_40kBPS_CFG1 (0x03)
#define MCP_8MHz_40kBPS_CFG2 (0xBF)
#define MCP_8MHz_40kBPS_CFG3 (0x87)
#define MCP_8MHz_33k3BPS_CFG1 (0x47)
#define MCP_8MHz_33k3BPS_CFG2 (0xE2)
#define MCP_8MHz_33k3BPS_CFG3 (0x85)
#define MCP_8MHz_31k25BPS_CFG1 (0x07)
#define MCP_8MHz_31k25BPS_CFG2 (0xA4)
#define MCP_8MHz_31k25BPS_CFG3 (0x84)
#define MCP_8MHz_20kBPS_CFG1 (0x07)
#define MCP_8MHz_20kBPS_CFG2 (0xBF)
#define MCP_8MHz_20kBPS_CFG3 (0x87)
#define MCP_8MHz_10kBPS_CFG1 (0x0F)
#define MCP_8MHz_10kBPS_CFG2 (0xBF)
#define MCP_8MHz_10kBPS_CFG3 (0x87)
#define MCP_8MHz_5kBPS_CFG1 (0x1F)
#define MCP_8MHz_5kBPS_CFG2 (0xBF)
#define MCP_8MHz_5kBPS_CFG3 (0x87)
/*
speed 16M
*/
#define MCP_16MHz_1000kBPS_CFG1 (0x00)
#define MCP_16MHz_1000kBPS_CFG2 (0xD0)
#define MCP_16MHz_1000kBPS_CFG3 (0x82)
#define MCP_16MHz_500kBPS_CFG1 (0x00)
#define MCP_16MHz_500kBPS_CFG2 (0xF0)
#define MCP_16MHz_500kBPS_CFG3 (0x86)
#define MCP_16MHz_250kBPS_CFG1 (0x41)
#define MCP_16MHz_250kBPS_CFG2 (0xF1)
#define MCP_16MHz_250kBPS_CFG3 (0x85)
#define MCP_16MHz_200kBPS_CFG1 (0x01)
#define MCP_16MHz_200kBPS_CFG2 (0xFA)
#define MCP_16MHz_200kBPS_CFG3 (0x87)
#define MCP_16MHz_125kBPS_CFG1 (0x03)
#define MCP_16MHz_125kBPS_CFG2 (0xF0)
#define MCP_16MHz_125kBPS_CFG3 (0x86)
#define MCP_16MHz_100kBPS_CFG1 (0x03)
#define MCP_16MHz_100kBPS_CFG2 (0xFA)
#define MCP_16MHz_100kBPS_CFG3 (0x87)
#define MCP_16MHz_80kBPS_CFG1 (0x03)
#define MCP_16MHz_80kBPS_CFG2 (0xFF)
#define MCP_16MHz_80kBPS_CFG3 (0x87)
#define MCP_16MHz_83k3BPS_CFG1 (0x03)
#define MCP_16MHz_83k3BPS_CFG2 (0xBE)
#define MCP_16MHz_83k3BPS_CFG3 (0x07)
#define MCP_16MHz_50kBPS_CFG1 (0x07)
#define MCP_16MHz_50kBPS_CFG2 (0xFA)
#define MCP_16MHz_50kBPS_CFG3 (0x87)
#define MCP_16MHz_40kBPS_CFG1 (0x07)
#define MCP_16MHz_40kBPS_CFG2 (0xFF)
#define MCP_16MHz_40kBPS_CFG3 (0x87)
#define MCP_16MHz_33k3BPS_CFG1 (0x4E)
#define MCP_16MHz_33k3BPS_CFG2 (0xF1)
#define MCP_16MHz_33k3BPS_CFG3 (0x85)
#define MCP_16MHz_20kBPS_CFG1 (0x0F)
#define MCP_16MHz_20kBPS_CFG2 (0xFF)
#define MCP_16MHz_20kBPS_CFG3 (0x87)
#define MCP_16MHz_10kBPS_CFG1 (0x1F)
#define MCP_16MHz_10kBPS_CFG2 (0xFF)
#define MCP_16MHz_10kBPS_CFG3 (0x87)
#define MCP_16MHz_5kBPS_CFG1 (0x3F)
#define MCP_16MHz_5kBPS_CFG2 (0xFF)
#define MCP_16MHz_5kBPS_CFG3 (0x87)
/*
speed 20M
*/
#define MCP_20MHz_1000kBPS_CFG1 (0x00)
#define MCP_20MHz_1000kBPS_CFG2 (0xD9)
#define MCP_20MHz_1000kBPS_CFG3 (0x82)
#define MCP_20MHz_500kBPS_CFG1 (0x00)
#define MCP_20MHz_500kBPS_CFG2 (0xFA)
#define MCP_20MHz_500kBPS_CFG3 (0x87)
#define MCP_20MHz_250kBPS_CFG1 (0x41)
#define MCP_20MHz_250kBPS_CFG2 (0xFB)
#define MCP_20MHz_250kBPS_CFG3 (0x86)
#define MCP_20MHz_200kBPS_CFG1 (0x01)
#define MCP_20MHz_200kBPS_CFG2 (0xFF)
#define MCP_20MHz_200kBPS_CFG3 (0x87)
#define MCP_20MHz_125kBPS_CFG1 (0x03)
#define MCP_20MHz_125kBPS_CFG2 (0xFA)
#define MCP_20MHz_125kBPS_CFG3 (0x87)
#define MCP_20MHz_100kBPS_CFG1 (0x04)
#define MCP_20MHz_100kBPS_CFG2 (0xFA)
#define MCP_20MHz_100kBPS_CFG3 (0x87)
#define MCP_20MHz_83k3BPS_CFG1 (0x04)
#define MCP_20MHz_83k3BPS_CFG2 (0xFE)
#define MCP_20MHz_83k3BPS_CFG3 (0x87)
#define MCP_20MHz_80kBPS_CFG1 (0x04)
#define MCP_20MHz_80kBPS_CFG2 (0xFF)
#define MCP_20MHz_80kBPS_CFG3 (0x87)
#define MCP_20MHz_50kBPS_CFG1 (0x09)
#define MCP_20MHz_50kBPS_CFG2 (0xFA)
#define MCP_20MHz_50kBPS_CFG3 (0x87)
#define MCP_20MHz_40kBPS_CFG1 (0x09)
#define MCP_20MHz_40kBPS_CFG2 (0xFF)
#define MCP_20MHz_40kBPS_CFG3 (0x87)
#define MCP_20MHz_33k3BPS_CFG1 (0x0B)
#define MCP_20MHz_33k3BPS_CFG2 (0xFF)
#define MCP_20MHz_33k3BPS_CFG3 (0x87)
enum CAN_CLOCK {
MCP_20MHZ,
MCP_16MHZ,
MCP_8MHZ
};
enum CAN_SPEED {
CAN_5KBPS,
CAN_10KBPS,
CAN_20KBPS,
CAN_31K25BPS,
CAN_33KBPS,
CAN_40KBPS,
CAN_50KBPS,
CAN_80KBPS,
CAN_83K3BPS,
CAN_95KBPS,
CAN_100KBPS,
CAN_125KBPS,
CAN_200KBPS,
CAN_250KBPS,
CAN_500KBPS,
CAN_1000KBPS
};
enum CAN_CLKOUT {
CLKOUT_DISABLE = -1,
CLKOUT_DIV1 = 0x0,
CLKOUT_DIV2 = 0x1,
CLKOUT_DIV4 = 0x2,
CLKOUT_DIV8 = 0x3,
};
class MCP2515
{
public:
enum ERROR {
ERROR_OK = 0,
ERROR_FAIL = 1,
ERROR_ALLTXBUSY = 2,
ERROR_FAILINIT = 3,
ERROR_FAILTX = 4,
ERROR_NOMSG = 5
};
enum MASK {
MASK0,
MASK1
};
enum RXF {
RXF0 = 0,
RXF1 = 1,
RXF2 = 2,
RXF3 = 3,
RXF4 = 4,
RXF5 = 5
};
enum RXBn {
RXB0 = 0,
RXB1 = 1
};
enum TXBn {
TXB0 = 0,
TXB1 = 1,
TXB2 = 2
};
enum /*class*/ CANINTF : uint8_t {
CANINTF_RX0IF = 0x01,
CANINTF_RX1IF = 0x02,
CANINTF_TX0IF = 0x04,
CANINTF_TX1IF = 0x08,
CANINTF_TX2IF = 0x10,
CANINTF_ERRIF = 0x20,
CANINTF_WAKIF = 0x40,
CANINTF_MERRF = 0x80
};
enum /*class*/ EFLG : uint8_t {
EFLG_RX1OVR = (1 << 7),
EFLG_RX0OVR = (1 << 6),
EFLG_TXBO = (1 << 5),
EFLG_TXEP = (1 << 4),
EFLG_RXEP = (1 << 3),
EFLG_TXWAR = (1 << 2),
EFLG_RXWAR = (1 << 1),
EFLG_EWARN = (1 << 0)
};
static const uint32_t SPI_CLOCK = 10000000; // 10MHz
private:
static const uint8_t CANCTRL_REQOP = 0xE0;
static const uint8_t CANCTRL_ABAT = 0x10;
static const uint8_t CANCTRL_OSM = 0x08;
static const uint8_t CANCTRL_CLKEN = 0x04;
static const uint8_t CANCTRL_CLKPRE = 0x03;
enum /*class*/ CANCTRL_REQOP_MODE : uint8_t {
CANCTRL_REQOP_NORMAL = 0x00,
CANCTRL_REQOP_SLEEP = 0x20,
CANCTRL_REQOP_LOOPBACK = 0x40,
CANCTRL_REQOP_LISTENONLY = 0x60,
CANCTRL_REQOP_CONFIG = 0x80,
CANCTRL_REQOP_POWERUP = 0xE0
};
static const uint8_t CANSTAT_OPMOD = 0xE0;
static const uint8_t CANSTAT_ICOD = 0x0E;
static const uint8_t CNF3_SOF = 0x80;
static const uint8_t CNF3_WAKFIL = 0x40;
static const uint8_t TXB_EXIDE_MASK = 0x08;
static const uint8_t DLC_MASK = 0x0F;
static const uint8_t RTR_MASK = 0x40;
static const uint8_t RXBnCTRL_RXM_STD = 0x20;
static const uint8_t RXBnCTRL_RXM_EXT = 0x40;
static const uint8_t RXBnCTRL_RXM_STDEXT = 0x00;
static const uint8_t RXBnCTRL_RXM_MASK = 0x60;
static const uint8_t RXBnCTRL_RTR = 0x08;
static const uint8_t RXB0CTRL_BUKT = 0x04;
static const uint8_t MCP_SIDH = 0;
static const uint8_t MCP_SIDL = 1;
static const uint8_t MCP_EID8 = 2;
static const uint8_t MCP_EID0 = 3;
static const uint8_t MCP_DLC = 4;
static const uint8_t MCP_DATA = 5;
enum /*class*/ STAT : uint8_t {
STAT_RX0IF = (1 << 0),
STAT_RX1IF = (1 << 1)
};
static const uint8_t STAT_RXIF_MASK = STAT_RX0IF | STAT_RX1IF;
enum /*class*/ TXBnCTRL : uint8_t {
TXB_ABTF = 0x40,
TXB_MLOA = 0x20,
TXB_TXERR = 0x10,
TXB_TXREQ = 0x08,
TXB_TXIE = 0x04,
TXB_TXP = 0x03
};
static const uint8_t EFLG_ERRORMASK = EFLG_RX1OVR
| EFLG_RX0OVR
| EFLG_TXBO
| EFLG_TXEP
| EFLG_RXEP;
enum /*class*/ INSTRUCTION : uint8_t {
INSTRUCTION_WRITE = 0x02,
INSTRUCTION_READ = 0x03,
INSTRUCTION_BITMOD = 0x05,
INSTRUCTION_LOAD_TX0 = 0x40,
INSTRUCTION_LOAD_TX1 = 0x42,
INSTRUCTION_LOAD_TX2 = 0x44,
INSTRUCTION_RTS_TX0 = 0x81,
INSTRUCTION_RTS_TX1 = 0x82,
INSTRUCTION_RTS_TX2 = 0x84,
INSTRUCTION_RTS_ALL = 0x87,
INSTRUCTION_READ_RX0 = 0x90,
INSTRUCTION_READ_RX1 = 0x94,
INSTRUCTION_READ_STATUS = 0xA0,
INSTRUCTION_RX_STATUS = 0xB0,
INSTRUCTION_RESET = 0xC0
};
enum /*class*/ REGISTER : uint8_t {
MCP_RXF0SIDH = 0x00,
MCP_RXF0SIDL = 0x01,
MCP_RXF0EID8 = 0x02,
MCP_RXF0EID0 = 0x03,
MCP_RXF1SIDH = 0x04,
MCP_RXF1SIDL = 0x05,
MCP_RXF1EID8 = 0x06,
MCP_RXF1EID0 = 0x07,
MCP_RXF2SIDH = 0x08,
MCP_RXF2SIDL = 0x09,
MCP_RXF2EID8 = 0x0A,
MCP_RXF2EID0 = 0x0B,
MCP_PinControl = 0x0C,
MCP_CANSTAT = 0x0E,
MCP_CANCTRL = 0x0F,
MCP_RXF3SIDH = 0x10,
MCP_RXF3SIDL = 0x11,
MCP_RXF3EID8 = 0x12,
MCP_RXF3EID0 = 0x13,
MCP_RXF4SIDH = 0x14,
MCP_RXF4SIDL = 0x15,
MCP_RXF4EID8 = 0x16,
MCP_RXF4EID0 = 0x17,
MCP_RXF5SIDH = 0x18,
MCP_RXF5SIDL = 0x19,
MCP_RXF5EID8 = 0x1A,
MCP_RXF5EID0 = 0x1B,
MCP_TEC = 0x1C,
MCP_REC = 0x1D,
MCP_RXM0SIDH = 0x20,
MCP_RXM0SIDL = 0x21,
MCP_RXM0EID8 = 0x22,
MCP_RXM0EID0 = 0x23,
MCP_RXM1SIDH = 0x24,
MCP_RXM1SIDL = 0x25,
MCP_RXM1EID8 = 0x26,
MCP_RXM1EID0 = 0x27,
MCP_CNF3 = 0x28,
MCP_CNF2 = 0x29,
MCP_CNF1 = 0x2A,
MCP_CANINTE = 0x2B,
MCP_CANINTF = 0x2C,
MCP_EFLG = 0x2D,
MCP_TXB0CTRL = 0x30,
MCP_TXB0SIDH = 0x31,
MCP_TXB0SIDL = 0x32,
MCP_TXB0EID8 = 0x33,
MCP_TXB0EID0 = 0x34,
MCP_TXB0DLC = 0x35,
MCP_TXB0DATA = 0x36,
MCP_TXB1CTRL = 0x40,
MCP_TXB1SIDH = 0x41,
MCP_TXB1SIDL = 0x42,
MCP_TXB1EID8 = 0x43,
MCP_TXB1EID0 = 0x44,
MCP_TXB1DLC = 0x45,
MCP_TXB1DATA = 0x46,
MCP_TXB2CTRL = 0x50,
MCP_TXB2SIDH = 0x51,
MCP_TXB2SIDL = 0x52,
MCP_TXB2EID8 = 0x53,
MCP_TXB2EID0 = 0x54,
MCP_TXB2DLC = 0x55,
MCP_TXB2DATA = 0x56,
MCP_RXB0CTRL = 0x60,
MCP_RXB0SIDH = 0x61,
MCP_RXB0SIDL = 0x62,
MCP_RXB0EID8 = 0x63,
MCP_RXB0EID0 = 0x64,
MCP_RXB0DLC = 0x65,
MCP_RXB0DATA = 0x66,
MCP_RXB1CTRL = 0x70,
MCP_RXB1SIDH = 0x71,
MCP_RXB1SIDL = 0x72,
MCP_RXB1EID8 = 0x73,
MCP_RXB1EID0 = 0x74,
MCP_RXB1DLC = 0x75,
MCP_RXB1DATA = 0x76
};
static const int N_TXBUFFERS = 3;
static const int N_RXBUFFERS = 2;
static const struct TXBn_REGS {
REGISTER CTRL;
REGISTER SIDH;
REGISTER DATA;
} TXB[N_TXBUFFERS];
static const struct RXBn_REGS {
REGISTER CTRL;
REGISTER SIDH;
REGISTER DATA;
CANINTF CANINTF_RXnIF;
} RXB[N_RXBUFFERS];
uint8_t SPICS;
private:
void startSPI();
void endSPI();
ERROR setMode(const CANCTRL_REQOP_MODE mode);
uint8_t readRegister(const REGISTER reg);
void readRegisters(const REGISTER reg, uint8_t values[], const uint8_t n);
void setRegister(const REGISTER reg, const uint8_t value);
void setRegisters(const REGISTER reg, const uint8_t values[], const uint8_t n);
void modifyRegister(const REGISTER reg, const uint8_t mask, const uint8_t data);
void prepareId(uint8_t *buffer, const bool ext, const uint32_t id);
public:
MCP2515(const uint8_t _CS);
ERROR reset(void);
void setPin(uint8_t pinstate);
ERROR setConfigMode();
ERROR setListenOnlyMode();
ERROR setSleepMode();
ERROR setLoopbackMode();
ERROR setNormalMode();
ERROR setClkOut(const CAN_CLKOUT divisor);
ERROR setBitrate(const CAN_SPEED canSpeed);
ERROR setBitrate(const CAN_SPEED canSpeed, const CAN_CLOCK canClock);
ERROR setFilterMask(const MASK num, const bool ext, const uint32_t ulData);
ERROR setFilter(const RXF num, const bool ext, const uint32_t ulData);
ERROR sendMessage(const TXBn txbn, const struct can_frame *frame);
ERROR sendMessage(const struct can_frame *frame);
ERROR readMessage(const RXBn rxbn, struct can_frame *frame);
ERROR readMessage(struct can_frame *frame);
bool checkReceive(void);
bool checkError(void);
uint8_t getErrorFlags(void);
void clearRXnOVRFlags(void);
uint8_t getInterrupts(void);
uint8_t getInterruptMask(void);
void clearInterrupts(void);
void clearTXInterrupts(void);
uint8_t getStatus(void);
void clearRXnOVR(void);
void clearMERR();
void clearERRIF();
};
#endif
Re: Der AVR-/ARDUINO-Faden
Ja dafür gibt es das Design-Pattern Singleton.
Es wird genau nur eine Instanz erzeugt.
Jede weitere ist nur eine Kopie der Ur-Instanz.
Jedes C++ Handbuch oder Google verrät Dir wie die 2 Hand voll Codezeilen heißen.
Grüße Jan
- Bastelbruder
- Beiträge: 11550
- Registriert: Mi 14. Aug 2013, 18:28
Re: Der AVR-/ARDUINO-Faden
Aktiv Low ist bei TTL und 74HC immer noch gültig, genau wie der Takt die fallende Flanke beschreibt. Die ansteigende Flanke ist der Tickt, Jetzt wird hoffentlich auch klar warum die Uhr Tick-Tack macht.