6 |
7 | #include "stm32f4xx.h"
8 | #include "stm32f4xx_rcc.h"
9 | #include "stm32f4xx_iwdg.h""
10 |
11 | //AnalogOut my_output(PA_4);
12 |
13 | //short buffer[BUFFER_SIZE];
14 |
15 | enum kiss_state_e {
16 | KS_SEARCHING, /* Looking for FEND to start KISS frame. */
17 | KS_COLLECTING}; /* In process of collecting KISS frame. */
18 |
19 |
20 | #define MAX_KISS_LEN 2048 /* Spec calls for at least 1024. */
21 | /* Might want to make it longer to accomodate */
22 | /* maximum packet length. */
23 |
24 | #define MAX_NOISE_LEN 100
25 |
26 |
27 | typedef struct kiss_frame_s {
28 |
29 | enum kiss_state_e state;
30 |
31 | unsigned char kiss_msg[MAX_KISS_LEN];
32 | /* Leading FEND is optional. */
33 | /* Contains escapes and ending FEND. */
34 | int kiss_len;
35 |
36 | unsigned char noise[MAX_NOISE_LEN];
37 | int noise_len;
38 |
39 | } kiss_frame_t;
40 |
41 |
42 |
43 | extern "C" {
44 | void InitValidFrameTypes();
45 | void GetNextFECFrame();
46 | void direwolfmain();
47 | void printtick(char * msg);
48 | void Debugprintf(const char * format, ...);
49 | void dw_printf(const char * format, ...);
50 | void Config_ADC_DMA(void);
51 | void Start_ADC_DMA(void);
52 | void ProcessNewSamples(short * Samples, int nSamples);
53 | void CheckTimers();
54 | void MainPoll();
55 | void HostPoll();
56 | //void SetARDOPProtocolState(int State);
57 |
58 | uint32_t DMA_GetCurrentMemoryTarget(DMA_Stream_TypeDef* DMAy_Streamx);
59 | void PollReceivedSamples();
60 | void SetLED(int blnPTT);
61 | void Sleep(int delay);
62 | void SerialSink(UCHAR c);
63 | void SerialSendData(unsigned char * Msg, int Len);
64 | void PollReceivedSamples();
65 |
66 | void init_I2C1(void);
67 | void initdisplay();
68 | void kiss_rec_byte(kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfun)(int,unsigned char*,int));
69 | // void kiss_rec_byte(kiss_frame_t *kf, unsigned char ch, int debug);
70 | void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen);
71 | void recv_process();
72 | int xmit_process();
73 | }
74 |
75 |
76 |
77 | InterruptIn mybutton(USER_BUTTON);
78 | DigitalOut myled(LED1);
79 | Ticker ti;
80 |
81 | Serial serial(USBTX, USBRX); // Host PTC Emulation
82 | Serial serial3(PC_10, PC_11); // Debug Port
83 |
84 | float delay = 0.001; // 1 mS
85 |
86 | volatile int iTick = 0;
87 | volatile bool bTick = 0;
88 | volatile int iClick = 0;
89 | volatile bool bClick = 0;
90 | volatile int ticks;
91 | volatile int ADCInterrupts = 0;
92 | extern volatile int adc_buffer_mem;
93 |
94 | #define SAMPLES_PER_BLOCK 1200
95 |
96 | int i = 0;
97 |
98 | void SetLED(int blnPTT)
99 | {
100 | myled = blnPTT;
101 | }
102 |
103 | void tick()
104 | {
105 | bTick = true;
106 | ticks++;
107 | }
108 |
109 | void pressed()
110 | {
111 | iClick++;
112 | bClick = true;
113 | }
114 |
115 |
116 | int lastchan = 0;
117 |
118 |
119 | // USB Port is used for SCS Host mode link to host.
120 |
121 | // Must use interrupts (or possibly DMA) as we can't wait while processing sound.
122 |
123 | // HostMode has a maximum frame size of around 262 bytes, and as it is polled
124 | // we only need room for 1 frame
125 |
126 | #define SCSBufferSize 280
127 | #define KISSBufferSize 1024
128 |
129 | char tx_buffer[SCSBufferSize];
130 |
131 | // Circular buffer pointers
132 | // volatile makes read-modify-write atomic
133 | volatile int tx_in=0;
134 | volatile int tx_out=0;
135 | volatile int tx_stopped = 1;
136 |
137 | unsigned char rx_buffer[KISSBufferSize];
138 |
139 | // Circular buffer pointers
140 | // volatile makes read-modify-write atomic
141 | volatile int rx_in=0;
142 | volatile int rx_out=0;
143 |
144 |
145 | char line[80];
146 |
147 | void SerialSendData(unsigned char * Msg, int Len)
148 | {
149 | int i;
150 | i = 0;
151 |
152 | while (i < Len)
153 | {
154 | tx_buffer[tx_in] = Msg[i++];
155 | tx_in = (tx_in + 1) % SCSBufferSize;
156 | }
157 |
158 | // disable ints to avoid possible race
159 |
160 | // Send first character to start tx interrupts, if stopped
161 |
162 | __disable_irq();
163 |
164 | if (tx_stopped)
165 | {
166 | serial.putc(tx_buffer[tx_out]);
167 | tx_out = (tx_out + 1) % SCSBufferSize;
168 | tx_stopped = 0;
169 | }
170 | __enable_irq();
171 |
172 | return;
173 | }
174 |
175 | void rxcallback()
176 | {
177 | // Note: you need to actually read from the serial to clear the RX interrupt
178 |
179 | unsigned char c;
180 | c = serial.getc();
181 |
182 | rx_buffer[rx_in] = c;
183 | rx_in = (rx_in + 1) % KISSBufferSize;
184 |
185 | }
186 |
187 | void txcallback()
188 | {
189 | // Loop to fill more than one character in UART's transmit FIFO buffer
190 | // Stop if buffer empty
191 |
192 | while ((serial.writeable()) && (tx_in != tx_out))
193 | {
194 | serial.putc(tx_buffer[tx_out]);
195 | tx_out = (tx_out + 1) % SCSBufferSize;
196 | }
197 |
198 | if (tx_in == tx_out)
199 | tx_stopped = 1;
200 |
201 | return;
202 | }
203 |
204 | // Port 3 is used for debugging
205 |
206 | // Must use interrupts (or possibly DMA) as we can't wait while processing sound.
207 |
208 | // Not sure how big it needs to be. Don't want to use too mach RAM
209 |
210 | #define DebugBufferSize 1024
211 |
212 | char tx3_buffer[DebugBufferSize];
213 |
214 | // Circular buffer pointers
215 | // volatile makes read-modify-write atomic
216 | volatile int tx3_in=0;
217 | volatile int tx3_out=0;
218 | volatile int tx3_stopped = 1;
219 |
220 |
221 | void Serial3SendData(unsigned char * Msg, int Len)
222 | {
223 | int i;
224 | i = 0;
225 |
226 | while (i < Len)
227 | {
228 | tx3_buffer[tx3_in] = Msg[i++];
229 | tx3_in = (tx3_in + 1) % DebugBufferSize;
230 | }
231 |
232 | // disable ints to avoid possible race
233 |
234 | // Send first character to start tx interrupts, if stopped
235 |
236 | __disable_irq();
237 |
238 | if (tx3_stopped)
239 | {
240 | serial3.putc(tx3_buffer[tx3_out]);
241 | tx3_out = (tx3_out + 1) % DebugBufferSize;
242 | tx3_stopped = 0;
243 | }
244 | __enable_irq();
245 |
246 | return;
247 | }
248 |
249 | void rx3callback()
250 | {
251 | // Note: you need to actually read from the serial to clear the RX interrupt
252 |
253 | unsigned char c;
254 |
255 | c = serial3.getc();
256 |
257 | // SerialSink(c);
258 | // serial2.printf("%c", c);
259 |
260 | // myled = !myled;
261 | }
262 |
263 | void tx3callback()
264 | {
265 | // Loop to fill more than one character in UART's transmit FIFO buffer
266 | // Stop if buffer empty
267 |
268 | while ((serial3.writeable()) && (tx3_in != tx3_out))
269 | {
270 | serial3.putc(tx3_buffer[tx3_out]);
271 | tx3_out = (tx3_out + 1) % DebugBufferSize;
272 | }
273 |
274 | if (tx3_in == tx3_out)
275 | tx3_stopped = 1;
276 |
277 | return;
278 | }
279 |
280 |
281 | extern kiss_frame_t kf; /* Accumulated KISS frame and state of decoder. */
282 |
283 | int kissdebug = 2;
284 |
285 | int main()
286 | {
287 | serial.baud(115200);
288 | serial3.baud(115200);
289 |
290 | serial.attach(&rxcallback);
291 | serial.attach(&txcallback, Serial::TxIrq);
292 |
293 | // serial3.attach(&rxc3allback);
294 | serial3.attach(&tx3callback, Serial::TxIrq);
295 |
296 | /* Check if the system has resumed from WWDG reset */
297 |
298 | if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET)
299 | {
300 | Debugprintf("Reset by watchdog");
301 | RCC_ClearFlag();
302 | }
303 |
304 | /* Enable write access to IWDG_PR and IWDG_RLR registers */
305 | IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
306 |
307 | /* IWDG counter clock: LSI/256, ~6.4ms */
308 | IWDG_SetPrescaler(IWDG_Prescaler_256);
309 |
310 | IWDG_SetReload(2000); // ~12 secs
311 |
312 | /* Reload IWDG counter */
313 | IWDG_ReloadCounter();
314 |
315 | /* Enable IWDG (the LSI oscillator will be enabled by hardware) */
316 | // IWDG_Enable();
317 |
318 |
319 | Debugprintf("Clock Freq %d", SystemCoreClock);
320 |
321 | // init_I2C1();
322 |
323 | Debugprintf("i2c init returned");
324 |
325 | // initdisplay();
326 |
327 | mybutton.fall(&pressed);
328 |
329 | ti.attach(tick, .001);
330 |
331 | direwolfmain();
332 |
333 | myled = 0;
334 |
335 | while (1)
336 | {
337 | unsigned char ch;
338 |
339 | if (rx_in != rx_out)
340 | {
341 | ch = rx_buffer[rx_out];
342 | rx_out = (rx_out + 1) % KISSBufferSize;
343 | kiss_rec_byte (&kf, ch, kissdebug, kiss_send_rec_packet);
344 | }
345 |
346 | PollReceivedSamples();
347 |
348 | recv_process();
349 |
350 | // See if we have anything to send
351 |
352 | xmit_process();
353 | }
354 | }
355 |
356 |
357 | extern "C" void PlatformSleep()
358 | {
359 | // Called at end of main loop
360 |
361 | IWDG_ReloadCounter();
362 |
363 | if (bTick)
364 | {
365 | // serial.printf("ADCInterrupts %i %d %d buffer no %d \r\n", ADCInterrupts,
366 | // ADC_Buffer[0][0], ADC_Buffer[1][0], DMA_GetCurrentMemoryTarget(DMA2_Stream0));
367 |
368 | bTick = false;
369 | }
370 |
371 | if (bClick)
372 | {
373 | bClick = false;
374 | }
375 |
376 | myled = !myled;
377 |
378 | wait(delay);
379 | }
380 |
381 | void Sleep(int delay)
382 | {
383 | wait(delay/1000);
384 | return;
385 | }
386 |
387 | VOID Debugprintf(const char * format, ...)
388 | {
389 | char Mess[1000];
390 | va_list(arglist);
391 |
392 | va_start(arglist, format);
393 | vsprintf(Mess, format, arglist);
394 | strcat(Mess, "\r\n");
395 |
396 | Serial3SendData((unsigned char *)Mess, strlen(Mess));
397 |
398 | return;
399 | }
400 |
401 | VOID dw_printf(const char * format, ...)
402 | {
403 | char Mess[1000];
404 | va_list(arglist);
405 |
406 | va_start(arglist, format);
407 | vsprintf(Mess, format, arglist);
408 |
409 | Serial3SendData((unsigned char *)Mess, strlen(Mess));
410 |
411 | return;
412 | }
413 |
414 |
415 |
416 |
417 |
418 |
419 |
--------------------------------------------------------------------------------
/ardop1ofdm/SerialIntefaceSpec.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
16 | ARDOP Serial Interface Protocol.
17 |
18 |
19 |
20 | ARDOP Serial Interface Protocol.
21 |
22 | Overview.
23 |
24 | The Host to TNC protocol used over Serisl and Bluetooth links operates in two modes, Text or Hostmode.
25 | Text mode is primarily used for initialisation and testing, but can be used for simple interative
26 | QSO's using a standard ASCII terminal program. There is no error checking on the text interface.
27 | Hostmode is used for automatic operation, and provides error detection and recovery over the
28 | serial/Bluetooth line. ARDOP Hostmode is based on the SCS CRC Hostmode (as used in
29 | the PTC and Dragon Controllers) which is itself an extension of WA8DED Hostmode.
30 |
31 |
Operating Modes.
32 |
33 | Either Protocol Mode can be used on one of two Operating Modes. ARDOP Native or PTC Emulation.
34 | The former supports the full ARDOP functionality, the latter enables ARDOP to be used with programs
35 | that support an SCS Pactor controllet, but don't have native ARDOP support.
36 |
37 |
ARDOP Native Mode.
38 |
39 | ARDOP Native Mode is designed to be functionally similar to the TCP mode, to simplify host
40 | program and TNC support. Two DED Channels are assigned to the functions of the two TCP ports,
41 | Channel 32 for Control and Channel 33 for Data. A third channel (34) is used for debug information,
42 | as a standalone TNC doesn't usually have local storage for writing a debug log. The Protocol control channels
43 | (255 for polling, 254 for status and 253 for rig control) are used as defined by SCS.
44 |
45 |
PTC Emulation Mode.
46 |
47 | This supports basic ARQ mode operation with software designed for Pactor. FEC operation isn't
48 | supported, but the Pactor Levels 1-4 are mapped to the ARDOP Bandwidths of 200 to 2000. Pactor
49 | Host Mode commands are converted to equivalent ARDOP commands, and ARDOP link status values mapped
50 | to equivalent Pactor values.
51 |
52 |
Protocol Overview
53 |
54 | The protocol is polled master/slave, with a single bit sequence toggle to detect lost or duplicated frames.
55 | The host program is the master and the TNC the slave. The polling frequency isn't defined, but a maximum interval of 100mS is recommended to minimise latency.
56 |
57 | The link is considered Idle when the master has received an in-sequence response to its previous transmission.
58 | The master can transmit at any time the link is idle. If it has data to send, it sends it, otherwise it sends a
59 | General Poll message. The slave will respond with a list (possibly empty) of channels with data available. The master then polls each channel in the list.
60 |
61 | If the master doesn't receive a valid response from the slave in a resonable time, it will repeat its last transmission. It it doesn't get a reponse after a reasonable number of retries it will report a link failure to
62 | the higher lovel software, which will abort any transmission n progess, then try to reestablish the link.
63 |
64 |
65 | If the master receives an out of sequence response following a timeout and retry it will assume a delayed response
66 | from the slave, discard the respeat, and continue to wait for a valid frame. If it receives an out of sequence response at any other time it will assume a protocol error and reset and restart the link.
67 |
68 |
69 | If the slave receives an out of sequence message it will assume that its previous response was lost, discard the message and resend the previous response unchanged.
70 |
71 |
72 |
73 |
Packet Formats
74 |
75 |
76 |
77 | All packets have a two byte header of 0xAAAA and a two byte CRC-16 checksum on end
78 | The top bit of the Opcode field is a sequence toggle.
79 |
80 | The basic packet format is:
81 | -----------------------------------------------
82 | |Header|Chan|Opcode| Payload | CRC |
83 | -----------------------------------------------
84 | |AA AA | XX | XX | XX XX XX .... XX |XX XX|
85 | -----------------------------------------------
86 |
87 | Payload can have two formats, either a Null terminated ASCII string or a Byte Count of
88 | 0 to 255 followed by 1 to 256 bytes of data.
89 |
90 | There are two opcodes for Host to TNC packets, and eight for TNC to Host, though not
91 | all are used in ARDOP Native Mode.
92 |
93 | From Host to TNC
94 |
95 | Opcode 0 - Data
96 | Opcode 1 - Command
97 |
98 | From TNC to Host
99 |
100 | Opcode 0 - Response Success (no data follows)
101 | Opcode 1 - Response Success, followed by null terminated message
102 | Opcode 2 - Response Failure, followed by null terminated message
103 | Opcode 7 - Data, preceeded by (length-1)
104 |
105 | Channel 32 is used for Commands, Channel 33 for Data and Channel 34 for Debug information. Typical messages
106 | are shown below (control fields in Hex). Note that the Command format is used for hostmode
107 | protocol level commands. ARDOP commands (such as "ARQCALL") are sent as data on the
108 | Command channel. The Debug channel is for information to be written to a debug log.
109 |
110 | General Poll
111 |
112 | ----------------------------------
113 | |Header|Chan|Opcode|Payload| CRC |
114 | ----------------------------------
115 | |AA AA | FF | 01 | 00 47 |XX XX|
116 | ----------------------------------
117 |
118 | Response is a null terminated list of channels with available data. Value is Channel plus 1
119 |
120 | ----------------------------------
121 | |AA AA | FF | 01 | 21 00 |XX XX| Channel 32 has data (0x21 = 32 + 1)
122 | ----------------------------------
123 |
124 | Poll to TNC Command Channel
125 |
126 | ----------------------------------
127 | |Header|Chan|Opcode|Payload| CRC |
128 | ----------------------------------
129 | |AA AA | 20 | 01 | 00 47 |XX XX|
130 | ----------------------------------
131 |
132 | Response is an async ARDOP response message
133 |
134 | --------------------------------------------------
135 | |Header|Chan|Opcode| Payload | CRC |
136 | --------------------------------------------------
137 | |AA AA | 20 | 07 |<0F>c:NEWSTATE ISS <0D>|XX XX|
138 | --------------------------------------------------
139 |
140 |
141 | ARDOP Command to TNC
142 | -----------------------------------------------------
143 | |Header|Chan|Opcode| Payload | CRC |
144 | -----------------------------------------------------
145 | |AA AA | 20 | 00 |C:MYCALL G8BPQ <00> |XX XX|
146 | ----------------------------------------------------
147 | response
148 | -----------------------------------------------------
149 | |AA AA | 20 | 01 |c:MYCALL now G8BPQ <00> |XX XX|
150 | -----------------------------------------------------
151 |
152 | Data to be transmitted
153 | -----------------------------------------------------
154 | |Header|Chan|Opcode| Payload | CRC |
155 | -----------------------------------------------------
156 | |AA AA | 21 | 00 |<12>D:(Len)Message to Send|XX XX|
157 | -----------------------------------------------------
158 | response
159 | -----------------------------------------------------
160 | |AA AA | 21 | 00 | |XX XX|
161 | -----------------------------------------------------
162 |
163 | Received Data from TNC
164 |
165 | Data to be transmitted
166 | -----------------------------------------------------
167 | |Header|Chan|Opcode| Payload | CRC |
168 | -----------------------------------------------------
169 | |AA AA | 21 | 07 |<13>d:(Len)ARQMsg Received|XX XX|
170 | -----------------------------------------------------
171 | This is no response to inbound frames, apart from the implied ACK of the
172 | next host frame having an inverted toggle.
173 |
174 |
175 |
176 |
177 | Appendix
178 |
179 | SCS CRC Hostmode
180 |
181 | See the PTC-IIIusb Manual Chapter 10 for details of CRC Hostmode .
182 |
183 | WA8DED Packet Format
184 |
185 | There are two data formats, null terminted or counted.
186 |
187 |
188 |
189 | Byte 0 Channel Number
190 | byte 1 Type Code (see table below)
191 | byte 3-end Null Terminated string (max length 255)
192 | or
193 | byte 3 Info Length - 1
194 | byte 4-end Info (length 1 to 256)
195 |
196 | SCS CRC Hostmode adds two byte of 170 to the front and a crc-16 checksum to the end.
197 | It also defines the top bit (bit 7) of the Type Code as a sequence toggle and next bit
198 | (bit 6) as a sequence reset flag. Data transparency is ensured by adding a null byte
199 | after any occurence of 170 in the message or crc.
200 |
201 | Command Codes
202 |
203 | Host to Tnc
204 | -----------
205 | CHANNEL CODE DESCRIPTION
206 | ------- ---- -----------
207 | n 0 Information (preceeded by length-1)
208 | n 1 Command (preceeded by length-1)
209 |
210 | Tnc to Host
211 | -----------
212 | CHANNEL CODE DESCRIPTION
213 | ------- ---- -----------
214 | n 0 Success (nothing follows)
215 | n 1 Success (message follows, null terminated)
216 | n 2 Failure (message follows, null terminated)
217 | n 3 Link Status (null terminated)
218 | n 4 Monitor Header (null terminated)
219 | n 5 Monitor Header (null terminated)
220 | n 6 Monitor Information (preceeded by length-1)
221 | n 7 Connect Information (preceeded by length-1)
222 |
223 |
--------------------------------------------------------------------------------
/ardop2ofdm/SerialIntefaceSpec.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
16 | ARDOP Serial Interface Protocol.
17 |
18 |
19 |
20 | ARDOP Serial Interface Protocol.
21 |
22 | Overview.
23 |
24 | The Host to TNC protocol used over Serisl and Bluetooth links operates in two modes, Text or Hostmode.
25 | Text mode is primarily used for initialisation and testing, but can be used for simple interative
26 | QSO's using a standard ASCII terminal program. There is no error checking on the text interface.
27 | Hostmode is used for automatic operation, and provides error detection and recovery over the
28 | serial/Bluetooth line. ARDOP Hostmode is based on the SCS CRC Hostmode (as used in
29 | the PTC and Dragon Controllers) which is itself an extension of WA8DED Hostmode.
30 |
31 |
Operating Modes.
32 |
33 | Either Protocol Mode can be used on one of two Operating Modes. ARDOP Native or PTC Emulation.
34 | The former supports the full ARDOP functionality, the latter enables ARDOP to be used with programs
35 | that support an SCS Pactor controllet, but don't have native ARDOP support.
36 |
37 |
ARDOP Native Mode.
38 |
39 | ARDOP Native Mode is designed to be functionally similar to the TCP mode, to simplify host
40 | program and TNC support. Two DED Channels are assigned to the functions of the two TCP ports,
41 | Channel 32 for Control and Channel 33 for Data. A third channel (34) is used for debug information,
42 | as a standalone TNC doesn't usually have local storage for writing a debug log. The Protocol control channels
43 | (255 for polling, 254 for status and 253 for rig control) are used as defined by SCS.
44 |
45 |
PTC Emulation Mode.
46 |
47 | This supports basic ARQ mode operation with software designed for Pactor. FEC operation isn't
48 | supported, but the Pactor Levels 1-4 are mapped to the ARDOP Bandwidths of 200 to 2000. Pactor
49 | Host Mode commands are converted to equivalent ARDOP commands, and ARDOP link status values mapped
50 | to equivalent Pactor values.
51 |
52 |
Protocol Overview
53 |
54 | The protocol is polled master/slave, with a single bit sequence toggle to detect lost or duplicated frames.
55 | The host program is the master and the TNC the slave. The polling frequency isn't defined, but a maximum interval of 100mS is recommended to minimise latency.
56 |
57 | The link is considered Idle when the master has received an in-sequence response to its previous transmission.
58 | The master can transmit at any time the link is idle. If it has data to send, it sends it, otherwise it sends a
59 | General Poll message. The slave will respond with a list (possibly empty) of channels with data available. The master then polls each channel in the list.
60 |
61 | If the master doesn't receive a valid response from the slave in a resonable time, it will repeat its last transmission. It it doesn't get a reponse after a reasonable number of retries it will report a link failure to
62 | the higher lovel software, which will abort any transmission n progess, then try to reestablish the link.
63 |
64 |
65 | If the master receives an out of sequence response following a timeout and retry it will assume a delayed response
66 | from the slave, discard the respeat, and continue to wait for a valid frame. If it receives an out of sequence response at any other time it will assume a protocol error and reset and restart the link.
67 |
68 |
69 | If the slave receives an out of sequence message it will assume that its previous response was lost, discard the message and resend the previous response unchanged.
70 |
71 |
72 |
73 |
Packet Formats
74 |
75 |
76 |
77 | All packets have a two byte header of 0xAAAA and a two byte CRC-16 checksum on end
78 | The top bit of the Opcode field is a sequence toggle.
79 |
80 | The basic packet format is:
81 | -----------------------------------------------
82 | |Header|Chan|Opcode| Payload | CRC |
83 | -----------------------------------------------
84 | |AA AA | XX | XX | XX XX XX .... XX |XX XX|
85 | -----------------------------------------------
86 |
87 | Payload can have two formats, either a Null terminated ASCII string or a Byte Count of
88 | 0 to 255 followed by 1 to 256 bytes of data.
89 |
90 | There are two opcodes for Host to TNC packets, and eight for TNC to Host, though not
91 | all are used in ARDOP Native Mode.
92 |
93 | From Host to TNC
94 |
95 | Opcode 0 - Data
96 | Opcode 1 - Command
97 |
98 | From TNC to Host
99 |
100 | Opcode 0 - Response Success (no data follows)
101 | Opcode 1 - Response Success, followed by null terminated message
102 | Opcode 2 - Response Failure, followed by null terminated message
103 | Opcode 7 - Data, preceeded by (length-1)
104 |
105 | Channel 32 is used for Commands, Channel 33 for Data and Channel 34 for Debug information. Typical messages
106 | are shown below (control fields in Hex). Note that the Command format is used for hostmode
107 | protocol level commands. ARDOP commands (such as "ARQCALL") are sent as data on the
108 | Command channel. The Debug channel is for information to be written to a debug log.
109 |
110 | General Poll
111 |
112 | ----------------------------------
113 | |Header|Chan|Opcode|Payload| CRC |
114 | ----------------------------------
115 | |AA AA | FF | 01 | 00 47 |XX XX|
116 | ----------------------------------
117 |
118 | Response is a null terminated list of channels with available data. Value is Channel plus 1
119 |
120 | ----------------------------------
121 | |AA AA | FF | 01 | 21 00 |XX XX| Channel 32 has data (0x21 = 32 + 1)
122 | ----------------------------------
123 |
124 | Poll to TNC Command Channel
125 |
126 | ----------------------------------
127 | |Header|Chan|Opcode|Payload| CRC |
128 | ----------------------------------
129 | |AA AA | 20 | 01 | 00 47 |XX XX|
130 | ----------------------------------
131 |
132 | Response is an async ARDOP response message
133 |
134 | --------------------------------------------------
135 | |Header|Chan|Opcode| Payload | CRC |
136 | --------------------------------------------------
137 | |AA AA | 20 | 07 |<0F>c:NEWSTATE ISS <0D>|XX XX|
138 | --------------------------------------------------
139 |
140 |
141 | ARDOP Command to TNC
142 | -----------------------------------------------------
143 | |Header|Chan|Opcode| Payload | CRC |
144 | -----------------------------------------------------
145 | |AA AA | 20 | 00 |C:MYCALL G8BPQ <00> |XX XX|
146 | ----------------------------------------------------
147 | response
148 | -----------------------------------------------------
149 | |AA AA | 20 | 01 |c:MYCALL now G8BPQ <00> |XX XX|
150 | -----------------------------------------------------
151 |
152 | Data to be transmitted
153 | -----------------------------------------------------
154 | |Header|Chan|Opcode| Payload | CRC |
155 | -----------------------------------------------------
156 | |AA AA | 21 | 00 |<12>D:(Len)Message to Send|XX XX|
157 | -----------------------------------------------------
158 | response
159 | -----------------------------------------------------
160 | |AA AA | 21 | 00 | |XX XX|
161 | -----------------------------------------------------
162 |
163 | Received Data from TNC
164 |
165 | Data to be transmitted
166 | -----------------------------------------------------
167 | |Header|Chan|Opcode| Payload | CRC |
168 | -----------------------------------------------------
169 | |AA AA | 21 | 07 |<13>d:(Len)ARQMsg Received|XX XX|
170 | -----------------------------------------------------
171 | This is no response to inbound frames, apart from the implied ACK of the
172 | next host frame having an inverted toggle.
173 |
174 |
175 |
176 |
177 | Appendix
178 |
179 | SCS CRC Hostmode
180 |
181 | See the PTC-IIIusb Manual Chapter 10 for details of CRC Hostmode .
182 |
183 | WA8DED Packet Format
184 |
185 | There are two data formats, null terminted or counted.
186 |
187 |
188 |
189 | Byte 0 Channel Number
190 | byte 1 Type Code (see table below)
191 | byte 3-end Null Terminated string (max length 255)
192 | or
193 | byte 3 Info Length - 1
194 | byte 4-end Info (length 1 to 256)
195 |
196 | SCS CRC Hostmode adds two byte of 170 to the front and a crc-16 checksum to the end.
197 | It also defines the top bit (bit 7) of the Type Code as a sequence toggle and next bit
198 | (bit 6) as a sequence reset flag. Data transparency is ensured by adding a null byte
199 | after any occurence of 170 in the message or crc.
200 |
201 | Command Codes
202 |
203 | Host to Tnc
204 | -----------
205 | CHANNEL CODE DESCRIPTION
206 | ------- ---- -----------
207 | n 0 Information (preceeded by length-1)
208 | n 1 Command (preceeded by length-1)
209 |
210 | Tnc to Host
211 | -----------
212 | CHANNEL CODE DESCRIPTION
213 | ------- ---- -----------
214 | n 0 Success (nothing follows)
215 | n 1 Success (message follows, null terminated)
216 | n 2 Failure (message follows, null terminated)
217 | n 3 Link Status (null terminated)
218 | n 4 Monitor Header (null terminated)
219 | n 5 Monitor Header (null terminated)
220 | n 6 Monitor Information (preceeded by length-1)
221 | n 7 Connect Information (preceeded by length-1)
222 |
223 |
--------------------------------------------------------------------------------
/ardop1ofdm/Nucleo_i2c.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "stm32f4xx_gpio.h"
4 | #include
5 |
6 | #include "ARDOPC.h"
7 |
8 | /* setup SCL and SDA pins
9 | * You can connect I2C1 to two different
10 | * pairs of pins:
11 | * 1. SCL on PB6 and SDA on PB7
12 | * 2. SCL on PB8 and SDA on PB9
13 | */
14 |
15 | void init_I2C1(void){
16 |
17 | GPIO_InitTypeDef GPIO_InitStruct;
18 | I2C_InitTypeDef I2C_InitStruct;
19 |
20 | // enable APB1 peripheral clock for I2C1
21 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
22 | // enable clock for SCL and SDA pins
23 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
24 |
25 | GPIO_StructInit(&GPIO_InitStruct);
26 | GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; // PB8 and PB9
27 | GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; // set pins to alternate function
28 | GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; // set GPIO speed
29 | GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; // set output to open drain --> the line has to be only pulled low, not driven high |
30 | GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; // enable pull up resistors <---------------------<-------------------------------<|
31 | GPIO_Init(GPIOB, &GPIO_InitStruct); // init GPIOB
32 |
33 | // Connect I2C1 pins to AF
34 | GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_I2C1); // SCL
35 | GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_I2C1); // SDA
36 |
37 | // configure I2C1
38 | I2C_StructInit(&I2C_InitStruct);
39 | I2C_InitStruct.I2C_ClockSpeed = 100000; // 100kHz(standard) vs 400
40 | I2C_InitStruct.I2C_Mode = I2C_Mode_I2C; // I2C mode
41 | I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; // 50% duty cycle --> standard
42 | I2C_InitStruct.I2C_OwnAddress1 = 0x00; // own address, not relevant in master mode
43 | I2C_InitStruct.I2C_Ack = I2C_Ack_Disable; // disable acknowledge when reading (can be changed later on)
44 | I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; // set address length to 7 bit addresses
45 | I2C_Init(I2C1, &I2C_InitStruct); // init I2C1
46 |
47 | // enable I2C1
48 | I2C_Cmd(I2C1, ENABLE); //sets PE bit in CR1, at end`
49 | }
50 | /* This function issues a start condition and2
51 | * transmits the slave address + R/W bit
52 | *
53 | * Parameters:
54 | * I2Cx --> the I2C peripheral e.g. I2C1
55 | * address --> the 7 bit slave address
56 | * direction --> the tranmission direction can be:
57 | * I2C_Direction_Tranmitter for Master transmitter mode
58 | * I2C_Direction_Receiver for Master receiver
59 | */
60 | void I2C_start(I2C_TypeDef* I2Cx, uint8_t address, uint8_t direction){
61 | // wait until I2C1 is not busy anymore
62 |
63 | while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));
64 |
65 | // Send I2C1 START condition
66 | I2C_GenerateSTART(I2Cx, ENABLE);
67 |
68 | // wait for I2C1 EV5 --> Slave has acknowledged start condition
69 | while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));
70 |
71 | // Send slave Address for write
72 | I2C_Send7bitAddress(I2Cx, address, direction);
73 |
74 | /* wait for I2C1 EV6, check if
75 | * either Slave has acknowledged Master transmitter or
76 | * Master receiver mode, depending on the transmission
77 | * direction
78 | */
79 | if(direction == I2C_Direction_Transmitter){
80 | while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
81 | }
82 | else if(direction == I2C_Direction_Receiver){
83 | while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
84 | }
85 | }
86 |
87 | /* This function transmits one byte to the slave device
88 | * Parameters:
89 | * I2Cx --> the I2C peripheral e.g. I2C1
90 | * data --> the data byte to be transmitted
91 | */
92 | void I2C_write(I2C_TypeDef* I2Cx, uint8_t data){
93 | I2C_SendData(I2Cx, data);
94 | // wait for I2C1 EV8_2 --> byte has been transmitted
95 | while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
96 | }
97 |
98 | /* This function reads one byte from the slave device
99 | * and acknowledges the byte (requests another byte)
100 | */
101 | uint8_t I2C_read_ack(I2C_TypeDef* I2Cx){
102 | // enable acknowledge of recieved data
103 | I2C_AcknowledgeConfig(I2Cx, ENABLE);
104 | // wait until one byte has been received
105 | while( !I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED) );
106 | // read data from I2C data register and return data byte
107 | uint8_t data = I2C_ReceiveData(I2Cx);
108 | return data;
109 | }
110 |
111 | /* This function reads one byte from the slave device
112 | * and doesn't acknowledge the recieved data
113 | */
114 | uint8_t I2C_read_nack(I2C_TypeDef* I2Cx){
115 | // disabe acknowledge of received data
116 | I2C_AcknowledgeConfig(I2Cx, DISABLE);
117 | // wait until one byte has been received
118 | while( !I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED) );
119 | // read data from I2C data register and return data byte
120 | uint8_t data = I2C_ReceiveData(I2Cx);
121 | return data;
122 | }
123 |
124 | /* This funtion issues a stop condition and therefore
125 | * releases the bus
126 | */
127 | void I2C_stop(I2C_TypeDef* I2Cx){
128 | // Send I2C1 STOP Condition
129 | I2C_GenerateSTOP(I2Cx, ENABLE);
130 | }
131 |
132 |
133 | // Display Interface
134 |
135 | unsigned short i2c_ADDR = 0x4E; // Display address
136 |
137 | // COMMANDOS
138 | #define LCD_CLEARDISPLAY 0x01
139 | #define LCD_RETURNHOME 0x02
140 | #define LCD_ENTRYMODESET 0x04
141 | #define LCD_DISPLAYCONTROL 0x08
142 | #define LCD_CURSORSHIFT 0x10
143 | #define LCD_FUNCTIONSET 0x20
144 | #define LCD_SETCGRAMADDR 0x40
145 | #define LCD_SETDDRAMADDR 0x80
146 | #define LCD_BACKLIGHT 0x08
147 | #define LCD_NOBACKLIGHT 0x00
148 |
149 | // FLAGS PARA EL MODO DE ENTRADA
150 | #define LCD_ENTRYRIGHT 0x00
151 | #define LCD_ENTRYLEFT 0x02
152 | #define LCD_ENTRYSHIFTINCREMENT 0x01
153 | #define LCD_ENTRYSHIFTDECREMENT 0x00
154 |
155 | // FLAGS DE DISPLAY CONTROL
156 | #define LCD_DISPLAYON 0x04
157 | #define LCD_DISPLAYOFF 0x00
158 | #define LCD_CURSORON 0x02
159 | #define LCD_CURSOROFF 0x00
160 | #define LCD_BLINKON 0x01
161 | #define LCD_BLINKOFF 0x00
162 |
163 | // FLAGS DE FUNCTION SET
164 | #define LCD_8BITMODE 0x10
165 | #define LCD_4BITMODE 0x00
166 | #define LCD_2LINE 0x08
167 | #define LCD_1LINE 0x00
168 | #define LCD_5x10DOTS 0x04
169 | #define LCD_5x8DOTS 0x00
170 |
171 |
172 | #define LCD_EN 0x04 // Enable bit
173 | #define LCD_RW 0x02 // Read/Write bit
174 | #define LCD_RS 0x01 // Register select bit
175 |
176 |
177 |
178 | void clear(int i2cfile);
179 | void home(int i2cfile);
180 | void locate(int i2cfile, int row, int col);
181 | void print(int i2cfile, const char *text);
182 | int initialize(const char *i2c_device, int addr);
183 | void finalize(int i2cfile);
184 |
185 | //#include
186 |
187 | int i2cfile;
188 |
189 | void usleep(float usec)
190 | {
191 | wait(usec/1000000);
192 | }
193 |
194 |
195 | void expanderWrite(int i2cfile, char value)
196 | {
197 | char buffer = value | LCD_BACKLIGHT;
198 |
199 | I2C_start(I2C1, i2c_ADDR, I2C_Direction_Transmitter);
200 | I2C_write(I2C1, buffer);
201 | I2C_stop(I2C1);
202 | }
203 |
204 | void pulseEnable(int i2cfile, char value)
205 | {
206 | expanderWrite(i2cfile, value | LCD_EN);
207 | // usleep(1);
208 |
209 | expanderWrite(i2cfile, value & ~LCD_EN);
210 | // usleep(50);
211 | }
212 |
213 | void write4bits(int i2cfile, char value)
214 | {
215 | expanderWrite(i2cfile, value);
216 | pulseEnable(i2cfile, value);
217 | }
218 |
219 | void i2csend(int i2cfile, char value, char mode)
220 | {
221 | char h = value & 0xf0;
222 | char l = (value << 4) & 0xf0;
223 | write4bits(i2cfile, h | mode);
224 | write4bits(i2cfile, l | mode);
225 | }
226 |
227 | void command(int i2cfile, char value)
228 | {
229 | i2csend(i2cfile, value, 0);
230 | }
231 |
232 | int initialize(const char *i2c_device, int addr)
233 | {
234 | expanderWrite(i2cfile, LCD_BACKLIGHT);
235 |
236 | write4bits(i2cfile, 0x03 << 4);
237 |
238 | write4bits(i2cfile, 0x30);
239 | // usleep(4500);
240 | write4bits(i2cfile, 0x30);
241 | // usleep(150);
242 |
243 | write4bits(i2cfile, 0x20);
244 |
245 | // Set 2 Lines
246 |
247 | command(i2cfile, LCD_FUNCTIONSET | LCD_2LINE);
248 | command(i2cfile, LCD_DISPLAYCONTROL | LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF);
249 | clear(i2cfile);
250 | command(i2cfile, LCD_ENTRYMODESET | LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT);
251 | home(i2cfile);
252 |
253 | return i2cfile;
254 | }
255 |
256 | void finalize(int i2cfile)
257 | {
258 | }
259 |
260 | void clear(int i2cfile)
261 | {
262 | command(i2cfile, LCD_CLEARDISPLAY);
263 | }
264 |
265 | void home(int i2cfile)
266 | {
267 | command(i2cfile, LCD_RETURNHOME);
268 | }
269 |
270 | void locate(int i2cfile, int row, int col)
271 | {
272 | static int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
273 | command(i2cfile, LCD_SETDDRAMADDR | ((col % 16) + row_offsets[row % 2]));
274 | }
275 |
276 | // Point to Character Generator RAM
277 |
278 | void locateCG(int i2cfile, int n)
279 | {
280 | command(i2cfile, LCD_SETCGRAMADDR + n);
281 | }
282 |
283 |
284 | void print(int i2cfile, const char *text)
285 | {
286 | int i = 0;
287 | int tlen = strlen(text);
288 | for (i = 0; i < tlen; i++)
289 | i2csend(i2cfile, text[i], LCD_RS);
290 | }
291 |
292 | // Signal Level uses a Half block defined as char 1
293 | // Use 5 chars to show 10 levels
294 |
295 | const char level[10][5] = {
296 | {1,32,32,32,32},
297 | {255,32,32,32,32},
298 | {255,1,32,32,32},
299 | {255,255,32,32,32},
300 | {255,255,1,32,32},
301 | {255,255,255,32,32},
302 | {255,255,255,1,32},
303 | {255,255,255,255,32},
304 | {255,255,255,255,1},
305 | {255,255,255,255,255}};
306 |
307 |
308 | void displayState()
309 | {
310 | // printtick("enter displaystate");
311 | locate(i2cfile, 1, 0);
312 | print(i2cfile, " ");
313 | locate(i2cfile, 1, 0);
314 | print(i2cfile, ARDOPStates[ProtocolState]);
315 | // printtick("exit displaystate");
316 | }
317 |
318 |
319 | void displayLevel(int max)
320 | {
321 | int i, j;
322 |
323 | i = max/3276;
324 |
325 | if (i > 9)
326 | i = 9;
327 |
328 | locate(i2cfile, 1, 11);
329 |
330 | for (j= 0; j < 5; j++)
331 | {
332 | i2csend(i2cfile, level[i][j], LCD_RS);
333 | }
334 | }
335 |
336 | void displayCall(int dirn, char * Call)
337 | {
338 | char paddedcall[12] = " ";
339 |
340 | paddedcall[0] = dirn;
341 | memcpy(paddedcall+1, Call, strlen(Call));
342 |
343 | locate(i2cfile, 0, 0);
344 | print(i2cfile, paddedcall);
345 | }
346 |
347 |
348 | void initdisplay()
349 | {
350 | i2cfile = initialize("/dev/i2c-1", 0X27);
351 |
352 | Debugprintf("Returned from Init");
353 |
354 | // Set font for half bar display for sig level
355 |
356 | locateCG(i2cfile, 0);
357 | print(i2cfile, "\x7\x7\x7\x7\x7\x7\x7\x7");
358 | print(i2cfile, "\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c");
359 | }
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
--------------------------------------------------------------------------------
/ardop1ofdm/KISSModule.c:
--------------------------------------------------------------------------------
1 | //
2 | // KISS Code for ARDOPC.
3 | //
4 | // Allows Packet modes and ARDOP to coexist in same program
5 | // Mainly for Teensy Version.
6 | //
7 | // Teensy will probably only support KISS over i2c,
8 | // but for testing Windows version uses a real com port
9 |
10 | // New idea is to support via SCS Host Channel 250, but will
11 | // probably leave serial/i2c support in
12 |
13 | // Now supports KISS over SCS Channel 250 or a KISS over TCP Connection
14 |
15 |
16 |
17 | #ifdef WIN32
18 | #define _CRT_SECURE_NO_DEPRECATE
19 | #define _USE_32BIT_TIME_T
20 |
21 | #include
22 | #include
23 | #else
24 | #define HANDLE int
25 | #define SOCKET int
26 | #include
27 | #ifndef TEENSY
28 | #include
29 | #endif
30 | #endif
31 |
32 | #include "ARDOPC.h"
33 |
34 | #define FEND 0xC0
35 | #define FESC 0xDB
36 | #define TFEND 0xDC
37 | #define TFESC 0xDD
38 |
39 |
40 | HANDLE OpenCOMPort(VOID * pPort, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet, int Stopbits);
41 | int ReadCOMBlock(HANDLE fd, char * Block, int MaxLength);
42 | VOID ProcessKISSBlock(UCHAR * KISSBUFFER, int Len);
43 | VOID EncodePacket(UCHAR * Data, int Len);
44 | VOID ProcessKISSBytes(UCHAR * RXBUFFER, int Read);
45 | void PacketStartTX();
46 | VOID EmCRCStuffAndSend(UCHAR * Msg, int Len);
47 | VOID ProcessKISSControlFrame();
48 | VOID ptkSessionBG();
49 |
50 |
51 |
52 | extern HANDLE hDevice;
53 |
54 | char KISSPORTNAME[80] = ""; // for now just support over Host Interface;
55 |
56 | HANDLE hControl;
57 |
58 | typedef struct _SERIAL_STATUS {
59 | unsigned long Errors;
60 | unsigned long HoldReasons;
61 | unsigned long AmountInInQueue;
62 | unsigned long AmountInOutQueue;
63 | BOOL EofReceived;
64 | BOOL WaitForImmediate;
65 | } SERIAL_STATUS,*PSERIAL_STATUS;
66 |
67 | // Buffers for KISS frames to and from the host. Uses cyclic buffers
68 | // Size must be modulo 2 so we can AND with mask to make cyclic
69 |
70 | #define KISSBUFFERSIZE 4096
71 | #define KISSBUFFERMASK 4095
72 |
73 |
74 | // KISS bytes from a serial, i2c or Host Mode link are placed in
75 | // the cyclic TX buffer as received. As we can only transmit complete
76 | // packets, we need some indicator of how many packets there are
77 | // in the buffer. Or maybe just how many FENDS, especially if
78 | // we remove any extra ones (just leave one at end of frame)
79 |
80 |
81 | UCHAR KISSRXBUFF[KISSBUFFERSIZE]; // Host to RF
82 | int KRXPutPtr = 0;
83 | int KRXGetPtr = 0;
84 | int FENDCount = 0;
85 |
86 |
87 | UCHAR KISSTXBUFF[KISSBUFFERSIZE]; // RF to Host
88 | int KTXPutPtr = 0;
89 | int KTXGetPtr = 0;
90 |
91 |
92 | UCHAR KISSBUFFER[500]; // Long enough for stuffed KISS frame
93 | UCHAR * RXMPTR = &KISSBUFFER[0];
94 | int KISSLength = 0;
95 |
96 | BOOL ESCFlag = FALSE;
97 |
98 | HANDLE KISSHandle = 0;
99 |
100 | extern BOOL PacketHost;
101 |
102 | int TXDelay = 500;
103 |
104 | extern SOCKET PktSock;
105 | extern BOOL PKTCONNECTED;
106 |
107 | BOOL KISSInit()
108 | {
109 | // char * Baud = strlop(KISSPORTNAME, ',');
110 |
111 | #ifdef WIN32
112 |
113 | if (KISSPORTNAME[0])
114 | KISSHandle = OpenCOMPort(KISSPORTNAME, 19200, FALSE, FALSE, FALSE, 0);
115 |
116 | if (KISSHandle)
117 | WriteDebugLog(LOGALERT, "KISS interface Using port %s", KISSPORTNAME);
118 | #endif
119 |
120 | return TRUE;
121 | }
122 |
123 | VOID KISSPoll()
124 | {
125 | #ifdef WIN32
126 |
127 | unsigned long Read;
128 | unsigned char RXBuffer[512];
129 |
130 | if (KISSHandle == NULL)
131 | return;
132 |
133 | Read = ReadCOMBlock(KISSHandle, RXBuffer, 512);
134 |
135 | if (Read == 0)
136 | return;
137 |
138 | ProcessKISSBytes(RXBuffer, Read);
139 | #endif
140 | }
141 | VOID ProcessPacketBytes(UCHAR * Buffer, int Read)
142 | {
143 | // Called when frame received on TCP Packet Connection. Could be for
144 | // KISS or ARDOP Packet Session mode
145 |
146 | // Assumes that Complete KISS or Host packet will be received (pretty safe
147 | // with TCP (I hope!)
148 |
149 | // But could be more than one in buffer
150 |
151 | if (Buffer[0] == 192)
152 | ProcessKISSBytes(Buffer, Read);
153 | else
154 | {
155 | while (Read > 0)
156 | {
157 | int Used = Buffer[2] + 4;
158 | ProcessPacketHostBytes(Buffer, Read);
159 | Read -= Used;
160 |
161 | if (Read > 0)
162 | {
163 | memmove(Buffer, &Buffer[Used], Read);
164 | }
165 | }
166 | }
167 | }
168 |
169 | VOID ProcessKISSBytes(UCHAR * RXBuffer, int Read)
170 | {
171 | // Store in cyclic buffer, counting FENDS so we know if we
172 | // have a full frame in the buffer
173 |
174 | UCHAR c;
175 |
176 | WriteDebugLog(LOGALERT, "Queuing %d Packet Bytes", Read);
177 |
178 |
179 | while (Read--)
180 | {
181 | c = *(RXBuffer++);
182 |
183 | if (c == FEND)
184 | FENDCount++;
185 |
186 | KISSRXBUFF[KRXPutPtr++] = c;
187 | KRXPutPtr &= KISSBUFFERMASK;
188 |
189 | if (KRXPutPtr == KRXGetPtr) // should never happen, but nasty if it does
190 | FENDCount = 0; // Buffer is now empty
191 | }
192 | }
193 |
194 |
195 | VOID ProcessKISSByte(UCHAR c)
196 | {
197 | // Store in cyclic buffer, counting FENDS so we know if we
198 | // have a full frame in the buffer
199 |
200 | if (c == FEND)
201 | FENDCount++;
202 |
203 | KISSRXBUFF[KRXPutPtr++] = c;
204 | KRXPutPtr &= KISSBUFFERMASK;
205 |
206 | if (KRXPutPtr == KRXGetPtr) // should never happen, but nasty if it does
207 | FENDCount = 0; // Buffer is now empty
208 | }
209 |
210 |
211 | BOOL GetNextKISSFrame()
212 | {
213 | // Called to get a frame to send, either before starting or
214 | // when current frame has been sent (for back to back sends)
215 |
216 | unsigned char c;
217 | UCHAR * RXMPTR;
218 |
219 | ptkSessionBG(); // See if any session events to process
220 |
221 | if (KRXPutPtr == KRXGetPtr) // Nothing to send
222 | {
223 | ptkSessionBG(); // See if any session events to process
224 |
225 | if (KRXPutPtr == KRXGetPtr) // Still nothing to send
226 | return FALSE; // Buffer empty
227 |
228 | }
229 | if (FENDCount < 2)
230 | return FALSE; // Not a complete KISS frame
231 |
232 | if (KISSRXBUFF[KRXGetPtr++] != FEND)
233 | {
234 | // First char should always be FEND. If not Buffer has
235 | // wrapped. Remove the partial frame and discard
236 |
237 | while (KRXPutPtr != KRXGetPtr)
238 | {
239 | if (KISSRXBUFF[KRXGetPtr++] == FEND)
240 | {
241 | // Found a FEND.
242 |
243 | KRXGetPtr &= KISSBUFFERMASK;
244 | FENDCount --;
245 |
246 | // Next should also be a FEND, but can check next time round
247 |
248 | // As this shouldn't happen often, just exit and get frame next time
249 |
250 | return FALSE;
251 | }
252 | }
253 |
254 | // no FENDS in buffer!!!
255 |
256 | FENDCount = 0; // Buffer is now empty
257 | return FALSE;
258 | }
259 |
260 | // First char is a FEND, and get pointer points to next char
261 |
262 | RXMPTR = &KISSBUFFER[0]; // Initialise buffer pointer
263 |
264 | KRXGetPtr &= KISSBUFFERMASK;
265 | FENDCount --;
266 |
267 | while (KRXPutPtr != KRXGetPtr)
268 | {
269 | c = KISSRXBUFF[KRXGetPtr++];
270 | KRXGetPtr &= KISSBUFFERMASK;
271 |
272 | if (ESCFlag)
273 | {
274 | //
275 | // FESC received - next should be TFESC or TFEND
276 |
277 | ESCFlag = FALSE;
278 |
279 | if (c == TFESC)
280 | c = FESC;
281 |
282 | if (c == TFEND)
283 | c = FEND;
284 | }
285 | else
286 | {
287 | switch (c)
288 | {
289 | case FEND:
290 |
291 | //
292 | // Either start of message or message complete
293 | //
294 |
295 | if (RXMPTR == &KISSBUFFER[0])
296 | {
297 | // Start of Message. Shouldn't Happen
298 | FENDCount--;
299 | continue;
300 | }
301 |
302 | FENDCount--;
303 | KISSLength = RXMPTR - &KISSBUFFER[0];
304 |
305 | // Process Control Frames here
306 |
307 | if (KISSBUFFER[0] != 0 && KISSBUFFER[0] != 6 && KISSBUFFER[0] != 12)
308 | {
309 | ProcessKISSControlFrame();
310 | return FALSE;
311 | }
312 |
313 | return TRUE; // Got complete frame in KISSBUFFER
314 |
315 | case FESC:
316 |
317 | ESCFlag = TRUE;
318 | continue;
319 |
320 | }
321 | }
322 |
323 | //
324 | // Ok, a normal char
325 | //
326 |
327 | *(RXMPTR++) = c;
328 |
329 | if (RXMPTR == &KISSBUFFER[499])
330 | RXMPTR--; // Protect Buffer
331 | }
332 |
333 | // We shouldnt get here, as it means FENDCOUNT is wrong. Reset it
334 |
335 | FENDCount = 0;
336 | return FALSE;
337 | }
338 |
339 |
340 | // Called by SCS Host Interface
341 |
342 | BOOL CheckKISS(UCHAR * SCSReply)
343 | {
344 | int Length = KTXPutPtr - KTXGetPtr;
345 | int n;
346 | int get = KTXGetPtr;
347 |
348 | if (Length == 0)
349 | return FALSE;
350 |
351 | if (Length < 0)
352 | Length += KISSBUFFERSIZE;
353 |
354 | // Return up to 256 chars
355 |
356 | if (Length > 256)
357 | Length = 256;
358 |
359 | n = 0;
360 |
361 | while (n < Length)
362 | {
363 | SCSReply[n++ + 5] = KISSTXBUFF[get++];
364 | get &= KISSBUFFERMASK;
365 | }
366 |
367 | KTXGetPtr = get;
368 |
369 | SCSReply[2] = 250;
370 | SCSReply[3] = 7;
371 | SCSReply[4] = Length - 1;
372 |
373 | EmCRCStuffAndSend(SCSReply, Length + 5);
374 | return TRUE;
375 | }
376 |
377 |
378 | VOID ProcessKISSControlFrame()
379 | {
380 | }
381 |
382 | VOID SendAckModeAck()
383 | {
384 | KISSTXBUFF[KTXPutPtr++] = FEND;
385 | KTXPutPtr &= KISSBUFFERMASK;
386 | KISSTXBUFF[KTXPutPtr++] = 12; // AckMode opcode
387 | KTXPutPtr &= KISSBUFFERMASK;
388 | KISSTXBUFF[KTXPutPtr++] = KISSBUFFER[1];
389 | KTXPutPtr &= KISSBUFFERMASK;
390 | KISSTXBUFF[KTXPutPtr++] = KISSBUFFER[2];
391 | KTXPutPtr &= KISSBUFFERMASK;
392 | KISSTXBUFF[KTXPutPtr++] = FEND;
393 |
394 | // If using KISS over TCP, send it
395 |
396 | #ifndef TEENSY
397 |
398 | // If Using TCP, send it
399 |
400 | if (pktport)
401 | {
402 | if (PKTCONNECTED)
403 | send(PktSock, KISSTXBUFF, KTXPutPtr, 0);
404 |
405 | KTXPutPtr = 0;
406 | }
407 |
408 | #endif
409 |
410 | }
411 |
412 | void SendFrametoHost(unsigned char *data, unsigned dlen)
413 | {
414 | KISSTXBUFF[KTXPutPtr++] = FEND;
415 | KTXPutPtr &= KISSBUFFERMASK;
416 |
417 | KISSTXBUFF[KTXPutPtr++] = 0; // Data
418 | KTXPutPtr &= KISSBUFFERMASK;
419 |
420 | for (; dlen > 0; dlen--, data++)
421 | {
422 | if (*data == FEND)
423 | {
424 | KISSTXBUFF[KTXPutPtr++] = FESC;
425 | KTXPutPtr &= KISSBUFFERMASK;
426 | KISSTXBUFF[KTXPutPtr++] = TFEND;
427 | KTXPutPtr &= KISSBUFFERMASK;
428 | }
429 | else if (*data == FESC)
430 | {
431 | KISSTXBUFF[KTXPutPtr++] = FESC;
432 | KTXPutPtr &= KISSBUFFERMASK;
433 | KISSTXBUFF[KTXPutPtr++] = TFESC;
434 | KTXPutPtr &= KISSBUFFERMASK;
435 | }
436 | else
437 | {
438 | KISSTXBUFF[KTXPutPtr++] = *data;
439 | KTXPutPtr &= KISSBUFFERMASK;
440 | }
441 | }
442 |
443 | KISSTXBUFF[KTXPutPtr++] = FEND;
444 | KTXPutPtr &= KISSBUFFERMASK;
445 |
446 | #ifndef TEENSY
447 |
448 | // If Using TCP, send it
449 |
450 | if (pktport)
451 | {
452 | if (PKTCONNECTED)
453 | send(PktSock, KISSTXBUFF, KTXPutPtr, 0);
454 |
455 | KTXPutPtr = 0;
456 | }
457 |
458 | #endif
459 | }
460 |
461 |
462 |
463 |
--------------------------------------------------------------------------------
/ardop2ofdm/KISSModule.c:
--------------------------------------------------------------------------------
1 | //
2 | // KISS Code for ARDOPC.
3 | //
4 | // Allows Packet modes and ARDOP to coexist in same program
5 | // Mainly for Teensy Version.
6 | //
7 | // Teensy will probably only support KISS over i2c,
8 | // but for testing Windows version uses a real com port
9 |
10 | // New idea is to support via SCS Host Channel 250, but will
11 | // probably leave serial/i2c support in
12 |
13 | // Now supports KISS over SCS Channel 250 or a KISS over TCP Connection
14 |
15 |
16 |
17 | #ifdef WIN32
18 | #define _CRT_SECURE_NO_DEPRECATE
19 | #define _USE_32BIT_TIME_T
20 |
21 | #include
22 | #include
23 | #else
24 | #define HANDLE int
25 | #define SOCKET int
26 | #include
27 | #ifndef TEENSY
28 | #include
29 | #endif
30 | #endif
31 |
32 | #include "ARDOPC.h"
33 |
34 | #define FEND 0xC0
35 | #define FESC 0xDB
36 | #define TFEND 0xDC
37 | #define TFESC 0xDD
38 |
39 |
40 | HANDLE OpenCOMPort(VOID * pPort, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet, int Stopbits);
41 | int ReadCOMBlock(HANDLE fd, char * Block, int MaxLength);
42 | VOID ProcessKISSBlock(UCHAR * KISSBUFFER, int Len);
43 | VOID EncodePacket(UCHAR * Data, int Len);
44 | VOID ProcessKISSBytes(UCHAR * RXBUFFER, int Read);
45 | void PacketStartTX();
46 | VOID EmCRCStuffAndSend(UCHAR * Msg, int Len);
47 | VOID ProcessKISSControlFrame();
48 | VOID ptkSessionBG();
49 |
50 |
51 |
52 | extern HANDLE hDevice;
53 |
54 | char KISSPORTNAME[80] = ""; // for now just support over Host Interface;
55 |
56 | HANDLE hControl;
57 |
58 | typedef struct _SERIAL_STATUS {
59 | unsigned long Errors;
60 | unsigned long HoldReasons;
61 | unsigned long AmountInInQueue;
62 | unsigned long AmountInOutQueue;
63 | BOOL EofReceived;
64 | BOOL WaitForImmediate;
65 | } SERIAL_STATUS,*PSERIAL_STATUS;
66 |
67 | // Buffers for KISS frames to and from the host. Uses cyclic buffers
68 | // Size must be modulo 2 so we can AND with mask to make cyclic
69 |
70 | #define KISSBUFFERSIZE 4096
71 | #define KISSBUFFERMASK 4095
72 |
73 |
74 | // KISS bytes from a serial, i2c or Host Mode link are placed in
75 | // the cyclic TX buffer as received. As we can only transmit complete
76 | // packets, we need some indicator of how many packets there are
77 | // in the buffer. Or maybe just how many FENDS, especially if
78 | // we remove any extra ones (just leave one at end of frame)
79 |
80 |
81 | UCHAR KISSRXBUFF[KISSBUFFERSIZE]; // Host to RF
82 | int KRXPutPtr = 0;
83 | int KRXGetPtr = 0;
84 | int FENDCount = 0;
85 |
86 |
87 | UCHAR KISSTXBUFF[KISSBUFFERSIZE]; // RF to Host
88 | int KTXPutPtr = 0;
89 | int KTXGetPtr = 0;
90 |
91 |
92 | UCHAR KISSBUFFER[500]; // Long enough for stuffed KISS frame
93 | UCHAR * RXMPTR = &KISSBUFFER[0];
94 | int KISSLength = 0;
95 |
96 | BOOL ESCFlag = FALSE;
97 |
98 | HANDLE KISSHandle = 0;
99 |
100 | extern BOOL PacketHost;
101 |
102 | int TXDelay = 500;
103 |
104 | extern SOCKET PktSock;
105 | extern BOOL PKTCONNECTED;
106 |
107 | BOOL KISSInit()
108 | {
109 | // char * Baud = strlop(KISSPORTNAME, ',');
110 |
111 | #ifdef WIN32
112 |
113 | if (KISSPORTNAME[0])
114 | KISSHandle = OpenCOMPort(KISSPORTNAME, 19200, FALSE, FALSE, FALSE, 0);
115 |
116 | if (KISSHandle)
117 | WriteDebugLog(LOGALERT, "KISS interface Using port %s", KISSPORTNAME);
118 | #endif
119 |
120 | return TRUE;
121 | }
122 |
123 | VOID KISSPoll()
124 | {
125 | #ifdef WIN32
126 |
127 | unsigned long Read;
128 | unsigned char RXBuffer[512];
129 |
130 | if (KISSHandle == NULL)
131 | return;
132 |
133 | Read = ReadCOMBlock(KISSHandle, RXBuffer, 512);
134 |
135 | if (Read == 0)
136 | return;
137 |
138 | ProcessKISSBytes(RXBuffer, Read);
139 | #endif
140 | }
141 | VOID ProcessPacketBytes(UCHAR * Buffer, int Read)
142 | {
143 | // Called when frame received on TCP Packet Connection. Could be for
144 | // KISS or ARDOP Packet Session mode
145 |
146 | // Assumes that Complete KISS or Host packet will be received (pretty safe
147 | // with TCP (I hope!)
148 |
149 | // But could be more than one in buffer
150 |
151 | if (Buffer[0] == 192)
152 | ProcessKISSBytes(Buffer, Read);
153 | else
154 | {
155 | while (Read > 0)
156 | {
157 | int Used = Buffer[2] + 4;
158 | ProcessPacketHostBytes(Buffer, Read);
159 | Read -= Used;
160 |
161 | if (Read > 0)
162 | {
163 | memmove(Buffer, &Buffer[Used], Read);
164 | }
165 | }
166 | }
167 | }
168 |
169 | VOID ProcessKISSBytes(UCHAR * RXBuffer, int Read)
170 | {
171 | // Store in cyclic buffer, counting FENDS so we know if we
172 | // have a full frame in the buffer
173 |
174 | UCHAR c;
175 |
176 | WriteDebugLog(LOGALERT, "Queuing %d Packet Bytes", Read);
177 |
178 |
179 | while (Read--)
180 | {
181 | c = *(RXBuffer++);
182 |
183 | if (c == FEND)
184 | FENDCount++;
185 |
186 | KISSRXBUFF[KRXPutPtr++] = c;
187 | KRXPutPtr &= KISSBUFFERMASK;
188 |
189 | if (KRXPutPtr == KRXGetPtr) // should never happen, but nasty if it does
190 | FENDCount = 0; // Buffer is now empty
191 | }
192 | }
193 |
194 |
195 | VOID ProcessKISSByte(UCHAR c)
196 | {
197 | // Store in cyclic buffer, counting FENDS so we know if we
198 | // have a full frame in the buffer
199 |
200 | if (c == FEND)
201 | FENDCount++;
202 |
203 | KISSRXBUFF[KRXPutPtr++] = c;
204 | KRXPutPtr &= KISSBUFFERMASK;
205 |
206 | if (KRXPutPtr == KRXGetPtr) // should never happen, but nasty if it does
207 | FENDCount = 0; // Buffer is now empty
208 | }
209 |
210 |
211 | BOOL GetNextKISSFrame()
212 | {
213 | // Called to get a frame to send, either before starting or
214 | // when current frame has been sent (for back to back sends)
215 |
216 | unsigned char c;
217 | UCHAR * RXMPTR;
218 |
219 | ptkSessionBG(); // See if any session events to process
220 |
221 | if (KRXPutPtr == KRXGetPtr) // Nothing to send
222 | {
223 | ptkSessionBG(); // See if any session events to process
224 |
225 | if (KRXPutPtr == KRXGetPtr) // Still nothing to send
226 | return FALSE; // Buffer empty
227 |
228 | }
229 | if (FENDCount < 2)
230 | return FALSE; // Not a complete KISS frame
231 |
232 | if (KISSRXBUFF[KRXGetPtr++] != FEND)
233 | {
234 | // First char should always be FEND. If not Buffer has
235 | // wrapped. Remove the partial frame and discard
236 |
237 | while (KRXPutPtr != KRXGetPtr)
238 | {
239 | if (KISSRXBUFF[KRXGetPtr++] == FEND)
240 | {
241 | // Found a FEND.
242 |
243 | KRXGetPtr &= KISSBUFFERMASK;
244 | FENDCount --;
245 |
246 | // Next should also be a FEND, but can check next time round
247 |
248 | // As this shouldn't happen often, just exit and get frame next time
249 |
250 | return FALSE;
251 | }
252 | }
253 |
254 | // no FENDS in buffer!!!
255 |
256 | FENDCount = 0; // Buffer is now empty
257 | return FALSE;
258 | }
259 |
260 | // First char is a FEND, and get pointer points to next char
261 |
262 | RXMPTR = &KISSBUFFER[0]; // Initialise buffer pointer
263 |
264 | KRXGetPtr &= KISSBUFFERMASK;
265 | FENDCount --;
266 |
267 | while (KRXPutPtr != KRXGetPtr)
268 | {
269 | c = KISSRXBUFF[KRXGetPtr++];
270 | KRXGetPtr &= KISSBUFFERMASK;
271 |
272 | if (ESCFlag)
273 | {
274 | //
275 | // FESC received - next should be TFESC or TFEND
276 |
277 | ESCFlag = FALSE;
278 |
279 | if (c == TFESC)
280 | c = FESC;
281 |
282 | if (c == TFEND)
283 | c = FEND;
284 | }
285 | else
286 | {
287 | switch (c)
288 | {
289 | case FEND:
290 |
291 | //
292 | // Either start of message or message complete
293 | //
294 |
295 | if (RXMPTR == &KISSBUFFER[0])
296 | {
297 | // Start of Message. Shouldn't Happen
298 | FENDCount--;
299 | continue;
300 | }
301 |
302 | FENDCount--;
303 | KISSLength = RXMPTR - &KISSBUFFER[0];
304 |
305 | // Process Control Frames here
306 |
307 | if (KISSBUFFER[0] != 0 && KISSBUFFER[0] != 6 && KISSBUFFER[0] != 12)
308 | {
309 | ProcessKISSControlFrame();
310 | return FALSE;
311 | }
312 |
313 | return TRUE; // Got complete frame in KISSBUFFER
314 |
315 | case FESC:
316 |
317 | ESCFlag = TRUE;
318 | continue;
319 |
320 | }
321 | }
322 |
323 | //
324 | // Ok, a normal char
325 | //
326 |
327 | *(RXMPTR++) = c;
328 |
329 | if (RXMPTR == &KISSBUFFER[499])
330 | RXMPTR--; // Protect Buffer
331 | }
332 |
333 | // We shouldnt get here, as it means FENDCOUNT is wrong. Reset it
334 |
335 | FENDCount = 0;
336 | return FALSE;
337 | }
338 |
339 |
340 | // Called by SCS Host Interface
341 |
342 | BOOL CheckKISS(UCHAR * SCSReply)
343 | {
344 | int Length = KTXPutPtr - KTXGetPtr;
345 | int n;
346 | int get = KTXGetPtr;
347 |
348 | if (Length == 0)
349 | return FALSE;
350 |
351 | if (Length < 0)
352 | Length += KISSBUFFERSIZE;
353 |
354 | // Return up to 256 chars
355 |
356 | if (Length > 256)
357 | Length = 256;
358 |
359 | n = 0;
360 |
361 | while (n < Length)
362 | {
363 | SCSReply[n++ + 5] = KISSTXBUFF[get++];
364 | get &= KISSBUFFERMASK;
365 | }
366 |
367 | KTXGetPtr = get;
368 |
369 | SCSReply[2] = 250;
370 | SCSReply[3] = 7;
371 | SCSReply[4] = Length - 1;
372 |
373 | EmCRCStuffAndSend(SCSReply, Length + 5);
374 | return TRUE;
375 | }
376 |
377 |
378 | VOID ProcessKISSControlFrame()
379 | {
380 | }
381 |
382 | VOID SendAckModeAck()
383 | {
384 | KISSTXBUFF[KTXPutPtr++] = FEND;
385 | KTXPutPtr &= KISSBUFFERMASK;
386 | KISSTXBUFF[KTXPutPtr++] = 12; // AckMode opcode
387 | KTXPutPtr &= KISSBUFFERMASK;
388 | KISSTXBUFF[KTXPutPtr++] = KISSBUFFER[1];
389 | KTXPutPtr &= KISSBUFFERMASK;
390 | KISSTXBUFF[KTXPutPtr++] = KISSBUFFER[2];
391 | KTXPutPtr &= KISSBUFFERMASK;
392 | KISSTXBUFF[KTXPutPtr++] = FEND;
393 |
394 | // If using KISS over TCP, send it
395 |
396 | #ifndef TEENSY
397 |
398 | // If Using TCP, send it
399 |
400 | if (pktport)
401 | {
402 | if (PKTCONNECTED)
403 | send(PktSock, KISSTXBUFF, KTXPutPtr, 0);
404 |
405 | KTXPutPtr = 0;
406 | }
407 |
408 | #endif
409 |
410 | }
411 |
412 | void SendFrametoHost(unsigned char *data, unsigned dlen)
413 | {
414 | KISSTXBUFF[KTXPutPtr++] = FEND;
415 | KTXPutPtr &= KISSBUFFERMASK;
416 |
417 | KISSTXBUFF[KTXPutPtr++] = 0; // Data
418 | KTXPutPtr &= KISSBUFFERMASK;
419 |
420 | for (; dlen > 0; dlen--, data++)
421 | {
422 | if (*data == FEND)
423 | {
424 | KISSTXBUFF[KTXPutPtr++] = FESC;
425 | KTXPutPtr &= KISSBUFFERMASK;
426 | KISSTXBUFF[KTXPutPtr++] = TFEND;
427 | KTXPutPtr &= KISSBUFFERMASK;
428 | }
429 | else if (*data == FESC)
430 | {
431 | KISSTXBUFF[KTXPutPtr++] = FESC;
432 | KTXPutPtr &= KISSBUFFERMASK;
433 | KISSTXBUFF[KTXPutPtr++] = TFESC;
434 | KTXPutPtr &= KISSBUFFERMASK;
435 | }
436 | else
437 | {
438 | KISSTXBUFF[KTXPutPtr++] = *data;
439 | KTXPutPtr &= KISSBUFFERMASK;
440 | }
441 | }
442 |
443 | KISSTXBUFF[KTXPutPtr++] = FEND;
444 | KTXPutPtr &= KISSBUFFERMASK;
445 |
446 | #ifndef TEENSY
447 |
448 | // If Using TCP, send it
449 |
450 | if (pktport)
451 | {
452 | if (PKTCONNECTED)
453 | send(PktSock, KISSTXBUFF, KTXPutPtr, 0);
454 |
455 | KTXPutPtr = 0;
456 | }
457 |
458 | #endif
459 | }
460 |
461 |
462 |
463 |
--------------------------------------------------------------------------------