Go Down

Topic: Fast 7-segment number display for TFT  (Read 5587 times) previous topic - next topic

wzaggle

Hey,  New to the Arduino forum but thought I would share a short routine to draw 7-segment formatted numbers.  The draw string function was really slow and I needed to update several numbers in real-time on the same display without always clearing the background, etc. Plus really large numbers did not look very nice.  This worked well on a UNO with a Version 1 Seeduino TFT.  Maybe there is something like this already available, but I could not find it. It just uses the DrawHorizontal and DrawVertical line functions of the TFT library and offers 10 sizes in any foreground or background color with sign and zero blanking up to 10 digits or 9 with - sign. It is pretty compressed so not that readable, but I'll post below in case someone would like to copy/paste and play around with it. Only thing really not protected is drawing off the screen.

Code: [Select]

/**********************************************************************************
 Routine to Draw Large 7-Segment formated number with Arduino TFT Library
    by William Zaggle  (Uses TFT Library DrawLine functions).

   int n - The number to be displayed
   int xLoc = The x location of the upper left corner of the number
   int yLoc = The y location of the upper left corner of the number
   int cSe = The size of the number. Range 1 to 10 uses Large Shaped Segments.
   fC is the foreground color of the number
   bC is the background color of the number (prevents having to clear previous space)
   nD is the number of digit spaces to occupy (must include space for minus sign for numbers < 0)
   nD < 0 Suppresses leading zero
 
   Sample Use: Fill the screen with a 2-digit number suppressing leading zero

          draw7Number(38,20,40,10,WHITE, BLACK, -2);

**********************************************************************************/
void draw7Number(int n, unsigned int xLoc, unsigned int yLoc, char cS, unsigned int fC, unsigned int bC, char nD) {
  unsigned int num=abs(n),i,s,t,w,col,h,a,b,si=0,j=1,d=0,S1=cS,S2=5*cS,S3=2*cS,S4=7*cS,x1=(S3/2)+1,x2=(2*S1)+S2+1,y1=yLoc+x1,y3=yLoc+(2*S1)+S4+1;
  unsigned int seg[7][3]={{(S3/2)+1,yLoc,1},{x2,y1,0},{x2,y3+x1,0},{x1,(2*y3)-yLoc,1},{0,y3+x1,0},{0,y1,0},{x1,y3,1}};
  unsigned char nums[12]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x67,0x00,0x40},c=(c=abs(cS))>10?10:(c<1)?1:c,cnt=(cnt=abs(nD))>10?10:(cnt<1)?1:cnt;
  for (xLoc+=cnt*(d=(2*S1)+S2+(2*S3)+2);cnt>0;cnt--){
    for (i=(num>9)?num%10:((!cnt)&&(n<0))?11:((nD<0)&&(!num))?10:num,xLoc-=d,num/=10,j=0;j<7;++j){
      col=(nums[i]&(1<<j))?fC:bC;s=(2*S1)/S3;
      if (seg[j][2])for(w=S2,t=seg[j][1]+S3,h=seg[j][1]+(S3/2),a=xLoc+seg[j][0]+S1,b=seg[j][1];b<h;b++,a-=s,w+=(2*s))Tft.drawHorizontalLine(a,b,w,col);
      else for(w=S4,t=xLoc+seg[j][0]+S3,h=xLoc+seg[j][0]+S3/2,b=xLoc+seg[j][0],a=seg[j][1]+S1;b<h;b++,a-=s,w+=(2*s))Tft.drawVerticalLine(b,a,w,col);
      for (;b<t;b++,a+=s,w-=(2*s))seg[j][2]?Tft.drawHorizontalLine(a,b,w,col):Tft.drawVerticalLine(b,a,w,col);
    }
  }
}


common_ground

#1
Mar 13, 2015, 11:52 pm Last Edit: Mar 13, 2015, 11:57 pm by common_ground
Thanks   :D

Looks great, very useful addition for TFT screens.


only minor adaptation is necessary for full compatibility with ILI9341 TFT:

Code: [Select]

Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

void setup() {
  tft.begin();
  tft.fillScreen(ILI9341_BLACK); // nema default
}

void loop() {
  draw7Number(millis() / 1000 , 20 , 40 , 10 , ILI9341_WHITE , ILI9341_BLACK, -2);
}


