TM1637 LED Driver
Moderators: David Barker, Jerry Messina
-
- Registered User
- Posts: 185
- Joined: Mon Mar 10, 2008 8:20 am
- Location: Seattle, WA USA
- Contact:
TM1637 LED Driver
I'm trying to wrap my head around the best way to control a TM1637 6-Digit LED Driver. Four and six digit display assemblies can be had for next to nothing from the usual sources. Has anybody developed code for these guys? Note the TM1638 from the same company uses a standard I2C interface while the TM1637 does not.
The interface is a mixed up, totally different version of I2C. Bits sent in the reverse order, different start and stop commands, etc. I think I can use the shift module to send each byte, with start and stop commands added as needed.
[start], Shift[byte], [stop]
It might be easier to bit bang everything, but I'm not space nor speed limited.
Suggestions appreciated.
The interface is a mixed up, totally different version of I2C. Bits sent in the reverse order, different start and stop commands, etc. I think I can use the shift module to send each byte, with start and stop commands added as needed.
[start], Shift[byte], [stop]
It might be easier to bit bang everything, but I'm not space nor speed limited.
Suggestions appreciated.
-
- Swordfish Developer
- Posts: 1473
- Joined: Fri Jan 30, 2009 6:27 pm
- Location: US
Re: TM1637 LED Driver
That looks like normal I2C to me, just with no address byte, so I2C.Start, Stop, WriteByte, etc should work.
Since there's no addressing you'd have to have a bus dedicated to it with SDA and SCL pullups as usual. 100KHz speed should work.
Am I missing something?
Since there's no addressing you'd have to have a bus dedicated to it with SDA and SCL pullups as usual. 100KHz speed should work.
Am I missing something?
-
- Registered User
- Posts: 185
- Joined: Mon Mar 10, 2008 8:20 am
- Location: Seattle, WA USA
- Contact:
Re: TM1637 LED Driver
One major thing you're missing - the bits are sent LSB first instead of MSB first. I suppose you could just invert the data to be sent. There are only a few commands and you just reverse the order of the LED segments to be sent.
This page provides some more details of the interface
Thanks Jerry.
This page provides some more details of the interface
Thanks Jerry.
-
- Swordfish Developer
- Posts: 1473
- Joined: Fri Jan 30, 2009 6:27 pm
- Location: US
Re: TM1637 LED Driver
The ref code in that link looks a little dodgy to me... it never sets the DIO pin to input mode.
Here's a port of that reference C code with the CLK and DIO pins used as open-drain like I2C.
I added an option for the CLK pin so it can be used in push-pull mode w/out a pullup, but DIO requires one
Another option would be to start with the library module SI2C.bas and change the byte in/byte out routines to do LSB first...
Here's a port of that reference C code with the CLK and DIO pins used as open-drain like I2C.
I added an option for the CLK pin so it can be used in push-pull mode w/out a pullup, but DIO requires one
Code: Select all
// TM1637 driver ref code from http://www.microcontroller.it/english/Tutorials/Elettronica/componenti/TM1637.htm
// Copyright: Shenzhen Tian Microelectronics
// File name: TM1637
// Current Version: 1.0
module TM1637
// CLK and DIO pins
#option TM1637_CLK = PORTB.0 // pullup optional (see below)
#option TM1637_DIO = PORTB.1 // requires a pullup
// set CLK pin mode
// true = open-drain (requires pullup), false = push-pull
// TM1637 does not drive the CLK, so it can be push-pull
#option TM1637_OD_CLK = false
dim clk as TM1637_CLK.TM1637_CLK@,
dio as TM1637_DIO.TM1637_DIO@
// assumes open-drain mode with pullups on CLK and DIO pins
inline sub CLK_HIGH()
#if (TM1637_OD_CLK)
input(clk)
#else // push-pull
high(clk)
#endif
end sub
inline sub CLK_LOW()
low(clk)
end sub
inline sub DIO_HIGH()
input(dio)
end sub
inline sub DIO_LOW()
low(dio)
end sub
// 1637 start
public sub Start()
CLK_HIGH() // clk = 1
DIO_HIGH() // dio = 1
delayus(2)
DIO_LOW() // dio = 0
end sub
// 1637 Answer
public sub Ask()
CLK_LOW() // clk = 0
DIO_HIGH() // sets dio to input
delayus(5) // After the falling edge of the eighth clock delay 5us, ACK signals the beginning of judgment
while (dio = 1)
end while
CLK_HIGH() // clk = 1
delayus(2)
CLK_LOW() // clk = 0
end sub
// 1637 Stop
public sub Stop()
CLK_LOW() // clk = 0
delayus(2)
DIO_LOW() // dio = 0
delayus(2)
CLK_HIGH() // clk = 1
delayus(2)
DIO_HIGH() // dio = 1
end sub
// write a byte
public sub WriteByte(b as byte)
dim i as byte
for i = 0 to 7
CLK_LOW() // clk = 0
if (b.bits(0) = 1) then
DIO_HIGH() // dio = 1
else
DIO_LOW() // dio = 0
endif
delayus(3)
b = b >> 1
CLK_HIGH() // clk = 1
delayus(3)
next
DIO_HIGH() // set dio to OD mode
end sub
// read buttons
public function ScanKey() as byte
dim rekey, i as byte
Start()
WriteByte($42) // read command buttons
Ask()
DIO_HIGH() // dio = 1, read keys before data lines pulled
for i = 0 to 7 // start reading from the low
CLK_LOW() // clk = 0
rekey = rekey >> 1
delayus(30)
CLK_HIGH() // clk = 1
if (dio = 1) then
rekey.bits(7) = 1
endif
delayus(30)
next
Ask()
Stop()
result = rekey
end function
// Write display register
public sub SmgDisplay()
dim i as byte
Start()
WriteByte($40) //40H address is automatically incremented by 1 mode, 44H fixed address mode
Ask()
Stop()
Start()
WriteByte($C0) // Set the first address
Ask()
for i = 0 to 5 // Addresses from Canada, do not always write address (???)
WriteByte($FF) // Send data
Ask()
next
Stop()
Start()
WriteByte($8F) // Open display, maximum brightness
Ask()
Stop()
end sub
// Initialization routine
// CLK and DIO are open-drain pins with pullups
public sub Init()
CLK_HIGH()
DIO_HIGH()
end sub
// startup code
Init()
end module
Another option would be to start with the library module SI2C.bas and change the byte in/byte out routines to do LSB first...
Code: Select all
{
****************************************************************************
* Name : ShiftOut (PRIVATE) *
* Purpose : Shift out a byte value, LSB first *
****************************************************************************
}
sub ShiftOut(pData as byte)
dim count as byte
count = 8
repeat
SDA = pData.bits(0)
DATA_SETUP_DELAY()
ToggleClock()
pData = pData >> 1
dec(count)
until (count = 0)
ClrWDT()
end sub
{
****************************************************************************
* Name : ShiftIn (PRIVATE) *
* Purpose : Shift in a byte value, LSB first, sample before clock *
****************************************************************************
}
function ShiftIn() as byte
dim count as byte
input(SDA)
count = 8
result = 0
repeat
result = result >> 1
result.bits(7) = SDA
ToggleClock()
dec(count)
until (count = 0)
output(SDA)
ClrWDT()
end function
-
- Registered User
- Posts: 185
- Joined: Mon Mar 10, 2008 8:20 am
- Location: Seattle, WA USA
- Contact:
Re: TM1637 LED Driver
I am not making much progress on this....or I have IR 7-segment displays!
I'm using the module translated from the example code.
#option statements before the include statements to set the port pins I'm using for clock and data.
In my program, I think all I should need to do is this:
This should light up all the segments as is hardcoded in the module.
The LED is flashing as expected.
Am I missing obvious somewhere?
The TM1637 has built-in pullup resistors.
![Confused :?](./images/smilies/icon_confused.gif)
I'm using the module translated from the example code.
#option statements before the include statements to set the port pins I'm using for clock and data.
In my program, I think all I should need to do is this:
Code: Select all
TM1637.Init
While 1 = 1
SmgDisplay()
DelayMS(1000)
Toggle(LED1)
Wend
This should light up all the segments as is hardcoded in the module.
The LED is flashing as expected.
Am I missing obvious somewhere?
The TM1637 has built-in pullup resistors.
-
- Swordfish Developer
- Posts: 1473
- Joined: Fri Jan 30, 2009 6:27 pm
- Location: US
Re: TM1637 LED Driver
Jon -
try getting rid of that last DIO_HIGH at the end of the WriteByte() sub
try getting rid of that last DIO_HIGH at the end of the WriteByte() sub
Code: Select all
next
//DIO_HIGH() // set dio to OD mode
end sub
-
- Registered User
- Posts: 185
- Joined: Mon Mar 10, 2008 8:20 am
- Location: Seattle, WA USA
- Contact:
Re: TM1637 LED Driver
That didn't change anything.
Just to verify my thoughts - the TM1637 module takes care of setting input/output state, so I don't need to do anything in the main program.
I don't see any problems, it looks like the right commands are all being sent.
Here's another reference and sample code which seems to agree with the first.
Jon
Just to verify my thoughts - the TM1637 module takes care of setting input/output state, so I don't need to do anything in the main program.
I don't see any problems, it looks like the right commands are all being sent.
Here's another reference and sample code which seems to agree with the first.
Jon
-
- Swordfish Developer
- Posts: 1473
- Joined: Fri Jan 30, 2009 6:27 pm
- Location: US
Re: TM1637 LED Driver
I found the arduino code that last link used, and adapted it. That looks to be a bit better than the first.
The TM1637.bas module is in the zip file, along with the following test program.
See if this works out any better...
The TM1637.bas module is in the zip file, along with the following test program.
See if this works out any better...
Code: Select all
// TM1637 test
device = 18F26K22
clock = 64
// intosc
include "intosc.bas"
// digital/analog mode
#option DIGITALIO_INIT = true // automatically call setalldigital at startup
include "setdigitalio.bas"
// TM1637
#option TM1637_CLK = PORTB.0
#option TM1637_DIO = PORTB.1
#option TM1637_BIT_DELAY = 5
'#option TM1637_OD_CLK = false // true=OD mode, false=push-pull(no pullup reqd)
include "TM1637.bas"
// segment data for 4 displays
dim seg(4) as byte
const SEG_DONE() as byte =
(
SEG_B + SEG_C + SEG_D + SEG_E + SEG_G, // d
SEG_A + SEG_B + SEG_C + SEG_D + SEG_E + SEG_F, // O
SEG_C + SEG_E + SEG_G, // n
SEG_A + SEG_D + SEG_E + SEG_F + SEG_G // E
)
dim i, k as byte
main:
// this is redundant since option DIGITALIO_INIT=true already took care of it,
// so it's just here as a reminder
SetAllDigital()
// init pins (this is also optional... done at module startup)
TM1637.init()
// all segments on
seg(0) = $FF
seg(1) = $FF
seg(2) = $FF
seg(3) = $FF
TM1637.setSegments(seg)
// turn on display (full on)
TM1637.displayCmd(7, 1)
// brightness test
for k = 0 to 7
TM1637.displayCmd(k, 1)
delayms(1000)
next
// on/off test
for k = 1 to 10
TM1637.displayCmd(7, 0)
delayms(500)
TM1637.displayCmd(7, 1)
delayms(500)
next
// wait 5 secs and clear
delayms(5000)
TM1637.clearDisplay()
// Run through all the dots
for i = 0 to 3
k = 1
repeat
seg(i) = k
TM1637.setSegments(seg)
k = k << 1
until (k = $80)
next
// Done!
seg = SEG_DONE
TM1637.setSegments(seg)
while (true)
end while
- Attachments
-
- TM1637.zip
- (2.37 KiB) Downloaded 174 times
-
- Registered User
- Posts: 185
- Joined: Mon Mar 10, 2008 8:20 am
- Location: Seattle, WA USA
- Contact:
Re: TM1637 LED Driver
I rolled up my sleeves, engaged my brain, and started from the data sheet. Progress is being made...I can at least see where it's hanging up.
I flash LEDs for START, ACK and STOP. The unlined events occur and the program hangs.
The send subroutine is this. I inserted a number of uS delays thinking this might help. Thanks for the help - it got me moving in the right direction!
I flash LEDs for START, ACK and STOP. The unlined events occur and the program hangs.
The send subroutine is this. I inserted a number of uS delays thinking this might help. Thanks for the help - it got me moving in the right direction!
Code: Select all
Sub TM1637Display(Level As Byte)
'Digit data stored in DigitData array prior to calling
'Brightness set by constant array - TM1637Brightness
Dim I As Byte
TM1637Start
ShiftOut($40) ' sequential addressing
TM1637Ack
DelayUS(2)
TM1637Stop
DelayUS(5)
TM1637Start
ShiftOut($C0) 'set first address
TM1637Ack
DelayUS(2)
For I = 0 To 5 'shift out digit data
ShiftOut(DigitData(I))
TM1637Ack
DelayUS(2)
Next
TM1637Start
ShiftOut(TM1637Brightness(Level))
TM1637Ack
DelayUS(2)
TM1637Stop
End Sub
-
- Registered User
- Posts: 185
- Joined: Mon Mar 10, 2008 8:20 am
- Location: Seattle, WA USA
- Contact:
Re: TM1637 LED Driver
Thanks Jerry,
I missed your post when I made my last post. Appreciate all the help for something that shouldn't be that difficult!
I'll let you know how it goes.
Happy new year. Let's hope it's a great one!
Jon
I missed your post when I made my last post. Appreciate all the help for something that shouldn't be that difficult!
I'll let you know how it goes.
Happy new year. Let's hope it's a great one!
Jon
-
- Swordfish Developer
- Posts: 1473
- Joined: Fri Jan 30, 2009 6:27 pm
- Location: US
Re: TM1637 LED Driver
Thanks Jon. Happy New Year to you too.
Since I don't have any display hdw, if you could try out the code in that zip file to see if it works that'd be great.
I modified it a bit from that arduino code so we'll have to see.
Using ShiftOut might not be the best of implementations since it actively drives both the clk and data lines, and that could cause a problem during the ack cycle.
Anyway, keep me posted. If that works out there are some more subs in that arduino code to help generate chars that I didn't bother to add yet.
Since I don't have any display hdw, if you could try out the code in that zip file to see if it works that'd be great.
I modified it a bit from that arduino code so we'll have to see.
Using ShiftOut might not be the best of implementations since it actively drives both the clk and data lines, and that could cause a problem during the ack cycle.
Anyway, keep me posted. If that works out there are some more subs in that arduino code to help generate chars that I didn't bother to add yet.
-
- Registered User
- Posts: 185
- Joined: Mon Mar 10, 2008 8:20 am
- Location: Seattle, WA USA
- Contact:
Re: TM1637 LED Driver
I'm on a cruise and brought minimal stuff to get started on an "easy project"....which is becoming very frustrating!
The version I'm trying to work on is bit-banging the clock and data lines, not using the shift module. I had high hopes when I found a version for Proton Basic that was nearly identical to what I had been doing with a slight rearrangement of commands...and a very interesting note that 2uS delays were not reliable, but 50 or 100uS delays were.
I'm not sure I'm actually generating clock and data pulses, as the PICkit 3 I have with me isn't showing anything using the third input that does not have a pulldown resistor. I'm never before tried the logic analyzer function with the PICkit 3 and stand-alone GUI so I have some doubts it may not work.
The subroutines I'm using to control the clock and DIO lines are below. I believe these should be doing the task.
This may have to wait until I get home....
The version I'm trying to work on is bit-banging the clock and data lines, not using the shift module. I had high hopes when I found a version for Proton Basic that was nearly identical to what I had been doing with a slight rearrangement of commands...and a very interesting note that 2uS delays were not reliable, but 50 or 100uS delays were.
I'm not sure I'm actually generating clock and data pulses, as the PICkit 3 I have with me isn't showing anything using the third input that does not have a pulldown resistor. I'm never before tried the logic analyzer function with the PICkit 3 and stand-alone GUI so I have some doubts it may not work.
The subroutines I'm using to control the clock and DIO lines are below. I believe these should be doing the task.
Code: Select all
Sub TM1637ClockPin(Level As Byte)
'Clock is pulled high
If Level = 1 Then
Input(TM1637Clock)
Else
Output(TM1637Clock)
TM1637clock = 0
End If
End Sub
Sub TM1637DIOPin(Level As Byte)
'DIO is pulled high
If Level = 1 Then
Input(TM1637DIO)
Else
Output(TM1637DIO)
TM1637DIO = 0
End If
End Sub
-
- Registered User
- Posts: 185
- Joined: Mon Mar 10, 2008 8:20 am
- Location: Seattle, WA USA
- Contact:
Re: TM1637 LED Driver
One question is answered anyway. The PICkit 3 (at least THIS PICkit 3) channel 3 does not function in the logic analyzer. And channels 1 and 2 are pretty much useless to me for this, as the clock and data line 10k pullup resistors won't have much impact against the 4.7k pulldowns on channels 1 & 2.
Another gotcha to be aware of is the logic analyzer doesn't seem to be happy with external power to the board being tested. PICkit 3 power must be on for the logic analyzer to work.
Another gotcha to be aware of is the logic analyzer doesn't seem to be happy with external power to the board being tested. PICkit 3 power must be on for the logic analyzer to work.
-
- Swordfish Developer
- Posts: 1473
- Joined: Fri Jan 30, 2009 6:27 pm
- Location: US
Re: TM1637 LED Driver
So much for the PK3 LA function!
Better to do:
or just use 'low(TM1637clock)', since that does the same thing.
'Low()' and 'High()' set the LAT register first and then the TRIS so you won't get a glitch in case the LAT was 1
It's interesting you mention 50-100us bit timing. I don't see anything in the datasheet that makes me think it would need to be that slow, but the arduino code I ported has it set for 100us too.
The TM1637.bas module has a setting for that...
#option TM1637_BIT_DELAY = 5 // bit high/low time, in usecs
I don't think it would matter much in this instance, but when you go to enable the output low I'd swap the order just to be safe...The subroutines I'm using to control the clock and DIO lines are below. I believe these should be doing the task.
Code: Select all
Else
Output(TM1637Clock) // this clears the TRIS bit making it an output with whatever's currently in the LAT register
TM1637clock = 0 // this writes to the LAT register... and now it goes low
Code: Select all
Else
TM1637clock = 0 // this writes to the LAT register...
Output(TM1637Clock) // this clears the TRIS bit making it an output low
'Low()' and 'High()' set the LAT register first and then the TRIS so you won't get a glitch in case the LAT was 1
It's interesting you mention 50-100us bit timing. I don't see anything in the datasheet that makes me think it would need to be that slow, but the arduino code I ported has it set for 100us too.
The TM1637.bas module has a setting for that...
#option TM1637_BIT_DELAY = 5 // bit high/low time, in usecs
-
- Registered User
- Posts: 185
- Joined: Mon Mar 10, 2008 8:20 am
- Location: Seattle, WA USA
- Contact:
Re: TM1637 LED Driver
I'm putting this on hold for now until I get home in a couple weeks. Hard to troubleshoot when you can't see what's happening.
I took a look at the TM6318. It's got a more conventional shift-register type interface. It supports 8 digits instead of 6, and common anode OR cathod digits. Interestingly, it has 10 segment drivers per digit, which can be used for other LEDs. It should be a good fit for my project, at a significant increase in BOM costs....a 25% increase to 25 cents each. Which is just unbelievable!
Thanks for the help with this – I'll get back to it in a couple weeks.
I took a look at the TM6318. It's got a more conventional shift-register type interface. It supports 8 digits instead of 6, and common anode OR cathod digits. Interestingly, it has 10 segment drivers per digit, which can be used for other LEDs. It should be a good fit for my project, at a significant increase in BOM costs....a 25% increase to 25 cents each. Which is just unbelievable!
Thanks for the help with this – I'll get back to it in a couple weeks.