Demo code for basic 3D graphics on the MBED application shield's LCD (K64F). Left pot changes Z depth, joystick rotates one of the cubes.

Dependencies:   C12832 FXOS8700CQ gfx3d mbed

main.cpp

Committer:
co657_frmb
Date:
2015-11-29
Revision:
4:2828d6a81907
Parent:
3:23a93f2f8cac

File content as of revision 4:2828d6a81907:

/*
 *  co657_lcdplay (main.cpp): playing with the MBED shield's LCD
 *  Copyright (C) 2015 Fred Barnes, University of Kent <frmb@kent.ac.uk>
 */

#include "mbed.h"
#include "C12832.h"
#include "FXOS8700CQ.h"

#include "gfx3d.h"
#include "g3d_textures.h"

// #define BENCHMARK

/* some global objects */
Serial host (USBTX, USBRX);

C12832 shld_lcd (D11, D13, D12, D7, D10);                   /* LCD on the shield (128x32) */

FXOS8700CQ fxos (PTE25, PTE24, FXOS8700CQ_SLAVE_ADDR1);     /* On-board accelerometer: SDA, SCL, (addr << 1) */

InterruptIn sw2_int (PTC6);                                 /* SW2 (also unused interrupt for FXOS8700CQ) */
InterruptIn fxos_int2 (PTC13);                              /* should just be the Data-Ready interrupt */
InterruptIn sw3_int (PTA4);                                 /* switch SW3 */

InterruptIn joy_up (PTB10);
InterruptIn joy_down (PTB11);
InterruptIn joy_left (PTC11);
InterruptIn joy_right (PTC10);
InterruptIn joy_fire (PTB23);

#define FRAME_RATE_FPS      (40)                            /* how fast we want it to go */
#define FRAME_RATE_US       (1000000 / FRAME_RATE_FPS)
Ticker frametimer;                                          /* timer that will be used to update frames and whatnot */

AnalogIn shld_pot1 (A0);
AnalogIn shld_pot2 (A1);


#ifdef BENCHMARK
Timer perftimer;
#endif

#define PFLAG_TIMER         (0x01)                          /* next frame please */
#define PFLAG_FXOS          (0x02)                          /* accel/magno data ready */
#define PFLAG_JOY           (0x04)                          /* joystick activity */

#define JOY_NONE            (0x00)
#define JOY_UP              (0x01)
#define JOY_DOWN            (0x02)
#define JOY_LEFT            (0x04)
#define JOY_RIGHT           (0x08)
#define JOY_FIRE            (0x10)

#define JOY_ANGLE_ADVANCE   (2)                             /* how many 8-bit-degrees */

static volatile uint8_t pflags = 0x00;                      /* program flags */
static volatile uint8_t joybits = 0x00;                     /* joystick bits */

#define TXBUF_WIDTH         (256)
static uint8_t ltxbuf[TXBUF_WIDTH * 4];                     /* a 256x32 buffer that holds 8 32x32 textures, organised as 4-byte strips L-to-R */


/*
 *  assorted interrupt handlers, mostly just setting/clearing bits.
 *  FXOS read-ready interrupt, button triggers (pulled high but not SW2 on my board).
 *  Interrupt handler for frame-timeout (bang out frame, compute next).
 *  Interrupt handlers for joystick up/down/etc. (pulled to ground).
 *  Crushed up because MBED's editor doesn't fold yet.
 */
static void trigger_fxos_int2 (void)        { pflags |= PFLAG_FXOS; }
static void trigger_sw2_int (void)          { return; }
static void trigger_sw3_int (void)          { return; }
static void trigger_frametimer (void)       { pflags |= PFLAG_TIMER; }
static void trigger_joy_up_rise (void)      { joybits |= JOY_UP;    pflags |= PFLAG_JOY; }
static void trigger_joy_up_fall (void)      { joybits &= ~JOY_UP;   pflags |= PFLAG_JOY; }
static void trigger_joy_down_rise (void)    { joybits |= JOY_DOWN;  pflags |= PFLAG_JOY; }
static void trigger_joy_down_fall (void)    { joybits &= ~JOY_DOWN; pflags |= PFLAG_JOY; }
static void trigger_joy_left_rise (void)    { joybits |= JOY_LEFT;  pflags |= PFLAG_JOY; }
static void trigger_joy_left_fall (void)    { joybits &= ~JOY_LEFT; pflags |= PFLAG_JOY; }
static void trigger_joy_right_rise (void)   { joybits |= JOY_RIGHT; pflags |= PFLAG_JOY; }
static void trigger_joy_right_fall (void)   { joybits &= ~JOY_RIGHT;pflags |= PFLAG_JOY; }
static void trigger_joy_fire_rise (void)    { joybits |= JOY_FIRE;  pflags |= PFLAG_JOY; }
static void trigger_joy_fire_fall (void)    { joybits &= ~JOY_FIRE; pflags |= PFLAG_JOY; }



/*
 *  dumps the global accelerometer/magnetomer reading to the serial-port "host"
 */
void print_reading (FXOSData_t *p)
{
    host.printf ("A X:%5d Y:%5d Z:%5d   M X:%5d Y:%5d Z:%5d\r\n",
            p->s.acc_x, p->s.acc_y, p->s.acc_z, p->s.mag_x, p->s.mag_y, p->s.mag_z);
}


static char *scrolly_message = "  ...  Welcome to the wonderful world of MBED and CO657 ...       ";


/*
 *  some grot for handling scrolling text
 */
static void scroll_text_update (int *firstch, int *xdelta, const char *str)
{
    int xoff = *xdelta;
    int fch = *firstch;
    int hcnt, ccnt, lxoff, hxoff;
    
    /* see how many characters starting with fch will fit into the buffer (allow overrun) */
    for (hcnt=0; (str[fch + hcnt] != '\0') && (xoff < (TXBUF_WIDTH >> 1)); hcnt++) {
        xoff += gfx3d_font04b_char_dpw (str[fch + hcnt]);
    }
    hxoff = xoff;
    for (ccnt=hcnt; (str[fch + ccnt] != '\0') && (xoff < TXBUF_WIDTH); ccnt++) {
        xoff += gfx3d_font04b_char_dpw (str[fch + ccnt]);
    }
    
    /* clear and render */
    memset (ltxbuf, 0xff, TXBUF_WIDTH * 4);
    lxoff = *xdelta;
    gfx3d_font04b_tx_putstrn (ltxbuf, TXBUF_WIDTH, &lxoff, 8, str + fch, ccnt, true);

    if (hxoff == (TXBUF_WIDTH >> 1)) {
        /* hit exactly */
        *xdelta = 0;
        *firstch = fch + hcnt;
    } else if (hxoff > (TXBUF_WIDTH >> 1)) {
        /* last character overshoots middle, include it next time */
        *firstch = fch + (hcnt - 1);
        *xdelta = (TXBUF_WIDTH >> 1) - hxoff;
    } else {
        /* ran out of string, redo from start */
        *firstch = 0;
        *xdelta = 0;
    }
}


/*
 *  start here
 */
int main (void)
{
    angle_t a;
#ifdef BENCHMARK
    int bt_start, bt_now, bt_update = 0, bt_trans = 0, bt_render = 0;
#endif
    angle_t x_rot = 0;
    angle_t y_rot = 0;
    FXOSData_t fxos_data;
    uint8_t ljoybits = 0x00;            /* local joystick bits (JOY_...) */
    const uint8_t *std_cube_txmap[6] = {g3d_texture_face, g3d_texture_hlife, g3d_texture_ccube, g3d_texture_hlife, g3d_texture_face, g3d_texture_ccube};
    const uint8_t *wcube_txmap[6] = {g3d_texture_wcube, g3d_texture_wcube, g3d_texture_wcube, g3d_texture_wcube, g3d_texture_wcube, g3d_texture_wcube};
    int ltxoffs = 0;
    int sc_fch = 0;
    int sc_xdt = 0;
    
    uint16_t pot1val, pot2val;
    
    /* initialise */
    host.baud (38400);
    
    shld_lcd.set_auto_up (0);           /* we'll do it ourselves */
    shld_lcd.cls ();
    shld_lcd.copy_to_lcd ();
    
    fxos_int2.fall (trigger_fxos_int2);    /* level triggered interrupt */
    fxos.enable();                          /* enable device */

    /* Interrupt for SW2/SW3 button-down state */
    sw2_int.mode (PullUp);
    sw2_int.fall (trigger_sw2_int);
    
    sw3_int.fall (trigger_sw3_int);

    /* sort out joystick interrupts */
    joy_up.rise (trigger_joy_up_rise);
    joy_up.fall (trigger_joy_up_fall);
    joy_down.rise (trigger_joy_down_rise);
    joy_down.fall (trigger_joy_down_fall);
    joy_left.rise (trigger_joy_left_rise);
    joy_left.fall (trigger_joy_left_fall);
    joy_right.rise (trigger_joy_right_rise);
    joy_right.fall (trigger_joy_right_fall);
    joy_fire.rise (trigger_joy_fire_rise);
    joy_fire.fall (trigger_joy_fire_fall);

    /* read the two potentiometers */
    pot1val = shld_pot1.read_u16 ();
    pot2val = shld_pot2.read_u16 ();

    float pot1f = G3D_Z_DEPTH_MIN + (((G3D_Z_DEPTH_MAX - G3D_Z_DEPTH_MIN) * (float)pot1val) / 65535.0f);

    gfx3d_set_z_depth (pot1f);

    /* setup frame timer */
    frametimer.attach_us (trigger_frametimer, FRAME_RATE_US);

    /* Example data printing */
    fxos.get_data (&fxos_data);
//    print_reading (&fxos_data);
    
    /* clear texture buffer */
    scroll_text_update (&sc_fch, &sc_xdt, scrolly_message);

#ifdef BENCHMARK
    perftimer.reset ();
    perftimer.start ();
#endif

    a = 0;
    for (;;) {
        uint8_t cflags;
        
        __disable_irq ();
        cflags = pflags;
        pflags = 0x00;
        __enable_irq ();
        
        if (cflags & PFLAG_TIMER) {
            g3d_p3_t pts1[8];                   /* points for rotates/translated cubes */
            g3d_2p3_t pts2[8];                  /* points for projected cube */
            g3d_poly_t poly[12];                /* polygons for cube */
            g3d_edgebuf_t pedge[12];            /* edge-buffers for polygons */
            int npolys = 0;                     /* number of polygons that are meaningful */
            int i;
            uint16_t n_pot1, n_pot2;
            const uint8_t *wrapmap[6] = {&(ltxbuf[ltxoffs + 0]), &(ltxbuf[ltxoffs + 128]), &(ltxbuf[ltxoffs + 256]), &(ltxbuf[ltxoffs + 384]), g3d_texture_check, g3d_texture_check};
            
#ifdef BENCHMARK
            bt_start = perftimer.read_us ();
            bt_update = 0;
            bt_trans = 0;
            bt_render = 0;
#endif

            /* push last frame first */
            shld_lcd.copy_to_lcd ();
            shld_lcd.cls ();

#ifdef BENCHMARK
            bt_now = perftimer.read_us ();
            bt_update += (bt_now - bt_start);
            bt_start = bt_now;
#endif

            /* read potentiometers */
            n_pot1 = shld_pot1.read_u16 ();
            n_pot2 = shld_pot2.read_u16 ();
            if (pot1val != n_pot1) {
                /* FIXME: ... */
                pot1val = n_pot1;
                pot1f = G3D_Z_DEPTH_MIN + (((G3D_Z_DEPTH_MAX - G3D_Z_DEPTH_MIN) * (float)pot1val) / 65535.0f);

                gfx3d_set_z_depth (pot1f);
            }
            if (pot2val != n_pot2) {
                /* FIXME: ... */
                pot2val = n_pot2;
            }
            gfx3d_clear_zb ();

#if 0
            /* DEBUG: for texture mapping, simple rotating square (2 polys) */
            {
                int j;
                g3d_p3_t scale = {2.0, 2.0, 1.0};
                g3d_p3_t trans = {0.0, 0.0, -10.0 + ((20.0f * (float)a) / 256.0f)};
                
                gfx3d_scale (g3d_xyfacepnts, pts1, 4, scale);
                gfx3d_rotate_x (pts1, pts1, 4, a);
                gfx3d_translate (pts1, pts1, 4, trans);
                gfx3d_rotate_z (pts1, pts1, 4, a * 2);
                gfx3d_project (pts1, pts2, 4);
                gfx3d_squarify_points (pts2, poly, &npolys, 0);     /* hide backfaces */
                for (j=0; j<npolys; j++) {
                    gfx3d_sort_poly (&poly[j]);
                }
#ifdef BENCHMARK
                bt_now = perftimer.read_us ();
                bt_trans += (bt_now - bt_start);
                bt_start = bt_now;
#endif
                for (j=0; j<npolys; j++) {
                    gfx3d_polytxmap (&poly[j], g3d_texture_face, shld_lcd);
                }
                
#ifdef BENCHMARK
                bt_now = perftimer.read_us ();
                bt_render += (bt_now - bt_start);
                bt_start = bt_now;
#endif

            }
#else            
            for (i=0; i<4; i++) {
                g3d_p3_t trans = {3.0f * gfx3d_sin (a + (i * 64)), 0.0f, 5.0f * gfx3d_cos (a + (i * 64))};
                int j;
            
                /* rotate, translate and render! */
                if (i == 0) {
                    gfx3d_rotate_y (g3d_cubepnts, pts1, 8, x_rot);
                    gfx3d_rotate_x (pts1, pts1, 8, y_rot);
                    gfx3d_translate (pts1, pts1, 8, trans);
                } else {
                    gfx3d_rotate_demo (g3d_cubepnts, pts1, 8, (a * i) & 0xff);
                    gfx3d_translate (pts1, pts1, 8, trans);
                }

                gfx3d_project (pts1, pts2, 8);
                if (i == 0) {
                    gfx3d_cubify_points (pts2, poly, &npolys, 0, wrapmap);       /* hide backfaces */
                } else if (i == 3) {
                    gfx3d_cubify_points (pts2, poly, &npolys, 0, wcube_txmap);       /* hide backfaces */
                } else {
                    gfx3d_cubify_points (pts2, poly, &npolys, 0, std_cube_txmap);       /* hide backfaces */
                }
                for (j=0; j<npolys; j++) {
                    gfx3d_sort_poly (&poly[j]);
                }

#if 0
if (i == 2) {
if ((a & 0x0f) == 0) {
    host.printf ("poly[0].tx_pts[0]=%4.4x, [1]=%4.4x, [2]=%4.4x, norm=%8.8x\r\n", poly[0].tx_pts[0], poly[0].tx_pts[1], poly[0].tx_pts[2], poly[0].norm);
}
}
#endif

#ifdef BENCHMARK
                bt_now = perftimer.read_us ();
                bt_trans += (bt_now - bt_start);
                bt_start = bt_now;
#endif

                for (j=0; j<npolys; j++) {
                    if (i == 2) {
                        gfx3d_polynormmap (&poly[j], shld_lcd);
                    } else {
                        gfx3d_polytxmap (&poly[j], shld_lcd);
                    }
                }
                
#ifdef BENCHMARK
                bt_now = perftimer.read_us ();
                bt_render += (bt_now - bt_start);
                bt_start = bt_now;
#endif

            }
#endif  /* (debugging what polygons to use) */
            
            /* filled up the frame-buffer, setup things for next time */
            a++;
            
            if (ljoybits & JOY_UP) {
                y_rot += JOY_ANGLE_ADVANCE;
            } else if (ljoybits & JOY_DOWN) {
                y_rot -= JOY_ANGLE_ADVANCE;
            }
            
            if (ljoybits & JOY_LEFT) {
                x_rot += JOY_ANGLE_ADVANCE;
            } else if (ljoybits & JOY_RIGHT) {
                x_rot -= JOY_ANGLE_ADVANCE;
            }
            
            ltxoffs += 4;
            if (ltxoffs >= 512) {
                /* update scrolly text */
                scroll_text_update (&sc_fch, &sc_xdt, scrolly_message);
                ltxoffs -= 512;
            }

#ifdef BENCHMARK
            /* write bt_trans and bt_render into the display */
            shld_lcd.locate (0, 24);
            shld_lcd.printf ("U:%d T:%d R:%d", bt_update, bt_trans, bt_render);
#endif
        }
        if (cflags & PFLAG_FXOS) {
            fxos.get_data (&fxos_data);
        }
        if (cflags & PFLAG_JOY) {
            /* just update our local version of the variable (might not see changes in A-B-A situations) */
            ljoybits = joybits;
        }

        
        __disable_irq ();
        if (!pflags) {
            sleep ();
        }
        __enable_irq ();
    }
    
}