Die Mikrocontroller
PIC16F1454,
PIC16F1455 und
PIC16F1459
sind gerade fĂŒr Bastler Ă€uĂerst interessante Chips,
da es diese sehr preiswert im Durchsteck-GehÀuse
zu kaufen gibt und diese einen quarzlosen
USB-Full-Speed-Devicecontroller enthalten.
Mit gerade mal 2 externen Kondensatoren kann damit
ein USB-Seriell-Konverter USB-standardkonform realisiert werden.
Mit 8 KWort Flash kann man auch komlexere Aufgaben in C
ausprogrammieren.
FĂŒr 14-Bit-Core-PICs bieten diese Chips ziemlich
umfangreiche Peripherie, etwa einen 16-Bit-Zeitgeber,
serielle Schnittstellen
(UART,
SPI und
IÂČC)
sowie (nicht beim '1454) einen
10-Bit-A/D-Wandler.
AuĂerdem einen asynchronen Quarzoszillator, gemacht fĂŒr autonome Datenlogger.
Wie bei allen vierstelligen PICs mit 14-Bit-Core gibt es (endlich!)
eine lineare RAM-Adressierung, die die jeweils 80 RAM-Bytes in jeder einzelnen Bank
ab der Adresse 0x2000 spiegelt.
Und auch der Flash-Speicher ist ohne goto
-Gewurstel
ansprechbar und ab Adresse 0x8000 eingeblendet. (D.h. nur die Low-Bytes.)
Der PferdefuĂ mit diesem Chips ist nur, dass diese nicht mit einem USB-Urlader ausgeliefert werden, sodass deren Erstkontakt mit einem Programmierinterface erfolgen muss, etwa dem PICKIT2 oder einer einfachen Bastelschaltung am Parallelport.
Deshalb gibt es im Web eine Reihe USB-Urlader, mit eher mauer FunktionalitĂ€t und mieser KomatibilitĂ€t, oftmals viel zu groĂ. Die IdealgröĂe des Urladers fĂŒr diese Chips ist 512 Programmworte, weil diese GröĂe via Konfigurationsbits gesondert vor Ăberschreiben abgesichert werden kann. USB bekommt man da nur mit AssemblerÂprogrammierung hinein, bei Verzicht auf Interrupts sogar bequem.
Der ĂŒbrig bleibende Platz in der Firmware dient schlieĂlich dazu, einfache Aufgaben der Labormesstechnik ganz ohne eigene Firmware lösen zu können, wie das Einlesen eines Analogwertes oder die Steuerung einer digitalen Leitung. Damit erleichtert sich der Einstieg in diese Mikrocontroller ungemein: Kein Assembler, kein C-Compiler wird benötigt, um Portpins analog oder digital abzufragen oder zu setzen.
Der Einsatz eines USB-Urladers macht das Programmieren rasend schnell! Die 8 KWort sind innerhalb von 1,3 s gelöscht und neu beschrieben. Die Gesamtzeit setzt sich aus 1,1 s Flash-Zeit und 0,2 s Datentransferzeit zusammen.
Eingesetzt wird bei mir ein solcher Mikrocontroller in folgenden Projekten:
Hier ist er: Der Urlader Er lÀuft nun auch unter Windows. Und erzeugt nicht permanent USB-Verkehr wie die Vorlage: Diese generierte stÀndig 0-Byte-Pakete, anstatt die Sache mit dem Data-Toggle richtig zu imlementieren und nur dann etwas zu senden, wenn er gefragt wird.
Die beigefĂŒgte INF-Datei ist derart frisiert, dass fortan alle USB-Seriell-Konverter (u.a. alle Arduinos) von Windows ohne Treiberanfrage installert werden. Eine Bindung an USB-VID&PID erfolgt nicht.
Der Urlader ist trotzdem kleiner als das Original von der SoftwaremĂŒllhalde. Die Art der Funktion wird von der LĂ€nge des OUT-Transfers bestimmt. Das obligatorische IN-Byte (Antwort) macht zwar die Kommunikation etwas langsamer, verhindert jedoch zuverlĂ€ssig, dass Softwareschichten (bspw. NI-VISA) Bytes zusammenfassen, was hier unzulĂ€ssig wĂ€re.
LĂ€nge in Bytes | Funktion | Daten (LĂ€nge Bytes) | Antwort (1 Byte) |
---|---|---|---|
0 | wird ignoriert | keine | keine |
1 | Reset: Sprung zum Anwenderprogramm = Adresse 0x0200 | 'R' |
|
2 | Byte lesen | Adresse | Datenbyte |
3 | Byte schreiben |
|
|
4 | Flash-Schreiben vorbereiten und ggf. Flash-Speicherseite löschen; Flash lesen |
|
|
64 | Flash beschreiben | 32 14-Bit-Worte (High-Bits 0) |
|
Mit der Funktion Byte lesen
kann man ein Byte
von jedem I/O-Port, vom RAM und vom Flash lesen.
Um Bytes vom Flash zu lesen, ist das Bit 15 der Adresse zu setzen.
Es kann so nur das Low-Byte gelesen werden.
Achtung! Einige Portadressen haben beim Lesen Seiteneffekte!
Mit der Funktion Byte schreiben
kann man ein Byte
auf jedes I/O-Port und in den RAM schreiben.
Achtung! Alle Portadressen haben (logischerweise) Seiteneffekte!
Aber auch das Beschreiben des RAM und von Sonderfunktionsregistern
kann den Urlader durcheinanderbringen.
Dann hilft nur noch ein Reset = Ab- und Anstecken des USB-Kabels.
Die Urlader-Firmware im Flash-Speicher ist unzerstörbar,
durch Fuses gesichert.
Die entsprechenden Portadressen und Bitbelegungen holt man sich vom Datenblatt des Mikrocontrollers. Die wichtigsten:
Name | Adresse | Bedeutung | Vorgabe |
---|---|---|---|
PORTA | 00C |
| |
TRISA | 08C |
| 3B |
LATA | 10C | Ausgangstreiber-Latch | 00 |
ANSELA | 18C | Analog-Auswahl: 0 = analog+digital, 1 = analog | 10 |
WPUA | 20C | Pullup-Enable: 0 = kein Pullup, 1 = Pullup | 38 |
OPTION_REG | 095 | Bit 7: 0 = globale Pullup-Freigabe | 7F |
Um sich ein PIC-ProgrammiergerÀt zu ersparen,
habe ich einen PC mit Parallelport aufgesucht
und die Firmware mit dem Low-Voltage-Programmierinterface
(also keine hohe Reset-Spannung) draufgetan.
Das ist so Àhnlich wie beim
AVR ISP mit MOSI und MISO.
Braucht man unbedingt den
Am einfachsten nehme man sich einen PC mit Parallelport. Man benötigt:
Der mir ĂŒberlassene GALEP-III geht dafĂŒr nicht, die Software GALEP32 kennt diesen Mikrocontroller nicht. Blöd.
Da diese Mikrocontroller verhĂ€ltnismĂ€Ăig viel Flash-Speicher haben, wird man sie in aller Regel in C programmieren. Will man den Urlader fĂŒr Anwendungen benutzen, ist folgendes zu beachten:
#pragma
-Anweisungen sind wirkungslos.
Hier ist es:
Die Portpin-Fummel-Ăpp mit Flash-Funktion
.
Das Windows-Programm ermöglicht es, mit allen Portadessen und Speicherzellen herumzuspielen sowie Firmware hochzuladen. Es lÀuft von Windows 98 bis mindestens Windows 10 und benötigt keine besonderen DLLs. Es ist zweisprachig.
Die Möglichkeit der Manipulation ist eher als Demo-Programm zu sehen, wie man ohne einen Strich eigene Firmware schreiben zu mĂŒssen einige alltĂ€gliche Probleme der Laborautomatisierung sehr preiswert lösen kann. So genĂŒgt es bspw. in LabVIEW, mit dem vorhandenen Knoten zum Senden und Empfangen von der seriellen Schnittstelle (seltsamerweise unter VISA zu finden) digitale AusgĂ€nge zu steuern, einzulesen und in der Richtung umzuschalten. Ein NI USB-6008 und ein Arduino (jener leider nur mit Firmware) kann das auch, aber ein Durchsteck-Chip ohne Quarz am USB und mit einem winzigen USB-Urlader, das ist doch schon fetzig.
AuĂerdem kann man damit ohne Steckerstöpseln und ohne sich ĂŒber die USB-Vendor- und Produkt-ID (VID&PID) einen Kopf machen zu mĂŒssen seine Firmware drauftun. Der mit den Fuses gesperrte Urlader-Bereich, der nur 1â16 des gesamten Flashs abzwackt, ist vor dem Ăberschreiben sicher. ZurĂŒck zum Urlader geht es mit dem RESET-Eingang, falls sich die Firmware mal verhakt. (Dieser Eingang steht fĂŒrs Anwendungsprogramm erst mal nicht zur VerfĂŒgung.)
Beim DrĂŒcken auf âProgrammierenâ wird die angegebene HEX-Datei stets neu eingelesen. Damit kann man das Fenster immer offen stehen lassen. Emuliert das Anwendungsprogramm keine serielle Schnittstelle via USB, wird bei âAnschlussâ umgehend nichts angezeigt. Hervorholen lĂ€sst sich das COM-Port durch Reset des Mikrocontrollers:
Die aktuelle Version kann auch mit Kommandozeile (Hex-Dateiname) gestartet werden, dann wird diese Datei geflasht. Die angeschlossene PIC wird automatisch erkannt. Zudem zeigt sie 16-Bit-Werte 16-bittrig an, etwa FSR. Nicht vom Lesezugriff abhÀngige Register (d.h. die meisten) werden permanent ausgelesen. So kann man beim Beobachten von PORTC den Pegel der EingÀnge verfolgen. Nicht vergessen, ANSELC muss dazu vorher auf 0 gesetzt werden.
Der freie C-Compiler ist fĂŒr PICs mit 14 Bit Wortbreite wie dem PIC16F1459 nicht allzu eingĂ€ngig verwendbar. Es mĂŒssen eine Reihe von HĂŒrden genommen werden, um ein brauchbares Kompilat sowie die Kontrollmöglichkeit eines gut lesbaren Disassemblerlistings zu bekommen, was mit avr-gcc kein Problem darstellt. ZunĂ€chst geht es um die Kontrolle des Startups. Ist dieser Teil verstanden, geht es um die Verschiebung des Kodes um die GröĂe des Urladers von 512 Words.
Gemeinsam mit SDCC benötigt man GPUTILS. Möchte man keine PIC18 bearbeiten, kann man getrost alle Dateien mit dem Muster "p18*" und "pic18*" löschen, um den genutzten Festplattenplatz zu halbieren.
Ich habe Umgebungsvariablen so gesetzt.
Beim Umstieg von acr-gcc auf SDCC sind folgende Programmier-Eigenheiten zu beachten:
avr-gcc | SDCC | Anmerkung |
---|---|---|
|
| Byte-Ausgabe: Andere, verwirrende Namen fĂŒr dasselbe Ding |
|
| Byte-Eingabe: Andere, verwirrende Namen fĂŒr dasselbe Ding; SDCC kann kein C99, keine Variablendeklaration mittendrin |
|
| Bit-Definitionen bei PIC als Maske, nicht als Bitnummer |
|
| Einzelbit-Zugriff; Tristate-Bits andersherum |
|
| DigitaleingÀnge sind bei PIC nach Reset abgeschaltet |
|
| Bei PIC wird stets die Peripherie mit abgeschaltet! |
Um den Startup-Kode zu minimieren oder zu entfernen, muss man an der Zwischen-Assemblerdatei herumfummeln. Es geht wirklich nicht besser! Dort habe ich die Info her. Im Beispiel:
sdcc -mpic14 -p16f1459 --use-non-free -S main.c sed -e "/^STARTUP/./goto/ d" main.asm > main.a14
Die erste Zeile compiliert die C-Datei zu einer Assemblerdatei (Option "-S"). Diese enthÀlt die folgenden Zeilen:
STARTUP code 0x0000 nop pagesel __sdcc_gsinit_startupgoto __sdcc_gsinit_startup
TatsĂ€chlich enthĂ€lt also die relozierbare Assemblerdatei "main.asm" einen nicht-relozierbaren Bereich. Dieser muss weg und kann durch einen eigene Startup-Datei ersetzt werden. Das erledigt das sed-Kommando. Dabei ermöglicht die Endung "a14" das Einrichten von Text-Editoren fĂŒr spezifische Architekturen. sed und awk sind bereits auf dem Computer, wenn man winavr installiert hat: Kleine Utilities (= EXE-Dateien), die keiner Installation bedĂŒrfen. Angenehm!
Das nop am Anfang ist fĂŒr den Debugger reserviert. Benötigt man es nicht, wird an der Adresse 0x0002 Platz fĂŒr einen weiteren Einsprung, was in Verbindung mit dem obigen Urlader interessant wird. Dazu siehe unten.
Das Programm ohne Startup-Kode kann immer noch lauffÀhig sein!
Allerdings nur wenn es ausschlieĂlich aus main()
besteht.
Initialisierungsaufgaben werden nicht ausgefĂŒhrt:
Statische Variablen werden nicht initialisiert oder auf Null gesetzt.
Ein Unterprogramm wĂŒrde vor main()
aufgerufen
und beim anschlieĂenden return
abstĂŒrzen.
Daher ist ein eigener Startup-Kode gĂŒnstig:
STARTUP code 0 extern _mainpagesel _maingoto _main end
So wird an Adresse Null ein Sprung zu main()
realisiert, egal wo die Funktion im Flash liegt.
(pagesel ist einer der
neuen Befehle der Enhanced-PICs:
Compiliert zu movlp adr>>8, setzt PCLATH.)
Das ist schon sehr minimalistisch.
Beide Assemblerdateien werden wie folgt zu einer HEX-Datei zusammengefĂŒhrt:
gpasm -c main.a14 gpasm -p 16f1459 startup.a14 gplink -o main.hex -C startup.o main.o pic16f1459.lib
Die Option "-C" unterdrĂŒckt _cinit-Warnungen. Die Prozessorangabe wird bei "main.a14" nicht benötigt, dort steht sie im Quelltext. Umgebungsvariablen mĂŒssen folgendermaĂen gesetzt sein:
set SDCC_HOME=c:/programs/pic/sdcc set GPUTILS_HEADER_PATH=%SDCC_HOME%/header set GPUTILS_LIB_PATH=%SDCC_HOME%/non-free/lib/pic14 set GPUTILS_LKR_PATH=%SDCC_HOME%/lkr
Jetzt wĂ€re eine brauchbare Listing-Datei zur Kontrolle hilfreich. gpdasm muss man etwas auf die SprĂŒnge helfen, wenn auffindbare Labels eingefĂŒgt werden sollen. Dazu muss eine Label-Datei generiert werden. Aus der COD-Datei, die beim Linken entsteht, mittels gpvc. Damit fĂŒttert man dann gpdasm, und bearbeitet den Output von gpdasm nochmals, um Leerzeilen und schwachsinnige Kommentarzeilen zu entfernen.
gpvc -s main.cod | awk -- "BEGIN{print \"[CODE]\"} /address$/ {print $1=\"0x\"$3}" > main.ulist gpdasm -p16f1459 -nt -k main.ulist main.hex | sed -e "/^$/ d" -e "/^;/ d" > main.d14
awk ist um einiges leistungsfÀhiger als sed. Hier geht es um das Davorsetzen von [CODE] vor die Liste.
Alternativ wÀre es möglich, eine MAP-Datei als Ausgangspunkt zu verwenden. Hier stehen auch lokale (statische) Adressen. Ist aber um einiges schwieriger zu filtern.
Eine vernĂŒnftige Beschreibung dieser ulist-Datei gibt es nicht, nur eine Beispiel-Datei.
Dazu muss an 2 Stellen angesetzt werden:
Das Linkerskript, das normalerweise so aussieht:
⊠CODEPAGE NAME=page0 START=0x0 END=0x7FF CODEPAGE NAME=page1 START=0x800 END=0xFFF CODEPAGE NAME=page2 START=0x1000 END=0x17FF CODEPAGE NAME=page3 START=0x1800 END=0x1FFF CODEPAGE NAME=.idlocs START=0x8000 END=0x8003 PROTECTED CODEPAGE NAME=.devid START=0x8006 END=0x8006 PROTECTED CODEPAGE NAME=.config START=0x8007 END=0x8008 PROTECTED âŠ
wird an der ersten Zeile verdoppelt:
CODEPAGE NAME=boot START=0x0 END=0x1FF PROTECTED CODEPAGE NAME=page0 START=0x200 END=0x7FF
Ich habe es kurzerhand im Verzeichnis der Linkerskripte geÀndert.
Wie man sieht, ist der Kodespeicher in vier Abschnitte (Pages)
so aufgeteilt,
dass in jedem ein goto
oder call
lokal funktioniert.
Diese Befehle haben 11-Bit-Absolutadressen.
Um von einer Page zur anderen zu springen wird
movlp
benötigt.
Nur bei sequenzieller Kode-Abarbeitung sowie beim neuen
bra
-Befehl (relativer Sprung) wandert man
problemlos zwischen den Pages; die Pages werden bedeutungslos.
Die Verschiebung der beiden festen Adressen erfordert das Patchen der Zwischen-Assemblerdateien, wie oben angegeben. Besser geht's leider nicht!
sdcc -mpic14 -p16f1459 --use-non-free -S main.c sed -e "s/0x0000$/0x0200/" -e "s/0x0004$/0x0204/" main.asm > main.a14
Um einen Einsprung an Adresse 0x0202 fĂŒr den Urlader-Kode zu ermöglichen, muss der Startup-Kode ersetzt werden. UngefĂ€hr so:
STARTUP code 0x0200 extern _main,_usbRxpagesel _maingoto _main pagesel _usbRxgoto _usbRx end
Die main.asm wird dann wie folgt bearbeitet:
sdcc -mpic14 -p16f1459 --use-non-free -S main.c sed -e "/^STARTUP/./goto/ d" -e "/0x0004$/0x0204/" main.asm > main.a14
Das entfernt den Startupkode und verschiebt den Interruptkode. Beides wird wie oben beschrieben zusammengefĂŒgt. Das Hauptprogramm muss irgendwo die Funktion usbRx() definieren.
Der Urlader ist so gemacht, dass man die USB-Routinen samt Deskriptoren und Puffer im Anwendungsprogramm wiederverwenden kann. Dazu sind folgende Vorkehrungen zu treffen:
Zu beachten ist, dass ein Datenpaket von usbTx(len)
erst dann
bei Windows/Linux ankommt, wenn es kĂŒrzer als 64 Byte lang ist.
64-Byte-Pakete werden hingegend von der USB-Treiberschicht zu einem lÀngeren Block zusammengesetzt;
ein ReadFile()
bzw. read()
bleibt blockiert,
selbst wenn nur 64 Byte oder weniger gelesen werden sollen,
denn das ZerstĂŒckeln ĂŒbernimmt eine andere, höher liegende Treiberschicht.
Daher merke: Endet das letzte Paket eines Blocks mit genau 64 Byte,
muss ein 0-Byte-Paket zum Abschluss abgesendet werden.
Da (die gesichteten) C-Compiler ein einzelnes Byte-Argument in W ĂŒbergeben,
können usbTx(byte len)
und usbRx(byte len)
direkt als eine solche Funktion deklariert werden;
es wird kein Assembler-Stub benötigt.
Alle Einsprungpunkte liefern nichts, also void
.
FĂŒr PIC18F2550 und PIC18F25K50 wĂ€re das gleiche zu implementieren. Wegen ihrer 16-Bit-Struktur und der fehlenden Von-Neumannisierung ist das Assemblerprogramm erheblich umzustrukturieren. Andererseits ist deren Urlader-Bereich mit 1 KWord (2 KByte) doppelt so groĂ, was das zusĂ€tzliche Anbieten von HID und/oder WinUSB/WebUSB ermöglicht.
Unter Linux könnte man ja das Python-Skript verwenden, wenn da nicht jedesmal das Problem der fehlenden serial-Bibliothek wĂ€re! Denn ĂŒblicherweise geht die Installation nur fĂŒr Python3, und die gĂ€ngigen Skripte sind Python2.7. Und Python3 ist zu Python2.7 so gut wie niemals kompatibel! Weniger noch als C und C++!
Unter den vielen vermeintlichen Lösungen fand ich (unter Ubuntu 2022) nur diese funktionierend:
cd /usr/local/lib/python2.7/dist-packages sudo ln -s ../../python3.8/dist-packages/serial serial
Klar, der Vorschlag war Kopieren, aber ich will immer noch Platz sparen und nicht nur kopieren, daher ln -s = symbolischen Link erstellen.