Jump to content
43oh

RGB to HSV Conversion with MSP430G2231 - Help!


Recommended Posts

Hi all!

 

I was wondering if anyone has used this algorithm with the G2231 before?

I'm trying to use it but I keep getting an error message saying:

"program will not fit into available memory.  placement with alignment fails for section ".text" size 0x142e ." 

 

The reason I am trying to implement this is so I can represent exact colours for RGB LEDS in a large-ish matrix.

 

Does anyone know a method to decrease the amount of memory being consumed by this algorithm, or increase the amount of memory allocated to ".text"?

Any advice would be greatly appreciated.
 

Here is the code I am using:

#include <msp430g2231.h>
#include <math.h>
// TLC inputs
#define SCLK_PIN	BIT5
#define MOSI_PIN	BIT7
#define GSCLK_PIN	BIT4
#define BLANK_PIN	BIT2
#define XLAT_PIN	BIT1
#define VPRG_PIN	BIT0
// 595 Inputs
#define DATA BIT6  // DS -> P2.6
#define LATCH BIT6 // ST_CP -> 1.6
#define CLOCK BIT7 // 11 -> 2.7
// --------------  MACROS ------------------------------------------//
#define setHigh(n)   ( P1OUT |= n )                   // Only for TLC!
#define setLow(n)   ( P1OUT &= ~n )                   // TLC!!
#define pulse(n)  do { setHigh(n); setLow(n); } while(0)
// ---------------------------------------------------------------------//
typedef unsigned char u_char;
typedef unsigned int u_int;
typedef unsigned short int us_int;
// Prototypes
void init(void);
void updateTLC();
void sendMOSI(u_int data);
void pulseClock ( void );
void pinWrite ( unsigned int, int );
void shiftOut(unsigned char);
void shift(unsigned int);
void allrowsON(void);
void allrowsOFF(void);
void allcolsON(void);
void delay (unsigned int);
void bounce (int);
void knight(int);
void HSV2RGB(u_char *r, u_char *g, u_char *b, signed int h, u_char s, u_char v);

#define NUMBER_OF_LEDS 24
#define NUMBER_OF_COLUMNS 8
#define NUMBER_OF_ROWS 8

u_int leds[NUMBER_OF_LEDS] = { 0, };  // 0 - 7 Red Rows, 8 - 15 Green Rows, 16 - 23 Blue Rows
u_int rows[NUMBER_OF_ROWS] = { 0, };
u_char timerCounter = 0;
short int icount = 0;

void init(void)
{
	WDTCTL = WDTPW + WDTHOLD; // disable WDT
	DCOCTL |= DCO0 + DCO1; // DCO = 15.25MHz
	BCSCTL1 |= RSEL0 + RSEL1 + RSEL2 + RSEL3; // as above
	BCSCTL2 |= DIVS_2; // divide clock by 8

	P1OUT &= ~(VPRG_PIN + BLANK_PIN + XLAT_PIN + SCLK_PIN + MOSI_PIN );
	P1DIR |= VPRG_PIN + BLANK_PIN + XLAT_PIN + SCLK_PIN + MOSI_PIN;
	// 595
	P2SEL &= ~(CLOCK + LATCH);
	P1DIR |= DATA; 			// Setup pins as outputs
	P2DIR |= (CLOCK + LATCH);
	P1DIR |= GSCLK_PIN; 	// port 1.4 configured as SMCLK out
	P1SEL |= GSCLK_PIN;

	// setup timer
	CCR0 = 0xFFF;
	TACTL = TASSEL_2 + MC_1 + ID_0; // SMCLK, up mode, 1:1
	CCTL0 = CCIE; // CCR0 interrupt enabled
}

void main(void)
{
 init();
 updateTLC();

 pulse(XLAT_PIN);

	_bis_SR_register(GIE);

	bounce(300);
	knight(300);
	int loopCounter = 0;
	int p,hue;

	for(; {
		// this loop will be executed every 16.384ms, ~61Hz
		//leds[0-7]   = Red
		//leds[8-15]  = Green
		//leds[16-23] = Blue

		for( hue = 0 ; hue < 360 ; hue ++)       // this is where the problem lies!
			{
				HSV2RGB(0,0,0,hue,1,1);
			}
		allcolsON();
		if (loopCounter < 512) {//512
			for( p = 0 ; p < 8 ; p++) // R (0-7)
				{
					leds[p]++;
				}
		} else if (loopCounter < 1024) {  // G (8-15)
			for( p = 8 ; p < 16 ; p++)
				{
					leds[p]++;
				}
		} else if (loopCounter < 1536) {  //dim R (0-7)
			for( p = 0 ; p < 8 ; p++)
				{
					leds[p]--;
				}
		} else if (loopCounter < 2048) { // B (16 - 23)
			for( p = 16 ; p < 24 ; p++)
				{
					leds[p]++;
				}
		} else if (loopCounter < 2560) { //dim G (8-15)
			for( p = 8 ; p < 16 ; p++)
				{
					leds[p]--;
				}
		} else if (loopCounter < 3072) { // R (0-7)
			for( p = 0 ; p < 8 ; p++)
				{
					leds[p]++;
				}
		} else if (loopCounter < 3584) { //dim B (16-23)
			for( p = 16 ; p < 24 ; p++)
				{
					leds[p]--;
				}
		} else if (loopCounter < 4096) { //dim R  (0-7)
			for( p = 0 ; p < 8 ; p++)
				{
					leds[p]--;
				}
			}
		  else
		  	  {
			 loopCounter = 0;
		  	  }

		// do not edit below
		loopCounter++;
		// sleep

		_bis_SR_register(LPM0_bits);
	}
}

void updateTLC()
{
 u_char ledCounter = NUMBER_OF_LEDS >> 1;


	while (ledCounter-- > 0)
		{
		u_char i = ledCounter << 1;
		sendMOSI(leds[i + 1]);
		sendMOSI(leds[i]);
		}
}
// the HSV algorithm
void HSV2RGB(u_char *r, u_char *g, u_char *b, signed int h, u_char s, u_char v)
{
 int i,z;
 float f, p, q, t, hf, sf, vf;

 hf=(float)h;
 sf=(float)s;
 vf=(float)v;

 sf /=255;

 if( sf == 0 ) { // achromatic (grey)
 *r = *g = *b = vf;
 return;
 }

 hf /= 60;            // sector 0 to 5
 i = floor( hf );
 f = hf - i;            // factorial part of h
 p = (u_char)(v * ( 1 - sf ));
 q = (u_char)(v * ( 1 - sf * f ));
 t = (u_char)(v * ( 1 - sf * ( 1 - f ) ));

 switch( i ) {
 case 0:
 *r = v;
 *g = t;
 *b = p;
 break;
 case 1:
 *r = q;
 *g = v;
 *b = p;
 break;
 case 2:
 *r = p;
 *g = v;
 *b = t;
 break;
 case 3:
 *r = p;
 *g = q;
 *b = v;
 break;
 case 4:
 *r = t;
 *g = p;
 *b = v;
 break;
 default:        // case 5:
 *r = v;
 *g = p;
 *b = q;
 break;
 }

 for( z = 0 ; z < 8 ; z++)
 {
	 leds[z] = *r;    // red pins
	 leds[z+8] = *g;  // green pins
	 leds[z+16] = *b; // blue pins
 }
}

void allcolsON(void)
{
	for( icount = 0; icount < NUMBER_OF_COLUMNS ; icount++ )
		{
			shiftOut(1 << icount); //1 <<
		}
		for( icount = NUMBER_OF_COLUMNS-1; icount >= 0 ; icount-- )
		{
			shiftOut(1 << icount); //1 <<
		}
}

void allrowsON(void)
{
	int x;
	for( x = 0; x < 8 ; x++ )
		{
			leds[x] = 4095;
		}
		for( x = 7; x >= 0 ; x-- )
			{
			leds[x] = 4095;
			}
}

void allrowsOFF(void)
{
	int x;
		for( x = 0; x < 8 ; x++ )
			{
				leds[x] = 0;
			}
			for( x = 7; x >= 0 ; x-- )
				{
				leds[x] = 0;
				}
}

void delay(unsigned int ms)
{
 while (ms--)
    {
      __delay_cycles(2000); // set for 16Mhz change it to 1000 for 1 Mhz
    }
}

void bounce (int time)
{
	int a;
	allcolsON();
	for( a = 0; a < 8 ; a++)      // red
	{
		leds[a] = 4095;
		delay(time);
		leds[a] = 0;
	}
	for( a = 7 ; a >= 0 ; a--)
	{
		leds[a] = 4095;
		delay(time);
		leds[a] = 0;
	}
	for( a = 0; a < 8 ; a++)  // yellow
		{
			leds[a] = 4095;
			leds[a+8] = 3500;
			delay(time);
			leds[a] = 0;
			leds[a+8] = 0;
		}
		for( a = 7 ; a >= 0 ; a--)
		{
			leds[a] = 4095;
			leds[a+8] = 3500;
			delay(time);
			leds[a] = 0;
			leds[a + 8] = 0;
		}
		for( a = 0; a < 8 ; a++)   // green
		{
			leds[a+8] = 4095;
			delay(time);
			leds[a+8] = 0;
		}
		for( a = 7 ; a >= 0 ; a--)
		{
			leds[a+8] = 4095;
			delay(time);
			leds[a+8] = 0;
		}
		for( a = 0; a < 8 ; a++)   // cyan
		{
			leds[a+8] = 4095;
			leds[a+16] = 4095;
			delay(time);
			leds[a+8] = 0;
			leds[a+16] = 0;
		}
		for( a = 7 ; a >= 0 ; a--)
		{
			leds[a+8] = 4095;
			leds[a+16] = 4095;
			delay(time);
			leds[a+8] = 0;
			leds[a+16] = 0;
		}
		for( a = 0; a < 8 ; a++)   // blue
		{
			leds[a+16] = 4095;
			delay(time);
			leds[a+16] = 0;
		}
		for( a = 7 ; a >= 0 ; a--)
		{
			leds[a+16] = 4095;
			delay(time);
			leds[a+16] = 0;
		}
		for( a = 0; a < 8 ; a++)   // magenta
		{
			leds[a] = 4095;
			leds[a+16] = 4095;
			delay(time);
			leds[a] = 0;
			leds[a+16] = 0;
		}
		for( a = 7 ; a >= 0 ; a--)
		{
			leds[a] = 4095;
			leds[a+16] = 4095;
			delay(time);
			leds[a] = 0;
			leds[a+16] = 0;
		}
		for( a = 0; a < 8 ; a++)   // white
			{
				leds[a] = 4095;
				leds[a+8] = 4095;
				leds[a+16] = 4095;
				delay(time);
				leds[a] = 0;
				leds[a+8] = 0;
				leds[a+16] = 0;
			}
			for( a = 7 ; a >= 0 ; a--)
			{
				leds[a] = 4095;
				leds[a+8] =4095;
				leds[a+16] = 4095;
				delay(time);
				leds[a] = 0;
				leds[a+8] = 0;
				leds[a+16] = 0;
			}
}

void knight(int time)
{
	int x;

	allrowsON();

	for( x = 0 ; x < 8 ; x++)
	{
		shift(1 << x);
		delay(time);
	}
	for( x = 7 ; x >= 0 ; x--)
	{
		shift(1 << x);
		delay(time);
	}
	allrowsOFF();
}

#pragma vector = TIMER0_A0_VECTOR
__interrupt void Timer_A0(void) {
	setHigh(BLANK_PIN);
	pulse(XLAT_PIN);
	setLow(BLANK_PIN);
	timerCounter++;
		if (timerCounter == 8) { // 0x08 - 2ms * 8 = 16.384ms, ~61Hz
		updateTLC();
		timerCounter = 0;
		_bic_SR_register_on_exit(LPM0_bits);
	}
}

void sendMOSI(u_int mosi)
{
	u_char c = 0;
	while (c < 12)
	{
		(mosi & 0x0800) ? (P1OUT |= MOSI_PIN) : (P1OUT &= ~MOSI_PIN);
		pulse(SCLK_PIN);
		mosi <<= 1;
		c++;
	}
}


void shiftOut(unsigned char val)
{
 
  P2OUT &= ~LATCH;

  pinWrite(DATA, val);
  pulseClock();
  P2OUT |= LATCH;
  P2OUT &= ~LATCH;
}

void shift(unsigned int val)
{
  P2OUT &= ~LATCH;
  int i;
  for (i = 0; i < 8; i++)  {
      pinWrite(DATA, (val & (1 << i)));
      pulseClock();
  }

  P2OUT |= LATCH;
  P2OUT &= ~LATCH;
}

void pinWrite( unsigned int bit, int val )
{
if (val){
    P1OUT |= bit;
  } else {
    P1OUT &= ~bit;
  }
}

// Pulse the clock pin
void pulseClock( void )
{
  P2OUT |= CLOCK;
  P2OUT ^= CLOCK;
}

 

Link to post
Share on other sites
  • Replies 35
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

Popular Posts

Crash course pointers in C:   C only knows to types of things: 1) values 2) pointers every variable you use is either of these types, most primitive types are called-by-value for example a charac

G2231 (2K flash) is a bad choice of chip for this. Try a G2553 (16K flash) or maybe G2452 (8K flash).   Sent from my C3PO via Tapatalk    

If this is your full code (what are you trying to do in bounce()?), you don't need a full-lfedged HSV2RGB function     for( hue = 0 ; hue < 360 ; hue ++) // this is where the problem lies!      

Assume you tried telling compiler to optimize for code size.

 

Since your problem is code size, one quick way to reduce size would be to combine the code for

AllRowsOn and AllRowsOff (parameterize the function with the value to assign, then make AllRowsOn or Off be macros).

 

I would also look at your main program - see what code the compiler generates for all those little loops.

You might be able to make them into a function passing a few parameters (could see if that shrinks or expands code size).

Similarly could look at bounce to see if can make into multiple calls to a function to reduce the amount of code.

(Not sure whether extra code for generality/function preamble would mean net gain or loss.)

 

--

Asside:

 

Code is a bit confusing, why are you passing 0, 0, 0 in to first 3 arguments of HSV2RGB

 

HSV2RGB(0,0,0,hue,1,1);

 

code says those arguments are supposed to be pointers to unsigned characters.

 

 

void HSV2RGB(u_char *r, u_char *g, u_char *b, signed int h, u_char s, u_char v)

 

 

I don't know off hand what is stored at address 0 on the 430, but seems like poor style, even if that is available RAM.

It also means that inside the call to HSV2RGB r = g = b (not clear why this is desired).

Suggest create a variable and pass in the address.

 

Also unclear why call HSV2RGB 360 times, since looks like effect would be the same if just called it the final time, or even just precomputed the

final values (unless there is there some side effect of storing a value in location 0).  

Link to post
Share on other sites

If this is your full code (what are you trying to do in bounce()?), you don't need a full-lfedged HSV2RGB function

 

  for( hue = 0 ; hue < 360 ; hue ++) // this is where the problem lies!
            {
                HSV2RGB(0,0,0,hue,1,1);
            }

This is the only place you're using the HSV2RGB function, and as I can see here (apart from the fact that you'd need to provide variables in the first three arguments), you're not using the saturation and value parameters; they're always 1.

So, what you actually "need" is a ranbow lookup table, which would take 360*3 bytes, assuming you'd store the full table. However, you can cut it in 6 symmetric hexants.

uint8 HueLUT[60] = {...};

if (hue < 60)
  R = 255, G = HueLUT[hue], B = 0;
else if (hue < 120)
  R = HueLUT[120 - hue], G = 255, B = 0;
else if (hue < 180)
  R = 0, G = 255, B = HueLUT[hue - 120];
