Difference between revisions of "FBDirectOnConsoleLCD"
Line 1: | Line 1: | ||
− | + | =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:
- Include the appropriate headers
- Open the "/dev/fb0" device file
- Use the
ioctl
syscall to get information about the device - Use
mmap
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. 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 );
}