![]() |
Forum Index : Microcontroller and PC projects : [Micromite]Rotary Encoder
Author | Message | ||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
Hi MMBasic makes life so much easier on MCUs ! I had a rotary encoder working within 20 minutes of getting the encoder out of my junk-box following the instructions on Page 22 of the "Micromite User Manual". However as it says, the code snippet is only sufficient for a basic demonstration of the principle. I'm using the rotary encoder with the Micromite to drive a DDS based VFO and so dropped/skipped values would be a real pain, hence the code below. The key benefit is that the main value is incremented/decremented at the rate at which the MAIN program loop can react to. If one uses the VAR Value directly you will see many skips in the value displayed/printed out from the Main loop, whereas the method used below should near enough eliminate such skips. I'd be very interested in other people's experiences/code for handling Rotary Encoders. 73 Peter (GM8JCF) '*********************************************** 'Rotary Encoder Test (c) Peter Carnegie 2014 'Wiring as per Page 22 Micromite User Manual 'Use LCD wired as follows (see Page 19) '26=D7, 25=D6, 24=d5, 23=d4 '22=RS, 21=EN 'If NO LCD, then REM out lines starting with "LCD" ' 'This software improves on the snippet listed 'in the Micromite User Manual in that the count 'increases/decreases more accurately '*********************************************** 'Declare all Global Variables here 'Rotary Encoder Input pins - see page 22 Dim RA,RB RA=2 RB=3 SetPin RB, Din SetPin RA, INTH, RInt 'Variable incremented/decremented by the Interrupt handling 'routine for the Rotary Encoder Dim Value 'Go run the Program Main End Sub Main Local LastValue Local DispValue, LastDispValue Local DispLen Local A$, B$ 'Initialise the LCD Display DispLen=16 '26=D7, 25=D6, 24=d5, 23=d4 '22=RS, 21=EN lcd init 26,25,24,23,22,21 A$="Rotary Enc Test" LCD 1, (dispLen-Len(A$) )/2, A$ A$="Rotate Encoder" LCD 2, (dispLen-Len(A$) )/2, A$ 'The Main Program Loop Do 'If encoder is turning ANTI-clockwise, Decrement If Value < LastValue then DispValue=DispValue-1 Endif 'If encoder is turning CLOCKwise, Increment If Value > LastValue then DispValue=DispValue+1 Endif 'Was there a Change in DispValue ? If LastDispValue <> DispValue then LastValue=Value A$=Str$(DispValue) 'Find out how much space padding is required 'Before and After the digits B$=String$((Displen-Len(A$))/2," ") 'Make the Display String A$=B$ + A$ + B$ 'Send to the Console print A$ 'Display on the LCD LCD 2, 1, A$ LastDispValue=DispValue Else 'Nothing to do so wait Pause 100 endif Loop End Sub '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 The only Konstant is Change |
||||
MicroBlocks![]() Guru ![]() Joined: 12/05/2012 Location: ThailandPosts: 2209 |
Nice way of smoothing out those values! Just one remark for your use of the LCD command. You can use a C16 (or C8, C20, C40 depending which lcd you have) as the second argument for the LCD command to center a tekst. Microblocks. Build with logic. |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
Hi TZ Indeed, but when I did that I had remnants left on the display as the number went from say 100 to 99, the 1 would remain on the LCD, or from -1 to 1, that's why I send an entire 16 char string to the LCD so that every char position is rewritten every time Reading through Page 22 again, I notice that Geoff credits you with the code snippet on P22 - I feel a little abashed now ! 73 Peter - GM8JCF The only Konstant is Change |
||||
MicroBlocks![]() Guru ![]() Joined: 12/05/2012 Location: ThailandPosts: 2209 |
:) Read this thread to see how it all started. ![]() http://www.thebackshed.com/forum/forum_posts.asp?TID=6342&KW =rotary+encoder&PN=0&TPN=1 My preference is to use a small cap to debounce the signal, especially with cheaper encoders this can become necessary. A small test in the interrupt routine to check the time between the last and current interrupt can also be used to debounce and you can use it also to determine how fast someone turns the encoder. Faster = bigger steps, slower is higher precision. I mostly need to up/down count in the interrupt routine to capture rapid turns. Works best when you need to select from a wide range. It allows the user to quickly go through the values. It all depends on what you need. Microblocks. Build with logic. |
||||
TassyJim![]() Guru ![]() Joined: 07/08/2011 Location: AustraliaPosts: 6220 |
Peter, What DDS are you using? I have a AD9850 DDS in the to-do pile but need time to play with it. Jim VK7JH MMedit |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
Hi Jim I'm using an AD9850 - one of those Chinese cheap modules from eBay, and also NJQRP DDS30 module. BTW, that's why I need long precision arithmetic, as you know Fout=TuningWord x (CLKIn/ (2^32) or TuningWord=Fout * (2^32) / Clkin which in the case for the eBay modules is TuningWord=Fout * (2^32)/125,000,000 = 34.359738368 in double precision. If I use MMBasic Single arithmetic, then I get significant errors when setting a specific frequency. So what I've done is scale everything up by 1E6, do the calcs in long precision integer arithmetic, and just before sending the TuningWord out to the DDS chip, scale the result back by 1E6. If you like, I can directly email you a copy of my MMBasic source. The only problem I'm having right now, is that the program randomly stops with "Error: Too many nested FOR loops" and I can assure you that I don't nest FOR loops more than 3 deep anywhere. I've got it less prone to crashing by moving variables from LOCAL to GLOBAL, but that's making the program more and more "mucky". BTW if I stub out the actual H/W lines, then I can run the code on MMBasic for DOS and there's no crashing out. I think this must have to do with there being a limit on the stack frame for subroutines/functions. 73 Peter - GM8JCF The only Konstant is Change |
||||
JohnS Guru ![]() Joined: 18/11/2011 Location: United KingdomPosts: 3998 |
If you post the actual code that gives the error (so anyone can run it and see) there's a fair chance someone will explain why. John |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
Hi JohnS I've completed extensive tests, and since I've eliminated ALL "EXIT FOR" statements from my code, (800+ lines) I no longer get "Too Many Nested For Loops" Error. What I did was replace each EXIT FOR statement by setting the loop variable to 1+ or -1 the loop termination value depending on the direction of the step. EG For I = 1 To Len(A$) If Mid$(A$, I, 1) <> "0" Then L = I I=Len(A$)+1 'Exit For EndIf Next I AND For I = 11 To 0 Step -1 If A1(I) <> 0 Then T = I I=-1 'Exit For EndIf Next I Perhaps I've spoken too soon, but I have done the same with another uMite program and that too no longer generates "Too Many Nested For Loops" Error now. So hopefully this is a good fix until something more permanent turns up. Do you know how to send this kind of report to Geoff ? Hope this info may help someone else. 73 Peter - GM8JCF The only Konstant is Change |
||||
Geoffg![]() Guru ![]() Joined: 06/06/2011 Location: AustraliaPosts: 3269 |
If you can post a short segment of code which demonstrates the error I will work on it when I get back from overseas (mid August). BUT you must condense it down to a few lines - too many times people send me their whole program which makes it impossible to discover the alleged bug. The other thing that often happens when they try to demonstrate the error with a short code segment is that they discover that the problem was in their programming, not MMBasic. Geoff Geoff Graham - http://geoffg.net |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
Hi Geoff No worries. I'll try and write a test suite which demonstrates the problem on a consistent basis. As I reported, I never get this problem with MMBasic for DOS. And the "fix" seems to work for me thus far, so I can carry on programming on the uMite with minimal impact. It could of course be my error, hence why a test suite will be required, and I shall do my best to write one. Hopefully you're having a well earned rest. Take care Peter - GM8JCF The only Konstant is Change |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
Hi The trick to avoiding the "Too Many Nested For Loops" Error seems to be to NOT use EXIT FUNCTION from inside a FOR Loop, having made that change I no longer get this error - see example below. Previous, failing Code '==================================== ' 'Remove leading CHARacters from a string 'If char$ is ommitted or blank then trim ZEROES ' Function TrimL$(A$, Char$) Local I, CH$(1) Length 1 TrimL$ = A$ ch$(1) = Left$(Char$, 1) If ch$(1) = "" Then ch$(1) = "0" For I = 1 To Len(A$) If Mid$(A$, I, 1) <> "0" Then TrimL$ = Mid$(A$, I) Exit Function EndIf Next I End Function Now, Working Code '==================================== ' 'Remove leading CHARacters from a string 'If char$ is ommitted or blank then trim ZEROES ' Function TrimL$(A$, Char$) Local I, CH$(1) Length 1 TrimL$ = A$ ch$(1) = Left$(Char$, 1) If ch$(1) = "" Then ch$(1) = "0" For I = 1 To Len(A$) If Mid$(A$, I, 1) <> "0" Then TrimL$ = Mid$(A$, I) Exit For EndIf Next I End Function 73 Peter The only Konstant is Change |
||||
Geoffg![]() Guru ![]() Joined: 06/06/2011 Location: AustraliaPosts: 3269 |
Ah, much appreciated Peter. Thanks to your great detective work I can see where the bug crept in. I will add it to the todo list for the next release. Geoff Geoff Graham - http://geoffg.net |
||||
paceman Guru ![]() Joined: 07/10/2011 Location: AustraliaPosts: 1329 |
Well I guess that's consistent with the Manual Peter, the EXIT is within a FOR first, or do you still find a problem with EXITs. Something for us to watch though isn't it - easy to miss. Another simple method I use of lining up the LCD display and eliminating 'old' characters is to just be consistent with how you write the new display lines in the code and include any leading/trailing spaces into each new line. It's quick to code, runs fast, gives you instant feedback if it's too long and it's pretty obvious when things aren't displayed right. Greg Edit: Geoff got in ahead of me there - looks there is still a problem with EXIT FOR. |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
@Geoff Glad I can help. Thanks for such a great tool BTW. @paceman I had expected that an EXIT Function/Sub would clean up all of the local stackframe and hence why it shouldn't matter where an Exit Function/Sub is issued. But GeoffG has found a bug. Anyway, as you say, if it's a For loop which is to be exited, then EXIT FOR is logically the correct thing to do, and from a structured programming perspective it is perhaps the right thing since control is being transferred from the current scope to the next outer level of scope rather than to a completely different scope entirely. I think I've just been spoiled by VB ! 73 Peter The only Konstant is Change |
||||
paceman Guru ![]() Joined: 07/10/2011 Location: AustraliaPosts: 1329 |
Yes but I don't think I've seen this actually spelled out in the Manual though so maybe it should be. Something like:"Using EXIT FUNCTION/SUB within a DO/FOR loop which is inside the FUNCTION/SUB, does/does not, automatically exit the program from the FUNCTION/SUB" I know I've 'done the wrong thing' with this too in the past and gotten away with it but maybe that was just dumb luck with the way the code went. Greg |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
Hi Greg I think the key thing here is that one expects that when one exits a Sub/Function, all the local variables, local control structures, IF/ENDIFs etc would all be cleaned up. If Instead of doing Exit For, one was to do Goto FunctionEnd, eg Function TrimL$(A$, Char$) Local I, CH$(1) Length 1 TrimL$=A$ ch$(1) = Left$(Char$, 1) If ch$(1) = "" Then ch$(1) = "0" For I = 1 To Len(A$) If Mid$(A$, I, 1) <> "0" Then TrimL$ = Mid$(A$, I) Goto FunctionEnd EndIf Next I FunctionEnd: End Function would it not be unreasonable to expect that all the Local Vars disappear, and that the FOR loop and IF/ENDIF structure were all cleaned up ? Anyway, Geoff is going to fix it in the next release which is great news and minimises the potential for days of head scratching ! 73 Peter The only Konstant is Change |
||||
paceman Guru ![]() Joined: 07/10/2011 Location: AustraliaPosts: 1329 |
Interesting that you give that GOTO example because I think that's exactly the sort of thing I've done and found it was OK - mind you I may have only had a few loops so the "too many..." error might not have triggered. The local variables are released of course as is given in the Manual so guess it'll likely be just the EXIT FUNCTION/SUB where Geoff sees an issue. As you say, I think we can safely lave it with him ![]() Greg |
||||
JohnS Guru ![]() Joined: 18/11/2011 Location: United KingdomPosts: 3998 |
Peter - in case you'd not noticed, your code's comment does not match what the code does. This line: If Mid$(A$, I, 1) <> "0" Then looks like it ought to be: If Mid$(A$, I, 1) <> ch$(1) Then John |
||||
G8JCF![]() Guru ![]() Joined: 15/05/2014 Location: United KingdomPosts: 676 |
Thank you John, yes indeed, I've only been Trimming out leading Zeroes so I never got caught by that bug. Once again, thanks Peter The only Konstant is Change |
||||
JohnS Guru ![]() Joined: 18/11/2011 Location: United KingdomPosts: 3998 |
You're welcome. Looks like you found a bug with Exit For, and that's a much more valuable find than my little suggestion to you. John |
||||
![]() |
![]() |
The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2025 |