Hallo zusammen,
ich habe mir ein tolles Bastelprojekt ausgedacht, um mal ordentlich
etwas zu lernen über Elektronik, direkte µC-Programmierung und
eingerostetes Programmieren wieder zu üben. Am Ende soll mal eine
"Zeitraffer-Kamera" stehen, die ein Bild am Tag macht.
Angefangen habe ich vor drei Wochen mit meinen ersten Arduinos. Da wurde
schnell klar, dass die damit wahrscheinlich hoffnungslos überfordert
sind. Selbst zum Zwischenspeichern der Pixel von der OV7670-Kamera (640
x 480 x 2 Byte) in eine SPI-RAM-Bank dürfte das Tempo schlicht nicht
reichen. Immerhin bin ich da schon auf Eclipse+avr-gcc ohne Arduino-Lib
umgestiegen und habe alles zu Fuß programmiert. Vergangenen Freitag kam
endlich der STM32F0-Discovery an, IDE war schon vorbereitet. Nach der
üblichen steilen Lernkurve beginnend von LED-Blinken läuft da jetzt auch
was drauf.
Zunächst habe ich serielle Konsole/USART nicht hinbekommen, als ich
meine RAM-Bank aus fünf Modulen 23LC1024, die über einen 74HC138 via
drei Pins auf !CS angesprochen werden, einfach im sequentiellen Modus
vollgeschrieben und wieder ausgelesen sowie verglichen habe. Da habe ich
einerseits Zeitmessung mit Spielzeug-Oszilloskop gemacht (DSO Nano 201)
und mir die Ergebnisse via LED blinken lassen.
Heute habe ich dann doch Ausgabe auf serielle Konsole hinbekommen (man
darf RX/TX nicht beliebig remappen? Ach.). Dort klappte kurze Zeit auch
Lesen und Schreiben auf alle fünf Module. Zwar gab es Auslesefehler und
die Module wurden somit als Defekt gemeldet, aber es lief.
Inzwischen habe ich viel auf- und umgeräumt, etwa direktere Register-
und Pinmanipulation. Das sollte schneller sein - und eben näher an der
Hardware. Nur leider hört es jetzt direkt nach dem Schreiben auf, egal,
wie ich den SPI-Transfer gestalte, was ich an den Pins anstelle, ob ich
einen IRQ-Handler im NVMI einrichte, direkt einzelne RXNE/TXE-Interrupts
aktivere, ...
Vielleicht sehe ich auch einfach irgend etwas offensichtliches nicht.
Habt ihr vielleicht eine Idee?
Vielen Dank schon mal fürs Lesen :)
Ok, das war mein WTF des Tages. Dafür vergeudet man einen Manntag.
Es funktioniert nicht, wenn die geschachtelten Schleifen direkt
untereinanderstehen. Da muss eine Leerzeile hin - und schon läuft es.
Also anstatt:
Langsam zweifle ich ver.
Nachdem das "read" lief, habe ich mich daran gemacht, auch mal etwas
anderes als '0' zu empfangen. Habe bei den Transferroutinen ordentlich
durchgemischt, auch die SPI-Flags entrümpelt, aber an den eigentlichen
Schleifen nichts verändert. Das Chip-Select habe ich etwas weniger
elegant entrümpelt, da ich mit dem Oszi kein CS=low erkennen konnte -
das klappt jetzt auch.
Irgendwann zwischendrin wollte nun auch kein "write" mehr klappen. Er
durchläuft die innere for(j)-Schleife und schubst die Bytes, dass es
eine Freude ist. Nur bleibt das for(i) bei i=0.
Ich verwende im Wesentlichen das Setup wie hier:
http://grafixmafia.net/stm32f4-discovery-and-mac-osx-10-9-mavericks/
Nur mit der aktuellen Toolchain
gcc-arm-none-eabi-4_8-2014q1-20140314-mac
Ich habe die Compiler-Standards schon mal durchgeschaltet (ISO-C99,
GNU-C99, GNU-C11, ...), aber das Ergebnis bleibt gleich. Eine Suche im
Netz fördert bislang nichts Erhellendes zu Tage. startup und
linker-Script passt auf das Board und entstammt (automatisch von Eclipse
eingebunden) aus dem stm32f0-Discovery-FW-Verzeichnis. Eine zusätzlich
im Netz aufgestöberte linker-Script-Datei bringt auch keine Änderung.
Anbei mal ein Schnappschuss, wie der Code grade aussieht.
Dirk K. schrieb:> Nur bleibt das for(i) bei i=0
Meinst du, dass es nur einmal durchläuft und dann returned ?
Oder es bleibt irgendwo (wo ?) in dem Abschnitt hängen?
Oder es läuft immerwieder mit i=0 durch ?
Hallo Max,
es läuft ewig die for(j)-Schleife durch. Immer wieder von 0 bis
<buffer_size. Anstatt bei 255 zur for(i)-Schleife zu springen, fängt er
einfach wieder die for(j) an. i bleibt einfach bei 0, wird nicht erhöht.
Danke euch beiden - man, wie bekloppt kann ein Fehler sein :)
Läuft wieder, kann mich jetzt wieder an das korrekte Senden und
Empfangen von Daten machen.
Dirk K. schrieb:> (640 x 480 x 2 Byte) in eine SPI-RAM-Bank
das wird auch bei einem ARM von der Geschwindigkeit her knapp werden.
Sinnvoller wäre ein AL422-Fifo-Speicher.
Ich habe beide Versionen des Kameramoduls ;-) Zudem möchte ich keine 30
fps VGA-Ausgabe, sondern ein Bild am Tag.
Aber vielleicht verbrenne ich einfach den STM32F0. Egal, welchen
SPI-Mode ich einstelle, welche Flags ich beim Lesen/Schreiben abwarte
(TXE, RXNE, BSY), welche SPI-Geschwindigkeit ich einstelle ... Ich
bekomme immer nur 0x00 zurück. (Den Fehler mit dem Pin für das Oszi habe
ich schon korrigiert.)
Chip Select läuft richtig. Werde nochmal auf DIV255 mit dem DSO Nano
SCK, MISO und MOSI abprüfen, aber habe da keine Hoffnung, den Fehler zu
finden. Meine DuPont-Stecker-Hülsen sind heute angekommen, habe die
Anschlusskabel damit neu abgepatcht. Elektrischer Kontakt ist damit
wesentlich solider als mit den bisher mmissbrauchten Aderendhülsen und
die Zugentlastung äußerst willkommen.
Wenn meine beiden Billigst-STM32-103-CortexM3-Boards ankommen, bin ich
auch wieder gespannt. Vielleicht bekomme ich die ja in den Griff ...
Ein Bearbeiten des Beitrags ist nicht mehr möglich, aber das DSO-Nano
sagt: Es laufen Signale auf MISO, MOSI und SCK. Hatte erst den Verdacht,
dass ich irgendwelche "Schein-Ergebnisse" auf "Nichts" erhalte.
Habe heute morgen noch das Programm mit SPI-Div128 aufgepsielt. Es
sollte daher mehrere Sekunden dauern - die Zeiten (ohne serielle Konsole
am Controller) entsprechen aber dem, was ich sonst mit SPI...-DIV4
erhalte.
Vielleicht SPI2 statt SPI1 antesten, gibt es da einen signifikanten
Unterschied? Es liest sich in diversen Beiträgen im Netz so, als ob das
einen Unterschied machen könnte. Finde nur nicht, welchen.
>RAM-Bank aus fünf Modulen 23LC1024, die über einen 74HC138
Ich verstehe deinen Ansatz nicht. Du hast einen OV7670 mit AL422 FIfo.
Der hat 3M Speicher und schafft im Eingang PCLK mit 24Mhz. Was sollen
deine 5 über spi angesteuerten Speichermodule bringen? Warum greifst du
nicht einfach den fifo ab?
Der Ansatz ist der, dass ich auch das Modul ohne FIFO habe. Zudem ist
beim FIFO der /RE-Pin nicht ausgeführt. Daher kann ich die Übertragung
nicht einfach anhalten, sondern muss den Chip wohl mit 1MHz Takt in
einem Rutsch auslesen.
Daher die Pufferlösung mit RAM-Bank.
Bin inzwischen weiter. SPI-Div-4 als Takt, der Rest relativ egal, in die
MISO-Zuführung mal noch einen 10k-Pull-up eingelötet. Jetzt liefert mir
die Bank Daten. Anstatt: 0 1 2 3 4 bekomme ich derzeit jedoch 255 255
254 255 0. Habe durch Spielereien mit den Schleifen auch mal 254 255 0 1
2 geschafft. (Nur die ersten 5 Bytes der jeweiigen Bank mal ausgegeben.)
Sicherheitshalber am Arduino gegengetestet, da läuft das Modul
einwandfrei. Wobei mich die Signale dort etwas wundern. Hätte sowohl auf
MISO als auch auf MOSI gleichzeitig Signale erwartet. Auf dem Arduino
jedenfalls ist das wie unidirektional bei write/read. Bei der jetzt halb
funktionierenden stm32-Variante habe ich noch nicht gegengeprüft, bei
den zuvorigen Null-Ergebnissen war auf jeden Fall MISO wie tot;
"rauschen" auf 1/10 des sonstigen Signalpegels.
Ich bin aber langsam zuversichtlich, dass da noch was geht.
Da ich auch gerade versuche den ov7670 zum
Laufen zu bringen, interessiert mich dein Ansatz. Bei dem
ebay-fifo-modul http://www.ebay.de/itm/201066260621 für 10€ ist
OE-output enable nach außen geführt? Wenn du Speicher-Module verwendest,
warum nimmst du dann serielle Speicher? Du kannst dann doch nicht den
schnellen Takt vom ov nehmen sondern musst mit der mcu einen eigenen
Takt generieren, der exact 8x den Takt des ov ergeben muss? Wie willst
du die Takte synchronisieren?
Wenn dein modul auf dem avr läuft, kann das Problem eigentlich nur in
der Initialisierung des stm-spi liegen.
OE ist glaube ich da (habe das Modul zuhause und bin jetzt auf der
Arbeit). Laut OV7670-Datenblatt darf ich aber OE nicht ändern während
der Übertragung, weil der Chip das sonst als "fertig" interpretiert und
das nächste Bild von der Sensorfläche einliest. (Ist jetzt nur
Gedächtnisprotokoll, kann das jetzt grade nicht nochmal nachschlagen.
Wollte gegebenenfalls da den Takt runterdrehen, um den Pin nicht
"loslassen" zu müssen. Der ist allerdings mit mindestens 1 MHz
spezifiziert ... )
Mit dem Kameramodul bin ich aber noch gar nicht so weit. Das kommt, wenn
ich SPI im Griff habe. Nach Ostern habe ich Urlaub und wollte mich da
massiver ins Basteln stürzen ;) Die bisherige Idee war, PCLK vom OV7670
zu nehmen (der sagt [ohne FIFO], dass jetzt ein gültiges Byte anliegt),
und dann einfach die 8 Daten-Pins auszulesen. Anschließend erst mal
manuell das Datum auf SPI-RAM rausgeben. Daher der sequential mode:
einfach den Chip mit maximal möglicher Geschwindigkeit auslesen und die
Daten rüberschieben.
Ich lerne erst noch µC und alle möglichen Funktionen/Funktionseinheiten;
DMA habe ich sehr großen Respekt vor und wollte mich dann ranmachen,
wenn die ersten Ansätze funktionieren.
Da ich also alles "zu Fuß" erledige, brauche ich eine RAM-Bank. In 3
MBit passen 6 MBit (640 x 480 x 2 [Pixel * Byte]) einfach ganz schlecht
rein.
Püh. Ich habe es endlich hinbekommen. Die Lösung ist zum Haareraufen:
SPIx->DR, also das Senden-/Empfangen-Datenregister, ist 16 Bit breit.
Dem kann man natürlich einfach auch uint8_t übergeben. Sieht soweit auch
super aus.
Das Problem ist, das es so nicht funktioniert. ST macht das in der
Bibliothek von hinten durch die Brust ins Auge. Funktionsadressen
suchen, etwas magische Zeigerarithmetik, und da wird dann der 8Bit-Wert
übergeben. Ich hasse so etwas und habe mich daher sehr dagegen
gesträubt, es so zu machen.
Ein wenig optimiert habe ich den Code jetzt jedoch so übernommen. Da ST
in der Bibliothek bei jedem Funktionsaufruf die Adresse erstmal sucht,
habe ich das ausgelagert und eine globale Variable mit der Adresse
hergenommen. Damit klappt dann die Datenübergabe 33% schneller.
Allerdings habe ich bislang eine Datenrate, die mehr als peinlich ist.
Auf einem schäbigen ATmega328/Arduino schaffe ich selbst mit Arduino-Lib
mehr als die doppelte Geschwindigkeit (etwa 500kb/s).
SPI_BaudRate_Prescaler ist schon auf 2, schneller geht es damit nicht.
Womöglich muss ich an die Clocks für das System und die Peripherie,
wovor ich noch etwas Respekt habe. Auf jeden Fall freue ich mir schon
mal ein Loch in den Bauch, endlich auch SPI auf dem STM32F051xx
hinbekommen zu haben.
1
Ram-Tests begin. HCLK is 48 MHz.
2
........Writing RAM chip 0 in 665 ms -> 197 kByte/s.
3
........Writing RAM chip 1 in 666 ms -> 196 kByte/s.
4
........Writing RAM chip 2 in 665 ms -> 197 kByte/s.
5
........Writing RAM chip 3 in 666 ms -> 196 kByte/s.
6
........Writing RAM chip 4 in 665 ms -> 197 kByte/s.
7
........Reading and comparing RAM chip 0 in 690 ms -> 189 kByte/s. OK!
8
........Reading and comparing RAM chip 1 in 690 ms -> 189 kByte/s. OK!
9
........Reading and comparing RAM chip 2 in 690 ms -> 189 kByte/s. OK!
10
........Reading and comparing RAM chip 3 in 690 ms -> 189 kByte/s. OK!
11
........Reading and comparing RAM chip 4 in 690 ms -> 189 kByte/s. OK!
Heute war der Postmann gnädig und hat endlich einige neue
China-Utensilien vorbeigebracht. Da wären etwa zwei billige (rund 6€ pro
Stück) STM32F103-Basic-Developer-Boards. Und, worauf ich sehr lange
gewartet habe, Stiftleisten! Endlich die Platinen mal sauber herrichten,
mit ordentlichen Anschlüssen. Vorgestern kam bereits das Victor VC97,
damit habe ich unter anderem einen Taktmesser und Durchgangsprüfer,
sollte sich heute als äußerst nützlich erweisen.
Die Timelapse-Kamera wollte ich mit diesen billigen Boards aufbauen,
damit der Verlust (immerhin steht die dann ja irgendwo draußen rum,
könnte jemand gut finden und mitnehmen) nicht so schmerzhaft wäre. Also
habe ich erstmal geguckt, wie ich dafür ein Programm hinbekomme und wie
das auf das Board kommt. Dazu gibt es leider 0 (in Worten: Null)
Dokumentation. Und immerhin lustige Jumper "Boot0", "Boot1", die man
umsetzen kann, und einen offen/zu "P2". Richtung USB-Mini-Buchse ist die
Anschlussleiste nur noch einreihig und ordentlich mit +5V, TX, RX, GND
beschriftet. Das führt den USART1 heraus :)
Und darüber kommt auch die Software da rein - USB-2-TTL-Wandler wie
CP2102 & Co da ran, vor dem Bestromen Boot0 auf Position 2 stecken,
USB-Wandler an VM durchreichen, und in Windows XP dann mit dem
"STM_Flashloader_Demo" (kommt von STM) die .hex-Datei rauf flashen. USB
wieder ab, Jumper umstecken, Terminal anwerfen und die eigenen
Debug-Ausgaben beobachten.
1
Ram-Tests begin. HCLK is 72 MHz, PCLK2 72 MHz, System 72 MHz.
2
........Writing RAM chip 0 in 257 ms -> 510 kByte/s.
3
........Writing RAM chip 1 in 258 ms -> 508 kByte/s.
4
........Writing RAM chip 2 in 258 ms -> 508 kByte/s.
5
........Writing RAM chip 3 in 258 ms -> 508 kByte/s.
6
........Writing RAM chip 4 in 258 ms -> 508 kByte/s.
7
........Reading and comparing RAM chip 0 in 274 ms -> 478 kByte/s. OK!
8
........Reading and comparing RAM chip 1 in 274 ms -> 478 kByte/s. OK!
9
........Reading and comparing RAM chip 2 in 274 ms -> 478 kByte/s. OK!
10
........Reading and comparing RAM chip 3 in 275 ms -> 476 kByte/s. OK!
11
........Reading and comparing RAM chip 4 in 274 ms -> 478 kByte/s. OK!
Die SPI-Geschwindigkeit ist schon etwas besser, aber immer noch nicht
so, wie ich sie erwarten würde. Das sind ja nur knapp 10% mehr als auf
dem Atmel (Arduino) mit 8MHz SPI. Hier mit Baudrate_DIV4. DIV2 hat beim
Schreiben ähnliche Werte ausgespuckt, beim Lesen aber viele Fehler
produziert. Möglicherweise sind meine Kabel jetzt zu lang für mehr als
10 MHz. Und irgendwo ist noch ein Vor-Teiler, sodass das niemals 18 MHz
sind (bei DIV4 zu PCLK2 zu erwarten).
Durch die Stiftleiste bin ich dazu gekommen, die RAM-Bank etwas hübscher
umzuarbeiten. Die handgeklöppelten Klingeldrähte für die CS-Anschlüsse
und weitere mussten Kupferlackdraht weichen; auch der Saitenschneider
hat seine Schuldigkeit getant und die Pins der Kerkos abgetrennt. Dann
noch den Dymo befragt, ob er nicht eine Beschriftung beisteuern mag,
falls ich das Ding in zwei Tagen noch mal woanders anstecken möchte.
Im Anhang der umgearbeitete Hauptprogrammteil main.c - im Wesentlichen
heißen die GPIO-Optionen anders, einige Register ebenfalls, war etwa
frickelig umzuarbeiten. Die .h blieb gleich. Als Projekt sollte man
jetzt ein Template für eine STM32F10x-Anwendung in Eclipse nehmen und
das vorbefüllte Beispielprogramm dann rauslöschen. Damit sind alle
#includes gleich korrekt.
Als Eye-Candy auch noch das billige STM32F103-Board (grade mal doppelt
so breit wie ein Arduino Nano) zusammen mit der RAM-Bank und Bilder von
der überarbeiteten Platine.
Vielleicht hat noch jemand eine Idee, was mit SPI da schief läuft, dass
das Tempo nicht so ganz passt?
>Vielleicht hat noch jemand eine Idee, was mit SPI da schief läuft, dass>das Tempo nicht so ganz passt?
Hast du ein Oscilloscope?
Vieleicht ist es ja das hier:
Beitrag "STM32F407 und das SPI Modul"
Das einzige was SPI wirklich schnell macht ist DMA.
Ohje, DMA wollte ich mich zwar auch heranwagen, aber erst, nachdem ich
das vollständig lauffähig ohne habe ;) Mein DSO Nano ist ein Spielzeug,
dafür nicht geeignet. Habe aber ein Cypress EZ-USB-Dev-Board im Zulauf
und kann damit mal in etwa drei bis vier Wochen draufschauen.
Ich gehe jetzt erst mal deinen Link durch, ich glaube zwar, das schon
gelesen zu haben, aber man lernt ja täglich dazu und versteht manches
erst beim zweiten/dritten/vierten Mal. Danke für die Tipps :)
Exkurs: STM32F103 unter Mac OS X flashen.
Der Umweg über die virtuelle Maschine mit STM-Tools zum Hochladen des
Kompilats in das Dev-Board war mir ein wenig zu nervig, um an den Test
mit der Kamera zu gehen. Daher habe ich etwas gesucht und doch noch ein
paar Informationen gefunden, wie es direkter geht.
Zunächst zum Verständnis der ominösen Jumper Boot[0|1]: Das besagt nur,
was der STM32F103 startet nach Drücken der Reset-Taste (ja, den Jumper
kann man im laufenden Betrieb umsetzen, schont den USB-Port ;) ).
1
Boot0 Boot1 startet
2
0 X User-Code
3
1 0 System Memory (Bootloader, zum Flashen via USART1)
4
1 1 System RAM
Unter Mac OS X ist Python standardmäßig installiert. Zur Kommunikation
mit seriellen Schnittstellen muss man pySerial installieren.
Download des tar.gz-Archivs hier:
http://sourceforge.net/projects/pyserial/
Mit tar -xvfz entpacken, dann als root das setup-Script aufrufen: sudo
./setup.py install
Fehlt eigentlich nur noch ein Programm zum flashen - stm32loader.py.
Download hier: https://github.com/jsnyder/stm32loader
Das stm32loader-Script kann leider nur .bin-Dateien flashen, also muss
man das .hex aus dem Compilerlauf erst mal konvertieren. Ich habe ein
Beispielscript angehängt, das das komfortabel für mich erledigt. Ich
kopiere stm32loader.py und flash.sh einfach in meinen Debug-Ordner des
Projekts, und öffne einen Terminal. Darin dann ebenfalls in den
Debug-Ordner wechseln und flash.sh starten -Sekunden später hat man
erfolgreich das Programm auf dem Board :)
Anpassen muss man an dem Script noch den Pfad zum
gnu-arm-none-eabi-gcc-Compiler und die USB-Schnittstelle zum USART1 (ls
/dev/tty.* listet alle gefundenen Schnittstellen). Die .hex-Datei sucht
das Script und macht den ganzen Rest automatisch.
1
macbookair:Debug koepi$ ./flash.sh
2
Hexfile to convert and flash: VGACam-RamBank.hex
3
VGACam-RamBank.hex converted to flash.bin. Starting upload to STM32F103:
4
Bootloader version 22
5
Chip id `['0x4', '0x10']'
6
Write 256 bytes at 0x8000000
7
Write 256 bytes at 0x8000100
8
[...]
9
Write 256 bytes at 0x8006000
10
Write 256 bytes at 0x8006100
11
Read 256 bytes at 0x8000000
12
Read 256 bytes at 0x8000100
13
[...]
14
Read 256 bytes at 0x8006000
15
Read 256 bytes at 0x8006100
16
Verification OK
17
Done! Now switch Boot0 pin and hit Reset.
Vielleicht ist das ja noch für andere nützlich, daher poste ich die
Anleitung mal hier :) (Ehrlich, für rund 6 Euro [hierher habe ich es,
kam in nur 2 1/2 Wochen an: http://www.ebay.de/itm/111297094334 ] ist
das ein Hammer-Entwicklungsboard - leider nur wenig verstreute Doku zu
finden.)
Edit: Bei der weiteren Suche gefunden, das ganze Python-Zeugs kann man
sich sparen, wenn man dieses Tool selber compiliert (also GCC&Co für den
Mac installiert hat): https://code.google.com/p/stm32flash/ - es
unterstützt .hex auch direkt. Außerdem weiß ich jetzt auch endlich, was
der Chip so alles mitbringt: