Home
JAQForum Ver 20.06
Log In or Join  
Active Topics
Local Time 08:49 26 Jun 2022 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

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 Kingdom
Posts: 676
Posted: 12:24pm 11 Jul 2014
Copy link to clipboard 
Print this post

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: Canada
Posts: 268
Posted: 01:20pm 12 Jul 2014
Copy link to clipboard 
Print this post

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 Kingdom
Posts: 676
Posted: 02:20pm 12 Jul 2014
Copy link to clipboard 
Print this post

@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: Canada
Posts: 268
Posted: 02:37pm 12 Jul 2014
Copy link to clipboard 
Print this post

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 Kingdom
Posts: 676
Posted: 03:10pm 12 Jul 2014
Copy link to clipboard 
Print this post

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: Canada
Posts: 268
Posted: 03:15pm 12 Jul 2014
Copy link to clipboard 
Print this post

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: Australia
Posts: 935
Posted: 03:19pm 12 Jul 2014
Copy link to clipboard 
Print this post

  boss said   Btw. we have a terrible hot day in Vancouver.
Regards
boss

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: Canada
Posts: 268
Posted: 03:25pm 12 Jul 2014
Copy link to clipboard 
Print this post

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: Canada
Posts: 268
Posted: 03:34pm 12 Jul 2014
Copy link to clipboard 
Print this post

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: Canada
Posts: 268
Posted: 04:11pm 12 Jul 2014
Copy link to clipboard 
Print this post

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 States
Posts: 925
Posted: 10:24pm 12 Jul 2014
Copy link to clipboard 
Print this post

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 Kingdom
Posts: 676
Posted: 05:09am 13 Jul 2014
Copy link to clipboard 
Print this post

@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: Canada
Posts: 268
Posted: 05:21am 13 Jul 2014
Copy link to clipboard 
Print this post

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 Kingdom
Posts: 676
Posted: 06:05am 13 Jul 2014
Copy link to clipboard 
Print this post

@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 Kingdom
Posts: 676
Posted: 06:25am 13 Jul 2014
Copy link to clipboard 
Print this post

@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 Kingdom
Posts: 676
Posted: 07:09am 13 Jul 2014
Copy link to clipboard 
Print this post

@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: Canada
Posts: 268
Posted: 08:22am 13 Jul 2014
Copy link to clipboard 
Print this post

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: Canada
Posts: 268
Posted: 08:58am 13 Jul 2014
Copy link to clipboard 
Print this post

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: Canada
Posts: 268
Posted: 01:02pm 13 Jul 2014
Copy link to clipboard 
Print this post

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: Canada
Posts: 268
Posted: 01:06pm 13 Jul 2014
Copy link to clipboard 
Print this post

Ooopps, there is the missing


2014-07-13_230532_AD9850_A4.zip zip file.
 
     Page 1 of 2    
Print this page
© JAQ Software 2022