Playing around with yet another version of ILI9341_t3 library (ILI9341_t3n)

Looks like it is doing exactly what it is supposed to do with Rubik_Regular.... You might want to ask Frank about this one...

For example I tried to output 12 pt version... So if you look at the font definition you will see:
Code:
static const unsigned char RubikRegular12_data[] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
/* font data size: 190 bytes */

static const unsigned char RubikRegular12_index[] = {
0x00,0x02,0x04,0x06,0x08,0x0A,0x0C,0x0E,0x10,0x12,
0x14,0x16,0x18,0x1A,0x1C,0x1E,0x20,0x22,0x24,0x26,
0x28,0x2A,0x2C,0x2E,0x30,0x32,0x34,0x36,0x38,0x3A,
0x3C,0x3E,0x40,0x42,0x44,0x46,0x48,0x4A,0x4C,0x4E,
0x50,0x52,0x54,0x56,0x58,0x5A,0x5C,0x5E,0x60,0x62,
0x64,0x66,0x68,0x6A,0x6C,0x6E,0x70,0x72,0x74,0x76,
0x78,0x7A,0x7C,0x7E,0x80,0x82,0x84,0x86,0x88,0x8A,
0x8C,0x8E,0x90,0x92,0x94,0x96,0x98,0x9A,0x9C,0x9E,
0xA0,0xA2,0xA4,0xA6,0xA8,0xAA,0xAC,0xAE,0xB0,0xB2,
0xB4,0xB6,0xB8,0xBA,0xBC,
};
/* font index size: 95 bytes */

const ILI9341_t3_font_t RubikRegular12 = {
	RubikRegular12_index,
	0,
	RubikRegular12_data,
	1,
	0,
	32,
	126,
	0,
	0,
	8,    // Bits per index. 
	1,    // bits width
	1,    // bits height
	2,    // x offset
	2,    // y offset
	1,    // delta 
	0,    // line spacing
	0     // cap height
};
The first obvious thing you will notice is that the font data is all 0's so no pixels set... (Also pretty small)... It shows it defines characters 32-126... And the definition data
looks like garbage as I commented in that area...
 
:eek: - I started off by adding it to the ILI_Ada_FontTest4 and when nothing showed up, I then was about to add some more debug printing, but decided to first see if the font data looked reasonable...
Probably should have done that first... The bright side is I can not go back and discard the changes...
 
My apologies, I blindly went through it and didn't even realise it was all set to 0x00..
I decided to use another font for now from Frank's library and its working thanks to mjs513's guidelines.
 
Last edited:
Ok out of curiosity I wanted to see what it would take to convert a font from google - pain in the neck - but there is the rubik regular font. REASON: I am on windows and everything is set up for Linux :)
 

Attachments

  • Rubik.zip
    93.9 KB · Views: 109
Not a bug. Had to do a couple things to get it to work on windows:
1. Install "Strawberry Perl"
2. Edit the .pl file to change grep to findstr (grep only on Linux)
3. Compile bdf2ttf converter which I had no issue with the compilation as long as you remember to install gcc.
4. Had to download a missing dll that it wanted: freetype6.dll (google searched it and I just placed in the extras folder)

Once I did that it all worked. PIH since I never did it before - with the changes to get it working on Windows its easy to convert the font
 
Is there an online tool I can use to create more fonts? I have a ttf file for the new font I want to create
 
Thanks mjs, I'll try that on Bootcamp later on.

@KurtE, I'm seeing some odd behavior with the serClipRect.

I set the display background to white, then used your example code for the gauge and needle. I have another gauge image next to it with a slightly different form of indication.
When I clip and clear just the boost gauge (based on your code) - the entire screen is white besides the area surrounding the two images (as they have black backgrounds)
But when I set another clip area for the 2nd gauge, only a small portion of the display is white as can be seen in the image attached.
The 2nd clip is supposed to take care of the 2nd gauge but doesn't seem to be working as expected (not clearing the 2nd gauge)
clipRect.jpg

Here is the code responsible for drawing the "led" on the 2nd gauge
Code:
void ringMeter(float value)
{
    int x = 160, y= 45,r = 75;
  
  x += r; y += r;   // Calculate coords of centre of ring
  int w = 5;    // Width of outer ring
  int angle = 150;  // Half the sweep angle of meter (300 degrees)
  int v = map(value, 10, 18, -angle, angle); // Map the value to an angle v
  byte seg = 5; // Segments are 5 degrees wide = 60 segments for 300 degrees
  int led_color;

    // Calculate pair of coordinates for segment end
    float sx2 = cos((v + seg - 90) * 0.0174532925);
    float sy2 = sin((v + seg - 90) * 0.0174532925);
    int x2 = sx2 * (r - w) + x;
    int y2 = sy2 * (r - w) + y;


  if(value < 13.50 ){
    led_color = ILI9341_GREEN;
    }

  if( value > 13.50 && value < 15.75){
    led_color = ILI9341_YELLOW;
    }

  if(value > 15.75){
    led_color = ILI9341_RED;
    }
    tft.fillCircle (x2, y2, 4,led_color);
    
        g_circle_rect_min_x = x2 - 4;
        g_circle_rect_min_y = y2 - 4 ;
        g_circle_rect_max_x = 8;
        g_circle_rect_max_y = 8;
  
}

And here is the clip function in the main loop
Code:
    tft.writeRect(160, 45, 150, 150, (const uint16_t*)afr_150); // display the gauge image
    ringMeter(afr); //display the led
    tft.setClipRect(g_circle_rect_min_x, g_circle_rect_min_y, g_circle_rect_max_x+1, g_circle_rect_max_y+1);
    tft.updateScreen();
    tft.setClipRect();

Could this be a bug?

I'm setting the clip in between both gauges two more times for the number readout.
 
Hard to say... One would need to walk through all of the code to see who is responsible for drawing each area.

The updateScreen with a clip rectangle is pretty simply simple stuff, it simply only draws that region of the screen. But for example any code you run that is without a clip rectangle set will write over all regions of the frame buffer that the graphic primitives are aimed at. And if all of your outputs are done using clip rectangles, than those portions of the display that are not contained in any of these rectangles are whatever they are...

So if it were me, I would probably instrument the code and maybe wait for keyboard input over each of the main display output sections and see if a) someone is overwriting areas that they should not be or b) no one is writing that area of the display...
 
I'll add some 3-5 second delays between each set of draw-clip functions and see what happens.

Just to make sure I understand this right.. looking at the image below - anything I load into the buffer will remain there unless I use the clip function to set and clear it (which would be the white boxes).
So the only place I would see a change is within these white boxes. Anything outside the boxes is pulled from the buffer and is unchanged.
Screen Shot 2020-02-25 at 17.42.12.png
 
@KurtE

I'm facing an issue with the clip rectangle function on my second gauge.
For some reason its not clearing the previous drawing position. The only way I can clear it is if I clear the entire gauge face, which introduces more delay to the loop
IMG_2578.png

Here is the code (used on the HX display, but exhibits the same behaviour on the ILI9341)
Code:
uint16_t g2_center_x;
uint16_t g2_center_y;
uint16_t g2_offset_x;
uint16_t g2_offset_y;
int16_t g_circle_rect_min_x;
int16_t g_circle_rect_min_y;
int16_t g_circle_rect_max_x;
int16_t g_circle_rect_max_y;

void setup(){
    tft.begin();
    tft.setRotation(3);
    g2_center_x = 360 ; //center of afr gauge
    g2_center_y = 120; //center of afr gauge
    g2_offset_x = 240; // top left corner of afr gauge
    g2_offset_y = 0; // top left corner of afr gauge
    
    tft.fillScreen(COLOR_BLACK);
    tft.writeRect(g2_offset_x, g2_offset_y, 240, 240, (const uint16_t*)afr_240);
    tft.useFrameBuffer(true);

}


void loops(){
    tft.writeRect(g2_offset_x, g2_offset_y, 240, 240, (const uint16_t*)afr_240); // afr gauge image
    ringMeter_n(airFuelRatio); // Value between 10 and 18
    min_x1 = g_circle_rect_min_x;
    min_y1 = g_circle_rect_min_y;
    tft.setClipRect(g_circle_rect_min_x - 2, g_circle_rect_min_y -2, g_circle_rect_max_x + 4, g_circle_rect_max_x + 4);
    tft.updateScreen();
    tft.setClipRect();


}

