Help using UCS8904B

dimitre

Well-known member
UCS8904B chip is a LED drive cascade chip with 16bit colors, RGBW output.
I'm trying to drive it from a Teensy 3.2 with no success until now.
Information is sparse about the data packet protocol, I'm assuming it is similar to ws2812 maybe with a different startFrame / endFrame length.

For now I only have the Blue channel on, the rest off. It is the "power on, no data" state.

I've got two different datasheets (incomplete) and some more information from another site to double check the timing requirements.
Already ttested with NeoPixelsBus library which have support to RGBW 16 bit colors, with no luck
Tried to change the timing to fit better some information I've found online here

this is using Teensy 3.2 F_CPU = 72000000
Code:
    // static const uint32_t CyclesT0h = (F_CPU / 4000000);
    // static const uint32_t CyclesT1h = (F_CPU / 1250000);
    // static const uint32_t Cycles = (F_CPU / 800000);
    static const uint32_t CyclesT0h = (F_CPU / 2500000); //= 28,8 ciclos = 0.4us
    static const uint32_t CyclesT1h = (F_CPU / 1176470); //= 61,2 = 0,85us
    static const uint32_t Cycles = (F_CPU / 800000); //= 90 = 1.25 us

I suppose maybe the startFrame / endFrame are 64 bits instead of 32, because the LED packet is 64bits.

I'm using a protoboard and a chip adapter, runs with succes a similar older chip UCS2903B.
Any ideas of things to try?
Thank you
 

Attachments

  • IMG_8331.JPG
    IMG_8331.JPG
    209.1 KB · Views: 26
I've write now the minimal code to drive some pixels using WS2812 protocol, it is working OK.
Code is adapted from Adafruit Neopixels
Data pin is shared with UCS2903B. I suppose "something" should be happening there as well, but still behaving as no data input.
Code:
#include <Arduino.h>
#if defined(__MK20DX256__) 
#pragma message "__MK20DX256__"
#endif
#define PIN 11
#define NUMPIXELS 8
uint8_t *pixels;
#define CYCLES_800_T0H (F_CPU / 4000000)
#define CYCLES_800_T1H (F_CPU / 1250000)
#define CYCLES_800 (F_CPU / 800000)
// #define CYCLES_800_T0H (F_CPU / 2500000)
// #define CYCLES_800_T1H (F_CPU / 1176470)
// #define CYCLES_800 (F_CPU / 800000)
uint32_t endTime;
int16_t pin = 11; 
uint16_t numBytes;
bool canShow() {
    uint32_t now = micros();
    if (endTime > now) {
        endTime = now;
    }
    return (now - endTime) >= 300L;
}
void setup() {
    ARM_DEMCR |= ARM_DEMCR_TRCENA;
    ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
    free(pixels); 
    numBytes = 4 * NUMPIXELS; // 4 * 8 depois
    if ((pixels = (uint8_t *)malloc(numBytes))) {
        memset(pixels, 0, numBytes);
    }
    for (int a=0; a<8; a++) {
        pixels[a*3 + 0] = 255;
    }
    pinMode(pin, OUTPUT);
    digitalWrite(pin, LOW);
}
void loop() {
    float max = 30.0f;
    for (int a=0; a<8; a++) {
        float r = std::sin(millis() * 0.004f + a * 0.3f) * max + max;
        float g = std::sin(millis() * 0.0035f + a * 0.3f) * max + max;
        float b = std::sin(millis() * 0.003f + a * 0.3f) * max + max;
        pixels[a*3] = g;
        pixels[a*3 + 1] = r;
        pixels[a*3 + 2] = b;
    }
    while (!canShow())
    ;
    noInterrupts();
    uint8_t *p = pixels, *end = p + numBytes, pix, mask;
    volatile uint8_t *set = portSetRegister(pin), *clr = portClearRegister(pin);
    uint32_t cyc;
    ARM_DEMCR |= ARM_DEMCR_TRCENA;
    ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
    cyc = ARM_DWT_CYCCNT + CYCLES_800;
    while (p < end) {
      pix = *p++;
      for (mask = 0x80; mask; mask >>= 1) {
        while (ARM_DWT_CYCCNT - cyc < CYCLES_800)
          ;
        cyc = ARM_DWT_CYCCNT;
        *set = 1;
        if (pix & mask) {
          while (ARM_DWT_CYCCNT - cyc < CYCLES_800_T1H)
            ;
        } else {
          while (ARM_DWT_CYCCNT - cyc < CYCLES_800_T0H)
            ;
        }
        *clr = 1;
      }
    }
    while (ARM_DWT_CYCCNT - cyc < CYCLES_800)
      ;
    endTime = micros();
    interrupts();
}
 
For each LED you need to send 4x16 bits.
Perhaps numBytes is confusing since every pixel is 16 bits, perhaps call it numWords?
Shouldn't this declaration uint8_t *pixels; be uint16_t *pixels;?

Paul
 
Yes you're right. I was trying to write WS2812 protocol. it drives well ws2812 strips
but as they are both similar with timings and packet, at least some flickering would be displayed in UCS8904B
 
but as they are both similar with timings and packet, at least some flickering would be displayed in UCS8904B
I tend to agree but I'm not 100% sure yet.
Sending out data for 8pcs WS2812 equals to 192 bits. That is exactly the data needed for 3pcs UCS8904B.
Does your code from message #2 light up 8pcs WS2812?

Paul
 
By the way, are you using a levelshifter?
The spec of the UCS8904B states:
1722794763458.png

High voltage level VIH = minimum 0.7VDD which equals to 3.5V. A Teensy 3.2 outputs 3V3 max...

Paul
 
Last edited:
Thank you I was wondering the same today, I'm not. I'll be using a level shifter in my next test.
It is probably the only issue, I'll report back here
 
@PaulS yes great it was the missing level shifter
I had to increase the time of reset packet also, it was unstable with the 300us
 
I found something very interesting yesterday while investigating timing issues and testing with a WS2812 strip.
Using the correct timing ratios I could increase data rate 2.4x, sending data at 1920khz.
I know this will have different results in different strips, but it is nice to know it tolerates a more speed (higher framerate for long strips)
This is the maximum rate I could use on Teensy 3.2 (running at 72khz)
Code:
static constexpr uint32_t Cycles = F_CPU / 1840000;
static constexpr uint32_t CyclesT0h = Cycles * 1 / 4;
static constexpr uint32_t CyclesT1h = Cycles * 2 / 3;
 
Back
Top