-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make sure clock pulses have a defined length #7
base: master
Are you sure you want to change the base?
Conversation
Currently, clock pulses can be arbitrarily short, as the whole clock delay is done after setting clk to low, setting the data bit, and setting clk to high again. On fast processors, the clk pulse can become too short to be recognized reliably, which may be the cause for issue igelbox#5. To fix that, split the delay in two equal halves, and do one of them while the clock is low. I duplicated all other calls to delay() to keep the delay lengths stable. This might not be necessary, but shouldn't hurt either.
I do believe this could be fixed outside of the library that's why I force to supply the So, for now I'm not sure this PR changes are required for all of the devices this library may work on. |
The issue is not that I'm not sure about the exact specs of the tm1637, but according do this data sheet, the clock pulse must be at least 400ns long: https://raw.githubusercontent.com/avishorp/TM1637/master/docs/TM1637_V2.4_EN.pdf The issue came up with an rp2040, which runs at 125MHz, and the code is optimized down to 6 clock cycles (if I counted correctly), making the clock pulse 48ns long. Much shorter than the specified minimum. I think the safest option is to create a clock signal with 50% duty cycle, and that's exactly what I implemented with the patch. |
Ouch, now I see. Thank you for being patient and outlining the bug. Also, it seems my code for reading ACKs is weird and even dangerous coz it ties the pin to 1 but chip does "lower the DIO pin" in response which may cause a quick short circuit:shit: UPD: ah I used |
@@ -141,7 +144,7 @@ where | |||
|
|||
const MAX_FREQ_KHZ: u16 = 500; | |||
const USECS_IN_MSEC: u16 = 1_000; | |||
const DELAY_USECS: u16 = USECS_IN_MSEC / MAX_FREQ_KHZ; | |||
const DELAY_USECS: u16 = USECS_IN_MSEC.div_ceil(MAX_FREQ_KHZ * 2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The datasheet says "the clock frequency should be less than 250K" and now I have no idea why this library works at all =) coz it sends data at 500khz speed.
So I believe we could leave the original value here then 2 delays would result in 500khz / 2 = 250khz clock frequency (like datasheet requires).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
He-he, here it was speedup
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://raw.githubusercontent.com/avishorp/TM1637/master/docs/TM1637_V2.4_EN.pdf specifies the maximum frequency as 500kHz. Perhaps there are different revisions of the chip with different requirements?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And they mention "the clock frequency should be less than 250K". However it may relate to reading not writing. Not sure though.
I had just one chip. I'm not an experienced developer of embeddeds 🤷)
@@ -128,6 +130,7 @@ where | |||
} else { | |||
self.dio.set_low()?; | |||
} | |||
self.delay(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please move this before if
and test whether it works or not? It would make it more symmetric, e.g.:
- low CLK, delay
- set DIO
- high CLK, delay
- (sometimes in
start
andstop
routines) set DIO
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that sounds like a good idea. The current patch is intentionally doing the minimal change to make the clock pulse longer if necessary.
Do you have a better data sheet than https://raw.githubusercontent.com/avishorp/TM1637/master/docs/TM1637_V2.4_EN.pdf? The timing information in that one confuses me a bit. It doesn't help that the table on the bottom of page 10 lists some minimum timings without exactly telling what they measure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait: No! With that change, now there is no longer a delay between two bits:
- first bit:
- low CLK
- delay
- set DIO
- delay
- high CLK
- next bit:
- low CLK
- ...
So there's the same problem as initially, only that now the high time between two bits can become arbitrarily short. That doesn't work reliably either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also tested this variant, it works but is less reliable:
fn send_bit_and_delay(&mut self, value: Bit) -> Res<E> {
self.clk.set_low()?;
self.delay();
if let Bit::ONE = value {
self.dio.set_high()?;
} else {
self.dio.set_low()?;
}
self.clk.set_high()?;
self.delay();
Ok(())
}
In this combination, if I use a push-pull pin for CLK, it does no longer work.
I think the issue with this is that the DIO change happens to shortly before the rising edge of CLK. If the DIO line rises slightly slower than the CLK line, the data line will be sampled before the signal level is valid.
(This could also happen if both CLK and DIO are open-drain outputs, if the rise times are different due to tolerances in the pull-up resistors.)
In summary, this version seems to be the most reliable one, at least without adding additional delay statements:
fn send_bit_and_delay(&mut self, value: Bit) -> Res<E> {
self.clk.set_low()?;
if let Bit::ONE = value {
self.dio.set_high()?;
} else {
self.dio.set_low()?;
}
self.delay();
self.clk.set_high()?;
self.delay();
Ok(())
}
Another option would be to half the individual delays again, and have delays both before and after setting DIO. But that would require delays shorther than 1µs, or would slow down the communication.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
due to tolerances in the pull-up resistors
Darn circuits) Also there may be some filtering capacitors (like mentioned in http://www.microcontroller.it/english/Tutorials/Elettronica/componenti/TM1637.htm) which may make signal fronts less predictable. I think the best/safest solution is shift DIO vs CLK in 90 degree out of phase, e.g.:
- low CLK
- delay 1µs
- set DIO
- delay 1µs
- high CLK
- delay 2µs
but yeah it'll slowdown the communication to about 250khz
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have a better data sheet than
Nope. Moreover, I cannot recall whether I followed some datasheet while implementing the lib or just looked into some Arduino C++ libs implementations (
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait: No! With that change, now there is no longer a delay between two bits:
But lib has delay just after set_high -
Line 135 in 25e5e7e
self.delay(); |
In this combination, if I use a push-pull pin for CLK, it does no longer work.
Okay. I have no hardware to test now. So, I'll choose the variant that works for you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it work with just one additional delay
in the send_bit_and_delay
method? (I mean without dividing the DELAY_USECS by 2)
This change was too hasty: While now there are nice delays between CLK and DIO changes, there's no longer a delay between CLK rising at the end of a bit, and CLK falling for the next bit. That doesn't work. This reverts commit 5b75875.
Yes, that's nothing the library can change, as the pin config gets done by the caller. Perhaps add some doc comment explaining that the DIO pin should be configured as open-drain output? |
Well, it appears the library already forces the DIO pin to be open-drain by
|
Currently, clock pulses can be arbitrarily short, as the whole clock delay is done after setting clk to low, setting the data bit, and setting clk to high again. On fast processors, the clk pulse can become too short to be recognized reliably, which may be the cause for issue #5.
To fix that, split the delay in two equal halves, and do one of them while the clock is low.
I duplicated all other calls to delay() to keep the delay lengths stable. This might not be necessary, but shouldn't hurt either.