Difference between revisions of "FBDirectOnConsoleLCD"

From HBMobile
Jump to navigationJump to search
 
Line 1: Line 1:
foo. you got here before I could update this page.
+
=Does the Frame Buffer Device Work?=
 +
 
 +
Well... this is a pretty simple question to answer... Yes, the frame buffer works, otherwise we wouldn't get the GumStix logo on the screen after booting. But I figured it might be prudent to spend a little time verifying that it works the way I think it's supposed to work.
 +
 
 +
==Getting Frame Buffer Info==
 +
 
 +
==Drawing Stuff on the Screen By Writing to /dev/fb0==
 +
 
 +
Here's some code I wrote to exercise the frame buffer device. You can see the results on [http://www.youtube.com/watch?v=VHG3sumWweU episode 2 of the hbmobile vlog].
 +
 
 +
When you want to draw stuff on the screen, you take the following steps:
 +
 
 +
# Include the appropriate headers
 +
# Open the "/dev/fb0" device file
 +
# Use the <code>ioctl</code> syscall to get information about the device
 +
# Use <code>mmap</code> to map the frame buffer into your process' memory space
 +
# Poke pixels into screen memory
 +
 
 +
You can download a tar file with this code (which is released under a BSD style license) and a Makefile to help compile it. [[Image:Fbtest.tgz|fbtest code]]
 +
 
 +
===Include Headers, Create a main(), Yadda, Yadda, Yadda===
 +
 
 +
So one of the things you can see that I do is I made an "App" data structure that's supposed to store the state of the application. You don't have to do this, I just thought it made things a little easier to deal with.
 +
 
 +
The most important part of the following code fragment is that it lists the headers you're supposed to use.
 +
 
 +
<code><pre>
 +
/* Macro Definitions */
 +
 
 +
/* File Inclusions */
 +
#include <stdio.h>
 +
#include <sys/types.h>
 +
#include <stdlib.h>
 +
#include <linux/fb.h>
 +
#include <unistd.h>
 +
#include <fcntl.h>
 +
#include <sys/mman.h>
 +
#include <asm/param.h>
 +
#include <string.h>
 +
#include <sys/time.h>
 +
 
 +
/* Typedefs, Structs, Unions, Enums, Etc. */
 +
typedef enum {
 +
  FBT_E_NOERR = 0,
 +
  FBT_E_USAGE,
 +
  FBT_E_NULL,
 +
  FBT_E_MALLOC,
 +
  FBT_E_FILE,
 +
  FBT_E_SCREENO,
 +
  FBT_E_SCREENM
 +
} FBTErr;
 +
 
 +
typedef struct {
 +
  char *device;
 +
  int screen_fd;
 +
  unsigned char *screen_ptr;
 +
  unsigned int size;
 +
  int width;
 +
  int height;
 +
  int bpp;
 +
  int left_margin;
 +
  int right_margin;
 +
  int top_margin;
 +
  int bottom_margin;
 +
} App;
 +
 
 +
/* Static Function Prototypes */
 +
static FBTErr fbt_init( App **app, int argc, char *argv[] );
 +
static FBTErr fbt_clear( App *app );
 +
static FBTErr fbt_cleanup( App *app );
 +
static FBTErr fbt_rainbow( App *app, unsigned int offset );
 +
static FBTErr fbt_marching_rainbow( App *app );
 +
 
 +
/* Global Variable Declarations */
 +
 
 +
/* Function Definitions */
 +
 
 +
int main( int argc, char *argv[] ) {
 +
  FBTErr err = FBT_E_NOERR;
 +
  App *app = NULL;
 +
 
 +
  do {
 +
    /* initialize the app structure */
 +
    if( ( FBT_E_NOERR != ( err = fbt_init( &app, argc, argv ) ) ) ) {
 +
      break;
 +
    }
 +
 
 +
    /* clear the screen */
 +
    if( ( FBT_E_NOERR != ( err = fbt_clear( app ) ) ) ) {
 +
      break;
 +
    }
 +
 
 +
    /* paint a rainbow */
 +
    if( ( FBT_E_NOERR != ( err = fbt_marching_rainbow( app ) ) ) ) {
 +
      break;
 +
    }
 +
   
 +
  } while( 0 );
 +
 
 +
  if( ( FBT_E_FILE == err ) || ( FBT_E_SCREENO == err ) ||
 +
      ( FBT_E_SCREENM == err ) ) {
 +
    perror( NULL );
 +
  }
 +
 
 +
  fbt_cleanup( app );
 +
 
 +
  return( err & 0xFF );
 +
}
 +
</pre></code>
 +
 
 +
===Initializing Things===
 +
 
 +
For my money, this is where the most interesting stuff relating to frame buffer access occurs.. and it's not really that interesting. It's just boilerplate code.
 +
 
 +
<code><pre>
 +
/* fbt_init() - initialize the app structure
 +
** if *app is null, the function will allocate a new app structure for you.
 +
** otherwise it assumes you've done it yourself. If app is null... well, that's
 +
** an error.
 +
*/
 +
 
 +
FBTErr fbt_init( App **app, int argc, char *argv[] ) {
 +
  FBTErr err = FBT_E_NOERR;
 +
  unsigned int unalloc = 0;
 +
  struct fb_var_screeninfo screeninfo;
 +
  unsigned int bpp;
 +
 
 +
  do {
 +
    if( NULL == app ) {
 +
      err = FBT_E_NULL;
 +
      break;
 +
    }
 +
 
 +
    if( NULL == *app ) {
 +
      *app = (App *) malloc( sizeof( App ) );
 +
      unalloc = 1;
 +
      if( NULL == *app ) {
 +
unalloc = 0;
 +
err = FBT_E_MALLOC;
 +
break;
 +
      }
 +
    }
 +
 
 +
    memset( *app, 0, sizeof( App ) );
 +
 
 +
    (*app)->screen_fd = -1;
 +
    (*app)->device = "/dev/fb0";
 +
    printf( "opening %s\n", (*app)->device );
 +
    if( ( (*app)->screen_fd = open( (*app)->device, O_RDWR ) ) < 0 ) {
 +
      err = FBT_E_FILE;
 +
      break;
 +
    }
 +
    printf( "device %s is file descriptor %d\n", (*app)->device, (*app)->screen_fd );
 +
 
 +
    if( ( ioctl( (*app)->screen_fd, FBIOGET_VSCREENINFO, &screeninfo ) ) > 0 ) {
 +
      err = FBT_E_SCREENO;
 +
      break;
 +
    }
 +
 
 +
    if( screeninfo.bits_per_pixel > 24 ) {
 +
      bpp = 32;
 +
    } else if( screeninfo.bits_per_pixel > 16 ) {
 +
      bpp = 24;
 +
    } else if( screeninfo.bits_per_pixel > 8 ) {
 +
      bpp = 16;
 +
    } else {
 +
      bpp = 8;
 +
    }
 +
 
 +
    (*app)->width = screeninfo.xres_virtual;
 +
    (*app)->height = screeninfo.yres_virtual;
 +
    (*app)->bpp = screeninfo.bits_per_pixel;
 +
    (*app)->size = (*app)->height * (*app)->width * ( bpp / 8 );
 +
    (*app)->left_margin = screeninfo.left_margin;
 +
    (*app)->right_margin = screeninfo.right_margin;
 +
    (*app)->top_margin = screeninfo.upper_margin;
 +
    (*app)->bottom_margin = screeninfo.lower_margin;
 +
 
 +
    printf( "got device info: %d x %d (%d bits per pixels)\nmargin is (%d,%d,%d,%d)\n",
 +
    (*app)->width, (*app)->height, (*app)->bpp, (*app)->left_margin,
 +
    (*app)->right_margin, (*app)->top_margin, (*app)->bottom_margin );
 +
 
 +
    (*app)->screen_ptr = (unsigned char *) mmap( 0, (*app)->size,
 +
    ( PROT_READ | PROT_WRITE ), MAP_SHARED, (*app)->screen_fd, 0);
 +
 
 +
    if( MAP_FAILED == (*app)->screen_ptr ) {
 +
      err = FBT_E_SCREENM;
 +
      break;
 +
    }
 +
     
 +
    printf( "screen pointer is %d bytes at %08X\n", (*app)->size, (*app)->screen_ptr );
 +
    unalloc = 0;
 +
  } while( 0 );
 +
 
 +
  if( 0 != unalloc ) {
 +
    free( *app );
 +
    *app = NULL;
 +
  }
 +
 
 +
  return( err );
 +
}
 +
</pre></code>
 +
 
 +
===Clearing the Screen===
 +
 
 +
This code demonstrates simple access to the screen. We put a 0xFF in every byte in the frame buffer. It's pretty straight forward...
 +
 
 +
<code><pre>
 +
static FBTErr fbt_clear( App *app ) {
 +
  FBTErr err = FBT_E_NOERR;
 +
  unsigned int i, size;
 +
 
 +
  do {
 +
    if( NULL == app ) {
 +
      err = FBT_E_NULL;
 +
      break;
 +
    }
 +
 
 +
    for( i = 0; i < app->size; i++ ) {
 +
      app->screen_ptr[i] = 0xFF;
 +
    }
 +
  } while( 0 );
 +
 
 +
  return( err );
 +
 
 +
}
 +
</pre></code>
 +
 
 +
===Drawing a Rainbow===
 +
 
 +
This code shows how I draw a rainbow pattern.
 +
 
 +
<code><pre>
 +
static FBTErr fbt_rainbow( App *app, unsigned int offset ) {
 +
  unsigned int i,c,x;
 +
 
 +
  for( i = 0; i < app->size; i++ ) {
 +
    switch( i % 3 ) {
 +
    case 0:
 +
      c = ( offset + i ) % 0x40000;
 +
      x = c;
 +
      break;
 +
    case 1:
 +
      x = c >> 9;
 +
      break;
 +
    case 2:
 +
      x = c >> 18;
 +
    }
 +
    app->screen_ptr[i] = (unsigned char) ( x & 0xFF );
 +
  }
 +
}
 +
 
 +
static FBTErr fbt_marching_rainbow( App *app ) {
 +
  unsigned int i;
 +
  fd_set foo;
 +
  struct timeval time;
 +
 
 +
  FD_ZERO( &foo );
 +
  memset( &time, 0, sizeof( struct timeval ) );
 +
 
 +
  time.tv_sec = 0;
 +
  time.tv_usec = 150000;
 +
 
 +
  for( i = 0; i < 130560; i++ ) {
 +
    fbt_rainbow( app, i * 3 );
 +
    select( 0, &foo, &foo, &foo, &time );
 +
  }
 +
 
 +
  return( FBT_E_NOERR );
 +
}
 +
</pre></code>
 +
 
 +
===Cleaning Up===
 +
 
 +
Before we quit, we unmap the screen and close the device file. I think that Linux is smart enough to do this automagically when your process quits, but hey, it's always polite to explicitly tell the system your intentions.
 +
 
 +
<code><pre>
 +
static FBTErr fbt_cleanup( App *app ) {
 +
  FBTErr err = FBT_E_NOERR;
 +
 
 +
  do {
 +
    if( NULL == app ) {
 +
      err = FBT_E_NULL;
 +
      break;
 +
    }
 +
 
 +
    if( ( NULL != app->screen_ptr ) && ( MAP_FAILED != app->screen_ptr ) ) {
 +
      munmap( app->screen_ptr, app->size );
 +
    }
 +
 
 +
    if( app->screen_fd >= 0 ) {
 +
      close( app->screen_fd );
 +
    }
 +
  } while( 0 );
 +
 
 +
  return( err );
 +
}
 +
</pre></code>

Revision as of 21:02, 5 October 2007

Does the Frame Buffer Device Work?

Well... this is a pretty simple question to answer... Yes, the frame buffer works, otherwise we wouldn't get the GumStix logo on the screen after booting. But I figured it might be prudent to spend a little time verifying that it works the way I think it's supposed to work.

Getting Frame Buffer Info

Drawing Stuff on the Screen By Writing to /dev/fb0

Here's some code I wrote to exercise the frame buffer device. You can see the results on episode 2 of the hbmobile vlog.

When you want to draw stuff on the screen, you take the following steps:

  1. Include the appropriate headers
  2. Open the "/dev/fb0" device file
  3. Use the ioctl syscall to get information about the device
  4. Use mmap to map the frame buffer into your process' memory space
  5. Poke pixels into screen memory

You can download a tar file with this code (which is released under a BSD style license) and a Makefile to help compile it. File:Fbtest.tgz

Include Headers, Create a main(), Yadda, Yadda, Yadda

So one of the things you can see that I do is I made an "App" data structure that's supposed to store the state of the application. You don't have to do this, I just thought it made things a little easier to deal with.

The most important part of the following code fragment is that it lists the headers you're supposed to use.

/* Macro Definitions */

/* File Inclusions */
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <linux/fb.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <asm/param.h>
#include <string.h>
#include <sys/time.h>

/* Typedefs, Structs, Unions, Enums, Etc. */
typedef enum {
  FBT_E_NOERR = 0,
  FBT_E_USAGE,
  FBT_E_NULL,
  FBT_E_MALLOC,
  FBT_E_FILE,
  FBT_E_SCREENO,
  FBT_E_SCREENM
} FBTErr;

typedef struct {
  char *device;
  int screen_fd;
  unsigned char *screen_ptr;
  unsigned int size;
  int width;
  int height;
  int bpp;
  int left_margin;
  int right_margin;
  int top_margin;
  int bottom_margin;
} App;

/* Static Function Prototypes */
static FBTErr fbt_init( App **app, int argc, char *argv[] );
static FBTErr fbt_clear( App *app );
static FBTErr fbt_cleanup( App *app );
static FBTErr fbt_rainbow( App *app, unsigned int offset );
static FBTErr fbt_marching_rainbow( App *app );

/* Global Variable Declarations */

/* Function Definitions */

int main( int argc, char *argv[] ) {
  FBTErr err = FBT_E_NOERR;
  App *app = NULL;

  do {
    /* initialize the app structure */
    if( ( FBT_E_NOERR != ( err = fbt_init( &app, argc, argv ) ) ) ) {
      break;
    }

    /* clear the screen */
    if( ( FBT_E_NOERR != ( err = fbt_clear( app ) ) ) ) {
      break;
    }

    /* paint a rainbow */
    if( ( FBT_E_NOERR != ( err = fbt_marching_rainbow( app ) ) ) ) {
      break;
    }
    
  } while( 0 );

  if( ( FBT_E_FILE == err ) || ( FBT_E_SCREENO == err ) ||
      ( FBT_E_SCREENM == err ) ) {
    perror( NULL );
  }

  fbt_cleanup( app );

  return( err & 0xFF );
}

Initializing Things

For my money, this is where the most interesting stuff relating to frame buffer access occurs.. and it's not really that interesting. It's just boilerplate code.

/* fbt_init() - initialize the app structure
** if *app is null, the function will allocate a new app structure for you.
** otherwise it assumes you've done it yourself. If app is null... well, that's
** an error.
*/

