Jump to content

Gareeeesh

Members
  • Content Count

    48
  • Joined

  • Last visited


Reputation Activity

  1. Like
    Gareeeesh reacted to davebranton in 16x16 RGB LED Matrix Displaying Images - Help!   
    What you're attempting may very well be beyond the capabilities of an MSP430. I did a project a while back driving RGB LEDs, and that required multiple FPGAs to actually drive the LEDs fast enough, and used megabit serial (!) to transmit data to the FPGAs. This was generated by an LPC1768 running at a clock speed well out of reach of an MSP430.
     
    Anyway, let's do some calculations:
     
    You have 16x16 display, which means you have 256*3 = 768 bits of data to output if all you were going to do is have each LED either on or off (no PWM).
     
    You are trying to PWM with 255 brightness levels for each LED, which means you must output 768 bits of data 255 times over, and fast enough to not perceive any flickering. I've found 75Hz is about what's required to avoid any perception of flickering, so you'll have to output 768*255*75 bits of data per second. That's about 14 megabits, which means you'd need a clock speed of 30Mhz just to get the data out - leaving no time over to calculate anything or load any data into registers or anything.
     
    Also, if you want to get this data into the MSP over serial, I presume you'll want to store the image data in RAM on the MSP. Your 16x16x3 bytes of memory is 768 bytes, which is more RAM than is available in the MSP.
     
    So my advice is to drop the number of brightness levels you need to (say) four, this is two bits per channel per pixel. Forget about trying to calculate HSV to RGB transformations on the MSP430. Work on just getting the LEDs on or off to start with, to make sure you can get everything running fast enough to get that going. And then try four brightness levels.
     
    My feeling is though that to get enough speed for this project you will either need to get into assembly or use a more powerful micro. The LPC1768 is a nice one, but anything with a bit more grunt is probably what you'll need. Your shift registers go up to 100Mhz input, why not use a micro that can give you that kind of speed?
  2. Like
    Gareeeesh got a reaction from cubeberg in Please help! I'm going mad trying to get this to work!   
    @Roadrunner & Tripwire - Thanks for all your help guys!. Mission accomplished!
    Here's another terrible video of my results!

  3. Like
    Gareeeesh got a reaction from tripwire in Please help! I'm going mad trying to get this to work!   
    @Roadrunner & Tripwire - Thanks for all your help guys!. Mission accomplished!
    Here's another terrible video of my results!

  4. Like
    Gareeeesh reacted to roadrunner84 in Please help! I'm going mad trying to get this to work!   
    First, you must use a separate cable/USB-UART interface, the launchpad can only run at 9600 baud.
    Second, this code is weird
    #pragma vector = USCI_A0_VECTOR __interrupt void USCI0RX_ISR(void) { if(UCA0RXBUF < 256) { if(rowCnt < 8) //if(rowCnt < 16) { if(colCnt < 8) //if(colCnt < 16) { if( rgb == 0 ) { image[rowCnt][colCnt][rgb] = UCA0RXBUF; // R } else if ( rgb == 1 ) { image[rowCnt][colCnt][rgb] = UCA0RXBUF; // G } else if( rgb == 2 ) { image[rowCnt][colCnt][rgb] = UCA0RXBUF; // B } else if( rgb == 3) { colCnt++; rgb = 0; } rgb++; } else { colCnt = 0; rowCnt++; } } } } You are either incrementing the row or parsing the byte. Same for wrapping from blue to red.
    Also, a single byte will never ever be 256 or higher, this check can be left out, provided you set your UART to use 8 bit or less.
    Your code for red, green and blue is identical, separating it is artificial in this case. You could just as well use a single line for this.
    Alternatively do something like this
    #pragma vector = USCI_A0_VECTOR __interrupt void USCI0RX_ISR(void) { if(rowCnt < 8) //if(rowCnt < 16) { image[rowCnt][colCnt][rgb] = UCA0RXBUF; } rgb++; if (rgb >= 3) { rgb = 0; colCnt++; if (colCnt >= 8) { colCnt = 0; rowCnt++; if (rowCnt >= 8) { // do something here? } } } } More compact (slightly less readable) code:
    #pragma vector = USCI_A0_VECTOR __interrupt void USCI0RX_ISR(void) { if(rowCnt < 8) //if(rowCnt < 16) { image[rowCnt][colCnt][rgb] = UCA0RXBUF; } if (++rgb >= 3) { rgb = 0; if (++colCnt >= 8) { colCnt = 0; rowCnt++; } } }  
  5. Like
    Gareeeesh reacted to tripwire in Please help! I'm going mad trying to get this to work!   
    It took me a while to figure that command line out, and it turns out to be the source of the problem. The command you're using is:
     
    set /p x="255" <nul >//./COM5
     
    Apart from the redirections, the command is setting the value of environment variable "x" to a value provided by the user, and showing "255" as a prompt. The input is redirected from nul so the command doesn't wait for user input. The output is redirected to the COM5 port. Crikey!
     
    It looks like a roundabout way of printing the string "255" to the COM5 port. What that means is the UART first receives the character '2', then '5', then another '5'. In other words three bytes instead of one.
     
    You'll need to find some way of sending raw data to the COM port instead of text. I'm not sure what to suggest for that.
     
    Anyway, the ASCII values of those characters are 50, 53 and 53. That's what's actually stored in the first three slots of the image array. When you inspect the array, however, they're shown as 2, 5, 5. That's the debugger helpfully displaying the values of character variables as characters rather than the ASCII value. If you click on the rows of that display you'll see the value in decimal, like so:

     
    Alternatively you can right click on the "image" array variable and "Add Watch Expression". Then you can right click the "image" variable in the watch window and set Number Format->Decimal. After that the debugger should show all the pixels as decimal values from 0-255.
  6. Like
    Gareeeesh reacted to tripwire in Sending Image as RGB Array to G2553   
    No, the array is arranged like this:
    GIMP array layout for a 4x4 pixel image. Pixel coordinates are shown as (X,Y). 0 1 2 3 4 5 ... R G B R G B R G B R G B R G B R G B R G B R G B R G B R G B R G B R G B R G B R G B R G B R G B 0 | \_/ \_/ | | \_/ \_/ | | | | | | | | | | | | | | | | | | \_ Null terminator | (1,0) (2,0) | | (1,1) (2,1) | | | | | | | | | | | | | \___________________/ \___________________/ \___________________/ \___________________/ | | | | Row 0 Row 1 Row 2 Row 3  
    The red, green and blue components of each pixel are stored as three consecutive elements of the array. The pixel at (X, Y) begins at index ((Y * WIDTH) + X) * 3) - that's the red component. Then green is at (((Y * WIDTH) + X) * 3) + 1) and blue is at (((Y * WIDTH) + X) * 3) + 2).
     
    For the example you quoted, that would mean:
    Pixel (7,2) red is at bytes_to_send[ (( 2 * gimp_image_width ) + 7) * 3] Pixel (7,2) green is at bytes_to_send[ ((( 2 * gimp_image_width ) + 7) * 3) + 1] Pixel (7,2) blue is at bytes_to_send[ ((( 2 * gimp_image_width ) + 7) * 3) + 2]  
    Hope that helps
  7. Like
    Gareeeesh reacted to roadrunner84 in Sending Image as RGB Array to G2553   
    The red component would be at 117, the green component . would be at 117+1=118 and the blue component would be at 117+2=119.
  8. Like
    Gareeeesh reacted to roadrunner84 in Sending Image as RGB Array to G2553   
    No, a unsigned short array of [16][16] is 512 bytes long. Since you have 512 bytes of RAM in total you have no RAM left for execution of the code. A [16][16] array of unsigned char can be done. But if you want to store HSV, you have the same limitation; you can store only 2 or 3 bits per component. Unless you put limitations on yourself. You could for example use 6 1 1 values; 6 bits hue, 1 bit (colour/white) saturation, 1 bit (on/off) value. Then you have 26=64 different colours (rainbow palette), plus white, plus black.
    Or 5 1 2; 32 colours in either full or half intensity, full white, half intensity white and off.
    But please, you're getting ahead of yourself again. You'd rather go for monochrome 8-bit value for now, if you prefer to stick with [16][16] dimension. You can choose to drive green and keep red and blue on a hard value of 0, or drive each colour with the same value and have a white image.
  9. Like
    Gareeeesh reacted to roadrunner84 in Sending Image as RGB Array to G2553   
    Erm.... no. Not really.
    You could link the two launchpads together over some interface (SPI, UART, etc.) and have one dedictate its behaviour to reading or writing data it sends or receives over this interface to RAM. But that would require both chips to run some more code and sacrifice pins. Additionally both will need some memory dedictated to this communication as buffers. Plus, the "external RAM" is slower than the internal RAM.
    If you really want to try something like this, try splitting the display from 16x16 into two 16x8 displays and load both with half the image. You'd need to keep them in sync, but that won't be much of a problem if you use both as output device from a PC. If you let them run standalone with moveing images you'd need to have some kind of sync pulse from one to the other.
    Again, such a solution will make your code more complex and thus a little harder to debug.
  10. Like
    Gareeeesh reacted to roadrunner84 in Sending Image as RGB Array to G2553   
    I hope you do understand that I kind of lost track of the current state of your code.
    If you have not enough memory to store [16][48] or [16][16][3] then you can juggle your bytes as much as you want, you just loose information. To show a picture of [16][16][3] you need at least that much memory. You can try to squeeze data in a more compact format, but I'd recommend to reduce the picture size first and verify that it's working as expected.
     
    Old-school computers (like the C64 and the Amiga) used to share 16 bits of memory over the three colour components. Most used the 5-6-5 scheme; 5 bits red, 6 bits green and 5 bits blue. This was chosen this way because the human eye is most sensitive to green light. To implement this, you'd need some more code and processing time. But again, please verify that the rest is working as expected before using these kind of tricks.
     
    The columnArray and rowArray contain constant data, since you're not changing them, prefix these lines with const to make these values immutable. As a result, the compiler will place those values in flash instead of RAM, this gives you another 48 bytes to play with.
     
    Oh, and you send plain picture data over serial, if you'd ever run out of sync, you'll never have your picture showing up as expected anymore.
     
    Think of a way to solve your memory issue. As you won't fit a 16x16 picture in RAM, not even using the 16-bit trick, you're bound to take a compromise. Will you reduce pixel count, will you accept using 1 byte (2 or 3 bits per colour) per pixel, or will you use a different chip with 1kB RAM?
  11. Like
    Gareeeesh reacted to roadrunner84 in RGB to HSV Conversion with MSP430G2231 - Help!   
    Sorry, my bad. I had assumed you used the timer to drive PWM out of the MSP directly, those support 16-bits. Ofcourse your shift register supports only 8 bits values, for which my suggested alterations did not account.
    I think *r is in the range 0-255 before multiplying with v. So you should (in theory) multiply by v/255. however, that is always 0 except for v==255.
    Maybe using
    unsigned short R; // do the fancy calculations, but with R instead of *r R *= v; *r = R / 256;  
    Will give you better results without resorting to expensive floating point code again.
     
    Yet again, sorry or throwing you off like this.
  12. Like
    Gareeeesh reacted to RobG in RGB to HSV Conversion with MSP430G2231 - Help!   
    Good to hear.
    Now when you have your function fixed, try speeding things up by changing TACTL's ID_x and timerCounter.
    Try to figure out how many cycles are needed, HSV2RGB about 450, times 8 LEDs, ~4000. 
    @16MHz that's 250us. You have 8 rows, 2ms, that would let you do 500fps!
    Now you have to figure out how many cycles it takes to updateTLC, but I think you should be OK.
  13. Like
    Gareeeesh reacted to RobG in RGB to HSV Conversion with MSP430G2231 - Help!   
    Are you sure you are not overflowing some of your values?
    Put the break point in HSV2RGB and see what's going on.
     
    Here for example,
    *r *= v; *g *= v; *b *= v; or here
    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; FYI, here are two examples which use 8/16 bit RGB
    http://www.ruinelli.ch/rgb-to-hsv
    http://code.google.com/p/streumix-frei0r-goodies/wiki/Integer_based_RGB_HSV_conversion
  14. Like
    Gareeeesh reacted to roadrunner84 in RGB to HSV Conversion with MSP430G2231 - Help!   
    for(; { allcolsON(); // just sets columns to +3.6V for( z = 0 ; z < 8 ; z++) { if (hue < 60) { ... } } hue++; _bis_SR_register(LPM0_bits); } You're "testing" a lot of stuff at the same time, why don't you start out with the inner for loop removed (where z count from 0 to 8) and set z to a fixed value of 0. only 1/8th of your LEDs should light up, but it's enough to test the colour behaviour.
    Next, are you sure you wake up from low power mode in the right time? Just for a short test replace it temporarily with a __delay_cycles(10000) to see what's happening.
    Then, describe how the erratic stuff happens, is it going too fast, can you still make out the different colours (although too fast)?
    Also, try to put the call to updateTCL() in the main loop instead of the timer interrupt, just after going to low power mode. SPI communication might take up quite a while of time, you preferably do not do this in your timer interrupt.
  15. Like
    Gareeeesh reacted to RobG in RGB to HSV Conversion with MSP430G2231 - Help!   
    Again, you are trying to control your timing in the main loop.
    Do not use _delay_cycles(), it's evil (and I know something about evil, I am Dr. Doofenshmirtz after all.)
    HSV2RGB takes about 450 clock cycles, with other conditionals probably somewhere about 500, so this function is not a problem.
     
    main and ISR should run independently.
     
    ISR restarts PWM to make sure we don't have dark LEDs and flickering. It is also used to time animation and push updated data to TLC.
    Once it's time for next animation frame, it wakes MCU and allows main loop to run (once.)
     
    main loop's job is to update data for the next frame and then go back to sleep. That update should happen faster than frame rate, which I see is not a problem here.
     
     
    BTW, short in MSP430 C is the same thing as int so short int is redundant
     
     
    main { ... setup ... for(; { //OK, it's time to calculate new data hue = something * someParam; // now update LED array using my HUE function for each LED { HUE(...) } // OK, done updating LED array with new values // let's go back to sleep, ISR will update TLC when the time is right _bis_SR_register(LPM0_bits); } } ISR { // 4096 GS clock cycles have been generated, time to restart PWM clockXLATandBLANK // increase timer counter timerCounter++; if (timerCounter == 0x08) // is it time to for next frame? { updateTLC(); // data was already prepared by main loop when it was awake last time timerCounter = 0; _bic_SR_register_on_exit(LPM0_bits); // wake up main loop so that it can prepare data for the next frame } }
  16. Like
    Gareeeesh reacted to roadrunner84 in RGB to HSV Conversion with MSP430G2231 - Help!   
    I didn't test my code, it's "all from the head" coding
    I think your code is almost right, but you should supply it with pointers to the values to write in
    // this line HSV2RGB(leds[z],leds[z+8],leds[z+16],hue,sat,val); // should be like this HSV2RGB(&leds[z],&leds[z+8],&leds[z+16],hue,sat,val);
  17. Like
    Gareeeesh reacted to roadrunner84 in RGB to HSV Conversion with MSP430G2231 - Help!   
    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 character, an integer or a floating point number. Most other types are called-by-reference or pointers. The best known example of a pointer is the array.
    An array is a pointer to a set of values (or other pointers), by using the square brackets you can select an indexed element in this set.
    Would I want to access tha fourth integer in the array foo, I'd write foo[4]. By using the square brackets, without knowing I'm "dereferencing" the pointer called foo.
    A pointer is basically a value that is equal to the address of the value in memory. This means that foo would be the address at which the first element of the array is stored. When writing foo[4] I tell C to take that address, add 4 integer sized memory blocks to that and take the value at that position.
    Another way to dereference a pointer is using the asterisk in front of the variable name; this means that writing foo[0] is the same as writing *foo. Would I want to have the fourth element (which is index 3, as the first element is index 0), I could write foo[3] or *(foo + 3). Here you can more explicitly see that I add the number of element I want to skip over to the pointer and take the value at that location.
     
    Now, this all sounds quite complicated, but it really isn't. It just takes some practice to figure out where and when to write an asterisk ( * ) or an ampersand ( & ). For the rest, try to ever realize wether your variable name denotes a value or a memory address.
    // Pointer cheat sheet int v; // v is an integer int* p; // p is a pointer to an integer p = &v; // take the address of v and assign that to p *p = 6; // take the value where p is pointing to and assign 6 to it // expect a integer value as first element, this value is a copy of the parameter supplied at the function call and this copy will no longer exist after this function ends // expect a pointer to an integer as second element, this pointer is known as p in this function, although the pointer seizes to exist after the function ends, the address it was pointing to still exists void func(int q, int* p) { *p = 6; // write the value 6 to the location p is pointing to q = 6; // write 6 to the local variable q, this is of no use because the value is gone when the function ends p = 6; // WRONG! let p point to address 6 in memory, unless you're really sure, you'll probably access memory you should not touch! } Sometimes you do alter the address a pointer is pointing to, for example in printing routines. Secretly, strings are arrays of characters. Which means you cannot supply a string to any function, just the location of the first character in it.
    char* str = "hello"; print(str); // supply print with a pointer to the first character of "hello" // The first character is str[0] or *str // A string is always internally "terminated" by a '\0' character to tell C this is that last character. void print(char* s) { while(*s != '\0') // is the value of the address s is pointing to equal to 0? serial.putch(*s++); // supply the value of the address s is pointing to to putch and then increment the pointer s by one element. }  
    So what I'm doing in the HSV function is writing a value (255, or t) to the address r is pointing to. Also I must supply the address where r is kept to the function, so that's why I cannot pass leds[z], but must pass &leds[z] or leds + z. Since the latter is much more difficult to read, I suggest you stick to the first method of writing for now.
  18. Like
    Gareeeesh reacted to roadrunner84 in RGB to HSV Conversion with MSP430G2231 - Help!   
    /* 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.
  19. Like
    Gareeeesh reacted to RobG in TLC5940 examples   
    That piece of code toggles SPI data line
     
    Here's a template you can use
     
    #include <msp430g2231.h> #define SCLK_PIN BIT5 #define MOSI_PIN BIT7 #define GSCLK_PIN BIT4 #define BLANK_PIN BIT2 #define XLAT_PIN BIT1 #define DCPRG_PIN BIT7 //P2 #define VPRG_PIN BIT0 typedef unsigned char u_char; typedef unsigned int u_int; #define NUMBER_OF_LEDS 16 u_int leds[NUMBER_OF_LEDS] = { 0, }; u_char timerCounter = 0; void updateTLC(); void sendData(u_int data); void main(void) { WDTCTL = WDTPW + WDTHOLD; // disable WDT DCOCTL |= DCO0 + DCO1; // DCO = 15.25MHz BCSCTL1 |= RSEL0 + RSEL1 + RSEL2 + RSEL3; // as above BCSCTL2 |= DIVS_3; // 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; P1DIR |= GSCLK_PIN; // port 1.4 configured as SMCLK out P1SEL |= GSCLK_PIN; P2SEL &= ~(BIT6 | BIT7); P2OUT &= ~DCPRG_PIN; P2DIR |= DCPRG_PIN; // setup timer CCR0 = 0xFFF; TACTL = TASSEL_2 + MC_1 + ID_0; // SMCLK, up mode, 1:1 CCTL0 = CCIE; // CCR0 interrupt enabled updateTLC(); P1OUT |= XLAT_PIN; P1OUT &= ~XLAT_PIN; _bis_SR_register(GIE); int loopCounter = 0; while (1) { // add your logic below // this loop will be executed every 16.384ms, ~61Hz //leds[0] = R1 //leds[1] = G1 //leds[2] = B1 //this is not the best way to do it, but it's very simple to understand if (loopCounter < 256) { leds[0]++; // R } else if (loopCounter < 512) { leds[1]++; // G } else if (loopCounter < 768) { leds[0]--; // dim R } else if (loopCounter < 1024) { leds[2]++; // B } else if (loopCounter < 1280) { leds[1]--; // dim G } else if (loopCounter < 1536) { leds[0]++; // R } else if (loopCounter < 1792) { leds[2]--; // dim B } else if (loopCounter < 2048) { leds[0]--; // dim R } 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; sendData(leds[i + 1]); sendData(leds[i]); } } #pragma vector = TIMER0_A0_VECTOR __interrupt void Timer_A0(void) { P1OUT |= BLANK_PIN; P1OUT |= XLAT_PIN; P1OUT &= ~XLAT_PIN; P1OUT &= ~BLANK_PIN; timerCounter++; if (timerCounter == 0x08) { // 0x08 - 2ms * 8 = 16.384ms, ~61Hz updateTLC(); timerCounter = 0; _bic_SR_register_on_exit(LPM0_bits); } } void sendData(u_int data) { u_char c = 0; while (c < 12) { (data & 0x0800) ? (P1OUT |= MOSI_PIN) : (P1OUT &= ~MOSI_PIN); P1OUT |= SCLK_PIN; P1OUT &= ~SCLK_PIN; data <<= 1; c++; } }
  20. Like
    Gareeeesh reacted to RobG in TLC5940 examples   
    Right, if I was using hardware SPI, then P1.6 would be used. I am doing software SPI and using P1.7, which is MOSI on 2553.
     
     
     
    Well, this could be your problem, TLC5940 is a sink driver, meaning outputs should be connected to cathodes and anodes are common.
    Try connecting a single LED, anode to Vcc and cathode to TLC, and see if it lights up.
×
×
  • Create New...