Der AVR-/ARDUINO-Faden

Der chaotische Hauptfaden

Moderatoren: Heaterman, Finger, Sven, TDI, Marsupilami72, duese

Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Der AVR-/ARDUINO-Faden

Beitrag von Hightech »

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?

hier fehlt auch das 0xff dazwischen

Bild

Aber wie bekomme ich den CS-Pin jetzt in der conti_mpc2515 Lib geschriben?? Darum gings es ja eigentlich.
jay
Beiträge: 55
Registriert: Di 20. Sep 2022, 12:45

Re: Der AVR-/ARDUINO-Faden

Beitrag von jay »

Hat Jemand eine empfehlenswerte Typenliste von Standardbauteilen die man für Arduino Frickelei Herumliegen haben sollte?
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. :oops:
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Der AVR-/ARDUINO-Faden

Beitrag von Hightech »

Also gut denn, nun wird es haarig:

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.....
Benutzeravatar
ProgBernie
Beiträge: 584
Registriert: Fr 16. Sep 2022, 21:59
Wohnort: Zwischen Hamburg und Haiti ^W Lübeck

Re: Der AVR-/ARDUINO-Faden

Beitrag von ProgBernie »

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?
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
Kenakapheus
Beiträge: 173
Registriert: Fr 1. Jan 2016, 20:43
Wohnort: Freie Feldlage (Ja, da wo das Treffen ist))

Re: Der AVR-/ARDUINO-Faden

Beitrag von Kenakapheus »

Hightech hat geschrieben: Fr 6. Jan 2023, 07:29 ... Jetzt muss ich die aber in die Funktionen und Klassen weiterleiten, ...
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)
}
funktionieren.
(Das ist jetzt aus dem Stegreif kann also Syntaxfehler enthalten)

Erklärung dazu:
Das

Code: Alles auswählen

PCF8574 *pcf
bedeutet das die Funktion einen Pointer zu einer PCF8574 Instanz übergeben bekommt.
Mit

Code: Alles auswählen

pcf->write(pin,state)
wird die Funktion write der übergebenen Instanz aufgerufen.

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
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Der AVR-/ARDUINO-Faden

Beitrag von Hightech »

ProgBernie 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
Exakt
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Der AVR-/ARDUINO-Faden

Beitrag von Hightech »

Vielen Dank schonmal an alle, es ist leicht mit mir.

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:34
Hightech hat geschrieben: Fr 6. Jan 2023, 07:29 ... Jetzt muss ich die aber in die Funktionen und Klassen weiterleiten, ...
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)
}
funktionieren.
(Das ist jetzt aus dem Stegreif kann also Syntaxfehler enthalten)

Erklärung dazu:
Das

Code: Alles auswählen

PCF8574 *pcf
bedeutet das die Funktion einen Pointer zu einer PCF8574 Instanz übergeben bekommt.
Mit

Code: Alles auswählen

pcf->write(pin,state)
wird die Funktion write der übergebenen Instanz aufgerufen.

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
Kenakapheus
Beiträge: 173
Registriert: Fr 1. Jan 2016, 20:43
Wohnort: Freie Feldlage (Ja, da wo das Treffen ist))

Re: Der AVR-/ARDUINO-Faden

Beitrag von Kenakapheus »

Die Initialisierung bleib wie vorher:

Code: Alles auswählen

PCF8574 pcf(0x26);


void setup() {
	...
	pcf.begin();
	...
}
Der Aufruf wird dann zu:

Code: Alles auswählen

i2c_datasend(&pcf,1,0);
Das '&' macht einen pointer aus daraus, ohne das würde eine komplette Kopie Erzeugt werden.

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];
Das müsste jetzt so umgebaut werden das das 'akku' array am besten direkt die PCF8574 Objekte enthält:

Code: Alles auswählen

typedef struct akku_s {
	PCF8574 pcf,
	int toggle_pin
} akku_t;

Die Initialisierung sollte dann direkt darin passieren können (werte sind jetzt ausgedacht):

Code: Alles auswählen

akku_t akku[8] = [
{.pcf = pcf(0x26), .toggle_pin = 2},
{.pcf = pcf(0x27), .toggle_pin = 3},
...
];
Eh, eventuell erzähle ich da aber auch gerade quatsch, mein C++ ist etwas eingerostet.

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
Zuletzt geändert von Kenakapheus am Fr 6. Jan 2023, 14:33, insgesamt 1-mal geändert.
Kenakapheus
Beiträge: 173
Registriert: Fr 1. Jan 2016, 20:43
Wohnort: Freie Feldlage (Ja, da wo das Treffen ist))

Re: Der AVR-/ARDUINO-Faden

Beitrag von Kenakapheus »

(Sorry, Doppelpost)
ch_ris
Beiträge: 3029
Registriert: Mo 30. Nov 2015, 10:08

Re: Der AVR-/ARDUINO-Faden

Beitrag von ch_ris »

gibts da kein Konstruktor um ohne parameter zu init. und dann per Methode den pin zu setzen?
dann bräuchst nur eine Instanz?
nur ne idee ich weis ja nicht was du da vorhast/ ob die Instanzen nötig sind.
ch_ris
Beiträge: 3029
Registriert: Mo 30. Nov 2015, 10:08

Re: Der AVR-/ARDUINO-Faden

Beitrag von ch_ris »

