Главный недостаток предыдущей версии было резкое включение мощного вентилятора. Из-за высокого уровня постороннего шума при работе мощного вентилятора на малых оборотах, мне пришлось добавить постоянно работающий тихий маломощный вентилятор. После перебора нескольких видов вентиляторов с алиэкспресс (рассматривались только модели 120*120*38) нашёлся супервентилятор San Ace 9SG1212P1G06:

При разработке данного устройства я обнаружил ещё пару интересных особенностей digispark. Все они вытекают из главной фишки, возможности прошивки платы от компьтера через стандарный USB.
Под подключение tm1637 я выделил под неё выводы 1 и 3. С первым проблем не было, а вот 3й используется при подключении к USB. В итоге при подключении к разным источникам питанияс присоединенным tm1637 скетч иногда зависал при включении и не стартовал. Причина в том, что digispark 4 секунды ждёт подключения к компьютеру, а tm1637 данные не только принимает, но и передаёт. В итоге загрузчик думает, что его сейчас прошивать будут и не запускает основной код. Решение логическая развязка:


Следующая проблема запуска digispark в том, что на время ожидания прошивки - 4 сек - его выходы находятся в подвешенном состоянии, не притянуты ни к земле, ни к 5 Вольтам. В чём проявилась проблема - вентиляторы крутятся на минимальных оборотах при входе PWM, притянутом к земле, в результат при включении вентиляторы за 4 сек пытались раскрутиться до максимума, что немного шумновато :) Решение та же логическая развязка. Сперва попробовал вариант с инверсией. К тому времени пришли с китая маломощные полевики 2sk3019, попробовал собрать на нём:

Ещё была проблема, что tm1637 не работал, пока я с платы digispark не выпаял резистор подтягивающий выход 3 к 5 Вольтам, но это ошибочный путь, более правильный логическая развязка.
Поскольку планировалось ставить несколько мощных вентиляторов 4 шт * 12 Вольт * 4 Ампера = 192 Ватта :) силовой части пришлось уделить дополнительное внимание. Да конечно, большую часть времени вентиляторы работают практически на ХХ, но если вдруг приспичит развить максимальную мощность не хотелось бы фейерверков. Начал с разьёма подключения вентилятора, заказал тройники с алиэкспресса:

Вместо подключения питания от SATA разъёма, который сам по себе небольшого сечения, да ещё и бюджет линии БП не резиновый, взял провод подачи питания на видеокарту 6 pin. Разъём можно или взять с б\у видеокарты с авито, или закать на али:

Схема выглядит следующим образом:

Плата до добавления двух tc4420 выглядела так:



