Bare-metal mbed; connecting to the Gameduino
(Last modified 3 Jan 2012)

I have a Gameduino and I have an mbed; guess what's next...


For those who don't know, the Gameduino is a small board that takes SPI data in from virtually anything and provides VGA signals out.  Runs on 3.3 VDC, takes about 25 mA of current, runs at an SPI clock of up to 8 MHz, and provides a wealth of graphics coolness.  Check the home page above for lots more info.

First off, here is my lashup development platform...

mbed hooked to Gameduino

Yeah, that's not the most robust wiring hookup, but it's good enough for test purposes.  The mbed is plugged into the white experimenter's board and the Gameduino is the green Arduino shield in the foreground.  I'm powering the mbed from the USB cable, then using the mbed's 3.3 VDC output on pin 40 to power the Gameduino.  Note that I hooked 3.3 VDC to both the 3.3 VDC and 5 VDC inputs on the Gameduino.

I really bought the Gameduino (that's a wad to type; I'll call it GD for now) so I could put text on a VGA monitor from a small MCU.  So no fancy graphics in this write-up; here's some text on a 15" NEC MultySync monitor...

Text from the Gameduino

The code supplied by the GD website is in C++, using typical object-oriented programming techniques.  I usually work in C, so I recoded the class library as C functions.  This file, called gdsupport.c, also contains a custom function that allows me to keep the support functions target-independent.  The main program (in gd_test.c) calls a support function named GD_register() and provides pointers to the three target-specific functions needed for GD support.  These are _select(), which enables the SPI connection to the GD, _xchg(), which exchanges a single byte of data with the GD, and _deselect(), which disables the SPI connection.  Code in the gd_test program is invoked through these function pointers whenever the support library needs to exchange data with the GD.

Here is the entire gd_test.c program, showing the mbed and Gameduino setup and the technique for displaying text on the VGA screen:




/*
 *  gd_test.c      test platform for playing with the Gameduino
 *
 *  This program allows the mbed development board to control a Gameduino
 *  via SPI.
 *
 *  The code here initializes the Gameduino, then displays ASCII text on
 *  the GD's VGA screen.  I developed this primarily as a way to play with
 *  text on the GD, since the rest of the world is using the GD for graphics.  :-)
 */

#define  OWNER

#include  <stdio.h>
#include  <string.h>
#include  <errno.h>

#include  "LPC17xx.h"
#include  "gdsupport.h"


/*
 *  I'm going to be the owner of errno (the global error number variable),
 *  so I need to undefine it here, then redefine it as an int.  All other
 *  modules that link with me must do the same, declaring errno as extern.
 */
#undef  errno
int                            errno;


/*
 *  Rather than put target-specific SPI code inside this module, I use
 *  a set of three routines that are target-specific, then rely on the
 *  gdsupport module to do all of the non-specific SPI operations.
 *
 *  This works through the following three callback functions.  test_select()
 *  is called by the gdsupport code to select the Gameduino.  test_xchg()
 *  is called to exchange a byte of data with the GD.  test_deslect()
 *  is called to deselect the GD.
 */
void                        test_select(void);
unsigned char               test_xchg(unsigned char);
void                        test_deselect(void);

/*
 *  outstr() is a wrapper function that lets me display a null-terminated
 *  string on the GD's VGA screen using the Gameduino outch() function.
 */
void                        outstr(char  *s);


unsigned int                SystemCoreClock;                // global variable, core clock frequency

/*
 *  Use P0.16 as chip-select for the GameDuino.
 */
#define  GD_CS_BIT            16
#define  GD_CS_MASK           (1<<GD_CS_BIT)

#define  GD_ENABLE            (LPC_GPIO0->FIOCLR = GD_CS_MASK)
#define  GD_DISABLE           (LPC_GPIO0->FIOSET = GD_CS_MASK)


