MM2(+): TM1637 7-segment clock display


Author Message
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 3211
Posted: 08:54am 13 Feb 2016      

If you want a cheap and cheerful clock display you can't get cheaper than this






These displays use the TM1637 controller which has a i2c-like interface (pullups required on the clock and data leads)

The attached code drives this display using simple bit-banging from Basic (not a CFunction in sight)


option explicit
option default none
dim digittosegment%(15)=(&H3F,&H06,&H5B,&H4F,&H66,&H6D,&H7D,&H07,&H7F,&H6F,&H77,&H7C,&H39,&H47,&H79,&H71)
dim integer m_brightness 'holds the brightness value for the display 0-15 are valid
DIM INTEGER displaydata(3)=(0,0,0,0) 'holds the bit values of the segments to be lit, ordered left to right
const colon=1
const nocolon=0
const noleadingzeros=1
const leadingzeros=0
const TM1637_I2C_COMM1 = &H40
const TM1637_I2C_COMM2 = &HC0
const TM1637_I2C_COMM3 = &H80
'*************************************************************************************************
'
' Test / demo program
'
dim mytime$ length 8
DIM integer j
TM1637Init(44,1)' initialise the display using pin 44 for the clock and pin 1 for the data
setbrightness(12)' load the brightness into the global variable
setsegments(displaydata()) 'displaydata is set to zeroes so clear the display
mytime$=time$
pause 1000
timer=0
do
if mytime$<>time$ then 'the clock seconds have changed
if val(right$(time$,1)) mod 2 then 'flash the colon each second
converttime(displaydata(),colon,noleadingzeros) 'set the time into the display array including the colon but no leading zeroes
else
converttime(displaydata(),nocolon,noleadingzeros)'set the time into the display array without the colon or leading zeroes
endif
setsegments(displaydata()) 'update the display
mytime$=time$
endif
loop while timer <60000 'update the time over a minute
'
for j=-999 to 1999 step 17 'demo positive and negative numbers without leading zeroes
convertnumber(displaydata(),j,noleadingzeros)
setsegments(displaydata())
next j
for j=-999 to 1999 step 17 'demo positive and negative numbers with leading zeroes
convertnumber(displaydata(),j,leadingzeros)
setsegments(displaydata())
next j
end

'************************************************************************************************

sub TM1637Init(pinClk as integer, pinDIO as integer)
dim integer m_pinClk,m_pinDIO
' Copy the pin numbers
m_pinClk = pinClk
m_pinDIO = pinDIO

' Set the pin direction and default value.
' Both pins are set as inputs, allowing the pull-up resistors to pull them up
pin(m_pinDIO)=0
pin(m_pinClk)=0
setpin m_pinDIO,DIN
setpin m_pinClk,DIN
end sub

sub setBrightness(brightness as integer)
m_brightness = brightness
end sub

sub setSegments(segments() as integer)
local integer k
' Write COMM1
start
writeByte(TM1637_I2C_COMM1)
stop

' Write COMM2 + first digit address
start
writeByte(TM1637_I2C_COMM2)

' Write the data bytes
for k=0 to 3
writeByte(segments(k))
next k
stop

' Write COMM3 + brightness
start
writeByte(TM1637_I2C_COMM3 + (m_brightness AND &H0f))
stop
end sub

sub start
setpin m_pinDIO, DOUT
' bitDelay
end sub

sub stop

setpin m_pinDIO, dout
setpin m_pinClk, DIN
setpin m_pinDIO, DIN
end sub

sub writeByte(b as integer) as integer
local integer i,ack,data = b
' 8 Data Bits
for i=0 to 7
' CLK low
setpin m_pinClk,dout
' Set data bit
if (data and 1) then
setpin m_pinDIO,DIN
else
setpin m_pinDIO,DOUT
endif
' CLK high
setpin m_pinClk, DIN
data = data >> 1
next i

' Wait for acknowledge
' CLK to zero
setpin m_pinClk, dout
setpin m_pinDIO, DIN
' CLK to high
setpin m_pinClk, din
ack = pin(m_pinDIO)
if (ack = 0) then
setpin m_pinDIO, dout
endif
setpin m_pinClk, dout
end sub

function encodeDigit(digit as integer) as integer
encodeDigit=digitToSegment%(digit and &H0f)
end function

sub convertnumber(dd() as integer, n as integer, nolead as integer)
local integer d(2),m
m=abs(n mod 10000)
if n<0 then m=abs(n mod 1000)
d(0)=(m - (m mod 1000))
d(1)=(m - d(0) - (m MOD 100))
d(2)=(m - d(0) - d(1) - (m mod 10))
dd(3)=encodeDigit(m - d(0) -d(1)- d(2))
dd(0)=encodeDigit(d(0)\1000)
dd(1)=encodeDigit(d(1)\100)
dd(2)=encodeDigit(d(2)\10)
if nolead then
if d(0)=0 then
dd(0)=0
if d(1)=0 then
dd(1)=0
if d(2)=0 then
dd(2)=0
endif
endif
endif
if n<0 then
if dd(2)=0 then
dd(2)=64
else
if(dd(1)=0) then
dd(1)=64
else
dd(0)=64
endif
endif
endif
else
IF n<0 then dd(0)= 64
endif
end sub

sub converttime(dd() as integer, displaycolon as integer, nolead as integer) as integer
local integer i=val(left$(time$,2))*100+val(mid$(time$,4,2))
convertnumber(dd(),i,nolead)
if displaycolon then dd(1)=dd(1) OR &H80
end sub




Edited by matherp 2016-02-14

viscomjim
Guru

Joined: 08/01/2014
Location: United States
Posts: 925
Posted: 01:35am 15 Feb 2016      

This is a very cost effective display. I am wondering what the protocol is and why it is different that I2C. Can't find too much data on this one. Thanks for the code, just ordered a couple of these to play with.

Nabuky
Newbie

Joined: 09/07/2019
Location: Spain
Posts: 3
Posted: 04:32pm 14 Jul 2019      

Hi all,
I am a newbie in electronics and I need to adapt this program to handle the TM1637 display from VBA, in which I have experience. Is it crazy?
The intention is simple, show on a display the time of the PC with Windows OS.
I have it connected through a USB-I2C adapter, CH341A without socket.


The first problem is with the hardware, I need to know if this converter is able to communicate with the TM1637 directly or is another circuit needed?
The second problem is with the software, it seems that a link to some library is missing, there are some variables and functions that I am unable to adapt (p.ex. pin (m_pinDIO) = 0, setpin m_pinDIO, din).

Can someone give me an idea?
All tips are also welcome, even if they involve the use of another display.

Thanks in advance.

Regards.Edited by Nabuky 2019-07-16

CaptainBoing

Guru

Joined: 07/09/2016
Location: United Kingdom
Posts: 1199
Posted: 07:34pm 14 Jul 2019      

Hello Nabuky. welcome to the forum. I hope you are well.

matherp's prog above should give you all the registers you need to drive the module.

I don't know how much success you going to have doing it directly from a PC to i2c as the module isn't a pure i2c interface. I would be tempted to put something in between e.g. a Micromite Mk2 and issue commands via a com port on your PC with a USB<->serial module. The commands could then be interpreted by your microcontroller to drive the i2c using a modified version of the code above.

This isn't as difficult or expensive as you might think and having a microcontroller allows you to have a lot more flexibility and then your VBA can vary it's command set easily - the PC end will be easy to tweak.Edited by CaptainBoing 2019-07-16

Bill7300
Senior Member

Joined: 05/08/2014
Location: Australia
Posts: 156
Posted: 11:34pm 14 Jul 2019      

I see the eBay link Peter provided now says the sale has ended because the seller had an error in the advertisement - probably the price?

Bill

