Паяльная станция на ATTiny13 для жал С245 и T12 с индикатором ТМ1637
Паяльная станция на ATTiny13 для жал С245 и T12 с индикатором ТМ1637
Продолжаю находить применение давно валявшейся без дела кучке ATTiny13.
В этот раз решил попробовать сделать простую паяльную станцию для жал С245 и Т12 с питанием от блока питания ноутбука на 19 вольт и 3,7 А. В качестве корпуса использовал старый корпус беспроводного телефона, чтобы объединить подставку с корпусом устройства.
Функционал максимально простой:
— контроль температуры на цифровом индикаторе,
— звуковая индикация основных событий,
— настройка температуры с шагом 10 градусов,
— возможность калибровки,
— сохранение заданной температуры и калибровки в ПЗУ,
— включение турбо режима (+50 градусов на 30 секунд),
— возможность отображение на индикаторе не только текущей, но и заданной температуры,
— индикация нагрева жала,
— режим «термометра», когда нагрев отключен, а индикация температуры есть,
— защита от перегрева жала (переход в режим аварии при температуре выше 500 градусов),
— возможность подстройки усилителя сигнала термопары,
— работа по режимам сна и отключения, как для ручек С245, так и Т12 с датчиком вибрации.
Для потенциальных критиков, считающих, что нужно было делать не на Тиньке, а на 328, ESP32 и т.д., скажу, что этот проект задумывался как демонстрация возможностей простенького контроллера для реализации большинства функций, достаточных основной массе любителей паяльников.
Следующим будет проект паяльной станции на основе проекта от Stefan Wagner (T12 Soldering Station V4), но с русифицированным меню, на LGT328, с автоматическим распознаванием ручек Т12 и С245 и использованием, как мощных блоков питания, так и блоков питания ноутбуков или им подобных.
Схема показана ниже:

Некоторые особенности станции:
В разъёме ручки С245 есть перемычка между контактами 2 и 4 для распознавания ручек.
С помощью этой же перемычки переключается электронный коммутатор входов BL1555, переключающий вход ОУ и вход PC817, с помощью которого выдаётся сигнал о нахождении ручки на подставке.
В скетче можно указать под какой тип ручки компилируется программа.
Переключение типа жала выполняется комментированием (для Т12), раскомментированием строки №127
(// ===== Закомvентировать при использовании Т12 =====
#define __C245__ // Установлено жало С245)
Для С245 устанавливается мощность 50%, чтобы не уходил в аварию БП ноутбука при жале с сопротивлением 2,5 — 2,7 Ом.
Для Т12 устанавливается мощность 90%, чтобы получить от БП ноутбука при жале с сопротивлением 7-8 Ом максимальную отдачу в 60 Вт.
Для звуковой индикации пришлось использовать таймер 555, который запускается при выдаче сигнала на индикацию в пятый (не существующий на индикаторе) разряд.
Компиляция для ATtiny13 с ядром MicroCore.
Работает на частоте 1,2 МГц.
Скетч использует 1012 байт (98%) памяти устройства. Есть ещё чуток на модернизацию. ))
Глобальные переменные используют 21 байт (32%) динамической памяти, оставляя 36 байт для локальных переменных.
Один цикл программы длится около 200 мс.
Этот же период в 200 мс используется для отсчёта временных интервалов в программе для таймеров сна и записи в EEPROM.
Индикация данных после нажатия кнопок длится только пока удерживается кнопка.
По умолчанию на индикаторе текущая температура с небольшим гистерезисом около целевого значения.
Есть звуковая индикация — писк 0,1 сек:
— при аварии (температура больше 500 градусов),
— при старте,
— при достижении первый раз заданной температуры,
— при переходе в режим отключения.
Управление можно реализовать джойстиком или отдельными кнопками (у меня четырёхпозиционный джойстик с нажатием кнопки).
Кнопки / джойстик:
+ — добавление заданной температуры на 10 градусов при одном нажатии / джойстик вправо
— — убавление заданной температуры на 10 градусов при одном нажатии / джойстик влево
Калиб. — калибровка температуры — добавляет по 5 градусов по кругу в диапазоне от -30 до +30 градусов / джойстик вверх
Турбо — добавляет 50 градусов нагрева на 30 секунд / джойстик вниз
Tset — показывает установленную температуру пока держишь кнопку / джойстик нажать
Термометр – отдельная кнопка — переводит схему в режим термометра (измерение температуры есть, а нагрев отключен) и обратно в режим паяльника повторным нажатием
Текущая температура показывается с учётом калибровки и температуры холодного спая.
После нажатия кнопок изменения температуры или калибровки, на дисплее пока держим кнопку отображается изменённая температура или калибровка.
Изменённая температура или калибровка запоминается в EEPROM через 30 секунд после последнего нажатия кнопки.
При следующем старте из EEPROM считываются данные заданной температуры и калибровки.
При включении режима Термометр, перестаёт работать нагрев и сигнализация об аварии (перегреве выше 500 градусов) и получаем просто измеритель температуры жала.
Можно использовать для калибровки. Индикация отличается отсутствием в правом разряде значка градуса.
Авария — переход в состояние аварии с отключением нагрева и постоянным писком происходит если температура превысила 500 градусов.
При аварии в правый разряд выводится знак "-" (минус).
Нагрев включается только если нет флага термометра и флага аварии
Температура по умолчанию при первом старте установлена на 250 градусов, калибровка на ноль градусов.
На индикатор выводится текущая температура. При этом в правом разряде отображаеся знак градуса.
При нажатии кнопки калибровки, отображается уровень поправки.
Смена показаний на индикаторе с частотой 5 Гц.
LED индикатор показывает работу нагревателя.
При установке ручки паяльника на подставку, паяльник сразу уходит в сон.
Мне так удобнее, чтобы не перегревать электронику, т.к. подставка находится в том же корпусе, где вся электроника.
Желающие могут раскомментировать строку № 391 и настроить время сна.
Температура сна — 100 градусов.
Через 210 секунд паяльник выключается. На индикаторе выводятся нули.
Кнопки подключены к ТМ1637.
Правильные HEX-коды для кнопок на линии K2
Байт передаётся младшим битом вперёд
Линия — код
SG1 — 0xEF
SG2 — 0xEE
SG3 — 0xED
SG4 — 0xEC
SG5 — 0xEB
SG6 — 0xEA
SG7 — 0xE9
SG8 — 0xE8
Ничего не нажато — 0xFF
Частота процессора 1,2 МГц.
Для АЦП устанавливаем коэффициент деления на 8 и тактовая частота АЦП составляет 150 кГц.
Это укладывается в идеальный паспортный диапазон для ATtiny13 (50–200 кГц),
при котором гарантируется максимальное разрешение в 10 бит.
Одно преобразование занимает 13 циклов АЦП (первое — 25).
При 150 кГц одно измерение длится 86,67 мкс. и один цикл длится (1/150 000 = 6.667 мкс).
Первый замер — до основного цикла — холостой, чтобы выбрать мусор из АЦП.
Серия из 16 замеров займет около 1.4 мс.
Функция _delay_ms() заменена на более лёгкую свою delayTK(), чтобы уменьшить расход памяти.
Общий алгоритм:
1. Отключить нагрев
2. Выдержать паузу 2 мс. для установки напряжения на термопаре
3. Измерить температуру (среднее по 16 замерам) и применить быстрое цифровое сглаживание (экспоненциальное скользящее среднее, EMA)
4. Проверить на аварию — если температура выше 500 градусов — отключить нагрев и включить постоянный писк. Сброс аварии только через выключение.
5. Опросить кнопки
6. Отобразить текущую температуру
7. Если ручка на подставке
==== Проверить значение таймера
===== если вышло время сна — перевести в сон и установить целевую температуру на 100.
===== если вышло время отключения — выключить нагрев и установить целевую температуру в 0.
8. если не на подставке
===== задаём целевую температуру
===== определяем необходимую мощность нагрева от типа жала
9. Если нет аварии, то управляем нагревом с плавным снижением мощности при приближении текущей температуры к заданной.
10. Если есть режим авария или термометр, нагрев отключен
11. Проверка таймера 30 секунд после изменения температуры и запись данных в EERPOM при обнулении таймера
Скетч приведён ниже:


Желающие могут поупражняться с настройками нагрева, изменяя число циклов нагрева или соотношение длительности включения и отключения нагревателя.
Также, теоретически, можно на ножке CLK в паузах между выдачей сигналов индикатору, опрашивать состояние контакта разъёма №2 и автоматически определять тип установленной ручки. У меня руки пока не дошли. ))
Продолжаю находить применение давно валявшейся без дела кучке ATTiny13.
В этот раз решил попробовать сделать простую паяльную станцию для жал С245 и Т12 с питанием от блока питания ноутбука на 19 вольт и 3,7 А. В качестве корпуса использовал старый корпус беспроводного телефона, чтобы объединить подставку с корпусом устройства.
Функционал максимально простой:
— контроль температуры на цифровом индикаторе,
— звуковая индикация основных событий,
— настройка температуры с шагом 10 градусов,
— возможность калибровки,
— сохранение заданной температуры и калибровки в ПЗУ,
— включение турбо режима (+50 градусов на 30 секунд),
— возможность отображение на индикаторе не только текущей, но и заданной температуры,
— индикация нагрева жала,
— режим «термометра», когда нагрев отключен, а индикация температуры есть,
— защита от перегрева жала (переход в режим аварии при температуре выше 500 градусов),
— возможность подстройки усилителя сигнала термопары,
— работа по режимам сна и отключения, как для ручек С245, так и Т12 с датчиком вибрации.
Для потенциальных критиков, считающих, что нужно было делать не на Тиньке, а на 328, ESP32 и т.д., скажу, что этот проект задумывался как демонстрация возможностей простенького контроллера для реализации большинства функций, достаточных основной массе любителей паяльников.
Следующим будет проект паяльной станции на основе проекта от Stefan Wagner (T12 Soldering Station V4), но с русифицированным меню, на LGT328, с автоматическим распознаванием ручек Т12 и С245 и использованием, как мощных блоков питания, так и блоков питания ноутбуков или им подобных.
Схема показана ниже:

