Параллельные задачи исключают любые циклы?

Artas
Offline
Зарегистрирован: 27.01.2020

Есть два участка программы которые должны работать параллельно. По отдельности они выполняют свои функции на отлично. Решил использовать библиотеку <Thread.h>, но она исключает delay. Тогда решил использовать millis для задержек. Сделал, но в части кода есть цикл do while, во время выполнения которого все равно ничего другого не работает. Неужели нельзя написать более-менее сложную программу с несколькими потоками вычислений?

 

Программа меряет Вольтаж и усредняет за промежуток времени, после записывает на флешку (пока не выводит, т.к. настраиваю через монитор порта).

Moderator : пожалуйста, вставьте код правильно (возможно, новым сообщением в тему), 

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

если ты задачу опишешь внятно, да код вставишь правильно, то мошт чо и придумаеца. 

http://arduino.ru/forum/obshchii/vstavka-programmnogo-koda-v-temukommentarii

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Artas пишет:

Неужели нельзя написать более-менее сложную программу с несколькими потоками вычислений?

"Кто-то не умеет" и "невозможно", это, как говорят в Одессе, две большие разницы.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Artas пишет:

Неужели нельзя написать более-менее сложную программу с несколькими потоками вычислений?

Можно... наверное.

-NMi-
Offline
Зарегистрирован: 20.08.2018

Дурдуина с несколькими потоками вычислений???     Поржал!

b707
Offline
Зарегистрирован: 26.05.2017

Artas пишет:

Сделал, но в части кода есть цикл do while, во время выполнения которого все равно ничего другого не работает. Неужели нельзя написать более-менее сложную программу с несколькими потоками вычислений?

можно, но от цикла while , скорее всего, придется отказаться. Все это отлично пишется на одних миллис, без всяких потоков

Цитата:
Программа меряет Вольтаж и усредняет за промежуток времени, после записывает на флешку (пока не выводит, т.к. настраиваю через монитор порта).

и где тут два потока? :)
Artas
Offline
Зарегистрирован: 27.01.2020

Спасибо, извините, первый день тут

Artas
Offline
Зарегистрирован: 27.01.2020
#include <SPI.h>    //Подключаем библиотеки
#include <SD.h>
#include <Thread.h>

Thread Razbavlenie = Thread();
Thread Data = Thread();

#define Vremya_izmereniya_min 0.5    //Параметры для управления времени запси данных и количества измерений
#define Kolichestvo_izmereniy 100
#define Uslovie_zakr 4.4  //Параметры работы клапана
#define Uslovie_otkr 2.8


float Uphre = A0; //Присваиваем имя и аналоговым и цифровым контактам и задаем тип данных
int SDled = 9;
int Rele = 8;
const int chipSelect = 4;
float Uanalog = 0;     //Присваиваем имя переменным и задаем тип данных
float Udigital[Kolichestvo_izmereniy];
float Uavgdigital = 0;
int i = 0;
int ledblink = 0;
float UdigitalKlap = 0;
float sum = 0;


void setup(){
Serial.begin(9600);  //Инициируем последовательное соединение и задаем скорость передачи данных между ПК и Ардуино в бит/c 
pinMode(Rele, OUTPUT);
pinMode(SDled, OUTPUT);
Data.onRun(Dannie);     // назначаем потоку задачу
Data.setInterval(100);
Razbavlenie.onRun(Klapan);  // назначаем потоку задачу
Razbavlenie.setInterval(10000); // задаём интервал срабатывания, мсек
}


void loop() {

if (Razbavlenie.shouldRun())
Razbavlenie.run(); // запускаем поток
if (Data.shouldRun())
Data.run(); // запускаем поток
}



void Dannie()
{
for(i=0; i<Kolichestvo_izmereniy; i++){
Uanalog = analogRead(Uphre);   //Считываем значение с указанного аналогового входа
Udigital[i] = Uanalog*5.0/1023.0;   //Преобразуем поток аналоговых данных в цифровые данные
Uavgdigital += Udigital[i];
//Serial.print("SumSD =\t");
//Serial.print(i);
//Serial.print("\t");
//Serial.println(Uavgdigital);
delay(Vremya_izmereniya_min*60000/Kolichestvo_izmereniy);//расчет разрыва измерений = время измерений (мин) * 60000 (кол-во милисекунд в минуте) / количество измерений
}
//Serial.print("UavgSD =\t");   
//Serial.println(Uavgdigital/Kolichestvo_izmereniy);//Производим вывод данных в монитор последовательного интерфейса

File dataFile = SD.open("lite.txt", FILE_WRITE); //Запись на флешку данных в фаил лайт
if (dataFile) {
dataFile.println(Uavgdigital/Kolichestvo_izmereniy);
dataFile.close();
}
Uavgdigital = 0;   //обнуление среднего значения после записи

}



void Klapan()
{
Uanalog = analogRead(Uphre);          
UdigitalKlap = Uanalog*5.0/1023.0;
//Serial.print("UdigitalKlap =\t");
//Serial.println(UdigitalKlap); 
if (UdigitalKlap <= Uslovie_otkr)
{
Uavgdigital = 0;
sum = 0;
for(i=0; i<100; i++){
Uavgdigital = 0.0;
Uanalog = analogRead(Uphre);                                                     
Udigital[i] = Uanalog*5.0/1024.0;                                                 
sum += Udigital[i];
//Serial.print("SUM1C =\t");
//Serial.print(i);
//Serial.print("\t");
//Serial.println(sum);
delay(100);
}
Uavgdigital = sum/100;
//Serial.print("UavgD1C =\t");
//Serial.println(Uavgdigital);
if (Uavgdigital <= Uslovie_otkr)
{
do {
Uavgdigital = 0;
sum = 0;
digitalWrite(Rele, HIGH);
delay(5000);
digitalWrite(Rele, LOW);
delay(10000);
for(i=0; i<99; i++){
Uanalog = analogRead(Uphre);                                                      
Udigital[i] = Uanalog*5.0/1024.0;                                                
sum += Udigital[i];
//Serial.print("SUM2C =\t");
//Serial.print(i);
//Serial.print("\t");
//Serial.println(sum);
delay(100);
}
Uavgdigital = sum/100;
//Serial.print("UavgD2C =\t");
//Serial.println(Uavgdigital);
} while(Uavgdigital <= Uslovie_zakr);
digitalWrite(Rele, LOW);
}
}
}

 

b707
Offline
Зарегистрирован: 26.05.2017

и где вы "переписали на миллис"? Сплошные делеи в коде, такое никакими потоками не вылечить

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

b707 пишет:

Сплошные делеи в коде, такое никакими потоками не вылечить

