Damit man ein selbst geschriebenes Programm in einen PIC brennen kann, benötigt man einen Brenner und eine Software, die diesen Brenner steuert. sprut hatte dafür eine recht komfortable Windowssoftware in Delphi geschrieben. Die entsprechenden Quellen hat er nicht veröffentlicht. Für Linux schrieb er ein Kommandozeilentool „usburn“ mit entsprechenden Quellen, die er auch veröffentlichte. Dieses in C geschriebene Programm bildete die Grundlage für das hier vorgestellte USBurnCPP.
Als ich mit der Portierung begann, war ich noch in der Annahme, die Portierung in wenigen Wochen abzuschließen. Es hat doch etwas länger gedauert, insbesondere weil ich die Quellen von sprut nicht vorbehaltlos übernehmen und ein paar moderne Bibliotheken einbauen wollte. Da ich den kompletten Code auch veröffentlichen wollte, hatte ich auch den Anspruch einen „sauberen“ Code zu schreiben. Meine letzte (professionelle) C++ Programmierung liegt mehr als 25 Jahre zurück und somit ist auch meine C++ Erfahrung zu relativieren. Erfahrene C++ Programmierer werden einige Stellen im Code oder auch die Struktur zurecht kritisieren können. Gern nehme ich diese konstruktive Kritik entgegen.
Erste Herausforderung: die USB-Bibliothek
sprut hat auf die Bibliothek libusb-0.1 gesetzt. Diese Bibliothek wird nicht länger gepflegt. Für mich völlig unverständlich ist die Tatsache, dass Debian und Ubuntu diese alte Bibliothek mit ihren Distributionen weiterhin ausliefern. Es wird noch nicht einmal die bessere Variante libusb-compat-0.1 (compatibility library) genutzt. Demgegenüber gibt es die klare Empfehlung für neuere Projekte die Bibliothek libusb-1.0 zu verwenden. Siehe dazu auch https://github.com/libusb/libusb-compat-0.1/wiki.
Auch USBurnCPP nutzt die libusb-1.0
Zweite Herausforderung: die usburn-Datenbank
sprut hat damals eine Pascal-Datenbank genutzt, um die PIC-spezifischen Daten zu speichern. Es gab eine rudimentäre Dokumentation. Da ich eine einfache Möglichkeit anstrebte, diese Datenbank und damit die Reihe der unterstützten PICs zu erweitern, wollte ich diese Datenbank auf ein JSON-Format portieren und somit eine Erweiterung mit einem normalen Text-Editor ermöglichen.
Die Umsetzung der binären Daten in ein lesbares JSON-Format war relativ einfach. Dazu nutzte ich die Bibliothek „JSON for Modern C++“ von Niels Lohmann.
Nachdem ich die Datenbank in JSON umgesetzt hatte, habe ich während der Programmierung einige Daten in der Datenbank verändert, ohne die grundsätzliche Struktur zu verändern. Diese Veränderung betraf vor allem die Definition von Bereichen und diente dazu, die C++ Programmierung aus meiner Sicht lesbarer zu gestalten.
Zum Beispiel definierte sprut die Programmspeicher-Bereich (flash) eines PICs mit folgenden Einträgen (die erste und die letzte Adresse wurde definiert):
"PGMMEM_Min": 0,
Abgesehen davon, dass ich den Bezeichnern einen etwas mehr lesbaren Begriff gegeben habe, habe ich diesen Bereich mit der Start-Adresse und der Größe des Programmspeichers definiert:
"ProgramMemory_Address": 0,
In die Datenbank habe ich zudem eine Versionierung aufgenommen. Die sprut-Datenbank „setdef03.dat“ (settings) habe ich nicht in das Programm aufgenommen, da sich mir der Zweck nicht erschloss.
Dritte Herausforderung: Schreiben des Intel-Hex-Formats
Der Programmcode wird vom Assembler oder Compiler in ein Intel-Hex-Format umgesetzt. Die mit einem normalen Text-Editor lesbaren Programmdaten werden später vom Brenner in den PIC geschrieben (gebrannt). sprut hat dafür eigene Routinen geschrieben. Ich wollte nicht auf diese Routinen vertrauen und suchte nach einer Bibliothek, die sich auch in anderen Projekten bewährt hatte. Fündig bin ich auf GitHub geworden und habe die dort veröffentlichen Sourcen Intel HEX (IHEX) format read/write library in C genutzt. Allerdings hatte ich ein paar nicht so schöne Aspekte bei der Integration in das C++ Programm. Um das zu verbessern, habe ich ein paar C++ Klassen geschrieben, die eine Integration in C++ Programmen transparenter oder verständlicher machen.
Ursprünglich wollte ich die Anpassungen dem Autor zur Verfügung stellen, damit er diese Anpassung in sein GitHub-Projekt aufnehmen kann. Eine diesbezügliche Anfrage ließ er aber unbeantwortet.
Das Ergebnis USBurnCPP
Herausgekommen ist ein C++ Programm mit folgenden Quelldateien:
- Brenner8.h
Globale Definitionen von Datenstrukturen und Datentypen. sprut hat für den Brenner8 die Vendor-ID 0x04D8 und die Produkt-ID 0xFF0B defininiert. Diese Definitionen befinden sich auch in dieser Datei. - Main.cpp
Wie es der Name auch schon sagt, die Datei mit der main-Funktion (Start des Programms). - TUsbBasis.cpp, TUsbBasis.h
Klassen-Definition zur Ansteuerung des USB-Interfaces - TUsbBrennerBasis.cpp, TUsbBrennerBasis.h
erweitert die Klasse TUsbBasis
Klassen-Definition zur Ansteuerung des Brenners8 - TUsbBrenner.cpp, TUsbBrenner.h
erweitert die Klasse TUsbBrennerBasis
Klassen-Definition zur Steuern der Programmierung eines PICs - TCalibration.cpp, TCalibration.h
Friend-Klasse der Klasse TUsbBrennerBasis
Klassen-Definition zur Kalibrierung des Brenners8 - THardwareTest.cpp, THardwareTest.h
Friend-Klasse der Klasse TUsbBrennerBasis
Klassen-Definition für den Hardware-Test des Brenners8 - TCommandLineOptions.cpp, TCommandLineOptions.h
Klassen-Definition der Kommandozeilen-Schalter und Parameter - THelpinfos.cpp, THelpinfos.h
Klassen-Definition zur Ausgabe von Hilfe-Informationen - TPicHexContent.cpp, TPicHexContent.h
Klassen-Definition zum Lesen und Schreiben in das Intel-Hex-Format - TPicDatabase.cpp, TPicDatabase.h
Klassen-Definition zum Lesen und Suchen in der „Datenbank“-Datei PicDatabase.json - TConfigDatabase.cpp, TConfigDatabase.h
Klassen-Definition zum Lesen und Suchen in der „Datenbank“-Datei ConfigDatabase.json - TFieldDatabase.cpp, TFieldDatabase.h
Klassen-Definition zum Lesen und Suchen in der „Datenbank“-Datei FieldDatabase.json - TUsbException.cpp, TUsbException.h
Klassen-Definition für einige spezifische Exceptions - TemplateProperties.h
Template-Definitionen zur Definitionen von Eigenschaften (Verwendet für TCommandLineOptions)
Kompilieren von USBurnCPP
USBurnCPP wurde in C++ erstellt. USBurnCPP läuft unter Ubuntu 24.04 und ist im Zip-Archiv als binäre Programmdatei verfügbar. Zusätzlich sind die Quellen des Programms und json-Datenbanken verfügbar, die sich mit einem normalen Text-Editor editieren lassen. Zum Kompilieren der C++ Quellen ist ein GNU C++ Compiler nötig. Dabei ist der Dialekt 'ISO C++20' (-std=c++20) einzustellen. USBurnCPP nutzt die Bibliothek „JSON for Modern C++“ von Niels Lohmann. Diese Bibliothek ist auf GitHub verfügbar und von dort herunterzuladen. Weitere Infos auch in der ReadMe-Datei, welche sich im Zip-Archiv befindet.
Das Zip-Archiv mit USBurnCPP ist herunterzuladen und in ein beliebiges (Projekt-)verzeichnis zu entpacken. Im entpackten Archiv befindet sich das Build-Skript BuildUSBurnCPP.bash, welches zum Kompilieren mit normalen User-Rechten aufgerufen werden kann. Nach dem Kompilieren wird das Installationsskript aufgerufen, für welches man root-Rechte benötigt.
Zip-Archiv mit den Programm-Quelldateien USBurnCPPSource.zip herunterladen
ChangeLog:
- 13. Juli 2026 Version 1.1
- Der Modus für die Stabilisierung der Programmierspannung habe ich nun auf "Continuous" gesetzt. Dieser Modus war in dem von sprut bereitgestellten Programm usburn noch auf "ReducingVoltage" gestellt. Siehe auch die Source-Datei TUsbBrennerBasis.h Zeile 95.
- Anpassungen der Build- und Installations-Skripte, um das Übersetzen und die Installation für den unerfahrenden Linux-Benutzer zu verbessern.
- 30.05.2026 Version 1.0
- Erste veröffentlichte Version
Installation von USBurnCPP
Zur Installation sind root-Rechte erforderlich. Die Installation wird vom mitgelieferten Skript BuildUSBurnCPP.bash angestoßen. Das Skript überprüft die Verfügbarkeit der zu installierenden Dateien und gliedert sich in drei Blöcke:
- Programmdatei USBurnCPP in /usr/bin kopieren (Entweder wird die im Zip-Archiv mitgelieferte Programmdatei unter ./database/USBurnCPP oder die selbst kompilierte Programmdatei unter ./build/USBurnCPP genutzt.)
Das Kompilieren der Programmdatei kann man durch Löschen der Datei ./database/USBurnCPP erzwingen. - Die json-Datenbank-Dateien (PicDatabase.json, ConfigDatabase.json, FieldDatabase.json) werden in das Vereichnis /etc/USBurnCPP kopiert. Wenn dieses Vereichnis nicht existiert, wird es angelegt.
- Die Regeldatei (sprut.rules) zur Steuerung der Berechtigung für den Zugriff auf das USB-Gerät wird in das Verzeichnis /etc/udev/rules.d/ kopiert und danach der Dienst udev neu gestartet.
Ablauf des Build- und Installationsskripts am Terminal:

Nach der Installation sollte sich das Programm ohne Fehler aufrufen lassen. Ohne Parameter gibt es eine Hilfe-Seite mit den verfügbaren Schaltern aus. Ist ein Brenner8 angeschlossen, kann man sich mit USBurnCPP -l die unterstützten PIC-Prozessoren anzeigen lassen.
Berechtigung für USBurnCPP um auf das USB-Gerät zuzugreifen
Die hier erläuterte Konfiguration wird vom Installationsskript vorgenommen und dient hier nur der Dokumentation. Es ist also nicht nötig, diese Schritte manuell durchzuführen.
Der Brenner8 wird von USBurnCPP über USB angesprochen. Damit das einer Applikation wie USBurnCPP auch gelingt, muss der Zugriff auf das USB-Device berechtigt werden. Dies geschieht am einfachsten über den Dienst udev, welches Gerätedateien dynamisch verwaltet. Die dafür geltenden Regeln legt man in einer einfachen Text-Datei im Verzeichnis /etc/udev/rules.d/ fest. Der folgende Inhalt erlaubt Schreib- und Leserechte auf das USB-Interface für alle Benutzer:
SUBSYSTEM=="usb_device", ATTRS{idVendor}=="04d8", MODE="0666"
Die Vendor-ID "04d8" hat sprut festgelegt und wird auch vom Programm USBurnCPP verwendet (siehe auch die Header-Datei Brenner8.h). Idealerweise legt man den Namen der Datei mit sprut.rules oder auch brenner8.rules fest. Wenn man möchte, kann man den Zugriff auf das USB-Interface auch mit der Produkt-ID weiter einschränken. Dazu müssten die Zeilen hinter der Angabe zur Vendor-ID mit ATTRS{idProduct}=="ff0b" ergänzt werden (Komma nicht vergessen). Dies ist aber hier nicht notwendig.
Nach dem diese Regeln hinzugefügt wurden, muss der Dienst udev neu gestartet werden.
Alternativ können auch die Regeln manuell getriggert werden:
Danach sollte USBurnCPP ohne root-Rechte auf das USB-Gerät bzw. auf den Brenner8 zugreifen können.
Anwendung von USBurnCPP
USBurnCPP ist ein Kommandozeilen-Tool. Eine grafische Benutzersoberfläche (GUI) gibt es (von mir) nicht. Ruft man USBurnCPP ohne irgendwelche Parameter auf, werden folgende Hilfe-Informationen ausgegeben:

Eine kurze Erläuterung der wichtigsten Optionen des Programms USBurnCPP findet ihr auf einer eigenen Seite: Beschreibung der Schalteroptionen für USBurnCPP.
Funktionale Tests mit USBurnCPP und dem Brenner8
Ich habe USBurnCPP nur mit dem Brenner8 getestet. Ob USBurnCPP mit dem Brenner8mini oder dem Brenner9 funktioniert, kann ich in Ermangelung dieser Brenner nicht sagen. Vermutlich funktionieren diese. Hier wäre ich an ein Feedback interessiert. Die in der Tabelle unten stehenden PICs habe ich mit USBurnCPP/Brenner8 erfolgreich getestet. Die verwendeten Schalter gehen aus der Tabelle hervor.
| PIC | CPU-Familie | Testsockel / ICSP | --blankcheck | --READ | --COMPARE | --WRITE |
| PIC12F508 | 12-Bit-Kern | Testsockel, 8 Pins | -S 8 -PPIC12F508 -b | -S8 -PPIC12F508 -R | -S8 -PPIC12F508 -C PIC12F508.hex |
-S8 -PPIC12F508 -W PIC12F508.hex |
| PIC12F629 | 14-Bit-Kern | Testsockel, 8 Pins | -S 8 -b | -S8 -R | -S8 -C PIC12F629.hex | -S8 -W PIC12F629.hex |
| PIC12F675 | 14-Bit-Kern | Testsockel, 8 Pins | -S 8 -b | -S 8 -R | -S8 -C PIC12F675.hex | -S8 -W PIC12F675.hex |
| PIC16F636 | 14-Bit-Kern | ICSP | -S ICSP -F 14 -b | -S ICSP -F 14 -R | -S ICSP -F 14 -C PIC16F636.hex |
-S ICSP -F 14 -W PIC16F636.hex |
| PIC16F1826 | 14-Bit-Kern | Testsockel, 18 Pins | -S 18 -b | -S 18 -R | -S 18 -C PIC16F1826.hex |
-S 18 -W PIC16F1826.hex |
| PIC18F2550 | 16-Bit-Kern | Testsockel, 28 Pins | -S 28 -F16 -b -S 28 -P PIC18F2550 -b |
-S 28 -F16 -R | -S 28 -F16 -C PIC18F2550.hex |
-S 28 -F16 -W PIC18F2550.hex |
| PIC18F442 | 16-Bit-Kern | Testsockel, 40 Pins | -S 40 -F 16 -b | -S 40 -F16 -R PICProgram.hex | -S 40 -F 16 -C PIC18F442.hex |
-S 40 -F 16 -W PIC18F442.hex |
| PIC18F458 | 16-Bit-Kern | Testsockel, 40 Pins | -S 40 -F 16 -b | -S 40 -F 16 -R | -S 40 -F 16 -C PIC18F458.hex |
-S 40 -F 16 -W PIC18F458.hex |
Bekannte Probleme
Durch das Löschen der Code-Protection mit dem Schalter (-p) von 12-Bit-Kern PICs wird auch der Backup-Wert für den OSCCAL-Wert (beim PIC12F508 an der Adresse 204h) gelöscht. Dieser Backup-Wert wird nicht zurückgeschrieben. Der OSCCAL-Wert bleibt aber im Programmspeicher des PICs erhalten, wodurch dieser Wert nicht wirklich verloren geht. Ich habe die Funktionalität zum Erhalt des OSCCAL-Backup-Wertes nicht programmiert, da dies nur die sehr einfachen PICs betrifft und diese kaum noch verwendet werden (glaube ich - man möge mich belehren ;-).
Nach dem Lesen oder dem Schreiben eines PIC16F1826 wird die Chip-ID des Prozessors vom Brenner8 nicht mehr ausgelesen. Da ich keinen Zugriff auf die Quellen der Firmware des Brenners8 habe, kann ich mir nicht die Ursache anschauen. Abhilfe: das USB-Kabel zum Brenner kurz aus dem PC ziehen und wieder einstecken. Durch diesen Reset liest der Brenner auch wieder die Chip-ID des PIC16F1826.
Aufruf des Programms gibt einen Fehler "undefined symbol: libusb_strerror" aus
Durch eine Aktualisierung des Betriebssystems kann es vorkommen, dass bestimmte Bibliotheken nicht mehr gefunden werden, da sich der Pfad zu diesen geändert hat. Auch ein Aufruf von
schlägt fehl. Abhilfe schafft man, indem man den Pfad zu der Bibliothek in die Umgebungsvariable LD_LIBRARY_PATH schreibt.
Auflisten der Pfade zu den Bibliotheken:
...
/usr/lib/x86_64-linux-gnu/libusb-1.0.a
...
/usr/lib/x86_64-linux-gnu/libusb-1.0.so
...
$
