Using cog counter as a serial I/O
Is there someone who tried using a cog counter as a shift register something like below ?
MOV FRQx, #1 ' can be any of power of 2
ADD PHSx, PHSx
...
ADD PHSx, PHSx
MOV somewhere, PHSx
Comments
" For Propeller Assembly, only readable as a source register (i.e., mov dest, source); read-modify-write not possible as a destination register."
-Phil
Maybe you could adapt it for async somehow? I'm not experienced enough to know how, or how to alter it to change speeds, but it could perhaps be a starting point.
Cheers,
Jesse
Actually, you can, but the destination read will come from the shadow register, which contains whatever was last written to that register.
To use cog counter as a serial input, it is to be assumed that the counter is updated only ONCE ( or always TWICE through receiving entire bit stream ) between previous instruction's Write cycle and Dest cycle of following "ADD PHSx,PHSx" instruction. This means sampling window is 2 sysclk wide.
"ADD PHSx,PHSx" causes updating shadow ram by previous value of itself plus incremented value of PHS.
That is, new value is sum of twice of ( i.e. left-shifted ) old value, and, if any, 1 ( or 2 ).
Some code added.
MOV CTRx, somevalue ' ex. LOGIC A and APIN, etc. MOV FRQx, #1 MOV PHSx, #0 ' start sampling here ADD PHSx, PHSx ' shift in first bit ADD PHSx, PHSx ' and second bit ADD PHSx, PHSx ' third, and so on ... (abbrev) ADD PHSx, PHSx MOV FRQx, #0 ' stop sampling, if needed ADD PHSx, PHSx ' last bit MOV somewhere, PHSx ' save value REV somewhere, #revvalue
(edit)
Instead of using "ADD PHSx,PHSx" instruction, using "SHL FRQx,#1" can be more flexible ?
(but sampling window becomes wider and contiguous)
I've used that one and it works great.
Infact, here it is, copy-pasted straight from one of my own source files, so this should work
sd_DImask long 1<<spiDI sd_DOmask long 1<<spiDO sd_CLKmask long 1<<spiCLK ctr_clock long (%00110 << 26) | (spiCLK << 0) ' DUTY, 25% duty cycle ctr_read long (%11000 << 26) | (spiDO << 0) | (spiCLK << 9) ctr_write long (%00100 << 26) | (spiDI << 0) serbuffer long 0 fastread mov vm_junk,#128 or outa,sd_DImask mov ctra,ctr_read movi phsb,#%11_0000000 :readloop ' Start my clock mov frqa,#1<<7 mov phsa,#0 movi frqb,#%01_0000000 ' keep reading in my value, one bit at a time! (Kuneko - "Wh) shr frqa,#1 shr frqa,#1 shr frqa,#1 shr frqa,#1 shr frqa,#1 shr frqa,#1 shr frqa,#1 shl frqa,#15 shr frqa,#1 shr frqa,#1 shr frqa,#1 shr frqa,#1 shr frqa,#1 shr frqa,#1 shr frqa,#1 shl frqa,#15 shr frqa,#1 shr frqa,#1 shr frqa,#1 shr frqa,#1 shr frqa,#1 shr frqa,#1 shr frqa,#1 shl frqa,#15 shr frqa,#1 shr frqa,#1 shr frqa,#1 shr frqa,#1 shr frqa,#1 shr frqa,#1 shr frqa,#1 mov frqb,#0 ' stop the clock mov serbuffer,phsa wrlong serbuffer,vm_par2 add vm_par2,#4 djnz vm_junk,#:readloop mov frqa,#0 fastread_ret ret fastwrite mov vm_junk,#128 andn outa,sd_DImask mov ctra,ctr_write :writeloop rdlong phsa,vm_par2 add vm_par2,#4 movi phsb,#%11_0000000 ''TODO: can this be moved out of the loop? ' a long in LE order is DCBA rol phsa,#24 ' move A7 into position, so I can do the swizzled version movi frqb,#%01_0000000 ' start the clock (remember A7 is already in place) rol phsa,#1 ' A7 is going out, at the end of this instr, A6 is in place rol phsa,#1 ' A5 rol phsa,#1 ' A4 rol phsa,#1 ' A3 rol phsa,#1 ' A2 rol phsa,#1 ' A1 rol phsa,#1 ' A0 rol phsa,#17 ' B7 rol phsa,#1 ' B6 rol phsa,#1 ' B5 rol phsa,#1 ' B4 rol phsa,#1 ' B3 rol phsa,#1 ' B2 rol phsa,#1 ' B1 rol phsa,#1 ' B0 rol phsa,#17 ' C7 rol phsa,#1 ' C6 rol phsa,#1 ' C5 rol phsa,#1 ' C4 rol phsa,#1 ' C3 rol phsa,#1 ' C2 rol phsa,#1 ' C1 rol phsa,#1 ' C0 rol phsa,#17 ' D7 rol phsa,#1 ' D6 rol phsa,#1 ' D5 rol phsa,#1 ' D4 rol phsa,#1 ' D3 rol phsa,#1 ' D2 rol phsa,#1 ' D1 rol phsa,#1 ' D0 will be in place _after_ this instruction mov frqb,#0 ' shuts the clock off, _after_ this instruction djnz vm_junk,#:writeloop fastwrite_ret ret
FDS_entry ' Counter A's job is to output data on the TX pin ' Counter B's job is to count how many clocks the RX pin is low ' (specifically for aligning the read to the start bit).
Source thread:
forum: https://forums.parallax.com/discussion/143514/fast-full-duplex-serial-1-cog-a-k-a-ffds1
FFDS1 (Fast Full-Duplex Serial, 1 cog) version 0.9 Jonathan Dummer (lonesock) FFDS1 provides a fast and stable serial interface using a single cog. Max baudrate = clkfreq / (86 * 2) Clock | MaxBaud | Standard -------+---------+--------- 96 MHz | 558_139 | 500_000 <- 6MHz XTAL at 16x PLL 80 MHz | 465_116 | 460_800 <- 5MHz XTAL at 16x PLL (most common) 12 MHz | 69_767 | 57_600 <- approx RCFAST 20 kHz | 116 | hah hah <- approx RCSLOW Bit period is calculated to the nearest 2 clocks. So, the bit period should be within 1 clock, or 12.5 ns at 80 MHz.
Of note too is that while a link may be full duplex, very often the protocol is essentially half duplex anyway.
By the way, there's an experimental code to try detecting baudrate automatically.
I implemented my concept straight forward, so this code is not so optimised, I think.
Acceptable format is 8bit-nonparity.
Only lower 7 bit characters ( $00 thru $7F ) are detectable, so do not set MSB to high.
First character must be sent with trailing 68 bit or more idle state ( logic high ) in worst case ( i.e. on receiving NUL character).
In this code, both cog counters are used for measuring positive and negative pulse width precisely.