void ringMeter_n(float value){
  int r = 110;
  value = constrain(value, 10, 18);
  int v = map(value, 10, 18, 15, 85); // Map the value to an angle v
  int led_color;
    
      
  double rads = HALF_PI + TWO_PI * v *0.01;
  uint16_t x0 = cos(rads) * r + g2_center_x;
  uint16_t y0 = sin(rads) * r + g2_center_y;
  if(value < 13.50 ){
      led_color = HX8357_GREEN;
      }
  
  if( value > 13.50 && value < 15.75){
      led_color = HX8357_YELLOW;
      }
  
  if(value > 15.75){
      led_color = HX8357_RED;
      }
  
  tft.fillCircle (x0, y0, 6, led_color);
      
          g_circle_rect_min_x = x0 - 8 ;
          g_circle_rect_min_y = y0 - 8 ;
          g_circle_rect_max_x = 16;
          g_circle_rect_max_y = 16;
}

EDIT:

Forgot I had posted something similar before.
I was thinking, what if I draw a black circle on the previous location, then draw the coloured circle on the new location - that should clear the gauge properly?
 
Last edited:
Again in cases like this, if you have a small complete sketch, that runs, it would help for me to see it and maybe get an idea of what might be going on.
Otherwise it requires me to either try guess what is going on and/or fill in all of the gaps to make a compilable sketch.
 
Here is a version that updates a little better. I did it quick and dirty. Note: I did not upload the picture here...

Code:
#include "HX8357_t3n.h"
#include "picture.c"
#define TFT_DC  9
#define TFT_CS 10
#define TFT_RST 8
#define DEBUG_PIN 7
//
// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC
HX8357_t3n tft = HX8357_t3n(TFT_CS, TFT_DC, TFT_RST);


uint16_t g2_center_x;
uint16_t g2_center_y;
uint16_t g2_offset_x;
uint16_t g2_offset_y;
int16_t g_circle_rect_min_x;
int16_t g_circle_rect_min_y;
int16_t g_circle_rect_max_x;
int16_t g_circle_rect_max_y;

void setup() {
  tft.begin();
  tft.setRotation(3);
  g2_center_x = 360 ; //center of afr gauge
  g2_center_y = 120; //center of afr gauge
  g2_offset_x = 240; // top left corner of afr gauge
  g2_offset_y = 0; // top left corner of afr gauge

  tft.fillScreen(HX8357_BLACK);
  tft.writeRect(g2_offset_x, g2_offset_y, 240, 240, (const uint16_t*)afr_240);
  tft.useFrameBuffer(true);

}

float airFuelRatio = 10.0;
float airFuleRatio_delta = .25;

void loop() {
  tft.writeRect(g2_offset_x, g2_offset_y, 240, 240, (const uint16_t*)afr_240); // afr gauge image
  ringMeter_n(airFuelRatio); // Value between 10 and 18
  tft.setClipRect(g_circle_rect_min_x - 2, g_circle_rect_min_y - 2, g_circle_rect_max_x + 4, g_circle_rect_max_x + 4);
  tft.updateScreen();
  tft.setClipRect();
  
  airFuelRatio += airFuleRatio_delta;
  if (airFuelRatio > 18.0) {
    airFuelRatio = 18.0;
    airFuleRatio_delta = -airFuleRatio_delta;
  }
  if (airFuelRatio < 10.0) {
    airFuelRatio = 10.0;
    airFuleRatio_delta = -airFuleRatio_delta;
  }
  delay(250);
}

uint16_t x0_prev = 0;
uint16_t y0_prev = 0;

void ringMeter_n(float value) {
  int r = 110;
  value = constrain(value, 10, 18);
  int v = map(value, 10, 18, 15, 85); // Map the value to an angle v
  int led_color;


  double rads = HALF_PI + TWO_PI * v * 0.01;
  uint16_t x0 = cos(rads) * r + g2_center_x;
  uint16_t y0 = sin(rads) * r + g2_center_y;
  if (value < 13.50 ) {
    led_color = HX8357_GREEN;
  }

  if ( value > 13.50 && value < 15.75) {
    led_color = HX8357_YELLOW;
  }

  if (value > 15.75) {
    led_color = HX8357_RED;
  }

  tft.fillCircle (x0, y0, 6, led_color);

  g_circle_rect_min_x = min(x0, x0_prev) - 8;
  g_circle_rect_min_y = min(y0, y0_prev) - 8;
  g_circle_rect_max_x = max(x0, x0_prev) + 8;
  g_circle_rect_max_y = max(y0, y0_prev) + 8;
  x0_prev = x0;
  y0_prev = y0;
  
}
 
