LPC800 Pong!

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

LPC800 Pong!

653 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by gertk on Sun Jul 14 14:39:13 MST 2013
Having received the demo board I could not wait to try some stuff on this tiny controller.
The datasheet states that the clock frequency can be adjusted to a maximum of 30 MHz, the demo code defaults to 20 MHz.

With some cut and paste work in the system_LPC800.h file I managed to get the LPC to run at 30 MHz but it seems that it only works correctly if I execute the SystemCoreClockUpdate() call at the beginning of my main routine. What is the correct way to set the frequency ?

BTW, the code I am fiddling with is a PONG clone generating PAL video output using the SPI port as videoshifter and the MRT as horizontal sync (interrupt)timer. I am planning to use the analog comparator pins as paddle inputs.

So far I am quite impressed with the speed of the LPC800 and the quality of the LPCxpresso compiler.
Labels (1)
0 Kudos
9 Replies

578 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Pacman on Wed Sep 11 21:54:46 MST 2013
Well done!

My suggestion was also to use the HBL to read the comparator value. I think this is a great idea. I never thought of using it as a poor-man's ADC. :)

For audio, you might also be able use the HBL-interrupt and just toggle 1 bit, connect it to a speaker via a transistor (to protect the 812) - think ZX Spectrum.
It might be possible to (ab)use the I2C, in order to make a poor-man's PWM for audio-output; the I2C speed should be sufficient to make simple beeps (and perhaps even low quality audio samples - I think I have a piano sample which is around 400 bytes somewhere - but you could take any sample which is approximately 4K in size and then convert it to a 1-bit sample. =)

I hope you find a bit of this useful. ;)
0 Kudos

578 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by gertk on Sat Aug 10 10:02:46 MST 2013
Preview on youtube: http://youtu.be/DCRYHES4E9E
0 Kudos

578 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by gertk on Fri Aug 09 14:41:58 MST 2013
Got PONG working with a simple joystick as digital controller for one player (using only up and down). The other player is (now) controlled by the CPU but it already plays a mean game of pong :-)

Too bad the analog part is broken..

So if anyone has a source for (fully working) LPC810 in DIP 8...
0 Kudos

578 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by cpldcpu on Mon Jul 29 11:50:26 MST 2013

Quote: gertk

With some cut and paste work in the system_LPC800.h file I managed to get the LPC to run at 30 MHz but it seems that it only works correctly if I execute the SystemCoreClockUpdate() call at the beginning of my main routine. What is the correct way to set the frequency ?



This is because the LPC810 codebase lacked the  __USE_CMSIS define. If you add it, the clock will be set automatically. I have committed this as an update on Github.

0 Kudos

578 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by gertk on Sat Jul 27 05:55:05 MST 2013
Seems the code is mangled by the formatter, I reckoned that once put inside code tags it would not touch the source..

The lines in the active display generation should read:

// send contents of linebuffer
for(n=0; n[notag]<[/notag]MAX_X; n++)
{
while ( (LPC_SPI0->STAT & SPI_STAT_TXRDY) == 0 );
LPC_SPI0->TXDATCTL = SPI_TXDATCTL_FSIZE(8-1) | linebuffer[n] | SPI_TXDATACTL_RXIGNORE;
linebuffer[n]=0;// clear linebuffer along the way
}

0 Kudos

578 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by gertk on Thu Jul 25 13:39:05 MST 2013
This is drving me nuts, now I have found out why pin 5 reads as Vdd (because of the pull up resistor of the ISP button of course!) I still cannot get the pin to work as analog input... With my potmeter wiper connected with a small resistor in series to pin 5 I can pull the pin down to about 0.2 Volts and go all the way up to Vdd

But the comparator output will not change from 0 to 1 or vice versa...

This my code so far:

/**************************************************************************/
/*!
    @file     main.c

    @section LICENSE

    Software License Agreement (BSD License)

    Copyright (c) 2013, K. Townsend (microBuilder.eu)
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:
    1. Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
    2. Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
    3. Neither the name of the copyright holders nor the
    names of its contributors may be used to endorse or promote products
    derived from this software without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**************************************************************************/
#include <stdio.h>
#include "LPC8xx.h"
#include "gpio.h"
#include "mrt.h"
#include "uart.h"
#include "spi.h"

#if defined(__CODE_RED)
#include <cr_section_macros.h>
#include <NXP/crp.h>
__CRP const unsigned int CRP_WORD = CRP_NO_CRP ;
#endif

// hmm, this seems to assert the bit number of the LED
// #define LED_LOCATION    (2)

// let's try that for my sync pin..
#define SYNC_LOCATION (2)


// as it says: the line counter
volatile int line_counter;


// define PAL signals
#define VTOTAL 312
#define VSYNCSTART VTOTAL-8
#define VSYNCEND VTOTAL-4
#define VISIBLESTART30
#define VISIBLEEND270
#define HSYNCWIDTH12-1
// #define HSYNCWIDTH16-1

#define MAX_X26
#define MAX_YVISIBLEEND-8

// define BAT positions and size
#define BATLX 3
#define BATRX MAX_X-BATLX-1
#define BATSIZE 20
#define SCORELX((MAX_X/2)-3)
#define SCORERX((MAX_X/2)+3)
#define SCOREY(VISIBLESTART+10)
#define BATSTEP((VISIBLEEND-VISIBLESTART)/32)

#define COMP_VP0
#define COMP_VM1

#define COMPSA(0x1 << 6)
#define EDGECLR(0x1 << 20)
#define COMPSTAT(0x1 << 21)
#define COMPEDGE(0x1 << 23)

void SwitchMatrix_Init()
{
   /* Enable SWM clock */
    LPC_SYSCON->SYSAHBCLKCTRL |= (1<<7);

    // Enable IOCON clock
    LPC_SYSCON->SYSAHBCLKCTRL |= (1<<18);

    /* Enable AHB clock to the Comparator. */
    LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 19);

    /* fixed Pin Assign 1 bit Configuration */
    /* ACMP_I1 */
    /* ACMP_I2 */
    /* RESET */
    LPC_SWM->PINENABLE0 = 0xffffffbdUL;


    /* Pin Assign 8 bit Configuration */
    /* SPI0_MOSI */
    LPC_SWM->PINASSIGN4 = 0xffffff03UL;

/* Set the SYNC output pin to output (1 = output, 0 = input) */
LPC_GPIO_PORT->DIR0 |= (1 << SYNC_LOCATION);

// init SPI to run at 30/6 = 5 MHz, no delay (0)
spiInit(LPC_SPI0,(6-1),0);

    /* Comparator should be powered up first. Use of comparator requires BOD ? */
    LPC_SYSCON->PDRUNCFG &= ~( (0x1 << 15) | (0x1 << 3) );


    /* Peripheral reset control to Comparator, a "1" bring it out of reset. */
    LPC_SYSCON->PRESETCTRL &= ~(1 << 12);
    LPC_SYSCON->PRESETCTRL |= (1 << 12);

    /* Pin I/O Configuration */
    // disable pull up resistors on analog input pins
    LPC_IOCON->PIO0_0 &= ~(0x3 << 3);
    LPC_IOCON->PIO0_1 &= ~(0x3 << 3);

    // preset analog comparator CTL register
    // select COMP_VP_SEL for voltage ladder output
    // select COMP_VM_SEL for ACMP_I2
    // deselect COMPSA
    LPC_CMP->CTRL |= ((2<<11));

    // disable IOCON clock
    LPC_SYSCON->SYSAHBCLKCTRL &= ~(1<<18);

    /* SWM clock no longer needed now it's configured */
    LPC_SYSCON->SYSAHBCLKCTRL &= ~(1<<7);
}

static unsigned char bitmap[][8]={
{255,129,129,129,129,129,129,255},// 0
{1,1,1,1,1,1,1,1},// 1
{255,1,1,255,128,128,128,255},// 2
{255,1,1,15,1,1,1,255},// 3
{129,129,129,255,1,1,1,1},// 4
{255,128,128,255,1,1,1,255},// 5
{255,128,128,255,129,129,129,255},// 6
{255,1,1,1,1,1,1,1},// 7
{255,129,129,255,129,129,129,255},// 8
{255,129,129,255,1,1,1,255},// 9
{24,60,126,126,126,126,60,24},// ball
};

volatile unsigned char linebuffer[MAX_X+1];
volatile unsigned short ball_x,ball_y;
volatile unsigned short batly,batry;
volatile unsigned short score_l,score_r;

volatile unsigned char vbl_flag;
volatile unsigned shortpaddle_l,paddle_r;
volatile unsigned int paddle_counter;


int main(void)
{

signed char dx,dy,by;
SystemCoreClockUpdate();// retrieve the clock

/* Initialise the GPIO block */
gpioInit();

/* Configure the switch matrix (setup pins) */
SwitchMatrix_Init();

/* Configure the multi-rate timer for 64us ticks and start interrupts */
mrtInit(__SYSTEM_CLOCK/15525);

// ball
ball_x=100;
ball_y=50;

score_l=0;
score_r=0;

batly=100;
batry=100;

// set vector
dx=2;
dy=1;
by=1;

// endless loop
while(1)
{
vbl_flag=0;
//LPC_CMP->LAD=1;
while (!vbl_flag);// wait for vsync to happen
//LPC_CMP->LAD=1|(15<<1);

// bounce ball around
ball_x+=dx;
if (ball_x > ((MAX_X-1)*8))
{
if (dx>0)
{
if (score_l<9) score_l++; else score_l=0;
}
else
{
if (score_r<9) score_r++; else score_r=0;
}
dx=-dx;
ball_x+=dx;
}

if (( LPC_CMP->CTRL & (1<<21)) !=0 ) score_r=1; else score_r=0;

ball_y+=dy;
if ((ball_y>(MAX_Y-1)) || (ball_y<VISIBLESTART))
{
dy=-dy;
}

// bounce left bat
batly+=by;
if ((batly>(VISIBLEEND-BATSIZE-1)) || (batly<VISIBLESTART))
{
by=-by;
}

//batry=VISIBLESTART+(paddle_counter*BATSTEP);
//paddle_counter++;
//paddle_counter &= 0x1fUL;


//LPC_CMP->LAD=(1 | (paddle_counter<<1));

}
}

// state machine list for the complete screen output
static void state_before_vsync(void);

static void hsync()
{
// set sync pin low
LPC_GPIO_PORT->CLR0 = 1 << SYNC_LOCATION;

// send three more nulls (sync width)
while ( (LPC_SPI0->STAT & SPI_STAT_TXRDY) == 0 );
LPC_SPI0->TXDATCTL = SPI_TXDATCTL_FSIZE(HSYNCWIDTH)| SPI_TXDATACTL_RXIGNORE ;
while ( (LPC_SPI0->STAT & SPI_STAT_TXRDY) == 0 );
LPC_SPI0->TXDATCTL = SPI_TXDATCTL_FSIZE(HSYNCWIDTH)| SPI_TXDATACTL_RXIGNORE ;
while ( (LPC_SPI0->STAT & SPI_STAT_TXRDY) == 0 );
LPC_SPI0->TXDATCTL = SPI_TXDATCTL_FSIZE(HSYNCWIDTH)| SPI_TXDATACTL_RXIGNORE ;

// set sync pin high
LPC_GPIO_PORT->SET0 = 1 << SYNC_LOCATION;

}

static void (*state)(void) = state_before_vsync;

// emit a line from the visible area (framebuffer)
static void state_visible_area(void) {
register unsigned short ofs_y;
register unsigned short ofs_x;
register unsigned short temp16;
extern volatile unsigned char linebuffer[];

// always emit horizontal sync
hsync();

// wait for active part to start
if (line_counter < VISIBLESTART) return;

// if less than active part end
if( line_counter<VISIBLEEND+1)
{
// emit back porch (black level)
while ( (LPC_SPI0->STAT & SPI_STAT_TXRDY) == 0 );
LPC_SPI0->TXDATCTL = SPI_TXDATCTL_FSIZE(16-1) | SPI_TXDATACTL_RXIGNORE;
while ( (LPC_SPI0->STAT & SPI_STAT_TXRDY) == 0 );
LPC_SPI0->TXDATCTL = SPI_TXDATCTL_FSIZE(8-1) | SPI_TXDATACTL_RXIGNORE;
//while ( (LPC_SPI0->STAT & SPI_STAT_TXRDY) == 0 );
//LPC_SPI0->TXDATCTL = SPI_TXDATCTL_FSIZE(16-1) | SPI_TXDATACTL_RXIGNORE;

// enable ladder and set voltage ladder
paddle_counter++;
paddle_counter &= 0x1f;
LPC_CMP->LAD = (1|(paddle_counter<<1));

// active line part
// now send MAX_X * 8 dots
int n;

// send contents of linebuffer
for(n=0; n<MAX_X; n++){
while ( (LPC_SPI0->STAT & SPI_STAT_TXRDY) == 0 );
LPC_SPI0->TXDATCTL = SPI_TXDATCTL_FSIZE(8-1) | linebuffer[n] | SPI_TXDATACTL_RXIGNORE;
linebuffer[n]=0;// clear linebuffer along the way
}

// now send 1 bytes blanking
while ( (LPC_SPI0->STAT & SPI_STAT_TXRDY) == 0 );
LPC_SPI0->TXDATCTL = SPI_TXDATCTL_FSIZE(8-1)| SPI_TXDATACTL_RXIGNORE ;

// check Analog comparator output bit and set batry if set (first time)
if ((LPC_CMP->CTRL & (1UL<<21)) && (batry==VISIBLESTART)) batry+=(paddle_counter*BATSTEP);

// mount bats left and right and score
ofs_y=line_counter-batly;
if (ofs_y<BATSIZE) linebuffer[BATLX]|=0x0f;

ofs_y=line_counter-batry;
if (ofs_y<BATSIZE) linebuffer[BATRX]|=0xf0;

ofs_y=line_counter-SCOREY;
if (ofs_y<8)
{
linebuffer[SCORELX]|=bitmap[score_l][ofs_y];
linebuffer[SCORERX]|=bitmap[score_r][ofs_y];
}

// place the ball
ofs_y=line_counter-ball_y;
if (ofs_y<8)
{
ofs_x=ball_x>>3;

// pre shift object data
temp16=bitmap[10][ofs_y]<<(8-(ball_x % 8));
// place object data in linebuffer
linebuffer[ofs_x]|=(temp16>>8);
linebuffer[ofs_x+1]|=(temp16 & 0xff);
}
}
else   state = state_before_vsync;
}


static void state_after_vsync(void)
{   // VSYNC is over, now wait for end of frame

// always emit horizontal sync
hsync();

if (line_counter != VTOTAL) return;

line_counter = 0;                   // reset line counter
batry=VISIBLESTART;
vbl_flag=1;
state = state_visible_area;         // we're in visible mode
}

static void state_during_vsync(void)
{  // VSYNC is active

// keep sending horizontal sync pulses (inverted)
LPC_GPIO_PORT->SET0 = 1 << SYNC_LOCATION;

// send three more nulls (sync width)
while ( (LPC_SPI0->STAT & SPI_STAT_TXRDY) == 0 );
LPC_SPI0->TXDATCTL = SPI_TXDATCTL_FSIZE(HSYNCWIDTH)| SPI_TXDATACTL_RXIGNORE ;
while ( (LPC_SPI0->STAT & SPI_STAT_TXRDY) == 0 );
LPC_SPI0->TXDATCTL = SPI_TXDATCTL_FSIZE(HSYNCWIDTH)| SPI_TXDATACTL_RXIGNORE ;
while ( (LPC_SPI0->STAT & SPI_STAT_TXRDY) == 0 );
LPC_SPI0->TXDATCTL = SPI_TXDATCTL_FSIZE(HSYNCWIDTH)| SPI_TXDATACTL_RXIGNORE ;

// set sync pin low
LPC_GPIO_PORT->CLR0 = 1 << SYNC_LOCATION;

if (line_counter != VSYNCEND) return;

LPC_GPIO_PORT->SET0 = 1 << SYNC_LOCATION;
state = state_after_vsync;          // VSYNC is done
}

static void state_before_vsync(void)
{
// always emit horizontal sync
hsync();
// wait for VSYNC start
if (line_counter != VSYNCSTART)return;

state = state_during_vsync;     // VSYNC has started
}



void MRT_IRQHandler(void)
{
if ( LPC_MRT->Channel[0].STAT & MRT_STAT_IRQ_FLAG )
{
state();
line_counter++;

LPC_MRT->Channel[0].STAT = MRT_STAT_IRQ_FLAG;      /* clear interrupt flag */
}
return;
}

0 Kudos

578 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by gertk on Mon Jul 22 15:09:40 MST 2013
Seems I cannot read out the comparator output, it always reads as 'zero'. Does the COMPSA bit need to be set to read it out ?

Also the analog input pin (pin 5) I am trying to use delivers a voltage regardless of the fact that I have set it to Analog in and set the pull-up/down to none.


void SwitchMatrix_Init()
{
   /* Enable SWM clock */
    LPC_SYSCON->SYSAHBCLKCTRL |= (1<<7);

    /* Comparator should be powered up first. Use of comparator requires BOD */
    LPC_SYSCON->PDRUNCFG &= ~( (0x1 << 15) | (0x1 << 3) );
    /* Enable AHB clock to the Comparator. */
    LPC_SYSCON->SYSAHBCLKCTRL |= (0x1 << 19);
    /* Peripheral reset control to Comparator, a "1" bring it out of reset. */
    // LPC_SYSCON->PRESETCTRL &= ~(0x1 << 12);
    LPC_SYSCON->PRESETCTRL |= (0x1 << 12);

    /* Pin Assign 8 bit Configuration */
    /* SPI0_MOSI */
    LPC_SWM->PINASSIGN4 = 0xffffff03UL;

    /* Pin Assign 1 bit Configuration */
    /* ACMP_I1 */
    /* ACMP_I2 */
    /* RESET */
    LPC_SWM->PINENABLE0 = 0xffffffbcUL;

    /* Enable UART clock */
    LPC_SYSCON->SYSAHBCLKCTRL |= (1<<18);

    /* Pin I/O Configuration */
    LPC_IOCON->PIO0_0 = 0x80;
    LPC_IOCON->PIO0_1 = 0x80;


    /* LPC_IOCON->PIO0_2 = 0x90; */
    /* LPC_IOCON->PIO0_3 = 0x90; */
    /* LPC_IOCON->PIO0_4 = 0x90; */
    /* LPC_IOCON->PIO0_5 = 0x90; */

    // preset analog comparator CTL register
    // select COMP_VP_SEL for voltage ladder output
    // select COMP_VM_SEL for ACMP_I2
    // select COMPSA
    LPC_CMP->CTRL=((2UL<<11)|(1<<6));

    //gpioSetDir(0,0,0);
    //gpioSetDir(0,1,0);

}



After running this, pin 5 measures as Vdd like it (still) has weak pullup and the comparator output bit never changes.
0 Kudos

578 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by gertk on Tue Jul 16 22:53:57 MST 2013
The challenge is to get video output, sync and two paddle inputs all working at the same time. I shifted some pin assignments around to get analog inputs on pins 8 and 5, Video (SPI MOSI) on pin 3 and sync (PIO) on pin 4.

With some luck I could even produce some sound on pin 2.

Only thing missing is a DMA function on the SPI (like LPC1768) :-) That would release the CPU during the SPI output.

Next step is to decipher the comparator function :-)
With the help of the comprehensive datasheet that should be no trouble.

To read the paddles I am planning to use the horizontal sync timer to count up the ladder value and read out the comparator status bit. Since the ladder is only 5 bits it should be doable within a single frame to read out both paddles.

0 Kudos

578 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Wouter on Mon Jul 15 03:08:26 MST 2013
Hi Gert,

Great to hear you're impressed by our LPC800! Your pong clone looks really good! If you're interested in sharing details, you could create blog here on LPCWare.com once you're done.

Regarding your question:
SystemCoreClockUpdate() calculates which speed the core should be running at, by determining the clock source (IRC, main clk, ...), the input frequency (in case of external clock the frequency should be defined in the system.c file, default is 12MHz), PLL settings and clock dividers. It writes back the result to the global variable SystemCoreClock.
Most probably your initialization of the MRT makes use of the SystemCoreClock variable. Without calling SystemCoreClockUpdate(), SystemCoreClock contains the wrong value and the MRT is not configured at the right frequency, causing the video generation to fail.

Regards,
Wouter
0 Kudos