Home
JAQForum Ver 20.06
Log In or Join  
Active Topics
Local Time 03:55 26 Apr 2024 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 2 of 2    
Author Message
boss

Senior Member

Joined: 19/08/2011
Location: Canada
Posts: 268
Posted: 03:56pm 13 Jul 2014
Copy link to clipboard 
Print this post

Hi,

this is the best working version for today, but still far from perfect, I have to consult with Peter how to setup upper and lower limits and a lot other things.




2014-07-14_015112_AD9850_A4.zip

boss
 
G8JCF

Guru

Joined: 15/05/2014
Location: United Kingdom
Posts: 676
Posted: 09:06pm 13 Jul 2014
Copy link to clipboard 
Print this post

@boss

Regarding Interrupts, according to Page 26 of the Micromite User Manual,
  Quote  Return from an interrupt is
via the IRETURN statement except where a user defined subroutine is used (in that case END SUB or EXIT
SUB is used).

I have successfully replaced LABEL:....IRETURN with SUB LABEL....END SUB for all my interrupt routines and they all seem to work just as before.

Regarding the speed, yes indeed that is a problem with high PPR encoders. U could try the following in MAIN.

Change

IF Change<0 THEN
DecCurFreq(StepHz(CurStepIndex))
ELSE
IncCurFreq(StepHz(CurStepIndex))
ENDIF

TO

IF Change<0 THEN
DecCurFreq(StepHz(CurStepIndex) * abs(Change))
ELSE
IncCurFreq(StepHz(CurStepIndex) * abs(Change))
ENDIF

AND change
'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

TO

'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)*abs(Change))+Cy THEN
RawTword(I)=RawTword(I)-(CurStepTW(I)*abs(Change)) - Cy
Cy=0
ELSE
RawTword(I)=RawTword(I)+1000 - (CurStepTW(I)*abs(Change)) - Cy
Cy=1
ENDIF
NEXT I
ELSE
'Increment
Cy=0
FOR I=0 TO 7
RawTword(I)=RawTword(I) + (CurStepTW(I)*abs(Change)) + Cy
IF RawTword(I)>999 THEN
Cy=RawTword(I)\1000
RawTword(I)=RawTword(I) MOD 1000
ELSE
Cy=0
ENDIF
NEXT I
ENDIF


What those changes do is capture all the Encoder increments/decrements which have occurred during the main loop execution. Now what this will mean is the frequency will jump I would have thought, but it's worth trying. I've got some 128 PPR optical encoders coming, so hopefully next week I can put in some serious study on this matter.

The bottom line is because there is no native Double floating point arithmetic or 64 bit integer arithmetic, I've had to implement my own extended precision arithmetic functions in MMBasic and that's never going to be as quick as proper compiled code unfortunately. But MMBasic makes up for that in so many other ways.

73

Peter


The only Konstant is Change
 
boss

Senior Member

Joined: 19/08/2011
Location: Canada
Posts: 268
Posted: 05:08am 14 Jul 2014
Copy link to clipboard 
Print this post

Hi Peter,

thank you for your help. I agree that main problem is nonexistent 32bit Integer arithmetic. The BCD arithmetic you wrote in Basic is slow and consuming a lot of chip resources. I personally begged Geoff many times to add at least 32bit Integer. There is rumor that he is that he already started work on 32/64 Integer. For work with 128/256 encoder the best solution is to have two modes (coarse counting every pulse and fine counting every 2nd or 4th pulse). I'm using for most project optical encoders because durability, accuracy (no false pulses)and life span (~10M revolutions). Crucial for DDS project will be the information when Integer arithmetic will be available. I'll test the code this evening and let you know.

Btw. on eBay is Chinese AD9959 board (4Channels up to 200MHz) around US$ 100,

Regards
boss
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 3655
Posted: 05:38am 14 Jul 2014
Copy link to clipboard 
Print this post

It's not necessary (or desirable if you want speed!) to use BCD strings to get bigger integers.

If faster code is wanted, have a look on the net about such things and adapt some code, probably from C, and it'll run a LOT faster.

It's great to have almost arbitrary length via BCD strings, but as usual it's also a trade-off against speed...

John
 
G8JCF

Guru

Joined: 15/05/2014
Location: United Kingdom
Posts: 676
Posted: 07:59am 14 Jul 2014
Copy link to clipboard 
Print this post

I spent many weeks trying to find an extended precision library in C/Pascal and even Assembler which I could convert to MMBasic. Unfortunately I was unable to find anything useful. If someone out there could point me at such code I really would be very grateful indeed. The extended precision arithmetic routines in AD9850Controller.bas have been adjusted to suit the needs of calculating the Tuning Word of the AD9850 and no better, eg that's why the Tuning Word calcs are all Adds/Subtracts and require no Muls/Divs. I do have a general purpose BCD arithmetic library which calculates things to 24 decimal digit precision but that's not the same code as is used in AD9850Controller.bas.

