Conti Fahrrad Akku Technik Faden.

Der chaotische Hauptfaden

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

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

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Kuddel hat geschrieben: Mo 26. Sep 2022, 16:09 Mal eine ganz doofe Frage: Ist es möglich, den 36 Volt Akku an einen 24 Volt 1000 Watt Insel-Billigwechselrichter anzuschließen, in dem man dazwischen einen PWM-Drehzahlsteller
.....
Gruß
Kuddel
Denk daran: ein voller Akku hat 42V!
An einen 24V Eingang kann man bestimmt 36V hängen ohne das es explodiert. Den Eingangskreis müsste man sich mal anschauen.
lange5766
Beiträge: 33
Registriert: So 11. Aug 2013, 15:51

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von lange5766 »

Hi,

Ich könnte folgende Möglichkeiten bieten:

KQI C3 mit nur C3
http://www.smdmark.com/en-us/search/code?id=C3
N-Channel JFET SST113
Code _C3_SST113-Vishay.pdf

LPSG
http://www.smdmark.com/en-us/search/code?id=LPSG
5Volt LDO

6R t 79 mit nur 6R
http://www.smdmark.com/en-us/search/code?id=6R
N-Channel Switch MMBFJ112
Code _6R_MMBFJ112-D.PDF

K6 J 01 mit nur K6
http://www.smdmark.com/en-us/search/code?id=K6
SOT23 NPN BCV71R
Code _K6_bcv71r-1163905.pdf

I 303 L mit nur 303
http://www.smdmark.com/en-us/search/code?id=303
Digital FET, N-Channel FDV303N
Code _303_FDV303N.pdf

K205
http://www.smdmark.com/en-us/search/code?id=K205
OUTPUT RAIL TO RAIL OP
Code _K205_TS931.pdf

Wenn gefunden hab ich jeweils die PDF angehängt

Gruß: Marco (DB6ML) [lange5766]
Dateianhänge
Code _K205_TS931.pdf
(130.75 KiB) 54-mal heruntergeladen
Code _303_FDV303N.pdf
(68.69 KiB) 51-mal heruntergeladen
Code _K6_bcv71r-1163905.pdf
(28.11 KiB) 50-mal heruntergeladen
Code _6R_MMBFJ112-D.PDF
(444.95 KiB) 52-mal heruntergeladen
Code _C3_SST113-Vishay.pdf
(52.97 KiB) 51-mal heruntergeladen
Benutzeravatar
MatthiasK
Beiträge: 2952
Registriert: Mo 19. Aug 2013, 22:12

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von MatthiasK »

Es gibt nur wenige Firmen, die an einem 24V-Eingang 50V-Elkos verbauen. Das ist zu teuer und der Markt verlangt billig, billiger, Hühnersuppe. Mit etwas Glück kommt da ein 35V-Elko rein, mit Pech 25V-Typ. Das riecht an 36V nicht gleich nach Fisch, aber viel Haltbarkeit sollte man nicht erwarten.

Bei einem "dummen" Motor ist das etwas anders. Der geht an zu hoher Spannung (wenn man es nicht total übertreibt) nur durch zwei Fehler kaputt: Zu hohe Drehzahl (=>zerlegt sich mechanisch) und Übertemperatur. Übertemperatur kommt durch zu viel Strom bei zu wenig Kühlung. Wenn ich dort die Spannung kurz einschalte kommt der Strom erst langsam ins fließen (die Induktivität grüßt). Schalte ich die Spannung wieder ab, fließt der Strom weiter, also Freilaufdioden nicht vergessen. Ist der Motor erst mal auf Drehzahl, arbeitet er auch als Generator. Mit einer PWM mittelt sich der Strom irgendwas passendes raus, das Ganze wird ein Schaltregler, bei dem die Induktivitiät gleichzeitig die Last ist.
Benutzeravatar
Bastelbruder
Beiträge: 11482
Registriert: Mi 14. Aug 2013, 18:28

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Bastelbruder »

Bei einem explizit als Drehzahlsteller vertriebenen PWM-Dingens ist die Freilaufdiode auf jeden Fall drin. Unter dem Display steht "Speed Controller". Damit läßt sich sicher die Geschwindigkeit einstellen mit der das dahinter angeschlossene Gerät desintegriert.
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Hallo Marco,
vielen Dank für deine Unterstützung!
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Ich hab mal den Schaltplan angefangen,
wer will, dem schicke ich gerne die KiCad Datei.

Ist sicher nicht korrekt und auch nicht vollständig.

Kuriosum 1 : Der 1. Temperaturfühler ist am BQ77 nicht angeschlossen, da ist ein Widerstand am TEMP Eingang.
Der 1. Temperaturfühler hängt am PIC

Kuriosum 2 : Der 2 Temperaturfühler hängt nirgends dran, der geht nur auf Test-Pins.
http://www.fingers-welt.de/imghost/up/2 ... ltplan.png

Bild
MatthiasBastel
Beiträge: 54
Registriert: Sa 25. Jun 2022, 06:20

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von MatthiasBastel »

Yay, danke, Boris!

Leider bisher noch nichts drauf, was meine komischen Effekte erklärt. In ner Woche komm ich vermutlich nochmal länger zu den Akkus.

Kurze Frage: Welche Pins gehen zum BQ77? Die CHG und DIS klar. Dann hast du die Labels nur mit den Nummern, die gehen zum PIC. Dann gibts einmal PIN4. Naja, oder ich warte einfach, bis du weitergekommen bist :-) Dank dir vielmals für die Arbeit! Ich war ja jetzt irgendwie zu faul für sowas :D
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Zum pl77 gehen

XALERT(42) und
Die I2C Leitungen
Mehr nicht aber ich hab den Bereich ausgelassen

PIN4 ist der von mir Data+ genannte Anschluss, über den man mit dem 38k Widerstand den Akku starten kann.
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Ein BMS Zombie:
Ein Atmega328pb statt dem PICel aufgelötet.
Klappt schon mal so halbwegs.
06A889DE-DE16-4710-9B23-15D5F623C754.jpeg
Benutzeravatar
Nicki
Beiträge: 3127
Registriert: So 11. Aug 2013, 20:16
Wohnort: wo Mosel und Rhein sich treffen

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Nicki »

Hat schon jemand herausgefunden, ob der bq34 zu (sealed) ist? Der Registerdump passt nicht wirklich zu der Übersicht im Datenblatt - oder die haben eine seltsame Art zu rechnen...
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Der bq34 lässt sich ganz normal bedienen.
Wenn man mit den fertigen Librarys arbeitet stimmt es alles.
MatthiasBastel
Beiträge: 54
Registriert: Sa 25. Jun 2022, 06:20

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von MatthiasBastel »