Again in cases like this, if you have a small complete sketch, that runs, it would help for me to see it and maybe get an idea of what might be going on.
Otherwise it requires me to either try guess what is going on and/or fill in all of the gaps to make a compilable sketch.
I’ll put together a sketch tomorrow for the ILI9341 and upload it, Similar to the initial gauge sketch you wrote for me.
Only reason I didn’t upload the full sketch is its about 500 lines long and has a bunch of peripherals that aren’t related to this specific function
 
I’ll put together a sketch tomorrow for the ILI9341 and upload it, Similar to the initial gauge sketch you wrote for me.
Only reason I didn’t upload the full sketch is its about 500 lines long and has a bunch of peripherals that aren’t related to this specific function
You might take a look to see if the changes I made in the post two up, fixes the issue for you?

What I do is simply to remember the last X, Y of where you draw the circle and generate a simple bounding box of the min and max of the current and previous position +-8 Note the 8, could be 7 and maybe 6... But I saw you started with 8 so...
 
You might take a look to see if the changes I made in the post two up, fixes the issue for you?

What I do is simply to remember the last X, Y of where you draw the circle and generate a simple bounding box of the min and max of the current and previous position +-8 Note the 8, could be 7 and maybe 6... But I saw you started with 8 so...

Tested that out, it works well for clearing the "led" but it sometimes causes the strings in the middle of the gauge to flicker. I assume that your code basically draws a rectangle between the last position and the current position and clears anything inside of it, correct? And in that case, the clip covers the center of the gauge as well at times.
 
Another option is to do two update screens, one that covers the old position and one that covers the new position. like:
Code:
#include "HX8357_t3n.h"
#include "picture.c"
#define TFT_DC  9
#define TFT_CS 10
#define TFT_RST 8
#define DEBUG_PIN 7
//
// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC
HX8357_t3n tft = HX8357_t3n(TFT_CS, TFT_DC, TFT_RST);


uint16_t g2_center_x;
uint16_t g2_center_y;
uint16_t g2_offset_x;
uint16_t g2_offset_y;

void setup() {
  tft.begin();
  tft.setRotation(3);
  g2_center_x = 360 ; //center of afr gauge
  g2_center_y = 120; //center of afr gauge
  g2_offset_x = 240; // top left corner of afr gauge
  g2_offset_y = 0; // top left corner of afr gauge

  tft.fillScreen(HX8357_BLACK);
  tft.writeRect(g2_offset_x, g2_offset_y, 240, 240, (const uint16_t*)afr_240);
  tft.useFrameBuffer(true);

}

float airFuelRatio = 10.0;
float airFuleRatio_delta = .25;

uint16_t x_gauge_marker_prev = 0;
uint16_t y_gauge_marker_prev = 0;
uint16_t x_gauge_marker = 0x7fff;
uint16_t y_gauge_marker = 0x7fff;

void loop() {
  tft.writeRect(g2_offset_x, g2_offset_y, 240, 240, (const uint16_t*)afr_240); // afr gauge image
  ringMeter_n(airFuelRatio); // Value between 10 and 18
  tft.setClipRect(x_gauge_marker_prev - 7, y_gauge_marker_prev - 7, 14, 14);
  tft.updateScreen();
  // could maybe reduce but
  tft.setClipRect(x_gauge_marker - 7, y_gauge_marker - 7, 14, 14);
  tft.updateScreen();
  tft.setClipRect();

  airFuelRatio += airFuleRatio_delta;
  if (airFuelRatio > 18.0) {
    airFuelRatio = 18.0;
    airFuleRatio_delta = -airFuleRatio_delta;
  }
  if (airFuelRatio < 10.0) {
    airFuelRatio = 10.0;
    airFuleRatio_delta = -airFuleRatio_delta;
  }
  delay(250);
}


