Hallo,
ich hoffe, dass ich im richtigen Film bin. Ich habe mir das STM32F100
beispiel der Chat Fat in Embitz importiert und kann es problemlos
kompilieren.
Allerdings weiss ich nicht, ob der STM32F103C8 uC des Bluepill Boads mit
dem Beispiel 100% komptibel ist.
Normalerweise benutze ich diese Includes und binde damit alle CMSIS und
StdPeriphLib Ressourcen des F103 ein.
#include "stm32f10x_conf.h"
#include "system_stm32f10x.h"
Chan benutzt
#include "stm32f100.h"
Da stehen nur Registerdefintionen drin an welcher Adresse wo was ist
aber es werden keine StdPeriphLibs eingebunden.
Die beiden sind nicht gegeneinander austauschbar oder gleichzeitig
benutzbar, es erscheinen hunderte Fehlermeldungen.
Hardware habe ich noch keine aufgebaut, frage trotzdem schon mal, da
eine evtl Portierung nahezu unmöglich ist, da Chan nur auf Registerebene
Makros geschrieben hat, ich aber generell die StdPeriph Lib benutze.
Gruss,
Christian
Hat sich echt noch niemand mit diesem SD Karten System befasst auf einem
kleinen STM32 Board? Und das nicht auf Arduino Basis sondern unter
Verwendung der StdPeriph Library.
Knmisch....
Christian J. schrieb:> Und das nicht auf Arduino Basis sondern unter Verwendung der StdPeriph> Library.
Mach dich doch mal von der Library-Schubladendenke frei.
FatFS ist so strukturiert, dass das eigentliche FS von den
Hardwarezugriffen entkoppelt ist. Guckst du hier unter "Media Access
Interface"
http://elm-chan.org/fsw/ff/00index_e.html
D.h. du musst nur die Funktionen wie disk_read, disk_write, disk_status,
etc. für deine Hardware implementieren. Ob überhaupt und wenn ja, was
für eine Library du für diese Funktionen verwendest ist dem FS-Layer
komplett egal.
Beispiele dafür gibt es im Internet massenweise und natürlich auch für
STM32. Hier das erstbeste was ich gefunden habe (mit einfachem SPI):
https://github.com/g4lvanix/STM32F4-workarea/blob/master/Project/FatFS/lib/ff/src/diskio.c
Am besten wäre aber ein DMA-Beispiel, dann gibt es keine periodischen
Aussetzer immer wenn der 512Byte Block geschrieben wird.
Funktioniert das mit SPI auch mit den neuen, großen SD-Karten?
Es gibt doch glaube ich eine SD-Bibliothek im HAL der STM32 Bibliothek.
grundschüler schrieb:> am einfachsten ist foolproof - bitbanging.
Am Besten ist ein DMA gesteuertes SPI Transfer mit maximalen 20 Mhz was
noch so eben auf einem Lochraster durchgeht. Ich kann das selbst
schreiben aber das dauert mit Testen halt 3-4 Tage je abends daher
fragte ich ob es vielleicht schon jemand gemacht hat.
Ich bin den Code durch, den kann man so übernehmen und alles weglöschen
was er mit diesen Registern angestellt hat, wo mir jeder Nerv zu fehlen
würde da die Bits einzeln nachzugucken.
De facto bleiben einzig und allein 2 SPI Routinen übrig, eine für
Einzelbytes und eine führ mehrere. Die Einstellungen der SPI kenne ich
aber nicht, da die ganz erheblich konfigurierbar sind, jeder Idle State
usw. Diese Kartenn hängen sich auch gern mal auf, daher muss ich einen
Reset vorsehen an der Stromversorgung. Mit P Kanal Mosfet oder
ähnlich....
Christopher J. schrieb:> Hier das erstbeste was ich gefunden habe (mit einfachem SPI):> https://github.com/g4lvanix/STM32F4-workarea/blob/master/Project/FatFS/lib/ff/src/diskio.c
Ist leider nicht kompatibel zu der aktuellen Version, bzw wurd das total
umgemodelt, der Header sieht komplett anders aus. Der Code ist eh Müll,
die Transferroutine ist nicht vollständig, es fehlen die Init Routinen
für die SPI usw. Keine Ahnung wieso jemand so einen Schrott öffentlich
macht.
Ich werde da wohl selbst mal Hand anlegen müssen.......
B.A. schrieb:> Funktioniert das mit SPI auch mit den neuen, großen SD-Karten?> Es gibt doch glaube ich eine SD-Bibliothek im HAL der STM32 Bibliothek.
Die HAL Lib basiert glaube ich auch auf dieser Chan Fat. Und
idealerweise betreibt man diese Karten auch mit einer SDIO und nicht
einer SPI. Einige Chipse wie der F200 haben die auch und natürlich auch
die großen F4 und F7. Nur damit erreicht man maximalen Durchsatz.
Für meine kleine Anwendung reicht es aber vollkommen die per SPI zu
betreiben. Long Filenames und Codepages muss ich eh drauf verzichten,
das kompilat ist dann 80kb gross und damit zu gross für den F103.
Christian J. schrieb:> Ich werde da wohl selbst mal Hand anlegen müssen.......
So war das auch gemeint.
Christian J. schrieb:> Ist leider nicht kompatibel zu der aktuellen Version, bzw wurd das total> umgemodelt, der Header sieht komplett anders aus. Der Code ist eh Müll,> die Transferroutine ist nicht vollständig, es fehlen die Init Routinen> für die SPI usw. Keine Ahnung wieso jemand so einen Schrott öffentlich> macht.
Das der Code hübsch ist habe ich nie behauptet und noch nicht einmal,
dass er funktioniert. Keine Ahnung wo der Typ seine SPI initialisiert.
Ist mir auch ehrlich gesagt völlig wurscht. Es ging nur um das Prinzip
und das hat sich meines Wissens nach nicht verändert. diskio.h ist immer
noch der gleiche und die folgenden Funktionen musst du implementieren:
Christopher J. schrieb:> diskio.h ist immer> noch der gleiche und die folgenden Funktionen musst du implementieren:
Nee, die hat sich schwer verändert :-) Er hat vielem anderen Namen
gegeben usw. Und je tiefer man ändert, desto mehr Fehler. Ich koüpiere
mir jetzt meine SPI Routinen in das Demoprojekt rein, was ja an einem
VT100 Terminal läuft und schaue mal weiter. Er ändert zb auch den SPI
Mode von 16 Bit auf 8 Bit und wieder zurück. Hat ja auch seinen Grund.
Da kommen dann nämlich 2 Bytes raus und nicht eines. Diese Karten
arbeiten aber laut MMC Spec mit 8 Byte Datenworten.
Ich meine nur, eine korrekt SPI Rotuine sieht so aus:
1
uint8_tSPI_TransferByte(uint8_tdata)
2
{
3
while(SPI_I2S_GetFlagStatus(RF_SPI,SPI_I2S_FLAG_TXE)==RESET);/* Wait for SPIz Tx buffer empty */
4
SPI_I2S_SendData(RF_SPI,data);/* Send RF_SPI data */
5
while(SPI_I2S_GetFlagStatus(RF_SPI,SPI_I2S_FLAG_RXNE)==RESET);/* Wait for RF_SPI data reception */
6
while(SPI_I2S_GetFlagStatus(RF_SPI,SPI_I2S_FLAG_BSY)==SET);/* Wait for RF_SPI Busy Flag cleared */
7
returnSPI_I2S_ReceiveData(RF_SPI);/* Read RF_SPI received data */
8
}
und nicht so, die funktioniert wohl nur zufällig, aber eben kein
robuster Code
static BYTE xchg_spi (BYTE dat)
{
SPIx_DR = dat;
return (BYTE)SPIx_DR;
}
Und Probleme scheint es noch jede Menge zu geben:
http://elm-chan.org/fsw/ff/bd/
Christian J. schrieb:> Long Filenames und Codepages muss ich eh drauf verzichten, das kompilat> ist dann 80kb gross und damit zu gross für den F103.
Da muss man ja nicht mal auf einem mickrigen ATmega drauf verzichten,
mit zwei SD-Karten gleichzeitig im Zugriff.
Leo C. schrieb:> Da muss man ja nicht mal auf einem mickrigen ATmega drauf verzichten,> mit zwei SD-Karten gleichzeitig im Zugriff.
Scheinbar aber doch...... bei mir kompiliert es zu 80kb mit Os
Einstellung. Bisschen viel wie ich finde. Aber es werden auch alle
Routinen eingebunden aus dem Demofile, was man real ja nie braucht, eine
handvoll reicht ja komplett.
Bin gerade nicht zuhause und nachschauen ist mir zu umständlich. Sieht
aber für mich so aus, als hättest Du japanische Codepages aktiviert.
Vielleicht kannst Du die rauswerfen und ein oder zwei westliche
aktivieren.
http://elm-chan.org/fsw/ff/doc/appnote.html#lfn
Christian J. schrieb:> Christopher J. schrieb:>> diskio.h ist immer>> noch der gleiche und die folgenden Funktionen musst du implementieren:>> Nee, die hat sich schwer verändert :-)
Also irgendwie meinen wir dann wohl verschiedene diskio.h, weil die aus
der aktuellen R0.13 ist quasi identisch mit der aus dem oben von mir
verlinkten Github-Repo von vor vier Jahren. Selbst die teilweise
zerhackte Formatierung ist noch die selbe.
Christian J. schrieb:> static BYTE xchg_spi (BYTE dat)> {> SPIx_DR = dat;> return (BYTE)SPIx_DR;> }
Ich hab ehrlich gesagt keine Ahnung wo du das her hast aber ja, sieht
nicht sonderlich robust aus.
Moin,
soll ja auch einen Nutzen für die Googler der Zukunft bringen,
wenngleich der Code nicht getestet wurde, sondern stur übersetzt in die
Schreibweise der StdPeriph Libs, da wo nötig und alle Bezüge auf die
STM32F100.h sind entfernt, da die nicht kompatibel ist.
Also hier das diskio.c Modul für den STM32F103C8 Chip, ungeprüft aber
dafür sorgfältig umgesetzt.
Christian J. schrieb:> Ich meine nur, eine korrekt SPI Rotuine sieht so aus:> uint8_t SPI_TransferByte(uint8_t data)> {> while (SPI_I2S_GetFlagStatus(RF_SPI, SPI_I2S_FLAG_TXE) == RESET);> /* Wait for SPIz Tx buffer empty */> SPI_I2S_SendData(RF_SPI,data);> /* Send RF_SPI data */> while (SPI_I2S_GetFlagStatus(RF_SPI, SPI_I2S_FLAG_RXNE) == RESET);> /* Wait for RF_SPI data reception */> while (SPI_I2S_GetFlagStatus(RF_SPI, SPI_I2S_FLAG_BSY) == SET);> /* Wait for RF_SPI Busy Flag cleared */> return SPI_I2S_ReceiveData(RF_SPI);> /* Read RF_SPI received data */> }
So kann man natürlich auch die CPU Power verbrennen ...
3 while-loops in Folge ...
Smart ist anders.
grundschüler schrieb:> Für die mit wenig Ahnung: Kannst du den Sinn der drei whiles erklären?
Nur weil du zu faul bist Datenblatt und Sourcecode (SPL) selbst
zu lesen? Nö Danke.
Wer meint es schneller und besser machen zu können der darf
gerne seine Kreationen hier kundtun.
Ansonsten gilt das was Christian schon geschrieben hat.
Christian J. schrieb:> Ich meine nur, eine korrekt SPI Rotuine sieht so aus:
Allerhöchstens kann man sich über das letzte Warten
streiten (/* Wait for RF_SPI Busy Flag cleared */)
Wer wirklich Zeit sparen will kann die Aufrufe von
SPI_I2S_SendData(RF_SPI,data);
und
SPI_I2S_ReceiveData(RF_SPI);
mit direkten Register-Operationen beschleunigen.
Frickelfritze schrieb:> grundschüler schrieb:> Für die mit wenig Ahnung: Kannst du den Sinn der drei whiles erklären?> SPI_I2S_SendData(RF_SPI,data);> und> SPI_I2S_ReceiveData(RF_SPI);>> mit direkten Register-Operationen beschleunigen.
Die reduziert der Compiler auf genau eine Registeroperation. Da bleibt
nicht mal ein Call uebrig. Und den letzten brauchst Du nur formal
korrekt, es geht auch ohne.
grundschüler schrieb:> Frickelfritze schrieb:>> Ahnung haben ist auch anders.>> Für die mit wenig Ahnung: Kannst du den Sinn der drei whiles erklären?
Das ist im Grunde einfach nur blockierender Datenaustausch per SPI, also
ohne Interrupt oder DMA.
Im Prinzip steht es ja schon in den Kommentaren:
1. Warten bis das TXE-Flag ("TX buffer empty") im Statusregister gesetzt
ist, damit man etwas ins SPI-Datenregister schreiben kann
2. Schreiben der Daten (in dem Fall 8 Bit) ins Datenregister
3. Warten bis das RXNE-Flag ("RX buffer not empty") im Statusregister
gesetzt ist, weil erst dann die empfangenen Daten im Datenregister
liegen
4. Warten bis das BUSY-Flag im Statusregister gelöscht ist
5. Lesen der empfangenen Daten (8 Bit) aus dem Datenregister
Punkt 4. ist tatsächlich überflüssig aber um die anderen beiden
while-Schleifen kommt man nicht herum, es sei denn man nutzt Interrupts
oder DMA. Da der TO wohl nur gelegentlich ein paar Byte auf die SD-Karte
schreiben will kommt es aber auf die Performance in dem Fall wohl nicht
an.
Ohne Library, mit direktem Registeraufruf würde das in etwa so aussehen:
1
staticuint8_txchg_spi(uint8_tdata)
2
{
3
while(!(SPI1->SR&SPI_SR_TXE));/* Wait for SPI1 Tx buffer empty */
4
SPI1->DR=data;/* Send SPI1 data */
5
while(!(SPI1->SR&SPI_SR_RXNE));/* Wait for SPI1 data reception */
6
returnSPI1->DR;/* Return SPI1 received data */
7
}
Natürlich wäre es sinnvoll das nicht fix für SPI1 zu schreiben, sondern
etwa SPIx als bezeichner zu nehmen und diesen per #define auf die
gewünschte Schnittstelle zu setzen.
Von der Performance her geben sich die beiden Varianten (SPL oder
Register) wahrscheinlich wirklich nichts, so lange der Compiler alle
Funktionsaufrufe schön inlined aber ich persönlich finde die Variante
mit dem direkten Registeraufruf tatsächlich auch leserlicher. Man sieht
direkt, was wo passiert und wenn ich fremden Code habe und nicht
verstehe was z.B. in der ersten Zeile passiert, dann muss ich nur das
Manual öffnen, mit STRG+F nach "SPI_SR" suchen und schon weiß ich was
abgeht.
Ich erreiche uebrigens 20 Mbyte/s mit meiner Loesung. Und laege ich
nicht grad im Krankenhaus mit nem Smartphone wuerde ich da auch noch
mehr zu schreiben.
Christian J. schrieb:> Lies bitte das Manual! RTFM. Diese Spi ist kein Kinderspielzeug> sondern> eim sehr komplexes Werkzeug.
Deswegen muss man nicht arrogant werden. Außerdem kann man sehr wohl
schreiben ohne RXNE auszuwerten. Nur beim nächsten Lesevorgang muss dann
aufgepasst werden.
Und SPI ist nicht komplex - auch nicht beim STM32. Komplexität ist was
ganz anderes.
Christian J. schrieb:> Ich erreiche uebrigens 20 Mbyte/s mit meiner Loesung.
20Mbit/s? 20MB/s erreichen nicht mal die neuen STM32H7. Selbst mit
20Mbit/s bewegst du dich schon außerhalb der Spezifikation des
STM32F103.
Holm T. schrieb:> Oha, was machst Du da?>> Gruß und gute Besserung,>> Holm
Hoersturz..... bei der Arbeit im Büro. Scheiss Stress. und das Tippen
auf diesem Miniteil hier ist ne Katastrophe.
avr schrieb:> Selbst mit> 20Mbit/s bewegst du dich schon außerhalb der Spezifikation des> STM32F103.
Nicht ganz. Der AHB Takt an dem die SPI hängt spielt mit 36 Mhz (DIV2)
bei einem Clock von 72 Mhz. Die SPI selbst wird durch DIV2 geclocked,
also 18 Mhz. Die 18 Mhz liegen voll am Pin an, also sind mit etwas
Verlusten rund 16-max 18 Mhz drin. Letztres aber nur per DMA. In Bytes
sind das 2,2 MBytes/s. Da habe ich einen Fehler in der 10er Potenz
gemacht, der aber auf die Flüchtigkeit "zwischen Tür uns Angel"
geschoben werden kann.
Christopher Johnson schrieb:> Ohne Library, mit direktem Registeraufruf würde das in etwa so aussehen:
Ich hatte auch schon einige Probleme mit dem stm-spi:
Beitrag "Re: Stm32f103 Problem mit Spi2"
mit
SPI2->DR = dat;
while (!(SPI2->SR & 1<<0));//0_rxne 1_txe 7_bsy// ok
ist es dann aber gegangen.
Die zusätzliche Abfrage
while (!(SPI1->SR & SPI_SR_TXE)); /* Wait for SPI1 Tx buffer empty
*/
scheint mir überflüssig. wenn rxne erfüllt ist muss der tx-buffer leer
sein, weil die Übertragung abgeschlossen ist bevor die Funktion erneut
aufgerufen wird. Vielleicht war aber genau das das Problem, das ich
seinerzeit hatte.
grundschüler schrieb:> SPI2->DR = dat;> while (!(SPI2->SR & 1<<0));//0_rxne 1_txe 7_bsy// ok>> ist es dann aber gegangen.
Ich empfehle wirklich nur den den Standard APIs zu arbeiten wie
StdPeriphLib oder HAL. Das ist erstens viel lesbarer und bei
Autoergänzung auch kein zusätzlicher Schreibkram. Der Code ist (oft aber
nicht immer) zwischen den Familien portierbar, wenn die Hardware gleich
ist und das sind zb bei I2C fixe prime Cells. Zudem wird zb die HAL
ständig weiter entwickelt und enthält sehr mächtige Funktionen. Wenn Du
mal mit Systemen wie ChibiOS oder NuttX arbeitest wirst Du merken, dass
das viel einfacher geht und vor allem deutlich lesbarer.
while (!(SPI1->SR & SPI_SR_TXE)); /* Wait for SPI1 Tx buffer empty
Dieser Aufruf muss am Anfang stehen, wenn Du keine Endabfrage hast, ob
die Bits raus sind. Die SPI ist eine Statemachine, die Event Flags hat,
welche bedient werden wollen.
Wenn Du das hier, eine Init Routine für einen ADC Wandler mit zyklischem
DMA Zugriff, getriggert durch einen Timer, auf Registerebene kodierst
blickst Du nachher komplett nicht mehr durch....
1
voidInit_DMA_LDR()
2
{
3
#define ADC1_DR_Address 0x4001244C // ADC Regular Data Register
Christian J. schrieb:> Dieser Aufruf muss am Anfang stehen, wenn Du keine Endabfrage hast, ob> die Bits raus sind.
Dieser Aufruf muss n u r am Anfang stehen, wenn Du keine Endabfrage
hast ob
die Bits raus sind.
Damit ist die oben ob dargestellte "korrekte SPI Rotuine" ziemlich
aufgebläht. Wir sind uns einig, dass es auch mit weniger Code geht,
nämlich einmal Abfrage TXE vor dem Aufruf der ersten spi-routine - wenn
die Möglichkeit besteht, dass in tx irgendetwas stehen könnte - und dann
nur noch rxne.
> Zudem wird zb die HAL ständig weiter entwickelt und enthält sehr> mächtige Funktionen.
Nichts gegen Programme wie Arduino und Hal. es gibt code, der so
kompliziert ist, dass man sich auf solche Programmierhilfen verlassen
muss. Bei einer schlichten spi-routine weiß ich aber gerne, was mein
Programm macht.
Sowas
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
ist völlig überflüssig.
Karl K. schrieb:> Sowas> /* Check the parameters */> assert_param(IS_SPI_ALL_PERIPH(SPIx));> ist völlig überflüssig.
Na.... wenn Du genauer hinschaust wirst du merken, dass das
assert_param, was in jeder Funktion der StdPeriph steht nur ein Makro
ist, das sich mit USE_FULL_ASSERT 1 ein und ausschalten lässt :-) Den
zulässigen Wertebereich soll eine Funktion schon testen, das ist guter
Programmstil.
Christian J. schrieb:> Den> zulässigen Wertebereich soll eine Funktion schon testen, das ist guter> Programmstil.
Völlig überflüssig. Den zulässigen Wertebereich muss ein Programmierer
immer beachten. Wenn der nicht stimmt, ist das Programmm Murks.
Derartige Überwachungen des Programmierers haben in vielfach
aufgerufenen Funktionen nichts zu suchen. Das gilt selbst dann, wenn man
das irgendwo abschalten kann. Wer weiß das denn schon, dass man das
abschalten und wo man das abschalten kann?
grundschüler schrieb:> Wenn der nicht stimmt, ist das Programmm Murks.
Richtig. Dann muß man herausfinden, wo der Bug steckt, das nennt sich
Debuggen, und dazu können Debug-Builds sehr hilfreich sein.
grundschüler schrieb:> Völlig überflüssig. Den zulässigen Wertebereich muss ein Programmierer> immer beachten. Wenn der nicht stimmt, ist das Programmm Murks.
Du bist aber auch ein kleiner Klugs..... Schon mal drüber nachgedacht,
dass Software von einem Team geschrieben wird? Schon mal mit svn im Team
gearbeitet? Mit Leuten, die gar nicht in Deinem Land leben? Dass Module
nach dem V&V Modell der Qualitätssicherung geprüft werden müssen? Dass
es sowas wie Verfikation und Validation gibt?
Ich habe Jahre für Automotive Software geschrieben nach MISRA Standard,
ISO 25010, ISO 9126 und weiteren etlichen internen
Codierungsrichtlinien....
Chris J. schrieb:> Ich habe Jahre für Automotive Software geschrieben nach MISRA Standard,> ISO 25010, ISO 9126 und weiteren etlichen internen> Codierungsrichtlinien....
Und trotzdem behauptest du immer wieder, dass der Compiler schuld ist,
wenn dein Code nur ohne Optimierung funktioniert?
Vielleicht wären da ein paar Schulungen in C-Programmierung doch mal
sinnvoll für dich.
Ich hoffe nur, dass du nicht irgendwas sicherheitsrelevantes z.B. von
KFZ programmiert hast. Sonst fahren alle Autos beim nächsten
Compilerupdate gegen den Baum. Aber da ist dann natürlich der Compiler
dran schuld und nicht dein Code...