So, ich hab mir nochmal etwas Zeit genommen.

Grundproblem: Mein BMS geht bei hohen Stromspitzen aus, obwohl ich das nicht erwarte. Wiedereinschalten trotz Programmierung des BQ für "selbst einschalten nach 12,5 Sekunden" erst nach Anlegen eines Netzteils.
Bei geringen Überströmen funktioniert das selbst-wiedereinschalten.
Aufbau: Original Conti BMS modifiziert nach https://matthias-larisch.de/2022/07/11/ ... ttery.html, sprich: PIC raus, bq77pl900 Kontrolle über die Ausgangs-Fets verpasst.

Analyse:
Der BQ bringt mir kurz ne Short-Circuit detect (für 12 Sekunden) und hat dann den Status wieder auf 0xC0 - alles in Ordnung, DISC und CHG Fets an. Leider ist aber kein Strom am Ausgang :-(
Gleichzeitig ist die FuelGauge auch aus/nicht per I2C erreichbar.

Sprich:
- Entweder macht der BQ was anderes als im Datenblatt/Statusregister steht
- Oder da ist noch eine analoge? Schaltung, welche den Überstrom erkennt & latched & irgendwie die Fets ausgeschaltet lassen kann.

Leider ist der Akku zu und mein Torx-10 120km entfernt :D
Ideen?
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Der PIC ist aber runter oder?

Es kann sein, das durch den hohen Strom eine Zelle kleiner als UV geht?
Dann sperrt das auch.
Hast du Zero-Voltage Charge aktiv?
MatthiasBastel
Beiträge: 54
Registriert: Sa 25. Jun 2022, 06:20

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von MatthiasBastel »

Wie gesagt, PIC runter, außerdem:
Der BQ sagt im Statusregister 0xC0: Mosfets an, keine Fehler (kein Überstrom/Kurzschluss/Unterspannung/Überspannung/Übertemperatur).
Zero-Voltage-Charge hatte ich bisher aus - aber ich probier das mal, danke! Jetzt wo wir sehen, was am GPOD hängt :-)

Einzige Erklärung wäre ja sonst, dass der BQ die Mosfets nicht anbekommt obwohl er sagt, dass er das macht...
MatthiasBastel
Beiträge: 54
Registriert: Sa 25. Jun 2022, 06:20

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von MatthiasBastel »

Hmmm also da ich das EEPROM ja laut Datenblatt nur 3x beschreiben darf, wollte ich jetzt nicht endlos herumprobieren. Ich habe kombiniert:
- Erhöhung Zeit bis Unterspannungsabschaltung (auf angeblich 8 Sekunden, vorher 3 Sekunden was aber vll ein Schreibfehler im Datenblatt ist und die Tabelle auf Seite 48 für FS=0 nicht 2x von 1s-8s geht?)
- Unterspannungsabschaltung auf 2,0V gestellt (reicht mir auch als Akkuschutz, normalerweise schaltet mein Gehirn, spätestens das Gerät vorher ab)
- Kurzschlussstrom auf 60A erhöht bis zu 900µs (also das heißt ja, dass 900µs mehr als fließen dürfen, dann aber nurnoch 60A für max. 1,6s und dann nurnoch 25A)

Code: Alles auswählen

#define OV_CFG 0xFA
#define UV_CFG 0x06 /* 2,0V with 0,2V hysteresis */
#define OC_UV_DELAY 0xF8  /* 8s undervoltage, 25A overcurrent */
#define OCD_CFG 0xBF /* CBEN, (ZVC=0), SOR, overcurrent after 1,6s */
#define SCD_CFG 0xFC /* 900µs short circuit, 60A */
Das ganze habe ich einmal MIT und einmal OHNE zvc bit probiert - in beiden Fällen konnte ich meinen Audioverstärker nun problemlos einschalten/anstecken mit heftigem Funken, aber das BMS blieb an. Mit meinen alten Settings:

Code: Alles auswählen

#define OV_CFG 0xFA
#define UV_CFG 0x1D /* try 0x1b for 2,5V */
#define OC_UV_DELAY 0x2A  /* try 0xCF -> 5s under voltage, 32,5A overcurrent */
#define OCD_CFG 0xBF /* overcurrent after 1,6s */
#define SCD_CFG 0x74 /* try 0xFC -> 900µs short circuit, 60A */
ging das auch mit zwei parallel geschalteten Conti-Akkus nicht, da war es aus (Achtung: Im 2. Codeschnippsel stimmen die Kommentare nicht)...

Fazit: Mir reicht das umgebaute BMS jetzt so. Ich würde trotzdem niemals 20A Dauerstrom aus den Akkus ziehen, eher so 15A.
Benutzeravatar
zauberkopf
Beiträge: 9481
Registriert: So 11. Aug 2013, 15:33
Wohnort: gefährliches Halbwissen

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von zauberkopf »

- Unterspannungsabschaltung auf 2,0V gestellt (reicht mir auch als Akkuschutz, normalerweise schaltet mein Gehirn, spätestens das Gerät vorher ab)
Mutig !
Wenn man mal daran denkt, wieviel Kapazität zwischen 3V und 2V steckt..
Das ganze habe ich einmal MIT und einmal OHNE zvc bit probiert - in beiden Fällen konnte ich meinen Audioverstärker nun problemlos einschalten/anstecken mit heftigem Funken, aber das BMS blieb an. Mit meinen alten Settings:
Also das was Du da schreibst, ist folgendes :
Ich habe sämtlichen Schutz für die Discharge Mosfets ausgehebelt !
Warum baut man sowas ein ? ganz einfach :
Die hohen Ströme die beim anschließen fließen können, wenn die Last fette Kondensatoren hat, können die Mosfets schädigen.
Deswegen ist der Entwickler bestimmt stolz darauf, das SSR so konstruiert zu haben, das es gleich wieder zu macht, BEVOR der Strom zu hoch wird.
So ein Mosfet, ist nicht wahrheit nicht EIN Mosfet, sondern viele kleine Parallel geschaltete.
Die können bei genau solchen Aktionen, stück für stück sterben.. bis irgendwann gar nix mehr geht.

Dafür gibts verschiedene Lösungsansätze.. z.B. den DCHFet brutal überdimensionieren...

umgekehrt besteht das Problem ggf auch.. wenn das Ladegerät fette Kondis hat, und der Akku leer ist, und die Strippen kurz und dick sind...
MatthiasBastel
Beiträge: 54
Registriert: Sa 25. Jun 2022, 06:20

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von MatthiasBastel »

Och noe :-( Hab gerade ganz viel gerechnet, dass das alles passt und jetzt kam das Login-Fenster und alles war weg :-(

Also nochmal in Kürze:

https://www.mouser.com/datasheet/2/308/ ... 802396.pdf Datenblatt zu den beiden Fets.

Innenwiderstand Akku etwa 100 mOhm (in der Praxis eher mehr). Für Rds, Kabel, Stecker und ESR rechnen wir nochmal 50mOhm.

Macht beim vollen Akku max. 280A. (Und bei Rds=6 mOhm Uds=1,7V)

Fall a) Mosfet vorher an:
SOA sagt etwa 5ms für 300A/2V. Gemäß Tau=R*C ist ein 10mF(10000µF) FET nach 1,5ms bei 63% Spannung angekommen, der Strom also bei etwa 130A, was für mehr als 100ms erlaubt ist.
In der Praxis liegt der Strom geringer da selbst meine optimistischen Einstellungen ja >60A nur für <1ms zulassen - ok, ich hab auch nur 4800µF dran. Merke: Selbst diese Kurzschlussschutzeinstellungen sind also noch schützend für den Fet, da die Kurve in jedem Punkt unter der SOA liegt; Durch den Widerstand der Batterie ist der Strom auf 280A begrenzt, das darf er für >1ms aber nach 1ms schaltet das BMS ab.
Im Abschaltmoment durchlaufen wir noch kurz nen ungünstigen Moment, aber siehe Fall b) passt das vermutlich auch locker.

-> Mosfet hält das also aus.

Fall b) Mosfet vorher aus:
Mit ner Gate-Kapazität von 15nF und ner Gate-Treiber-Impedanz von 100 Ohm ist der Mosfet nach 3µs an (meinetwegen nach 5).
Nach SOA dürfen wir aber bei Uds=40V sogar für 10µs 250A ziehen - brauchen wir aber nicht.

Jetzt müssn wir das bestimmt alles noch etwas deraten, weil Fet warm etc, aber mir ist hier genug Puffer.


Sooo und zu den Akkus:
Ne Lithiumzelle ist bei Entladung bis 2,0V nicht direkt kaputt. Die Akkus sind eh schon 5-7 Jahre alt. Dieser "Hack" des BMS hilft mir, die Akkus zu benutzen, was ich sonst nicht tun würde. Was ist besser: Akku nicht nutzen und herumliegen lassen oder Akku nutzen mit einer ganz geringen Gefahr, mal nicht früh genug abzuschalten und dann ?5%? Restzyklen einzubüßen? Für mich ist die Entscheidung klar.

Nochmal zur Klarheit: Ich vermute, dass das BMS "fälschlicherweise" zu früh in die Unterspannungsabschaltung gegangen ist; ich möchte die Zellen nicht bis 2,0V benutzen, in der Praxis hör ich normalerweise bei 3,3-3,5V auf. Es geht mir nur darum, dass das BMS an bleibt. Eventuell reicht auch irgendeine andere der Änderungen der Parameter für sich alleine aus - kann man nochmal austesten, ich wollte aber jetzt nicht die EEPROM Schreibzyklen aufbrauchen :-)

Danke trotzdem für deine Concerns, zauberkopf :-)
Benutzeravatar
zauberkopf
Beiträge: 9481
Registriert: So 11. Aug 2013, 15:33
Wohnort: gefährliches Halbwissen

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von zauberkopf »

Na gut... Du hast also nachgerechnet.. ;-)
Ich habe in den letzten Monaten so einiges über Akkus und BMS in der QRO-Klasse ( hohe leistung) gelernt.
Durch den Widerstand der Batterie ist der Strom auf 280A begrenzt, das darf er für >1ms aber nach 1ms schaltet das BMS ab.
Naja.. die Batterie hat aber auch einen eingbauten Kondensator... ;-)
Und der sitzt vor dem eingebauten Innenwiderstand.. ;-)
Musste mal aufmachen.. ;-)
MatthiasBastel
Beiträge: 54
Registriert: Sa 25. Jun 2022, 06:20

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von MatthiasBastel »

zauberkopf hat geschrieben: So 6. Nov 2022, 16:09 Naja.. die Batterie hat aber auch einen eingbauten Kondensator... ;-)
Und der sitzt vor dem eingebauten Innenwiderstand.. ;-)
Musste mal aufmachen.. ;-)
Könntest du das näher ausführen / belegen? In den gängig auffindbaren Ersatzschaltbildern für Lithium-Zellen sind keine Kapazitäten außerhalb des Serien-Rs eingezeichnet; auch auf der BMS Platine selbst ist keine (nennenswerte) Kapazität über dem Akku vorhanden. Mir ist also unklar, was du damit meinst.

Ein 10S5P Akku hat in etwa den doppelten Innenwiderstand einer einzelnen Zelle - dazu kommt noch der ganze Kleinkram (Nickel, Verbindungen). Bei den Zellen hier sind wir also bei 60 mOhm + X. Ich hätte also verstanden, wenn du argumentierst, dass mein angenommener Innenwiderstand etwas hoch ist.
Im Fall a) haben wir aber einen Puffer von (zeitlich) mehr als 90% bzw. immernoch knapp 50%, wenn wir den "echten" Kurzschluss nehmen (nicht die Aufladung eines Elkos)

Vielleicht bin ich insbesondere deswegen irritiert, weil wir hier im Finger-Forum sind und nicht im professionellen Schaltungsdesign :D Ja, wenn der Mosfet dann doch mal beim Anschließen durchbrennt, dann muss er halt getauscht werden... Da fließt genug Strom, dass der mit Sicherheit nicht mehr leitet nach dem Durchbrennen :D Also alles safe.
Benutzeravatar
zauberkopf
Beiträge: 9481
Registriert: So 11. Aug 2013, 15:33
Wohnort: gefährliches Halbwissen

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von zauberkopf »

Natürlich hast Du eingebaute Kapazitäten.
Die kannste sogar messen.
Impedanzspektroskopie..

Das Ding ist der : Wenn alles optimal ist.. kurze verbindungen(wenig L), dicke kabel.. usw.. dann treten wie immer bei QRO die tollsten Effekte auf, die Du sonst nicht hast.
bzw können auftreten.
Besonders wenn die Dinger in Serie produziert werden, und es dann Rückläufer gibt.

Deswegen sag ich ja : Du hast den Entwickler, der das SSR entwickelt hat, jetzt sehr traurig gemacht.. ;-)
Vielleicht bin ich insbesondere deswegen irritiert, weil wir hier im Finger-Forum sind und nicht im professionellen Schaltungsdesign
Richtig.
Aber jetzt weist Du wenigstens was Du getan hast.. ;-)
Ich habe demnächst ja ein ähnliches problem..
MatthiasBastel
Beiträge: 54
Registriert: Sa 25. Jun 2022, 06:20

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von MatthiasBastel »

