notro / fbtft

Linux Framebuffer drivers for small TFT LCD display modules. Development has moved to https://git.kernel.org/cgit/linux/kernel/git/gregkh/staging.git/tree/drivers/staging/fbtft?h=staging-testing
1.85k stars 494 forks source link

8 bit to 16 bit color? #402

Closed bvs7 closed 7 years ago

bvs7 commented 8 years ago

I've been using this blog to make a low level interface for output to my 2.2", 320x240 screen with an ili9341 .

I use ioctl to get the variable screen info from the framebuffer (x, y resolutions, bits per pixel).

This shows that the resolution is 320x240 and that bits per pixel is 16. I cannot change these (like I could for an HDMI output or something else). However, when I write 16 bits to a pixel of the framebuffer, only the last 8 bits are used with 3 bits red, 2 blue, and 3 green. How can I change this so I am back to 16 bit color?

flexiti commented 8 years ago

How You write ?

bvs7 commented 8 years ago
#include "SmallRpiScreen.h"

SmallRpiScreen::SmallRpiScreen()
{
    int w, h, bpp;
    init(&w, &h, &bpp);
}

SmallRpiScreen::SmallRpiScreen(int *w, int *h, int *bpp)
{
    init(w,h,bpp);
}

SmallRpiScreen::~SmallRpiScreen()
{
    debug("Starting SmallRpiScreen cleanup");

    // Unmap framebuffer
    munmap(fbp, len_fb);

    // Restore orig screen
    if (ioctl(fbfd, FBIOPUT_VSCREENINFO, &orig_vinfo))
        debug("Error restoring orig screen info");

    debug("Restored orig screen");

    // Unmap pages (sm screen specific)
    munmap(fbpage, PAGES*len_fb);

    // Restore cursor blink; close keyboard
    if (kbfd >= 0) {
        ioctl(kbfd, KDSETMODE, KD_TEXT);
        close(kbfd);
        debug("Restored orig keyboard");
    }

    // Close framebuffer
    close(fbfd);
    debug("Closed framebuffer device");

    debug("Finished cleanup");
}

void SmallRpiScreen::draw_pixel(int x, int y, int c)
{
    debug("Drawing pixel", 3);

    // Calculate pixel's offset inside the buffer
    unsigned int pix_offset = (x * vinfo.bits_per_pixel / 8)+ y * finfo.line_length;

    //Do page calc (not in this implementation)

    // Similar to fbpage[pix_offset] = c 
    //(Normally would use fbp and page offset)
    *((short*)(fbpage + pix_offset + (page * len_fb))) = c;
}

void SmallRpiScreen::clear_screen(int c)
{
    debug("Clearing Screen", 2);

    memset(fbpage + (page * len_fb), c, len_fb);
}

void SmallRpiScreen::switch_page()
{
    debug("Switching page", 2);

    // Naive implementation because we don't have virtual fbp space
    memcpy(fbp, fbpage + (page * len_fb), len_fb);
    page = (page + 1) % PAGES;
}

