#ifdef SLOW_SEND
#define SSP_BIT_CLK_COUNT 6 // 6x125=750ns, x2=1.5us
#define SSP_BYTE_CLOCK_NS 15000 // 15us per byte
#else
#define SSP_BIT_CLK_COUNT 5 // 5x125=625ns, x2=1.25us
#define SSP_BYTE_CLOCK_NS 12500 // 12.5us per byte
#endif
// Set TMR2 to 625ns/750ns
#ifdef T2HLT
T2HLT=0;
#endif
#ifdef T2CLKCON
T2CLKCON=1; // 1=Fosc/4 (8MHz=125ns)
#endif
PR2=SSP_BIT_CLK_COUNT-1; // 5*125=625ns, x2=1.25us per bit, 6*125=750ns, x2=1.5us per bit
T2CON=1<<_T2CON_TMR2ON_POSITION; // T2CON = TRM2ON, POS=00, if not CLKCON, then CLKbits = FOSC/4
// Point CCP1/CCP2 to use TMR2 (they do by default, but just in case)
#ifndef CCPTMRS
#ifdef CCPTMRS0
#define CCPTMRS CCPTMRS0
#endif
#endif
#ifdef CCPTMRS
CCPTMRS&=0b11110000; // Preserve whatever CCP3/4 are using
CCPTMRS|=0b00000101; // Set CCP2/CCP1 to use TMR2
#endif
// Setup CCP1 for the '0' pulse duration, so 375ns high time
CCP1CON=0b10001100; // EC.?.OUT.FMT.MMMM, so PWM Mode, Right aligned format, Mode=1100=PWM
CCPR1H=0; //High period 3*125=375
CCPR1L=8+3; //8+3=375ns
// Setup CCP2 for the '1' pulse duration, so 750ns high time
// BUT: That's longer than TMR2, so set it to the extra part (750-625)==125ns (so 625+125=750ns, or 750+125=825ns)
CCP2CON=0b10001100; //EC.?.OUT.FMT.MMMM, so PWM Mode, Right aligned format, Mode=1100=PWM
CCPR2H=0; //High period
CCPR2L=4; //4=125ns
// Watch out: Both CCP1 and CCP2 seem to have a few clocks delay to the SPI, are probably syncronised to Fosc/4
// Set SPI to TMR2/2, so 1250ns/1500ns per bits
SSP1CON1=(1<<_SSP1CON1_SSPEN_POSITION)|0x03; //0x03=TMR2/2
SSP1CON3=(1<<_SSP1CON3_BOEN_POSITION);
// This needs the enhanced version of the CLC, that has 4 full registers, not the 2 x nibble registers...
// It uses CLC1 to generate the complicated '1' signal (it's over a TMR2 period, so needs to be TMR2+CCP2
// Select the 3 inputs we need
CLC1SEL0=CLCSEL_SCLK; //MSSP1_clk_out (SCLK)
CLC1SEL1=CLCSEL_SDO; //MSSP1_data_out (SDO)
CLC1SEL2=0; //None
CLC1SEL3=CLCSEL_CCP2; //CCP2_out (short pulses)
// To stretch the output over a TMR2 period (and handle the CCP2 offset relative to SDO/SCLK)
// This uses D-type flipflop with Set and Reset
// If SDO==1, at start of CCP2 the output is set, it's cleared by clocking SCLK on the trailing edge of second CCP2
// CLC1GLS0=Clk, CLC1GLS1=D, CLC1GLS2=R, CLC1GLS3=S
CLC1GLS0=0b01000000; // !CCP2 This will clock the data through on both the first and second halves of the SCLK
CLC1GLS1=0b00000101; // SCLK && SDO = !(!SCLK || !SDO), we merge this with SDO, so that if SDO is '1' it has no effect during the 1/2 half of SCLK.
CLC1GLS2=0; // None
CLC1GLS3=0b01000101; // CCP2 && SCLK && SDO = !(!CCP2 || !SCLK || !SDO) If SDO==1, then set the output at the start of the CCP2 (gate on SCLK to avoid triggers between bytes)
CLC1POL=0b1010; // Inverts: The first level blocks are OR, so we do Y=(A && B) with Y=!(!A || !B)
CLC1CON=(1<<_CLC1CON_LC1EN_POSITION)|4; // Output enabled, Mode=4==Dtype - this gives a 750ns or 825ns '1' pulse when SDO=='1' and the SCLK is active, this is the long input to CLC2
// Merge in the long '1' pulse from CLC1, and the short '0' pulse from the gated CCP1 depending on SDO
// Use dual 2 input AND + single OR = device type of 0
// Select the 4 inputs we need
CLC2SEL0=CLCSEL_SCLK; // MSSP1_clk_out (SCLK)
CLC2SEL1=CLCSEL_SDO; // MSSP1_data_out (SDO)
CLC2SEL2=CLCSEL_CCP1; // CCP1_out (short pulses, for '0' output)
CLC2SEL3=CLCSEL_CLC1; // CLC1_out (stretched CCP2 pulses gated on SDO/SCLK, for '1' output)
CLC2GLS0=0b10000000; // CLC2GLS0=And1.1 - CLC1 - The long '1' pulse (already gated on SDO/SCLK)
CLC2GLS1=0b00001000; // CLC2GLS1=And1.2 - SDO- Gate based on SDO==1 (already done but we have spare logic)
CLC2GLS2=0b00010001; // CLC2GLS2=And2.1 - CCP1 && SCLK = !(!CCP1 || !SCLK) - CCP1 is the short '0' pulse, gate this with SCLK to avoid fake pulses between bytes
CLC2GLS3=0b00000100; // CLC2GLS2=And2.2 - !SDO Gate based on SDO==0
CLC2POL=0b0100; // Invert (!CCP1 || !SCLK) to get CCP1 && SCLK
CLC2CON=(1<<_CLC2CON_LC2EN_POSITION)|0; // Output enabled, 0=dual 2 input ANDs feeding into dual OR - This is the WS28 output
|
; Set TMR2 to 625ns/750ns
BANKSET T2CON
#ifdef T2HLT
clrf T2HLT
#endif
#ifdef T2CLKCON
movlw 1
movwf T2CLKCON ; 1=Fosc/4 (8MHz=125ns)
#endif
movlw SSP_BIT_CLK_COUNT-1 ; 5*125=625ns, x2=1.25us per bit, 6*125=750ns, x2=1.5us per bit
movwf PR2
movlw 1<<TMR2ON
movwf T2CON ; T2CON = TRM2ON, POS=00, if not CLKCON, then CLKbits = FOSC/4
; Point CCP1/CCP2 to use TMR2 (they do by default, but just in case)
#ifndef CCPTMRS
#ifdef CCPTMRS0
#define CCPTMRS CCPTMRS0
#endif
#endif
#ifdef CCPTMRS
BANKSET CCPTMRS
movfw CCPTMRS
andlw b'11110000' ; Preserve whatever CCP3/4 are using
iorlw b'00000101' ; Set CCP2/CCP1 to use TMR2
movwf CCPTMRS
#endif
; Setup CCP1 for the '0' pulse duration, so 375ns high time
BANKSET CCP1CON
movlw b'10001100' ; EC.?.OUT.FMT.MMMM, so PWM Mode, Right aligned format, Mode=1100=PWM
movwf CCP1CON
clrf CCPR1H ; High period 3*125=375
movlw 8+3 ; 8+3=375ns
movwf CCPR1L
; Setup CCP2 for the '1' pulse duration, so 750ns high time
; BUT: That's longer than TMR2, so set it to the extra part (750-625)==125ns (so 625+125=750ns, or 750+125=825ns)
BANKSET CCP2CON
movlw b'10001100' ; EC.?.OUT.FMT.MMMM, so PWM Mode, Right aligned format, Mode=1100=PWM
movwf CCP2CON
clrf CCPR2H ; High period
movlw 4 ; 4=125ns
movwf CCPR2L
; Watch out: Both CCP1 and CCP2 seem to have a few clocks delay to the SPI, are probably syncronised to Fosc/4
; Set SPI to TMR2/2, so 1250ns/1500ns per bits
BANKSET SSP1CON1
movlw (1<<SSPEN)|0x03 ; 0x03=TMR2/2, 0x0A=FOSC/((SSP1ADD + 1) *4)
movwf SSP1CON1
movlw (1<<BOEN)
movwf SSP1CON3
; This needs the enhanced version of the CLC, that has 4 full registers, not the 2 x nibble registers...
; It uses CLC1 to generate the complicated '1' signal (it's over a TMR2 period, so needs to be TMR2+CCP2
; Select the 3 inputs we need
BANKSET CLC1SEL0
movlw CLCSEL_SCLK ; MSSP1_clk_out (SCLK)
movwf CLC1SEL0
movlw CLCSEL_SDO ; MSSP1_data_out (SDO)
movwf CLC1SEL1
clrf CLC1SEL2 ; None
movlw CLCSEL_CCP2 ; CCP2_out (short pulses)
movwf CLC1SEL3
; To stretch the output over a TMR2 period (and handle the CCP2 offset relative to SDO/SCLK)
; This uses D-type flipflop with Set and Reset
; If SDO==1, at start of CCP2 the output is set, it's cleared by clocking SCLK on the trailing edge of second CCP2
; CLC1GLS0=Clk
movlw b'01000000' ; !CCP2
movwf CLC1GLS0 ; This will clock the data through on both the first and second halves of the SCLK
; CLC1GLS1=D
movlw b'00000101' ; SCLK && SDO = !(!SCLK || !SDO)
movwf CLC1GLS1 ; We merge this with SDO, so that if SDO is '1' it has no effect during the 1/2 half of SCLK.
; CLC1GLS2=R=
clrf CLC1GLS2 ; None
; CLC1GLS2=S=
movlw b'01000101' ; CCP2 && SCLK && SDO = !(!CCP2 || !SCLK || !SDO)
movwf CLC1GLS3 ; If SDO==1, then set the output at the start of the CCP2 (gate on SCLK to avoid triggers between bytes)
movlw b'1010'
movwf CLC1POL ; Inverts: The first level blocks are OR, so we do Y=(A && B) with Y=!(!A || !B)
movlw (1<<LC1EN)|4 ; Output enabled, Mode=4==Dtype
movwf CLC1CON ; this gives a 750ns or 825ns '1' pulse when SDO=='1' and the SCLK is active
; Merge in the long '1' pulse from CLC1, and the short '0' pulse from the gated CCP1 depending on SDO
; Select the 4 inputs we need
BANKSET CLC2SEL0
movlw CLCSEL_SCLK ; MSSP1_clk_out (SCLK)
movwf CLC2SEL0
movlw CLCSEL_SDO ; MSSP1_data_out (SDO)
movwf CLC2SEL1
movlw CLCSEL_CCP1 ; CCP1_out (short pulses, for '0' output)
movwf CLC2SEL2
movlw CLCSEL_CLC1 ; CLC1_out (stretched CCP2 pulses gated on SDO/SCLK, for '1' output)
movwf CLC2SEL3
; Use dual 2 input AND + single OR = 000
; CLC2GLS0=And1.1
movlw b'10000000' ; CLC1
movwf CLC2GLS0 ; The long '1' pulse (already gated on SDO/SCLK)
; CLC2GLS1=And1.2
movlw b'00001000' ; SDO
movwf CLC2GLS1 ; Gate based on SDO==1 (already done but we have spare logic)
; CLC2GLS2=And2.1
movlw b'00010001' ; CCP1 && SCLK = !(!CCP1 || !SCLK)
movwf CLC2GLS2 ; CCP1 is the short '0' pulse, gate this with SCLK to avoid fake pulses between bytes
; CLC2GLS2=And2.2
movlw b'00000100' ; !SDO
movwf CLC2GLS3 ; Gate based on SDO==0
movlw b'0100'
movwf CLC2POL ; Invert (!CCP1 || !SCLK) to get CCP1 && SCLK
movlw (1<<LC2EN)|0 ; Output enabled, dual 2 input ANDs feeding into dual OR
movwf CLC2CON ; This is the WS28 output
|