Commit e4351837 authored by Oleg Nikulin's avatar Oleg Nikulin

2 файла проишвки: удерживание заданных оборотов и регулятор температуры

parent 7e6ca871
//Попробовать работать с таймером.
//Чтобы при его помощи вручную управлять шимом (это позволит точно знать время его включения, при этом иметь шим нормальной частоты)
//И чтобы чтобы через прерывания чекать тахометры.
//Можно еще попробовать реализовать вариант, когда для измерения оборотв на короткое время на питание подается постоянное напряжение вместо шима, и нужно успеть измерить обороты за это время.
#include <Wire.h>
#define FAN_COUNT 6 //Количество вентиляторов
#define BAUDRATE 19200 //Скорость serial порта
#define CHECK_TEMP_DELAY 200 //Интервал получения температуры от пк (мс)
#define CHECK_TEMP_DELAY 1000 //Интервал получения температуры от пк (мс)
#define GET_TEMP_TIMEOUT 1000 //Если в течение этого времени температура не была прислана, считается что пк не отвечает (мс)
#define RPM_CHECK_INTERVAL 200 //Интервал подсчета rpm и обнуления tachRevs (мс)
#define RPM_VALUES_COUNT 5 //Количество значений RPM, которые записываются и усредняются (скользящее среднее)
#define MIN_PWM_DUTY_CYCLE 20 //Минимальная скважность ШИМ
#define MAX_PWM_DUTY_CYCLE 254 //Максимальная скважность ШИМ
#define DEFAULT_PWM_DUTY_CYCLE 254 //Скважность ШИМ по умолчанию (до подулючения к пк)
#define BEEP_INTERVAL 1000 //Интервал пищания во время потери связи или перегрева (мс)
#define BEEP_STRENGTH 255 //Уровень ШИМ для пищалки. (При 255 не работает, нужно чтобы был именно ШИМ)
#define BEEP_STRENGTH 100 //Уровень ШИМ для пищалки. (При 255 не работает, нужно чтобы был именно ШИМ)
#define BLINK_INTERVAL 200 //Интервал мигания светодиода во время ожидания ответа от пк (мс)
#define SOUND_PIN 10 //Номер пина пищалки
......@@ -49,7 +46,7 @@ volatile bool tachStates[FAN_COUNT] = {}; //Состояния (оборот с
*/
int temp; //Температура, полученная от пк
int target_rpm = 0;
int target_temp = 33;
uint32_t last_beep_time = 0;
uint32_t last_check_time = 0;
......@@ -60,9 +57,9 @@ float p = 0;
float i = 0;
float d = 0;
float k_p = 0.3;
float k_i = 0.0004;
float k_d = 160;
float k_p = 30;
float k_i = 0.005;
float k_d = 0;
......@@ -163,7 +160,6 @@ void autocontrol()//Автоконтроль температуры
//pwmDutyCycle = 254;
}
*/
target_rpm = temp;
}
......@@ -177,27 +173,21 @@ void rpm_control() {
tachRevs[fan] = 0;
}
uint32_t avg_rpm = 0;
const int fan = 0;
for (int val = 0; val < RPM_VALUES_COUNT; val++) {
avg_rpm += tachRpm[fan][val];
}
avg_rpm /= RPM_VALUES_COUNT;
float pwmDutyCycle = 0;
uint8_t byte_pwmDutyCycle = 0;
d = (target_rpm - int(avg_rpm) - p) / float(millis() - lastRpmCheck);
p = target_rpm - int(avg_rpm);
d = (temp - target_temp - p) / float(millis() - lastRpmCheck);
p = temp - target_temp;
i = i + p * (millis() - lastRpmCheck);
if (fabs(i * k_i) >= MAX_PWM_DUTY_CYCLE){
i = MAX_PWM_DUTY_CYCLE / k_i;
}
pwmDutyCycle = round(p * k_p + i * k_i + d * k_d);
if (pwmDutyCycle <= 1) {
byte_pwmDutyCycle = 1;
if (pwmDutyCycle <= MIN_PWM_DUTY_CYCLE) {
byte_pwmDutyCycle = MIN_PWM_DUTY_CYCLE;
}
else if (pwmDutyCycle >= MAX_PWM_DUTY_CYCLE) {
byte_pwmDutyCycle = MAX_PWM_DUTY_CYCLE;
......@@ -302,7 +292,7 @@ void loop() {
last_check_time = millis();
autocontrol(); //Получаем температуру от ПК, устанавливаем соотв. скорость вращения
/*
if (temp >= 80) { //Если перегрев, включается пищалка
if (millis() >= last_beep_time + BEEP_INTERVAL * 2) {
analogWrite(SOUND_PIN, BEEP_STRENGTH);
......@@ -315,7 +305,7 @@ void loop() {
else {
analogWrite(SOUND_PIN, 0); //Чтобы постоянно не пищал, если температура станет < 80 в то время, как пищалка включена
}
*/
}
if (uint32_t(millis() - lastRpmCheck) >= RPM_CHECK_INTERVAL) {
......
......@@ -169,13 +169,6 @@ while True:
print('Fan RPMs: ' + rpm_str)
if args.manual: print('Enter the temperature value: ')
if 'debug' in incoming_line:
debug_pos = incoming_line.find('debug')
debug_str = incoming_line[debug_pos + 6:]
if args.manual: print()
print('debug: ' + debug_str)
if args.manual: print('Enter the temperature value: ')
except OSError:
if connected:
print('Serial divice was disconnected. Trying to reconnect...')
......
#include <Wire.h>
#define FAN_COUNT 6 //Количество вентиляторов
#define BAUDRATE 19200 //Скорость serial порта
#define CHECK_TEMP_DELAY 200 //Интервал получения температуры от пк (мс)
#define GET_TEMP_TIMEOUT 1000 //Если в течение этого времени температура не была прислана, считается что пк не отвечает (мс)
#define RPM_CHECK_INTERVAL 200 //Интервал подсчета rpm и обнуления tachRevs (мс)
#define RPM_VALUES_COUNT 5 //Количество значений RPM, которые записываются и усредняются (скользящее среднее)
#define MAX_PWM_DUTY_CYCLE 254 //Максимальная скважность ШИМ
#define DEFAULT_PWM_DUTY_CYCLE 254 //Скважность ШИМ по умолчанию (до подулючения к пк)
#define BEEP_INTERVAL 1000 //Интервал пищания во время потери связи или перегрева (мс)
#define BEEP_STRENGTH 100 //Уровень ШИМ для пищалки. (При 255 не работает, нужно чтобы был именно ШИМ)
#define BLINK_INTERVAL 200 //Интервал мигания светодиода во время ожидания ответа от пк (мс)
#define SOUND_PIN 10 //Номер пина пищалки
#define LED_PIN 13 //Номер пина светодиода
int pwmPins[3] = {3, 5, 6}; //Номера пинов ШИМ
volatile bool pwmState = 0; //Состояние ШИМ
volatile bool pwm_on = 1; // ==1, если ШИМ включен
int tachPins[FAN_COUNT] = {14, 15, 16, 17, 18, 19}; //Номера пинов для считывания оборотов
volatile uint16_t tachRevs[FAN_COUNT] = {}; //Обороты (просто сколько насчитано оборотов, а не rpm)
uint16_t tachRpm[FAN_COUNT][RPM_VALUES_COUNT] = {}; //rpm
volatile bool tachStates[FAN_COUNT] = {}; //Состояния (оборот считывается только если состояние == 0, и считывается 0)
/*
struct fan { //Данные о вентиляторе
int tachPin; //Номер пина тахометра
bool tachState; //Состояние тахометра
bool tachType; //Тип датчика. 0 = униполярный, 1 = биполярный
uint16_t revs; //Количество оборотв (просто, не в минуту)
uint16_t rpm; //Обороты в минуту
uint32_t lastSignalStart; //Время начала последнего сигнала тахометра
uint32_t lastSignalEnd; //Время окончания последнего сигнала тахометра
};
struct control { //Данные о контроллерах вентилятора
int pwmPin; //Номер пина ШИМ
bool pwmState; //Состояние пина ШИМ
};
*/
int temp; //Температура, полученная от пк
int target_rpm = 0;
uint32_t last_beep_time = 0;
uint32_t last_check_time = 0;
uint32_t lastRpmCheck = 0;
uint32_t lastRevTime = 0;
float p = 0;
float i = 0;
float d = 0;
float k_p = 0.3;
float k_i = 0.0004;
float k_d = 160;
void wait_for_connection(bool beep) { //функция, которая мигает светодиодом и пищит (если beep == true) до тех пор, пока не будет установлено соединение с ПК
Serial.println("start_transmission");
digitalWrite(LED_PIN, 1);
unsigned long last_blink_time = millis();
while (Serial.available() == 0) {//ждем ответа
if (millis() >= last_blink_time + BLINK_INTERVAL * 2) {
digitalWrite(LED_PIN, 1);
last_blink_time = millis();
}
else if (millis() >= last_blink_time + BLINK_INTERVAL) {
digitalWrite(LED_PIN, 0);
}
if (beep) {
if (millis() >= last_beep_time + BEEP_INTERVAL * 2) {
analogWrite(SOUND_PIN, BEEP_STRENGTH);
last_beep_time = millis();
}
else if (millis() >= last_beep_time + BEEP_INTERVAL) {
analogWrite(SOUND_PIN, 0);
}
}
}
digitalWrite(LED_PIN, 0); //гасим светодиод
analogWrite(SOUND_PIN, 0); //и выключаем пищалку
while (Serial.available()) {// очистка буфера
Serial.readString();
}
}
void tempFromPc()// получаем температуру от пк
{
String rpm_string = "send_rpm_";
for (int fan = 0; fan < FAN_COUNT; fan++) {
uint32_t avg_rpm = 0;
for (int val = 0; val < RPM_VALUES_COUNT; val++) {
avg_rpm += tachRpm[fan][val];
}
avg_rpm /= RPM_VALUES_COUNT;
rpm_string += String(avg_rpm);
if (fan < 5) {
rpm_string += "_";
}
}
Serial.println("req_temperature_" + rpm_string + "_debug_" + String(OCR2A)); //запрос к пк
unsigned long query_time = millis();
while (Serial.available() == 0) { //ждем пока ответит
if (millis() >= query_time + GET_TEMP_TIMEOUT) { //если слишком долго не отвечает
//Венитялторы на максимум
for (int i = 0; i < 3; i++) {
OCR2A = 254;
}
wait_for_connection(true);//снова ждем подключения
break;
}
}
String response = Serial.readStringUntil('\n');
temp = response.substring(12).toInt();
}
void autocontrol()//Автоконтроль температуры
{
tempFromPc();//получаем температуру от пк
target_rpm = temp;
}
void rpm_control() {
for (int fan = 0; fan < FAN_COUNT; fan++) {
for (int val = 0; val < RPM_VALUES_COUNT - 1; val++) {
tachRpm[fan][val] = tachRpm[fan][val + 1];
}
tachRpm[fan][RPM_VALUES_COUNT - 1] = (tachRevs[fan] * (60000 / uint32_t(millis() - lastRpmCheck))) / 2;
tachRevs[fan] = 0;
}
uint32_t avg_rpm = 0;
const int fan = 0;
for (int val = 0; val < RPM_VALUES_COUNT; val++) {
avg_rpm += tachRpm[fan][val];
}
avg_rpm /= RPM_VALUES_COUNT;
float pwmDutyCycle = 0;
uint8_t byte_pwmDutyCycle = 0;
d = (target_rpm - int(avg_rpm) - p) / float(millis() - lastRpmCheck);
p = target_rpm - int(avg_rpm);
i = i + p * (millis() - lastRpmCheck);
pwmDutyCycle = round(p * k_p + i * k_i + d * k_d);
if (pwmDutyCycle <= 1) {
byte_pwmDutyCycle = 1;
}
else if (pwmDutyCycle >= MAX_PWM_DUTY_CYCLE) {
byte_pwmDutyCycle = MAX_PWM_DUTY_CYCLE;
}
else {
byte_pwmDutyCycle = pwmDutyCycle;
}
if (byte_pwmDutyCycle == 0) {
if (pwm_on == 1) {
pwm_on = 0; //выкл ШИМ
for (int i = 0; i < 3; i++) {//выкл пины ШИМ
digitalWrite(pwmPins[i], 0);
}
}
}
else {
if (pwm_on == 0) {
pwm_on = 1; //вкл ШИМ
}
OCR2A = byte_pwmDutyCycle; //установка скважности
}
//Почему-то иногда может заглючить: скважность стоит 254, pwm_on равно 1, но вентилятор не крутится (но пищит). Похоже каким-то образом меняется pwmState, когда не надо
//Serial.println("debug_" + String(byte_pwmDutyCycle));
lastRpmCheck = millis();
}
ISR(TIMER2_COMPA_vect) { //Эта функия вызывается при прерывании по таймеру 2
if (pwmState == 1) {
pwmState = 0;
}
else {
pwmState = 1;
}
for (int i = 0; i < 3; i++) {//управление пинами ШИМ
digitalWrite(pwmPins[i], pwmState);
}
if (pwmState == 1) { //Если питание включилось, то проверяем состояния тахометров
for (int i = 0; i < FAN_COUNT; i++) {
if (digitalRead(tachPins[i]) == 0 && tachStates[i] == 0) { //сигнал тахометра появился
tachStates[i] = 1;
//digitalWrite(13, 1);
}
else if (digitalRead(tachPins[i]) == 1 && tachStates[i] == 1) { //сигнал тахометра пропал
//digitalWrite(13, 0);
tachStates[i] = 0;
tachRevs[i]++; //засчитывается оборот
}
}
}
}
void setup() {
pinMode(LED_PIN, OUTPUT);
pinMode(SOUND_PIN, OUTPUT);
for (int i = 0; i < 3; i++) {
pinMode(pwmPins[i], OUTPUT);
}
for (int i = 0; i < FAN_COUNT; i++) {
pinMode(tachPins[i], INPUT_PULLUP);
}
TCCR2A |= (1 << WGM20); //запуск таймера
OCR2A = DEFAULT_PWM_DUTY_CYCLE; //скважность
TIMSK2 |= (1 << OCIE2A); //вкл. вызов прерывания
Serial.begin(BAUDRATE);
wait_for_connection(false); //Ждем установленя связи с ПК
}
void loop() {
if (uint32_t(millis() - last_check_time) >= CHECK_TEMP_DELAY) {
last_check_time = millis();
autocontrol(); //Получаем температуру от ПК, устанавливаем соотв. скорость вращения
/*
if (temp >= 80) { //Если перегрев, включается пищалка
if (millis() >= last_beep_time + BEEP_INTERVAL * 2) {
analogWrite(SOUND_PIN, BEEP_STRENGTH);
last_beep_time = millis();
}
else if (millis() >= last_beep_time + BEEP_INTERVAL) {
analogWrite(SOUND_PIN, 0);
}
}
else {
analogWrite(SOUND_PIN, 0); //Чтобы постоянно не пищал, если температура станет < 80 в то время, как пищалка включена
}
*/
}
if (uint32_t(millis() - lastRpmCheck) >= RPM_CHECK_INTERVAL) {
rpm_control();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment