![]() |
Forum Index : Microcontroller and PC projects : [Micromite]KY040 Rotary Encoder Module.
Page 1 of 2 ![]() ![]() |
|||||
Author | Message | ||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
Hi The KY040 Rotary Encoder module is widely available on eBay for less than US$2.00 inc P&P to UK, See Link Anyway, here's some demonstrator software to show how to wire the KY040 up to a uMite and use it. The code is also available on MMReference.com Thanks David. Hopefully this code will help more people to use Rotary Encoders with their uMites. 73 Peter '*********************************************************** ************************ ' Rotary Encoder (KY040) Demonstrator ' ' MMBasic 4.5 or newer. ' ' (c) Peter Carnegie 2014 ' ' ' The KY040 module contains a rotary encoder together with a push-switch on a small ' PCB with 5 Connections. The KY040 was developed for Arduino, but of course us uMite ' folk can use it as well, and the KY040 is incredibly cheap, US$2.0 inc P&P, just ' search eBay for KY040. ' ' Wiring it up ' ' KY040 Micromite ' ----- --------- ' CLK 2 ---> 0.1uF ---> Ov ' DT 3 ---> 0.1uF ---> Ov ' SW 4 ---> 0.1uF ---> Ov ' + 3V3 ' Gnd 0V ' ' NB ' 1) It is VERY important to decouple the DT,CLK & SW pins to 0V ' using 0.1uF at the uMite end of the link wire. ' If you don't decouple these signals, severe contact bounce will be evident, ' eg one click will generate a dozen or more increment/decrements. ' ' 2) The KY040 module has on-board pullup resistors. You must supply 3V3 or less ' to the + pin of the KY040 module ' ' 3) This s/w works fine with just a standalone rotary encoder, but you will need ' to supply your own 10K pullups to 3V3. For a standalone encoder RA=CLK, RB=DT, & SW=SW ' ' When this software runs, each CLOCKwise step of the rotary encoder will result ' in an increase of the variable DisplayValue which will be printed to the Console. ' Each ANTI-clockwise step of the rotary encoder will result in a decrease of the ' variable DisplayValue which will be printed to the Console. ' Pushing the push-switch will clear the variable DisplayValue to 0. ' This demonstration s/w is Interrupt based for maximum responsiveness. ' This software improves on the snippet listed ' in the Micromite User Manual in that the count, DisplayValue ' increases/decreases more smoothly/accurately. ' '*********************************************************** ************************ 'Declare all Global Variables here 'Rotary Encoder Input pins DIM CLK,DT CLK=2 DT=3 'Push Switch Input Pin DIM SW SW=4 'Configure uMite pins SETPIN DT, DIN SETPIN CLK, INTH, REInt SETPIN SW, INTH, SWInt 'Variable incremented/decremented by the Interrupt handling 'routine for the Rotary Encoder.Needs to be global so that 'REInt ISR can access it DIM Value 'Var Display Value Inc/Decremented in Main Loop 'Cleared when Switch is pushed.Needs to be Global so that 'SWInt ISR can access it DIM DispValue 'Set to 1 to request a refresh of the display.Needs to be Global so that 'SWInt ISR can access it DIM DisplayRefresh 'Go run the Program Main END SUB Main LOCAL LastValue LOCAL Change 'The Main Program Loop DO 'Has there been any movement of the encoder ? Change=Value-LastValue 'We've caught the change so prepare for next time round LastValue=Value 'Was there any movement of the encoder ? IF Change<>0 THEN 'If encoder is turning ANTI-clockwise, Decrement IF Change<0 THEN DispValue=DispValue-1 ENDIF 'If encoder is turning CLOCKwise, Increment IF Change>0 THEN DispValue=DispValue+1 ENDIF 'Request a Display Refresh DisplayRefresh=1 ENDIF 'Update the Display ? IF DisplayRefresh=1 THEN 'Reset the trigger DisplayRefresh=0 'Display on the Console PRINT STR$(DispValue) ENDIF LOOP END SUB 'ISR for Rotary Encoder 'Here when the Rotary encoder moves SUB REInt() IF PIN(DT)=1 THEN 'Clockwise rotation Value=Value+1 ELSE 'Anti-Clockwise rotation Value=Value-1 ENDIF END SUB 'ISR for Push-Switch 'Here when Switch changes state SUB SWInt() 'Clear the Displayed Value DispValue=0 'And trigger a DisplayRefresh of DispValue DisplayRefresh=1 END SUB The only Konstant is Change |
||||
boss![]() Senior Member ![]() Joined: 19/08/2011 Location: CanadaPosts: 268 |
Hi Peter, I bought the same encoder module on e-Bay, so thank you for the description and the code. I'm using right now an optical encoder (Bourns ENA1P-028) purchased a couple of months ago on eBay for a bargain price(~$6 + shipping). Although optical encoders are designed for 5V they work with 3.3V as well. Regards boss |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
@boss Bourns encoders are really first class, but usually a lot more expensive, so you were really lucky to get an optical Bourns encoder for so little. I have been unable to find the part number you gave, is that the complete part number ? 73 Peter The only Konstant is Change |
||||
boss![]() Senior Member ![]() Joined: 19/08/2011 Location: CanadaPosts: 268 |
Hi Peter, yes, the encoder label says: ENA1P-B28 LDO128 1050M MEX but nothing such can be found in catalog. Looks like an obsolete part and that's why the price was so low. The encoder has resolution 128 pulses/turn. Closest type in catalog is ENA1P-B50. Works perfectly. Regards Bo |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
Mouser sells ENA1P-B28-L00128L which looks very similar, and is ROHS at £20.97 + VAT (20%)so looks like U got an absolute bargain !! 73 Peter The only Konstant is Change |
||||
boss![]() Senior Member ![]() Joined: 19/08/2011 Location: CanadaPosts: 268 |
Hi Peter, because of Bourns encoder has 128pulses/revolution the steps 1-10-100-1000Hz are to fine. I decided to change the steps to 1-100-10000-1000000Hz and got an error message "String is to Long". Can you get me a clue how to fix that problem? Thanks. Btw. we have a terrible hot day in Vancouver. Regards boss |
||||
BobD![]() Guru ![]() Joined: 07/12/2011 Location: AustraliaPosts: 935 |
How hot? My brother lives in Burnaby. I'm freezing my ???? off here at -1.1 C this morning. |
||||
boss![]() Senior Member ![]() Joined: 19/08/2011 Location: CanadaPosts: 268 |
Yah, most bargain parts I purchsed become from US estate, you can find the real treasure there. Unfortunately most of Canadians throw the parts from Silent key estate to the garbage can. bo |
||||
boss![]() Senior Member ![]() Joined: 19/08/2011 Location: CanadaPosts: 268 |
OMG, we start every morning with 20deg at 7am, 31deg at 4pm and 28+ now. And I'm living next to the ocean in Burnaby they have even more hot evening. -1.1 in July that's because of Global warming. Good reason for government to increase taxation. bo |
||||
boss![]() Senior Member ![]() Joined: 19/08/2011 Location: CanadaPosts: 268 |
Hi Peter, I changed: StepHz(0)=1 StepHz(1)=100 StepHz(2)=10000 StepHz(3)=100000 and there is the error 10005100 10005000 10004900 10004800 10004700 10004600 10004700 10004800 10004900 10005000 10005100 10005200 10005300 10005400 10005500 10015500 10025500 10035500 10045500 10055500 10065500 10075500 10085500 10095500 10105500 10115500 10125500 10135500 10145500 10155500 10165500 10175500 [218] CurFreq$(1)=CurFreq2Asc$() Error: String too long > |
||||
viscomjim Guru ![]() Joined: 08/01/2014 Location: United StatesPosts: 925 |
Hi Peter, Very nice code for encoder with pushbutton. I happened to have purchased this exact same encoder and it does indeed work very well with the .1uf caps. I was needing to have available a means of returning to a "menu" using this encoder and discovered the long press and short press routines in Jmans most excellent meter clock code. I extracted this and am using it this way... When a user is adjusting his settings with the encoder, a short press makes his selection and a long press always returns to the main menu. Here is basic code idea which can be adjusted to suit your needs. Again, a big thanks to Jman for this. LCD INIT 4, 5, 6, 7, 2, 3 LCD CLEAR VALUE = 0 SETPIN 9, DIN 'PIN 9 = CLK SETPIN 10, INTH, REI 'PIN 10 = DT BUTTON = 14 SETPIN BUTTON, DIN LONGPRESS = 15 DO IF PIN(Button) = 0 THEN GOSUB GetButton IF ButtonPress = 2 THEN LONGBUTTON IF ButtonPress = 1 THEN GOSUB SHORTBUTTON LOOP GetButton: ' Constant LONGPRESS determines LongPress Time ' ButtonPress=0 - No Press ' ButtonPress=1 - Short Press ' ButtonPress=2 - Long Press ButtonPress=0 DO WHILE PIN(Button)=0 IF ButtonPress<255 THEN ButtonPress=ButtonPress+1 PAUSE 25 ' This is also our Debounce value LOOP IF ButtonPress>0 THEN IF ButtonPress=>LongPress THEN ButtonPress=2 ELSE ButtonPress=1 ENDIF ENDIF RETURN SUB LONGBUTTON LCD 2,1," " LCD 2,1,"LONG PRESS" BUTTONPRESS = 0 END SUB SHORTBUTTON: LCD 2,1," " LCD 2,1,"SHORT PRESS" BUTTONPRESS = 0 RETURN REI: IF PIN(9)=1 THEN VALUE = VALUE - 1 ELSE VALUE = VALUE + 1 ENDIF IF VALUE <= 0 THEN VALUE = 0 'SET MIN VALUE HERE IF VALUE >= 20 THEN VALUE = 20 'SET MAX VALUE HERE PRINT "VALUE = ";VALUE LCD 1,1," " LCD 1,1,STR$(VALUE) IRETURN |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
@boss I'm assuming that you are referring to the AD9850Controller software I published on TBS ? 1) I can't find line 218 which says CurFreq$(1)=CurFreq2Asc$() 2) Are U using MMEdit with Auto-Crunch and "Delete Blank Line" set ON ? Could you please make sure that MMEdit is configured with "Delete Blank Lines" set to OFF so that my source line numbers and your source line numbers line up. 3) I think your line 218 may be referring to my source line 420, in which case try changing line 418 LOCAL CurFreq$(1) Length 8 to say LOCAL CurFreq$(1) Length 10 4) The best thing to do would be implement "gearing" in the Rotary Encoder handling, Try this replacement ISR for RINT. 'Interrupt Routine 'Here when the Rotary encoder moves Sub RInt Local Ratio Local Gearing Gearing=10 'Do gearing - change Gearing to suit your tastes Ratio=Ratio+1 if Ratio>Gearing then Ratio=0 IF PIN(RB)=1 THEN 'Clockwise rotation Value=Value+1 End If ELSE 'Anti-Clockwise rotation Value=Value-1 END IF End Sub Please let us all know how it goes. 73 Peter The only Konstant is Change |
||||
boss![]() Senior Member ![]() Joined: 19/08/2011 Location: CanadaPosts: 268 |
Hi Peter, thank you for response. I'm using your code and I added few routines (ie. serial display, RTC). The code was tested for week and is working perfectly. The problem appeared when I decided to change "STEP". Here is the code: 'AD9850 VFO Controller V9 '(c)Peter Carnegie - GM8JCF - 2014 '================= 'Rotary Encoder - exactly as per page 22 Micromite User Manual 'RA -> 2 'RB -> 3 '=================== 'Ebay AD9850 Module see http://www.alhin.de/arduino/index.php?n=7 '3 (FQUD) -> 18 '4 (Data) -> 17 '2 (WCLK) -> 16 '5 -> GND '6 -> GND '1 -> Vcc (3V3) '20 -> Vcc (3V3) '19 -> 4K7 -> Vcc(3V3) '18 -> 4K7 -> Vcc(3V3) '17 -> Gnd '11 -> Gnd '================== 'LCD - 16x2 lines - see page 19 Micromite USer Manual '4 (RS) -> 22 '6 (EN) -> 21 '14 (D7) -> 26 '13 (D6) -> 25 '12 (D5) -> 24 '11 (D4) -> 23 '================== 'Future rekon -with I2C display '1 MCLR '2 ADC-1 '3 ADC-2 '4 ADC-3 '5 ADC-4 Battery Voltage '6 RA - Rotary Encoder '7 RB - Rotary Encoder '8 Ground '9 FQUD - AD9850 '10 WCLK - AD9850 '11 Console Tx '12 Console Rx '13 Power '14 SData - AD9850 '15 KPD-R1 '16 Tune Step Change/Wake Up '17 I2C-SCL - LCD '18 I2C-SDA - LCD '19 Gnd '20 Vcap '21 KPD-R2 '22 KPD-R3 '23 KPD-R4 '24 KPD-C1 '25 KPD-C2 '26 KPD-C3 '27 Analog Gnd '28 Analog Pwr '================ 'eBay AD9850 Modules use 125,000,000 Hz Clocks 'AD9850 Fout= (? Phase CLKIN)/2^32 where Fout=32 bit tuning word 'TuningWord= Fout * 2^32 / CLKIN = Fout * (2^32/125,000,000) '2^32/125,000,000 = 34.359738368 - VB6 Double Precision 'Using Integer only arithmetic, multiply by 1,000,000 'DivConst= 34359738 'We then divide the result of TuningWord=DivConst * Fout by 1,000,000 'Initialise the BCD Library InitBCDLib Dim DivConst(7) 'Load it up in BCD LoadBCD DivConst(0),"34359738" 'Cur Freq in BCD Dim CurFreq(7) 'Load it up in BCD LoadBCD CurFreq(0),"10000000" 'Define DDS Control pins FQUD=18 SData=17 WCLK=16 'Initialise the LCD Display DispLen=16 '26=D7, 25=D6, 24=d5, 23=d4 '22=RS, 21=EN LCD init 23,24,25,26,22,21 'Rotary Encoder Input pins - see page 22 uMite User Manual Dim RA,RB RB=2 'If you use mechanical encoder RA=3 'If you use mechanical encoder RSW=4 'Roll switch SetPin RB, Din SetPin RA, INTH, RInt SETPIN RSW, INTL, Roll 'Variable incremented/decremented by the Interrupt handling 'routine for the Rotary Encoder Dim Value,LastValue 'Raw Tuning Word, ie scaled by 1E6 Dim RawTWord(7) 'Tuning Word scaled back to real world units, ie divide by 1E6 Dim TWord(7) 'Index of which Step size is in force Dim CurStepIndex 'Startup with 100Hz tuning steps CurStepIndex=2 'Current Step in TWord units Dim CurStepTW(7) 'Array of Possible Step Sizes in Hz Dim StepHz(4) StepHz(0)=1 StepHz(1)=10 StepHz(2)=100 StepHz(3)=1000 'What to Add/Subtract from the Scaled tuning Word 'for each Encoder Up/Down Dim StepTWord$(4) Length 24 'These TuningWord sizes were calculated using Double precision arithmetic 'in VB6 StepTWord$(0)="34359738" '1Hz Step StepTWord$(1)="343597384" '10Hz StepTWord$(2)="3435973837" '100Hz 3435973836.8 StepTWord$(3)="34359738368" '1000Hz 'Load the CurStepTW with the startup Step in TW LoadBCD CurStepTW(0),StepTWord$(CurStepIndex) 'Calculate Starting TuningWord CopyBCD DivConst(0),BCDA(0) CopyBCD CurFreq(0),BCDB(0) 'Calculate the Tuning Word, this one is * 1E6 MulAB2C 'Save the RawTWord CopyBCD BCDC(0), RawTWord(0) 'Start with clean BCD Registers ClrBCD(BCDA(0)) ClrBCD(BCDB(0)) ClrBCD(BCDC(0),15) 'Go do Main Loop Main End Sub Main 'General purpose String used in Main Loop Local D$(1) length 24 Local I,L,Cy 'Flag when rotary encoder has moved. Local Change D$(1)="GM8JCF "+Time$ SetTick 1001,DOIT Memory LCD 1, C16, D$(1) DispFreq 'Initialise the I/O & DDS chip InitDDS 'Save Power 'CPU 5 'Do for ever ! Do Change=Value-LastValue Value=LastValue 'Any movement ? If Change<>0 Then 'We need all the speed we can get for the calculations CPU 48 'If encoder is turning ANTI-clockwise, Decrement 'If encoder is turning CLOCKwise, Increment If Change<0 Then DecCurFreq(StepHz(CurStepIndex)) Else IncCurFreq(StepHz(CurStepIndex)) EndIf 'Calculate the change to the Tuning Word 'If ANTI-clockwise then decrement TuningWORD 'If CLOCKwise then incrementg TuningWORD If Change<0 Then 'Decrement Cy=0 For I=0 To 7 If RawTword(I)>=CurStepTW(I)+Cy Then RawTword(I)=RawTword(I)-CurStepTW(I) - Cy Cy=0 Else RawTword(I)=RawTword(I)+1000 - CurStepTW(I) - Cy Cy=1 EndIf Next I Else 'Increment Cy=0 For I=0 To 7 RawTword(I)=RawTword(I) + CurStepTW(I) + Cy If RawTword(I)>999 Then Cy=RawTword(I)\1000 RawTword(I)=RawTword(I) Mod 1000 Else Cy=0 EndIf Next I EndIf 'Save the New Tuning Word for next time For I=0 To 7 BCDC(I)=RawTword(I) Next I 'Round to the nearest 1,000,000 'ie Add 500,000, then chop off the last 6 digits Cy=500 For I=1 To 3 BCDC(I)=BCDC(I)+Cy If BCDC(I)<1000 Then Exit For Else Cy=BCDC(I)\1000 BCDC(I)=BCDC(I) Mod 1000 EndIf Next I 'And make the Actual DDS Tuning Word, ie scale down by 1E6 'For BCD that just means removing the last 6 digits For I=2 To 7 BCDA(I-2)=BCDC(I) Next I BCDA(6)=0:BCDA(7)=0 'Convert BCD to UINT32 BCDA2Bin SendDDSData DispFreq 'Reset change flag for next iteration Change=0 'Slowdown the CPU to save power 'CPU 5 EndIf 'Go check again Loop End Sub End 'Interrupt Routine 'Here when the Rotary encoder moves RInt: If Pin(RB)=1 Then 'Clockwise rotation Value=Value-1 Else 'Anti-Clockwise rotation Value=Value+1 EndIf IReturn '=============== DOIT: LCD 1,9,Time$ IReturn '========================================================= 'Increment CurFreq() by Inc ' ' Sub IncCurFreq(Inc) Local I,Cy Cy=Inc For I=0 To 2 CurFreq(I)=CurFreq(I) + Cy If CurFreq(I)<1000 Then Exit For Else Cy=CurFreq(I)\1000 CurFreq(I)=CurFreq(I) Mod 1000 EndIf Next I End Sub '========================================================= 'Decrement CurFreq() by Dec ' ' Sub DecCurFreq(Dec) Dim I, Cy Cy=Dec For I=0 To 2 If CurFreq(I)>=Cy Then CurFreq(I)=CurFreq(I)-Cy Exit For EndIf CurFreq(I)=CurFreq(I)+1000 - Cy Cy=1 Next I End Sub '========================================================= 'Returns a string representing the BCD Digits from CurFreq() 'eg if CurFreq(0)=123, CurFreq(1)=456,CurFreq(2)=789, then 'CurFreq2ASC$ will return "789456123" ' Function CurFreq2Asc$() Local I,J,A$(1) Length BCDSize*3,B$(1) Length BCDSize*3 'Frequency is 8 digits long , eg 30,000,000 For I=2 To 0 Step -1 'Find First non Zero Triplet If CurFreq(I)<>0 Then For J=I To 0 Step -1 B$(1)=B$(1)+Str$(CurFreq(J),3,0,"0") Next J 'Now Remove any leading Zeroes For J=1 To 3 If Peek(VAR B$(1),J)<>&H30 Then B$(1)=Mid$(B$(1),J) Exit For EndIf Next J Exit For EndIf Next I CurFreq2Asc$=B$(1) End Function '========================================================= ' ' ' Sub InitDDS SetPin FQUD, DOUT : Pin(FQUD)=0 SetPin SData, DOUT : Pin(SData)=0 SetPin WCLK, Dout : Pin(WCLK)=0 SetFreq End Sub '========================================================= ' 'Fundamental equation for DDS 'Tuningword = FrequencyDesired * (AccumulatorBits / ClockIn) 'For an AD9850, TuningWord = F * ( (2^32) / ClkIn ) ' Sub SetFreq Local I,J,TWord(7) For I=0 To 7 BCDA(I)=CurFreq(I) Next I For I=0 To 7 BCDB(I)=DivConst(I) Next I 'Calc the Tuning Word MulAB2C 'Save this Long Tuning Word For I=0 To 7 RawTWord(I)=BCDC(I) Next I 'Div by 1E6 J=0 ClrBCD(BCDA(0)) For I=2 To 7 BCDA(J)=BCDC(I) J=J+1 Next I 'Convert BCDA() to 8 hex digits, ie 32 bits BCDA2Bin 'Tuning Word is now in BCDC() 'Send it to out SendDDSData End Sub '========================================================= ' 'Tuning Word to be sent to the DDS must be in BCDC() ' ' Sub SendDDSData Local I BCDC(4)=0 Pin(FQUD)=0 'Make sure FQUD is Low Pin(WCLK)=0 'Clock pin Low Pin(Sdata)=0 'Data Pin Low 'Send the Tuning Word and Control bytes to the AD9850 For I=0 To 4 Send8Bits(BCDC(I)) Next I Pulse FQUD,0.05 'Latch the data into the DDS chip End Sub '========================================================= ' ' ' Sub Send8Bits(Byte) Local B 'Make a copy so that we don't damage the callers data B=Byte For I=0 To 7 If (B And 1)<>0 Then Pin(SData)=1 Else Pin(Sdata)=0 EndIf B=B \ 2 Pulse WCLK,0.05 'Pulse the clock line Next I End Sub '========================================================= ' ' ' Sub DispFreq Local CurFreq$(1) Length 8 CurFreq$(1)=CurFreq2Asc$() 'Display Frequency on the console Print CurFreq$(1) 'Display Frequency on the LCD on Line 2 LCD 2, C16, CurFreq$(1) LCD 2, 15,"Hz" End Sub '========================================================= ' ' ' Sub InitBCDLib(NumDigits) If NumDigits=0 Then NumDigits=24 Dim BCDSize 'Number of elements per BCD register 'Each Element holds 3 digits BCDSize=(NumDigits\3) 'The next version of the BCDLib will use BCDSize 'instead of hard-coded values of 7, 15, 24 etc 'to allow arbitrary precision arithmetic Dim BCDA(BCDSize-1),BCDB(BCDSize-1),BCDC(BCDSize*3) End Sub '========================================================= 'BCDC()=BCDA() x BCDB() 'This algorithm works on Triplets of digits 'For optimum speed, make sure the shortest multiplier is in BCDB() ' Sub MulAB2C Local Cy, I, J, K, L, M, N 'Array for the intermediate multiplication Local C1(16) For I=0 To 15 BCDC(I)=0 Next I 'Set default length M=7 'Find length of BCDA() For I = 7 To 0 Step -1 If BCDA(I) <> 0 Then M=I Exit For EndIf Next I 'Set default length N=7 'Find Length of BCDB() For I = 7 To 0 Step -1 If BCDB(I) <> 0 Then N=I Exit For EndIf Next I 'Now Do the Long Multiplication of the two numbers L = 0 K = 0 For I = 0 To M+1 Cy = 0 For J = 0 To N+1 C1(J)=(BCDA(I)*BCDB(J))+Cy If C1(J) > 999 Then Cy = C1(J) \ 1000 C1(J) = C1(J) Mod 1000 Else Cy = 0 EndIf Next J 'Add the partial result to the overall total 'Add each element of C() to D() result into D() Cy = 0 For J = 0 To 7 '23 \ 3 K = J + L BCDC(K)=BCDC(K)+C1(J)+Cy If BCDC(K) > 999 Then Cy=BCDC(K)\1000 BCDC(K)=BCDC(K) Mod 1000 Else Cy = 0 EndIf Next J L=L+1 Next I End Sub '========================================================= 'Clear BCD Register 'Dest is Element 0 of a BCD register, eg FREQ(0) 'NumDigits is Number of digits in the BCD register, is Optional ' Sub ClrBCD(Dest,NumDigits) Local I If NumDigits=0 Then NumDigits=7 For I=0 To (NumDigits*4)-1 Poke VAR Dest,I,0 Next I End Sub '========================================================= ' 'Copy a BCD string into a BCD Register/Destination 'Dest is the first element of a BCD register, eg BCDA(0), CurFreq(0) 'A$ is a string of BCD digits, eg "1234567890" ' Sub LoadBCD(Dest,A$,NumDigits) Local I,J,M Local B(15),A2$(28) If NumDigits=0 Then NumDigits=7 'Pad to make sure string is a multiple of 3 digits A2$(1)=LPad$(A$,3) J = 0 M=Len(A2$(1)) For I = M - 2 To 1 Step -3 B(J) = Val(Mid$(A2$(1), I, 3)) J = J + 1 Next I For I=0 To (NumDigits*4)-1 Poke VAR Dest,I,Peek(VAR B(0),I) Next I End Sub '========================================================= 'Prepend leading 0 to make sure 'string is a multiple of ML digits ' Function LPad$(A$,ML) Local I,J,M M=Len(A$) If M Mod ML <> 0 Then For I=ML To 24 Step ML If M<I Then ' Print "LPad$:A$";A$ 'DEBUG LPad$=String$(I-M,"0")+A$ Exit For EndIf Next I Else LPad$=A$ EndIf End Function '============================================== ' 'Convert BCDA() into an unsigned 32 bit integer 'in BCDC() 'Algorithm process triplets of decimal digits ' Sub BCDA2Bin Local I,T,L,Cy For I=0 To 15 BCDC(I)=0 Next I T=7 For I=7 To 0 Step -1 If BCDA(I)<>0 Then T=I Exit For EndIf Next I 'Each triplet of digits are worth 0-999, ie 1000's L=1000 ^ (T) For I=T To 0 Step -1 '0 To 11 'Add Next Decimal Digit into the Binary Long word BCDC(0)=BCDC(0)+(BCDA(I)*L) 'Has this caused an overflow If BCDC(0)>&HFF Then 'Yes 'Now ripple the overflow across the other bytes Cy=BCDC(0) And &H7FFFFF00 BCDC(0)=BCDC(0)And &HFF BCDC(1)=BCDC(1)+(Cy\&H100) 'Has this caused an overflow If (BCDC(1)>&HFF) Then 'Yes 'Now ripple the overflow across the other bytes Cy=BCDC(1) And &H7FFFFF00 BCDC(1)=BCDC(1) And &HFF BCDC(2)=BCDC(2)+(Cy\&H100) 'Has this caused an overflow If (BCDC(2)>&HFF) Then 'Yes 'Now ripple the overflow across the other bytes Cy=BCDC(2) And &H7FFFFF00 BCDC(2)=BCDC(2) And &HFF BCDC(3)=BCDC(3)+(Cy\&H100) EndIf EndIf EndIf L=L\1000 Next I 'Was there an overflow If (BCDC(3)>&HFF) Then Error "OverFlow" EndIf End Sub '============================================== ' ' General purpose way to move BCD data between source and dest ' arrays. NOT as fast as doing a direct copy using a FOR loop. ' Sub CopyBCD(Src,Dest,NumDigits) Local I If NumDigits=0 Then NumDigits=7 For I=0 To (NumDigits * 4)-1 Poke VAR Dest,I,Peek(VAR Src,I) Next I End Sub '============================================= Roll: StepCHANGE IRETURN '=========================================== 'Here on Step Switch click Sub StepChange CurStepIndex=CurStepIndex+1 'Roll round CurStepIndex if greater than 3 CurStepIndex=CurStepIndex Mod 4 'Load the CurStepTW with the Step in TW LoadBCD CurStepTW(0),StepTWord$(CurStepIndex) 'Could display something on the LCD to indicate New Step in force 'eg Underline digit. End Sub |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
@boss I pasted your source into MMEdit, but line 218 says Cy=0, and also the step sizes in the code you posted seem to be the same as the original, ie 'Array of Possible Step Sizes in Hz Dim StepHz(4) StepHz(0)=1 StepHz(1)=10 StepHz(2)=100 StepHz(3)=1000 If you need larger step sizes then proceed as follows 1) Increase the size of the StepHz array, eg Dim StepHz(6) StepHz(0)=1 StepHz(1)=10 StepHz(2)=100 StepHz(3)=1000 StepHz(4)=10000 StepHZ(5)=100000 2) Increase the number of Step Tuning Words, eg 'What to Add/Subtract from the Scaled tuning Word 'for each Encoder Up/Down Dim StepTWord$(6) Length 24 'These TuningWord sizes were calculated using Double precision arithmetic 'in VB6, using 2^32/125.0 ' StepTWord$(0)="34359738" '1Hz 34359738.368 StepTWord$(1)="343597384" '10Hz 343597383.68 StepTWord$(2)="3435973837" '100Hz 3435973836.8 StepTWord$(3)="34359738368" '1000Hz 34359738368 StepTWord$(4)="343597383680" '10000Hz 343597383680 StepTWord$(5)="3435973836800" '100000Hz 3435973836800 3) In SUB StepChange make the modification to accommodate the extra step sizes, eg Sub StepChange CurStepIndex=CurStepIndex+1 'Roll round CurStepIndex if greater than 5 CurStepIndex=CurStepIndex Mod 6 'Load the CurStepTW with the Step in TW LoadBCD CurStepTW(0),StepTWord$(CurStepIndex) 'Could display something on the LCD to indicate New Step in force 'eg Underline digit. End Sub $) Also you could change - simply for neatness line 98 SETPIN RSW, INTL, Roll TO Line 98 SETPIN RSW, INTL, StepChange and delete '============================================= Roll: StepCHANGE IRETURN Did U try the Gearing/Ratio suggestion in my last post ? That should do it, the frequency should never be longer than 8 digits when using an AD9850, the highest frequency the AD9850 can generate with a 125 MHz clock is 125/2=62.5 MHz clock, and with these eBay modules anything above 30 MHz is not really worth using. Unfortunately I've partially dismantled my AD9850Controller setup, so I can't test the code right now. But, as soon as I get the chance I will reassemble the test-rig and check out larger step-sizes. OK ? Peter The only Konstant is Change |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
@boss I found a bug with 100,000 Hz step change when decrementing the frequency which gives rise to the error message you reported. I will have to take a look, and hopefully will be able to send out a fix soon. Thanks for helping debug the code 73 Peter The only Konstant is Change |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
@boss I think I've found the bug. I've tested with an INC/DEC of 100,000 Hz and it seems to work correctly now. The fix comprises of replacing Sub IncFreq and Sub DecFreq with these newer versions '========================================================= 'Increment CurFreq() by Inc ' ' SUB IncCurFreq(Inc) LOCAL I,Cy Cy=Inc FOR I=0 TO 3 CurFreq(I)=CurFreq(I) + Cy IF CurFreq(I)<1000 THEN EXIT FOR ELSE Cy=CurFreq(I)\1000 CurFreq(I)=CurFreq(I) MOD 1000 ENDIF NEXT I END SUB '========================================================= 'Decrement CurFreq() by Dec ' ' SUB DecCurFreq(Dec) LOCAL I, Bw Bw=Dec FOR I=0 TO 3 CurFreq(I)=CurFreq(I)+1000 - BW BW=1 - (CurFreq(I) \ 1000) CurFreq(I)=CurFreq(I) MOD 1000 NEXT I END SUB Please let me know how it goes. I will repost the new version of AD9850Controller.bas when you report back that all is good ! Many thanks for reporting the bug Peter The only Konstant is Change |
||||
boss![]() Senior Member ![]() Joined: 19/08/2011 Location: CanadaPosts: 268 |
Hi Peter, I just came back from Church. Thank you very much for the code. I'll test it and let you know. Regards bo |
||||
boss![]() Senior Member ![]() Joined: 19/08/2011 Location: CanadaPosts: 268 |
Hi Peter, yes the code is working like a charm, big thanks for helping me. Only what I didn't change was that: line 98 SETPIN RSW, INTL, Roll TO Line 98 SETPIN RSW, INTL, StepChange and delete '============================================= Roll: StepCHANGE IRETURN I think you must leave an interrupt routine via IRETURN. Otherwise next interrupt service cannot be processed. Am I wrong? Regards bo |
||||
boss![]() Senior Member ![]() Joined: 19/08/2011 Location: CanadaPosts: 268 |
Hi Peter, I spent most of this hot Sunday afternoon testing the code. First conclusion: this small Microchip buddy looks to be heavily overloaded. That's because you generate a new tuning word with every encoder single pulse. Well, and when my encoder sends 128pulses/revolution, the code must generate 128 tuning words in less than (maybe) second. Poor small chip is not powerful enough for such a heavy load. I suggest to wait at least 100ms or more and just count the INC/DEC pulses. When that waiting period expires, then it is the right time to generate a fresh tuning word. The AD9850_A4 code is enclosed as ZIP file. Regards Bo |
||||
boss![]() Senior Member ![]() Joined: 19/08/2011 Location: CanadaPosts: 268 |
Ooopps, there is the missing 2014-07-13_230532_AD9850_A4.zip zip file. |
||||
Page 1 of 2 ![]() ![]() |
![]() |
![]() |
The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2025 |