ns to us delay

Im runing at 64 mhz trying to come up with a delay for ns

Heres the code its for ws2812b leds
Code:
/*
 * File:   newmain.c
 * Author: burt
 *
 * Created on March 8, 2025, 3:44 PM
 */

#define _XTAL_FREQ 64000000
#include <xc.h>
#include <stdint.h>
//#include <libpic30.h> // For delay functions

// Configuration bits for PIC18F25Q10 (adjust as needed)
#pragma config FEXTOSC = OFF, RSTOSC = HFINTOSC_64MHZ // Use internal 64MHz oscillator
#pragma config CLKOUTEN = OFF, CSWEN = ON, FCMEN = ON
#pragma config MCLRE = ON, PWRTE = OFF, WDTE = OFF, LPBOREN = OFF, BOREN = OFF
#pragma config LVP = OFF, PPS1WAY = ON, STVREN = ON, DEBUG = OFF
#pragma config WRT = OFF, CP = OFF, CPD = OFF

#define WS2812_PIN LATCbits.LATC0 // Use RC0 for WS2812B data
#define WS2812_TRIS TRISCbits.TRISC0

// Timing constants (adjust if needed, based on 64MHz clock)
//#define T0H 350 // Time high for 0 bit (ns)
//#define T0L 900 // Time low for 0 bit (ns)
//#define T1H 800 // Time high for 1 bit (ns)
//#define T1L 450 // Time low for 1 bit (ns)
//#define RESET_TIME 55 // Reset time in microseconds

#define NUM_LEDS 30 // Number of LEDs
uint8_t ledData[NUM_LEDS * 3]; // RGB data

void sendBit(uint8_t bit) {
    WS2812_PIN = 1;
    if (bit) {
        __delay_ns(800);
        WS2812_PIN = 0;
        __delay_ns(450);
    } else {
        __delay_ns(350);
        WS2812_PIN = 0;
        __delay_ns(900);
    }
}

void sendByte(uint8_t byte) {
    for (int i = 7; i >= 0; i--) {
        sendBit((byte >> i) & 1);
    }
}

void sendLEDData() {
    for (int i = 0; i < NUM_LEDS * 3; i++) {
        sendByte(ledData[i]);
    }
    __delay_us(55);
}

void main(void) {
    // Oscillator setup (if needed, but 64MHz is set in config)
    // OSCCON1 = 0x60; // Select HFINTOSC (already in config)

    WS2812_TRIS = 0; // Set RC0 as output

    // Initialize LED data (example: red, green, blue pattern)
    for (int i = 0; i < NUM_LEDS; i++) {
        if(i%3 == 0){ledData[i * 3] = 255; ledData[i * 3 + 1] = 0; ledData[i * 3 + 2] = 0;} //red
        else if (i%3 == 1){ledData[i * 3] = 0; ledData[i * 3 + 1] = 255; ledData[i * 3 + 2] = 0;} //green
        else{ledData[i * 3] = 0; ledData[i * 3 + 1] = 0; ledData[i * 3 + 2] = 255;} //blue
    }

    while (1) {
        sendLEDData();
        __delay_ms(20); // Delay between frames
    }
}
 
this did build
Code:
/*
 * File:   newmain.c
 * Author: burt
 *
 * Created on March 8, 2025, 3:44 PM
 */

#define _XTAL_FREQ 64000000
#include <xc.h>
#include <stdint.h>
//#include <libpic30.h> // For delay functions

// Configuration bits for PIC18F25Q10 (adjust as needed)
#pragma config FEXTOSC = OFF, RSTOSC = HFINTOSC_64MHZ // Use internal 64MHz oscillator
#pragma config CLKOUTEN = OFF, CSWEN = ON, FCMEN = ON
#pragma config MCLRE = EXTMCLR, PWRTE = OFF, WDTE = OFF, LPBOREN = OFF, BOREN = OFF
#pragma config LVP = OFF, PPS1WAY = ON, STVREN = ON
#pragma config CP = OFF, CPD = OFF
#pragma config WRT0 = OFF       // Write Protection Block 0 (Block 0 (000800-001FFFh) not write-protected)
#pragma config WRT1 = OFF       // Write Protection Block 1 (Block 1 (002000-003FFFh) not write-protected)
#pragma config WRT2 = OFF       // Write Protection Block 2 (Block 2 (004000-005FFFh) not write-protected)
#pragma config WRT3 = OFF       // Write Protection Block 3 (Block 3 (006000-007FFFh) not write-protected)

#define WS2812_PIN LATCbits.LATC0 // Use RC0 for WS2812B data
#define WS2812_TRIS TRISCbits.TRISC0

// Timing constants (adjust if needed, based on 64MHz clock)
//#define T0H 350 // Time high for 0 bit (ns)
//#define T0L 900 // Time low for 0 bit (ns)
//#define T1H 800 // Time high for 1 bit (ns)
//#define T1L 450 // Time low for 1 bit (ns)
//#define RESET_TIME 55 // Reset time in microseconds

#define NUM_LEDS 30 // Number of LEDs
uint8_t ledData[NUM_LEDS * 3]; // RGB data

void sendBit(uint8_t bit) {
    WS2812_PIN = 1;
    if (bit) {
        __delay_ms(.8);
        WS2812_PIN = 0;
        __delay_ms(.45);
    } else {
        __delay_ms(.35);
        WS2812_PIN = 0;
        __delay_ms(.9);
    }
}

void sendByte(uint8_t byte) {
    for (int i = 7; i >= 0; i--) {
        sendBit((byte >> i) & 1);
    }
}

void sendLEDData() {
    for (int i = 0; i < NUM_LEDS * 3; i++) {
        sendByte(ledData[i]);
    }
    __delay_us(55);
}

void main(void) {
    // Oscillator setup (if needed, but 64MHz is set in config)
    // OSCCON1 = 0x60; // Select HFINTOSC (already in config)
    ANSELCbits.ANSELC0 = 0;
    WS2812_TRIS = 0; // Set RC0 as output

    // Initialize LED data (example: red, green, blue pattern)
    for (int i = 0; i < NUM_LEDS; i++) {
        if(i%3 == 0){ledData[i * 3] = 255; ledData[i * 3 + 1] = 0; ledData[i * 3 + 2] = 0;} //red
        else if (i%3 == 1){ledData[i * 3] = 0; ledData[i * 3 + 1] = 255; ledData[i * 3 + 2] = 0;} //green
        else{ledData[i * 3] = 0; ledData[i * 3 + 1] = 0; ledData[i * 3 + 2] = 255;} //blue
    }

    while (1) {
        sendLEDData();
        __delay_ms(20); // Delay between frames
    }
}
 
Last edited:
You can just execute about 12
NOP();
Instructions and you'll have your 800nSec delay (do the math, this is an estimate for four clock cycles per instruction at 64MHz
 
I have a work-in-progress neopixels style driver that may help;

I was formatting the data bytes up to padded 32 bit values, then use SPI to DMA blast it to the LEDs.

C:
//
// Convert a byte to neopixel format
//
void repack(uint32_t *outbits, int8_t inp) {
    //
    //
    uint32_t *pixbits;
    int8_t val;
    pixbits = outbits;
    val = inp;
        
    *pixbits = 0x88888888L;
        
    if(val & 0x01) *pixbits |= 0x00000004L;
    if(val & 0x02) *pixbits |= 0x00000040L;
    if(val & 0x04) *pixbits |= 0x00000400L;
    if(val & 0x08) *pixbits |= 0x00004000L;
    if(val & 0x10) *pixbits |= 0x00040000L;
    if(val & 0x20) *pixbits |= 0x00400000L;
    if(val & 0x40) *pixbits |= 0x04000000L;
    if(val & 0x80) *pixbits |= 0x40000000L;
        
}


Using ping-pong buffering, so two buffer sets, one for loading and one for output. With the CCS compiler I use, a DMA output is a single line instruction. There is also a function to check when that is complete.

C:
//
        // ping-pong buffer system;
        // exchange input and output buffers each time through.
        bufsel++;
        
        if(bufsel & 1) {
            b_set = buffer_b;
            b_out = buffer_a;
        }
        else {
            b_set = buffer_a;
            b_out = buffer_b;
        }
        
        
        // Send last updated buffer
        spi_transfer_write(b_out, cbytes);

I'm aiming for smooth animation speeds on large strings or arrays..
The MCU is a DSPIC33E series, so lots of RAM available.

A similar routine could be used to output one pixel worth at a time though, using SPI to send one pixel while preparing the next.
 
I imagine if you're wanting nS delays, then ASM is the way to go, as you don't really know what the C compiler is going to do with the code - particularly with the free version of XC8.

You've also got the added issue of the surrounding code, calling and returning from the delay - which could easily mess it up.

However, there's example code for NeoPixels at MicroChip, using the CLC - I've used that for various PIC processors and all have worked fine - which just very minor tweaks.

Here's a YouTube video:

 
I've switched to Dotstar style LEDs instead of Neopixel. The dotstar are similar but way faster. They don't rely in specific timing and have the flexibility of an external clock.
 
as you don't really know what the C compiler is going to do with the code
It si so easy to do the experiment (compile and check), just click "dis-assemble" and view the ASM code that XC8 generates to see if your list (or loop) or NOP commands still exist (or whichever other time killing commands you want to use (turn an I/O on and off or whatever)). Also, look up the ASM for the "standard" millisecond or microsecond delay functions from the library. They work in a similar way by executing nonsense time consuming commands. .
 
I'm making led tube lights 36 of these in each tube i was going to make a simple tester to test these before i solder them together. They are on little buttons that you can chain together.
Im making 30 tubes so i wanted to test each led first
 
Cookies are required to use this site. You must accept them to continue using the site. Learn more…