FRDM-KL25Z with Nokia 3110 type LCD display Using accelerometer to make movements

Dependencies:   MMA8451Q N3310LCD mbed

Fork of FRDM_MMA8451Q by mbed official

main.cpp

Committer:
SomeRandomBloke
Date:
2013-03-24
Revision:
12:dbd6c9c366ac
Parent:
11:90d35ac294af

File content as of revision 12:dbd6c9c366ac:

#include "mbed.h"
#include "MMA8451Q.h"
#include "N3310SPIConfig.h"
#include "N3310LCD.h"
#include "Joystick.h"
#include "splash.h"

#define MMA8451_I2C_ADDRESS (0x1d<<1)
Serial pc(USBTX,USBRX);

#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

// menu starting points
#define MENU_X    10        // 0-83
#define MENU_Y    1        // 0-5

#define MENU_ITEMS 4

int8_t blflag = 1;  // Backlight initially ON

void about(N3310LCD* lcd, Joystick* jstick);
void draw(N3310LCD* lcd, Joystick* jstick);
void snakeGame(N3310LCD* lcd, Joystick* jstick);
void setup(N3310LCD* lcd, Joystick* jstick);
void waitforOKKey(N3310LCD* lcd, Joystick* jstick);
uint8_t checkKeypressed( Joystick* jstick);

// menu definition
char menu_items[MENU_ITEMS][12] = {
    "SKETCH",
    "SNAKE",
    "SETUP",
    "ABOUT"
};

void (*menu_funcs[MENU_ITEMS])(N3310LCD*,Joystick*) = {
    draw,
    snakeGame,
    setup,
    about
};


void about(N3310LCD* lcd, Joystick* jstick)
{
    lcd->writeString(6, 0,  "mbed and LCD", NORMAL);
    lcd->writeString(0, 1, "demos on KL25Z", NORMAL);
    lcd->writeString(36, 2, "By", NORMAL);
    lcd->writeString(0, 3,  "Andrew Lindsay", NORMAL);
}

void setup(N3310LCD* lcd, Joystick* jstick)
{
    lcd->writeString( 0, 1, "Toggle", NORMAL);
    lcd->writeString( 0, 2, "Backlight", NORMAL);
    if( blflag != 0 ) {
        lcd->writeString( 60, 2, "Off", HIGHLIGHT);
    } else {
        lcd->writeString( 60, 2, "On", HIGHLIGHT);
    }

    bool exitFlag = false;

    while( !exitFlag ) {
        for (int i = 0; i < NUM_KEYS; i++) {
            if (jstick->getKeyState(i) != 0) {
                jstick->resetKeyState(i);  // reset button flag
                switch(i) {
                    case CENTER_KEY:
                        exitFlag = true;
                        break;
                    case UP_KEY:
                    case DOWN_KEY:
                        if( blflag != 0 ) {
                            blflag=0;
                            lcd->backlight( OFF );
                        }   else {
                            blflag = 1;
                            lcd->backlight( ON );
                        }
                        lcd->writeString( 60, 2, (blflag != 0 ? (char*)"Off" : (char*)"On "), HIGHLIGHT);
                        break;
                }
            }
        }
    }
}

void centreBoard( MMA8451Q *acc, float *cValues )
{
    // Take 100 readings to get stable centre point
    for( int i = 0; i < 100; i++ ) {
        float axis[3] = { 0.0, 0.0, 0.0 };
        acc->getAccAllAxis( axis );
        cValues[0] += axis[0];
        cValues[1] += axis[1];
        cValues[2] += axis[2];
    }
    cValues[0] /= 100.0;
    cValues[1] /= 100.0;
    cValues[2] /= 100.0;
//    pc.printf( "Steady State X: %f Y: %f Z: %f\n", cValues[0], cValues[1], cValues[2] );
}


void draw(N3310LCD* lcd, Joystick* jstick)
{
    MMA8451Q acc(PTE25, PTE24, MMA8451_I2C_ADDRESS, false, 4);
    float x, y;
    float cValues[3] = { 0.0, 0.0, 0.0 };
    int16_t xI, yI;
    int16_t xInc, yInc;
    int8_t px = 42;
    int8_t py = 24;
    int8_t nx = px;
    int8_t ny = py;

    lcd->drawBitmap(0, 0, splashSketch, 84, 48);
    wait( 5 );

    centreBoard( &acc, cValues );
    lcd->cls();

    // Draw a rectangle
    lcd->drawRectangle(0,0,83,47, PIXEL_ON);
    lcd->setPixel( px, py, PIXEL_ON );

    // Exit on joystick pressed
    bool exitFlag = false;
    while ( !exitFlag ) {
        uint8_t keyPress = checkKeypressed( jstick );
        if( keyPress == CENTER_KEY )
            exitFlag = true;

        x = (acc.getAccX() - cValues[0]) * 100.0;
        y = (acc.getAccY() - cValues[1]) * 100.0;

        xI = (int16_t)(x);
        yI = (int16_t)(y);
        xInc = xI/10;
        yInc = ((yI/10) * -1);

        nx += xInc;
        ny += yInc;

        nx = MAX( 1, MIN( nx, 82 ) );
        ny = MAX( 1, MIN( ny, 46 ) );
//pc.printf( "X: %d Y: %d\n", nx, ny );

        if( abs(xInc) > 1 || abs(yInc) > 1 ) {
            // Draw line
            lcd->drawLine((uint8_t)px, (uint8_t)py, (uint8_t)nx, (uint8_t)ny, PIXEL_ON);
        } else {
            // Plot pixel
            lcd->setPixel( (uint8_t)nx, (uint8_t)ny, PIXEL_ON );
        }

        px = nx;
        py = ny;

        wait(0.1);
    }
}

#define MAX_SNAKE_LEN 400
#define MAX_FOOD 5
#define SNAKE_X 0
#define SNAKE_Y 1
#define DIR_N 1
#define DIR_E 2
#define DIR_S 3
#define DIR_W 4
#define FOOD_X 0
#define FOOD_Y 1

int16_t snakeLen = 10;
int16_t foodCount = 0;

int8_t snake[MAX_SNAKE_LEN][2];       // Snake positions, use -1 for no point
int8_t food[MAX_FOOD][2];

void initSnake( void )
{
    snakeLen = 10;
    for( int i=0; i< MAX_SNAKE_LEN; i++ ) {
        snake[i][SNAKE_X] = -1;
        snake[i][SNAKE_Y] = -1;
    }

    // Add initial snake points
    for(int i=0; i<snakeLen; i++ ) {
        snake[i][SNAKE_X] = 42 + i;
        snake[i][SNAKE_Y] = 24;
    }
}

void drawSnake( N3310LCD* lcd )
{
    for(int i=0; i<snakeLen; i++ ) {
        lcd->setPixel( snake[i][SNAKE_X], snake[i][SNAKE_Y], PIXEL_ON );
    }
}


void moveSnake( N3310LCD* lcd, uint8_t direction, uint8_t growLength )
{
    int8_t oX = snake[0][SNAKE_X];
    int8_t oY = snake[0][SNAKE_Y];
    switch( direction ) {
        case DIR_N:
            snake[0][SNAKE_Y]--;
            break;
        case DIR_E:
            snake[0][SNAKE_X]++;
            break;
        case DIR_S:
            snake[0][SNAKE_Y]++;
            break;
        case DIR_W:
            snake[0][SNAKE_X]--;
            break;
    }

    int8_t nX = -1;
    int8_t nY = -1;
    for(int i=1; i<snakeLen; i++ ) {
        nX = oX;
        nY = oY;
        oX = snake[i][SNAKE_X];
        oY = snake[i][SNAKE_Y];
        snake[i][SNAKE_X] = nX;
        snake[i][SNAKE_Y] = nY;
    }

    // Plot head at new position as this is only part that is moving
    lcd->setPixel( snake[0][SNAKE_X], snake[0][SNAKE_Y], PIXEL_ON );
    
    if( growLength > 0 && snakeLen < MAX_SNAKE_LEN ) {
        // Dont clear tail, add point to tail
        snake[snakeLen][SNAKE_X] = oX;
        snake[snakeLen][SNAKE_Y] = oY;
        snakeLen++;
    } else {
        // Clear tail
        lcd->setPixel( oX, oY, PIXEL_OFF );
    }
}