doppelt
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Der AVR-/ARDUINO-Faden

Beitrag von Hightech »

Ich habs mal soweit zusammen gelötet das der compiler nicht mehr weint.

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);
Akku struct

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);
  }
i2cdatasend

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);
}
loop

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);

Jannyboy
Beiträge: 1406
Registriert: So 11. Aug 2013, 14:49
Wohnort: Kreis Augsburg

Re: Der AVR-/ARDUINO-Faden

Beitrag von Jannyboy »

Hightech hat geschrieben: Fr 6. Jan 2023, 23:04 Wie bekomme ich in der MCP2515.cpp den CS Pin über den PCF gesteuert?
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
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Der AVR-/ARDUINO-Faden

Beitrag von Hightech »

Davon verstehe ich zu wenig. Vielleicht nehme ich den Holzhammer und mache es so:

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?
Benutzeravatar
ProgBernie
Beiträge: 584
Registriert: Fr 16. Sep 2022, 21:59
Wohnort: Zwischen Hamburg und Haiti ^W Lübeck

Re: Der AVR-/ARDUINO-Faden

Beitrag von ProgBernie »

Hightech hat geschrieben: Sa 7. Jan 2023, 14:06 Davon verstehe ich zu wenig. Vielleicht nehme ich den Holzhammer und mache es so:
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).
Hightech hat geschrieben: Sa 7. Jan 2023, 14:06 Wobei, kann ich alle 24 MCP2515 gleichzeitig initialisieren, wenn ich alle CS pins LOW ziehe und dann das Init sende, bzw den Reset?
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
Benutzeravatar
Toni
Beiträge: 2523
Registriert: Di 13. Aug 2013, 18:24

Re: Der AVR-/ARDUINO-Faden

Beitrag von Toni »

Habe generelle Fragen zu den Fuses beim ATTINY13A

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?
Jannyboy
Beiträge: 1406
Registriert: So 11. Aug 2013, 14:49
Wohnort: Kreis Augsburg

Re: Der AVR-/ARDUINO-Faden

Beitrag von Jannyboy »

Toni hat geschrieben: So 8. Jan 2023, 00:22 Wie kann ich die FUSE-Werte ändern?

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)?

Sind die ATTINYS jetzt noch zu retten?
Hier findest du die Antworten auf deine Fragen ;)
https://www.mikrocontroller.net/articles/AVR_Fuses

Grüße Jan
Benutzeravatar
Bastelbruder
Beiträge: 11482
Registriert: Mi 14. Aug 2013, 18:28

Re: Der AVR-/ARDUINO-Faden

Beitrag von Bastelbruder »

Wenn so ein Controller "verfust" ist, bedeutet das im Allgemeinen daß die Takterzeugung nicht mehr läuft. Und ohne Takt gehts nicht. Aber es gibt da einen Trick, ein Kollege hat die Zusatzschaltung Defi genannt. :D Ein Oszillator mit mindestens 100 kHz, der mit 100 pF an den Takteingang gekoppelt wird. Das darf ein Oszillator im DIL-Gehäuse sein oder auch ein 555 mit Minimalbeschaltung.
Benutzeravatar
Toni
Beiträge: 2523
Registriert: Di 13. Aug 2013, 18:24

Re: Der AVR-/ARDUINO-Faden

Beitrag von Toni »

Danke, das hilft mir schonmal weiter, auch wenn ich auf der Microcontroller-Seite noch nicht alles verstanden habe :?
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 :)
DocDan
Beiträge: 69
Registriert: Mo 15. Jun 2015, 17:17
Wohnort: Enzkreis

Re: Der AVR-/ARDUINO-Faden

Beitrag von DocDan »

Moin,

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.
ch_ris
Beiträge: 3029
Registriert: Mo 30. Nov 2015, 10:08

Re: Der AVR-/ARDUINO-Faden

Beitrag von ch_ris »

Nee, braucht kein HV.
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)
2023-01-08 08_37_58-ATtiny13 Fuses.png
einfach Finger weg von den Roten, dann passiert nix schlimmes.
Benutzeravatar
Toni
Beiträge: 2523
Registriert: Di 13. Aug 2013, 18:24

Re: Der AVR-/ARDUINO-Faden

Beitrag von Toni »

OK, damit sollte alles klar sein :)

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 :D
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Der AVR-/ARDUINO-Faden

Beitrag von Hightech »

Ich renne immer gegen alle Wände.
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;
    }
    }
  }
}


conti.h

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);

}
mcp2515.cpp

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



[/code]
Jannyboy
Beiträge: 1406
Registriert: So 11. Aug 2013, 14:49
Wohnort: Kreis Augsburg

Re: Der AVR-/ARDUINO-Faden

Beitrag von Jannyboy »

Hightech hat geschrieben: So 8. Jan 2023, 20:09 Man darf aber immer nur eine Instanz je PCF8574 haben, denn wenn man den immer neu initialisiert, gehen alle Pins des PCF8574 auf High.
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
Benutzeravatar
Bastelbruder
Beiträge: 11482
Registriert: Mi 14. Aug 2013, 18:28

Re: Der AVR-/ARDUINO-Faden

Beitrag von Bastelbruder »

