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 einfachsten ist foolproof - bitbanging. Da muss nur der io-teil angepasst werden. Wennn das läuft, ist die Umstellung auf spi einfach.
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:
1 | DSTATUS disk_initialize (BYTE pdrv); |
2 | DSTATUS disk_status (BYTE pdrv); |
3 | DRESULT disk_read (BYTE pdrv, BYTE*buff, DWORD sector, UINT count); |
4 | DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); |
5 | DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); |
6 | DWORD get_fattime (void); |
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_t SPI_TransferByte(uint8_t data) |
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 | return SPI_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.
Frickelfritze schrieb: > Ahnung haben ist auch anders. Für die mit wenig Ahnung: Kannst du den Sinn der drei whiles erklären?
Vielleicht einfach mal drueber nachdenken.... Zettel dazu nehmen, ueberlegen dass verschiedene Takte existieren.... dann kommt man drauf :-)
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 | static uint8_t xchg_spi(uint8_t data) |
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 | return SPI1->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.
Wenn ich was schreiben will warte ich doch nicht noch unsinnig auf ein Zeichen was mich nicht interessiert.
Lies bitte das Manual! RTFM. Diese Spi ist kein Kinderspielzeug sondern eim sehr komplexes Werkzeug.
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.
Oha, was machst Du da? Gruß und gute Besserung, Holm
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 | void Init_DMA_LDR() |
2 | {
|
3 | #define ADC1_DR_Address 0x4001244C // ADC Regular Data Register
|
4 | |
5 | GPIO_InitTypeDef GPIO_InitStructure; |
6 | ADC_InitTypeDef ADC_InitStructure; |
7 | DMA_InitTypeDef DMA_InitStructure; |
8 | |
9 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); // GPIOA Clock enable |
10 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); // ADC Clock enable |
11 | RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); |
12 | |
13 | // ADC Clock auf unter 14 Mhz bringen
|
14 | RCC_ADCCLKConfig(RCC_PCLK2_Div8); |
15 | |
16 | /* ADC Pin konfigurieren --------------------------------------------------*/
|
17 | GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // AN1 Pin (PA0) aktivieren |
18 | GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; // Analog Input |
19 | GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; |
20 | GPIO_Init(GPIOA, &GPIO_InitStructure); |
21 | |
22 | /* ADC1 configuration ------------------------------------------------------*/
|
23 | ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; // ADC1 werkelt allein |
24 | ADC_InitStructure.ADC_ScanConvMode = DISABLE; // Nur 1 Kanal, keine Scans |
25 | ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // Keine ständigen Wandlungen |
26 | ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // Start mit Software, nicht durch Hardware |
27 | ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // Bits rechts ausrichten im Half-Word |
28 | ADC_InitStructure.ADC_NbrOfChannel = 1; // 1 Kanal sampeln |
29 | ADC_Init(ADC1, &ADC_InitStructure); |
30 | |
31 | ADC_Cmd(ADC1, ENABLE); |
32 | |
33 | /* DMA1 Channel1 configuration ----------------------------------------------*/
|
34 | DMA_DeInit(DMA1_Channel1); |
35 | DMA_StructInit(&DMA_InitStructure); |
36 | DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // Peripherie ist Datenquelle |
37 | DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR_Address; // ADC Basisadresse Datenregister |
38 | DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)LDR; // Memory Array Adresse |
39 | DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // Auf Source ADC keine Inkrementierung |
40 | DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // Auf Target Array inkrementieren |
41 | DMA_InitStructure.DMA_BufferSize = (uint16_t)LDR_HIST; // Groesse des Arrays |
42 | DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // ADC Wandler 16 Bit |
43 | DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // Memory auch 16 Bit |
44 | DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // Array circular füllen |
45 | DMA_InitStructure.DMA_Priority = DMA_Priority_Low; // Niedrige Prio |
46 | DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; |
47 | DMA_Init(DMA1_Channel1, &DMA_InitStructure); // Mache Einstellungen |
48 | |
49 | /* Enable DMA1 Channel 1 */
|
50 | DMA_Cmd(DMA1_Channel1, ENABLE); |
51 | |
52 | /* ADC1 Sequencer Kanal 1 auf Rang 1 configuration */
|
53 | ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_71Cycles5); // PA0 |
54 | |
55 | /* Enable ADC1 reset calibaration register */
|
56 | ADC_ResetCalibration(ADC1); |
57 | |
58 | /* Check the end of ADC1 reset calibration register */
|
59 | while(ADC_GetResetCalibrationStatus(ADC1)); |
60 | |
61 | /* Start ADC1 calibaration */
|
62 | ADC_StartCalibration(ADC1); |
63 | |
64 | /* Check the end of ADC1 calibration */
|
65 | while(ADC_GetCalibrationStatus(ADC1)); |
66 | |
67 | /* Keine Triggerung durch Events etc */
|
68 | ADC_ExternalTrigConvCmd(ADC1, DISABLE); |
69 | |
70 | /* Erzeuge nach jeder Wandlung einen DMA Request */
|
71 | ADC_DMACmd(ADC1, ENABLE); |
72 | |
73 | /* Trigger die erste Wandlung, weitere Trigger durch ADC / DMA Interrupt */
|
74 | ADC_SoftwareStartConvCmd(ADC1, ENABLE); |
75 | }
|
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...
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.