CubeIDE Variablen in externen Modul unbekannt

OP #7453939
Lesenswert?

Hi Leute, ich steh auf dem Schlauch: Ich schreibe einen Treiber für einen VFD-Controller, habe dafür eine externe VFD.h und VFD.c geschrieben. Die Main.c tut noch gar nichts zur Sache, da ist kein User Code drin. Die von der CubeMX erzeugte main.h deklariert darin die ganzen Bezeichner für GPIO Pins.

Der SPI-Handler aus der main.c ist in VFD.c unbekannt. Das konnte ich umschiffen, indem ich ihn als extern SPI_HandleTypeDef hspi1; neu deklariert habe. Ich kriege da sonst eine Fehlermeldung 'hspi1' undeclared (first use in this function)

Was mache ich da falsch? Im Code von anderen sehe ich das nicht. Habe noch nicht viel Erfahrung mit Modularisierung.

Und dann nochwas: Der alte Chip LC5710NE erwartet eine 8-Bit Adresse mit CE=L, dann drei Datenworte mit CE=H. Ich habe mir da mit einem Callback und Pufferpointer beholfen. Geht das auch besser? Dies mal unter der Voraussetzung, dass ich das mit der SPI-Hardware und nicht per Bit-Banging erledige.

Danke!

Angehängte Dateien:
#7453956
Lesenswert?

Gunnar F. schrieb:

Der SPI-Handler aus der main.c ist in VFD.c unbekannt. Das konnte ich umschiffen, indem ich ihn als extern SPI_HandleTypeDef hspi1; neu deklariert habe.

Dafür gibt es die Callback-Funktionen. Neu definieren ist eine ganz, ganz schlechte Idee!

Gunnar F. schrieb:

Ich kriege da sonst eine Fehlermeldung 'hspi1' undeclared (first use in this function)

#include "spi.h"

Doku lesen und verstehen! Hier als Beispiel für F3xx (gibts entsprechend für alle anderen Familien genauso) https://www.st.com/resource/en/user_manual/um1786-description-of-stm32f3-hal-and-lowlayer-drivers-stmicroelectronics.pdf

OP #7454049
Lesenswert?

Harry L. schrieb:

#include "spi.h" Doku lesen und verstehen!

ist ja sehr nett von dir, aber ich verstehe es leider nicht. hspi1 ist eine Variable aus der main.c. Warum soll die über spi.h deklariert werden?

Harry L. schrieb:

Dafür gibt es die Callback-Funktionen. Neu definieren ist eine ganz, ganz schlechte Idee!

die verwende ich ja! Die ist __weak deklariert und ST erklärt in deren Trainings und Webinaren, dass man die im eigenen Code neu definieren soll. So kenne ich das und es hat auch so funktioniert.

Aber das war auch nicht meine Frage, sondern wenn ich vier Bytes per SPI übertrage und nur beim ersten davon ein CE Pin High sein soll, ob ich dann per Zähler und erneutem Senden, nach dem ersten Byte den Pegel ändere.

#7454098
Lesenswert?

Rahul D. schrieb:

Gunnar F. schrieb:

extern SPI_HandleTypeDef hspi1; neu deklariert habe.

Was sagt der Linker dazu? Wenn der nicht meckert, ist doch alles gut (natürlich sollte man die Variable auch irgendwann mal verwenden, weil sie sonst wegoptimiert wird.)

Wird sie nicht! Darin stecken alle Register der Schnittstelle, und die werden bereits bei der von CubeMX generierten Initialisierung referenziert.

Ansonsten hilft ein Blick in die spi.h-Datei, was da alles veröffentlich wurde.

SPI_HandleTypeDef steckt in stm32f3xx_hal_spi.h und nicht in spi.h spi.h gibt es sowieso nur, wenn man die entsprechnde Option in CubeMX gesetzt hat. Ansonsten steckt das in der main.h (default)

1
harry@harry-DT:~/LaborLocal/STM32Cube/STM32Cube_FW_F3_V1.11.0/Drivers/STM32F3xx_HAL_Driver/Inc$ ls *spi.h
2
stm32f3xx_hal_spi.h  stm32f3xx_ll_spi.h
Gast #7454713
Lesenswert?

Gunnar F. schrieb:

Der SPI-Handler aus der main.c ist in VFD.c unbekannt. Das konnte ich umschiffen, indem ich ihn als extern SPI_HandleTypeDef hspi1; neu deklariert habe.

Das ist doch das geplante Verhalten: In einer (main.c)-Datei wird die Variable deklariert, in einer weiteren (vfd.c)-Datei wird die Variable extern deklariert, so dass der Linker alles zusammenbasteln kann.

Das war so in the beginning (K&R, 1.Edition).

Oder habe ich das Problem nicht richtig verstanden?

Gruesse

Th.

OP #7454722
Lesenswert?

Thomas W. schrieb:

