Hallo Forum, Ich will einen Pi Pico als Kommunikationsmodul verwenden. Über CDC sollen Kommandos vom PC zum Pico geschickt werden und später auch entsprechende Antworten zurückkommen. An den beiden anderen seriellen Schnittstellen hängen zwei weitere Geräte (kabelgebundene Fernsteuerung, Motorregler). Das ganze habe ich momentan nur soweit aufgebaut, dass am PC alle Schittstellen ankommen (1x CDC, 2xRS232). Dem Pico habe ich ein entsprechendes Programm verpasst, dass wenn auf einer Schnittstelle Daten eintreffen, diese an die nächste gesendet werden. Ich kann also am PC von einer auf die andere Schnittstelle senden. Das klappt soweit wenn ich 3x putty aufmache. Das ist aber nicht das reale Leben. Also habe ich ein Lazarus-Programm geschrieben mit dem ich alle drei Schnittstellen mit Zufallstexten und Timergesteuert in zufälligen Intervallen zw. 20 und 90 ms versorge. Allen drei Schnittstellen habe ich beigebracht ein Ende-Zeichen ~ zu senden, damit ich die Daten besser auswerten kann. Am PC lausche ich auf das was an den drei Schnittstellen eintrifft und vergleiche es mit dem was ich gesendet habe. Dabei liefert mir der Eingang der CDC (USB) - Schnittstelle immer Fehler -> es werden immer wieder einige Zeichen verschluckt. Das tritt nur an dieser Schnittstelle auf. Das Problem scheint mir im Pico zu liegen, aber ich verstehe nicht wieso die Daten nicht vollständig vom Pico Empfangen bzw. Gesendet werden. Wenn ich die Sende-Timer der anderen beiden Schnittstellen deaktiviere tritt der Fehler nicht auf. Meine Vermutung liegt darin, dass während ich den printf der CDC-Schnittstelle ausführe ein Interrupt auf einer der seriellen Schnittstellen die Übertragung stört. Für mich ist eine saubere Kommunikation sehr wichtig. Bitte um Hilfe anbei der Code: Controlbox.c:
1 | #include <stdio.h> |
2 | #include <string.h> |
3 | #include "pico/stdlib.h" |
4 | #include "hardware/uart.h" |
5 | #include "hardware/irq.h" |
6 | #include "hardware/gpio.h" |
7 | |
8 | // UART defines
|
9 | // UART0
|
10 | #define UART_ID0 uart0
|
11 | #define BAUD_RATE0 115200 // 230400
|
12 | // UART1
|
13 | #define UART_ID1 uart1
|
14 | #define BAUD_RATE1 115200 // 230400
|
15 | |
16 | // Pin-Connections for
|
17 | // UART0
|
18 | #define UART_TX_PIN0 0
|
19 | #define UART_RX_PIN0 1
|
20 | // UART1
|
21 | #define UART_TX_PIN1 8
|
22 | #define UART_RX_PIN1 9
|
23 | |
24 | // Datenstruktur für Befehle von HB -> ControlBox
|
25 | struct __attribute__ ((__packed__)) HB_Cmd { |
26 | char Cmd[1]; |
27 | char Buf[8]; |
28 | };
|
29 | union HB_InUnion { |
30 | char In[10]; |
31 | struct HB_Cmd Cmb; |
32 | } HB_In; |
33 | uint8_t HB_InCnt = 0; |
34 | bool HB_InRd = false; |
35 | bool HB_InDo = false; |
36 | |
37 | // RX interrupt handler für UART0
|
38 | void on_uart_rx0() { |
39 | // Solange Zeichen im Puffer sind -> Zeichen einlesen
|
40 | while (uart_is_readable(UART_ID0)) { |
41 | uint8_t ch = uart_getc(UART_ID0); |
42 | // Startzeichen / initiert den Befehlsempfang
|
43 | if (ch == '/') { |
44 | HB_InCnt = 0; // Zeichen-Zähler ist 0 |
45 | strcpy(HB_In.In," "); // Input-String ist LEER |
46 | HB_InRd = false; // Lesevorgang ist nicht fertig. |
47 | HB_InDo = true; // Daten einfügen |
48 | }
|
49 | // Endezeichen ~ beendet den Befehlsempfang
|
50 | else if (ch == '~') { |
51 | HB_InRd = true; // Lesevorgang ist fertig |
52 | HB_InDo = false; // Daten einfügen beendet |
53 | }
|
54 | else if (HB_InDo){ |
55 | HB_In.In[HB_InCnt++] = ch; |
56 | }
|
57 | }
|
58 | // Verarbeiten des Kommandos wenn vollständig
|
59 | if (HB_InRd) { |
60 | HB_InRd = false; // Flag für die Datenverarbeitung zurücksetzen. |
61 | HB_In.In[HB_InCnt] = 0; // Nullterminierung einfügen |
62 | char Out[13]; |
63 | // Daten für Testausgabe vorbereiten und ...
|
64 | strcpy(Out, HB_In.In); |
65 | strcat(Out, "~"); |
66 | // ... auf anderem Port ausgeben
|
67 | while (!uart_is_writable(UART_ID1)) {} |
68 | uart_puts(UART_ID1, Out); |
69 | }
|
70 | }
|
71 | |
72 | // Datenstruktur für Feedbacks vom Motorregler -> ControlBox
|
73 | struct __attribute__ ((__packed__)) MR_Cmd { |
74 | char Cmd[1]; |
75 | char Buf[8]; |
76 | };
|
77 | union MR_InUnion { |
78 | char In[13]; |
79 | struct HB_Cmd Cmb; |
80 | } MR_In; |
81 | uint8_t MR_InCnt = 0; |
82 | bool MR_InRd = false; |
83 | |
84 | // RX interrupt handler für UART1
|
85 | void on_uart_rx1() { |
86 | // Solange Zeichen im Puffer sind -> Zeichen einlesen
|
87 | while (uart_is_readable(UART_ID1)) { |
88 | uint8_t ch = uart_getc(UART_ID1); |
89 | // Startzeichen gibt es hier nicht !!!!!
|
90 | if (ch == ';') { |
91 | MR_InRd = true; // Lesevorgang ist fertig |
92 | }
|
93 | else { |
94 | MR_In.In[MR_InCnt++] = ch; |
95 | if (MR_InCnt > 13) { |
96 | strcpy(MR_In.In," "); |
97 | MR_InRd = false; |
98 | MR_InCnt = 0; |
99 | }
|
100 | }
|
101 | }
|
102 | // Verarbeiten des Kommandos wenn vollständig
|
103 | if (MR_InRd) { |
104 | MR_InRd = false; // Flag für die Datenverarbeitung zurücksetzen. |
105 | MR_In.In[MR_InCnt] = 0; // Nullterminierung einfügen |
106 | char Out[16]; |
107 | // Daten für Testausgabe vorbereiten und ...
|
108 | strcpy(Out, MR_In.In); |
109 | strcat(Out, "~"); |
110 | // ... auf anderem Port ausgeben
|
111 | printf(Out); |
112 | //puts(Out);
|
113 | strcpy(MR_In.In," "); |
114 | MR_InCnt = 0; |
115 | }
|
116 | }
|
117 | |
118 | // GPIO defines
|
119 | // Example uses GPIO 2
|
120 | #define GPIO 2
|
121 | |
122 | |
123 | // Datenstruktur für Befehle von PC -> ControlBox
|
124 | struct __attribute__ ((__packed__)) PC_Cmd { |
125 | char Cmd[1]; |
126 | char CRC[2]; |
127 | char Buf[16]; |
128 | };
|
129 | union PC_InUnion { |
130 | char In[20]; |
131 | struct PC_Cmd Cmb; |
132 | } PC_In; |
133 | uint8_t PC_InCnt = 0; |
134 | bool PC_InRd = false; |
135 | bool PC_InDo = false; |
136 | |
137 | |
138 | // RX interrupt handler für VCOM
|
139 | void on_uart_rx2() { |
140 | // Liest schnellstmöglich ein Zeichen ein.
|
141 | int i = getchar_timeout_us (0); |
142 | // Solange die Lese-Aktion keinen Timeout hat -> Zeichen verarbeiten
|
143 | while (i != PICO_ERROR_TIMEOUT) { |
144 | uint8_t ch = i; |
145 | // Startzeichen / initiert den Befehlsempfang
|
146 | if (ch == '/') { |
147 | PC_InCnt = 0; // Zeichen-Zähler ist 0 |
148 | strcpy(PC_In.In," "); // Input-String ist LEER |
149 | PC_InRd = false; // Lesevorgang ist nicht fertig. |
150 | PC_InDo = true; // Daten einfügen |
151 | }
|
152 | // Endezeichen ~ beendet den Befehlsempfang
|
153 | else if (ch == '~') { |
154 | PC_InRd = true; // Lesevorgang ist fertig |
155 | PC_InDo = false; // Daten einfügen beendet |
156 | }
|
157 | else if (PC_InDo){ |
158 | PC_In.In[PC_InCnt++] = ch; |
159 | }
|
160 | i = getchar_timeout_us (0); // Nächstes Zeichen lesen |
161 | }
|
162 | // Verarbeiten des Kommandos wenn vollständig
|
163 | if (PC_InRd) { |
164 | PC_InRd = false; // Flag für die Datenverarbeitung zurücksetzen. |
165 | PC_In.In[PC_InCnt] = 0; // Nullterminierung einfügen |
166 | char Out[22]; |
167 | // Daten für Testausgabe vorbereiten und ...
|
168 | strcpy(Out, PC_In.In); |
169 | strcat(Out, "~"); |
170 | // ... auf anderem Port ausgeben
|
171 | while (!uart_is_writable(UART_ID0)) {} |
172 | uart_puts(UART_ID0, Out); |
173 | }
|
174 | }
|
175 | |
176 | const uint LED_PIN = PICO_DEFAULT_LED_PIN; |
177 | bool led_state = 0; |
178 | |
179 | // Callback-Routine für den Repeating-timer
|
180 | bool repeating_timer_callback(struct repeating_timer *t) |
181 | {
|
182 | gpio_put(LED_PIN, led_state); |
183 | led_state = !led_state; |
184 | return true; |
185 | }
|
186 | |
187 | int main() |
188 | {
|
189 | gpio_init(LED_PIN); |
190 | gpio_set_dir(LED_PIN, GPIO_OUT); |
191 | |
192 | stdio_init_all(); |
193 | |
194 | // Set up our UART
|
195 | uart_init(UART_ID0, BAUD_RATE0); |
196 | uart_init(UART_ID1, BAUD_RATE1); |
197 | // Set the TX and RX pins by using the function select on the GPIO
|
198 | // Set datasheet for more information on function select
|
199 | gpio_set_function(UART_TX_PIN0, GPIO_FUNC_UART); |
200 | gpio_set_function(UART_RX_PIN0, GPIO_FUNC_UART); |
201 | gpio_set_function(UART_TX_PIN1, GPIO_FUNC_UART); |
202 | gpio_set_function(UART_RX_PIN1, GPIO_FUNC_UART); |
203 | |
204 | // Turn off FIFO's - we want to do this character by character
|
205 | uart_set_fifo_enabled(UART_ID0, false); |
206 | uart_set_fifo_enabled(UART_ID1, false); |
207 | |
208 | // Set up a RX interrupt
|
209 | // We need to set up the handler first
|
210 | // Select correct interrupt for the UART we are using
|
211 | int UART_IRQ0 = UART0_IRQ; |
212 | int UART_IRQ1 = UART1_IRQ; |
213 | |
214 | // And set up and enable the interrupt handlers
|
215 | irq_set_exclusive_handler(UART_IRQ0, on_uart_rx0); |
216 | irq_set_enabled(UART_IRQ0, true); |
217 | irq_set_exclusive_handler(UART_IRQ1, on_uart_rx1); |
218 | irq_set_enabled(UART_IRQ1, true); |
219 | |
220 | // Now enable the UART to send interrupts - RX only
|
221 | uart_set_irq_enables(UART_ID0, true, false); |
222 | uart_set_irq_enables(UART_ID1, true, false); |
223 | |
224 | // GPIO initialisation.
|
225 | // We will make this GPIO an input, and pull it up by default
|
226 | gpio_init(GPIO); |
227 | gpio_set_dir(GPIO, GPIO_IN); |
228 | gpio_pull_up(GPIO); |
229 | |
230 | struct repeating_timer timer; |
231 | bool rm_art = add_repeating_timer_ms(500, repeating_timer_callback, NULL, &timer); |
232 | |
233 | while (true) { |
234 | on_uart_rx2(); |
235 | }
|
236 | }
|
CMakeLists.txt:
1 | # Generated Cmake Pico project file |
2 | |
3 | cmake_minimum_required(VERSION 3.13) |
4 | |
5 | set(CMAKE_C_STANDARD 11) |
6 | set(CMAKE_CXX_STANDARD 17) |
7 | |
8 | # Initialise pico_sdk from installed location |
9 | # (note this can come from environment, CMake cache etc) |
10 | set(PICO_SDK_PATH "/home/peter/pico/pico-sdk") |
11 | |
12 | set(PICO_BOARD pico CACHE STRING "Board type") |
13 | |
14 | # Pull in Raspberry Pi Pico SDK (must be before project) |
15 | include(pico_sdk_import.cmake) |
16 | |
17 | if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0") |
18 | message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}") |
19 | endif() |
20 | |
21 | project(ControlBox C CXX ASM) |
22 | |
23 | # Initialise the Raspberry Pi Pico SDK |
24 | pico_sdk_init() |
25 | |
26 | # Add executable. Default name is the project name, version 0.1 |
27 | |
28 | add_executable(ControlBox ControlBox.c ) |
29 | |
30 | pico_set_program_name(ControlBox "ControlBox") |
31 | pico_set_program_version(ControlBox "0.1") |
32 | |
33 | pico_enable_stdio_uart(ControlBox 0) |
34 | pico_enable_stdio_usb(ControlBox 1) |
35 | |
36 | # Add the standard library to the build |
37 | target_link_libraries(ControlBox |
38 | pico_stdlib) |
39 | # pico_multicore) |
40 | |
41 | # Add the standard include files to the build |
42 | target_include_directories(ControlBox PRIVATE |
43 | ${CMAKE_CURRENT_LIST_DIR} |
44 | ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required |
45 | ) |
46 | |
47 | # Add any user requested libraries |
48 | target_link_libraries(ControlBox |
49 | hardware_spi |
50 | hardware_i2c |
51 | hardware_pio |
52 | ) |
53 | |
54 | pico_add_extra_outputs(ControlBox) |
Pascal-Programm:
1 | unit Unit1; |
2 | |
3 | {$mode objfpc}{$H+} |
4 | |
5 | interface |
6 | |
7 | uses |
8 | Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, |
9 | SdpoSerial, LazSerial; |
10 | |
11 | type |
12 | |
13 | { TForm1 } |
14 | |
15 | TForm1 = class(TForm) |
16 | Button1: TButton; |
17 | CheckBox1: TCheckBox; |
18 | CheckBox2: TCheckBox; |
19 | Label1: TLabel; |
20 | Memo1: TMemo; |
21 | Memo2: TMemo; |
22 | PC: TSdpoSerial; |
23 | HB: TSdpoSerial; |
24 | MR: TSdpoSerial; |
25 | THB: TTimer; |
26 | TMR: TTimer; |
27 | TPC: TTimer; |
28 | procedure Button1Click(Sender: TObject); |
29 | procedure CheckBox1Change(Sender: TObject); |
30 | procedure CheckBox2Change(Sender: TObject); |
31 | procedure FormCreate(Sender: TObject); |
32 | procedure HBRxData(Sender: TObject); |
33 | procedure MRRxData(Sender: TObject); |
34 | procedure PCRxData(Sender: TObject); |
35 | procedure THBTimer(Sender: TObject); |
36 | procedure TMRTimer(Sender: TObject); |
37 | procedure TPCTimer(Sender: TObject); |
38 | private |
39 | PC_Buf, HB_Buf, MR_Buf : string; |
40 | PC_Snd, HB_Snd, MR_Snd : string; |
41 | RecvCnt : Integer; |
42 | function RandStr(Min, Max : Integer) : string; |
43 | public |
44 | |
45 | end; |
46 | |
47 | var |
48 | Form1: TForm1; |
49 | |
50 | implementation |
51 | |
52 | uses |
53 | Math; |
54 | |
55 | {$R *.lfm} |
56 | |
57 | { TForm1 } |
58 | |
59 | function TForm1.RandStr(Min, Max : Integer) : string; |
60 | Var |
61 | i, j : Integer; |
62 | begin |
63 | j := RandomRange(Min, Max); |
64 | result := ''; |
65 | for i := 1 to j do |
66 | result := result + Chr(RandomRange(65,90)) |
67 | end; |
68 | |
69 | procedure TForm1.Button1Click(Sender: TObject); |
70 | begin |
71 | PC_Snd := RandStr(3,16)+'~'; |
72 | HB_Snd := RandStr(3,12)+'~'; |
73 | MR_Snd := RandStr(3,10); |
74 | Memo1.Lines.Add('PC_Snd: '+PC_Snd); |
75 | Memo1.Lines.Add('HB_Snd: '+HB_Snd); |
76 | PC.WriteData('/'+PC_Snd); |
77 | HB.WriteData('/'+HB_Snd); |
78 | MR.WriteData(MR_Snd+';'); |
79 | MR_Snd := MR_Snd + '~'; |
80 | Memo1.Lines.Add('MR_Snd: '+MR_Snd); |
81 | end; |
82 | |
83 | procedure TForm1.CheckBox1Change(Sender: TObject); |
84 | begin |
85 | PC.Active := CheckBox1.Checked; |
86 | HB.Active := CheckBox1.Checked; |
87 | MR.Active := CheckBox1.Checked; |
88 | end; |
89 | |
90 | procedure TForm1.CheckBox2Change(Sender: TObject); |
91 | begin |
92 | Memo1.Lines.Add('Start/Stopp: '+FloatToStr(Now)); |
93 | TPC.Enabled := CheckBox2.Checked; |
94 | THB.Enabled := CheckBox2.Checked; |
95 | TMR.Enabled := CheckBox2.Checked; |
96 | end; |
97 | |
98 | procedure TForm1.FormCreate(Sender: TObject); |
99 | begin |
100 | Randomize; |
101 | PC_Buf := ''; |
102 | HB_Buf := ''; |
103 | MR_Buf := ''; |
104 | RecvCnt := 0; |
105 | end; |
106 | |
107 | procedure TForm1.HBRxData(Sender: TObject); |
108 | Var |
109 | s : string; |
110 | begin |
111 | HB_Buf := HB_Buf + HB.ReadData; |
112 | if Pos('~',HB_Buf) <> 0 then |
113 | begin |
114 | inc(RecvCnt); |
115 | //Memo1.Lines.Add('HB: '+HB_Buf); |
116 | if PC_Snd <> HB_Buf then |
117 | begin |
118 | s := FloatToStr(Now) + ': PC (' + PC_Snd + ') <> HB (' + HB_Buf + ')'; |
119 | Memo1.Lines.Add(s); |
120 | end; |
121 | HB_Buf := ''; |
122 | label1.Caption := IntToStr(RecvCnt); |
123 | end; |
124 | end; |
125 | |
126 | procedure TForm1.MRRxData(Sender: TObject); |
127 | Var |
128 | s : string; |
129 | begin |
130 | MR_Buf := MR_Buf + MR.ReadData; |
131 | if Pos('~',MR_Buf) <> 0 then |
132 | begin |
133 | inc(RecvCnt); |
134 | //Memo1.Lines.Add('MR: '+MR_Buf); |
135 | if HB_Snd <> MR_Buf then |
136 | begin |
137 | s := FloatToStr(Now) + ': HB (' + HB_Snd + ') <> MR (' + MR_Buf + ')'; |
138 | Memo1.Lines.Add(s); |
139 | end; |
140 | MR_Buf := ''; |
141 | label1.Caption := IntToStr(RecvCnt); |
142 | end; |
143 | end; |
144 | |
145 | procedure TForm1.PCRxData(Sender: TObject); |
146 | Var |
147 | s : string; |
148 | begin |
149 | s := PC.ReadData; |
150 | Memo2.Lines.Add('Recv ' + s); |
151 | PC_Buf := PC_Buf + s; |
152 | if Pos('~',PC_Buf) <> 0 then |
153 | begin |
154 | inc(RecvCnt); |
155 | //Memo1.Lines.Add('PC: '+PC_Buf); |
156 | if MR_Snd <> PC_Buf then |
157 | begin |
158 | s := FloatToStr(Now) + ' MR (' + MR_Snd + ') <> PC (' + PC_Buf + ')'; |
159 | Memo1.Lines.Add(s); |
160 | end; |
161 | PC_Buf := ''; |
162 | label1.Caption := IntToStr(RecvCnt); |
163 | end; |
164 | end; |
165 | |
166 | procedure TForm1.THBTimer(Sender: TObject); |
167 | begin |
168 | THB.Enabled := False; |
169 | HB_Snd := RandStr(3,12)+'~'; |
170 | HB.WriteData('/'+HB_Snd); |
171 | THB.Interval := RandomRange(20,90); |
172 | THB.Enabled := True; |
173 | end; |
174 | |
175 | procedure TForm1.TMRTimer(Sender: TObject); |
176 | begin |
177 | TMR.Enabled := False; |
178 | MR_Snd := RandStr(3,9); |
179 | //MR_Snd := CalcCRC8(MR_Snd) + MR_Snd; |
180 | MR.WriteData(MR_Snd+';'); |
181 | MR_Snd := MR_Snd + '~'; |
182 | Memo2.Lines.Add('Send ' + MR_Snd); |
183 | TMR.Interval := RandomRange(20,90); |
184 | TMR.Enabled := True; |
185 | end; |
186 | |
187 | procedure TForm1.TPCTimer(Sender: TObject); |
188 | begin |
189 | TPC.Enabled := False; |
190 | PC_Snd := RandStr(3,16)+'~'; |
191 | PC.WriteData('/'+PC_Snd); |
192 | TPC.Interval := RandomRange(20,90); |
193 | TPC.Enabled := True; |
194 | end; |
195 | |
196 | end. |