При практической эксплутации индикатора на максимальной яркости плата digispark сильно грелась, причина была в высокой нагрузке на преобразователь 12 Вольт -> 5 Вольт, отчего пришлось программно убавить яркость индикатора. В общем при большом количестве потребителей 5 Вольт желательно предусмотреть отдельную линию.
У данного устройства было две версии кода, промежуточную приводь смысла не вижу, сразу окончательная:
#include <core_adc.h>
#define CLK 3
#define DIO 1
#define tm1637bit(b) (1 << (b))
#define _dash 0x40 //-
#define _degree 0x63
#define _low_degree 0x5c
#define avr_iterations_pow2 12 //1 = 2, 10 = 1024
const byte tm1637translator[17] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,
0x7f, 0x6f, 0x77, 0x7f, 0x39, 0x3f, 0x79, 0x71, 0};
const unsigned char lm19translator[380] = { 254, 254, 254, 254, 254, 254, 254, 254,
254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 253,
252, 251, 251, 250, 249, 248, 248, 247, 246, 245, 244, 244, 243, 242, 241, 241,
240, 239, 238, 237, 237, 236, 235, 234, 233, 233, 232, 231, 230, 229, 229, 228,
227, 226, 226, 225, 224, 223, 222, 222, 221, 220, 219, 218, 218, 217, 216, 215,
214, 214, 213, 212, 211, 211, 210, 209, 208, 207, 207, 206, 205, 204, 203, 203,
202, 201, 200, 199, 199, 198, 197, 196, 195, 195, 194, 193, 192, 191, 191, 190,
189, 188, 187, 187, 186, 185, 184, 183, 183, 182, 181, 180, 179, 179, 178, 177,
176, 175, 175, 174, 173, 172, 171, 171, 170, 169, 168, 167, 167, 166, 165, 164,
163, 163, 162, 161, 160, 159, 159, 158, 157, 156, 155, 155, 154, 153, 152, 151,
150, 150, 149, 148, 147, 146, 146, 145, 144, 143, 142, 142, 141, 140, 139, 138,
138, 137, 136, 135, 134, 133, 133, 132, 131, 130, 129, 129, 128, 127, 126, 125,
125, 124, 123, 122, 121, 120, 120, 119, 118, 117, 116, 116, 115, 114, 113, 112,
111, 111, 110, 109, 108, 107, 107, 106, 105, 104, 103, 102, 102, 101, 100, 99,
98, 98, 97, 96, 95, 94, 93, 93, 92, 91, 90, 89, 88, 88, 87, 86, 85, 84, 84, 83,
82, 81, 80, 79, 79, 78, 77, 76, 75, 74, 74, 73, 72, 71, 70, 69, 69, 68, 67, 66,
65, 64, 64, 63, 62, 61, 60, 59, 59, 58, 57, 56, 55, 54, 54, 53, 52, 51, 50, 49,
49, 48, 47, 46, 45, 44, 44, 43, 42, 41, 40, 39, 39, 38, 37, 36, 35, 34, 34, 33,
32, 31, 30, 29, 28, 28, 27, 26, 25, 24, 23, 23, 22, 21, 20, 19, 18, 18, 17, 16,
15, 14, 13, 12, 12, 11, 10, 9, 8, 7, 7, 6, 5, 4, 3, 2};
byte old_pwm;
void CLKDIO11(){
// *(byte*)0x38 |= tm1637bit(CLK) + tm1637bit(DIO);
CLK1();
DIO1();
}
void CLK1(){//inverse
*(byte*)0x38 |= tm1637bit(CLK);
}
void DIO1(){
*(byte*)0x38 |= tm1637bit(DIO);
}
void CLK0(){//inverse
*(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[4], byte howmuch){
CLKDIO11();
DIO0();
CLK0();
tm1637send( 0xc0);
tm1637send(tablo[0]);
tm1637send(tablo[1]);
tm1637send(tablo[2]);
tm1637send(tablo[3]);
DIO0();
CLK0();
CLKDIO11();
CLKDIO11();
CLKDIO11();
DIO0();
CLK0();
tm1637send(0x88 + (howmuch & 7));
DIO0();
CLK0();
CLKDIO11();
CLKDIO11();
}
void setup() {
pinMode(0, OUTPUT);
digitalWrite(0, LOW);//pwm
pinMode(CLK, OUTPUT);
pinMode(2, INPUT);//t in
pinMode(DIO, OUTPUT);
pinMode(4, INPUT);//t out
byte tablo[4] = {_dash, _dash, _dash, _dash};
tm1637out(tablo, 7);
}
void loop() {
int t_in, t_out, diff;
word new_pwm;
long avr_t_in, avr_t_out;
avr_t_in = 0;
avr_t_out = 0;
for(int c = 0; c < (1 << avr_iterations_pow2); c++){
ADC_SetInputChannel((adc_ic_t) 1);
ADC_StartConversion();
while( ADC_ConversionInProgress() );
avr_t_in += ADC_GetDataRegister();
ADC_SetInputChannel((adc_ic_t) 2);
ADC_StartConversion();
while( ADC_ConversionInProgress() );
avr_t_out += ADC_GetDataRegister();
}
t_in = avr_t_in >> avr_iterations_pow2;
t_out = avr_t_out >> avr_iterations_pow2;
if( t_in >= 379 ){ t_in = 379; }
t_in = lm19translator[t_in];
if( t_out >= 379 ){ t_out = 379; }
t_out = lm19translator[t_out];
diff = (t_out - t_in);
if( diff <= 20 ){
new_pwm = 0;
}else{
if( diff >= 70 ){
new_pwm = 100;
}else{
new_pwm = diff - 20;
new_pwm = new_pwm << 1;
}
}
if( new_pwm >= 100 ) new_pwm = 254;
if( new_pwm < old_pwm ){
new_pwm = old_pwm - ((old_pwm - new_pwm) >> 3) - 1;
}
analogWrite(0, new_pwm);
old_pwm = new_pwm;
byte tablo[4] = {0, 0, 0, 0};
word x = 0;
byte x100 = 0, x10 = 0;
x = t_in;
if( (x & 1) > 0 ){ tablo[2] = tm1637translator[5]; }
else{ tablo[2] = tm1637translator[0]; }
x >>= 1;
x100 = x10 = 0;
if( x > 999 ) x = 999;
while( x >= 100 ){ x -= 100; x100++; } if( x100 == 0 ) x100 = 17;
while( x >= 10 ){ x -= 10; x10++; }
tablo[0] = tm1637translator[x10];
tablo[1] = tm1637translator[x] + 128;
tablo[3] = _low_degree;
tm1637out(tablo, 5);
delay(1000);
x = t_out;
if( (x & 1) > 0 ){ tablo[2] = tm1637translator[5]; }
else{ tablo[2] = tm1637translator[0]; }
x >>= 1;
x100 = x10 = 0;
if( x > 999 ) x = 999;
while( x >= 100 ){ x -= 100; x100++; } if( x100 == 0 ) x100 = 17;
while( x >= 10 ){ x -= 10; x10++; }
tablo[0] = tm1637translator[x10];
tablo[1] = tm1637translator[x] + 128;
tablo[3] = _degree;
tm1637out(tablo, 5);
delay(1000);
tablo[0] = 0;
x = new_pwm;
x100 = x10 = 0;
if( x > 999 ) x = 999;
while( x >= 100 ){ x -= 100; x100++; } if( x100 == 0 ) x100 = 17;
while( x >= 10 ){ x -= 10; x10++; }
tablo[1] = tm1637translator[x100];
tablo[2] = tm1637translator[x10];
tablo[3] = tm1637translator[x];
tm1637out(tablo, 5);
}
Пояснения по коду - отражается температура на входе, потом на выходе, затем уровень PWM сигнала, доли от 256. Алгоритм расчета уровня PWM - вычитаем из выходной температуры входящую, из разницы вычитаем 10, если больше 0, то умножаем на 4. Замер температуры идёт с максимальной точностью - 0.5 градуса. Из приколов сперва мерял температуру один раз, разброс был до трёх градусов:
Ещё из интересных фич - при сбросе уровня он снижается за одну итерацию на 1/8 + 1 пункт. Это сделано для плавного уменьшения шума, эффект от этого сугубо положительный. Итог радует:
По итогу устройство выполняет все необходимые функции и при необходимости легко перепрошивается под новые условия.