as ist doch das geplante Verhalten: In einer (main.c)-Datei wird die Variable deklariert, in einer weiteren (vfd.c)-Datei wird die Variable extern deklariert, so dass der Linker alles zusammenbasteln kann.

Danke Thomas! Ja, ich habe mich inzwischen mit einem erfahrenen Programmierer unterhalten, der hat mir dasselbe bestätigt. Das ist normal, die Variable dann im externen Modul als extern nochmal neu zu deklarieren. Ich hatte das nur noch nie in fremden Code so aktiv zur Kenntnis genommen. Als ich dann (dank zum ersten mal der Versuch einer ordentlichen Modularisierung!) in die Situation kam, erschien es mir seltsam.

#7454729
Lesenswert?

Gunnar F. schrieb:

Das ist normal, die Variable dann im externen Modul als extern nochmal neu zu deklarieren.

CubeMX klatscht das als Voreinstellung alles in main.c, entsprechend müsste man die externals in main.h reinschreiben. Schöner ist es die neuere Option in den Projekteinstellungen zu verwenden die für die Peripherie eigene .c / .h Pärchen anlegt. Dann bekommt man die spi.h mit den externals geschenkt.

Gast #7456526
Lesenswert?

Thomas F. schrieb:

Wo wird die global.h includiert?

In allen Quelltexten, die auf globale Variablen zugreifen. Im Zweifelsfall in wirklich allen (kann nicht schaden).

Thomas F. schrieb:

Und was steht dann in den Header-Dateien der "Unter"-Module?

Da werden die Kopfzeilen der Funktionen aufgelistet, die woanders benutzbar sein sollen.

Eine Header Datei geschreibt, was die zugehörige C Datei nach außen bereit stellt. Außerden definiert man in Header Dateien die zugehörigen Konstanten (#define) und Strukturen (typedef).

Gast #7456681
Lesenswert?

Man muss natuerlich aufpassen, dass man die richtigen Datentypen benutzt (bei Dir SPI_HandleTypeDef).

Nehmen wir zwei Prograemmchen:

1
#include "stdio.h"
2
void call_zwei();
3

4
float var = 0;
5

6
int main(int argc, char* argv[])
7
{
8
  var=42.0;
9
  printf("var = %f\r\n", var);
10
  call_zwei();
11
  printf("var = %d\r\n", var);
12
}

und das zweite Modul:

1
#include "stdio.h"
2
extern int var;
3
void call_zwei()
4
{
5
  var = 42;
6
}

D.h. ich habe aus Versehen die Variable var (den Speicherbereich) einmal als Float und einmal als int benutzt. Jetzt compile, link (wir ignorieren die sehr eindeutige Warnung) und execute:

1
tom@dbstw:/tmp$ gcc -Wall -c -o eins.o eins.c
2
eins.c: In function 'main':
3
eins.c:13:18: warning: format '%d' expects argument of type 'int', but argument 2 has type 'double' [-Wformat=]
4
   11 |   printf("var = %d\r\n", var);
5
      |                 ~^       ~~~
6
      |                  |       |
7
      |                  int     double
8
      |                 %f
9
tom@dbstw:/tmp$ gcc -Wall -c -o zwei.o zwei.c
10
tom@dbstw:/tmp$ gcc -o drei eins.o zwei.o
11
tom@dbstw:/tmp$ ./drei
12
var = 42.000000
13
var = 964698784

Bei diesem Drei-Zeiler ist das alles offensichtlich, ist das Modul etwas groesser ist so ein Type-Mismatch manchmal schwierig zu finden.

Du siehst, external ist ein sehr leistungsfaehiges Instrument um sich in das Knie zu schiessen.

Gruesse

Th.

OP #7456759
Lesenswert?

Thomas W. schrieb:

Du siehst, external ist ein sehr leistungsfaehiges Instrument um sich in das Knie zu schiessen.

Na das macht mich ja jetzt noch glücklicher! ;-) Jetzt habe ich mich am WE gefreut, mein Projekt in separate .c/.h Dateien aufgeteilt, wie von Stefan geraten. Ja, das wird schon übersichtlicher. Dann mein hspi1 nochmal als extern im der VFD.c deklariert und alles passte. Bin auch schon weiter gekommen, das könnte demnächst funktionieren. Aber ich verstehe schon, dass Du hier auf Feinheiten hinweist, die sich mir als (noch immer) Einsteiger, noch gar nicht so erschließen. Aber so wie ich bisher verstand, ist C sowieso die Sprache, mit der man sich effektvoll ins Knie schießen kann, nein?

Da der F103 (Bluepill) nur ein SPI hat, baue ich jetzt gerade das zweite Interface zu Fuß als State Machine und BitBanging auf, und bin dann gespannt, ob etwas funktioniert. Aber auf jeden Fall danke fürs Augen Öffnen!

Happy knee shooting!

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren