How to write code for piTFT (2.8")?

EL Wire/Tape/Panels, LEDs, pixels and strips, LCDs and TFTs, etc products from Adafruit

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
edkars
 
Posts: 8
Joined: Tue Dec 16, 2014 7:43 pm

How to write code for piTFT (2.8")?

Post by edkars »

Hi,

Could somebody please provide a pointer to some tutorials describing how I can write C/C++ code for the PiTFT and touchscreen without invoking any Desktop UI manager . All I need to do is display static images/buttons and react to user touch events on the screen but the user will not need (nor want) a desktop.

X11 seems like it'll take a while to figure out. I've heard of QT, but it seems that it runs on top of a desktop environment. Are there high-level libraries that don't require a desktop? I downloaded the source for fbi (ida) but was surprised by how much code it took to do something so seemingly simple.

As for touchscreen events, I downloaded the source for evtest. and that looks simple enough, but if there are libraries out there that already integrate the touchscreen with graphics (eg, draw button --> handle click on button / debounce) , that would be nice.

Thanks,
Ed.

User avatar
edkars
 
Posts: 8
Joined: Tue Dec 16, 2014 7:43 pm

Re: How to write code for piTFT (2.8")?

Post by edkars »

Since nobody else has responded, I'll post my own response...

I'm starting with SDL 2.0 (http://www.willusher.io/pages/sdl2/) and adding FBCP to it (https://github.com/tasanakorn/rpi-fbcp). Instead of running FBCP in the background, I'm going to add the code to copy the frame buffer directly into the SDL screen update function. Unfortunately, the commands used by FBCP scale the content, and I haven't figured out how to work around that "feature." So in order to do a pixel perfect copy, I read FB0 into memory, then only write the top 320x240 pixels into FB1 memory. I also set up the PI to always enable HDMI on boot (/boot/config.txt ... hdmi_force_hotplug=1). I tried forcing HDMI to 320x240, but that's not a valid mode (somehow it accepts the setting, but when reading the FB it is actually larger).

Here's the code snippet for clipping the corner... the original FBCP copied the snapshot directly into FB1, I'm copying it into intermediate memory so that I can copy just what I need. NOTE: This snippet is incomplete, hopefully you can figure out the rest if you need it.
// This will be the size of the image created from the screen snapshot. The screenshot
// will be resized to fit this resource, this is NOT a crop area. This needs to match
// the source display (HDMI) size in order for the image to remain pixel perfect.
screen_resource = vc_dispmanx_resource_create( VC_IMAGE_RGB565, display_info.width, display_info.height, &image_prt );

// This memory will hold the screenshot.
char *viewbuf = (char *)malloc( display_info.height * display_info.width * 8 );

// This should be the cropping rectangle, but it doesn't seem to work that way.
// vc_dispmanx_resource_read_data uses the height value, but ignores the width.
vc_dispmanx_rect_set( &rect1, 0, 0, vinfo.xres, vinfo.yres );

// The length of a source display line
int nDispLineLen = display_info.width * (vinfo.bits_per_pixel / 8);
// The length of a target display line.
int nTftLineLen = vinfo.xres * (vinfo.bits_per_pixel / 8);


ret = vc_dispmanx_snapshot( display, screen_resource, DISPMANX_NO_ROTATE );

// Read the display into the view buffer memory.
// Resize the image to fit the screen resource.
// Use the rect to crop the height, but the width is ignored.
vc_dispmanx_resource_read_data( screen_resource, &rect1, viewbuf, display_info.width * (vinfo.bits_per_pixel / 8) );

// The image has to be cropped manually.
// Read only the first pixels of each line
// that fit into the target display area.
// For the piTFT (320x240) this means read
// the first 320 pixels for 240 lines.
int nOffset = 0;
int nLimit = finfo.smem_len / nTftLineLen;
while( nOffset < nLimit )
{
memcpy( fbp + (nOffset * nTftLineLen), viewbuf + (nOffset * nDispLineLen), nTftLineLen);
nOffset++;
}




Ed.

User avatar
king beetle
 
Posts: 1
Joined: Sat Mar 16, 2013 10:30 pm

Re: How to write code for piTFT (2.8")?

Post by king beetle »

I know this is a three year old post, but I just wanted to drop a line and let you know I was able to use your code snippet to modify fbcp to clone framebuffer 0 to framebuffer 1 attached to an oddball sized TFT panel (220x176). fbcp worked fine with the display in it's default rotation (176x220), but when the display was rotated 90° to a 220x176 configuration, fbcp failed to clone it properly, offsetting each horizontal row by four pixels.
After some research I determined that vc_dispmanx_resource_read_data() expects the destination to have an x-axis resolution that is a multiple of 16, which my 220x176 TFT panel wasn't. Using the understanding I gained from your post, I was able to modify fbcp to store the snapshot in an intermediate buffer, from which I could then copy 220 pixels per row to framebuffer 1, resolving my issue.

Thanks!

Code: Select all

#include <stdio.h>
#include <syslog.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <bcm_host.h>

int process() {
    DISPMANX_DISPLAY_HANDLE_T display;
    DISPMANX_MODEINFO_T display_info;
    DISPMANX_RESOURCE_HANDLE_T screen_resource;
    VC_IMAGE_TRANSFORM_T transform;
    uint32_t image_prt;
    VC_RECT_T rect1;
    int ret;
    int fbfd = 0;
    char *fbp = 0;

    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;


    bcm_host_init();

    display = vc_dispmanx_display_open(0);
    if (!display) {
        syslog(LOG_ERR, "Unable to open primary display");
        return -1;
    }
    ret = vc_dispmanx_display_get_info(display, &display_info);
    if (ret) {
        syslog(LOG_ERR, "Unable to get primary display information");
        return -1;
    }
    syslog(LOG_INFO, "Primary display is %d x %d", display_info.width, display_info.height);


    fbfd = open("/dev/fb1", O_RDWR);
    if (fbfd == -1) {
        syslog(LOG_ERR, "Unable to open secondary display");
        return -1;
    }
    if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {
        syslog(LOG_ERR, "Unable to get secondary display information");
        return -1;
    }
    if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
        syslog(LOG_ERR, "Unable to get secondary display information");
        return -1;
    }

    syslog(LOG_INFO, "Second display is %d x %d %dbps\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);

    screen_resource = vc_dispmanx_resource_create(VC_IMAGE_RGB565, vinfo.xres, vinfo.yres, &image_prt);

    if (!screen_resource) {
        syslog(LOG_ERR, "Unable to create screen buffer");
        close(fbfd);
        vc_dispmanx_display_close(display);
        return -1;
    }

    fbp = (char*) mmap(0, finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);
    if (fbp <= 0) {
        syslog(LOG_ERR, "Unable to create mamory mapping");
        close(fbfd);
        ret = vc_dispmanx_resource_delete(screen_resource);
        vc_dispmanx_display_close(display);
        return -1;
    }

	// This memory will hold the screenshot.
    char *viewbuf = (char *)malloc( display_info.height * display_info.width * 8 );

	// The length of a target display line.
    int	nTftLineLen = vinfo.xres * (vinfo.bits_per_pixel / 8);

    // If the output framebuffer x-size is not a multiple of 16, pad @rect with 16 - (x-size modulus 16)
    int padding = (vinfo.xres % 16);
    if (padding == 0) padding = 16;
    vc_dispmanx_rect_set( &rect1, 0, 0, vinfo.xres + 16 - padding, vinfo.yres );

    while (1) {
        ret = vc_dispmanx_snapshot( display, screen_resource, DISPMANX_NO_ROTATE );

	// Read the display into the view buffer memory.
	// Resizing of the image to fit the screen resource will be done automatically
        vc_dispmanx_resource_read_data(screen_resource, &rect1, viewbuf, vinfo.xres * vinfo.bits_per_pixel / 8);

        // In the event that the output frambuffer x-size is not a multiple of 16,
		// Read only the first x-size byes that fit into the target display area for each line,
		// then skip over any padded bytes before the next line in the output framebuffer

		int	nOffset = 0;
		int	nLimit = finfo.smem_len / nTftLineLen;
		int paddedBytes = (16 - padding) * (vinfo.bits_per_pixel / 8);
		
		while( nOffset < vinfo.yres )
		{
			memcpy( fbp + (nOffset * nTftLineLen), viewbuf + (nOffset * nTftLineLen) + (paddedBytes * nOffset), nTftLineLen);
			nOffset++;
		}
        usleep(25 * 1000);
    }

    munmap(fbp, finfo.smem_len);
    close(fbfd);
    ret = vc_dispmanx_resource_delete(screen_resource);
    vc_dispmanx_display_close(display);
}

int main(int argc, char **argv) {
    setlogmask(LOG_UPTO(LOG_DEBUG));
    openlog("fbcp", LOG_NDELAY | LOG_PID, LOG_USER);

    return process();
}

Locked
Please be positive and constructive with your questions and comments.

Return to “Glowy things (LCD, LED, TFT, EL) purchased at Adafruit”