Добрый день! Делаю нижний подогрев для пайки по примеру с ютуба, но другой датчик, и дисплей не OLED, а 7-сег. Скетч также по примеру автора, но у меня симистор не отключается по достижению заданной температуры, хаотически ВКЛ\ВЫКЛ но греет дальше (симистор сделан на отдельном модуле, раньше проверялся и переход через ноль и управление работает). Посмотрите пожалуйста, я не до конца понимаю как работает алгоритм.
МОЙ СКЕТЧ
#include <GyverMAX6675.h>
#include "LedControl.h"
#include "GyverPID.h"
#include <EncButton.h>
#include <CyberLib.h>
#define AC_LOAD 5 // симистор
#define zeroPin 2 // переход через ноль
#define LEFT_BUTTON 3 // левая кнопка
#define RIGHT_BUTTON 4 // правая кнопка
#define CLK_PIN 7 // Пин SCK // MAX6675
#define DATA_PIN 9 // Пин SO
#define CS_PIN 8 // Пин CS
GyverMAX6675<CLK_PIN, DATA_PIN, CS_PIN> sens; // датчик тепературы
LedControl lc=LedControl(12,11,10,1); // дисплей
Button left_btn(LEFT_BUTTON); // левая кнопка
Button right_btn(RIGHT_BUTTON); // правая кнопка
GyverPID pid(1.6, 0.015, 17); // пид - контроллер
int period = 300; // частота измерений
uint16_t setTemp = 40; // установленная температура
uint16_t curTemp = 0; // текущая температура
volatile int tic = 0; // счетчик тиков в прерывании
volatile int Dimmer = 128; // уровень мощности (0-128) 128 = OFF, 0 = ON
unsigned long timer; // таймер для считывания тепературы ...
void setup()
{
pid.setpoint = setTemp; // установочная температура, к которой стремимся
pid.setDt(period); // период обновления пид значения
pinMode(AC_LOAD, OUTPUT); // Пин на выход симистор
digitalWrite(AC_LOAD, 0); // на всякий случай симистор выключаем сразу
pinMode(zeroPin, INPUT); // переход через ноль
attachInterrupt(0, detect_up, FALLING); // Установить прерывание при переходе сетевого напряжжения через "0"
StartTimer1(timer_interrupt, 40); // время для одного разряда ШИМ
StopTimer1(); // остановить таймер
lc.shutdown(0,false); // пробуждение дисплея
lc.setIntensity(0,3); // установка яркости
lc.clearDisplay(0); // очистка дисплея
}
void loop()
{
updateSetTemp(); // обработка кнопок и установка температуры
updateDisplay(1,setTemp); // обноление дисплея
updateDisplay(0,curTemp);
if (millis() - timer >= period) // (period - 300)
{
timer = millis();
curTemp = sens.getTemp(); // считываем температуру
pid.getResult(); // получаем результат
Dimmer = pid.output; //
if (Dimmer > 255) Dimmer = 255; //
if (Dimmer < 0) Dimmer = 0; //
Dimmer = 255 - Dimmer; //
}
}
//----------------------ОБРАБОТЧИКИ ПРЕРЫВАНИЙ--------------------------
void timer_interrupt() // прерывания таймера срабатывают каждые 40 мкс
{
tic++; // счетчик
if (tic > Dimmer) // если настало время включать ток
digitalWrite(AC_LOAD, 1); // врубить ток
}
void detect_up() // обработка внешнего прерывания на пересекание нуля снизу
{
tic = 0; // обнулить счетчик
ResumeTimer1(); // перезапустить таймер
attachInterrupt(0, detect_down, RISING);// перенастроить прерывание
}
void detect_down() // обработка внешнего прерывания на пересекание нуля сверху
{
tic = 0; // обнулить счетчик
StopTimer1(); // остановить таймер
digitalWrite(AC_LOAD, 0); // вырубить ток
attachInterrupt(0, detect_up, FALLING); // перенастроить прерывание
}
//----------------------ОБНОВЛЕНИЕ ДИСПЛЕЯ--------------------------
void updateDisplay(bool place, uint16_t number)
{
uint8_t first, second, third;
first = number / 100;
second = number % 100 / 10;
third = number % 10;
if (place == 0)
{
lc.setDigit(0,7,first,false);
lc.setDigit(0,6,second,false);
lc.setDigit(0,5,third,false);
}
else if (place == 1)
{
lc.setDigit(0,2,first,false);
lc.setDigit(0,1,second,false);
lc.setDigit(0,0,third,false);
}
}
//----------------------ОБНОВЛЕНИЕ КНОПОК--------------------------
void updateSetTemp()
{
left_btn.tick(); // опрос и обработка кнопок
right_btn.tick();
if (left_btn.click())
{
if (setTemp > 5) setTemp -=5;
}
if (right_btn.click())
{
if (setTemp < 275) setTemp +=5;
}
}
СКЕТЧ АВТОРА С ПРИМЕРА
#include <Wire.h>
#include "GyverPID.h"
#include "GyverEncoder.h"
#include <Adafruit_MAX31865.h>
#include <EEPROM.h>
#include "U8glib.h" // библиотека дисплея
#include <CyberLib.h>
#define AC_LOAD 3 // Выход для управления семистором
#define zeroPin 2
#define RREF 430.0
#define RNOMINAL 100.0
#define Enc1_Pin 7
#define Enc2_Pin 8
#define Button_Pin 9 //
//#define DEBUG_ENABLE // вывод в сериал данных для отладки
#define PRINT_DECIMAL // отображения десятисной температуры
volatile int Dimmer = 128; // Уровень мощности (0-128) 128 = OFF, 0 = ON
volatile int tic = 0;
int SetPoint;
int Temperatute = 0;
#ifdef PRINT_DECIMAL
byte dt = 0;
#endif
bool save = false;
unsigned long delaySave = 0;
unsigned long DisplayUpdate = 0;
bool HeatingAllow = false;
byte s = 0;
char Buffer[] = "1234";
uint16_t rtd;
unsigned long tmr;
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_NO_ACK|U8G_I2C_OPT_FAST); // Fast I2C / TWI
Adafruit_MAX31865 thermo = Adafruit_MAX31865(10);
Encoder enc1(Enc1_Pin, Enc2_Pin, Button_Pin); // для работы c кнопкой
GyverPID pid(1.6, 0.015, 17);
int period = 300;
void draw(void) {
u8g.firstPage();
do {
if(save){
u8g.setFont(u8g_font_fub42n);
sprintf(Buffer,"%d", SetPoint);
if(SetPoint > 99){
u8g.drawStr( 15, 43, Buffer);
}else{
u8g.drawStr( 32, 43, Buffer);
}
u8g.setFont(u8g_font_fub14n);
sprintf(Buffer,"%d", Temperatute);
u8g.drawStr( 66, 63, Buffer);
}else{
u8g.setFont(u8g_font_fub42n);
sprintf(Buffer,"%d", Temperatute);
if(Temperatute > 99){
u8g.drawStr( 15, 43, Buffer);
}else{
u8g.drawStr( 32, 43, Buffer);
}
u8g.setFont(u8g_font_fub14n);
#ifdef PRINT_DECIMAL
sprintf(Buffer,"%d", dt);
u8g.drawStr( 117, 43, Buffer);
#endif
sprintf(Buffer,"%d", SetPoint);
u8g.drawStr( 66, 63, Buffer);
if(HeatingAllow){
u8g.setFont(u8g_font_10x20_75r);
switch (s) {
case 3: u8g.drawStr( 23, 63, "7776"); break;
case 0: u8g.drawStr( 23, 63, "6777"); break;
case 1: u8g.drawStr( 23, 63, "7677"); break;
case 2: u8g.drawStr( 23, 63, "7767"); break;
}
}
}
} while( u8g.nextPage() );
}
void setup() {
SetPoint = EEPROM.read(0);
if(SetPoint == 255){
SetPoint = 60;
EEPROM.update(0, SetPoint);
}
#ifdef DEBUG_ENABLE
Serial.begin(9600);
#endif
Serial.setTimeout(50);
pid.setpoint = SetPoint; // то, к чему стремимся
pid.setDt(period); // период измерения - 100 мс
pinMode(AC_LOAD, OUTPUT); // Пин на выход
digitalWrite(AC_LOAD, 0);
pinMode(zeroPin, INPUT);
attachInterrupt(0, detect_up, FALLING); // Установить прерывание при переходе сетевого напряжжения через "0"
StartTimer1(timer_interrupt, 40); // время для одного разряда ШИМ
StopTimer1(); // остановить таймер
Wire.setClock(3400000);
thermo.begin(MAX31865_3WIRE); // set to 2WIRE or 4WIRE as necessary
enc1.setType(TYPE2);
}
void loop() {
if(!save){
if (millis() - tmr >= period) {
tmr = millis();
rtd = thermo.readRTD();
pid.input = thermo.temperature(RNOMINAL, RREF);
Temperatute = int(pid.input);
#ifdef PRINT_DECIMAL
dt = int((pid.input - Temperatute)*10);
#endif
if(HeatingAllow){
pid.getResult();
Dimmer = pid.output;
if (Dimmer > 255) Dimmer = 255;
if (Dimmer < 0) Dimmer = 0;
Dimmer = 255 - Dimmer;
}else{
Dimmer = 255;
}
#ifdef DEBUG_ENABLE
Serial.print(pid.input); Serial.print(' ');
Serial.print(pid.output); Serial.print(' ');
Serial.print(pid.integral); Serial.print(' ');
Serial.println(pid.setpoint);
#endif
}
}else{
Dimmer = 255;
}
#ifdef DEBUG_ENABLE
parsing();
#endif
enc1.tick();
if (enc1.isRight()){
SetPoint++;
if(SetPoint > 210){
SetPoint = 210;
}
pid.setpoint = SetPoint;
delaySave = millis();
save = true;
draw();
}
if (enc1.isLeft()){
SetPoint--;
if(SetPoint < 0){
SetPoint = 0;
}
pid.setpoint = SetPoint;
delaySave = millis();
save = true;
draw();
}
if (enc1.isClick()) HeatingAllow = !HeatingAllow; // кнопка нажата
if(save){
if((millis() - 3000) >= delaySave){
EEPROM.update(0, SetPoint);
save = false;
}
}
if((millis() - DisplayUpdate) >= 500){
DisplayUpdate = millis();
s++;
if(s>3){s=0;};
draw();
}
}
#ifdef DEBUG_ENABLE
// ------------------управление через плоттер-------------------------
void parsing() {
if (Serial.available() > 1) {
char incoming = Serial.read();
float value = Serial.parseFloat();
switch (incoming) {
case 'p': pid.Kp = value; break;
case 'i': pid.Ki = value; break;
case 'd': pid.Kd = value; break;
case 's': pid.setpoint = value; SetPoint = value; break;
case 'h': HeatingAllow = !HeatingAllow; break;
}
}
}
#endif
//----------------------ОБРАБОТЧИКИ ПРЕРЫВАНИЙ--------------------------
void timer_interrupt() { // прерывания таймера срабатывают каждые 40 мкс
tic++; // счетчик
if (tic > Dimmer) // если настало время включать ток
digitalWrite(AC_LOAD, 1); // врубить ток
}
void detect_up() { // обработка внешнего прерывания на пересекание нуля снизу
tic = 0; // обнулить счетчик
ResumeTimer1(); // перезапустить таймер
attachInterrupt(0, detect_down, RISING);// перенастроить прерывание
}
void detect_down() { // обработка внешнего прерывания на пересекание нуля сверху
tic = 0; // обнулить счетчик
StopTimer1(); // остановить таймер
digitalWrite(AC_LOAD, 0); // вырубить ток
attachInterrupt(0, detect_up, FALLING); // перенастроить прерывание
}