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 (); } }