Several months ago, I bought a handfull of TM1638 8-Digit/7-Segment & 16 button keypad modules identified as "HCW-132". They were really cheap and I thought I'd add them to some on-going projects as a quick and dirty input/output interface. I had already done some code development for the TM1638 "LED&KEY" modules (see my earlier post on this forum), so I thought all I would need to do is scrub the code a bit. Both of these modules have identical 3-wire interfaces (data, clock and strobe) plus power & ground, so I thought it would be a plug & play event. But the HCW-132 module displayed nothing but rubbish -- just a bunch of scrambled segments lit up. No documentation was provided with these modules, and when I checked the part numbers printed on the displays, I discovered that the LED&KEY module's displays are common cathode, while the HCW-132's displays are common anode.

To make matters slightly more confusing, the related TM1637 4-digit display module (also a topic of an earlier post of mine) uses a common anode LED (marked as P/N 3461B) where the common anode is connected to the TM1637's GRID pin, while the TM1638 LED&KEY module uses a common cathode LED (marked as P/N 3461A) where the common cathode is connected to the TM1638's GRID pin. So the TM1637 and TM1638 differ by more than just the number of digits that each is capable of driving. The TM1637's GRID pins are normally 0 volts and swing to 5 volts when the corresponding digit is active, while the TM1638's GRID pin is normally 5 volts and swings to 0 volts when the corresponding digit is active. The HCW-132 module uses a common anode LED (the same as the TM1637 display), but because the TM1638's GRID pins are opposite polarity, the HCW-132 has to be sent data structured differently.

While the TM1638's compatibility with either common anode or common cathode 7-segment LED configuration seems useful, there is not (unfortunately) simply an input pin on the TM1638 that can be tied to Vcc or Ground to select the common anode or common cathode mode. The common cathode mode used in the LED&KEY module is simple enough to use. All that's required is to send a 7-segment character code (0x5B for the digit "2", for example) to the LED position address (0xC8 for the fifth position, for example) using a simple 4-byte sequence comprising <data command><display address><digit data><display control>. The complete command would be <0x44><0xC8><0x5B><0x89> in order to make "2" appear at the fifth position.

When the TM1638 is used in the common anode mode (as it is in the HCW-132), the segments and digit positions become transposed. That is, when a data byte is written to an address, the address is one particular segment in each of the 8 7-segment displays. This means that each digit is not individually addressable and alterable as described in the above paragraph. In order to alter a particular digit on the HCW-132 display, the entire display needs to be rewritten. So the digits that are to be displayed need to be stored in an 8 byte array and the bits need to be transposed in order for the characters to be correctly displayed. If this array is viewed as being 8 bits by 8 bits, tranposing means that the bit located at position x-y needs to be moved to position y-x across all 64 bits.

The header commentary provided in the sample code listing provided here provides a more thorough description of this transposition as well as the structure of this simple demo program. This code was developed on a Microchip Curiosity Board (DM164137) equipped with a PC16F18346 microcontroller, but can be adapted to any PIC device if the pin assignments and configuration file are changed as required. This sample code displays all 16 hexadecimal characters in a scrolling pattern, and for simplicity the 16 hex characters are stored in a 16 x 8 array, and those characters to be displayed are transposed and placed in an 8 x 8 bit array.

Much of the code is similar to that in my earlier post on the LED&KEY module. The major difference in the code contained in the subroutine "TRANSPOSE", and hopefully the comments included there will clarify how this transposition is occurring. This subroutine code might not be the most elegant or efficient. Other Forum members who remain closer to academia and vector algebra might suggest better methods of accomplishing the required task, and I welcome their comments.

'*  Name    : TM1638_8Digits_16Keys.PBP                             *
'*  Author  : Roberts                                               *
'*  Notice  : Hack it as you wish                                   *
'*          :                                                       *
'*  Date    : 11/22/2020                                            *
'*  Version : 1.0                                                   *
'*  Notes   : Demo program for TM1638 "HCW-132" PCB that has 8      *
'*          : 7-segment LED digits and 16 pushbutton switches.      *
'*          : This PCB display module uses common anode 7-segment   *
'*          : LED display components and requires more complicated  *
'*          : processing of data for displaying.                    *
'*          :                                                       *
' This is a primitive demo program that exercises the functions of the TM1638 7-Segment LED driver IC as it is
' used in an experimenter's printed circuit board (available from many online sources). This PCB marked "HCW-132"
' features 8 7-segment display characters, 16 pushbutton switches, and the TM1638 controller.  This
' PCB interfaces a microcontroller with a 3-wire (data, clock, and strobe) interface, and resembles industry standard
' SPI so that PIC Basic's SHIFTIN and SHIFTOUT commands are usable.
' The TM1638 data sheet provides the essential information for commands, addressing, and timing.  Experimentation with 
' PIC Basic's modes resulted in finding mode 0 is optimal for SHIFTOUT and mode 1 is optimal for SHIFTIN.
' Unlike the other TM1638 experimenter's board ("LED&KEY") that has 8 7-segment display characters, 8 pushbutton
' switches and 8 discrete LEDs, this HCW-132 PCB uses common anode 7-segment LED devices instead of common cathode 
' type.  The TM1638 spec sheet V1.3 illustrates on pages 13 and 14 the differences in how the TM1638 is wired in these
' 2 alternate configurations.
' In the common cathode configuration, each 7-segment digit is wired to one of the TM1638's "GRID" pins, while 8 segment
' pins are bus-wired and shared by all 8 7-segment "SEG" pins.  In the common anode wiring of the HCW-132, each 7-segment
' digits is wired to one of the TM1638's "SEG" pins, while the 8 segment pins are bus-wired to the GRID pins and shared 
' by all 8 7-segment digits.
' In the common cathode configuration, a character can be sent to any chosen 7-segment digit by simply specifying the
' digit's address ($C0 through $CE, even value addresses) and the hex code of the character.  For example, sending the
' sequence $44-$C0-$3F-$89 will place the character "0" in the right-most 7-segment digit.
' In the common anode configuration, the addresses $C0 through $CE (even value addresses) become segment addresses.
' Sending the very same sequence $44-$C0-$3F-$89 will instead light up the top horizontal bar (commonly referred to as
' segment "a" on the 6 right-most digits.  That is the $3F data, which is binary 00111111, is shared across all 8 
' 7-segment digits. The TM1638 spec sheet obliquely describes this at the bottom of page 6, but stops short of defining
' the full procedure required to write to all 8 digits.
' The common anode wiring arrangement requires that the entire display -- all 8 7-segment digits -- be written each time
' any digit or digits are changed.  This means that the user's program needs to create an 8-byte array into which the
' display characters are placed.  Furthermore, the 8-byte array then needs to be transposed such that the data bits
' for each digit/segment are entered into the TM1638 correctly so that the digits appear properly.
' Below is a diagram that illustrates this transposition:
'          0   0x3F     |0 0 1 1 1 1 1 1|      |1 1 1 0 1 1 0 1|
'          1   0x06     |0 0 0 0 0 1 1 0|      |1 0 0 1 1 1 1 1|
'          2   0x5B     |0 1 0 1 1 0 1 1|      |1 1 1 1 1 0 1 1|
'          3   0x4F     |0 1 0 0 1 1 1 1|  >>  |0 1 1 0 1 1 0 1|
'          4   0x66     |0 1 1 0 0 1 1 0|      |0 1 0 0 0 1 0 1|
'          5   0x6D     |0 1 1 0 1 1 0 1|      |0 1 1 1 0 0 0 1|
'          6   0x7D     |0 1 1 1 1 1 0 1|      |0 1 1 1 1 1 0 0|
'          7   0x07     |0 0 0 0 0 1 1 1|      |0 0 0 0 0 0 0 0|
' The first column are numberical digits and the second column are the corresponding 7-segment hex codes that are sent
' to a TM1638 when wired to a common cathode display.  The left binary matrix are the hex code equivalents, and the
' right matrix is the transposition of data required for the TM1638 common anode display. This transposed matrix is
' the left matrix flipped on its bottom-left to top-right diagonal.  Another way of looking at this is that a digit
' appearing in location x,y in the left matrix appears at location y,x in the right matrix.  For example, "1" appears
' in the left matrix at row 5, column 7, (row count starts at top/column count starts at right) and this is transposed 
' into row 7, column 5.
' The result of this transposition is each data byte sent to the TM1638 is one particular segment for all 8 7-segment
' digits. If the transposition diagonal is opposite (top-left to bottom-right, or row count starts at top and column
' count starts at the left, the ordering of the digits on the 7-segment display are reversed as detailed in the code
' below in subroutine "TRANSPOSE".
' The demo program is segmented into the following parts:
' "Declare Variables and Constants"
' "MAIN" fills the array HDIGIT[16] with 7-segment hex codes for digits "0" through "F" and calls subroutines.
' "CLEARALL" flushes the TM1638 internal memory as is recommended in the spec sheet page 5.  CLEARALL addresses the
' TM1638 as if it were connected in the common cathode arrangement, but this is OK since all 0's are being written.
' "TRANSPOSE" performs the data transposition described above on HDIGIT[0]-HDIGIT[7] and creates TDIGIT[0]-TDIGIT[7]
' The algorithm used is a shortcut that first places 0's in all locations in TDIGIT[i], then finds 1's in HDIGIT[j].
' TDIGIT[i] accumulates 1's in transposed locations using a shift left method.
' "TDIGIT_OUTPUT" shifts out the TDIGIT[i] array to the TM1638
' "SCROLL" shifts the characters stored in the HDIGIT[i] array to create a scrolling display effect
' "READKEY" demonstrated use of the 16 keys. Pressing any key causes that key's number to appear in all 8 display
' locations.  READKEY calls subroutines, but is otherwise separate from the rest of the program.  Deleting the
' GOTO READKEY directive at the bottom of "main:" allows READKEY to be deleted from the remainder of the code.
' Upon reset, the program begins by showing the characters 0 through 7 (left to right) on the 8-digit display. The
' display shifts right to left one character at a time 0 through F at 1 second intervals once and returns to the
' orignal 0 through 7, and remains static.  At this point, the program is cycling in READKEY waiting for a key press.  
' Pressing any key produces that key's number on all 8 digits.  The program can be reinitialized by pressing the "0" 
' and "3" keys(labeled "S1" and "S4" on the PCB).
' Hopefully the comments included below will explain the details.
'********************************Declare Variables and Constants**********************************************
' Place the PIC initialization INCLUDE file here (See PBP PIC Configuration Files)  
INCLUDE "PIC16F18346_Initialize.pbp"
ANSELB = 000000               'Port B is used for all interface pins
TRISB = 000000

SDA Var PORTB.4                  'Serial data port pin (bi-directional)
SCK Var PORTB.6                  'Serial clock port pin (uC output)
STB Var LATB.5                   'Chip Select/Stobe (uC output/active low)

HIGH SDA                         'Initialize Data Output
HIGH STB                         'Initialize Strobe Output

i VAR BYTE                       'loop variable for multiple use
j VAR BYTE                       'loop variable for multiple use
k VAR BYTE                       'loop variable for multiple use
m VAR BYTE                       'loop variable for multiple use
n VAR BYTE                       'loop variable for multiple use
p VAR BYTE                       'loop variable for multiple use

                                 'These are 3 of the 4 commands used by the TM1638 controller: ($40 not used)
CMD1 CON $44                     'CMD1=$44 is the write command for fixed addressing
CMD2 CON $42                     'CMD2=$42 is the read key matrix command
CMD3 CON $89                     'CMD3=$89 is the end command that turns the display on and sets brightness at 2/16
                                 '(CMD3=$8F for max brightness - see TM1638 spec sheet)
B1 VAR BYTE                      'B1 is the first byte value from the keypad
B2 VAR BYTE                      'B2 is the second byte value from the keypad
B3 VAR BYTE                      'B3 is the third byte value from the keypad
B4 VAR BYTE                      'B4 is the fourth byte value from the keypad
KEYNO VAR BYTE                   'KEYNO is the key number or key number hex code

HDIGIT VAR BYTE[16]              'HDIGIT is the array that holds the hex codes for 16 display digits 0 thru F
TDIGIT VAR BYTE[8]               'TDIGIT is the array that holds the transposed hex codes for the 8 display segments
'*************************************************** MAIN *********************************************************

    For i = 0 to 15                                'Initialize HDIGIT[i] digits to "1"-"F"
    LOOKUP i,[$3F,$06,$5B,$4F,$66,$6D,$7D,$07,$7F,$67,$77,$7C,$39,$5E,$79,$71],j   
    HDIGIT[i] = j                                  'Add 0x80 to the hex codes to include decimal point with any digit
    Next i

For n = 1 to 17                                    'Execute this loop 17 times to return to original "01234567"
    GOSUB CLEARALL                                 'Clear TM1638's internal memory
    GOSUB TRANSPOSE                                'Transpose 8 HDIGIT[j] array digits to TDIGIT[i] array
    GOSUB TDIGIT_OUTPUT                            'Output TDIGIT[i] to 7-Segment display
    GOSUB SCROLL                                   'Shift displayed digits to left 
Next n    
'********************************************* SUBROUTINE CLEARALL ************************************************
'TM1638 internal memory clear

    LOW STB                                        'Enable chip select
    SHIFTOUT SDA, SCK, 0, [CMD1]                   'This shifts out the 8-bit write command character $44                                            'and set the brightness duty cyle
    HIGH STB                                       'for fixed mode (random access) addressing
    FOR i = $C0 to $CE                             'Clear all memory for blank display
    LOW STB                                        'Enable chip select
    SHIFTOUT SDA, SCK, 0, [i, $00]                 'Can use common cathode addressing mode since data is all 0's
    HIGH STB                                       'Disable chip select
    Next i

    LOW STB                                        'Enable chip select                                
    SHIFTOUT SDA, SCK, 0, [CMD3]                   'The output command sends CMD3 which turns on the display
    HIGH STB                                       'and sets the brightness level
    PAUSE 20

'*********************************************** SUBROUTINE SCROLL *********************************************
'Digit Scrolling

  k = HDIGIT[0]                                    'This routine rotates the 7-segment hex code bytes within the
    For p = 0 to 15                                'HDIGIT[p] array to create a right-to-left scrolling effect
      IF p = 15 THEN                               'in order to demonstrate dynamic ability of data transposition
      HDIGIT[15] = k                               'in creating the TDIGIT[i] array.
      Return                                       'Done when all 16 digits have been shifted 1 location
    HDIGIT[p] = HDIGIT[p+1]                        'Shift all 16 digits 1 location in the array HDIGIT[j]
    Next p
'*********************************************** SUBROUTINE TRANSPOSE *********************************************  
'Transpose HDIGIT[j] digits to TDIGIT[i] segments (only HDIGIT[0] through HDIGIT[7] are transposed)
    m = 000001                       'm is the bit masking variable that isolates the bit being transposed
    For i = 0 to 7                      'i is the index for the array TDIGIT[i]
    TDIGIT[i] = 0                       'Clear the TDIGIT[i] byte prior to transposing             
      For j = 0 to 7                    'j is the index for the display digit byte HDIGIT[j] being transposed
          k = HDIGIT[j] & m             'k is the isolated m-th bit in the HDIGIT[j] byte
          IF k != 0 THEN                'If m-th bit is not 0, then make the i-th bit of TDIGIT = 1
          TDIGIT[i] = TDIGIT[i] + 1<<j  'Add all the non-zero bits in HDIGIT[j] to TDIGIT[i]
                                        'Change "1<<j" to 128>>j to reverse order of digits on display or
                                        'remove the TDIGIT[i] "REV" command below.                                                                                        
          ENDIF                         'TDIGIT[i] holds the transposed bits for a segment across all display digits
      Next j                            'Transpose all the j-th bits of HDIGIT[j] into TDIGIT[i]
      TDIGIT[i] = TDIGIT[i] REV 8       'Reverse the display order of the digits to left to right
      m = m<<1                          'Shift m for next bit processing
    Next i                              'Transpose all HDIGIT[j] array digits
'********************************************** SUBROUTINE TDIGIT_OUTPUT ******************************************
'Output TDIGIT[i] to 7-segment display

    SHIFTOUT SDA, SCK, 0, [CMD1]                   'The output command sends CMD3 which turns on the display                                            'and set the brightness duty cyle
    HIGH STB                                       'and sets the brightness level
    FOR j = 0 to 7                                 'Output the display digits
    i = $C0 + (2*j)                                '$Cn address is the display's digit segments
    SHIFTOUT SDA, SCK, 0, [i, TDIGIT[j]]           'TDIGIT[j] is the data byte containing one segment for all 8 digits
    Next j

    LOW STB                                        'Enable chip select                                
    SHIFTOUT SDA, SCK, 0, [CMD3]                   'This shifts out the 8-bit write command character $44
    HIGH STB                                       'for fixed mode (random access) addressing
    PAUSE 1000

'The following section of code reads the keypad byte. If no key is pressed the KEYNO byte default value is $00
'Delete this section of code if keypad reading is not required         

READKEY:                                                'READKEY processes only single key inputs except for restart
                                                        'which responds to any B1/B2 key combination
    LOW STB    
      SHIFTOUT SDA, SCK, 0, [CMD2]                      'Send the Read keypad command CMD2 = $42
    PAUSEUS 10
      SHIFTIN SDA, SCK, 1, [B1,B2,B3,B4]                'Shiftin all 4 keypad bytes   
    PAUSEUS 1                                           
    PAUSE 50                                            'Delay to allow for key release
    KEYNO = B1 | B2 | B3 | B4                           
    IF KEYNO = $00 THEN                                 'If all Bn's = $00 this means no key is pressed                   
    GOTO READKEY                                        'If no key is pressed, loop to top and read key bytes again            
    ELSE                                                'When KEYNO is not $00, then assign a key number                                 
  IF B1 & B2 != 0 THEN                                  'Pressing 2 keys (Ex: S1 and S4) restarts the program
  GOTO main
                                                        'Bytes B1-B4 can have values $02,$04,$20 or $40 depending on key
  LOOKDOWN KEYNO,[$02,$04,$20,$40],i                    'Associate KEYNO value with index 0,1,2 or 3
  IF B1 != 0 THEN                                       'If B1 is not zero, then key #8,#0,#9, or #1 is pressed                                                  
    LOOKUP i,[$08,$00,$09,$01],KEYNO                    'KEYNO is reassigned as the key number
  ELSEIF B2 != 0 THEN                                   'If B2 is not zero, then key #A,#2,#B, or #3 is pressed
    LOOKUP i,[$0A,$02,$0B,$03],KEYNO
  ELSEIF B3 != 0 THEN                                   'If B3 is not zero, then key #C,#4,#D, or #5 is pressed                
    LOOKUP i,[$0C,$04,$0D,$05],KEYNO
  ELSEIF B4 != 0 THEN                                   'If B4 is not zero, then key #E,#6,#F, or #7 is pressed                
    LOOKUP i,[$0E,$06,$0F,$07],KEYNO
                                                        'Lookup the 7-segment hex code 0-F for the key number
  LOOKUP KEYNO,[$3F,$06,$5B,$4F,$66,$6D,$7D,$07,$7F,$67,$77,$7C,$39,$5E,$79,$71],j
   For k = 0 to 7                                       'Make all 8 digits equal to the key number 0 thru F
   HDIGIT[k] = j
   Next k
   GOSUB CLEARALL                                       'Clear the 8 digit display                                                
   GOSUB TRANSPOSE                                      'Transpose HDIGIT[k] to TDIGIT[i]
   GOSUB TDIGIT_OUTPUT                                  'Output key number stored in TDIGIT[i] data
 GOTO READKEY                                           'Go wait for another key press input
'************************************** End of Keypad Reading ****************************************************