"High" war in der Digitaltechnik Jahrzehnte lang der Ruhepegel. Die Regel hat sich wurde geändert unterwandert als die Maker-Riege ohne Bezug zur Hardware aus Dummheit Ignoranz die Regeln geändert hat. So hat man das eben halt.

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. :lol:
ch_ris
Beiträge: 3029
Registriert: Mo 30. Nov 2015, 10:08

Re: Der AVR-/ARDUINO-Faden

Beitrag von ch_ris »

langer text, timeout, text weg :(
deshalb jetzt ganz kurz:
warum ist mein Vorschlag scheise, 1 can modul per expander (kascade) auf die 24 akkus zu verteilen?
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Der AVR-/ARDUINO-Faden

Beitrag von Hightech »

ch_ris hat geschrieben: Mo 9. Jan 2023, 07:04 langer text, timeout, text weg :(
deshalb jetzt ganz kurz:
warum ist mein Vorschlag scheise, 1 can modul per expander (kascade) auf die 24 akkus zu verteilen?
Weil die Teilnehmer auf dem CAN-BUS regelmäßig Daten senden. Die werden dann übersehen, und alle Teilnehmer benötigen alle 100ms einen Datensatz damit die wach bleiben.
Das CAN-Modul puffert die Daten und kann auch empfangen wenn der MC grade was anderes macht.
Der MCP2515 kann sogar einen Interrupt erzeugen auf einen bestimmten Inhalt.
ch_ris
Beiträge: 3029
Registriert: Mo 30. Nov 2015, 10:08

Re: Der AVR-/ARDUINO-Faden

Beitrag von ch_ris »

danke für die Erklärung, dachte die sollen nur gepollt werden.
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Der AVR-/ARDUINO-Faden

Beitrag von Hightech »

Gibt es in Visual Code / Platformio eine Möglichkeit Inhalte der Zeiger/ Klassen/Variablen sichtbar zu machen? So das ich dann sehe wo zB ein Zeiger hin geht und das es ein Zeiger ist?
Kenakapheus
Beiträge: 173
Registriert: Fr 1. Jan 2016, 20:43
Wohnort: Freie Feldlage (Ja, da wo das Treffen ist))

Re: Der AVR-/ARDUINO-Faden

Beitrag von Kenakapheus »

Meinst du jetzt die Adresse anzeigen?
Oder willst du wissen auf welche spezifische variable ein pointer Zeigt?
Zweiteres wird vermutlich nicht gehen, a das IDE dafür den gesamten Code simulieren müsste um zu wissen wo welcher pointer wann hin zeigt.

- Phiona
Kenakapheus
Beiträge: 173
Registriert: Fr 1. Jan 2016, 20:43
Wohnort: Freie Feldlage (Ja, da wo das Treffen ist))

Re: Der AVR-/ARDUINO-Faden

Beitrag von Kenakapheus »

Hightech hat geschrieben: So 8. Jan 2023, 20:09 Ich renne immer gegen alle Wände.
Vielleicht schaut jemand mal rein?
...
Ich habe mir das mal angesehen und versucht so umzubauen wie ich die Aufgabe verstehe:

Mein Konzept ist das jeder MCP2515 ein eigenes Objekt bekommt, welches schon den Pointer zum PCF8574 und die Pin Nummer kennt und sich darum kümmerst den CS pin zu setzen.
Nachteil davon ist das es relativ viel Speicher benötigt und nicht ohne weiteres mit Interrupts funktioniert.
Außerdem wird es so etwas mehr Aufwand Broadcast zu machen, daher habe ich das erstmal gelassen.
das Interrupt Problem existiert aber in jedem Fall und lässt sich potentiell lösen indem die SPI Transfers Atomic gemacht werden.
(Also zu Begin Interrupt deaktivieren und danach wieder Aktivieren)

Ich hoffe der Code ist halbwegs verständlich.

- Phiona

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);

    // init port expanders
    pcf_01.begin();
    pcf_02.begin();
    pcf_03.begin();
    pcf_04.begin();
    pcf_05.begin();
    pcf_06.begin();
    
    // init each mcp2515
    for (int a = 0; a < 24; a += 1)
    {
        Serial.print("Start MCP2515 ");
        Serial.println(a, 10);
        akku[a].mcp.setBitrate(CAN_250KBPS);
        akku[a].mcp.setNormalMode();
        akku[a].mcp.reset();
    }

}

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();
        // send keepalive to each
        for (int a = 0; a < 24; a += 1)
        {
            sendCAN(&akku[a].mcp, 0x201, 4, 0, 1, 0, 0, 0, 0, 0, 0);
        }
    }
    // check all mcp2515 for errors and new messages
    for (int a = 0; a < 24; a += 1)
    {
        if (akku[a].mcp.getErrorFlags() == 0x15)
        {   // On Can Error 0x15 restart CAN & Toggle Akku-DATA+
            Serial.println("CAN-Error 0x15, CAN-RESET & Toggle Data+");
            akku[a].mcp.reset();
            akku[a].mcp.setBitrate(CAN_250KBPS);
            akku[a].mcp.setNormalMode();
        }

        if (akku[a].mcp.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;
                }
            }
        }
    }
}
conti.h:

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);

int Akku_Nr = 0;

