Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

More fun with ws2812 this time XC8 and CLC

be80be

Well-Known Member
Im totally lost here need some pointers

Code:
/**
 * @file main.c
 * @brief WS2812 LED Driver using CLC, PWM, and SPI on PIC18F26Q10
 * @author Gemini
 * @date July 12, 2024
 *
 * @details
 * This program demonstrates a highly efficient method for driving WS2812/NeoPixel
 * LEDs using the hardware peripherals of a PIC18F26Q10 microcontroller.
 * It offloads the precise, timing-critical signal generation from the CPU
 * to the hardware, freeing up the processor for other tasks and preventing
 * timing jitter from interrupts.
 *
 * Hardware Logic:
 * 1. TMR2 + PWM6: Generates the base 'T0H' pulse width (~0.4us) for every bit.
 * 2. TMR2 + PWM7: Generates a "stretcher" pulse, phase-shifted to start after
 * the T0H pulse. This is used to extend the high time for a 'T1H' bit.
 * 3. SPI1: Shifts out the color data (e.g., 24 bits for one LED) at the same
 * frequency as the bit period (800kHz). The SDO line is high for a '1' and
 * low for a '0'.
 * 4. CLC1: Combines these signals with the logic:
 * Output = (PWM6_out) OR (PWM7_out AND SPI1_SDO)
 * - If SDO is 0, output is just the short PWM6 pulse (T0H).
 * - If SDO is 1, output is PWM6 OR'd with PWM7, creating a single, long
 * high pulse (T1H).
 * 5. PPS: Routes the final CLC1 output to a physical pin (RC2).
 *
 * Configuration:
 * - MCU Clock (FOSC): 64 MHz from HFINTOSC
 * - Bit Period: 1.25us (800 kHz), set by TMR2
 * - T0H (logic 0 high time): ~0.406 us
 * - T1H (logic 1 high time): ~0.812 us
 * - WS2812 Data Pin: RC2
 */

//==============================================================================
// CONFIGURATION BITS
//==============================================================================
#pragma config FEXTOSC = OFF    // External Oscillator Selection->Oscillator not enabled
#pragma config RSTOSC = HFINTOSC_64MHZ// Reset Oscillator Selection->HFINTOSC with HFFRQ = 64 MHz and CDIV = 1:1
#pragma config CLKOUTEN = OFF   // Clock out Enable bit->CLKOUT function is disabled
#pragma config PR1WAY = ON      // PRLOCKED One-Way Set Enable bit->PRLOCKED bit can be cleared and set only once
#pragma config CSWEN = ON       // Clock Switch Enable bit->Writing to NOSC and NDIV is allowed
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable bit->Fail-Safe Clock Monitor enabled

#pragma config MCLRE = EXTMCLR  // MCLR Enable bit->MCLR pin enabled, RE3 input pin disabled
#pragma config PWRTS = PWRT_OFF // Power-up timer selection bits->PWRT is disabled
#pragma config MVECEN = ON      // Multi-vector enable bit->Multi-vector enabled, Vector table used for interrupts
#pragma config IVT1WAY = ON     // IVTLOCK bit can be cleared and set only once
#pragma config LPBOREN = OFF    // Low Power BOR Enable bit->ULPBOR disabled
#pragma config BOREN = SBORDIS  // Brown-out Reset Enable bits->Brown-out Reset enabled , SBOREN bit is ignored
#pragma config BORV = VBOR_2P45 // Brown-out Reset Voltage selection bits->Brown-out Reset Voltage (VBOR) set to 2.45V
#pragma config ZCD = OFF        // ZCD Disable bit->ZCD disabled. ZCD can be enabled by setting the ZCDSEN bit of ZCDCON
#pragma config PPS1WAY = ON     // PPSLOCK bit One-Way Set Enable bit->PPSLOCK bit can be cleared and set only once; PPS registers remain locked after one clear/set cycle
#pragma config STVREN = ON      // Stack Full/Underflow Reset Enable bit->Stack full/underflow will cause Reset
#pragma config DEBUG = OFF      // Debugger Enable bit->Background debugger disabled
#pragma config XINST = OFF      // Extended Instruction Set Enable bit->Extended Instruction Set and Indexed Addressing Mode disabled

#pragma config WDTCS = WDTCLK_31KHZ // WDT Clock source select bits->WDT uses LPRC 31kHz for clock
#pragma config WDTE = OFF       // WDT operating mode->WDT Disabled

//==============================================================================
// INCLUDES
//==============================================================================
#include <xc.h>
#include <stdint.h>

//==============================================================================
// DEFINES
//==============================================================================
#define _XTAL_FREQ 64000000UL
#define NUM_LEDS 12 // Define how many LEDs are in your strip

//==============================================================================
// PROTOTYPES
//==============================================================================
void SYSTEM_Initialize(void);
void CLC_Initialize(void);
void PWM_Initialize(void);
void SPI_Initialize(void);
void PINS_Initialize(void);

void ws2812_send_byte(uint8_t data);
void ws2812_send_color(uint8_t r, uint8_t g, uint8_t b);
void ws2812_latch(void);
void ws2812_clear_all(void);

//==============================================================================
// DRIVER FUNCTIONS
//==============================================================================

/**
 * @brief Sends a single byte to the SPI buffer for the CLC to process.
 * @param data The byte to send.
 */
void ws2812_send_byte(uint8_t data) {
    while(!SPI1STATUSbits.TXBE); // Wait for SPI Tx buffer to be empty
    SPI1TXB = data;
}

/**
 * @brief Sends a 24-bit color value (GRB format) for one LED.
 * @param r Red component (0-255)
 * @param g Green component (0-255)
 * @param b Blue component (0-255)
 */
void ws2812_send_color(uint8_t r, uint8_t g, uint8_t b) {
    // WS2812 LEDs expect data in Green-Red-Blue order
    ws2812_send_byte(g);
    ws2812_send_byte(r);
    ws2812_send_byte(b);
}

/**
 * @brief Latches the color data to the LEDs and resets the data line.
 * This function must be called after sending color data for all LEDs.
 */
void ws2812_latch() {
    // Wait for the last byte to finish transmitting completely
    while(!SPI1STATUSbits.SRMT);

    // Disable the CLC. This forces the output pin low, creating the
    // required >50us reset pulse to latch the colors.
    CLC1CONbits.EN = 0;

    // A 100us delay is safe for the reset condition.
    __delay_us(100);

    // Re-enable the CLC for the next data transmission.
    CLC1CONbits.EN = 1;
}

/**
 * @brief Sets all LEDs in the strip to off (black).
 */
void ws2812_clear_all() {
    for (uint8_t i = 0; i < NUM_LEDS; i++) {
        ws2812_send_color(0, 0, 0);
    }
    ws2812_latch();
}


//==============================================================================
// INITIALIZATION ROUTINES
//==============================================================================

/**
 * @brief Initializes all required peripherals by calling their specific init functions.
 */
void SYSTEM_Initialize(void) {
    PINS_Initialize();
    PWM_Initialize();
    SPI_Initialize();
    CLC_Initialize(); // CLC should be last, as it uses other peripheral outputs
}

/**
 * @brief Configures the pins and Peripheral Pin Select (PPS) registers.
 */
void PINS_Initialize(void) {
    // Set RC2 as an output (this will be our WS2812 data pin)
    TRISCbits.TRISC2 = 0;
    LATCbits.LATC2 = 0;

    // Route CLC1 output to RC2 pin
    RC2PPS = 0b00001001; // RC2 <- CLC1OUT
}

/**
 * @brief Configures TMR2, PWM6, and PWM7.
 */
void PWM_Initialize(void) {
    // TMR2 provides the 800kHz (1.25us) period for both PWMs
    T2CON = 0b10000000; // TMR2 ON, 1:1 prescaler, 1:1 postscaler
    PR2 = 19;           // Period = (19+1) * 4 / 64MHz = 1.25us

    // PWM6 generates the base T0H pulse (~0.4us)
    // Duty Cycle = 26 -> (26 * 62.5ns) = 0.406us
    PWM6DCH = 6;
    PWM6DCL = 0b11000000;
    PWM6CON = 0b10000000; // PWM6 enabled, active high

    // PWM7 generates the "stretcher" pulse
    // Same duty cycle as PWM6
    PWM7DCH = 6;
    PWM7DCL = 0b11000000;
    // Phase shift PWM7 to start right after PWM6 ends
    PWM7PHH = 6;
    PWM7PHL = 0b11000000;
    PWM7CON = 0b10000000; // PWM7 enabled, active high

    // Enable and load shadow register values into active registers
    PWMEN |= 0b11000000;  // Enable PWM6 and PWM7
    PWMLOAD |= 0b11000000; // Load buffer values
}

/**
 * @brief Configures the SPI1 peripheral.
 */
void SPI_Initialize(void) {
    // Set SPI1 clock source to FOSC
    SPI1CLK = 0x00;
    // Set Baud Rate to 800kHz to match the PWM period
    // Baud = 64MHz / (2 * (BaudRate + 1)) -> 39 = 64MHz / (2 * 40)
    SPI1BAUD = 39;
    // Master mode, continuous transfers, MSB first
    SPI1CON0 = 0b10000010;
    // SPI Enabled
    SPI1CON1 = 0b10000000;
    // Default settings for CON2
    SPI1CON2 = 0x00;
}

/**
 * @brief Configures CLC1 to combine the PWM and SPI signals.
 */
void CLC_Initialize(void) {
    // --- Input Selection ---
    // Route peripheral outputs to the CLC data inputs
    CLC1SEL0 = 0b00010110; // CLCIN0 <- PWM6_out
    CLC1SEL1 = 0b00010111; // CLCIN1 <- PWM7_out
    CLC1SEL2 = 0b00011000; // CLCIN2 <- SPI1_SDO
    CLC1SEL3 = 0b01000011; // CLCIN3 <- SCK1_CONST1 (not used in logic but good practice)

    // --- Logic Gate Configuration (4-input LUT) ---
    // The logic is: F(D2,D1,D0) = D0 | (D1 & D2)
    // Where D0=PWM6, D1=PWM7, D2=SDO
    // This translates to the truth table value 0b11111010
    CLC1GLS0 = 0xFA;
    CLC1GLS1 = 0x00; // Gate 2 unused
    CLC1GLS2 = 0x00; // Gate 3 unused
    CLC1GLS3 = 0x00; // Gate 4 unused

    // No output inversions needed
    CLC1POL = 0x00;

    // --- CLC Mode and Enable ---
    // Mode '010' = 4-input LUT, output from Gate 1
    // EN bit is set to 1 to enable the CLC
    CLC1CON = 0b10000010;
}

//==============================================================================
// MAIN APPLICATION
//==============================================================================
void main(void) {
    SYSTEM_Initialize();

    // Give the LEDs a moment to power up and stabilize
    __delay_ms(10);
    ws2812_clear_all();
    __delay_ms(10);

    uint8_t led_position = 0;

    while (1) {
        // --- Simple "Knight Rider" / Cylon Animation ---

        // Turn on the current LED in red
        for (uint8_t i = 0; i < NUM_LEDS; i++) {
            if (i == led_position) {
                ws2812_send_color(50, 0, 0); // Set one LED to a dim red
            } else {
                ws2812_send_color(0, 0, 0); // All others are off
            }
        }
        ws2812_latch(); // Send the data

        // Move to the next LED
        led_position++;
        if (led_position >= NUM_LEDS) {
            led_position = 0;
        }

        __delay_ms(50); // Animation speed
    }
}
 
Here's hopefully working code
Code:
/**
 * @file main.c
 * @brief Final WS2812 LED Driver using CLC, SSP, and TMR on PIC18F26Q10
 * @author Gemini
 * @date July 13, 2024
 *
 * @details
 * This program drives WS2812 LEDs using a robust, hardware-only method.
 * It uses the SR-Latch mode of the CLC, which is simpler and less
 * prone to compiler naming issues than the PWM/CCP method.
 *
 * Hardware Logic:
 * 1. TMR2: Sets the bit period to 1.25us (800 kHz). Its output resets the latch.
 * 2. SSP1 (SPI): Runs at 2.4MHz. It sends a 3-bit pattern for each LED data bit.
 * - '1' bit = SPI sends `110`
 * - '0' bit = SPI sends `100`
 * The SDO line sets the latch.
 * 3. CLC1 (SR Latch):
 * - Set (S) input is connected to the SPI Data Out (SDO).
 * - Reset (R) input is connected to the Timer2 Match signal.
 * This combination creates the precise WS2812 waveform on the CLC output pin.
 * 4. PPS: Routes the final CLC1 output to a physical pin (RC2).
 *
 * This version uses legacy register names (SSP) and direct bitmasking to
 * ensure maximum compatibility with all XC8 compiler versions.
 */

//==============================================================================
// 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 = EXTMCLR
#pragma config PWRTE = OFF
#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

// Buffer to hold the GRB color data for each LED
static uint8_t led_color_data[NUM_LEDS * 3];

// Buffer to hold the encoded SPI data. 3 SPI bytes for each color byte.
static uint8_t spi_data_buffer[NUM_LEDS * 3 * 3];

//==============================================================================
// PROTOTYPES
//==============================================================================
void SYSTEM_Initialize(void);
void PINS_Initialize(void);
void TMR2_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) {
        // WS2812B LEDs expect data in Green, Red, Blue order
        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) {
    uint16_t spi_buf_idx = 0;
    // These are the 3-bit patterns sent by the SPI for each WS2812B bit.
    // WS2812B '0' bit is SPI pattern 100 (0x4)
    // WS2812B '1' bit is SPI pattern 110 (0x6)
    const uint8_t patterns[] = {0x4, 0x6};

    // Go through the entire GRB color buffer
    for (uint16_t i = 0; i < sizeof(led_color_data); i++) {
        uint8_t color_byte = led_color_data[i];
        uint32_t spi_bits = 0;

        // Convert one 8-bit color value into 24 SPI bits
        for (int bit_index = 7; bit_index >= 0; bit_index--) {
            spi_bits <<= 3;
            spi_bits |= patterns[(color_byte >> bit_index) & 0x01];
        }

        // Unpack the 24 bits into 3 bytes for the SPI buffer
        spi_data_buffer[spi_buf_idx++] = (spi_bits >> 16) & 0xFF;
        spi_data_buffer[spi_buf_idx++] = (spi_bits >> 8) & 0xFF;
        spi_data_buffer[spi_buf_idx++] = spi_bits & 0xFF;
    }

    // Send the data using a blocking write
    for(uint16_t i = 0; i < sizeof(spi_data_buffer); i++) {
        while(!(PIR7 & 0x01)); // Wait for SSP1TXIF to be set
        SSP1BUF = spi_data_buffer[i];
    }
    
    // Wait for the last byte to finish transmitting
    while(SSP1STATbits.BF);

    // A delay of >50us is required to latch the colors
    __delay_us(100);
}


//==============================================================================
// INITIALIZATION ROUTINES
//==============================================================================

void SYSTEM_Initialize(void) {
    PINS_Initialize();
    TMR2_Initialize();
    SPI_Initialize();
    CLC_Initialize();
}

void PINS_Initialize(void) {
    // Set RC2 as an output for the CLC
    TRISCbits.TRISC2 = 0;
    ANSELCbits.ANSELC2 = 0;
    LATCbits.LATC2 = 0;
    RC2PPS = 0b00001001; // RC2 <- CLC1OUT

    // Set RC1 as SDO1 output for SPI
    TRISCbits.TRISC1 = 0;
    ANSELCbits.ANSELC1 = 0;
    RC1PPS = 0b00010111; // RC1 -> SDO1
}

void TMR2_Initialize(void) {
    // FOSC=64MHz, TMR2_CLK=FOSC/4=16MHz, Tick=62.5ns
    // Period = 1.25us (800kHz) -> PR2 = 1.25us / 62.5ns - 1 = 19
    PR2 = 19;
    T2CON = 0b10000000; // TMR2 ON, 1:1 prescaler, 1:1 postscaler
}

void SPI_Initialize(void) {
    // Baud Rate = FOSC / (4 * (SSP1ADD + 1))
    // 2.4MHz = 64MHz / (4 * (SSP1ADD + 1)) -> SSP1ADD = 5.66 -> use 6
    // Actual Baud = 64MHz / (4 * 7) = 2.28MHz (This is close enough)
    SSP1ADD = 6;
    SSP1STAT = 0b01000000; // CKE=1, SMP=0
    SSP1CON1 = 0b00100000; // SSPEN=1, Master Mode
}

void CLC_Initialize(void) {
    // Configure CLC1 as an SR Latch
    // Set Input = SDO1 (SPI Data Out)
    // Reset Input = TMR2
    CLC1SEL0 = 0x17; // CLCIN0 <- SDO1
    CLC1SEL1 = 0x0F; // CLCIN1 <- TMR2_OUT
    
    // Gates are not used in SR Latch mode, but clear them
    CLC1GLS0 = 0x00;
    CLC1GLS1 = 0x00;
    CLC1GLS2 = 0x00;
    CLC1GLS3 = 0x00;
    CLC1POL = 0x00; // No inversions

    // Mode '011' = SR Latch, EN=1 to enable
    CLC1CON = 0b10000011;
}

