'( ################################################################################ project: VHF Hardware Defined Radio 2014 -------------------------------------------------------------------------------- name : VHF_VFO_07.bas copyright : (c) Chris, OE3HBW, JN87AQ, 2014 purpose : Control Silabs Si570 VFO for VHF-RX micro : ATmega324A programmer : homebrew (STK200/STK300 emulation) compiler : MCS BASCOM-AVR 2.0.7.7 flash : 22% code build : 29032014 (29. Mar. 2014) status : test -------------------------------------------------------------------------------- Control Si570 VFO by I2C bus. Smooth-tuning feature for frequency hops smaller as 3500 ppm is implemented. Note: Code not for Si570 version with 7ppm temperatur stability (other register) -------------------------------------------------------------------------------- ########################### HARDWARE ########################################### uC ATmega324A-AU 20MHz 44A-TQFP PIN 1 PB5 PORTB.5 ISP MOSI 2 PB6 PORTB.6 ISP MISO 3 PB7 PORTB.7 ISP SCK 4 /RESET ISP RST and HW-Reset 5 VCC +5V 6 GND GND 7 XTAL2 Quartz 18.4320 MHz -> 15pF - GND 8 XTAL1 Quartz 18.4320 MHz -> 15pF - GND 9 PD0 PORTD.0 RXD 10 PD1 PORTD.1 TXD 11 PD2 PORTD.2 INT0 12 PD3 PORTD.3 INT1 13 PD4 PORTD.4 Rotary Encoder A 14 PB5 PORTD.5 Rotary Encoder B 15 PB6 PORTD.6 16 PB7 PORTD.7 17 VCC +5V 18 GND GND 19 PC0 PORTC.0 SCL I2C 20 PC1 PORTC.1 SDA I2C 21 PC2 PORTC.2 LCD D7 22 PC3 PORTC.3 LCD D6 23 PC4 PORTC.4 LCD D5 24 PC5 PORTC.5 LCD D4 25 PC6 PORTC.6 LCD E 26 PC7 PORTC.7 LCD RS 27 AVCC AVCC - 100uH - VCC +5V 28 GND GND 29 AREF AREF - 100n - GND 30 PA7 PORTA.7 31 PA6 PORTA.6 32 PA5 PORTA.5 33 PA4 PORTA.4 34 PA3 PORTA.3 35 PA2 PORTA.2 36 PA1 PORTA.1 ADC1 37 PA0 PORTA.0 ADC0 38 VCC +5V 39 GND GND 40 PB0 PORTB.0 41 PB1 PORTB.1 42 PB2 PORTB.2 43 PB3 PORTB.3 44 PB4 PORTB.4 '----------------------------- Lock bits: &hFF Fuse bits: &hD7 | &hD1 CKDIV8 1 OCDEN 1 CKOUT 1 JTAGEN 1 SUT1 0 SPIEN 0 SUT0 1 WDTON 1 CKSEL3 0 EESAVE 0 CKSEL2 1 BOOTSZ1 0 CKSEL1 1 BOOTSZ0 0 CKSEL0 1 BOOTRST 1 Ext Fuse bits: &hFC BODLEVEL2 1 BODLEVEL1 0 BODLEVEL0 0 '----------------------------- Si570 BBB000164DG 10-810 MHz, LVDS, 3.3V, 20ppm, 400 MHz SUF nominal '----------------------------- Rotary Encoder "ddm427" A GND B 15 pulse / 360° CCW CW A B A B 1 1 1 1 1 0 0 1 0 0 0 0 0 1 1 0 1 1 1 1 ################################################################################ ') '$Sim 'Only for simulation mode! '$Prog &hFF,&hD7,&hD1,&hFC '!! Take care. Lock and Fuse Bits !! $regfile = "M324Adef.dat" 'ATmega324A $crystal = 18432000 '18.4320 MHz $baud = 19200 'stack and framesize not optimized! $hwstack = 128 $framesize = 128 $swstack = 128 'Hardware TWI (I2C) $Lib "I2C_TWI.lbx" $Lib "double.lbx" '--- Config Ports -------------- DDRD.4 = 0 'PD.4 as Input PortD.4 = 1 'and PullUp enabled for Rotary Encoder A DDRD.5 = 0 'PD.5 as Input PortD.5 = 1 'and PullUp enabled for Rotary Encoder B '--- Config LCD ---------------- Config LCD = 16 * 2 Config LCDmode = PORT Config LCDbus = 4 Config LCDpin = Pin , Db4 = PortC.5 , Db5 = PortC.4 , Db6 = PortC.3 Config LCDpin = Pin , Db7 = PortC.2 , E = PortC.6 , RS = PortC.7 '--- Config Timer -------------- Config Timer0 = Timer , Prescale = 256 On Timer0 isr_Timer0 '--- Config I2C ---------------- Config TWI = 100000 'I2C clock speed (kBit/s) Config SDA = PortC.1 Config SCL = PortC.0 I2cInit 'initializes the SCL/DSA pins '---------------------- Declarations ------------------------------------------- Declare Sub Init() Declare Sub ReadStartupConfig() Declare Sub SetNewFrequency() Declare Sub CalcNewConfig() Declare Sub LoadNewConfig() Declare Sub ShowFreq(ByVal F As DWord) Dim SUF As Double 'start up frequency [MHz] from actual Si570 Dim fXTAL As DWord 'actual crystal afreq Dim nfreq As DWord 'new frequency Dim N1 As Byte 'slow divider Dim HSDIV As Byte 'fast divider Dim RFREQ As Double '38-bit multiplier Dim RFREQ30 As DWord 'upper 30 bits from 38 bit Dim RFREQ08 As Byte 'lower 8 bits from 38 bit Dim cfgdata(6) As Byte 'config data array Dim ffr As DWord 'last frequency with full reprograming Dim dvar(2) As Byte Dim stune As Byte 'smooth tuning with clickless sound Dim i As Byte Dim afreq As DWord 'actual frequency Dim tstep As Word 'tuning step Dim state As Byte 'status of rotary encoder Dim reold As Byte Dim renew As Byte '--- Si570 data ---------------- SUF = 400.0 'Start Up frequency from actual Si570 '400 MHz, MEASURED! Const Si570WAdr = &hAA 'write mode; I2C slave base address = &h55 Const Si570RAdr = &hAB 'read mode; I2C slave base address = &h55 Const fmin = 2542796800 '4850*2^19 = min. fDCO frequence 4850 in MHz*2^19 Const fmax = 2972712960 '5670*2^19 = max. fDCO frequence 5670 in MHz*2^19 Const favr = 2757754880 '5260*2^19 = average fDCO frequence in MHz*2^19 Const P2_28 = 268435456.0 '2^28 Const P2_24 = 16777216.0 '2^24 '--------------- Main ---------------------------------------------------------- Enable Timer0 'enable Timer0 Enable Interrupts 'enable global interrupts Cls Cursor Off Lcd "VHF-VFO" LowerLine Lcd "OE3HBW" Wait 2 Deflcdchar 0,16,24,28,31,31,28,24,16 'right arrow symbol for menu Cls Cursor Off Noblink afreq = 290000000 '290 MHz VFO: 1/2 divider --> 145 MHz VFO tstep = 400 '400 Hz tuning step: 1/2 divider --> 200 Hz step Err = 0 Call Init() If Err <> 0 Then 'I2C Error! Lcd "I2C/Si570 Error" 'write error End If Call ReadStartupConfig() 'read actual RFREQ, HSDIV, N1 --> fXTAL nfreq = afreq Call SetNewFrequency() Call ShowFreq(afreq) Do Disable Timer0 If state = 1 Then afreq = afreq - tstep If state = 2 Then afreq = afreq + tstep If state > 0 Then 'rotary encoder is turned nfreq = afreq Call SetNewFrequency() Call ShowFreq(afreq) state = 0 End If Enable Timer0 Loop End '------------------------- Main End ------------------------------------ '------------------------------------------------------------------------------- ' Initalizing Si570 (RECALL) and test I2C bus connection | '------------------------------------------------------------------------------- Sub Init() ' Initalizing Si570 (RECALL) and test I2C bus connection I2cstart 'start condition I2cwbyte Si570WAdr 'write slave address from Si570 I2cwbyte 135 'adress of Si570 register I2cwbyte &b0000_0001 'recall NVM nfreq --> D0 = 1 I2cStop 'stop condition End Sub '------------------------------------------------------------------------------- 'Read Si570 startup configuration for HSDIV, N1, RFREQ and calculates the | 'internal FXTAL. fXTAL = SUF x HSDIV x N1 / RFREQ | 'Before it is required to measure the actual StartUp frequency (= SUF) | '------------------------------------------------------------------------------- Sub ReadStartupConfig() Dim tmp21 As Byte 'auxiliary variable Dim tmp22 As Byte Dim iRFREQ As Word Dim fRFREQ As DWord Dim dtmp As Double Dim axf As Double 'read the six Si570 RAM registers I2cstart 'start condition I2cwbyte Si570WAdr 'write mode adress from Si570 I2cwbyte 7 'address of Si570 register I2crepstart 'repeated start I2cwbyte Si570RAdr 'read mode address from Si570 For i = 1 to 5 'read 5 bytes in cfgdata I2crbyte cfgdata(i) , ack 'read byte Next i I2crbyte cfgdata(6) , Nack 'read byte 6 I2cstop 'generate stop 'HSDIV divider conversion HSDIV = cfgdata(1) AND &b1110_0000 Shift HSDIV , right , 5 HSDIV = HSDIV + 4 'HSDIV = {4,5,6,7,9,11} 'N1 divider conversion tmp21 = cfgdata(1) AND &b0001_1111 Shift tmp21 , left , 2 tmp22 = cfgdata(2) AND &b1100_0000 Shift tmp22 , right , 6 N1 = tmp21 + tmp22 'complete N1 Byte If N1 = 0 Then N1 = 1 'exception value 1 of N1 Else tmp21 = N1 And &h01 'odd divider values If tmp21 <> 0 Then 'are illegal N1 = N1 + 1 'make even End If 'N1 = {1,2,4,6...128} End If 'RFREQ conversion 'first the integer portion of RFREQ (10 MSB) 'stored as WORD = unsigned 16-bit binary number iRFREQ = cfgdata(2) AND &b0011_1111 'register 8, 6 MSB of RFREQ (4 in use!) shift iRFREQ , left , 4 '0000_00xx|xxxx_0000 tmp21 = cfgdata(3) AND &b1111_0000 'MSN half of register 9 shift tmp21 , right , 4 '0000_xxxx iRFREQ = iRFREQ + tmp21 '0000_00xx|xxxx_xxxx = integer portion 'fractional portion (stored as DWORD = unsigned 32-bit binary number) fRFREQ = cfgdata(3) And &b0000_1111 '0000_0000|0000_0000|0000_0000|0000_xxxx shift fRFREQ , left , 8 '0000_0000|0000_0000|0000_xxxx|0000_0000 fRFREQ = fRFREQ + cfgdata(4) '0000_0000|0000_0000|0000_xxxx|xxxx_xxxx shift fRFREQ , left , 8 '0000_0000|0000_xxxx|xxxx_xxxx|0000_0000 fRFREQ = fRFREQ + cfgdata(5) '0000_0000|0000_xxxx|xxxx_xxxx|xxxx_xxxx shift fRFREQ , left , 8 '0000_xxxx|xxxx_xxxx|xxxx_xxxx|0000_0000 fRFREQ = fRFREQ + cfgdata(6) '0000_xxxx|xxxx_xxxx|xxxx_xxxx|xxxx_xxxx 'fractional portion with 28 bit 'RFREQ stored as DOUBLE (= signed 64-bit binary number) for 2^28 calculation RFREQ = fRFREQ 'type casting to Double RFREQ = RFREQ / P2_28 '/2^28 --> result is a number < 1 dtmp = iRFREQ 'type casting to Double RFREQ = dtmp + RFREQ 'total RFREQ 'actual Si570 crystal frequency dtmp = HSDIV 'type casting to Double axf = SUF axf = axf * dtmp dtmp = N1 'type casting to Double axf = axf * dtmp axf = axf / RFREQ 'XTAL frequency [MHz] from actual Si570 device dtmp = axf * P2_24 fXTAL = dtmp 'fXTAL (DWord) in MHz*2^24 ffr = SUF 'init first frequency dvar(1) = cfgdata(1) 'HSDIV, N1 dvar(2) = cfgdata(2) 'N1 End Sub '------------------------------------------------------------------------------- ' Main routine for frequency change - set new Si570 afreq | '------------------------------------------------------------------------------- Sub SetNewFrequency() Dim fmem As DWord Dim delta As DWord Dim fscaled As DWord Dim fract As DWord Dim tmp09 As DWord Dim tmp0 As DWord Dim tmp10 As Byte Dim tmp11 As Byte Dim tmp12 As DWord 'calculation freq * 2^21 / 10^6 by approximation 'freq in MHz * 2^21 units, not in Hz 'freq * 2^21/10^6 = freq*2.097152 'freq*2 + fix-point multiplication by the fractional part 'freq = freq*2 + freg*0.097152 fmem = nfreq fscaled = nfreq tmp09 = 0 fract = &h18DEF417 'h18DEF417 = 2^32 * 0.097152 = b0001_1000_1101_1110_1111_0100_0001_0111 'h80000000 = 2^31 = b1000_0000_0000_0000_0000_0000_0000_0000 For i = 1 To 32 tmp0 = fract AND &h80000000 If tmp0 = &h80000000 Then tmp09 = tmp09 + fscaled Shift fscaled , right , 1 Shift fract , left , 1 Next i Shift tmp09 , right , 1 Shift nfreq , left , 1 nfreq = nfreq + tmp09 'freq = freq*2.097152 in MHz*2^21 units Call CalcNewConfig() 'calculate new values of RFREQ and deviders 'if frequency has changed by less than 3500ppm since last full reprogram and 'the dividers have not changed then smooth-tuning is possible If fmem > ffr Then delta = fmem - ffr Else delta = ffr - fmem End If tmp10 = cfgdata(2) AND &hC0 tmp11 = dvar(2) AND &hC0 tmp12 = delta*300 If cfgdata(1) = dvar(1) AND tmp10 = tmp11 AND delta < 100000 AND tmp12 < ffr Then 'small frequency change <= 3500 ppm stune = 1 Call LoadNewConfig() Else 'large frequency change > 3500 ppm stune = 0 Call LoadNewConfig() 'remember frequency and devider settings ffr = fmem dvar(1) = cfgdata(1) dvar(2) = cfgdata(2) End If End Sub '------------------------------------------------------------------------------- ' Calculates new RFREQ and devider settings | ' Some tricky code parts originate from a Si570 C-firmware from Tom, DG8SAQ | '------------------------------------------------------------------------------- Sub CalcNewConfig() Dim N0 As DWord Dim xN As Word Dim xHSDIV As Byte Dim xRFREQ As Byte Dim fDCO As DWord Dim tmp01 As Long Dim tmp02 As Long Dim tmp03 As Byte Dim tmp04 As DWord Dim tmp08 As DWord N0 = nfreq / 8 N0 = favr / N0 fDCO = fmax 'DCO frequency in MHz*2^19 xHSDIV = 11 'find best divider values with alowed smallest DCO freqeuncy While xHSDIV > 3 N0 = N0 AND &h0000FFFF 'DWord --> Word xN = N0 / xHSDIV 'last 0.5 digit for rounding xN = xN + 1 Shift xN , right , 1 'nearest integer If xN > 2 Then xN = xN AND &hFE 'N = 1 or even If xN > 128 Then xN = 128 'N <= 128 If xN <> 1 Then tmp08 = nfreq / 2 tmp08 = tmp08 * xHSDIV tmp01 = xN / 2 tmp08 = tmp08 * tmp01 else tmp08 = nfreq / 4 tmp08 = tmp08 * xHSDIV End If If tmp08 >= fmin AND tmp08 <= fDCO Then 'valid dividers fDCO = tmp08 N1 = xN HSDIV = xHSDIV End If xHSDIV = xHSDIV - 1 If xHSDIV = 10 OR xHSDIV = 8 Then xHSDIV = xHSDIV - 1 WEnd N1 = N1 - 1 'convert divider N1 HSDIV = HSDIV - 4 'convert divider HSDIV 'RFREQ = fDCO/fxtal with tricky non floating-point mathematics 'The most significant nibble are always 0 - RFREQ = 34 bits RFREQ30 = 0 'higher 30 bit --> 26 bit RFREQ08 = 0 'lower 8 bit For i = 1 to 34 tmp04 = fDCO / fXTAL '0 or 1 xRFREQ = tmp04 If i <= 26 Then shift RFREQ30 , left , 1 RFREQ30 = RFREQ30 + xRFREQ Else shift RFREQ08 , left , 1 RFREQ08 = RFREQ08 + xRFREQ End If tmp02 = xRFREQ * fXTAL fDCO = fDCO - tmp02 shift fDCO , left , 1 Next i 'write RFREQ and dividers in config data array cfgdata(6) = RFREQ08 cfgdata(5) = Low(RFREQ30) 'LSB of RFREQ30 shift RFREQ30 , right , 8 '0000_0000|xxxx_xxxx|xxxx_xxxx|xxxx_xxxx cfgdata(4) = Low(RFREQ30) shift RFREQ30 , right , 8 '0000_0000|0000_0000|xxxx_xxxx|xxxx_xxxx cfgdata(3) = Low(RFREQ30) shift RFREQ30 , right , 8 '0000_0000|0000_0000|0000_0000|xxxx_xxxx cfgdata(2) = Low(RFREQ30) cfgdata(2) = cfgdata(2) AND &h3F tmp03 = N1 shift tmp03 , left , 6 cfgdata(2) = cfgdata(2) OR tmp03 shift N1 , right , 2 shift HSDIV , left , 5 cfgdata(1) = N1 OR HSDIV End Sub '------------------------------------------------------------------------------- ' Write new configuration data in the Si570 register by I2C bus | '------------------------------------------------------------------------------- Sub LoadNewConfig() Dim rofs As Byte 'register offset If stune = 0 Then 'smooth tuning = false rofs = 1 'register 7..12 I2cstart I2cwbyte Si570WAdr I2cwbyte 137 'Si570 register no 137 I2cwbyte &h10 'set freeze DCO bit reg137.4 I2cstop Else 'smooth tuning = true rofs = 2 'register 8..12 I2cstart I2cwbyte Si570WAdr I2cwbyte 135 'Si570 register no 135 I2cwbyte &h20 'set freeze M bit reg135.5 I2cstop End If I2cstart I2cwbyte Si570WAdr i = rofs + 6 'register adress I2cwbyte i For i = rofs To 6 'load new configuration I2cwbyte cfgdata(i) 'in 5 or 6 Si570 register Next i I2cstop If stune = 0 Then 'smooth tuning = false I2cstart I2cwbyte Si570WAdr I2cwbyte 137 I2cwbyte &h00 'unfreeze DCO I2cstop I2cstart I2cwbyte Si570WAdr I2cwbyte 135 I2cwbyte &h40 'set reg135.6 - new freq applied I2cstop Else 'smooth tuning = true I2cstart I2cwbyte Si570WAdr I2cwbyte 135 I2cwbyte &h00 'unfreeze M I2cstop End If End Sub '------------------------------------------------------------------------------- 'Show frequency with LCD | '------------------------------------------------------------------------------- Sub ShowFreq(ByVal F As DWord) 'Show frequency with LCD Dim s as String * 9 Dim s1 as String * 11 F = F / 1 'divider, only for TEST = 1! s = Str(F) s = Format(s , "000000000") s1 = Left(s , 3) + "." + Mid(s , 4 , 3) + "." + Right(s , 3) LowerLine Lcd Spc(16) Locate 2 , 1 Lcd "F " ; s1 ; " Hz" End Sub '------------------------------------------------------------------------------- 'ISR - process rotary encoder | 'code after an idea from Tom Baer, 2008 | '------------------------------------------------------------------------------- isr_Timer0: Timer0 = 64 'Timer preset (initialized with ...) < 255 renew.0 = PIND.4 renew.1 = PIND.5 If renew <> reold Then 'rotary encoder is turned If renew = &b0000_0000 AND reold = &b0000_0001 Then state = 2 If renew = &b0000_0000 AND reold = &b0000_0010 Then state = 1 If renew = &b0000_0011 AND reold = &b0000_0010 Then state = 2 If renew = &b0000_0011 AND reold = &b0000_0001 Then state = 1 reold = renew End If Return '( -------------------------------------------------------------------------------- If you can't do it in BASCOM, do it in assembly language. If you can't do it in assembly language, it isn't worth doing. ################### End of program ############################################# ')