; ******************************************************************* ; * ; Program the Si571 for G8JVM's FM Modulator. * ; * ; 06April2013 * ; Revision: 2j * ; * ; ******************************************************************* ; This program uses a PIC 16F629 to set the proper register values in ; a Silicon Labs Si57X to generate a single frequency signal. ; The required Si57X registers, for each frequency, must be pre- ; calculated and are determined by: ; Si57X power up frequency. ; Required Output Frequency. ; Selected HS_DIV and N1 dividers. ; These registers are stored in EEPROM for each frequency. The ; Si57X output frequency is then selected by two input pins to the ; 12F629. ; This program will handle frequency range of all versions of the ; Si57X. ; ; Program Operation: ; The user must supply Si57X power on frequency in the configuartion ; section. ; After the firmware burn, program will read Si57X registers and extract ; HS_DIV and N1 from the registers. The Si57X crystal oscillator ; frequency is calculated using power on fequency, HS_DIV and N1. ; For each required output frequency, HS_DIV and N1 are chosen and ; RFREQ is calculated. The Si57X registers are generated and saved ; in EEPROM. ; Two input pins, GP0 (pin 7) and GP1 (pin 6) of the 12F629 are looked at ; to determine which output frequency to generate. The registers, for that ; frequency, are retrieved from EEPROM and sent to the Si57X. ; The 12F629 then goes to sleep. The two input pins are configured to ; generate an interrupt if there is a change. This wakes up the 12F629 ; which then scans the input pins and sends the new required output ; frequency. ; ; Most of the Si57X calculation code, in this program, came from: ; Craig Johnson, AA0ZZ, program Pegen570-v77, www.cbjohn.com/AA0ZZ ; Thank you Craig. ; ; C. Buck Ewing, KD8RRI ; ******************************************************************* ; * ; CONFIGURATION SECTION * ; * ; ******************************************************************* radix hex ; ******************************************************************* ; Si570/571 DEFAULT POWER UP FREQUENCY * ; * ; The Si57X power up frequency must be enterd at Si57X_POWERUP_FREQ.* ; This is the frequency generated by the Si57X upon power-up. The * ; frequency should be as accurate as possible since it is used to * ; determine the Si57X crystal oscillator frequency and determines * ; the ACCURACY OF THE OUTPUT FREQUENCY. * ; ******************************************************************* Si57X_POWERUP_FREQ equ D'125000450' ; Hz - PRECISOULY!! ; ^^^^^^^^^ ; MMMKKKHHH ; ******************************************************************* ; REQUIRED OUTPUT FREQUENCY * ; * ; Configure the required output frequency. The output frequency * ; selected depends on the two input pins GP0 (Pin 7) and * ; GP1 (Pin 6). * ; Enter the frequency in Hz. * ; ******************************************************************* ; GP0 GP1 Frequency_1 equ D'009000510' ; Frequency, Hz, 0 0 Frequency_2 equ D'008400000' ; Frequency, Hz, 1 0 Frequency_3 equ D'010600000' ; Frequency, Hz, 0 1 Frequency_4 equ D'009500000' ; Frequency, Hz 1 1 ; ^^^^^^^^^ ; MMMKKKHHH ; ******************************************************************* ; SWITCH DE-BOUNCE * ; * ; If the two inputs are from switch or relay contacts, de-bounce * ; of the contacts may be required. The de-bounce will read inputs, * ; wait for 10 milliseconds, and read the inputs again and compare. * ; If inputs are the same, it is assumed that contacts are not * ; bouncing. If the inputs are different, another 10 millisecond * ; wait is done and the inputs are again compared until they two * ; samples compare. * ; Uncomment the following DEFINE to enable contact de-bounce. * ; ******************************************************************* ;#define SW_DEBOUNCE ; Uncomment to de-bounce inputs ; ******************************************************************* ; * PIC Microcontroller Device type and options. * ; ******************************************************************* ; processor PIC12F629 ;directive to define processor #include ;processor specific variable definitions ;******************************************************************** ;Configuration bits ; The CONFIG directive defines configuration data within the .ASM file. ; The labels following the directive are defined in the P12F629.INC file. ; The PIC12F629 Data Sheet explains the functions of the configuration bits. ; PIC12F629 Internal Oscillator frequency is 4.0 MHz. ;CONFIG __CONFIG _CP_OFF & _CPD_OFF & _MCLRE_ON & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT errorlevel -302 ; Turn off Banking messages ;******************************************************************** ; P ; Controller - PIC12F629 P ; __________ P ; +5V---------->Vdd |1 8| Vss>----GND P ; SCL--------->D'8'&H'FF' ; Next byte of power up frequency movwf freq1 ; and store movlw (Frequency_1)>>D'16'&H'FF' ; Next byte of power up frequency movwf freq2 ; and store movlw (Frequency_1)>>D'24'&H'FF' ; Hi order byte of power up frequency movwf freq3 ; and store goto selhn ; Go select HS_DIV, N1 and build the Si57X registers do_2 movlw (Frequency_2)&H'FF' ; Get low order byte of power up frequency movwf freq0 ; and store movlw (Frequency_2)>>D'8'&H'FF' ; Next byte of power up frequency movwf freq1 ; and store movlw (Frequency_2)>>D'16'&H'FF' ; Next byte of power up frequency movwf freq2 ; and store movlw (Frequency_2)>>D'24'&H'FF' ; Hi order byte of power up frequency movwf freq3 ; and store goto selhn do_3 movlw (Frequency_3)&H'FF' ; Get low order byte of power up frequency movwf freq0 ; and store movlw (Frequency_3)>>D'8'&H'FF' ; Next byte of power up frequency movwf freq1 ; and store movlw (Frequency_3)>>D'16'&H'FF' ; Next byte of power up frequency movwf freq2 ; and store movlw (Frequency_3)>>D'24'&H'FF' ; Hi order byte of power up frequency movwf freq3 ; and store goto selhn do_4 movlw (Frequency_4)&H'FF' ; Get low order byte of power up frequency movwf freq0 ; and store movlw (Frequency_4)>>D'8'&H'FF' ; Next byte of power up frequency movwf freq1 ; and store movlw (Frequency_4)>>D'16'&H'FF' ; Next byte of power up frequency movwf freq2 ; and store movlw (Frequency_4)>>D'24'&H'FF' ; Hi order byte of power up frequency movwf freq3 ; and store goto selhn selhn ; Determine HS_DIV and N1 for this frequency. Then build the Si57X registers call Selecthsdivn1 ; Select HS_DIV and N1. Build the Si57X regs goto save_regs ; Go do the next frequency done ; Registers have been built for all four frequencies and saved in EEPROM. ; Now set bit in startup control to bypass the build on future startup's. movlw startup ; Address in EEPROM banksel EEADR ; Switch to bank 1 movwf EEADR ; Set the address in EEPROM banksel GPIO ; Back to bank 0 movlw H'80' ; Set bit seven in startup call Write_EEPROM ; and write the control word in EEPROM ; Enable the change-0n-port interrupt. go movf GPIO,w ; Get the input port andlw H'03' ; Just the two input pins movwf input ; and save movlw H'88' ; Enable global interrupts and port change interrupt movwf INTCON ; Determine output frequency to activate, retrieve registers from EEPROM ; and send to Si57X. Port Change Interrupt comes here. Sendsifreq movf input,w ; Get the port inputs movwf ftemp0 ; Table Frequency # to be retrieved from EEPROM movlw D'6' ; Times Six bytes movwf work0 call Mult8x8 ; Calculate index into EEPROM ; Retrieve the required Si57X registers from EEPROM. movf work2,w ; Get index into EEPROM; 0, 6, 12, or 18 banksel EEADR movwf EEADR ; Set EEPROM address banksel GPIO movlw sibyte7 ; Starting location to receive Si57X bytes movwf FSR ; from EEPROM movlw D'6' ; Number of bytes to read movwf bytcnt ; into counter read_si_loop call Read_EEPROM ; Read from EEPROM movwf INDF ; Move the byte to sibytes banksel EEADR incf EEADR,f ; Increment EEPROM address banksel GPIO incf FSR,f ; Increment store location decfsz bytcnt,f ; Decrement counter, if zero skip goto read_si_loop ; Not zero, read next byte call Sisend ; Send the Si57X regs, HS_DIV, N1, RFREQ to the SI57X sleep ; ZZZZZZ! Wait for change of inputs GPIO 0 or 1 goto Sendsifreq ; Point Change Interrupt comes here. Go process port pin change. ;==================================================================== ; Determine the crystal frequency in this SI57X. For this calculation ; to be accurate, the actual power up output frequency of the Si57X ; must be specified in the Configuration Section. The accuracy of ; power up frequency determines accuracy of the output frequencies. ;==================================================================== Setupfxtal call Readsiregs ; Get the Si57X registers from NVRAM ; The following 12 instructions are used for SIMULATION only. They ; provide the Si57X registers with known values and are used in ; place of the "call Readsiregs" instruction above. Values obtained ; from Si571 of Richard, G8JVM. ; UNCOMMENT "call Readsiregs" above for actual program use. ; COMMENT these instructions for actual program use. ; movlw H'21' ; Simulate registers in Si57X NVRAM ; movwf sibyte7 ; " ; movlw H'C2' ; " ; movwf sibyte8 ; " ; movlw H'BC' ; " ; movwf sibyte9 ; " ; movlw H'01' ; " ; movwf sibyte10 ; " ; movlw H'14' ; " ; movwf sibyte11 ; " ; movlw H'49' ; " ; movwf sibyte12 ; " ; Power up Si57X registers in sibyte7..sibyte12. Save in EEPROM. movlw siregs ; Location in EEPROM to save regs read from Si57X banksel EEADR movwf EEADR ; Set EEPROM address banksel GPIO movlw sibyte7 ; FSR points at data source movwf FSR movlw D'6' ; Number of bytes to save movwf bytcnt save_pu_loop movf INDF,w ; Get a byte to write in EEPROM call Write_EEPROM ; and write to EEPROM banksel EEADR incf EEADR,f ; Increment EEPROM address banksel GPIO incf FSR,f ; Increment data source decfsz bytcnt,f ; Decrement counter, if zero skip goto save_pu_loop ; Not done, save next register ; Extract INITIAL_HSDIV-INDEX from SiReg[7] movlw 0xE0 ; Mask upper 3 bits andwf sibyte7,w ; Isolate HSDIV-INDEX movwf suhsdivindex ; Index is in upper 3 bits bcf STATUS,C ; rrf suhsdivindex,f ; position rrf suhsdivindex,f ; position rrf suhsdivindex,f ; position rrf suhsdivindex,f ; position rrf suhsdivindex,f ; Now HSDIV-INDEX positioned in lower bits ; Convert HSDIV-INDEX to HSDIV movf suhsdivindex,w ; call Indextohsdiv ; Start of table movwf work0 ; Save HSDIV ; Extract N1 movlw 0x1F ; Mask lower 5 bits andwf sibyte7,w ; movwf ftemp0 ; Now has N1[6:2] in temp1[4:0] bcf STATUS,C rlf sibyte8,f ; Get upper bit into C rlf ftemp0,f ; Move into ftemp0 LSB rlf sibyte8,w ; Move another (this time don't modify sibyte8) rlf ftemp0,f ; Move into ftemp0 LSB, Now temp1 has N1[7:0] rrf sibyte8,f ; Reposition back to original incf ftemp0,f ; Now ftemp0 has N1 instead of (N1-1) ; Multiply N1 by HSDIV to get HSDIVN1 call Mult8x8 ; Multiply ftemp0 by work0, Result in work3,work2 ; Now divide by 2 bcf STATUS,C rrf work3,f ; Move LSB of upper byte into C rrf work2,f ; Move C into lower byte movf work3,w ; Now into save in HSDIVN1/2 movwf curhsdivn1d2_1 movf work2,w ; " movwf curhsdivn1d2_0 ; Calculate Fxtal/HSDIVN1 by dividing Fout(power up) by RFREQ(extracted) ; Set up numerator in work4..work0 with Fout defined in Configuration Section. clrf work4 ; MSB movlw (Si57X_POWERUP_FREQ)&H'FF' ; Get low order byte of power up frequency movwf work0 ; and store movlw (Si57X_POWERUP_FREQ)>>D'8'&H'FF' ; Next byte of power up frequency movwf work1 ; and store movlw (Si57X_POWERUP_FREQ)>>D'16'&H'FF' ; Next byte of power up frequency movwf work2 ; and store movlw (Si57X_POWERUP_FREQ)>>D'24'&H'FF' ; Hi order byte of power up frequency movwf work3 ; and store ; Convert Fout to (Fout*16) by left shift 4 times 4 bcf STATUS,C rlf work0,f rlf work1,f rlf work2,f rlf work3,f rlf work4,f bcf STATUS,C rlf work0,f rlf work1,f rlf work2,f rlf work3,f rlf work4,f bcf STATUS,C rlf work0,f rlf work1,f rlf work2,f rlf work3,f rlf work4,f bcf STATUS,C rlf work0,f rlf work1,f rlf work2,f rlf work3,f rlf work4,f ; Extract RFREQ (38-bit) from SiReg[8:12] ; and set up as denominator movlw 0x3F ; Mask RFREQ bits andwf sibyte8,w movwf ftemp4 ; RFREQ[37:32] is in Reg8[5:0] movf sibyte9,w movwf ftemp3 movf sibyte10,w movwf ftemp2 movf sibyte11,w movwf ftemp1 movf sibyte12,w ; LSB of RFREQ now set up for divide movwf ftemp0 ; Now divide by RFREQ AND MULTIPLY BY 256 ; Divide 40-bit work4..work0 by 40-bit (ftemp4..ftemp0) AND MULTIPLY BY 256 ; =================================================================== ; Example: Start with (Fout * 16) in work4..work0 ; Fout = 10,000,000 so Fout*16 = 0x09896800 ; (work4..work0 = 00 09 89 68 00) ; Divide by RFREQ-extracted in ftemp4..ftemp0 ; RFREQ * 2^28 = 0x02A050E9FD ; (ftemp4..ftemp0 = 02 A0 50 E9 FD) ; ; Since RFREQ is already multiplied by 2^28, divide routine ; multiplies numerator by 2^28 also. ; ; RESULT: This divide routine produces 0x3A19F4D ; (Note: uses (Fout * 16) to keep extra significant figure) ; so this result gives (Fxtal*256) / (HSDIVN1) ; ; Division Result is in work4:work0 = 0x00 03 A1 9F 4D (This is now x256) ; Multiply this by HSDIVN1 and divide by 256 to give Fxtal ; =================================================================== CalDivide clrf quo0 ; Clear remainder clrf quo1 clrf quo2 clrf quo3 clrf quo4 movlw D'72' ; Loop counter increment because sign-shift removed movwf temp CalDvloop rlf work0,f ; Shift dividend (REGA) msb into remainder (REGC) rlf work1,f rlf work2,f rlf work3,f rlf work4,f rlf quo0,f rlf quo1,f rlf quo2,f rlf quo3,f rlf quo4,f movf ftemp4,w ; Test if remainder (REGC) >= divisor (REGB) subwf quo4,w ; btfss STATUS,Z ; goto CalDtstgt ; Byte4 <> Byte4 so jump movf ftemp3,w ; (Byte4=Byte4) so subwf quo3,w ; look at Byte3 btfss STATUS,Z ; goto CalDtstgt ; Byte3 <> Byte3 so jump movf ftemp2,w ; (Byte4=Byte4) and (Byte3=Byte3) so subwf quo2,w ; look at Byte2 btfss STATUS,Z ; goto CalDtstgt ; Byte2 <> Byte2 so jump movf ftemp1,w ; (Byte4=Byte4) and (Byte3=Byte3) and (Byte2=Byte2) so subwf quo1,w ; look at Byte1 btfss STATUS,Z ; goto CalDtstgt ; Byte1 <> Byte1 so jump movf ftemp0,w ; (Byte4=Byte4) and (Byte3=Byte3) and (Byte2=Byte2) subwf quo0,w ; and (Byte1=Byte1) so look at Byte0 CalDtstgt btfss STATUS,C ; Carry set if remainder >= divisor goto CalDremlt ; Divisor byte greater than remainder so quit movf ftemp0,w ; Subtract divisor (REGB) from remainder (REGC) subwf quo0,f ; movf ftemp1,w ; btfss STATUS,C ; incfsz ftemp1,w ; subwf quo1,f ; movf ftemp2,w ; btfss STATUS,C ; incfsz ftemp2,w ; subwf quo2,f ; movf ftemp3,w ; btfss STATUS,C ; incfsz ftemp3,w ; subwf quo3,f ; movf ftemp4,w ; btfss STATUS,C ; incfsz ftemp4,w ; subwf quo4,f ; bcf STATUS,C bsf work0,0 ; Set quotient bit CalDremlt bcf STATUS,C decfsz temp,f ; Next goto CalDvloop ; ; End of division ; EXAMPLE: work4:work0 = 0x00 03 A1 9F 4D (This is now x256) ; =================================================================== ; Calculate Fxtal*256 by multiplying result above by HSDIVN1 for given N1 and HSDIV ; ((Fxtal*256)/HSDIVN1-extracted) = (Fout-default * 256) / Rfreq-extracted ; ; EXAMPLE: Fout*256/RFREQ = 0x00 03 A1 9F 4D ; HSDIV(extracted) = 6, N1(extracted)= 80 so HSDIVN1/2 = 240 = 0xF0 ; NOTE: Uses HSDIVN1/2 in denominator so need to compensate by multiplying ; result by 2 at the end. ; ; Mult routine has been tailored to do 32 bits by 16 bits. ; movf curhsdivn1d2_0,w ; (Don't disturb original) movwf quo0 movf curhsdivn1d2_1,w movwf quo1 call Mult32x16 ; Now mult work3:work0 by quo1.0 -> result in ftemp5..0 ; MOVE ftemp4:ftemp0 to xtal_4:xtal_0(*256) movf ftemp4,w movwf xtal4 movf ftemp3,w movwf xtal3 movf ftemp2,w movwf xtal2 movf ftemp1,w movwf xtal1 movf ftemp0,w movwf xtal0 ; Now multiply result by 2 with one left shift since we multiplied by HSN1DIV/2 bcf STATUS,C ; rlf xtal0,f ; LSB Was Fxtalx256_0 rlf xtal1,f ; rlf xtal2,f ; rlf xtal3,f ; rlf xtal4,f ; MSB ; Save the Si57X Crystal Oscillator frequency in EEPROM. Actually Crystal ; Oscillator frequency times 256. movlw sifxtal ; Location in EEPROM to save banksel EEADR movwf EEADR ; Set EEPROM address banksel GPIO movlw xtal4 ; FSR points at data source movwf FSR movlw D'5' ; Number of bytes to save movwf bytcnt save_xtal_loop movf INDF,w ; Get a byte to write in EEPROM call Write_EEPROM ; and write to EEPROM banksel EEADR incf EEADR,f ; Increment EEPROM address banksel GPIO incf FSR,f ; Increment data source decfsz bytcnt,f ; Decrement counter, if zero skip goto save_xtal_loop ; Not done, save next register ; Now have Fxtalx256 set up ; EXAMPLE: Fxtalx256 = 0x06 CF 0A B0 60 << FIXED FOR 10 MHz Si570 ; 0x06CF0AB060 = 29243388000; 10009982720/256 = 114,231,984 << FIXED FOR 10 MHz Si570 goto outfreqreg ; Go to generate Si57X registers ;==================================================================== Selecthsdivn1 ; ; This routine will select valid HS_DIV and N1 dividers for the desired ; output frequency. ; Si570 Specifications states that keeping the DCO frequency as low as ; possible will minimize power consumption of the Si57X. It also ; states that lowest value of N1 and highest value of HS_DIV also ; lowers power consumption. ; So, start with HS_DIV = 11 (maximum) and N1 = 1 (lowest). Compute ; Fout*HS_DIV*N1 and see if frequency is between 4.85GHz and 5.67GHz. ; If not increment N1 and try all values of N1. If no go, use next ; lowest HS_DIV and try all values of N1 starting at N1=1. If valid ; combination not found - HALT!!!!!! movlw D'1' ; Set trial N1 to minimum movwf work0 ; Set multiplier movwf n1hx ; Save movlw D'7' ; Set trial HS_DIV index to maximum movwf newhsdivindex ; Save hsdiv_loop movf newhsdivindex,w ; Get the index into HS_DIV table call Indextohsdiv ; Get HS_DIV value movwf ftemp0 ; Save for multiply movf ftemp0,w ; Move to W for test btfss STATUS,Z ; Is the value zero? goto hsdiv_ok ; No next_hsdiv decf newhsdivindex,f ; Yes, decrement to new HS_DIV index btfss newhsdivindex,7 ; Did new HS_DIV index go negative? goto hsdiv_loop ; No, try next value hsdiv_fail movlw H'F1' ; Yes, set error code goto error_halt ; HSDIV value went negative hsdiv_ok movf n1hx,w ; Move N1 value for multiply movwf work0 call Mult8x8 ; Multiply trial HS_DIV * N1 movf work2,w ; Save the result movwf curhsdivn1d2_0 movf work3,w ; " movwf curhsdivn1d2_1 ; Now multiply output frequency in freq3..freq0 by HS_DIV*N1 product. ; Results in ftemp5...ftemp0 movf work2,w ; Move multiplier - HSDIV*N1 movwf quo0 movf work3,w ; " movwf quo1 movf freq0,w ; Move output frequency to multiplicand movwf work0 movf freq1,w movwf work1 movf freq2,w movwf work2 movf freq3,w movwf work3 call Mult32x16 ; Multiply output frequency by test HSDIV*N1 ; ftemp5...ftemp0 contains a trial Fdco. ; Output Frequency*HS_DIVN1 calculated. See if between 4.85 GHz and 5.67 GHz. movf ftemp4,w ; Get MSByte of calculated Fdco btfsc STATUS,Z ; Is high order byte zero? goto inc_n1 ; Yes, try next combination of HS_DIV & N1 sublw D'1' ; No, subtract 1 from Fdco Msbyte btfss STATUS,Z ; Is result zero goto hsn1_new ; No, went over maximum Fdco - try next lower HS_DIV movlw H'51' ; Maximum value for 2nd frequency byte subwf ftemp3,w ; Subtract from 2nd frequency byte btfss STATUS,Z ; Was second Fdco byte = "51" goto chk_b2_more ; No goto new_hsn1div2 ; Yes, use this combination of HS_DIV and N1 chk_b2_more btfsc STATUS,C ; If Carry, 2nd frequency byte is more than H'51' goto hsn1_new ; No, went over maximum Fdco - try next lower HS_DIV movlw H'21' ; Subtract 0x21 from Fdco 2nd MSByte subwf ftemp3,w btfss STATUS,C ; Is value 0x21 or greater? goto inc_n1 ; No, try next combination goto new_hsn1div2 ; HS_DIV and N1 determined for the desired ; output frequency. Do next step to build ; the SI57X registers inc_n1 incf n1hx,f ; Increment N1 btfss n1hx,0 ; Is LSbit a one? goto inc_n1_chk ; No incf n1hx,f ; Yes, make it even inc_n1_chk movf n1hx,w ; Get the new N1 sublw D'130' ; See if N1 is over N1 maximum limit btfss STATUS,Z ; Subtract = zero? goto hsdiv_loop ; No, try with new N1 hsn1_new movlw D'1' ; Yes, start over, set N1 to 1 movwf n1hx ; Save new value of N1 decf newhsdivindex,f ; Decrement the value of HS_DIV index btfsc STATUS,C ; Is the HS_DIV index zero or positive? goto hsdiv_loop ; Yes, do the calculations again movlw H'F2' ; No, went negative goto error_halt ; All values of HS_DIV and N1 failed to find ; a valid combination ; Have the new HS_DIV and N1 for output frequency. Build new curhsdivn1d2. new_hsn1div2 bcf STATUS,C rrf curhsdivn1d2_1,f ; Shift MSByte, LSBit to C rrf curhsdivn1d2_0,f ; and into LSByte -> divide by 2 goto Calcfx ; Continue calculations error_halt ; Write error code to EEPROM at location halt_code. The die in halt_loop. movwf temp ; Save error code banksel EEADR ; Switch to bank 1 movlw halt_code ; Get EEPROM address movwf EEADR ; and set banksel GPIO ; Back to bank 0 movf temp,w ; Get error code call Write_EEPROM ; Write to EEPROM halt_loop goto halt_loop ; HALT!!!!!! ; =================================================================== Calcfx movf xtal4,w ; Copy Si57X crystal frequency * 256 movwf work4 movf xtal3,w ; to numerator movwf work3 movf xtal2,w movwf work2 movf xtal1,w movwf work1 movf xtal0,w movwf work0 movf curhsdivn1d2_0,w ; Copy HSDIV*N1/2 to denominator movwf ftemp0 movf curhsdivn1d2_1,w movwf ftemp1 FxFacDivide clrf quo0 ; Clear remainder clrf quo1 movlw D'40' ; For 40 by 8 divide movwf temp FxFacDvloop rlf work0,f ; Shift dividend (work) msb into remainder (quo) rlf work1,f rlf work2,f rlf work3,f rlf work4,f rlf quo0,f rlf quo1,f movf ftemp1,w ; Test if remainder (quo) >= divisor (ftemp) subwf quo1,w ; btfss STATUS,Z ; goto FxFacDtstgt ; Byte1 <> Byte1 so jump movf ftemp0,w ; Test if remainder (quo) >= divisor (ftemp) subwf quo0,w FxFacDtstgt btfss STATUS,C ; Carry set if remainder >= divisor goto FxFacDremlt movf ftemp0,w ; btfss STATUS,C ; incfsz ftemp0,w ; subwf quo0,f ; movf ftemp1,w ; btfss STATUS,C ; incfsz ftemp1,w ; subwf quo1,f ; bcf STATUS,C bsf work0,0 ; Set quotient bit FxFacDremlt bcf STATUS,C decfsz temp,f ; Next goto FxFacDvloop ; Need to divide by 2 because HSDIVN1/2 was used in denominator bcf STATUS,C ; rrf work4,f ; rrf work3,f ; rrf work2,f ; rrf work1,f ; rrf work0,f ; movf work3,w ; Copy result to home registers movwf fx3 movf work2,w ; Si57X crystal freq * 256 / HSNDIV*N1 movwf fx2 movf work1,w movwf fx1 movf work0,w movwf fx0 ; =================================================================== Buildsiregs call CalcRFREQ ; Calculate RFREQ movf newhsdivindex,w ; Get HS_DIV for output frequency movwf temp bcf STATUS,C rlf temp,f ; Position index for Si register rlf temp,f rlf temp,f rlf temp,f rlf temp,f movf temp,w movwf sibyte7 movlw D'1' ; Subtract 1 from the new N1 subwf n1hx,w ; N1-1 movwf temp ; move to temporary storage clrf work0 ; intialize bcf STATUS,C ; Clear carry (don't want junk in bit 7 after rrf) rrf temp,f ; rotate into position, bit into C rrf work0,f ; rotate C into new temp reg rrf temp,f ; rotate into position, bit into C rrf work0,f ; rotate C into new temp reg movf temp,w ; iorwf sibyte7,f ; Now (N1-1)[6:2] is in SiReg7[4:0] with HSDIV[2:0] movf work0,w ; iorwf sibyte8,f ; Now have (N1-1)[1:0] in [7:6] and RFREQ[37:32] in [5:0] return ; =================================================================== CalcRFREQ movf freq3,w ; Copy numerator to work registers movwf work4 movf freq2,w movwf work3 movf freq1,w movwf work2 movf freq0,w movwf work1 clrf work0 movf fx3,w ; Move denominator to work registers movwf ftemp3 movf fx2,w movwf ftemp2 movf fx1,w movwf ftemp1 movf fx0,w movwf ftemp0 RFREQDivide40by32 clrf quo0 ; Clear remaninder clrf quo1 clrf quo2 clrf quo3 movlw D'68' ; Loop counter- position for 36 bits in 40 bit field movwf temp FoDvloop rlf work0,f ; Shift dividend (work) msb into remainder (quo) rlf work1,f rlf work2,f rlf work3,f rlf work4,f rlf quo0,f rlf quo1,f rlf quo2,f rlf quo3,f movf ftemp3,w ; Test if remainder (REGC) >= divisor (REGB) subwf quo3,w ; btfss STATUS,Z ; goto FoDtstgt ; Byte3 <> Byte3 so jump movf ftemp2,w ; (Byte3=Byte3) so subwf quo2,w ; look at Byte2 btfss STATUS,Z ; goto FoDtstgt ; Byte2 <> Byte2 so jump movf ftemp1,w ; (Byte3=Byte3) and (Byte2=Byte2) so subwf quo1,w ; look at Byte1 btfss STATUS,Z ; goto FoDtstgt ; Byte1 <> Byte1 so jump movf ftemp0,w ; (Byte3=Byte3) and (Byte2=Byte2) and (Byte1=Byte1) subwf quo0,w ; so look at Byte0 FoDtstgt btfss STATUS,C ; Carry set if remainder >= divisor goto FoDremlt ; Divisor byte greater than remainder so quit movf ftemp0,w ; Subtract divisor (ftemp) from remainder (quo) subwf quo0,f ; movf ftemp1,w ; btfss STATUS,C ; incfsz ftemp1,w ; subwf quo1,f ; movf ftemp2,w ; btfss STATUS,C ; incfsz ftemp2,w ; subwf quo2,f ; movf ftemp3,w ; btfss STATUS,C ; incfsz ftemp3,w ; subwf quo3,f ; bcf STATUS,C ; bsf work0,0 ; Set quotient bit FoDremlt bcf STATUS,C ; decfsz temp,f ; Next goto FoDvloop movf work4,w ; Move result to Si registers movwf sibyte8 movf work3,w movwf sibyte9 movf work2,w movwf sibyte10 movf work1,w movwf sibyte11 movf work0,w movwf sibyte12 return ;==================================================================== ; Read control bytes from the Si57X. Readsiregs ; ; First set bit 0 (RECALL) in Si570 register 135. This recalls NVM bits into Si570 RAM ; movlw D'135' ; Register 135 - Reset / Memory control register in Si570 movwf eadd call openw ; Open for Write (signal start, send 0xAA, re-send until ACK) ; Then send address byte in eadd movlw H'01' ; Set RECALL bit in byte to write movwf ebyte call putbyte ; Write to Register 135 call stop ; Stop ; Now read registers 7 through 12 from Si57X ; movlw H'07' ; address of 1st frequency define register in Si570 movwf eadd call openrd ; Set up for read call getbyte ; Get byte call sendack ; ACK - continue reading movf ebyte,w movwf sibyte7 call getbyte call sendack ; ACK - continue reading movf ebyte,w movwf sibyte8 call getbyte call sendack ; ACK - continue reading movf ebyte,w movwf sibyte9 call getbyte call sendack ; ACK - continue reading movf ebyte,w movwf sibyte10 call getbyte call sendack ; ACK - continue reading movf ebyte,w movwf sibyte11 call getbyte ; Get byte call sendnoack ; Last byte so send NOACK and call stop ; stop instead of ACK this time movf ebyte,w movwf sibyte12 return ;==================================================================== ; Send control bytes to the Si57X. Sisend movlw D'137' ; Register address for Freeze DCO bit movwf eadd call openw ; Open for write movlw B'00010000' ; Freeze DCO bit movwf ebyte call putbyte ; Freeze the DCO call stop movlw D'7' ; Address of 1st frequency define register movwf eadd call openw ; Open Register 7 for write movf sibyte7,w ; Send the 6 bytes in sibyte7 through sibyte12 movwf ebyte ; to the Si57X call putbyte movf sibyte8,w movwf ebyte call putbyte movf sibyte9,w movwf ebyte call putbyte movf sibyte10,w movwf ebyte call putbyte movf sibyte11,w movwf ebyte call putbyte movf sibyte12,w movwf ebyte call putbyte call stop ; Stop transfer movlw D'137' ; Address freeze DCO bit movwf eadd call openw clrf ebyte ; Clear freeze DCO bit call putbyte call stop movlw D'135' ; Address of Newfreq movwf eadd call openw movlw B'01000000' ; Assert Newfreq movwf ebyte call putbyte call stop return ;==================================================================== ; Multiply 8 bits of ftemp0 by 8 bits of work0. Result in work3 & work2 Mult8x8 clrf work3 ; Clear the product regs clrf work2 clrf work1 ; Clear working register mult8x8loop: movf ftemp0,W ; Check to see if multiplier is empty btfsc STATUS,Z ; Done if zero goto mult8x8exit ; bcf STATUS,C ; clear carry rrf ftemp0,F ; Get the next bit btfss STATUS,C ; Add if carry is set goto mult8x8shift ; shift only if 0 movf work0,W ; Add the 8 bits of multiplicand to product addwf work2,F ; each addwf sets the carry btfsc STATUS,C ; Don't increment if no carry incf work3,F movf work1,W ; addwf work3,F ; mult8x8shift: bcf STATUS,C ; clear carry rlf work0,F ; Shift the 16 bits one bit to the left rlf work1,F ; goto mult8x8loop mult8x8exit return ;==================================================================== ; Multiply 32 bits by 16 bits (unsigned) ; Multiply work3...0 by quo1.0. Result in ftemp5...0 Mult32x16 clrf ftemp0 ; Clear the result registers clrf ftemp1 clrf ftemp2 clrf ftemp3 clrf ftemp4 clrf ftemp5 movlw D'16' ; Set count to 16 (2 multiplier bytes of 8 bits) movwf temp m32x16loop movlw work0 movwf FSR ; Point at multiplicand work0, LSByte bcf STATUS,C ; Start with Carry clear btfss quo0,0 ; Is multiplier bit 0 (Least Significant bit) set? goto mshift ; No, don't need to add multiplicand term to total movf INDF,w ; Yes, get the multiplicand addwf ftemp2,f ; Yes, add and check for another carry btfss STATUS,C ; Carry? goto mpy1 ; No, continue with next multiplicand term incfsz ftemp3,f ; Add infintum goto mpy1 ; Does this addition result in a carry? incfsz ftemp4,f ; Yes, add one and check for another carry goto mpy1 ; No, continue with next multiplicand term incf ftemp5,f ; Yes, add one and check for another carry mpy1 decf FSR,f ; Point a multiplicand, work1 movf INDF,w ; Use the work2 term addwf ftemp3,f ; Add multiplicand term to total in correct position btfss STATUS,C ; Carry? goto mpy2 ; Does this addition result in a carry? incfsz ftemp4,f ; Yes, add one and check for another carry goto mpy2 ; No, continue with next multiplicand term incf ftemp5,f ; Yes, add one and continue mpy2 decf FSR,f ; Point ay multiplicand, work2 movf INDF,w ; Use the work1 term addwf ftemp4,f ; Add multiplicand term to total in correct position btfss STATUS,C goto mpy3 ; Does this addition result in a carry? incf ftemp5,f ; Yes, add one and continue mpy3 decf FSR,f ; Point at multiplicand, work3 movf INDF,w ; Use the work0 term addwf ftemp5,f ; Add mulp term to total in correct position mshift bcf STATUS,C ; Clear carry rrf ftemp5,f ; Shift product to next bit position rrf ftemp4,f ; rrf ftemp3,f ; rrf ftemp2,f ; rrf ftemp1,f ; rrf ftemp0,f ; rrf quo1,f ; Shift next multiplier bit into position rrf quo0,f ; Rotate bits to right from byte to byte decfsz temp,f ; One more bit has been done. Are we done? goto m32x16loop ; No, go back to use this bit return ; Multiply answer is in bytes ftemp5...0 ; ***************************************************************************** ; * I2C Routines * ; * ; ***************************************************************************** ; strt ; Start condition; data goes from hi to low with SCL high. banksel TRISIO ; Change to bank 1 for Tristate operation bcf TRISIO,SDA ; Turn SDA (GP4) into an Output banksel GPIO ; Change back to bank 0 bsf GPIO,SDA ; SDA High bsf GPIO,SCL ; SCL high bcf GPIO,SDA ; SDA low Data high-to-low with CLK High bcf GPIO,SCL ; SCL low Release SCL return ;------------------------------------------------ stop ; Stop condition; data goes from low to hi with SCL high. ; Normal state is: SCL low, SDA high banksel TRISIO ; Change to bank 1 for Tristate operation bcf TRISIO,SDA ; Turn SDA (GP4) into an Output banksel GPIO ; Change back to bank 0 bcf GPIO,SCL ; Start with SCL low bcf GPIO,SDA ; and SDA low bsf GPIO,SCL ; SCL high bsf GPIO,SDA ; SDA goes high while SCL is high bcf GPIO,SCL ; SCL low return ;------------------------------------------------ getack ; Read acknowledge signal from Si57x. ; Returns C set if no acknowledge (SDA high) from Si57X banksel TRISIO ; Change to bank 1 for Tristate operation bsf TRISIO,SDA ; Turn SDA (GP4) into an Input banksel GPIO ; Change back to bank 0 bcf STATUS,C ; Clear C (default) bsf GPIO,SCL ; nop ; For Read-Modify-Write problem btfss GPIO,SDA ; Test data input goto I2Cgax ; SDA is low (ACK) so jump around with Carry clear bsf STATUS,C ; SDA is high (NOACK) so set Carry flag I2Cgax bcf GPIO,SCL return ; Exit with C clear if ACK, C set if NOACK ;------------------------------------------------ sendack ; banksel TRISIO ; Change to bank 1 for Tristate operation bcf TRISIO,SDA ; Turn SDA (GP4) into an Output banksel GPIO ; Change back to bank 0 ; ACK - low SDA during a clock pulse. bcf GPIO,SDA ; SDA low << This is key for ACK bsf GPIO,SCL ; SCL high bcf GPIO,SCL ; SCL low return ;------------------------------------------------ sendnoack ; banksel TRISIO ; Change to bank 1 for Tristate operation bcf TRISIO,SDA ; Turn SDA (GP4) into an Output banksel GPIO ; Change back to bank 0 ; NOACK - low SDA during a clock pulse. bsf GPIO,SDA ; SDA HIGH << This is key for NOACK bsf GPIO,SCL ; SCL high bcf GPIO,SCL ; SCL low return ;------------------------------------------------ getbyte ; Input a byte from Si57X ; you must send ack after this routine ! clrf ebyte ; ebyte will contain the data banksel TRISIO ; Change to bank 1 for Tristate operation bsf TRISIO,SDA ; Turn SDA (GP4) into an Input banksel GPIO ; Change back to bank 0 bcf STATUS,C ; Clear Carry bsf ebyte,0 ; Set Marker bit in receive data byte inbit rlf ebyte,f ; Shift for next input bit bsf GPIO,SCL ; Raise SCL to clock in data btfss GPIO,SDA ; Is data a "one"? goto bitlo ; No bsf ebyte,0 ; Yes set a "one" in LSBit of received data byte bitlo bcf GPIO,SCL ; Drop SCL btfss STATUS,C ; Did marker bit move into Carry after shift? goto inbit ; NO, get next bit return ; Yes, exit with data in ebyte ;------------------------------------------------ putbyte ; Output a byte in ebyte to Si57X and then get an ACK banksel TRISIO ; Change to bank 1 for Tristate operation bcf TRISIO,SDA ; Turn SDA (GP4) into an Output banksel GPIO ; Change back to bank 0 movlw H'08' ; Set Counter for 8 bits to send movwf bytcnt senlp rlf ebyte,f ; Shift the bit into Carry btfss STATUS,C ; If carry true, send one goto zerb ; If Carry not true, send zero bsf GPIO,SDA ; SDA hi - Send one goto zerb1 zerb bcf GPIO,SDA ; SDA low - Send zero zerb1 bsf GPIO,SCL ; SCL hi bcf GPIO,SCL ; SCL low decfsz bytcnt,f ; Decrement # bits to send goto senlp ; If not zero, go send next bit call getack ; Get ACK/NOACK Return with C clear if ACK return ;------------------------------------------------ openrd ; Initial part of Si57X read sequence for random read. ; Call with initial register address in eadd. RdStartLoop call strt ; Signal Start condition ; Slave address for Si57X is 0x55. movlw H'AA' ; Address 0x55. Shifted 1 bit left movwf ebyte ; call putbyte ; Output the byte and get ACK/NOACK btfsc STATUS,C ; C will be set if NO ACK goto RdStartLoop ; C is set (NOACK) so loop until Ack movf eadd,W ; Carry is clear (ACK) so set up register movwf ebyte ; address as data byte call putbyte ; Si570 start word address in ebyte, getack call strt ; Send signal for start of data movlw H'AB' ; Si57X address of 55 shifted 1 bit to left movwf ebyte ; and insert READ in bit 0 call putbyte ; Output a byte and get ACK. ; Now ready to read a single byte followed by stop ; or read a byte followed by ACK and continue reading return ;------------------------------------------------ openw ; open for write. Call with start address in eadd WrStartLoop call strt ; Signal Start condition movlw H'AA' ; Si57X Address 0x55, shifted 1 bit to the left. movwf ebyte ; call putbyte ; Output a byte and get ACK btfsc STATUS,C ; C will be set if NO ACK goto WrStartLoop ; C is set (NOACK) so loop until ACK movf eadd,W ; Start address for write movwf ebyte ; call putbyte ; Start word address, getack return ; Now send bytes using putbyte. End with stop ; ***************************************************************************** ; * * ; * Purpose: Write the byte of data at EEDATA to the EEPROM at address * ; * EEADR. * ; * * ; * Input: W contains the data to be written. * ; * EEADR contains the write address. * ; * * ; * Output: The EEPROM value is updated. * ; * * ; ***************************************************************************** Write_EEPROM banksel EECON1 movwf EEDATA ; Set data to be written bsf EECON1,WREN ; Set the EEPROM write enable bit movlw H'55' ; Write 0x55 and 0xAA to EEPROM control movwf EECON2 movlw H'AA' ; register, as required for the write movwf EECON2 bsf EECON1,WR ; Set WR to initiate write write_wait btfsc EECON1,WR ; Has WR cleared? goto write_wait ; No, wait until it does bcf EECON1,WREN ; Clear the EEPROM write enable bit banksel GPIO return ; Return to the caller ; ***************************************************************************** ; * * ; * Purpose: Read a byte of EEPROM data at address EEADR into EEDATA. * ; * * ; * Input: The address in EEADR. * ; * * ; * Output: The EEPROM data byte is in W. * ; * * ; ***************************************************************************** Read_EEPROM banksel EECON1 bsf EECON1,RD ; Request the read read_EEPROM_wait btfsc EECON1,RD goto read_EEPROM_wait movf EEDATA,w ; Get the data banksel GPIO return ; Return to the caller ; ***************************************************************************** ; * * ; * Purpose: Wait for a specified number of milliseconds. * ; * * ; * Entry point wait_10ms : Wait for 10 msec * ; * Entry point wait_1ms * ; * * ; * Input: None * ; * * ; * Output: None * ; * * ; ***************************************************************************** wait_10ms ; ****** Entry point ****** movlw D'20' ; Set up outer loop counter to 20 movwf count goto outer_loop ; Into the wait loops wait_1ms movlw D'2' ; Set up outer loop counter to 2 movwf count ; ; Wait loops used by other wait routines ; - 1 microsecond per instruction (with a 4 MHz microprocessor clock) ; - 498 instructions per inner loop ; - (temp * 498) instructions (.498 msec) per outer loop ; - Round off to .5 ms per outer loop ; outer_loop movlw H'A4' ; Set up inner loop counter to 165 movwf temp inner_loop decfsz temp,f ; Decrement inner loop counter goto inner_loop ; If inner loop counter not down to zero, ; then go back to inner loop again decfsz count,f ; Yes, Decrement outer loop counter goto outer_loop ; If outer loop counter not down to zero, ; then go back to outer loop again return ; Yes, return to caller ; **************************************************************************** ; * EEPROM Memory * ; * * ; * Contains: * ; * Si57X registers for each of the four defined frequencies. * ; * Startup control byte. * ; * Saved Si57X power up registers. * ; * Saved Si57X Crystal oscillator Frequency * 256 * ; * Halt code if Select HSDIV - N1 routine fails. * ; * * ; **************************************************************************** ; ORG EEPROM_BASE ; 24 locations reserved for the Si571 registers. 4 frequencies * 6 bytes DATA 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Frequency #0 Data 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Frequency #1 DATA 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Frequency #2 DATA 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Frequency #3 startup_loc startup equ startup_loc - EEPROM_BASE DATA 0x00 ; Startup control byte. Bit 7 = 0 Initial start up ; Bit 7 = 1, subsequent start up siregs_loc siregs equ siregs_loc - EEPROM_BASE DATA 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Power up Si57X registers sifxtal_loc sifxtal equ sifxtal_loc - EEPROM_BASE DATA 0x00, 0x00, 0x00, 0x00, 0x00 ; Calculated Si57X crystal oscillator * 256 halt_code_loc halt_code equ halt_code_loc - EEPROM_BASE DATA 0x00 ; Halt code from the Select HSDIV & N1 routine ;================================================ END ;================================================