The single precision arithmetic in MMBasic offers about 6 digits of precision, which means that sub 100Hz precision above 10MHz is not achievable, eg if one wants 10,000,767 MHz, then using MMBasic single precision arithmetic, the tuning word will be (10000767 * (2^32) / 125000000) = 3.43624e+08, and then using that tuning word to calculate the DDS's Fout, the actual generated frequency will be Fout = 3.43624e+08 * 125E6 / (2^32)= 1.00008e+07 = 10,000,800 Hz ie an error of 23 Hz in this case.

So if one can live with 100Hz or greater steps then it seems to me that the native Single precision arithmetic of MMBasic works well and is plenty fast enough, but if one needs a 'proper' vfo for SSB reception for example, then 10Hz resolution is required, ie 7 digit precision, and 1 Hz precision would be really 'nice', ie 8 digit accuracy (for HF anyway).
The only Konstant is Change
 
boss

Senior Member

Joined: 19/08/2011
Location: Canada
Posts: 268
Posted: 01:04pm 14 Jul 2014
Copy link to clipboard 
Print this post

Hi Peter,

actually this is not your fault you did awesome job. The problem is that MMBasic is locked. I don't know how many people need double precision but for 32bit processor equipped with 32bit FPU and 32 registers it should be standard (together with signed/unsigned 16/32/64 Integer). I was consider to sacrifice accuracy as well but then I realized that even old-fashioned 8bit Atmel processors have full scale of arithmetic available (double & single precision and signed/unsigned Integer 16/32,64 bit (Bascom compiler). I thing we have to ask Geoff if and when at integer arithmetic will be available. Hopefully it will be soon.

I experienced strange behaving |when frequency decrease and step is 10kHz or 100kHz
9993600 |
9993600 |
9993600 <----------------------
9993600 |
9894-400 |
9794-400 <- --------------------
9694-400
9594-400
9494-400
9394-400
9493600
9593600
9693600
9793600
9694-400
9793600
9893600
9993600
10093600
10193600
10293600
10393600
[227] CurFreq$(1)=CurFreq2Asc$()
Error: String too long

This is before last upgrade
The latest upgrade contains a minor bug as well and I'll try to fix it later today or tomorrow.

Regards
bo
 
G8JCF

Guru

Joined: 15/05/2014
Location: United Kingdom
Posts: 676
Posted: 01:11pm 14 Jul 2014
Copy link to clipboard 
Print this post

@boss

I've been experimenting quite a bit here with AD9850Controller code, and using MMBasic SINGLE precision arithmetic on a uMite to calculate the tuning word for step sizes of greater than or equal to 100Hz, I can get the Sub Main loop time down to around 50 mS which is better than the BCD extended precision loop time.

What is interesting is that the actual calculations of the Tuning Word and the displayed frequency and sending the tuning word to the DDS chip take about 28 mS, and the display of the frequency on the console and LCD takes about 22 mS !

Anyway, more tomorrow, and hopefully I will release the new code as well.

73

Peter
The only Konstant is Change
 
boss

Senior Member

Joined: 19/08/2011
Location: Canada
Posts: 268
Posted: 06:37pm 14 Jul 2014
Copy link to clipboard 
Print this post


@Peter,

yah, sounds good I think this code should work.I'm ready for testing...

boss
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 3655
Posted: 11:36pm 14 Jul 2014
Copy link to clipboard 
Print this post

  G8JCF said   I spent many weeks trying to find an extended precision library in C/Pascal and even Assembler which I could convert to MMBasic. Unfortunately I was unable to find anything useful. If someone out there could point me at such code I really would be very grateful indeed. The extended precision arithmetic routines in AD9850Controller.bas have been adjusted to suit the needs of calculating the Tuning Word of the AD9850 and no better, eg that's why the Tuning Word calcs are all Adds/Subtracts and require no Muls/Divs. I do have a general purpose BCD arithmetic library which calculates things to 24 decimal digit precision but that's not the same code as is used in AD9850Controller.bas.

The single precision arithmetic in MMBasic offers about 6 digits of precision, which means that sub 100Hz precision above 10MHz is not achievable, eg if one wants 10,000,767 MHz, then using MMBasic single precision arithmetic, the tuning word will be (10000767 * (2^32) / 125000000) = 3.43624e+08, and then using that tuning word to calculate the DDS's Fout, the actual generated frequency will be Fout = 3.43624e+08 * 125E6 / (2^32)= 1.00008e+07 = 10,000,800 Hz ie an error of 23 Hz in this case.

So if one can live with 100Hz or greater steps then it seems to me that the native Single precision arithmetic of MMBasic works well and is plenty fast enough, but if one needs a 'proper' vfo for SSB reception for example, then 10Hz resolution is required, ie 7 digit precision, and 1 Hz precision would be really 'nice', ie 8 digit accuracy (for HF anyway).


I expect you found things like GMP and on wiki but good though GMP et al are they're overkill for the few precisions and mathematical operations I gather you want.

For just 32-bit where MMBasic provides nearly that, 2 of what is provided (since it's more than 16 bits) will do. And for just add & subtract then short code looks doable. (Multiply is not much more pain. Divide is more work still.)

However, if by 32-bit or 64-bit one means not integer but some kind of fp (floating point) then to go to fixed point means you have to look at the range of values and then the number of MMBasic variables (numeric storage places) will increase.

For integers, 32-bit is just 2 variables, 64-bit needs 3.

Whether to use a power of 2 or 10 per variable - see how the code looks. It'll be easier to print if a power of 10 is used.

An advantage of just 2 variables (also somewhat true of 3) is that for add/subtract I don't think you'd need any loops.

I'd try code using powers of 10, and try the largest that will fit in an MMbasic variable without loss of precision. I am disappointed that this is only 1E7 aka 10000000 (because of MMBasic's 16777100). If multiply is wanted, see below.

The snag of wanting Geoff to add more to MMBasic is it'll complicate a lot of things and the flash memory size will tend to balloon as will the RAM needed to store program variables. Both flash & RAM look OK on the PIC32MZ but on the MX are in short supply on many of the chips. (There will also probably be bugs for a while due to not converting data internally in every instance.)

