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.86k stars 495 forks source link

RA8875 #96

Closed Pfannex closed 9 years ago

Pfannex commented 10 years ago

Hi, I'd like to use the excellent fbtft-functionality with my 7" TFT. My TFT has a RA8875-Controller. If I read right, the RA8875 is not supported by the fbtft-package.

At the moment I run the Display with a Python-Script, write by my own. So probably I can help to transfer it in a C-Library.

Did anyone like to help me.....

Greatings from Hamburg Pf@nne

notro commented 10 years ago

I'm sorry, but I don't have time to make a driver. Adafruit has a Arduino library: https://github.com/adafruit/Adafruit_RA8875

FBTFT driver: init_display() Adafruit_RA8875::initialize() You should use the sequence from your python code if they differ.

set_addr_win(): This is the Adafruit_RA8875::setXY() code + register setting to prepare to write pixel data

You can skip set_var() and set_gamma(), at least to begin with.

https://github.com/notro/fbtft/wiki/Development

Pfannex commented 10 years ago

Hi, thanks for your tipps......

I got a look at your development hints. I checked out the latest linux kernel inclusive your fbtft-library without any problems.

At the moment I stuck at the preperation:

root@RaspBerry:~# cp ~/extra/{.config,Module.symvers} ~/linux/ cp: cannot stat /root/extra/.config': No such file or directory cp: cannot stat /root/extra/Module.symvers': No such file or directory

It looks like the /extra directory is missing...... but why, what did I forgot....

Regards from Hamburg Pf@nne

notro commented 10 years ago

The folder is /root/.rpi-firmware/extra

rpi-update has changed recently, so if you use that to install the kernel, you have to use this to preserve the content: SKIP_REPODELETE=1, otherwise /root/.rpi-firmware is deleted.

Pfannex commented 10 years ago

Hi,

compiling a existing module works now!!!

For testing I make some minor changes at the HX8340BN-module and create a new Module.

After calling it with "modprobe fbtft_device name=adafruit22", I get some errors: [ 197.213917] fb_hx8340bn: disagrees about version of symbol fbtft_remove_common [ 197.213957] fb_hx8340bn: Unknown symbol fbtft_remove_common (err -22) [ 197.214004] fb_hx8340bn: disagrees about version of symbol fbtft_probe_common [ 197.214018] fb_hx8340bn: Unknown symbol fbtft_probe_common (err -22) [ 244.585851] fb_hx8340bn: disagrees about version of symbol fbtft_remove_common [ 244.585891] fb_hx8340bn: Unknown symbol fbtft_remove_common (err -22) [ 244.585937] fb_hx8340bn: disagrees about version of symbol fbtft_probe_common [ 244.585951] fb_hx8340bn: Unknown symbol fbtft_probe_common (err -22)

Changes: static int init_display(struct fbtft_par *par) { fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", func);

printk("####################");
printk("Hello World");
printk("####################");

par->fbtftops.reset(par);

I hope you can help me.....

Greetings Pf@nne

Pfannex commented 10 years ago

Sorry, my fault...... I forgot to copy all build-files, also I forgot to run depmod...... Compile existing modules works now.

At the moment I work with 3.10.33+ from the 2014-01-07-wheezy-raspbian-2014-03-12-fbtft-master-firmware.zip image.

After compiling and entering "modprobe fbtft_device name=adafruit22" the command runs without any errormessages..... but the Pi is hanging up now.......

How can I test in small steps?

(I did not modify the driver)

Greetings Pf@nne

notro commented 10 years ago

Did you follow this guide https://github.com/notro/fbtft/wiki/Development ?

Pfannex commented 10 years ago

Yes....

notro commented 10 years ago

I had a look at that Deleopment page, and it hasn't been updated after switching to the new FBTFT images. Sorry about that. I'm doing a rewrite now...

Pfannex commented 10 years ago

Hi, no problem..... Is it also possible, that you forgot to link the download directory bevor you execute the make-command?

ln -s ~/linux /lib/modules/$(uname -r)/build

make -C /lib/modules/$(uname -r)/build SUBDIRS=drivers/video/fbtft modules
notro commented 10 years ago

I have updated the guide and tested the instructions in the process.

Pfannex commented 10 years ago

Thanks.....

cp ~/.rpi-firmware/extra/{.config,Module.symvers} ~/linux/
Pfannex commented 10 years ago

Great work!!

The compiled files run without hang up......

The Kernel is compiled with

gcc Linux version 3.10.33+ (pi@raspi2) (gcc version 4.7.1 20120402

ggc ist updated to

gcc (Debian 4.7.2-5+rpi1) 4.7.2

is this OK?

notro commented 10 years ago

That's as close as we get. The kernel is built on Ubuntu i386 using the rasberrypi/tools compiler which is 4.7.1 On Raspian the closest is 4.7.2

Pfannex commented 10 years ago

OK,

tomorrow I will try to compile the testdriver.....

I hope the great support will hold on.... :-)

Good Night


Von: notro [mailto:notifications@github.com] Gesendet: Mittwoch, 23. April 2014 22:28 An: notro/fbtft Cc: Pfannex Betreff: Re: [fbtft] RA8875 (#96)

That's as close as we get. The kernel is built on Ubuntu i386 using the rasberrypi/tools compiler which is 4.7.1 On Raspian the closest is 4.7.2

Reply to this email directly or view it https://github.com/notro/fbtft/issues/96#issuecomment-41210387 on GitHub. https://github.com/notifications/beacon/7262813__eyJzY29wZSI6Ik5ld3NpZXM6Qm VhY29uIiwiZXhwaXJlcyI6MTcxMzkwNDA2NCwiZGF0YSI6eyJpZCI6Mjk3OTk4NjF9fQ==--744d ec41cf08454fda681277b45d4bcc34cbc00f.gif

Pfannex commented 10 years ago
[ 2624.101207] fb_hello spi0.0: init_display()
[ 2624.101217] Hello World
[ 2624.112049] fb_hello spi0.0: Display update: 2970 kB/s (10.771 ms), fps=0 (0.000 ms)

You have done a really good job!!!! Many thanks for your Support!!!

Now I try to write my own SPI-TFT-driver..... therefore I need to read something about the C-language and the SPI-Commands. So far I only programmed in Pascal (Deplphi/Lazarus), VBA and a little bit in Python.

For the RA8875 I need SPI-Commands with two Bytes per Command. Like this (written in Python):

###########################################################
# SPI-Routines
###########################################################
def WriteCMD(Register):
    GPIO.output(8, False)
    spi.writebytes([0x80, Register])
    GPIO.output(8, True)
    time.sleep(0.0001)
def WriteDATA(DataValue):
    GPIO.output(8, False)
    spi.writebytes([0x00, DataValue])
    GPIO.output(8, True)
    time.sleep(0.0001)
def WriteREG(Register, DataValue):
    WriteCMD(Register)
    WriteDATA(DataValue)
notro commented 10 years ago

Your python WriteREG function does the same as the FBTFT write_reg() macro, and can be used in the same way. write_reg(display "object", register, value);

Pfannex commented 10 years ago

Write SPI-Data works great, with excelent timing! (I did not expect anything different!!!!)

    write_reg(par, 0xAA, 0xBB);
    write_reg(par, 0xCC, 0xDD);
    write_reg(par, 0xEE, 0xFF);
    write_reg(par, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07);

screenshot

                .bgr = true,
                .gpios = (const struct fbtft_gpio []) {
                    { "reset", 25 },
                    { "dc", 24 },
                    {},

But how to controll the /RESET Pin connected to GPIO25 (Pin22)? Right now nothing happend on GPIO25.

def RA8875_Reset():
    print "RA8875 Reset"
    GPIO.output(25, False)
    time.sleep(0.5)
    GPIO.output(25, True)
    time.sleep(0.3)

What does "dc" in combination with SPI means?

Greetings Pf@nne

notro commented 10 years ago

But how to controll the /RESET Pin connected to GPIO25 (Pin22)?

static int init_display(struct fbtft_par *par)
{
    fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);

    par->fbtftops.reset(par);

reset toggles the 'reset' gpio if it is set. fbtft_reset(): https://github.com/notro/fbtft/blob/master/fbtft-core.c#L310

What does "dc" in combination with SPI means?

D/C or RS as it is also called, is used to let the controller distinguish between data/command coming in on the interface. Some controller/modes use a start byte prepended to every transaction to do the same. A quick look at the Adafruit library seem to indicate such a start byte on the RA8875?

Pfannex commented 10 years ago

I add this:

void fbtft_reset(struct fbtft_par *par)
{

    printk("------------------------\n");
    printk("RESET Display\n");
    printk("------------------------\n");

    if (par->gpio.reset == -1)
        return;
    fbtft_par_dbg(DEBUG_RESET, par, "%s()\n", __func__);
    gpio_set_value(par->gpio.reset, 0);
    mdelay(500);
    gpio_set_value(par->gpio.reset, 1);
    mdelay(300);
}

and this:

static int init_display(struct fbtft_par *par)
{
    fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);

    par->fbtftops.reset(par);

    printk("------------------------\n");
    printk("Hello World\n");
    printk("------------------------\n");

but the GPIO25(Pin22) still did not toggle....??

The RA8875 don't need a StartByte.

###########################################################
# RA8875 PLLINI
###########################################################
def RA8875_PLL_ini():
    print "RA8875 PLL setting"

    WriteREG(0x88 , 0x0b)                                       # PLL setting 800*400
    WriteREG(0x89 , 0x02)

###########################################################
# RA8875 INIT
###########################################################
def RA8875_INIT():
    RA8875_Reset()
    print "RA8875 INIT"
    RA8875_PLL_ini()
    CheckBusy()

    WriteREG(0x10 , 0x0C)                                       #//SYSR bit[4:3] color  bit[2:1]=  MPU interface  65K

    WriteREG(0x04 , 0x81)                                       #PCLK

    #Horizontal set
    WriteREG(0x14 , 0x63)                                       #99 + 1 x 8 =800
    WriteREG(0x15 , 0x00)
        ....
        ....
        ....
notro commented 10 years ago

You don't have to add fbtft_reset(), I just pointed to the default implementation. fbtftops.reset is by default set to fbtft_reset here: https://github.com/notro/fbtft/blob/master/fbtft-core.c#L829 If you add debug=7 to the fbtft_device commandline you get some output to the kernel log (dmesg) https://github.com/notro/fbtft/wiki/Debug

It's strange that 'reset' is not changing. It should go low for 20us. With debug=7 you also get some info on gpio assignment

If you add your own reset function, you have to assign it as well:

static struct fbtft_display display = {
[...]
    .fbtftops = {
[...]
        .reset = whatever_reset_function,
    },
};
Pfannex commented 10 years ago

I add something like that:

static void reset(struct fbtft_par *par)
{
    gpio_set_value(par->gpio.reset, 0);
    mdelay(500);
    gpio_set_value(par->gpio.reset, 1);
    mdelay(300);

}

static struct fbtft_display display = {
    .regwidth = 8,
    .width = WIDTH,
    .height = HEIGHT,
    .fbtftops = {
        .init_display = init_display,
        .set_addr_win = set_addr_win,
        .reset = reset,
    },
};

error message

make: Entering directory `/root/linux'
  CC [M]  drivers/video/fbtft/fb_hello.o
drivers/video/fbtft/fb_hello.c: In function ‘reset’:
drivers/video/fbtft/fb_hello.c:59:2: error: implicit declaration of function ‘gpio_set_value’ [-Werror=implicit-function-declaration]
cc1: some warnings being treated as errors
make[1]: *** [drivers/video/fbtft/fb_hello.o] Error 1
make: *** [_module_drivers/video/fbtft] Error 2
make: Leaving directory `/root/linux'
Pfannex commented 10 years ago
#include <linux/gpio.h>

static int reset(struct fbtft_par *par)
{
    gpio_set_value(par->gpio.reset, 0);
    mdelay(500);
    gpio_set_value(par->gpio.reset, 1);
    mdelay(300);

    return 0;
}

static struct fbtft_display display = {
    .regwidth = 8,
    .width = WIDTH,
    .height = HEIGHT,
    .fbtftops = {
        .init_display = init_display,
        .set_addr_win = set_addr_win,
        .reset = reset,
    },
};

works, I forgot the header!!! screenshot

but makes some warnings....

make: Entering directory `/root/linux'
  CC [M]  drivers/video/fbtft/fb_hello.o
drivers/video/fbtft/fb_hello.c:77:3: warning: initialization from incompatible pointer type [enabled by default]
drivers/video/fbtft/fb_hello.c:77:3: warning: (near initialization for ‘display.fbtftops.reset’) [enabled by default]
  Building modules, stage 2.
notro commented 10 years ago

reset is a void function: https://github.com/notro/fbtft/blob/master/fbtft.h#L89 not

static int reset(struct fbtft_par *par)
{

but

static void reset(struct fbtft_par *par)
{
Pfannex commented 10 years ago

see below...

Pfannex commented 10 years ago

Another beginner's fault...., sorry... I changed this for testing before I found the missing header.

How can I control how many bytes were sending within one "/ChipSelect-cycle"? It seams, that not all Bytes

    write_reg(par, 0x80, 0x88 , 0x00, 0x0b);                                        
    write_reg(par, 0x80, 0x89 , 0x00, 0x02);

will be send in the same "/cs-cycle"....

At the moment it looks like this: screenshot

I WILL TRY to write my own write_reg function or is there a other way?

the needed function should work like this: WriteCMD is leading by 0x80 for CommandWrite and followed by the register adress. Both Bytes must be send in the same /CS-cycle. WriteData is leading by 0x00 for DataWrite and followed by the data-value. Both Bytes must be send in the same /CS-cycle.

A quick look at the Adafruit library seem to indicate such a start byte on the RA8875?

Now I know what you mean.... Yes, the switch between command and data is made with the leading Byte. Exceptional continuous write data to the display memory. The data to write will only need one leading 0x00 for writing data in the same /cs-cycle.

These functions will be contained in WriteREG(Register, DataValue).

###########################################################
# SPI-Routines
###########################################################
def WriteCMD(Register):
    GPIO.output(8, False)
    spi.writebytes([0x80, Register])
    GPIO.output(8, True)
    time.sleep(0.0001)
def WriteDATA(DataValue):
    GPIO.output(8, False)
    spi.writebytes([0x00, DataValue])
    GPIO.output(8, True)
    time.sleep(0.0001)
def WriteREG(Register, DataValue):
    WriteCMD(Register)
    WriteDATA(DataValue)
....
#MemoryWrite
        WriteCMD(0x02)                                #MemoryWriteCommand
        GPIO.output(8, False)
        spi.writebytes([0x00])                          #WriteDataByte
        for block in blocks:
            spi.writebytes(map(ord, block))  #WriteData 
        GPIO.output(8, True)

Gruß Pf@nne

notro commented 10 years ago

I have hacked the default functions from https://github.com/notro/fbtft/blob/master/fbtft-bus.c Not tested.

static void write_reg8_bus8(struct fbtft_par *par, int len, ...)
{
    va_list args;
    int i, ret;
    int pad = 0;
    u8 *buf = (u8 *)par->buf;

    if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) {
        va_start(args, len);
        for (i = 0; i < len; i++)
            *(((u8 *)buf) + i) = (u8)va_arg(args, unsigned int);
        va_end(args);
        fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par,
            par->info->device, u8, buf, len, "%s: ", __func__);
    }
    if (len <= 0)
        return;

    *buf++ = 0x80;
    va_start(args, len);
    i = len;
    while (i--) {
        *buf++ = (u8)va_arg(args, unsigned int);
    }
    va_end(args);
    ret = par->fbtftops.write(par, par->buf, len + 1);
    if (ret < 0) {
        dev_err(par->info->device,
            "%s: write() failed and returned %d\n", __func__, ret);
        return;
    }
}

static int write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len)
{
    u16 *vmem16;
    u16 *txbuf16 = (u16 *)par->txbuf.buf;
    size_t remain;
    size_t to_copy;
    size_t tx_array_size;
    int i;
    int ret = 0;
    size_t startbyte_size = 0;

    fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s(offset=%zu, len=%zu)\n",
        __func__, offset, len);

    remain = len / 2;
    vmem16 = (u16 *)(par->info->screen_base + offset);

//  if (par->gpio.dc != -1)
//      gpio_set_value(par->gpio.dc, 1);
//
//  /* non buffered write */
//  if (!par->txbuf.buf)
//      return par->fbtftops.write(par, vmem16, len);
//
//  /* buffered write */
    tx_array_size = par->txbuf.len / 2;

//  if (par->startbyte) {
        txbuf16 = (u16 *)(par->txbuf.buf + 1);
        tx_array_size -= 2;
//      *(u8 *)(par->txbuf.buf) = par->startbyte | 0x2;
        *(u8 *)(par->txbuf.buf) = 0x00;
        startbyte_size = 1;
//  }

    while (remain) {
        to_copy = remain > tx_array_size ? tx_array_size : remain;
        dev_dbg(par->info->device, "    to_copy=%zu, remain=%zu\n",
                        to_copy, remain - to_copy);

        for (i = 0; i < to_copy; i++)
            txbuf16[i] = cpu_to_be16(vmem16[i]);

        vmem16 = vmem16 + to_copy;
        ret = par->fbtftops.write(par, par->txbuf.buf,
                        startbyte_size + to_copy * 2);
        if (ret < 0)
            return ret;
        remain -= to_copy;
    }

    return ret;
}

static struct fbtft_display display = {
    .regwidth = 8,
    .width = WIDTH,
    .height = HEIGHT,
    .fbtftops = {
        .init_display = init_display,
        .set_addr_win = set_addr_win,
        .reset = reset,
        .write_vmem = write_vmem16_bus8,
        .write_register = write_reg8_bus8,
    },
};
Pfannex commented 10 years ago

first I start to implement the "write_reg8_bus8" by adding this lines (this funktion was not implementet by default)

fbtft-bus.c

define_fbtft_write_reg(fbtft_write_reg8_bus8, u8, )   //line 73
define_fbtft_write_reg(fbtft_write_reg16_bus8, u16, cpu_to_be16)
define_fbtft_write_reg(fbtft_write_reg16_bus16, u16, )

//START********************************************************************************
static void fbtft_write_reg8_bus8(struct fbtft_par *par, int len, ...)  //line 78
{
    va_list args;
    int i, ret;
    //int pad = 0;
    u8 *buf = (u8 *)par->buf;

fb_hello.c

        .set_addr_win = set_addr_win,
        .reset = reset,
        .write_register = write_reg8_bus8,
   },
};

errormessage

make: Entering directory `/root/linux'
  CC [M]  drivers/video/fbtft/fbtft-bus.o
drivers/video/fbtft/fbtft-bus.c:78:13: error: redefinition of ‘fbtft_write_reg8_bus8’
drivers/video/fbtft/fbtft-bus.c:73:1: note: previous definition of ‘fbtft_write_reg8_bus8’ was here
drivers/video/fbtft/fbtft-bus.c:78:13: warning: ‘fbtft_write_reg8_bus8’ defined but not used [-Wunused-function]
make[1]: *** [drivers/video/fbtft/fbtft-bus.o] Error 1
make: *** [_module_drivers/video/fbtft] Error 2
make: Leaving directory `/root/linux'

please excuse my bad c-skills..... but I think we are very close to accomplish our journey... :-)

notro commented 10 years ago

write_reg8_bus8() and write_vmem16_bus8() should be in your driver. In FBTFT you can override any function you want, and still take advantage of the rest of FBTFT. fbtftops contains the functions used to make the driver work and it has some defaults assigned. You can hook into any of those with your own version.

So no changing drivers/video/fbtft/fbtft-bus.c

Pfannex commented 10 years ago

I feel like a idiot...... The same we made with the reset-function!!!! Of course the code will compile, this code comes from the MASTER..... I will test the changes......

Pfannex commented 10 years ago
    write_reg(par,0xAA);                                        
    write_reg(par,0xBB);                                        

Now a leading 0x80 (Command) will be sent every time, screenshot

but how to send data with a leading 0x00?

I think for the moment it is enough to send each committed bytes in one /cs-cycle..... e.g.

write_reg(par,0xAA);                 -->SPI 0xAA
write_reg(par,0xAA,0xBB);            -->SPI 0xAA|0xBB
write_reg(par,0xAA,0xBB,0xCC);       -->SPI 0xAA|0xBB|0xCC
...

How to add and declare a simple void to my driver-code??

notro commented 10 years ago

How about this

static void write_reg8_bus8(struct fbtft_par *par, int len, ...)
{
    va_list args;
    int i, ret;
    u8 *buf = (u8 *)par->buf;

    if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) {
        va_start(args, len);
        for (i = 0; i < len; i++) {
            buf[i] = (u8)va_arg(args, unsigned int);
        }
        va_end(args);
        fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, par->info->device, u8, buf, len, "%s: ", __func__);
    }

    va_start(args, len);

    *buf++ = 0x80;
    *buf = (u8)va_arg(args, unsigned int);
    ret = par->fbtftops.write(par, par->buf, 2);
    if (ret < 0) {
        va_end(args);
        dev_err(par->info->device, "%s: write() failed and returned %dn", __func__, ret);
        return;
    }
    len--;

    if (len) {
        buf = (u8 *)par->buf;
        *buf++ = 0x00;
        i = len;
        while (i--) {
            *buf++ = (u8)va_arg(args, unsigned int);
        }
        ret = par->fbtftops.write(par, par->buf, len + 1);
        if (ret < 0) {
            va_end(args);
            dev_err(par->info->device, "%s: write() failed and returned %dn", __func__, ret);
            return;
        }
    }
    va_end(args);
}
Pfannex commented 10 years ago
    write_reg(par,0xAA);                                        

screenshot01

    write_reg(par,0xBB,0xCC);   

screenshot02

    write_reg(par,0xDD,0xEE,0xFF);  

screenshot03

Case 1 and 2 will be OK !!!

I have to look how far case 3 works with memory-block-write.... Now I have to finish the Init-Routine.......

stay tuned.....

Pfannex commented 10 years ago

http://youtu.be/VkyLIXG4Dus

Init works fine..... stay tuned......

Pfannex commented 10 years ago

How to use

#include <stdlib.h>    
#include <stdio.h>    

inside my driver???

make: Entering directory `/root/linux'
  CC [M]  drivers/video/fbtft/fb_hello.o
drivers/video/fbtft/fb_hello.c:12:24: fatal error: stdlib.h: No such file or directory
compilation terminated.
make[1]: *** [drivers/video/fbtft/fb_hello.o] Error 1
make: *** [_module_drivers/video/fbtft] Error 2
make: Leaving directory `/root/linux'

In a "normal" C-file outside fbtft ist works....

notro commented 10 years ago

Why do you need it?

Pfannex commented 10 years ago

Good morning....

I need random bytes to test some speed/timing functions. I will remove them later....

By the way, where do you come from?

Greetings from Hamburg..

notro commented 10 years ago

See http://stackoverflow.com/questions/12961299/generate-random-number-in-kernel-module

I live in Norway.

notro commented 10 years ago

This is how I do performance testing: https://github.com/notro/fbtft/wiki/Performance

Pfannex commented 10 years ago

Thanks for the links.....

I will have a look asap.....

Now it´s BBQ-Time.....


Von: notro [mailto:notifications@github.com] Gesendet: Samstag, 26. April 2014 15:00 An: notro/fbtft Cc: Pfannex Betreff: Re: [fbtft] RA8875 (#96)

This is how I do performance testing: https://github.com/notro/fbtft/wiki/Performance

— Reply to this email directly or view it https://github.com/notro/fbtft/issues/96#issuecomment-41468049 on GitHub. https://github.com/notifications/beacon/7262813__eyJzY29wZSI6Ik5ld3NpZXM6Qm VhY29uIiwiZXhwaXJlcyI6MTcxNDEzNjM3OCwiZGF0YSI6eyJpZCI6Mjk3OTk4NjF9fQ==--f53d 8c5080907adb917b92c72d601d24ca8c0653.gif

Pfannex commented 10 years ago

Hi Notro,

for testing the timing controll I need to put a adjustable delay time between the command byte and the data byte and also between command-byte and register-byte resp. data-byte and value-byte

0x80  delay 0x..  delay  0x00  delay  0x..

Also I thought about how to write the framebuffer, I´m not shure if the current mem-write function works. This is what the RA8875 need:

//prepare to memorywrite 
SPI 0x80|0x02 //Command Start MemoryWrite (only one time)
SPI 0x00          //leading DataByte (only one time)

//Start to write Pixel-Data continuous
SPI 0x.., 0x.., 0x.. // Write first DataBufferBlock [adjustable / full FrameBuffer or BlockWise]
SPI 0x.., 0x.., 0x.. // Write next DataBufferBlock
SPI 0x.., 0x.., 0x.. // Write last DataBufferBlock

By the way, I understood a little bit more...... It seams that there are "little" differences between C-programming and C-programming in kernel-modules......

I hope we get the RA8875 startet.....

Greetings from the sunny Hamburg.... Pf@nne

Pfannex commented 10 years ago

Another good morning.....

I still got some timing problems. Maybe the time between /CS and the rising CLK is to short. It would be good if this time also can be adjusted for testing. Maybe you can give me a hint were I can add a delay.

Python /CS timing (very slow!) screenshot01 fbtft /CS timing (much faster!) screenshot02

Normally 1.4µs should be not a problem for the RA8875 driven with 20MHz ..... But I have to check this out.....

Nice Sunday.. Pf@nne

notro commented 10 years ago

Maybe you can give me a hint were I can add a delay.

You can't put a delay between CS going low and the clock rising. This is handled by hardware. But you can slow down the overall transfer a little by turning off DMA:

sudo modprobe --first-time fbtft dma=0

This gives bursts of ~120 bytes, instead of txbuflen (2048) bytes.

Also I thought about how to write the framebuffer, I´m not shure if the current mem-write function works.

I can tell you have FBTFT works: When someone writes to /dev/fb1, a memory page fault occurs (4k page). This memory page is recorded and a delayed function is scheduled. Further page faults is addded. When the scheduled function (fbtft_deferred_io) fires it transforms those pages into display pixel lines. Then fbtft_update_display() is called with a startline and an endline. fbtft_update_display calls set_addr_win() to tell the controller which area will be updated (xs=0, ys=startline, xe=WIDTH, ye=endline). Then the start/endline is turned into an offset into video memory and a length, and passed on to write_mem(). write_mem then writes this memory block to the controller in chunks of txbuflen.

If the RA8875 can change it's endianess, it's possible to simplify write_mem a bit. The Pi is little endian and all controllers I have seen is big endian. This means the pixels has to go through a byteswap. But that's for later when we get this working.

Pfannex commented 10 years ago

OK, draw lines will be OK....

Most of the time the biggest problem is behind the keyboard....

http://youtu.be/M1ob5mlLyHk

Now I need to test the memory write function of the RA8875. Therefor it is necessary to send after the MemWrite-Command

    write_reg(par, 0x02);     //SPI 0x80|0x02 

only one zero byte (0x00) followed from the RGB-PixelData.

How to send a single byte, without a leading 0x80?

    write_reg(par, 0x02); //0x80|0x02
    write_reg(par, 0x00, 0x1F,0x00, 0x1F,0x00, 0x1F,0x00); //0x80|0x00 ; 0x00|0x1F ; 0x00|0x1F|0x00
notro commented 10 years ago

How to send a single byte, without a leading 0x80?

In write_vmem you can just add it to the transfer buffer txbuf.

Can you explain to me how the RA8875 expects a pixel transfer? Do you set the x,y position, issue command 0x02 and then send the pixels, with each SPI transfer chunk having a startbyte of 0x00 to signify data? How do you set x,y?

Pfannex commented 10 years ago

//prepare to memorywrite

SPI 0x80|0x02 //Command Start MemoryWrite (only one time)
SPI 0x00          //leading DataByte (only one time)
//Start to write Pixel-Data continuous
SPI 0x.., 0x.., 0x.. // Write first DataBufferBlock [adjustable / full FrameBuffer or BlockWise]
SPI 0x.., 0x.., 0x.. // Write next DataBufferBlock
SPI 0x.., 0x.., 0x.. // Write last DataBufferBlock

Only one start byte, only one time is necessary....

How do you set x,y?

static void Set_Memory_Write_Cursor(struct fbtft_par *par, int x, int y)
{
    write_reg(par, 0x46,  x & 0xff);            // X [10Bit]    1111_1111 -> LSB
    write_reg(par, 0x47, (x >> 8) & 0x03);      //              xxxx_xx11 -> HSB
    write_reg(par, 0x48,  y & 0xff);            // Y [9Bit]     1111_1111 -> LSB
    write_reg(par, 0x49, (y >> 8) & 0x01);      //              xxxx_xxx1 -> HSB
}
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
    fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
        "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);

        Set_Active_Window(par, xs, ys, xs+xe, ys+ye);
    Set_Memory_Write_Cursor(par, xs, ys-1);
    write_reg(par, 0x02);
}

After the init I left a blue screen and close with the memory write command:

    write_reg(par, 0x01 , 0x80);                                        //display on

    //ClearScreen
    ClearScreen(par, 0x1F);
    mdelay(1000);

    //MemWrite
    //Set_Memory_Write_Cursor(par, 0, 0);
    write_reg(par, 0x02);

It looks like this.... http://youtu.be/mSjxK8JPEKg

after mapping the frame buffer

con2fbmap 1 1

It looks like this.... http://youtu.be/QDMGhttC0vo

I think I also got some problems with the active Windows size. To send a single 0x00 byte will be useful for testing the buffer write.

Greetings Pf@nne

Pfannex commented 10 years ago
/*
 * Copyright and GPL statement
*/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>

#include <linux/gpio.h>
#include "fbtft.h"

#define DRVNAME     "fb_hello"
#define WIDTH       800
#define HEIGHT      480

//#######################################################################################
//# DECLARATION
//#######################################################################################
static void Set_Active_Window(struct fbtft_par *par, int x1, int y1, int x2, int y2);
static int Split_RGB_RED(struct fbtft_par *par, int Color);
static int Split_RGB_GREEN(struct fbtft_par *par, int Color);
static int Split_RGB_BLUE(struct fbtft_par *par, int Color);
static void Set_BG_RGB_Color(struct fbtft_par *par, int R, int G, int B);
static void Set_FG_RGB_Color(struct fbtft_par *par, int R, int G, int B);
static void Set_BG_Color(struct fbtft_par *par, int Color);
static void Set_FG_Color(struct fbtft_par *par, int Color);
static void ClearScreen(struct fbtft_par *par, int Color);
static void DrawLine(struct fbtft_par *par, int x1, int y1, int x2, int y2, int Color);
static void Set_Memory_Write_Cursor(struct fbtft_par *par, int x, int y);
static void Memory_Write(struct fbtft_par *par);

//#######################################################################################
//# User Functions
//#######################################################################################
static void Set_Active_Window(struct fbtft_par *par, int x1, int y1, int x2, int y2)
{
    printk("Set active Window\n");
    write_reg(par, 0x30 , x1 & 0x00FF);                         // x1-LOW
    write_reg(par, 0x31 , (x1 & 0xFF00) >> 8);                  // x1-HIGH
    write_reg(par, 0x32 , y1 & 0x00FF);                         // y1-LOW
    write_reg(par, 0x33 , (y1 & 0xFF00) >> 8);                  // y1-HIGH
    write_reg(par, 0x34 , x2 & 0x00FF);                         // x2-LOW
    write_reg(par, 0x35 , (x2 & 0xFF00) >> 8);                  // x2-HIGH
    write_reg(par, 0x36 , y2 & 0x00FF);                         // y2-LOW
    write_reg(par, 0x37 , (y2 & 0xFF00) >> 8);                  // y2-HIGH
}

static int Split_RGB_RED(struct fbtft_par *par, int Color)
{
    return (Color >> 11);           //(1111_1|xxx_xxx|x_xxxx >> 11) = 1_1111                                = 0x1F
}
static int Split_RGB_GREEN(struct fbtft_par *par, int Color)
{
    return ((Color >> 5) & 0x3F);   //(1111_1|111_111|x_xxxx >> 5)  = (111_11|11_1111 & 11_1111) = 11_1111  = 0x3F
}
static int Split_RGB_BLUE(struct fbtft_par *par, int Color)
{
    return (Color & 0x1f);          //(xxxx_x|xxx_xxx|1_1111 & 1_1111) = 1_1111                                 = 0x1F
}

static void Set_BG_RGB_Color(struct fbtft_par *par, int R, int G, int B)
{
    write_reg(par, 0x60, R);                                        //xxxx_x|xxx_xxx|x_xxxx / in 16-Bit-Mode
    write_reg(par, 0x61, G);        
    write_reg(par, 0x62, B);
}   
static void Set_FG_RGB_Color(struct fbtft_par *par, int R, int G, int B)
{
    write_reg(par, 0x63, R);
    write_reg(par, 0x64, G);
    write_reg(par, 0x65, B);
}

static void Set_BG_Color(struct fbtft_par *par, int Color)
{
    Set_BG_RGB_Color(par, Split_RGB_RED(par, Color), Split_RGB_GREEN(par, Color), Split_RGB_BLUE(par, Color));
}
static void Set_FG_Color(struct fbtft_par *par, int Color)
{
    Set_FG_RGB_Color(par, Split_RGB_RED(par, Color), Split_RGB_GREEN(par, Color), Split_RGB_BLUE(par, Color));
}

static void ClearScreen(struct fbtft_par *par, int Color)
{
    Set_Active_Window(par, 0, 0, 200, 200);
    Set_BG_Color(par, Color);
    write_reg(par, 0x8e, 0x80);
}

static void DrawLine(struct fbtft_par *par, int x1, int y1, int x2, int y2, int Color)
{
    write_reg(par, 0x91,  x1 & 0xff);           // X_Start [10Bit]  1111_1111 -> LSB
    write_reg(par, 0x92, (x1 >> 8) & 0x03);     //                  xxxx_xx11 -> HSB
    write_reg(par, 0x93,  y1 & 0xff);           // Y_Start [9Bit]   1111_1111 -> LSB
    write_reg(par, 0x94, (y1 >> 8) & 0x01);     //                  xxxx_xxx1 -> HSB

    write_reg(par, 0x95,  x2 & 0xff);           // X_Ende  [10Bit]  1111_1111 -> LSB
    write_reg(par, 0x96, (x2 >> 8) & 0x03);     //                  xxxx_xx11 -> HSB
    write_reg(par, 0x97,  y2 & 0xff);           // Y_Ende  [9Bit]   1111_1111 -> LSB
    write_reg(par, 0x98, (y2 >> 8) & 0x01);     //                  xxxx_xxx1 -> HSB

    Set_FG_Color(par, Color);

    write_reg(par, 0x90, 0x80);                 //DrawLine Start Signal 1000_xxx0
    write_reg(par, 0x90, 0x00); 
}

static void Set_Memory_Write_Cursor(struct fbtft_par *par, int x, int y)
{
    write_reg(par, 0x46,  x & 0xff);            // X [10Bit]    1111_1111 -> LSB
    write_reg(par, 0x47, (x >> 8) & 0x03);      //              xxxx_xx11 -> HSB
    write_reg(par, 0x48,  y & 0xff);            // Y [9Bit]     1111_1111 -> LSB
    write_reg(par, 0x49, (y >> 8) & 0x01);      //              xxxx_xxx1 -> HSB
}
static void Memory_Write(struct fbtft_par *par)
{
    int i;

    Set_Memory_Write_Cursor(par, 100, 100);
    write_reg(par, 0x02);
    write_reg(par, 0x00, 0x1F,0x00, 0x1F,0x00, 0x1F,0x00);

    for (i=0;i<100;i++) {
        //write_reg(par, 0x00, 0x1F,0x00, 0x1F,0x00, 0x1F,0x00);

        //ClearScreen(par, i*1000);
        //DrawLine(par, i,0,200,200, i*80);
        //mdelay(200);
    }   

}

//#######################################################################################
//# fbtft Funktions
//#######################################################################################
static int init_display(struct fbtft_par *par)
{
    int i,j,k;

//###############
    //gpio_set_value(par->gpio.dc, 1);

    fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
    par->fbtftops.reset(par);

    write_reg(par, 0x88 , 0x0b);                                        // PLL setting 800*400
    write_reg(par, 0x89 , 0x02);
    mdelay(10);

    write_reg(par, 0x10 , 0x0C);                                        //SYSR  bit[4:3] color  bit[2:1]=  MPU interface  65K
    write_reg(par, 0x04 , 0x81);                                        //PCLK

    //Horizontal set
    write_reg(par, 0x14 , 0x63);                                        //99 + 1 x 8 =800
    write_reg(par, 0x15 , 0x00);
    write_reg(par, 0x16 , 0x03);
    write_reg(par, 0x17 , 0x03);
    write_reg(par, 0x18 , 0x0B);
    // Vertical set
    write_reg(par, 0x19 , 0xDF);                                        //479 low
    write_reg(par, 0x1a , 0x01);                                        //479 high
    write_reg(par, 0x1b , 0x20);
    write_reg(par, 0x1c , 0x00);
    write_reg(par, 0x1d , 0x16);
    write_reg(par, 0x1e , 0x00);
    write_reg(par, 0x1f , 0x01);

    //ActiveWindow(0, 0, 799, 479)
    Set_Active_Window(par, 0, 0, 799, 479);

    write_reg(par, 0x8a , 0x81);                                        //1000_0001=0x81 / Enable SysCLK/2
    write_reg(par, 0x8b , 0xFF);                                        //Brightness parameter 0xff-0x00
    //time.sleep(0.01)
    mdelay(10);

    write_reg(par, 0x01 , 0x80);                                        //display on

    //ClearScreen
    ClearScreen(par, 0x1F);
    mdelay(1000);

    //MemWrite
    //Set_Memory_Write_Cursor(par, 0, 0);
    write_reg(par, 0x02);

//Run LineTest ########################################

/*  for (i=0;i<10;i++) {
        for (j=0;j<10;j++) {
            for (k=0;k<10;k++) {

                DrawLine(par, i*20,j*20,k*20,210, i*j*k*65);

            }
        }
    }   

//Memory_Write(par);

    for (i=0;i<255;i++) {
        //ClearScreen(par, i*1000);
        DrawLine(par, i,0,200,200, i*80);
        //mdelay(200);
    }   
    for (i=0;i<255;i++) {
        //ClearScreen(par, i*1000);
        DrawLine(par, 0,i,200,200, i*160);
        //mdelay(200);
    }   
    for (i=0;i<255;i++) {
        //ClearScreen(par, i*1000);
        DrawLine(par, 0,i*2,400,400, i*160);
        //mdelay(200);
    }   

    //ClearScreen(par, 0xFFFF);

    //DrawLine(par, 0,0,799,479, 5000);

    mdelay(1);
    DrawLine(par, 20,20,200,200);
    mdelay(1);
    DrawLine(par, 10,10,200,200);
    mdelay(100);

    Set_Active_Window(par, 0, 0, 799, 479);
    //ClearScreen(par, 0xFFFF);

    for (i=0;i<10;i++) {
        for (j=0;j<10;j++) {
            for (k=0;k<10;k++) {

                DrawLine(par, i*20,j*20,k*20,210, i*j*k*65);

            }
        }
    }   
    //DrawLine(par, 0,0,799,479, 0x1F);
    //DrawLine(par, 479,0,799,0, 0x1F);

    DrawLine(par, 0,0,799,479, 5000);
*/  

    return 0;
}

static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
    fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
        "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);

    Set_Active_Window(par, xs, ys, xs+xe, ys+ye);
    Set_Memory_Write_Cursor(par, xs, ys-1);
    write_reg(par, 0x02);
}

static void reset(struct fbtft_par *par)
{
    gpio_set_value(par->gpio.reset, 0);
    mdelay(500);
    gpio_set_value(par->gpio.reset, 1);
    mdelay(300);

}

static void write_reg8_bus8(struct fbtft_par *par, int len, ...)
{
    va_list args;
    int i, ret;
    u8 *buf = (u8 *)par->buf;

//###############
    //gpio_set_value(par->gpio.dc, 0);

    if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) {
        va_start(args, len);
        for (i = 0; i < len; i++) {
            buf[i] = (u8)va_arg(args, unsigned int);
        }
        va_end(args);
        fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, par->info->device, u8, buf, len, "%s: ", __func__);
    }

    va_start(args, len);

    *buf++ = 0x80;
    *buf = (u8)va_arg(args, unsigned int);
    //mdelay(1);
    ret = par->fbtftops.write(par, par->buf, 2);
    if (ret < 0) {
        va_end(args);
        dev_err(par->info->device, "%s: write() failed and returned %dn", __func__, ret);
        return;
    }
    len--;

//###############
    //gpio_set_value(par->gpio.dc, 1);
    //mdelay(0.01);                     //  /CS_HighTime between 0x80 and 0x00
    //gpio_set_value(par->gpio.dc, 0);

    if (len) {
        buf = (u8 *)par->buf;
        *buf++ = 0x00;
        i = len;
        while (i--) {
            *buf++ = (u8)va_arg(args, unsigned int);
        }
        //mdelay(1);

        ret = par->fbtftops.write(par, par->buf, len + 1);
        if (ret < 0) {
            va_end(args);
            dev_err(par->info->device, "%s: write() failed and returned %dn", __func__, ret);
            return;
        }
    }
    va_end(args);

//###############
    //gpio_set_value(par->gpio.dc, 1);
    //mdelay(0.1);                      //   /CS_HighTime between write_reg()

}

static int write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len)
{
    u16 *vmem16;
    u16 *txbuf16 = (u16 *)par->txbuf.buf;
    size_t remain;
    size_t to_copy;
    size_t tx_array_size;
    int i;
    int ret = 0;
    size_t startbyte_size = 0;

    fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s(offset=%zu, len=%zu)\n",
        __func__, offset, len);

    remain = len / 2;
    vmem16 = (u16 *)(par->info->screen_base + offset);

//  if (par->gpio.dc != -1)
//      gpio_set_value(par->gpio.dc, 1);
//
//  /* non buffered write */
//  if (!par->txbuf.buf)
//      return par->fbtftops.write(par, vmem16, len);
//
//  /* buffered write */
    tx_array_size = par->txbuf.len / 2;

//  if (par->startbyte) {
        txbuf16 = (u16 *)(par->txbuf.buf + 1);
        tx_array_size -= 2;
//      *(u8 *)(par->txbuf.buf) = par->startbyte | 0x2;
        *(u8 *)(par->txbuf.buf) = 0x00;
        startbyte_size = 1;
//  }

    while (remain) {
        to_copy = remain > tx_array_size ? tx_array_size : remain;
        dev_dbg(par->info->device, "    to_copy=%zu, remain=%zu\n",
                        to_copy, remain - to_copy);

        for (i = 0; i < to_copy; i++)
            txbuf16[i] = cpu_to_be16(vmem16[i]);

        vmem16 = vmem16 + to_copy;
        ret = par->fbtftops.write(par, par->txbuf.buf,
                        startbyte_size + to_copy * 2);
        if (ret < 0)
            return ret;
        remain -= to_copy;
    }

    return ret;
}

static struct fbtft_display display = {
    .regwidth = 8,
    .width = WIDTH,
    .height = HEIGHT,
    .fbtftops = {
        .reset = reset,
        .init_display = init_display,
        .set_addr_win = set_addr_win,
        .write_register = write_reg8_bus8,
        .write_vmem = write_vmem16_bus8,
   },
};
FBTFT_REGISTER_DRIVER(DRVNAME, &display);

/*
    This makes the driver autoload if the device is present
    modprobe will load it from /lib/modules and not the build directory
    If your driver changes doesn't come into effect, this could be why
*/
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);

MODULE_DESCRIPTION("FB driver for the Hello LCD Controller");
MODULE_AUTHOR("Pf@nne");
MODULE_LICENSE("GPL");
Pfannex commented 10 years ago

After adding the active window size:

static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
    fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
        "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);

    Set_Active_Window(par, xs, ys, xs+xe, ys+ye);
    Set_Memory_Write_Cursor(par, xs, ys);
    write_reg(par, 0x02);
}

http://youtu.be/wIZfS2lHWhQ

That means, it might well be that it basically works...

Pfannex commented 10 years ago

OK,

it looks like it works.....

http://youtu.be/bcb_DXRWlKw

Now I´m looking for some more speed..... My wiring dont like more than 2,5Mhz......

notro commented 10 years ago

That's great, it will be interesting to see how many frames per second it can do.

Pfannex commented 10 years ago

Hello again.... do you have any wiring tips for me..... More than 5 Mhz don't work.....

http://youtu.be/Bed21e0k5Zg