PeterB
Guru

Joined: 05/02/2015
Location: Australia
Posts: 483
Posted: 06:05am 15 Jul 2019      

If you Google 4 digit 7 segment display or similar, every man and his dog sells them.
You may need to look at the Arduino stuff.

Peter

Nabuky
Newbie

Joined: 09/07/2019
Location: Spain
Posts: 3
Posted: 04:24pm 15 Jul 2019      

Thank you for your answers.

  Quote  
matherp's prog above should give you all the registers you need to drive the module.

I'm missing some parameters that I do not know, probably from some library. I would appreciate some additional documentation of the program.

  Quote  
I don't know how much success you going to have doing it directly from a PC to i2c as the module isn't a pure i2c interface.

This is a main problem. The term "I2C" should not be used if it is not a device that meets the standards or is at least compatible, the TM1637 only uses 7 bits, should be called "I2C-Arduino".

  Quote  I would be tempted to put something in between e.g. a Micromite Mk2 and issue commands via a com port on your PC with a USB<->serial module. The commands could then be interpreted by your microcontroller to drive the i2c using a modified version of the code above.

My specialty is management software and databases, mainy for bussines. I regret that my knowledge in microcontrollers are so scarce, I can not consider now the development of a solution with them.

  Quote  I see the eBay link Peter provided now says the sale has ended because the seller had an error in the advertisement - probably the price?

It is very easy to find other valid links, with acceptable prices.

  Quote  If you Google 4 digit 7 segment display or similar, every man and his dog sells them. You may need to look at the Arduino stuff.

I try to avoid using Arduino. Maybe I'm annoyed with them because of their lack of rigor in the use of the standards.

I never imagined that it would be so complicated to show the PC time on an external display.

Thanks again.

Regards.

CaptainBoing

Guru

Joined: 07/09/2016
Location: United Kingdom
Posts: 1199
Posted: 05:03pm 15 Jul 2019      

  Nabuky said  
  Quote  I would be tempted to put something in between e.g. a Micromite Mk2 and issue commands via a com port on your PC with a USB<->serial module. The commands could then be interpreted by your microcontroller to drive the i2c using a modified version of the code above.

My specialty is management software and databases, mainly for business. I regret that my knowledge in microcontrollers are so scarce, I can not consider now the development of a solution with them.


Mine too! but my first love is micro electronics.

get hold of a small 28 pin micromite unit - there are lots of suppliers here who can sell you a ready to go board for not much $$$. You only need a USB lead to talk to it - really easy - no, really! It will present as a COM port on your PC - no need for your USB <-> i2c module. You can do the whole thing with putty and notepad... no need to load an IDE or anything. With no affiliation and just grabbing a link out of the air for Europe, you have this which is ideal Explore28 or the "keyring" if you can solder and you want to save a few $ (but I wouldn't if I was at the stage you are). If you had the bits, this could be working tonight - This thread contains all the know-how to put it all together and someone has even given you the code!

Connect up the display as detailed above, load the code onto the MM and it should immediately(ish?) work. There is a good IDE for MMs though if you are happier with an integrated environment - search this forum for MMEdit - it is a little less hardcore than Putty & Notepad++

Then send "commands" from your PC to the COMx port, augment the above code a little bit at a time to parse those commands and you are pretty much done. You can add more to the code over time to increase the functionality - perhaps flipping between the time and temperature, or supply voltages etc?

I really do understand this looks like a black art but it is easy and you can just come back to the forum here if/when you get stuck. Once you have done the first project your head will be buzzing with the next when you realise what a capable little thing the MM is

have fun

Edited by CaptainBoing 2019-07-17

Nabuky
Newbie

Joined: 09/07/2019
Location: Spain
Posts: 3
Posted: 06:46am 16 Jul 2019      

Thank you CaptainBoing.

Everything seems very easy when the subject is mastered, it is not my case.

Of course I would like to practice with the Explore28, but this should be an optional improvement of the project it has taken up too much time.

I think I should postpone this improvement for when the project is already working and I have more time to "play" with the microcontroller.

Best regards.

jman

Guru

Joined: 12/06/2011
Location: New Zealand
Posts: 711
Posted: 09:26am 08 Aug 2019      

@matherp

Should this run on the MM(28 pin) after the appropriate pin changes ?
I see the PCB's have 10k pull ups fitted and looks like 100pf caps to ground (clock and data).

I have a couple of displays but no joy with the supplied code on a 28 pin device. With added pullups still no result.

Many thanks
Jman

matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 3211
Posted: 02:01pm 08 Aug 2019      

  Quote  Should this run on the MM(28 pin) after the appropriate pin changes ?


Yes, but no

Recent releases of MMbasic releases have tightened up on using reserved words as variable names and my programs used some. Try the attached

option explicit
option default none
dim digittosegment%(15)=(&H3F,&H06,&H5B,&H4F,&H66,&H6D,&H7D,&H07,&H7F,&H6F,&H77,&H7C,&H39,&H47,&H79,&H71)
dim integer m_brightness 'holds the brightness value for the display 0-15 are valid
DIM INTEGER displaydata(3)=(0,0,0,0) 'holds the bit values of the segments to be lit, ordered left to right
const colon=1
const nocolon=0
const noleadingzeros=1
const leadingzeros=0
const TM1637_I2C_COMM1 = &H40
const TM1637_I2C_COMM2 = &HC0
const TM1637_I2C_COMM3 = &H80
'*************************************************************************************************
'
' Test /demo program
'
dim mytime$ length 8
DIM integer j
TM1637Init(18,17)' initialise the display using pin 44 for the clock and pin 1 for the data
setbrightness(15)' load the brightness into the global variable
setsegments(displaydata()) 'displaydata is set to zeroes so clear the display
j=ds18b20(4)*10
convertnumber(displaydata(),j,noleadingzeros)
displaydata(3)=&B01011000
setsegments(displaydata()) 'displaydata is set to zeroes so clear the display
mytime$=time$
pause 1000
timer=0
do
if mytime$<>time$ then 'the clock seconds have changed
if val(right$(time$,1)) mod 2 then 'flash the colon each second
converttime(displaydata(),colon,noleadingzeros) 'set the time into the display array including the colon but no leading zeroes
else
converttime(displaydata(),nocolon,noleadingzeros)'set the time into the display array without the colon or leading zeroes
endif
setsegments(displaydata()) 'update the display
mytime$=time$
endif
loop while timer <60000 'update the time over a minute
'
for j=-999 to 1999 step 17 'demo positive and negative numbers without leading zeroes
convertnumber(displaydata(),j,noleadingzeros)
setsegments(displaydata())
next j
for j=-999 to 1999 step 17 'demo positive and negative numbers with leading zeroes
convertnumber(displaydata(),j,leadingzeros)
setsegments(displaydata())
next j
end

'************************************************************************************************

sub TM1637Init(pinClk as integer, pinDIO as integer)
dim integer m_pinClk,m_pinDIO
' Copy the pin numbers
m_pinClk = pinClk
m_pinDIO = pinDIO

' Set the pin direction and default value.
' Both pins are set as inputs, allowing the pull-up resistors to pull them up
pin(m_pinDIO)=0
pin(m_pinClk)=0
setpin m_pinDIO,DIN
setpin m_pinClk,DIN
end sub

sub setBrightness(brightness as integer)
m_brightness = brightness
end sub

sub setSegments(segments() as integer)
local integer k
' Write COMM1
sendstart
writeByte(TM1637_I2C_COMM1)
sendstop

' Write COMM2 + first digit address
sendstart
writeByte(TM1637_I2C_COMM2)

' Write the data bytes
for k=0 to 3
writeByte(segments(k))
next k
sendstop

' Write COMM3 + brightness
sendstart
writeByte(TM1637_I2C_COMM3 + (m_brightness AND &H0f))
sendstop
end sub

sub sendstart
setpin m_pinDIO, DOUT
' bitDelay
end sub

sub sendstop

setpin m_pinDIO, dout
setpin m_pinClk, DIN
setpin m_pinDIO, DIN
end sub

sub writeByte(b as integer) as integer
local integer i,ack,odata = b
' 8 Data Bits
for i=0 to 7
' CLK low
setpin m_pinClk,dout
' Set data bit
if (odata and 1) then
setpin m_pinDIO,DIN
else
setpin m_pinDIO,DOUT
endif
' CLK high
setpin m_pinClk, DIN
odata = odata >> 1
next i

' Wait for acknowledge
' CLK to zero
setpin m_pinClk, dout
setpin m_pinDIO, DIN
' CLK to high
setpin m_pinClk, din
ack = pin(m_pinDIO)
if (ack = 0) then
setpin m_pinDIO, dout
endif
setpin m_pinClk, dout
end sub

function encodeDigit(digit as integer) as integer
encodeDigit=digitToSegment%(digit and &H0f)
end function

sub convertnumber(dd() as integer,n as integer, nolead as integer)
local integer d(2),m
m=abs(n mod 10000)
if n<0 then m=abs(n mod 1000)
d(0)=(m - (m mod 1000))
d(1)=(m - d(0) - (m MOD 100))
d(2)=(m - d(0) - d(1) - (m mod 10))
dd(3)=encodeDigit(m - d(0) -d(1)- d(2))
dd(0)=encodeDigit(d(0)1000)
dd(1)=encodeDigit(d(1)100)
dd(2)=encodeDigit(d(2)10)
if nolead then
if d(0)=0 then
dd(0)=0
if d(1)=0 then
dd(1)=0
if d(2)=0 then
dd(2)=0
endif
endif
endif
if n<0 then
if dd(2)=0 then
dd(2)=64
else
if(dd(1)=0) then
dd(1)=64
else
dd(0)=64
endif
endif
endif
else
IF n<0 then dd(0)= 64
endif
end sub

sub converttime(dd() as integer,displaycolon as integer,nolead as integer) as integer
local integer i=val(left$(time$,2))*100+val(mid$(time$,4,2))
convertnumber(dd(),i,nolead)
if displaycolon then dd(1)=dd(1) OR &H80
end sub

Edited 2019-08-09 00:03 by matherp

jman

Guru

Joined: 12/06/2011
Location: New Zealand
Posts: 711
Posted: 07:46pm 08 Aug 2019      

Hi

Thanks for the help. Unfortunately still no go (No errors just no display)
I have just tried it with an E100 and same result, could be dodgy modules (all 3 do the same thing)

Thanks
Jman

matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 3211
Posted: 09:49pm 08 Aug 2019      

Looks like the BB is corrupting the listing

Try


tm1637.zip


Connect pin 18 to clk and 17 to dio (28-pin with pullups)
Edited 2019-08-09 07:54 by matherp

TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 3305
Posted: 10:18pm 08 Aug 2019      

Lines 148-150 use \ (the backslash).
The forum is having problems with the backslash.
dd(0)=encodeDigit(d(0)\1000)
dd(1)=encodeDigit(d(1)\100)
dd(2)=encodeDigit(d(2)\10)


It works OK in the preview but not when saving the post

I will let Glenn know.

Jim
Edited 2019-08-09 08:21 by TassyJim
It all started with the ZX81....
VK7JH
http://www.c-com.com.au/MMedit.htm

jman

Guru

Joined: 12/06/2011
Location: New Zealand
Posts: 711
Posted: 05:46am 09 Aug 2019      

  matherp said  Looks like the BB is corrupting the listing

Try

tm1637.zip
Connect pin 18 to clk and 17 to dio (28-pin with pullups)


Awesome that did the trick

Many thanks
Jman