void i2c_datasend(const 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 {

    private:

        const PCF8574 *PCF;
        int CS;

    public:

        MCPCONTI(const PCF8574 *_PCF, const uint8_t _CS) : MCP2515(_CS)
        {
            CS = _CS;
            PCF = _PCF;
            endSPI();
        }

        void startSPI() {
            SPI.beginTransaction(SPISettings(SPI_CLOCK, MSBFIRST, SPI_MODE0));
            i2c_datasend(PCF, CS, 0);
        }

        void endSPI() {
            i2c_datasend(PCF, CS, 1);
            SPI.endTransaction();
        }
};

typedef struct
{
  MCPCONTI mcp;
  int toggle_pin;
  int intr_pin;
} akku_t;

akku_t akku[24] = {
    {.mcp = MCPCONTI(&pcf_01, 3), 0, 49},
    {.mcp = MCPCONTI(&pcf_01, 2), 1, 48},
    {.mcp = MCPCONTI(&pcf_01, 5), 6, 47},
    {.mcp = MCPCONTI(&pcf_01, 4), 7, 46},
    {.mcp = MCPCONTI(&pcf_02, 1), 4, 45},
    {.mcp = MCPCONTI(&pcf_02, 3), 5, 44},
    {.mcp = MCPCONTI(&pcf_02, 2), 6, 43},
    {.mcp = MCPCONTI(&pcf_02, 0), 7, 42},
    {.mcp = MCPCONTI(&pcf_03, 3), 0, 30},
    {.mcp = MCPCONTI(&pcf_03, 2), 1, 31},
    {.mcp = MCPCONTI(&pcf_03, 5), 6, 32},
    {.mcp = MCPCONTI(&pcf_03, 4), 7, 33},
    {.mcp = MCPCONTI(&pcf_04, 1), 4, 34},
    {.mcp = MCPCONTI(&pcf_04, 3), 5, 35},
    {.mcp = MCPCONTI(&pcf_04, 2), 6, 36},
    {.mcp = MCPCONTI(&pcf_04, 0), 7, 37},
    {.mcp = MCPCONTI(&pcf_05, 3), 0, 22},
    {.mcp = MCPCONTI(&pcf_05, 2), 1, 23},
    {.mcp = MCPCONTI(&pcf_05, 5), 6, 24},
    {.mcp = MCPCONTI(&pcf_05, 4), 7, 25},
    {.mcp = MCPCONTI(&pcf_06, 1), 4, 26},
    {.mcp = MCPCONTI(&pcf_06, 3), 5, 27},
    {.mcp = MCPCONTI(&pcf_06, 2), 6, 28},
    {.mcp = MCPCONTI(&pcf_06, 0), 7, 29}
    };

void sendCAN(MCPCONTI *mcp,  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;
  mcp->sendMessage(MCPCONTI::TXB1, &canMsg1);

}
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Der AVR-/ARDUINO-Faden

Beitrag von Hightech »

Vielen lieben Dank für deine Mühe!!!
Ich versuche das mal zu lesen und zumindest grob zu verstehen.

Lg
Boris
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Der AVR-/ARDUINO-Faden

Beitrag von Hightech »

Interessant:
Der code wird compiliert, mit Warnungen. Wenn ich den code hier mit drin habe, hängt die CPU direkt, lasse ich es weg läuft die CPU und sendet zumindest Serial Daten.



Code: Alles auswählen

typedef struct
{
  MCPCONTI mcp;
  int toggle_pin;
  int intr_pin;
} akku_t;

akku_t akku[24] = {
    {.mcp = MCPCONTI(&pcf_01, 3), 0, 49},
    {.mcp = MCPCONTI(&pcf_01, 2), 1, 48},
    {.mcp = MCPCONTI(&pcf_01, 5), 6, 47},
    {.mcp = MCPCONTI(&pcf_01, 4), 7, 46},
    {.mcp = MCPCONTI(&pcf_02, 1), 4, 45},
    {.mcp = MCPCONTI(&pcf_02, 3), 5, 44},
    {.mcp = MCPCONTI(&pcf_02, 2), 6, 43},
    {.mcp = MCPCONTI(&pcf_02, 0), 7, 42},
    {.mcp = MCPCONTI(&pcf_03, 3), 0, 30},
    {.mcp = MCPCONTI(&pcf_03, 2), 1, 31},
    {.mcp = MCPCONTI(&pcf_03, 5), 6, 32},
    {.mcp = MCPCONTI(&pcf_03, 4), 7, 33},
    {.mcp = MCPCONTI(&pcf_04, 1), 4, 34},
    {.mcp = MCPCONTI(&pcf_04, 3), 5, 35},
    {.mcp = MCPCONTI(&pcf_04, 2), 6, 36},
    {.mcp = MCPCONTI(&pcf_04, 0), 7, 37},
    {.mcp = MCPCONTI(&pcf_05, 3), 0, 22},
    {.mcp = MCPCONTI(&pcf_05, 2), 1, 23},
    {.mcp = MCPCONTI(&pcf_05, 5), 6, 24},
    {.mcp = MCPCONTI(&pcf_05, 4), 7, 25},
    {.mcp = MCPCONTI(&pcf_06, 1), 4, 26},
    {.mcp = MCPCONTI(&pcf_06, 3), 5, 27},
    {.mcp = MCPCONTI(&pcf_06, 2), 6, 28},
    {.mcp = MCPCONTI(&pcf_06, 0), 7, 29}
    };
*/


Fehler:

Code: Alles auswählen

mpiling .pio/build/sparkfun_megapro16MHz/libc0b/PCF8574/PCF8574.cpp.o
src/main.cpp: In function 'void i2c_datasend(const PCF8574*, int, bool)':
src/main.cpp:25:35: warning: passing 'const PCF8574' as 'this' argument discards qualifiers [-fpermissive]
   if (!(status = pcf->isConnected()))
                                   ^
In file included from src/main.cpp:4:0:
.pio/libdeps/sparkfun_megapro16MHz/PCF8574/PCF8574.h:37:11: note:   in call to 'bool PCF8574::isConnected()'
   bool    isConnected();
           ^~~~~~~~~~~
src/main.cpp:30:22: warning: passing 'const PCF8574' as 'this' argument discards qualifiers [-fpermissive]
   if (pcf->lastError())
                      ^
In file included from src/main.cpp:4:0:
.pio/libdeps/sparkfun_megapro16MHz/PCF8574/PCF8574.h:80:11: note:   in call to 'int PCF8574::lastError()'
   int     lastError();
           ^~~~~~~~~
src/main.cpp:33:36: warning: passing 'const PCF8574' as 'this' argument discards qualifiers [-fpermissive]
     Serial.println(pcf->getAddress());
                                    ^
In file included from src/main.cpp:4:0:
.pio/libdeps/sparkfun_megapro16MHz/PCF8574/PCF8574.h:43:11: note:   in call to 'uint8_t PCF8574::getAddress()'
   uint8_t getAddress();  
           ^~~~~~~~~~
src/main.cpp:36:24: warning: passing 'const PCF8574' as 'this' argument discards qualifiers [-fpermissive]
   pcf->write(pin, state);
                        ^
In file included from src/main.cpp:4:0:
.pio/libdeps/sparkfun_megapro16MHz/PCF8574/PCF8574.h:52:11: note:   in call to 'void PCF8574::write(uint8_t, uint8_t)'
   void    write(const uint8_t pin, const uint8_t value)
Benutzeravatar
Toni
Beiträge: 2523
Registriert: Di 13. Aug 2013, 18:24

Re: Der AVR-/ARDUINO-Faden

Beitrag von Toni »

Toni hat geschrieben: So 8. Jan 2023, 11:29 OK, damit sollte alles klar sein :)

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 :D
Ich wollte dazu noch eine Rückmeldung geben:
Bastelbruder hat geschrieben: So 8. Jan 2023, 01:04 Wenn so ein Controller "verfust" ist, bedeutet das im Allgemeinen daß die Takterzeugung nicht mehr läuft. Und ohne Takt gehts nicht. Aber es gibt da einen Trick, ein Kollege hat die Zusatzschaltung Defi genannt. :D Ein Oszillator mit mindestens 100 kHz, der mit 100 pF an den Takteingang gekoppelt wird. Das darf ein Oszillator im DIL-Gehäuse sein oder auch ein 555 mit Minimalbeschaltung.
Das funktioniert! Ich habe jetzt standardmäßig einen Quarzoszillator mit C=470pF in Reihe am Programmieradapter, der bei Bedarf an den CLKI-Pin gejumpert werden kann.

