![]() |
Forum Index : Microcontroller and PC projects : [Micromite]KY040 Rotary Encoder Module.
![]() ![]() |
|||||
Author | Message | ||||
boss![]() Senior Member ![]() Joined: 19/08/2011 Location: CanadaPosts: 268 |
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 KingdomPosts: 676 |
@boss Regarding Interrupts, according to Page 26 of the Micromite User Manual, 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: CanadaPosts: 268 |
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 KingdomPosts: 3998 |
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 KingdomPosts: 676 |
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: CanadaPosts: 268 |
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 KingdomPosts: 676 |
@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: CanadaPosts: 268 |
@Peter, yah, sounds good I think this code should work.I'm ready for testing... boss |
||||
JohnS Guru ![]() Joined: 18/11/2011 Location: United KingdomPosts: 3998 |
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? John |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
@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: ThailandPosts: 2209 |
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. Microblocks. Build with logic. |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
@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 KingdomPosts: 676 |
@viscomjim Thanks for the code 73 Peter The only Konstant is Change |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
@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: AustraliaPosts: 118 |
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 KingdomPosts: 3998 |
Er, same answer (& questions) as in the AD9850 thread I think! John |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
@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 |
||||
![]() ![]() |
![]() |
![]() |
The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2025 |