//==============================================================================
// MAIN APPLICATION
//==============================================================================
void main(void) {
    SYSTEM_Initialize();
    __delay_ms(10);

    // Set all LEDs to off initially
    memset(led_color_data, 0, sizeof(led_color_data));
    WS2812_Show();
    __delay_ms(10);

    uint8_t led_position = 0;

    while (1) {
        // Simple "Knight Rider" / Cylon Animation
        for (uint8_t i = 0; i < NUM_LEDS; i++) {
            if (i == led_position) {
                WS2812_SetPixelColor(i, 50, 0, 0); // Dim red
            } else {
                WS2812_SetPixelColor(i, 0, 0, 0);  // Off
            }
        }
        WS2812_Show();

        led_position++;
        if (led_position >= NUM_LEDS) {
            led_position = 0;
        }

        __delay_ms(50);
    }
}
 
The method I came up with was a function to (You just updated the thread).

Similar to your new post, but no bulk bitshifting.
For each input byte, start with a fixed template output value. (100100100100100etc).

Test each bit in the input byte, if set then set the corresponding bit in the output word.
I used bitmasks for testing and setting, shifted one & three places at each increment.

Using a union & pointer to overlay a 32 bit word over the three [four] bytes in the output buffer makes it directly accessible.
 
I'm looking at this now... The speed of pulse on the W28 is 1.05us which should be 0.7us low and 0.35us high

What is your clock speed? The pic's fastest speed is 64Mhz ergo 16Mhz tick cycle so about 0.25 us.. Does the CLC go faster? If I put this in an interrupt nothing else will be able to run.

24 bits per unit with 12 units connected in series. The datasheet on these devices is quite crap..
do we have to refresh the led or do they latch, because you'll need a decent hardware interface. I have downloaded the Arduino library to see how they do it of a 16Mhz UNO.
 
The hardware is using the CLC to make a 1 or 0 and the spi to send it the led data but its not working
I have code that bit bangs it works great figured id try the CLC but I'm using 18f26q10 and XC8 and datasheet have not lined up I had 100 name errors
 
There's a useful YouTube page here:


Complete with links.

I've used the MicroChip CLC NeoPixel examples on various PIC devices, and had no problems moving the code - the differences are basically just the names of some of the 'registers' used.

You don't need 64MHz for it to work, as far as I recall I was using the internal 32MHz clock on a range of different 16F devices.
 
This builds but it's hanging in the while loop
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++) {
        SSP1BUF = led_color_data[i];
        while(!(PIR7 & 0x01)); // 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 = 0x11; // SDO1 -> RC1
    RC3PPS = 0x10; // SCK1 -> RC3
    RC2PPS = 0x01; // 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
    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 = 0b00101011; // SSPEN=1, Set clock source to TMR2
}

void CLC_Initialize(void) {
    // Configure CLC1 as a MUX: (CCP2_OUT & !SDO) | (SCK & SDO)
    CLC1SEL0 = 0x0C;       // Input 1: CCP2_OUT
    CLC1SEL1 = 0x11;       // Input 2: SDO1
    CLC1SEL2 = 0x10;       // Input 3: SCK1
    CLC1SEL3 = 0x11;       // 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 = 0b00001000;  // 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, 50, 0); // Dim Green
            } else {
                WS2812_SetPixelColor(i, 0, 0, 0);
            }
        }
        WS2812_Show();

        led_position++;
        if (led_position >= NUM_LEDS) {
            led_position = 0;
        }

        __delay_ms(100);
    }
}
 
Nigel Goodwin I been in name hell not nothing matches the data sheet I've fixed errors here for the last 8 hours
 
I started this to see if AI can write it but fixing what it writes has been killing me.
 
I found TMR2 isn't running.. Ergo SSP isn't running.

T2 has a clock control and a Halt control.. Both need setting.. The CLC output isn't configured correctly.

It would appear that AI cannot read... This code ain't for a Q10 I tried both 26Q10 and 27Q10..

I'm nearly there! I have an output on RC2
 
AI gave up lol the problem is XC8 and datasheet AI using datasheet and saying the setting should work but your right there not set right nothing happens in while loop Its hanging cause The CLC spi and PWM are only sending a High out
RC2 Im looking at Nigels code and the setup is not the same. I don't care what code I use I been making lights for are car wash hardware is good But im using esp 32 right now I don't want 32 esp32 needing wifi some only need red blue or green then a rainbow
 
Last edited:

Latest threads

New Articles From Microcontroller Tips

Back
Top