Jump to content
Sign in to follow this  
naren

MSP430G2553 DCOCTL/BCSCTL1 weirdness with ws2812b leds

Recommended Posts

Hi,

 

I'm trying to drive 15 ws2812b leds using a g2553 sitting on a Launchpad

 

I'm finding the following behaviour a bit confusing and am wondering if anyone can provide an explanation...

 

The code below is borrowed from RobG's post (http://forum.43oh.com/topic/2680-group-buy-10-o-rgb-smd-led-with-built-in-controller/page-4#entry23841 )

 

If I try to run at 16MHz the strip does not behave/work as expected

BCSCTL1 = CALBC1_16MHZ;
DCOCTL = CALDCO_16MHZ;

However, if I set DCOCTL to CALDCO_1MHZ and keep BCSCTL1 at CALBC1_16MHZ (see code below) , the strip works fine...

 

Is the clock calibration on my G2553 shot? How would one reset this?

#include <msp430g2553.h>
typedef unsigned char u_char;
typedef unsigned int u_int;
typedef struct {
	u_char r;
	u_char g;
	u_char b;
} RGBLED;
#define DATA_OUT_PIN BIT7
void sendRGB(u_char numberOfLEDs);
RGBLED data[15] = { 0, }; // 0.5m strip
void main(void) {
	WDTCTL = WDTPW + WDTHOLD;
	BCSCTL1 = CALBC1_16MHZ;
	DCOCTL = CALDCO_1MHZ;
	// setup USIB
	P1SEL |= DATA_OUT_PIN;
	P1SEL2 |= DATA_OUT_PIN;
	UCB0CTL0 |= UCCKPH + UCMSB + UCMST + UCSYNC; // 3-pin, 8-bit SPI master
	UCB0CTL1 |= UCSSEL_2; // SMCLK
	UCB0BR0 |= 0x03; // 1:3 - 16MHz/3 = 0.1875us
	UCB0BR1 = 0;
	UCB0CTL1 &= ~UCSWRST;
	u_char x = 0;
	u_char dir = 1;
	while (1)
	{
		u_char c = 0;
		while (c < 14) {
			data[c].r = x; //x << 3;
			data[c].b = x; //127 - (x << 3);
			data[c].g = x; //255 - (x << 3);
			data[c+1].r = 255 - x;
			data[c+1].b =255 - x ;
			data[c+1].g =255 - x;
			c+=2;
		}
		sendRGB(15);
		__delay_cycles(100000);
		if (dir) {
			x++;
			if (x == 0xFF)
				dir = 0;
		}
		else {
			x--;
			if (x == 0)
					dir = 1;
		}
	}
}
void sendRGB(u_char numberOfLEDs) {
	u_int c = 0;
	u_char led = 0;
	u_char leds[3];
	u_char d;
	while (c < numberOfLEDs) {
		leds[0] = data[c].g;
		leds[1] = data[c].r;
		leds[2] = data[c].b;
		while (led < 3) {
			u_char b = 0;
			d = leds[led];
			while (b < 8) {
				while (!(IFG2 & UCB0TXIFG))
					;
				(d & 0x80) ? (UCB0TXBUF = 0xF0) : (UCB0TXBUF = 0xC0);
				d <<= 1;
				b++;
			}
			led++;
		}
		led = 0;
		c++;
	}
	__delay_cycles(800); // delay 50us to latch data, may not be necessary
}

Thanks for any pointers

Naren

Share this post


Link to post
Share on other sites

The DCOCTL register "tunes" the frequency that is set using the DCSCTL1 register. So in your case, you'll still be running at a frequency that is about 16MHz. The DCOCTL register consists of two parts, one part changes the base frequency the CPU is operating upon, the other is a "modulation" value, which allows for a finer control on the long run. However, this modulation will cause the CPU to run on a non-constant frequency; it changes every so many cycles to allow for an overall frequency that is closer to the desired value.

This modulation might just screw up your ws2812b driver, because it might require a stable frequency.

 

On a totally different point, let me introduce you to the for-loop:

void sendRGB(u_char numberOfLEDs) {
	u_int c = 0;
	u_char led = 0;
	u_char leds[3];
	u_char d;
	while (c < numberOfLEDs) {
		leds[0] = data[c].g;
		leds[1] = data[c].r;
		leds[2] = data[c].b;
		while (led < 3) {
			u_char b = 0;
			d = leds[led];
			while (b < 8) {
				while (!(IFG2 & UCB0TXIFG))
					;
				(d & 0x80) ? (UCB0TXBUF = 0xF0) : (UCB0TXBUF = 0xC0);
				d <<= 1;
				b++;
			}
			led++;
		}
		led = 0;
		c++;
	}
	__delay_cycles(800); // delay 50us to latch data, may not be necessary
}

is fine, but you could also use:

void sendRGB(u_char numberOfLEDs) {
	u_int c;
	u_char led;
	u_char leds[3];
	u_char d;
        for (c = 0; c < numberOfLEDs; c++) {
		leds[0] = data[c].g;
		leds[1] = data[c].r;
		leds[2] = data[c].b;
                for (led = 0; led < 3; led++) {
                        uchar b;
			d = leds[led];
                        for (b = 0; b < 8; b++) {
				while (!(IFG2 & UCB0TXIFG))
					;
				(d & 0x80) ? (UCB0TXBUF = 0xF0) : (UCB0TXBUF = 0xC0);
				d <<= 1;
			}
		}
	}
	__delay_cycles(800); // delay 50us to latch data, may not be necessary
}

The for-loop is always written with two semi-colons in it:

for (a; b; c) d;
// or
for (a; b; c) {
  d;
}

It works like this:

  • do a
  • check condition b, if true
  • - do d
  • - do c
  • - go back to check b

the great advantage is that all the loop bookkeeping is tied closely together; initialising the variable, checking its condition, updating it for the next iteration through the loop.

There is no technical advantage in using it, but since you're doing things that match this looping structure more closely, a very intelligent compiler might just make it a teeny tiny bit more efficient than using the more verbose while-loop.

Share this post


Link to post
Share on other sites

Thanks roadrunner84 for the DCOCTL explanation. TI documentation hasn't been the easiest to read especially when it spans multiple documents.

 

The for-loop intro was thoughtful and yes, I've had enough experience with code to see your point.

 

Cheers.

Naren

Share this post


Link to post
Share on other sites

As a side note, perhaps putting a very short delay after the DCOCTL/BCSCTL1 commands would help a bit-

_delay_cycles(1000);

 

This might give the DCO some time to settle to its final frequency.

Also I always see DCOCTL set *before* BCSCTL1, but I'm not sure how much that matters.

Share this post


Link to post
Share on other sites

Setting UCB0BR0 to 3 or 4 works.

 

I am trying to run at a lower frequency (8MHz) and setting UCB0BR0 to 2 should work in theory but looks like there are longer delays than the ws2812b is willing to tolerate...

Right now with 16Mhz I see the leds work well with a 500KHz bitstream.

If that's the (only) sweet spot for the leds, the code needs to be reworked if I want to run at a lower frequency...

 

As an aside, for some reason the code lights up 14 of the 15 leds... the last one in the strip does not light up. The LED is not broken... and even after playing around with the array index I am not able to figure out why...

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...