Menu

TM1637 TMHex command bug, xx1x displays as xx10

Bonkers
2025-01-11
2025-01-12
  • Bonkers

    Bonkers - 2025-01-11

    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:

    MAIN:
    for ICOUNT = 1 to 4
    wait 100 ms     ;to ensure good repeat rate for 'scope
    TMHex counter
    next ICOUNT
    COUNTER = COUNTER + 1 ; increase counter ~2Hz
    
    goto MAIN
    

    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.

     
  • Anobium

    Anobium - 2025-01-11

    @mmotte should be able to help.

     
  • Bonkers

    Bonkers - 2025-01-11

    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:

    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
    
        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...

    ;********************************************************************************
    
    TMHEX
        clrf    TMSVAL
        clrf    TMEVAL
        clrf    TMHVAL
        clrf    TMLVAL
        movf    TMVALUE,W
        movwf   SysWORDTempA
        movf    TMVALUE_H,W
        movwf   SysWORDTempA_H
        clrf    SysWORDTempB
        movlw   16
        movwf   SysWORDTempB_H
        call    SYSCOMPLESSTHAN16
        comf    SysByteTempX,F
        btfss   SysByteTempX,0
        goto    ENDIF2
        movf    TMVALUE,W
        movwf   SysWORDTempA
        movf    TMVALUE_H,W
        movwf   SysWORDTempA_H
        clrf    SysWORDTempB
        movlw   16
        movwf   SysWORDTempB_H
        call    SYSDIVSUB16
        movf    SysWORDTempA,W
        movwf   TMSVAL
        movf    TMVALUE,W
        movwf   SysWORDTempA
        movf    TMVALUE_H,W
        movwf   SysWORDTempA_H
        clrf    SysWORDTempB
        movlw   16
        movwf   SysWORDTempB_H
        call    SYSDIVSUB16
        movf    SysWORDTempX,W
        movwf   TMVALUE
        movf    SysWORDTempX_H,W
        movwf   TMVALUE_H
    ENDIF2
        movf    TMVALUE,W
        movwf   SysWORDTempA
        movf    TMVALUE_H,W
        movwf   SysWORDTempA_H
        clrf    SysWORDTempB
        movlw   1
        movwf   SysWORDTempB_H
        call    SYSCOMPLESSTHAN16
        comf    SysByteTempX,F
        btfss   SysByteTempX,0
        goto    ENDIF3
        movf    TMVALUE,W
        movwf   SysWORDTempA
        movf    TMVALUE_H,W
        movwf   SysWORDTempA_H
        clrf    SysWORDTempB
        movlw   1
        movwf   SysWORDTempB_H
        call    SYSDIVSUB16
        movf    SysWORDTempA,W
        movwf   TMEVAL
        movf    TMVALUE,W
        movwf   SysWORDTempA
        movf    TMVALUE_H,W
        movwf   SysWORDTempA_H
        clrf    SysWORDTempB
        movlw   1
        movwf   SysWORDTempB_H
        call    SYSDIVSUB16
        movf    SysWORDTempX,W
        movwf   TMVALUE
        movf    SysWORDTempX_H,W
        movwf   TMVALUE_H
    ENDIF3
        movf    TMVALUE,W
        movwf   SysWORDTempA
        movf    TMVALUE_H,W
        movwf   SysWORDTempA_H
        movlw   16
        movwf   SysWORDTempB
        clrf    SysWORDTempB_H
        call    SYSCOMPLESSTHAN16
        comf    SysByteTempX,F
        btfss   SysByteTempX,0
        goto    ENDIF4
        movf    TMVALUE,W
        movwf   SysWORDTempA
        movf    TMVALUE_H,W
        movwf   SysWORDTempA_H
        movlw   16
        movwf   SysWORDTempB
        clrf    SysWORDTempB_H
        call    SYSDIVSUB16
        movf    SysWORDTempA,W
        movwf   TMHVAL
        movf    TMVALUE,W
        movwf   SysWORDTempA
        movf    TMVALUE_H,W
        movwf   SysWORDTempA_H
        movlw   16
        movwf   SysWORDTempB
        clrf    SysWORDTempB_H
        call    SYSDIVSUB16
        movf    SysWORDTempX,W
        movwf   TMLVAL
    ENDIF4
        movf    TMSVAL,W
        movwf   TMDIG1
        movf    TMEVAL,W
        movwf   TMDIG2
        movf    TMHVAL,W
        movwf   TMDIG3
        movf    TMLVAL,W
        movwf   TMDIG4
        clrf    COLONON
        goto    TMWRITE4DIG
    
    ;********************************************************************************
    

    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.

     
    ❤️
    1
    • Anobium

      Anobium - 2025-01-11

      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.

       
      • Anobium

        Anobium - 2025-01-12

        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.

         
  • mmotte

    mmotte - 2025-01-12

    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?

     
  • mmotte

    mmotte - 2025-01-12

    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

     
  • Bonkers

    Bonkers - 2025-01-12

    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.

     
  • mmotte

    mmotte - 2025-01-12

    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.

     

    Last edit: mmotte 2025-01-12
  • Anobium

    Anobium - 2025-01-12

    What should I do re the build? Let me know.

     
  • Bonkers

    Bonkers - 2025-01-12

    Thanks Mike, good spot.
    I will test the new files and report back.

     
  • Bonkers

    Bonkers - 2025-01-12

    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.

     
    ❤️
    1

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.