zauberkopf hat geschrieben: So 6. Nov 2022, 18:32 Natürlich hast Du eingebaute Kapazitäten.
Die kannste sogar messen.
Impedanzspektroskopie..
An "parasitäre" Kapazitäten, welche 400A über mehrere µs liefern wollen glaube ich immer noch nicht :D Das sind ja dann mindestens 10µF (50 mOhm, Tau=1µs), wir sollten hier eher im nF Bereich sein, nicht wahr?
Screenshot from 2022-11-06 18-17-42.png
Benutzeravatar
zauberkopf
Beiträge: 9481
Registriert: So 11. Aug 2013, 15:33
Wohnort: gefährliches Halbwissen

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von zauberkopf »

Du.. frag mich jetzt sowas nicht..
wie gesagt.. ich habe entsprechenden Schutzschaltungen gesehen.. nachgefragt, und mir wurde dann gesagt, das ohne so eine schnelle abschaltung der Mosfet schneller "altert".
Damit meine ich nicht, das er sofort hin ist.. ggf hast Du auch glück, und er überlebt den Pack.
Das einzige was ich sagen will : Solche Features wurden entwickelt, damit weniger Leute "Hühnersuppe" schreien.

Ich muss auch sagen, das ich solche Schaltungen liebe...
z.b. habe ich mal einen Laser gesehen, der konnte ne Stahlplatte durchtrennen, aber sobald er durch war, hat er sofort abgeschaltet.
Mit sofort meine ich : Das Blatt das da hinter lag, war unberührt.
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Ich hab das Programm mal so angepasst, das es den Akku und den CAN-BUS Treiber zurücksetzt, wenn keine Antwort vom Akku kommt.

Wie schreibt man das so um, dass das Zurücksetzen NICHT ÜBER DELAY geht, ich will die Wartezeit des Pin-Toggle anders nutzen.
Ich muss schließlich 20 Akkus bespaßen
if (mcp2515.getErrorFlags() == 0x15)
{ // On Can Error 0x15 restart CAN & Toggle Akku-DATA+
Serial.println("CAN-Error 0x15, CAN-RESET & Toggle Data+");
mcp2515.reset();
digitalWrite(DATA_P, HIGH);
delay(3000);
digitalWrite(DATA_P, LOW);
delay(3000);
mcp2515.setBitrate(CAN_250KBPS);
mcp2515.setNormalMode();
}

Code: Alles auswählen

#include <Wire.h>
#include <SPI.h>
#include <mcp2515.h> // https://github.com/atc1441/arduino-mcp2515
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);

struct can_frame canMsg;
MCP2515 mcp2515(5); // CS Pin


int DATA_P = 3; //Pin mit BC547 zum Pull-Down vom Data+ des Akkus

void setup()
{
  Serial.begin(115200);
  SPI.begin();

  mcp2515.reset();
  mcp2515.setBitrate(CAN_250KBPS);
  mcp2515.setNormalMode();

  pinMode(DATA_P, OUTPUT);
  digitalWrite(DATA_P, LOW);

 }

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()
{

  if (mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK)
  {
    if (0 == 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;
    }
    }
  }

  if (millis() - lastsend >= 100)
  { // Keeps the Battery alive needs to be send periodically
    lastsend = millis();
    sendCAN(0x201, 4, 0, 1, 0, 0);

    if (mcp2515.getErrorFlags() == 0x15)
    { // On Can Error 0x15 restart CAN & Toggle Akku-DATA+
      Serial.println("CAN-Error 0x15, CAN-RESET & Toggle Data+");
      mcp2515.reset();
      digitalWrite(DATA_P, HIGH);
      delay(3000);
      digitalWrite(DATA_P, LOW);
      delay(3000);
      mcp2515.setBitrate(CAN_250KBPS);
      mcp2515.setNormalMode();
    }
  }
}

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(MCP2515::TXB1, &canMsg1);
}

Derkiki
Beiträge: 69
Registriert: Mo 23. Mai 2022, 10:57

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Derkiki »

Einfachste Variante, die mir auf die schnelle einfällt:
Separate Funktion schreiben, die alle drei Sekunden aufgerufen wird, siehe millis() im restlichen Code.
Diese arbeitet für alle Akkus synchron und prüft zu erst, ob Bedingung für Akku x,y,z,.. erfüllt ist und merkt sich den Status. Wo nötig setze den Pin Low, High, whatever...
Drei Sekunden später:
Setze Pins für entsprechende Akkus auf neuen Status....
Das durchschalten der verschiedenen Stati per Zustandswort, das bei jedem state weitergeschaltet wird.
Theoretisch kannst du dann auch unterschiedlich lange Waits realisieren ( bzw wie "oft" die Funktion aufgerufen wird), wie es eben benötigt wird
Wäre das eine Lösung?!
MatthiasBastel
Beiträge: 54
Registriert: Sa 25. Jun 2022, 06:20

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von MatthiasBastel »

Ui, ich hab das Update verpasst, dass ATC es geschafft hat!

Mein Vorschlag:
Kleine Platine mit µC + CAN in den Akku einbauen (z.B. STM32F072 + SN65HVD230)
Stromversorgung von 3,3V von der intelligenten Platine
Nach dem Powerup sendet sie zyklisch die CAN message.

Akku kann dann ganz normal An und Aus geschaltet werden. Fertig...
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Och naja, im Prinzip kann man das auch auf der Zusatzplatine machen, da ist ja ein STM32 drauf, der das erledigen kann.
Der hängt ja auch direkt am CAN-Bus
MatthiasBastel
Beiträge: 54
Registriert: Sa 25. Jun 2022, 06:20

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von MatthiasBastel »

Ah oh aeh moment, wir haben ja die Firmare, oder? Gibts die Binaries zum herunterladen? Kriegt man das USB leicht in den DFU Modus?

Dann könnte man die vielleicht auch einfach patchen... Ich finds spannend, das BLE Zeug drin zu lassen. Und soweit ich das verstanden hab, ist die CAN Message um die es hier geht vom STM32 interpretiert, nicht vom BMS. Der macht dann alles nötige, um das BMS anzulassen.
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Der STM 32 ist quasi nur das Display. Er sollte für den Betrieb des Akkus im Prinzip nicht notwendig sein.
MatthiasBastel
Beiträge: 54
Registriert: Sa 25. Jun 2022, 06:20

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von MatthiasBastel »

? Die von ATC im Youtube Video gezeigte Variante mit dem 0x201'er CAN message geht doch NUR mit vorhandenem STM/"Display"
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Müsste ohne das „Display“ laufen.
Teste ich mal.

Ok, Test ergab: Ohne die Zusatzplatine kommt keine Spannung. Das liegt aber im Bereich der CAN- Communikation und der Leitung vom Taster. Der Data+ Anschluss geht über das Zusatzboard zum BMS Board, das müsste man noch brücken. Was das Zusatzboard zum BMS sendet kann man ja simulieren.
Lokaro
Beiträge: 16
Registriert: So 20. Nov 2022, 23:18

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Lokaro »

Man kann die "Display" firmware so byte patchen das sie immer denkt es ist ein motor vorhanden, dann sollte es an bleiben
MatthiasBastel
Beiträge: 54
Registriert: Sa 25. Jun 2022, 06:20

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von MatthiasBastel »

Genau, die Lösung mit dem Patchen gefällt mir am besten. Hab ich verpasst, wo es den dump gibt? Gern direkt mit den reverse engineerten Symbolen/Adressen... Dann könnte man mal nen Patch entwickeln. Hab leider gerade weder ne Platine noch ne entsprechend ausgerüstete Werkstatt hier :-( Aber Wenn man die Firmware gepatched bekommt & dann der USB drauf laden kann, was täte mir gut gefallen :-)
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Hier müsste alles drin sein
https://atcnetz.de/downloads/conti.zip
Lokaro
Beiträge: 16
Registriert: So 20. Nov 2022, 23:18

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Lokaro »

In dem ZIP ist nun eine .hex mit gepatchtem "Always on" jedoch wird der akku sich derzeit noch relativ schnell wieder ausschalten, ca. 5minuten, da er nicht fährt und in den StandBy schaltet. das muss noch gepatcht werden.
https://atcnetz.de/downloads/conti.zip

Das DFU proto zu reversen ist nicht einfach ohne die original Software und dauert stunden, also lieber direkt flashen.
SWD Pinout ist auch im ZIP
MatthiasBastel
Beiträge: 54
Registriert: Sa 25. Jun 2022, 06:20

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von MatthiasBastel »

Wow, danke! Ghidra analysiert fleissig, ich muss ja den periodischen Reset des Flags entfernen sowie es initial setzen, das klingt aber irgendwie realistisch.

Beim Programmieren komm ich nicht weiter und hab ja wie gesagt auch gerade keine Platine da:
Der BOOT0 Pin, der den DFU Bootloader aktivieren würde, ist per Widerstand auf Masse gelegt (=User Anwendung) und liegt auf ne Test-Pin auf der Rückseite.
Mir scheint auch so, als gäbe es aus der Anwendung keinen Jump in den Systemmemory-Bereich, also wird der Bootloader auch nicht Fern-Aktiviert.

Hat mal jemand den USB angesteckt und geschaut was passiert?

Edit: Oh wow, du hasts schon gemacht :-) Danke! Statt SWD könnte man eben originales DFU nehmen, dazu Akku aus, Akku auf, nen Draht anlöten an den Widerstand links vom stm32 (links vom Pin1) auf 3,3V, Akku einschalten (oder reicht USB dranstecken?) -> STM bekommt Strom, DFU läuft.
Benutzeravatar
Kuddel
Beiträge: 5074
Registriert: Fr 28. Jun 2013, 10:56
Wohnort: Denk immer an St. Alamo!

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Kuddel »

Ich konnte mit dem Akku auch endlich loslegen und habe dazu erstmal einen Adapter gebaut. Wichtig war mir auch ein Leistungstest, und siehe da, der Akku schaltet sich bei zu hohen Strömen einfach ab. Vorwiderstand dazuwischen und es geht.
Hier die Video-Dokumentation dazu:
https://youtu.be/VlQznpBGTC0
Gruß
Kuddel
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Ich hab mir ein Akku8 Board gemacht, an welchem 8 Akkus angeschlossen werden können.
Es werden dort 8 MCP2515 CAN Boards aufgesteckt, die die Can-Kommunikation pro Akku übernehmen, da ja alle Akkus die gleiche CAN_ID haben. Zudem sind 2 PCF8574 I2C Port-Expander drauf, die je Board die Chip_Select und den Akku-Start-Knopf bedienen.
Die Interrupt der MCP2515 gehen direkt zum Arduino.
Zudem ein ULN2803 Transitorarray für den Akku-Knopf, da kommen gerne mal 42V.
Warum der Aufwand?
Ich will die Betriebsdaten der Akkus, man könnte auch einfach stupmf auf dem CAN-Bus das 0x201 senden, aber dann weiß man nicht ob der Akku an ist und wie die Spannung und Strom sind.

Bild

Ich brauche 3 Stück von den Boards, hab aber 5Stück, 2 Wären also zu haben.
Jannyboy
Beiträge: 1406
Registriert: So 11. Aug 2013, 14:49
Wohnort: Kreis Augsburg

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Jannyboy »

Lokaro hat geschrieben: Mi 30. Nov 2022, 13:43 Das DFU proto zu reversen ist nicht einfach ohne die original Software und dauert stunden, also lieber direkt flashen.
Da brauchst du nichts reversen die gibt es fertig zum Download bei ST. Guck mal bei dein Bootloader Application Notes.

Grüße Jan
Lokaro
Beiträge: 16
Registriert: So 20. Nov 2022, 23:18

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Lokaro »

Jannyboy hat geschrieben: Do 1. Dez 2022, 23:22 Da brauchst du nichts reversen die gibt es fertig zum Download bei ST. Guck mal bei dein Bootloader Application Notes.
Meinte das DFU Protokoll vom Continental selber, um die Akkus zu flashen ohne zu öffnen.
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Nach dem Mittag muss ich die Leitungen mal durchbimmeln, ob alles wie geplant verdrahtet ist. Zu 100% ist da ein-zehn Fehler drauf. Wie immer

Die Conti-Hydra

Bild
MatthiasBastel
Beiträge: 54
Registriert: Sa 25. Jun 2022, 06:20

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von MatthiasBastel »

Lokaro hat geschrieben: Mi 30. Nov 2022, 13:43 In dem ZIP ist nun eine .hex mit gepatchtem "Always on" jedoch wird der akku sich derzeit noch relativ schnell wieder ausschalten, ca. 5minuten, da er nicht fährt und in den StandBy schaltet. das muss noch gepatcht werden.
Das ist ja schon richtig cool von dir - siehst du ne Chance, eventuell demnächst auch noch die 5 Minuten rauszupatchen? Ich komm mit meiner Firmwareanalyse doch nicht so rein, wie ich dachte und du hast ja die ganze Arbeit schon gemacht :D

