Most people think of floating point math when they think of AWC. After all, our first PAK coprocessors handled floating point numbers. The math PAKs (the PAK-I, II, and IX) sacrifice ease of use in favor of efficiency. Once you've mastered the PAK-I, it is pretty easy to use, but the learning curve can be a little steep. Also, the efficient and flexible protocol used by the PAK can pose a problem when you are trying to use a microprocessor that we don't ordinarily support.
That's why we've introduced the PAK-XII. This PAK uses 9600 baud RS232 and ASCII characters to communicate. That makes it very easy to use. Of course, it also makes it less efficient at transferring long strings of numbers. It also is not very useful (in most cases) for handling processing concurrent with your program. On the other hand, it is dead simple to use. In addition to math, the PAK-XII has our best-ever A/D converter (6 channels with up to 10 bit resolution and a selectable reference). It also has an analog comparator that I won't be using this month.
This month I'll show you a Stamp project that uses the PAK-XII to read the temperature for an LM34. If you aren't familiar with the LM34, it is a transistor-like device that outputs 10mV for every degree F that it senses. So if your office is 80F, the device will output 800mV.
First, though, let's look at a simple example:
Consider this Basic Stamp code:
SEROUT TX,Baud,["1,3/="]
This sends a 1 and a 3 to the PAK. The PAK has a stack much like an HP calculator (that is, it uses reverse polish notation or RPN). So the stack has a 1 and a 3 in it (the PAK's stack is 15 levels deep). The / character does division, which leaves .333333 on the stack. The = character causes the PAK to output the top of the stack (it also deletes the top of the stack, so after these commands the stack is empty).
That's it! If you've used an HP calculator, this seems way too easy. But it really is this simple. You can even output decimal or negative numbers:
SEROUT TX,Baud,[dec x,",3.1415926*="]
This takes the value in variable x and multiplies it by 3.1415926 (it would have been better to use the one character command for pi, but I spelled it out here just to make a point). So you could have written:
SEROUT TX,Baud,[dec x,",p*="]
The last two lines are more or less equivalent (subject to the internal representation of pi).
The PAK-XII can handle trig functions, logarithms, and many other math functions. It can respect the Stamp's handshaking, or you can ask it to pace its results to allow the Stamp to keep up. The PAK even has 10 temporary registers so you don't always have to shuttle data back and forth. Rather than elaborate on every command, I'll refer you to the manual for more details.
As you've seen, it is pretty easy to write an integer number out to the PAK (you just use the dec modifier of SEROUT). However, reading results in is a bit trickier. The PAK tries to format data in such a way that you can read what you want out of it. The first character of output is always a space or a - if the number is negative. Then there is always the whole number part, a decimal point, and the fractional part. There are always a fixed number of digits after the decimal point.
Reading numbers, then, depends on what you want to do with the result. If you just want to copy the numbers to an LCD or a serial port, you may just want to read the ASCII characters one at a time. If you want to read an integer number, you can read the number up to the decimal point. You can even read whole and fractional parts, although remember the Stamp can only read up to 65535. So in practice, you can read the first four characters after the decimal point. If you want to read more, you have to put the extra digits in another variable.
The code below shows a simple example that calls GetResult to display the answers. There is also a GetResult1 and GetResult2 subroutine. Each subroutine does the same job in a different way. Be sure to study these to understand how the different routines handle the numbers.
The schematic above will let you run this month's projects. Simply connect Stamp P2 to pin 2 of the PAK and P3 to pin 3 of the PAK. In addition, connect pin 14 of the PAK to ground or the Stamp's P4 (some of the GetResult routines require handshaking if you don't use the PAK's pacing command).
For the temperature part you'll need an LM34. Pin 1 is +5V, pin 3 is ground, and pin 2 is the temperature output. Connect pin 2 to pin 28 of the PAK. That's all you need. If you want to see the alarm output (part of the program we'll write), connect an LED in the usual way to P15 of the Stamp.
The code below sends several strings to the PAK:
"!!" - Reset the chip (just in case it is in the middle of something) | |
"P2" - Sets 2 mS pacing on the serial line | |
"R3" - Picks the internal 2.56V reference | |
"a5a5a5a5a5++++5/.25*=" - Reads 5 analog values to the stack, adds them, divides by 5 and then multiplies by .25 (see text) |
The last line, is the most complex. Each a5 reads an analog value from channel 5 (pin 28) to the stack. The number is in whole number counts from 0 to 1023 where 1024 would equal 2.56V (because we selected that reference; you can select a 5V reference or an external reference if you prefer). That means each count is worth .0025V and so each count is worth 0.25 degrees (remember, a degree is 10mV). So "a5.25*=" would read a single sample as degrees. However, the actual code reads 5 samples and averages them first.
The code also compares the whole number value (GetResult leaves this in the w variable) to see if the temperature is above 82 degrees:
IF w>=82 THEN HIGH ALARM ELSE LOW ALARM ENDIF
It would probably have made sense to round the number up (by adding .5) before doing the comparison. However, doing this in the PAK would require a "silent" version of GetResult to prevent printing the rounded number. If you aren't printing at all, of course, that wouldn't be a big issue.
On the other hand, the PAK also can compare numbers itself. So if you wanted to compare the temperature to, say, 80.5 degrees, you could use the > or < operators to make that comparison. These operators output a "1" or a "0" (ASCII characters) depending on the truth of the comparison. They don't change the stack at all. You can also use # to test for equality.
Of course, you can also fudge the results to make them more useful to the Stamp. For instance, another way to compare to a temperature of 80.5 would be to multiply the answer by 10 and then compare the whole number part to 805 in the Stamp program.
'{$STAMP BS2} '{$PBASIC 2.5} TX PIN 2 RX PIN 3 ' Optional hardware handshake line HS PIN 4 Baud CON 84 ' Use 240 for BS2P c VAR Byte w VAR Word flag VAR Byte frac VAR Word fracrem VAR Word x VAR Word top: ' If you want to use hardware handshake, add this line ' If you are trying to read character by character ' (that is using GetResult1) you must use hardware ' handshaking (or a very high pacing value. Otherwise ' the Stamp will fall behind. ' Even with hardware handshaking you also must use Px to set ' pacing to give the Stamp a chance to process individual ' characters. ' If you use GetResult, this reads numbers from the PAK ' instead of characters. Then you don't need hardware ' handshaking or pacing (although you can still use either) HIGH HS ' Do not need this line if not using hardware handshake frac=1234 DEBUG "Here we go",CR SEROUT TX,Baud,["!!"] ' good idea to reset the chip first SEROUT TX,Baud,["P2"] ' If you want to set pacing, here's your chance ' This part of the code just computes a table of square and cube roots DEBUG "X",TAB,"Square Root",TAB,"Cube Root",CR FOR x=1 TO 500 DEBUG DEC x,TAB SEROUT TX,Baud,[DEC x,"@%="] ' DUP and compute square root... GOSUB GetResult ' change to GetResult1 or 2, if desired DEBUG TAB SEROUT TX, Baud,["1,3/^="] ' And cube root too (could have stored 1/3 in a register btw) GOSUB GetResult ' change to GetResult1 or 2, if desired DEBUG CR NEXT ' End of test program DEBUG "Done" END ' This is one of three "GetResult" subroutines ' This one reads several integer numbers and is fast enough to not require ' handshaking or pacing on a BS2P. For a BS2, you will still need pacing ' of at least 2 or hardware handshaking and a pacing of 1 ' Note that this routine is plauged by the Stamp's word size. So if the whole number part is ' >=65535 this is going to fail. The fractional part is handled by reading the first 4 digits ' You could do something similar with the whole number part (which means you'd have one variable ' in units of 10,000's and another in single units. So a number like 500,009 would ' be in two varaibles: 50 and 9. Alternately, if you just want to print the result to an LCD or ' similar device you could read the text as a string. ' Keep in mind that if you don't use hardware handshaking, you must immediately call this routine after ' you issue an = or E command! On a slower processor you may HAVE to use hardware handshaking GetResult: ' Have to read 4 digits of fraction since something like .70000 would blow the word size ' variable. Can read more precision in framrem of just skip it SERIN RX\HS,Baud,[flag, DEC w,WAIT("."),DEC4 frac,DEC fracrem] ' alternate SERIN for hardware handshake 'SERIN RX\HS,Baud,[flag, DEC w,WAIT("."),DEC4 frac,DEC fracrem] DEBUG flag,DEC w,"." ' Print correct number of leading zeros for fraction IF frac>=1000 THEN emitfrac DEBUG "0" IF frac>=100 THEN emitfrac DEBUG "0" IF frac>=100 THEN emitfrac DEBUG "0" emitfrac: DEBUG DEC frac ' Note you might be tempted to add 5 to frac or look at frac rem to try to round up ' You can do this, but you must be careful to handle overflow to the whole number part! RETURN ' This routine simply reads characters and dumps them ' This prints every digit the PAK returns (unlike GetResult) ' If you use this, be sure to use hardware handshaking and/or a high pacing value ' If using handshaking 1mS pacing is enough (BS2 or BS2P). ' If not using handshake, use at least 3mS (BS2P) or 4mS (BS2) ' ' This is necssary because the Stamp is relatively slow at fetching instructions ' from its EEPROM. When you read a bunch of characters in one SERIN, the Stamp's ' speed is much faster since it doesn't have to load new data from EEPROM GetResult1: DO SERIN RX\HS,Baud,[c] IF c<> 13 THEN DEBUG c LOOP WHILE c<>13 RETURN cbuf VAR Byte(16) ' This just reads a string ' You can find a version that ' reads numeric variables online ' Note the Stamp is not fast enough ' to read a character at a time, ' but it can read the whole string ' at once ' Works on a BS2 w/o handshaking and pacing=1 ' But requires no pacing with handshaking GetResult2: SERIN RX\HS,Baud,[STR cbuf\16\13] DEBUG STR cbuf RETURN
'{$STAMP BS2} '{$PBASIC 2.5} TX PIN 2 RX PIN 3 ' Optional hardware handshake line HS PIN 4 Baud CON 84 ' Use 240 for BS2P ALARM CON 15 c VAR Byte w VAR Word flag VAR Byte frac VAR Word fracrem VAR Word x VAR Word top: ' If you want to use hardware handshake, add this line ' If you are trying to read character by character ' (that is using GetResult1) you must use hardware ' handshaking (or a very high pacing value. Otherwise ' the Stamp will fall behind. ' Even with hardware handshaking you also must use Px to set ' pacing to give the Stamp a chance to process individual ' characters. ' If you use GetResult, this reads numbers from the PAK ' instead of characters. Then you don't need hardware ' handshaking or pacing (although you can still use either) HIGH HS ' Do not need this line if not using hardware handshake frac=1234 DEBUG "Here we go",CR SEROUT TX,Baud,["!!"] ' good idea to reset the chip first SEROUT TX,Baud,["P2"] ' If you want to set pacing, here's your chance SEROUT TX,Baud,["R3"] ' pick 2.56V reference MainLoop: SEROUT TX,Baud,["a5a5a5a5a5++++5/.25*="] GOSUB GetResult ' see if whole number is>=82 IF w>=82 THEN HIGH ALARM ELSE LOW ALARM ENDIF DEBUG CR PAUSE 1000 GOTO MainLoop GOTO MainLoop ' End of test program DEBUG "Done" END ' This is one of three "GetResult" subroutines ' This one reads several integer numbers and is fast enough to not require ' handshaking or pacing on a BS2P. For a BS2, you will still need pacing ' of at least 2 or hardware handshaking and a pacing of 1 ' Note that this routine is plauged by the Stamp's word size. So if the whole number part is ' >=65535 this is going to fail. The fractional part is handled by reading the first 4 digits ' You could do something similar with the whole number part (which means you'd have one variable ' in units of 10,000's and another in single units. So a number like 500,009 would ' be in two varaibles: 50 and 9. Alternately, if you just want to print the result to an LCD or ' similar device you could read the text as a string. ' Keep in mind that if you don't use hardware handshaking, you must immediately call this routine after ' you issue an = or E command! On a slower processor you may HAVE to use hardware handshaking GetResult: ' Have to read 4 digits of fraction since something like .70000 would blow the word size ' variable. Can read more precision in framrem of just skip it SERIN RX\HS,Baud,[flag, DEC w,WAIT("."),DEC4 frac,DEC fracrem] ' alternate SERIN for hardware handshake 'SERIN RX\HS,Baud,[flag, DEC w,WAIT("."),DEC4 frac,DEC fracrem] DEBUG flag,DEC w,"." ' Print correct number of leading zeros for fraction IF frac>=1000 THEN emitfrac DEBUG "0" IF frac>=100 THEN emitfrac DEBUG "0" IF frac>=100 THEN emitfrac DEBUG "0" emitfrac: DEBUG DEC frac ' Note you might be tempted to add 5 to frac or look at frac rem to try to round up ' You can do this, but you must be careful to handle overflow to the whole number part! RETURN
Site contents © 1997-2018 by AWC, Houston TX (281) 334-4341