Hallo zusammen, mit der Funktion udp_recv() kann man eine Callback-Funktion festlegen, die automatisch aufgerufen wird wenn ein UDP-Paket empfangen wird. Das Problem ist, dass diese Callback-Funktion udp_echo_recv() nicht aufgerufen wird. Hier mein Code (abgespeckt): //Funktionen: void udp_echo_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) { HAL_GPIO_WritePin(LD6_GPIO_Port, LD6_Pin, GPIO_PIN_SET); //Blaue LED an } void udpApp1_init(void){ err_t udpErr; ip_addr_t ownIPaddr; udpPcb1_p = udp_new(); if(udpPcb1_p != NULL) { IP4_ADDR(&ownIPaddr, 192, 168, 1, 3); //STM32-IP udp_bind(udpPcb1_p, &ownIPaddr, 65100); } } int main(void){ udp_recv(udpPcb1_p, udp_echo_recv, NULL); udpApp1_init(); while(1){} } Ich bedanke mich schon mal für die Hilfe.
Ich hätte bei der Bind Addresse einfach 0.0.0.0 genommen, aber ansonsten: Fehler ist im nicht geposteten Code.
Matthis S. schrieb: > udp_recv Du rufst udp_recv(...) auf bevor du udp_new() gemacht hast. Damit ist udpPcb1_p nicht gültig gesetzt und udp_recv(...) wird damit nichts Vernünfiges anfangen können. Also
1 | udpPcb1_p = udp_new(); |
2 | IP4_ADDR(&ownIPaddr, 192, 168, 1, 3); //STM32-IP |
3 | udp_bind(udpPcb1_p, &ownIPaddr, 65100); |
4 | udp_recv(udpPcb1_p, udp_echo_recv, NULL); |
Das müsste dann funktionieren. Für die Portnummer NULL anzugeben ist vielleicht nicht die glücklichste Wahl.
STM Apprentice schrieb: > Für die Portnummer NULL anzugeben ist vielleicht nicht > die glücklichste Wahl. ooops .... das war wohl ein falscher Fehler von mir. STM Apprentice schrieb: > Das müsste dann funktionieren. Dann habe ich noch gelernt dass man als Minimum eine wie auch immer getriggerte Event-Loop braucht damit LwIP was tut. Also:
1 | while (1) |
2 | {
|
3 | /* check if any packet received */
|
4 | if (ETH_CheckFrameReceived()) |
5 | {
|
6 | LwIP_Pkt_Handle(); |
7 | }
|
8 | /* handle periodic timers for LwIP */
|
9 | LwIP_Periodic_Handle (LocalTime); |
10 | }
|
Ich wuerde LwIP_Pkt_Handle(); im while(1) loop lassen, nicht hinter ETH_CheckFrameReceived(). Bei pings und UDP oder mehrere Verbindungen geht das ganze durcheinander. Das war meine Erfahrung
STM Apprentice schrieb: > Matthis S. schrieb: Erstmal danke für die Antworten. >> udp_recv > > Du rufst udp_recv(...) auf bevor du udp_new() gemacht hast. > Damit ist udpPcb1_p nicht gültig gesetzt und udp_recv(...) > wird damit nichts Vernünfiges anfangen können. Sorry, ich habe es falsch eingefügt. Ich rufe also udp_recv() nach udpApp1_init() auf. Also wie du es geschrieben hast, aber funktioniert trotzdem nicht. >Dann habe ich noch gelernt dass man als Minimum eine >wie auch immer getriggerte Event-Loop braucht damit >LwIP was tut. >Also: >while (1) > { > /* check if any packet received */ > if (ETH_CheckFrameReceived()) > { > LwIP_Pkt_Handle(); > } > /* handle periodic timers for LwIP */ > LwIP_Periodic_Handle (LocalTime); > } Ohne den Inhalt dieser While-Schleife kann ich UDP-Pakete senden. Nur empfangen klappt nicht. Die drei Funktionen sind außerdem bei mir nirgendwo deklariert. Ich benutze die HAL Treiber, die von STM32CubeMX generiert sind. Wo sind bei dir diese Funktionen deklariert? Ich bin ziemlich neu in dem Gebiet. Entschuldigt meine Fragen :)
Welche andere Möglichkeiten gibt es denn UDP-Pakete zu empfangen? Ich habe von der Funktion ip_input gelesen, aber daraus werde ich nicht schlau.
Matthis S. schrieb: > Ohne den Inhalt dieser While-Schleife kann ich UDP-Pakete senden. Nur > empfangen klappt nicht. UDP Pakete senden geht ohne ein Pollen in der Schleife. In der Funktion udp_send werden die Daten direkt an den Ethernet MAC Treiber uebergeben. Fuer das Empfangen muss dem lwIP Stack der empfangen Ethernet-Frame uebergeben werden. Deshalb brauchst du da sehr wohl eine Poll-Funktion in der while-Schleife.
Matthis S. schrieb: > Welche andere Möglichkeiten gibt es denn UDP-Pakete zu empfangen? Bevor wir endgültig ins Rätselraten geraten solltest du vielleicht doch mal etwas vollständigeren Code zu deinem Problem posten. Das Prinzip von meinem geposteten Vorschlag funktioniert bei mir jedenfalls. Zudem solltest du dir angewöhnen: -------------------------------------------------- Wichtige Regeln - erst lesen, dann posten! Formatierung (mehr Informationen...) [c]C-Code[ /c] ^^^^^^^^^^^^^^^^^^^^ ............. --------------------------------------------------
Matthis S. schrieb: > Nur empfangen klappt nicht. Womit sendest du denn dass du weisst dass dein LwIP was empfangen soll? Und was sendest du? Adresse und Port ok? Firewall blockings? UDP senden kann man immer, da sich kein Schwein darum kümmert ob das Zeugs irgendwo ankommt (im Gegensatz zu TCP).
> Womit sendest du denn dass du weisst dass dein LwIP was > empfangen soll? > > Und was sendest du? Adresse und Port ok? > Firewall blockings? > > UDP senden kann man immer, da sich kein Schwein darum > kümmert ob das Zeugs irgendwo ankommt (im Gegensatz zu TCP). Firewall gibt es nicht. Zum empfangen der von meinem PHY gesendeten Pakete benutze ich das VN5610 Ethernet Interface von Vector. Welche Infos noch hättest du gerne? Mit dem Code unten sendet der PHY jede Sekunde ein Paket. vollständiger Code:
1 | #include "main.h" |
2 | #include "stm32f4xx_hal.h" |
3 | #include "lwip.h" |
4 | #include "gpio.h" |
5 | #include "udp.h" |
6 | #include <string.h> |
7 | |
8 | void SystemClock_Config(void); |
9 | void Error_Handler(void); |
10 | |
11 | extern struct netif gnetif; |
12 | static struct udp_pcb *udpPcb1_p; |
13 | |
14 | #define CLIENT1_MAC_0 0x9C
|
15 | #define CLIENT1_MAC_1 0x5C
|
16 | #define CLIENT1_MAC_2 0x66
|
17 | #define CLIENT1_MAC_3 0x10
|
18 | #define CLIENT1_MAC_4 0x46
|
19 | #define CLIENT1_MAC_5 0x93
|
20 | |
21 | static const char clientMagicPacket_c[] = |
22 | {
|
23 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
24 | CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5, |
25 | CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5, |
26 | CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5, |
27 | CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5, |
28 | CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5, |
29 | CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5, |
30 | CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5, |
31 | CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5, |
32 | CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5, |
33 | CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5, |
34 | CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5, |
35 | CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5, |
36 | CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5, |
37 | CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5, |
38 | CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5, |
39 | CLIENT1_MAC_0, CLIENT1_MAC_1, CLIENT1_MAC_2, CLIENT1_MAC_3, CLIENT1_MAC_4, CLIENT1_MAC_5, |
40 | };
|
41 | |
42 | |
43 | //Funktionen:
|
44 | |
45 | void udpApp1_sendMagicPacket(void){ |
46 | ip_addr_t client1IpAddr; |
47 | struct pbuf *ethTxBuffer_p; |
48 | |
49 | IP4_ADDR(&client1IpAddr, 192, 168, 1, 255); |
50 | |
51 | ethTxBuffer_p = pbuf_alloc(PBUF_TRANSPORT, sizeof(clientMagicPacket_c), PBUF_RAM); |
52 | if (ethTxBuffer_p == NULL){} |
53 | |
54 | memcpy(ethTxBuffer_p->payload, clientMagicPacket_c, sizeof(clientMagicPacket_c)); |
55 | |
56 | udp_sendto(udpPcb1_p, ethTxBuffer_p, &client1IpAddr,9); |
57 | |
58 | pbuf_free(ethTxBuffer_p); |
59 | }
|
60 | |
61 | void udp_echo_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) |
62 | {
|
63 | HAL_GPIO_WritePin(LD6_GPIO_Port, LD6_Pin, GPIO_PIN_SET); //Blaue LED an |
64 | }
|
65 | |
66 | void udpApp1_init(void){ |
67 | err_t udpErr; |
68 | ip_addr_t ownIPaddr; |
69 | |
70 | udpPcb1_p = udp_new(); |
71 | |
72 | if(udpPcb1_p != NULL) |
73 | {
|
74 | IP4_ADDR(&ownIPaddr, 192, 168, 1, 3); //STM32-IP |
75 | |
76 | udpErr = udp_bind(udpPcb1_p, &ownIPaddr, 65100); |
77 | |
78 | udp_recv(udpPcb1_p, udp_echo_recv, NULL); |
79 | |
80 | if (udpErr ==ERR_OK){} |
81 | }
|
82 | }
|
83 | |
84 | int main(void) |
85 | {
|
86 | |
87 | /* MCU Configuration----------------------------------------------------------*/
|
88 | |
89 | /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
|
90 | HAL_Init(); |
91 | |
92 | /* Configure the system clock */
|
93 | SystemClock_Config(); |
94 | |
95 | /* Initialize all configured peripherals */
|
96 | MX_GPIO_Init(); |
97 | MX_LWIP_Init(); |
98 | |
99 | /* USER CODE BEGIN 2 */
|
100 | |
101 | udpApp1_init(); |
102 | |
103 | /* USER CODE END 2 */
|
104 | |
105 | /* Infinite loop */
|
106 | /* USER CODE BEGIN WHILE */
|
107 | while (1) |
108 | {
|
109 | |
110 | /* USER CODE END WHILE */
|
111 | |
112 | /* USER CODE BEGIN 3 */
|
113 | |
114 | udpApp1_sendMagicPacket(); |
115 | HAL_Delay(1000); |
116 | |
117 | |
118 | ethernetif_input(&gnetif); |
119 | sys_check_timeouts(); |
120 | }
|
> Das Prinzip von meinem geposteten Vorschlag funktioniert > bei mir jedenfalls. Kannst du bitte deinen vollständigen Code posten, damit ich direkt vergleichen kann. >UDP Pakete senden geht ohne ein Pollen in der Schleife. In der Funktion >udp_send werden die Daten direkt an den Ethernet MAC Treiber uebergeben. >Fuer das Empfangen muss dem lwIP Stack der empfangen Ethernet-Frame >uebergeben werden. Deshalb brauchst du da sehr wohl eine Poll-Funktion >in der while-Schleife. Ah ok. Daran wirds wohl liegen. Aber die drei funktionen sind in meiner Lib nicht drin. ETH_CheckFrameReceived() LwIP_Pkt_Handle(); LwIP_Periodic_Handle (LocalTime); Kannst du mir bitte sagen wo die bei dir zu finden sind? >Wichtige Regeln - erst lesen, dann posten! Was habe ich überlesen?
Matthis S. schrieb: > Mit dem Code unten sendet der PHY jede Sekunde ein Paket. > vollständiger Code: Matthis S. schrieb: > Welche Infos noch hättest du gerne? Die Problemstellung zum geposteten Code. Ausserdem die Antwort auf meine Frage: STM Apprentice schrieb: > Womit sendest du denn dass du weisst dass dein LwIP was > empfangen soll? Welche Hardware benutzt du? Eval Board oder selbstgestrickte Ethernet-Anbindung?
Matthis S. schrieb: > Was habe ich überlesen? Beim Posten des ersten Codes hast du was überlesen. Beim Posten des letzten Codes hast du es dann kapiert. Vergleiche selbst.
Matthis S. schrieb: > Aber die drei funktionen sind in meiner Lib nicht drin. > > ETH_CheckFrameReceived() Müsste in <stm32f4x7_eth.c> vorhanden sein. > LwIP_Pkt_Handle(); findet sich bei mir in <netconf.c> Macht nichts anderes als ethernetif_input(&gnetif); > LwIP_Periodic_Handle (LocalTime); findet sich bei mir in <netconf.c> und bedient DHCP Timer, TCP Timer und ARP Timer. Ich rede vom LwIP 1.4.1, kann sein dass du eine Version 2.xx verwendest.
STM Apprentice schrieb: > Die Problemstellung zum geposteten Code. Die Problemstellung steht im ersten Post: >mit der Funktion udp_recv() kann man eine Callback-Funktion festlegen, >die automatisch aufgerufen wird wenn ein UDP-Paket empfangen wird. >Das Problem ist, dass diese Callback-Funktion udp_echo_recv() nicht >aufgerufen wird. > Ausserdem die Antwort auf meine Frage: > STM Apprentice schrieb: >> Womit sendest du denn dass du weisst dass dein LwIP was >> empfangen soll? Steht im vollständigen Code. Mit udp_sendto(). Oder meinst du was anderes? > Welche Hardware benutzt du? Eval Board oder selbstgestrickte > Ethernet-Anbindung? STM32F407-Discoveryboard verbunden über RMII mit dem PHY DP83848. Das PHY ist mit dem VN5610 Interface über Ethernet verbunden. VN5610 kennt ihr vielleicht nicht wenn ihr nicht in der Automotiv-Branche beschäftigt seid. Das ist aber nicht so wichtig. Tatsache ist, die VN5610 empfängt die vom PHY kommende Pakete. Nur wenn ich von VN5610 was an mein PHY sende, wird die Funktion udp_echo_recv() nicht aufgerufen.
Ich bin nicht grad der Experte wenn es um LwIP geht. Ich beschäftige mich erst seit ein paar Wochen damit.
> Ich rede vom LwIP 1.4.1, kann sein dass du eine Version 2.xx > verwendest. Ich weiss gar nicht wo ich die Version finde. Muss erstmal schauen...
STM Apprentice schrieb: > Matthis S. schrieb: >> Aber die drei funktionen sind in meiner Lib nicht drin. >> >> ETH_CheckFrameReceived() > > Müsste in <stm32f4x7_eth.c> vorhanden sein. Bei mir sind u.a. nur die Funktionen: HAL_ETH_GetReceivedFrame(); HAL_ETH_GetReceivedFrame_IT(); >> LwIP_Pkt_Handle(); > > findet sich bei mir in <netconf.c> > Macht nichts anderes als ethernetif_input(&gnetif); ethernetif_input(&gnetif) ist bei mir in <ethernetif.c> vorhanden. Also kann ich die stattdessen benutzen? >> LwIP_Periodic_Handle (LocalTime); > > findet sich bei mir in <netconf.c> und bedient DHCP Timer, > TCP Timer und ARP Timer. Bei mir ist weder LwIP_Periodic_Handle (LocalTime) noch <netconf.c> vorhanden. > Ich rede vom LwIP 1.4.1, kann sein dass du eine Version 2.xx > verwendest. Die Version finde ich leider nicht.
Matthis S. schrieb: > ethernetif_input(&gnetif); In dieser Funktion muesste eine Funktion - z.B. low_level_input, falls der lwIP Ethernet Skeleton verwendet wird - aufgerufen werden, die aus dem Deskriptor des Ethernet MACs den Frame in einen lwIP pbuf kopiert. Dann muesste weiter in der o.g. Funktion ethernet_input mit dem pbuf aufgerufen werden. In der Funktion wird dann ueber ip_input etc im Idealfall die receive callback Funktion aufgerufen. Wie sieht die Funktion bei dir aus? Empfaengt dein Ethernet MAC den Frame? Gibt es da evtl. einen Receive Interrupt?
Jetzt wirds schwierig für mich. whip schrieb: > Matthis S. schrieb: >> ethernetif_input(&gnetif); > > In dieser Funktion muesste eine Funktion - z.B. low_level_input, falls > der lwIP Ethernet Skeleton verwendet wird - aufgerufen werden, die aus > dem Deskriptor des Ethernet MACs den Frame in einen lwIP pbuf kopiert. Bisher kapier ich das. low_level_input() ist auch vorhanden. Kannst du bitte das etwas genauer erklären: > Dann muesste weiter in der o.g. Funktion ethernet_input mit dem pbuf > aufgerufen werden. In der Funktion wird dann ueber ip_input etc im > Idealfall die receive callback Funktion aufgerufen. > > Wie sieht die Funktion bei dir aus? Empfaengt dein Ethernet MAC den > Frame? Gibt es da evtl. einen Receive Interrupt?
Matthis S. schrieb: > Kannst du bitte das etwas genauer erklären Der Funktionkopf von low_level_input:
1 | static struct pbuf * |
2 | low_level_input(struct netif *netif) |
Es wird ein Pointer auf einen pbuf zurueckgegeben. struct pbuf ist der Datentyp, in dem lwIP die Frames speichert. Jetzt hast du ja einen Treiber fuer den Ethernet MAC. Der sollte eine Funktion readFrame haben. Diese muss in low_level_input aufgerufen werden, damit der empfangene Frame von dem Speicherbereich des Ethernet MACs, typischerweise wird da mit so genannten Deskriptoren gearbeitet, in den Speicherbereich des lwIP Stacks kopiert werden kann. Der Rueckgabewert von low_level_input ist der empfangen Frame, der dann vom lwIP Stack weiterverarbeitet werden kann. Da wo low_level_input aufgerufen wird, wird zur Weiterverarbeitung durch lwIP typischerweise eine input Funktion aufgerufen, in der dann der Frame ausgewertet wird und die Callback Funktion aufgerufen wird. So ungefaehr:
1 | static struct pbuf *p = low_level_input(struct netif *netif); |
2 | |
3 | if (p != NULL) |
4 | {
|
5 | netif->input(p, netif); |
6 | }
|
Idealerweise setzt du mal einen Breakpoint auf den input-Aufruf, dann kannst du sicher sein, dass Hardware seitig der Frame empfangen wird und korrekt an lwIP weitergeleitet wird. Den Frameinhalt kann man mit sich mit p->payload anschauen und z.B. mit Wireshark vergleichen.
Beitrag #5107797 wurde von einem Moderator gelöscht.
Es hat funktioniert. Als ich in Wireshark das Paket gesendet habe, habe ich vergessen die MAC-Adresse auf Broadcast einzustellen. Dämlicher Fehler :) Ich bedanke mich sehr für eure tolle Hilfe. Edit: Das Destination Port war auch falsch
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.