Alternativ überleg ich gerade, nen kleines Board mit stm32f072 + SN65HVD230 Can Transceiver und ner Mini-Firmware, die alle 200ms die Motorwerte sendet mit in die Akkus zu packen - das geht dann ja ganz ohne Firmware Patch.
Das Teil wird von der STM Platine mit Strom versorgt, geht also an, sobald der Power-Taster gedrückt wird und hält den Akku dann an.
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Das Smart- Board könnte doch den Motor mit zB 5kmh simulieren oder?
Oder ist das Always-On ein q&d patch auf die original Firmware?
MatthiasBastel
Beiträge: 54
Registriert: Sa 25. Jun 2022, 06:20

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von MatthiasBastel »

Nein, der Patch ist die originale Smart-Board Firmware mit 3 geänderten Bytes. Ich weiß garnicht, ob es bisher schon einen proof-of-concept für "original BMS ohne SmartBoard" gibt - da müsste halt ne Ecke mehr auf dem CAN passieren.
Und natürlich ist das Smartboard auch ziemlich cool :-)
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

So, da ich das Konzept verworfen habe, den CS über einen I2C Expander zu expandieren, klappt es schon mal mit 4 Akkus.

kku 3 0 Light:0 076% 38,42V -0,02A
Akku 0 0 Light:0 100% 41,15V -0,04A
Akku 2 0 Light:0 099% 41,20V 00,00A
Akku 1 0 Light:0 100% 40,94V -0,02A
Akku 3 0 Light:0 076% 38,42V -0,02A
Akku 0 0 Light:0 100% 41,15V -0,04A
Akku 2 0 Light:0 099% 41,20V 00,00A

Der Anschluss DATA+ heißt jetzt WakeUp, hat ca die Akkuspannung und wird 3 Sekunden gegen Masse gezogen, dann startet der Akku.
Der Anschluss Data- heißt jetzt 12Vout, dort kommen 12V und ein paar mA raus, wenn der Akku läuft.



Hier schon mal ein bisschen Nachtlektüre:


main.cpp

Code: Alles auswählen

#include <Arduino.h>
#include "conti.h"

void setup()
{
    mcp2515.reset();
    mcp2515.setBitrate(CAN_250KBPS);
    mcp2515.setNormalMode();

    Serial.begin(115200);
    Serial.println("Start");

    for (int a = 0; a < 24; a += 4)
    {
        // akku[a].pcf.begin();
        Serial.print("Start PCF 0x");
        Serial.println(akku[a].i2c_addr, 16);
        pcf.begin(akku[a].i2c_addr);
    }
}

