ПИД-регулятор и дискретное управление

Обсуждаем контроллеры компании Atmel.
Ответить
Вымогатель припоя
Сообщения: 580
Зарегистрирован: Пт ноя 08, 2013 01:01:18

Сообщение melandr »

Добрый день. Стоит такая задача, необходимо управлять тремя ТЭНами, для поддержания заданной температуры. Сам программный ПИД-регулятор для AVR я уже нашел. Но проблема в том, что на выходе ПИД-регулятора мы имеем от 0 до 100 %. А мне этот сигнал нужно выдать на 3 или более дискретных выхода, которые управляют нагревателями. То есть допустим имеем три ступени по 3 кВт. Соответственно мощность каждой ступени составляет 33 %. ПИД-регулятор посчитал, что для поддержания необходимой мощности необходимо 50 %. Следовательно нужно включить первую ступень постоянно - это 33%. Остается 17%. Значит вторая ступень должна работать половину времени. То есть, если период квантования 1 мин. Значит необходимо первую ступень включить на 60 секунд, а вторую ступень на 30 секунд. Получается ПИД-регулятор с ШИМ выходом. Сам этот алгоритм я подсмотрел в FBD блоке "Электрокалорифер" у РАУТ-Автоматик. Правда возникает сложность, как реализовать ШИМированный выход на три ступени или больше.
Изображение
Может кому-то попадалось описание этого алгоритма на Си, Встречал и в других промышленных контроллерах реализацию такого управления. А вот как это сделать для AVR не нашел, а самому с нуля написать алгоритм управления пока клепки не хватает. Заранее благодарен за помощь.
Вложения
EK.pdf
описание к этому блоку.
(89.73 КБ) 311 скачиваний
Реклама
Собутыльник Кота
Аватара пользователя
Сообщения: 2708
Зарегистрирован: Сб май 14, 2011 21:16:04
Откуда: г. Чайковский

Сообщение Z_h_e »

А в чем сложность то у Вас?
Получили Вы % ПИД регулятора U. Ваш выход ШИМ имеет, как я понял, период 1 мин.

Тогда :
ТЭН1 = U/33*60сек.
ТЭН2= (U-33)/33*60сек
ТЭН3= (U-66)/33*60сек

Если результат меньше 0 то 0, если болше 60, то 60 сек.

Математику можно оптимизировать. Результат выхода за рамки сразу предвидеть и тогда совсем не считать. Например, если у Вас U меньше 34, ТЭН 2 и ТЭН3 явно будет равен 0. Но если позволяют ресурсы, то можно и так оставить.
Изображение
Добро всегда побеждает зло. Поэтому кто победил - тот и добрый.
Реклама
Вымогатель припоя
Сообщения: 580
Зарегистрирован: Пт ноя 08, 2013 01:01:18

Сообщение melandr »

Спасибо за помощь. Период ШИМа будет меняться, я указал 1 мин как пример. Еще есть период квантования. Это время, в течение которого не обновляется вычисленная необходимая мощность. Сложность возникает как организовать ШИМированный выход. Я так понимаю ТЭН1, ТЭН2, ТЭН3 - имеет размерность секунды. И указывает, сколько времени должен быть активен дискретный выход каждой ступени нагревателя. Также есть еще время минимальной работы ступени, чтобы она не включалась на время, меньшее минимального.Хотелось посмотреть как это реализуется на Си. А то пока опыта не хватает, чтобы правильно все написать. А все примеры термостатов, которые я находил, почему то такой алгоритм никто не использует. Но спасибо за подсказку, буду дальше разбираться. Если будут вопросы по реализации, буду дальше на форум отписываться.
Модератор
Аватара пользователя
Сообщения: 4614
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля

Сообщение Аlex »

melandr писал(а):Сложность возникает как организовать ШИМированный выход.
Сложность в чём конкретно ?
Организовать ШИМ на выходе можно переключением логического уровня на выводе МК, с определённой периодичностью. Сложность в выводе конкретного уровня на вывод МК ? Или сложность в реализации периода между переключениями ?
Вопрос, на самом деле, не понятен. Отсюда и ответить на него сложно :dont_know:
Реклама
Эиком - электронные компоненты и радиодетали
Вымогатель припоя
Сообщения: 580
Зарегистрирован: Пт ноя 08, 2013 01:01:18

Сообщение melandr »

Аlex писал(а):Или сложность в реализации периода между переключениями ?
Допустим , у меня есть прерывание, которое отсчитывает системные тики каждую 1 мс. Мне не понятно как реализовать как Вы написали, период между переключениями. то есть у меня есть период регулирования (1 мин) - время, через которое обновляется значение мощности, насчитанное ПИД. Есть период квантования (10 сек) - это минимальное время работы каждой ступени. То есть, если мы насчитали, допустим: выход ПИД = 37 %
ТЭН1 = (37%*60 сек)/33% = 67,3 сек. > 60 сек. - ступень работает постоянно
ТЭН2 = ((37%-33%)*60сек)/33% = 7,3 сек < 10 сек - ступень на работает
Когда ПИД насчитает 39%. после этого ступень ТЭН2 включится на 11 сек.
И сложность в реализации , как это сделать красиво. Чтобы не было задержек в программе, так как будет еще индикация, управление кнопками. Хочется сделать, используя либо класс или структуру, допустим Нагреватель. В этой структуре будет указано количество ступеней, мощность каждой ступени, период регулирования и период квантования. Вот для примера я покажу, написал программу для управления гелиосистемой. Я сам понимаю, что написано похабно, но опыта не хватает, чтобы писать красивый и понятный код, который легко модернизировать
Спойлер#define F_CPU 14745600UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
//#include <stdio.h>
//#include <stdint.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include "onewire.h"
#include "ds18b20.h"
#include "7seg.h"

#define PORT_OUTPUT PORTD
#define DDR_OUTPUT DDRD
#define PUMP_1 PD0
#define PUMP_2 PD1
#define PUMP_3 PD2
#define SIRENA PD3

#define SETBIT(x,y) (x |= (y)) // установка бита y в байте x
#define CLEARBIT(x,y) (x &= (~y)) // сброс бита y в байте x
#define CHECKBIT(x,y) (x & (y)) // проверка бита y в байте x
// Определяем маску-константу бита статуса
#define PUMP1_ON 0x01 // разряд 0 : Флаг работы насоса коллектора 1
#define PUMP2_ON 0x02 // разряд 1 : Флаг работы насоса коллектора 2
#define PUMP3_ON 0x04 // разряд 2 : Флаг работы насоса отопления
#define SIRENA_ON 0x08 // разряд 3 : Флаг звуковой сигнализации

//дефайны задержек времени
#define T_1SEC 1000
/*значение для счетного регистра*/
#define T_POLL 198 //Fcpu = 14.7456 IAo, k = 256,
//Tt0 = 256/14745600=0.0174 in, n = 1/0.0174=58
//T_POLL = 256 - 58 = 198

// Массив значений для индикатора
const char SEGMENTE[] PROGMEM = { //массив для перевода чисел в код понятный для индикатора
0x3F, // 0
0x06, // 1
0x5B, // 2
0x4F, // 3
0x66, // 4
0x6D, // 5
0x7D, // 6
0x07, // 7
0x7F, // 8
0x6F, // 9
0x40, // -
0x63, // знак градуса
0x00, // " "
0x5E // d
};

unsigned char ds18b20_devices = 4; // количество сенсоров в оперативной памяти
unsigned char owDevicesIDs[MAXDEVICES][8]; // Их ID
unsigned int temperature [4] = {0,0,0,0};
unsigned char error_crc[4] = {0,0,0,0};
static unsigned char znak[4]; //если 0 - плюс, 1 - минус

/*
unsigned char DS18B20_rom[MAXDEVICES][8] PROGMEM = {
{0x28, 0xFF, 0xFE, 0x3F, 0x54, 0x15, 0x03, 0x8A},
{0x28, 0xFF, 0xE5, 0xC6, 0x62, 0x15, 0x02, 0x35},
{0x28, 0xFF, 0xA9, 0x41, 0x54, 0x15, 0x03, 0xF3},
{0x28, 0xFF, 0xDB, 0xC6, 0x62, 0x15, 0x01, 0xF1}
};*/
unsigned char DS18B20_rom[MAXDEVICES][8] PROGMEM = {
{0x28, 0xFF, 0x01, 0x9A, 0x62, 0x15, 0x02, 0x3A},
{0x28, 0xFF, 0xE5, 0xC6, 0x62, 0x15, 0x02, 0x35},
{0x28, 0xFF, 0xA9, 0x41, 0x54, 0x15, 0x03, 0xF3},
{0x28, 0xFF, 0xDB, 0xC6, 0x62, 0x15, 0x01, 0xF1}
};
/*unsigned char DS18B20_rom[MAXDEVICES][8] PROGMEM = {
{0x28,0x7B,0xC6,0x67,0x00,0x00,0x00,0xD6},
{0x28,0x5B,0xC6,0x67,0x00,0x00,0x00,0x60},
{0x28,0x6B,0xC6,0x67,0x00,0x00,0x00,0x8D},
{0x28,0x8B,0xC6,0x67,0x00,0x00,0x00,0xA4}
};*/

