I have converted yours an I get outputs... The CLC has to be set up correctly as the PPS has to redirect them.
I have to work out the final output with the CLC SD0 and the SCK and SD0... I get a completely wrong stream out.
If I want red blue green I get white blue and blah kinda rust!!!
Here is the changes I made...
The issue you were having was just T2 control... If you are real hardware you will have better luck as the software I have here... Waaaaay too slow with 12 RGB units.. ( I'll change it to three to get some speed up.)
I have to work out the final output with the CLC SD0 and the SCK and SD0... I get a completely wrong stream out.
If I want red blue green I get white blue and blah kinda rust!!!
Here is the changes I made...
The issue you were having was just T2 control... If you are real hardware you will have better luck as the software I have here... Waaaaay too slow with 12 RGB units.. ( I'll change it to three to get some speed up.)
Code:
/**
* @file main.c
* @brief Final WS2812 LED Driver using CLC, SPI, and TMR on PIC18F26Q10
* @author Gemini (Corrected Version)
* @date July 14, 2025
*
* @details
* This program drives WS2812 LEDs using a robust, hardware-only method.
* This version uses the CLC as a 2-to-1 Multiplexer and standard CCP/MSSP
* peripherals to ensure maximum compiler compatibility.
*
* Hardware Logic:
* 1. TMR2: Sets the bit period to 1.25us (800 kHz).
* 2. CCP2 (in PWM mode): Generates the short high pulse for a '0' bit (~437ns).
* 3. SPI1 (MSSP1): The SPI Clock (SCK) provides the long high pulse for a '1' bit.
* The SPI Data (SDO) acts as the selector for the MUX.
* 4. CLC1 (AND-OR MUX):
* - Logic: (CCP2_OUT & !SDO) | (SCK & SDO)
* - If SDO is 0, it selects the CCP2 pulse.
* - If SDO is 1, it selects the SCK pulse.
* 5. PPS: Routes the final CLC1 output to a physical pin (RC2).
*/
//==============================================================================
// CONFIGURATION BITS
//==============================================================================
#pragma config FEXTOSC = OFF
#pragma config RSTOSC = HFINTOSC_64MHZ // IMPORTANT: 64MHz Clock is required
#pragma config CLKOUTEN = OFF
#pragma config CSWEN = ON
#pragma config FCMEN = ON
#pragma config MCLRE = INTMCLR
#pragma config PWRTE = ON
#pragma config BOREN = SBORDIS
#pragma config BORV = VBOR_190
#pragma config ZCD = OFF
#pragma config PPS1WAY = ON
#pragma config STVREN = ON
#pragma config WDTE = OFF
//==============================================================================
// INCLUDES
//==============================================================================
#include <xc.h>
#include <stdint.h>
#include <string.h>
//==============================================================================
// DEFINES
//==============================================================================
#define _XTAL_FREQ 64000000UL
#define NUM_LEDS 12 // Define how many LEDs are in your strip
#define DIAGNOSTIC_LED_LAT LATCbits.LATC0
// Buffer to hold the GRB color data for each LED
static uint8_t led_color_data[NUM_LEDS * 3];
//==============================================================================
// PROTOTYPES
//==============================================================================
void SYSTEM_Initialize(void);
void PINS_Initialize(void);
void TMR2_Initialize(void);
void CCP2_Initialize(void);
void SPI_Initialize(void);
void CLC_Initialize(void);
void WS2812_SetPixelColor(uint16_t pixel, uint8_t red, uint8_t green, uint8_t blue);
void WS2812_Show(void);
//==============================================================================
// DRIVER FUNCTIONS
//==============================================================================
void WS2812_SetPixelColor(uint16_t pixel, uint8_t red, uint8_t green, uint8_t blue) {
if (pixel < NUM_LEDS) {
uint16_t index = pixel * 3;
led_color_data[index + 0] = green;
led_color_data[index + 1] = red;
led_color_data[index + 2] = blue;
}
}
void WS2812_Show(void) {
for(uint16_t i = 0; i < sizeof(led_color_data); i++) {
SSP1IF = 0;
SSP1BUF = led_color_data[i];
while(!SSP1IF); // Wait for transmission to complete
}
__delay_us(100); // Latch pulse
}
//==============================================================================
// INITIALIZATION ROUTINES
//==============================================================================
void SYSTEM_Initialize(void) {
OSCCON1 = 0x60;
OSCFRQ = 0x08;
PINS_Initialize();
TMR2_Initialize();
CCP2_Initialize(); // Use CCP2 instead of PWM6
SPI_Initialize();
CLC_Initialize();
}
void PINS_Initialize(void) {
ANSELC = 0x00;
TRISC = 0x00; // Set all PORTC pins as output for simplicity
LATC = 0x00; // Start with all pins low
PPSLOCK = 0x55;
PPSLOCK = 0xAA;
PPSLOCK = 0x00; // Unlock PPS
RC1PPS = 0x10; // SDO1 -> RC1
RC3PPS = 0x0F; // SCK1 -> RC3
RC2PPS = 0x18; // CLC1OUT -> RC2
PPSLOCK = 0x55;
PPSLOCK = 0xAA;
PPSLOCK = 0x01; // Lock PPS
}
void TMR2_Initialize(void) {
T2PR = 19; // Period = (19+1) * 4 * (1/64MHz) = 1.25us => 800kHz
T2CLKCON = 3;
T2HLT = 3;
T2CON = 0b10000000;
}
void CCP2_Initialize(void) {
// FIX: Use the correct register name as suggested by the compiler.
CCPTMRSbits.C2TSEL = 0b01;
// Configure CCP2 for PWM mode
// FIX: Removed reference to non-existent DC2B bit. The lower two bits
// of the duty cycle are set to 0 by this single assignment.
CCP2CON = 0b10001100; // Enable PWM, P2M<1:0> = 00 for single output
// Set Duty Cycle for the '0' bit high time (~437.5ns)
// Duty cycle value is 10 bits. (437.5ns / 62.5ns) = 7
CCPR2L = 7;
}
void SPI_Initialize(void) {
SSP1ADD = 0; // Not used when clock is TMR2
SSP1STAT = 0b01000000; // CKE=1
// SSPM<3:0> = 1011 -> Master mode, clock = TMR2_match/2
SSP1CON1 = 0b00100011; // SSPEN=1, Set clock source to TMR2
}
void CLC_Initialize(void) {
// Configure CLC1 as a MUX: (CCP2_OUT & !SDO) | (SCK & SDO)
CLC1SEL0 = 0x19; // Input 1: CCP2_OUT
CLC1SEL1 = 0x2D; // Input 2: SDO1
CLC1SEL2 = 0x2E; // Input 3: SCK1
CLC1SEL3 = 0x2D; // Input 4: SDO1
CLC1GLS0 = 0b00000010; // Gate 1 logic = S1
CLC1GLS1 = 0b00001000; // Gate 2 logic = S2
CLC1GLS2 = 0b00100000; // Gate 3 logic = S3
CLC1GLS3 = 0b10000000; // Gate 4 logic = S4
CLC1POL = 0b00000010; // Invert Gate 2 output to get !SDO
CLC1CON = 0b10000100; // Enable, AND-OR mode
}
//==============================================================================
// MAIN APPLICATION
//==============================================================================
void main(void) {
SYSTEM_Initialize();
__delay_ms(10);
memset(led_color_data, 0, sizeof(led_color_data));
WS2812_Show();
__delay_ms(10);
uint8_t led_position = 0;
while (1) {
DIAGNOSTIC_LED_LAT = ~DIAGNOSTIC_LED_LAT;
for (uint8_t i = 0; i < NUM_LEDS; i++) {
if (i == led_position) {
WS2812_SetPixelColor(i, 0, 150, 0); // Dim Green
} else {
WS2812_SetPixelColor(i, 150, 0, 00);
}
}
WS2812_Show();
led_position++;
if (led_position >= NUM_LEDS) {
led_position = 0;
}
__delay_ms(100);
}
}