Der Burn-O-Mat funktionierte bei mir nicht mit Ubuntu, dafür aber der AVRDUDESS über MONO. Ich kann Fuses auslesen und beschreiben. Testweises Verstellen der CKSEL-Fuses führte auch prompt dazu, dass der Tiny nicht mehr ansprechbar war, auch nicht mit externer Taktquelle. Mit dem Defi war das aber rückgängig zu machen???
War ein externes Problem: mit C=100nF direkt an den Versorgungspins funktionierte es auch mit dem externen Takt.

Noch ein Hinweis: wenn über CKSEL 128kHz intern eingestellt wird, muss zum Ansprechen mit AVRDUDE die Datenrate stark runtergedreht werden. 16kHz funktioniert.

Die gesammelten verfusten Tinys konnte ich zurücksetzen :D
ch_ris
Beiträge: 3029
Registriert: Mo 30. Nov 2015, 10:08

Re: Der AVR-/ARDUINO-Faden

Beitrag von ch_ris »

der burn mat unter linux...irgendwas war da, habs vergessen wie ich das hingekriegt hab. man könnte ihn mal per konsole starten bei Interesse.
Benutzeravatar
Toni
Beiträge: 2523
Registriert: Di 13. Aug 2013, 18:24

Re: Der AVR-/ARDUINO-Faden

Beitrag von Toni »

starten ging, aber der ProgAdapter (USBasp) war nicht erreichbar. Mit AVRDUDESS lief es auf Anhieb
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Der AVR-/ARDUINO-Faden

Beitrag von Hightech »

Sehr seltsam das der MC abkackt, wenn ich das hier hochlade.


