CubeIDE Variablen in externen Modul unbekannt

OP #7453939

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:

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

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.

Gunnar F. schrieb:

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.

Nö. Dann musst du /CE eben manuell setzen. Auch das kann man im CubeMX einstellen.

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.)

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

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
OP

Rahul D. schrieb:

Was sagt der Linker dazu? Wenn der nicht meckert, ist doch alles gut

Der Linker kommt gar nicht zum Zuge, weil schon der Compiler mit Fehler abbricht.

Nochmal, es geht nicht um einen Datentyp, sondern: Gunnar F. schrieb im Beitrag #7454049:

hspi1 ist eine Variable aus der main.c. Warum soll die über spi.h deklariert werden?

Gast

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

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.

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

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

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

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

Eine eigene Frage? Dieser Thread ist über 3 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.