The title says it all.
Sending a uint16 value, a word variable in GCB, using TMHex, space, variable-of-type=word, CR/LF,
displays only xx10, instead of xx10, xx11 . . . xx1D, xx1F.
It sticks at xx10 for 16 counts, then increments xx21, xx22, . . . xx2D, xx2F - xx30, as you would expect.
I've attached the 3 files, gcb, asm, html - but simply, the gcb call is here:
Yes It's sloppy about upper/lower case - this was an earlier test to see if GCB threw errors , or simply and wrongly created a new COUNTER variable as well as counter. The #explicit directive should catch the latter. Turns out GCB is case-insensitive, which is what I prefer.
Anyway, I've looked on the 'scope, and proper I2C commands are generated for all xx1x values, it's just they're all the same, xx10, for any xx1x value.
I don't mean to bang-on about things, but I would like to point out a critical benefit of using GCBASIC versus Arduino... You can trace back from the compiled assembler, to the functions called, and your source that calls them. I'll illustrate this point with the code above.
It means that I can dig-down through the layers, right to the metal, without having to rely on forums and experts.
I've tried to do this with Arduino code, with the #includes that are invoked by #includes - but got 5-levels down with no end in sight, and gave up.
I love the simple, comprehensive approach of GCBASIC, I think it should be mandatory, especially if it has an educational remit - to install and allow best-practise amongst novice programmers.
Firstly there is the html file generated by GCB on compile. This shows what other code has been included through your use of library functions. See "subroutines.png"
I'm interested in TMHex - a function that takes a uint16 (a WORD variable in GCB) and displays it as four hex digits in a 7-segment display. The display is driven by the TM1637 display driver IC.
The html file shows that TMHex is called within my MAIN:
outgoing calls: INITTM1637(3), Delay_MS(1), TMHEX(1), INITSYS(23)
and that, under its own entry, TMHEX, it calls another 3 subroutines:
SYSDIVSUB16(6), SYSCOMPLESSTHAN16(3), TMWRITE4DIG(1)
The GCB code for the TM1637 is easily found in GCBASIC/include/TM1637.h
In that file, we find the following:
subTMHex(InTMValueasword)'TMValue must be in the range of 0 to 65535 (Dec)'TMValue can be entered as dec, binary or hex TMsVal = 0 TMeVal = 0 TMhVal = 0 TMlVal = 0 IF TMValue >= 4096 Then TMsVal = TMValue / 4096 TMValue = TMValue % 4096 End If IF TMValue >= 256 Then TMeVal = TMValue / 256 TMValue = TMValue % 256 End If IF TMValue >= 16 Then TMhVal = TMValue / 16 TMlVal = TMValue % 16 End If Write4Dig ( TMsVal, TMeVal, TMhVal, TMlVal)end sub
I don't see any error in the code above, but at least I have the privilege of review. If I spotted an error, I could rename the TM1637.h file to TM1637_old.h, and patch-in my amended code - in a direct, simple, and reversible manner.
Similarly, I could delve into the Write4Dig function.
Secondly, if I can't find errors here, I can also go all the way down to bare-metal, ie look at the machine-code (assembler) output of the compiler.
The filename.asm file shows exactly what assembler has been generated, in this instance, for every higher-level function called.
See below for the assembler generated by the TMHEX function under investigation...
I'm not saying that you need to go this deep, in the general case, but you have the option.
GCB puts everything it does in front of you.
Arduino doesn't.
They say that a good programmer inspects the assembler that his HLL code generates, against the HLL source. This is simply not possible in Arduino, there is zero opportunity for trace-back, you get a hex image as output, that's all - unless I'm mistaken.
This isn't intended to be a "religious" argument between languages, or on neatest procedure. I'm trying, as a matter of principle, to keep the loop closed - i.e. deterministic and inspectable - between HLL code and machine code.
On a strictly practical note I've used the inspection of assembler to pick-up some naughty things in GCBASIC - like the enabling of interrupts from sources that are not intended, or not yet properly set-up. Small things, I will agree, but nevertheless possible vulnerabilities that can be simply eliminated to make better, more robust code.
I dug around in the old used proto boards and found a pic16F886 to put your program in. Went to the new module box and got a new TM1637. Soldered a couple pins in and hooked the 4 jumper wires up. Put the program in and
it didn't work but differently than you reported. i had the 'b' segment stuck off but otherwise it was counting just fine even over '9'.
Had a bad TM1637 board.
Hooked up another new TM1637 board and everything works fine. I don't think it is a program issue.
Have you tried TMDec instead of TMHex? does it count right?
The extra writes are for your oscope?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Thanks Mike, sounds like you have the bug squarely in your sights.
The TMHex write every 100ms is just to get a regular 'scope trace. The code above is a stripped-down test example. The real source code has an interesting new rotary-encoder decode algorithm, running on interrupts - which would complicate the bug investigation.
I intend to present that code under a new thread, once I'm sure it works under all circumstances.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
So the error was when the counter would have single digit in the low order byte of the display.
example : 05FE,05FF,0600,0600,0600,0600......0600,0610,0611
sub TMHex ( In TMValue as word)
'TMValue must be in the range of 0 to 65535 (Dec)
'TMValue can be entered as dec, binary or hex
TMsVal = 0
TMeVal = 0
TMhVal = 0
TMlVal = 0
IF TMValue >= 4096 Then
TMsVal = TMValue / 4096
TMValue = TMValue % 4096
End If
IF TMValue >= 256 Then
TMeVal = TMValue / 256
TMValue = TMValue % 256
End If
IF TMValue >= 16 Then
TMhVal = TMValue / 16
TMlVal = TMValue % 16
End If
TMWrite4Dig ( TMsVal, TMeVal, TMhVal, TMlVal,TM_Bright,0)
end sub
So it is never executing the less than 16 values.
Solution is the take out the last IF.
sub TMHex ( In TMValue as word)
'TMValue must be in the range of 0 to 65535 (Dec)
'TMValue can be entered as dec, binary or hex
TMsVal = 0
TMeVal = 0
TMhVal = 0
TMlVal = 0
IF TMValue >= 4096 Then
TMsVal = TMValue / 4096
TMValue = TMValue % 4096
End If
IF TMValue >= 256 Then
TMeVal = TMValue / 256
TMValue = TMValue % 256
End If
TMhVal = TMValue / 16
TMlVal = TMValue % 16
TMWrite4Dig ( TMsVal, TMeVal, TMhVal, TMlVal,TM_Bright,0)
end sub
This seems to work fine!
the difference between TM1637.h and TM1637a.h is that TM1637a.h has an addition charset for Seikoo. This makes it slightly bigger.
IT WORKS!!
Excellent.
I've tested up and down the number range, using my original code which decodes a rotary-encoder.
I'm planning a post on this, it's a new method - as yet not fully verified - but early code is attached.
It relies on switching interrupts on and off, for the "A" and "B" signals from any incremental rotary encoder - such as a mouse scroll-wheel, an easy 3-wire hack using one dead mouse.
Configuring the interrupts, their flags, and their enable bits, is done with a few direct writes to PIC registers, if you use GCB commands (i.e. not assembler) then GCB will sort out RAMBANK paging for you - I strongly recommend this approach.
I'm using the "interrupt-on-change" on my target PIC16F616 - many PICs have this feature. If you need to do it differently then do it carefully.
It's dead-handy to have a wheel that you can spin to produce any value of variable within a GCB program, and to have a simple TM1637 display to confirm the 0-FFFF value of that variable.
The various files are attached - use the new TM1637 files if you want to avoid the OP bug described above. I've only tested TM1637.h, it's the only function I call - i think... - but I assume the TM1637a.h file is similarly fixed.
The encoder is wired to the pins PORTA.sinbit and PORTA.cosbit. It really does not matter if they're swapped or inverted, swapping reverses the direction, and any inversion reverses also - but all configurations will count sensibly.
The title says it all.
Sending a uint16 value, a word variable in GCB, using TMHex, space, variable-of-type=word, CR/LF,
displays only xx10, instead of xx10, xx11 . . . xx1D, xx1F.
It sticks at xx10 for 16 counts, then increments xx21, xx22, . . . xx2D, xx2F - xx30, as you would expect.
I've attached the 3 files, gcb, asm, html - but simply, the gcb call is here:
Yes It's sloppy about upper/lower case - this was an earlier test to see if GCB threw errors , or simply and wrongly created a new COUNTER variable as well as counter. The #explicit directive should catch the latter. Turns out GCB is case-insensitive, which is what I prefer.
Anyway, I've looked on the 'scope, and proper I2C commands are generated for all xx1x values, it's just they're all the same, xx10, for any xx1x value.
@mmotte should be able to help.
I don't mean to bang-on about things, but I would like to point out a critical benefit of using GCBASIC versus Arduino... You can trace back from the compiled assembler, to the functions called, and your source that calls them. I'll illustrate this point with the code above.
It means that I can dig-down through the layers, right to the metal, without having to rely on forums and experts.
I've tried to do this with Arduino code, with the #includes that are invoked by #includes - but got 5-levels down with no end in sight, and gave up.
I love the simple, comprehensive approach of GCBASIC, I think it should be mandatory, especially if it has an educational remit - to install and allow best-practise amongst novice programmers.
Firstly there is the html file generated by GCB on compile. This shows what other code has been included through your use of library functions. See "subroutines.png"
I'm interested in TMHex - a function that takes a uint16 (a WORD variable in GCB) and displays it as four hex digits in a 7-segment display. The display is driven by the TM1637 display driver IC.
The html file shows that TMHex is called within my MAIN:
outgoing calls: INITTM1637(3), Delay_MS(1), TMHEX(1), INITSYS(23)
and that, under its own entry, TMHEX, it calls another 3 subroutines:
SYSDIVSUB16(6), SYSCOMPLESSTHAN16(3), TMWRITE4DIG(1)
The GCB code for the TM1637 is easily found in GCBASIC/include/TM1637.h
In that file, we find the following:
I don't see any error in the code above, but at least I have the privilege of review. If I spotted an error, I could rename the TM1637.h file to TM1637_old.h, and patch-in my amended code - in a direct, simple, and reversible manner.
Similarly, I could delve into the Write4Dig function.
Secondly, if I can't find errors here, I can also go all the way down to bare-metal, ie look at the machine-code (assembler) output of the compiler.
The filename.asm file shows exactly what assembler has been generated, in this instance, for every higher-level function called.
See below for the assembler generated by the TMHEX function under investigation...
I'm not saying that you need to go this deep, in the general case, but you have the option.
GCB puts everything it does in front of you.
Arduino doesn't.
They say that a good programmer inspects the assembler that his HLL code generates, against the HLL source. This is simply not possible in Arduino, there is zero opportunity for trace-back, you get a hex image as output, that's all - unless I'm mistaken.
This isn't intended to be a "religious" argument between languages, or on neatest procedure. I'm trying, as a matter of principle, to keep the loop closed - i.e. deterministic and inspectable - between HLL code and machine code.
On a strictly practical note I've used the inspection of assembler to pick-up some naughty things in GCBASIC - like the enabling of interrupts from sources that are not intended, or not yet properly set-up. Small things, I will agree, but nevertheless possible vulnerabilities that can be simply eliminated to make better, more robust code.
Your insights are very good. Today, I wrote email that was very similar explaining the various compile outputs that can be used.
Share with me the interrupt insights. We should resolve.
I just compiled the GCB source (above). I am guessing you are using an updated/improved GCB source. Let me know re the interrupts code.
I dug around in the old used proto boards and found a pic16F886 to put your program in. Went to the new module box and got a new TM1637. Soldered a couple pins in and hooked the 4 jumper wires up. Put the program in and
it didn't work but differently than you reported. i had the 'b' segment stuck off but otherwise it was counting just fine even over '9'.
Had a bad TM1637 board.
Hooked up another new TM1637 board and everything works fine. I don't think it is a program issue.
Have you tried TMDec instead of TMHex? does it count right?
The extra writes are for your oscope?
update!
I saw an anomaly. when the counter crosses/updates '256's and the lower byte is at '00'.
I'll look at it, later today.
M
Thanks Mike, sounds like you have the bug squarely in your sights.
The TMHex write every 100ms is just to get a regular 'scope trace. The code above is a stripped-down test example. The real source code has an interesting new rotary-encoder decode algorithm, running on interrupts - which would complicate the bug investigation.
I intend to present that code under a new thread, once I'm sure it works under all circumstances.
So the error was when the counter would have single digit in the low order byte of the display.
example : 05FE,05FF,0600,0600,0600,0600......0600,0610,0611
So it is never executing the less than 16 values.
Solution is the take out the last IF.
This seems to work fine!
the difference between TM1637.h and TM1637a.h is that TM1637a.h has an addition charset for Seikoo. This makes it slightly bigger.
Last edit: mmotte 2025-01-12
What should I do re the build? Let me know.
Thanks Mike, good spot.
I will test the new files and report back.
IT WORKS!!
Excellent.
I've tested up and down the number range, using my original code which decodes a rotary-encoder.
I'm planning a post on this, it's a new method - as yet not fully verified - but early code is attached.
It relies on switching interrupts on and off, for the "A" and "B" signals from any incremental rotary encoder - such as a mouse scroll-wheel, an easy 3-wire hack using one dead mouse.
Configuring the interrupts, their flags, and their enable bits, is done with a few direct writes to PIC registers, if you use GCB commands (i.e. not assembler) then GCB will sort out RAMBANK paging for you - I strongly recommend this approach.
I'm using the "interrupt-on-change" on my target PIC16F616 - many PICs have this feature. If you need to do it differently then do it carefully.
It's dead-handy to have a wheel that you can spin to produce any value of variable within a GCB program, and to have a simple TM1637 display to confirm the 0-FFFF value of that variable.
The various files are attached - use the new TM1637 files if you want to avoid the OP bug described above. I've only tested TM1637.h, it's the only function I call - i think... - but I assume the TM1637a.h file is similarly fixed.
The encoder is wired to the pins PORTA.sinbit and PORTA.cosbit. It really does not matter if they're swapped or inverted, swapping reverses the direction, and any inversion reverses also - but all configurations will count sensibly.