//Функция чтения rom датчиков из памяти программ в оперативную память
void ReadID(void)
{
int i,j;
for(i = 0; i < MAXDEVICES; i++)
for(j = 0; j < 8; j++)
{
owDevicesIDs[j] = pgm_read_byte(&(DS18B20_rom[j]));
}
}

//функция настройки таймера Т0
void Timer_init(void)
{
TCNT0 = T_POLL; //установка начального значения TCNT0
TIFR |= (1 << TOV0); //сброс флагов в регистре TIFR
TIMSK |= (1 << TOIE0); //разрешение прерывания по переполнению в TIMSK
TCCR0B |= (1 << CS02) | (0 << CS01) | (0 << CS00); //установка предделителя в TCCR0 (делитель 256)
}

// обработчик прерывания по переполнению Т0
ISR(TIMER0_OVF_vect)
{
static uint16_t cnt1sec = 0; //счетчик одной секунды
//перезапись счетного регистра
TCNT0 = T_POLL;

//сбрасываем сторожевой таймер
// wdt_reset();

if (cnt1sec == 1000)
{
// fDebug = 1;
PORTB ^= (1 << PB4);
cnt1sec = 0;
}
cnt1sec++;
}

int main(void)
{
// wdt_enable(WDTO_2S);
char flag = 0; // Объявляем байт статуса
Timer_init(); // инициализация таймера Т0

DDRB = 0b11010000; //Пины 6,7 - на вывод.
DATA_PORT = 0xFF; //все выводы 0
//настраиваем выводы для управления насосами и звуковым сигналом
DDR_OUTPUT |= (1 << PUMP_1)|(1 << PUMP_2)|(1 << PUMP_3)|(1 << SIRENA);
PORT_OUTPUT |= (1 << PUMP_1)|(1 << PUMP_2)|(1 << PUMP_3)|(1 << SIRENA);

ReadID();
/*
write_byte(pgm_read_byte(&SEGMENTE[ds18b20_devices])); // Выводим 3 разряд
write_byte(pgm_read_byte(&SEGMENTE[10])); // Выводим 2 разряд
write_byte(pgm_read_byte(&SEGMENTE[13])); // Выводим 1 разряд
_delay_ms(1000);*/

sei();
while(1)
{
if (ds18b20_devices > 0) //если датчики подключены производим опрос
{
DS18B20_StartMeasure(0); // запускаем измерение для всех датчиков
_delay_ms(1000); // ждем минимум 750 мс, пока конвертируется температура
for (unsigned char i=0; i<ds18b20_devices; i++) // теперь сортируем устройства и запрашиваем данные
{
unsigned char data[2]; // переменная для хранения старшего и младшего байта данных
cli();
error_crc = DS18B20_ReadData(owDevicesIDs, data); // считываем данные
temperature = DS18B20_ConvertToThemperature(data,&znak); // преобразовываем температуру в человекопонятный вид
sei();
}
}

for (unsigned char i=0; i<ds18b20_devices; i++)
{
write_byte(pgm_read_byte(&SEGMENTE[10])); // Выводим "-"
write_byte(pgm_read_byte(&SEGMENTE[i+1])); // Выводим номер датчика
write_byte(pgm_read_byte(&SEGMENTE[10])); // Выводим "-"

_delay_ms(1000);
if(error_crc)
{
if (znak == 0)
{
write_byte(pgm_read_byte(&SEGMENTE[temperature%10])); // Выводим 3 разряд
write_byte((pgm_read_byte(&SEGMENTE[temperature%100/10]))|0x80); // Выводим 2 разряд
write_byte(pgm_read_byte(&SEGMENTE[temperature[i]%1000/100])); // Выводим 1 разряд
}
if (znak[i] == 1)
{
write_byte(pgm_read_byte(&SEGMENTE[temperature[i]%10])); // Выводим 3 разряд
write_byte((pgm_read_byte(&SEGMENTE[temperature[i]%100/10]))|0x80); // Выводим 2 разряд
write_byte(pgm_read_byte(&SEGMENTE[10])); // Выводим 1 разряд
}
}
else
{
write_byte(pgm_read_byte(&SEGMENTE[10])); // Выводим "-"
write_byte(pgm_read_byte(&SEGMENTE[10])); // Выводим "-"
write_byte(pgm_read_byte(&SEGMENTE[10])); // Выводим "-"
}
_delay_ms(2000);
}
//Если темп. коллектора 1 больше темп. низа бака на 10 град и насос выключен - включаем насос
if (temperature[0]>(temperature[2]+100) && !(CHECKBIT(flag, PUMP1_ON)) && error_crc[0] && error_crc[2])
{
PORT_OUTPUT &= ~(1<<PUMP_1);
SETBIT(flag, PUMP1_ON);
}
//Если темп. коллектора 1 больше темп. низа бака на 8 град.Ц и насос включен - выключаем насос
if (temperature[0]<=(temperature[2]+80) && CHECKBIT(flag, PUMP1_ON))
{
PORT_OUTPUT |= (1<<PUMP_1);
CLEARBIT(flag, PUMP1_ON);
}

//Если темп. коллектора 2 больше темп. низа бака на 10 град и насос выключен - включаем насос
if (temperature[1]>(temperature[2]+100) && !(CHECKBIT(flag, PUMP2_ON)) && error_crc[1] && error_crc[2])
{
PORT_OUTPUT &= ~(1<<PUMP_2);
SETBIT(flag, PUMP2_ON);
}
//Если темп. коллектора 2 больше темп. низа бака на 8 град.Ц и насос включен - выключаем насос
if (temperature[1]<=(temperature[2]+80) && CHECKBIT(flag, PUMP2_ON))
{
PORT_OUTPUT |= (1<<PUMP_2);
CLEARBIT(flag, PUMP2_ON);
}
//Если темп. низа бака больше 85 град.Ц и насос выключен - включаем насос отопления
if (temperature[2]>850 && !(CHECKBIT(flag, PUMP3_ON)) && error_crc[2])
{
PORT_OUTPUT &= ~(1<<PUMP_3);
SETBIT(flag, PUMP3_ON);
}
//Если темп. низа бака меньше 75 град.Ц и насос включен - выключаем насос отопления
if (temperature[2]<=750 && CHECKBIT(flag, PUMP3_ON))
{
PORT_OUTPUT |= (1<<PUMP_3);
CLEARBIT(flag, PUMP3_ON);
}
//Если темп. верха больше 90 град.Ц или какая-либо температура пропала включаем звуковую сигнализацию
if (temperature[3]>900 || !error_crc[0] || !error_crc[1] || !error_crc[2] || !error_crc[3])
{
PORT_OUTPUT &= ~(1<<SIRENA);
SETBIT(flag, SIRENA_ON);
}
//Если темп. верха меньше 80 град.Ц и все температуры в норме - выключаем звуковую сигнализацию
if (temperature[3]<=800 && error_crc[0] && error_crc[1] && error_crc[2] && error_crc[3])
{
PORT_OUTPUT |= (1<<SIRENA);
CLEARBIT(flag, SIRENA_ON);
}
}
}

}

