Jump to content
Sign in to follow this  
Gareeeesh

Please help! I'm going mad trying to get this to work!

Recommended Posts

Hello! I know I've been posting loads of problems which you have all been very helpful with and I appreciate it.

I'm at the last stage in my project where I have to send an image as a byte array (values from 0-255) via serial communication, use H/W UART to receive bytes and map them onto my RGB LED display to create the image.

I've configured the UART at 115200 BAUD, clock at 18MHz, I'm using the UCA0 ISR routine to map the received bytes into a 3D array "image[8][8][3]" where the 1st, 2nd, and 3rd terms are row, column, RGB component, respectively.

 

I am seeing results on the display when sending individual bytes, but not the correct results. I'll post a video/screenshots to go with the code.

 

#include <msp430f5438a.h>
// TLC INPUTS
#define GSCLK BIT6 // P1.6 (SMCLK OUT pin 9 RF3)
#define MOSI BIT1 // P3.1 (UCB0MOSI pin 18 RF3)
#define SCLK BIT3 // P3.3 (UCB0CLK pin 16 RF3)
#define BLANK BIT2 // P1.2 (GPIO pin 5 RF3)
#define XLAT BIT5 // P1.5 (GPIO PIN 7 RF3)
// SERIAL COMMS
#define RX_PIN BIT5 // P3.5 (UCA0RXD bottom header)
// 74HC595
#define DATA BIT7 // P3.7 (UCB1MOSI)
#define CLOCK BIT5 // P5.5 (UCB1CLK)
#define LATCH BIT0 // P4.0 (GPIO)
//==============================================================
typedef unsigned char u_char;
typedef unsigned int u_int;
typedef unsigned short u_short;
typedef unsigned long u_long;
//=============================================================
// PROTOTYPES
void init(void); // put clock and GPIO pin directions etc
void init595(void);
void SPI_TLC(void); // for GSCLK + MOSI + DATA + CLOCK
void SPI_595(void);
void UART_init(void); // for RX
void updateTLC(void);
void SPI_shift(u_short);
void shiftOut(u_long);
void HSV2RGB(u_short*, u_short*, u_short*, short, u_char);
void SetVcoreUp (unsigned int level);
void calibrateClock(void);

//=====================================================================
// GLOBAL DEFINES
//======================================================================
#define NUMBER_OF_OUTS 48
#define NUMBER_OF_COLUMNS 16
#define UPDATE {P1OUT |= BLANK;P1OUT |= XLAT;P1OUT &= ~XLAT;P1OUT &= ~BLANK; updateTLC();}
#define OFF 0
#define SAT 255
u_short leds[NUMBER_OF_OUTS] = {0,}; // 0 - 15 Red Rows, 16 - 31 Blue Rows, 32 - 47 Green Rows {0, }
const u_long column16Array[NUMBER_OF_COLUMNS] = {0x8000,0x4000,0x2000,0x1000,0x0800,0x0400,0x0200,0x0100,0x0080,0x0040,0x0020,0x0010,0x0008,0x0004,0x0002,0x0001};
const u_long column8Array[8] = {0x0080,0x0040,0x0020,0x0010,0x0008,0x0004,0x0002,0x0001};
const u_char rowArray[NUMBER_OF_COLUMNS] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
//------------------------------------
// SERIAL GLOBAL VARIABLES
u_char image[8][8][3] = {0,};
u_char rowCnt = 0, colCnt = 0, rgb = 0;
volatile u_short x = 0,y;
u_char clear = 0;