int SmallRpiScreen::init(int *w, int *h, int *bpp)
{
    FRAMEBUFFER = "/dev/fb1";
    CONSOLE = "/dev/tty1";

        PAGES = 1;

        fbfd = 0; // Framebuffer file descriptor
        kbfd = 0; // Keyboard file descriptor

        len_fb = 0; // Length of framebuffer memory
        fbp = 0; // Framebuffer pointer

        fbpage = 0; // Pointer to the page memory location, because
                                          // The small screen can't load in any virtua$
        page = 0;  // Current page being written to (always 0 for sm screen)

    debug("Starting SmallRpiScreen init");

    // Open framebuffer device file
    fbfd = open(FRAMEBUFFER, O_RDWR);
    if (fbfd == -1){
      debug("Error: cannot open framebuffer device.");
      return (-1);
    }

    debug("Framebuffer device opened");

    // Open console and hide cursor (Do this in keyboard???)
    kbfd = open(CONSOLE, O_WRONLY);
    if (kbfd >= 0) {
        ioctl(kbfd, KDSETMODE, KD_GRAPHICS);
    }
    else{
      debug("Could not open console to disable cursor.");
      return -1;
    }

    debug("Diabled cursor blink.");

    // Get orig variable screen info
    if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)){
      debug("Error reading orig vinfo");
      return -1;
    }

    if(DEBUG >= 1) printf("Original info: %dx%d, %dbpp\n",
               vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);

    // Store orig vinfo
    memcpy(&orig_vinfo, &vinfo, sizeof(struct fb_var_screeninfo));

    //Change vinfo (not in this implementation... but we can try)
    /*vinfo.xres = *w;
    vinfo.yres = *h;
    vinfo.bits_per_pixel = *bpp;
    vinfo.xres_virtual = *w;*/
    vinfo.yres_virtual = vinfo.yres * 2;

    // Set vinfo
    if (ioctl(fbfd, FBIOPUT_VSCREENINFO, &vinfo)){
      debug("Error setting vinfo");
      return -1;
    }

    if(DEBUG >= 1) printf("New info: %dx%d, %dbpp\n          %dx%d virtual\n",
               vinfo.xres, vinfo.yres, vinfo.bits_per_pixel,
               vinfo.xres_virtual, vinfo.yres_virtual);

    // Get fix screen info
    if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)){
      debug("Error reading finfo");
      return -1;
    }

    if (DEBUG >= 1) printf("Fixed info: %d smem_len, %d line_length\n",
                           finfo.smem_len, finfo.line_length);

    // Map framebuffer to user memory
    len_fb = finfo.smem_len;

    fbp = (char*) mmap(0, len_fb,
                  PROT_READ | PROT_WRITE,
                  MAP_SHARED,
                  fbfd, 0);
    if ((int)fbp == -1){
      debug("Failed to map a pointer to fb");
      return -1;
    }

    fbpage = (char*)malloc(len_fb*PAGES);

    if ((int)fbpage == -1){
      debug("Failed to allocate pages");
      return -1;
    }

    *w = vinfo.xres;
    *h = vinfo.yres;
    *bpp = vinfo.bits_per_pixel;

    debug("Finished init");

    return 0;
}

int main(int argc, char *argv[])
{
  int w = 320, h = 240, bpp = 16;
  SmallRpiScreen sc(&w,&h,&bpp);
  printf("\n-------------------\nDisplay: %dx%d\n", w, h);
  int c;
  for(c = 0; c <= 0b11011110; c += 0b01001010){
    sc.clear_screen(c);
    sc.switch_page();
    sleep(1);
  }
}

I'm writing to the framebuffer. I use the clear_screen function to prepare a page, then copy it to the framebuffer with switch_page. When the test code in main runs, the screen goes from black to gray to light gray to white, (one second each).

(Sorry about the wall of code)

flexiti commented 8 years ago

Eg. 16 bit colors looks like this:

define White 0xFFFF

define Black 0x0000

define Blue 0x001F

define Blue2 0x051F

define Red 0xF800

define Magenta 0xF81F

define Green 0x07E0

define Cyan 0x7FFF

define Yellow 0xFFE0

or

u_int16_t GetColor(u_int16_t r,u_int16_t g, u_int16_t b)
{   
 return(((r<<8)& 0xF800) + ((g<<3) & 0x07E0) + ((b>>3) & 0x1F));   
}

wher r,g,b is 0-255 and only high 5 or 6 bits are valid depending on color

try *((u_int16_t *)(fbpage + pix_offset + (page * len_fb))) = (u_int16_t)c; and check init sequence when You load fb module at startup ( what is oryginal info of vinfo.bits_per_pixel ??, display can be set as 8,16 colors etc.)

bvs7 commented 8 years ago

Thanks for the help. My function draw_pixel works well now. I think my clear_screen function was acting strange because memset applies its argument as an unsigned char.

Thanks again.

notro commented 7 years ago

Closing issue since there has been no activity in a long time. Reopen if needed.