Некоторые особенности станции:
В разъёме ручки С245 есть перемычка между контактами 2 и 4 для распознавания ручек.
С помощью этой же перемычки переключается электронный коммутатор входов BL1555, переключающий вход ОУ и вход PC817, с помощью которого выдаётся сигнал о нахождении ручки на подставке.
В скетче можно указать под какой тип ручки компилируется программа.
Переключение типа жала выполняется комментированием (для Т12), раскомментированием строки №127
(// ===== Закомvентировать при использовании Т12 =====
#define __C245__ // Установлено жало С245)
Для С245 устанавливается мощность 50%, чтобы не уходил в аварию БП ноутбука при жале с сопротивлением 2,5 — 2,7 Ом.
Для Т12 устанавливается мощность 90%, чтобы получить от БП ноутбука при жале с сопротивлением 7-8 Ом максимальную отдачу в 60 Вт.
Для звуковой индикации пришлось использовать таймер 555, который запускается при выдаче сигнала на индикацию в пятый (не существующий на индикаторе) разряд.
Компиляция для ATtiny13 с ядром MicroCore.
Работает на частоте 1,2 МГц.
Скетч использует 1012 байт (98%) памяти устройства. Есть ещё чуток на модернизацию. ))
Глобальные переменные используют 21 байт (32%) динамической памяти, оставляя 36 байт для локальных переменных.
Один цикл программы длится около 200 мс.
Этот же период в 200 мс используется для отсчёта временных интервалов в программе для таймеров сна и записи в EEPROM.
Индикация данных после нажатия кнопок длится только пока удерживается кнопка.
По умолчанию на индикаторе текущая температура с небольшим гистерезисом около целевого значения.
Есть звуковая индикация — писк 0,1 сек:
— при аварии (температура больше 500 градусов),
— при старте,
— при достижении первый раз заданной температуры,
— при переходе в режим отключения.
Управление можно реализовать джойстиком или отдельными кнопками (у меня четырёхпозиционный джойстик с нажатием кнопки).
Кнопки / джойстик:
+ — добавление заданной температуры на 10 градусов при одном нажатии / джойстик вправо
— — убавление заданной температуры на 10 градусов при одном нажатии / джойстик влево
Калиб. — калибровка температуры — добавляет по 5 градусов по кругу в диапазоне от -30 до +30 градусов / джойстик вверх
Турбо — добавляет 50 градусов нагрева на 30 секунд / джойстик вниз
Tset — показывает установленную температуру пока держишь кнопку / джойстик нажать
Термометр – отдельная кнопка — переводит схему в режим термометра (измерение температуры есть, а нагрев отключен) и обратно в режим паяльника повторным нажатием
Текущая температура показывается с учётом калибровки и температуры холодного спая.
После нажатия кнопок изменения температуры или калибровки, на дисплее пока держим кнопку отображается изменённая температура или калибровка.
Изменённая температура или калибровка запоминается в EEPROM через 30 секунд после последнего нажатия кнопки.
При следующем старте из EEPROM считываются данные заданной температуры и калибровки.
При включении режима Термометр, перестаёт работать нагрев и сигнализация об аварии (перегреве выше 500 градусов) и получаем просто измеритель температуры жала.
Можно использовать для калибровки. Индикация отличается отсутствием в правом разряде значка градуса.
Авария — переход в состояние аварии с отключением нагрева и постоянным писком происходит если температура превысила 500 градусов.
При аварии в правый разряд выводится знак "-" (минус).
Нагрев включается только если нет флага термометра и флага аварии
Температура по умолчанию при первом старте установлена на 250 градусов, калибровка на ноль градусов.
На индикатор выводится текущая температура. При этом в правом разряде отображаеся знак градуса.
При нажатии кнопки калибровки, отображается уровень поправки.
Смена показаний на индикаторе с частотой 5 Гц.
LED индикатор показывает работу нагревателя.
При установке ручки паяльника на подставку, паяльник сразу уходит в сон.
Мне так удобнее, чтобы не перегревать электронику, т.к. подставка находится в том же корпусе, где вся электроника.
Желающие могут раскомментировать строку № 391 и настроить время сна.
Температура сна — 100 градусов.
Через 210 секунд паяльник выключается. На индикаторе выводятся нули.
Кнопки подключены к ТМ1637.
Правильные HEX-коды для кнопок на линии K2
Байт передаётся младшим битом вперёд
Линия — код
SG1 — 0xEF
SG2 — 0xEE
SG3 — 0xED
SG4 — 0xEC
SG5 — 0xEB
SG6 — 0xEA
SG7 — 0xE9
SG8 — 0xE8
Ничего не нажато — 0xFF
Частота процессора 1,2 МГц.
Для АЦП устанавливаем коэффициент деления на 8 и тактовая частота АЦП составляет 150 кГц.
Это укладывается в идеальный паспортный диапазон для ATtiny13 (50–200 кГц),
при котором гарантируется максимальное разрешение в 10 бит.
Одно преобразование занимает 13 циклов АЦП (первое — 25).
При 150 кГц одно измерение длится 86,67 мкс. и один цикл длится (1/150 000 = 6.667 мкс).
Первый замер — до основного цикла — холостой, чтобы выбрать мусор из АЦП.
Серия из 16 замеров займет около 1.4 мс.
Функция _delay_ms() заменена на более лёгкую свою delayTK(), чтобы уменьшить расход памяти.
Общий алгоритм:
1. Отключить нагрев
2. Выдержать паузу 2 мс. для установки напряжения на термопаре
3. Измерить температуру (среднее по 16 замерам) и применить быстрое цифровое сглаживание (экспоненциальное скользящее среднее, EMA)
4. Проверить на аварию — если температура выше 500 градусов — отключить нагрев и включить постоянный писк. Сброс аварии только через выключение.
5. Опросить кнопки
6. Отобразить текущую температуру
7. Если ручка на подставке
==== Проверить значение таймера
===== если вышло время сна — перевести в сон и установить целевую температуру на 100.
===== если вышло время отключения — выключить нагрев и установить целевую температуру в 0.
8. если не на подставке
===== задаём целевую температуру
===== определяем необходимую мощность нагрева от типа жала
9. Если нет аварии, то управляем нагревом с плавным снижением мощности при приближении текущей температуры к заданной.
10. Если есть режим авария или термометр, нагрев отключен
11. Проверка таймера 30 секунд после изменения температуры и запись данных в EERPOM при обнулении таймера
Скетч приведён ниже:
Скетч
/****************************************************************************************************************
Паяльная станция на ATTiny13 для жал С245 и T12 с индикатором ТМ1637
В разъёме ручки С245 есть перемычка между контактами 2 и 4.
Переключение типа жала выполняется комментированием (для Т12), раскомментированием строки 127
(// ===== Закомvентировать при использовании Т12 =====
#define __C245__ // Установлено жало С245)
Для С245 устанавливается мощность 50%, чтобы не уходил в аварию БП ноутбука при жале с сопротивлением 2,5 - 2,7 Ом.
Для Т12 устанавливается мощность 90%, чтобы получить от БП ноутбука при жале с сопротивлением 7-8 Ом мксимальную отдачу в 60 Вт.
Компиляция для ATtiny13 с ядром MicroCore.
Работает на частоте 1,2 МГц.
Скетч использует 1012 байт (98%) памяти устройства. Всего доступно 1024 байт.
Глобальные переменные используют 21 байт (32%) динамической памяти, оставляя 36 байт для локальных переменных. Максимум: 64 байт.
Один цикл программы длится около 200 мс.
Этот же 200 мс период используется для отсчёта временных интервалов в программе для таймеров сна и записи в EEPROM.
Индикация данных после нажатия кнопок длится только пока удерживается кнопка.
По умолчанию на индикаторе текущая температура с небольшим гистерезисом около целевого значения.
Есть звуковая индикация - писк 0,1 сек:
- при аварии (температура больше 500 градусов)
- при старте
- при достижении первый раз заданной температуры
- при переходе в режим отключения
Управление джойстиком или кнопками.
Кнопки / джойстик:
+ - добавление температуры на 10 градусов при одном нажатии / джойстик вправо
- - убавление температуры на 10 градусов при одном нажатии / джойстик влево
Калиб. - калибровка температуры - добавляет по 5 градусов по кругу в диапазоне от -30 до +30 градусов / джойстик вверх
Турбо - добавляет 50 градусов нагрева на 30 секунд / джойстик вниз
Tset - показывает установленную температуру пока держишь кнопку / джойстик нажать
Термометр - переводит схему в режим термометра (измерение температуры есть, а нагрев отключен) и обратно в режим паяльника повторным нажатием
Текущая температура показывается с учётом калибровки и температуры холодного спая.
После нажатия кнопок изменения температуры или калибровки, на дисплее пока держим кнопку отображается изменённая температура или калибровка.
После изменения температуры или калибровки через 30 секунд изменённая температура запоминается в EEPROM.
При следующем старте из EEPROM считываются данные заданной температуры и калибровки
При установке режима Термометр, перестаёт работать нагрев и сигнализация об аварии - перегреве выше 500 градусов и получаем просто измеритель температуры жала.
Можно использовать для калибровки.Индикация отличается отсутствием в правом разряде значка градуса.
Авария - переход в состояние аварии с отключением нагрева и постоянным писком происходит если температура превысила 500 градусов.
При аварии в правый разряд выводится знак "-" (минус).
Нагрев включается только если нет флага термометра и флага аварии
Температура по умолчанию при первом старте установлена на 250 градусов, поправка на ноль градусов
На индикатор выводится текущая температура.
При нажатии кнопки калибровки, отображается уровень поправки.
Смена показаний на индикаторе с частотой 5 Гц.
LED индикатор показывает работу нагревателя.
При установке ручки паяльника на подставку, паяльник сразу уходит в сон.
Мне так удобнее, чтобы не перегревать электронику, т.к. подставка находится в том же корпусе, где вся электроника.
Желающие могут раскомментировать строку № 391 и настроить время сна.
Температура сна - 150 градусов.
Через 210 секунд паяльник выключается. НА иникаторе выводятся нули.
Правильные HEX-коды для кнопок на линии K2
Байт передаётся младшим битом вперёд
Линия - код
SG1 - 0xEF
SG2 - 0xEE
SG3 - 0xED
SG4 - 0xEC
SG5 - 0xEB
SG6 - 0xEA
SG7 - 0xE9
SG8 - 0xE8
Ничего не нажато - 0xFF
Частота процессора 1,2 МГц.
Для АЦП устанавливаем коэффициент деления на 8 и тактовая частота АЦП составляет 150 кГц.
Это укладывается в идеальный паспортный диапазон для ATtiny13 (50–200 кГц),
при котором гарантируется максимальное разрешение в 10 бит.
Одно преобразование занимает 13 циклов АЦП (первое — 25).
При 150 кГц одно измерение длится 86,67 мкс. и один цикл длится (1/150 000 = 6.667 мкс).
Первый замер - до основного цикла - холостой, чтобы выбрать мусор из АЦП.
Серия из 16 замеров займет около 1.4 мс.
Функция _delay_ms() заменена на более лёгкую свою delayTK().
Общий алгоритм:
1. Отключить нагрев
2. Выдержать паузу 2 мс для установки напряжения на термопаре
3. Измерить температуру (среднее по 16 замерам) и применить быстрое цифровое сглаживание (экспоненциальное скользящее среднее, EMA)
4. Проверить на аварию, если температура выше 500 градусов - отключить нагрев и включить постоянный писк. Сброс аварии только через выключение.
5. Опросить кнопки
6. Отобразить текущую температуру
7. Если ручка на подставке
==== Проверить значение таймера
======= если вышло время сна - перевести в сон и устанвить целевую температуру на 100
========== если вышло время отключения - выключить нагрев и установить целевую температуру в 0
8. если не на подставке
===== задаём целевую температуру
===== определяем необходимую мощность нагрева от типа жала
9. Если нет аварии, то управляем нагревом с плавным снижением мощности при приближении ктекущей температуры к заданной.
10. Если есть авария или термометр, нагрев отключен
11. Проверка таймера 30 секунд после изменения температуры и запись данных в EERPOM при обнулении таймера
***************************************************************************************************************/
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <avr/io.h>
#include <avr/eeprom.h>
//#include <avr/wdt.h>
//#include <util/delay_basic.h>
#include <avr/sleep.h> // for the sleep modes
#include <avr/interrupt.h> // for the interrupts
// ===== Закомментировать при использовании Т12 =====
#define __C245__ // Установлено жало С245
#define HEATER_ON() PORTB |= (1 << PB0) // Установка 1 на пине 5 (Включить нагрев)
#define HEATER_OFF() PORTB &= ~(1 << PB0) // Установка 0 на пине 5 (Выключить нагрев)
// Определяем пины для программного I2C для подключения к ТМ1637
#define CLK_BIT (1 << PB3) // PB3 (CLK)
#define DIO_BIT (1 << PB4) // PB4 (DIO)
// Определить состояние пинов для кодключения к TM1637
#define DIO_H() (PORTB |= DIO_BIT)
#define DIO_L() (PORTB &= ~DIO_BIT)
#define DIO_OUT() (DDRB |= DIO_BIT)
#define DIO_IN() (DDRB &= ~DIO_BIT)
#define DIO_RD() (((PINB & DIO_BIT) > 0) ? 1 : 0)
#define CLK_H() (PORTB |= CLK_BIT)
#define CLK_L() (PORTB &= ~CLK_BIT)
// --- ДОБАВИТЬ ДЛЯ обработки сигнала типа жала НА CLK ---
#define CLK_OUT() (DDRB |= CLK_BIT) // Перевод CLK на выход (для дисплея)
#define CLK_IN() (DDRB &= ~CLK_BIT) // Перевод CLK на вход (для кнопки)
//------------- ¤ркость свечени¤ индикатора ----------
#define Bright0 0x88
#define Bright1 0x89
#define Bright2 0x8A
#define Bright3 0x8B
#define Bright4 0x8C
#define Bright5 0x8D
#define Bright6 0x8E
#define Bright7 0x8F
#define SetBright Bright3 //сюда прописать яркость от 0 до 7
//---------------------- Пины назначить --------------
#define STAND_PIN PB1 // Пин контроля установки ручки на подставку
#define ADC_PIN PB2 // Пин контроля температуры - выход ОУ
//--------------------- Настроечные константы -------------------------
#define COLD_JUNCTION 25 // Температура холодного спая
#define TIME_TO_SLEEP 0 // Время до сна после установки на подставку
#define TEMP_SLEEP 100 // Температура сна
// === Глобальные переменные =========================================
uint8_t setTemp; // Заданная температура
int8_t calib; // Калибровка - переменная для поправки температуры
static uint8_t lastKey = 0xFF; // Код последней нажатой кнопки - по умолчанию - ничего не нажато
static uint8_t isReached; // Флаг: температура была достигнута
uint8_t tipType = 1; // Флаг типа жала 1 -> C245
uint8_t switchDisplay = 0; // Признак отображения одного из вариантов температуры
// 1 - заданная температура
// 2 - величина калибровки
// 0 - текущая температура
uint8_t timerSleep = 0; // Таймер сна на подставке
// Коды цифр 0-9, пусто и знаки градуса, минуса и буквы С для сегментов (A-B-C-D-E-F-G)
const uint8_t digits[] = // Коды для включения символов на дисплее
{
// Соответствие разрядов и сегментов
0x3F, // 0 - B00111111
0x06, // 1 - B00000110
0x5B, // 2 - B01011011
0x4F, // 3 - B01001111
0x66, // 4 - B01100110 _1_
0x6D, // 5 - B01101101 6 | | 2
0x7D, // 6 - B01111101 |_7_|
0x07, // 7 - B00000111 5 | | 3
0x7F, // 8 - B01111111 |_4_|
0x6F, // 9 - B01101111
0x00, // пусто - B00000000 - 10
0x63, // знак градус - B01100011 - 11
0x40, // минус - B01000000 - 12
0x39 // знак "С" - B00111001 - 13
};
// ADC interrupt service routine
EMPTY_INTERRUPT(ADC_vect); // nothing to be done here
//===================================================================================================
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++ Начало SETUP +++++++++++++++++++++++++++++++++
int main()
{
uint8_t timer30Sec = 150; // Таймер 30 секунд для счётчика времени сна и отключения
uint8_t timerEEPROM = 0; // Таймер 30 секунд записи в EEPROM
uint8_t turboTimer = 0; // Таймер режима Турбо
uint8_t failFlag = 0; // Флаг аварии
uint8_t termometrFlag = 0; // Флаг режима Термометр
uint8_t soundFlag = 1; // Флаг выдачи звука
int16_t smoothTemp = 0; // Сглаженная и с калибровкой температура
uint16_t rawTemp = 0; // Сырая температура с термодатчика
int16_t currentTemp = 0; // Текущая температура с поправкой калибровки
uint8_t timeON = 0; // Таймер 25 секунд записи в EEPROM
uint8_t timeOFF = 0; // Таймер 25 секунд записи в EEPROM
// ==== Инициализация сторожевого таймера ==============
//MCUSR = 0; // Очищаем MCUSR целиком (сбрасывает WDRF) - сбрасываем флаги перезагрузки, освобождаем доступ к WDT
//WDTCR = (1 << WDCE) | (1 << WDE); // Разрешаем изменения
//WDTCR = (1 << WDE) | (1 << WDP2) | (1 << WDP1); // Включаем WDT на интервал 1 секунда
//=========== Инициализация выходов дисплея и нагревателя ===================
// Настраиваем ВЫХОДЫ: PB0 (нагреватель), CLK_BIT и DIO_BIT (дисплей).
// Если вам нужно, чтобы на этих выходах сразу был логический ноль (0 В):
// Специально ничего делать не надо. После сброса микроконтроллера в регистре PORTB по умолчанию уже записаны нули,
// поэтому на настроенных выходах сразу появится низкий уровень
DDRB = (1 << PB0) | CLK_BIT | DIO_BIT;
//================= Настройка порта PORTB =======================
// Подаем "1" на входной пин STAND_PIN, чтобы включить внутренний подтягивающий резистор (PULL-UP).
// Это удерживает на входе логическую единицу, пока датчик подставки не прижмет её к земле.
PORTB = (1 << STAND_PIN);
//=============== Настройка источника опорного напряжения ===============
// REFS0: выбираем источник опорного напряжения Vcc Вольт.
// MUX0: выбираем канал ADC1 (физическая ножка PB2), куда подключен датчик температуры.
ADMUX = (1 << MUX0);
//========== Читаем заданную температуру и калибровку из EEPROM =============
//setTemp = eeprom_read_word((uint16_t*)0);
setTemp = eeprom_read_byte((uint8_t*)0);
if (setTemp > 245) // Если в EEPROM оказалась температура выше 490 градусов, то ...
setTemp = 125; // ... установить заданную температуру на 250 градусов
calib = eeprom_read_byte((uint8_t*)2);// Читаем поправку калибровки
if (calib > 30 || calib < -30) // Если калибровка не в диапазоне от 0 до +50, то ...
calib = 0; // ... установить калибровка на 0 градусов
// === Проверяем, чтобы при компиляции была выбрана правильная частота контроллера ===
#if F_CPU != 1200000UL
#error "Частота в коде не совпадает с настройками IDE!"
#endif
// Разрешаем глобальные прерывания
sei(); // enable global interrupts
// Setup ADC and read temperature as start value for smoothing
// Инициализация АЦП и чтение температуры со сглаживанием - предварительный разогев АЦП
ADC_init();
// Пауза для стабилизации опорного напряжения при старте
delayTK(10);
//========================================================== //
// Конец SETUP //
//========================================================== //
//========================================================= //
// Основной цикл //
//========================================================= //
while(1)
{
// ==== Задаём уровень нагрева в зависимости от типа жала =========
#if defined (__C245__)
#define TIME_ON 5 // Задать управления нагревом для C245 - длительность включения 50%
#define TIME_OFF 5 // Задать управления остыванием для C245 - длительность выключения 50%
tipType = 1;
#else
#define TIME_ON 9 // Задать управления нагревом для T12 - длительность включения 90%
#define TIME_OFF 1 // Задать управления остыванием для T12 - длительность выключения 10%
#endif
uint16_t target = (setTemp << 1); // Установить целевую температуру равную заданной (однобайтовую setTemp умножаем на 2)
if (turboTimer) // Если не истекло время таймера Турбо - не равен 0
{
target = (setTemp << 1) + 50; // Держим температуру повышенной на 50 градусов от заданной
turboTimer--; // ... и уменьшаем турботаймер
}
timer30Sec--; // Уменьшаем таймер 30 секунд на каждом цикле по кругу от 150 до 0
// === Начальное отключение нагрева жала на каждом повторе программы ===
//HEATER_OFF();
delayTK(4); // Задержка 4 мс - ждём установления напряжения на входе АЦП
uint16_t acc = 0; // Локальная переменная для суммы замеров АЦП (аккумулятор)
rawTemp = get_adc(); // Получаем сырую температуру
// ============= Вычисляем текущую температуру =====================
// Реализуем быстрое цифровое сглаживание (экспоненциальное скользящее среднее, EMA) для измеренной температуры
// Математически это аналог формулы низкочастотного RC-фильтра (семь восьмых старого плюс одна востмая нового значения):
// на каждом шаге подмешиваем 6.25% от нового измерения
// Результат записываем в currentTemp.
smoothTemp += (int16_t)(rawTemp - smoothTemp) >> 4;
// Добавляем к результату калибровку и температуру холодного спая
currentTemp = smoothTemp + calib + COLD_JUNCTION;
// ============= Проверяем на превышение температуры ==================
// Сигнализация о перегреве
if (currentTemp > 500) // Если температура выше 500, то ...
failFlag = 1; // ... поднимаем флаг ошибки
//====== Обработка нажатия кнопок ===========================
uint8_t key = read_keys(); // Получить код клавиши
if (key != lastKey) // Если код отличается от предыдущего (имеем новое нажатие)
{
switchDisplay = 1; // ... установить признак отображения целевой температуры
switch (key)
{
case 0xEA: // Если есть нажатие кнопки ТЕРМОМЕТР, то ...
termometrFlag = !termometrFlag; // ... инвертируем флаг ТЕРМОМЕТР
break;
case 0xEC: // Если есть нажатие кнопки МИНУС ...
setTemp -= 5; // Уменьшить заданную температуру на 10 градусов
timerEEPROM = 150; // Переустанавливаем таймер для отсчёта 30 секунд перед записью в EEPROM
isReached = 0; // Сбросить признак первого достижения температуры
break;
case 0xED: // Если есть нажатие кнопки ПЛЮС ...
setTemp += 5; // Увеличить заданную температуру на 10 градусов
timerEEPROM = 150; // Переустанавливаем таймер для отсчёта 30 секунд перед записью в EEPROM
isReached = 0; // Сбросить признак первого достижения температуры
break;
case 0xEF: // Если есть нажатие кнопки ТУРБО ...
turboTimer = 150; // Установить таймер ТУРБО на 30 секунд (150 циклов по 20 мс.)
isReached = 0; // Сбросить признак первого достижения температуры
break;
case 0xEE: // Если есть нажатие кнопки КАЛИБРОВКА ...
calib += 5; // Увеличить величину калибровки на 5 градусов
if (calib > 30) // Если дошли до 30 градусов, то ...
calib = -30; // ... переходим на -30 градусов
switchDisplay = 2; // Установить признак отображения значения калибровки
timerEEPROM = 150; // Переустанавливаем таймер для отсчёта 30 секунд перед записью в EEPROM
break;
case 0xFF: // ОТПУСКАНИЕ КНОПКИ (Ловим тот самый момент!)
switchDisplay = 0; // Сразу возвращаем экран в режим показа текущей температуры
break;
}
}
lastKey = key; // Запоминаем текущий код кнопки
// =========== Если ручка на подставке ============================
// == Если при признаке жала С245 на STAND_PIN (контроль ручки на подставке) логический 0 или при Т12 логическая 1, то ручка на подставке
if (tipType != !(!(PINB & (1 << STAND_PIN))))
{
if (timerSleep < 6) // Если не прошло 180 секунд нахождения на подставке, то ...
{
//if (timerSleep > 2) // Если прошло 90 секунд, то ...
target = TEMP_SLEEP; // ... установить целевую температуру равной 100 градусов
if (!timer30Sec) // Заходим в это условие каждые 30 секунд - когда timer30Sec = 0
{
timerSleep++; // ... увеличиваем таймер на единицу
if (timerSleep == 6) // Если прошло 180 секунд, то перейти в режим отключения и ...
{
soundFlag = 1; // ... пискнуть
}
}
}
else // Если timerSleep = 6, то включен режим отключения и ...
target = 0; // ... целевую температуру устанавливаем в 0
}
else // Если ручка в работе (не на подставке)
timerSleep = 0; // Сброс таймера отсчёта трёх минут нахождении на подставке
// ======= Если нет аварии и режима Термометр, то управляем нагревом =================================================
if (!failFlag && !termometrFlag) // Если нет аварии (флаг аварии в нуле) И Термометра, то включаем нагрев
{
// Вычисляем разницу температур текущей и заданной
int16_t error = target - currentTemp;
uint8_t power = 0; // Переменная управления мощностью
if (error > 0) // Если есть ненулевая ошибка
{
// Ограничиваем максимальную мощность:
// Если до цели далеко, мощность будет жестко ограничена на уровне 15 из 20 шагов (75% мощности).
// Если приблизились к заданной температуре (ошибка меньше 30 градусов), мощность снижается до 8 из 20 шагов (40% мощности).
if (error > 30) // Если до цели больше 30 градусов, то греем на полную - на 15 из 20 циклов
power = 15;
else if (error > 10) // Если до цели от 30 до 20 градусов, то греем на 8 из 20 циклов
power = 8;
else
power = error; // Если до цели 10 и менее градусов, то уменьшаем нагрев с каждым градусом
}
// === ЦИКЛ НАГРЕВА С ЧАСТЫМИ ПАУЗАМИ (20 шагов по 10 мс = 200 мс общего времени)
for (uint8_t step = 0; step < 20; step++)
{
// Распределяем шаги нагрева. БП не будет перегружаться,
// так как нагрев включится максимум на 15 шагов по 5 мс (75 мс суммарно за весь цикл)
if (step < power)
{
HEATER_ON();
delayTK(TIME_ON); // Жестко фиксированное безопасное время импульса (5 мс) для БП ноутбука
HEATER_OFF();
delayTK(TIME_OFF); // Обязательная пауза для отдыха БП и фильтрации шумов (5 мс)
}
else
{
// Оставшиеся из 20 шагов паяльник просто «отдыхает» по 10 мс
HEATER_OFF();
delayTK(10);
}
}
HEATER_OFF(); // Защитное выключение в конце общего цикла
// Если температура не ниже target - 3 (достигли target), то нагрев не включаем и проверяем первое ли это достижение заданной температуры
if (!isReached && (currentTemp > (target - 3))) // Проверить - если температура впервые достигла target - 3
{
isReached = 1; // Запомнить, что заданная температура уже была достигнута - поднять флаг её достижения
soundFlag = 1; // Пискнуть при первом достижении заданной температуры
}
}
else // Если есть или авария или термометр или оба, то ...
{
delayTK(200); // Задержка для организации времени цикла
if (!termometrFlag) // Если же поднят флаг Авария и нет режима Термометр, то ...
{
soundFlag = 1; // ... пищим при флаге аварии без термометра на каждом цикле программы - то есть постоянно
}
}
int16_t tempToDisplay = currentTemp; // По умолчанию реальная
// == Определяем, какие данные выводить на индикатор ===
switch (switchDisplay)
{
case 1:
tempToDisplay = target;
break;
case 2:
tempToDisplay = calib;
break;
default: // Это случай 0 (умное удержание)
// Если температура отклонилась от заданной меньше ,чем на 10 градусов, то показываем заданную температуру
if ((currentTemp > target + 9) || (currentTemp < target - 9)) // ((smooth + 10 > setpoint) && (setpoint + 10 > smooth))
{
tempToDisplay = currentTemp;
}
else
{
tempToDisplay = target;
}
break;
}
//============== Вывод четырёх знаков на дисплей =========
uint16_t t = 0;
if (timerSleep != 6) // Если мы не в режиме отключения (target не = 0), то выводим температуру по модулю
t = abs (tempToDisplay); // ... иначе выводим нули
byte ks=0; // Переменная для сотен градусов
byte kd=0; // Переменная для десятков градусов
while (t >= 100) // Вычисляем цифру для сотен
{
t -= 100;
ks++;
}
while (t >= 10) // Вычисляем цифру для десятков
{
t -= 10;
kd++;
}
// ====== Вывести на дисплей с частотой 5 Гц текущую, установленную, Турбо температуру или калибровку ============
if (ks == 0) // Если в старшем разряде ноль, то ...
ks = 10; // ... выводим в старший разряд "пусто"
if (tempToDisplay < 0) // Если температура была отрицательной (например у калибровки), то ...
ks = 12; // ... выводим в старший разряд "минус"
//СОТНИ
TM1637_dig (0, ks); // Вывести сотни (или пусто или минус) градусов в первый (слева) разряд
//ДЕСЯТКИ
TM1637_dig (1, kd); // Вывести десятки градусов во второй (слева) разряд
//ЕДИНИЦЫ
TM1637_dig (2, t); // Вывести единицы градусов в третий (слева) разряд
//знак градуса
uint8_t lastDigit = 11; // Готовим знак "Градус"
if (failFlag)
lastDigit = 12; // В случае аварии готовим "Минус"
else if (turboTimer)
lastDigit = 13; // В турбо режиме готовим букву "С"
else if (termometrFlag)
lastDigit = 10; // Для режима термометра готовим "Пустой символ"
TM1637_dig (3, lastDigit); // Вывести символ в 4-й разряд
// Включаем или отключаем звук подачей 8 или 0 на 5-й
if (soundFlag) // Если есть флаг звука, то ...
TM1637_dig (4, 8); // Вывести 8 в 5-й (слева) разряд
else // Если нет флага звука
TM1637_dig (4, 10); // Выключаем звук - выводим ПУСТО в 5-й разряд
tm_cmd(SetBright); // Установка яркости (см. таблицу в начале скетча)
soundFlag = 0; // ... сбрасываем флаг звука
// ==== Проверка таймера 25 секунд после изменения температуры и запись данных в EERPOM ==============
if (timerEEPROM) // Если ещё считает таймер EEPROM, то ...
{
timerEEPROM--; // Уменьшаем таймер
if (!timerEEPROM) // Проверяем - досчитал ли таймер до нуля. Если да, то ...
writeToEEPROM (); // Пишем изменённые данные в EERPOM
}
if (!timer30Sec) // Если дошли до нуля, то обновляем счётчик
timer30Sec = 150;
}
//=============================================================//
// Конец основного цикла //
//=============================================================//
}
//==============================================================//
// КОНЕЦ MAIN //
//==============================================================//
//#############################################################
//============================================================ //
// ФУНКЦИИ //
//============================================================ //
//----------------------- Отправка байта ---------------------------------------------------------
// Функция отправки одного байта на дисплей
void writeByte(uint8_t value)
{
for (uint8_t i = 8; i; i--)
{
CLK_L();
//CLK_L();
if (value & 0x01)
DIO_H();
else
DIO_L();
value >>= 1;
CLK_H();
}
CLK_L();
DIO_IN();
//DIO_IN();
CLK_H();
DIO_OUT();
}
//------------------ Команда СТАРТ --------------------
void start()
{
DIO_H();
CLK_H();
//CLK_H();
DIO_L();
}
//------------------ Команда СТОП ---------------------
void stop()
{
CLK_L();
DIO_L();
//DIO_L();
CLK_H();
DIO_H();
}
//=========== Отправка команд на дисплей ===============
void tm_cmd(uint8_t data)
{
start(); // Установка в ноль PB4
writeByte(data);
stop(); // Установка в 1 PB4
}
// ====================== ФУНКЦИЯ ЧТЕНИЯ КНОПОК ИЗ TM1637 ======================
uint8_t read_keys()
{
uint8_t result = 0;
DIO_L();
writeByte(0x42); // Команда сканирования клавиатуры
// Переводим DIO во входной режим (DDRB)
DIO_IN();
DIO_H(); // Включаем подтяжку Pull-up для стабильности опроса
// Пропускаем ACK от чипа
CLK_H();
CLK_L();
// ОПТИМИЗАЦИЯ: Цикл считает вниз от 8 до 0
for (uint8_t i = 8; i; i--)
{
result >>= 1; // Заранее сдвигаем результат
CLK_H();
CLK_H();
// Проверяем состояние пина
if (PINB & DIO_BIT)
result |= 0x80; // Быстрая вставка бита без сдвига (1 << i)
CLK_L();
}
// Перед выходом возвращаем пин в режим выхода и ставим HIGH
CLK_L();
DIO_OUT();
DIO_H();
return result;
}
// === Функция для замены _delay_ms() для экономии памяти =================
// === Формируем задержку в указанное число мс - один цикл - одна мс ========
void delayTK (uint8_t delay_tk)
{
asm volatile
(
"1: ldi r25, 238 \n\t" // (1 такт)
"2: nop \n\t" // (1 такт) Добавили для удлинения цикла
" nop \n\t" // (1 такт) Добавили для удлинения цикла
" dec r25 \n\t" // (1 такт)
" brne 2b \n\t" // (2 такта, когда переходит)
" dec %0 \n\t" // (1 такт)
" brne 1b \n\t" // (2 такта, когда переходит)
: "+r" (delay_tk)
: : "r25"
);
}
// === Функция записи в ПЗУ значения по указанному адресу =====
void EEPROM_write(uint8_t address, uint8_t value)
{
while(EECR & (1 << EEPE)); // Ждем, пока завершится предыдущая запись
EEARL = address; // Задаем адрес (у ATtiny13 регистр называется EEARL)
EEDR = value; // Записываем данные
EECR |= (1 << EEMPE); // Разрешаем запись (мастер-бит)
EECR |= (1 << EEPE); // Запускаем запись
}
// === Функция записи в ПЗУ значения температуры и калибровки =====
void writeToEEPROM()
{
// Вместо update используем прямую компактную запись
EEPROM_write(0, (uint8_t)setTemp);
//EEPROM_write(1, (uint8_t)(setTemp >> 8));
EEPROM_write(2, calib);
}
// === Вывод в позицию position символа digit =================
void TM1637_dig(const uint8_t position, const uint8_t digit)
{
uint8_t segments = digits[digit];
tm_cmd(0x40 | 0x04); // Включаем режим фиксированного адреса
start(); // Передаём команду Старт
writeByte(0xC0 | position); // Не огнаничиваем диапазон пятью разрядами
writeByte(segments); // Записываем данные в выбранный ранее разряд
stop(); // Передаём команду Стоп
}
// ==== Считываем АЦП ==============================
uint16_t get_adc()
{
uint16_t result = 0;
ADCSRA |= (1<<ADIF); // Отключить все ожидающие прерывания
set_sleep_mode (SLEEP_MODE_ADC); // Уходим в сон во время измерений для снижения уровня шума
// ХОЛОСТОЙ ЗАМЕР: сбрасываем переходные процессы в АЦП после работы TM1637
sleep_mode();
while (ADCSRA & (1 << ADSC));
for (uint8_t i=16; i; i--)
{
sleep_mode();
//ADCSRA |= (1 << ADSC); // Запуск преобразования АЦП
while (ADCSRA & (1 << ADSC)); // Ожидание, пока бит ADSC станет нулем (конец замера)
result += ADC; // Суммируем 16 результатов
}
return (result >> 4);
}
// Инициализация АЦП
void ADC_init(void)
{
ADCSRA = (1<<ADEN) // Включаем АЦП
| (1<<ADPS1) | (1<<ADPS0) // Установить делитель частоты АЦП на 8
| (1<<ADIE); // Включение прерываний АЦП, чтобы мог просыпаться после сна по окончании изменения АЦП
}