Спасибо за обсуждение и помощь
Реклама
Модератор
Аватара пользователя
Сообщения: 4614
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля

Сообщение Аlex »

Что-то Вы опять смешали всё в кучу, запутывая нас и себя самого. На простой вопрос про выводы МК и реализацию временных интервалов, Вы начали заново рассказывать про ТЭНы, проценты, ступени, ... , описывая своё устройство в целом и спрашивая, как его сделать. Ответ на такой вопрос очевиден - делайте как Вам удобно :) Ваш вопрос равносилен вопросу "Как положить слона в холодильник в 3 действия ?" :)
Допустим , у меня есть прерывание, которое отсчитывает системные тики каждую 1 мс. Мне не понятно как реализовать как Вы написали, период между переключениями.
Дык, это... Просто считайте эти тики обычным счётчиком (аля переменная) и всё :dont_know:

Вот тут :
ТЭН1 = (37%*60 сек)/33% = 67,3 сек. > 60 сек. - ступень работает постоянно
очень интересно получается, учитывая, что достаточно одного условия : 37% > 33% :dont_know:
Дальше :
ТЭН2 = ((37%-33%)*60сек)/33% = 7,3 сек
Не знаю, откуда у Вас получилось 7.3 сек., но остаток от первого вычисления = 4% (37-33), а это, от 60 секунд = 2.4 сек :dont_know:
Реклама
Собутыльник Кота
Аватара пользователя
Сообщения: 2708
Зарегистрирован: Сб май 14, 2011 21:16:04
Откуда: г. Чайковский

