FBDirectOnConsoleLCD: Difference between revisions
No edit summary |
No edit summary |
||
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 );
}