akku_t akku[24] = {
{.mcp = MCPCONTI(&pcf_01, 3), 0, 49},



Ich habe diesen Abschnitt für die PCF Lib gefunden

Code: Alles auswählen

bool setAddress(const uint8_t deviceAddress) sets the device address after construction. 
Can be used to switch between PCF8574 modules runtime. Note this corrupts internal buffered values, so one might need to call read8() and/or write8(). 
Returns true if address can be found on I2C bus.
Das müsste ja bedeuten, ich kann auf verschiedenen PCFs herumschreiben, ohne das die neu initialisiert werden, so wie ich das beim ersten mal gemacht hatte.
Also einen nur einen Constructor für die PCF und dann nur die Adressen umbiegen.
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Der AVR-/ARDUINO-Faden

Beitrag von Hightech »

Also das mit den Klassen....
klappt nicht. Ich muss das mal an einem Testprogramm versuchen.

Mal hierzu,

Code: Alles auswählen

class HighTechMCP2515 : public MCP2515 {

void startSPI() {
	SPIn->beginTransaction(SPISettings(SPI_CLOCK, MSBFIRST, SPI_MODE0));
	i2c_datasend(..., 3, 0);
	}

	void endSPI() {
	i2c_datasend(..., 3, 1);
	SPIn->endTransaction();
}
}
Wenn ich das richtig verstanden habe, kann ich damit zwar mit
HighTechMCP2515 htmcp2515;

htmcp2515.startSPI
die neue Funktion benutzen, und die MCP2515 Funktion startSPI wird überschrieben, aber wenn von der MCP2515 Klasse eine Funktion die Funktion startSPI aufruft, wird wieder die original Funktion benutzt oder?
Benutzeravatar
ProgBernie
Beiträge: 584
Registriert: Fr 16. Sep 2022, 21:59
Wohnort: Zwischen Hamburg und Haiti ^W Lübeck

Re: Der AVR-/ARDUINO-Faden

Beitrag von ProgBernie »

Hightech hat geschrieben: Fr 13. Jan 2023, 20:29
[überladene Methode]

Wenn ich das richtig verstanden habe, kann ich damit zwar mit
HighTechMCP2515 htmcp2515;

htmcp2515.startSPI
die neue Funktion benutzen, und die MCP2515 Funktion startSPI wird überschrieben, aber wenn von der MCP2515 Klasse eine Funktion die Funktion startSPI aufruft, wird wieder die original Funktion benutzt oder?
Ich habe mir gerade den Sourcecode von MCP2515 angesehen. Du hast Recht, die Klasse ist nicht darauf vorbereitet abgeleitet zu werden, dazu müssten die Methoden virtual sein. Eine der vielen Fallgruben in C++.
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Der AVR-/ARDUINO-Faden

Beitrag von Hightech »

Ich geb auf. Da bin ich ja 2525 noch dran.
Also fädel ich jetzt um, der CS kommt hart an den Arduino, der INT dann halt über den PCF. Den Int brauche ich nicht so dringend.
Benutzeravatar
Weisskeinen
Beiträge: 3942
Registriert: Di 27. Aug 2013, 16:19

Re: Der AVR-/ARDUINO-Faden

Beitrag von Weisskeinen »

Andere Library nehmen oder Library ganz verschrotten und zu Fuß programmieren geht nicht?
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Der AVR-/ARDUINO-Faden

Beitrag von Hightech »

Ich denke, bei 24 Akkus die gleichzeitig wild herum senden wird es echt wahnsinnig kniffelig, die dann per SPI und I2C gleichzeitig zu verhackstücken.
Zudem hab ich schon bei 4 Akkus gleichzeitig schon Probleme, die korrekt zu initialisieren. Ich muss die ja per Hardware-Toggle aufwecken, schauen das die auch laufen, wenn nicht dann nochmal wecken, timeout, wenn es zu lange dauert, timeout-counter das es nicht für immer hängen bleibt......

So komme ich dann zur Einsicht, das ich wohl je Akku einen MC nehme und den dann schlicht Modbus-RTU sprechen lasse.

Ich kenn da jemand der so ModBus macht, allerdings aufm STM und nicht auf AVR, aber vielleicht kann ich den ja überreden, mir was schickes kleines in Modbus zu frickeln.
Jannyboy
Beiträge: 1406
Registriert: So 11. Aug 2013, 14:49
Wohnort: Kreis Augsburg

Re: Der AVR-/ARDUINO-Faden

Beitrag von Jannyboy »

Hightech hat geschrieben: Di 17. Jan 2023, 20:13 Ich denke, bei 24 Akkus die gleichzeitig wild herum senden wird es echt wahnsinnig kniffelig,
CAN-Bus ist doch genau dafür gemacht.
Daher verstehe ich nicht warum die Verrenkungen mit den einzelnen CAN-Bausteine und Port-Expandern.
Ich Auto quatschen auch zig Steuergeräte via CAN und es gibt kaum Kollisionen.

Grüße Jan
Benutzeravatar
sukram
Beiträge: 3063
Registriert: Sa 10. Mär 2018, 18:27
Wohnort: Leibzsch

Re: Der AVR-/ARDUINO-Faden

Beitrag von sukram »

Weil die Akkus blöd wie 10m Feldweg sind und alle auf die gleiche Adresse ansprechem...
Benutzeravatar
Fritzler
Beiträge: 12579
Registriert: So 11. Aug 2013, 19:42
Wohnort: D:/Berlin/Adlershof/Technologiepark
Kontaktdaten:

Re: Der AVR-/ARDUINO-Faden

Beitrag von Fritzler »

Hightech hat geschrieben: Di 17. Jan 2023, 20:13 Ich kenn da jemand der so ModBus macht, allerdings aufm STM und nicht auf AVR, aber vielleicht kann ich den ja überreden, mir was schickes kleines in Modbus zu frickeln.
Ich?
Modbus hatte ich zuerst aufm AVR geshrieben und dann auf STM32 portiert und dann gleichzeitig nach C++ für eine bessere Abstraktion zwischen Modbus Logik und UART HW-
Hier die alte AVR Grütze im Anhang, angaben ohne Gewehr.

Neue Grütze:
https://git.fritzler-avr.de/fritzler/modbus-slave
Dateianhänge
mb_remotemode.h
(868 Bytes) 19-mal heruntergeladen
mb_remotemode.c
(4.32 KiB) 18-mal heruntergeladen
mb_process.h
(312 Bytes) 17-mal heruntergeladen
mb_process.c
(9.22 KiB) 17-mal heruntergeladen
mb_menu.h
(1.34 KiB) 18-mal heruntergeladen
mb_menu.c
(7.19 KiB) 18-mal heruntergeladen
mb_defs.h
(1.27 KiB) 17-mal heruntergeladen
mb_crc.h
(448 Bytes) 18-mal heruntergeladen
mb_crc.c
(1.34 KiB) 18-mal heruntergeladen
mb_callbacks.h
(392 Bytes) 18-mal heruntergeladen
mb_callbacks.c
(3.07 KiB) 20-mal heruntergeladen
Kenakapheus
Beiträge: 173
Registriert: Fr 1. Jan 2016, 20:43
Wohnort: Freie Feldlage (Ja, da wo das Treffen ist))