FBTErr fbt_init( App **app, int argc, char *argv[] ) {
  FBTErr err = FBT_E_NOERR;
  unsigned int unalloc = 0;
  struct fb_var_screeninfo screeninfo;
  unsigned int bpp;

  do {
    if( NULL == app ) {
      err = FBT_E_NULL;
      break;
    }

    if( NULL == *app ) {
      *app = (App *) malloc( sizeof( App ) );
      unalloc = 1;
      if( NULL == *app ) {
	unalloc = 0;
	err = FBT_E_MALLOC;
	break;
      }
    }

    memset( *app, 0, sizeof( App ) );

    (*app)->screen_fd = -1;
    (*app)->device = "/dev/fb0";
    printf( "opening %s\n", (*app)->device );
    if( ( (*app)->screen_fd = open( (*app)->device, O_RDWR ) ) < 0 ) {
      err = FBT_E_FILE;
      break;
    }
    printf( "device %s is file descriptor %d\n", (*app)->device, (*app)->screen_fd );

    if( ( ioctl( (*app)->screen_fd, FBIOGET_VSCREENINFO, &screeninfo ) ) > 0 ) {
      err = FBT_E_SCREENO;
      break;
    }

    if( screeninfo.bits_per_pixel > 24 ) {
      bpp = 32;
    } else if( screeninfo.bits_per_pixel > 16 ) {
      bpp = 24;
    } else if( screeninfo.bits_per_pixel > 8 ) {
      bpp = 16;
    } else {
      bpp = 8;
    }

    (*app)->width = screeninfo.xres_virtual;
    (*app)->height = screeninfo.yres_virtual;
    (*app)->bpp = screeninfo.bits_per_pixel;
    (*app)->size = (*app)->height * (*app)->width * ( bpp / 8 );
    (*app)->left_margin = screeninfo.left_margin;
    (*app)->right_margin = screeninfo.right_margin;
    (*app)->top_margin = screeninfo.upper_margin;
    (*app)->bottom_margin = screeninfo.lower_margin;

    printf( "got device info: %d x %d (%d bits per pixels)\nmargin is (%d,%d,%d,%d)\n",
	    (*app)->width, (*app)->height, (*app)->bpp, (*app)->left_margin,
	    (*app)->right_margin, (*app)->top_margin, (*app)->bottom_margin );

    (*app)->screen_ptr = (unsigned char *) mmap( 0, (*app)->size,
	    ( PROT_READ | PROT_WRITE ), MAP_SHARED, (*app)->screen_fd, 0);

    if( MAP_FAILED == (*app)->screen_ptr ) {
      err = FBT_E_SCREENM;
      break;
    }
      
    printf( "screen pointer is %d bytes at %08X\n", (*app)->size, (*app)->screen_ptr );
    unalloc = 0;
  } while( 0 );

  if( 0 != unalloc ) {
    free( *app );
    *app = NULL;
  }

  return( err );
}

Clearing the Screen

This code demonstrates simple access to the screen. We put a 0xFF in every byte in the frame buffer. It's pretty straight forward...

static FBTErr fbt_clear( App *app ) {
  FBTErr err = FBT_E_NOERR;
  unsigned int i, size;

  do {
    if( NULL == app ) {
      err = FBT_E_NULL;
      break;
    }

    for( i = 0; i < app->size; i++ ) {
      app->screen_ptr[i] = 0xFF;
    }
  } while( 0 );

  return( err );
  
}

Drawing a Rainbow

This code shows how I draw a rainbow pattern.

static FBTErr fbt_rainbow( App *app, unsigned int offset ) {
  unsigned int i,c,x;

  for( i = 0; i < app->size; i++ ) {
    switch( i % 3 ) {
    case 0:
      c = ( offset + i ) % 0x40000;
      x = c;
      break;
    case 1:
      x = c >> 9;
      break;
    case 2:
      x = c >> 18;
    }
    app->screen_ptr[i] = (unsigned char) ( x & 0xFF );
  }
}

static FBTErr fbt_marching_rainbow( App *app ) {
  unsigned int i;
  fd_set foo;
  struct timeval time;

  FD_ZERO( &foo );
  memset( &time, 0, sizeof( struct timeval ) );

  time.tv_sec = 0;
  time.tv_usec = 150000;

  for( i = 0; i < 130560; i++ ) {
    fbt_rainbow( app, i * 3 );
    select( 0, &foo, &foo, &foo, &time );
  }

  return( FBT_E_NOERR );
}

Cleaning Up

Before we quit, we unmap the screen and close the device file. I think that Linux is smart enough to do this automagically when your process quits, but hey, it's always polite to explicitly tell the system your intentions.

static FBTErr fbt_cleanup( App *app ) {
  FBTErr err = FBT_E_NOERR;

  do {
    if( NULL == app ) {
      err = FBT_E_NULL;
      break;
    }

    if( ( NULL != app->screen_ptr ) && ( MAP_FAILED != app->screen_ptr ) ) {
      munmap( app->screen_ptr, app->size );
    }

    if( app->screen_fd >= 0 ) {
      close( app->screen_fd );
    }
  } while( 0 );

  return( err );
}