void loop()
{

    if (millis() - lastsend >= 100)
    { // Keeps the Battery alive needs to be send periodically
        lastsend = millis();
        // send keepalive to each
        for (int a = 0; a < 4; a++)
        {
            sendCAN(akku[a].cs_pin, 0x201, 4, 0, 1, 0, 0, 0, 0, 0, 0);
        }
    }

    for (int a = 0; a < 4; a++)
    {
        if (MCP2515(akku[a].cs_pin).getErrorFlags() == 0x15)
        { // On Can Error 0x15 restart CAN & Toggle Akku-WakeUp
            //Serial.print("CAN-Error 0x15, CAN-RESET & Toggle Akku-WakeUp :");
            //Serial.println(a);
            WakeUp(a);                   
          
        };        
     
    }



for (int a = 0; a < 4; a++)
{
    if (AkkuValue[a].idle_time + WakeUpTimeOut < millis()) 
    {

        WakeUp(a);
    }


    if (MCP2515(akku[a].cs_pin).readMessage(&canMsg) == MCP2515::ERROR_OK)
    {    AkkuValue[a].idle_time=millis();
        if (1 == 0)
        {                                     // 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];
            AkkuValue[a].amp=ampere;
            AkkuValue[a].volt=voltage;
            AkkuValue[a].soc=percent;

            Serial.print("Akku ");
            Serial.print(a);
            Serial.print(" ");
            //printf(out_string, "%u%%  %i Light:%i            ", percent, power_setting, light_status);
            sprintf(out_string, "%i Light:%i   %03u%% ", power_setting, light_status, percent);
            Serial.print(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 %02i,%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 <SPI.h>
#include <mcp2515.h> // https://github.com/atc1441/arduino-mcp2515
#include <PCF8574.h>

PCF8574 pcf(0x26);
MCP2515 mcp2515(0);
struct can_frame canMsg;
void sendCAN(uint8_t mcp_addr, 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);
void WakeUp (int a);
void i2c_datasend(int pcf_addr, int pin, bool lvl);


boolean light_status = 0;
uint8_t power_setting = 0;
char out_string[100];
char test;
uint32_t lastsend = 0;
uint32_t lastmsg = 0;
const int WakeUpTimeOut=10000;
const int WakeUpToggle=3000;
const int WakeUpWait=6000; 


typedef struct
{
    uint16_t volt;
    int16_t amp;
    uint8_t soc;
     unsigned long a_time;
     unsigned long idle_time;
    int status;
} akku_values_t;

akku_values_t AkkuValue[24] = {
    /*
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0},
    {0.0,0.0,0.00,0,0,0}
    */
};

typedef struct
{
    int i2c_addr;
    int intr_pin;
    int toggle_pin;
    int cs_pin;
} akku_t;

akku_t akku[24] = {

    {0x26, 3, 0, 49},
    {0x26, 2, 1, 48},
    {0x26, 5, 6, 47},
    {0x26, 4, 7, 46},
    {0x25, 1, 4, 45},
    {0x25, 3, 5, 44},
    {0x25, 2, 6, 43},
    {0x25, 0, 7, 42},
    {0x24, 3, 0, 30},
    {0x24, 2, 1, 31},
    {0x24, 5, 6, 32},
    {0x24, 4, 7, 33},
    {0x23, 1, 4, 34},
    {0x23, 3, 5, 35},
    {0x23, 2, 6, 36},
    {0x23, 0, 7, 37},
    {0x22, 3, 0, 22},
    {0x22, 2, 1, 23},
    {0x22, 5, 6, 24},
    {0x22, 4, 7, 25},
    {0x21, 1, 4, 26},
    {0x21, 3, 5, 27},
    {0x21, 2, 6, 28},
    {0x21, 0, 7, 29}};




void WakeUp (int a){
if ((!AkkuValue[a].status) && (AkkuValue[a].a_time + WakeUpWait < millis()))
            {
                Serial.print(" Start Toggle Akku-WakeUp :");
                Serial.println(a);
                AkkuValue[a].status =1;
                AkkuValue[a].a_time = millis();             
                i2c_datasend(akku[a].i2c_addr, akku[a].toggle_pin, 1);
                MCP2515(akku[a].cs_pin).reset();
                MCP2515(akku[a].cs_pin).setBitrate(CAN_250KBPS);
                MCP2515(akku[a].cs_pin).setNormalMode();
            }

            else if ((AkkuValue[a].a_time + WakeUpToggle < millis() ) && AkkuValue[a].status) 
            {    
                Serial.print(" END Akku-WakeUp :");
                Serial.println(a);
                i2c_datasend(akku[a].i2c_addr, akku[a].toggle_pin, 0);                
                AkkuValue[a].status = 0;         
            }
}


void sendCAN(uint8_t mcp_addr, 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)
{
    MCP2515 mcp_i(mcp_addr);
    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_i.sendMessage(MCP2515::TXB1, &canMsg1);
}

void i2c_datasend(int pcf_addr, int pin, bool lvl) // i2c Adresse , Pin, Low/High
{
    int status;

    if (!(status = pcf.isConnected()))
    {
        Serial.print("Not connected ");
    }

    if (pcf.lastError())
    {
        Serial.print("I2C Fehler Adresse 0x");
        Serial.println(pcf.getAddress());
    }

    pcf.setAddress(pcf_addr);
    pcf.write(pin, lvl);
}


Tiefdruck
Beiträge: 43
Registriert: So 28. Aug 2022, 15:50

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Tiefdruck »

Hallo zusammen, sieht sehr gut aus.

Was mir momentan noch nicht ganz klar ist: Was brauche ich mindestens um einen Akku in betrieb nehmen zu können?

So wie ich das verstanden habe währe das Mikrocontroller -> MCP2515 -> Akku. Stimmt das so weit?
Für meine Anwendung bräuchte ich die Daten per MQTT über WLAN an mein Smarthome. Das heißt als Mikrocontroller währen ein ESP8266 oder ein ESP32 geeignet. Falls sich jemand hier mit GitHub auskennt könnten wir das Projekt an der Stelle gemeinsam vorantreiben. Da der Arduino und die ESP kompatibel sind währen beide Plattformen abgedeckt.

Viele Grüße Thomas
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Also ein ESP.egal oder ein Arduino.egal mit dem MCP2515 sollte ausreichen.

Ich hab heute zum ersten mal das Haus am 6-Conti-Pack, alle 6 Akkus stumpf parallel, die gleichen sich dann gut an.
das läuft mit einem Arduino micro.

Das ich mir mal wieder einen SB3000 dabei gekillt habe.... naja.
Ich glaub ich hab noch Ersatztransen da.

Bild
D2A209DA-E3BF-4AEC-ABBD-96A7066F2947.jpeg
Tiefdruck
Beiträge: 43
Registriert: So 28. Aug 2022, 15:50

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Tiefdruck »

Hallo zusammen,

gibt es schon Neuigkeiten oder Veränderungen an der Software?

Ich bin momentan dabei mein Balkonkraftwerk aufzubauen und habe mich dazu schon in Grafana eingearbeitet. Der nächste Schritt währe die Akkus per Arduino und dem MCP auszulesen. Ich würde die bestehende Software dann auch um MQTT erweitern und hier zur Verfügung stellen.

Viele Grüße Thomas
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Das hier ist mein Code für einen einzelnen Akku-Client, der redet mit dem Akku über Can und mit dem dem Master über i2c.
Main.cpp

Code: Alles auswählen

#include <Arduino.h>
#include <Wire.h>
#include <conti.h>

void setup()
{
  Serial.begin(115200);
  i2c_addr = (EEPROM.read(1));
  Serial.print("I2C: ");
  Serial.println(i2c_addr);
  delay(1000);
  Wire.begin(i2c_addr);
  Wire.onReceive(receiveEvent); // Function call when Slave receives value from master
  Wire.onRequest(requestEvent); // Function call when Master request value from Slave
  DDRD |= (1 << 3);
  PORTD |= (1 << 3);
  Serial.print(" INIT MCP ");
  WakeUp(1);
  PORTD &= ~(1 << 3);
}

void loop()
{
  akku_err = akku_read();

  if (millis() - lastmsg >= 1000)
  {
    lastmsg = millis();
    print_data();
  }
  if (millis() - lastsend >= 90)
  { // Keeps the Battery alive needs to be send periodically
    lastsend = millis();

    // send keepalive to each
    if (command == 1 && fail == 128)

    {
      sendCAN(0x201, 4, 0, 1, 0, 0);
      Serial.print("^");
    }
    else
    {
      Serial.print(".");
    }

    if ((a_time + WakeUpToggle < millis()) && status)
    {
      PORTD &= ~(1 << 3);
      a_time = millis();
      idle_time = millis();
      status = 0;
    }
  }
}
conti.h

Code: Alles auswählen

#include <Arduino.h>
#include <SPI.h>
#include "c_mcp2515.h" // https://github.com/atc1441/arduino-mcp2515
#include <Wire.h>
#include <EEPROM.h>

#define cs_pin 7 // PD7
#define Rx1BF 8  // PB0
#define Rx0BF 9  // PB1
#define BT1 16   // PC2
#define BT2 15   // PC1
#define BT3 14   // PC0
#define BT4 17   // PC3
MCP2515 mcp2515(cs_pin);
int akku_err;
int i2c_addr;

// #define toggle_pin 3
// #define Pow1 4
// #define mcp_int 7

int can400[4];
int can404[8];
//int can405[8];
int can408[8];
int can3B4[8];
int command = 0;
int fail = 222;

void receiveEvent();
void reqeustEvent();

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);
void WakeUp(int reason);
int akku_read();

uint32_t lastsend = 0;
uint32_t lastmsg = 0;
const int32_t WakeUpTimeOut = 15000;
const int WakeUpToggle = 3000;
const int32_t WakeUpWait = 15000;
const int32_t MCPWait = 30000;
bool status = 0;
bool reset;
int count = 0;
unsigned long a_time;
unsigned long idle_time;
unsigned long mcp_time;

void receiveEvent()
{
  command = Wire.read();
}

void requestEvent()
{
  Wire.write(can404[4]); // Akku percent 0
  Wire.write(can404[0]); // ampere low 1
  Wire.write(can404[1]); // ampere high 2
  Wire.write(can404[2]); // voltage low 3
  Wire.write(can404[3]); // voltage high 4
  Wire.write(can400[0]); // Power_fail 5
  Wire.write(fail); // PowerStatus 6
}

void WakeUp(int reason)
{
  if ((!(status)) && (count < 100) && (a_time + WakeUpWait < millis()))
  {
    switch (reason)
    {
    case 1:
      Serial.println("PF");
      break;
    case 2:
      Serial.println("TO");
      break;
    case 3:
      Serial.println("MCP");
      break;
    default:
      break;
    }
    mcp2515.reset();
    mcp2515.setBitrate(CAN_250KBPS, MCP_8MHZ);
    // mcp2515.setConfigMode();
    // mcp2515.setInterruptRx1();
    // mcp2515.setFilterMask(MCP2515::MASK1 , false, 0x7FF); // Initialisiere Maskierung für alle IDs
    // mcp2515.setFilter(MCP2515::RXF1, false, 0x400);      // Initialisiere Filter für ID 400
    // mcp2515.setFilter(MCP2515::RXF2, false, 0x404);      // Initialisiere Filter für ID 404
    // mcp2515.setFilter(MCP2515::RXF3, false, 0x405);      // Initialisiere Filter für ID 405
    // mcp2515.setFilter(MCP2515::RXF4, false, 0x405);      // Initialisiere Filter für ID 408   
    mcp2515.setNormalMode();  
    status = 1;
    count++;
    a_time = millis();
    PORTD |= (1 << 3);
  }
}

int akku_read()
{
  struct can_frame canMsg;

  

  if (idle_time + WakeUpTimeOut < millis())
  {
    fail = 111;
    mcp2515.reset();
    mcp2515.setBitrate(CAN_250KBPS, MCP_8MHZ);
    mcp2515.setNormalMode();
    WakeUp(2); // 2 = Reason TimeOut
  }
  
  if (mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK && !status)
  {     

  if (mcp_time + MCPWait < millis()&& mcp2515.getErrorFlags())
  {
    mcp_time=millis();
    fail = 66;
    mcp2515.reset();
    mcp2515.setBitrate(CAN_250KBPS, MCP_8MHZ);
    mcp2515.setNormalMode();
    WakeUp(3); // 2 = Reason TimeOut
  }
  else if (!mcp2515.getErrorFlags()){
   mcp_time=millis();
  fail = 128;
  }
  




    idle_time = millis();  
    switch (canMsg.can_id)
    {
    case 0x400:
    {
      
      count = 0;
      for (int i = 0; i < canMsg.can_dlc; i++)
      {
        can400[i] = (canMsg.data[i]);

        if (canMsg.data[0] == 0x00 && !status && command)
        {

          WakeUp(1); // Reason 1 = PowerFail
        }
      }
      break;
    }

    case 0x408:
    {
     
      count = 0;
      for (int i = 0; i < canMsg.can_dlc; i++)
      {
        can408[i] = (canMsg.data[i]);
      }
      break;
    }

/*    case 0x405:
    {
      idle_time = millis();
      count = 0;
      fail = 0;
      for (int i = 0; i < canMsg.can_dlc; i++)
      {
        can405[i] = (canMsg.data[i]);
      }

      break;
    }
*/
    case 0x404:
    {
      count = 0;

      for (int i = 0; i < canMsg.can_dlc; i++)
      {
        can404[i] = (canMsg.data[i]);
      }
    }
    }
  }

  return 0;
}

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(MCP2515::TXB1, &canMsg1);
}

void print_data()
{
  
  Serial.print(" 404 ");
  for (int i = 0; i < 8; i++)
  {
    Serial.print(can404[i], HEX);
    Serial.print(" ");
  }
  Serial.print(" 400 ");
  for (int i = 0; i < 4; i++)
  {
    Serial.print(can400[i], HEX);
    Serial.print(" ");
  }
  //Serial.print(" 405 ");
  //for (int i = 0; i < 8; i++)
  //{
  //  Serial.print(can405[i], HEX);
  //  Serial.print(" ");
 // }

  Serial.print(" 408 ");
  for (int i = 0; i < 8; i++)
  {
    Serial.print(can408[i], HEX);
    Serial.print(" ");
  }

   Serial.print(" fail: ");
  Serial.print(fail);

  Serial.print(" a-time ");
  Serial.print(a_time);
  
   Serial.print(" idle: ");
  Serial.print(idle_time);

  Serial.print(" MCP: ");
  Serial.print(mcp_time);

  Serial.print(" ErrIf: ");
   Serial.println(mcp2515.getErrorFlags(),2);
}
c_mcp2515.cpp

Code: Alles auswählen

#include "c_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);
}

 void MCP2515::setInterruptRx1()
 {

setRegister(MCP_PinControl,4);
setRegister(MCP_RXB1CTRL,5);
 }
 

c_mcp2515.h

Code: Alles auswählen

#ifndef _C_MCP2515_H_
#define _C_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)
    };

  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 uint32_t SPI_CLOCK = 10000000; // 10MHz

    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 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);
    void setRegister(const REGISTER reg, const uint8_t value);
    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();
    void setInterruptRx1();
};

