:!DOCTYPE html> Erstellung 2023.10.06 - Update 2023.12.19 08:00 Uhr

Vorwort


Worum gehts hier?

Es ist eine Zusammenfassung von Schritten zum Aufbau einer
STM32-Testumgebung auf Basis von STM32 F746 und H743.
Mit vielen Fragen und einigen Antworten.

Es ist für Mitarbeiter und Freunde und/oder einfach interessierte
Mitleser gedacht.

Insbesondere für die, die bei der Nutzung von CubeIDE und CubeMX
und anderen Klickibunti-IDEs regelmässig die Krise bekommen.
Es wird beschrieben, wie man auch von der Kommandozeile (natürlich
rede ich hier von Linux) Programme für die beiden µCs schreiben
und testen kann.

Das Ganze hat keinen kommerziellen Hintergrund. Und ich bin auch
keine C-Programmiererin ;-)
Eigentlich hasse ich C wie die Pest.....

Aber man will nun auch nicht alles in ASM schreiben.

Also werde ich sicherlich hier Fehler machen. Aber das ist egal.
Sie werden dann halt (irgendwann) behoben.


Wo es hinführen soll?

Ich hätte gern eine Art "mini-Logic-Analyzer" auf Basis der H7-
oder F7-Serie.
Laut Datenblatt sollte da etwas ganz ordentenliches bei heraus
kommen können.
Je weiter man jedoch kommt, um so mehr Probleme treten auf - das kennt
man ja....
Angefangen hat es damit:
Mit der verrückten Idee "Wenn ich doch die Daten eh schon gelesen habe
und ausgeben will - warum nicht gleich 'semigraphisch' als ANSI-Code?

Der erste Versuch....

Das ist eine zyklische Portabfrage mit 16 Bit Breite innerhalb einer
Zeit von ein paar µSec.
Darauf wird dann auf der µC seite direkt eine ANSI-Sequenz gebaut,
die jedes Terminal anzeigen kann.
Da dies jedoch auf die Zeichenbreite des Terminals beschränkt ist - und
ausserdem ANSI-Darstellungen recht gemächlich voran gehen - musste natürlich
irgendwas graphisches her.

Also ein kleine bisschen JAVA und dann wurde das daraus:



und dann das



Die aktuelle Fassung geht noch etwas weiter und zeigt auch die
Signallaufzeiten mit an.
Das soll nun noch ein bisschen schöner und leistungsfähiger werden
Und dazu braucht man dann zuverlässige Timings.
Und bei den Timings fingen dann die neuen Probleme an.

Also für mich neu - andere kannten sie wohl schon länger
https://metebalci.com/blog/stm32h7-gpio-toggling/

"Das sind doch nur Output-Probleme" - ja, sicher.
Aber gibts die nicht auch beim Input?
Das wollte ich mir ansehen und dann hab ich mir gedacht:
Warum das nicht alles mal aufschreiben, damit andere das auch
mal ausprobieren können...

Ausserdem würde ich ja gerne mit meiner Idee
"mappe SHMEM von PC "A" auf GPIO von µC "B"
weitermachen. Und da gibts diese Probleme dann ja auch
- aber das ist wieder ein ganz andere Thema ;-)

Dann fangen wir mal an.....
 
Anlegen 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