/**********************************************************************************
 Routine to Draw Large 7-Segment formated number with Arduino TFT Library
    by William Zaggle  (Uses TFT Library DrawLine functions).

   int n - The number to be displayed
   int xLoc = The x location of the upper left corner of the number
   int yLoc = The y location of the upper left corner of the number
   int cSe = The size of the number. Range 1 to 10 uses Large Shaped Segments.
   fC is the foreground color of the number
   bC is the background color of the number (prevents having to clear previous space)
   nD is the number of digit spaces to occupy (must include space for minus sign for numbers < 0)
   nD < 0 Suppresses leading zero
 
   Sample Use: Fill the screen with a 2-digit number suppressing leading zero

          draw7Number(38,20,40,10,WHITE, BLACK, -2);

**********************************************************************************/
void draw7Number(int n, unsigned int xLoc, unsigned int yLoc, char cS, unsigned int fC, unsigned int bC, char nD) {
  unsigned int num=abs(n),i,s,t,w,col,h,a,b,si=0,j=1,d=0,S1=cS,S2=5*cS,S3=2*cS,S4=7*cS,x1=(S3/2)+1,x2=(2*S1)+S2+1,y1=yLoc+x1,y3=yLoc+(2*S1)+S4+1;
  unsigned int seg[7][3]={{(S3/2)+1,yLoc,1},{x2,y1,0},{x2,y3+x1,0},{x1,(2*y3)-yLoc,1},{0,y3+x1,0},{0,y1,0},{x1,y3,1}};
  unsigned char nums[12]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x67,0x00,0x40},c=(c=abs(cS))>10?10:(c<1)?1:c,cnt=(cnt=abs(nD))>10?10:(cnt<1)?1:cnt;
  for (xLoc+=cnt*(d=(2*S1)+S2+(2*S3)+2);cnt>0;cnt--){
    for (i=(num>9)?num%10:((!cnt)&&(n<0))?11:((nD<0)&&(!num))?10:num,xLoc-=d,num/=10,j=0;j<7;++j){
      col=(nums[i]&(1<<j))?fC:bC;s=(2*S1)/S3;
      if (seg[j][2])for(w=S2,t=seg[j][1]+S3,h=seg[j][1]+(S3/2),a=xLoc+seg[j][0]+S1,b=seg[j][1];b<h;b++,a-=s,w+=(2*s))tft.drawFastHLine(a,b,w,col);
      else for(w=S4,t=xLoc+seg[j][0]+S3,h=xLoc+seg[j][0]+S3/2,b=xLoc+seg[j][0],a=seg[j][1]+S1;b<h;b++,a-=s,w+=(2*s))tft.drawFastVLine(b,a,w,col);
      for (;b<t;b++,a+=s,w-=(2*s))seg[j][2]?tft.drawFastHLine(a,b,w,col):tft.drawFastVLine(b,a,w,col);
    }
  }
}


And realy big numbers on 240x320 tft. becomes available thanks to Mr. William Zaggle

wzaggle

I realized some may be using other orientations of the display and need numbers shown accordingly.

Here are versions for all four directions just in case...   :)

Code: [Select]

/* Routine to Draw Large 7-Segment formated number with Arduino TFT Library
   Contributed by William Zaggle  (Uses TFT Library DrawLine and Fill Rectangle functions).

   int n - The number to be displayed
   int xLoc = The x location of the upper left corner of the number
   int yLoc = The y location of the upper left corner of the number
   int cS = The size of the number.
   fC is the foreground color of the number
   bC is the background color of the number (prevents having to clear previous space)
   nD is the number of digit spaces to occupy (must include space for minus sign for numbers < 0).


    // Example to draw the number 37 in four directions in four corners of the display
    draw7Number(37,10,10,4,WHITE,BLACK,2);          //LEFT2RIGHT
    draw7Number90(37,10,310,4,WHITE,BLACK,2);     //DOWN2UP
    draw7Number180(37,230,310,4,WHITE,BLACK,2);  //RIGHT2LEFT
    draw7Number270(37,230,10,4,WHITE,BLACK,2);    //UP2DOWN
*/
void draw7Number(int n, unsigned int xLoc, unsigned int yLoc, char cS, unsigned int fC, unsigned int bC, char nD) {
  unsigned int num=abs(n),i,s,t,w,col,h,a,b,si=0,j=1,d=0,S2=5*cS,S3=2*cS,S4=7*cS,x1=cS+1,x2=S3+S2+1,y1=yLoc+x1,y3=yLoc+S3+S4+1;
  unsigned int seg[7][3]={{x1,yLoc,1},{x2,y1,0},{x2,y3+x1,0},{x1,(2*y3)-yLoc,1},{0,y3+x1,0},{0,y1,0},{x1,y3,1}};
  unsigned char nums[12]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x67,0x00,0x40},c=(c=abs(cS))>10?10:(c<1)?1:c,cnt=(cnt=abs(nD))>10?10:(cnt<1)?1:cnt;
  for (xLoc+=cnt*(d=S2+(3*S3)+2);cnt>0;cnt--){
    for (i=(num>9)?num%10:((!cnt)&&(n<0))?11:((nD<0)&&(!num))?10:num,xLoc-=d,num/=10,j=0;j<7;++j){
      col=(nums[i]&(1<<j))?fC:bC;
      if (seg[j][2])for(w=S2,t=seg[j][1]+S3,h=seg[j][1]+cS,a=xLoc+seg[j][0]+cS,b=seg[j][1];b<h;b++,a--,w+=2)Tft.drawHorizontalLine(a,b,w,col);
      else for(w=S4,t=xLoc+seg[j][0]+S3,h=xLoc+seg[j][0]+cS,b=xLoc+seg[j][0],a=seg[j][1]+cS;b<h;b++,a--,w+=2)Tft.drawVerticalLine(b,a,w,col);
      for (;b<t;b++,a++,w-=2)seg[j][2]?Tft.drawHorizontalLine(a,b,w,col):Tft.drawVerticalLine(b,a,w,col);
    }
  }
}

void draw7Number90(int n, unsigned int xLoc, unsigned int yLoc, char cS, unsigned int fC, unsigned int bC, char nD) {
  unsigned int num=abs(n),i,s,t,w,col,h,a,b,si=0,j=1,d=0,S2=5*cS,S3=2*cS,S4=7*cS;
  unsigned int x1=cS+1,x2=S3+S2+1,y1=xLoc+x1,y3=xLoc+S3+S4+1;
  unsigned int seg[7][3]={{x1,xLoc,1},{x2,y1,0},{x2,y3+x1,0},{x1,(2*y3)-xLoc,1},{0,y3+x1,0},{0,y1,0},{x1,y3,1}};
  unsigned char nums[12]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x67,0x00,0x40},c=(c=abs(cS))>10?10:(c<1)?1:c,cnt=(cnt=abs(nD))>10?10:(cnt<1)?1:cnt;
  for (yLoc-=cnt*(d=S2+(3*S3)+2);cnt>0;cnt--){
    for (  i=(num>9)?num%10:((!cnt)&&(n<0))?11:((nD<0)&&(!num))?10:num,yLoc+=d,num/=10,j=0;j<7;++j){
      col=(nums[i]&(1<<j))?fC:bC;
      if (seg[j][2])for(w=S2,t=seg[j][1]+S3,h=seg[j][1]+cS,a=yLoc-(seg[j][0]+cS+S2-1),b=seg[j][1];b<h;b++,a--,w+=2)Tft.drawVerticalLine(b,a,w,col);
      else for(w=S4,t=yLoc-seg[j][0]-S3,h=yLoc-seg[j][0]-cS,b=yLoc-seg[j][0],a=seg[j][1]+cS;b>h;b--,a--,w+=2)Tft.drawHorizontalLine(a,b,w,col);
      for (;seg[j][2]?b<t:b>t;seg[j][2]?b++:b--,a++,w-=2)seg[j][2]?Tft.drawVerticalLine(b,a,w,col):Tft.drawHorizontalLine(a,b,w,col);
    }
  }
}


void draw7Number180(int n, unsigned int xLoc, unsigned int yLoc, char cS, unsigned int fC, unsigned int bC, char nD) {
  unsigned int num=abs(n),i,s,t,w,col,h,a,b,si=0,j=1,d=0,S2=5*cS,S3=2*cS,S4=7*cS;
  unsigned int x1=cS,x2=S3+S2+1,y1=yLoc-x1,y3=yLoc-S3-S4-1;
  unsigned int seg[7][3]={{x1,yLoc,1},{x2,y1,0},{x2,y3-x1,0},{x1,(2*y3)-yLoc,1},{0,y3-x1,0},{0,y1,0},{x1,y3,1}};
  unsigned char nums[12]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x67,0x00,0x40},c=(c=abs(cS))>10?10:(c<1)?1:c,cnt=(cnt=abs(nD))>10?10:(cnt<1)?1:cnt;
  for (xLoc-=cnt*(d=S2+(3*S3)+2);cnt>0;cnt--){
    for (i=(num>9)?num%10:((!cnt)&&(n<0))?11:((nD<0)&&(!num))?10:num,xLoc+=d,num/=10,j=0;j<7;++j){
      col=(nums[i]&(1<<j))?fC:bC;
      if (seg[j][2])for(w=S2,t=seg[j][1]-S3,h=seg[j][1]-cS,a=xLoc-(seg[j][0]+cS+S2),b=seg[j][1];b>h;b--,a--,w+=2)Tft.drawHorizontalLine(a,b,w,col);
      else for(w=S4,t=xLoc-seg[j][0]-S3,h=xLoc-seg[j][0]-cS,b=xLoc-seg[j][0],a=seg[j][1]-cS-S4;b>h;b--,a--,w+=2)Tft.drawVerticalLine(b,a,w,col);
      for (;b>t;b--,a++,w-=2)seg[j][2]?Tft.drawHorizontalLine(a,b,w,col):Tft.drawVerticalLine(b,a,w,col);
    }
  }
}

void draw7Number270(int n, unsigned int xLoc, unsigned int yLoc, char cS, unsigned int fC, unsigned int bC, char nD) {
  unsigned int num=abs(n),i,s,t,w,col,h,a,b,si=0,j=1,d=0,S2=5*cS,S3=2*cS,S4=7*cS;
  unsigned int x1=cS+1,x2=S3+S2+1,y1=xLoc-x1,y3=xLoc-S3-S4-1;
  unsigned int seg[7][3]={{x1,xLoc,1},{x2,y1,0},{x2,y3-x1,0},{x1,(2*y3)-xLoc,1},{0,y3-x1,0},{0,y1,0},{x1,y3,1}};
  unsigned char nums[12]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x67,0x00,0x40},c=(c=abs(cS))>10?10:(c<1)?1:c,cnt=(cnt=abs(nD))>10?10:(cnt<1)?1:cnt;
  for (yLoc+=cnt*(d=S2+(3*S3)+2);cnt>0;cnt--){
    for (i=(num>9)?num%10:((!cnt)&&(n<0))?11:((nD<0)&&(!num))?10:num,yLoc-=d,num/=10,j=0;j<7;++j){
      col=(nums[i]&(1<<j))?fC:bC;
      if (seg[j][2])for(w=S2,t=seg[j][1]-S3,h=seg[j][1]-cS,a=yLoc+(seg[j][0]+cS),b=seg[j][1];b>h;b--,a--,w+=2)Tft.drawVerticalLine(b,a,w,col);
      else for(w=S4,t=yLoc+seg[j][0]+S3,h=yLoc+seg[j][0]+cS,b=yLoc+seg[j][0],a=seg[j][1]-cS-S4+1;b<h;b++,a--,w+=2)Tft.drawHorizontalLine(a,b,w,col);
      for (;seg[j][2]?b>t:b<t;seg[j][2]?b--:b++,a++,w-=2)seg[j][2]?Tft.drawVerticalLine(b,a,w,col):Tft.drawHorizontalLine(a,b,w,col);
    }
  }
}




wzaggle

Here are the full conversions for all the Adafruit library users.  Thanks Common_Ground.

I picked up one of these just to test and be sure they all worked.  Also attached is a diagram that shows the pixel spacing for the numbers.  The diagram should help because with really large leading zero blanked numbers, the actual displayed number often needs to be centered.  One example is when displaying something like the day of the month in the center of the screen.  The 31st, the 11th and the 1st should all be centered.

For those who don't know already, the way to do this is to first use the width function nD(11cS+2)-2cS to find what the width of the two digits will be for the size.  Then use half of this as the xoffset from the center of the screen. So a 2 digit number at size 8 would be width 2(11x8+2)-16 or 164 pixels.  Half of that would be 82 pixels so this is the amount to offset the width to start. So using xLoc as the center of the screen (120 for a 240 pixel width screen)...

draw7number(N, xLoc-82, yloc, 8, fC, bC, 2)

Now if you wanted even the # 1 to be centered as well as the number 11 or the number 31, you have to take a look at the number itself.  Looking at the diagram, it works out that for half the pixel change for those numbersis 4 x cS pixels for numbers less than 20 and less than 2, plus an additional 2 x cS pixels for numbers less than 10. 

draw7number(N, xLoc-82-((N<20)+(n<2))*20-(N<10)*10, yLoc, 8, fC, bC, -2)

This also exposes that the 5x and 7x scalers S2 and S4 could easily be changed on the 1st line of the function if someone wanted taller, shorter, fatter or skinnier numbers.  All three scalers S2, S3 and S4 could also be adjusted down in fact if someone wanted really tiny numbers. 

Hope this helps someone. :)



Code: [Select]
#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"

// For the Adafruit shield, these are the default.
#define TFT_DC 9
#define TFT_CS 10

// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
// If using the breakout, change pins as desired
//Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO);

void setup() {
 
  tft.begin();
  tft.fillScreen(ILI9341_BLACK);
}


void loop(void) {
    draw7Number(millis() / 1000 ,10,10,4, ILI9341_WHITE , ILI9341_BLACK,2);        //LEFT2RIGHT
    draw7Number90(millis() / 1000 ,10,310,4, ILI9341_WHITE , ILI9341_BLACK,2);     //DOWN2UP
    draw7Number180(millis() / 1000 ,230,310,4, ILI9341_WHITE , ILI9341_BLACK,2);  //RIGHT2LEFT
    draw7Number270(millis() / 1000 ,230,10,4, ILI9341_WHITE , ILI9341_BLACK,2);    //UP2DOWN
}

/* Routine to Draw Large 7-Segment formated number with Arduino TFT Library
   Contributed by William Zaggle  (Uses TFT Library DrawLine and Fill Rectangle functions).

   int n - The number to be displayed
   int xLoc = The x location of the upper left corner of the number
   int yLoc = The y location of the upper left corner of the number
   int cS = The size of the number.
   fC is the foreground color of the number
   bC is the background color of the number (prevents having to clear previous space)
   nD is the number of digit spaces to occupy (must include space for minus sign for numbers < 0).


    // Example to draw the number 37 in four directions in four corners of the display
    draw7Number(37,10,10,4, ILI9341_WHITE , ILI9341_BLACK,2);          //LEFT2RIGHT
    draw7Number90(37,10,310,4, ILI9341_WHITE , ILI9341_BLACK,2);     //DOWN2UP
    draw7Number180(37,230,310,4, ILI9341_WHITE , ILI9341_BLACK,2);  //RIGHT2LEFT
    draw7Number270(37,230,10,4, ILI9341_WHITE , ILI9341_BLACK,2);    //UP2DOWN
*/
void draw7Number(int n, unsigned int xLoc, unsigned int yLoc, char cS, unsigned int fC, unsigned int bC, char nD) {
  unsigned int num=abs(n),i,s,t,w,col,h,a,b,si=0,j=1,d=0,S2=5*cS,S3=2*cS,S4=7*cS,x1=cS+1,x2=S3+S2+1,y1=yLoc+x1,y3=yLoc+S3+S4+1;
  unsigned int seg[7][3]={{x1,yLoc,1},{x2,y1,0},{x2,y3+x1,0},{x1,(2*y3)-yLoc,1},{0,y3+x1,0},{0,y1,0},{x1,y3,1}};
  unsigned char nums[12]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x67,0x00,0x40},c=(c=abs(cS))>10?10:(c<1)?1:c,cnt=(cnt=abs(nD))>10?10:(cnt<1)?1:cnt;
  for (xLoc+=cnt*(d=S2+(3*S3)+2);cnt>0;cnt--){
    for (i=(num>9)?num%10:((!cnt)&&(n<0))?11:((nD<0)&&(!num))?10:num,xLoc-=d,num/=10,j=0;j<7;++j){
      col=(nums[i]&(1<<j))?fC:bC;
      if (seg[j][2])for(w=S2,t=seg[j][1]+S3,h=seg[j][1]+cS,a=xLoc+seg[j][0]+cS,b=seg[j][1];b<h;b++,a--,w+=2)tft.drawFastHLine(a,b,w,col);
      else for(w=S4,t=xLoc+seg[j][0]+S3,h=xLoc+seg[j][0]+cS,b=xLoc+seg[j][0],a=seg[j][1]+cS;b<h;b++,a--,w+=2)tft.drawFastVLine(b,a,w,col);
      for (;b<t;b++,a++,w-=2)seg[j][2]?tft.drawFastHLine(a,b,w,col):tft.drawFastVLine(b,a,w,col);
    }
  }
}
 
void draw7Number90(int n, unsigned int xLoc, unsigned int yLoc, char cS, unsigned int fC, unsigned int bC, char nD) {
  unsigned int num=abs(n),i,s,t,w,col,h,a,b,si=0,j=1,d=0,S2=5*cS,S3=2*cS,S4=7*cS;
  unsigned int x1=cS+1,x2=S3+S2+1,y1=xLoc+x1,y3=xLoc+S3+S4+1;
  unsigned int seg[7][3]={{x1,xLoc,1},{x2,y1,0},{x2,y3+x1,0},{x1,(2*y3)-xLoc,1},{0,y3+x1,0},{0,y1,0},{x1,y3,1}};
  unsigned char nums[12]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x67,0x00,0x40},c=(c=abs(cS))>10?10:(c<1)?1:c,cnt=(cnt=abs(nD))>10?10:(cnt<1)?1:cnt;
  for (yLoc-=cnt*(d=S2+(3*S3)+2);cnt>0;cnt--){
    for (  i=(num>9)?num%10:((!cnt)&&(n<0))?11:((nD<0)&&(!num))?10:num,yLoc+=d,num/=10,j=0;j<7;++j){
      col=(nums[i]&(1<<j))?fC:bC;
      if (seg[j][2])for(w=S2,t=seg[j][1]+S3,h=seg[j][1]+cS,a=yLoc-(seg[j][0]+cS+S2-1),b=seg[j][1];b<h;b++,a--,w+=2)tft.drawFastVLine(b,a,w,col);
      else for(w=S4,t=yLoc-seg[j][0]-S3,h=yLoc-seg[j][0]-cS,b=yLoc-seg[j][0],a=seg[j][1]+cS;b>h;b--,a--,w+=2)tft.drawFastHLine(a,b,w,col);
      for (;seg[j][2]?b<t:b>t;seg[j][2]?b++:b--,a++,w-=2)seg[j][2]?tft.drawFastVLine(b,a,w,col):tft.drawFastHLine(a,b,w,col);
    }
  }
}


void draw7Number180(int n, unsigned int xLoc, unsigned int yLoc, char cS, unsigned int fC, unsigned int bC, char nD) {
  unsigned int num=abs(n),i,s,t,w,col,h,a,b,si=0,j=1,d=0,S2=5*cS,S3=2*cS,S4=7*cS;
  unsigned int x1=cS,x2=S3+S2+1,y1=yLoc-x1,y3=yLoc-S3-S4-1;
  unsigned int seg[7][3]={{x1,yLoc,1},{x2,y1,0},{x2,y3-x1,0},{x1,(2*y3)-yLoc,1},{0,y3-x1,0},{0,y1,0},{x1,y3,1}};
  unsigned char nums[12]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x67,0x00,0x40},c=(c=abs(cS))>10?10:(c<1)?1:c,cnt=(cnt=abs(nD))>10?10:(cnt<1)?1:cnt;
  for (xLoc-=cnt*(d=S2+(3*S3)+2);cnt>0;cnt--){
    for (i=(num>9)?num%10:((!cnt)&&(n<0))?11:((nD<0)&&(!num))?10:num,xLoc+=d,num/=10,j=0;j<7;++j){
      col=(nums[i]&(1<<j))?fC:bC;
      if (seg[j][2])for(w=S2,t=seg[j][1]-S3,h=seg[j][1]-cS,a=xLoc-(seg[j][0]+cS+S2),b=seg[j][1];b>h;b--,a--,w+=2)tft.drawFastHLine(a,b,w,col);
      else for(w=S4,t=xLoc-seg[j][0]-S3,h=xLoc-seg[j][0]-cS,b=xLoc-seg[j][0],a=seg[j][1]-cS-S4;b>h;b--,a--,w+=2)tft.drawFastVLine(b,a,w,col);
      for (;b>t;b--,a++,w-=2)seg[j][2]?tft.drawFastHLine(a,b,w,col):tft.drawFastVLine(b,a,w,col);
    }
  }
}

void draw7Number270(int n, unsigned int xLoc, unsigned int yLoc, char cS, unsigned int fC, unsigned int bC, char nD) {
  unsigned int num=abs(n),i,s,t,w,col,h,a,b,si=0,j=1,d=0,S2=5*cS,S3=2*cS,S4=7*cS;
  unsigned int x1=cS+1,x2=S3+S2+1,y1=xLoc-x1,y3=xLoc-S3-S4-1;
  unsigned int seg[7][3]={{x1,xLoc,1},{x2,y1,0},{x2,y3-x1,0},{x1,(2*y3)-xLoc,1},{0,y3-x1,0},{0,y1,0},{x1,y3,1}};
  unsigned char nums[12]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x67,0x00,0x40},c=(c=abs(cS))>10?10:(c<1)?1:c,cnt=(cnt=abs(nD))>10?10:(cnt<1)?1:cnt;
  for (yLoc+=cnt*(d=S2+(3*S3)+2);cnt>0;cnt--){
    for (i=(num>9)?num%10:((!cnt)&&(n<0))?11:((nD<0)&&(!num))?10:num,yLoc-=d,num/=10,j=0;j<7;++j){
      col=(nums[i]&(1<<j))?fC:bC;
      if (seg[j][2])for(w=S2,t=seg[j][1]-S3,h=seg[j][1]-cS,a=yLoc+(seg[j][0]+cS),b=seg[j][1];b>h;b--,a--,w+=2)tft.drawFastVLine(b,a,w,col);
      else for(w=S4,t=yLoc+seg[j][0]+S3,h=yLoc+seg[j][0]+cS,b=yLoc+seg[j][0],a=seg[j][1]-cS-S4+1;b<h;b++,a--,w+=2)tft.drawFastHLine(a,b,w,col);
      for (;seg[j][2]?b>t:b<t;seg[j][2]?b--:b++,a++,w-=2)seg[j][2]?tft.drawFastVLine(b,a,w,col):tft.drawFastHLine(a,b,w,col);
    }
  }
}


westfw

Quote
It is pretty compressed so not that readable
Why?  That's gotta be some of the ugliest code I've seen in a long time.

wzaggle

Sorry Wesfw,

Didn't know there was extra points for pretty...  :'(   But it is a fair request.

Here is a greatly expanded and commented version of Draw7.   I will work on expanding the other three directions for all the big code lovers or those that just like to follow what is going on.

WZ

Code: [Select]

void draw7Number(int n, unsigned int xLoc, unsigned int yLoc, char cS, unsigned int fC, unsigned int bC, char nD) {
  unsigned int num = abs(n), i, s, t, w, col, h, a, b, si = 0, j = 1, d = 0;
  unsigned int S2 = 5 * cS;  // width of horizontal segments   5 times the cS
  unsigned int S3 = 2 * cS;  // thickness of a segment 2 times the cs
  unsigned int S4 = 7 * cS;  // height of vertical segments 7 times the cS
  unsigned int x1 = cS + 1;  // starting x location of horizontal segments
  unsigned int x2 = S3 + S2 + 1; // starting x location of right side segments
  unsigned int y1 = yLoc + x1; // starting y location of top side segments
  unsigned int y3 = yLoc + S3 + S4 + 1; // starting y location of bottom side segments
  unsigned int seg[7][3] = {{x1, yLoc, 1}, {x2, y1, 0}, {x2, y3 + x1, 0}, {x1, (2 * y3) - yLoc, 1}, {0, y3 + x1, 0}, {0, y1, 0}, {x1, y3, 1}}; // actual x,y locations of all 7 segments with direction
  unsigned char nums[12] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x67, 0x00, 0x40};  // segment defintions for all 10 numbers plus blank and minus sign
  unsigned char c, cnt;
 
  c = abs(cS);         // get character size between 1 and 10 ignoring sign
  if (c>10) c= 10;
  if (c<1) c = 1;
 
  cnt = abs(nD);      // get number of digits between 1 and 10 ignoring sign
  if (cnt > 10) cnt = 10;
  if (cnt < 1) cnt = 1;
 
  xLoc += (cnt-1) * (d = S2 + (3 * S3) + 2);   // set starting x at last digit location
 
  while( cnt > 0) {                   // for cnt number of places
 
    --cnt;
   
    if (num > 9) i = num%10;           //get the last digit
    else if (!cnt && n<0) i = 11;              //show minus sign if 1st position and negative number
    else if (nD < 0 && !num) i = 10;   //show blanks if remaining number is zero
    else i = num;

    num = num/10;   // trim this digit from the number 
   
    for (j = 0; j < 7; ++j) {          // draw all seven segments
     
      if (nums[i] & (1 << j)) col = fC;  // if segment is On use foreground color
      else col = bC;                    // else use background color

      if (seg[j][2]) {
       
        w = S2;                        // Starting width of segment (side)
        t = seg[j][1] + S3;            // maximum thickness of segment
        h = seg[j][1] + cS;            // half way point thickness of segment
        a = xLoc + seg[j][0] + cS;     // starting x location
        b = seg[j][1];                 // starting y location
       
        while (b < h) {                // until x location = half way
           tft.drawFastHLine(a, b, w, col); //Draw a horizontal segment top
           a--;                         // move the x position by -1
           b++;                         // move the y position by 1
           w += 2;                      // make the line wider by 2
        }
       
      } else {
       
        w = S4;                        // Starting height of segment (side)
        t = xLoc + seg[j][0] + S3;     // maximum thickness of segment
        h = xLoc + seg[j][0] + cS;     // half way point thickness of segment
        a = seg[j][1] + cS;            // starting y location
        b = xLoc + seg[j][0];          // starting x location
       
        while (b < h) {                // until x location = half way
          tft.drawFastVLine(b, a, w, col);  // Draw a vertical line right side
          a--;                          //  move the y position by -1
          b++;                          //  move teh x position by 1
          w += 2;                       //  make the line wider by 2
        }
      }
     
      while (b < t) {  //finish drawing horizontal bottom or vertical left side of segment
        if (seg[j][2]) {
           tft.drawFastHLine(a, b, w, col);  // Draw Horizonal line bottom
        } else {
           tft.drawFastVLine(b, a, w, col);  // Draw Vertical line left side
        }
        b++;        // keep moving the x or y draw position until t
        a++;        // move the length or height starting position back the other way.
        w -= 2;     // move the length or height back the other way
      }
    }
   
    xLoc -=d;       // move to next digit position
  }
}

westfw

Thanks.  Much nicer!  They compile to the same code, right?

You might get a very small improvement by making "nums" static const...
Are fastvline and fasthline about the same speed?  I was wondering if it could draw all the lines in the faster direction, and benefit... (raster scans strike again!)

Do you want N to be a long?  An int on avr won't give you more than 6 digit positions...

Ps: the diagram in msg3 was lovely, and really helpful in understanding what you were doing.

wzaggle

Thanks again westfw...

Good call with the long.   Not sure about the direction change if the extra looping on one side would offset less looping in the display memory direction or not, but might be worth testing if indeed raster is faster. 

Just displaying 10 five digit numbers of size 1 showing current millis() gave:

00593
00603
00613
00622
00632
00642
00652
00662
00671
00681

So it got all 10 displayed inside of the second at size 1 averaging 9.8 ms.

Changing to 7 five digit numbers of size 2 showing current millis() gave:

00593
00618
00644
00668
00694
00719
00744

So this size slowed to an average 25.2ms.


Maybe using these benchmarks, I will try drawing in the bit friendly direction only and see if it makes a difference.

But compared to what I was getting with sprintf and drawstring and all the filled rectangles it was a big improvement in both speed and looks for any number.   

WZ

 






wzaggle

#8
Mar 22, 2015, 09:34 am Last Edit: Mar 22, 2015, 09:39 am by wzaggle
Here is the expanded code in all four direction for those interested, with comments and the Long rather than Int Number value (thanks westfw  ;) ).  If I find time I may look at combining them into a single function with a direction parameter.

This latest code is still the Adafruit library version with FastHLine and FastVLine.  

Sorry all four versions no longer fit in-line in their big readable format.   So you will have to see the attached file to get the changes.

BTW Westfw... writing in only the raster direction was not enough of a difference to overcome the overhead of writing more lines in the segment width direction rather than fewer in the segment length direction.   But thanks again for the suggestion.

WZ

PaulMurrayCbr

So, seeing as we are laying into wzaggle and critiquing his code :) ,

Code: [Select]

   a--;   // move the x position by -1


This kind of comment is never necessary.

Code: [Select]

   foo(2);   // call the foo function, passing it a parameter of 2


Doesn't tell you anything. Just call 'a' something a bit more descriptive.
http://paulmurraycbr.github.io/ArduinoTheOOWay.html

PaulMurrayCbr

Here's a pair of functions just to draw the segments.

Code: [Select]

// note that a thickness of n will give you a segment 2n+1 pixels wide. Minimum thickness is zero.
// take care not to make your line thicker than 2 times the length
void horizontalSegment(int x, int y, int segLen, int segThick, int foreC, int backC) {
  tft.drawFastHLine(x, y, segLen, foreC);
 
  while(segThick>0) {
    tft.drawFastHLine(x, y-segThick, segThick, backC);
    tft.drawFastHLine(x+segThick, y-segThick, segLen-segThick*2, foreC);
    tft.drawFastHLine(x+segLen-segThick, y-segThick, segThick, foreC);
   
    tft.drawFastHLine(x, y+segThick, segThick, backC);
    tft.drawFastHLine(x+segThick, y+segThick, segLen-segThick*2, foreC);
    tft.drawFastHLine(x+segLen-segThick, y+segThick, segThick, foreC);
   
    segThick--;
  }
}

// note that a thickness of n will give you a segment 2n+1 pixels wide. Minimum thickness is zero.
// take care not to make your line thicker than 2 times the length
void verticalSegment(int x, int y, int segLen, int segThick, int foreC, int backC) {
  tft.drawFastVLine(x, y, segLen, foreC);
 
  while(segThick>0) {
    tft.drawFastVLine(x-segThick, y, segThick, backC);
    tft.drawFastVLine(x-segThick, y+segThick, segLen-segThick*2, foreC);
    tft.drawFastVLine(x-segThick, y+segLen-segThick, segThick, backC);
   
    tft.drawFastVLine(x+segThick, y, segThick, backC);
    tft.drawFastVLine(x+segThick, y+segThick, segLen-segThick*2, foreC);
    tft.drawFastVLine(x+segThick, y+segLen-segThick, segThick, backC);
   
    segThick--;
  }
}
http://paulmurraycbr.github.io/ArduinoTheOOWay.html

westfw

Quote
Code: [Select]
a--;   // move the x position by -1

This kind of comment is never necessary.
In this case, it's sorta nice since sometimes x is called y...

wzaggle

Cool! 

Looks like I went full circle from no comments to too many comments.  I guess the nice thing about having too many of them is that people can always delete them or just ignore them.  But I am plenty tough so feel free to critique away!  I've been coding for 45 years but there is always more to learn. :)

In the readability effort I changed all the x's to always be x's and the y's to always be y's. 

I like PaulMurray's idea of drawing in from both sides at the same time.  (Thanks)  (I know a Paul Z Murray at McGill U. who would certainly have thought of this very thing.)  I taught university level computer graphics with him back in the 80's but have obviously forgotten way too much. 

I just needed nicer numbers for my clock/calendar indoor/outdoor temp display project.  Hopefully others will benefit.

WZ

westfw

 
Quote
I taught university level computer graphics with him back in the 80's but have obviously forgotten way too much.
Did you by any chance know Norm Badler at UPenn?


wzaggle

Doesn't ring my somewhat cracked bell westfw.  Possibly crossed paths at a SIGGRAPH or ASEE conference someplace, but too long ago. 

Go Up