To do multiply (let alone divide) and yet not balloon the internal complexity and size I suggest having a tiny number of helper functions written in C. The reason is that otherwise the very restricted precision (23 bits, because 16777100 is less than 2^24) is hard to use efficiently. Strategies such as dropping to the square root (realistically that's 2^11 i.e. 2048) so that multiply doesn't overflow are cumbersome and slow (as you know).

I don't know if Geoff is even willing to add the tiny number of helper functions. Design the minimum set and ask?

JohnEdited by JohnS 2014-07-16
 
G8JCF

Guru

Joined: 15/05/2014
Location: United Kingdom
Posts: 676
Posted: 05:15am 15 Jul 2014
Copy link to clipboard 
Print this post

@JohnS

I understand what you're saying, at the moment the BCD numbers are stored with 3 decimal digits per MMBasic variable, ie 10^3, this was done so that Multiply wouldn't overflow and result in scientific notation and loss of precision, ie 999 x 999 = 998001 - 6 digits, if 4 decimal digits were stored per MMBasic variable then 9999 x 9999 = 99980001, - 8 digits, which would be beyond MMBasic's precision.

Clearly for Add and Subtract purposes only, one could easily increase the number of digits per MMBasic variable to 5 digits per MMBasic variable, ie 99999 + 99999 = 199998 avoiding overflow and loss of precision. I'll certainly try upping the number of digits to 5 per MMBasic variable and see how quick that is.

@boss
In the meantime, I have modified the AD9850Controller code to use native SINGLE precision MMBasic arithmetic for step sizes of 100Hz or greater and that does show some promise certainly, although we're now starting to run into other overheads such as the time taken to write to the LCD/Console, eg it takes about 18 mS to write a string to the LCD at 48MHz, try this

CPU 48

LCD INIT 26,25,24,23,22,21

TIMER=L
LCD 1, C16, "10,000,000 MHz"
PRINT TIMER-L

TIMER=L
PRINT "10,000,000 MHz"
PRINT TIMER-L


and 3 mS to write the same string to the console at 38,400 baud. So just displaying the frequency takes about 21 mS. With single precision arithmetic, the calculation time is around 2 mS - 1 Add/Subtract and 1 Multiply, then talking to the AD9850 itself takes around 15 mS, a total transaction time per encoder pulse of around 37 mS.

So using SINGLE precision arithmetic with CPU 48, the "loop" time per encoder pulse is

Calculation 1 mS
AD9850 Comms 15 mS
LCD Comms 18 mS
Console Comms 3 mS
---------------------
Total 37 mS


The Console Comms is not necessary so let's call it 35 mS per encoder pulse, meaning that a 48 MHz uMite could process 1/0.035=28 pulses per second without losing a pulse. A 128 PPR encoder manually turned would generate many more than 28 pulses/second probably on the order of at least 256 pulses/second. Something could be done about the LCD comms, eg update the LCD only 10 times per second, reducing the average "loop" time to 1+15=16mS, doubling the pulse/sec to 56 pulse/sec, but still not enough to cope with a 128/256 PPR encoder. I do think that "gearing/ratio" is required for high pulse rates.

There must be a whole body of work on using encoders for tuning purposes somewhere !!

Anyway, I hope this analysis and ramblings help ! I'll hopefully release the new faster today or tomorrow.

73

Peter

The only Konstant is Change
 
MicroBlocks

Guru

Joined: 12/05/2012
Location: Thailand
Posts: 2209
Posted: 06:15am 15 Jul 2014
Copy link to clipboard 
Print this post

I see that only 1mS in that list is really necessary when turning the rotary encoder.
That would mean you can get about 1000 pulses per second, just to be save halve that to 500 per second.
Updating the AD9850, LCD and serial can be done when the rotary does not pulse for about 100ms.
You can also use the speed that the rotary encoder is turned to set the amount that would be 'counted'. Fast turn larger steps, very slow turn single steps.
Edited by TZAdvantage 2014-07-16
Microblocks. Build with logic.
 
G8JCF

Guru

Joined: 15/05/2014
Location: United Kingdom
Posts: 676
Posted: 06:44am 15 Jul 2014
Copy link to clipboard 
Print this post

@TZ

I think that we still need to update the AD9850 else we will get a sudden jump in frequency. I think that for many applications other than for a Receiver VFO that jump would be tolerable, but for an RX VFO I'm not sure.

It's certainly worth a try of course.

Also, if one wanted to create a sweep generator, then one needs to update the DDS in realtime to generate a smooth sweep, 20 updates/sec may be sufficient, but if one needed to sweep 1 MHz, with a step size of 10KHz that would need 1,000,000 / 10,000=100 points or about 5 seconds, (and I'm not sure that 10KHz would be fine enough granularity, and also not sure that 5 seconds would be acceptable unless one had a DSO or a long persistence analogue 'scope).

Mind you, I'm not complaining, a uMite chip only costs a few bucks, so it's amazing what what can achieve with so little coding - thanks Geoff !!

73

Peter

The only Konstant is Change
 
G8JCF

Guru

Joined: 15/05/2014
Location: United Kingdom
Posts: 676
Posted: 07:22am 17 Jul 2014
Copy link to clipboard 
Print this post

@viscomjim

Thanks for the code

73

Peter
The only Konstant is Change
 
G8JCF

Guru

Joined: 15/05/2014
Location: United Kingdom
Posts: 676
Posted: 02:13pm 17 Jul 2014
Copy link to clipboard 
Print this post

@JohnS

Would it be possible for you to provide us all with a/some code example(s) of what you mean please ?

Many thanks

Peter

The only Konstant is Change
 
Keith W.
Senior Member

Joined: 09/10/2011
Location: Australia
Posts: 118
Posted: 04:05pm 17 Jul 2014
Copy link to clipboard 
Print this post

Hi Peter,

When sweeping 10 KHz is often much too coarse.My sweep system uses a variable for the number of steps. Displaying a quick but course sweep, or up to a maximum 500 steps to draw a fine display. Otherwise it takes too long when allowing time for the measuring analog to digital converter to settle and respond, for me about 30 seconds for 500 steps. Using the selected steps variable the controlling PC progressively calculates the frequencies using a log frequency scale and the sweep range to give equal increments across the log graph.
The PC determines the generator frequency when sweeping. The graph gratical is drawn using a fixed 10 step log table multiplied by the width of the graph decade to position the axis lines.

An inconvenient tradeoff between time and accuracy is that narrow/fine events in the response may be missed, but if expected they may be found by limiting the frequency range of the sweep and using the maximum number of steps.

Keith W.
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 3655
Posted: 02:47am 18 Jul 2014
Copy link to clipboard 
Print this post

  G8JCF said   @JohnS

Would it be possible for you to provide us all with a/some code example(s) of what you mean please ?

Many thanks

Peter

Er, same answer (& questions) as in the AD9850 thread I think!

John
 
G8JCF

Guru

Joined: 15/05/2014
Location: United Kingdom
Posts: 676
Posted: 04:08am 18 Jul 2014
Copy link to clipboard 
Print this post

@KeithW

Thanks for that information. I suppose I was thinking of a wobulator type of sweep displaying the results on my analogue 'scope, this would be for characterising LC filters, and antennae. For IF/Xtal filters a much narrower sweep band with much finer steps, eg 25Hz would suffice. So I guess I'll have to give up on the analogue 'scope idea, and use a PC to graphically display the filter/antenna response, or perhaps one of those graphic LCDs ?

Once again, thanks for information and help

73

Peter
The only Konstant is Change
 
     Page 2 of 2    
Print this page


To reply to this topic, you need to log in.

© JAQ Software 2024