Questions submitted via the Answer Line forms are answered here. When you submit a question, you'll receive an automated response within 24 hours. If you don't receive that response, please let us know in e-mail or re-submit your question.
If you do get the automated response, but the answer to your question doesn't appear here within two or three days, it simply means that we're temporarily too busy to answer questions as quickly as we'd like. Please don't re-submit your question in this case, as doing so will only make us busier. If we can't answer your question, or if we must reject it because the answer to a similar question is already posted here, we'll let you know in e-mail.
We aren't looking for submissions, so please don't send us files of subroutines, etc., that you've compiled. Aside from possible intellectual-property problems, we don't have the resources necessary to check your submissions for accuracy (it's hard enough just making sure that our own answers are correct).
If you have files that you think might be useful to microcontroller users, put them up on your own web page and let us know... We'll be happy to add a link to your site.
With the window uncovered, a JW part can clear all its File Registers just by being exposed to light. If you're not explicitly initializing RAM, this is kinda handy. If you cover the window, though, the JW part behaves just like the OTP; the File Registers all come up in unknown states and the code doesn't work.
EDI Corporation also has a pretty complete line, and their prices are often considerably better than Emulation Technology's.
2. Now you change port B0 to an input (TRISB = xxxxxxx1), and read PORTB (with a MOVF PORTB,W or something). Since B0 is pulled up, you'll read a "1" on B0 (PORTB = xxxxxxx1).
Following so far? Good...
3. Now you make port B0 an output again (TRISB = xxxxxxx0), and, since PORTB = xxxxxxx1, the B0 pin will be driven HIGH.
To keep this from happening, you should always load PORTB with the values you expect on the output pins JUST BEFORE you make them outputs. In the example above, a BCF PORTB,0 just before step 3 would have ensured that the pin would drive low after changing the TRISB register.
Of course, you may just be neglecting to set the register-page select bits properly before you write to TRISB, and you might actually be writing to PORTB inadvertently. Since TRISB is on the upper page of registers, you must be sure that RP0 is set before you write to TRISB.
Well, ok... You can, but you don't want to. Use the DT directive instead.
DT "Test",0will assemble to:
RETLW "T" RETLW "e" RETLW "s" RETLW "t" RETLW 0
A 4 MHz clock speed will give you 32 machine cycles per bit, which should be plenty of time to do what you want. If you need more time, you can run faster.
LIST R=DEC ASCIIO EQU .... ;A FILE REGISTER. ASCIIT EQU .... ;ANOTHER FILE REGISTER. ASCIIH EQU .... ;ANOTHER FILE REGISTER. ; ENTER WITH ORIGINAL 8-BIT VALUE IN "ASCIIO". EXITS WITH ASCII ; REPRESENTATION OF ONES' DIGIT IN "ASCIIO", TENS' DIGIT IN "ASCIIT", ; AND HUNDREDS' DIGIT IN "ASCIIH". CONVRT MOVLW '0' MOVWF ASCIIH MOVWF ASCIIT DO100S MOVLW 100 SUBWF ASCIIO,W BNC DO10S MOVWF ASCIIO INCF ASCIIH GOTO DO100S DO10S MOVLW 10 SUBWF ASCIIO,W BNC ADJUST MOVWF ASCIIO INCF ASCIIT GOTO DO10S ADJUST MOVLW '0' ADDWF ASCIIO
Manufacturer Model Telephone ------------ ----- --------- Advanced Transdata PGM16X8 214 980-2960 Elan Digital Systems 5-940 800 541-ELAN ICE Technology Speedmaster 8000 716 475-0495 Logical Devices GANGPRO-S 305 428-6868 MQP Electronics MQP System 2000 44 666-8252146 Paragon Programming PPC-9201 513 244-1116 Stag Programmers Solar 408 988-1118 Unitec Electric Pim/10 81 255 74-4124If you're in Europe, try:
Elan Digital Systems 44 489-579799 (England) ICE Technology (c/o Magnadata) 49 6082 1615 +742 (Germany) ICE Technology (c/o Infratech) 49 40 81 75 78 (Germany) MQP Electronics 49 89/4602071 (Germany) Stag Programmers 44 707 332148 (England)
On the other hand, I wouldn't recommend over-revving PICs in any real production units of your product; the high-speed parts don't cost all that much more.
Here's a visual aid showing the contents of the stack at 6 different times, labeled a through f:
a. b. c. d. e. f. 0C0 0D0 0C0 070 060 060 0B0 0C0 0B0 060 060 060 0A0 0B0 0A0 060 060 060 090 0A0 090 060 060 060 080 090 080 060 060 060 070 080 070 060 060 060 060 070 060 060 060 060 050 060 060 060 060 060At a., the stack is full. After 8 RETURNs, we'll be back at our starting point, 050. At b., we've done one more CALL, pushing the lowest address off the stack. At c., we've done a RETURN... see what happens at the bottom of the stack?
The diagram at d. shows what's happened after five more RETURNS. Everything's still going smoothly at e., one RETURN later. After returning to address 060, though, a further RETURN will not send us back to our original starting point (address 050). Instead, we'll just get stuck in a loop between 060 and the first RETURN after 060.
CALL TEST ;Subroutine returns with W=0 for FALSE, W=1 for TRUE. ADDWF PCL ;For the 16C5x, this should be "ADDWF PC". GOTO FALSE TRUE ....Of course, care must be taken with page-boundary crossings. If you're using the 16Cxx, you'll need to make sure that PCLATH is pointing at the current page before you do the ADDWF. On the 16C5x, this code-fragment will only work if TRUE is on the first half of a page, and (for the 16C56/57/58) you'll need to ensure that the code-page select bits are pointing at the current page before you do the ADDWF.
If you're willing to go to a bit of trouble, you can do it in a two-step process:
Write a short 16C84 program that just programs your EEPROM to the desired values. Burn it into a 16C84, run the program, then burn the 16C84 with your real code.
If you really get lucky and find yourself trying to determine something simple, like whether the contents of a register are greater than 127, you can, of course, just do a single bit-test.
MOVLW constant ;If register = constant, jump. SUBWF register,W BZ R_EQUALS_C MOVLW constant ;If register >= constant, jump. SUBWF register,W BC R_GT_EQ_C MOVLW constant ;If register < constant, jump. SUBWF register,W BNC R_LT_C MOVLW constant ;If register > constant, jump. SUBWF register,W SKPZ SKPC GOTO $+2 GOTO R_GT_C MOVLW constant ;If register <= constant, jump. SUBWF register,W BZ R_LT_EQ_C BNC R_LT_EQ_CNote that the ">" and "<=" tests use more space than the others. Rather than testing for "greater than x", therefore, you may wish to test for "greater than or equal to x+1". Likewise, instead of testing for "less than or equal to x", you may wish to test for "less than x+1".
If you have an external clock (with an oscillator and everything... not just a crystal or ceramic resonator), you can obviously run its signal to the CLKIN (OSC1) pin of each PIC.
If you're using the PIC's built-in oscillator with an external crystal or ceramic resonator, you can simply tie one PIC's CLKOUT (OSC2) pin to the other's CLKIN, then put the crystal/resonator between the two remaining OSC pins. Some experimentation with capacitor values may be necessary.
Unfortunately, the PICs' instruction clocks run at 1/4 the external clock frequency. While it's pretty easy to sync the PICs to within 1 instruction cycle, there's no way to guarantee that each PIC's divided-down instruction clock is exactly in sync with the other's. Because the oscillator startup timer is dependent on an inaccurate internal R/C, the PICs will probably be out of sync by 1 or 2 oscillator periods in either direction.
This means is that you'll still have to allow some time between placing data on the bus and taking it off, even if your software is clever enough to get the PICs in sync to within 1 instruction cycle.
Actually, Martin J. Maney (who's apparently a little more level-headed than I) recently explained the inconsistency thusly:
The first PICs (all the way back to General Instruments' NMOS 1650) didn't include "subtract" instructions. They did have "literal" instructions (ADDLW, XORLW, etc.), but none of those instructions was order-dependent. The only instruction that was order-dependent was MOVLW, so GI/Microchip set the order of all the literal instructions to match it.
By the time that the "Subtract W from L" instruction was added to the newer PICs' instruction sets, it was too late... All the other literal instructions already used the "....LW" ordering, so Microchip applied it to the SUBLW instruction, too.
Personally, this explanation leaves me a little unsatisfied -- foolish consistency is, as they say, the hobgoblin of little minds -- but I guess it'll have to do. At least it takes the edge off my homicidal urge.
#DEFINE LED PORTA,0 ;The LED is on RA0. .... BSF LED ;Turn the LED on.
INTW EQU [any page-0 register] INTS EQU [any page-0 register] .... ; INTERRUPT SERVICE ROUTINE: PUSH MOVWF INTW ;SAVE THE W-REGISTER. SWAPF STATUS,W ;SAVE THE STATUS REGISTER. MOVWF INTS ; BCF STATUS,RP0 ;MAKE SURE WE'RE ON REGISTER PAGE 0 [optional]. .... POP SWAPF INTS,W ;RESTORE THE STATUS REGISTER. MOVWF STATUS ; SWAPF INTW ;RESTORE THE W-REGISTER. SWAPF INTW,W ; RETFIE ;RETURN AND RE-ENABLE INTERRUPTS.
INTW EQU [any page-0 register] INTS EQU [any page-0 register] INTW1 EQU INTW + 080H .... ; INTERRUPT SERVICE ROUTINE: PUSH MOVWF INTW MOVF STATUS,W BCF STATUS,RP0 MOVWF INTS ; save PCLATH and FSR here. .... POP ; restore PCLATH and FSR here. MOVF INTS,W MOVWF STATUS SWAPF INTW SWAPF INTW,W RETFIE
Note that the ADCON1 register defaults to 00 on power-up.
This is also an issue for the 16C71, in which ADCON1 must be set to xxxxxx11 binary.
; NOTE: THIS IS FOR THE CURRENT VERSION OF THE 16C74 ONLY! CP_ALL EQU 03F8FH ;NOTE: These equates are for the CP_75 EQU 03F9FH ; current version of the CP_50 EQU 03FAFH ; PIC16C74 ONLY! CP_OFF EQU 03FBFH ; PWRT EQU 03FBFH ; They are NOT for the 16C74A, NO_PWRT EQU 03FB7H ; nor are they guaranteed to WDT EQU 03FBFH ; work on any other devices. NO_WDT EQU 03FBBH ; LP_OSC EQU 03FBCH ; XT_OSC EQU 03FBDH ; HS_OSC EQU 03FBEH ; RC_OSC EQU 03FBFH ; __FUSES CP_ALL & PWRT & NO_WDT & XT_OSC ;Enable code ;protection and ;power-up ;timer, disable ;the watchdog ;timer, and ;select XT ;oscillator.Note that the __FUSES directive is preceded by two underscores, not one.
One more thing to note: If you have a GOTO at the interrupt vector (address 0004), remember that it behaves just like any other GOTO; if PCLATH is set when an interrupt occurs, the PIC will jump to an address on page 1. To avoid problems, don't put a GOTO at the interrupt vector. Instead, you can either put your whole interrupt routine at 004 or put just the register-saving code there, followed by a GOTO to the rest of your ISR.
The best way to save registers on the 16C73 is something like this:
INTW EQU any page-0 register INTW1 EQU INTW + 080H INTS EQU any page-0 register INTP EQU any page-0 register INTF EQU any page-0 register ORG 0004h INTVEC: MOVWF INTW MOVF STATUS,W BCF STATUS,RP0 MOVWF INTS MOVF PCLATH,W MOVWF INTP MOVF FSR,W MOVWF INTF ;at this point, if you like, you can modify PCLATH and ;jump to the rest of the ISR on another page.Then, to restore the registers at the end of your ISR, do this:
MOVF INTF,W MOVWF FSR MOVF INTP,W MOVWF PCLATH MOVF INTS,W MOVWF STATUS SWAPF INTW SWAPF INTW,W RETFIE
The 4-digit number, by the way, is a date-code. "9420" means that the chip was packaged in the 20th week of 1994.
The prescaler is not contained in the OPTION register. The OPTION register's PS0-PS2 bits only set the divide-by ratio, or range, of the prescaler, and they are changed only by RESET and explicit writes to the OPTION register. They are not affected by writes to the RTCC.
Put the signal on Port B0, leaving B1-B7 unused. Then:
SETUP MOVLW 00000001B ;MAKE B0 AN INPUT, B1-B7 OUTPUTS. TRIS PORTB ; SAMPLE RLF PORTB ;SHIFT 7 CONSECUTIVE SAMPLES OF RLF PORTB ;B0 INTO B7-B1. RLF PORTB ; RLF PORTB ; RLF PORTB ; RLF PORTB ; RLF PORTB ; MOVF PORTB,W ;W-REG CONTAINS LAST 8 SAMPLES ;(MSB = EARLIEST, LSB = LATEST).
To recover, MCLR must be pulsed or Vdd must be lowered (below 0.7V), then raised (more quickly than 0.05V/ms).
Once the OPTION register is set, every transition on the RTCC pin will increment the contents of the RTCC register. By monitoring that register, you can easily see when transitions occur.
INTW EQU [any page-0 register] INTS EQU [any page-0 register] .... ; INTERRUPT SERVICE ROUTINE: PUSH MOVWF INTW ;SAVE THE W-REGISTER. SWAPF STATUS,W ;SAVE THE STATUS REGISTER. MOVWF INTS ; BCF STATUS,RP0 ;MAKE SURE WE'RE ON REGISTER PAGE 0 [optional]. .... POP SWAPF INTS,W ;RESTORE THE STATUS REGISTER. MOVWF STATUS ; SWAPF INTW ;RESTORE THE W-REGISTER. SWAPF INTW,W ; RETFIE ;RETURN AND RE-ENABLE INTERRUPTS.Changing the SWAPF STATUS,W to MOVF STATUS,W will work just fine, since the Z-Flag (which can be changed by that MOVF) won't change until after the contents of STATUS are copied to the W-Register.
Of course, if you use MOVF STATUS,W in your "save" code, you must use a corresponding MOVF INTS,W in your "restore" code.
The best source, by far, is your local Microchip sales rep. Contact information is on Microchip's Web page.
If you don't mind reading data books on your PC screen, you can download the data sheets, in Adobe Acrobat format, from either Microchip's BBS or from their Web site (an Acrobat Reader is available from those sites, too). The BBS is accessible through Compuserve's network (although you don't have to be a Compuserve member to use it), so it's a local call from nearly everywhere in the U.S. The Web site is at http://www.ultranet.com/microchip.
Here's a 16C5x example:
; ;RETURN THE X'TH PRIME NUMBER, WHERE X IS IN THE RANGE [1-10]. ;ENTER WITH X IN THE W-REGISTER, EXITS WITH THE X'TH PRIME NUMBER ;IN THE W-REGISTER. ; LOOKUP ADDWF PC ;JUMP TO THE APPROPRIATE RETLW. NOP ;"ZERO'TH" PRIME NUMBER WOULD GO HERE. RETLW 2 ; 1ST PRIME NUMBER IS 2. RETLW 3 ; 2ND PRIME NUMBER IS 3. RETLW 5 ; 3RD PRIME NUMBER IS 5. RETLW 7 ; 4TH PRIME NUMBER IS 7. RETLW 11 ; 5TH PRIME NUMBER IS 11. RETLW 13 ; 6TH PRIME NUMBER IS 13. RETLW 17 ; 7TH PRIME NUMBER IS 17. RETLW 19 ; 8TH PRIME NUMBER IS 19. RETLW 23 ; 9TH PRIME NUMBER IS 23. RETLW 29 ;10TH PRIME NUMBER IS 29. .... MAIN MOVLW 7 ;LOOKUP THE 7TH PRIME NUMBER. CALL LOOKUP ; MOVWF RESULT ;STORE IT IN "RESULT".There are a couple of things to note here. First, the range of inputs to the LOOKUP subroutine must be tightly constrained. In our example, W must be in the range [1-10]; calling LOOKUP with W > 10 will cause disastrous results, as the Program Counter will end up pointing God-knows-where in your program. Second, you need to remember that if W = 0 on entry to the subroutine, the ADDWF will point the Program Counter to the instruction after the ADDWF, since the PIC increments its Program Counter before executing the instruction. For this reason, our example (which only deals with inputs > 0) puts a NOP right after the ADDWF.
Ok... Some more things:
On the 16C5x parts, the ADDWF and each of the RETLWs must be located in the first half of the code page in which they're located. That is, the entire subroutine must fit in the [000-0FF], [200-2FF], [400-4FF], or [600-6FF] areas.
For the 16Cxx parts, the subroutine can be anywhere in memory, but the PCLATH register should be loaded with the hi-byte of the table-elements' addresses before calling LOOKUP. That is, the MAIN routine should look like this:
MAIN MOVLW HIGH (LOOKUP+1) ;MAKE SURE THAT THE "ADDWF" IN "LOOKUP" MOVWF PCLATH ;JUMPS TO THE CORRECT PAGE. MOVLW 7 ;LOOKUP THE 7TH PRIME NUMBER. CALL LOOKUP ; MOVWF RESULT ;STORE IT IN "RESULT". MOVLW HIGH ($+2) ;RESTORE THE PCLATH REGISTER. MOVWF PCLATH ;This assumes that the whole lookup-table subroutine fits in one 256-byte page of memory. If the table crosses page-boundaries, the PCLATH must be pre-conditioned to point at the appropriate page for the element being accessed. Since it's usually very easy to keep tables in one page, I won't show the necessary code here; if you need it, it's in Microchip's Application Note #AN556.
185-A Legrand Avenue
Northvale, NJ 07647
Tel: 201 784-9700
MOVF PORTB,W ;READ ALL 8 BITS OF PORTB INTO THE W-REGISTER. or MOVLW 00001111B ;PULL RB4-RB7 OUTPUTS LOW. ANDWF PORTB ; or MOVF PORTB,W ;GRAB THE STATE OF ALL 8 PORTB PINS. MOVWF LAST ;STORE IT. WAIT_FOR_CHANGE: MOVF LAST,W ;COMPARE THE STORED STATE TO THE CURRENT STATE XORWF PORTB,W ;(WITHOUT AFFECTING EITHER "PORTB" OR "LAST"). BNZ WAIT_FOR_CHANGE ;IF ALL 8 PINS ARE STILL THE SAME, LOOP BACK ;AND KEEP WAITING.
For instance, the 16C54A-04 is rated for speeds up to 4 MHz; it can be user-programmed for RC or XT operation. The 16C54A-10 (rated for speeds up to 10 MHz) can be programmed for RC or XT operation up to 4 MHz, or HS operation to 10 MHz. The 16C54A-20 is rated for 20 MHz, and should only be used in HS mode. For LP mode, Microchip recommends the 16LC54A-04, which can be used up to 200 KHz.
The information in the previous paragraph isn't completely accurate. Actually, each of those chips will work in at least three oscillator modes; the -04 will do all 4 modes (but only up to 200 KHz for LP and 4 Mhz for the others), the -10 will do all but LP (but only up to 4 MHz for RC and XT modes), the -20 will also do all but LP (with the same RC and XT maximum-speed restrictions as the -10 part), and the LC54A-04 will do all but HS mode (but only up to 2 MHz in RC or XT mode). However, the parts aren't guaranteed to meet the databook's Min/Max specifications when they're used in these other modes (although they are tested for basic functionality), so it's probably best to stick to the oscillator-type recommendations in the previous paragraph.
ADDWF REG,W or ADDWF REG,0For a source-register destination, the instruction looks like this:
ADDWF REG or ADDWF REG,F or ADDWF REG,1These last three are equivalent, and most people just imply the source-register destination by using ADDWF REG.
The folks who man Microchip's Corporate Applications Tech-Support lines recently discovered that there are certain people in the world who are so brain-dead that they can't remember that ADDWF REG puts the result of the addition back in REG. For those people, Microchip has added (over the protests of MPASM's author, Kim Cooper) the "Using default destination...." message.
To keep those messages from appearing, put the following line at the start of your program:
This is fine; RA3 can still be used as a digital output. The problem, though, is that whenever you READ from RA3, you'll see a "0".
The BSF and BCF instructions are read-modify-write instructions. That is, a BSF will read the entire port, change the appropriate bit, then write the entire port back.
When your BSF PORTA,5 instruction executes, it reads all of port A, including RA3 (which reads as "0"). It then sets RA5 and writes the value back, with bit 3 now cleared.
There are a bunch of ways to deal with this situation. The most straightforward is to make changes to a "shadow" of PORTA that you keep in a general-purpose register. After the changes are made, you can copy the contents of this register to PORTA with a MOVWF. If you use this technique, your code would look something like this:
SHADOW EQU [any register] .... CLRF PORTA ;Initialize PORTA and its shadow. CLRF SHADOW ; .... BSF RP0 ;Switch to register-page 1. MOVLW 00000011B ;Set up PORTA2-5 as outputs. MOVWF TRISA ; BCF RP0 ;Switch back to register-page 0. .... MOVF SHADOW,W ;Grab the shadow. IORLW 00100000B ;Set RA5. MOVWF PORTA ; MOVWF SHADOW ;Make sure the shadow is still in ;sync with PORTA.If you're using interrupts that write to or read from PORTA, you'll need to make sure that no interrupts occur while SHADOW and PORTA are out of sync, which is a pain, but other than that (and the speed/space penalty incurred by using a shadow register), the above solution will work fine.
WAIT_FOR_EE: CLRWDT BTFSS EEIF GOTO WAIT_FOR_EE
CALL1 CALL SUB .... CALL2 CALL SUB .... SUB NOP RETLW 0New way:
#DEFINE RETTO1 REG,0 ;If this bit is hi, "return" to CALL1. ;Otherwise, "return" to CALL2. .... CALL1 BSF RETTO1 GOTO SUB1 .... CALL2 BCF RETTO1 GOTO SUB1 .... SUB1 NOP BTFSC RETTO1 GOTO CALL1+2 GOTO CALL2+2This uses only part of one register and a few extra instructions, and is much more efficent than simulating a full-blown stack.
CCPxX and CCPxY contain the two lowest-order bits of the 10-bit duty-cycle register. When you use "8-bit" mode, you're just setting those two low-order bits to "00", which effectively sets the PWM duty cycle to four times the number in the duty-cycle register. To use 10-bit mode, just put the low 2 bits of your desired duty-cycle in CCPxX and CCPxY, with the upper eight bits in the duty-cycle register.
There are other C compilers out there that are cheaper, but none are as complete and well-supported as MPC. MPC currently has a few bugs, and its code isn't as tight as that written by an expert assembly-language programmer, but it's still the best that's available.
You can download a fully-working (except for a generous limit on the size of the output assembly-code) demo copy of the compiler from Byte Craft's BBS.
Fortunately, Microchip is integrating MPSIM into their existing Windows-based MPLAB integrated development environment, which currently contains an editor, assembler, and emulator. They expect to have completed the integration by Spring, 1996.
"MPSIM-enabled" versions of MPLAB, like all of Microchip's currently-available development tools, will be made available at no charge.
MOVLW 005H ;POINT THE FSR AT REGISTER 5. MOVWF FSR ; MOVF INDF,W ;GRAB THE CONTENTS OF THE REGISTER ;POINTED TO BY THE FSR. INCF FSR ;NOW THE FSR POINTS TO REGISTER 6. MOVWF INDF ;WRITE THE CONTENTS OF THE W-REGISTER ;TO THE REGISTER POINTED TO BY THE FSR.
Floating-point values are represented by a two-number combination: a "mantissa" and an "exponent". For a floating-point value x,
x = mantissa * (2 ** exponent),where "**" means "raised to the power".
It's easy to see that there are an infinite number of mantissa/exponent combinations for any x. For example, all the following combinations give a result of 10 (decimal):
10 * (2 ** 0) 5 * (2 ** 1) 2.5 * (2 ** 2) 1.25 * (2 ** 3) 0.625 * (2 ** 4) 0.3125 * (2 ** 5)Binary representation of numbers less than 1 work just like decimal fractions, except that the first digit to the right of the decimal point is the 1/2's digit, the next is the 1/4's, the next is the 1/8's, etc. If we convert the mantissas listed above from decimal to binary, we get:
10 = 1010 5 = 101 2.5 = 10.1 1.25 = 1.01 0.625 = 0.101 0.3125 = 0.0101See a pattern?
Ok... There's a rule for choosing which of the infinite number of mantissa/exponent combinations to use. It's pretty simple: Find the mantissa with all 0's to the left of the decimal point and a "1" immediately to the right of the decimal point. That mantissa, with its corresponding exponent, is the one to use. Converting from one of the other combinations to that one is called "normalization", by the way.
In our case (floating-point representation for x = 10 decimal), we'd use 0.625 * (2 ** 4). Converting our mantissa and exponent to binary,
mantissa = 0.101 exponent = 100Everything clear so far? Good, because it gets a little tricky here.
Since the mantissa is, by definition, always 0.1xxxx..., we don't need to store the "0.1" portion of it. Instead, we'll just imply the "0.1" and make our mantissa contain only the bits which follow the "0.1".
"But wait," you ask, "What about negative numbers?"
Well, ok. Since we've saved a bit or two by not including the leading "0.1", we'll put the sign bit in the mantissa's most-significant (leftmost) bit position. For positive numbers, the sign bit is "1"; for negative numbers, the sign bit is "0".
After stripping off the "0.1", our mantissa is simply "01". Preceding it with the sign bit makes it "101".
Since math guys just can't leave well enough alone, the exponent gets messed with, too. They call it "biasing", and it's real simple: If "m" is the number of bits in the exponent (8 in our case), you just add 2 ** (m-1) to the exponent. After biasing the exponent for our example (and stripping / signing the mantissa), we have:
mantissa = 101 exponent = 10000100Since the PIC math routines use a 16-bit mantissa, the actual values are:
mantissa = 1010000000000000 exponent = 100000100, or mantissa = 0xA000 exponent = 0x84For -10 decimal, the mantissa is 0010000000000000 (note the leading "0" instead of "1"), or 0x2000, and the exponent is unchanged from the exponent for x = 10.
That's all there is to it.
For numbers between 0 and 1 (or between 0 and -1), it works exactly the same way. Reach back to your memory of high-school algebra class and remember that if y is a positive integer, then:
x ** -y = 1 / (x ** y)For example, 3 ** 2 = 9; 3 ** -2 = 1/9.
Applying this notion, we can see that the floating-point representation of, say, 1/10 is 0.8 * (2 ** -3). For x = 1/10, then, our mantissa and exponent are:
mantissa = 0.8 (decimal) exponent = -3 (decimal), or mantissa = 0.1100110011001100.... exponent = 11111101Note that the exponent uses two's complement notation for negative numbers (0xFF = -1, 0xFE = -2, ..., 0x81 = -127, 0x80 = -128).
After stripping/signing the mantissa and biasing the exponent:
mantissa = 1100110011001100 exponent = 01111101, or mantissa = 0xCCCC exponent = 0x7DOh, yeah... One more thing. For x = 0, the mantissa is 0 and the exponent is 0. In fact (because of the biasing), the exponent equals 0 only when x is 0.
Also, you may care that the PIC representation isn't exactly the same as the IEEE Standard; I don't.
; 8-Bit Divide. Written by Andy Warren. ; ; Copyright (C) 1994 Fast Forward Engineering. All rights reserved. ; Permission is hereby granted for any non-commercial use, ; so long as this copyright notice remains intact. ; ; Enter with Dividend in DIVIDEND, divisor in DIVISOR. ; ; Exits with quotient in QUOTIENT and remainder in REMAINDER, DIVIDEND ; scrambled, DIVISOR unchanged, carry-flag set. DIVIDE MOVLW 1 MOVWF QUOTIENT CLRF REMAINDER LOOP RLF DIVIDEND RLF REMAINDER MOVF DIVISOR,W SUBWF REMAINDER,W SKPNC MOVWF REMAINDER RLF QUOTIENT BNC LOOP
+5V +--------------------------------+--------PIC I/O PIN | | | | 1K V 10K | +-----/\/\/\/-----/\/\/\/-----/\/\/\/-----+ === 0.1 uF 25K | | | | GND GNDIn your code, make the I/O pin an output and pull it low to discharge the cap. Then make it an input and measure the time it takes for the pin to read "1".
The PIC's 0/1 threshold changes with Vdd, and there's a part-to-part variation, but neither of those factors should affect your application much.
If I were you, I'd forget about using a PIC altogether, and just use off-the-shelf sync-separator and sync-generator chips. Everybody makes these things... Try National's LM1881 sync-separator and LM1882 sync-gen (or, if your sync source is less than perfect, you may want to use Elantec's separator).
One thing to note: The program will compensate for the length of any code you want to place within the delay loop. However, it doesn't do this very intelligently when that user code takes more than half a dozen cycles or so. The generated code will still be accuately-timed, of course, but it won't look very pretty.
' ' Four-Variable PIC Delay Calculator, Version 4 ' ' v1 - Written by Andy Warren. 31 Jan 1995 ' v2 - Fixed the bug found by Eric Liu. 30 April 1995 ' v3 - In order to make it easier to transcribe the ' program from online sources, replaced all strings ' of spaces with "q$(x)" string variables. 16 Sep 1995 ' v4 - Fixed the bug found by Kalle Pihlajasaari. 23 May 1996 ' ' (C) 1995-96 Fast Forward Engineering ' ' Permission is hereby granted for all ' non-commercial use. ' defdbl a-z dim q$(40) for x = 1 to 40 q$(x) = "" for y = 1 to x: q$(x) = q$(x) + " ": next next start: x = 0 input "Number of cycles to delay:",delay input "Cycles consumed by user code within the delay loop:",user delay = int(delay): user = int(user) u$ = "; Your"+str$(user)+"-cycle code goes here." print if (delay - user) < 32 then print q$(34);u$: delay = delay - user: goto inline x = delay - 16 - user d = int(x/(256*(256*(256*(3+user)+2)+2)+2)): if d > 255 then d = 255 x = x - d * (256*(256*(256*(3+user)+2)+2)+2) c = int (x/(256*(256*(3+user)+2)+2)): if c > 255 then c = 255 x = x - c * (256*(256*(3+user)+2)+2) b = int (x/(256*(3+user)+2)): if b > 255 then b = 255 x = x - b * (256*(3+user)+2) a = int (x/(3+user)): if a > 255 then a = 255 x = a * (3+user) x = x + b * (256*(3+user)+2) x = x + c * (256*(256*(3+user)+2)+2) x = x + d * (256*(256*(256*(3+user)+2)+2)+2) x = x + 16 + user a = a + 1: b = b + 1: c = c + 1: d = d + 1 if a = 256 then a = 0 if b = 256 then b = 0 if c = 256 then c = 0 if d = 256 then d = 0 a$=right$(q$(3)+str$(a),3) b$=right$(q$(3)+str$(b),3) c$=right$(q$(3)+str$(c),3) d$=right$(q$(3)+str$(d),3) print q$(7);"list r=dec" print print "r1";q$(5);"equ";q$(5);"[any file register]" print "r2";q$(5);"equ";q$(5);"r1+1" print "r3";q$(5);"equ";q$(5);"r2+1" print "r4";q$(5);"equ";q$(5);"r3+1" print q$(20);"___________________" print "delay:";q$(1);"movlw";q$(1);a$;q$(3);"|";q$(19);"|" print q$(7);"movwf";q$(2);"r1";q$(3);"|";q$(19);"V" print q$(7);"movlw";q$(1);b$;q$(3);"|";q$(7);"loop:";q$(2);u$ print q$(7);"movwf";q$(2);"r2";q$(3);"|";q$(14);"decfsz";q$(1);"r1" print q$(7);"movlw";q$(1);c$;q$(3);"|";q$(14);"goto";q$(3);"loop" print q$(7);"movwf";q$(2);"r3";q$(3);"|";q$(14);"decfsz";q$(1);"r2" print q$(7);"movlw";q$(1);d$;q$(3);"|";q$(14);"goto";q$(3);"loop" print q$(7);"movwf";q$(2);"r4";q$(3);"|";q$(14);"decfsz";q$(1);"r3" print q$(11);"|";q$(7);"|";q$(14);"goto";q$(3);"loop" print q$(11);"|";Q$(7);"|";q$(14);"decfsz";q$(1);"r4" print q$(11);"|_______|";q$(14);"goto";q$(3);"loop" inline: x = delay - x g = int(x/2): if g = 0 then goto inline1 for y = 1 to g print q$(34);"goto";q$(3);"$+1" next inline1: if x/2 = int(x/2) then goto finis print q$(34);"nop" finis: end
ISA-Bus interfacing is a little beyond the scope of this page... Get a copy of the best book on the subject, ISA & EISA Theory and Operation, by Edward Solari. It explains everything you'll ever need to know.
If you've found what you think is a bug, you should report it to Microchip; they're very good at following up on these reports.
Francis Deck has a freeware PIC development system that includes an editor, assembler, and a "front panel" for an inexpensive PIC programmer, for which he also supplies full plans and firmware; details are on his web page at http://members.aol.com/fdeck/main.html.
If you prefer to use the PC-based tools available from Microchip, there are two ways: Buy a used PC or (if you have a suitably-powerful Mac) run Insignia Software's SoftWindows emulator on your Mac.
Jim Oslislo (firstname.lastname@example.org), a Mac user, recommends the following configuration:
Mac Picstart 6 7 8 1 2 3 4 5 3 4 5 6 7 8 9 1 2Connect Mac 6 to Mac 4 to Mac 8 to Picstart 5
If you have more money to spend and want real-time hardware emulation, I'd recommend the following combination; if you're honest and pay the WinEdit shareware-registration fee, you'll have a decent development system for right around $1,000:
Finally, if cost is no object whatsoever, I'd recommend the following system, which is what we use here:
There's one more thing you should keep in mind if you're considering the purchase of any third-party PIC development tools, including assemblers, emulators, simulators, etc.:
As dedicated as your third-party vendor may be, it's possible that he will eventually tire of updating his software or hardware, and you'll be left with tools that contain unresolved bugs and/or slowly become obsolete as new microcontrollers are introduced. This seems to be a particular problem with shareware tools written by people who always have a "day job" to go back to.
Many third-party tool developers have introduced PIC development tools with great fanfare, only to drop support for them within a couple of years. Even the largest of the third-party suppliers are not immune.
Microchip, on the other hand, will always support its silicon products with development tools. Those tools may not always be the best -- in fact, they may rarely be, since Microchip's primary focus is VLSI semiconductor manufacture, not tool design -- but I can live with that, because at least they'll be current.
For more information on third-party development tools, see the companies listed in the "Embedded Systems Programming" section of our "Links" page.
If these chips are windowed EPROM parts, you should run them through a UV eraser; Microchip does not guarantee that windowed parts will arrive blank. If they're OTP parts, you should return them and/or your programmer.
LFSR: RLF RANDOM,W RLF RANDOM,W BTFSC RANDOM,4 XORLW 1 BTFSC RANDOM,5 XORLW 1 BTFSC RANDOM,3 XORLW 1 MOVWF RANDOM RETLW 0Here's another routine, written by Marv Isaacman:
MARV: MOVLW 01DH CLRC RLF RANDOM SKPNC XORWF RANDOM RETLW 0
CRCHI EQU some register CRCLO EQU another register CLRF CRCHI CLRF CRCLO ;Append 16 "0" bits to your message here. LOOP ;If there are no more bits in your message, go to ;"DONE". Otherwise, left-shift the next bit of your ;message into the carry here. RLF CRCLO RLF CRCHI SKPC GOTO LOOP MOVLW 00100001B XORWF CRCLO MOVLW 00010000B XORWF CRCHI GOTO LOOP DONE ;The CRC is in "CRCHI" and "CRCLO".This isn't the fastest possible implementation, but it should be quick enough for most purposes. It uses the standard X.25 (and XMODEM) polynomial: x^16+x^12+x^5+1.
You may also want to see the answer to Question #20, in the "Other Processors/Miscellaneous" section, below.
MOVLW [some number] ;THIS COULD ALSO BE A "MOVF [register],W". CALL WAITMS ;CALL THE "DELAY" SUBROUTINE.If you're using the 16C5x family, which doesn't have an ADDLW instruction, the "WAITMS" subroutine looks like this (I'm assuming that you're running at 4 MHz):
MSTMR EQU [any register] MSTMR2 EQU [another register] ; ; WAIT SOME NUMBER OF MILLISECONDS. ENTER WITH NUMBER OF ; MILLISECONDS IN W (0 = 256). ; ; AT 4 MHz, 1 MILLISECOND TAKES 1000 CYCLES. ; WAITMS: MOVWF MSTMR ;MSTMR = THE NUMBER OF MILLISECONDS. WAITMS1: MOVLW 249 ;2-SETUP TO WAIT 1 MILLISECOND. MOVWF MSTMR2 ; WAITMS2: NOP ;249-WASTE A CYCLE. THIS COULD ALSO BE A ;"CLRWDT". DECFSZ MSTMR2 ;746- GOTO WAITMS2 ; DECFSZ MSTMR ;3-HAVE WE WAITED ENOUGH TIME? GOTO WAITMS1 ; IF NOT, LOOP BACK. RETLW 0 ;OTHERWISE, RETURN.If you're using a PIC whose instruction set includes the ADDLW instruction, you can save a register by rewriting the routine like this:
MSTMR EQU [any register] ; ; WAIT SOME NUMBER OF MILLISECONDS. ENTER WITH NUMBER OF ; MILLISECONDS IN W (0 = 256). ; ; AT 4 MHz, 1 MILLISECOND TAKES 1000 CYCLES. ; WAITMS: MOVWF MSTIMR ;STORE THE NUMBER OF MILLISECONDS. WAITMS1: MOVLW 249 ;1-SETUP TO WAIT A MILLISECOND. ADDLW -1 ;995-WASTE 995 MICROSECONDS. SKPZ ; GOTO $-2 ; DECFSZ MSTIMR ;HAVE WE WAITED ENOUGH? GOTO WAITMS1 ;IF NOT, LOOP BACK. RETURN ;OTHERWISE, RETURN.Note that neither of these routines will give exact results, since they don't compensate for the CALL/RETURN overhead. They'll be accurate to within a couple of microseconds, though.
; THIS MACRO JUST WAITS UNTIL PORTA0 GOES HIGH. WAIT4A0 MACRO LOCAL TESTA0 TESTA0 BTFSS PORTA,0 GOTO TESTA0With TESTA0 defined this way, you can invoke the macro as often as you like, and MPASM will automatically select a unique name for the label in each case.
As far as I can tell, the PIC16C52 die is identical to the 16C54. If Microchip can figure out how to save money by manufacturing a chip with only 384 words of EPROM rather than 512, though, future 16C52s may actually be something other than crippled 16C54s.
More precisely, I think you're misunderstanding the function of the PWM generator. The PIC's PWM output is a continuous stream of pulses, all identical, at a user-specified frequency and duty-cycle. I believe that you've confused this with PWM encoding of data, in which a finite string of "ones" and "zeros" are represented by long and short pulses.
If you're on the up-and-up and/or your PIC isn't code-protected, you can read the contents of the chip with any PIC programmer, then load the resulting ".hex" file into MPLAB and save the disassembled file that it creates.
At this instant, MPLAB-C and MPC are nearly identical. As time passes, however, I'd expect the two compilers to evolve in different directions, with MPLAB-C becoming more memory-efficient and MPC growing to include more data types (including floats and 24/32-bit integers), more advanced math functions, etc.
This prediction is my opinion only; please don't misconstrue it as representing the official plans of either Microchip or Bytecraft.
This is why it's called an "interrupt"; it interrupts the normal program execution, jumps to your "Interrupt Service Routine" (ISR) at address 4, executes the ISR, then returns to where it was before the interrupt and continues.
The upshot of all this is that you can't really depend on the Change-On-PortB interrupt to be reliable for anything other than waking-up the processor from sleep mode.
Sucks, don't it?
The book is written for complete beginners; no previous assembly-language programming is assumed. The book isn't intended to take its readers much beyond the beginning level, so most programmers with even moderate experience will find it too elementary, but for what it is, the book is excellent. Benson's style is light and conversational, and the book includes lots of clear examples.
Whenever you start using a new PIC, it'd probably be a good idea to make sure you have the latest assembler; the latest version of MPASM (as well as Microchip's other free development tools) can always be downloaded from the Microchip web page... A link to that page is in the "Embedded Systems Programming" section of our "Links" page.
Sample code for generating DTMF tones is all over the web... I'd suggest looking at Eric Smith's page; there's a link to it in the "Embedded Systems Programming" section of our "Links" page.
The key is to run your PWM frequency as fast as possible and to use a low-pass filter with a cutoff frequency as low as possible... LCD displays are pretty slow, but their internal refresh can "beat" with your PWM frequency if it's not filtered very well, leading to really awful flicker.
The "better" way is to put a low-threshold N-channel FET in the negative lead (sourced to the negative terminal of the battery) and put a resistor between the gate and the battery's positive terminal, with a zener between the gate and source. I believe that this arrangement is patented by National Semiconductor.
; 16-Bit Divide. Written by Andy Warren. ; ; Copyright (C) 1996 Fast Forward Engineering. All rights reserved. ; Permission is hereby granted for any non-commercial use, ; so long as this copyright notice remains intact. ; ; Enter with Dividend in DIVIDENDHI:DIVIDENDLO, divisor in ; DIVISORHI:DIVISORLO. ; ; Exits with quotient in QUOTIENTHI:QUOTIENTLO, remainder in ; REMAINDERHI:REMAINDERLO. DIVIDE16: CLRF REMAINDERHI ;CLEAR THE REMAINDER. CLRF REMAINDERLO ; MOVLW 16 ;WE'RE DIVIDING BY A 16-BIT DIVISOR. MOVWF COUNT ; DIVLOOP: RLF DIVIDENDLO ;SHIFT DIVIDEND LEFT 1 BIT INTO RLF DIVIDENDHI ;REMAINDERHI:REMAINDERLO. RLF REMAINDERLO ; RLF REMAINDERHI ; MOVF DIVISORHI,W ;COMPARE THE DIVISOR TO THE PORTION OF THE SUBWF REMAINDERHI,W ;DIVIDEND THAT'S BEEN SHIFTED INTO REMHI. BNZ CHECKLESS ;IF THE TWO HI-BYTES AREN'T THE SAME, JUMP ;AHEAD. MOVF DIVISORLO,W ;OTHERWISE, WE HAVE TO COMPARE THE SUBWF REMAINDERLO,W ;LO-BYTES. CHECKLESS: BNC NOSUB ;IF THE SHIFTED PORTION OF THE DIVIDEND WAS ;LESS THAN THE DIVISOR, JUMP AHEAD. MOVF DIVISORLO,W ;OTHERWISE, REMAINDER = REMAINDER - DIVISOR. SUBWF REMAINDERLO ; MOVF DIVISORHI,W ; SKPC ; INCFSZ DIVISORHI ; SUBWF REMAINDERHI ; (CARRY'S ALWAYS SET AT THIS POINT.) NOSUB: RLF QUOTIENTLO ;IF WE JUST SUBTRACTED, SHIFT A "1" INTO RLF QUOTIENTHI ;THE QUOTIENT. OTHERWISE, SHIFT A "0". DECFSZ COUNT ;HAVE WE SHIFTED ENOUGH BITS? GOTO DIVLOOP ;IF NOT, LOOP BACK. ; AT THIS POINT, THE QUOTIENT IS IN QUOTIENTHI:LO AND THE REMAINDER IS ; IN REMAINDERHI:LO.
For the P3, the "normal" vector locations are at $7F8-$7FF, so the "programming mode" vectors would be at $7F0-$7F7. For the R3/U3, the "normal" vectors are at $FF8-$FFF and the "programming mode" vectors are at $FF0-$FF7.
The "programming mode" RESET vector points to the built-in "bootstrap" code (at $785-$7F7 [P3] or $F80-$FF7 [U3/R3]), which handles all programming functions. Unfortunately, the P3, U3, and R3 parts do not contain bootstrap routines for "verify" or "contents dump".
For more information, see Motorola's application note #857, "MC68705P3/R3/U3 8-Bit EPROM Microcomputer Programming Module".
There are two big differences between the PIC and the 6805:
1996 will be a leap year, since it's divisible by 4 and not by 100.
2000 will be a leap year, even though it's divisible by 100, because it's also divisible by 400.
2100 will not be a leap year, because it's divisible by 100 but not by 400.
To form the Gray code for a sequence of consecutive numbers, take each number in the sequence, shift it right one bit, then XOR the result with the original number.
Original Gray Code Sequence ((x >> 1) ^ x) (x) 000 000 001 001 010 011 011 010 100 110 101 111 110 101 111 100
However, it's equally well-known that programmers, in general, have zero ability to estimate the time necessary to complete a job. Caveat emptor.
I used to work for Linear Corp. While there, I designed their data-encoding and -transmission protocols and wrote the software for nearly all their products, so I may be a little biased here.
You have been warned.
There are a bunch of companies who make RF links, but I'd recommend the products manufactured by Linear Corporation. They sell a whole range of transmitters, from matchbook size on up, and a variety of super-regen and superhet receivers. They have versions for use in both North America and Europe, although they might not be type-accepted in Japan.
As an OEM customer, you can get the transmitters with or without encoders and the receivers with or without decoders. Of course, if you want to be FCC-legal, you'll have to either use their encoders or get yours type-accepted.
The domestic transmitter/receiver pairs are mostly in the 300 MHz range and the European ones are at 433.92 MHz.
Linear Corp. can be reached at 800 421-1587 or 760 438-7000. Ask for OEM Sales.
1. Good programmers can write good code in any language, and mediocre programmers will write crappy code in all languages. Writing in a high-level language makes it easy to avoid some "stupid" mistakes, but so does experience.
2. The nature of microcontroller programming is such that, often, very little code is reusable between applications. There are so many differences between one microcontroller-based project and another that similar algorithms must often be coded in wildly different ways, which is why the traditional reasons for writing in a high-level language (portability across platforms and reusability from one project to the next) are generally not reasons to use a high-level language to program small microcontrollers. There are reasons -- about 25% of the microcontroller code I write is written in C, for example -- but they have little to do with reuse.
3. Some problems are better expressed in C than assembly, and vice-versa. You've got to pick the right tool for the job.
4. Without a good understanding of the underlying assembly language, you won't be able to write optimal C.
5a. An excellent C compiler can generate better code than an excellent assembly-language programmer, because the compiler can safely generate incomprehensible, non-maintainable, "brittle" code that the programmer wouldn't dare write.
5b. An excellent assembly-language programmer can write better code than an excellent C compiler, because the programmer can see a much larger picture of the problem than the compiler can.
6. C is easier to debug. Compiler code-generation errors are rare, so debugging a C program involves mostly finding and fixing bugs in the program's design, rather than in its implementation. Since C offers a higher level of abstraction than assembly, you don't get bogged down in "can't see the forest for the trees"-type debugging.
7. Writing in C is faster than writing in assembly. I'm about the fastest assembly-language programmer I know, but I can write good C code even faster.
8. Because they're so easy to write and debug, programs written in C often suffer from massive "feature-creep".
9. With intelligent use of a really good macro assembler, you can have your cake and eat it, too. With an assembler that supports macros, multi-module code, long symbol-names, etc., you can write "C-like" (or BASIC-like, or... well, ok, not Lisp-like) macros that make assembly-language programming almost as easy as high-level programming.
10. If you spend your whole life honing your assembly-language skills, you won't be nearly as employable as any of the hundreds of thousands of C programmers out there. While C programs aren't always portable from one processor to another, C programmers are.
First, since they use the AC zero-crossings as a timebase, they don't work when the power goes out.
Second, they often don't work across circuits.
Third, there are some appliances (Sony TVs are the classic example) that do such excellent filtering of their incoming AC power that they suck the X-10 signal right off the power line. One Sony TV won't usually do it, but two or three in the same house will generally cause problems.
Fourth, X-10 is very slow... Simple commands can take a second or so to be executed, and complicated ones (like the sequence required to activate BSR's burglar-alarm sirens) can take many seconds. This isn't a problem for most typical X-10 applications.
10 parts per million, uncorrected in software, works out to an error of, at most, 5 minutes per year. If that's too much error for you, you can find people who make 5 ppm crystals. Instead of simply plating the crystals with silver, however, the manufacturer has to deposit a small layer of chrome or gold under the silver base plate, then vapor-deposit a final plate of solid gold over the whole thing. More manufacturing steps + more labor + higher raw-material cost = a very expensive crystal.
I'm assuming that your Manchester representation uses a gap/pulse sequence for "1" and a pulse/gap sequence for "0", and that the message begins with a couple of "1" start bits:
When I say, "Start the TIMER", etc., in the description above, you can either use a hardware timer or a software loop-counter. I'd prefer the latter, but it'll work either way.
The IF_SHORT and FIRST_HALF flags may require a bit of explanation... FIRST_HALF simply tells us whether we're looking at the first half of a frame or the second. IF_SHORT is a little more complicated; it tells us three things:
Besides, it doesn't pay very well.
I have a feeling that the cost to develop an ASIC would be greater than the cost to simply switch to another processor... Have you considered the Motorola 6805? Its instruction set is nearly the same as the 6303's (with the exception of the double-accumulator instructions, instructions which require a 16-bit index register, and a couple of the more obscure bit-test instructions) and many members of the 6805 family can access external ROM through a 64K address bus.
SAW resonators don't inherently improve a transmitter's range. They do, however, keep its frequency from drifting. Since frequency-drift over time can be a primary cause of decreased-range complaints with tuned-L/C transmitters, this is a good thing.
SAWs aren't quite as accurate as crystals, but they cost a lot less and are certainly good enough to be used with the relatively wide-bandwidth receivers that they're usually mated to in car alarms, automatic garage-door operators, etc.
For all you ever wanted to know about SAWs, talk to RF Monolithics. They don't seem to have a web page, but you can call them at 214 233-2903 (fax: 214 387-8148).
You can find it at ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt.
We use Hamming codes to ensure correct reception of radio data transmissions, to increase the effective endurance of EEPROM cells, etc.
In order to keep from boring you, I'm not going to discuss any of the math behind Hamming codes. If you'd like a rigorous mathematical explanation, there are plenty of discrete-math and error-control coding books available. One of the easiest to follow, by the way, was written by Hamming himself; it's called Coding and Information Theory (ISBN 0-13-139139-9). If you're a real glutton for punishment, you might also want to try to wade through Error Control Coding, by Lin & Costello (ISBN 0-13-283796-X), or An Introduction to Error Correcting Codes with Applications, by Vanstone & van Oorschot (ISBN 0-7923-9017-2).
Let's start with an analogy to binary error-correction. Instead of dealing with binary 1's and 0's, think about the problems of error-correcting English speech. If someone says, "base" and we hear "case", we accept the word, since "case" is in our language. This is the problem we face when we send binary messages without encoding; all combinations of 1's and 0's are allowed, so if a word has errors, we can't tell.
Fortunately, not all combinations of letters form valid English words. If you're reading English text and see "xase", you know there's an error (because "xase" isn't in the language), but you don't know whether the word should be "base", "case", or "vase". This is weakly analogous to the way that checksums and single-bit (odd or even) parity work.
The problem is that the words are too close together; only one letter separates the three words from each other. Even worse, the "b", "c", and "v" keys are adjacent on most typewriter keyboards. In order to correct the error, rather than just detecting it, we need some way to distance the words from each other, so that a large number of errors will have to be made before one word can be confused with another.
The "Alpha", "Bravo", "Charlie"... phonetic alphabet was developed for precisely this reason; none of the 26 words are similar to any of the others. If a pilot hears "Barley", he knows not only that an error occurred (since "Barley" isn't one of the 26 valid words), but he also knows the correct message, since the only word close enough to be incorrectly heard as "Barley" is "Charlie".
We can separate our words by adding a distinctive tag to each of them. Instead of saying "base" and "case", we could say "baseball" and "caseload". If someone hears "caseball", he knows it should be "baseball", since those two words are close (only one letter apart), while "caseball" and "caseload" are distant (four letters apart). He can mentally strip off the tag and safely assume that he should have heard "base".
What Hamming did was to apply these ideas to binary messages. Here's how:
The "distance" between one string of binary digits and another is the number of digits in which the two differ. For example, the distance between 1011 and 1010 is 1. The distance between 1011 and 1101 is 2.
In order to correct all 1-bit errors (that is, 1 incorrect bit anywhere in the word), we need to construct a set of code words such that the distance between any two code words in the set is at least 3. Take a minute to understand why this is true.
By the way, a distance of 3 also allows us to detect all 2-bit errors.
Let's say we want to send 4-bit words. Without encoding, the minimum distance between any two of the 16 possible words is only 1. This is no good for us.
To separate the words further, we construct our code words by adding a 3-bit tag to each 4-bit word, like this:
0000 becomes 0000 000 1000 becomes 1000 110 0001 becomes 0001 111 1001 becomes 1001 001 0010 becomes 0010 011 1010 becomes 1010 101 0011 becomes 0011 100 1011 becomes 1011 010 0100 becomes 0100 101 1100 becomes 1100 011 0101 becomes 0101 010 1101 becomes 1101 100 0110 becomes 0110 110 1110 becomes 1110 000 0111 becomes 0111 001 1111 becomes 1111 111If you have nothing better to do, you can examine the above list and see that no two 7-bit code words are closer than three digits.
There are sophisticated ways to decode Hamming codes using syndromes and large matrices. For a microcontroller application using a small Hamming code like this one, though, the following method is probably easier:
Upon receiving a code word, compare it to the code words in the list above. If it matches any of them, accept the first four bits of the code word. If changing the state of any single bit in the received code word produces a word that matches any in the list, change that bit, then accept the first four bits.
Note that proper decoding is not dependent upon error-free reception of the 3 tag bits... That is, there's no chance that an error in the tags will cause your decoding algorithm to erroneously "correct" an otherwise-good message.
We welcome your comments and suggestions. You can reach us by e-mail
If you link your Web site to ours, please let us know.
Last modified 17 April 1998 by Andrew Warren.
Copyright © 1995-98 Fast Forward Engineering. All rights reserved.