128x64 Bar Graph

128x64 Bar Graph

Here is my graphics driver and code to display a bar graph. Once you understand how to draw bar graphs of different heights, it is a simple step towards making a bar meter indicator, by using the output value from the ADC to adjust the height of the bar. I have written a lot of code for this however, it is a matter of finding the time to publish it all. I was hoping to show a video of it but my video camera bust hence I am saving up…

Project Files

Main Programbargraph.c
My C Library128x64.c
My declarations and bitmap data array128x64.h
Optional AVR Studio 4 project filebargraph.aps
bargraph.zip

Graphics Function: Bar()

This was one of my earliest bar graph codes that I made when I was just starting out using this type of LCD. I have decided to make this code available at this time. I have written countless other functions for drawing a bar on the LCD, which I will publish in due course if there are any hits or interest shown.

Although this C code is good enough to begin with, I found that I needed more efficient and faster code when displaying the screen for the spectrum analyser; hence, my most advanced bar graph code is in assembly language. Since I use it for commercial work, it will not be published for everyone to copy and paste.

I have written the functions in the simplest way possible so that everyone can understand, hence I am using if statements to compare "pbytes". I could easily apply a register shift operation and modulo arithmetic, but I think it would confuse beginners.

This function does not draw the bar one pixel at a time, but draws 8 pixels at once, which is much faster. I have written two functions, one to draw the bar, Bar(), and another to clear the bar, BarC().