void main(void)
{
    WDTCTL = WDTPW + WDTHOLD; // Kill watchdog timer
    init();
    updateTLC();
    P1OUT |= (XLAT);
    P1OUT &= ~(XLAT);
    _bis_SR_register(GIE);
    for(;
    {
            for(x=0;x<8;x++) // rows
            {
                for(y=0;y<8;y++) // columns
                {
                    leds[x] = image[x][y][0]*16; // Red
                    leds[x+32] = image[x][y][1]*16; // Green
                    leds[x+16] = image[x][y][2]*16; // Blue
                    UPDATE; // this line pulses TLC XLAT and then updates the TLC5940
                    shiftOut(column8Array[y]); // controls columns (74HC595 shift register)
                    leds[x]=0;
                    leds[x+32]=0;
                    leds[x+16]=0;
                    UPDATE;
                }
            }
        _bis_SR_register(LPM0_bits); // Enter LPM0, interrupts enabled
    }
}


void calibrateClock(void)
{
    SetVcoreUp (PMMCOREV_2); // Set VCore = 1.8V for 18MHz clock
    UCSCTL3 |= SELREF_2; // Set DCO FLL reference = REFO
    UCSCTL4 |= SELA_2; // Set ACLK = REFO
    __bis_SR_register(SCG0); // Disable the FLL control loop
    UCSCTL0 = 0x0000; // Set lowest possible DCOx, MODx
    UCSCTL1 = DCORSEL_6; // Select DCO range 24MHz operation
    UCSCTL2 = FLLD_1 + 547; //547 // Set DCO Multiplier for 18MHz
    __bic_SR_register(SCG0);                  // (N + 1) * FLLRef = Fdco
    __delay_cycles(562500);                     // (547 + 1) * 32768 = 18MHz
    do{                                            // Loop until XT1,XT2 & DCO fault flag is cleared
        UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + XT1HFOFFG + DCOFFG);
        // Clear XT2,XT1,DCO fault flags
        SFRIFG1 &= ~OFIFG; // Clear fault flags
    } while (SFRIFG1&OFIFG); // Test oscillator fault flag
                                             // Set FLL Div = fDCOCLK/2
                                   // Enable the FLL control loop
    // Worst-case settling time for the DCO when the DCO range bits have been
    // changed is n x 32 x 32 x f_MCLK / f_FLL_reference.
    // 32 x 32 x 18 MHz / 32,768 Hz = 562500 = MCLK cycles for DCO to settle
}



void init(void)
{
    calibrateClock();
// SETUP GPIO PINS AS OUTPUTS
    P1DIR |= (GSCLK + BLANK + XLAT + BIT0);
    P1SEL |= GSCLK; // P1.6 AS SMCLK OUT
    P1OUT &= ~(BLANK + XLAT);
    TA0CCR0 = 0xFFF;
    TA0CTL = TASSEL_2 + MC_1 + ID_0; // SMCLK, up mode, 1:1
    TA0CCTL0 = CCIE; // CCR0 interrupt enabled
    init595(); // only if SPI isnt used.
    SPI_TLC();
    UART_init();
}

void init595(void)
{
    P4DIR |= LATCH; P4OUT &= ~LATCH;
    P3DIR |= DATA; P3OUT &= ~DATA;
    P5DIR |= CLOCK; P5OUT &= ~CLOCK;
}

void SPI_TLC(void)
{
    // TLC: SPI setup UCB0
    P3SEL |= MOSI + SCLK; // P3.1 + P3.3, UCB0SIMO + UCB0CLK
    UCB0CTL0 = UCCKPH + UCMSB + UCMST + UCSYNC; // data captured on 1st UCLK edge/changed on follwing edge, MSB first, master, 3-pin SPI,synchronous
    UCB0CTL1 |= UCSSEL_2; // SMCLK
    UCB0BR0 |= 0x01; // 1:1
    UCB0BR1 = 0;
    UCB0CTL1 &= ~UCSWRST; // clear SW
}
void UART_init(void)
{
    // UART Setup UCA0
    P3SEL |= 0x30; // P3.4,5 = USCI_A0 TXD/RXD
    UCA0CTL1 |= UCSWRST; // **Put state machine in reset**
    UCA0CTL1 |= UCSSEL_2;    //SMCLK
    UCA0BR0 |= 9;            //115200 at 18MHz
    UCA0BR1 = 0;            //115200 at 18MHz
    UCA0MCTL = UCOS16 + UCBRF3 + UCBRF2; //Modulation
    UCA0CTL1 &= ~UCSWRST;    //Start USCI
    UCA0IE |= UCRXIE; // Enable USCI_A0 RX interrupt
}

void updateTLC(void)
{
    u_char ledCounter = NUMBER_OF_OUTS >> 1;
    while (ledCounter-- > 0)
    {
        u_char i = ledCounter << 1;
        UCB0TXBUF = leds[i + 1] >> 4;
        while (!(UCB0IFG & UCTXIFG)); // TX buffer ready? ????????? NEED TO CHECK THIS
        u_char unib = leds[i + 1] << 4;
        u_char lnib = (leds[i] >> 8) & 0x0F;
        UCB0TXBUF = unib | lnib;
        while (!(UCB0IFG & UCTXIFG)); // TX buffer ready? ????????? NEED TO CHECK THIS
        UCB0TXBUF = leds[i];
        while (!(UCB0IFG & UCTXIFG)); // TX buffer ready? ????????? NEED TO CHECK THIS
    }
}


void shiftOut(u_long val)
{
    P4OUT &= ~LATCH;
    int i;
    for (i = 0; i < 16; i++) // iterate over each bit 16
    {
        if(val & (1 << i)) {
            P3OUT |= DATA;
        } else {
            P3OUT &= ~DATA;
        }
        P5OUT |= CLOCK; // PULSE CLOCK
        P5OUT ^= CLOCK;
    }
    P4OUT |= LATCH; // PULSE LATCH
    P4OUT &= ~LATCH;
}

