Closed Pfannex closed 9 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.
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
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.
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
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
Did you follow this guide https://github.com/notro/fbtft/wiki/Development ?
Yes....
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...
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
I have updated the guide and tested the instructions in the process.
Thanks.....
cp ~/.rpi-firmware/extra/{.config,Module.symvers} ~/linux/
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?
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
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
[ 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)
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);
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);
.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
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?
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)
....
....
....
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,
},
};
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'
#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!!!
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.
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)
{
see below...
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:
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
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,
},
};
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... :-)
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
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......
write_reg(par,0xAA);
write_reg(par,0xBB);
Now a leading 0x80 (Command) will be sent every time,
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??
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);
}
write_reg(par,0xAA);
write_reg(par,0xBB,0xCC);
write_reg(par,0xDD,0xEE,0xFF);
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.....
Init works fine..... stay tuned......
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....
Why do you need it?
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..
See http://stackoverflow.com/questions/12961299/generate-random-number-in-kernel-module
I live in Norway.
This is how I do performance testing: https://github.com/notro/fbtft/wiki/Performance
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
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
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!) fbtft /CS timing (much faster!)
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
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.
OK, draw lines will be OK....
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
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?
//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
/*
* 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");
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);
}
That means, it might well be that it basically works...
OK,
Now I´m looking for some more speed..... My wiring dont like more than 2,5Mhz......
That's great, it will be interesting to see how many frames per second it can do.
Hello again.... do you have any wiring tips for me..... More than 5 Mhz don't work.....
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