void ringMeter_n(float value) {
  int r = 110;
  value = constrain(value, 10, 18);
  int v = map(value, 10, 18, 15, 85); // Map the value to an angle v
  int led_color;

  // save the last one
  x_gauge_marker_prev = x_gauge_marker;
  y_gauge_marker_prev = y_gauge_marker;

  double rads = HALF_PI + TWO_PI * v * 0.01;
  x_gauge_marker = cos(rads) * r + g2_center_x;
  y_gauge_marker = sin(rads) * r + g2_center_y;
  if (value < 13.50 ) {
    led_color = HX8357_GREEN;
  }

  if ( value > 13.50 && value < 15.75) {
    led_color = HX8357_YELLOW;
  }

  if (value > 15.75) {
    led_color = HX8357_RED;
  }

  tft.fillCircle (x_gauge_marker, y_gauge_marker, 6, led_color);
  // fix first pass through
  if (x_gauge_marker_prev == 0x7fff) x_gauge_marker_prev = x_gauge_marker;
  if (y_gauge_marker_prev == 0x7fff) y_gauge_marker_prev = y_gauge_marker;

}
 
While experimenting with some other display libraries on T4.x boards I have been wondering about seeing if I could have a workable version of the library that does the DMA updates without the need to copy the data from the screen buffer to a secondary buffer that I then do the dma to the screen. Earlier this was done to avoid the issues associated with using a frame buffer that is in DMAMEM or likewise malloc memory, where there is a hardware cache for that memory range. I won't go into the details as I probably have mentioned it once or twice (or 100 times) before :D

Instead I updated to have DMASettings chain of 3, and just do a straight dma update.... Nice thing is you don't have the wasted memory, In non-continuous updates mode, it works fine as we simply flush the cache when we start.

But in continuous mode, you run the risk of updating some stuff while it is updating and some of it automatically flushes through and other parts not.

So in continuous mode, I flush the cache again at each frame completes.

Also I hacked in the code that allows a user to specify a callback function to be called so they can update things if they want. Also gave the option to have this method called when the frame is half completed, so you can play game of updating one half at a time, while the other half is being output...

Put up a new branch: https://github.com/KurtE/ILI9341_t3n/tree/T4x_DMA_try_full_dma_chain

May still experiment and change things, like the call back has no arguments. Right now have a call that the callback can call that says what part sub-frame am I being called for...
Also not sure if it would make much sense to allow more call backs per frame: like 3 could work easily (one per dmaSetting. Likewise 6... But not sure how useful?

Currently for T4 this new code is the default in the branch but can be disabled. If anyone wants to give it a try, that would be great.
 
Kurt, as I am currently have a ILI941 set up, i'll test it. Don't know if I have time tomorrow :-( Now is too late.
It would be great if it works :)
 
@KurtE '...\libraries\ILI9341_t3n\examples\ili9341_t3n_UpdateAsyncCont_Test\ili9341_t3n_UpdateAsyncCont_Test.ino' WORKS! ::
>> downloaded 'full_dma_chain' - 9341 plugged into T_4.1 with PJRC Purple test board.

Nice clean and quick screen draws and transitions!
 
@KurtE '...\libraries\ILI9341_t3n\examples\ili9341_t3n_UpdateAsyncCont_Test\ili9341_t3n_UpdateAsyncCont_Test.ino' WORKS! ::
>> downloaded 'full_dma_chain' - 9341 plugged into T_4.1 with PJRC Purple test board.

Nice clean and quick screen draws and transitions!

@KurtE == just did the same as @defragster and works like a charm :)
 
Thanks guys. I probably should then integrate the new members with the T3.5/6 code.

I am still wondering about my sub-frame method. Right now the callback gets no information, Which if you only have one call back per frame is great. Else you can call to get the logical sub-frame, (0,1).
Wondering if I should have simple value that get sent as argument that gets passed to call back...

Also as mentioned, I could potentially allow for more call backs, but may be overkill. Currently have:
Code:
	void	setFrameCompleteCB(void (*pcb)(), bool fCallAlsoHalfDone=false);

Could potentially instead have something like:
Code:
	void	setFrameCompleteCB(void (*pcb)(uint8_t which_one), uint8_t cb_when);
Shows something being passed to call back, plus the when could be up to 6 per frame... Where you can a bit on the chain item completing(interruptAtCompletion)and another bit for a dma chain item being half done
(interruptAtHalf),

So the current one would be defined as: 0x1 for each frame completing. and maybe the two would be: (0x00001001) (bottom 6 bits)...
But my guess is that this might be overkill?
 
Back
Top