#gpio #tm1637 #driver #micro-controller #display #interface #generic

no-std tm1637-gpio-driver

Generic GPIO driver for the TM1637 micro controller, primarily for educational purpose. Doesn't need std-lib and you can choose any GPIO interface/library you want.

17 stable releases

2.0.6 Oct 2, 2022
2.0.5 Apr 1, 2021
2.0.4 Oct 27, 2020
2.0.2 Jul 7, 2020
1.2.2 Jul 5, 2020

#51 in Embedded development

MIT license

64KB
756 lines

Generic TM1637 GPIO Driver

Generic GPIO driver for the TM1637 microcontroller, primarily for educational purpose. For example, the TM1637 built into the 4-digit 7-segment display by AZ-Delivery (Link). Generic means that it is not dependent on a specific GPIO interface. You can choose the GPIO interface/library on your own.

TL;DR: minimal setup

Cargo.toml

[dependencies]
tm1637-gpio-driver = "<insert latest version>"
# or if you need no_std
tm1637-gpio-driver = { version = "<insert latest version>", default-features = false }

Code

use std::thread::sleep;
use std::time::Duration;
use tm1637_gpio_driver::gpio_api::setup_gpio_cdev;
use tm1637_gpio_driver::TM1637Adapter;

// example that definitely works on Raspberry Pi
fn main() {
    // use any GPIO pin you want. This is the number of the pin on the board.
    let (clk_pin, dio_pin) = (18, 23);

    let bit_delay_fn = Box::from(|| sleep(Duration::from_micros(10)));
    let tm1637display = setup_gpio_cdev(clk_pin, dio_pin, bit_delay_fn, "/dev/gpiochip0");

    // display "1 2 3 4"
    let data: [u8; 4] = [
        TM1637Adapter::encode_digit(1),
        TM1637Adapter::encode_digit(2),
        TM1637Adapter::encode_digit(3),
        TM1637Adapter::encode_digit(4),
    ];
    tm1637display.write_segments_raw(&data, 0);
}

no_std

This crate is #![no_std] An allocator is necessary on embedded systems because extern crate alloc (core library) is used. In #![no_std] you have to disable the default features in Cargo.toml.

This driver works with other displays too if they use a TM1637 micro controller with the same I2C-like serial bus protocol specified in the data sheet.

I created this library/driver for fun and to learn new things!

See this demo (gif) I made with my Raspberry Pi using regular GPIO pins:

Moving Text:

gpio demonstration

Time with blinking double point:

gpio demonstration

How does this work? How do I write a driver for that thing?

This was my first time writing a (super simple basic) kind of a device driver. As of now I'm not that much experienced with micro controllers. After some time I understood how it works by looking at the data sheet. Have a look into my code too! I tried to make as many comments as possible.

How can I use it?

You can find code examples in the github repository!

My driver/library is not dependent on a specific GPIO interface. You can use crates.io: wiringpi or crates.io: gpio for example. I strongly recommend crates.io: gpio_cdev. I tested them on my Raspberry Pi. My TM1637Adapter needs functions/closures as parameters. These functions are wrappers to write High/Low to the desired Pins.

There are also utility functions on top of the driver in the module fourdigit7segdis for the 4-digit 7-segment display. You can use them, learn from them or just write your own functions on top of the driver.

To add this driver to your project just add the crate to your Rust project.

Supported GPIO interfaces/libs/crates

As I already said this crate is independent from a specific strategy to access GPIO. But I provide several setup functions for different strategies as listed below (all of them need standard library). To use them activate on of the features in your Cargo.toml:

  • gpio-api-gpio_cdev
    • provides a setup function for the TM1637Adapter that uses "gpio_cdev"-crate as GPIO interface
    • tm1637_gpio_driver::gpio_cdev::setup_gpio_cdev()
    • this uses the character device driver-based api/interface in the Linux kernel
    • This is the RECOMMENDED, modern way! Sysfs is deprecated
  • gpio-api-gpio
    • provides a setup function for the TM1637Adapter that uses "gpio"-crate as GPIO interface
    • tm1637_gpio_driver::gpio_api::setup_gpio()
    • this uses the "sysfs"-Interface which probably requires root/sudo when executed
  • gpio-api-sysfs_gpio
    • provides a setup function for the TM1637Adapter that uses "sysfs_gpio"-crate as GPIO interface
    • tm1637_gpio_driver::sysfs_gpio::setup_sysfs_gpio()
    • this uses the "sysfs"-Interface which probably requires root/sudo when executed
    • this uses the "sysfs"-Interface which probably requires root/sudo when executed
  • gpio-api-wiringpi
    • provides a setup function for the TM1637Adapter that uses "wiringpi"-crate as GPIO interface
    • tm1637_gpio_driver::sysfs_gpio::setup_wiringpi()
    • make sure "wiringpi" is installed on your Pi

Does this work only on Raspberry Pi?

Probably no! Although I can't test it because I don't have an Arduino or another similar device. This should work on every device where you can write a Rust program for. Since this lib uses no standard library this should work on embedded devices. If you use it let me know what things you've built!

But yes, it was only tested using regular GPIO pins on my Raspberry Pi running Rasbperry Pi OS so far.

Who Am I?

I'm Philipp :) Feel free to contribute on Github or message me on Twitter (https://twitter.com/phip1611)!

Special thanks

Special thanks to the creator of the driver for the Arduino. His/her (? - don't know) driver for the Arduino platform helped me to understand how the TM1637 micro controller works. With this work and my effort I put into understanding the data sheet I could make this driver. I also learned a lot about serial data transfer and the I2C-like serial bus protocol used by the TM1637.

I don't use any of the code. It just gave me some inspiration.

Troubleshooting

  • Data is not correctly displayed on display
    • either your device is broken (I ordered 3 and 1 of 3 were broken) or you probably have a too high frequency. Make sure the bit-delay for TM1637Adapter::new is not too short. 100µs on Raspberry Pi should be totally fine (but 1µs worked also for me)
    • check cables and GPIO-pins (clk, dio)
  • Raspberry Pi / Raspberry Pi OS
    • "Permission denied"
      • make sure your user is part of the "gpio" group
      • sudo usermod -a -G gpio <your-user-name>
  • bit delay function: no difference between 1 and 100µs
    • if you use thread::sleep() as your bit delay function then you gonna have a problem when it comes to a few micro seconds: the operating system (or better to say the hardware) is not fast enough for switching threads in that short period of time
    • in that case you should use a "busy waiting"-like approach that doesn't send the thread into sleep mode but wait in a loop until a certain time has been reached.

MSRV

The MSRV is 1.52.1.

Trivia

  • There is another library on crates.io for the TM1637: https://github.com/igelbox/tm1637-rs It uses the "embedded-hal"-crate and takes another approach. Check this out too. :)

  • I don't know if "driver" is the right word for this because it is not tied to the operating system. But the display is a device and my library can talk with it .. so yes.. basically a driver, right?

Dependencies

~0–495KB