Я пытался сделать потоки с делеями на специально для этого предназначенном языке - получил эпичный облом :-(

rkit
Offline
Зарегистрирован: 23.11.2016

Потоки с делеями есть на rtos. Которая прожорлива по памяти, но для мелких задач помещается в уно.

Artas
Offline
Зарегистрирован: 27.01.2020

да, это старый участок кода, сейчас поделюсь миллис, но он не доделан. Вторая часть кода отключена, т.к. проблема в первом участке, т.к. пока крутится do while ничего другого не происходит.  Суть - есть два потока - один записывает данные вольтметра (фоторезистор стоит напротив светодиода, а между ними протекает жидкость, которая со временем пропускает все меньше света (загрязняется), и когда напряжение достигает нижнего порога Uslovie_otkr происходит открытие клапана, который эту жидкость разбавляет уже до верхнего предела Uslovie_zakr) на SD с усреднением за установленный параметрами период времени Vremya_izmereniya_min с установленным шагом Kolichestvo_izmereniy. Второй поток поверяет вольтаж уже чаще и если он меньше нижнего порога  Uslovie_otkr то начинает усреднять вольтаж - 100 измерений с задержкой 100 мс. Если условие уже усредненного значения вольтажа все еще меньше Uslovie_otkr, то программа запускает открытие клапана на 5 секунд, ждет 10 секунд (пока идет перемешивание), затем опять измеряет вольтаж, усредняет его и опять сравнивает условие уже этого усреднения с Uslovie_zakr, если оно выполняется, то выходит из цикла. По сути пол получается пилообразный график.

 

 

#include <SPI.h>                         //Подключаем библиотеки
#include <SD.h>

#define Vremya_izmereniya_min 0.2           //Параметры для управления времени запси данных и количества измерений
#define Kolichestvo_izmereniy 10
#define Uslovie_zakr 4.4  //Параметры работы клапана
#define Uslovie_otkr 2.8


float Uphre = A0;         //Присваиваем имя и аналоговым и цифровым контактам и задаем тип данных
int SDled = 9;
int Rele = 8;
const int chipSelect = 4;
float Uanalog = 0;         //Присваиваем имя переменным и задаем тип данных
float Udigital[Kolichestvo_izmereniy];
float Uavgdigital = 0;
int i = 0;
int k = 0;
int m = 0;
int ledblink = 0;
float UdigitalKlap = 0;
float sum = 0;
unsigned long Time1;
unsigned long Time2;  
unsigned long Time3;
unsigned long Time4;

void setup(){
Serial.begin(9600);       //Инициируем последовательное соединение и задаем скорость передачи данных между ПК и Ардуино в бит/c 
pinMode(Rele, OUTPUT);
pinMode(SDled, OUTPUT);
}


void loop() {
if (millis()- Time1 > 100) 
{
Time1 = millis();
do{
if( millis()- Time2 > Vremya_izmereniya_min*60000/Kolichestvo_izmereniy)                        //расчет разрыва измерений = время измерений (мин) * 60000 (кол-во милисекунд в минуте) / количество измерений
{
Time2 = millis();
for(i=0; i<1; i++){
Uanalog = analogRead(Uphre);       //Считываем значение с указанного аналогового входа
Udigital[i] = Uanalog*5.0/1023.0;    //Преобразуем поток аналоговых данных в цифровые данные
Uavgdigital += Udigital[i];
k++;
Serial.print("SumSD =\t");
Serial.print(k);
Serial.print("\t");
Serial.println(Uavgdigital);
}                           
}
}while (k<Kolichestvo_izmereniy);
//Serial.print("UavgSD =\t");    //Производим вывод данных в монитор последовательного интерфейса
//Serial.println(Uavgdigital/Kolichestvo_izmereniy);
 
File dataFile = SD.open("lite.txt", FILE_WRITE);       //Запись на флешку данных на SD карту
if (dataFile) {
dataFile.println(Uavgdigital/Kolichestvo_izmereniy);
dataFile.close();
}
Uavgdigital = 0;
k = 0;  
}



if (millis()- Time3 > 1000)   
{ Time3 = millis ();
Serial.println(1111);
}
/*
Uanalog = analogRead(Uphre);        //Считываем значение с указанного аналогового входа
UdigitalKlap = Uanalog*5.0/1023.0;
Serial.print("UdigitalKlap =\t");
Serial.println(UdigitalKlap);

 
if (UdigitalKlap <= Uslovie_otkr)
{


do {  
Uavgdigital = 0;
sum = 0;
if(millis()- Time1 > 100)                     
{
for(i=0; i<1; i++){
Uanalog = analogRead(Uphre);                                                     
Udigital[i] = Uanalog*5.0/1024.0;                                                 
sum += Udigital[i];
m++;
Serial.print("SUM1C =\t");
Serial.print(m);
Serial.print("\t");
Serial.println(sum);
}
}
}while (m<100);
Uavgdigital = sum/100;
//Serial.print("UavgD1C =\t");
//Serial.println(Uavgdigital);





if (Uavgdigital <= Uslovie_otkr)
{
do {
Uavgdigital = 0;
sum = 0;
digitalWrite(Rele, HIGH);
delay(5000);
digitalWrite(Rele, LOW);
delay(10000);
for(i=0; i<99; i++){
Uanalog = analogRead(Uphre);                                                      
Udigital[i] = Uanalog*5.0/1024.0;                                                
sum += Udigital[i];
//Serial.print("SUM2C =\t");
//Serial.print(i);
//Serial.print("\t");
//Serial.println(sum);
delay(100);
}
Uavgdigital = sum/100;
//Serial.print("UavgD2C =\t");
//Serial.println(Uavgdigital);
} while(Uavgdigital <= Uslovie_zakr);
digitalWrite(Rele, LOW);
}
}
}*/
}

 

bwn
Offline
Зарегистрирован: 25.08.2014

Кроме лупа и прочих циклов, в ардуине, и не только, существуют функции, заглядывать в них разрешается из любого места.

b707
Offline
Зарегистрирован: 26.05.2017

Artas - конкретно в ваши обьяснения я не вчитывался, но по коду могу рассказать. что вам нужно изменить.

Если вы хотите работать в несколько потоков на однопроцессорной системе, все длинные циклы. где программа проводит десятки секунд - все эти while и for - должны быть безжалостно выкинуты. Программу надо кардинально переписать.

Общий принцип - возьмем для примера ваш цикл for. который делает 100 измерений с интервалом 100мс. У вас в программе он написан в блокирующем стиле - то есть программа заходит в цикл и сидит в нем, пока не сделает все измерения - то есть 100 * 0.1 сек = 10 сек. По меркам мира электроники 10 сек - это вечность.

Вместо цикла делаем так: - пишем коротенькую процедуру, которая делает один замер (а не 100!) и без всяких задержек заканчивает работу, позволяя программе перейти к работе с другим потоком. Делаем что-то во втором потоке, а через 100мс (отмеряем с помощью миллис )-  снова заходим в процедуру и снова делаем один замер. И так, пока не сделаем все 100 измерений.

естесственно, второй поток, все ваши бесконечные while - тоже должен быть переписан так, чтобы система делала одно короткое действие и выходила из процедуры в основной цикл.

таким образом можно делать параллельно и два дела. и три, больше... и без всяких тяжелых библиотек типа Threads или RTOS- которые, в общем-то - работают ровно на том же принципе переключения

 

Artas
Offline
Зарегистрирован: 27.01.2020

b707 пишет:

Вместо цикла делаем так: - пишем коротенькую процедуру, которая делает один замер (а не 100!) и без всяких задержек заканчивает работу, позволяя программе перейти к работе с другим потоком. Делаем что-то во втором потоке, а через 100мс (отмеряем с помощью миллис )-  снова заходим в процедуру и снова делаем один замер. И так, пока не сделаем все 100 измерений.

Естественно, второй поток, все ваши бесконечные while - тоже должен быть переписан так, чтобы система делала одно короткое действие и выходила из процедуры в основной цикл.

Действительно помогло, Спасибо. Мне, как человеку, который занимается этим ок. месяца было очень трудно, странно, что нет никаких реально работающих библиотек. В общем с кучей костылей все работает, спасибо большое) 

      
b707
Offline
Зарегистрирован: 26.05.2017

Artas пишет:

Действительно помогло, Спасибо. Мне, как человеку, который занимается этим ок. месяца было очень трудно, странно, что нет никаких реально работающих библиотек. В общем с кучей костылей все работает, спасибо большое) 



Конечно, когда уже есть готовый код и надо весь перекорежить - это непрсото. А вот если сразу писать в этом стиле - будет легче, и костылей не понадобится.