else if (hue < 240)
  R = 0, G = HueLUT[240 - hue], B = 255;
else if (hue < 300)
  R = HueLUT[hue - 240], G = 0, B = 255;
else if (hue < 300)
  R = 255, G = 0, B = HueLUT[300 - hue];
else // hue >= 360
  R = 255, G = 255, B = 255; // white
Link to post
Share on other sites

Thanks for your replys.

@igor - The compiler is set to optimise for code size. I have removed bounce(), allrowsON() and allrowsOFF() from the code because they aren't really necessary at the moment, but it seems that the issue is primarily the HSV2RGB function. With respect to this:

 

Code is a bit confusing, why are you passing 0, 0, 0 in to first 3 arguments of HSV2RGB Gareeeesh, on 05 Feb 2013 - 21:26, said: HSV2RGB(0,0,0,hue,1,1); code says those arguments are supposed to be pointers to unsigned characters. Gareeeesh, on 05 Feb 2013 - 21:26, said: void HSV2RGB(u_char *r, u_char *g, u_char *b, signed int h, u_char s, u_char v) I don't know off hand what is stored at address 0 on the 430, but seems like poor style, even if that is available RAM. It also means that inside the call to HSV2RGB r = g = b (not clear why this is desired). Suggest create a variable and pass in the address.

I'll be honest, I'm not entirely sure about how to generate values for r,g and b for that function call, the 0's were there just as a test to see if the uC had enough memory for the floating point operations required - which it didn't. If you have any suggestions that'd be really appreciated .

 

@roadrunner84 - bounce() was just a function I used to test the rows of my matrix, so it doesn't really matter that much just now.

 

This is the only place you're using the HSV2RGB function, and as I can see here (apart from the fact that you'd need to provide variables in the first three arguments), you're not using the saturation and value parameters; they're always 1.

The only reason that I used the values of 1 for value and saturation was just to see if the program would run. My end design will (hopefully) be able to interact ith a computer, similar to this - http://www.instructables.com/id/64-pixel-RGB-LED-Display-Another-Arduino-Clone/#intro - therefore it would be ideal to be able to represent all possible colours (or as close to as possible) and shades.

 

I did think that the G2231 would not have enough memory for this kind of function so I had ordered a G2553 but I'm still waiting on its arrival.

 

Thanks
 

Link to post
Share on other sites

You mean you weren't planning on using a GUI frondend? Just a plain command line? Hmm, that might be a little more complex. You could (if you're using a command line annyway) make a command line application, just google on how to interface with a serial port from (for example) visual C++. The user interaction can be done using scanf() and printf().

Alternatively, use a website to convert a desired color form HSV to RGB and enter those values into your command line in Energia ;)

 

If you feed HSV or HSL to a microcontroller, the best you could get is the same depth resolution in RGB, you cannot enhance the abilities by moving the calculation to the microcontroller.

Link to post
Share on other sites
/*
h = 0 - 359
s = 0 - 255
v = 0 - 255
*/
void hsvToRgb(unsigned short* r, unsigned short* g, unsigned short* b, short h, unsigned char s, unsigned char v)
{
  unsigned char i, p, q, t;
  float fs;

  if (h < 60) i = 0;
  else if (h < 120) i = 1;
  else if (h < 180) i = 2;
  else if (h < 240) i = 3;
  else if (h < 300) i = 4;
  else if (h < 360) i = 5;
  else return;

  fs = (h / 60.0 - i) * s;
  p = 255 - s;
  q = 255 - fs;
  t = 255 - s + fs;

  switch(i){
    case 0: *r = 255, *g = t, *b = p; break;
    case 1: *r = q, *g = 255, *b = p; break;
    case 2: *r = p, *g = 255, *b = t; break;
    case 3: *r = p, *g = q, *b = 255; break;
    case 4: *r = t, *g = p, *b = 255; break;
    case 5: *r = 255, *g = p, *b = q; break;
  }
  *r *= v;
  *g *= v;
  *b *= v;
}

You see it takes way less floating points now?