void SetVcoreUp (unsigned int level) // only used to calibrate clock at 18MHz
{
// Open PMM registers for write
PMMCTL0_H = PMMPW_H;
// Set SVS/SVM high side new level
SVSMHCTL = SVSHE + SVSHRVL0 * level + SVMHE + SVSMHRRL0 * level;
// Set SVM low side to new level
SVSMLCTL = SVSLE + SVMLE + SVSMLRRL0 * level;
// Wait till SVM is settled
while ((PMMIFG & SVSMLDLYIFG) == 0);
// Clear already set flags
PMMIFG &= ~(SVMLVLRIFG + SVMLIFG);
// Set VCore to new level
PMMCTL0_L = PMMCOREV0 * level;
// Wait till new level reached
if ((PMMIFG & SVMLIFG))
while ((PMMIFG & SVMLVLRIFG) == 0);
// Set SVS/SVM low side to new level
SVSMLCTL = SVSLE + SVSLRVL0 * level + SVMLE + SVSMLRRL0 * level;
// Lock PMM registers for write access
PMMCTL0_H = 0x00;
}

#pragma vector = TIMER0_A0_VECTOR
__interrupt void Timer_A0(void)
{_bic_SR_register_on_exit(LPM0_bits);}

#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++;
            }
        }
    }
}

 

The way I want the code to work is as follows:

1. row, column and rgb are initialised to 0.

2. byte received? is it less than 256? if yes then go to step 3, else wait for a new byte.

3. Then check the values of row and column so see if they are less than 8.

4. if rgb = 0, then assign the value of RXBUF to red component of current pixel (for row = 0, column = 0 & rgb = 0, then "image[0][0][0] = RXBUF" )

5. In "main()" each component of the 3D array is assigned to the corresponding value in the "leds[]" array, and the correct column is lit.

6. Increment rgb by one

7. Repeat for green and blue (rgb = 1, rgb = 2).

8. If rgb reaches 3, then rgb is reset to 0 and column is incremented by one

9. Repeat..

10. If column = 8, then row is incremented by one

11. Repeat until all spaces in array have been filled.

 

That is how I want it to work, but below is a video of what is actually happening:

 

BTW, I'm using this for the serial connection.

 

If you could just have a look at the video/program and if anything pops out that is obviously wrong then give me a shout, cos it is driving me insane trying to get this to work.

 

Thanks good people of 43oh!

Share this post


Link to post
Share on other sites

Sorry everyone for posting several of the same post. My connection froze and i clicked refresh a bunch of times. I didn't realise I'd posted anything until I went back to the General section of the forum and saw them.

 

That's it fixed now, so just ignore this.

Share this post


Link to post
Share on other sites

Sorry everyone for posting several of the same post. My connection froze and i clicked refresh a bunch of times. I didn't realise I'd posted anything until I went back to the General section of the forum and saw them. It was an accident, and I am genuinely stuck on this so please don't think I did it deliberately. Thanks

No worries. Fixed.

Share this post


Link to post
Share on other sites

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:

post-30355-0-08163000-1365978868_thumb.png

 

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.

Share this post


Link to post
Share on other sites

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.

Okay, thanks for clearing that up. I had a look through the ASCII character table and there are some extended characters such as ? (ASCII value 189) which could maybe be used?

Using the command line is only for testing purposes to see if the program was mapping the RX'd bytes to the correct RGB component of the correct pixel.

So for now if I am using the command line I'll only put one character in the x="?" so that only one byte is transmitted. 

I wanted to be able to send a whole byte array using that program from here, that is why I wanted to use the gimp (.c) source file to use as a byte array (since I'd be able to create an image, export it as .c file, then load it into that program and send it to my display - which would be awesome :) )

Share this post


Link to post
Share on other sites

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++;
    }
  }
}

 


Share this post


Link to post
Share on other sites

First, you must use a separate cable/USB-UART interface, the launchpad can only run at 9600 baud.

 

I'm using the H/W UART on the MSP430F5438 board which can do greater than 9600 baud, with a USB-to-Serial converter cable connected to an RS-232 shifter board. The UART RX at 115200 baud is working because previous to this, I created a program in which I could control each pixel with buttons on my keyboard through HyperTerminal,

. Also, my code is weird? :P

 

 

#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++;
    }
  }
}

Right, I see. It is essentially the same code. I'll compare the results with the previous and see if it makes a difference.

As usual Roadrunner - the most helpful dude ever!

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...