Желающие могут поупражняться с настройками нагрева, изменяя число циклов нагрева или соотношение длительности включения и отключения нагревателя.
Также, теоретически, можно на ножке CLK в паузах между выдачей сигналов индикатору, опрашивать состояние контакта разъёма №2 и автоматически определять тип установленной ручки. У меня руки пока не дошли. ))
свернутьразвернуть
Комментарии (6)
RSS
0
- 01 ноября 2026, 22:48
Т.е. вместо могучих 120вт получаем только 60?
0
- 01 ноября 2026, 22:53
Если есть мощный блок питания, можете получить все 120. У меня БП от ноутбука на 70 ватт.
0
- 01 ноября 2026, 23:01
А печатная палат есть? или все на монтажной
+1
- 01 ноября 2026, 23:17
На макетке. Тинька в панельке, чтобы было удобно вынуть и перепрошить. Пришлось больше десятка раз перепрошивать при наладке. BL1555 и OPA333 на переходных платах.
+1
- 01 ноября 2026, 23:31
Не станция, а экономия на спичках какая-то. Принципиальная схема как у космического корабля, но проц выбран самый дешмаский, с минимальным количеством памяти (видно автора тоже кризис ЫЫ накрыл) и на алгоритм PID места не осталось.
0
- 01 ноября 2026, 23:48
Странный выбор микроконтроллера. Puya32 в аналогичном форм-факторе стоит 13 рублей и там будет достаточно ресурсов на все, включая PID. Еще и OLED поместится на I2C. За схему спасибо и обзор интересный — плюсанул. А нельзя ли ее в более удобоваримом формате выложить? Тот же KiCad? А я плату натрассирую бесплатно — как раз есть желание потренироваться.
Самые обсуждаемые обзоры
+21
+54