/*
 *  The following strings are stored and initialized two different ways.  HelloConst
 *  is read direcly from read-only data (.rodata) while HelloRam is copied from
 *  flash to RAM and read from RAM (.data).
 *
 *  These strings are here to confirm proper operation of the linker script that
 *  builds this program.
 */
const char                 HelloConst[] = " Const Hello, world!                    ";
char                       HelloRam[] =   " RAM Hello, world!                      ";



/*
 *  init      perform low-level initialization of the mbed board
 *
 *  main() must call this routine before doing any other major tasks on the mbed!
 */
void init(void)
{
/*
 *  Clock initialization for the Mbed development board.  This board
 *  uses a 12 MHz oscillator as the main clock source.  This code
 *  sets the core clock to 96 MHz.
 */
    LPC_SC->SCS = 0x20;                  // enable external oscillator (12 MHz crystal)
    while (!(LPC_SC->SCS & (1<<6))) ;    // wait for main oscillator to stablilize
   
    LPC_SC->CLKSRCSEL = 0x01;            // set main oscillator (12 MHz crystal) as PLL source
    LPC_SC->CCLKCFG = 0x03;              // set CPU clock (CCLK) to PLL0 output / 4
   
    LPC_SC->PCLKSEL0 = 0x0;              // peripherals use CPU clock / 4
    LPC_SC->PCLKSEL1 = 0x0;              // peripherals use CPU clock / 4

/*
 *  The following code sets up the Mbed's core clock to 96 MHz.
 *  This code sets PLL0 output to 384 MHz.  The CPU clock
 *  configuration above (CCLKCFG) divides this value by 4
 *  to get a final CPU clock of 96 MHz.
 */
    LPC_SC->PLL0CFG = (0<<16) | (15<<0);  // PPL0 config, M=16, N=1, Fout=384
    LPC_SC->PLL0FEED = 0xAA;             // feed the PLL
    LPC_SC->PLL0FEED = 0x55;

    LPC_SC->PLL0CON = 0x01;              // enable PLL0
    LPC_SC->PLL0FEED = 0xAA;             // feed the PLL
    LPC_SC->PLL0FEED = 0x55;

    while (!(LPC_SC->PLL0STAT & (1<<26)))    ;        // wait for PLL0 lock

    LPC_SC->PLL0CON = 0x03;              // enable and connect PLL0
    LPC_SC->PLL0FEED = 0xAA;             // feed the PLL
    LPC_SC->PLL0FEED = 0x55;

    while (!(LPC_SC->PLL0STAT & ((1<<25) | (1<<24))))  ;    // wait for PLL0 enable and connect

    SystemCoreClock = 96000000;          // need to make this value available globally
   
/*
 *  Set up PLL1 for use as a USB clock.  The output must be set to 48 MHz, based
 *  on an oscillator of 12 MHz.
 */
    LPC_SC->PLL1CFG = 0x00000023;        // PLL1 config, M=4 (bits 4:0=3), P=2 (bits 6:5=1)
    LPC_SC->PLL1FEED = 0xAA;             // feed the PLL
    LPC_SC->PLL1FEED = 0x55;

    LPC_SC->PLL1CON = 0x01;              // enable PLL1
    LPC_SC->PLL1FEED = 0xAA;
    LPC_SC->PLL1FEED = 0x55;

    while (!(LPC_SC->PLL1STAT & (1<<10)))  ;        // wait for PLL1 lock

    LPC_SC->PLL1CON = 0x03;              // enable and connect PLL1
    LPC_SC->PLL1FEED = 0xAA;             // feed the PLL
    LPC_SC->PLL1FEED = 0x55;

    while (!(LPC_SC->PLL1STAT & ((1<<9) | (1<<8))))  ;  // wait for PLL1 enable and connect
}



int  main(void)
{
    int                        n;

    init();                                     // do low-level setup first!
   
/*
 *  Configure the SPI port for connection to the Gameduino.
 */
    LPC_SC->PCONP |= (1<<8);                    // apply power to the SPI interface
    LPC_SC->PCLKSEL0 |= (1<<16);                // use CCLK as SPI clock
    LPC_SPI->SPCCR = 24;                        // now divide SPI clock by this much (must be >= 8, must be even!)

    LPC_PINCON->PINSEL0 |= (3<<30);             // set P0.15 as SCK (collides with UART1 TXD1)
    LPC_PINCON->PINSEL1 |= (3<<2);              // set P0.17 as MISO (collides with UART1 CTS1)
    LPC_PINCON->PINSEL1 |= (3<<4);              // set P0.18 as MOSI (collides with UART1 DCD1)

    LPC_PINCON->PINMODE1 |= (2<<2);             // set MISO with no pull-up or pull-down

    LPC_SPI->SPCR = (1<<5);                     // set SPI to master mode

    LPC_GPIO0->FIODIR |= GD_CS_MASK;            // use SSEL as GPIO slave select line; make it an output
    GD_DISABLE;                                 // pull the chip-select line high

/*
 *  The SPI is set up.  Now we need to register our callback functions
 *  with the Gameduino support module.
 */
    GD_register(&test_select, &test_xchg, &test_deselect);


/*
 *  All done, strut our stuff!
 */
    GD_begin();                                 // set up the GD hardware
    GD_ascii();                                 // set up for using text
    GD_cls();

    for (n=0; n<100; n++)                       // use the GD_outch function...
    {
        outstr("0123 456 789 0123 456 789 0123 456 789\n\r");
    }

/*
 *  Use the direct-write functions in the GD library.
 */
    GD_putstr(0, 19, HelloConst);
    GD_putstr(0, 20, HelloRam);
    GD_putstr(0, 21, "21 String constant within GD_putstr() Hello, world!");
    GD_putstr(0, 22, "012345678901234567890123456789012345678901234567890");
    GD_putstr(0, 23, "abcdefghijklmnopqrstuvwxyz       ");


/*
 *  Use some local functions that in turn use the GD_outch library function.
 */
    outstr("Row 0\n\r");
    outstr("Row 1\n\r");
    outstr("Row 2\n\r");
    outstr("Row 3\n\r");
    outstr("Row 4\n\r");
    outstr("Row 5: abcdefghijklmnopqrstuvwxyz\n\r");

    outstr("\n\r");
    outstr("\n\r> ");

    while (1)  ;
    return  0;
}




/*
 *  test_select      callback function; selects GD adapter
 */
void  test_select(void)
{
    GD_ENABLE;
}


/*
 *  test_xchg      callback function; exchanges a byte with GD adapter
 */
unsigned char  test_xchg(unsigned char  val)
{
    unsigned char                    c;

    LPC_SPI->SPDR = val;                                // send a byte
    while ((LPC_SPI->SPSR & (1<<7)) == 0)  ;            // loop until data reg is empty
    c = LPC_SPI->SPSR;                                  // NXP says read this one more time!
    return  LPC_SPI->SPDR;                              // return the result
}


/*
 *  test_deselect      callback function; deselects GD adapter
 */
void  test_deselect(void)
{
    GD_DISABLE;
}



/*
 *  outstr      quick-and-dirty string print function
 */
void  outstr(char  *s)
{
    while (*s)
    {
        GD_outch(*s);
        s++;
    }
}



The init() function above configures the mbed for a core clock of 96 MHz, then sets up an SPI connection at 4 MHz, which is well below the 8 MHz listed as the top-end for the GD.  I"ve chosen to use the LPC1768's SPI subsystem, rather than either of the SSPs.

After configuring the SPI, I call GD_register() to provide pointers to the three SPI support functions, test_select(), test_xchg(), and test_deselect().  Once that's done, the rest of the code is plain-vanilla GD function calls

I've put all of the files used in creating this project into this zip file.  If you want to rebuild, you will need a setup similar to what I've described elsewhere in this site for bare-metal work using CodeSourcery and Visual Studio 2005.  If you don't want to rebuild but just want to watch the program run, I've included the .bin file as well; all 5.5 KB of it.  :-)




Home