Re: Der AVR-/ARDUINO-Faden

Beitrag von Kenakapheus »

Ich kann sonst auch das hier sehr Empfehlen:

https://github.com/mbs38/yaMBSiavr

Das geht allerdings von komplett Baremetal aus.
Benutzeravatar
sukram
Beiträge: 3063
Registriert: Sa 10. Mär 2018, 18:27
Wohnort: Leibzsch

Re: Der AVR-/ARDUINO-Faden

Beitrag von sukram »

Kenakapheus hat geschrieben: Di 17. Jan 2023, 21:38 Ich kann sonst auch das hier sehr Empfehlen:

https://github.com/mbs38/yaMBSiavr

Das geht allerdings von komplett Baremetal aus.
Darauf aufbauend habe ich meine 1Wire - Modbus Adapter gebaut. Die funktionieren mittlerweile sehr zufriedenstellend. Das wollte ich mal noch etwas "schön machen" (Race Conditions und RAM Verschwendung entfernen und den I2C BME280 non-blocking einbinden...) und dann hier vorstellen. Momentan sieht der Code nach Frankensteins Monster aus. Aus 3 oder 4 Quellen zusammengeklaut (1W von uCnet, Modbus von yaMBSiavr, BME von Bosch & Fritzler...) und mit heißer Nadel zusammengestrickt.
Benutzeravatar
ProgBernie
Beiträge: 584
Registriert: Fr 16. Sep 2022, 21:59
Wohnort: Zwischen Hamburg und Haiti ^W Lübeck

Re: Der AVR-/ARDUINO-Faden

Beitrag von ProgBernie »

Jannyboy hat geschrieben: Di 17. Jan 2023, 20:30 Ich Auto quatschen auch zig Steuergeräte via CAN und es gibt kaum Kollisionen.
Das geht nur weil jede blöde Birne in dem Käfig einen eigenen speziellen Controller hat der die Frames mit eindeutigen Inhalten verhackstückt. Es gibt bei CAN ja keine adressierbaren Komponenten, lediglich Nachrichtentypen. Hätte nicht wirklich jedes Teil einen eigenen Controller würde es Dir überhaupt nichts nützen wenn da sowas wie "Birne defekt" auf dem Bus ankommt. Man weiss ja nicht welche. So ist das auch bei den Akkus, die senden alle brav ihre Nachricht "Akkuspannung 39.75V", aber man hat dann keinen Schimmer von welchem Akku das kommt. Das Conti-System war ja nie dafür gemacht mehr als einen Akku am Rad spazieren zu fahren.
Ka6Pilot
Beiträge: 10
Registriert: Mi 29. Jan 2020, 22:43

Attiny 412 (tinyAVR) SPI ausschalten

Beitrag von Ka6Pilot »

Hallo in die Runde,

ich bin gerade dabei, mit einem Attiny 412 und einem MAX31855 herumzuexperimentieren. Der Attiny412 soll dabei regelmäßig den MAX31855 mit Strom versorgen und prüfen, ob sich der (TC+)-Pin (SENSE) mit einem Pull-Up-Widerstand auf HIGH ziehen lässt. Wenn das nicht der Fall ist, wird eine Messung gestartet. Anschließend wird die Stromversorgung zum MAX31855 wieder unterbrochen und die SCK- und CS-Leitung werden als INPUT definiert, um den MAX31855 nicht darüber mit Strom zu versorgen.
Später soll ein µC mit mehr Pins zum Einsatz kommen und das Messergebnis auf eine SD-Karte geschrieben werden.
Nun zum Problem: Initiiert man den MAX31855 mit Software-SPI, funktioniert der Aufbau problemlos (Bild 1; Gelb: SCK; Blau: SO). Mit Hardware-SPI kommt bei ansonsten unverändertem Code überhaupt keine Übertragung zustande (Bild 2). Lässt man in Zeile 38 in dem anghängten Code das

Code: Alles auswählen

pinMode(2, OUTPUT); 
weg, funktioniert die Übertragung gelegentlich (Bild 3).
Gibt es eine Möglichkeit, das Hardware-SPI zu verwenden und die entspechenden Pins bei Nichtgebrauch hochohmig zu schalten?
Viele Grüße

