Home > Uncategorized > Nokia LCD + WiiNunchuck with MSP430

Nokia LCD + WiiNunchuck with MSP430

I bought myself couple of msp430 Launchpad’s when they first came out. At $4.30 with free shipping from TI, it’s one heck of a deal for what comes in the package. I wanted to use the lesser of the two mcu’s that comes in the package to do something cool. I first worried about 2kB flash size being an issue, but I was wrong. The 16bit architecture yields smaller compiled text and it packs enough space to do build some serious applications. The number of peripherals and features these mcu’s support are rather generous and I found these mcu’s quite pleasant/fun to work with.

Having the option to develop in linux was important to me. With msp430-gcc and mspdebug, I had all the toolchain necessary to start coding for these little suckers. So I wanted to see if it was possible to make a simple hand-held gaming device with just one chip. This required a small LCD screen and a user input device like Wii Nunchuck.

The source code that produces that runs the setup above is available here:  http://www.remnantone.com/pkgs/msp430/noklcd.zip

The code is just barebones. It contains just enough to communicate with the LCD and Wii Nunchuck. Read through after the break for more details.

As my first msp430 project, I wanted to try communicating with Nokia 6100 LCD I got from Sparkfun. In addition, I wanted to use Wii Nunchuk to provide user input and do something fun with the LCD. Several hurdles jump out when you start thinking about projects like this:

  1. Can you do hardware SPI with nokia LCD, or software bitbang?
  2. Are there existing Nokia LCD libraries I can use? How hard is it to convert libs written for other MCU’s?
  3. How do you do hardware I2C with msp430? What about I2C from software?
  4. I have 10 GPIO pins, am I going to run out of them?
  5. Do I need an external crystal for higher frequencies?

As it turns out, my learning experience for this project took many digressing paths, which ultimately helped me understand more about these value line mcu’s. These divergences from the core goal of the project was helpful, and they served to build necessary know-how for piecing together the necessary parts.

The challenges of working with msp430 valueline mcu’s compared to avrs:

  • internal digital clock (DCO) needs software controlled calibration to operate above 1Mhz – it can go upto 16Mhz
  • operating voltage is 3.3v and current output/sink is different (smaller) from avr’s
  • Linux toolchain, msp430-gcc, is not supported by TI, and there are certain quirks. For example, TI provides example source code, but they only work on their compilers that run only on Windows (for now).
  • uart is not so friendly under linux (see: link here)
  • not as much working code examples compared to avr’s.

Having pointed out those challenges, I couldn’t have made progress on this little project without the resources and great amount of information from www.43oh.com. A small plug: the members are great, and it’s one of the best technical discussion forums I’ve visited.

Prerequisites

Only one frequency is calibrated by default on  msp430G2xx1 series chips. Out of the box, they run the main clock at 1Mhz. I needed a way to speed this up, considering the fact that I might need to refresh the LCD screen as fast as possible. The datasheet shows operating DCO at 16Mhz requires about 3.6V. I decided on 12Mhz since I am supplying 3.3v through voltage regulator and assumed that it would make mcu operation more stable. However, you won’t be able to run these chips at higher frequencies right off the bat, because they are missing calibration values. These calibration values are basically 2 set of numbers indicating how fast the DCO should “tick”. Each frequency has 2 bytes of reserved area in the flash. Notice the calibration values are missing (0xff) for the higher frequencies.

Now, the calibration method works like this:

  1. Use auxiliary clock (ACLK) driven from 32.768Khz crystal to trigger interrupts at known time intervals.
  2. Use the main clock driven from DCO to increment a number between each interval.
  3. Crank up or down DCO speed until the counter matches what the theoretical value should be.

