Learning to use FIELD

When Chip added field to Spin2 I got an earful from people using my jm_fullduplexserial.spin2 object because I was using field as a local variable. After that got sorted I never paid attention to what field was about. @"Stephen Moraco" told me it was pretty neat, but until I asked Chip about it a few weeks ago at the Parallax day I didn't actually give it a try.
For those like me who ignored it, here's the skinny: field lets us create and n-bit array within a block of RAM. For example, S.BUS packets arrive 25 bytes at a time, and bytes 1-22 represent a packed array of sixteen 11-bit values. I struggled at first because I couldn't get my 25-byte array to translate properly. Then I read the details in the docs and found this comment.
Given the 11-bit size of S.BUS values I was forced to copy my byte array into an array of longs. Then it all worked. Here's the original method with a stacked loop for unpacking the values
pub decode_sbus(p_sbus, p_chans) | ch, work, bc, flags ' ~107us @ 200MHz
'' Convert S.BUS array (of bytes) to channel values (words)
'' -- p_sbus is [byte] pointer to S.BUS data
'' -- p_chans is [word] pointer to channel values
if (byte[p_sbus][0] <> $0F) ' check header
return
if (byte[p_sbus][24] <> $00) ' check footer
return
p_sbus += 1 ' point to data byte #1
longfill(@ch, 0, 3) ' clear locals
repeat while (ch <= 15) ' Ch 1-16 decoding
repeat while (bc < 11) ' collect enough bits for channel
work |= (byte[p_sbus++] << bc) ' get byte, position, add to work
bc += 8 ' bump bit count
word[p_chans][ch++] := work & $07FF ' extract and save channel
work >>= 11 ' remove channel, re-align lsb
bc -= 11 ' fix bit count
flags := byte[p_sbus] ' flags byte follows channels
word[p_chans][16] := (flags & %0001) ? $07FF : 0 ' convert ch17 bit
word[p_chans][17] := (flags & %0010) ? $07FF : 0 ' convert ch18 bit
And here's the update using field which runs about twice as fast as the original.
pub decode_sbus_v2(p_sbus, p_chans) | sb[6], pf, ch, flags ' ~49us @ 200MHz
'' Convert S.BUS array (of bytes) to channel values (words)
'' -- p_sbus is [byte] pointer to S.BUS data
'' -- p_chans is [word] pointer to channel values
if (byte[p_sbus][0] <> $0F) ' check header
return
if (byte[p_sbus][24] <> $00) ' check footer
return
bytemove(@sb, p_sbus+1, 22) ' leave out header, flags, footer
pf := ^@sb.[10..0] ' set base field pointer
repeat ch from 0 to 15 ' Ch 1-16 decoding
word[p_chans][ch] := field[pf][ch]
flags := byte[p_sbus][23]
word[p_chans][16] := (flags & %0001) ? $07FF : 0 ' convert ch17 bit
word[p_chans][17] := (flags & %0010) ? $07FF : 0 ' convert ch18 bit
As you can see, the second version is a bit tidier and runs faster.
With P2 smart pins I could write a cogless version of my S.BUS interface and will do that later that would be fine for projects that don't have a screaming fast process loop. Having started with the BASIC Stamp 1 I am very stingy about resources, so I've come up with cogless drivers where I have more time in my process loop than I have cogs that I can spare.
Comments
nice insights, thanks for sharing.
Field looks powerful, but the documentation is pretty spartan....
Maybe someone can use AI one day to make better documentation?
Don't fully understand it. Can see how it can work within a single long/word/byte.
But appears @JonnyMac is able to span many bytes somehow...
Don't think this is documented?
I tried variation of the example in docs and it doesn't work. No idea why not...
CON _clkfreq = 10_000_000 var word k2[2] PUB go() | p, k, i p := ^@k2.[2..0] 'get a pointer to the three lowest bits of k repeat 10 field[p][i++]~~ 'set three bits at a time, progressing upwards debug(ubin_long(k2))
I think the problem has to do with debug; you're pointing to a word (k2) but telling it to print as 32 bits. Internally, the upper 32 bits will be set to 0.
I poked around the debug docs and found a mechanism that will show you what you wanted to see -- and it proves the field feature is working.
Interesting... Was wondering if it work with bits offset from LSB, seems it does:
CON _clkfreq = 10_000_000
var word k2[2] PUB go() | p, k, i p := ^@k2.[3..1] 'get a pointer to the three nearly lowest bits of k, starting at bit #1 repeat 10 field[p][i++]~~ 'set three bits at a time, progressing upwards debug(ubin_long_array(@k2,1))