Horizontal Bar Graphs with BPK- and BPI- LCDs
Bar graphs provide a quick visual indication of relative values like battery charge, signal strength, control setting, etc. Although alphanumeric LCDs like the BPI-216 lack the ability to plot points or draw lines, their custom characters can be used as building blocks for visually appealing bar graphs. This app note shows how, using a Parallax BASIC Stamp II as the host computer.
The principles demonstrated here can be used with any alphanumeric or graphics LCD that supports custom characters. The program included with this app note uses a couple of tricks that define the custom bar-graph characters with minimal BS2 resources (just a few bytes of program memory and no permanent RAM variables).
Horizontal Bar Graphs on Text LCDs
The simplest bar graph you can generate with an alphanumeric LCD is a row of repeating characters. For example, a series of X's could represent a quantity. On a 16-column-wide display:
x | value= 1 |
xxxxxxxx | value= 8 |
xxxxxxxxxxxxxxxx | value=16 |
For a more graphical presentation, a block character could be substituted for X's. for example, on most LCDs, ASCII 255 is a 5x8-pixel black rectangle.
This approach limits the graph's resolution--the minimum change that can be depicted is 1 part in 16.
You can increase the resolution by using the LCD's set of 8 custom characters. Each character block is 5 pixels wide, so in principle you could break a 16-column display into 5 x 16 = 80 columns of pixels.
The problem here is that there are fixed, 1-pixel gaps between each character position. As a changing bar graph crosses a gap, it lurches 2 pixel spaces for a 1-unit change. This is disconcerting in dynamic bar graphs, making smooth changes appear jerky.
A nice compromise is to use every other pixel column so that each character position represents 3 units. This matches up with the gaps between character positions and creates an nifty, striped bar, as shown below. (Note that the on-screen displays in TVs universally use a similar striped bar, perhaps to emphasize changes as opposed to absolute quantity.)

With 3 units per character block, a 16-column-wide LCD can depict values from 0 to 48 (assuming that the full screen width is used; in the example here it isn't).
Hookup for Demo
The schematic at right shows how to connect the display to the
BS2 for the demo. Data for the graph comes from the pot/capacitor
hookup shown. If you want to omit the pot, you may, but you will
have to work out some other way to supply data to the bar-graph
routine.
Running the BS2 Program
The BS2 program is listed below. Comments explain most of the
mechanics of it. It makes generous use of a lesser-known features
of the BS2 Serout
instruction, the REP
(repeat) modifier.
Within a Serout
list (inside the square brackets
[...]), REP
causes a particular byte to be sent
repeatedly. For example,
serout pin,baudmode,[REP "X" \26]
..causes 26 Xs to be sent. A couple handy aspects of REP are that..
- The number of reps can be a variable.
- If the number of reps is 0, nothing is sent.
In our bar-graph application, we divide the value to be graphed by 3 (the max number of vertical bars in a single character position), and print that number of 3-bar character blocks (|||). Then we take the remainder of division by 3 and print the appropriate bar block (| for one, or || for two). The rest of the bar-graph width gets filled with blank spaces.
' Program: BAR_BPK1.BS2 (Horizontal bar graphs, BS2/BPK/BPI-series) ' This program demonstrates how to generate horizontal bar ' graphs with the BASIC Stamp 2 controller and BPK- or BPI- ' series displays. Connect the display SER input to BS2 pin P0. ' Set BPS switch on the display for 9600-baud operation. ' Connect pot to pin P1 as shown in the schematic. ' ==BPK/BPI CONTROL CONSTANTS========================== I con 254 ' Instruction prefix for LCD. LCDCLS con 1 ' Clear-screen instruction for LCD. CGRAM con 64 ' Starting point of CG RAM. LINE1 con 128 ' Beginning of line 1. LINE2 con 192 ' Beginning of line 2. BARPOS con 198 ' Position: char 6 of 2nd line. S_PIN con 0 ' Serial output to Backpack/LCD. N9600 con $4054 ' 9600 baud. ' ==BAR-GRAPH CONSTANTS================================ WIDTH con 10 ' Max width in characters of bar. MAXBAR con width * 3 ' Max bar counts. FULBAR con 3 ' ASCII value of ||| bar. BASBAR con 0 ' ASCII value of 0 bar (blank). ' ==PROGRAM VARIABLES================================== graf var word ' Value to be graphed. bars var byte ' Number of full ||| bars to draw. bal var byte ' Balance left after all |||s drawn. pad var byte ' Number of spaces to pad to fill width. balF var bit ' Is a 'bal' character needed? (1=yes, 0=no). ' Note: the variables below are used only at program startup to ' initialize the bar-graph bit maps. Since they are never used ' in the main program, they are defined as aliases of the ' main-program variables. This costs nothing, and prevents ' wasting 3 bytes of RAM. cnt var bars ' Counter (0-15) for EEPROM/bit pat load. row var bal ' Row of bit pattern. char var pad ' Bit pattern read from EEPROM. ' ==DEMO PROGRAM======================================= pause 1000 ' Wait a second. gosub loadBitPats ' Load bit patterns for bars. ' Clear the LCD, print labels "-Bar Graph Demo-" on line 1 and "POWER" ' on line 2. serout S_PIN,N9600,[I,LCDCLS,I,LINE1,"-Bar Graph Demo-",I,LINE2,"POWER"] ' Main loop--get a pot measurement, position the cursor, graph, repeat. Again: gosub getPotVal ' Sample the pot. serout S_PIN,N9600,[I,BARPOS] ' Move to beginning of bar. gosub barGraph ' Display bar graph. goto Again ' Do it continuously. ' ==SUBROUTINES======================================== ' BARGRAPH: Take the value in 'graf' and present it as a ' horizontal bar graph from the current cursor location with a ' total width (in characters) set by the WIDTH constant. Each ' character position can represent a maximum value of 3 using ' the FULBAR character |||. The routine calculates how many full ' bars to use by dividing by 3. If there's a remainder after ' dividing by 3, the routine tacks on a partial-bar character ' ( | or ||) to represent the balance('bal'). Finally, it pads ' the remainder of the bar width with blank spaces in order to ' erase any leftovers from previous graphs. barGraph: bars = (graf max MAXBAR)/3 ' One full bar for each 3 graph units. bal = (graf max MAXBAR) // 3 ' Bal is remainder after division by 3. balF = bal max 1 ' If bal <> 0 then balF=1. pad = WIDTH - (bars+balF) ' Number of spaces to fill bar width. serout S_PIN,N9600,[rep FULBAR \bars, rep (bal+BASBAR) \balF, rep " " \pad] return ' GETPOTVAL: For demo data, take a pot measurement through pin 1. Cap is ' 0.1uF to +5V; pot is 10k to ground, as shown in BS2 manual RCTIME entry. getPotVal: ' Output pot value high 1: pause 1 ' Discharge cap on pin 1. rctime 1,1,graf ' Measure charge time (approx. 0-600) graf = graf/14 ' Scale to 0-50 approx. return ' LOADBITPATS: Transfer the bit patterns that make up the bars. Since ' vertical bars are made up of 8 identical bit patterns (e.g., ' a | bar consists of %10000, %10000, %10000...%10000), we save ' some program memory by repeating each pattern from lookup table ' 8 times. Otherwise, we'd have 32 items in the lookup table instead ' of just 4. loadBitPats: serout S_PIN,N9600,[I,CGRAM] ' Point to character-generator RAM. for cnt = 0 to 3 lookup cnt,[0,%10000,%10100,%10101],char ' Get the bit pattern. for row = 0 to 7 serout S_PIN,N9600,[char] ' Write the pattern to LCD CG RAM. next ' ..row next ' ..char pattern return
Summary and Modifications
Modifying the bar-graph code is pretty straightforward. First, set the constant WIDTH at the beginning of the program to the maximum width (in characters) that you want the bar to cover. The maximum value that the bar can display will be 3x WIDTH.
Before a GOSUB barGraph
, make sure to position the
cursor to the desired starting position on the LCD screen. It's
probably obvious, but that position should be WIDTH or more
characters from the right-hand edge of the screen, or the bar-graph
will wrap around.
Finally, put the value you want to graph into the variable
graf and GOSUB barGraph
. After the bar
graph has been generated, the cursor will be in the next character
position after the end of the graph.
The bar graph can be updated pretty rapidly for a nice, animated effect. It's a good idea to turn off the visible cursor when the bar graph is displayed, since the blinking effects and/or ghost images of the rapidly moving cursor can be distracting.