Сообщение Z_h_e »

Можно вот так сделать.

Дискретность скважности ШИМ 10 сек.
Функция включения/выключения ТЭНов вызывается каждые 10 сек и есть циклический счетчик вызывов функции от 0 до 5.

оператор switch (счетчик)
case 0:
Если ПИД>0 включ ТЭН1 иначе выключить ТЭН1
Если ПИД>33 включ ТЭН2 иначе выключить ТЭН2
Если ПИД>66 включ ТЭН3 иначе выключить ТЭН3
case 1:
Если ПИД>10 включ ТЭН1 иначе выключить ТЭН1
Если ПИД>43 включ ТЭН2 иначе выключить ТЭН2
Если ПИД>76 включ ТЭН3 иначе выключить ТЭН3

......
Изображение
Добро всегда побеждает зло. Поэтому кто победил - тот и добрый.
pcb
Опытный кот
Сообщения: 833
Зарегистрирован: Пт авг 12, 2011 09:14:27
Откуда: Млечный путь/Земля/РФ/Екатеринбург

Сообщение pcb »

Если мощность 3 кВт и трех фазное питание, то это скорее симисторный регулятор, в этом случае шим не нужен... Алгоритм на три фазы аналогичен алгоритму на одну фазу. :tea:
Разработал:
-BLDC
-ФУОЗ/МПСЗ
-SMART BMS
-ECU/EDC на STM32F4(43%)+CPLD(57%)
-Моноинжектор на ATSAMD20G16
-контроллер эффектов для RGB LED ленты
-умные часы/обратный счет/секундомер
-устройство измерения емкости АКБ
Вымогатель припоя
Сообщения: 580
Зарегистрирован: Пт ноя 08, 2013 01:01:18

Сообщение melandr »

pcb писал(а):Если мощность 3 кВт и трех фазное питание, то это скорее симисторный регулятор, в этом случае шим не нужен...
Мощность 3 тэна по 3кВт, которые включаются не одновременно, а 3 ступенями. Чтобы не нагружать сеть 9кВт, если нет необходимости в полной мощности. А симисторным регулятором будет включаться полные 9 кВт.
melandr писал(а):Не знаю, откуда у Вас получилось 7.3 сек., но остаток от первого вычисления = 4% (37-33), а это, от 60 секунд = 2.4 сек
ПИД-регулятор выдает сигнал от 0 до 100 %. Имеем три тэна по 3 кВт. Каждый тэн грубо говоря по 33%. Если мы хотим получить мощность 66% мы должны включить два тэна на полный период работы. А допустим мы хотим получить 49,5%. Для этого мы включаем первый тэн на полный период работы 60 сек, а второй тэн должен работать 30 сек. А по Вашей логике второй тэн должен работать 9,9 сек.
pcb
Опытный кот
Сообщения: 833
Зарегистрирован: Пт авг 12, 2011 09:14:27
Откуда: Млечный путь/Земля/РФ/Екатеринбург

Сообщение pcb »

Не вижу проблемы.

3фазы*1% = 3%% мощности, как и
1фаза*3% = 3%% мощности.

нашел книжку в своей библиотеке: Кит Сукер. 2007. Додека. Силовая электроника. Руководство разработчика. Начиная со страницы 129 - вам к прочтению.
Мне думается, что загвоздка не в непонимании, как сделать сдвиг, а в непонимании принципа управления.

И не плохо бы показывать свой код или что там у вас. Хоть посмотрю, что там люди копируют. :tea:
Разработал:
-BLDC
-ФУОЗ/МПСЗ
-SMART BMS
-ECU/EDC на STM32F4(43%)+CPLD(57%)
-Моноинжектор на ATSAMD20G16
-контроллер эффектов для RGB LED ленты
-умные часы/обратный счет/секундомер
-устройство измерения емкости АКБ
Модератор
Аватара пользователя
Сообщения: 4614
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля

Сообщение Аlex »

melandr писал(а):ПИД-регулятор выдает сигнал от 0 до 100 %. Имеем три тэна по 3 кВт. Каждый тэн грубо говоря по 33%. Если мы хотим получить мощность 66% мы должны включить два тэна на полный период работы. А допустим мы хотим получить 49,5%. Для этого мы включаем первый тэн на полный период работы 60 сек, а второй тэн должен работать 30 сек. А по Вашей логике второй тэн должен работать 9,9 сек.
Всё, теперь понял, что у Вас там за ступени, ТЭНы, секунды, ... Тогда да, для каждой ступени, 100% - это 33% от общего.
Вот, накидал примерчик :

Код: Выделить всё


/**********************************************************************************/
#define         OUT_STEPS       3                   // Кол-во ступеней
#define         STEP_PERC       (100/OUT_STEPS)     // Процент полного шага (ступени)
/**********************************************************************************/
unsigned char	perc_steps[OUT_STEPS];      // Массив процентов для каждой ступени
/**********************************************************************************/


/**********************************************************************************/
void outs_calc(unsigned char pid_out){        // pid_out - расчитанная мощность регулятора
unsigned char   i;

for(i=0; i<OUT_STEPS; i++){
    int mod = (int)pid_out - STEP_PERC*i;
    if(mod<0)   mod=0;
    if(mod>STEP_PERC)   mod=STEP_PERC;
	perc_steps[i]=(mod*100)/STEP_PERC;
}
}
/**********************************************************************************/
Функция outs_calc расчитывает процент для каждой ступени и заносит их в массив perc_steps. Принимаемый параметр - мощность на выходе регулятора.
Результаты :
Вход = 30, результат в массиве - 90, 0, 0
Вход = 50, результат в массиве - 100, 51, 0
Вход = 70, результат в массиве - 100, 100, 12
Вход = 100, результат в массиве - 100, 100, 100

Теперь Вам осталось только перевести каждый процент в время включения, относительно полного периода ШИМ.
Вымогатель припоя
Сообщения: 580
Зарегистрирован: Пт ноя 08, 2013 01:01:18

Сообщение melandr »

Аlex, спасибо за содействие и помощь. Буду делать. Пока код нечего показывать. Да и хочется сделать как-то так, как по этой ссылке
http://avr.ru/beginer/devices/furnace. Но пока разбираюсь.
Модератор
Аватара пользователя
Сообщения: 19077
Зарегистрирован: Сб авг 14, 2010 15:05:51
Откуда: г. Озерск, Челябинская обл.

Сообщение Starichok51 »

я полагаю, что не обязательно привязываться к целым значениям процентов. пусть это будет более "дробный" шаг у ШИМ.
мы можем оперировать полным диапазоном значения одного байта. тогда ШИМ каждого ТЭНа будет иметь дискретность 1/256.

1. поскольку период у ШИМ довольно большой (предлагается 60 секунд), то организуем 3 программных ШИМа.
2. для ПИД регулятора организуем двухбайтовый "выход" с максимальным значением, например, 768 (256 * 3).
3. допустим, ПИД насчитал, что нужно на выходе иметь число, например, 312.
4. вычисляем 312 - 256 = 56.
5. первый ШИМ включаем постоянно.
6. на втором ШИМе формируем ширину 56/256 от постоянного включения.
7. на третий ШИМ у нас не осталось ничего, оставляем третий выключенным.
аналогично получится, если ПИД сформирует свой "выход" более 512 - тогда первых 2 канала включатся постоянно, а на третий канал пойдет остаток.
для полной мощности (100%) у нас для каждого ШИМа будет число 256 - постоянное включение всех ТЭНов.
в итоге общая мощность будет регулироваться с довольно высокой точностью - с дискретностью 1/768 от полной суммарной мощности.

если так точно не нужно, то можно сделать регулировку грубее. ПИД ограничить одним байтом (255 максимум), а на 3 ШИМа выводить с дискретностью 1/85 (85 * 3 = 255). 85 будет означать полную ширину ШИМ (постоянное включение).
Мудрость приходит вместе с импотенцией...
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Ответить

Вернуться в «AVR»