Automatic Shifting Bike Description and Build...
Transcript of Automatic Shifting Bike Description and Build...
1
Design Details
Our design project is a bicycle that shifts gears automatically based on several inputs; these inputs are rear wheel speed, pedaling direction, and initial chain position. This is accomplished by utilizing the factory derailleur/cable
system but with high-‐torque servo motors operating them instead of hand-‐turned levers. The bicycle uses 4 PIC microcontrollers to process the inputs, control the shifting, and display information to the user. The microcontroller programs were written using PicBasic Pro, and compiled with the Melabs U2 Programmer 4.40 (Refer to Bill of
Materials). The control box houses an LCD display which shows current speed and gear information, and notifies the rider when a shift is occurring. The keypad below the display is used to enter the initial chain position so that the servos can move to the appropriate location before operating the bike. Also found on the control box is a toggle switch used to
supply batter power to the entire system, and a buzzer that sounds during shifts (Refer to Figure 1 component 1). One servo motor controls the position of the derailleur for the pedal sprockets (2); the other servo motor controls the position of the derailleur for the wheel sprockets (3). The appropriate position is determined based on wheel speed
information received from the speed sensor circuit (5). The speed sensor circuit is in constant communication with the control box, so that current speed information can be displayed to the user at all times (6). The shaft encoder (4) tells the control box (1) if the pedals are rotating forward, in reverse, or not rotating. Regardless of speed and current gear,
the control box (1) will prevent the servos (2,3) from moving if the shaft encoder (4) detects that the pedals are not rotating or are rotating backward. If the rider is pedaling forward, then the servos (2, 3) will shift the gears of the bicycle to a comfortable gear ratio with respect to the current speed.
1
6
5
3
4 2
Figure 1-‐ Overview picture of bicycle
2
Figure 2 -‐ Speed Sensor & Related Components
1.) Speed Measurement (Note: all component numbers refer to Figure 2)
To measure the speed of the rear wheel, our sensor of choice was an LED-‐phototransistor pair (1) [note that this component cannot be seen in the photo on the left as it is mounted on the opposite side of the circuit board]; this is
manufactured as a single unit, with both components mounted on a common plane, and an open-‐collector output to which a pull-‐up resistor is connected. When light from the LED is reflected back to the phototransistor, the transistor turns on and pulls the output voltage low. At all other times, the transistor is in cutoff (resembles an open circuit) and
the output voltage is pulled high by the pull-‐up resistor.
Many small strips of reflective tape (2) are placed around the rear wheel’s outer perimeter, all at an equal radius. The sensor is mounted at that same radius, in close proximity to the strips of tape. Thus, as the wheel rotates, the sensor outputs a train of pulses as the phototransistor is repeatedly switched on and off by the bursts of reflected
LED light. The number of pulses generated in one revolution of the wheel is, of course, equal to the number of strips of reflective tape.
Since the raw output of the sensor is not discrete (the output merely approaches 0V as a reflective object is brought closer), the signal must pass through a Schmitt trigger (3) before being sent to the PIC. This ensures a clean,
rectangular (digital) waveform whose transitions can be recognized easily. Also, the sensitivity of the sensor (i.e. how close the reflective tape needs to be in order to turn on the transistor) was found to be adjustable, and proportional to the value of the variable pull-‐up resistor (4) connected at the sensor’s output. This is presumably because a transistor is
essentially a current amplifier; by increasing the resistance in the collector circuit, less base current (or in this case, LED light) is required to drive the transistor into its saturation region. Therefore, the final resistance value we chose was not arbitrary; rather, it is the optimal value which produce reliable pulses from wheel rotation while remaining unaffected
by ambient light.
Having conditioned the sensor output into an appropriate form, the last step is converting that output into a numerical speed (in miles per hour). This function is performed by a PIC devoted to the speed sensor subsystem (hereinafter referred to as PIC #4, in accordance with the convention established on our wiring diagram). PIC #4 counts
the number of pulses received from the speed sensor within a short period of time (we found an appropriate interval to be 0.25 sec.). This count is then converted to a speed in mph using the following formula:
1 2
3
4
3
€
speedmph =pulses0.25sec
×1rev
60pulses×80inrev
×1mi
63,360in×3600sechr
Here the parameters that must be known are pulses per revolution (number of strips of reflective tape) and the distance
per revolution (wheel circumference). The above calculation is performed at the end of every 0.25 sec sampling interval, after which it is sent to other subsystems as they require it.
This process of counting pulses, converting them to speed, and communicating the speed to other PICs requires the full attention of PIC #4 at all times; no other tasks can be performed while the PIC is counting pulses. Because of
this, we found it necessary to devote an entire PIC to the task of managing the speed sensor and its surrounding assembly. In fact, a dedicated PIC turned out to be necessary for most of our key components in this project
Figure 3 -‐ Servos
2.) Gear Shifting (Note: all component numbers refer to Figure 3)
To accomplish the function of automatic shifting, two high-‐torque RC servo motors (1,2) are used. As is the case
on most modern bicycles, there are two sets of gears to be shifted: one located on the pedals, and the other on the rear wheel. Since ours is a 15-‐speed bike, these gear sets each contain 3 and 5 gears, respectively. To interface the gear sets with the servos, a pulley (3) is attached to each servo. Around each pulley is wrapped one of the bicycle’s original shift
cables (4); of course, the choice between the two shift cables determines which gear set is controlled by that particular servo. With this arrangement, we were able to retain most of the bicycle’s original shifting hardware; it was only necessary to remove the portion of the system from the shift cables up to the handlebars, where the manual shifting
1 2
3 4
4
equipment was formerly located. Shifting gears is accomplished by indexing each servo to the appropriate position, which then pulls the shift cable by the exact amount needed to shift to the desired gear.
Each RC servo is moved through its range of motion by a single digital control signal (power and ground
connections are also required). The servo’s position is determined by the duty cycle of that control signal. Therefore, any time a servo is sent a signal of a known duty cycle, the position of the servo will also be known; these servos have an internal feedback system which ensures this is always the case. All we had to do was experimentally determine the duty
cycle (or equivalently pulse width, since the control signal is of a constant frequency) needed to move each servo to the positions corresponding to every available gear on its associated gear set. This was done using the following circuit:
Figure 4 -‐ Servo Test Circuit
The circuit in Fig. 4 consists of a dual NAND switch debounce circuit (1) and a “one-‐shot” 555 timer circuit (2). When the push button is depressed, a single pulse is sent out on the output pin of the 555. The length of the pulse is set
using a 100k trim pot, and can be adjusted until the appropriate pulse length is found for each gear. We found these servos to be unique in that the digital controller on board each servo will read a control pulse length, store it, move the servo head to the corresponding position, and actively hold that position for approximately 1.5 seconds. Therefore, we
had some liberty in how often we needed to send a pulse to each servo. The servos were also found to have an initialization period in which they require approximately 15 pulses on the signal line before being capable of normal operation. This requires knowing which gear the bike is in upon system power-‐up, so that the initialization pulses do not
1
2
5
inadvertently shift the bicycle to a new gear. We will send this initial gear information serially to PIC #2 from another PIC, as will be explained in a later section.
Having found the pulse widths corresponding to all possible gears, the overall gear settings of the bicycle can
then be adjusted or maintained by varying the pulse width of each servo’s control signal among these experimentally determined values. The values are different for each servo (i.e. gear 3 on the pedals requires a different pulse width than gear 3 on the wheel) due to the dynamic differences between the two gear sets, as well as manufacturing
variability which causes the two servo motors (despite both being the same model) to behave slightly differently. Also, in general a different pulse width is required to upshift into a certain gear versus downshifting into that same gear.
To perform all of this control signal manipulation, another PIC microcontroller is utilized (this one will be referred to as PIC #2). As mentioned before, the first function PIC #2 performs is receiving the initial gear values (this
only occurs once, upon power-‐on). Then, PIC #2 enters its main program loop. Its first task in that loop is to receive the current speed of the bicycle from PIC #4. Next, a sequence of logic is executed which compares the speed to the current gear and determines whether or not that gear is ideal. The principle is that each gear on the bicycle has a “comfortable”
speed range associated with it; when the speed comes to be above or below the limits of that range, the bicycle should respectively upshift or downshift. The logic in PIC #2 has a separate section corresponding to each gear. Within each section, the speed is compared to a different range, and action is taken based on the result of that comparison.
In order to elaborate further, it is necessary to divulge the information structures used by PIC #2 to shift and
maintain gears. Since the PIC must keep track of the bicycle’s current gear at all times, the gear settings are stored numerically in a variable called “gear” and updated each time a shift occurs. Although there are really two separate gear settings (one for each gear set), they are stored and manipulated as a single base-‐ten number for convenience’s sake.
For example, gear 3 on the pedals and gear 2 on the wheel corresponds to “gear = 32”. Also, the pulse widths required by each servo to maintain a certain gear setting are stored using a pair of variables, one for each servo/gearset. These
variables are called “T1_high” and “T2_high”.
Each time the logic in PIC #2 determines that a shift is needed, the three variables mentioned above are all updated to reflect the gear which is being shifted into. Also, a shift-‐indicator line connected to PIC #1 (which has not yet been discussed) is set high, as a means of communicating that a shift is about to occur. This interface will be explained
in a following section, but suffice it to say that the purpose is to give the rider a cue that the bike is shifting. Finally, the PIC sends out a pulse of width “T1_high” to servo 1, and a pulse of width “T2_high” to servo 2, thus shifting the bicycle to the new gear. This is the end of the PIC #2 program loop, after which it returns to the beginning and repeats all of
these functions.
The next time through the loop, PIC #2 will find that the current gear is now appropriate for the speed, and no shift will occur. Thus, the variables “gear”, “T1_high” and “T2_high” will remain at their previous values. The servos will again be sent pulses; since the lengths of these pulses will not have changed, the current gear will be held until it is
necessary to shift once more. As was the case for the speed sensor, it should be apparent that this shifting functionality could not have been accomplished with anything less than a single dedicated PIC; not only do the servos require a relatively continuous stream of pulses, but the speed-‐to-‐gear comparisons must be done often in order to ensure that
the bike is adequately responsive.
6
Figure 5 -‐ Shaft Encoder
3.) Directional Sensing of Pedal Rotation (Note: all component numbers refer to Figure 5)
The preceding discussion of the conditions under which a shift will occur is, admittedly, not entirely complete. At first glance it may seem that the current speed and gear should be the sole determining factors of whether or not to
shift. However, there is one additional element which cannot be ignored in the design of a practical automatic-‐shifting bike, and that is pedal rotation. If the bicycle attempts to shift while the rider is not pedaling (or even worse, pedaling backward), chain misalignment will almost certainly occur, requiring the rider to dismount and reset the entire system.
Therefore, a crucial component of our project is a subsystem which determines the current condition of the pedals at all times: forward motion, backward motion, or no motion.
The heart of this subsystem is an absolute rotary encoder, whose two digital outputs (1,2) assume four different states per revolution of its shaft (3); the encoder is driven by the pedals via a belt drive system (4). When the state
changes, the direction of pedal rotation can be ascertained by comparing the new state to the previous state. The pedals can be assumed stationary if the state does not change for a certain amount of time. To execute this logic, an additional PIC (PIC #3) was added to our design. Once again, the use of a dedicated PIC is justified by the all-‐consuming
nature of this task. It is impossible to do anything else while continuously analyzing the states of the shaft encoder.
PIC #3 runs in a short main program loop, within which it remains until the shaft encoder changes state and triggers an interrupt. The main loop first reads the two shaft encoder outputs and stores their current state in two variables, then executes a very short pause. Both of these instructions are enclosed within a “for” loop, and will repeat a
number of times determined by the maximum value of the counter variable (which is specified within the “for” loop initialization). Since reading the state of the shaft encoder takes under a microsecond, the execution time of a single iteration of the “for” loop is determined almost entirely by the length of the pause. As such, the time it takes the entire
main loop to execute can be considered equal to the length of the pause multiplied by the maximum value of the counter variable.
1 2
3
4
7
With this simple main program (and without yet analyzing the states of the shaft encoder), we are equipped to detect the presence or absence of pedal rotation. Since the main program loop will continue to execute until the shaft
encoder changes state, we can use the total length of time the main loop takes to execute as the “time-‐out” period; if no encoder motion occurs within this period, the pedals can be judged to be stationary. As discussed above, the “time-‐out” period can be controlled by adjusting both the length of the pause and the maximum value of the “for” loop’s counter
variable; we found 0.5 seconds to be the optimal time-‐out period. The instruction following the “for” loop is one which sets a “shift-‐enable” line low. This line is connected to PIC #2, and is the only interface PIC #3 has with any of the other PIC’s. PIC #2 will not perform any of its speed-‐gear comparisons unless this line is high (this is accomplished by enclosing
the comparisons within an “if (shift-‐enable == 1)” statement). Therefore, if the shift-‐enable line is low, PIC #2 cannot shift gears, since there is no other section of the program in which T1_high and T2_high (see the preceding section) can be changed. However, PIC #2 will still send out pulses to the servos in order to maintain the current gear settings.
Of course, if the shaft encoder does change state within the 0.5 second time-‐out period, then the “shift-‐enable”
line is not set low, because an interrupt will occur before that instruction is reached. Upon being interrupted, the program finishes execution of its current instruction, then immediately branches to the interrupt service routine. The interrupt service routine first reads the new state of the shaft encoder; the fact that an interrupt occurs guarantees that
this will be different from the old state which was being continuously read in by the main program. Then, the new state is compared to the old state; although the necessary code is lengthy, the concept is simple. For any of the four possible “new” states, there are two values which the previous state could have been (assuming a state is never skipped, which
we found to be a reasonable assumption). One of those values corresponds to forward rotation, and the other to backward rotation. For forward rotation, the shift-‐enable line is set high; for backward rotation, it is set low. The final task of the interrupt service routine is to reset the counter variable used in the main program loop. That way, when
program control is returned to the main loop, the whole process starts over again.
Figure 6 -‐ User Interface Components (Outside of Control Box)
4.) Display and User Interface (Note: all component numbers refer to Figure 6)
1
2 3
5 6 7 8
9
8
Thus far, only the core operation of our system has been discussed, with no attention paid to how it interacts with the user. However, a major component of our project is the control box, which contains an LCD screen (1), keypad
(2), and buzzer (3) for user interface. The control box also contains all four PIC’s (4-‐8) and all other project circuitry. The keypad is used only once, upon system power-‐on, to input the initial gear settings of the bicycle. This is necessary because the system “forgets” what gear it is in when turned off, and so a new reference point for the shifting maps must
be established (a.k.a. the initial gear must be input) every time the system is turned on. We contemplated storing the gear settings in flash memory instead, but decided against it. With our method, should any type of mechanical or electrical malfunction occur, the system can easily be reset and started in any desired gear setup.
The fourth PIC in our design (referred to as PIC #1) manages the LCD display, keypad, and buzzer. Immediately
after system power-‐on, PIC #1’s program displays a message on the LCD prompting the user to enter the first gear (this corresponds to the gear set located on the pedals). It then receives this gear value serially from a keypad encoder IC (9), whose row and column lines are connected directly to the keypad. This greatly reduces the I/O requirements on PIC #1,
since the keypad encoder needs only one line to communicate the value of the pressed key to the PIC; without this component, the seven row and column lines from the keypad would need to be connected directly to the PIC, and additional programming would be required to continuously scan the keypad.
Once PIC #1 receives the first key press value, it checks the value for validity. If the received value does not
correspond to one of the possible choices for that gear set (the only options in this case are 1, 2, and 3), the LCD will inform the user that an invalid gear was selected, and the user will be re-‐prompted to enter the first gear. The same will happen if any key outside the range 1 through 5 is pressed when prompted for the second gear. Since the data from the
keypad encoder will be in ASCII form rather than decimal (e.g. $30 = 0, $31 = 1, etc.), each value must be converted upon receipt by the PIC; then, the two gear settings are combined and stored in a variable called “gear”, much like in the PIC #2 program.
PIC #1 now needs to send PIC #2 the initial gear settings so that PIC #2 can initialize the servos to the correct gears. This communication is performed using a standard serial interface with a separate handshake line, a method which is widely used throughout our project. The technical details of this interface are beyond the scope of this
discussion, but are well documented in the commented code provided. After PIC #1 has sent the initial gear settings, it enters into its main program loop. This loop alternately displays current gear and speed information on the LCD display, switching from one to the other about every 2.5 seconds. The speed is refreshed 10 times within these few seconds as
new serial data is provided from PIC #4, so the speed information is always very up-‐to-‐date. On the other hand, the current gear settings are only received once prior to their 2.5 second display, and do not need to be refreshed during this period. This is because an interrupt will occur whenever the gears are shifted.
In the previous section concerning the Gear Shifting subsystem, it was mentioned that PIC #2 sets a shift-‐
indicator line high when a shift is occurring. This line is connected to PIC #1 and is used to trigger an interrupt, the purpose of which is to alert the rider that the bicycle is shifting. As such, the first instruction in the interrupt service routine outputs the message “Now Shifting” to the LCD display. Then, a piezoelectric buzzer is activated in such a way
that it plays a short musical phrase. The details of this are worth elaborating on briefly.
When a digital signal of a certain frequency is applied to our buzzer, it produces a tone of that same frequency. Thus, by varying the signal’s frequency between frequencies which correspond to certain musical notes, it is possible to generate music with the buzzer. Within PIC #1’s interrupt service routine, there is a short subroutine which plays a note
for a preset (short) duration of time by toggling the buzzer between high and low a certain number of times. The length of time the buzzer is high (or low) is determined by the variable “note”, which then also defines the frequency of the
9
tone emitted by the buzzer. The variable “note” can be redefined prior to each subroutine call so that a different tone is produced each time; using this technique, we configured the buzzer to play a series of arpeggios (which are notes of a
chord played in rapid succession rather than simultaneously) of a short chord progression: C major / A minor / D minor / G major / C major. This entire process takes less than a second, after which the interrupt service routine ends and control is returned to the main program.
Construction Instructions
Servo Mounting Tray The mounting tray for the servos was constructed using a plate of 3/16” 6061-‐T6 aluminum bar. Slots for the servos
were milled out of this piece to a depth of approximately 1/8”. This was sufficient to prevent the servo from moving in the direction of the cable tension. Two stiffeners in the form of aluminum angle were riveted to the bottom of the 3/16” plate to strengthen it after the removal of more than half its thickness in the servo slots. Two holes were then
drilled in the center of the 3/16” plate in locations which corresponded to the pre-‐existing threaded holes on the bicycle frame originally intended to mount the water bottle holder. Dimensions for these holes will vary depending on the bicycle being used and will need to be measured. In order to hold the servos down, slots were cut in the aluminum
angle stiffeners directly below each servo mounting slot to allow steel wire to be wrapped around the body of the servo as the servo itself has no mounting lugs. Stainless steel aircraft safety wire was used for this application. Figure 7 shows
the mounting model for the servo motors as described above.
Shaft Encoder Mount and Drive Mechanism In order to drive the shaft encoder, a belt and pulley system was implemented. It was desired that the shaft encoder spin faster than the bicycle pedal shaft, thus the pulley attached to the pedal is twice as large as the one attached to the encoder to achieve a 2:1 gear ratio. The pulleys are custom made by welding washers together. In order to achieve a
Figure 7-‐ Servo Mounting Model
10
track for the belt to run in, a smaller diameter washer is sandwiched between two larger diameter washers. The pedal pulley is 3” in diameter, and it is bolted onto the back side of the pedal opposite of the chain sprocket assembly. The
encoder pulley is 1.5” in diameter and is welded to a shaft supported between two ball bearings. The shaft of the encoder is interfaced to the pulley shaft using a flexible rubber tube in order to accommodate any axis misalignment. The mounting framework for the pulley bearings and shaft encoder is made from 1/16” by 1/2" aluminum strip bent to
shape and riveted together. The mounting framework is attached to one of two pieces of aluminum angle which can be clamped together on either side of the bicycle frame with a central screw as can be seen in the diagrams below. Figure 8, 9, and 10 show the model of the shaft encoder mounting mechanism.
Figure 8-‐Shaft Encoder Mounting Model View 1
12
Project Box In this project, a 6x4x2 box was used to house the LCD display, the keypad, the buzzer, and the on/off switch (all of
which can be seen externally). Holes for these components were cut in the top of the box (the sizes of these holes will vary according to the specific component used). Refer to the picture of the top view of the project box to see the orientation and assembly. The copper clad circuit board (which contains the four PICs), the keypad circuit, and the
batteries were put inside the project box (which can not be seen externally). The inside components rested securely on a sheet of foam rubber to help prevent damage to the components. The project box was put on the bike’s handle bars by drilling holes in the bottom of the box and using cable ties to secure the box to the handle bars.
Circuit Boards The circuit board for the PICs, the keypad, and the speed circuit board should be designed according to the schematic/ wiring diagram provided. The circuits should be laid out with electromagnetic interference taken into account, which
means that it is important to minimize the size of the loops created by circuit wiring.
Rear Wheel Speed Sensor The speed sensor circuit (including the photo sensor, Schmitt trigger, and sensitivity potentiometer) was constructed on a separate circuit board such that it could be mounted next to the rear wheel. Three wires (power, ground, and signal)
were used to connect it to the main control circuit board. These three wires were twisted together in order to minimize electromagnetic interference issues. The photo sensor was triggered by patches of reflective tape mounted around the inner circumference of the rim of the rear wheel. The strips of tape were mounted on a ring of plywood whose outer
diameter was equal to the inner diameter of the rim. The width of the ring was 2 inches in order to accommodate a 1 inch square of tape. The ring was attached to the wheel using adhesive cable tie mounts with cable ties running through the individual spokes (as shown in Figure 11 below).
Reflective tape squares were placed around the circumference of the ring at even intervals. One square was placed in
line with each of the 36 spokes, making it easy to establish the spacing. Once the ring was attached to the wheel, the sensor circuit board was mounted on the frame of the bicycle such that the photo transistor tracked the plywood ring (as can be seen in the pictures).
Figure 11-‐Cable Tie Mounting Diagram
T:\students\UNGRAD\ME\ben1984j\shared\drop-box\PIC 1-1202 (2).pbp
' Program for PIC #1 - Display and Interface
define OSC 8OSCCON.4 = 1 : OSCCON.5 = 1 : OSCCON.6 = 1
ANSEL = 0
INTCON = $90 ' Interrupt on rising edge of RB0; thisOPTION_REG = $FF ' allows the program to deviate from ' normal operation when PIC #2 ' indicates that a shift is about to occuron interrupt goto shift_alert
speed_ser41 var PORTB.7 ' The current speed of the bike speed_hs41 var PORTB.6 ' (in mph x 10) will be received seriallyspeed var byte ' from PIC #4 (again, a handshake line ' is required in addition to the data line) ' and stored in a variable. speed_1 var byte ' It is then necessary to split the speedspeed_0 var byte ' so that the decimal part (tenths digit) ' is stored in one variable (speed_0) and ' the integer part is stored in another ' variable (speed_1). If these were not ' two separate variables, it would be ' impossible to display a decimal point ' in between them.
gear_ascii var byte ' Stores the ASCII value for each gear as ' received from the keypad encoder. These ' ASCII values will be converted to decimal ' and used to determine the initial gear ' settings.
gear var byte '"gear" is a single base-ten numbergear_1 var byte ' which contains information about the currentgear_2 var byte ' state of both gears. It is advantageous to ' combine both gears into a single number ' to simplify the serial data transfer, but for ' display on the LCD screen "gear" must be split ' into its components "gear_1" and "gear_2".
gear_ser21 var PORTB.2 ' These two pins are used to serially gear_hs21 var PORTB.1 ' transmit information from PIC #2 ' regarding what gear the bike is in. ' "gear_ser" is the data line, while ' "gear_hs" is a handshake line used ' to synchronize the serial ' communication.
key_mode con 0 ' 2400 baud serial communication is ' used for the keypad encoderser_mode con 2 ' 9600 baud serial communication is ' used for everything else
shifting_now21 var PORTB.0 ' This line will be set high by PIC #2 when ' a shift is about to occur.buzzer var PORTB.4 ' This output will be used to sound a ' buzzer which alerts the rider to an
Page 1 of 7 12/9/2011 7:47 AM
T:\students\UNGRAD\ME\ben1984j\shared\drop-box\PIC 1-1202 (2).pbp
' impending shift.key_value var PORTB.5 ' This pin is connected to the keypad ' encoder
i var byte ' These are counter variables used inj var word ' various loops throughout the program.k var byte
d7tone con 426 ' tones played by buzzerc7tone con 478b6tone con 506a6tone con 568g6tone con 638f6tone con 716e6tone con 758d6tone con 851c6tone con 956b5tone con 1012g5tone con 1276d5tone con 1703
ontime con 30 ' defines how long each tone sounds
l var word ' variables used in note play subroutinenote var word
' Main program:
disable
low gear_hs21low speed_hs41
pause 500 ' Allow LCD display to power up
enter1:lcdout $FE, 1, "Please enter the"lcdout $FE, $C0, "first gear: " ' The rider must input the initial settingsserin key_value, key_mode, gear_ascii ' of the gears every time our system is ' powered on, so that a starting point ' for the shifting maps can be established.if (gear_ascii == $31) then gear_1 = 2else if (gear_ascii == $32) then gear_1 = 3else lcdout $FE, 1, "Invalid gear." pause 1000 goto enter1endifendif
lcdout dec gear_1 pause 1000 enter2: lcdout $FE, 1, "Please enter the"
Page 2 of 7 12/9/2011 7:47 AM
T:\students\UNGRAD\ME\ben1984j\shared\drop-box\PIC 1-1202 (2).pbp
lcdout $FE, $C0, "second gear: " serin key_value, key_mode, gear_ascii if (gear_ascii == $31) then gear_2 = 2else if (gear_ascii == $34) then gear_2 = 4else if (gear_ascii == $35) then gear_2 = 5else lcdout $FE, 1, "Invalid gear." ' For both gear 1 and gear 2, only pause 1000 ' the values corresponding to gears goto enter2 ' that are actually used are allowed.endifendifendif lcdout dec gear_2 pause 1000
gear = (gear_1 * 10) + gear_2
high gear_hs21pauseus 10serout gear_ser21, ser_mode, [gear] ' sends initial gear values to PIC #2 low gear_hs21
TRISB.1 = 1 ' reconfigures gear_hs21 from outputpauseus 50 ' to input, gives PIC 2 time to do ' the opposite
lcdout $FE, 1, "Initializing"lcdout $FE, $C0, "servos…"
while (gear_hs21 == 0) ' Waits for signal from PIC #2 indicatingwend ' that servo initialization is done
pauseus 50 ' After PIC #2 has stopped using gear_hslow gear_hs21 ' as an output, set it low so PIC #2 ' won't later think PIC #1 is ready to ' receive data if it really isn'tenable main:
for i = 1 to 10 disable high speed_hs41 ' Receives current speed serin speed_ser41, ser_mode, speed ' information from PIC #4 low speed_hs41 enable
speed_1 = speed / 10 'Separates the speed into speed_0 = speed // 10 ' its integer and decimal ' components
Page 3 of 7 12/9/2011 7:47 AM
T:\students\UNGRAD\ME\ben1984j\shared\drop-box\PIC 1-1202 (2).pbp
lcdout $FE, 1, "Current speed:" ' Displays speed on screenlcdout $FE, $C0, dec speed_1, ".", dec speed_0, " mph"next i
disablehigh gear_hs21 ' Receives current gearserin gear_ser21, ser_mode, gear ' information from PIC #2low gear_hs21enable
gear_1 = gear / 10gear_2 = gear // 10lcdout $FE, 1, "Current gear:" ' Displays gear info on screenlcdout $FE, $C0, " ", dec gear_1, "-", dec gear_2 ' for 2.5 secondsfor j = 1 to 250pause 10next j
goto main
disable
shift_alert: ' When a shift is about to occur, thelcdout $FE, 1, "Now shifting" ' program breaks out of its speed/gear ' display loop and informs the user ' via a message and buzzer. After the ' shift, the program returns to its ' normal operation.
goto intmain
noteplay:for l = 1 to ontime high buzzer pauseus note low buzzer pauseus notenext lreturn
intmain:
note = c7tone ' plays a short series of musicalgosub noteplay ' arpeggiosnote = g6tonegosub noteplaynote = e6tonegosub noteplaynote = g6tonegosub noteplaynote = c7tonegosub noteplaynote = g6tonegosub noteplaynote = e6tonegosub noteplaynote = g6tone
note = c7tone
Page 4 of 7 12/9/2011 7:47 AM
T:\students\UNGRAD\ME\ben1984j\shared\drop-box\PIC 1-1202 (2).pbp
gosub noteplaynote = g6tonegosub noteplaynote = e6tonegosub noteplaynote = g6tonegosub noteplaynote = c7tonegosub noteplaynote = g6tonegosub noteplaynote = e6tonegosub noteplaynote = g6tone
note = c7tonegosub noteplaynote = a6tonegosub noteplaynote = e6tonegosub noteplaynote = a6tonegosub noteplaynote = c7tonegosub noteplaynote = a6tonegosub noteplaynote = e6tonegosub noteplaynote = a6tone
note = c7tonegosub noteplaynote = a6tonegosub noteplaynote = e6tonegosub noteplaynote = a6tonegosub noteplaynote = c7tonegosub noteplaynote = a6tonegosub noteplaynote = e6tonegosub noteplaynote = a6tone
note = d7tonegosub noteplaynote = a6tonegosub noteplaynote = f6tonegosub noteplaynote = a6tonegosub noteplaynote = d7tonegosub noteplaynote = a6tonegosub noteplaynote = f6tone
Page 5 of 7 12/9/2011 7:47 AM
T:\students\UNGRAD\ME\ben1984j\shared\drop-box\PIC 1-1202 (2).pbp
gosub noteplaynote = a6tone
note = d7tonegosub noteplaynote = a6tonegosub noteplaynote = f6tonegosub noteplaynote = a6tonegosub noteplaynote = d7tonegosub noteplaynote = a6tonegosub noteplaynote = f6tonegosub noteplaynote = a6tone
note = d7tonegosub noteplaynote = b6tonegosub noteplaynote = g6tonegosub noteplaynote = b6tonegosub noteplaynote = d7tonegosub noteplaynote = b6tonegosub noteplaynote = g6tonegosub noteplaynote = b6tone
note = d7tonegosub noteplaynote = b6tonegosub noteplaynote = g6tonegosub noteplaynote = b6tonegosub noteplaynote = d7tonegosub noteplaynote = b6tonegosub noteplaynote = g6tonegosub noteplaynote = b6tone
note = c7tonegosub noteplaynote = c7tonegosub noteplaynote = c7tonegosub noteplaynote = c7tone
INTCON.1 = 0
Page 6 of 7 12/9/2011 7:47 AM
T:\students\UNGRAD\ME\ben1984j\shared\drop-box\PIC 1-1202 (2).pbp
j = 250resume enable
end
Page 7 of 7 12/9/2011 7:47 AM
T:\students\UNGRAD\ME\ben1984j\shared\drop-box\PIC2-1203-gearchange.pbp
' Program for PIC #2-Shifting Gears w/servos
define OSC 8OSCCON.4 = 1 : OSCCON.5 = 1 : OSCCON.6 = 1ANSEL = 0
'Define Ports
gear_ser21 var PORTB.2 'Communication to PIC 1 regarding which gear_hs21 var PORTB.1 'gear PIC 2 is in (so current gear can be ' displayed on LCD screen)
shift_enable32 var PORTA.1 'From PIC 3, tells PIC 2 if pedals are moving. 'When set high, PIC 2 is allowed to shift.
shifting_now21 var PORTA.0 'Set high when a shift is about to occur. This ' line is connected to PIC #1 and allows it ' to indicate to the rider that a shift is ' happening.
Servo_1 var PORTB.7 ' Send out pulse to set angle for the servo Servo_2 var PORTB.6 ' motors 1 and 2, which are used to shift gears speed_ser42 var PORTB.5 ' Receives current speed from PIC #4 speed_hs42 var PORTB.4 ' Handshake line used to synchronize ' communication with PIC 4 'Define Variables
ser_mode con 2 ' 2400 baud mode is used for all serial ' communication
speed var byte 'Variable for current speed
gear var byte 'Variable for current geargear_1 var bytegear_2 var byte
T con 30000 ' Period of servo pulses
T1_high var word ' These variables represent the length of theT2_high var word ' pulses sent to each servo
i var byte ' Counter variables used in loops which sendj var byte ' out the initialization pulses to the servos
n1gear2t con 1067 ' Pulse widths for Servo 1 gearsn1gear3t con 1921
n2gear2t_up con 898 ' Pulse widths for Servo 2 gears n2gear2t_down con 754 n2gear4t_up con 1626n2gear4t_down con 1303n2gear5t_up con 1900
Page 1 of 5 12/9/2011 7:47 AM
T:\students\UNGRAD\ME\ben1984j\shared\drop-box\PIC2-1203-gearchange.pbp
low shifting_now21 ' Initialize output signals lowlow speed_hs42
pause 100while(gear_hs21==0)wend serin gear_ser21, ser_mode, gear ' Receive initial gear info from PIC1
pauseus 10 ' Wait for PIC 1 to stop using gear_hs21low gear_hs21 ' as output
gear_1 = gear / 10gear_2 = gear // 10
if (gear_1 == 2) then ' Set initial pulse widths for servos, based T1_high = n1gear2t ' on initial gear settingsendifif (gear_1 == 3) then T1_high = n1gear3tendif
if (gear_2 == 2) then T2_high = n2gear2t_upendifif (gear_2 == 4) then T2_high = n2gear4t_upendifif (gear_2 == 5) then T2_high = n2gear5t_upendif
for i = 1 to 15 ' Initialize servoshigh Servo_1 pauseus T1_high low Servo_1pauseus T-T1_highnext ifor j = 1 to 15high Servo_2pauseus T2_highlow Servo_2pauseus T-T2_highnext j
high gear_hs21 ' Informs PIC #1 that servo initializationpauseus 10 ' is donelow gear_hs21
TRISB.1 = 1 ' reconfigure gear_hs21 as input
pauseus 100 ' give PIC #1 time to set gear_hs21 low
main: high speed_hs42 serin speed_ser42, ser_mode, speed ' Receive current speed from PIC 4 low speed_hs42
Page 2 of 5 12/9/2011 7:47 AM
T:\students\UNGRAD\ME\ben1984j\shared\drop-box\PIC2-1203-gearchange.pbp
if (gear_hs21==1) then ' If PIC 1 is ready to receive speed data, pause 1 ' send it serout gear_ser21, ser_mode, [gear] endif
' The following "if" statements determine whether the current gear is' appropriate for the current speed of the bike, based on pre-determined ideal' speed ranges for each gear. If the gear is not appropriate for the speed, an' upshift or downshift will be performed by changing the duty cycle of the' pulse(s) sent to the servo motor(s).
if (shift_enable32 == 1) then ' Only shift if pedaling forward if (gear==22) then if (speed > 50) then goto shift32up endif goto done endif if(gear == 25) then goto shift24up endif
if (gear==32) then if (speed > 100) then goto shift24up endif if (speed < 30) then goto shift22down endif goto done endif
if (gear==24) then if (speed > 150) then goto shift35up endif if (speed < 80) then goto shift32down endif goto done endif
if (gear == 34) then goto shift35up endif
if (gear==35) then if (speed < 125) then goto shift24down endif goto done endifendifgoto done
Page 3 of 5 12/9/2011 7:47 AM
T:\students\UNGRAD\ME\ben1984j\shared\drop-box\PIC2-1203-gearchange.pbp
shift22up: T1_high = n1gear2t T2_high = n2gear2t_up gear=22 goto shifting shift22down: T1_high = n1gear2t T2_high = n2gear2t_down gear=22 goto shifting shift32up: T1_high = n1gear3t T2_high = n2gear2t_up gear=32 goto shifting shift32down: T1_high = n1gear3t T2_high = n2gear2t_down gear=32 goto shifting shift24up: T1_high = n1gear2t T2_high = n2gear4t_up gear=24 goto shifting shift24down: T1_high = n1gear2t T2_high = n2gear4t_down gear=24 goto shifting
shift35up: T1_high = n1gear3t T2_high = n2gear5t_up gear=35 goto shifting
shifting: ' This portion of the program is only high shifting_now21 ' executed if the above logic determines pauseus 1 ' that a shift is necessary. The signal low shifting_now21 ' "shifting_now" informs PIC 1 that a shift ' is about to occur.done:
high Servo_1 ' Whether or not gear was changed, sendpauseus T1_high ' pulses to servos so they will move tolow Servo_1 ' or stay in the correct positions.pauseus T-T1_high
high Servo_2
Page 4 of 5 12/9/2011 7:47 AM
T:\students\UNGRAD\ME\ben1984j\shared\drop-box\PIC2-1203-gearchange.pbp
pauseus T2_highlow Servo_2pauseus T-T2_high goto mainend
Page 5 of 5 12/9/2011 7:47 AM
T:\students\UNGRAD\ME\ben1984j\shared\drop-box\PIC3-1202 (2).pbp
'Program for PIC #3 - Shaft Encoder
define OSC 8OSCCON.4 = 1 : OSCCON.5 = 1 : OSCCON.6 = 1ANSEL=0
OPTION_REG = $FFINTCON=$90
on interrupt goto statechange ' When the shaft encoder outputs change state, ' the program will stop its normal operation ' and execute a service routine which compares ' past and present values of the shaft encoder ' outputs.
shaftenc_1 var PORTB.1 ' The two shaft encoder outputs are connectedshaftenc_0 var PORTB.0 ' to these pins.
oldval_1 var byte ' These two variables are used to store the past oldval_0 var byte ' state of the shaft encoder (prior to it changing).newval_1 var byte ' These two variables are used to store the newnewval_0 var byte ' state of the shaft encoder (after it changes).
shift_enable32 var PORTA.1 ' This line will be asserted high by the ' program if it is determined that pedal rotation ' is in the forward direction. Otherwise, the line ' will be low. PIC #2 will be connected to this ' line, and will not shift unless it is high.
time var word ' This variable is used in the main program loop ' to keep track of elapsed time, measured in 10 ' microsecond increments. At the end of each ' 10us increment, the program checks for ' interrupts, and if none have occurred "time" is ' incremented and the process repeats. Once the ' elapsed time reaches a preset value determinedmax_time con 1200 ' by "max_time" (this only happens if the shaft ' encoder doesn't change state during that time) ' the pedals can be assumed to be stationary, and ' the shift enable signal will be set low.
main: time = 0 while (time != max_time)' Waits up to max_time milliseconds for the ' shaft encoder outputs to change state.
disable ' Interrupts must not be serviced during these ' two assignment statements, in which the ' current values of the shaft encoder inputs are oldval_1 = shaftenc_1' stored as variables for later processing. If an oldval_0 = shaftenc_0' interrupt occurs while the first statement is ' being executed, the second shaft encoder input ' will not yet have been updated, and thus the ' pair of digits oldval_1 and oldval_0 will contain enable ' misleading position information. pause 1 ' After saving the values of the shaft encoder time = time + 1 ' outputs, the program waits for 1 ms to see if ' these outputs will change state. If they do, ' an interrupt will occur and the direction of ' rotation can be established. Otherwise, the ' process repeats. wend
Page 1 of 2 12/9/2011 7:11 AM
T:\students\UNGRAD\ME\ben1984j\shared\drop-box\PIC3-1202 (2).pbp
low shift_enable32 ' If the shaft encoder hasn't changed state by the ' end of this loop, the pedals can be considered to ' be stationary, so a shift should not be allowed ' to occur.goto main
disable
statechange: INTCON.1 = 0 time = 0 newval_1 = shaftenc_1 'Read new state of shaft encoder.newval_0 = shaftenc_0
if (newval_1 == 0) && (newval_0 == 0) then 'By comparing the new state of if (oldval_1 == 1) && (oldval_0 == 0) then'the shaft encoder to its old high shift_enable32 'state, the direction of pedal else 'rotation can be determined. If the low shift_enable32 'pedals are moving forward it is endif 'OK to shift.endifif (newval_1 == 0) && (newval_0 == 1) then if (oldval_1 == 0) && (oldval_0 == 0) then high shift_enable32 else low shift_enable32 endifendifif (newval_1 == 1) && (newval_0 == 1) then if (oldval_1 == 0) && (oldval_0 == 1) then high shift_enable32 else low shift_enable32 endifendifif (newval_1 == 1) && (newval_0 == 0) then if (oldval_1 == 1) && (oldval_0 == 1) then high shift_enable32 else low shift_enable32 endifendif
resume
enable
end
Page 2 of 2 12/9/2011 7:11 AM
T:\students\UNGRAD\ME\ben1984j\shared\drop-box\PIC4-1202 (2).pbp
'Program for PIC #4 - Speed Sensor
define OSC 8OSCCON.4 = 1 : OSCCON.5 = 1 : OSCCON.6 = 1ANSEL = 0
sensor var PORTB.0 ' The speed sensor is connected to ' this input.
speed var byte ' Based on how many pulses ' occurred during a certain time interval, ' the speed in mph can be computed. The ' variable "speed" contains this speed in ' mph, multiplied by 10 (this is done to ' get rid of the decimal place - explained ' in more detail elsewhere).speed_calc var word ' It is necessary to use a word, rather than ' a byte, for calculating the speed. Although ' the final value of the speed will be less than ' 255, it takes on larger values during the ' calculation process.speed_ser41 var PORTB.7 ' This pin is used to serially transmit ' the current speed data to PIC #1.speed_hs41 var PORTB.6 ' A handshake line is used to synchronize ' this communication.
speed_ser42 var PORTB.5 ' In a similar fashion, these lines are usedspeed_hs42 var PORTB.4 ' to send speed data to PIC #2.
ser_mode con 2 ' 2400 baud serial communication is used ' throughout.
counter var byte ' This variable keeps track of how many ' pulses from the speed sensor ' occurred during the time interval.
main: count sensor, 250, counter ' Counts the pulses from the speed sensor speed_calc = counter * 400 / 132 ' over a 0.25 second interval, and uses this speed = speed_calc.lowbyte ' quantity to calculate the speed in mph. if (speed_hs41 == 1) then ' Sends speed data to other pauseus 1 ' PIC's if they are ready. serout speed_ser41, ser_mode,[speed] endif if (speed_hs42 == 1) then pauseus 1 serout speed_ser42, ser_mode, [speed] endif goto main end
Page 1 of 1 12/9/2011 7:03 AM