1
+ '' =================================================================================================
2
+ ''
3
+ '' File....... dat_fullduplexserial.spin2
4
+ '' Purpose.... Buffered serial communications using smart pins
5
+ '' -- mostly matches FullDuplexSerial from P1
6
+ '' -- does NOT support half-duplex communications using shared RX/TX pin
7
+ '' Authors.... Riley August
8
+ '' -- modified version of John MacPhalen's Full Duplex Serial to use DAT blocks for shared mem
9
+ '' -- based on work by Chip Gracey
10
+ '' -- see below for terms of use
11
+
12
+ '' Started....
13
+ '' Updated.... 1 SEPT 2020
14
+ '' mcalyer
15
+ '' -- Modified version of Riley August(DAT blocks for shared mem) and John MacPhalen's Full Duplex Serial
16
+ '' -- Based on work by Chip Gracey
17
+ '' -- Removed all but COM interface
18
+ ''
19
+ '' =================================================================================================
20
+
21
+ {{
22
+
23
+ Note: Buffer size no longer has to be power-of-2 integer.
24
+
25
+ Note: The dec(), bin(), and hex() methods will no longer require the digits parameter as
26
+ in older versions of FullDuplexSerial. Use fdec(), fbin(), and fhex() for code that
27
+ requires a specific field width.
28
+
29
+
30
+ The smart pin uarts use a 16-bit value for baud timing which can limit low baud rates for
31
+ some system frequencies -- beware of these limits when connecting to older devices.
32
+
33
+ Baud 20MHz 40MHz 80MHz 100MHz 200MHz 300MHz
34
+ ------ ----- ----- ----- ------ ------ ------
35
+ 300 No No No No No No
36
+ 600 Yes No No No No No
37
+ 1200 Yes Yes No No No No
38
+ 2400 Yes Yes Yes Yes No No
39
+ 4800 Yes Yes Yes Yes Yes Yes
40
+
41
+ }}
42
+
43
+
44
+ con { fixed io pins }
45
+
46
+ RX1 = 63 { I } ' programming / debug
47
+ TX1 = 62 { O }
48
+
49
+ SF_CS = 61 { O } ' serial flash
50
+ SF_SCK = 60 { O }
51
+ SF_SDO = 59 { O }
52
+ SF_SDI = 58 { I }
53
+
54
+
55
+ con
56
+
57
+ BUF_SIZE = 256
58
+
59
+
60
+ obj
61
+
62
+
63
+ dat
64
+
65
+ cog long 0 ' cog flag/id
66
+
67
+ rxp long 0 ' rx smart pin
68
+ txp long 0 ' tx smart pin
69
+ rxhub long 0 ' hub address of rxbuf
70
+ txhub long 0 ' hub address of txbuf
71
+
72
+ rxhead long 0 ' rx head index
73
+ rxtail long 0 ' rx tail index
74
+ txhead long 0 ' tx head index
75
+ txtail long 0 ' tx tail index
76
+
77
+ txdelay long 0 ' ticks to transmit one byte
78
+
79
+ rxbuf byte 0[BUF_SIZE] ' buffers
80
+ txbuf byte 0[BUF_SIZE]
81
+
82
+
83
+
84
+
85
+ pub null()
86
+
87
+ '' This is not a top level object
88
+
89
+
90
+ pub start(rxpin, txpin, mode, baud) : result | baudcfg, spmode
91
+
92
+ '' Start simple serial coms on rxpin and txpin at baud
93
+ '' -- rxpin... receive pin (-1 if not used)
94
+ '' -- txpin... transmit pin (-1 if not used)
95
+ '' -- mode.... %0xx1 = invert rx
96
+ '' %0x1x = invert tx
97
+ '' %01xx = open-drain/open-source tx
98
+
99
+
100
+ stop()
101
+
102
+ if (rxpin == txpin) ' pin must be unique
103
+ return false
104
+
105
+ longmove(@rxp, @rxpin, 2) ' save pins
106
+ rxhub := @rxbuf ' point to buffers
107
+ txhub := @txbuf
108
+
109
+ txdelay := clkfreq / baud * 11 ' tix to transmit one byte
110
+
111
+ baudcfg := muldiv64(clkfreq, $1_0000, baud) & $FFFFFC00 ' set bit timing
112
+ baudcfg |= (8-1) ' set bits (8)
113
+
114
+ if (rxp >= 0) ' configure rx pin if used
115
+ spmode := P_ASYNC_RX
116
+ if (mode.[0])
117
+ spmode |= P_INVERT_IN
118
+ pinstart(rxp, spmode, baudcfg, 0)
119
+
120
+ if (txp >= 0) ' configure tx pin if used
121
+ spmode := P_ASYNC_TX | P_OE
122
+ case mode.[2..1]
123
+ %01 : spmode |= P_INVERT_OUTPUT
124
+ %10 : spmode |= P_HIGH_FLOAT ' requires external pull-up
125
+ %11 : spmode |= P_INVERT_OUTPUT | P_LOW_FLOAT ' requires external pull-down
126
+ pinstart(txp, spmode, baudcfg, 0)
127
+
128
+ cog := coginit(COGEXEC_NEW, @uart_mgr, @rxp) + 1 ' start uart manager cog
129
+
130
+ return cog
131
+
132
+
133
+ pub stop()
134
+
135
+ '' Stop serial driver
136
+ '' -- frees a cog if driver was running
137
+
138
+ if (cog) ' cog active?
139
+ cogstop(cog-1) ' yes, shut it down
140
+ cog := 0 ' and mark stopped
141
+
142
+ longfill(@rxp, -1, 2) ' reset object globals
143
+ longfill(@rxhub, 0, 7)
144
+
145
+
146
+ pub rx() : b
147
+
148
+ '' Pulls byte from receive buffer if available
149
+ '' -- will wait if buffer is empty
150
+
151
+ repeat while (rxtail == rxhead) ' hold while buffer empty
152
+
153
+ b := rxbuf[rxtail] ' get a byte
154
+ if (++rxtail == BUF_SIZE) ' update tail pointer
155
+ rxtail := 0
156
+
157
+
158
+ pub rxcheck() : b
159
+
160
+ '' Pulls byte from receive buffer if available
161
+ '' -- returns -1 if buffer is empty
162
+
163
+ if (rxtail <> rxhead) ' something in buffer?
164
+ b := rxbuf[rxtail] ' get it
165
+ if (++rxtail == BUF_SIZE) ' update tail pointer
166
+ rxtail := 0
167
+ else
168
+ b := -1 ' mark no byte available
169
+
170
+
171
+ pub rxtime(ms) : b | mstix, t
172
+
173
+ '' Wait ms milliseconds for a byte to be received
174
+ '' -- returns -1 if no byte received, $00..$FF if byte
175
+
176
+ mstix := clkfreq / 1000
177
+
178
+ t := getct()
179
+ repeat until ((b := rxcheck()) >= 0) || (((getct()-t) / mstix) >= ms)
180
+
181
+
182
+ pub rxtix(tix) : b | t
183
+
184
+ '' Waits tix clock ticks for a byte to be received
185
+ '' -- returns -1 if no byte received
186
+
187
+ t := getct()
188
+ repeat until ((b := rxcheck()) >= 0) || ((getct()-t) >= tix)
189
+
190
+
191
+ pub available() : count
192
+
193
+ '' Returns # of bytes waiting in rx buffer
194
+
195
+ if (rxtail <> rxhead) ' if byte(s) available
196
+ count := rxhead - rxtail ' get count
197
+ if (count < 0)
198
+ count += BUF_SIZE ' fix for wrap around
199
+
200
+
201
+ pub rxflush()
202
+
203
+ '' Flush receive buffer
204
+
205
+ repeat while (rxcheck() >= 0)
206
+
207
+
208
+ pub tx(b) | n
209
+
210
+ '' Move byte into transmit buffer if room is available
211
+ '' -- will wait if buffer is full
212
+
213
+ repeat
214
+ n := txhead - txtail ' bytes in buffer
215
+ if (n < 0) ' fix for index wrap-around
216
+ n += BUF_SIZE
217
+ if (n < BUF_SIZE-1)
218
+ quit
219
+
220
+ txbuf[txhead] := b ' move to buffer
221
+ if (++txhead == BUF_SIZE) ' update head pointer
222
+ txhead := 0
223
+
224
+
225
+ pub txn(b, n)
226
+
227
+ '' Emit byte n times
228
+
229
+ repeat n
230
+ tx(b)
231
+
232
+
233
+ pub str(p_str)
234
+
235
+ '' Emit z-string at p_str
236
+ repeat (strsize(p_str))
237
+ tx(byte[p_str++])
238
+
239
+
240
+ pub txflush()
241
+
242
+ '' Wait for transmit buffer to empty
243
+ '' -- will delay one byte period after buffer is empty
244
+
245
+ repeat until (txtail == txhead) ' let buffer empty
246
+ waitct(getct() + txdelay) ' delay for last byte
247
+
248
+
249
+
250
+
251
+ dat { smart pin uart/buffer manager }
252
+
253
+ org
254
+
255
+ uart_mgr setq #4-1 ' get 4 parameters from hub
256
+ rdlong rxd, ptra
257
+
258
+ uart_main testb rxd, #31 wc ' rx in use?
259
+
260
+
261
+
262
+ if_nc call #rx_serial
263
+
264
+ testb txd, #31 wc ' tx in use?
265
+ if_nc call #tx_serial
266
+
267
+
268
+
269
+ jmp #uart_main
270
+
271
+
272
+ rx_serial testp rxd wc ' anything waiting?
273
+ if_nc ret
274
+
275
+
276
+
277
+ rdpin t3, rxd ' read new byte
278
+ shr t3, #24 ' align lsb
279
+ mov t1, p_rxbuf ' t1 := @rxbuf
280
+ rdlong t2, ptra[4] ' t2 := rxhead
281
+ add t1, t2
282
+ wrbyte t3, t1 ' rxbuf[rxhead] := t3
283
+ incmod t2, #(BUF_SIZE-1) ' update head index
284
+
285
+
286
+
287
+ _ret_ wrlong t2, ptra[4] ' write head index back to hub
288
+
289
+
290
+ tx_serial rdpin t1, txd wc ' check busy flag
291
+ if_c ret ' abort if busy
292
+
293
+ rdlong t1, ptra[6] ' t1 = txhead
294
+ rdlong t2, ptra[7] ' t2 = txtail
295
+ cmp t1, t2 wz ' byte(s) to tx?
296
+ if_e ret
297
+
298
+ mov t1, p_txbuf ' start of tx buffer
299
+ add t1, t2 ' add tail index
300
+ rdbyte t3, t1 ' t3 := txbuf[txtail]
301
+ wypin t3, txd ' load into sp uart
302
+ incmod t2, #(BUF_SIZE-1) ' update tail index
303
+ _ret_ wrlong t2, ptra[7] ' write tail index back to hub
304
+
305
+
306
+ ' --------------------------------------------------------------------------------------------------
307
+
308
+ rxd res 1 ' receive pin
309
+ txd res 1 ' transmit pin
310
+ p_rxbuf res 1 ' pointer to rxbuf
311
+ p_txbuf res 1 ' pointer to txbuf
312
+
313
+ indi res 1
314
+
315
+ t1 res 1 ' work vars
316
+ t2 res 1
317
+ t3 res 1
318
+
319
+ fit 472
320
+
321
+
322
+ con { license }
323
+
324
+ {{
325
+
326
+ Terms of Use: MIT License
327
+
328
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this
329
+ software and associated documentation files (the "Software"), to deal in the Software
330
+ without restriction, including without limitation the rights to use, copy, modify,
331
+ merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
332
+ permit persons to whom the Software is furnished to do so, subject to the following
333
+ conditions:
334
+
335
+ The above copyright notice and this permission notice shall be included in all copies
336
+ or substantial portions of the Software.
337
+
338
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
339
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
340
+ PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
341
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
342
+ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
343
+ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
344
+
345
+ }}
0 commit comments