
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.
Two notes:
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.
X-10, Inc.
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:
ERRORLEVEL -305
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
Old way:
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 (joslislo@soho.ios.com), 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.