In this first project, we build a programmable tachometer that can detect between 1 and 9999 pulses a second (or another time period) and display the results on 4 seven-segment LED numeric displays. These pulses can be counted in two ways. If we know we are getting a fixed duty cycle square wave, we can measure the total length of a pulse and determine the frequency of the signal from that. If the pulses are not coming at a regular rate, we can actually count the pulses over time and determine the rate that way. We can write the software for both techniques so we can see the difference. At the end of the project, we will take the source of the pulses into consideration and discuss how to modify the software to display an actual rpm (revolutions per minute).
When working with microprocessors, much of the information we deal with is read and processed as pulses, counts, frequencies, and the like because microprocessors are digital devices, and are usually not designed for handling analog values. So it becomes important that you be comfortable with gathering, processing, and displaying information of a digital nature, and this project focuses on that aspect of our knowledge and understanding regarding microprocessors. Once you are comfortable with the design and construction of this tachometer, you will be able to handle all other frequency input projects with confidence.
We will use the LAB-X1 board as a helper instrument as we go along. First, we will make the software work on the LAB-X1 (when we can) and once we know we can read, count, and display the inputs the way we want, we will move on to the fabrication of the seven-segment LED-based tachometer shown in Figure 15.1.
In this project, the LAB-X1 will be particularly helpful because seven-segment displays require a lot or wiring (that can go wrong) and it can be difficult to get things sorted
Figure 15.1 A programmable tachometer.
out at times. When using a LAB-X1, we know we have a working platform to develop the software without having to worry about hardware problems that we might have created for ourselves. After we have the tachometer software working, we can use the LABX1 to create pulses generated at known frequencies to check the accuracy of the instrument we created.
The displaying of the counts on 4 seven-segment displays has to do with learning how to use these commonly used and inexpensive displays. It is, of course, much easier to display the information on the 2 × 20 LCD on the LAB-X1, but in this exercise we will be using the LAB-X1 only as our initial software platform. If you prefer, the project can easily be modified to run on the LAB-X1, but then you will miss out on the experience of learning how to use the seven-segment displays. In any case, the PIC we use will be the 16F877A. (This PIC will be employed for all the projects in this book, and the LAB-X1 will be used to support all the projects as needed.)
The fact that we are using only four displays is because the integer math provided in the compiler can handle only 16-bit math (65535 max), which limits us to four displays if we want to go to a 9 in the left-most display. It also simplifies our problem at this stage and will demonstrate that refreshing four displays takes us to the maximum that the PIC can control (at 4 MHz) without having to use assembly language programming. It is also a compromise that has to do with the fact that four digits can be handled easily with one and a half ports (12 lines).
We will be using a small dc motor with a 20-slots-per-revolution encoder (available from my Website encodergeek.com) on it as our signal generator (see Figure 15.2), but before we do that we need to undertake some general discussion about counting pulses and what the problems related with low and high “counts/time period” are. If you have some other device that can generate pulses, you can use it if you like.
Figure 15.2 A small dc motor with open encoders attached for generating signal pulses. (Fewer encoder slots give you more time to count them. Open construction gives full access to electronics.)
Figure 15.3 shows a circuit used to make a simple and inexpensive pulse generator with a PIC 16F819. This device can be used to generate the many pulses and intervals needed for the numerous devices that will be created. The fact that it is programmable is the key. No program is provided because you now have the skills to program this device for the outputs you need. The following are a couple of hints. The three-pin output can be programmed to emulate an R/C hobby output, while the 5 volts that a servo needs are provided at the middle pin. The five pins connected to PORTB should be pulled up, and then as they are shorted to ground with jumpers, they select the type of output the signal generator will create. Use the three LEDs as indicators to show you what is going on in the program.
Figure 15.4 shows a picture of the finished device. Bare boards and kits for this are available at encodergeek.com.
PULSE RATE: LOW RATE CONSIDERATIONS
Let’s think about this in terms of a small encoded motor like the one shown in Figure 15.2.
First, let’s look at what the problems at the low rpm end of the spectrum are. Let’s assume we are looking at signals coming from a rotating shaft and want to know the speed of the shaft in rpm. The lowest speed of the shaft is 1 rps (revolutions per second) and we want to update the rpm display every 0.25 seconds. We need to be able to detect at least five counts every 0.25 seconds to indicate that the shaft is turning. If the shaft had 20 markers on it, we would have to look at the shaft for at least 0.25 seconds to get these five counts. We need a certain amount of time to process and display the information, so the count needs to be read four to six times a second if we are to meet our minimum specification. We have agreed that there are 20 markers on the shaft of
Figure 15.3 Pulse generator circuit diagram.
this motor. We can read these with a photosensitive detector, and the output from the detector is suitable for direct input into our PIC/MCU. This is a reasonable assumption in that most phototransistors will feed directly into a PIC port. (The encoder on our motor meets this specification.)
For low-speed accuracy, the 20 markers must be placed accurately around the shaft. At high rpm, this would not matter because we would be reading many revolutions every 0.25 seconds and the relative placement of the 20 markers around the shaft would be integrated out over time.
We can use the COUNT command in the PBP language to do the counting. For a 0.25-second period, the command would be:
COUNT PORTA.2, 250, W1
where PORTA.2 is the line the signal is coming in on
250 is for a 250-millisecond or 0.25-second counting interval
W1 is the variable the count will be placed in
Figure 15.4 Photo of pulse generator.
The PIC will actually take 0.25+ seconds to execute this command (because of the counting time specified) and there is nothing else that can be done while this command is being executed (because of the way the compiler is designed). Since we could get a feel for the rpm in a much shorter time at higher rpm, this can definitely be considered a low-rpm problem. The solution to this low-count situation is to have a higher count encoder on the shaft so a large number of counts can be intercepted in a short period of time. However, as we will see, there is a trade-off at the high speed end when we increase the encoder count.
HIGH SPEED CONSIDERATIONS
There is also a limit to how high the frequency can be and still be in the range that the PIC can respond to. For the PIC 16F877A that we are using in combination with the PBP software, this frequency is 100 kHz for a 20 MHz OSC and 25 kHz for a 4 MHz OSC (our case). If we were using an encoder that had 1000 counts per revolution, we could keep up with a shaft that was rotating at 25 revolutions per second.
25,000/1000 = 25
A motor running at 3600 rpm, which is quite common, spins at 60 revolutions per second. We can see that we start to exceed the limit of what we can do rather rapidly. So on the one hand, high-count encoders are desirable at low speeds; on the other hand they become a problem at high speed. A corollary to this is that it is very difficult to run a shaft accurately at low speed without some sort of gearing or belt reduction. Think about the problems you would encounter trying to run a shaft at 1 revolution per year accurately directly with a motor.
If our specification calls for a maximum speed of 3600 rpm our encoder cannot have more than 300 counts per revolution if we want to maintain the counting time. Using a shorter time will help on the high rpm end but will make things harder in the low rpm region. Of course, we can also use an encoder with many fewer counts and have a perfectly good tachometer.
The software we are using employs integer math, and the largest variable we can use is a 2-byte word. The largest number 2 bytes can accommodate is 65535. This means that in the COUNT instruction:
COUNT PORTA.2, 250, W1
The number that ends up in W1 cannot exceed 65535. We can accommodate this requirement by shortening the 250 millisecond (or less) time frame, but then we will have problems with the 60-rpm end of the specification. The solution is to use the lowest count that will serve our purposes on the shaft encoder at the lowest speed we are interested in. If we use the 20 counts per revolution we discussed earlier, we will be getting (20 * 3600 / 60) = 1200 counts every second and a quarter of that every 0.25 seconds or 300 counts every second. This indicates that we could use an encoder with a few hundred lines per revolution if accurate slow speed indication was an important consideration. However, adding a higher count encoder to an existing shaft may not be trivial, and adding a few (even 1) equally spaced markers onto the shaft manually with a little epoxy or paint may be adequate for what we need.
There is also the possibility that we could write software that would use different routines for different speeds of the signal, but for this project let’s keep it simple. Such sophistications can be added after we get proficient at doing the work at hand. The most important thing to keep in mind is that you must understand the problem in a comprehensive way before you can create a solution.
DETECTION
Next, we must consider the components and circuitry needed to actually react with the signal we are trying to measure (or collect). The simplest way to do this is to react to the changes in the light intensity either as reflected from markers on the shaft or as a disturbance of some other kind in the vicinity. Hall effect sensors are a popular way of detecting rotation in a dirty environment because they are not affected by anything other than magnetic fields. The signal must be converted to a TTL/CMOS-level signal that goes high and low reliably with every change in the stimulus. Figure 15.5 shows one way to create such an instrument interface for the hall effect device.
Since the input to the PIC are Schmidt triggers, we would not normally have to condition the signal for bounce and jitter.
Figure 15.5 From datasheet: Circuits for input from a Hall effect sensor into a PIC.(Melexis MLX90217.)
We will be using the input from a two-channel optical encoder attached to a small dc motor as our signal source. This encoder has 20 slots around its periphery, so we will get 20 pulses per revolution. We will look at just one channel because we are interested only in the signal frequency.
The circuitry for the LED-phototransistor pair for the one channel in this detector is as shown in Figure 15.6.
Figure 15.6 Circuits for the input of a tachometer signal into a PIC from the motor optics.
Let’s write the code for the LAB-X1 first and make sure we can read the signals, count them, and display them on the LCD. This lets us build the confidence we need to proceed to the next level. Then, we will build the tachometer as a stand-alone device with the 4 seven-segment displays for output.
First, as usual, let’s create the code to enable the LCD. See Program 15.1.
Program 15.1 Counting optical encoder pulses to the LAB-X1
CLEAR ;
DEFINE OSC 4 ;
DEFINE LCD_DREG PORTD ; Define LCD connections
DEFINE LCD_DBIT 4 ;
DEFINE LCD_RSREG PORTE ;
DEFINE LCD_RSBIT 0 ;
DEFINE LCD_EREG PORTE ;
DEFINE LCD_EBIT 1 ;
LOW PORTE.2 ;
ADCON1=%00000111 ; this must be set if you are using the LCD
PAUSE 500 ; it makes the A & E analog ports digital
;
; Next set up the I/O and the variables we will use.
TRISC = %00000011 ;
TRISD =%11110000 ;
PORTD =%00000000 ; Turns the bits off
;
ALPHA VAR WORD ;
RPM VAR WORD ;
;
; Then display the LCD message to tell us we are ready
;
LCDOUT $FE, $01, “Tachometer Ready” ; on the first line.
PAUSE 250 ;
;
; The loop that displays the count status for the tachometer is
; now ready to be created.
;
LOOP: ; the display loop
COUNT PORTC.0, 250, ALPHA ; read the counts into the PIC
RPM=60 *ALPHA * 4 / 5 ; convert counts to rpm
LCDOUT $FE, $C0, DEC4 RPM,” ” ; print on line 2
GOTO LOOP ; do it forever
END ;
It’s that simple with the LAB-X1 and the PBP compiler.
Next, let’s design the software for the 4 seven-segment displays. Four of them will allow for a maximum display of 9999 (but since we are taking samples for 0.25 seconds, and we have 20 counts per revolution, the maximum rpm we can recognize without reducing the sampling time is 119,400).
Max count: 9999 in 0.25 seconds
39,996 in 1 second
There are 20 counts per revolution, which gives:
1,999.0 revolutions per seconds, or
(39,996/20/)*60 revolutions per minute
We can detect (but not display) a maximum of 119,940 rpm if we count 20 counts per revolution for 0.25 seconds. If we want to detect and display a higher rpm, we must reduce the time interval in the COUNT instruction or decrease the encoder count.
We have already set up PORTD with the lower 4 bits as outputs (but if we had not we would have to add the line of code to do that). After inserting an appropriate decision-making instruction, we can use any of these pins for an output signal to an appropriate device.
If we amplify the signal appropriately, as covered in Chapter 5, the signal on any of the output pins mentioned earlier can be used to control a heater or pump or whatever else we have in mind.
SEVEN-SEGMENT DISPLAYS
Seven-segment displays come in two types: common anode and common cathode. In both types, one side of all the LEDs is wired together to make a common connection. In our experiments, we will use common anode (CA) displays only (see Figure 15.7).
Most so-called seven-segment LED displays actually have eight or nine LEDs in them. The eighth and ninth LEDs are the decimal points on one or two sides of the number displayed. We will not use the decimal points for this project, so we will need only seven lines, but we will wire in the eighth line of the port to activate one decimal point of each display for possible future use.
First, we need to understand a little bit about how seven-segment displays are used. In a seven-segment LED display, all the segments have one common leg, the one that will go to the anode. The data is impressed on the other ends of the LEDs,and when the common leg is connected to the power, whatever data is impressed on the segments gets turned on. The segments are turned on one at a time in most cases, but they can also all be turned on simultaneously. Often, there will be more than one anode pin on a display, but all of them will be connected together inside the display. These extra pins are provided to make it easier to lay out the wiring on a printed circuit board.
All the display segments for each of the displays are connected in parallel to the data lines to one PIC port, and each of the common lines is assigned to a separate line from the PIC on another port. Each of the seven-segment modules is energized one at a time in rapid succession so that it seems to the human eye that they are all on at the same time. Deciding how long each segment must be lit and how often the display must be refreshed can be done on a trial-and-error basis till you come up with the best display possible under the circumstances. The segments are all the same size, so they require the same current, but the decimal point is smaller and needs less current. It may be necessary to power it for a shorter time or provide a larger resistor for it to match the brightness of all the other segments.
Figure 15.7 A common anode seven-segment display.
One way of laying out the circuitry for a common anode four-character display would be as shown in Figure 15.8. We would use a contiguous port to connect to the eight segment lines and a half port (for the four units in our case) to select the display to be lighted using one line at a time, again in rapid succession.
As mentioned earlier, the common line is the common anode (+). The connections and the program code that control the segments must be adjusted to suit the devices selected. Even though we did wire it, we will ignore the decimal point for this particular application. However, we will test it to make sure it works.
How the data lines are wired (the wiring sequence) is not important because whatever is needed for the display desired can be described in the software and does not make
Figure 15.8 Schematic layout: wiring for a set of four seven-segment displays.
any difference to the wiring of the electronics. Once the segments have been wired, the scheme needed to display the various numbers on the segments can be figured out.
In Figure 15.8, the lighting scheme for the displays is as defined in the following meta code:
Execute the COUNT instruction. The COUNT instruction tells us how many pulses were detected. We cannot access the displays while this count is being read. This causes a blinking of the displays on a machine running at 4 MHz. Even when we use an interrupt-driven routine to light the displays, this does not solve this problem because interrupts are not serviced within an instruction in code generated by the PBP compiler.
Convert this to an rpm
Get the first number separated out
Impress the first number on the data lines
Activate the first common line as you cycle through the segments
Activate the seven segments one at a time
Get the second number separated out
Impress the second number on the data lines
Activate the second common line as you cycle through the segments
Activate the seven segments one at a time
Get the third number separated out
Impress the third number on the data lines
Activate the third common line as you cycle through the segments
Activate the seven segments one at a time
Get the fourth number separated out
Impress the fourth number on the data lines
Activate the fourth common line as you cycle through the segments
Activate the seven segments one at a time
Go back to the beginning to do it again
There is one big difference between displaying on the LCD and displaying on the seven-segment displays. Once you have written to the LCD, the information on the LCD stays on the LCD. You do not have to do anything else. However, on the seven-segments displays, the information must be updated constantly. This means the driving routine must be based on an arrangement that is called often enough to make the display look like it is on all the time.
The information you need to lay out the wiring to implement the use of the 4 seven segment displays is provided in Figures 15-9 to 15-11. Being able to look at the layout from above and below makes it easier to visual the need of the top and bottom of the PC board layout.
The actual code segment for the wiring shown in Figure 15.8 is laid out in Program 15.2.
Program 15.2 Programming segment for 4 seven-segment displays
DISPLAY: ; reads each digit and then
READ DIGIT1, VALUE ; select first digit
PORTA=%00000001 ; turn on the first common line
GOSUB SHOW ; turn on the segments
PORTA=%00000010 ; turn on the next common line
READ DIGIT2, VALUE ;
GOSUB SHOW ; turn on the segment
PORTA=%00001000 ; turn on the next common line
READ DIGIT3, VALUE ;
GOSUB SHOW ; turn on the segments
PORTA=%00100000 ; turn on the next common line
READ DIGIT4, VALUE ;
GOSUB SHOW ; turn on the segments
RETURN ; end of subroutine
;
SHOW: ; shows each segment one at a time
Z=%11111110 ; selects one segment at a time
FOR X=1 TO 8 ; do the 8 segments, includes dec. point
Y=VALUE ; get value to show
Y.2=1 ; inhibits the decimal point
PORTB=VALUE | Z ; makes value and z select one segment
Z=(Z<<1)+1 ; go to next segment
PAUSEUS P ; pause to show segment
NEXT X ; next segment
PORTA=0 ; clear PortA
PORTB=0 ; clear portb to prevent ghosting
PAUSEUS P ; pause to show clear segments
RETURN ; end of subroutine
Figure 15.9 One way of wiring for the seven-segment displays as seen from above.
The display routine is written as a self-contained subroutine so it can be called by a relatively frequent interrupt to keep the display alive while a long and complicated program goes about doing its work.
Program 15.2, with a bit more code, replaces the code for the LCD in the original program. The complete program is listed after the wiring diagrams and some seven-segment related notes that follow.
The lighting of the displays is somewhat like the reverse of scanning a keyboard. The segments that are lit represent the reverse of the keys that were pressed. In either case, the scan identifies either a displayed set of segments or a key. Most scanning routines serve similar functions and are implemented in this same way.
Let’s write a short routine to light all the segments in a seven-segment display one segment at a time. Assume that the seven segments and the decimal point are connected to PORTB. The common line is attached to PORTA.1. Let’s assume a common anode
Figure 15.10 One way of wiring for the seven-segment displays as seen from below.
display. A current limiting resistor of 220 to 470 ohms is needed between each segment and the common anode.
PORTA=%00000001 ; turn on the first segment
PORTB=%11111110 ; select the first segment
FOR X=1 TO 8 ; there are 8 segments
PAUSE 100 ; Look at each segment
PORTB=PORTB<<1 ; go to next segment
PORTB=PORTB + 1 ; put the 1 back in PortB bit 0
NEXT X ; do the next one
In the preceding program clip, we start with the segment connected to B.0 ON and shift the bits one position to the left with each iteration. Adding 1 to PORTB replaces the 0 in the bit after the shift to the left with a 1. The last iteration leaves %11111111 in PORTB and turns all the segments off. If we want to suppress the decimal point, we have to add a line of code to place a 1 in the affected bit after each iteration. Assume that the decimal point is connected to bit 3. Adding:
PORTB.3=1; ;
after the shift instruction puts a 1 back at bit 3 and keeps the decimal point turned off.
The finished working program (see Program 15.3) uses the pulse length determining instruction to calculate the pulse rate.
Figure 15.11 Wiring diagram for the programmable pulse counter.
Program 15.3 Finished pulse counting program
CLEAR ;
DEFINE OSC 4 ; Oscillator speed
;
; The following are the images of the numbers and are stored in
; memory
; before we do anything else. These will be placed in PORTB to
; display the
; number for each digit.
WRITE 1999, %11111111 ; blank, no LED is lit
WRITE 2000, %00000110 ; the number 0)
WRITE 2001, %10111110 ; the number 1)
WRITE 2002, %01001100 ; the number 2)
WRITE 2003, %00011100 ; the number 3) for our particular wiring
WRITE 2004, %10110100 ; the number 4) setup this defines the
WRITE 2005, %00010101 ; the number 5) ten digits that we need.
WRITE 2006, %00000101 ; the number 6)
WRITE 2007, %10011110 ; the number 7)
WRITE 2008, %00000100 ; the number 8)
WRITE 2009, %00010100 ; the number 9)
;
TRISA=%00010000 ; set up PORTA
TRISB=%00000000 ; set up PORTB
;
P VAR BYTE ; pause variable
X VAR BYTE ; counter variable
Y VAR BYTE ; counter variable
Z VAR BYTE ; counter variable
Q VAR BYTE ; counter variable
PULSE_LEN VAR WORD ; pulse length
VALUE VAR WORD ; value of metronome counts
TOTAL VAR WORD ; value of metronome counts
DIGIT1 VAR BYTE ; each digit in display
DIGIT2 VAR BYTE ; each digit in display
DIGIT3 VAR BYTE ; each digit in display
DIGIT4 VAR BYTE ; each digit in display
;
X =0 ; sets the initial value for X
P =400 ; set pause will be in microseconds
TOTAL=0 ; Initialize
Q =0 ; initialize
;
MAIN: ; main loop of program
DIGIT1=-1 ; set all 4 digits to a blank
DIGIT2=-1 ; set all 4 digits to a blank
DIGIT3=-1 ; set all 4 digits to a blank
DIGIT4=-1 ; set all 4 digits to a blank
Q=Q+1 ; counter to take jitter out of display
IF Q<15 THEN ; don’t do anything
ELSE ;
PULSIN PORTA.4, 1, PULSE_LEN ; read the pulse length on rise
Q=0 ; reset counter
ENDIF ;
VALUE=48500/PULSE_LEN ; converts pulse length to count
IF VALUE>1000 THEN DIGIT1=2000 + VALUE/1000 ; Separate
IF VALUE>100 THEN DIGIT2=2000 + (VALUE//1000)/100 ; out the
IF VALUE>10 THEN DIGIT3=2000 + ((VALUE//1000)//100)/10 ; four
IF VALUE>0 THEN DIGIT4=2000 + ((VALUE//1000)//100)//10 ; digits
GOSUB DISPLAY ; show value on the 4 seven seg displays
GOTO MAIN ; do it over
;
DISPLAY: ; reads each digit and then
READ DIGIT1, VALUE ; displays it
PORTA=%00000001 ;
GOSUB SHOW ;
PORTA=%00000010 ;
READ DIGIT2, VALUE ;
GOSUB SHOW ;
PORTA=%00001000 ;
READ DIGIT3, VALUE ;
GOSUB SHOW ;
PORTA=%00100000 ;
READ DIGIT4, VALUE ;
GOSUB SHOW ;
RETURN ;
;
SHOW: ; shows each segment one at a time
Z=%11111110 ; selects one segment at a time
FOR X=1 TO 8 ; do the 8 segments, includes dec point
Y=VALUE ; get value to show
Y.2=1 ; inhibits the decimal point
PORTB=VALUE | Z ; makes value and z select one segment
Z=(Z<<1)+1 ; go to next segment
PAUSEUS P ; pause to show segment
NEXT X ; next segment
PORTA=0 ; clear PORTA
PORTB=0 ; clear PORTB to prevent ghosting
PAUSEUS P ; Pause to show clear
RETURN ; end of subroutine
;
END ; end all programs with end
If we want to turn something on or off at a certain rpm with our controller, all we have to do is add the condition in the loop. For example, if we wanted to have an LED come on at 1750 rpm and go off at 1800 rpm, the code for adding that condition would be as follows. We will use the LED at PORTD.0.
Define the RPM variable and others with the rest of the variables. (See Program 15.4.) Then after the LCDOUT line in the loop add the code segment in Program 15.4.
Program 15.4 Code segment for simple decision making
IF (RPM>1749) AND (RPM<1801) THEN ;
PORTD.0=1 ;
ELSE ;
PORTD.0=0 ;
END IF ;
We now know how to read a frequency and display it either on the LCD or on a set of seven-segment displays.
Here (Program 15.5) is another version of that program that actually counts how many pulses are received in 0.25 seconds, and then multiplies the value by 4 and displays it four times a second. Since it takes the processor 0.25 seconds to count the pulses, you cannot avoid the flicker in a program as written in PBP.
Program 15.5 Code for using the 4 seven-segment displays
; The following are the images of the numbers
; stored in memory before we do anything. These will
; be used in PORTB to READ the number in each digit.
CLEAR ;
DEFINE OSC 4 ;
WRITE 1999, %11111111 ; blank, no LED is lit
WRITE 2000, %00000110 ; the number 0 in our case
WRITE 2001, %10111110 ; the number 1 in our case
WRITE 2002, %01001100 ; the number 2 in our case
WRITE 2003, %00011100 ; the number 3 in our case
WRITE 2004, %10110100 ; the number 4 in our case
WRITE 2005, %00010101 ; the number 5 in our case
WRITE 2006, %00000101 ; the number 6 in our case
WRITE 2007, %10011110 ; the number 7 in our case
WRITE 2008, %00000100 ; the number 8 in our case
WRITE 2009, %00010100 ; the number 9 in our case
;
TRISA=%00010000 ; set up PORTA
TRISB=%00000000 ; set up PORTB
;
P VAR BYTE ; pause variable
X VAR BYTE ; counter variable
Y VAR BYTE ; counter variable
Z VAR BYTE ; counter variable
Q VAR BYTE ; counter variable
PULSES VAR WORD ; pulse length
VALUE VAR WORD ; value of metronome counts
TOTAL VAR WORD ; value of metronome counts
DIGIT1 VAR BYTE ; each digit in display
DIGIT2 VAR BYTE ; each digit in display
DIGIT3 VAR BYTE ; each digit in display
DIGIT4 VAR BYTE ; each digit in display
;
X=0 ; sets the initial value for X
P=400 ; set pause will be in microseconds
TOTAL=0 ; Initialize
Q=0 ; initialize
;
MAIN: ; main loop of program
DIGIT1=-1 ; set all 4 digits to a blank
DIGIT2=-1 ; set all 4 digits to a blank
DIGIT3=-1 ; set all 4 digits to a blank
DIGIT4=-1 ; set all 4 digits to a blank
Q=Q+1 ; counter to take jitter out of display
IF Q<15 THEN ; don’t do anything
ELSE ;
COUNT PORTA.4, 250, PULSES ; read the pulses
Q=0 ; reset counter
ENDIF ;
VALUE=4*PULSES ; converts pulses to count
IF VALUE>1000 THEN DIGIT1= 2000 + VALUE/1000 ; Separate
IF VALUE>100 THEN DIGIT2= 2000 + (VALUE//1000)/100 ; out the
IF VALUE>10 THEN DIGIT3= 2000 + ((VALUE//1000)//100)/10; four
IF VALUE>0 THEN DIGIT4= 2000 + ((VALUE//1000)//100)//10; digits
GOSUB DISPLAY ; show value on the 4 seven seg. displays
GOTO MAIN ; do it over
;
DISPLAY: ; reads each digit and then
READ DIGIT1, VALUE ; displays it
PORTA=%00000001 ;
GOSUB SHOW ;
PORTA=%00000010 ;
READ DIGIT2, VALUE ;
GOSUB SHOW ;
PORTA=%00001000 ;
READ DIGIT3, VALUE ;
GOSUB SHOW ;
PORTA=%00100000 ;
READ DIGIT4, VALUE ;
GOSUB SHOW ;
RETURN ;
;
SHOW: ; shows each segment one at a time
Z=%11111110 ; selects one segment at a time
FOR X=1 TO 8 ; do the 8 segments, includes dec. point
Y=VALUE ; get value to show
Y.2=1 ; inhibits the decimal point
PORTB=VALUE | Z ; makes value and z select one segment
Z=(Z<<1)+1 ; go to next segment
PAUSEUS P ; pause to show segment
NEXT X ; next segment
PORTA=0 ; clear PORTA
PORTB=0 ; clear PORTB to prevent ghosting
PAUSEUS P ; pause to show clear
RETURN ; end of subroutine
;
END ; end all programs with end
Here is another way in which I made the tachometer after I needed a board that could be used as a base for all kinds of devices. I designed this board as a universal all-purpose board for my experiments. I have since improved the board and have been very happy with the results. It saved me much time and sped up the many experiments I had to make to write this book.
Figures 15.12 and 15.13 show the use of a flexible board that supports the use of both 4 seven-segment displays and that of a 2-line-by-20-character display. All the wiring to both types of devices is in place. This eliminates most of the tedious wiring that has to be done on almost all projects. On these cards, all the I/O has been left unconnected
Figure 15.12 Another way to make the tachometer/pulse counter, front.
Figure 15.13 The back of the tachometer/pulse counter. (Note how the custom wiring connects up the I/O only. The rest is pre-wired.)
and has to be wired the way you see fit. On the other hand, the power supply and the basic connections to the CPU are already connected and ready to use. If you plan to build many different instruments and controllers, you should consider designing a board based on this design. If you need just one or a few boards, these boards are available at encodergeek.com.