@beumerjm
Actually you can also just do tft.println or tft.print or tft.prinnt at the cursor postion![]()
Greetings all! I've been following this thread for awhile and finally got my display in to start playing with it for an upcoming controller project. I have ben able to get examples to compile for T40 (needed to change "setTextAt" to "setTextCursor" in both examples I tried) and have started to build some graphics for the controller. I was wondering if you had had any more success with screen and/or text rotation. I read up in the manual on initializing the screen and reorienting it in graphics mode, but even with changing the initialization in the library, I wasn't able to get it to rotate. I also discovered that as soon as you write text, it switches to text mode (makes sense), so the flip may not work for what I need anyways (a mixture of graphics and incoming data strings).
Also, I think I will be relying on the putString command, so I rewrote that in the library, as it was not using the inputted coordinates for the function.
Thanks everyone for their work on this, I look forward to helping in any way I can/sharing this project as it progresses.
Code://**************************************************************// /* Print a string using internal font or external font code */ //**************************************************************// void Ra8876_Lite::putString(ru16 x0,ru16 y0, const char *str) { textMode(true); setTextCursor(x0, y0);//added 16 May 2020 to place strings in the correct location while(*str != '\0') { write(*str); ++str; } }
Hi beumerjm,
I put in a weekend working on this. The RA8876 is going to be difficult to to implement full rotation with text. It seems to be geared more towards graphic rotation than text screen rotation. It will rotate text characters 90 degrees but that seems to be all. (Unless I am missing something). I am thinking that user defined characters defined in the three remaining angles my work but that's a lot of work. I have not had time to play with that any more.There are registers in RA8876 the change the origin and direction of the screen writes. Clear as mud HUHSame with me...
/* Demonstrate LvGL library on RA8876 screen with Teensy4 (but should work on other Arduino-based platforms) */
#include <lvgl.h> //in lv_arduino library: https://github.com/lvgl/lv_arduino
#include <RA8876_t3.h>
#include <MsTimer2.h> //One of the standard timer libraries included with Teensy - could use TimerOne or TimerThree if you prefer
#ifdef SPI_HAS_TRANSFER_ASYNC
#include "EventResponder.h" //Standard library in Teensy required for SPI direct memory access
#endif
#include <FT5206.h> //Works on any FocalTech capacative touch controller (all screen sizes under 8")
const int screenWidth = 1024;
const int screenHeight = 600;
#define BUFFER_SIZE (screenWidth * 50) //recommended is *10 but this sketch has nothing else useful to do with the memory available on Teensy 4, so go large!
#define RA8876_CS 10
#define RA8876_RESET 23
#define BACKLITE 14 //My copy of the display is set for external backlight control
RA8876_t3 tft = RA8876_t3(RA8876_CS, RA8876_RESET); //Using standard SPI pins
#define CTP_INT 0 // Use an interrupt capable pin such as pin 2 (any pin on a Teensy)
FT5206 cts = FT5206(CTP_INT);
/*A static or global variable to store the buffers*/
static lv_disp_buf_t disp_buf;
/*Static or global buffers. The second buffer is used while DMA is uploading the first buffer*/
static lv_color_t buf_1[BUFFER_SIZE];
static lv_color_t buf_2[BUFFER_SIZE];
lv_disp_drv_t disp_drv; /*A variable to hold the drivers. Can be local variable*/
lv_disp_t * disp;
#define LED_PIN 1 //optionally connect an LED to this pin to see it dimming
#define LVGL_TICK_PERIOD 1
lv_obj_t * slider_label;
#ifdef SPI_HAS_TRANSFER_ASYNC
EventResponder spiDMAEvent;
//**************************************************************//
// If using DMA, must close transaction and de-assert _CS
// after the data has been sent.
//**************************************************************//
void spiEventResponder(EventResponderRef event_responder) {
lv_disp_drv_t *disp = (lv_disp_drv_t*)event_responder.getContext();
tft.activeDMA = false;
tft.endSend(true);
lv_disp_flush_ready(disp);
}
#endif
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
uint16_t width = (area->x2 - area->x1 + 1);
uint16_t height = (area->y2 - area->y1 + 1);
/*
Serial.print("Flushing row ");
Serial.print(area->y1);
Serial.print(" millis ");
Serial.print(millis());
Serial.println();
*/
tft.bteMpuWriteWithROP(tft.currentPage, tft.width(), area->x1, area->y1, //Source 1 is ignored for ROP12
tft.currentPage, tft.width(), area->x1, area->y1, width, height, //destination address, pagewidth, x/y, width/height
RA8876_BTE_ROP_CODE_12); //code 12 means overwrite
tft.startSend();
SPI.transfer(RA8876_SPI_DATAWRITE);
#ifdef SPI_HAS_TRANSFER_ASYNC
tft.activeDMA = true;
spiDMAEvent.setContext(disp); // Set the contxt to us
SPI.transfer(color_p, NULL, width * height * 2, spiDMAEvent);
#else
SPI.transfer(color_p, NULL, width * height * 2);
tft.endSend(true);
lv_disp_flush_ready(disp);
#endif
}
bool my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data)
{
//The cts_touched() is set by an interrupt whenever something changes on the touch panel
//So many times that you call this, touched is false even when a finger is held against the panel,
//it just hasn't moved.
//But, when the last finger is removed, we get one final "touched" event telling us that the
//number of touches is zero. Very useful.
uint8_t registers[FT5206_REGISTERS];
uint16_t new_coordinates[5][2];
uint8_t current_touches = 0;
static int16_t touchX, touchY;
if (cts.touched())
{
cts.getTSregisters(registers);
current_touches = cts.getTScoordinates(new_coordinates, registers);
if (current_touches < 1) {
//No touches - this is a "release" - all fingers lifted off the panel
data->state = LV_INDEV_STATE_REL;
} else {
//Current touches can be more than 1 but we can't use multiple fingers.
//(LvGL doesn't have pinch-zoom or 3-finger swipe actions.)
//Just get the first touch out of the first entry in the cooordinates array.
data->state = LV_INDEV_STATE_PR;
//Touch panel is upside-down to the screen coordinates, so we must subtract
touchX = screenWidth - new_coordinates[0][0] - 1;
touchY = screenHeight - new_coordinates[0][1] - 1;
//Set the coordinates (if released use the last pressed coordinates)
data->point.x = touchX;
data->point.y = touchY;
/*
Serial.print("Touch x");
Serial.print(touchX);
Serial.print(" y");
Serial.println(touchY);
*/
}
}
return false; //Return `false` because we are not buffering and no more data to read
}
/* Interrupt driven periodic handler */
static void myTick(void)
{
lv_tick_inc(LVGL_TICK_PERIOD);
}
void slider_event_cb(lv_obj_t * slider, lv_event_t event)
{
printEvent("Slider", event);
if (event == LV_EVENT_VALUE_CHANGED) {
static char buf[4]; /* max 3 bytes for number plus 1 null terminating byte */
snprintf(buf, 4, "%u", lv_slider_get_value(slider));
lv_label_set_text(slider_label, buf); /*Refresh the text*/
int16_t value = lv_slider_get_value(slider);
value = map(value, 0, 100, 0, 255);
//analogWriteFrequency(2000); // Set PMW period to 2000 Hz instead of 1000
analogWrite(LED_PIN, value);//8-bit by default
}
}
void printEvent(String Event, lv_event_t event)
{
Serial.print(Event);
Serial.printf(" ");
switch (event) {
case LV_EVENT_PRESSED:
Serial.printf("Pressed\n");
break;
case LV_EVENT_SHORT_CLICKED:
Serial.printf("Short clicked\n");
break;
case LV_EVENT_CLICKED:
Serial.printf("Clicked\n");
break;
case LV_EVENT_LONG_PRESSED:
Serial.printf("Long press\n");
break;
case LV_EVENT_LONG_PRESSED_REPEAT:
Serial.printf("Long press repeat\n");
break;
case LV_EVENT_RELEASED:
Serial.printf("Released\n");
break;
}
}
void setup() {
Serial.begin(115200);
while (!Serial && millis() < 2000) {} //wait up to 2 seconds for Serial Monitor to connect
Serial.println("RA8876 LvGL test starting!");
Serial.print("Compiled ");
Serial.print(__DATE__);
Serial.print(" at ");
Serial.println(__TIME__);
//I'm guessing most copies of this display are using external PWM
//backlight control instead of the internal RA8876 PWM.
//Connect a Teensy pin to pin 14 on the display.
//Can use analogWrite() but I suggest you increase the PWM frequency first so it doesn't sing.
pinMode(BACKLITE, OUTPUT);
digitalWrite(BACKLITE, HIGH);
pinMode(LED_PIN, OUTPUT);
lv_init();
tft.begin(); // TFT init
cts.begin(); //capacative touchscreen init
lv_disp_buf_init(&disp_buf, buf_1, buf_2, BUFFER_SIZE);
//Initialize the display driver - basically just the buffers
lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = screenWidth;
disp_drv.ver_res = screenHeight;
disp_drv.flush_cb = my_disp_flush;
disp_drv.buffer = &disp_buf;
lv_disp_drv_register(&disp_drv);
//initialize the input device driver
lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv); /*Descriptor of a input device driver*/
indev_drv.type = LV_INDEV_TYPE_POINTER; /*Touch pad is a pointer-like device*/
indev_drv.read_cb = my_touchpad_read; /*Set your driver function*/
lv_indev_drv_register(&indev_drv); /*Finally register the driver*/
//start the 1ms callback, so that LvGL can run its animations
MsTimer2::set(LVGL_TICK_PERIOD, myTick);
MsTimer2::start();
//Configure EventResponder to run as immediately as possible in an interrupt when SPI DMA finishes
spiDMAEvent.attachImmediate(spiEventResponder);
//Set the theme.. NOTE THEMES MUST BE ENABLED INDIVIDUALLY IN lv_conf.h
//lv_theme_t * th = lv_theme_night_init(210, NULL); //Set a HUE value and a Font for the Night Theme
lv_theme_t * th = lv_theme_alien_init(10, NULL); //Set a HUE value and a Font for the Alien Theme
lv_theme_set_current(th);
lv_obj_t * scr = lv_cont_create(NULL, NULL);
lv_disp_load_scr(scr);
/* Create simple label */
lv_obj_t *label = lv_label_create(lv_scr_act(), NULL);
lv_label_set_text(label, "LED DIMMER");
lv_obj_align(label, NULL, LV_ALIGN_CENTER, 0, -50);
/* Create a slider in the center of the display */
lv_obj_t * slider = lv_slider_create(lv_scr_act(), NULL);
lv_obj_set_width(slider, screenWidth - 50); /*Set the width*/
lv_obj_set_height(slider, 50);
lv_obj_align(slider, NULL, LV_ALIGN_CENTER, 0, 0); /*Align to the center of the parent (screen)*/
lv_obj_set_event_cb(slider, slider_event_cb); /*Assign an event function*/
/* Create a label below the slider */
slider_label = lv_label_create(lv_scr_act(), NULL);
lv_label_set_text(slider_label, "0");
lv_obj_set_auto_realign(slider, true);
lv_obj_align(slider_label, slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
}
void loop() {
lv_task_handler(); /* let the GUI do its work */
delay(3);
}
If it has not already been done, maybe someone should port over the ILI9341 and GFX font handling code like we (@mjs513 mostly) did for RA7785...
There is hope: 2020-05-20 17:27ISC SAN FRANCISCO (USPS), Processed Through Facility -> Your item has been processed through our facility in ISC SAN FRANCISCO (USPS) at 5:27 pm on May 20...
Should get here next week
//**************************************************************//
/* JB ADD FOR ROTATING TEXT */
/* Turn RA8876 text rotate mode ON/OFF (True = ON) */
//**************************************************************//
void Ra8876_Lite::textRotate(boolean on)
{
if(on)
{
lcdRegDataWrite(RA8876_CCR1, RA8876_TEXT_ROTATION<<4);//cdh
}
else
{
lcdRegDataWrite(RA8876_CCR1, RA8876_TEXT_NO_ROTATION<<4);//cdh
}
}
//**************************************************************//
/* JB ADD FOR ROTATING Image */
/* Turn RA8876 image rotate mode ON/OFF (True = ON) */
//**************************************************************//
void Ra8876_Lite::imageRotate(boolean on)
{
if(on)
lcdRegDataWrite(RA8876_MACR, RA8876_WRITE_MEMORY_TBLR<<1);//02h
else
lcdRegDataWrite(RA8876_MACR, RA8876_WRITE_MEMORY_LRTB<<1);//02h
}
//**************************************************************//
/* JB ADD FOR printing rotated strings */
//**************************************************************//
void Ra8876_Lite::rotatePrint(ru16 x0,ru16 y0, const char *str)
{
textMode(true);
textRotate(true);
while(*str != '\0')
{
setTextCursor(x0, y0);//added 16 May 2020 to place strings in the correct location
write(*str);
++str;
y0 = y0 + _FNTwidth;
}
textRotate(false);
}
//**************************************************************//
/* JB ADD for having the screen write from the bottom up */
/* Puts y = 0 at the bottom, allowing for text flip... */
//**************************************************************//
void Ra8876_Lite::bottomUp(boolean on)
{
if(on)
lcdRegDataWrite(RA8876_DPCR,
XPCLK_INV<<7|RA8876_DISPLAY_ON<<6|RA8876_VDIR_BT<<3|RA8876_OUTPUT_RGB<<0);
else
lcdRegDataWrite(RA8876_DPCR,
XPCLK_INV<<7|RA8876_DISPLAY_ON<<6|RA8876_VDIR_TB<<3|RA8876_OUTPUT_RGB<<0);
}
Just to update all, I was able to get rotated text to appear on the screen. Sadly, doing so requires flipping the Vertical Scan Direction (Bottom to Top). Sadly, this disables PIP. It also makes me you think harder about writing graphics as the variables defined as TBLR, LRTB, etc. are now backwards from their acronym. I also had to build a custom function in the library that prints one character at a time and then resets the cursor in the x direction and moves it in the y direction - so the x and y axis are still oriented to the screen the same way, though y now moves from bottom to top as opposed to top to bottom. There may be a better way to build these functions - a programmer I am not - but I have copied below what I added to the library should this help others in the future. Also, in figuring this out, I discovered (again I very well could be wrong) that the initialization in lines 226 and 227 of Ra8876_Lite.cpp (called out as TFT timing configure) do not appear to be recognized (or they are being overwritten).
What I added for text rotation...
Code://**************************************************************// /* JB ADD FOR ROTATING TEXT */ /* Turn RA8876 text rotate mode ON/OFF (True = ON) */ //**************************************************************// void Ra8876_Lite::textRotate(boolean on) { if(on) { lcdRegDataWrite(RA8876_CCR1, RA8876_TEXT_ROTATION<<4);//cdh } else { lcdRegDataWrite(RA8876_CCR1, RA8876_TEXT_NO_ROTATION<<4);//cdh } } //**************************************************************// /* JB ADD FOR ROTATING Image */ /* Turn RA8876 image rotate mode ON/OFF (True = ON) */ //**************************************************************// void Ra8876_Lite::imageRotate(boolean on) { if(on) lcdRegDataWrite(RA8876_MACR, RA8876_WRITE_MEMORY_TBLR<<1);//02h else lcdRegDataWrite(RA8876_MACR, RA8876_WRITE_MEMORY_LRTB<<1);//02h } //**************************************************************// /* JB ADD FOR printing rotated strings */ //**************************************************************// void Ra8876_Lite::rotatePrint(ru16 x0,ru16 y0, const char *str) { textMode(true); textRotate(true); while(*str != '\0') { setTextCursor(x0, y0);//added 16 May 2020 to place strings in the correct location write(*str); ++str; y0 = y0 + _FNTwidth; } textRotate(false); } //**************************************************************// /* JB ADD for having the screen write from the bottom up */ /* Puts y = 0 at the bottom, allowing for text flip... */ //**************************************************************// void Ra8876_Lite::bottomUp(boolean on) { if(on) lcdRegDataWrite(RA8876_DPCR, XPCLK_INV<<7|RA8876_DISPLAY_ON<<6|RA8876_VDIR_BT<<3|RA8876_OUTPUT_RGB<<0); else lcdRegDataWrite(RA8876_DPCR, XPCLK_INV<<7|RA8876_DISPLAY_ON<<6|RA8876_VDIR_TB<<3|RA8876_OUTPUT_RGB<<0); }
It arrived yesterdayYou and @mjs513 have it going on. Can't wait to see what happens when @KurtE gets his display as well![]()
LCD Memory Transfer test starting!
Compiled May 28 2020 at 14:33:58
SPI transfer speed 5.0MHz
fillRect operation takes 0.675 milliseconds to fill the image area.
Put-picture from PROGMEM to display took 0.228ms to begin the operation.
But the next LCD operation was delayed by 101.808ms because data transfer was still underway
Copy from PROGMEM to display took 0.227ms to begin the transfer (data is on its way while you read this.)
16-bit copy from PROGMEM to display took 104.011ms
Chromakey copy from PROGMEM to display took 101.252ms to run to completion.
ROP 15 BTE copy took 0.280ms, followed by 0.783ms internal processing in the RAiO chip.
ROP 0 BTE copy took 0.280ms, followed by 0.784ms internal processing in the RAiO chip.
ROP 1 BTE copy took 0.280ms, followed by 1.779ms internal processing in the RAiO chip.
ROP 2 BTE copy took 0.279ms, followed by 2.139ms internal processing in the RAiO chip.
ROP 3 BTE copy took 0.279ms, followed by 1.288ms internal processing in the RAiO chip.
ROP 4 BTE copy took 0.281ms, followed by 2.152ms internal processing in the RAiO chip.
ROP 5 BTE copy took 0.279ms, followed by 1.118ms internal processing in the RAiO chip.
ROP 6 BTE copy took 0.279ms, followed by 2.172ms internal processing in the RAiO chip.
ROP 7 BTE copy took 0.280ms, followed by 2.137ms internal processing in the RAiO chip.
ROP 8 BTE copy took 0.279ms, followed by 1.497ms internal processing in the RAiO chip.
ROP 9 BTE copy took 0.280ms, followed by 2.160ms internal processing in the RAiO chip.
ROP 10 BTE copy took 0.279ms, followed by 1.124ms internal processing in the RAiO chip.
ROP 11 BTE copy took 0.280ms, followed by 2.147ms internal processing in the RAiO chip.
ROP 12 BTE copy took 0.280ms, followed by 1.341ms internal processing in the RAiO chip.
ROP 13 BTE copy took 0.279ms, followed by 2.138ms internal processing in the RAiO chip.
ROP 14 BTE copy took 0.281ms, followed by 1.991ms internal processing in the RAiO chip.
First Page Finished, PRESS ANY KEY...
Test Alpha...
pinMode(BL Pin, OUTPUT);
digitalWrite(BL Pin, HIGH);
@KurtE
The examples we have been working with has just been connecting the backlight pin to the T4.1 and doing:
Code:pinMode(BL Pin, OUTPUT); digitalWrite(BL Pin, HIGH);
Don't think we have played with using PWM?
Guess I am going to have to pull out the 8876 and start playing with it again![]()
//I'm guessing most copies of this display are using external PWM
//backlight control instead of the internal RA8876 PWM.
//Connect a Teensy pin to pin 14 on the display.
//Can use analogWrite() but I suggest you increase the PWM frequency first so it doesn't sing.
pinMode(BACKLITE, OUTPUT);
analogWriteFrequency(BACKLITE, 1000000);
//digitalWrite(BACKLITE, HIGH);
analogWrite(BACKLITE, 256);
@KurtE and @wwatson
Just set up the RA8876 again and found the same thing as @wwatson. When I changed to just digitalWrite(BL, HIGH) it worked no problem. With my setup seems I have to have the display on external power otherwise the T4.1 hangs
wwatson, do you have any experience with this device in outdoor lighting conditions?
This type of display would fit my bill for an integrated instrumentation system in an automotive application, but the 100 cd/m2 the datasheet calls out has me concerned about readability.
Note: I got it to work with a jumper to the backlight pin. Although while I got the display to show the proper data, I am having issue with it not talking USB back to PC. Will debug more maybe tomorrow.
KurtE are you using external power or all over USB?