Typically, the asynchronous interrupt  frequency 32.768Khz is scaled down by 8. This means that each interrupt triggers at 32768 / 8 = 4096Hz, or 4096 times a second. In order to calibrate the DCO to run at 1Mhz, the main clock needs to be fast enough to increment the counter to at least 244 = 4069 * 244 = 999424Hz (~1Mhz) within the interval. Think of turning a FM radio search dial to find your FM station. You dial it up and down until you find the “right” frequency to tune into that particular FM channel. That’s how DCO calibration works in a nutshell.

Here’s the C code that illustrates what I described above:

void set_dco_c(unsigned int delta)            // Set DCO to selected frequency
{
  unsigned int Compare, Oldcapture = 0;
  P1OUT &= ~BIT0;

  BCSCTL1 |= DIVA_3;                        // ACLK = LFXT1CLK/8
  TACCTL0 = CM_1 + CCIS_1 + CAP;            // CAP, ACLK
  TACTL = TASSEL_2 + MC_2 + TACLR;          // SMCLK, cont-mode, clear

  while (1)
  {
    while (!(CCIFG & TACCTL0));             // Wait until capture occured
    TACCTL0 &= ~CCIFG;                      // Capture occured, clear flag
    Compare = TACCR0;                       // Get current captured SMCLK
    Compare = Compare - Oldcapture;         // SMCLK difference
    Oldcapture = TACCR0;                    // Save current captured SMCLK

    if (delta == Compare)
      break;                                // If equal, leave "while(1)"
    else if (delta < Compare)
    {
      P1OUT ^= BIT0;
      DCOCTL--;                             // DCO is too fast, slow it down
      if (DCOCTL == 0xFF)                   // Did DCO roll under?
        if (BCSCTL1 & 0x0f)
          BCSCTL1--;                        // Select lower RSEL
    }
    else
    {
      P1OUT ^= BIT0;
      DCOCTL++;                             // DCO is too slow, speed it up
      if (DCOCTL == 0x00)                   // Did DCO roll over?
        if ((BCSCTL1 & 0x0f) != 0x0f)
          BCSCTL1++;                        // Sel higher RSEL
    }
  }
  TACCTL0 = 0;                              // Stop TACCR0
  TACTL = 0;                                // Stop Timer_A
  BCSCTL1 &= ~DIVA_3;                       // ACLK = LFXT1CLK

  P1OUT |= BIT0;
  for (i = 0; i < 0x4000; i++);           // SW Delay
}

There’s lots of helpful info on 43oh.com forums on calibrating DCO. NatureTM and simpleavr from the forums have helped make this process a lot easier/understandable.

Hardware

If you are interested in replicating this setup, you need:

Nokia 6100 LCD breakout is pricey, considering the lcd itself can be had for less than 8 bucks on ebay. However, the connectors are fiddly and you need to build voltage booster circuit to provide backlight for it. If you have the means to do so, I’d recommend homemade PCB using copper board and save some $$.

Below is the shot of the wiring on the breadboard. I’ll provide the schematics if this project grows a bit more sophisticated.

The wiring is made such that the LCD breakout board can be just stacked on top of the mini breadboard, having VBATT pin inserted at the left most socket on the breadboard. Notice the pins S1, S2 (push buttons) are not being used.

Interfacing Nokia 6100 LCD

There are already tons of examples on interfacing against this particular LCD. There aren’t any working examples for msp430′s, but the existing libraries I found online are relatively architecture-independent to be easily reworked. Combining bits from numerous source code I found here, I put together a library for msp430.  The only significant changes I’ve made are the macros to deal with bit masking on IO pins, and making necessary IO configuration a bit more decoupled from the main code.

The breakout board I got from sparkfun was Epson. You will need to experiment and figure out which LCD driver chip you have. Only two variants exist for these LCD’s: 1) epson 2) phillips. The commands are slightly different across these chips, but the macros in the code takes care of switching between the two.

The breakout board takes care of up-converting the voltage for backlight LED given lower input voltage. At 3.3v, the LED pin draws 324mA. I can’t drive this much current directly from the IO pins and had to supply a direct connection to the voltage source (LM regulator). Similarly, the GND pin for the breakout needs to be directly connected to ground instead of pulling IO pin low (small current sink).

The library only contains 4 major functions:

void lcd_command(uint8_t data);
void lcd_data(uint8_t data);
void lcd_init(void);
void lcd_fillrect(int color, uint8_t x, uint8_t dx, uint8_t y, uint8_t dy);

Once initialized, you can do primitive drawing by first sending the necessary coordinates and colors over SPI-like bus. The LCD controller chip allows you to draw arbitrarily sized rectangles by accepting start and end positions of both x and y coordinates. For example:

    lcd_command(PASET);
    lcd_data(x);
    lcd_data(x2);

    lcd_command(CASET);
    lcd_data(y);
    lcd_data(y2);

    lcd_command(RAMWR);
    for(i=0; i<(int)dx*dy+1; i++)
      lcd_command(color);  // on 8-bit colors

Interfacing Wii Nunchuck

What made making Wii nunchuck tricky was the fact that I could not use built in USI hardare peripheral to communication I2C. The 2 pins dedicated to USI were being used to drive the LCD and I needed software controlled I2C to be able to use other pins to talk to the nunchuck.

Fortunately, there are good reference source code I can use. Combining Peter Fluery’s I2C lib here and simpleavr’s work here, I created my own. The heart of the library is just 2 functions – i2c_write and i2c_read. Please keep in mind that this is Master-to-Slave R/W transmission only. Thus, it only works when your code assumes the role of the I2C master. The entire I2C protocol supports couple of other operating modes such as Slave-to-Master, multiple slaves (arbitration), etc… They are not implemented here. Here’s what the code looks like:

uint8_t i2c_write(uint8_t b)
{
  int8_t i;

  for (i=0;i<8;i++)
  {
    if(b & 0x80)  I2C_SDA_HI;
    else          I2C_SDA_LO;

    I2C_SCL_TOGGLE;       // delay, clock HI, delay, clock LO
    b <<= 1;
  }

  // leave SDA HI, pull up HI, made SDA input pin for read
  I2C_SDA_INPUT;

  HDEL;
  I2C_SCL_HI;             // clock back up

  b = I2CPIN & (1<<SDA);  // get the ACK bit

  HDEL;
  I2C_SCL_LO;            

  I2C_SDA_OUTPUT;         // change direction back to output
  HDEL;

  return (b == 0);        // return ACK value
}

uint8_t i2c_read(uint8_t ack)
{
  int8_t i;
  uint8_t b=0x00;

  I2C_SDA_INPUT;

  for (i=0;i<8;i++)
  {
    HDEL;
    I2C_SCL_HI;                    // clock HI

    b <<= 1;
    if (I2CPIN & (1<<SDA)) b |= 1; // read bit

    HDEL;
    I2C_SCL_LO;                    // clock LO
  }

  I2C_SDA_OUTPUT;                  // change direction to output on SDA line

  if (ack) I2C_SDA_LO;             // set ACK, more to read
  else     I2C_SDA_HI;             // set NAK, all done!

  I2C_SCL_TOGGLE;                  // clock pulse
  I2C_SDA_HI;                      // leave with SDA HI
  HDEL;

  return b;      // return received byte
}

Again, perusal of the code is encouraged for possible fixes, improvements.

Once the I2C lib was in place, communicating with Wii Nunchuck was straight forward. It’s as simple as running these sequence of functions:

#define NC_ADDR 0xA4  // write address - read address becomes 0xa5

void nc_init()
{
  i2c_start(NC_ADDR+I2C_WRITE);
  i2c_write(0x40);
  i2c_write(0x00);
  i2c_stop();                // send STOP transition
}

void nc_readdata(uint8_t *buf, int* data)
{
  unsigned int i;
  for(i=0; i<1; i++)
  {
    i2c_start(NC_ADDR+I2C_WRITE);
    i2c_write(0x00);
    i2c_stop();               // send STOP transition
  }
  _delay_us(100);

  i2c_start(NC_ADDR+I2C_READ);   // send DEVICE address
  for(i=0; i<5; i++)
    buf[i] = i2c_read(1);
  buf[5] = i2c_read(0);
  i2c_stop();               // send STOP transition

  nc_extractdata(buf, data);
}

There are tons of good resources on interfacing wii nunchuck, and I won’t detail them here.

TODO

As of this writing, I had 500 bytes of flash space left on the chip. I proceeded to write a small game on it and ran out of space. I’ll need to either 1) optimize/shrink my code so that produced .text region is smaller, 2) or use a secondary chip to offload rendering graphics to the LCD. Stay tuned for the update to this project.  :-)

About these ads
Categories: Uncategorized
  1. Roberto
    February 3, 2011 at 4:09 am | #1

    Neat work!!! Do you know if these LCDs are dis-assemblable, so I can make a projector with it?

    • February 3, 2011 at 9:11 pm | #2

      hmm.. probably not. I have the raw LCD’s without the breakout and I find them quite fiddly and fragile to work with.

  2. Luke
    February 3, 2011 at 3:27 pm | #3

    I have a question, is there any reason you didn’t assign the LCD controller pins to the pins that are not used by USI. As I understand it the LCD controller is a bit banging protocol anyways, so If you assign those pins you could get rid of your software i2c, hence freeing up some code space. Anyhow, if there’s a reason for having those pins there let me know.

    • February 3, 2011 at 9:11 pm | #4

      Right. I could have used USI to drive i2c. The LCD breakout contains other extras such as 3 led’s and 2 push buttons. I wanted to fit the breakout board directly on top of the breadboard, and it was purely for physical alignment and pin mapping reasons. I could’ve arranged things a lot better, but then it was a quick and dirty proof of concept I wanted to do. Also, it encouraged development for software i2c, which might come in handy later on. :-) Thanks for the comment.

  3. Jim Jones
    March 7, 2011 at 5:21 am | #5

    You’ve done some interesting work. I expect my launchpad to ship tomorrow. I’ve spent the entire weekend learning as much as I can about all this, I’ve even prepared code in CCS. The launchpad will be my first step into the micro controller world. Do you think there is a cheaper alternative to buying a breakout board for the lcd? There are a lot of projects I would like to do, but spending $30 bucks for these breakout boards is more than I can handle right now (gas, college, and food eat up my cash). Keep up the good work, I’ve learned a little more from your research.

    • May 14, 2011 at 1:46 am | #6

      Hi jim
      Apologies for the late respond. Glad to know you found my post ssmewhat useful. The lcd module itself is quite brittle. The connector pin is too ssall to work with unless you are an alien with uncanny soldering skills.you can probably build your own, however, with enough patience and some spare connectors to burn.I’ve tried it with a buddy of mine but ended up butchering one lcd in the process.

  4. sumit381
    April 21, 2011 at 11:31 pm | #7

    Very Nice. I used this LCD with a PIC32 to make a phone. http://skdevblog.wordpress.com/snapple-iphony/
    I got an MSP430 a while ago, so I’m think of try it out with this LCD. I’m of doing some CNC related stuff, so maybe I can use this screen as an output.

    Anyway, nice work!

    • May 14, 2011 at 1:48 am | #8

      Cool. I quite like the msp430′s. I did some avr’s before but something about these 16bit mcus gets me going.

  5. John Brown
    February 27, 2014 at 2:58 pm | #9

    i’ve been trying to do something similar with the MSP430 launchpad but i didn’t have the experience. This msp430 tutorial covers a lot of the topics but I needed the information from your post to try and make it work. thank you and place keep posting results if you get more. I want to see what is possible.

    John

  1. February 2, 2011 at 10:30 pm | #1
  2. February 2, 2011 at 10:47 pm | #2
  3. February 3, 2011 at 11:44 am | #3

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: