При разработке очередного девайса на базе digispark наткнулся на али на цифровой индикатор tm1637. Как показали эксперименты, плата действительно простая и универсальная. Цена вопроса примерно 200 рублей. Смысл в подключении по двум линиям для передачи данных, при этом никакого стандартного протокола нет. Можно сравнить с олед экраном, но проблема в том что почти все графические экраны требуют протокола i2c. А в нём есть конкретно прописанные тайминги, и фиксированная частота несущей, 400 кГц насколько помню. В простых контроллерах, вроде digispark, встроенной поддержки i2c нет, есть программная реализация, но она будет съедать много памяти и процессорного времени, и на остальные вычисления запросто может не хватить ресурсов.
Сама плата выглядит так:


#define CLK 1
#define DIO 2
#define _dash 0x40 //-
#define _degree 0x63
int c = 0;
byte tablo[5] = {0, 0, 0, 0, 7};//four digit + bright 0..7
const byte translator[16] = {0x3f, 0x06, 0x5b, 0x4f, 0x66,
0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7f,0x39, 0x3f, 0x79, 0x71};
void start(void){
digitalWrite(CLK, HIGH);
digitalWrite(DIO, HIGH);
digitalWrite(DIO, LOW);
digitalWrite(CLK, LOW);
}
void stop(void){
digitalWrite(CLK, LOW);
digitalWrite(DIO, LOW);
digitalWrite(CLK, HIGH);
digitalWrite(DIO, HIGH);
}
void writeByte(byte v){
for(byte i = 0; i < 8; i++){
digitalWrite(CLK, LOW);
digitalWrite(DIO, v & 1);
v >>= 1;
digitalWrite(CLK, HIGH);
}
digitalWrite(CLK, LOW);
digitalWrite(DIO, HIGH);
digitalWrite(CLK, HIGH);
digitalWrite(DIO, LOW);
}
void tm1637out(){
start();
writeByte(0x40);
stop();
start();
writeByte(0xc0);
writeByte(tablo[0]);
writeByte(tablo[1]);
writeByte(tablo[2]);
writeByte(tablo[3]);
stop();
start();
writeByte(0x88 + (tablo[4] & 7));
stop();
}
void setup() {
pinMode(CLK, OUTPUT);
pinMode(DIO, OUTPUT);
}
void loop() {
tablo[0] = translator[(c / 100) % 10];
tablo[1] = translator[(c / 10) % 10];
tablo[2] = translator[ c % 10];
tablo[3] = _degree;
tablo[4] = c % 8;
tm1637out();
c++;
}
Плата подключается к выводам 1 и 2. Сложного вроде ничего нет. Однако меня тут не устроила производительность.
Дело в том что хоть стандартный протокол и не воспроизводится, но управление битами происходит через digitalWrite,
что бессмысленно съедает немало процессорного времени, поэтому решил уйти от вызова этой функции. При этом
нужно подчеркнуть, следующий вариант будет работать только на digispark, так как явно указывается порт,
в который выводятся данные, порт я узнал выведя его на экран. Получился следующий оптимизированный код:
#define CLK 1
#define DIO 2
#define tm1637bit(b) (1 << (b))
#define _dash 0x40 //-
#define _degree 0x63
int c = 0;
byte tablo[5] = {0xc0, 0, 0, 0, 0};//four sign
const byte translator[16] = {0x3f, 0x06,0x5b,
0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7f,
0x39,0x3f,0x79,0x71};
void CLKDIO11(){
*(byte*)0x38 |= tm1637bit(CLK) + tm1637bit(DIO);
}
void CLK1(){
*(byte*)0x38 |= tm1637bit(CLK);
}
void DIO1(){
*(byte*)0x38 |= tm1637bit(DIO);
}
void CLK0(){
*(byte*)0x38 &= 255 - tm1637bit(CLK);
}
void DIO0(){
*(byte*)0x38 &= 255 - tm1637bit(DIO);
}
void start(void){
CLKDIO11();
DIO0();
CLK0();
}
void stop(void){
CLK0();
DIO0();
CLKDIO11();
}
void tm1637send(byte v){
for(byte i = 0; i < 8; i++){
CLK0();
if( v & 1 ){
DIO1();
}else{
DIO0();
}
v >>= 1;
CLK1();
}
CLK0();
CLKDIO11();
DIO0();
}
void tm1637out(){
start();
for(byte p = 0; p < 5; p++){
tm1637send(tablo[p]);
}
stop();
}
void tm1637bright(byte howmuch){
start();
tm1637send(0x88 + (howmuch & 7));
stop();
}
void setup() {
pinMode(CLK, OUTPUT);
pinMode(DIO, OUTPUT);
tm1637bright(7);
}
void loop() {
tablo[1] = translator[(c /1000) % 10];
tablo[2] = translator[(c / 100) % 10];
tablo[3] = translator[(c / 10) % 10];
tablo[4] = translator[ c % 10];
tm1637out();
c++;
//delay(4);
}
Из спортивного интереса снял осциллограмму и к удивлению обнаружил, что подготовка числа для
вывода занимает почти столько же процессорного времени, как и сам вывод. 92 против 100 микросекунд:

#define CLK 1
#define DIO 2
#define tm1637bit(b) (1 << (b))
#define _dash 0x40 //-
#define _degree 0x63
int c = 0;
const byte translator[16] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7f, 0x39, 0x3f, 0x79, 0x71};
void CLKDIO11(){
*(byte*)0x38 |= tm1637bit(CLK) + tm1637bit(DIO);
}
void CLK1(){
*(byte*)0x38 |= tm1637bit(CLK);
}
void DIO1(){
*(byte*)0x38 |= tm1637bit(DIO);
}
void CLK0(){
*(byte*)0x38 &= 255 - tm1637bit(CLK);
}
void DIO0(){
*(byte*)0x38 &= 255 - tm1637bit(DIO);
}
void tm1637send(byte v){
for(byte i = 0; i < 8; i++){
CLK0();
if( v & 1 ){
DIO1();
}else{
DIO0();
}
v >>= 1;
CLK1();
}
CLK0();
CLKDIO11();
DIO0();
}
void tm1637out(byte tablo[5]){
CLKDIO11();
DIO0();
CLK0();
tm1637send( 0xc0);
tm1637send(tablo[0]);
tm1637send(tablo[1]);
tm1637send(tablo[2]);
tm1637send(tablo[3]);
CLK0();
DIO0();
CLKDIO11();
CLKDIO11();
}
void tm1637bright(byte howmuch){
CLKDIO11();
DIO0();
CLK0();
tm1637send(0x88 + (howmuch & 7));
CLK0();
DIO0();
CLKDIO11();
CLKDIO11();
}
void setup() {
pinMode(CLK, OUTPUT);
pinMode(DIO, OUTPUT);
tm1637bright(7);
}
void loop() {
byte tablo[4] = {0, 0, 0, 0};//four sign
int c0;
c0 = c;
while( c0 >= 1000 ){
c0 -= 1000;
tablo[0]++;
}
while( c0 >= 100 ){
c0 -= 100;
tablo[1]++;
}
while( c0 >= 10 ){
c0 -= 10;
tablo[2]++;
}
tablo[0] = translator[tablo[0]];
tablo[1] = translator[tablo[1]];
tablo[2] = translator[tablo[2]];
tablo[3] = translator[c0];
tm1637out(tablo);
c++;
if( c >= 10000 ) c = 0;
//delay(250);
}
Получилась следующая осциллограмма:

Что имеем в итоге - плата неприхотливая, удобная и универсальная. Вырисовывается вариант спидометра/одометра на digispark и двух-трёх tm1637.
Давно уже купил на Али 6-значные платы на tm1637, выдалась свободная минутка, решил опробовать. Но на этот раз я скачал даташит и работу с платой привёл в соответствие с ним. В прошлый раз я творчески интерпретивал чужую библиотеку, при помощи метода научного тыка. По сути не так и сложно вышло, например биты данных устанавливать при нулевом уровне строба:


#define CLK 1
#define DIO 3
#define tm1637bit(b) (1 << (b))
#define _dash 0x40 //-
#define _degree 0x63
/*
# DIO change only CLK = Low
# Byte send start:
CLK = High; DIO to Low
so Idle CLK = High, DIO = High
# After send one (9th) CLK = High with DIO = Low, then idle both High
*/
long c = 0;
const byte translator[16] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7f, 0x39, 0x3f, 0x79, 0x71};
void CLK1(){
*(byte*)0x38 |= tm1637bit(CLK);
}
void DIO1(){
*(byte*)0x38 |= tm1637bit(DIO);
}
void CLK0(){
*(byte*)0x38 &= 255 - tm1637bit(CLK);
}
void DIO0(){
*(byte*)0x38 &= 255 - tm1637bit(DIO);
}
void tm1637idle(){
DIO1();
CLK1();
}
void tm1637cmdStart(){
DIO0();
}
void tm1637send(byte v){
for(byte i = 0; i < 8; i++){
CLK0();
if( v & 1 ){
DIO1();
}else{
DIO0();
}
v >>= 1;
CLK1();
}
CLK0();
DIO0();
CLK1();//9th stop CLK, wait for ACK
CLK0();
}
void tm1637out(byte tablo[6], byte bright){
tm1637cmdStart();
tm1637send( 0x40);//0x40 mode = output, autoincrement
CLK1();
DIO1();
DIO0();
CLK0();
tm1637send( 0xc0);//0xc0 address set command, 0..5 address
//210543 for 6-digit
//2345 for 4-digit
tm1637send(tablo[2]);
tm1637send(tablo[1]);
tm1637send(tablo[0]);
tm1637send(tablo[5]);
tm1637send(tablo[4]);
tm1637send(tablo[3]);
/*
tm1637send(tablo[2]);
tm1637send(tablo[3]);
tm1637send(tablo[4]);
tm1637send(tablo[5]);*/
CLK1();
DIO1();
DIO0();
CLK0();
tm1637send(0x80 + (bright & 15)); //0x80 control 0x8 - On, 0-7 - bright
tm1637idle();
}
void setup() {
pinMode(CLK, OUTPUT);
pinMode(DIO, OUTPUT);
tm1637idle();
}
void loop() {
byte tablo[6] = {0, 0, 0, 0, 0, 0};
long c0;
c0 = c;
while( c0 >= 100000 ){
c0 -= 100000;
tablo[0]++;
}
while( c0 >= 10000 ){
c0 -= 10000;
tablo[1]++;
}
while( c0 >= 1000 ){
c0 -= 1000;
tablo[2]++;
}
while( c0 >= 100 ){
c0 -= 100;
tablo[3]++;
}
while( c0 >= 10 ){
c0 -= 10;
tablo[4]++;
}
tablo[0] = translator[tablo[0]];
tablo[1] = translator[tablo[1]];
tablo[2] = translator[tablo[2]];
tablo[3] = translator[tablo[3]];
tablo[4] = translator[tablo[4]];
tablo[5] = translator[c0];
tm1637out(tablo, 15);
c += 5;
if( c >= 1000000 ) c = 0;
delay(10);
}
Пример работы ниже. Теоритическая максимальная скорость отображения 6 знаков примерно 500`000 / (9 * 9) = порядка 6000 fps.
Напомню, с 4 значным было более 8000 fps.