Repository navigation
Expand file tree
/
Copy pathSerialWombatUART.py
More file actions
586 lines (495 loc) · 24.5 KB
/
Copy pathSerialWombatUART.py
File metadata and controls
586 lines (495 loc) · 24.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
#pragma once
#include "Stream.h"
#include "SerialWombat.h"
"""! \file SerialWombatUART.h
"""
"""
Copyright 2020-2021 Broadwell Consulting Inc.
"Serial Wombat" is a registered trademark of Broadwell Consulting Inc. in
the United States. See SerialWombat.com for usage guidance.
Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
"""
import SerialWombat
from SerialWombatPin import SerialWombatPin
from SerialWombat import SW_LE16
from ArduinoFunctions import millis
import SerialWombatQueue
"""! @brief A class for the Serial Wombat 4B or SW18AB chips which creates an I2C to UART Bridge
This class allows use of the Serial Wombat 4B chips's internal UART hardware to send
and receive data at standard baud rates in 8-N-1 format.
A Tutorial video is avaialble:
@htmlonly
<iframe width="560" height="315" src="https://www.youtube.com/embed/C1FjcaiBYZs" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
@endhtmlonly
https://youtu.be/C1FjcaiBYZs
The class inherits from the Arduino Sream class, so functions such as println()
can be used once the UART is initialized.
The Serial Wombat 4B chip has a 64 byte transmit buffer and 128 byte receive buffer.
Therefore, up to 64 bytes can be sent to the SerialWombatUART at a time.
Attempts to send more than that will result in the write, print, etc command
blocking until space is available on the SerialWombatUART to buffer the data.
Received data is buffered on the Serial Wombat chip until it is retreived from the
host.
\warning Due to the overhead of querying and retreiving data from the SerialWombatUART,
data loss is likely when receiving streams of data greater than the buffer size at higher
baud rates.
To minimize this possiblity, read data frequently from the Serial Wombat chip, and set I2C frequency
to 400kHz to maximize throughput (SW4B only) .
This class can Send, Receive, or both. A single instance of this class is allowed per Serial Wombat 4B chip
due to the fact that it relies on the PIC16F15214's UART module, of which there is only one on the micro.
Two instances of this class can be used on the Serial Wombat 18AB chip by using the begin() call
which takes a hardware indicator of 0 or 1.
A full Serial Wombat packet send / receive sequence (8 bytes in each direction) over I2C is necessary to
query the status of the queues or to read or receive a byte of data.
The protocol becomes more efficient if multiple bytes are read or written using the readBytes or
write(const * buffer, size_t size) interfaces rather than read() or write( data).
The class must be assigned to a pin. This may be either the receive or transmit pin.
Pin 0 on the Serial Wombat 4B is suggested as a receive pin as it has only input capability.
Serial Wombat 18AB pins must be enhanced digital performance pins.
Available baud rates are:
- 300
- 1200
- 2400
- 4800
- 9600
- 19200
- 38400
- 57600
- 115200
"""
class SerialWombatUART (SerialWombatPin):
"""!
@brief Constructor for the SerialWombatUART class. Only one instance is allowed per SerialWombatChip 4B.
@param serialWombat The Serial Wombat chip on which the SerialWombatUART instance will run.
"""
def __init__(self,serial_wombat):
self._sw = serial_wombat
self._rxPin = 255
self._txPin = 255
self._baudMarker = 0
self.timeout = 5000
self._pinMode = SerialWombat.SerialWombatPinMode_t.PIN_MODE_UART_RX_TX
self._tx7Command = SerialWombat.SerialWombatCommands.COMMAND_UART0_TX_7BYTES
self._rx7Command = SerialWombat.SerialWombatCommands.COMMAND_UART0_RX_7BYTES
self._1ByteTransmissions = False
"""!
@brief Initalize the SerialWombatUART.
@param baudRate 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200
@param pin The pin that will host the state machine. This can be either the rxPin or txPin
@param rxPin The pin that will receive. All 4 pins on the SW4B may be used. 255 if no receive function is needed
@param txPin The pin that will transmit. Valid values for the SW4B are 1-3. 255 if no transmit function is needed
@param HWinterface 1 or 2 for HW UART 1 or 2
"""
def begin(self, baudRate, pin, rxPin, txPin, HWinterface = 1 ):
self._rxPin = rxPin
self._txPin = txPin
self._pin = pin
if (HWinterface == 2):
self._pinMode = SerialWombat.SerialWombatPinMode_t.PIN_MODE_UART1_RX_TX
self._tx7Command =SerialWombat.SerialWombatCommands.COMMAND_UART1_TX_7BYTES
self._rx7Command = SerialWombat.SerialWombatCommands.COMMAND_UART1_RX_7BYTES
elif (HWinterface == 1):
self._pinMode = 17 # PIN_MODE_UART_RX_TX
self._tx7Command = SerialWombat.SerialWombatCommands.COMMAND_UART0_TX_7BYTES
self._rx7Command = SerialWombat.SerialWombatCommands.COMMAND_UART0_RX_7BYTES
else:
return (-1)
if (baudRate == 300):
self._baudMarker = 0
elif (baudRate == 1200):
self._baudMarker = 1
elif (baudRate == 2400):
self._baudMarker = 2
elif (baudRate == 4800):
self._baudMarker = 3
elif (baudRate == 9600):
self._baudMarker = 4
elif (baudRate == 19200):
self._baudMarker = 5
elif (baudRate == 38400):
self._baudMarker = 6
elif (baudRate == 57600):
self._baudMarker = 7
else:
self._baudMarker = 8
tx = [ 200, self._pin,self._pinMode, self._baudMarker,self._rxPin,self._txPin,0x55, 0x55 ]
result,rx = self._sw.sendPacket(tx);
return result
"""!
@brief Queries the SerialWombatUART for number bytes available to read
@return Number of bytes available to read.
"""
def available(self):
tx = [ 201, self._pin,self._pinMode, 0,0x55,0x55,0x55,0x55 ]
result,rx = self._sw.sendPacket(tx)
return (rx[4])
"""!
@brief Reads a byte from the SerialWombatUART
@return A byte from 0-255, or -1 if no bytes were avaialble
"""
def read(self):
tx = [ 202, self._pin,self._pinMode, 1,0x55,0x55,0x55,0x55 ]
result,rx = self._sw.sendPacket(tx)
if (result < 0):
return -1
if (rx[3] != 0):
return (rx[4])
else:
return (-1)
"""!
@brief Discard all received bytes
"""
def flush(self):
tx = [ 200, self._pin,self._pinMode, self._baudMarker,self._rxPin,self._txPin,0x55, 0x55 ]
self._sw.sendPacket(tx)
"""!
@brief Query the SerialWombatUART for the next avaialble byte, but don't remove it from the queue
@return A byte from 0-255, or -1 if no bytes were avaialble
"""
def peek(self):
tx = [ 203, self._pin,self._pinMode,0x55,0x55,0x55,0x55,0x55 ]
result, rx = self._sw.sendPacket(tx)
if (result < 0):
return (-1)
if (rx[4] > 0):
return (rx[5])
else:
return (-1)
"""!
@brief Write a byte to the SerialWombatUART for Transmit
@param data Byte to write
@return Number of bytes written
This does not check to see if space is avaialble in order to improve perfomance
. This isn't an issue at high baud rates, as
overhead to transmit one byte at a time allows sufficent time for queuing data to be sent
by the UART. This could
be a problem at very low baud rates and high I2C bus speeds.
"""
def writebyte(self, data):
tx = [ 201, self._pin,self._pinMode,1,data,0x55,0x55,0x55 ]
self._sw.sendPacket(tx)
return (1)
"""!
@brief Write bytes to the SerialWombatUART for Transmit
@param buf An array of bytes to send
@param size the number of bytes to send
@return the number of bytes sent
This function queries the SerialWombatUART for avaialble TX
buffer space, and sends bytes as buffer space is avaialble.
If avaialable buffer space is not sufficient to send the entire
array then the function will block and continue trying until the
entire message has been sent to the SerialWombatUART transmit queue.
"""
def write(self, buf, size):
bytesAvailable = 0
bytesSent = 0
timeoutMillis = millis() + self.timeout
while( bytesSent < size ):
while (bytesAvailable < 4):
peektx = [ 203, self._pin,self._pinMode,0x55,0x55,0x55,0x55,0x55 ]
result, peekrx = self._sw.sendPacket(peektx)
bytesAvailable = peekrx[3]
if (timeoutMillis < millis()):
return (bytesSent)
timeoutMillis = millis() + self.timeout
while (bytesSent < size and bytesAvailable > 0 ):
if ((size - bytesSent) < 7 or bytesAvailable < 7 or self._1ByteTransmissions):
tx = [ 201, self._pin,self._pinMode,0,0x55,0x55,0x55,0x55 ]
txLen = 0
tx[3] = 0
while( txLen < 4 and txLen < bytesAvailable and bytesSent < size):
tx[4 + txLen] = buf[bytesSent]
bytesSent += 1
tx[3] += 1
txLen += 1
if (self._1ByteTransmissions):
break
result,rx = self._sw.sendPacket(tx)
bytesAvailable = rx[3]
else:
tx = [ self._tx7Command, 0x55,0x55,0x55,0x55,0x55,0x55,0x55 ]
for txLen in range(7):
tx[1 + txLen] = buf[bytesSent]
bytesSent += 1
bytesAvailable -= 1
self._sw.sendPacket(tx)
return (bytesSent)
"""!
@brief Queries the SerialWombatUART for the amount of free TX queue space
@return A value between 0 and 64 for the SW4B
"""
def availableForWrite(self):
peektx = [ 203, self._pin,self._pinMode,0x55,0x55,0x55,0x55,0x55 ]
result,peekrx = self._sw.sendPacket(peektx)
return peekrx[3]
def bytesToTransmit(self):
peektx = [ 203, self._pin,self._pinMode,0x55,0x55,0x55,0x55,0x55 ]
result,peekrx = self._sw.sendPacket(peektx)
if (result < 0):
return result
return peekrx[4]
"""!
@brief Reads a specified number of bytes from the SerialWombatUART RX queue
@param buffer An array into which to put received bytes
@param length The maximum number of bytes to be received
@return the number of bytes written to buffer
This function will read bytes from the SerialWombatUART RX queue into buffer.
If 'length' characters are not available to read then the value returned
will be less than length.
"""
def readBytes(self, length):
bytesAvailable = 0
buf = bytearray()
timeoutMillis = millis() + self.timeout
while (length > 0 and timeoutMillis > millis()):
bytecount = 4
if (length < 4):
bytecount = length
tx = [ 202, self._pin,self._pinMode, bytecount,0x55,0x55,0x55,0x55 ]
result,rx = self._sw.sendPacket(tx)
bytesAvailable = rx[3]
if (bytesAvailable == 0) :
continue
else:
timeoutMillis = millis() + self.timeout
bytesReturned = bytecount
if (rx[3] < bytecount):
bytesReturned = rx[3]
for i in range(bytesReturned):
buf.append( rx[i + 4])
bytesAvailable -= 1
length -= 1
while (bytesAvailable >= 7 and length >= 7):
tx = [ self._rx7Command, 0x55,0x55,0x55,0x55,0x55,0x55,0x55 ]
result,rx= self._sw.sendPacket(tx)
for i in range(7):
buf.append( rx[i + 1])
bytesAvailable -= 1
length -= 1
return (buf)
def setTimeout(self, timeout_mS):
if (timeout_mS == 0):
self.timeout = 0x80000000
else:
self.timeout = timeout_mS
def enable1ByteTransmissions(self,enabled = True):
self._1ByteTransmissions = enabled;
"""! @brief A class for the Serial Wombat 4B or SW18AB chips which creates a software based UART on the SW18AB
A Tutorial video is avaialble:
@htmlonly
<iframe width="560" height="315" src="https://www.youtube.com/embed/C1FjcaiBYZs" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
@endhtmlonly
https://youtu.be/C1FjcaiBYZs
The class inherits from the Arduino Sream class, so functions such as println()
can be used once the Software UART is initialized.
@warning Due to the overhead of querying and retreiving data from the SerialWombatUART,
data loss is likely when receiving streams of data greater than the buffer size at higher
baud rates.
Multiple instances of this class can be created on the Serial Wombat 18AB chip. This pin mode requires
more CPU time than most, particularly for higher baud rate ports. Serial Wombat chip CPU usage should
be checked if multiple software uarts are used. Exceeding the available SW18AB CPU resources will cause
bit errors in the UART.
A queue in the User Buffer area is allocated for RX and one for TX prior to as part of begin for this mode.
Size of these queues should be determined based on system needs. The User needs to ensure that the created queues do
not overlap with other structures created in the User Buffer
A full Serial Wombat packet send / receive sequence (8 bytes in each direction) over I2C or the main UART is necessary to
query the status of the queues or to read or receive a byte of data.
The protocol becomes more efficient if multiple bytes are read or written using the readBytes or
write(const * buffer, size_t size) interfaces rather than read() or write( data).
The class must be assigned to a pin. This may be either the receive or transmit pin.
Available baud rates are:
- 300
- 1200
- 2400
- 4800
- 9600
- 19200
- 28800 (Transmit only, receive may be unreliable )
- 57600 (Transmit only, receive may be unreliable )
"""
class SerialWombatSWUART ( SerialWombatUART):
"""!
@param baudRate 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200
@param pin The pin that will host the state machine. This can be either the rxPin or txPin
@param rxPin The pin that will receive. All 4 pins on the SW4B may be used. 255 if no receive function is needed
@param txPin The pin that will transmit. Valid values for the SW4B are 1-3. 255 if no transmit function is needed
@param userMemoryoffset The offset into User Memory where the software storage queues begin
@param rxLength The length in bytes of the on-chip rx queue (can be 0 if rxPin == 255).
@param txLength The length in bytes of the on-chip tx queue (can be 0 if txPin == 255).
"""
def beginUserMemoryQueues(self, baudRate, pin, rxPin, txPin, userMemoryOffset, rxLength, txLength):
self._rxPin = rxPin
self._txPin = txPin
self._pin = pin
self._pinMode = SerialWombat.SerialWombatPinMode_t.PIN_MODE_SW_UART
if (baudRate == 300):
self._baudMarker = 0
elif (baudRate == 1200):
self._baudMarker = 1
elif (baudRate == 2400):
self._baudMarker = 2
elif (baudRate == 4800):
self._baudMarker = 3
elif (baudRate == 9600):
self._baudMarker = 4
elif (baudRate == 19200):
self._baudMarker = 5
elif (baudRate == 38400):
self._baudMarker = 6
elif (baudRate == 57600):
self._baudMarker = 7
#elif (baud == 115200):
#default:
else:
self._baudMarker = 7; # Limit to 57600
tx = [ 200, self._pin,self._pinMode, self._baudMarker,self._rxPin,self._txPin,0x55, 0x55 ]
result,rx = self._sw.sendPacket(tx)
if (result < 0):
return (result)
rxoffset = 0
#if (self._rxPin != 255)
#{
if (rxLength == 0):
rxLength = 2
self.rxQueue = SerialWombatQueue.SerialWombatQueue(self._sw)
self.txQueue = SerialWombatQueue.SerialWombatQueue(self._sw)
rxoffset = self.rxQueue.begin(userMemoryOffset, rxLength)
if (rxoffset >= 0):
userMemoryOffset += rxoffset
else:
return (0); # No memory should have been allocated.
#}
txoffset = 0
#if (self._txPin != 255)
#{
if (txLength == 0):
txLength = 2
txoffset = self.txQueue.begin(userMemoryOffset, txLength)
if (txoffset >= 0):
userMemoryOffset += txoffset
else:
return rxoffset
#}
tx5 = bytearray([ 205,self._pin,self._pinMode]) + SW_LE16(self.txQueue.startIndex) + bytearray([0x55,0x55,0x55])
result,rx = self._sw.sendPacket(tx5)
if (result < 0):
return (result)
tx6 = bytearray([ 206,self._pin,self._pinMode]) + SW_LE16(self.rxQueue.startIndex) + bytearray([0x55,0x55,0x55])
result,rx = self._sw.sendPacket(tx6)
if (result < 0):
return (result)
return (txoffset + rxoffset)
"""!
@param baudRate 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200
@param pin The pin that will host the state machine. This can be either the rxPin or txPin
@param rxPin The pin that will receive. All 4 pins on the SW4B may be used. 255 if no receive function is needed
@param txPin The pin that will transmit. Valid values for the SW4B are 1-3. 255 if no transmit function is needed
@param HWinterface not used for SWUART, but required to match signature of HW UART begin function. Ignored if called on SWUART instance.
"""
def begin(self, baudRate, pin, rxPin, txPin, HWInterface = 1):
self._rxPin = rxPin
self._txPin = txPin
self._pin = pin
self.rxQueue = SerialWombatQueue.SerialWombatQueue(self._sw)
self.txQueue = SerialWombatQueue.SerialWombatQueue(self._sw)
self.rxQueue.startIndex = 0xFFFF
self.txQueue.startIndex = 0xFFFF
self._pinMode = SerialWombat.SerialWombatPinMode_t.PIN_MODE_SW_UART
if (baudRate == 300):
self._baudMarker = 0
elif (baudRate == 1200):
self._baudMarker = 1
elif (baudRate == 2400):
self._baudMarker = 2
elif (baudRate == 4800):
self._baudMarker = 3
elif (baudRate == 9600):
self._baudMarker = 4
elif (baudRate == 19200):
self._baudMarker = 5
elif (baudRate == 38400):
self._baudMarker = 6
elif (baudRate == 57600):
self._baudMarker = 7
#elif (baud == 115200):
#default:
else:
self._baudMarker = 7; # Limit to 57600
tx = [ 200, self._pin,self._pinMode, self._baudMarker,self._rxPin,self._txPin,0x55, 0x55 ]
result,rx = self._sw.sendPacket(tx)
return (result)
"""!
@brief Write bytes to the SerialWombatUART for Transmit
@param buffer An array of bytes to send
@param size the number of bytes to send
@return the number of bytes sent
This function queries the SerialWombatSWUART for avaialble TX
buffer space, and sends bytes as buffer space is avaialble.
If avaialable buffer space is not sufficient to send the entire
array then the function will block and continue trying until the
entire message has been sent to the SerialWombatUART transmit queue.
"""
def write(self, buffer, size):
if (self.txQueue.startIndex != 0xFFFF):
return (self.txQueue.writeBuffer(buffer, size))
else:
sent = 0
while (sent < size):
bytesToSend = 4
if ((size - sent) < 4):
bytesToSend = size - sent
tx = [ 201, self._pin,self._pinMode, bytesToSend,0x55,0x55,0x55,0x55 ]
for i in range(bytesToSend):
tx[4 + i] = buffer[sent]
sent += 1
result,rx = self._sw.sendPacket(tx)
if (rx[3] < 4):
return (sent) #Almost full
#delay (1000)
return sent
def readBytes(self, size):
if (self.rxQueue.startIndex != 0xFFFF):
return (self.rxQueue.readBytes( size))
else:
buffer = bytearray()
received = 0
bytesAvailable = 1
while (bytesAvailable > 0 and received < size):
bytesToReceive = 4
if ((size - received) < 4):
bytesToReceive = size - received
tx = [ 202, self._pin,self._pinMode, bytesToReceive,0x55,0x55,0x55,0x55 ]
result,rx = self._sw.sendPacket(tx)
if (rx[3] > 4):
rx[3] = 4
for i in range(rx[3]):
buffer.append(rx[4 + i])
received += 1
bytesAvailable = rx[3]
#delay(0)
return buffer
def bytesToTransmit(self):
tx = [ 203, self._pin,self._pinMode,0x55,0x55,0x55,0x55,0x55 ]
result,rx = self._sw.sendPacket(tx)
if (result < 0):
return 0
return rx[6]
"""!
@brief Discard all received bytes
"""
def flush(self):
self.rxQueue.flush()