bool isOnSnake( int8_t sX, int8_t sY )
{
    bool onSnake = false;
//    pc.printf("isOnSnake sX = %d, sY = %d \n", *sX, *sY );
    for( int i = 0; i< snakeLen; i++ ) {
        if( snake[i][SNAKE_X] == sX && snake[i][SNAKE_Y] == sY ) {
            onSnake = true;
            break;
        }
    }
    return onSnake;
}

bool checkCollision( uint8_t dir)
{
    bool collisionFlag = false;
    int8_t nX = snake[0][SNAKE_X];
    int8_t nY = snake[0][SNAKE_Y];
    
    switch( dir) {
        case DIR_N:
            nY--;
            break;
        case DIR_E:
            nX++;
            break;
        case DIR_S:
            nY++;
            break;
        case DIR_W:
            nX--;
            break;    
    }
    if( isOnSnake( nX, nY ) )
        return true;

    if( snake[0][SNAKE_X] <= 0 || snake[0][SNAKE_X] >= 83 || 
        snake[0][SNAKE_Y] <= 0 || snake[0][SNAKE_Y] >= 47 )
        return true;
        
    return collisionFlag;
}


void initFood()
{
    foodCount = 0;
    for( uint8_t i = 0; i< MAX_FOOD; i++ ) {
        food[i][FOOD_X] = -1;
        food[i][FOOD_Y] = -1;
    }
}


// Get current snake head and work out if position is a food
bool checkFood(uint8_t dir)
{
    bool foodFlag = false;
    for( int i = 0; i < MAX_FOOD; i++ ) {
        if( snake[0][SNAKE_X] == food[i][FOOD_X] && snake[0][SNAKE_Y] == food[i][FOOD_Y] ) {
            foodFlag = true;
            food[i][FOOD_X] = -1;
            food[i][FOOD_Y] = -1;
            foodCount--;
            break;
        }
    }
    return foodFlag;
}


void getRandomPosition( int8_t *rX, int8_t *rY )
{
    *rX = ((rand() % 100)*82)/100+1;
    *rY = ((rand() % 100)*46)/100+1;

    while( isOnSnake( *rX, *rY )) {
        *rX = ((rand() % 100)*82)/100+1;
        *rY = ((rand() % 100)*46)/100+1;
    }

}

void storeFood( int8_t fX, int8_t fY )
{
    for( int i = 0; i< MAX_FOOD; i++ ) {
        if( food[i][FOOD_X] < 0 ) {
            food[i][FOOD_X] = fX;
            food[i][FOOD_Y] = fY;
            break;
        }
    }
}


void dropFood(N3310LCD* lcd)
{
    // If no food present then drop some
    // Pick random x and y coords x >0 and x < 83, y > 0 and y <47
    while( foodCount < MAX_FOOD ) {
        int8_t fX = 0;
        int8_t fY = 0;
        getRandomPosition( &fX, &fY );
        lcd->setPixel( fX, fY, PIXEL_ON );
        storeFood( fX, fY );
        foodCount++;
    }
}

void snakeGame(N3310LCD* lcd, Joystick* jstick)
{
    MMA8451Q acc(PTE25, PTE24, MMA8451_I2C_ADDRESS, false, 4);
    float x, y; //, z;
    float cValues[3] = { 0.0, 0.0, 0.0 };
    int16_t xI, yI; //, zI;
    int16_t xInc, yInc;

    lcd->drawBitmap(0, 0, splashSnake, 84, 48);
    wait( 3 );
    
    centreBoard( &acc, cValues );

    lcd->cls();

    // Draw a rectangle
    lcd->drawRectangle(0,0,83,47, PIXEL_ON);

    initSnake();
    initFood();
    dropFood(lcd);
    drawSnake( lcd );
    /* Test to move snake
        for( int i=0; i<10; i++ ) {
            moveSnake(lcd, DIR_N, 1);
            wait(0.2);
        }
        for( int i=0; i<20; i++ ) {
            moveSnake(lcd, DIR_E, 0);
            wait(0.2);
        }
        for( int i=0; i<20; i++ ) {
            moveSnake(lcd, DIR_S, 1);
            wait(0.2);
        }
        for( int i=0; i<20; i++ ) {
            moveSnake(lcd, DIR_W, 1);
            wait(0.2);
        }
    */

    // Exit on joystick pressed
    bool exitFlag = false;
    uint8_t snakeDirection = DIR_W;
    uint8_t snakeGrowth = 0;

    while ( !exitFlag ) {
        uint8_t keyPress = checkKeypressed( jstick );
        if( keyPress == CENTER_KEY )
            exitFlag = true;

        x = (acc.getAccX() - cValues[0]) * 100.0;
        y = (acc.getAccY() - cValues[1]) * 100.0;
//       pc.printf( "X: %f Y: %f Z: %f\n", x, y, z);

        xI = (int16_t)(x);
        yI = (int16_t)(y);
        xInc = xI/10;
        yInc = ((yI/10) * -1);

        if( xInc > 2 )
            snakeDirection = DIR_E;
        else if( xInc < -2 )
            snakeDirection = DIR_W;
        else if( yInc > 2 )
            snakeDirection = DIR_S;
        else if( yInc < -2 )
            snakeDirection = DIR_N;

        if( snakeGrowth > 0 ) snakeGrowth--;

        moveSnake(lcd, snakeDirection, snakeGrowth);
        // TODO Check if we've collided with anything
        if( checkCollision(snakeDirection) ) {
            // Collision detected!!
            lcd->writeString(30, 2, "GAME", NORMAL);
            lcd->writeString(30, 3, "OVER", NORMAL);
            exitFlag = true;
            continue;
        }

        if( checkFood(snakeDirection) ) {
            // Gobbled some food!!
            // Indicate that snake is to grow during next moves
            snakeGrowth = 10;

            // Drop more food into area
            dropFood(lcd);
        }

        // A little pause - vary this after snake length has reached 100, then 200, then 300
        wait(0.2);

    }

}



void initMenu(N3310LCD* lcd)
{
    lcd->writeString(MENU_X, MENU_Y, menu_items[0], HIGHLIGHT );

    for (int i = 1; i < MENU_ITEMS; i++) {
        lcd->writeString(MENU_X, MENU_Y + i, menu_items[i], NORMAL);
    }
}

void waitforOKKey(N3310LCD* lcd, Joystick* jstick)
{
    lcd->writeString(38, 5, "OK", HIGHLIGHT );

    int key = 0xFF;
    while (key != CENTER_KEY) {
        for (int i = 0; i < NUM_KEYS; i++) {
            if (jstick->getKeyState(i) !=0) {
                jstick->resetKeyState(i);  // reset
                if (CENTER_KEY == i) key = CENTER_KEY;
            }
        }
    }
}

// Check if joystick is moved or pressed
uint8_t checkKeypressed( Joystick* jstick)
{
    uint8_t key = 0xFF;

    for(int i=0; i<NUM_KEYS; i++) {
        if (jstick->getKeyState(i) !=0) {
            jstick->resetKeyState(i);  // reset
            if (CENTER_KEY == i) key = CENTER_KEY;
        }
    }
    return key;
}


int main(void)
{
//    MMA8451Q acc(PTE25, PTE24, MMA8451_I2C_ADDRESS, false, 4);
    Joystick jstick(N3310SPIPort::AD0);
    N3310LCD lcd(N3310SPIPort::MOSI, N3310SPIPort::MISO, N3310SPIPort::SCK,
                 N3310SPIPort::CE, N3310SPIPort::DAT_CMD, N3310SPIPort::LCD_RST,
                 N3310SPIPort::BL_ON);
    lcd.init();
    lcd.cls();
    lcd.backlight(ON);

    // demo stuff
    // autoDemo(&lcd);

    lcd.drawBitmap(0, 0, splashMain, 84, 48);
    wait( 3 );
    lcd.cls();

    initMenu(&lcd);
    int currentMenuItem = 0;
    Ticker jstickPoll;
    jstickPoll.attach(&jstick, &Joystick::updateADCKey, 0.01);    // check ever 10ms

    while (true) {
        for (int i = 0; i < NUM_KEYS; i++) {
            if (jstick.getKeyState(i) != 0) {
                jstick.resetKeyState(i);  // reset button flag
                switch(i) {
                    case UP_KEY:
                        // current item to normal display
                        lcd.writeString(MENU_X, MENU_Y + currentMenuItem, menu_items[currentMenuItem], NORMAL);
                        currentMenuItem -=1;
                        if (currentMenuItem <0)  currentMenuItem = MENU_ITEMS -1;
                        // next item to highlight display
                        lcd.writeString(MENU_X, MENU_Y + currentMenuItem, menu_items[currentMenuItem], HIGHLIGHT);
                        break;

                    case DOWN_KEY:
                        // current item to normal display
                        lcd.writeString(MENU_X, MENU_Y + currentMenuItem, menu_items[currentMenuItem], NORMAL);
                        currentMenuItem +=1;
                        if(currentMenuItem >(MENU_ITEMS - 1))  currentMenuItem = 0;
                        // next item to highlight display
                        lcd.writeString(MENU_X, MENU_Y + currentMenuItem, menu_items[currentMenuItem], HIGHLIGHT);
                        break;

                    case LEFT_KEY:
                        initMenu(&lcd);
                        currentMenuItem = 0;
                        break;

                    case RIGHT_KEY:
                        lcd.cls();
                        (*menu_funcs[currentMenuItem])(&lcd, &jstick);
                        waitforOKKey(&lcd, &jstick);
                        lcd.cls();
                        initMenu(&lcd);
                        currentMenuItem = 0;
                        break;
                }
            }
        }
    }

    /*
    //    PwmOut rled(LED_RED);
    //    PwmOut gled(LED_GREEN);
    //    PwmOut bled(LED_BLUE);
        float x, y, z;
        float cValues[3] = { 0.0, 0.0, 0.0 };
        int16_t xI, yI, zI;

        // Take 100 readings to get stable centre point

        for( int i = 0; i < 100; i++ ) {
            float axis[3] = { 0.0, 0.0, 0.0 };
            acc.getAccAllAxis( axis );
            cValues[0] += axis[0];
            cValues[1] += axis[1];
            cValues[2] += axis[2];
        }

        cValues[0] /= 100.0;
        cValues[1] /= 100.0;
        cValues[2] /= 100.0;
        pc.printf( "Steady State X: %f Y: %f Z: %f\n", cValues[0], cValues[1], cValues[2] );

        while (true) {

            x = (acc.getAccX() - cValues[0]) * 100.0;
            y = (acc.getAccY() - cValues[1]) * 100.0;
            z = (acc.getAccZ() - cValues[2]) * 100.0;
     //       pc.printf( "X: %f Y: %f Z: %f\n", x, y, z);

            xI = (int16_t)(x);
            yI = (int16_t)(y);
            zI = (int16_t)(z);
            pc.printf( "X: %d Y: %d Z: %d\n", xI, yI, zI);
    //        pc.printf( "X: %f Y: %f Z: %f\n", x*100.0, y*100.0, z*100.0);
    //        rled = 1.0 - abs(acc.getAccX());
    //        gled = 1.0 - abs(acc.getAccY());
    //        bled = 1.0 - abs(acc.getAccZ());
            wait(0.5);
        }
    */
}