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 496 forks source link

SSD1306 having issues with 128x32 displays. Dividing display width by half. #438

Closed timonsku closed 7 years ago

timonsku commented 7 years ago

I'm trying to get the 128x32 version of the adafruit13m working as a console. So far its looking fairly good. I'm using the following module options: fbtft_device custom name=fb_ssd1306 speed=2000000 width=128 height=32 gpios=reset:18,dc:25

The console is drawing to the display using these option but it is only drawing to half of the width of the display. See this photo: http://imgur.com/GUVeZ1j The odd thing is, if I reboot, during the boot process there is some text getting drawn on the other half of the display and only to that other half, as you can see in the photo. As soon as I map the console to fb1 again with con2fbmap 1 1 it only draws to the left half again and also not updating the pixels that got drawn during the boot process, suggesting it doesn't draw in that area at all. Not drawing black pixels or anything like that. Checking the resolution of /dev/fb1 returns the correct resolution of 128x32.

Now if I omit the width and height settings the console draws to the full screen width but heavily skews the font horizontally, making it unreadable at a small font size and permitting way fewer characters than possible. See this photo: http://imgur.com/Fj6haRC

I looked into the source and recompiled the module with all conditionals removed where it writes different values to registers depending on the display height. This didn't change anything in the behaviour, suggesting that the bug is likely located in the write_vmem function? I have a hard time debugging that function. I can't seem to find a reason for the behaviour, though it seems that you somehow write on the x axis depending on yres here?

for (x = 0; x < par->info->var.xres; x++) {
    for (y = 0; y < par->info->var.yres/8; y++) {
        *buf = 0x00;
        for (i = 0; i < 8; i++)
            *buf |= (vmem16[(y * 8 + i) *
                    par->info->var.xres + x] ?
                 1 : 0) << i;
        buf++;
    }
}

 // Write data 
gpio_set_value(par->gpio.dc, 1);
ret = par->fbtftops.write(par, par->txbuf.buf,
              par->info->var.xres * par->info->var.yres /
              8);
notro commented 7 years ago

I can't look at this in detail, but here's something that might help:

timonsku commented 7 years ago

Thanks! The latest version of fb_ssd1306.c got me on the right path. The last patch adds somewhat of a hotfix for 64x48 displays. Not a good approach imo but it was addressing the issue I had. The problem can be solved for all display sizes by changing set_addr_win(..) to the code below. The problem was that the display driver was setup with hard coded values for the 128x64 display. The function below is also on par with how Adafruit does it with their SSD1306 python library: https://github.com/adafruit/Adafruit_Python_SSD1306/blob/master/Adafruit_SSD1306/SSD1306.py#L164-L169

Fix:

static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
    /* Set Column Address */
    write_reg(par, 0x21);
    write_reg(par, 0x0);
    write_reg(par, par->info->var.xres-1);

    /* Set Page Address */
    write_reg(par, 0x22);
    write_reg(par, 0x0);
    write_reg(par, (par->info->var.yres/8)-1);
}