Even better, there is only one FP calculation! Alas, there are two input variables in it: fs = (h / 60.0 - i) * s, in this i is is derived from h and thus not an input.

Would we want to make a lookup table off of this, it would be 60 * 255 cells (15K), so that's unacceptable.

On the other hand, if we define h - i * 60 as h_rem, the formula would become fs = (h_rem / 60) * s or fs = (h_rem * s) / 60.

So why is this nice? because we just reduced the "inputs" to a single value.

With these changes, there are no floating point calculations left. there is still a division, which is quite expensive, but not as much as any floating point operation.

/*
h = 0 - 359
s = 0 - 255
v = 0 - 255
*/
void hsvToRgb(unsigned short* r, unsigned short* g, unsigned short* b, short h, unsigned char s, unsigned char v)
{
  unsigned char i, p, q, t;
  unsigned char fs;

  if (h < 60) i = 0;
  else if (h < 120) i = 1;
  else if (h < 180) i = 2;
  else if (h < 240) i = 3;
  else if (h < 300) i = 4;
  else if (h < 360) i = 5;
  else return;

  fs = ((h - i * 60) * s) / 60;
  p = 255 - s;
  q = 255 - fs;
  t = 255 - s + fs;

  switch(i){
    case 0: *r = 255, *g = t, *b = p; break;
    case 1: *r = q, *g = 255, *b = p; break;
    case 2: *r = p, *g = 255, *b = t; break;
    case 3: *r = p, *g = q, *b = 255; break;
    case 4: *r = t, *g = p, *b = 255; break;
    case 5: *r = 255, *g = p, *b = q; break;
  }
  *r *= v;
  *g *= v;
  *b *= v;
}

Oh, note that the outputs are 16-bit integers, so in the range 0 - 65535.

Link to post
Share on other sites

Right, so would i declare h,s and v as floats in the function call and prototype? like:

//at top
#include <math.h>
.
.
void hsvToRgb(float h, float s, float v){
    int r, g, b;

    int i = floor(h * 6);
    int f = h * 6 - i;
    int p = v * (1 - s);
    int q = v * (1 - f * s);
    int t = v * (1 - (1 - f) * s);

    switch(i % 6){ // ive not changed this bit yet
        case 0: r = v, g = t, b = p; break;
        case 1: r = q, g = v, b = p; break;
        case 2: r = p, g = v, b = t; break;
        case 3: r = p, g = q, b = v; break;
        case 4: r = t, g = p, b = v; break;
        case 5: r = v, g = p, b = q; break;
    }

    return (r * 255, g * 255, b * 255);
}

How else could h,s and v be between 0 and 1 if they are not floats?

I apologise for my poor programming skills :-P

Cheers

Link to post
Share on other sites

Thanks for clarifying that for me.

I've included the code you provided and tried to create a test function call to first of see if the code will run, but I got about a thousand errors.

I thought I could create a couple of loops to create the values for h (0-359),s(0-255) and v(0-255).

short int hue;
unsigned char sat, val;
int z;
	for( z = 0 ; z < 8 ; z++)
	{
		for( hue = 0 ; hue < 360 ; hue++)
		{
			for( sat = 0 ; sat < 256 ; sat++)
			{
				for( val = 0; val < 256 ; val++)
				{

					HSV2RGB(leds[z],leds[z+8],leds[z+16],hue,sat,val);
				}
			}
		}
	}

Something like that. I really appreciate the help you've given me so far!

Link to post
Share on other sites

See in the case statement when you are setting *r,*g and *b :

 

switch(i){

case 0: *r = 255, *g = t, *b = p; break;

case 1: *r = q, *g = 255, *b = p; break;

case 2: *r = p, *g = 255, *b = t; break;

case 3: *r = p, *g = q, *b = 255; break;

case 4: *r = t, *g = p, *b = 255; break;

case 5: *r = 255, *g = p, *b = q; break;

}

Would I not need to do a similar thing and put an ampersand in front of the "t" for example?

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.


×
×
  • Create New...