#endif

can.h

Code: Alles auswählen

#ifndef CAN_H_
#define CAN_H_

#include <stdint.h>


typedef unsigned char __u8;
typedef unsigned short __u16;
typedef unsigned long __u32;


/* special address description flags for the CAN_ID */
#define CAN_EFF_FLAG 0x80000000UL /* EFF/SFF is set in the MSB */
#define CAN_RTR_FLAG 0x40000000UL /* remote transmission request */
#define CAN_ERR_FLAG 0x20000000UL /* error message frame */

/* valid bits in CAN ID for frame formats */
#define CAN_SFF_MASK 0x000007FFUL /* standard frame format (SFF) */
#define CAN_EFF_MASK 0x1FFFFFFFUL /* extended frame format (EFF) */
#define CAN_ERR_MASK 0x1FFFFFFFUL /* omit EFF, RTR, ERR flags */

/*
 * Controller Area Network Identifier structure
 *
 * bit 0-28 : CAN identifier (11/29 bit)
 * bit 29   : error message frame flag (0 = data frame, 1 = error message)
 * bit 30   : remote transmission request flag (1 = rtr frame)
 * bit 31   : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
 */
typedef __u32 canid_t;

#define CAN_SFF_ID_BITS     11
#define CAN_EFF_ID_BITS     29

/* CAN payload length and DLC definitions according to ISO 11898-1 */
#define CAN_MAX_DLC 8
#define CAN_MAX_DLEN 8

struct can_frame {
    canid_t can_id;  /* 32 bit CAN_ID + EFF/RTR/ERR flags */
    __u8    can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
    __u8    data[CAN_MAX_DLEN] __attribute__((aligned(8)));
};

#endif /* CAN_H_ */

Benutzeravatar
Toni
Beiträge: 2523
Registriert: Di 13. Aug 2013, 18:24

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Toni »

Hightech hat geschrieben: So 22. Jan 2023, 17:45 ....

Das ich mir mal wieder einen SB3000 dabei gekillt habe.... naja.
Ich glaub ich hab noch Ersatztransen ...
Weißt du was genau den SB3000 gekillt hatte?
Benutzeravatar
Hightech
Beiträge: 11306
Registriert: So 11. Aug 2013, 18:37

Re: Conti Fahrrad Akku Technik Faden.

Beitrag von Hightech »

Ja, ein leeren 2200yF Kondensator an den geladenen PV Eingang angeschlossen .
Antworten