Untenstehend ist der Programmcode, im Anhang drei Oszillogramme sowie der Schaltplan.

Code: Alles auswählen

/*
* ATTINY412  Pinout Convention: 
 *               ______
 *          VCC-|1    8|-GND
 * D0 PA6(TX)---|2    7|-PA3(SCK)  D4
 * D1 PA7(RX)---|3    6|-PA0(UPDI) D5
 * D2 PA1(MOSI)-|4____5|-PA2(MISO) D3
 */
 
#include <SPI.h>
#include <avr/sleep.h>
#include "Adafruit_MAX31855.h"

#define MAXDO   3     // software SPI
#define MAXCLK  4     // software SPI

#define MAXCS   2     // ADC Chip Select  
int VADC  = 1;        // ADC Power Supply
int SENSE = 0;        // ADC TC+ Input

Adafruit_MAX31855 thermocouple(MAXCLK, MAXCS, MAXDO); // software SPI
//Adafruit_MAX31855 thermocouple(MAXCS); // hardware SPI

void setup() {
  thermocouple.begin();
  RTC_init();                           // Initialize RTC 
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // Set sleep mode to POWER DOWN mode 
  sleep_enable();                       // Enable sleep mode, but not going to sleep yet
  pinMode(VADC, OUTPUT);                // ADC Power Supply   
}

void loop() {
  digitalWrite(VADC, HIGH);             // Power on ADC
  pinMode(SENSE, INPUT_PULLUP);         // Check if Thermocouple is connected
  delay(2);                             // Time constant with 10n between TC+ and TC- is about .5 ms
  if (digitalRead(SENSE)== LOW) {       // If TC is connected
    pinMode(SENSE, INPUT);              // sense pin floating
    pinMode(2, OUTPUT);                 // ADC CS Pin
    pinMode(4, OUTPUT);                 // ADC SCK Pin 
    delay(98);                          // First valid Data will be available 200 ms after Power-Up                   
    thermocouple.readCelsius();
    delay(1);  
    pinMode(2, INPUT);                  // deactivate ADC CS Pin 
    pinMode(4, INPUT);                  // deactivate ADC SCK Pin
  }
  digitalWrite(VADC, LOW);              // Power down ADC
  pinMode(SENSE, INPUT);                // sense pin floating
  sleep_cpu();  
}

void RTC_init(void)
{
  while (RTC.STATUS > 0);               // Wait for Synchronization
  RTC.CLKSEL = RTC_CLKSEL_INT32K_gc;    // 32.768kHz Internal Ultra-Low-Power Oscillator (OSCULP32K) 
  RTC.PITINTCTRL = RTC_PI_bm;           // PIT Interrupt: enabled 
  RTC.PITCTRLA = RTC_PERIOD_CYC16384_gc | RTC_PITEN_bm;  // RTC Clock Cycles 16384 | Enable PIT counter
}
    
ISR(RTC_PIT_vect)
{
  RTC.PITINTFLAGS = RTC_PI_bm;          // Clear interrupt flag 
}
Dateianhänge
MAX31855.png
Bild 2.png
Bild 3.png
Bild 1.png
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

MCP2515 Interruptfragen, komme nicht weiter :(

Beitrag von Hightech »

Moin zusammen,
ich versuche gerade den MCP2515 dazu zu bringen, nur auf die Can-Message mit der ID 0x404 zu reagieren und den Interrupt-Pin zu setzen.
Dann möchte ich nur diese eine Message lesen.

Leider wird der Interrupt immer ausgelöst, egal welche ID, auch wenn ich eine ID angebe, die es nicht gibt.

Im Setup setze ich
MCP2515(cspin).setConfigMode()
MCP2515(cspin).setFilterMask(MCP2515::MASK0, false,0x7FF)
MCP2515(cspin).setFilte(MCP2515::RXF0, false,0x404)
MCP2515(cspin).setNormalMode()

Leider kommt der Interrupt immer, und es sind auch alle Messages im RX Puffer.

Eigenlich sollte nach dem Lesen der Interrupt auch wieder automatisch zurück gesetzt werden.

Was mache ich da falsch?

Die MCP2515 LIb ist jene, die ich auch in dem Conti-Faden nutze.

Ist der Wir vielleicht der RX Overflow-Interrupt immer gesetzt, Muss ich das extra ausschalten?

Empfängt der MCP2515 im Interruptmodus weiter Messages und überschreibt den Puffer, oder wartet der, bis der Puffer geleert ist.
Das kann man wohl einstellen, das der RX Puffer überschrieben wird. Ist aber aus, soweit ich das verstanden habe.

Oder ist hier die Lösung beschrieben?
On taking an MCP2515 interrupt, the first thing we do is to disable interrupts from that chip in the interrupt controller. Then read canintf, eflg and canstat2 in the one three-byte read..
If either (or both) RX Interrupts are set we service them, ELSE if a transmit interrupt is set we service it, ELSE if an error interrupt is set we service it. Then the interrupts are enabled and the routine returns. It does NOT try to service more than "one event per customer". as we've got three of them to handle in rotation and don't want a long service on one starving the other two.
The interrupt flags are cleared in the status register with the "Bit Modify" instruction, only clearing the status that has just been serviced.
Antworten