cu-bdshot 1.0.0

Copper Bridge to Bidirectional DSHOT ESCs.
Documentation
; Bidirectional dshot at 300kbits/s

; This should be run with a target PIO Clock of 15_300_000 Hz
; It means one clock cycle (inst) or [wait] is 0.06536458us
; One bit at 300kbit/s is 3.33us so very close to 51 cycles.
; A complete frame is 106.72µs
;
; There is a 16 bit request make in long/short bit encoding
; a 30µs break where you need to release the line (459 cycles)
; then a 300kbits straight 16bit response
.program bdshot_300
.wrap_target
entry:
    ;pull  block
    set   pindirs, 1
    pull   block
    out    null, 16

bit:
    set pins, 0 [16]
    out pins, 1 [16]  ; depending on the value of the bit we either shorten or make it longer
    set pins, 1 [13]
    jmp !osre bit

    ; We are done transmitting, switch the pins to reception
    set  pindirs, 0
    mov  isr, null

    ; one bit takes 32 PIO cycles, so after 16 PIO cycles we begin to count it as a bit. After 32 more cycles the second bit etc.
    ; That means we round the cycle count to the nearest multiple of 32 and then divide by 32 to get the bit count.

    ; prepare reading of ERPM, this is a 21 bit long sequence
    set x, 21

    ; x = counter of bits remaining to be read
    mov osr, ~null
    ; osr full of 1s (0xFFFFFFFF), so we have access to 1s each time we read a 1 (without relying on the pin still being a 1)
    ; y = counter of loops remaining to be counted for the current bit

    ; now the response is encoded with 2.2us bits so 37 cycles

wait_for_pin:
    jmp pin, wait_for_pin [1]; wait for the pin to go low

new_zero:
    set y, 6          ;  half a bit -> 6 x 2 ticks per loop + 4 ticks to get to the loop = 18 ticks
    jmp meas_zero

another_zero:
    set y, 15 [2]     ;  full bit -> 15 x 2 ticks per loop + 4 ticks to get to the loop + 2 = 36 ticks

meas_zero:
    jmp pin new_one
    jmp y-- meas_zero
    in null, 1
    jmp x-- another_zero
    jmp entry; stop, as we have read all bits, autopush will take care of the rest

new_one:
    set y, 6 [1]
    jmp meas_one

another_one:
    set y, 15 [2]

meas_one:
    jmp pin cont_meas_one
    jmp new_zero

cont_meas_one:
    jmp y-- meas_one
    in osr, 1; read the bit (always 1 because osr = 0xFFFFFFFF)
    jmp x-- another_one
    ; we have read all bits, autopush will take care of the rest
.wrap

; This is a pure dshot variant (not waiting for the telemetry)
.program dshot_300
.wrap_target
entry:
    set  pindirs, 0       ; high-Z
    pull  block           ; get 32-bit frame in OSR
    set   pindirs, 1      ; drive the pin as output
    out    null, 16       ; align OSR as in bdshot_300

bit:
    set pins, 0 [16]
    out pins, 1 [16]
    set pins, 1 [13]
    jmp !osre bit         ; loop until 16 bits consumed
    jmp entry
.wrap