:!DOCTYPE html>
Erstellung 2023.10.06 - Update 2023.12.19 08:00 UhrAnlegen von 2 Projekten per Cubeide 1. 743zi_rawClock Cubeide starten (V 1.13.2) File->New->Stm32-Project Board 743zi auswählen ("Obsolete" ist egal) Name: 743zi_rawClock "Initialize Peripherals?" - >Yes PC9 mit RCC_MCO_2 verbinden das sollte dann so aussehen: (aus PC8 kann man auch mal für Tests einen Output machen) Clock einstellungen -> 400MHZ und Solution suchen lassen Sysclock-Prescaler auf "/15" (26MHz) einstellen .ioc schliessen und Code generieren lassen "Run" "Launch configuration properites" übernehmen Bei mehr als einem STM32: Properties->Run/Debug Settings->743zi_rawClock Debug->Debugger->Scan->ST-Link S/N Mit "st-info --probe" die richtige Serial ermitteln und auswählen Apply/Close "Run" Messen an PC9-> da kommt ein ziemlich grusliges 15MHz-Signal raus (müsste eigentlich das doppelte sein, aber das ist wohl der Signalqualität geschuldet) Reduzieren von RCC_OscInitStruct.PLL.PLLN = 100; auf RCC_OscInitStruct.PLL.PLLN = 50; im main.c: dann wird es zu einem recht stabilen 13.3MHz Signal. Bei "10" wird es zu einem beinahe Rechteck mit 2.66MHz Das entspricht dann 2.66*15=40MHz Sysclock. Nach Änderung von Hand: RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 1; RCC_OscInitStruct.PLL.PLLN = 10; RCC_OscInitStruct.PLL.PLLP = 2; RCC_OscInitStruct.PLL.PLLQ = 13; RCC_OscInitStruct.PLL.PLLR = 2; RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3; RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE; RCC_OscInitStruct.PLL.PLLFRACN = 0; Änderung in .ioc (Clock Configuration) Danach ist die von Cubeide generierte Lösung minimal anders: RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 1; RCC_OscInitStruct.PLL.PLLN = 20; RCC_OscInitStruct.PLL.PLLP = 2; RCC_OscInitStruct.PLL.PLLQ = 3; RCC_OscInitStruct.PLL.PLLR = 2; RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3; RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOMEDIUM; RCC_OscInitStruct.PLL.PLLFRACN = 0; Das Resultat an Pin PC9 aber das gleiche: 2.66MHz - 40MHz Systemtakt. Geht man auf 4MHz im Cubeide runter, dann han man 266kHz auf PC9 anliegen - und Cubeide meckert wg. der Usart-Parameter. Selbst 0.4MHz behauptet er einstellen zu können - läuft dann aber nicht. (Und bei 0.1MHz crasht das "Clock Configuration"-Interface) Bei 8 MHz wird der USART3 nicht mehr angemeckert und an PC9 kommen 1.06 MHz an. Warum? 8/15 sind nicht 1.06. Nein die Sysclk steht nun auf 16MHz. Und warum? Weil er jetzt das hier gebaut hat: RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 1; RCC_OscInitStruct.PLL.PLLN = 20; RCC_OscInitStruct.PLL.PLLP = 10; RCC_OscInitStruct.PLL.PLLQ = 3; RCC_OscInitStruct.PLL.PLLR = 2; RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3; RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOMEDIUM; RCC_OscInitStruct.PLL.PLLFRACN = 0; SysClock-Frequenz = (Eingangsfrequenz / PLLM) * PLLN / PLLP In diesem Fall: SysClock-Frequenz = (Eingangsfrequenz / 1) * 20 / 10 SysClock-Frequenz = (Eingangsfrequenz * 2) OK - er wird schon wissen was er tut.... Damit man auch sieht, dass er was tut - das hier in die while-Schleife vom "main" HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0); HAL_Delay(200); HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_7); HAL_Delay(200); HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_14); OK - nun sollte die Lightshow auch ohne die explizite Einrichtung von GPIO 0, 7 und 14 funktionieren. Jetzt wäre es schön, wenn er auch noch mit uns sprechen würde. Und das ist per Default eine Sache, die sehr viel Nerven kosten kann..... Ich weiss von Leuten, die aus Verzweifelung auf serielle Leitungen und Port-Adapter ausgewichen sind. ;-) Das ist nicht nötig, denn mit viel Zureden kann man den 743 überzeugen, einfach den USB-Port des Flash-Links zu benutzen. Also mal eine Ausgabe einbauen: /* USER CODE BEGIN 2 */ printf("start"); /* USER CODE END 2 */ Das ist jetzt überigens der Zeitpunkt, zu dem man ins "743zi_rawClock/Debug" wechseln und auf Kommandoebene weiterarbeiten kann. Dazu erzeugt man ein make.sh mit diesem Inhalt: #!/bin/bash make all&&\ arm-none-eabi-objcopy -O binary $1.elf $1.bin&&\ st-flash --serial 0670FF353631234567890123 --reset write $1.bin 0x8000000 Hier die Serial aus st-info ^^^^^^^^^^^^^ Dann braucht man noch ein Script "replace.cyc.sh". Das sieht so aus: #!/bin/bash # # -fcyclomatic-complexit -replacement # sed -e "s/\-fcyclomatic\-complexity//g" $1 >/tmp/$$.tmp;cp /tmp/$$.tmp $1 Denn Cubeide erzeugt Makefiles (die "makefile" heissen - mit kleinem "m") und eine Kommandozeilenoption "cyclomatic-complexity" beinhalten, die der Standard-Compiler so nicht versteht. Und dann passiert bei "./make.sh 743zi_rawClock" das hier: arm-none-eabi-gcc: error: unrecognized command-line option '-fcyclomatic-complexity' Abhilfe schafft for dat in `find .. -name "*.mk"`; do ./replace.cyc.sh $dat;done ("chmod +x replace.cyc.sh" und "chmod +x make.sh" nicht vergessen) Man kann überigens auch im Cubeide dieses "cyclomatic"-Zeug ausschalten. Sehr, sehr gut versteckt natürlich: Dann funktioniert auch "./make.sh 743zi_rawClock". ../Core/Src/main.c:120:1: warning: incompatible implicit declaration of built-in function 'printf' Denn "#include stdio.h" fehlt. Also rein damit: vim ../Core/Src/main.c /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "stdio.h" /* USER CODE END Includes * Nun sieht alle schön aus. 2023-10-05T08:27:03 INFO common.c: Flash written and verified! jolly good! Und die LEDs blinken. Leider sagt "/dev/ttyACM1" (oder ACM0) gar nichts..... Was ihm fehlt ist sowas hier: /* USER CODE BEGIN 0 */ int __io_putchar(int ch) { HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, HAL_MAX_DELAY); return ch; } /* USER CODE END 0 */ Dann klappts auch mit dem Printf auf die USB-Flash-Schnittstelle des Boards. Bei einem per Cubeide eingestellten 8MHz Takt ergibt diese Schleife 250Hz an PC8 (PC8 als Output deklarieren). while (1){ HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_8);HAL_Delay(1);} Daran ändert sich auch nichts, wenn man die Clock auf 80 oder 400 MHz stellt. Und eine Vollwelle ist 4 mSec lang. Das ist seltsam. Beläßt man die Clock bei 400MHz und ändert den Loop so while (1){ HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_8);HAL_Delay(1000); dann ist eine Vollwelle 2 Sekunden lang. Da stimmt doch irgendwas nicht? Bei einem Delay von 100mS blegt eine Vollwelle 200mS, bei einem Delay von 10mS ist eine Vollwelle 22mS lang. 5mS Delay=12mS Amplitudendauer 1mS Delay=4mS 0mS Delay=2mS Amplitudendauer = 500Hz Also es sieht so aus, als ob HAL_Delay(0); genau das tut, was man von HAL_Delay(1) eigentlich erwarten würde. Lässt man das Delay ganz weg, dann erreicht man tatsächlich eine Ausgangsfrequenz an Port von 43,5 MHz! Was dann den Schluss nahelegt: Ein "HAL_GPIO_TogglePin" verschlingt ca. 10 CPU-Taktyklen. Ich hätte mit deutlich mehr gerechnet - aber mal sehen, ob das nicht ein falscher gezogener Schluss ist. Das sieht allerdings auch ein bisschen gerupft aus: Eine "Welle", die sich um 0,5 Volt herum mit einer Amplitude von rund 200mV bei rund 43 MHz bewegt. Ich bezweife stark, dass das ein brauchbares Signal ist..... Aber es ist da - man mus nur sehr genau hinschauen um es überhaupt zu erkennen. Es ist überigens das gleiche Signal, das auch an PC9 anliegt. Also frequenzmässig... Eigentlich sollte dort (also an PC9) lt. Cubeide inzwischen 400MHz anliegen. Das ist also vermtlich schlicht un einfach ein Messfehler des alten Oszilloskops. Ja, denn die Änderung der Teilers in der Cubeide-Clock-Config von 1 auf 15 liefert dann wieder "saubere" 26,6 MHz an PC9. (die durch 15 geteilten 400MHz) Hier die beiden Signale bei 8MHz Takt: ja, das sieht schräg aus.... 1.65 MHz bei 8MHz Clock? Rauskommen sollten lt. Cubeide 1.066MHz. bei einem Teiler von 15 vor MC02.... Achso - Denkfehler.... Oben in der Mitte der Cubeide-Config steht ja gar nicht die SYSCLK. Die steht ja links daneben - vor dem Prescaler. Aber dennoch 1.065MHz sollten rauskommen. Und tun es ja auch.... Lesefehler... *grmpf* (Aktualisierung: das hat sich ebenfalls als Fehler rausgestellt - ich war mit beiden Scope-Kanälen an PC9 - ich sollte mal pausieren...) Zeitbasis testen - aber wie? Am besten mit der Sysyclock - deren Frequenz kennen wir ja nun ziemlich genau. Aber wie kommt man da ran? Man aktiviert wieder Cubeide und das .ioc-File. Un stellt einen Timer (TIM1) so ein, dass er die Clock zählt: und baut dann sowas: uint32_t start_uint32b=getTimer32(); HAL_Delay(1); uint32_t end_uint32b=getTimer32(); Dann müssten ja zwischen "start" und "end" so viel Timerticks liegen, wie die Sysclock Schwingungen ausführt. Also 400 Millionen... +/+ ein paar 100 für den "Overhead". also uint32_t getTimer32() { //liefert den wert des 32-bit timers zurück (später kaskadierte tim1 und tim2 - das hier ist die 16-bit version) uint16_t startl = __HAL_TIM_GET_COUNTER(&htim1); // low-part des timers uint32_t timer32= (uint32_t)startl; return timer32; } void ResetTimer(TIM_HandleTypeDef *htim) { __HAL_TIM_SET_COUNTER(htim, 0); // Zählerwert auf 0 setzen __HAL_TIM_CLEAR_FLAG(htim, TIM_FLAG_UPDATE); // Timer-Flag löschen } printf("Timerticks %ul\n",end_uint32b-start_uint32b); Hier das Resultat von dieser Testroutine: int delay=0; // Verzögerung mittels HAL_Delay int ddiff=0; // Veränderung der Verzögerung zur vorhergehenden int ticks=0; for (int f=0;f<10;f++){ ResetTimer(&htim1); start_uint32b=getTimer32(); HAL_Delay(delay); end_uint32b=getTimer32(); ticks=end_uint32b-start_uint32b; printf("Delay:%i Timerticks start %lu; %lu Diff:%i Ddiff:%i\n",delay,start_uint32b,end_uint32b,ticks,ticks-ddiff); ddiff=ticks; delay=delay+1; } Der Timer wird auf 0 gesetzt, der Inhalt in "start_uint32b" gesichert. Dann wird lediglich ein Delay (mit dem Startwert "0" ausgeführt - was ja wie der Test zuvor ergeben hat eine Verzögerung von 1mS bei 8MHz Takt zur Folge hat. Nach dem Delay wird der Stand des Timers in "end_uint32b" gespeichert und die Differenz in "ticks" abgelegt. Alles wird angezeigt, zusammen mit der Verzögerungsdifferenz zwischen dem aktuellen und dem vorhergehen Delay. (Also dem Wert, den jeweils eine mSekunde "HAL_Delay" für ihre Ausführeng benötigt) (Der Overflow, der bei 16MHz wg. der 16/32-Bit-Typen entsteht, der ist mit Absicht so belassen worden) 8 MHz Clock Press a key.... running.... Timerticks start 27; 69 Diff:42 Delay:0 Timerticks start 32; 3270 Diff:3238 Ddiff:3238 Delay:1 Timerticks start 32; 8204 Diff:8172 Ddiff:4934 Delay:2 Timerticks start 32; 12210 Diff:12178 Ddiff:4006 Delay:3 Timerticks start 32; 14444 Diff:14412 Ddiff:2234 Delay:4 Timerticks start 32; 18648 Diff:18616 Ddiff:4204 Delay:5 Timerticks start 32; 22444 Diff:22412 Ddiff:3796 Delay:6 Timerticks start 32; 26430 Diff:26398 Ddiff:3986 Delay:7 Timerticks start 32; 30444 Diff:30412 Ddiff:4014 Delay:8 Timerticks start 32; 34448 Diff:34416 Ddiff:4004 Delay:9 Timerticks start 32; 38444 Diff:38412 Ddiff:3996 Delay:10 Timerticks start 32; 42448 Diff:42416 Ddiff:4004 16MHz Clock Press a key.... running.... Timerticks start 23; 59 Diff:36 Delay:0 Timerticks start 28; 7866 Diff:7838 Ddiff:7838 Delay:1 Timerticks start 30; 13762 Diff:13732 Ddiff:5894 Delay:2 Timerticks start 30; 19564 Diff:19534 Ddiff:5802 Delay:3 Timerticks start 30; 27560 Diff:27530 Ddiff:7996 Delay:4 Timerticks start 30; 35558 Diff:35528 Ddiff:7998 Delay:5 Timerticks start 30; 43546 Diff:43516 Ddiff:7988 Delay:6 Timerticks start 30; 51604 Diff:51574 Ddiff:8058 Delay:7 Timerticks start 30; 59560 Diff:59530 Ddiff:7956 Delay:8 Timerticks start 30; 2008 Diff:1978 Ddiff:-57552 Delay:9 Timerticks start 30; 10058 Diff:10028 Ddiff:8050 Welche Schlüsse kann man nun aus diesen Ergebnissen ziehen? (ob diese ersten Schlüsse nun wirklich stimmen? wird sich noch zeigen...) Nr. 1 Ein "HAL_Delay" von 1 mS benötigt ziemlich genau 4000 Taktzyklen Nr. 2 Es kann auch schon mal krasse Abweichungen davon geben. (Auf eine mögliche Ursache gehe ich noch ein) Nr. 3 Auf den ersten Blick sieht das in etwa so aus, wie das Ergebnis, was man erwarten konnte. Nr. 4 (Eigentlich selbstverständlich) Man muss den 16-Bit Overflow im Auge behalten Aber nun mal Nachrechnen: bei 8 Mhz ist ein Taktzyklus 0,000000125 Sekunden, 0,000125 milliSec oder 0,125 µSekunden lang. 4000*0,000125 milliSec sind 0,5 milliSekunden. Geht man nun mal davon aus, dass die HAL_Delay-Implementation vermutlich eher mehr als weniger Zeit beanspruchen wird, dann wäre das Ergebnis zumindest merkwürdig. Betrachtet man das nun noch unter de Gesichtspunkt des obigen Fehlers mit der SYSCLK (die ist ja 16 und nicht 8 MHz), dann ist es noch seltsamer.... Ich kommest nochmals auf die oben geäßserte Vermutung 'Ein "HAL_GPIO_TogglePin" verschlingt ca. 10 CPU-Taktyklen.' zurück. Da ja nun auf PC9 die SYSCLK liegt und auf PC8 von einer Endlosschleife getoggelt wird (HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_8 - startet nach Deley-Messung) sieht man, dass diese Schätzung wohl stimmt: Gelb ist der PC8-Pin (hal_gpio_toggle) und Rot ist der PCN-Sysclk-Ausgang. Der Overflow des Systick-Counters - Der ist schnell da. Selbst bei nur 16 MHz SYSCLK nach 4 Millisekunden. Ein 32Bit Zähler würde dann schon fast 5 Minuten brauchen bis zum Overflow. Bei 400MHz ist der Overflow aber auch schon nach 10 Sekunden da.... Aber gehen wir erst mal dieses Problem an: Timer 2 wird von Cubeide angemeckert, weil der sich Ressourcen mit der default-mäßig aktivierten Netzwerkkarte teilt. Da die Netzwerkkarte später noch zum Einsatz kommen soll, weiche ich auf Timer2 aus. Sind ja genaug davon da.... Wozu? Um den Counter von Timer1 zu erweitern - um 16 weitere Bits. Einfach Timer3 auf "Slave-Mode" un den External-Trigger auf TR0 - Ausgang von Timer1. Compilieren tut er schon mal ;-) Aber irgendwie ist das blöd, wenn man die Timer 1 und 3 benutzt, weil Cubeide sie dann mit TIM1 und TIM2 bezeichnet. Das irritiert. Und ausserdem kann man nicht jeden Timer mit jedem anderen kaskadieren. (Ob 1 und 3 geht teste ich später mal) Dann lieber ETH deaktivieren und Timer1 und Timer2 benutzen. Das geht nur, indem man alle Pins, die von ETH benutzt werden, auf "reset state" einstellt. [ das hier ist falsch! Timer 2 wird auf "External Clock Mode 1" und beim Trigger auf TI1FP1, die steigende Flanke des Eingangssignals von TIM1 (TI1FP1), eingestellt. (Ganz nach unten in der Liste scrollen) ] Hier die richtigen Einstellungen mit den das Kascading funktioniert: uint32_t timer32= ((uint32_t)starth << 16) | ((uint32_t)startl); Aber mit werten wie diesen kann man natürlich nicht viel anfagen: (weil dazwischen immer die Zeit für das "printf" mit aufläuft) start_uint32b: 124 timer3: 0 timer1: 124 start_uint32b: 46460 timer3: 0 timer1: 46460 start_uint32b: 27222 timer3: 0 timer1: 27222 Also benötigt man etwas anderes. So was z.B: uint32_t stamps[100]; for (int f=0;f<100;f++){ // Erfassen von 100 Timestamps stamps[f]=getTimer32(); // for (int c=0;c<200;c++){} // Eine Verzögerungsschleife - wenn man mag } for (int f=0;f<100;f++){ // Eine Ausgabeschleife printf("stamp[%i]: %8lu Diff: %8lu\n",f,stamps[f],stamps[f]-stamps[f-1]); } Die Schleife nichts zu, ausser Ticks zu speichern, ist sie natürlich sehr schnell. stamp[ 2]: 220 Diff: 62 stamp[ 3]: 286 Diff: 66 stamp[ 4]: 354 Diff: 68 Daher mit Verzögerungsschleife (auf 1000 eingestellt) Das sieht gut aus: stamp[ 17]: 61950 Diff: 3654 stamp[ 18]: 65610 Diff: 3660 stamp[ 19]: 69260 Diff: 3650 stamp[ 20]: 72920 Diff: 3660 stamp[ 21]: 76556 Diff: 3636 stamp[ 22]: 80222 Diff: 3666 stamp[ 23]: 83874 Diff: 3652 stamp[ 24]: 87534 Diff: 3660 stamp[ 25]: 91170 Diff: 3636 stamp[ 26]: 94836 Diff: 3666 stamp[ 27]: 98486 Diff: 3650 stamp[ 28]: 102058 Diff: 3572 stamp[ 29]: 105702 Diff: 3644 Nun mal nachrechen.... Wenn die SYSCLK mit 16MHz läuft sollte man mit HAL_Delay(1000) jeweils 16.000.000 Im Zähler stehen haben. Also einfach mal HAL_Delay(1000) statt der Verzögerungsschleife einbauen: Da kommt aber nicht das erwartete ergenbis heraus. 16 Mio. werden erst nach 4 Sekunden erreicht. Das sieht aus, als würde die Clock nur mit 4 MHz reinkommen: stamp[ 1]: 4001722 Diff: 4001630 stamp[ 2]: 8005698 Diff: 4003976 stamp[ 3]: 12009700 Diff: 4004002 stamp[ 4]: 16013710 Diff: 4004010 Das liegt wohl am HPRE-Prescaler der von Cubeide auf "2" und somit auf 4MHz eingestellt ist. Das heisst aber auch: "Internal Clock" in der Cube-Oberfläche heisst: nicht die SYSCLK (MHz) aus der Cubeide "Clock-Configuration" sondern heisst "das, was aus HPRE rauskommt". Und dann passt auch die oben erwöhte Feststellung: 1 mSec entspricht ca. 4000 Taktzyklen. Dann ist man nach einer Sekunde bei 4 Millionen. Zwischendurch mal ein kleiner ADC-Exkurs. Hier die Einstellungen: und hier die Codezeilen dazu: /* Start the ADC conversion on channel 11 */ if (HAL_ADC_Start(&hadc1) != HAL_OK) { Error_Handler(); } /* Wait for the conversion to complete (polling method) */ if (HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY) != HAL_OK) { Error_Handler(); } /* Read the ADC value from channel 11 */ uint16_t adcValue = HAL_ADC_GetValue(&hadc1); printf("adcValue:%i\n",adcValue ); HAL_Delay(500);} Heraus kommt dann sowas: (PA0 an Poti zwischen 0 und 3.3 V. am Mittelabgriff) adcValue:142 adcValue:142 adcValue:146 adcValue:146 adcValue:142 adcValue:146 Nachdem wandeln und Timerticks zählen ja nun funktioniert: Dann schaun wir doch mal, wie lange so eine Wandlung, auf die man wartet, denn so dauert. start_uint32b=getTimer32(); HAL_Delay(delay); end_uint32b=getTimer32(); start_uint32b=getTimer32(); /* Start the ADC conversion on channel 11 */ if (HAL_ADC_Start(&hadc1) != HAL_OK) { Error_Handler(); } /* Wait for the conversion to complete (polling method) */ if (HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY) != HAL_OK) { Error_Handler(); } /* Read the ADC value from channel 11 */ uint16_t adcValue = HAL_ADC_GetValue(&hadc1); end_uint32b=getTimer32(); uint32_t ticks=end_uint32b-start_uint32b; printf("adcValue:%i Dauer der Wandlung: %10lu Ticks = %.4f Millisekunden\n",adcValue,ticks,(float)ticks/ 4000.0); Wenn man sich nun wundert, warum da keine Ausgabe in Millisekunden erfolgt sondern nur da steht: adcValue:126 Dauer der Wandlung: 1094 Ticks = Millisekunden Dann sollte man die Compiler-Einstellung nachsehen. Die Default-Einstellung sind nämlich nicht so der gelungene Wurf. Wenn die Floating-Point-Libs nicht aktiviert sind, dann werden diese Ausgaben ohne jede Fehlermeldung oder Warnung einfach verschluckt. Hat man das aber aktiviert dann gibts auch eine Ausgabe: adcValue:166 Dauer der Wandlung: 1120 Ticks = 0.2800 Millisekunden adcValue:158 Dauer der Wandlung: 1120 Ticks = 0.2800 Millisekunden adcValue:178 Dauer der Wandlung: 1120 Ticks = 0.2800 Millisekunden adcValue:166 Dauer der Wandlung: 1120 Ticks = 0.2800 Millisekunden Und 0.28 mS ist eigentlich ganz OK - Wenn man es sich leisten kann, auf die Wandlung zu warten. Wenn nicht, dann gibt es Alternativen. Wo wir grade einmal dabei sind.... Wie ist es mit dem DAC ? Probieren wir auch das gleich mal aus. // Starte den DAC-Kanal if (HAL_DAC_Start(&hdac1, DAC_CHANNEL_1) != HAL_OK) { Error_Handler(); } uint16_t dac_value = 0; uint8_t direction = 1; // Aufsteigend while (1) { // Schreibe DAC-Wert HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, dac_value); // Inkrementieren oder dekrementieren basierend auf der Richtung if (direction) { dac_value++; if (dac_value >= 4095) { direction = 0; // Richtung auf absteigend } } else { dac_value--; if (dac_value == 0) { direction = 1; // Richtung auf aufsteigend } } } Aber da tut sich erst mal nix auf PA4..... Einen Init-Code für PA4 hat Cubeide auch nicht erzeugt :-( Irgendwie ist dem Ding nicht zu trauen. mal von Hand nachtragen: GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); tut sich auch nix.... Liegt aber gar nicht am fehlenden GPIO_Init. Denn wenn man den _richtigen_ Pin zu messen anschliesst, dann kommt auch ein Signal. Auch, wenn man das obige GPIO_Init wieder ganz raus nimmt. 252Hz - das müsste man doch hören können.... kann man: h743zi_dac1.wav semi-sinusförmig klingt es etwas gefälliger int amplitude = 2047; // Halbe Amplitude, um den Bereich von 0 bis 4095 zu decken int frequency = 10; // Frequenz in Hertz int numSamples = 100; // Anzahl der Samples für die Sinuskurve for (int i = 0; i < numSamples; i++) { double value = amplitude * sin(2 * M_PI * frequency * i / numSamples) + amplitude; int scaledValue = (int)value; // Skalieren auf den Bereich von 0 bis 4095 // printf("%d\n", scaledValue); dac_value=scaledValue; HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, dac_value); } Auch nicht schön - aber zum testen reichts: h743zi_semi_sinus.wav Zeit einen Schlussstrich zu ziehen ###################################################################### Dait sind nun USB-Seriell, GPIO, ADC, DAC und Timer zumindest schon mal alle irgendwie in Betrieb. Das ist aber noch nicht mal die Hälfte dessen, was der 743 so alles an Bord hat. Es fehlen noch: SPI, I2C, DMA, ETH, LCD, SD/MMC, CAN, CRC, RNG, WDT, SDRAM/SDIO um nur einige zu nennen. Aber eine Bais für Messungen das Zeitverhaltens steht nun. Wie testet man nun? Sinnigerweise mit 2 Board würde ich sagen: Der bislang verwendete h743zi und als Ggenseite ein f746zg. Letzterer knann zwar "nur" 216MHz - aber wenn man damit Schaltgeschwindingkeiten im 2-stellingen MHz Breich erreichen kann, dann wäre schon viel gewonnen. Man könnte jetzt eine Schleife bauen, die mit immer weiter steigender Frequenz einen Port umschaltet und mit dem zweiten µC erfassen bis nichts mehr geht. Vernünftiger scheint mir aber: Der H743 schaltet einen Pin um, an den der H746 lesend angeschlossen ist, wartet auf eine Quittung per Umschaltung eines Eingangspins durch den F746 und schaltet danach den Pin wieder um. Dann sollten beide µCs sich gegenseitig bis an die Höchstgrenze ihrer Schaltgeschwindigkeiten heran tasten. Und die Geschwindigkeit, die kann dann mit dem Sysclock-Counter sehr genau bestimmt werden. Ich bin sehr gespannt. Eigentlich könnte man es je erst einmal mit einer Rückkopplung auf dem eigenenen Board versuchen. Also aus PC10 einen Input und aus PC11 einen Output (high- level/high- speed) machen und mit einander und einem Oszilloskop verbinden. while (1) { printf("loop....\n"); if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_10)==GPIO_PIN_SET) { printf("set PC11 to 0\n"); // ist gesetzt - dann zurück damit HAL_GPIO_WritePin(GPIOC, GPIO_PIN_11, GPIO_PIN_RESET); } if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_10)==GPIO_PIN_RESET) { printf("set PC11 to 1\n"); // ist nicht gesetzt - also setzen HAL_GPIO_WritePin(GPIOC, GPIO_PIN_11, GPIO_PIN_SET); } HAL_Delay(1); } Bei diesem Test mit 1mS Delay und Debug-Ausgabe kommt man auf 83Hz. Das ist jetzt nicht so der Hit.... ;-) Ohne printf-Ausgabe und mit 1mS Delay kommt er auf 250 Hz - das ist auch etwas mager - zeigt aber wohl, dass HAL_Delays <10mS ziemlicher Unfung sind, weil der Aufruf viel Ovwehead mit sich herumschleppt. Was sich gerade bei niedrigen Taktraten sehr deutlich auswirkt. Ganz ohne Delay kann sich das Oszilloskop nicht entscheiden, ob da nun 24kHz oder 10MHz oder ganz was andere bei herauskommt. (Das Scope kann nur 30MHz) Also muss da wohl am ein "richtiger" Logic-Analyzer ran. und da kommen dann Pulsbreiten von 20 uSec an - das ist nicht das, was ich erwartet hatte..... Das ist auch für 8MHz Sytemtakt etwas wenig..... Bei 400MHz kommen rund 1.05MHz heraus - lt. Oszilloskop. Was dann etwa 0.5 uSec Responszeit bedeuten würde. Das ist ganz OK - aber auch nicht das, was ich erwartet hätte. Wo liegt das Problem? Wer verbrät die Zeit? Läuft er nach dem Setzen von PC11 noch unverrichter Dinge über die nachfolgende Abfrage weil der Port nich so schnell schaltet? Nein, das ist nicht der Fall. Dann mal nachsehen, ob HAL hier so viel Rechenzeit verbrät. Mit direkter Registerprogrammierung: (aber ohne port-Abfrage dazwischen. Nur Ausgabe!) *gpioc_odr &= ~GPIO_PIN_11_MASK; *gpioc_odr |= GPIO_PIN_11_MASK; Kommt man rund 5MHz Ausgabe bei den GPIO-Ports. Betrachtet man das nun ma näher und speichert in der Schleife je 2x die Sysclock-Counter, while (f<100) { *gpioc_odr &= ~GPIO_PIN_11_MASK; stamps[f]=getTimer32();f++; *gpioc_odr |= GPIO_PIN_11_MASK; stamps[f]=getTimer32();f++; } dann kommt das dort heraus: stamp[ 45]: 4216 Diff: 88 stamp[ 46]: 4308 Diff: 92 stamp[ 47]: 4398 Diff: 90 stamp[ 48]: 4490 Diff: 92 stamp[ 49]: 4578 Diff: 88 stamp[ 50]: 4672 Diff: 94 Natürlich verfälschen die speichers, vergleichs- und zähl- Operation das ergebnis. Aber wenn man bei hier 400MHz von 200MHz effektivem (vom TIM1 gezählten) Takt ausgeht, dann sind das 0,000000005 Sekunden oder 5 Nanosekunden pro Takt. Wenn ein Schleifendurchlauf rund 200 Takte dauert, dann sind das 1 nano - oder eine Mikrosekunde. Messen kann ich ca. 0.5 µSec beim GPIO-Port PC11. Klingt erst mal komisch, aber es wird ja in jedem Schleifendruchlauf 2x geschaltet. Also etwas weniger als 100 Timerticks. Das wären rund 500 nS und das sind .5µSec die auch am Oszilloskop oder Analyser zu sehen sind. Wenn man das bisher erstellte kombiniert und den Pin mit der aus der per DAC generierten Sinuskurfe mit dem ADC-Pin eines anderen h743 (oder hier 746) verbindet, dann erhält man mit dieser Schleife den darunter stehenden Output, den man mit dem noch weiter unten steheden Python-Programm per Mathlib direkt in eine Live-Grafik umwandeln kann. sinus.ogg ------ ADC einlesen und ausgeben ------------- while (1) { if (HAL_ADC_Start(&hadc3) != HAL_OK) { Error_Handler(); } if (HAL_ADC_PollForConversion(&hadc3, HAL_MAX_DELAY) != HAL_OK) { Error_Handler(); } uint16_t adcValue = HAL_ADC_GetValue(&hadc3); printf("adcValue:%i\n",adcValue ); HAL_Delay(500); } ---------------------------------------------- Output: adcValue:2039 adcValue:2671 adcValue:3304 adcValue:3731 adcValue:4012 adcValue:4008 ..... verabeitung mit /dev/ttyACM0|head -n 40|python3 plot.py plot.py ======= import matplotlib.pyplot as plt import sys # leere Listen für Zeitpunkte und Daten time_points = [] data = [] # Öffnen von sys.stdin zum lesen per Pipe for line in sys.stdin: # Am Doppelpunkt trennen parts = line.strip().split(':') if len(parts) == 2 and parts[0] == 'adcValue': try: # Konvertieren nach int value = int(parts[1]) # Zeitpunkt (Anzahl der gelesenen Zeilen) und den Wert time_points.append(len(data)) data.append(value) # Begrenzen der Anzahl der angezeigten Datenpunktet if len(time_points) > 100: time_points.pop(0) data.pop(0) # Ein einfaches Liniendiagramm plt.clf() plt.plot(time_points, data, marker='o', linestyle='-') # Achsentitel plt.xlabel('Zeitpunkt') plt.ylabel('adcValue') # Anzeige Diagramm plt.pause(.1) # Aktualisieren alle 0.1 Sekunden except ValueError: print("Ungültige Daten:", line) # Schließen Diagramm plt.show() Nächster Schritt: ETH Das wird spanned.... Versucht 1 mit dem h743zi. Neues Projekt auf Basis dieser Hardware und mit Init-Defaults anlegen. Unter "System" den CPU-Chache enablen. Wichtig! Sonst kann LWIP nicht aktiviert werden. ja, es bleibt spannend.... ------------------------ 2023-06-04 06:13 AM There have been many discussions on the ethernet in this forum. It looks like CubeMX/IDE still struggles with generating working code for ethernet. -------- und ---- if you generate a new project in CubeIDE or CubeMX, it is not expected to work TL;DR so always do start from these examples, do not change a single bit until it works. https://github.com/STMicroelectronics/STM32CubeH7/tree/master/Projects/NUCLEO-H743ZI/Applications/LwIP/LwIP_HTTP_Server_Netconn_RTOS ----------------------- genau so sieht das aus.... weder für h743zi noch für f746zg klappt das. Seit mehr als 4 Jahren ist bekannt und wird darüber diskutiert, dass Cubeide/MX fehlerhaften Code generieren. Ich frage mich: warum wird das nicht einfach irgendwann mal geändert? Man hat ja auch die Zeit gefunden, in jedes dämliche Update immer wieder aufs neue dämliche PR-Videos einzubauen, die Übertragungszeit und Speicherplatz fressen :-( Also schön, machen wir das mal: LwIP_HTTP_Server_Netconn_RTOS runterladen in cubeide importieren und compilieren (auf dem h743) Resultat: es tut..... Aber damit sind nun sämtliche Möglichkeiten einer Cubeide/MX-Nutzung zu generieren von Code hinfällig. Bei diesen Examp,es sind keine .ioc-Files dabei. Können sie ja auch nicht sein - denn sie würden ja eh nicht funktionieren. Nun muss man sich natürlich wieder völlig umstellen. Die Strukturen sind anders ("main.c" liegt unter Application/User/), es gibt kein zentrales "main()" mit "while" mehr, es gibt kein /Debug/makefile mehr usw.... Nein, stimmt nicht.... Ein "makefile" gibt es doch. Ganz leicht zu finden. Unter STM32CubeH7/Projects/NUCLEO-H743ZI/Applications/LwIP/LwIP_HTTP_Server_Netconn_RTOS/STM32CubeIDE/Debug. Nun bauen wir was ein um zu sehen, ob die CPU noch lebt: int f=0; // Funktion zur Steuerung der Status-LED void toggleStatusLED(int led){ f++; if (f>500) { if (led & 1 ) HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0); if (led & 2 )HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_7); if (led & 4 )HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_14); } if (f>1000) f=0; } void vApplicationTickHook(void) { // Dieser Code wird bei jedem Systemtakt aufgerufen toggleStatusLED(1); } void vApplicationIdleHook(void) { // Wenn mal nix zu tun ist.... toggleStatusLED(2); } Aber das recht auch noch nicht. Man will ja auch was zum lesen haben: #include "stm32h7xx_hal.h" // Stellen Sie sicher, dass die richtige HAL-Datei für Ihr Mikrocontroller-Modell eingebunden ist. // Deklaration des UART3-Handles UART_HandleTypeDef huart3; int main(void) { // HAL-Initialisierung (z.B. HAL_Init() usw.) // Konfiguration für UART3 huart3.Instance = USART3; // UART3-Schnittstelleninstanz huart3.Init.BaudRate = 9600; // Baudrate einstellen huart3.Init.WordLength = UART_WORDLENGTH_8B; // Datenwortlänge 8 Bit huart3.Init.StopBits = UART_STOPBITS_1; // 1 Stop-Bit huart3.Init.Parity = UART_PARITY_NONE; // Keine Parität huart3.Init.Mode = UART_MODE_TX_RX; // Senden und Empfangen aktivieren huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE; // Keine Hardware-Flusskontrolle huart3.Init.OverSampling = UART_OVERSAMPLING_16; // Oversampling-Modus 16x if (HAL_UART_Init(&huart3) != HAL_OK) { // Fehler bei der Initialisierung des UART3 Error_Handler(); // Sie müssen eine Fehlerbehandlungsfunktion implementieren } // Weitere Initialisierung und Code hier... while (1) { // Hauptprogrammschleife } } Auch hieraus kann man einen LWIP-Server erzeugen, der zumindest auf "ping" reagiert. https://community.st.com/ysqtg83639/attachments/ysqtg83639/stm32-mcus-knowledge-base/24/2/STM32H7_ETH_Example_IDE1.6.1 Sonst kann der zwar gar nichts. Und er schmiert sofort ab, wenn man ihn von 2 Adressen aus anpingt oder versucht ihn zu lynxen. Aber hey, besser als nix ;-) Man wir ja anspruchsloser wenn man mit fehlerhafter Software arbeiten muss. Und nun kann man auch mit dem mitgelieferten .ioc-File z.b. den USART3 aktivieren und wenn man sein eigenesn "putchar" gebaut hat (siehe oben), dann kann man auch Meldungen ausgeben. Die .ioc-Config sieht zwar echt grottig aus - und ich hätte ihr nie zugetraut, dass das läuft - aber es geht: Ich hatte echt schon wunderschöne .ioc's ohne Warnungen oder rote Bereiche gebaut, die gar nichts taten..... Entfernt man die roten und gelben Pinbelegungen, dann funktioniert überigens der "ping" nicht mehr. Also schön drin lassen. Nur PB0 auf "Reset_state" und "USART3" ascnc-enablen. Achja, nur mit Linux wird bei ST wohl nicht ausschliesslich gearbeitet. Da war doch tatsächlich ein Backslash im Debug-Pfad (weswegen man nicht auf anhoieb compilieren kann). Insgeamt ist das ETH-Verhalten in Verbindung mit Cubeide (besonders des h743zi) recht enttäuschend. Ganz besodners dann, wenn man funktionierende Konfigurationen hinbekommen hat und dann nach einem Cube-Update plötzlich gar nichts mehr funktioniert. Jetzt reichts erstmal mit ETH - wieder ein paar kleine Exkurse... Auf einem Thread von https://www.mikrocontroller.net stammt ein Vorschlag, den ich so umgesetzt habe: ../Core/Inc/fastToggle.h #ifndef TOGGLE_H #define TOGGLE_H #include "stm32h7xx_hal.h" void fastToggle(GPIO_TypeDef* GPIOx, uint16_t pinMask); #endif ../Core/Src/fastToggle.c #include "fastToggle.h" void fastToggle(GPIO_TypeDef* GPIOx, uint16_t pinMask) { __asm__ volatile ( "1: str %[maskS], [%[BSRR]]\n" "str %[maskR], [%[BSRR]]\n" "str %[maskS], [%[BSRR]]\n" "str %[maskR], [%[BSRR]]\n" "str %[maskS], [%[BSRR]]\n" "str %[maskR], [%[BSRR]]\n" "str %[maskS], [%[BSRR]]\n" "str %[maskR], [%[BSRR]]\n" "str %[maskS], [%[BSRR]]\n" "str %[maskR], [%[BSRR]]\n" "str %[maskS], [%[BSRR]]\n" "str %[maskR], [%[BSRR]]\n" "str %[maskS], [%[BSRR]]\n" "str %[maskR], [%[BSRR]]\n" "b 1b" : : [maskS] "l" (pinMask), [maskR] "l" ((uint32_t)pinMask << 16), [BSRR] "l" (&GPIOx->BSRR) : "memory" ); } Das Resultat: 16.6 MHz Ausgangssignal - eigentlich schon ganz gut. Viel mehr, als mit einer HAL-GPIO-Schleife machbar war. 30nS von Pegelwechsel zu Pegelwechsel. Bei 400/2 MHz Sysclock. Würde bedeuten 6 Zyklen pro Toggle. Das erscheint auf den ersten Blick stimmig. "Sinnvoll" ist das schon aufgrund der Signalqualität eher nicht. Aber ein sehr schönes Experiment. Es geht hier bald mit neuen Infos weiter - aber nun erst mal wieder ein weiterer Exkurs - diesmal zum Thema SPI am Beispiel des max7219 mit einem 8-fach-7-Segment. Hier aber nicht die Verwendung des integrierten STM32-SPI sondern mit einem hangeschriebenen. Die Minimalfassung von ax7219.c: #include "max7219.h" void send_spi(uint8_t adr, uint8_t val){ // Segmentadresse, Ausgabewert uint16_t lval=adr*256+val; HAL_GPIO_WritePin(CS_MAX7219_MAN_Port, CS_MAX7219_MAN_Pin, GPIO_PIN_RESET); //CS/NSS-Signal for (int i = 15; i >= 0; i--) { HAL_GPIO_WritePin(CL_MAX7219_MAN_Port, CL_MAX7219_MAN_Pin, GPIO_PIN_RESET); //CLOCK-Signal uint16_t mask = 1 << i; if (lval & mask) { HAL_GPIO_WritePin(DT_MAX7219_MAN_Port, DT_MAX7219_MAN_Pin, GPIO_PIN_SET); // Datenleitung } else { HAL_GPIO_WritePin(DT_MAX7219_MAN_Port, DT_MAX7219_MAN_Pin, GPIO_PIN_RESET); // Datenleitung } HAL_GPIO_WritePin(CL_MAX7219_MAN_Port, CL_MAX7219_MAN_Pin, GPIO_PIN_SET); //CLOCK-Signal - übernahme bei steigender flanke HAL_GPIO_WritePin(CL_MAX7219_MAN_Port, CL_MAX7219_MAN_Pin, GPIO_PIN_RESET); //CLOCK-Signal } HAL_GPIO_WritePin(CS_MAX7219_MAN_Port, CS_MAX7219_MAN_Pin, GPIO_PIN_SET); //CS/NSS-Signal } Und die Minimalfassung von max7219.h: #define MANSPI // manuelles SPI-handling #define CS_MAX7219_MAN_Port GPIOB // manuelles setzen #define CS_MAX7219_MAN_Pin GPIO_PIN_4 // der NSS/CS-Leitung #define DT_MAX7219_MAN_Port GPIOA // manuelles erzeugen des Data-Signals #define DT_MAX7219_MAN_Pin GPIO_PIN_5 // Data-leitung #define CL_MAX7219_MAN_Port GPIOA // manuelles erzeugen des Clock-Signals #define CL_MAX7219_MAN_Pin GPIO_PIN_6 // CLK-leitung void max7219_init7Seg(void) ; // initialisierung des max7219 void max7219_writeInteger(int f); // ausgabe Integerwert Was wird noch benötigt? Eine Initalisierung, ein "Zeichensatz" und eine Ausgabefunktion: void max7219_init7Seg() { send_spi(0x0b,7); // Anzahl 7-Segment-Elemente send_spi(0x0a,1); // Helligkeit send_spi(0x9,0); // BDC-Modus Aus send_spi(0xff,0x00); // Testmode aus send_spi(0x0c,0x01); // Shutdown aus send_spi(0x02,0xff); } static uint8_t segchar[] = { // der "Zeichensatz" 0x7E, // "0" 0x30, // "1" 0x6D, // "2" 0x79, // "3" 0x33, // "4" 0x5B, // "5" 0x5F, // "6" 0x70, // "7" 0x7F, // "8" 0x7B, // "9" 0x01, // "-" 0x4F, // "E" 0x37, // "H" 0x0E, // "L" 0x67, // "P" 0x80, // "." 0x00 // " " }; void max7219_print(int pos,int val){ max7219_send(pos,val); } void max7219_printCh(int pos,int val){ max7219_send(pos,segchar[val]); } void max7219_setIntensity(int intensity){ max7219_send(0x0a,intensity); } #define maxSegments 8 // gesamte Segmentanzahl void max7219_writeInteger(int val) { // gibt Integerwert mit führenden Leerzeichen aus char chars[maxSegments+1]; sprintf(chars, "% 8d", val); int chr=0; for (int currentSegment = 0; currentSegment < maxSegments; currentSegment++) { chr=chars[currentSegment]-48; if (chr<0) chr=16; max7219_printCh(8-currentSegment,chr); } } Damit funktioniert die erste Ausgabe schon mal. Video Nun kann man das ganze auch mit dem "richtigen" SPI des Contraollers machen: Noch nicht fertig: (ws2812 16x16 LED-Matrix) Video Auch noch nicht fertig: (/DYP-ME007V1 Ultraschall-Entfernungsmesser) Video ############################################################# BREAK! Cubeide hat mich so daratig wütend gemacht, dass ich nun hier mal weitermache mit STM32, F7/H7 - am konkreten Beispiel F746ZG (ohne Kirmes) ############################################################# Warum findet man nichts gescheites zum FAT-FS/USB im Netz? Ich finde es jedenfalls ungehäuer ermüdend und wenig zielführend, wenn man sich halbstündige low-res-Videos, in denen in gebrochenem Englisch, mit Fliege und/oder nerviger Hintergrundmusik und blödsinnigen Trailern wild auf Entwicklungsumgebungen herumgeklickt wird - die bei nächsten Update sowieso wieder völlig anders aussehen.... Worum gehts? Ein FAT-Filesystem am USB-OTG des F746 oder H743 zu betriben, das wäre klasse. Sticks sind handlicher und gängiger als SD-Cards. Leider findet man unter den Cube-Examples gar nichts brauchbares. Doch halt! Da ist doch ein FatFS_USBDisk bei den Examples? Dafür lege ich ein neues "Workspace" an F746_DEMO. Darin dann NEW->STM32-Projekt->Example-Selector->746zg->FatFS_USBDisk Die dann erscheinende "natural/marjplace"-Warnung geflissentlich ignorieren. Project-Explorer aufrufen. Und - oh Wunder! Ist kein .IOC-File dabei. Ja, ST-Cube ist nichtmal kompatibel zur eigenen Example-Umgebung. Aber es kommt noch viiiiel besser. abwarten.... Erst mal complieren - naiv wie man so ist.... Run->STM32-Application->Debugger->ST-Link->Scan->Apply Astrein.... Grüne USB-LED (Nähe Netzwerkbuchse) geht an, LED am USB-Stick geht an. Alles toll... Was es aber nicht gibt: keine Status-LED, kleine Ausgabe auf USART3 (USB-Leitung zum PC), und natürlich keine Schreibzugriffe auf den Stick. Also ungefähr so geil wie n gebrickts Android-Handy. Erster Schritt: irgndeine Meldung irgendwo hin bekommen. Also sowas da rein (s.o.) /* USER CODE BEGIN 0 */ int __io_putchar(int ch) { HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, HAL_MAX_DELAY); return ch; } /* USER CODE END 0 */ neee.... ganz so einfach ist das nicht... Nach "USER CODE BEGIN 0" oder so muss man nicht suchen - ist halt kein .IOC-Projekt. Nach USART-Inits oder so natürlich auch nicht. Wäre ja zu viel Entwicklungsarbeit gewesen :-( Also von Hand rein: (irgendwo aus den eigenen Projekten klauen) UART_HandleTypeDef huart3; static void MX_USART3_UART_Init(void) { huart3.Instance = USART3; huart3.Init.BaudRate = 115200; huart3.Init.WordLength = UART_WORDLENGTH_8B; huart3.Init.StopBits = UART_STOPBITS_1; huart3.Init.Parity = UART_PARITY_NONE; huart3.Init.Mode = UART_MODE_TX_RX; huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart3.Init.OverSampling = UART_OVERSAMPLING_16; huart3.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE; huart3.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; if (HAL_UART_Init(&huart3) != HAL_OK) { Error_Handler(); } } und im main aufrufen. MX_USART3_UART_Init(); printf("/STM32/_CENSORED_/F746_DEMO/FatFs_USBDisk 2023.12.18 21:50\n"); printf("das wäre eigentlich die Arbeit von ST gewesen :-(\n"); HAL_Delay(2000); makse.sh erstellen und die dämlichen cyclo-kacke entfernen (s.o.) for dat in `find .. -name "*.mk"`; do /tmp/replace.cyc.sh $dat;done ./make.sh FatFs_USBDisk Aber tut sich nix am USB-PC-Port. Wird wohl am Init liegen. Also machen wirs zu Fuss und per Baremetal. scheiss auf HAL :-( Dem MX_USART... werfen wir weg -stattdessen kommt das hier rein: void USART3_SendString(const char *str) { USART3->CR1 |= USART_CR1_TE; //enable transmitter // USART3->CR1 &= ~USART_CR1_TXEIE; //IRs aus zum testen while (*str != '\0') { // Transmitter bereit ? while (!(USART3->ISR & USART_ISR_TXE)); // Zeichen senden USART3->TDR = *str++; } } int __io_putchar(int ch) { while (!(USART3->ISR & USART_ISR_TXE)); USART3->TDR = ch; return ch; } int _write(int file, char *ptr, int len) { int DataIdx; for (DataIdx = 0; DataIdx < len; DataIdx++) { __io_putchar(*ptr++); } return len; } void USART3_Init(void) { RCC->APB1ENR |= RCC_APB1ENR_USART3EN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; GPIOD->MODER |= GPIO_MODER_MODER8_1; // Alternative Funktion GPIOD->AFR[1] |= (7 << ((8 - 8) * 4)); // AF7 für USART3 USART3->BRR = SystemCoreClock / 19200; //9600; USART3->CR1 |= USART_CR1_TE; // Transmitter aktivieren USART3->CR1 |= USART_CR1_UE; // USART3 aktivieren } Aber mehr als das hier bekommt man dann auch nicht zu sehen: /STM32/_CENSORED_/F746_DEMO/FatFs_USBDisk 2023.12.18 21:50 das wäre eigentlich die Arbeit von ST gewesen :-( Irgendwo könnte man ja einen Debug-level vermuten. Und tatsächlich. Den gibts ../../../Inc/usbh_conf.h:#define USBH_DEBUG_LEVEL 0 Also mal auf 3 setzen. Und dann kommt tatsächlich was. Nicht so das, was man erwartet. Aber Hey, wer will schon unverschämte Ansprüche an eine Demo-Software des Herstellers stellen? das wäre eigentlich die Arbeit von ST gewesen :-( USB Device Connected USB Device Reset Completed PID: 1212h VID: 14cdh Address (#1) assigned. Manufacturer : Generic Product : Mass Storage Device Serial Number : 121220160204 Enumeration done. This device has only 1 configuration. Default configuration set. Switching to Interface (#0) Class : 8h SubClass : 6h Protocol : 50h MSC class started. Number of supported LUN: 1 LUN #0: Jedenfalls merkt er, wenn ein Device conected oder Disconnected und er erkennt Größe und Seriennummer. Wem das nicht reicht, der muss sich halt mal durchs Netz und freundliche Foren wühlen und wird dennoch keine gescheite Antwort finden. Fürs erste langs mir erst mal wieder. Aber das wird fortgesetzt...... .... und weiter gehts. Also es will nicht. Warum nicht? Ich habe das gleiche gemacht wie vor ein paar Tagen. Und wenn ich das damalige Ergebnis mit dem von jetzt vergleiche: Das alte läuf, das neue gibt nur die Startmeldung aus. Was ist da wieder los? Ein "wc diff.dat" (das Resultat eines "diff" auf alt/neu liefert 149 Zeilen. Daraus filtern wir die raus, die sich in jedem Fall ändern, da neu generiert. Also alle grep -v "\.su und"|grep -v "\.o und"|grep -v "\.d und "|wc Dann bleiben noch 45 Unterschiede in diff.filter. Wenn ich daraus dann alle "LICENSE.txt", tars usw. wegwerfe, dann bleiben noch übrig: 2 unterschiedliche converter.logs - die sich aber nur beim Datum unterscheiden Project: FatFs_USBDisk Converter: ST System Workbench for STM32 project converter Date: 20231218 Unknown value type "" for id gnu.c.link.option.libs on tool fr.ac6.managedbuild.tool.gnu.cross.c.linker Unknown value type "" for id gnu.c.link.option.paths on tool fr.ac6.managedbuild.tool.gnu.cross.c.linker Option fr.ac6.managedbuild.tool.gnu.cross.cpp.linker.noexceptions is no longer supported Option fr.ac6.managedbuild.tool.gnu.cross.cpp.linker.nortti is no longer supported Project: FatFs_USBDisk Converter: ST System Workbench for STM32 project converter Date: 20231213 Unknown value type "" for id gnu.c.link.option.libs on tool fr.ac6.managedbuild.tool.gnu.cross.c.linker Unknown value type "" for id gnu.c.link.option.paths on tool fr.ac6.managedbuild.tool.gnu.cross.c.linker Option fr.ac6.managedbuild.tool.gnu.cross.cpp.linker.noexceptions is no longer supported Option fr.ac6.managedbuild.tool.gnu.cross.cpp.linker.nortti is no longer supported Alle .mk-Files. Und ein paar Libs, die es nur in der neue Fassung gibt. (ja, ich habe die Libs einfach übergmangelt, weil ich ja weiss, dass die Libs, die in der FatFs_USBDisk des Examples mitgeliefert werden gar nicht funktionieren) Nur in /FatFs_USBDisk/Middlewares/ST/STM32_USB_Host_Library/Class: CDC. Nur in /FatFs_USBDisk/SW4STM32/STM32F746ZG_Nucleo/Debug/Drivers/BSP: Adafruit_Shield. Nur in /FatFs_USBDisk/SW4STM32/STM32F746ZG_Nucleo/Debug/Drivers/BSP: Components. Nur in /FatFs_USBDisk/SW4STM32/STM32F746ZG_Nucleo/Debug/Drivers/BSP: STM32F7xx_Nucleo_144. Nur in /FatFs_USBDisk/SW4STM32/STM32F746ZG_Nucleo/Debug/Drivers/CMSIS: Device. Nur in /FatFs_USBDisk/SW4STM32/STM32F746ZG_Nucleo/Debug/Drivers/CMSIS: Include. Nur in /FatFs_USBDisk/SW4STM32/STM32F746ZG_Nucleo/Debug/Drivers/STM32F7xx_HAL_Driver: Inc. Nur in /FatFs_USBDisk/SW4STM32/STM32F746ZG_Nucleo/Debug/Drivers/STM32F7xx_HAL_Driver: Src. Jetzt mal die ganz brutale Gegenprobe: (vorher sichere ich das gesamte Workspace "F746_DEMO") find /tmp/FatFs_USBDisk -name "*.mk" Liefert insgesamt 12 .mk-Files. Alt und neu nochmal sichern tar -czvf MKs.tgz `find /tmp/FatFs_USBDisk -name "*.mk"` und dann kopieren. Danach ein "make.sh" mit den neuen .mk-files. ./make.sh FatFs_USBDisk arm-none-eabi-size FatFs_USBDisk.elf text data bss dec hex filename 38988 176 8608 47772 ba9c FatFs_USBDisk.elf Finished building: default.size.stdout st-flash 1.7.0 2023-12-19T08:15:04 INFO common.c: F7xx: 320 KiB SRAM, 1024 KiB flash in at least 2 KiB pages. .... 2023-12-19T08:15:06 INFO common.c: Flash written and verified! jolly good! und - oh Wunder - es läuft! Denn anders als bei der out-of-stock-Demo (s.o.) geht es dann nämlich weiter: Number of supported LUN: 1 LUN #0: Inquiry Vendor : OTi Inquiry Product : Ultra Floppy Inquiry Version : 1.11 MSC Device ready MSC Device capacity : 32243200 Bytes Block number : 62975 Block Size : 512 mounting of 0:/ done try to write fnam_45.xxx file fnam_45.xxx opened fname:fnam_45.xxx f_close fname:fnam_45.xxx f_closed file fnam_45.xxx opened for read fname:fnam_45.xxx gelesen: Nun kommt es endlich in die Gänge..... fname:fnam_45.xxx closed fname:fnam_45.xxx SUCCESS. written: 124, blue LED ON FATFS_UnLinkDriver:0:/ APP idle ic:5000000 lc:46 AppliState:0 APP idle ic:10000000 lc:46 AppliState:0 Problem sind also die (eh unsäglichen) .mk-Files. Die werden vom Cubeide bei jedem "run" neu erstellt und beziehen sich auf die Sourcen, die er grade so in den Pfaden findet. Tauscht man Libs aus, dann enstehen auch neue .mk-Files. Die Libs tauscht man aber nicht nur von Hand aus, weil man das will. Nein, das macht Cubeide auch selbst. Ich werde das obige nochmal mit einem ganz neuen Workspace nachvollziehen. Die folgend Textpassage bezieht sich jetzt auf eine "from-scratch" generierte FatFS-USB-Version: STM32CubeIDE Version: 1.14.0 Build: 19471_20231121_1200 (UTC) ExampleToolKit:1063 - [info] SW Package located in: [....]/Repository/STM32Cube_FW_F7_V1.17.0 Das Problem der USB-Libs hatte ich ja schon geschildert. Wenn man die mit frisch vom Github geladenen Dateien austauscht, dann kann man es zum laufen bekommen. Er schreibt jetzt schon ein paar Tage lang 100te Testfiles auf einen uralt-Stick. Ich hab das ja extra so gebaut, dass man es mit "make" und "cube" benutzen kann, damit man auch "mal schnell" eine Config-Änderung einbauen kann. Dass man dabei mit den User-Tags im Quellcode höllisch aufpassen muss, damit der Cube-Generaor nicht die Quellen vernichtet (das kann der nämlich super) - das ist klar. Aber nun kommts: Auch nach einer marginalen Config-Änderung wollte der Code nicht mehr laufen. völlig unerkärlich. Bis ich dann festgestellt habe: der $%&§-cube-Generator baut nicht nur in /Src und /Inc rum - schreibt auch einfach die ganzen /Middelwares und /Drivers neu. Und mangelt damit alle händischen Änderungen einfach über. Mit den Versionen, die nicht funktionieren. einfach nur unglaublich! Aber es kommt noch besser: Setzt man "chmod -R -w Middlewares/ Drivers/", ändert im .ioc-file und lässt dann neuen Code generieren, dann läuft der. Das Drexding hat es nichtmal nötig, den Anwender über den gescheiterten überschreibe-Vversuch zu informieren. Also: Wenn man was mit cubeide macht, dann immer schön /Drivers&Co sichern... Bin gespannt, was noch alles so auftaucht..... to be continued..... So, nun das ganze Spiel nochmal: Alles wie oben schon mal beschrieben über den Example-Selector. Diesmal landet es in /STM32/_CENSORED_/F746_DEMO2/FatFs_USBDisk/Debug Dann aber erst einmal mit diesem test nachschauen, ob auch was aus dem USART3 rauskommt (4800 Baud bei dieser USAR3_init: USART3->BRR = SystemCoreClock / 19200; ) while(1) { USART3_SendString("\n\n--------FatFs_USBDisk start...--------\n\n\n\n"); } OK - das klappt.... Im 2. Schritt dieses while wieder weg und dafür dann das hier: printf("/STM32/_CENSORED_/F746_DEMO2/FatFs_USBDisk 2023.12.19 09:45\n"); printf("das wäre eigentlich die Arbeit von ST gewesen :-(\n"); HAL_Delay(2000); USB Device Connected ..... LUN #0: und Ende..... Und der nochmalige Vergleich Alt gegen Neu ud der .mk-Files zeigt jetzt auch, warum das Ding ein derart seltsame Verhalten an den Tag legt: In dem .mk-Files sind absolute Pfade verdrahtet. Also ein Projekt in einen anderen Pfad kopieren und neu per "make" compilieren, _kann_ funktionieren, wenn das alte Projekt noch auf den alten Pfaden zu finden ist. Wenn es dort verschwunden ist, dann geht erst mal gar nix mehr. Jedenfall: nach Austausch der Libs gegen die vom Github funktioniert FatFs_USBDisk - ohne das allerdings dem Anwender irgendwie mizuteilen. Dafür müssen dann schon mindestens ein paar printf ins die MSC_Application: static void MSC_Application(void) { printf("MSC_Application start\n"); FRESULT res; /* FatFs function common result code */ uint32_t byteswritten, bytesread; /* File write/read counts */ uint8_t wtext[] = "This is STM32 working with FatFs"; /* File write buffer */ uint8_t rtext[100]; /* File read buffer */ /* Register the file system object to the FatFs module */ if(f_mount(&USBDISKFatFs, (TCHAR const*)USBDISKPath, 0) != FR_OK) { printf("unable to mount\n"); /* FatFs Initialization Error */ Error_Handler(); } else { printf("mounted...\n"); /* Create and Open a new text file object with write access */ if(f_open(&MyFile, "STM32.TXT", FA_CREATE_ALWAYS | FA_WRITE) != FR_OK) { /* 'STM32.TXT' file Open for write Error */ Error_Handler(); } else { /* Write data to the text file */ res = f_write(&MyFile, wtext, sizeof(wtext), (void *)&byteswritten); if((byteswritten == 0) || (res != FR_OK)) { printf("ERROR on write..\n"); /* 'STM32.TXT' file Write or EOF Error */ Error_Handler(); } else { printf("written - close file...\n"); /* Close the open text file */ f_close(&MyFile); /* Open the text file object with read access */ if(f_open(&MyFile, "STM32.TXT", FA_READ) != FR_OK) { /* 'STM32.TXT' file Open for read Error */ Error_Handler(); } else { /* Read data from the text file */ res = f_read(&MyFile, rtext, sizeof(rtext), (void *)&bytesread); if((bytesread == 0) || (res != FR_OK)) { /* 'STM32.TXT' file Read or EOF Error */ Error_Handler(); } else { /* Close the open text file */ f_close(&MyFile); /* Compare read data with the expected data */ if((bytesread != byteswritten)) { /* Read data is different from the expected data */ Error_Handler(); } else { /* Success of the demo: no error occurrence */ BSP_LED_On(LED_BLUE); } } } } } } /* Unlink the USB disk I/O driver */ FATFS_UnLinkDriver(USBDISKPath); } Dann erhält man: MSC_Application start mounted... written - close file... Also alles schön.... Allerdings erwarte ich _eigentlich_ von einer aktuellen Version einer "Example-Software" direkt vom Hersteller der Hardware, dass diese Software auch auf Anhieb funktioniert - und sagt was sie tut. Ich hab keine Ahnung, was das für eine Geschäftspolitik ist, die ST da verfolgt. Ich hab ja nun so grob einige Jahrzehnte Programmiererfahrung, kenne den einen oder anderen Microkontroller und habe mich schon oft über schlechten Herstellersupport geärgert. Aber was sich ST hier geleistet hat, das ist einfach nur unglaublich. Und nun nochmal für Dummies Neues Projekt FatFs_USBDisk in neuem Workspace F746_DEMO5 from Example F747ZG. Die Abläufe kennen wir ja nun inzwischen. s.o. USART3 einbinden, Init, Startmeldung und die geänderte MSC_Application. Nebenbei - sloche Meldungen tragen natürlich auch mächtig zum Vetrauen in Cube/ST bei ./arm-none-eabi/lib/thumb/v7e-m+fp/hard/libc_nano.a(libc_a-readr.o): in function `_read_r': (.text._read_r+0x10): warning: _read is not implemented and will always fail einfach ignorieren den Dreck! Ändern usbh_conf.h (debug-level) nicht vergessen! Auch nett: nach Änderung in usbh_conf.h und complieren passiert erst mal nix neues. usbh_conf.h muss man nämlich vor dem Complieren speichern. Cube selbst rafft das nicht. https://github.com/STMicroelectronics/stm32_mw_usb_host.git Class und Core nach Middlewares/ST/STM32_USB_Host_Library kopieren, compilieren: MSC_Application start mounted... written - close file... 4800 Baud überigens - aber das kriegt ihr hin, gell? ;-) -------------------------------------------------------- letztens zufällig gefunden: (Einsteigerinfos zu Cubeide) http://stefanfrings.de/stm32/cube_ide.html to be continued..... --------------------------------------------------------- Stand 2023.12.19 08:00 Fragen, Fehlerhinweise usw. an wicki@erste.de