Driver Code

  1: void Bar(uint8_t height, uint8_t width,
  2: uint8_t xpos, uint8_t row) {
  3:  
  4:  int dP;
  5:  uint8_t c;
  6:  uint8_t j;
  7:  uint8_t fbytes=0;
  8: // Full Bytes containing all ones,
  9: // therefore all pixels.
 10:  
 11:  uint8_t pbytes=0;
 12: // Partial Bytes contain some ones,
 13: // therefore some pixels.
 14:  
 15:  uint8_t cmd;
 16: // This is just a command holder.
 17:  
 18:  page = row;
 19: // The bar starts from this
 20: // specified row.
 21:  
 22:  if(height > 64){
 23: // This resets the height back to 64 when
 24: // reached. The maximum is 64 pixels.
 25:  height=64;
 26:  }
 27:  
 28:  if(width > 128){
 29:  // This resets the width back to 128 when
 30:  // reached. The maximum is 128 pixels.
 31:  width=128;
 32:  }
 33:  
 34:  while(height > 8) { 
 35:  height = height - 8; 
 36:  fbytes = fbytes + 1; 
 37:  } 
 38:  pbytes = height;
 39:  
 40:  
 41:  for(j = 0; j <= fbytes; j++){
 42:  
 43:  cmd = SET_PAGE | page;
 44:  WriteCommand(cmd); 
 45:  page = page - 1;
 46:  
 47:  
 48:  cmd = SET_ADDRESS | xpos; 
 49:  WriteCommand(cmd);
 50:  
 51:  cSelect(xpos);
 52: // Select which half of the screen to use.
 53:  
 54:  for(c = 1; c <= width ;c++){
 55: // This is the width of the bar.
 56:  
 57:  
 58:  if (xpos+c > 64){
 59:  cSelect(xpos+c); 
 60:  }
 61:  
 62:  DataMode();
 63:  dPort = 0b11111111;
 64:  LocknLoad(); 
 65:  }
 66:  
 67:  }
 68:  
 69:  
 70:  
 71:  cmd = SET_ADDRESS | xpos; 
 72:  WriteCommand(cmd);
 73:  
 74:  
 75:  if (pbytes ==1){
 76: // The bits fill from the MSB side first.
 77: 
 78:  dP = 0b10000000;
 79: // The bar is drawn from the lower
 80: // end of the LCD to the top end.
 81: 
 82:  }
 83:  // options for filling part of a byte.
 84:  if (pbytes ==2){
 85:  dP = 0b11000000;
 86:  }
 87:  
 88:  if (pbytes ==3){
 89:  dP = 0b11100000;
 90:  }
 91:  
 92:  if (pbytes ==4){
 93:  dP = 0b11110000;
 94:  }
 95:  
 96:  if (pbytes ==5){
 97:  dP = 0b11111000;
 98:  }
 99:  
 100:  if (pbytes ==6){
 101:  dP = 0b11111100;
 102:  }
 103:  
 104:  if (pbytes ==7){
 105:  dP = 0b11111110;
 106:  }
 107:  
 108:  if (pbytes ==8){
 109:  dP = 0b11111111;
 110:  }
 111:  
 112:  cSelect(xpos);
 113: // This selects which half of the
 114: // screen width to use.
 115:  
 116:  
 117:  for(c = 1; c <= width ;c++){
 118:  
 119:  if (xpos+c > 64){
 120:  cSelect(xpos+c); 
 121:  }
 122:  
 123:  
 124:  DataMode();
 125:  dPort = dP;
 126:  LocknLoad();
 127:  }
 128:  
 129:  
 130: }
 131:  
 132: // ----------------------------------
 133: // This function is to clear the bar
 134: // ----------------------------------
 135:  
 136: void BarC(uint8_t height, uint8_t width,
 137: uint8_t xpos, uint8_t row) {
 138:  
 139:  uint8_t c;
 140:  uint8_t j;
 141:  uint8_t fbytes=0; // Full Bytes are zeros
 142:  
 143: // data bytes are set to zero to switch
 144: // off the pixels and clear the bar.
 145:  uint8_t pbytes=0; // Partial Bytes are zeros
 146:  
 147: 
 148: 
 149:  uint8_t cmd;
 150:  
 151: 
 152:  
 153:  page = row;
 154: // The bar starts from this
 155: // specified row.
 156:  
 157:  if(height > 64){ // Limit for height.
 158:   height=64;
 159:  }
 160:  
 161:  
 162:  
 163:  if(width > 128){ // Limit for width.
 164:   width=128;
 165:  }
 166:  
 167:  while(height > 8) { 
 168:  height = height - 8; 
 169:  fbytes = fbytes + 1; 
 170:  } 
 171:  pbytes = height;
 172:  
 173:  if (height > 0){
 174:  
 175: // If there is a partial byte we need to
 176: // clear that as well.
 177: 
 178:  fbytes = fbytes + 1;
 179:  }
 180:  
 181:  for(j = 1; j <= fbytes; j++){
 182:  
 183:  cmd = SET_PAGE | page;
 184:  WriteCommand(cmd); 
 185:  page = page - 1;
 186:  
 187:  
 188: // Set the x position of the bar.
 189:  cmd = SET_ADDRESS | xpos; 
 190:  WriteCommand(cmd);
 191:  
 192:  cSelect(xpos);
 193: // Select which half of the screen to use.
 194:  for(c = 1; c <= width ;c++){
 195:  
 196: // If column 64 has not been reached then
 197: // set the pixels on the LCD. 
 198: 
 199:  
 200:  if (xpos+c > 64){
 201:  cSelect(xpos+c); 
 202:  }
 203:  
 204:  DataMode(); 
 205:  dPort = 0b00000000;
 206:  LocknLoad(); 
 207:  }
 208:  
 209:  }
 210:  
 211: }

Modulo Arithmetic

You might be wondering what "Partial Bytes" and "Full Bytes" are. Instead of using the modulo 8 arithmetic function, which would be far quicker but confusing for beginners, I am using the long way to calculate the number of 8 bits in any given height of the bar.

If you wanted a bar that is 25 pixels (bits) in height, then 25 contains 3 full bytes and one partial byte of 1 bit. Alternatively, you could express it as (3 × 8 bits) + 1 bit. The partial byte is the leftover remainder. It is modulo arithmetic but the long and slow way. These are the lengths I go to, to make things simple…

Drawing the "Full Bytes" is easy, because you simply send a byte consisting of 11111111 for each of the full bytes. However partial bytes can be any number between 0 and 8, hence I am using "If" statements to translate the partial byte into a relevant binary mask. This part of the code works faster by a register shift operation, but I think it will lose most beginners.

This Article Continues...

128x64 LCD
Programming the 128x64 LCD
KS0108 128x64 LCD Hardware Control
128x64 Timing and Modes
128x64 LCD Graphics Driver Design
128x64 ASCII Text Driver Design
128x64 Bar Graph