SECTION 1 Hardware * Chapter 1 * Introduction * The Master plan * Getting the Act together * What is a microcontroller ? and Which microcontroller? * Which PIC language? * The Software Shopping List * The Tutor * Text Editor * MicroChip’s MPLAB * The program to get our program into the PIC * Starter Kit * Defining the Computer *
Introduction
There are so many embedded controller products in the house these days that many of us feel we ought to know a little more about how they work. They’re the brains inside things like the washing machine and so many other present day domestic appliances.
My first personal microchip contact was a friend’s radio controlled car which "died" and was brought to me for repair I being his resident radio expert.
The opened toy had very little inside, a small receiver, ( it was ok) and a small chip beside it, controlling the motors to the wheels. The chip was a PIC. I was out of my depth, best learn how….!!!
The reason for the PIC being there was obvious to me as an engineer. The printed circuit board was surprisingly empty. That meant the PIC program was replacing a lot of the traditional circuit’s resistors diodes and other hardware.
I decided I wanted to do something similar . Learn how to program was a priority in my mind, but not as a theoretical exercise, no, I like to get something useful at the end of any project.
Bathroom Scales? No, nor a toaster, nor the water heater. They all involved a lot of hardware, with unfamiliar thermistor sensors. My thoughts turned to my bike. It had a computer, very small and the sensor input was minimal. Yes, that was it, a Bike Computer project….. and so it started.
There are many ways to plan a project. But often, the steps we know don’t even figure because they are so obvious….This can cause enormous headaches for us beginners. So we will try and follow a logical sequence by spelling out everything that has to be done . There are two big areas to cover, the hardware and it’s construction, then the software and it’s application. The hardware is generally said to be the easiest part of the two so lets start there
The initial project outline was supposed to show the major activities and thus the equipment needed. The "equipment" including physical things like PCB and program things like the micro and language. The "idea" diagram is very bare and cryptic, but gets filled in as the project progresses. So we start with.
fig1 .Start
It was very clear from the start that ( for me ) the hardware box was easy to define and make, whereas the software box was full of unknowns. So let’s look at the Software questions.
What is a microcontroller ? and Which microcontroller?
I had found out by then that microcontroller is not just another word for microprocessor, no indeed. A micro controller is a stand-alone system that can calculate and connect itself with minimum peripherals directly to the outside world. The microprocessor also can calculate but needs a lot of interfacing to get the results to a screen or to a display.
Which microcontroller was harder to get to grips with. After going through an initial list it became all too clear that the abundant choice was aimed at experts, it was not for a beginner. That narrowed things down. The PIC , my first contact, caught my eye time and again, because of the many articles on offer on the internet and more important, explaining how to make things with it.
So I read more; specifically about PICs to find the really basic stuff. The PICLIST was a great starting point, as it talked about activities I had never even thought about. Especially, how do you test what you are proposing to make, some sort of simulation was an obvious must. This narrowed the field down further to things like the 16C84, now slightly obsolete.
My searching had uncovered a company called Microchip, which had from the goodness of it’s heart provided a program to check that the code you were writing was correct. It had a site that offered data sheets on it’s products, and almost all the net sites that talked about PIC’s directed you there.
What nobody actually said was which language was better,
ASSEMBLER OR C ( OR C++, OR ???).
As I know a little about C it initially attracted me, but somehow I began to get the feeling it was an overkill and that the routines were a bit unwieldy. Also it seemed to be an additional buffer between you and the microcontroller. During my search someone had commented in an article that correctly written assembler was very compact and made best use of the "scarce" memory.
So I decided on Assembler. But what was exactly assembler? I thought assembler code was universal, but it comes in many flavours. PIC assembler is not the same as that of Intel nor Motorola to cite some of the more important names., but PIC was widely used. Not a very logical way to arrive at a conclusion, but it happened that way.
We now had a starting point and the needed bits and pieces began to become clear.
There are so many. However one caught my eye as it was big, illustrated and FREE. After working through it steadily for a couple of days, it pointed me towards two important areas for getting started.
After a week of reading the tutor I found I could not put off any longer doing something about getting a suitable editor and an emulator. They had to be obtained.
When I’m not writing with a word processor, I use a smaller freeware program called EditPad . The big word processing programs use a lot of control characters which the PIC doesn’t want, (nor does it know what to do with them).
EditPad light is FREE, and also adds nothing to give the PIC indigestion. However any small Text Editor designed specifically for writing programs should be fine.
This is a big big program…. Why chose it ? Well ….
The program to get our program into the PIC
The MPLAB after checking that the code written can run error free finally produces a compiled file with the extension xxxx.HEX.
This .hex file is what the real world PIC needs to get up and running.
But to get the HEX file into the PIC we need a program that can check the PIC , we need a program that can read the contents of the PIC and write the code INTO it, burn it.
I personally like a nice little program that does this called ICPROG. The name I think says it all. It is a program to program your IC, your integrated circuit, your PIC!!!
It also caters for many different PICs.
ICPROG is easy to use and is , yes you guessed, it is FREE.
So where can we find all these programs? On the internet of course! If you search with the names above you will find several sites, some of which will be the nearest to your location.
If you don’t find any of them, then try the list at the end of the chapter which is hopefully not out of date. These sites will let you download the needed programs. Try it at night, because some programs are VERY BIG. I’m referring specifically to MPLAB.
We still have one very important item to add to our shopping list…..the data sheet for the PIC.
Which one?
Well the
16F84A is a bit bigger memory wise than the original 16C84 and is still not classed as "obsolete. It is limited as far as built in goodies are concerned.E.g. you don’t get on-board voltage references, nor UARTS nor ADCs, it is a little slower, but this is good for us learners because it is simpler to learn with. The bigger chips tend to have so many trees that it gets hard to see the forest.
To sum up , we need :-
Item |
Source |
What it does |
16F84A data sheet |
Microchip Site and others |
Explains about the PIC |
Text Editor |
EditPad Site |
For writing no-frills Text |
MPLAB |
Microchip Site and others |
Test drive your code |
PIC tutor |
Mikroelektronika |
Tells you How the PIC works |
ICPROG |
Icprog site |
Stuffs the PIC |
LCD data sheet 2x16 |
Hitachi 44780 controller |
displays speed, time dist. |
Up to here the total cost is your telephone Net connection Bill |
Once we have these items we can start to get to grips with the bicycle itself. So let’s see what the computer needs to get up and running. We will talk about the program later.
Here I can use a bit of hind-sight and point out things that are to be avoided. All bike computers have at least one input from a sensor, most will have this sensor plus a couple of buttons for changing menus. But the essential part is the sensor which sends a pulse to the PIC.
The sensor could be mechanical and every time the bike wheel turns it closes a switch, or it could be optical and every time the wheel goes round a photocell detects ( or loses) a light beam.
The mechanical contact on a mud covered mountain bike would be pretty unreliable, not only because of mud blocking the contacts but the bumps and leaps would give false readings.
The optical systems are very reliable in general, but not in the mountain bike situation. A piece of mud covering the detectors eye would happen so frequently that it would make the detecting system useless.
What’s left? Many systems. Just to give an idea of what is possible at the exotic end, you can carry a GPS terminal. Gives you all the travel data you want including the names of the place and country you are in, but a bit pricey. Besides we want to make it ourselves and write the program all at a very low cost. So is there a low cost reliable sensor?
Well the magnet on the wheel type is almost universal. It is cheap, very reliable and vibration proof for 20 years or so. ( Theoretically, the bike’s vibration, the flexing of the spokes, all will cause it eventually to demagnetise itself).
Another thing is that I don’t know anyone who has a 5yr old bike computer as we all tend to buy something more up market or simply get bored with our present one and buy another.
Getting back to the wheel magnet. It is fixed to a spoke, and usually on one side of the fork there is a detector. The magnet is aligned to pass this detector at a distance of not more than 1 to 3 mm every time the wheel rotates. (Less might be too close as the spoke flexes when the mountain bike lands, and magnet and detector could become entwined in each other with bad karma for the bike rider.
The magnetic detector can have many forms. It can be a many turn encapsulated coil, it can be a HALL EFFECT device proper, or it can be an MR ( magnetoresistive) device, or a Weigand sensor.
But let’s stick with the crowd and use a Hall effect sensor. It has a few drawbacks, but we can always change it later if we want to experiment.
So we want a detector which changes it’s output( goes high) every time the magnetic field gets close, and reverts to its low state when the magnet goes away. This is usually called a UNIPOLAR Hall effect device.
We want one with all the trimmings, (we don’t want to get involved in additional hardware design). That is , we want a sensor output with the correct level and good signal conditioning which gives clean, bounce free, and cmos level, HIGH and LOW states. There are quite a few to choose from. Melexis, Micronas, Allegro, Siemens… etc etc. So now for our first drawing of the computer below, + fig.2
fig. 2
Well fig.2 needs to be padded out a bit with things like what pin goes where, the connections between pins, and the power supply. To make things easier for those of us who have never seen a PIC before, lets just look at a
bit of the complete theoretical drawing to comment the components and functional blocks involved.Note if 2 wires cross with no dot, they don’t touch, if they have a dot where they cross , then they do touch, i.e., they make contact with each other.
fig.3
On the LEFT we have an integrated circuit ( between the two capacitors) which needs a minimum i/p of +7V to guarantee a stabilized output voltage of +5V, On the bike of course this would be a battery.
The next important point is the wire with the legend "Wheel pulse i/p. On the bike, this is where the sensor sends its output pulses to. Here testing, we have no bike, instead we generate pulses with another circuit ( a555). We will talk about the 555 later, it is nothing special, but clutters our initial view of the Bike computer.
The original project started with a PIC 16F84, but began to run out of memory. Fortunately it has a big brother, the 16F84A , with the same pin connections and accepts nearly the same code.
Just to the right of the PIC is the 4MHz xtal oscillator, composed of a 4MHz xtal and two 22pF capacitors. At such a low frequency the crystal is not very fussy, providing it is sited near the chip and we have short connections to the earth plane of the PCB.
Just beside the Xtal block, there is a 100k potentiometer. It adjusts the contrast of the LCD display. This pot is also non-critical, once adjusted, you will probably forget about it. So why have a pot there?
The problem is the display contrast. If your display gets fried and needs to be changed, then it is almost a certainty that you will have to readjust the pot to get the display contrast back to the "optimum viewing position".
In fact my original display would only become visible with a NEGATIVE voltage applied. Still lets hope there aren’t many of those left on the components suppliers shelves.
The last block is the display itself. There is an LCD standard, a 2 line by 16 character unit, which is show on top of the introduction on page 1. The display is the Hitachi type,
( there are many clones), but the important thing is that it has a 44780 controller on board. This is essential as all that follows presupposes that we are dealing with a 44780 controller to get our information to the display.
Lets look at the previous diagram again, focusing on the various pins of the PIC.
fig3b
Vss goes to ground and Vdd as well as the reset MCLR to the positive +5V rail. The osc1 and Osc2 pins were mentioned previously from the point of view of their physical position. The oscillator itself needs two external capacitors to function correctly. We have chosen a capacitor value of 22pF , which should be valid for most xtals. ( Here valid means that the xtal will see a suitable load and will oscillate correctly). The RA pins and the RB pins can be used as either input or outputs. The trick is to make the unused port pins OUTPUTS. Why not inputs? Well this is cmos and you would be wise to tie them to a reference, probably best, to ground. But then if you want to use the unused inputs later , this would mean track cutting. However an unused output can be left free. A reprogram can change it into an input, with no knife work involved. We mentioned above that there are RA pins, five of them, for PORTA. Only 2 RA pins are used to connect to the RS and E pins on the LCD , the other three are programmed as outputs and are not used. The two RA pins used (PORTA), essentially handshake the data transfer and indicate we are doing this in the half-byte or nibble mode of operation of the LCD.
The actual data is sent from PORTB to the LCD. It has to enter the LCD on pins D7-D4 for the half byte mode of operation. The remaining LCD data input lines are not used and to be safe, they are tied to earth as we talked about before. So D0-D3 are connected to ground directly. The data transfer from the PIC has a restriction, the high nibble always has to be sent first to the LCD, then the low nibble.
Is that about it? Well almost. The PORTB has 8 pins and any of them could be programmed as outputs or inputs. But there is another consideration. Later we will need to use some of these PORTB pins as inputs. Now only pins B4-B7 will give an interrupt signal asking for service. This means that the data out to the LCD can only be on pins B0-B3.
As an example, the wheel pulse INPUT SIGNAL ( having chosen PORTB), has got to go to one of the B4-B7 group as an input. It is seen to go to pin B4. Later we will use the other pins for buttons and LEDS during debugging .
This is an initial hardware justification, not complete but enough for the moment to enable us to get to the next stage which is mounting the PIC on a board to be able to program it.
http://www.editpadlite.com/ (The tutor from http://www.mikroelektronika.com/)
http://www.ic-prog.com/
http://semiconductor.hitachi.com/products/pdf/99rtd006d2.pdf
The last component is of course the printed circuit board..
There are various ways to build the circuit which can be divided roughly into two classes, solder and non-solder The " no soldering " techniques are attractive initially but are really misleading for us starters as it can lead to intermittent contacts. Of course if we are a bit masochistic then we may spend a pleasant evening wondering which wire has fallen out or worse, which one is in it’s hole but not making contact, or worst of all which wire is in a hole where it shouldn’t be. Our main goal is making a working bike computer, not messing about with the connections.
Let’s take the PCB route where everything has the connection planned and secure. Then we forget about that physical side of things and get on with the project.
To start making the PCB we need a few programs to get the project literally onto paper.
There are many very good schematic capture circuits, which number your components, adjust your drawing, anticipate the next stage which is the pcb layout…but they are also rather expensive.
We need something simpler which gives us the basic features, a program that allows us to draw the electrical circuit and number the components.
I used TinyCad. It is simple to learn but produces an excellent circuit diagram
The components are numbered and a list of materials generated, a parts list. As before, the program can be found on the internet and is FREE.
Below as an example our complete bike circuit and a list of the circuit components, generated by TinyCad.
====+ Parts List for Bk3.dsn +====
C1,C7 2 100n
C2,C3,C8 3 1uF
C4,C5 2 22p
C6 1 3uF
D2 1 IN4148
D1 1 LED
R1 1 100k
R3,R4 2 1k
R5 1 2k7
R2 1 300k
R6 1 390
S1,S2,S3 3
U1 1 VOLTREG
X1 1 XTAL 4 MHz
The Electrical circuit Diagram
fig 5 the theoretical circuit
We now need to make a physical circuit from the theoretical circuit of the Bike computer
Let’s examine and talk about the layout below, and the step to make the PCB physically.
Fig.4 the pcb
The twin towers and the IC between them is the stabilized power supply. The long thin yellow IC outline is the PIC itself. Above the middle of the PIC, a rounded yellow rectangle is the 4Mhz xtal flanked by it’s capacitors. To the left of the PIC, slightly down, a yellow square, the contrast potentiometer-. Finally occupying much of the bottom of the board, a large yellow rectangle with foursquare red fixing holes. The right most part which we will leave for later is the pulse generator, a 555.
For those of us who haven’t seen a PCB , printed circuit board, it is a sheet of plastic covered with copper on one or on both sides. We are looking at the side with copper. It is coloured green. The other side has no copper. Each red dot represents a drill hole. Once all the little dots have been drilled. We can insert the components like the PIC into the top side, the side which is NOT green. Look at the PIC yellow outline. It has a V shape on the left edge., a big notch. This is to tell us where pin one of the integrated circuit is.
Pin1 is the first dot above the V ,the first dot in the top row of PIC pins. We count the pins CLOCKWISE from one, going right to nine, then we jump across to the other line of pins, still going clockwise and go left along the lower line of pins till we come to 18 at the top of the V once more, but now on it’s bottom side. It is very important to understand this convention, so as to stick the components correctly into the waiting holes for soldering. Note if you look at the pins from the TOP side, they will now be numbered in an anti-clockwise direction, Think about this slowly and carefully.
The PCB TRACKS have been made as thick as possible. There are two reasons.
Thick tracks and wide earth zones mean good RF performance and heavy currents can be handled. Also soldering can be more ham-fisted before the tracks melt away.
The above PCB layout has been generated by a PCB program. This class of program can be very very expensive . Why? Well, it must be capable of producing a physical layout for the components, that reproduces all the dimensions to a fraction of a millimetre.
If this were not so, then automatic machinery which insert, or place components on the printed circuit board would probably place them with an offset. Then the next step, the flow solder bath, would solder the wrongly placed components with disastrous results. There would be many shorts between tracks and components.
We are not ( this time round) using SMD components. So our needs are not so stringent. But even so we need to be able to create the pads on the boards with sufficient accuracy to be able to insert the components without problems.
So? Well this time we are lucky as some PCB manufacturers give you a FREE professional program to design your PCB. They then manufacture it for you. There are standard prototype board sizes e.g. , 3.5 x 2.5 inch or 6 x 4 inch… many sizes. But for our project, this is not important,, we will use one of the smaller size boards.
This company which offers you the PCB generating program is called ExpressPCB. The program itself is called ExpressPCB The present version is V2.4 , but earlier ones will cover our needs perfectly . Most important, ExpressPCB. Exe is FREE.
Well using the program itself is very simple. But at this point we have to sit and study the controls. Once we have mastered the program we need to do the following.
We go to the component library and choose the component outlines of the components in the parts list generated with the TinyCad program. I used discrete resistors and capacitors . We can’t expect to find a PIC in the library, but yes, there is an outline for an 18 pin DIP . This defines an IC with two rows of pin holes. The pins spaced 0.1" apart and the rows spaced 0.3" apart. I placed this about the middle of the board as the starting point. Of course as the other components are placed , we might find we are having to squeeze things together in one place, while in another the board is empty.
On an up market PCB program, you simply move the offending components, and the connecting tracks reroute themselves. In other words the tracks you have drawn to connect the components are not broken. Also these better class programs will advise you if your layout is wrong. What does wrong mean?
Many things, but at the simplest level say the theoretical diagram shows the junction of R3 and C1 connected to pin 3 of the PIC, but we have counted down the line of pins wrongly and connected to Pin5. . The sophisticated program will tell you it is wrong.
Of course at an even more sophisticated level, the PCB program will place all the components inside the board size you specify, generate all the interconnecting tracks and use as many layers as needed if it is possible.
But those programs are very expensive. We get no warning that R2 should be connected to PIN 7 instead of pin17. But we can make our own checks. We can take a copy of the theoretical circuit and starting, for example with the PIC, we CAN create the tracks which connect to the LCD. Below we have a cut out a chunk of the full PCB and have written the pin names on the connecting tracks for the DATA between the PIC and the LCD. You should look at this carefully and check it is correct against the theoretical circuit diagram. Check that each track joins the correct pin number of the 16F84A to the corresponding LCD pin number. Once all pins have been checked, then we are ready to go to the next stage
In the process of making the PCB . fig4b track connection detail
For Each track that you draw on the PCB…. Mark it’s beginning and end on the theoretical electrical drawing with a fluorescent marker pen.
When you have finished creating all the tracks on the PCB, there MUST NOT be any unmarked lines on the schematic drawing. They must coincide, all lines must have the corresponding PCB track, otherwise there are errors.
Before leaving the pcb layout ( made with the Expresspcb program). Go back to fig4 the complete PCB layout drawing. There are two things wrong. Can you spot them?
Well the first thing would be fatal unless we know how to handle it. We have drawn the connections with the PIC pins numbered in an anticlockwise direction. We said earlier that this is the copper side of the board, and here you count the pins in a clockwise direction. Well maybe this is all a bit much to reason quickly, you need good special vision. But no panic. We carry on to the next stage where we will be transferring this image onto a clear acetate sheet. The program we will use, can FLIP the image so that we have the mirror image of fig4. This means that the bottom side pins are once again correctly positioned and go clockwise. Don’t believe me? Look at fig.4 on your monitor. Then look at it’s reflection in a mirror. I hope that convinces.
Why bother if it easy to solve? Well it would be so easy as well to overlook this little quirk of the Expresspcb program. Maybe there is a submenu command somewhere to flip the circuit , but I haven’t found it.
The second thing is a typical chicken and egg situation. For one of a myriad of reasons modifications to the original design meant that the pcb was cut and chopped and hacked…( Note. this is possible because our PCB circuit has very wide tracks and they can take a lot of punishment)… and pin connections reassigned. So the working circuit now does not agree with the theoretical diagram. Which one is right?
Here I am to blame. It is quick and easy to update the modifications to a circuit diagram, not so quick and easy to generate a new PCB layout. Compare the PCB fig.4 with the electrical diagram fig.3 and locate the differences. They are all associated with the port connections to the PIC and the output from the pulse generator.
The main reason for the changes was that ( as we will see later) that a green LED is a great help in confirming not only that the wheel pulses are getting to the PIC, but that the PIC is processing them and is looping correctly.
Later we will come to a NEW version of the PCB that corresponds to the theoretical electrical drawing, line for line, track for track.
We are now in a position to actually make the PCB.
A word on the print process itself. We have to get our PCB track drawing onto a piece of clear acetate sheet. It will look like a large photographic negative, because it will be exactly scaled 1:1.
We go through the following basic steps to achieve this.
That is it, we are ready for the next step after a few words on what we have just done.
It is too much to expect that the PCB dimensions after passing between two programs and then onto a printer … It doesn’t have to be laser, I use an HP710C … will come out exact first time. As the acetate sheet is rather expensive in my neck of the woods, I simply print the first trial run onto plain paper. Next I measure the length of the rows of pins where the PIC will go, to see that they are spaced exactly 0.1 " apart. We don’t even need a ruler., because we can place the PIC chip itself on our paper print out.
The first time we try this, probably the pins and marks won’t coincide.. The first two or three pins will, but then we may notice that the pins are gradually separating from their marks the further along the row we go, landing on the spaces between the outline’s pin marks. So we go back to step 3 above and adjust in the direction that is needed. How much adjustment? Well I sometimes have to scale up to 103% to get a printout where the pins coincide with the marks.
When the pins coincide exactly with the chip’s paper outline, then we can do the next print on the acetate sheet.
Any other problem? Yes, the acetate sheet itself! It should be thin, not those thick chunky ones. Can we use a thick sheet? Yes, but if your printer is like mine, the sheet goes into the printer , over a roller and does a U turn , and comes out the front again. Some thick sheets won’t bend.
Thick sheets have another problem. If we have managed to get the PCB tracks correctly onto them, then when we place the sheet over the photo sensitive plate to expose it, the lines will be fuzzy. This does not happen to the same extent with a thin sheet of acetate.
More Boobs… ExpressPCB was right !
Do I hear you say but it does happen.! Indeed sir, you are correct. We would have to turn the track drawing over so that is against the photosensitive plate.
You say again, but if we turn the sheet over, the pins will be the wrong way round, anticlockwise.
Yes sir that is correct … a pause as we gradually realise that the anticlockwise output from the ExpressPCB program was exactly what we needed. Our apologies to the wise men in ExpressPCB.
So we need to go back to point two above and remove the word FLIP.
Nothing serious, except that ExpressPCB does not let you produce an image file suitable for a drawing program to handle. There is no bmp nor jpg, nor tiff nor similar outputs. Just a native format for processing the drawing in their factory, logically.
I get round this by printing to the printer, but not the HP printer a fax driver .
The fax program lets you preview before sending. And HERE you can save the preview as a bmp, tif, dcx image format any will do. The fax route is nice because it gets rid of the colours and turns everything into black and white, which is what we need for the acetate sheet.
So MY complete process to get the tracks on to acetate sheet , yours maybe similar or better, has the following steps:
That is it. Well I sneaked in a program called IrfanView. It has all the bells and whistles we need, and….. yes it is FREE. Find it on the internet, using the program name IrfanView. It is not too big and downloads quickly
The chemicals we are going to use are CAUSTIC and AT ALL TIMES YOU MUST KEEP THEM AWAY FROM THE CHILDREN.
Caustic soda is not poisonous but it’s great affinity for water can cause serious burns in contact with the skin
. or being swallowedIf it gets into your eye, IMMEDIATELY wash you eye with abundant water for at least 5 minutes then get medical attention to check for damage. The prompt treatment is essential, to avoid permanent injury.
If in spite of the
WARNING above, the kids get hold of it and swallow it, then YOU are really in trouble. You must take immediate action. Do the following.Explain if they have swallowed crystals or the dilute solution we are going to use
Describe the symptoms and act immediately on instructions.
If it is not possible to get to a doctor or hospital then you must determine if the crystals or the dilute solution has been swallowed.
CRYSTALS: See if there are burn marks round the MOUTH. Administer 15cc/kg body weight of water or milk if possible. Rinse the mouth with abundant cold water, GET MEDICAL HELP AS SOON AS POSSIBLE
DILUTE SOLUTION we use A 0.7gm/liter. Solution. This is not so bad as the crystals when swallowed, but it needs IMMEDIATE treatment.
As before immediately drink water or milk , 15cc/kg body weight , and rinse mouth abundantly with water Even though this is a dilute solution it is caustic and the child should GET MEDICAL HELP AS SOON AS POSSIBLE.
Ferric Chloride
This at first sight is worse as it eats away many metals. But it has an awful taste , a foul smell and it is most unlikely that a child would swallow the stuff. The main danger then is that it is CORROSIVE and it will stain your clothes, your skin a brownish yellow colour. The skin stain will go away after a few days, but the clothing is usually ready for the dustbin.
If it gets in your eyes, or hands wash with abundant water for at least five minutes then get immediate medical help
There are many legislations locally in force for chemical disposal. You should consult before flushing these or any chemical down the toilet.
Having pointed out what can go wrong, lets concentrate on the positive side in the making of a PCB-
The exposure process is simple and largely non-critical.
Note . Buy the smallest possible quantities of the chemicals, as a little last a long time.
We need the following:
To keep our acetate sheet pressed hard against the photosensitised copper clad board, in such a fashion to allow the light to shine *through it, we need something where the top layer is glass, hence the photo frame, or photo holder… call it what you will.
Here the dirt cheap type is better than an up-market jobbie with gold beaded edges. We need a bit of clear glass, a firm hard to bend back beneath the glass, and a no frills clamping system to hold the photo in place.
*Note. If later when you are exposing, the plate doesn’t seem to expose or takes a very long long time, then check that the glass is really glass. Sometimes the front is clear plastic. This may or may not be good. Many plastics are excellent UV blockers. They don’t let the UV part of the spectrum pass even though visible light does.
Returning to describing the photo holder. This is all a bit hard to describe in words so lets look at the images below which help better to see how it all works.
The back of the paste board frame has four springy metal clips in grooves. The clips curl over to the front and hold the glass firmly.
This particular model is easy to open and remove the glass. The dimensions are far bigger than what we need, but if in the future you decide on a bigger and better circuit, this will hold them all!
Dimensions 18 x 24 cm
Fig . 6 backside of the photo holder
Now lets look at the glass-side of the holder.
I put the glass on a bit badly so that you can see it at the edges.The clips are holding the glass against the base, and sandwiching the track layout. I DIDN’T put the photosensitive copper clad board under the track layout as it was also black and the contrast was very bad.
The white tags are the sticky tapes used to fix the track sheet to the photosensitive board. Pull them tight before sticking to avoid wrinkles.
I cut the board to the track sheet size with kitchen scissors, then tape on the backside of the copper board.
Fig . 7
I hope the description is clear, ah and also…Obviously you can use anything that holds the acetate sheet tight against the copper clad photo sensitised board below it.
When mixing solid chemicals wear glasses if you have never done it before.
The reason is that most chemicals in crystal form that are used in reactions release heat on dissolving. If you pour water onto a crystal of that kind, it could literally heat up and explode in your face. So always add small quantities of the above crystals to water.
We are going to add the chemicals to water. Normally there are no fumes from both, BUT, this depends on the purity of what you have bought, so do not mix in a closed room. Have some form of ventilation, an open window for example.
Prepare 1 litre of tap water for the NaOH and 0.25litre of tap water for the FeCL3.
Note. if you have furred kettles in your area, maybe best buy some natural water from the supermarket.
Caustic Soda solution ( NaOH)…The developer
Pour one litre of water into the plastic tray ( check that it is big enough o hols 1 lt.)
Having weighed before hand the 0.7gm of NaOH, slowly add it to the water , stirring with the plastic rod.
When the solution is clear, use the funnel and pour into the bottle labelled Caustic Soda
Wash both tray and the funnel with abundant water.
Ferric Chloride solution the etchant
Pour a ¼ litre of water into the plastic tray ( check that it is big enough o hold .)
Open with care the 100gm plastic container of the Ferric Chloride crystals. slowly add the crystals to the water , stirring to aid the dissolving of the crystals with the plastic rod.
When the solution is a clear brownish yellow with no lumps on the bottom of the tray, use the funnel and pour into the bottle labelled Ferric Chloride
Wash both tray and the funnel with abundant water.
DON’T HEAT THE WATER
There is no need to heat either product to help dissolve. A normal temperature of 12 to 20 ºC for the tap water is sufficient. The colder water will take a little more time but apart from that the crystals will dissolve completely. Another reason for NOT heating is that the ferric chloride may begin to release fumes. This will not happen at normal temperatures.
The single sided copper clad board we have bought has a POSITIVE photo sensitive layer on top of the copper which responds to ultraviolet light. Now we are going to use normal sunlight which contains enough UV to expose the Copper clad board.
How long ? could be impossible to answer, but fortunately the test strip shown below solves this problem. The sun between 10a.m and 3 pm. Is not critical
The test strip is simply a piece of clear acetate sheet with numbers drawn on it, say 1 to 10. Use your kitchen scissors, the type that the TV advertises as being able to cut through steel). Cut a strip off the copper clad board about the same size. Mine were 10cm long by 2 cm wide. Dim the lights or draw the curtains. Peel off the protective covering of the copper clad board. Tape at the ends , the numbered acetate sheet onto the SENSITIVE side of the board ( the side you have just peeled)
Get your watch ready and a piece of cardboard big enough to cover the whole test strip .
Is it sunny? Good move the strip into the sunlight but still covered. Move the cardboard covering the test strip taped to the sensitised copper clad board and uncover ONLY the number ten for 30 seconds.
Then slide the cardboard a little more to uncover NINE for 30 seconds, then 8 for 30 seconds . Keep on uncovering each number for 30 seconds till you are at the end that is ONE is uncovered, then remove the test strip from the sunlight. Slightly cover the window to shut out direct sunlight.
Look at the test strip and the exposed copper below. You should soon see something like it.
fig.8 test strips
Number 8 has had 30 x 8 seconds exposure about 4 minutes
Number 7 has had 30 x 7 seconds exposure about 3mim 30 sec
…
…
…
Number 3 has had 30 x 3 seconds exposure about 1min 30 sec … not useable
Number 2 has had 30 x 2 seconds exposure about 1min … not useable
Number 1 is completely under exposed and can’t be seen
So 5, 6, 7, 8 look OK. (I used 7). The strip also indicates that the material is not critical, an exposure varied from 2.5 to 4 min makes little difference.
Getting back to the test strip you have just exposed. Peel off the acetate sheet with the numbers and submerge the test strip in the tray with the caustic soda. The correctly exposed numbers will appear within SECONDS in a slightly greyish form ( the top drawing) and the other numbers more slowly . Move the solution in the tray for a minute more or less. Then remove the copper clad board holding by the edges, from the tray and place the copper clad board under a running tap to wash off the traces of caustic soda.
Finally place the copper clad board in the tray with ferric chloride, the etchant. This is slower, it can take up to 30 minutes. But within half a minute the correctly exposed numbers will be visible, a different colour from the salmon coloured copper that is being eaten away by the ferric chloride. .
Keep on stirring once a minute or rock the board in the solution. The parts of the copper NOT covered by the numbers is gradually eaten away leaving the fibre glass board visible.
Why is there copper at each end? I had tape there and the sun didn’t get to the board..
As before wash under running water for at least 5 minutes, watch your eyes and clothes.
Finally put the chemicals back into their labelled bottles. Wash everything and put the bottles out of reach of children.
Well that is the initial preparation for making the PCB. We now know how long to expose the sensitive board for.
We need to prepare for the big moment, our first PCB. A semi darkened room, sunlight behind the curtains and the photo holder, unfastened . Peel off the protective covering and place the PCB Track acetate against the board, with the printed side in intimate contact with the board.. Tape the edged of the acetate to the board, don’t cover the track area with tape. You must tape the acetate FLAT against the Copper clad board as ripples will cause fuzz and loss of definition.
Place the Taped combination in the photo holder., so that the sun will shine on it. Cover the combination with the glass. Clip the glass firmly to the back of the frame so sandwiching the copper clad board and acetate sheet in the middle. Looks flat?
Congratulations!!! You now have your first PCB
Now is the time to use those DIY skills hidden inside of us. Most of us already have an electric drill for filling the house with holes- But in my case I had a problem . My drill would not hold well the small drill we need to make the holes in the PCB.
There was also an added difficulty in trying to make such fine holes on a closely defined area and at the same time hold steady a heavy machine and at the same time again, NOT break the very thin drill shank.
Most hobby shops sell a small hand held battery operated machine which is much easier to handle.
Here we can see my hobby machine stowed into its plastic blister with the drills and cable.
fig . 10 the box label.
I just added the back label, not to sell this brand, but simply to show the size of the drill machine
Fig. 9 the complete package, machine and a set of drills.
The drill itself is very small compared to a normal household "wall hole maker". We need a size that will take all most standard resistor, capacitor, integrated circuit, leads and pins, to pass through the board for soldering . I think a 0.8mm drill is about right for the job , but you decide when you come to this stage, maybe you would prefer a 0.5mm. Either way they are small drills.
Now if we look at the tracks on the copper PCB carefully we see that the points we are going to drill have a round mark ( the future holes for inserting a component lead or an IC pin ), also called a pad.
When we hold the drill machine over the pad to drill through it , the drill will probably skid. To avoid the skidding and breaking the drill, get a small hammer and a nail.
Place the nail over the centre of the pad and hit its head lightly with the hammer to produce a DENT, not a HOLE in the centre of the pad.
Dent all the pads to be drilled on the board, not long, a five minute job as it is a small board.
The final problem is clamping the board . I place a piece of wood o slightly bigger than the PCB. I place it at one edge of the table and after placing the PCB on this piece of wood, screw the clamp down (protect the contact area with a doubled and then doubled again sheet of paper) to hold the paper, the PCB and the wood, firm on the table . The piece of wood is to avoid making drill holes on your table so it should be thick enough, say 1cm.
Now we can drill hold the machine and guide the drill into the first pad’s dent. Try and keep the machine vertical. Drill… you don’t need pressure, it slides through the PCB like butter. Very little pressure ( think of your table below). You now , with a bit of practice, have perfectly centred holes in each pad and we are ready for the next and final stage.
Inserting and Soldering the Components
We probably have all the tools needed already but let’s make a small shopping list just in case.
Now how to solder well, is a big topic. The gentleman below has said it all, far better than I could. If you are not sure and don’t want to destroy our laboriously created PCB, or fry a transistor, then you should consult a web site like this one…
Basic Soldering & Desoldering Guide
by Alan Winstanley
http://www.epemag.wimborne.co.uk/solderfaq.htm
Fine, now we know all the precautions and things to do to make a good solder joint. One very practical detail is inserting the smaller components first. The reason is we insert from the top then we turn over the board to solder.
Now the components tend to slide out when not soldered. So you hold them in place with a fireproof finger. If the big components have already been soldered and the little one lies between them, then you can get your finger to hold the small component. What should be a two second job can become very difficult .
Don’t cut off the lead wires before soldering as bent slightly outwards they help to hold the component in place till we solder the leads. After cut them of as close as possible to the joins. This can avoid some nasty cuts if you accidentally slide your fingers across the underside of the board. Some of the cut leads are razor sharp, hence cut at low as possible. I once used to resolder after cutting to cover the lead edges but that is not a good idea as it reheats components.
Just a final word about the integrated circuits, especially the PIC. It must be mounted in a socket for an 18pin DIP as it will be removed to program and then reinserted many times before we are satisfied with the bike computer. Any special type of socket? No not really. I use a normal IC socket and it lasts a long time, indefinitely if you insert slowly pushing in alternately each end of the PIC.
For Taking the PIC out of the socket, I gently slide a small screwdriver blade between the PIC and its socket and lever up slightly , then I go to the other end and lever up slightly.
After a little with practice you can do this with no damage or strain to either the PIC or the IC socket..
Resistor Colour Code |
||
Colours used band 1 and 2 |
Colour used as a number |
Colour as a decimal multiplier |
BLACK |
0 |
X1 |
BROWN |
1 |
X10 |
RED |
2 |
X100 |
ORANGE |
3 |
X1,000 or 1K |
YELLOW |
4 |
X10,000 or 10K |
GREEN |
5 |
X100,000 or 100K |
BLUE |
6 |
X1,000,000 or 1M |
VIOLET |
7 |
Silver / 100 |
GRAY |
8 |
Gold /10 |
WHITE |
9 |
Tolerances Gold= 5% Silver=10% None=20 |
Going back a little to before we insert the components in the PCB. Do we need to learn the way components are marked, or maybe we already know. Here is a table with the basic information.
Believe it or not we are almost ready to talk about the programming of the PIC
There are really so many programmers available for the PIC, that we couldn’t possibly discuss the pros and the cons of them here. I would suggest we use a really simple cheap one, just in case we later need something better. But here cheap means reliable and one that does the job.
Any other condition? Yes, it has to be compatible with one of the types of board recognised by the ICPROG program we listed in chapter one as a FREE program.
Below we have a Picture of my own ( built from a kit) programmer. This is a com port model of the type called JDM ( I think JDM invented it). It uses the RS232 interface and has NO power supply. I t powers off the COM port output directly. As you can see from the photo, most of the components are diodes that either rectify the incoming signal or level shift signals.
The 9 pin D connector shows how small the whole thing is. The 18pin dip is empty but that is where we plug in the PIC for programming.
Just one word of warning. The socket has a V end and is mounted so that the V corresponds with the silk-screened white V. We MUST insert our PIC so that it’s V or mark also corresponds and is at this end. If we put it in the other way round, it is probably ( not guaranteed) the end of the PIC. RIP…. = $$$$$
Here we have come to the end of the steps to be able to program a PIC , not all PICs but quite a lot of them. Just to sum up we will reproduce the principal items from the various lists so that we can do a final check to see if there is anything we may have skipped.
Item |
Source |
What it does |
16F84A data sheet |
Microchip Site and others |
Explains about the PIC |
Text Editor |
EditPad Site |
For writing no-frills Text |
MPLAB |
Microchip Site and others |
Test drive your code |
PIC tutor |
Mikroelektronika |
Tells you How the PIC works |
ICPROG |
Icprog site |
Stuffs the PIC |
LCD data sheet 2x16 |
Hitachi 44780 controller |
displays speed, time dist. |
Up to here the total cost is your telephone Net connection Bill |
====+ Parts List for Bk3.dsn +====
C1,C7 2 100n
C2,C3,C8 3 1uF
C4,C5 2 22p
C6 1 3uF
D2 1 IN4148
D1 1 LED
R1 1 100k
R3,R4 2 1k
R5 1 2k7
R2 1 300k
R6 1 390
S1,S2,S3 3
U1 1 VOLTREG
X1 1 XTAL 4 MHz
Photographic list
Inserting and Soldering the Components
DeSolder wick
And the last item was a
I must have forgotten something…….. ah yes a
This is the end of the Hardware chapters the next section will deal with the programming of the PIC
SECTION 2 Writing Code for the PIC
I wanted to build a bike computer as an exercise to learn how to program in Assembly language as I explained in Chapter 1.
As always I start out by making a flow chart which is empty. Of course if I had known what to put into it then, well I wouldn’t have needed to learn how to write the PIC code.
Let’s say at this stage the flow charts are either very empty or meaningless. Best leave them till the end of the project. They look nice.
So a good starting point is do a little theoretical exercise where we speak about what features a bike computer should have, do the maths of each feature and try and define further at each step, the bike computer itself. Really we are identifying functional blocks which then, if feasible, can be turned into PIC code.
*Part of Fred’s original notes………………..
* What follows is very large, in that the bike modules identified are many, maths, speed , distance, time HP, calories, Angle, friction, wind speed….. these are all possible, and the PIC has enough input ports to handle the sensors. There is one basic problem.
The resulting PROGRAM IS TOO BIG FOR THE PIC 16F84A. So the original notes stop at the point where we have identified the basic features of the bike computer.
Within this limiting of the features, YES, the PIC can handle the resultant size of the program.. ( I haven’t got a crystal ball, I ran into the problem and there is no getting round the code-too-big for the chip memory limitation).
I have no doubt that optimising the code ( it becomes very illegible when optimised ) we could squeeze in the wind function. But when we arrive at the situation that we are at a memory limit and that we are running out of variable space, it would be better to move upwards to a bigger PIC, compatible as far as possible with the code we will write.
The First attempt to define the bike computer
Sorry if the way it is written rambles… but at this point there was just an idea and questions about how to give it form…. So please bear with me and don’t laugh as the original mistakes have been kept in. For example, the wheel distance for one revolution s taken as 4m when it should be 2m. Anyone spot the mistake?
….
the operations , data and the constants needed for the complete bike program.THE INITIAL STARTING CALCULATIONS AND DATA INPUTS
Wheel circumference= Radius*2pi (for a 26" wheel circ=26*2*PI*2.54/100 = 4.14m)
constant for diam. to distance is 4.14/26 =0.156. So this is a START calculation and is stored
in DATA memory as a the wheel circumference.
DATA memory will also need to have in kg
The rider weight =wrid and the bicycle weight = wbike. So total weight wrid+wbike = wtot
wtot for example calculations =85kg +15kg =100kg
Wind resistance can be calculated as a function of bike speed, more later.
Road friction can also be calculated when the bike type ( road or mountain) is entered and the
appropriate equation selected..
But maybe there is a better way, the "flat road time to come to rest measurement"
THE WHEEL
Wheel circumference is an initial constant which by now has been entered for a given bicycle.
In the EXAMPLE a 26 inch wheel has a circumference of 4.14 m. So as explained above circ=4.14
and is needed,, to calculate ALL OF the following a)... e) .
a) Trip total dist. sum of wheel pulses
b) Average speed dist / time
c) instantaneous speed dist / 1 sec
d) peak speed. highest recorded value of c)
If we sum a) to the previous total of distances from previous trips,, this gives the
e) Total kms the bike has covered since the computer was installed.
DISTANCE
Each circumference travelled is one wheel revolution. So the magnetic sensor
will generate a pulse per revolution. In this case 1 pulse every 4.14m. and thus the wheel constant
is 4.14m =circ
So to calculate a) we need to store the total of wheel pulses and multiply by 4.14. Store in
the Wheel Pulse Register
The result in meters is usefully displayed as km to two decimals. This means that we can view the
trip distance to within 10m. A 5 digit display gives a maximum trip distance 999.99 km,, so we will need
a five digit LCD display.
TIME & STANDARDS
To calculate b) we need to know the time that has elapsed. The STANDARD that generates
the time,, our local CLOCK has to have sufficient accuracy for this task How accurate?
We have three available standards for the PIC. Xtal,, ceramic resonator,, and RC.
Consider the RC. Well a one second a hour error at 15km/hr means a distance error of 4.14m
or in 100km 414m error, about half a km....an error of 0.5%.
This is probably a practical measurement limit,, tyre pressure,, skids,, bumps,, wind, slope.... will all
tend to produce worse errors. If we want real DISTANCE ACCURACY we can use GPS measurements
So at first sight any standard will do,, we could use the RC network.
The 4MHz xtals are very stable and theoretically will allow in the same circumstances at 15km/hr an error in
dist /time measurement to be only a cm and time a fraction of a second.
But the other uncertainties mean that in practice you can't use the accuracy. If we want the time STANDARD as a WATCH. days, hours minutes seconds... we may have to come back to this.. .
Lets look at the third option.
A ceramic resonator is small and offers a 1in 10000 precision and also a low frequency, say 32000Hz
We divide by four to get the machine cycle. We get two bonus points
The error is OK for a TRIP display,, 1 second error in 3 hours.
This is also very good for reducing the power consumption of the PIC and peripherals.
However it may be too slow as it limits the PIC to 8000 machine cycles /per sec!!!
We will use for the timing example 8000Hz = 0.125msec per cycle.
PIC System clock is 0.125 millisecond
On starting the computer we need a time elapsed counter for total trip time.
What could a maximum trip time be? Let's say 24 hours.
24 hours in seconds is = 86400 seconds
24 hours in machine cycles is =691,, 200,,000 Can the PIC manage this quantity?
Note find timer example on the internet.
Of course we limit the count to one hour =28,,800,,000 and increment a register in steps of 1. This
would be the hour register.
We can now calculate b) Average speed from Wheel pulse Register / hour register
To calculate c). Instantaneous speed we need to measure the time between consecutive
pulses from the wheel,, by reading the number of clock pulses before the 2nd pulse is detected.
An example could be that the second wheel pulse arrives 6000 clock cycles after the first.
So we can calculate the instantaneous speed as :-
8000/6000*4.14*3.6 = 19.872 km/hr at that moment.
Store the value in the instantaneous speed register
We have to do Two things with the Instantaneous speed . Let's look first at the easiest....
The instantaneous speed value is displayed and also internally this value is compared with the
previous value stored in the max speed register. This will give us d) peak speed
The condition is "If greater than stored value,, replace with actual value."
The second thing is a know problem because the speed display is constantly dancing
and visually this is annoying. So we need another routine to sum the last five seconds and
show the average instantaneous speed. This means we need to reserve a chain of
FIVE instantaneous speed registers. This routine will have to sum reg1,, 2,, 3,, 4,, 5 and
divide by five. The result is then displayed as instantaneous speed. It will also mean a shift
routine to keep the data moving, always losing the oldest reading.
There remains d) total km since installed. FROMDAY1 register should probably be
increased by 1 for every measured km covered during the trip which is in progress.
(I say probably because maybe
there is an easier way of showing bike's total km????)
wake up
On wake up you have last trip data still available.
If you wish to retain previous trip data .....do nothing with button B.
Note. You may want this to sum total km in a day after stopping to eat.
If you wish to start afresh.....
Reset to zero, distance, time, average speed, maximum speed, Calories burned… by holding down button B for five seconds.
Display 1 will show Speed and Trip distance to nearest 10m.
Pulse A
Display 2 will show Average speed and Trip time. to a maximum of 24 hours
Pulse A
Display 3 will show Top Speed and sum of all trips distances to a maximum of 99,999 km
Pulse A
Display 4 will show present HP and kCal/km at present rate.
Pulse A
Display 5 will show Peak HP and integrated trip kCal burned.
Pulse A
Display 1 will show Speed and Trip distance to nearest 10m.
We now move on using the above description to try and create the needed modules.
The First Steps after pseudo code
The above shows how idea has been captured my style, and translated into Maths equations.
We are going to change the rough outline and the equations into code blocks, but first we need to answer some basic questions as to what instructions exist and also to get our tools working.
So let’s sort out the initial problems and get started with the code.
Our program to run the PIC code and then send it to the 16F84A is MPLAB, but if we have never seen it before we may have problems in setting up the initial options. MPLAB presuppose that you have done some programming, but this is not necessarily true. I hadn’t, and I floundered for a bit till I discovered that MPLAB need to basic inputs to work.
I didn’t see this at first. If you haven’t created a project, them the assembler code can’t be run. And of course the other way round. If you have created a project but have no code written , then there is also nothing to run. In both cases MPLAB waits…
So out project is called Bike3.pjt (MPLAB adds the extension). But so that we can see the essential screens lets briefly open a NEW project. A reasonable name would be Firsttest.prj ( MPLAB will toss the name out as it has more than eight letters, but Firstest is OK as it is inside the maximum. Try both.. Ah before I forget, we will put this firstest.prj in my PICproj directory, which starts empty so that we can see the files that MPLAB is generating for us. So run MPLAB and go to the second menu Project, select New and we have an invitation to name the project.
Fig.20
We enter the wrong name firsttest and get the message, "too long"We enter again but with one "t" less. MPLAB accepts and opens the next window below fig.200 We can see that firstest.hex has been written in two `places by MPLAB. Fine, we will come to that a little later. The essential thing that MPLAB need to know is what kind of Chip are we going to use and what features will we employ. . These details have already been chosen by me as we see IN WINDOWS MPLAB SIM PIC16f84A and also LANGUAGE TOOL SUITE has been chosen as MICROCHIP fig. 200
How did I do it? Well if we look a little further down fig.200 we see a button labelled Change..
Pressing it takes us to the next menu in fig.201.
Fig.201 asks for details such as the ones we have just mentioned and more. We click the tags
We enter XT and 4.000000, not forgetting to tick MHz as well.
Configuration tag next. Enter watch dog timer none and postscale 1. Processor mode, microcontroller-
Break tag. Clear, global, freeze. Finish with APPLY . Now here MPLAB warns something about a hex file which at this moment doesn’t seem to make sense. If you know what is going on fine, but if you don’t it is a bit mysterious and you may feel you hit the wrong key to leave the menu fig. 201 No you did it OK. MPLAB is worried because it has no assembler file, ( it is looking for a file called firstest.asm, which we haven’t yet entered) and from that reason it tells you that it can’t go on..
Fig.201
So we return to fig.200 to clear up this problem that MPLAB has. How?
Well please remember all this is dummy, so that we can play a little with the menus.
We can create another dummy firstest. asm
And see what happens.
To do this we go to the top menu again
and open FILE, New.
Write anything in it, I wrote "First time."
Then save in the PICproj directory as firstest.asm. We then go back to the project menu and see what MPLAB thinks about the Firstest.asm file we are going to offer it. Here goes. We change to the project menu and at the top we see that firstest .prj is still open. Good. ( But if it wasn’t? Then use Open Project from the dropdown menu or simply click it from the list at the bottom.)
So we have our project open but as yet no Firstest.asm to feed to MPLAB. The only button not "Greyed Out" and which we haven’t touched is Add Node. This is MPLAB_speak for "find me an assembler file to play with".
So we press Add Node and enter from the PICproj directory firstest.asm.
( If MPLAB offers you a different directory in the previous step, change with the browse button. Somewhere I read that the project and it’s xxxx.asm files have got to be together)
Yes , fine MPLAB accepts our dummy firstest.asm file. And adds it to the project window.
Note I’m playing about with capital and small letters for the file names. Be careful. In some places MPLAB doesn’t care a hoot, in others it gets really shirty.
So we seem to have deceived MPLAB giving it a dud asm file. Well not really, but up to here it has enabled us to test the main buttons and set up MPLAB.
We have achieved our aim and have a file which MPLAB can do something with. It can run the code we have written in the dummy file firstest.asm ( remember we wrote " First Time") . Well to get MPLAB to attempt to do something with it , in the project menu, we try another button called "BUILD ALL"
Yes indeed something happens , MPLAB spits out the following
Building FIRSTEST.HEX...
Compiling FIRSTEST.ASM:
Command line: "C:\ARCHIV~1\MPLAB\MPASMWIN.EXE /e+ /l+ /x- /c+ /p16F84A /q C:\PICPROJ\FIRSTEST.ASM"
Error[122] C:\PICPROJ\FIRSTEST.ASM 1 : Illegal opcode (time.)
Error[125] C:\PICPROJ\FIRSTEST.ASM 2 : Illegal condition (EOF encountered before END or conditional end directive)
MPLAB is unable to find output file "FIRSTEST.HEX".
Build failed.
The last line says it all, "build failed." But it doesn’t matter. We have set up MPLAB for a PIC 126F84A, running at 4MHz and it is attempting to compile.
From here on, we can forget about setting up MPLAB and concentrate on writing code. There are lots of other things it will do but we are over the major hurdle in getting the MPLAB program started.
If all this is boring you stiff, skip the next bit, but as this is written for beginners like myself who get stuck on the most obvious things initially, we will try and explain all that is needed to make MPLAB happy with our dummy asm file.
Well MPLAB replied to our first compile attempt with "build all" ….and blew us a raspberry.
But it also gives clues to where it is wrong, or things missing.
The last line seems worried about the end do we need to add and end? Yes. Well let’s add the word end.
How? Well there is a window open showing our Firstest.asm file. If you haven’t got that window open. , go to the file menu and open it.
Then simply write the word end at the end to see if MPLAB feels a little happier. Compile with Build all in the project menu once more. We now get.
Building FIRSTEST.HEX...
Compiling FIRSTEST.ASM:
Command line: "C:\ARCHIV~1\MPLAB\MPASMWIN.EXE /e+ /l+ /x- /c+ /p16F84A /q C:\PICPROJ\FIRSTEST.ASM"
Error[122] C:\PICPROJ\FIRSTEST.ASM 1 : Illegal opcode (time.)
Warning[205] C:\PICPROJ\FIRSTEST.ASM 2 : Found directive in column 1. (end)
MPLAB is unable to find output file "FIRSTEST.HEX".
Build failed.
Still grumbles about end but something changed, it doesn’t like it in column one. So move it to column two ( shift key or simply spaces).
Compile again with build it….
Building FIRSTEST.HEX...
Compiling FIRSTEST.ASM:
Command line: "C:\ARCHIV~1\MPLAB\MPASMWIN.EXE /e+ /l+ /x- /c+ /p16F84A /q C:\PICPROJ\FIRSTEST.ASM"
Error[122] C:\PICPROJ\FIRSTEST.ASM 1 : Illegal opcode (time.)
MPLAB is unable to find output file "FIRSTEST.HEX".
Build failed.
Eureka!!!, the error message about the end has gone. Next thing is our text which is not an instruction… maybe we could recycle it as a comment. to the right of ; comments are ignored. (A comment begins with a semicolon).
Try it, compile again…..
Building FIRSTEST.HEX...
Compiling FIRSTEST.ASM:
Command line: "C:\ARCHIV~1\MPLAB\MPASMWIN.EXE /e+ /l+ /x- /c+ /p16F84A /q C:\PICPROJ\FIRSTEST.ASM"
Build completed successfully.
We are there. MPLAB gave us the green light. Also the message at the end
MPLAB is unable to find output file "FIRSTEST.HEX".
about "can’t find the hex file" has gone. Why? Well it must be MPLAB’s diplomatic way of saying, "I’m not going to produce a HEX file with that load of rubbish code", when it finds syntax errors.
Just before we forget , save the project for next time using "save project" then "close project" ( project menu). We can now examine a little further, we were saying….
On the other hand we have compiled an absolutely useless piece of code. How is this possible? We agree it is useless, but MPLAB accepts it because it doesn’t break any rules. …. There are no syntax errors.
You mean that you can compile something that is wrong? Well normally not something so absurd as out little asm file, but how do we define the word wrong?
Someone told me a long time ago….
A computer does what you tell it to do,
Not what you thought you told it to do
It is still true today. Providing what you write meets the rules, it’s fine by MPLAB and by extension we have something to pass on to the PIC. Useless? No, we are trying to test all our tools.
In our PICproj directory, we see…
firstest cod 3.072 11/03/02 13:28 FIRSTEST.COD
firstest err 0 11/03/02 13:28 FIRSTEST.ERR
firstest lst 680 11/03/02 13:28 FIRSTEST.LST
firstest pjt 1.482 11/03/02 13:51 firstest.pjt
firstest asm 20 11/03/02 13:28 firstest.asm
firstest $$$ 19 11/03/02 13:21 FIRSTEST.$$$
firstest hex 13 11/03/02 13:28 FIRSTEST.HEX
7 archivos 5.286 bytes
MPLAB has indeed been working, three of the seven we recognise immediately maybe above all the firstest ,hex file which is what we need to run the PIC.
Before going on, let’s open up the project again and press the Project then, project Edit buttons. The bottom button in the window that opens is called Node Properties, but is greyed out. Click on firstest.hex and the Node properties become alive. Click on it and here we have a whole host of things to fiddle with.
Leave them alone for the moment as they are the default settings established by what we have entered earlier and in part by what MPLAB thinks is "Normal"
Look at the HEX FORMAT row. We are offered three possible formats. We haven’t chosen any of them. Can we leave it like that? Well the short answer is yes. But be aware that extension. HEX is not universal and comes in different flavours.
In our case we are going to use ICPROG next, to get the hex file into the PIC, and fortunately we find that ICprog is happy with the default output of the PIC.
But if you use something other than ICprog, this may not be the case.
Icprog want to know above fig.21all what kind of test card is holding the PIC and you tell it which by choosing from the list options shown in the settings, hardware settings menu that is open in fig 21.
The other options that a different programmer could need to have changed are listed on the right under the heading communications.
In my case I selected the JDM programmer from the list, and COM2 The list also is useful in that it is a list of programmers that will work with Icprog.
So having set up ICPROG to work with our programming card, we then open the Hex file in PICproj directory.. Now as this is just a comment, we will not program a PIC, but we will exercise the COMMAND menu..
First we load from the PICproj directory "firstest.hex", then as supposedly we have a PIC 16F84A inserted in the programmer card, we check that it is blank. . press Blank check.
After a few seconds we should see confirmed blank, OR, that it has data stored in the 16F84A. ( In real life , maybe we used the PIC before for another version or program).
If the PIC is found not to be blank, then we need to wipe it. With ERASE ALL option in the command menu.
Here there are only two possibilities, it gets wiped or data remains. How can this be if we have just the ERASE ALL command? Yes, good question. If you program the PIC more than about the minimum 1000 times, well it is possible that the Flash programming stops responding in some cells. Then what? Best find another PIC.
In the other normal case, we get the message " Device is blank". Then we can move our hex file into the PIC clicking the COMMAND, PROGRAM ALL buttons.
Icprog then programs the PIC and does a verify at the end to make sure that what is in the PIC is the same as the hex file.
However we sometimes are not sure which hex file we have in the PIC at that moment. So we can do a recheck.
The dummy program Firstest.asm has enabled us to use many menus in the chain that takes us from untested code to compiled code and putting it into the PIC . In other words, we have tested a complete chain. So now we will test a simple module to learn a little more about what the PIC expects
A simple Hex to Decimal routine
In my snippets box, I have pulled out one of the partial routines or code blocks which I had to invent as the bike project developed. It is called myh2dec.asm.
In the first lines of the program I write it’s purpose and the date. I see I also commented on routines on offer in the internet, which simply didn’t work.
The routine itself is very straight forward and it’s purpose clear. The main program does all it’s calculations in Hexadecimal, but when we need to show the results on a Liquid Crystal Display, an LCD then we must change the hex into decimal.
For example, suppose the speed module calculates the present speed is 0xD.
Ah , 0x is the way we say that the following number is hexadecimal, so 0xD is 13.
If we don’t change to decimal, the LCD will show maybe Speed D km/hr
Not very useful, How about Speed 1A km/hr, ( 26km/hr I hope)
Lets comment the program to change the hex to dec. I use it extensively. It is slow but works fine.
I’ll add new comments useful to us for study,
in a different font letter style
;-----------------------------------------------------------------
; myh2dec.asm version 1 Sept 01 written by Fred
;-----------------------------------------------------------------
;
; MY HEX TO DECIMAL FOR LCD
; The ones from the internet simply didn't work, this is slower ; ;but incorporated into bike asm works fine.
; All of the lines above begin with ; and are just comments, they are useful for the ;information they contain, especially the date and version +My grumbles on wasted ;time
; the next three lines tell the compiler what microcontroller we are using. , Include
; has standard definitions, and _CONFIG is shorthand for no code protection, no
; watchdog timer, the oscillator type is an XT crystal and power up delay active
LIST P=16F84A ; 16F84A Runs at 4.000 MHz
INCLUDE "p16f84A.inc"
__CONFIG _CP_OFF & _WDT_OFF & _XT_OSC & _PWRTE_ON
; the error level comments are clear enough
ERRORLEVEL -224 ; supress annoying message because of tris
ERRORLEVEL -302 ; supress message because of page change
; The cblock is where the variables are defined. Note the starting address is given 0x0C
cblock 0x0C
ones
tens
hex
count
ENDC
; There is no interrupt routine ( jump to ORG 4) so not here but it is in the main prog.
ORG 0 ; start at location 0
GOTO Main ; jump over to main routine
; Here the program proper starts. Note that the Labels ( ending ":") are in the first line.
; Yet when you use it in a command, don’t write the ":" e.g. GOTO Main
; also if main and Main are written differently( case sensitive) ;then, we get an error ;message ( not found or not defined) .
Main:
clrw
movwf count
movwf hex
movwf ones
movwf tens
movlw 0xF
movwf hex
; put all variables to zero just in case they have been used else where and have ;a number inside them
; The conversion to decimal is very simple. A counter COUNT which initially is ; set to 0, is compared to the HEX number that is to be changed into decimal.
; if they are different, the counter increases by one. At the same time the ;variable "ones" also is increased by one, so they are the same. However
;when count increases from 9 to 10 (0xA), ONES is reset to 0 and TENS by+1.
; This means that for ever 10steps, units go to zero and decades increases by 1 ; Put another way we have converted to decimal and can now send the HEX
; equivalent as two decimal digits to the LCD
;The process stops when the xor test sets the STATUS, Z flag. When HEX and
; COUNT are NOT the same, the btfsc command skips and the conversion ; continues.
; When they finally are the same the xor gives 0 and so the btfsc test does not ;skip letting the
GOTO Decout instruction be executed and we exit the ;conversion. This system can easily be extended to hundreds, thousands….
H2d: ;change hex into dec, start all zero
movf count,w
xorwf hex,w
btfsc STATUS,2
GOTO Decout ; they are equal
incf count,f ; count < hex
incf ones,f ; this may make one = 10 so test
movlw 0xA
xorwf ones,w
btfss STATUS,2
GOTO H2d ; ones is not 10
incf tens,f ; ones is ten, so add 1 to tens
clrf ones ; put ones to 0
GOTO H2d ;
Decout: ; now tens and ones hold decimal o/p
nop
GOTO Decout
end
;------------------------------------------------------------
; End of Hex to Dec routine
;------------------------------------------------------------
Debugging " myh2dec.asm" with MPASM
Well now that we have added more comments to the file and think it is all right, let’s see what
new windows in MPASM we can use to test it.So as not to change things too much, we will use the same picproj directory after removing the bits and pieces of firstest.asm.
Just let’s compare beforehand what we are going to do with the steps we had to follow before . This time there is no setting up needed. MPLAB will remember unless told otherwise, that we are debugging a program.
Recalling the things we did, we set up:-
Next the sequence to start a new project. This a little like putting the cart before the horse, but we will name the new project the same as the assembler file, myh2dec.prj
(This is very necessary when we start debugging lots of different routine modules. E.g. Test 22.asm is a bit meaningless and can be downright confusing, when a couple of days have passed and we are back tracking trying to remember the last module that worked before changing.)
The names themselves, can be different but you must always have the file in the same directory as the project. Otherwise MPLAB refuses to go on.
So we open MPLAB and follow use the following menus
Select the text we have above for the hex to dec routine. Copy everything.
We should have copied
;-----------------------------------------------------------------
; myh2dec.asm version 1 Sept 01 written by Fred
;-----------------------------------------------------------------
;
; MY HEX TO DECIMAL FOR LCD
…………
……………..
…………
……….
end
;------------------------------------------------------------
; End of Hex to Dec routine
;------------------------------------------------------------
In MPLAB click on File in the top Bar
Select NEW
An empty file window opens
Use control V
To fill the empty window with the text we have copied
Save
Name as myh2dec.asm
We can go back to the PROJECT sequence, we were at point 6
The new project has been opened, named and connected to a file with the extension. Asm.
Now if we are lucky, when we click Build All in the project menu it will compile with no errors. But if it doesn’t look for items which have moved from their position because of moving the copy from the word. Doc, or comment ";" characters which have got lost.
Note When I did this text move I got the following warning messages.
Building MYH2DEC.HEX...
Compiling MYH2DEC.ASM:
Command line: "C:\ARCHIV~1\MPLAB\MPASMWIN.EXE /e+ /l+ /x- /c+ /p16F84A /q C:\PICPROJ\MYH2DEC.ASM"
Warning[205] C:\PICPROJ\MYH2DEC.ASM 21 : Found directive in column 1. (ERRORLEVEL)
Warning[205] C:\PICPROJ\MYH2DEC.ASM 22 : Found directive in column 1. (ERRORLEVEL)
Build completed successfully.
(The error level instructions had drifted to the left during the move.
So once moved away from column 1, the compile was then warning free.)
So once we have compiled the project error/warning free, we will start to debug and open some of the menus.
We press F5 and F6, we should see we have jumped to the start of the code.
So to start with we have the following view
. fig 23
Good. Now we can experiment advancing the program line by line to see where it goes during its execution. We SINGLE STEP pressing F7.
Try it. Return to the start, pressing F6
Now this is good, but we need to watch the values of the variables that we are using.
We click on WINDOW, watch window, New Watch window.
Fig 24
We need to follow the changes in the four main variables of the hex to dec module. So we look in the list and select count, tens, ones, hex.
These then appear in the previously empty Watch Window. Their initial colour is black, but every time the project passes through a line where they are named, they will change to red if they change actual.
Let’s position the Watch window Fig.25
on the right and resize with the mouse.
Start stepping with F7. The values in the watch window will change colour and increase as the program advances. It will finalize when COUNT is equal to HEX. It will then stay trapped in a little loop the just repeats itself.
The snapshot on the right shows the variables when count has reached D and tens +ones show 1 + 3
Read together = 13
The F5, F6, F7 and ( control + F9 ) keys
We will introduce two more keys. F5 and F9.
F5 stops execution when the program is running
F6 resets to the beginning of the program
F7 allows single stepping.
F9 starts the program, but it goes so fast we can’t see much, just the time that is passing.
So we need an execution speed that is somewhere in between F7 and F9
Control + F9 is what we need, slow execution.
We can still see the variable changing as the program advances
We will stop, reset and start again
Please note that reset means jump back to the start. It does not put the variables to zero. Some code lines in your program will do this if you want it to. Other times you will want to leave the variables alone and hold their values till needed later.
So run the program a couple of times looking at the variables as they change. Also when we jump back to start with F6, the variables hold the last values.
Finally try to run using F9 by itself. It is so fast that we don’t see the variables changing. Use F5 to stop the program, then you can see if anything has changed.. No’
But it has changed, all the variables have been cleared and the program starts each time at the beginning when we press F6. The reason we don’t see the change with F9 is that it is going full speed and takes very little time to reach the .
We will open another window now to do some time measurements.
This is very useful. It shows the time that an action takes, the action can be moving one line or cycling through the whole program.
Lets try timing how long the conversion of hex D to 13 takes. We will use the
Control F9 combination as F9 alone , is too fast. Right ,we see the program is at the end and in the do nothing loop, so stop and read the time taken, a little over 200usec.
If we watch, a few other things become clear. When we reset with F6, the time also goes to zero. If we don’t want this, untick the " clear on reset" button. It is also clear that the exact conversion time is difficult to gauge just looking at the variables and hitting the F5 stop button. So lets make the Decout label a break point
We get there from Debug, Break setting, Break point settings
Next click the start arrow and we are shown the avail points for setting a break. We select Decout and click Add then close.
Now when we run the program see how it stops when it gets to Decout.
Look at the time on the stopwatch, it is shorter than we thought, 182us.
The logical question that arises here is… is this a short time or a long time ? Well that depends on your application. In the bike application we have about 1 second between refreshes. Also the main calculations are done in a couple of milliseconds. That means we can spare some time to convert to decimal. Also the speed and the time and the Average velocity are all small numbers.
The distance is the exception, we measure up to 99999 …. ( good grief at first sight it will take more than a second. E.g. 99999 x 100us is 9.9999 secs….)
So as a little exercise lets add on a couple of more variables and expand the hex to dec routine to be able to measure up to 99999 . You do it . You just need to copy the loop adding new variables at the start. Lets call them hund, thou, tenthou… enough for 999999. Also we need more hex variables to hold the start number . The max for our variables is FF= 255. two variables in series would hold 65536 as FFFF.
But can you do this just like that?
Well yes and no, we need to program a bit more then?
Yes.
Ok to keep it easy we will stop at 255, making hex initially FF.
When we measure the time taken about 3 ms, a rough comparison gives that count to 15 ( subtracting the initial set up) takes about 165usec. So a count of 1 is around 13 to 14us. 255 x 13 = 3.3 ms. OK this scheme is linear. The bigger the number to convert , the longer it takes. You say then
"Well if it takes 10 sec to count to 99999, the bike is doing nothing when distance is a big number."
Fair comment, now think of a solution … an easy solution.
While we are doing all these modifications, have you noticed that just touching the asm file means we have to recompile all over again. That is, go to PROJECT menu and Click Build all. Get the message all correct and then run the new version.
This is inescapable. MPLAB has no way of knowing what has been changed. She simply knows that the file is different. So she won’t let you run the program until the program has been recompiled in a project rebuild
Another detail.
If at some point we stop to reset the Stopwatch to measure some lines of code. When we try and restart by pressing F7 or F9 or F9+control. It doesn’t start. The reason is when we have several visible windows, we need to have the program window active, or selected to make it respond to our key presses. Fair enough but not too obvious in the beginning
( This is for those of us who have never heard of a stack)
In the windows menu there is another option called stack. If we click on it we gat a new window simply called Stack window. It is blank initially with a cryptic message
"1 Return address"
This window shows you how many sub routines are being used at a given moment.
A little more information is necessary. It only tracks subroutines that are started with the CALL instruction, not with the GOTO instruction. You can jump about all over the program with GOTO instructions and the stack doesn’t care .
CALL is different. You go somewhere. OK, same as the GOTO. Yes but the GOTO doesn’t return to where you jumped from, the CALL instruction does.
When the CALL is finished, you expect to be able to carry on with what you are doing and so you have to store critical values before you go to the CALLED routine. There you will do something and could change the old value of the CALCULATION register, which we know by now is the W register. So we store it and some FLAGs, If these are changed or aren’t changed, we replace the old values anyway when we come back.
But can you call with in a call? Yes up to a value of seven calls. This is called nesting. The PIC has a stack depth of 7.
Not clear. OK, we’ll try and invent an example with a car computer. . We are cruising along at say 100km/hr. The computer get s an alarm that something is wrong with the engine.
We store speed in stack level 1 and call motor
Motor may have a couple of possible values but as this is an alarm we store checking water temp as Wtemp. In stack level 2
Motor checks the various processes and finds a flag in oil.
Checks oil and there is oil. Notes level =1 stack level 3
Oil module checks temperature and it is now normal, 180ºC Stack 4.
Sorry about the example it is not very good.
The only thing I want to say is we have to keep track simultaneously in the working register of 100, 2, 1, 180.
You can’t jump back with the value 180 as you were driving at 100. So you invert the process going back through the stack and restoring the values to each level.
Is seven levels enough? Depends how you program, but personally if I run into the situation I try and change the sequence of operations or change some CALLs into GOTOs. We have inserted these calls into the program and this is what we see when all the calls are active. Fig.28
The program does nothing for as each call routine has no operations except the last call. Then it see return followed by return, followed by return until we are back where we jumped from ready to accept the next instruction a GOTO. The GOTO also jumps but doesn’t return.
Please note that the last called, temperature is the top of the stack. So we can imagine if we try and call more times than the stack has levels, the first one called , Speed, will just vanish as it is pushed out of the bottom of the stack..
Here we can stop and take stock of the various new windows we are now using in MPASM. Of course there are more, but we have sufficient to do normal code development with the following windows SIMULTANEOUSLY in use ( i.e. opened for us to see what is happening).
The Menus we have used till now
The rest of the menus offer options that are needed but not essential at the moment.
As you get more hands on experience you will gradually open these menus to find features you hope exist in MPLAB. We will only mention specifically two of the menus.
HELP for example, offers TOO much and is complex, hard to use for a beginner.
EDIT, is simple but you may prefer to use your own favourite one.
EDIT takes us on to the next topic about getting started…
There are lots of little fiddly things about the way we WRITE the code itself. We notice that we can enter numbers in various formats, specifically in the beginning we are interested in Hexadecimal, Binary, and Decimal. Now MPLAB is very good in the sense that we can enter all three number formats providing we use the correct format designators. You know, give MPLAB a clue as to what you want to do.
The basic enter a number command is movlw
This is followed by the number. When I started this caused me a lot of confusion, especially the decimal form. The problem is that MPLAB accepts different ways of writing the same thing
Lets look at the ones I have found, ( there may be more).
A simple test is write them as a code fragment and use the MPLAB compile to see if it is accepted.
Copy the program and compile it.
Note. The CODE lines that DON’T work, or do something different are preceded by ;
Try removing some , compile and see the warning/error message. But put the
Comment ";" back , recompile then move on.
;-----------------------------------------------------------------
; numsysts.asm version 1 March 02 written by Fred
;-----------------------------------------------------------------
;
; A LOOK AT NUMBER FORMATS THAT MPLAB WILL ACCEPT
; We test various ways of presenting numbers to MPLAB
LIST P=16F84A ; 16F84A Runs at 4.000 MHz
INCLUDE "p16f84A.inc"
__CONFIG _CP_OFF & _WDT_OFF & _XT_OSC & _PWRTE_ON
; the error level comments are clear enough
ERRORLEVEL -224 ; suppress annoying message because of tris
ERRORLEVEL -302 ; suppress message because of page change
; The cblock is where the variables are defined. Note the starting address is given 0x0C
CBLOCK 0x0C
temp ; to hold a number for operations that can't be done on w.
ENDC
; There is no interrupt routine ( jump to ORG 4) so not here but it is in the main prog.
ORG 0 ; start at location 0
goto Main ; jump over to main routine
; We use the constant entry command MOVLW to try out as many variants of
; the different number forms that we have found MPLAB accepts-
; Note the instruction movlw can be written with / without capital
; letters. But to give the code we write a coherent appearance we will
; always use SMALL letters
; open a watch window for temp and w
Main:
;Test entering number 0. MPLAB accepts all the combinations
;of UPPER and LOWER case letters.
clrf temp
movlw 0
MOVLW 0
Movlw 0
movlW 0
; We also have offered 0 with no format, just plain 0
; Let's repeat with formats to see what happens.
movlw 0x0 ; Hex
MOVLW b'0' ; binary
Movlw D'0' ; decimal
movlW h'0' ; hex
movlw 0H ; Hex
movlw 0h ; hex
; just too many hex forms, be prepared. I use 0x as the hex identifier.
; Maybe another number would get different
; treatment, try with 9
; Note we will leave in place a format combination with
; a fault and write in the line below the "good" version
movlw 9 ; none
movlw 09 ; none
movlw 0x9 ; Hex
;movlw b'9' bin nums are only 1's or 0's
movlw b'1001' ; this is OK
movlw D'9' ; decimal
movlw h'9' ; hex
;Lets play a little more with the binary line (make it 5 to
; stay inside the 8 bit max)
movlw b'0101' ; this is OK
;movlw 0101 ; 2
;movlw 0102 ; 3
; MPLAB accepts 2 and 3 but we start getting strange warnings
; about CBLOCK. 0101 and 0102 can also be decimal or hex.
; Note one 8bit byte can hold up to max
; B'11111111' or D '255' or H'FF'
; let's test what MPLAB THINKS THEY ARE.
movlw b'0101' ; this is OK
movwf temp
incf temp,w ; if binary will be hex 5
; if decimal will be 0102,
; if hex will be 66 hex
; that has clarified, the number after adding 1 is 6,
;MPLAB treats as binary
; and if we take away the ' ' ? It will object to the b.
;If we also remove the b? well lets test
; movlw 0101 ; getting dodgy, MPLAB sees as 1
; movwf temp
; incf temp,w ; w is now 2
;Last tests on the decimal format
clrf temp
clrw
;addlw D255 non starter MPLAB SEES A NEW VARIABLE D255
movlw d'255'
movlw D'255'
movlw .255
; These three are equivalent but the last is almost dangerous
; often I have missed or misinterpreted the point.
oot:
goto oot
end
; to be consistent I use the small letter forms
; b' 'and h ' ' and d' '
; though personally I prefer the 0x for hex
;------------------------------------------------------------
; End of Numsystems
;------------------------------------------------------------
You can write the code in many ways, MPLAB is quite tolerant compared to other compiling programs. However it has some predefined words for variables/registers.
Where are they? Well after any compile when you have a watch window opened, the variables in your program become available for watching.
But you will also see a lot more that you didn’t add… often used ones,
with two forms like:-
Looking at the top one, the STATUS register, you need to add THE BIT NUMBER you want to observe , for example after doing an operation on a number, the result logically can be zero or not zero.
So we can bit test the status register to see if the zero flag is set with
the command 1 or z
btfss status ,1 BAD |
|
btfss STATUS, 1 GOOD |
|
btfss status, z BAD |
btfss STATUS, z BAD |
btfss status, Z BAD |
btfss STATUS Z GOOD |
Why only capitals? I don’t know, but MPLAB only will accept the upper case version for these registers. The important thing is to know what can happen and use the correct form of the word.
In the code I have written there are two groups. All MPLAB predefined names like those for STATUS, are in upper case.
My variables, my labels and all instructions are in lower case. Only the first letter of a label is in upper case. These helps me to see what is what in the code that is written.
But I insist, you can write the code anyway you want, providing you use the UPPER CASE where MPLAB seems to need it.
Most of us will have written code in other languages and have generated a few pet routines.
The practical difficulty with assembler is that it is so elemental that a lot of routines cannot be reused as the commands don’t exist.
Multiply, divide, subtract, add all need routines to be invented when we move away from simple bit operations.
Fortunately, many people have already sat down and sweated away forging these fundamental routines. Many of these routines and many of their inventors are to be found at PICLIST. COM on the web.
This is a huge very complete site on almost every aspect of code. To be more exact SOURCE CODE. There must be hundreds of examples classified under headings like math, lcd, … you name it.
We will lean very heavily on the site and refer to specific code routines already written
to get a second and probably better interpretation of how to do something.
The hex2dec Time problem with Big number
In the Debug Break settings paragraphs, we mentioned that the hex2dec module could have problems. We looked at the h2d routine, myh2dec.asm. It was fine for small numbers, with the conversions taking about 100usec…. But the big numbers like distance max, 99999m would on this basis take 9.999 seconds, too long.
There we were asked for a solution, a simple solution.
Well now for the answer.
Consider 99950 metres. Starting at zero and advancing one by one till we arrive at 99950, indeed would take a long time . But we can use the same routines for hex decimal conversions in a subtly different way. The variables instead of being recalculated can be INCREASED. So in the DISTANCE module we only have to e update every second the variables to show the INCREASE IN DISTANCE.
So the code instructions do not have to convert the whole number from scratch, but increase the numbers already held by ( 2 or 4 or 53 m), a small quickly executed increment.
This is a very different kettle of fish.
How long? We only ask the DISTANCE module to UPDATE the already calculated numbers by 2. The time required, once more drops to usecs.
We will see this soon in practice in the BIKE COMPUTER program
If you don’t see the difference immediately in the time involved, don’t worry, I didn’t see it either, until a lot of different ways were tested. But for the moment we accept that INCREASING the already calculated variables by 2m will take very little time,…. we have measured typical low number times of 60us and high number times of about 350usec.
The Bike Computer Flow diagram, simple version
So we are about ready to do some practical programming. The BIKE COMPUTER will be our model. Not that it is a brilliant piece of code writing, far from it. Let’s say it is useful because it is a first attempt and the code is large and simplistic. But that is where it helps us, it is indeed simple to follow. The blocks are well defined and reuse of variables is minimal. Even so we have the advantage of being able to study a big program that works.
How does it work? As it is all written, we can use a flow chart to show the functional blocks. Lets make a simplified flow diagram of the bike computer. Here we see the main blocks only. The very necessary routines that do the calculations and respect the protocols for timing and data transfer to the LCD display are better described in the discussion of each module’s features and operation.
As we read through the program listing, we will describe/ discuss the main points of the operation of the Bike computer program’s modules.
These being:-
At last we have a flowchart, which illustrates the major blocks of code within the bike3.asm program.
The diagram needs very little comment.
The action starts at the top left. There are a series of initiate routines, for registers, ports and LCD.
Once these have been completed we go to the top of the second column and are at MAIN. The firsts Label in main is Changesec. Changesec is the start of a cycle as it is the zero time reference for all the actions that follow.
It also implies that all that follows happens inside 1 second. This is true if the bike is moving faster than 6.4km/hr.
We start as a new second occurs and lets us escape the CHANGESEC loop
We move on to the wheelpulse loop. How many pulses can we have in a second?
Well to get a feel for the real life quantities, 2pulses/sec =15km/hr,
4pulses/sec =30 km/hr. looking at this another way
Expressing these times in ms…
There are limits to this system. It tends to give a jumpy speed indication above 50km/hr and ideally we should integrate taking the mean of several readings.
On the other hand there are few cyclists capable of maintaining more than 30km/hr on a level road as the HP you are using means that the quantity of calories needed rises very quickly to keep you going at these speeds.
Another factor is the resolution. Well this system detects ¼ ms intervals. This means for example that we can divide the time difference for the km between 15 and 30km/hr into 250 x 4 steps = 1000. Therefore we can display in steps of (30-15)/1000 km.
This 0.015km/hr. We could have a display then, like 15.28km/hr. But in the real world we don’t need so much and would round to one decimal place 15.3 km/hr.
We were talking about the Wheel pulse measurement limitations. Now the normal sequence will be measure the time between two pulses. This is a large routine but not very hard to understand reading the notes beside each piece of maths.
What are two pulses? They are the sensor output each time the magnet passes it.
They represent a distance of 2.0747m But we use two and compensate every so often adding the ignored 0.0747m.
So speed is obtained from Distance / Time, here = 2.0747/( Pul2 – Pul1 )
The transformation to km/hr is simple, multiply the result, m/s, by 3.6
Just a little aside . The same incoming pulses flash a Green LED briefly. This does two things which are useful for debugging, but doesn’t offer any other feature, except maybe at night, you could judge your speed from the cadence of the little green flashes.
The menu variable increases every second by 1
The top line of the LCD is always Elapsed Time
The Bottom line of the LCD changes according to the value of Menu
When menu is in the range 1 to 10 the LCD lower line displays Speed
The Chart arrows lead us to Distance:
When menu is in the range 11 to 20 the LCD lower line displays Distance
The Chart arrows lead us next to Average velocity:
When menu is in the range 21 to 30 the LCD lower line displays Average Speed
We then loop back and wait for a new second.
To sum up. Every second the menu loop calculates
But only one of them starting with speed, is displayed during ten seconds before the display updates and shows the next parameter, Distance for ten seconds, then the next Average Speed for ten seconds.
30 seconds have passed and menu increases to 31. This triggers a reset.
Menu resets to zero and the cycle repeats, SPEED, DISTANCE, AVERAGE SPEED in an endless loop
The Complete Loop Execution Time
Well as we are confined to a 1second cycle for everything to be done. Are we in trouble trying to get everything done?
Well fortunately the instruction execution time is nominally 1 usec.
The SPEED DISTANCE and AVERAGE SPEED modules are complete within about 2msec each when the numbers get big. This is worst case.
What takes a bit more time is getting the data onto the LCD, about 20ms. ( don’t forget there are two lines).
So being really pessimistic we need 30ms to cycle the menu + 2 display lines
A second has 1000 ms. This means that we can only handle a maximum of 33 wheel pulses per second..
Is this enough? Well each pulse represents about 2m. If we con only handle 33 in a second, then the maximum speed we can measure is about 66m/sec
Or 237km/hr or 150mph. This would seem to be sufficient for all normal cyclists.
We read of them in the introduction and in various parts of the following chapters. The initial project had more facilities, but at a certain point, it was discovered that we had run out of memory.
The variables you can have directly are 67, The cblock holds them in continuous space from 0Ch to 4Fh. There are many things you can do to get more memory.
But this is supposed to be a SIMPLE bike computer, and using sophisticated circuitry hardly helps to write a clear easy to understand article.
That is why it was decided to stop with a minimal , but functional block of code. One of the compromises was to complete all the actions inside the second. If something took long, well that could be accommodated in the next version.
Also in the previous paragraph we mentiones mph. This was to be another option of the computer, display in Miles or in kilometres. It too will have to wait.
We have talked a lot about functional blocks, but there is a lot of reused code which is common to all modules, the nuts and bolts which make the program possible and hold it together!
We must include in these categories two areas about which we have said very little.
All of the basic routines for hexadecimal maths
are freely available on the internet. They come in a multitude of shapes, 4 , 8, 16,24,32…bit.
There are just two little problems. Many of them aren’t what you want and, many of them are theoretical, and hard to put into assembler code ( a euphemism for maybe they never have worked).
Another more subtle problem is that some work with the example figures but not with others…. Gulp!!!
We need routines that work with all figures inside the bit range required.
So you can imagine that this was a long process narrowing down what was available and what was required.
Fortunately in the PICLIST. COM, you will find the routines that I have used and many others, which also work, but whose operation is a bit too subtle to be included in this SIMPLE first time code effort.
These have been kept to a minimum . Basically the Bike computer uses
An EQUAL TEST, which is the xorwf instruction. STATUS, Z
A less than equal =< coupled with greater than > test
Which use these instructions e.g. subwf dtothi,w followed by btfsc STATUS,C
These, AND MANY MORE, are well explained in the articles in PICLIST.COM.
The three main types that had to be invented were converting
This we have already discussed h2d in previous paragraphs but the basic routine is used in the three calculation modules for, SPEED, DISTANCE and AVERAGE SPEED.
You should be able to follow the conversion routines after a little bit of study. The notes in each routine to say where you are in the process are abundant.
HEX FRACTIONS to DECIMAL FRACTIONS
This merely is an extension of the previous routines with a little bit of reasoning to change hex remainder and fractions into decimal fractions.
The fraction routines are needed because the pics don’t handle directly fractions, so they have to be changed into whole numbers by suitable multiplication.
We do a lot of multiplication and division in the three main calculations for Speed, Distance and Average Speed.
But we have a practical 16bit number limit. The numbers can’t be bigger than 65536.
If they are, then we need to add more registers, and probably bigger calculation routines able to handle more than 16bits. So I decided to try and stay as far as possible within the 16bit routines.
In some places you divide and the result of the division has to be multiplied by another number. For example 2000.
So you divide your first two numbers. The result is say ten.
Then the next operation has no problem because 10 x 2000 is 20000 which is< 65536.
However if the first division had given as a result 300, then we have a problem.
300 x 2000 = 600000 way above the limit of 65536 we need to scale somewhere.
The scale factor here would be about 20. But this is one of those things that is best seen following the steps the code takes , say in the SPEED block.
Basically the number is tested for size and divided down until its product with a reference number is smaller than 65536. But as we said above, follow the steps taken in the actual code block to see how the scaling is done to solve a specific application problem.
LCD Setup, Signalling and data transfer routines
The LCD itself has on board a complex controller chip whose job is to interface with a uController, in this case our 16F84A and steer the incoming data messages to the correct position for display.
There are two basic operation modes; 4 bit and 8 bit. Most applications seem to use the 4 bit mode as it will free 4 lines of a uController to do other things.
The data is strobed into the LCD using appropriate signals on the E and RS lines. The protocols are also time sensitive. They have to be as long as the minimum times quoted on the LCD data sheets. Here lies a problem.
The LCD chosen is very popular. It has many clones. SOME of the clones need EVEN longer times. This means that the typical DELAYS programmed could be insufficient.
Before anything can be displayed, the LCD needs to be initialised. This is a really slow process taking about 50 to 100ms. The number of times the routines are repeated during the initiation sequences seems a little exaggerated but it has to be that way to get the LCD up and running, it is exactly what the manufacturers spell out on their data sheets.
Each command or data interchange between the PIC and the LCD has it’s corresponding minimum time. These have all to be scrupulously observed or the process fails.
That is the reason why there are so many different time blocks within the delay module plus some additional ones for other tests.
All of the routines can be found on the PICLIST.COM site. I take my hat off to whoever sat down and worked them out first time round, a really heroic and Herculean task.
The operation of Automatic menu was explained previously under the heading of that name when we were looking at the Flow Chart.
However this was one of two possibilities in the original project, the other, manual function selection needed two push buttons plus the corresponding code so that was dropped as the PIC was running out of space.
There is nothing special about the way the menu presents the three main features. Their combinations features to the LCD can be changed if you prefer. For example, maybe you would like to see at once Average Speed and Total distance, or maybe Speed and total distance.
There are many combinations possible. Of course if you do change then don’t forget to follow through into each module that is being used to see that there is no other unwanted effects.
We can now study the complete program which is called bike3.asm
With the help of the previous Flow Chart which shows the sequence of major functional blocks of the program, and reading through the code below, it should now be possible to understand, hopefully fairly easily, how the code blocks have been cobbled together.
Made to implement the basic equation we started out with.
Speed = Distance /Time.
;----------------------------------------------------------------------;
; bike3.asm Combines: Time , Dist, Av velocity, Inst Vel
; Fred Maher 1st March 2002
; BASIC BICYCLE COMPUTER WITH THESE FUNCTIONS
;----------------------------------------------------------------------;
; 1 Trip Time HRS:MIN:SEC
; 2 Trip Dist 000km 00m
; 3 Trip AvSpd 00.00km/hr
; 4 Speed 00.00km/hr
;---------------------------------------------------------------------
;------- HEADER ----------
;---------------------------------------------------------------------
; LCD MESSAGES
; position at beginning of 1st line col 0 movlw H'80
; position at beginning of second line movlw H'C0'
LIST P=16F84 ; 16F84 Runs at 4.096 MHz
INCLUDE "p16f84A.inc"
__CONFIG _CP_OFF & _WDT_OFF & _XT_OSC & _PWRTE_ON
ERRORLEVEL -224 ; suppress annoying message because of tris
ERRORLEVEL -302 ; suppress message because of page change
; Define Information
#DEFINE RS PORTA, 2
#DEFINE E PORTA, 3
;#DEFINE TOGGLESW PORTB, 6 ; not used at the moment
;#DEFINE LED PORTB, 5
; Macro
;---------------------------------------------------------------
CBLOCK 0CH ; from 0C to 4F = 67
;---------------------------------------------------------------
; MATH ROUTINES
; 16 X16 Mult , --> 32
; mah mal x mbh mbl = mq4 mq3 mq2 mq1 ( mq4 highest)
mq4
mq3
mq2
mq1
mbh
mbl
mah
mal
; 16 / 16 Div --> 16
denlo
denhi
numlo
numhi
rmdrlo
rmdrhi
reshi
reslo
;temp also used in nybble
; Addition 16+16 out max 65536 (no carry)
; Subtraction q1_16-q2_16 out r_16; neg numbers not allowed
q1hi
q1lo
q2hi
q2lo
rhi
rlo
;------------ Distance -----------------------
dtotlo ; total trip distance in metres
dtothi ; dtothi/lo hold a max of 65km 536m,
dtothi2 ; with this added max now 16777km
dist54 ; corrects +2m every 54m (27x2.0747 =56)
dm
dm10
dm100
dm1000
dm10000
dm100000
;------------ Average velocity Vav -------------------
;vavhi
;vavlo
;divrat
;stemphi ;second info passed to stemphi/lo
;stemplo
;dtemphi ;dist info passed to dtemphi/lo
;dtemplo
m
m10
m100
m1000
;--------- Time and trip time --------------------
;
sectotlo ; total trip seconds
sectothi ; max 65536 = aprox 18hrs
sec ; seconds digit
sec10 ; 10's of second digit
min ; minutes digit
min10 ; 10's of minutes digit
hr ; hours digit
hr10 ; 10's of hours digit
oldsec ; holds last value of sec
cntmsec ; count ms
TMR18 ; TMR0 217* TMR18 18 = 1 SEC
;---- isr interrupt service routine push pop -------
w_temp ; W isr var
status_temp ; STATUS isr var
fsr_temp ; FSR isr var
; --------- Instantaneous Velocity Vin --------------------
vinhi
vinlo
sectemp
oldtemp
totmslo
totmshi
totms2lo
totms2hi
mshi
mslo
spdflg ; 1st pass =0 2nd pass =1, diff is time between pulses
;-------- Miscellaneous ------------------
temp ; a temporary used in divide and nybble
count
menu
ENDC ; end of definition block
;-------------------------------------------------------------------
ORG 0 ; start at location 0
goto main ; jump over to main routine
ORG 4
goto Isr ; jump to interrupt routine
;----------------------------------------------------------------------;
; The Main routine ;
;----------------------------------------------------------------------;
; -------- THE MAIN MODULES ---------
; Isr stores elapsed TRIP time and generates hr:min:sec
; Disptime TRIP time, to max 99:59:59
; Dist Calculates total TRIP distance
; Dispdist TRIP distance to a max of 999km 999m
; Velav Average velocity, Totdist/totsecs max 99km/hr
; Dispvav Average TRIP speed up to 99.99 km/hr
; Velocity Speed from 2m/(time diff of pulse2-pulse1)
; Dispd Speed up to 99.99km/hr
; NOTE. Speed is your calculated velocity every second
; -------- more to come??? NO, running out of space ---------
;-----------------------------------------------------------------------
; M A I N
;-----------------------------------------------------------------------
main:
call Init ; Initialize ports, set up timer
call Initlcd ; Initialize the LCD DUMMY
Initend:
;------------------------------------------------------------------
; Changesec Changewhlpulse wait for interrupts from newsec or newwheelpulse
; -----------------------------------------------------------------
clrf menu ; initially set to 0 menu loop
clrf spdflg ; Initially = 0 2nd = sub for time from zero
; every new sec, set to zero
; CHANGESEC LOOPS TILL SEC CHANGES
;clrw ;DUMMY
;xorwf sec,w ;DUMMY
;btfsc STATUS,2 ;DUMMY
;incf sec,f ;DUMMY
Changesec: ;loops checking new sec and new wheel pulse
movf oldsec, w
xorwf sec,w
; if equal, w= 0 and Z bit=1
btfsc STATUS,2 ; test Z bit
goto Changesec ; no change, loop
movf sec,w ; sec has increased, update
movwf oldsec ; sec and oldsec are = again
incf menu,f ; increases menu every sec
; Note1, sectotlo/hi are updated every sec in ISR module
; Note2, Decimal time also created in ISR module
; but time display for LCD controlled from
; the automatic menu below
call Disptime ;TRIP time, will appear on LCD top line
Changewhp: ; 1st detected wheelpulse starts menu sequence
clrf PORTB
bcf PORTB,5 ; start with green LED off
loop:
btfss PORTB, 4 ;(PUT BTFSC FOR TEST); i/p hi,?DUMMY
goto loop ; not yet
; i/p hi detected
;green LED flashes with every wheel pulse
bsf PORTB, 5 ; LED on
; wait a while to make sure switch has
; settled
movlw D'10' ; wait about 10 msec
call nmsec
btfsc PORTB, 4 ; will be lo (0) when finished
goto $ -1 ; still low
; now must wait a make sure bouncing stopped
movlw D'10' ; 10 milliseconds
call nmsec
; and check again
btfsc PORTB, 4 ; if set, not finished
goto $ -5 ; still hi start debounce wait again
Tp1: ; green LED ready for next wheel pulse
bcf PORTB,5 ; i.e. LED off
movf PORTB,w ; reading to clear
; AUTOMATIC MENU: (LCD Top line always time)
; --------------
; menu selects at 10sec intervals in rotation
; Speed menu 1 to 10
; Dist ance menu 11 to 20
; Velav Avg vel menu 21 to 30
; MENU 0 TO 10
; as speed ( Speed) is stand alone, no sense in calculating it
; if it is not going to be displayed when menu is >10.
; BUT if SPEED AVERAGING IS TO BE USED, kill off the lines below that bypass
; the SPEED calc module
Menucheck:
movlw 0x0A ;check menu > 10 (to meet >10 AND =<20)
subwf menu,w
btfss STATUS,C ;compare with 10
goto $+2 ;menucount =< 10 not yet got to 11 jump to Speed
goto Distblk
call Speed ; Calculates instant speed,(time between pulses)
movlw 0x0A ; DUMMY, was movlw 0xA
subwf menu,w
btfss STATUS,C ;compare with 10
call Spdflgcheck ;menucount =< 10 ; also see 0-1 spdflg
goto Distblk
Spdflgcheck:
clrw
xorwf spdflg,w
btfsc STATUS,Z
call Dispd ;menucount =< 10 AND spdflg =1
Return
Distblk:
call Dist ;menucount > 10 skip Dispd update Dist
; MENU 11 TO 20
movlw 0x0A ;check menu > 10 (to meet >10 AND =<20)
subwf menu,w
btfss STATUS,C ;compare with 10
goto $+5 ;menucount =< 10 not yet got to 11 jump to velav
movlw 0x14
subwf menu,w
btfss STATUS,C ;compare with 20
call Dispdist ;menucount =< 20
call Velav ;menucount > 20 skip dispdist update Velav
; MENU 20 TO 30
movlw 0x14 ;check menu > 20 (to meet >20 AND =<30)
subwf menu,w
btfss STATUS,C ;compare with 20
goto $+5 ;menucount =< 20 not yet got to 21 jump to NEXT
movlw 0x1E
subwf menu,w
btfss STATUS,C ;compare with 30
call Dispvav ;menucount =< 30
movlw 0x1F ;if it has reached 31 reset menu, before return
xorwf menu,w
btfss STATUS,Z
goto $ +2 ; not 31 goto changewhp1 direct
clrf menu ; is 31, reset menu before changewhp1
Menuend:
; before changewhp jump, check that sec has not updated
; if it has , jump back to changesec routine
movf oldsec, w
xorwf sec,w ; if equal, w= 0 and Z bit=1
btfsc STATUS,2 ; test Z bit
goto Changewhp ; no sec change, check whpulse change
goto Changesec ; a new sec starts measurements
; loop while NOT newsec
; ---------------------------------------------------------------
; End Main
; ----------------------------------------------------------------
;----------------------------------------------------------------------;
; ISR, increments TMR0 by 1 every 256 µsec. Basically just ;
; reset the INTCON and TMR0 bits. The FSR,w, STATUS push pop kept ;
;----------------------------------------------------------------------;
Isr:
movwf w_temp ; save W
swapf STATUS,W ; save status
movwf status_temp ; without changing flags
swapf FSR,W ; save FSR
movwf fsr_temp ; without changing flags
;256us * 217 * 18 = 0.999936 sec.
;1hr_err = 0.23sec or 1 sec in 4hr
;The time loop starts with TMR0 loaded with (256-217)=39 After 217
;steps interrupt is set, TMR0 rolls to zero and the TMR18 inc +1
movlw D'39' ;39 = 0x27
movwf TMR0
incf TMR18,f
movlw D'18' ; DUMMY18 = 0x12
xorwf TMR18,w ; if equal, w= 0 and Z bit=1
btfss STATUS,2 ; test Z bit
goto restore ; NOT 18 so pop stack and return to main
Firstsec:
clrf TMR18 ; is 18. set TMR18 to zero and INC sec
incf sec,f ; also inc sectot (eventually lo and hi)
incf sectotlo,f ; check sectot lo 00->inc hi
clrw
xorwf sectotlo,w ; if equal, w= 0 and Z bit=1
btfsc STATUS,2
incf sectothi,f ; increases every 256 x sectotlo
movlw 0xA ; check if =10
xorwf sec,w ; if equal, w= 0 and Z bit=1
btfss STATUS,2 ; test Z bit
goto restore ; NOT 10 so pop stack and return to main
clrf sec ; is 10. set sec to zero and inc sec10
incf sec10,f
movlw 0x6 ; = 0x6
xorwf sec10,w ; if equal, w= 0 and Z bit=1
btfss STATUS,2 ; test Z bit
goto restore ; NOT 6 so pop stack and return to main
clrf sec10 ; is 6. set sec10 to zero and INC min
incf min,f
movlw 0xA ; check if =10
xorwf min,w ; if equal, w= 0 and Z bit=1
btfss STATUS,2 ; test Z bit
goto restore ; NOT 10 so pop stack and return to main
clrf min ; is 10. set sec to zero and inc min10
incf min10,f
movlw 0x6 ;= 0x6
xorwf min10,w ; if equal, w= 0 and Z bit=1
btfss STATUS,2 ; test Z bit
goto restore ; NOT 6 so pop stack and return to main
clrf min10 ; is 6. set min10 to zero and INC hr
incf hr,f
movlw 0xA ;check if =10
xorwf hr,w ; if equal, w= 0 and Z bit=1
btfss STATUS,2 ; test Z bit
goto restore ; NOT 10 so pop stack and return to main
clrf hr ; is 10. set hr to zero and inc hr10
incf hr10,f
movlw 0xA ;= 10
xorwf hr10,w ; if equal, w= 0 and Z bit=1
btfss STATUS,2 ; test Z bit
goto restore ; NOT 10 so pop stack and return to main
clrf sec ; is ten (99hr 59min 59sec +1sec) we are at MAX
clrf sec10 ;all reset to zero and start again
clrf min
clrf min10
clrf hr
clrf hr10
clrf TMR18
clrf oldsec
goto restore
restore:
swapf status_temp,W ; get original status back
movwf STATUS ; into status register
swapf fsr_temp,W ; get original fsr back
movwf FSR ; into status register
swapf w_temp,f ; old no flags trick again
swapf w_temp,W ; to restore W
bcf INTCON,T0IF ; clear the TMR0 interrupt flag
retfie ; finished, reset GIE
;----------------------------------------------------------------------;
; Initialize the ports ;
;----------------------------------------------------------------------;
Init:
clrf PORTA
clrf PORTB
movlw B'00000000' ; Porta all outputs
tris PORTA
movlw B'01010000' ; 7o 6i-Wire 5o-LED 4i-multivib,
tris PORTB ; 0to3lcd
movlw B'00000111' ; opt pull-ups enabled
; opt prescaler assigned to TMR18
; opt prescaler set to 1:256
option ; opt rolls over each 125th second
movlw 0 ; zero out all registers
clrf m
clrf m10
clrf m100
clrf m1000
clrf dm
clrf dm10
clrf dm100
clrf dm1000
clrf dm10000
clrf dm100000
clrf totmshi
clrf totmslo
clrf totms2hi
clrf totms2lo
clrf dist54
clrf hr10
clrf hr
clrf min10
clrf min
clrf oldsec
clrf sec10
clrf sec
clrf sectotlo
clrf dtotlo
clrf dtothi
clrf sectotlo
clrf sectothi
clrf TMR18 ; when this reaches 18, 1 sec has elapsed
; TMR0 has to start at 256-217 :39, so that the first rollover
; with prescaler 256, happens in 217*256us =0.055552sec
; The TMR18 incs each roll and after 18 1 sec has elapsed
movlw 0x27 ; D39
movwf TMR0 ; set to D39 217 counts later 256,
; TMR0 resets and inc TMR18 in Isr loop
clrf INTCON ; START WITH ALL AT 0
; movlw B'10100000' ; GIE set T0IE set, T0IF cleared
movlw B'10100000' ; Set:GIE,T0IE,RBIE Clrd:T0IF,RBIF
movwf INTCON ; ready to detect interrupts in ISR
return
;----------------------------------------------------------------------;
; Initialize the LCD ;
;----------------------------------------------------------------------;
Initlcd:
movlw D'40'
call nmsec ; Wait 40 msecs before Reset
bcf RS ; send an 8 bit instruction
movlw 0x03 ; Reset Command
call NybbleOut ; Send the Nybble
call Dlay5 ; Wait 5 msecs before Sending Again
call EStrobe
call Dlay160 ; Wait 160 usecs before Sending 2nd Time
call EStrobe
call Dlay160 ; Wait 160 usecs before Sending 3rd Time
bcf RS ; send an 8 bit instruction
movlw 0x02 ; Set 4 Bit Mode
call NybbleOut
call Dlay160
movlw 0x028 ; 4 bit, 2 Line, 5x7 font
call SendINS
movlw 0x010 ; display shift off
call SendINS
movlw 0x001 ; Clear the Display RAM
call SendINS
call Dlay5 ; Note, Can take up to 4.1 msecs
movlw 0x006 ; increment cursor
call SendINS
movlw 0x00C ; display on cursor off
call SendINS
return
;----------------------------------------------------------------------;
; Send the character in W out to the LCD ;
;----------------------------------------------------------------------;
SendASCII
addlw '0' ; Send nbr as ASCII character
SendCHAR: ; Send the Character to the LCD
movwf temp ; Save the temporary Value
swapf temp, w ; Send the High Nybble
bsf RS ; RS = 1
call NybbleOut
movf temp, w ; Send the Low Nybble
bsf RS
call NybbleOut
return
;-------------------------------------------------------------
; ES strobe
;-------------------------------------------------------------
EStrobe: ; Strobe the "E" Bit
bsf E
bcf E
return
;----------------------------------------------------------------------;
; Send an instruction in W out to the LCD ;
;----------------------------------------------------------------------;
SendINS: ; Send the Instruction to the LCD
movwf temp ; Save w
swapf temp, w ; send Hi Nybble
bcf RS ; RS to 0
call NybbleOut
movf temp, w ; Send Lo Nybble
bcf RS
call NybbleOut
return
;----------------------------------------------------------------------;
; Send the nibble in W out to the LCD ;
;----------------------------------------------------------------------;
NybbleOut: ; Send a Nybble to the LCD
movwf PORTB
call EStrobe ; Strobe out the LCD Data
bsf E
call Dlay160 ; delay for 160 usec
return
;----------------------------------------------------------------------;
; Output the message on the LCD ;
;----------------------------------------------------------------------;
OutMessage1:
movwf FSR ; Point at first letter
OutLoop:
movf FSR, w ; Get pointer into W
incf FSR, f ; Set up for next letter
call Dispmsg1 ; Get character to output
iorlw 0 ; At the End of the Message?
btfsc STATUS, Z ; Skip if not at end
return ; Yes - Equal to Zero
call SendCHAR ; Output the ASCII Character
goto OutLoop ; Get the next character
;----------------------------------------------------------------------;
; Data for message to be output ;
;----------------------------------------------------------------------;
Dispmsg1: ; Message to Output
addwf PCL, f ; Output the Characters
dt "Bike Computer", 0
Dispmsg2: ; Message to Output
addwf PCL, f ; Output the Characters
dt "Dist", 0
Dispmsg3:
addwf PCL,f ; message to output
dt " Av speed:",0 ; output characters
;----------------------------------------------------------------------;
; time delay routines ;
;----------------------------------------------------------------------;
;Note . The original application needed precise times from the delay code that
; follows. But the Simple Bike application does NOT use the routines for
; any CRITICAL time measurement.
Dlay160:
movlw D'41' ; delay about 160 usec
micro4:
addlw H'FF' ; subtract 1 from 'W'
btfss STATUS,Z ; skip when you reach zero
goto micro4 ; more loops
return
Dlay5:
movlw 5 ; delay for 5 milliseconds
goto $ + 2
msec250:
movlw D'250' ; delay for 250 milliseconds
; --- N millisecond delay routine ---
nmsec:
movwf cntmsec ; delay for N (you put in W) millisec
msecloop:
movlw D'254' ; load takes .9765625 microsec
call micro4 ; by itself CALL takes ...
; about 1ms
nop ; 1usec
decfsz cntmsec, f ; .98 skip not taken, else 1.95
goto msecloop ; 1.95 here: total ~1000 / loop
return ; final time through ~999 to here
; overhead in and out ignored
; this block is functional for tests, but not used in bike
Dlay1sec: ; this is a marker routine to see where
call msec250 ; the prg is at.
; bsf LED
call msec250 ;flash led on portb pin6
;bcf LED
call msec250
; bsf LED
call msec250 ; finishes with green led lit
return
;----------------------------------------------------------------------;
; Display the Time ;
;----------------------------------------------------------------------;
Disptime:
movlw H'80' ; position at beginning of first line
call SendINS
movf hr10, W ; tens of hours
call SendASCII
movf hr, W ; hours
call SendASCII
movlw ":"
call SendCHAR
movf min10, W ; tens of minutes
call SendASCII
movf min, W ; minutes
call SendASCII
movlw ":"
call SendCHAR
movf sec10, W ; tens of seconds
call SendASCII
movf sec, W ; seconds
call SendASCII
movlw " "
call SendCHAR ; the h m s really not needed
movlw "h"
call SendCHAR
movlw "m"
call SendCHAR
movlw "s"
call SendCHAR
movlw " "
call SendCHAR
Dispend:
RETURN
;----------------------------------------------------
; End of display time
;----------------------------------------------------
;----------------------------------------------------------------------;
; Display the Distance , (trip distance at moment) ;
;----------------------------------------------------------------------;
Dispdist:
movlw H'C0' ; position at beginning of second line
call SendINS
movf dm100000, W ; 0 of 065km 536m
call SendASCII
movf dm10000, W ; 6 of 065km 536m
call SendASCII
movf dm1000, W ; 5 of 065km 536m
call SendASCII
movlw "k" ; k of 065km 536m
call SendCHAR
movlw "m" ; m of 065km 536m
call SendCHAR
movlw " " ; " " of 065km 536m
call SendCHAR
movf dm100,W ; 5 of 065km 536m
call SendASCII
movf dm10,W ; 3 of 065km 536m
call SendASCII
movf dm, W ; 6 of 065km 536m
call SendASCII
movlw "m" ; m of 065km 536m
call SendCHAR
movlw " " ; " "
call SendCHAR
movlw "D" ; "D "
call SendCHAR
movlw "i" ; " i"
call SendCHAR
movlw "s" ; "s "
call SendCHAR
movlw "t" ; " t"
call SendCHAR
movlw " " ; " " ( to wipe screen)
call SendCHAR
Distend:
RETURN
;
;---------------------------------------------------------------------*
; Multiplication 16x16 Out32
;---------------------------------------------------------------------*
Mult16x16:
clrf mq4
clrf mq3
clrf mq2
clrf mq1
bsf mq2, 7
Mu1:
rrf mah, f
rrf mal, f
skpc
goto Mu2
movf mbl, w
addwf mq3, f
movf mbh, w
skpnc
incfsz mbh, w
addwf mq4, f
Mu2:
rrf mq4, f
rrf mq3, f
rrf mq2, f
rrf mq1, f
skpc
goto Mu1
Endmult16:
clrf mbh
clrf mbl
clrf mah
clrf mal
Return
;---------------------------------------------------------------------*
; End Multiplication 16x16 Out32
;---------------------------------------------------------------------*
;---------------------------------------------------------------------*
; New routine Division 16/16 Out 16
; ---------------------------------------------------------------------*
; Finally:- Result 000AH in reshi/lo, remainder 0096H in rmdrhi/lo
; so, checking end of division A=10 and 96H/E1H= 150/225 =0.666 OK
Div:
call D_divS ; remainder in Rmdr.
Divend:
nop
return
D_divS:
call setup
clrf rmdrhi
clrf rmdrlo
dloop:
bcf STATUS,C
rlf reslo, f
rlf reshi, f
rlf rmdrlo, f
rlf rmdrhi, f
movf denhi,w
subwf rmdrhi,w ;check if a>c
btfss STATUS,Z
goto nochk
movf denlo,w
subwf rmdrlo,w ;if msb equal then check lsb
nochk:
btfss STATUS,C ;carry set if c>a
goto nogo
movf denlo,w ;c-a into c
subwf rmdrlo, f
btfss STATUS,C
decf rmdrhi, f
movf denhi,w
subwf rmdrhi, f
bsf STATUS,C ;shift a 1 into b (result)
nogo:
rlf numlo,f
rlf numhi,f
decfsz temp, f ;loop untill all bits checked
goto dloop
setup:
movlw .16 ; for 16 shifts
movwf temp
movf numhi,w ;move Num to Res
movwf reshi
movf numlo,w
movwf reslo
clrf numhi
clrf numlo
retlw 0
;--------------------------End Div 16 by 16----------------------------
return
;---------------------------------------------------------------------*
; END DIVISION 16/16 OUT 16
;---------------------------------------------------------------
;---------------------------------------------------------------------*
; START Addition 16-16 OUT 16
;---------------------------------------------------------------
Add: ; R = q1 + q2
movf q1lo, W
addwf q2lo, W
movwf rlo
movf q1hi, W
btfsc STATUS, C
addlw .1 ; If A Carry Occurred, Add 1
addwf q2hi, W
movwf rhi
;clrf q1hi ; added this to stop next user summing;
;clrf q1lo
;clrf q2hi
;clrf q2lo
Return
;---------------------------------------------------------------------*
; END Addition 16-16 OUT 16
;---------------------------------------------------------------
;---------------------------------------------------------------------*
; START Subtraction 16-16 OUT 16
;---------------------------------------------------------------
Sub: ; R = q1 - q2
movf q2lo, W
subwf q1lo, W ; W = q1lo - q2lo
movwf rlo
btfss STATUS, C
Goto Borrow
Goto Sub1
Borrow:
Decf q1hi, F
Sub1:
movf q2hi, W
subwf q1hi, W ; W = q1hi - q2hi
movwf rhi
Return
;---------------------------------------------------------------
; END Subtraction 16-16 OUT 16
;---------------------------------------------------------------
;---------------------------------------------------------------
; Dist = counting wheel pulses
;---------------------------------------------------------------
Dist: ; whlcirc = 2.0747, for initial test =2
;
; movlw 0x2 ; <-- don't forget, this is just for 26"wheel
; movwf whlcirc ; start count at 0 and inc till same as whlcirc
; (range will be 1 to 4 m)
clrf count ; count can be 2 or 4 in this 26" case
Whloop: ; although the distance pulses ( in this example) jump in steps of 2m
; the m counter has to increase in 1's to catch the
; decimal rollover from 10 to 0
; so count, here, is stepped from 0 to 2 (whlcirc)
; Also, when the dist54 var increases 54m, COUNT adds an extra 2m
; to the hex and decimal totals, before resetting to zero.
; Note that as dist54 is in the loop it will also receive
; the 2m increase so it really counts to 56 (54 +2).
movlw 0x2 ; wheel circumference of 26 inch
xorwf count,w ; if equal, w= 0 and Z bit=1
btfsc STATUS,2 ; test Z bit
goto DistanceEnd ; they are the same, so jump distanceEnd and return
goto Mcount ; NOT yet = whlcirc, so inc mcount by 1 and return
Mcount: ; Distance to decimal for display , i/p m, o/p 000km 000m
; starts with m, m10, m100, m1000 , m10000, m100000 = 0
; Later correct m every 54m (add 2m ???? )
incf dm,f
movlw D'10'
xorwf dm,w ; if equal, w= 0 and Z bit=1
btfss STATUS,2 ; test Z bit
goto Mcountend ; NOT 0 so return
clrf dm
incf dm10,f
movlw D'10'
xorwf dm10,w ; if equal, w= 0 and Z bit=1
btfss STATUS,2 ; test Z bit
goto Mcountend ; NOT 0 so return
clrf dm10
incf dm100,f
movlw D'10'
xorwf dm100,w ; if equal, w= 0 and Z bit=1
btfss STATUS,2 ; test Z bit
goto Mcountend ; NOT 0 so return
clrf dm100
incf dm1000,f
movlw D'10'
xorwf dm1000,w ; if equal, w= 0 and Z bit=1
btfss STATUS,2 ; test Z bit
goto Mcountend ; NOT 0 so return
clrf dm1000
incf dm10000,f
movlw D'10'
xorwf dm10000,w ; if equal, w= 0 and Z bit=1
btfss STATUS,2 ; test Z bit
goto Mcountend ; NOT 0 so return
clrf dm10000
incf dm100000,f
movlw D'10'
xorwf dm100000,w ; if equal, w= 0 and Z bit=1
btfss STATUS,2 ; test Z bit
goto Mcountend ; NOT 0 so return
clrf dm ; is distmax, reset to 0
clrf dm10
clrf dm100
clrf dm1000
clrf dm10000
clrf dm100000
goto Mcountend ; see if whpl update is now finished
Mcountend: ;update the hex counters dtot /lo/hi/hi2
incf dtotlo,f
clrw
xorwf dtotlo,w ; if equal, w= 0 and Z bit=1
btfss STATUS,2 ; test Z bit
goto Dtotend ; dtotlo NOT 0
incf dtothi,f ; add 1, dtotlo is 0
clrw
xorwf dtothi,w ; if equal, w= 0 and Z bit=1
btfss STATUS,2 ; test Z bit
goto Dtotend ; dtothi NOT 0
incf dtothi2,f ; add 1, dtothi is 0
Dtotend:
movlw D'54'
movwf numhi ; not in use here, use to store 54
movf dist54,w
subwf numhi,w
btfsc STATUS,C
goto Noextra ; dist54=< 53, don't freeze COUNT
movlw D'56' ; dist54>53 add 2 extra counts
movwf numhi ; not in use here to store 56
movf dist54,w
subwf numhi,w
btfsc STATUS,C
goto Extra ; dist54=< 56, freeze COUNT
clrf dist54
goto Noextra
Noextra:
incf dist54,f
incf count,f
goto Whloop
Extra: ; an extra loop BUT count is NOT increased
incf dist54,f
goto Whloop
DistanceEnd: ; All conversion loops have executed and
; only left to display in decimal, when selected
;
return ; BACK TO MAIN
;--------------------------------------------------------------------------
; End of Trip Distance calculation Module
;--------------------------------------------------------------------------
;----------------------------------------------------------
; Calculate Average velocity from Velav = totdist/totsec
;-----------------------------------------------------------
; 1st time round distto = 0 and sectot =0.
; Disttot lo/hi zero is prob OKbut test sec=0. If zero return
Velav:
clrw
xorwf sectotlo,w
btfss STATUS,Z
goto Spdstart ; not 0, onto Avspeed calc
clrw ; lo was 0, but hi byte may not be
xorwf sectothi,w
btfss STATUS,Z
goto Spdstart ; was not 0, onto Avspeed calc
;incf sectotlo,f ; DUMMY
;goto Spdstart ; DUMMY
return ; was also 0, abort module wait for sec inc
; to slow down the number of whpul in the test we add Dummies
Spdstart: ; for test delay incf sec and sectot, gives 7.2km/hr
;incf sec,f ; DUMMY
;incf sectotlo,f ; DUMMY
;Lpstart:
movlw 0x07 ; 1800 comp hi bytes
subwf dtothi,w
btfsc STATUS,C
goto Divratio ; dtothi is > 1800hi and needs dividing
; we suppose that the hibyte test is sufficient,
; it may not be true. e.g hi's the same but lo's diff
; pass dtot to m1000,m100
movf dtothi,w ; dtothi <1800
movwf m1000
movf dtotlo,w
movwf m100 ; m1000,m100 hold dtothi/lo ( no div needed)
movf sectothi,w
movwf m10
movf sectotlo,w
movwf m ; m10,m hold sectothi/lo ( no div needed)
; note, this is dtot to m1000,m100 not needing division
goto Ratend ; if divratio is needed, then m1000,m100 is used there
Divratio: ; enter variables and 1800
movf dtothi,w
movwf numhi
movf dtotlo,w
movwf numlo
movlw 0x07
movwf denhi
movlw 0x08
movwf denlo
call Div
incf reslo,w ; reslo holds INT of Divratio +1
movwf count ; not used at the moment
movf dtothi,w ; divide dtothi/lo by count
movwf numhi
movf dtotlo,w
movwf numlo
clrf denhi
movf count,w ; e.g. D 37
movwf denlo
call Div ; we need parking space for results, REuse the m1000 etc
movf reshi,w
movwf m1000 ;
movf reslo,w
movwf m100 ; m1000, m100 =dtot/count
; repeat for sectot
movf sectothi,w
movwf numhi
movf sectotlo,w
movwf numlo
clrf denhi
movf count,w ; e.g D 37
movwf denlo
call Div
movf reshi,w
movwf m10
movf reslo,w
movwf m ; m10, m =sectot/count
Ratend:
; WE CAN NOW DO DTOT/SECTOT x3.6 as we have SCALED TO AVOID OVERFLOW
; m1000,m100 x 36 (top line)
; Vav= ------------------------- = Average speed in km/hr
; m10,m x 10 (bottom line) ; 36(24H), 10(AH)
movf m1000,w ; MULT dtot, top line, by 24H
movwf mah
movf m100,w
movwf mal
clrf mbh
movlw 0x24
movwf mbl
call Mult16x16 ; ( store top line result in the same variables)
movf mq2,w
movwf m1000
movf mq1,w
movwf m100 ; dtot x 36
movf m10,w ; MULT sectot, bottom line, by AH
movwf mah
movf m,w
movwf mal
clrf mbh
movlw 0xA
movwf mbl
call Mult16x16 ; ( store bottom line result in the same variables)
movf mq2,w
movwf m10
movf mq1,w
movwf m ; sectot x 10
Tp3:
; As we are reusing variables, trying to show where the results to here are being held
; m1000,m100
; We now have as Average speed ----- --------- km/hr
; m10,m
;the AVERAGE SPEED AT LAST
movf m1000,w ; dtotx36/sectotx10
movwf numhi
movf m100,w
movwf numlo
movf m10,w
movwf denhi ; needed below for decimal place
movf m,w
movwf denlo ; needed below for decimal place
call Div
; denhi/lo can chanfge as the div routine reuses bits
; so we reestablish previous denom values before losing
movf m10,w
movwf denhi
movf m,w
movwf denlo ; we can now write over m and m10
movf reshi,w ;will be zero
;(bikes don't exceed 256km/hr normally)
movf reslo,w ; reuse variables m10 INT and later m FRAC
movwf m10 ;we have integer part of Vav Average speed
; rmdrhi/lo holds decimal fraction.we will only use 1 decimal place.
; so mult the rmdrhi,lo x10 before dividing by denhi/lo
movf rmdrhi,w ; mult top by 10
movwf mah
movf rmdrlo,w
movwf mal ; rmdr in tot storage
clrf mbh
movlw 0xA
movwf mbl
call Mult16x16 ; rmdrhi,lo x 10
; top line is now mq2,mq1 ( nothing in mq4,mq3)
; Bottom line..remember we already have loaded
; the denominator above which says
; " needed below for decimal place"
Tp4:
movf mq2,w ;rmdrhi,w
movwf numhi
movf mq1,w ;rmdrlo,w
movwf numlo
call Div ; div for the decimal( just 1 place), in reslo
; reshi,reslo holds decimal place results
; m10+m are av speed as INT+DEC but in hex
; now change to decimal.
; m10 and m are going to be wiped, but there is no
;divide routine so for INT +FRAC we use reshi reslo
movf m10,w
movwf reshi ; OK? yes reshi +reslo = INT+FRAC in hex, convert
Vavh2d:
clrf m
clrf m10
clrf count
Vavloh2d: ; m is 2nd dec, use m10
movf count,w
xorwf reslo,w
btfsc STATUS,Z
goto Vavloend
incf count,f
incf m10,f
movlw 0xA
xorwf m10,w
btfss STATUS,Z
goto Vavloh2d
clrf m10
incf m,f
goto Vavloh2d
Vavloend:
clrf m100
clrf m1000
clrf count
Vavhih2d:
movf count,w
xorwf reshi,w
btfsc STATUS,Z
goto Vavhiend
incf count,f
incf m100,f
movlw 0xA
xorwf m100,w
btfss STATUS,Z
goto Vavhih2d
clrf m100
incf m1000,f
goto Vavhih2d
Vavhiend: ; We have Vav as: INT m1000,m100 and FRAC m10,m with Frac
Vavend:
nop
return
;----------------------------------------------------------------------;
; Display the Average velocity ;
;----------------------------------------------------------------------;
Dispvav:
movlw H'C0' ; position at beginning of second line
call SendINS
movf m1000, W ; x0 tens of km
call SendASCII
movf m100,W ; 0x ones of km
call SendASCII
movlw "." ; "." punto decimal
call SendCHAR
movf m10,W ; "0.x0" decimal
call SendASCII
movf m, W ; "0.0x" decimal
call SendASCII
movlw "k" ; k
call SendCHAR
movlw "m" ; m
call SendCHAR
movlw "/" ; /
call SendCHAR
movlw "h" ; h
call SendCHAR
movlw "r" ; r
call SendCHAR
movlw " " ; " "
call SendCHAR
movlw "V" ; V
call SendCHAR
movlw "e" ; e
call SendCHAR
movlw "l" ; l
call SendCHAR
movlw "A" ; A
call SendCHAR
movlw "v" ; v
call SendCHAR
movlw "g" ; g
call SendCHAR
movlw " " ; " "
call SendCHAR
Dispvavend:
Return ; to change seconds for next second
;--------------END OF AV SPEED-------------------
; ---------------------------------------------------------------
; Instantaneous Speed
;-----------------------------------------------------------------
; Previous time values are subtracted from the present
; sec, TMR18, TMR0 values.
; This time difference is the time between
; wheel pulses. This in turn for 26" wheel is a distance of 2.0747m
; is covered in totms
; ---------------------------------------------------------------
; Instantaneous Speed
;-----------------------------------------------------------------
; Note sec can only move between 0 and 9, 10 is a new 0
; We start by reading sec, TMR18, TMR0 TO GET START TIME
Speed:
Spdflg0: ; FIRST TIMEwith spdflg=0, second time with spdflg=1
; First time captures timezero 0 in ms in totms
clrw
xorwf spdflg,w
btfss STATUS ,Z
goto Spdflg1 ; spdflg is not 0, this is the second time
call Summs
movf mshi,w
movwf totmshi
movf mslo,w
movwf totmslo
incf spdflg,f ; (spdflg = 1 for SECOND TIME)
return ;jump back to main menu
Summs: ; this routine is common to the first and second time
movf sec,w ; spdflg = 0
movwf mbl ; sec can only be in range 0 to 9
clrf mbh
movlw 0x03 ; 1000 = 03E8
movwf mah
movlw 0xE8 ; 1000 = 03E8
movwf mal
call Mult16x16 ;
movf mq2,w ; hi byte
movwf mshi
movf mq1,w ; lo byte
movwf mslo
movf TMR18,w
movwf mal
clrf mah
movlw D'56'
movwf mbl
clrf mbh
call Mult16x16 ; after themult we add
movf mshi,w
movwf q1hi
movf mslo,w
movwf q1lo
movf mq2,w
movwf q2hi
movf mq1,w
movwf q2lo
call Add
movf rhi,w
movwf mshi
movf rlo,w
movwf mslo ; sum (sec +TMR18)
;TMR0 is a little more complex as we have to subtract39
; before we can calculate ms in TMR0
movf TMR0,w
movwf q1lo
clrf q1hi
movlw D'39'
movwf q2lo
clrf q2hi
call Sub
movf rlo, w
movwf numlo
clrf numhi
movlw 0x4
movwf denlo
clrf denhi
call Div
movf mshi,w
movwf q1hi
movf mslo,w
movwf q1lo
movf reshi,w
movwf q2hi
movf reslo,w
movwf q2lo
call Add
movf rhi,w
movwf mshi
movf rlo,w
movwf mslo ; ( SEC +TMR18+ TMR0) in ms mshi/lo
RETURN ; Summs
Summsend:
Spdflg1: ; SECOND TIME is with spdflg =1
; Second time captures timezero to 2nd Pulse
; in ms, store in totms2
; for test we add 250ms to simulate time between
; wheel pulses
; 250ms delay
;movlw D'63' ; DUMMY FOR TEST
;call nmsec ; DUMMY FOR TEST
call Summs
movf mshi,w ;
movwf totms2hi ;
movf mslo,w ;
movwf totms2lo ;
; totms2 should be larger than totms,however if totms2 has just rolled,
; then add 10000 to tot2
Testtot2:
movf totms2hi,w
subwf totmshi,w
btfss STATUS,C ;compare for greater
goto Diffms ;totms2 > totms no add 10000 needed
movlw 0xA ;totms2 < totms, add 10000, hex 2710
movlw 0x27
movwf q1hi
movlw 0x10
movwf q1lo
movf totms2hi,w
movwf q2hi
movf totms2lo,w
movwf q2lo
call Add ; tot2 +10000
movf rhi,w
movwf totms2hi
movf rlo,w
movwf totms2lo ; tot2 = tot2+10000
Diffms:
; sub (tot2-tot1)for time between wheel pulses
movf totms2hi,w ;
movwf q1hi
movf totms2lo,w
movwf q1lo
movf totmshi,w
movwf q2hi
movf totmslo,w
movwf q2lo
call Sub ; result is time diff, stored in rhi, rlo
tp2:
; we are almost ready to divide dist/time for speed
; complete equation is dist/time x3.6
; to avoid as far as possible fractions we multiply before divide
; but this time, better divide first.
; 2000 36 <--- this is more than 65536
; ---- x --------
; denhi/lo 10 ( denhi/lo is sub result rhi/lo)
; so we can divide by 10 and x by 36 ( always,)
; so top line is simply 7200.
; we finally have 7200/(denhi/lo) , SPEED IN HEX
movf rhi,w
movwf denhi
movf rlo,w
movwf denlo ; denom is time in hex
movlw 0x1C ; 7200, hex 1C20
movwf numhi
movlw 0x20 ; 7200, hex 1C20
movwf numlo
call Div
; the result , res, is more than 1
;and a remainder rmdr/den
;we now have 6 free variables
; mshi/lo for INT hex speed lo is enough
; totmshi/lo
; totms2hi/lo
;we will reuse to convert from hex to decimal
movf reslo,w
movwf mshi ; INT part of hex speed ( hi, because FRAC will be in lo)
;we will only use 1 decimal of
;the hex speed remainder, so 1st rmdrx10
; so next calculation is
; 10 x rmdrhi/lo
; ---------------
; denhi/lo
movf rmdrhi,w
movwf mah
movf rmdrlo,w
movwf mal
clrf mbh
movlw 0x0A
movwf mbl
call Mult16x16 ;( ten times top)
movf mq2,w
movwf numhi
movf mq1,w
movwf numlo ; the den is previously loaded so divide for
; decimal part of speed ( still in hex)
call Div ; we only use 1 decimal, so just use lo of Div result
movf reslo,w
movwf mslo ; FRAC of speed in hex
; now convert speed to decimal form
; i.e. mshi, mslo --> 99.9 km/hr
Spdh2d:
clrf m
clrf m10
clrf m100
clrf m1000
clrf count
Spdlo:
movf count,w
xorwf mslo,w
btfsc STATUS,Z
goto Spdhicnt
incf count,f
incf m10,f
movlw 0xA
xorwf m10,w
btfss STATUS,Z
goto Spdlo
clrf m10
incf m,f
goto Spdlo
Spdhicnt:
clrf m100
clrf m1000
clrf count
Spdhi:
movf count,w
xorwf mshi,w
btfsc STATUS,Z
goto Spdhiend
incf count,f
incf m100,f
movlw 0xA
xorwf m100,w
btfss STATUS,Z
goto Spdhi
clrf m100
incf m1000,f
goto Spdhi
Spdhiend:; We have filled m1000,m100 with INT and m10,m with Frac, of Vin.
; Note we only need m10 ( 1 decimal place)
clrf spdflg ; reset to 0 for next pair of pulses
; incf spdflg,f ; (spdflg = 1 for SECOND TIME)
return
; ---------------------------------------------------------------**
; End of Instantaneous Speed
; ---------------------------------------------------------------**
Dispd:
;----------------------------------------------------------------------;
; Display the Instantaneous Speed (Speed) ;
;----------------------------------------------------------------------;
movlw H'C0' ; position at beginning of second line
call SendINS
movf m1000, W ; x0 tens of km
call SendASCII
movf m100,W ; 0x ones of km
call SendASCII
movlw "." ; "." punto decimal
call SendCHAR
movf m10,W ; "0.x0" decimal
call SendASCII
movf m, W ; "0.0x" decimal
call SendASCII
movlw "k" ; k
call SendCHAR
movlw "m" ; m
call SendCHAR
movlw "/" ; /
call SendCHAR
movlw "h" ; h
call SendCHAR
movlw "r" ; r
call SendCHAR
movlw " " ; " "
call SendCHAR
movlw "S" ; S
call SendCHAR
movlw "p" ; p
call SendCHAR
movlw "e" ; e
call SendCHAR
movlw "e" ; e
call SendCHAR
movlw "d" ; d
call SendCHAR
Dispdend:
Return ; to change seconds for next second
;--------------END OF Inst SPEED-------------------
end
;-----------------------------------------------------
; bike computer program end ....enjoy
;-----------------------------------------------------
; WORK IN HAND
; CLEAN UP SPEED, readout still a little jumpy,
; can try integrating but need more space. Next version
; Coventions used in the program text.
;Variables all small letters , exceptions, those defined
; by microchip. e.g. STATUS
;Labels: First letter always a Capital, exceptions imported
; routines
; Test points. These have a comment DUMMY, the value of the code
; has/had been changed for testing or simulating. If YOU want
;to polish these points should be useful for value changes
; example. In ISR the D'18' value to reach a sec is very slow.
;Use for test , say 2, and you get going about 10 times faster.
;Also LCD INIT: can be bypassed to debug quicker
;----------------------------------------------------------------------------------------------------
; End of the Simple Bike Computer
;----------------------------------------------------------------------------------------------------
If you have managed to read to here, through all that has been written, then you certainly have determination.
Well " getting here" is the moment when you have mastered all the steps in writing the PIC assembler source code. Getting here also means you can do all the manual side as well for many future projects.
But maybe the most satisfaction is obtained by taking the final code version
and then burning it into the PIC to make your Bike Computer a reality,….not just a bit of theory.
Ah I almost forgot, here is the Bike3.asm squeezed into the END PRODUCT of our toil.
Bike3. hex
:020000000528D1
:080008004C289220BD20CB0121
:10001000C80138083206031909283208B800CB0A8B
:100020002E2186018612061A132886160A302221EE
:10003000061A18280A302221061A182886120608DD
:100040000A304B02031C25283028CB220A304B02F1
:10005000031C2B20302803014806031970230800D5
:10006000C4210A304B02031C392814304B02031CF4
:100070004B21122214304B02031C42281E304B022B
:10008000031CA4221F304B06031D4728CB01380850
:100090003206031911280928BB00030EBC00040E08
:1000A000BD0027308100BA0A12303A06031D8A28A3
:1000B000BA01B20AB00A030130060319B10A0A30C4
:1000C0003206031D8A28B201B30A06303306031D27
:1000D0008A28B301B40A0A303406031D8A28B40101
:1000E000B50A06303506031D8A28B501B60A0A305E
:1000F0003606031D8A28B601B70A0A303706031DE3
:100100008A28B201B301B401B501B601B701BA0141
:10011000B8018A283C0E83003D0E8400BB0E3B0EC6
:100120000B11090085018601003065005030660022
:10013000073062000030AC01AD01AE01AF01A60195
:10014000A701A801A901AA01AB01C301C201C50110
:10015000C401A501B701B601B501B401B801B301ED
:10016000B201B001A201A301B001B101BA0127306F
:1001700081008B01A0308B0008002830222105115E
:100180000330EB201F21E0201A21E0201A21051165
:100190000230EB201A212830E3201030E320013018
:1001A000E3201F210630E3200C30E3200800303E1E
:1001B000C900490E0515EB2049080515EB2008007C
:1001C000851585110800C900490E0511EB20490865
:1001D0000511EB2008008600E02085151A21080093
:1001E00084000408840AF920003803190800D82084
:1001F000F1288207423469346B3465342034433447
:100200006F346D3470347534743465347234003442
:1002100082074434693473347434003482072034E0
:100220004134763420347334703465346534643446
:100230003A3400342930FF3E031D1B2908000530E5
:100240002229FA30B900FE301B210000B90B232906
:100250000800212121212121212108008030E320D3
:100260003708D7203608D7203A30D8203508D7208D
:100270003408D7203A30D8203308D7203208D72086
:100280002030D8206830D8206D30D8207330D82066
:100290002030D8200800C030E3202B08D7202A08BF
:1002A000D7202908D7206B30D8206D30D8202030B7
:1002B000D8202808D7202708D7202608D7206D3037
:1002C000D8202030D8204430D8206930D82073304E
:1002D000D8207430D8202030D82008008C018D011F
:1002E0008E018F018E17920C930C031C7D2911082F
:1002F0008D0710080318100F8C078C0C8D0C8E0CBA
:100300008F0C031C73299001910192019301080045
:100310008B2100000800A6219901980103109B0D74
:100320009A0D980D990D15081902031D99291408A5
:100330001802031CA22914089802031C990315082B
:1003400099020314960D970DC90B8E291030C90020
:1003500017089A0016089B009701960100340800C0
:100360001D081F07A1001C080318013E1E07A0005E
:1003700008001F081D02A100031CBF29C0299C03FF
:100380001E081C02A0000800CA0102304A06031918
:10039000112AA60A0A302606031DF329A601A70A78
:1003A0000A302706031DF329A701A80A0A302806E8
:1003B000031DF329A801A90A0A302906031DF32900
:1003C000A901AA0A0A302A06031DF329AA01AB0AC9
:1003D0000A302B06031DF329A601A701A801A901D4
:1003E000AA01AB01F329A20A03012206031DFE297B
:1003F000A30A03012306031DFE29A40A3630970031
:100400002508170203180C2A383097002508170210
:1004100003180F2AA5010C2AA50ACA0AC529A50A8C
:10042000C529080003013006031D1B2A03013106FC
:10043000031D1B2A0800073023020318282A23085B
:10044000AF002208AE003108AD003008AC004B2AE6
:100450002308970022089600073095000830940082
:1004600088211B0ACA0023089700220896009501DC
:100470004A08940088211A08AF001B08AE00310812
:1004800097003008960095014A08940088211A08C0
:10049000AD001B08AC002F0892002E0893009001BD
:1004A000243091006E210E08AF000F08AE002D0819
:1004B00092002C08930090010A3091006E210E08E2
:1004C000AD000F08AC002F0897002E0896002D08ED
:1004D00095002C08940088212D0895002C08940084
:1004E0001A081B08AD001908920018089300900123
:1004F0000A3091006E210E0897000F08960088219F
:100500002D089A00AC01AD01CA014A081B06031967
:10051000922ACA0AAD0A0A302D06031D852AAD01AA
:10052000AC0A852AAE01AF01CA014A081A060319AE
:10053000A22ACA0AAE0A0A302E06031D952AAE0167
:10054000AF0A952A00000800C030E3202F08D7200A
:100550002E08D7202E30D8202D08D7202C08D720C1
:100560006B30D8206D30D8202F30D8206830D8207C
:100570007230D8202030D8205630D8206530D8208E
:100580006C30D8204130D8207630D8206730D82041
:100590002030D820080003014806031D122BD62264
:1005A0004608C3004708C200C80A08003208910084
:1005B000900103309200E83093006E210E08C600CF
:1005C0000F08C7003A08930092013830910090015B
:1005D0006E2146089C0047089D000E089E000F08EB
:1005E0009F00B0212008C6002108C70001089D0017
:1005F0009C0127309F009E01B92121089600970198
:10060000043094009501882146089C0047089D000D
:100610001A089E001B089F00B0212008C600210870
:10062000C7000800D6224608C5004708C400450890
:100630004302031C292B0A3027309C0010309D00F8
:1006400045089E0044089F00B0212008C5002108ED
:10065000C40045089C0044089D0043089E004208D1
:100660009F00B92120089500210894001C309700B4
:100670002030960088211B08C6001908920018082F
:10068000930090010A3091006E210E0897000F0828
:10069000960088211B08C700AC01AD01AE01AF0177
:1006A000CA014A08470603195E2BCA0AAD0A0A3076
:1006B0002D06031D512BAD01AC0A512BAE01AF012C
:1006C000CA014A08460603196E2BCA0AAE0A0A3046
:1006D0002E06031D612BAE01AF0A612BC801080075
:1006E000C030E3202F08D7202E08D7202E30D82066
:1006F0002D08D7202C08D7206B30D8206D30D8207B
:100700002F30D8206830D8207230D8202030D82020
:100710005330D8207030D8206530D8206530D820AC
:060720006430D82008003F
:02400E00F13F80
:00000001FF
Hasta la vista y Buena suerte
PCB of Bike computer and test accessories.
This is the modified PCB diagram that now corresponds with the Theoretical bike and test Electrical circuit Diagram.
The Theoretical diag. +component layout
The theoretical electrical circuit of the Bike computer circuit , the test components and a layout diagram for component placement.