So I ported some my old (working) MicroPython TM1637 code over to the Milk-V Duo and... it wouldn't work. Then, out of the blue, it worked, one time, and then never worked again. Very weird, because I could look at the output with the logic analyzer and it looked fine. Well... as fine as it's possible to look - the TM1637 protocol is like backward i2c (LSB first) with no address. So in the logic analyzer it doesn't decode properly. You have to just read the DIO pulses (bits) on the screen backward to see what it's doing.

I spent SO many hours trying all kinds of things, and looking at various TM1637 library source code - they were all doing pretty much the same thing I was. Nothing worked and I had no clue why.

Finally I looked at someone's source and they did something a little bit different. I tried it in my code and the display lit up and worked.

Here's what I found. In my TM1637 init() I was doing this:

void init(void){      //initialize TM1637
  start();
  byteout(0x40);      //write data to display register, auto address, normal
  byteout(0x8c);      //display on - brightness (0x88 to 0x8f)
  stop();
}

On a slower MCU that works just fine, but the TM1637 wants a little time to process the 0x40 before you hit it with the 0x8c, and the Milk-V Duo is clocked pretty fast. I found that I could either add a stop() and a start() between bytes:

  void init(void){      //initialize TM1637
  start();
  byteout(0x40);      //write data to display register, auto address, normal
  stop();
  start();
  byteout(0x8c);      //display on - brightness (0x88 to 0x8f)
  stop();
}

or a short delay:

  void init(void){      //initialize TM1637
  start();
  byteout(0x40);      //write data to display register, auto address, normal
  usleep(1000);       //could probably be shorter - haven't tested
  byteout(0x8c);      //display on - brightness (0x88 to 0x8f)
  stop();
}

and the display now works perfect. Whew! That took WAY too long to figure out.

I'm using pins 16 and 17 to control the TM1637. When the Milk-V Duo reboots it defaults to having those pins set as UART. To make the pins GPIO so my program will work means pin_muxing them to GPIOs. WiringX should do that for me, but it doesn't yet - they're working on it. I use a tiny shellscript to run the program. It sets the two pins to GPIO and then runs the program:

cvi_pinmux -w UART0_TX/XGPIOA_16
cvi_pinmux -w UART0_RX/XGPIOA_17
./tm1637_4digit

tm1637_4digit.c

#include <unistd.h>
#include <wiringx.h>
#include "tm1637_4digit.h"

int dio = 16;
int clk = 17;

// 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,h,l,p,3-lines,blank
unsigned char font[22] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x76,0x38,0x73,0x49,0x00};
int div[5] = {0x1000,0x100,0x10,0x01};
int digit[5] = {0,0,0,0};

int main(){
  if(wiringXSetup("duo", NULL) == -1) { //wiringx initialize
    wiringXGC();
    return -1;
  }

  pinMode(dio,PINMODE_OUTPUT);  //set up dio & clk pins
  pinMode(clk,PINMODE_OUTPUT);
  digitalWrite(dio, LOW);       //dio == 0 when set as output
  digitalWrite(clk, LOW);       //clk == 0 when set as output
  pinMode(dio,PINMODE_INPUT);   //dio pin high
  pinMode(clk,PINMODE_INPUT);   //clk pin high

  init();                       //initialize TM1637

  help();                       //print HELP
  sleep(3);

  while(1){                     //print $0000 to $ffff
    for(int x=0;x<0x10000;x++)
      hexconv(x,0,0);
  }
}

//display 4-digit number in hex
void hexconv(int num,int leadzero,int colon){
  int x;
  for(x=0;x<4;x++){
    digit[x] = num / div[x];
    num = num - (digit[x] * div[x]);
  }
  if(leadzero == 0)
    leadz();
  start();
  byteout(0xc0);
  for(x=0;x<4;x++){
    if(x == 1 & colon == 1)
      byteout(font[digit[x]] + 0x80);
    else
      byteout(font[digit[x]]);
  }
  stop();
  usleep(50);
}

void leadz(void){                   //suppress leading zeroes
  int x;
  for(x=0;x<4;x++){
    if(digit[x] != 0)
      return;
    else
      digit[x] = 20;
  }
}

void byteout(unsigned char cmd){    //write one byte to TM1637
  int x;
  unsigned char ack;
  for(x=0;x<8;x++){                 //clock out cmd byte
    pinMode(clk,PINMODE_OUTPUT);    //clk low
    usleep(50);
    if(cmd & 0x01)                  //set data bit
      pinMode(dio,PINMODE_INPUT);   //dio high
    else
      pinMode(dio,PINMODE_OUTPUT);  //dio low
    usleep(50);
    pinMode(clk,PINMODE_INPUT);     //clk high
    usleep(50);
    cmd >>= 1;                      //rotate next bit into position
  }
  pinMode(clk,PINMODE_OUTPUT);      //do 9th clock pulse - clk low
  pinMode(dio,PINMODE_INPUT);       //dio input
  usleep(25);
  pinMode(clk,PINMODE_INPUT);       //clk high
  usleep(25);
  ack = digitalRead(dio);           //read the ack/nack
  if(ack == 0)
    pinMode(dio,PINMODE_OUTPUT);
  usleep(50);
  pinMode(clk,PINMODE_OUTPUT);      //clk low
  usleep(50);
}

void start(void){
  pinMode(dio,PINMODE_OUTPUT);  //dio low
  usleep(50);
}

void stop(void){
  pinMode(dio,PINMODE_OUTPUT);  //dio low
  usleep(50);
  pinMode(clk,PINMODE_INPUT);   //clk high
  usleep(50);
  pinMode(dio,PINMODE_INPUT);   //dio high
  usleep(50);
}

void brightness(unsigned char level){       //0 - 7
  level = level + 0x88;
  start();
  byteout(level);               //display on - brightness
  stop();
}

void init(void){      //initialize TM1637
  start();
  byteout(0x40);      //write data to display register, auto address, normal
  stop();
  start();
//  usleep(1000);
  byteout(0x8c);      //display on - brightness (0x88 to 0x8f)
  stop();
}

  void help(void){    //display HELP
  start();
  byteout(0xc0);
  byteout(font[0x10]);
  byteout(font[0x0e]);
  byteout(font[0x11]);
  byteout(font[0x12]);
  stop();
}

tm1637_4digit.h

void help(void);
void hexconv(int,int,int);
void leadz(void);
void byteout(unsigned char);
void start(void);
void stop(void);
void brightness(unsigned char);
void init(void);

Next Post Previous Post