ARDUINO Нужна помощь по написанию кода

Статус
В этой теме нельзя размещать новые ответы.

Tazovod

✩✩✩✩✩✩✩
25 Ноя 2020
3
0
Оформи код соответствующим тэгом
Есть код (прикреплен ниже), работает, но в 6/8 случаев.
Может быть код не правильно написан, мб в реализации недечеты.
Пока прошу помочь с кодом, если есть ошибки написания.
В кратце о проге: ардуино получая сигнал с датчика холла определяет обороты коленвала ДВС, и на основании измерения времени между сигналами с датчика холла подает сигнал на коммутатор (pinSpark) для образования искры, т.е., например, имеем время между сигналами с датчика t=0,005с, что соответствует 6000 об/мин, вычисляем время до подачи сигнала на коммутатор: Tign=t-(время прохождения одного градуса * количество градусов (Угол опережения зажигания)).
Т.о. между сигналами с датчика холла подается сигнал на коммутатор и т.д.





int uozbyrpm [6][10]={
{ 5, 5, 5, 5, 5,5 ,5 ,5 ,5 ,5 }, // rpm: [0;999]
{6 ,6 ,7 ,8 ,8 ,9 ,10,10,11,11}, // rpm: [1000;1999]
{12,12,13,14,14,15,16,16,17,17}, // rpm: [2000;2999]
{18,18,19,19,20,20,21,21,22,22}, // rpm: [3000;3999] обороты коленчатого вала
{23,23,24,24,25,25,26,26,27,27}, // rpm: [4000;4999]
{27,27,27,27,27,27,27,27,27,27} // rpm: [5000;5980]
};
unsigned int rpm;
byte x,y,pinSpark=10; //pinSpark - пин для открытия оптрона
volatile unsigned long lastflash,p,i; //Tign - время до подачи сигнала на катушку
volatile unsigned long t,tz,Tign;
boolean f=1;


void setup(){
Serial.begin(9600);
attachInterrupt(0,sens,FALLING);
pinMode(pinSpark,OUTPUT); //устанавливает режим выхода на пин для катушки
pinMode(12,OUTPUT); //устанавливает режим выхода на 12 пин
digitalWrite(12,HIGH); //подает 5 вольт на 12 пин (для оптопары)

Serial.print("RPM ");
Serial.print("t ");
Serial.print("Угол зажи ");
Serial.println("Tign");
}

void sens(){

t=(micros()-lastflash); //время между данными с датчика Холла
rpm=30/((float)(t)/1000000); //расчет оборотов
f=1;
lastflash=micros(); //запоминает время


}

void loop (){

if(rpm<5980){ //если обороты двигателя больше 5980, то искра не подается (отсечка)

if (f==1){
x=rpm/1000; //вычисление строки массива с углами зажигания
y=(rpm%1000)/100; //вычисление столбца массива с углами зажигания
Tign=t-((float)uozbyrpm[x][y]0.5((float)(t)/90)); //расчет задержки до подачи сигнала на катушку
tz=(t/90)*10; //длительность подачи сигнала на катушку

delayMicroseconds(Tign); //задержка до подачи сигнала на катушку
digitalWrite(pinSpark,HIGH); //подача сигнала на катушку
delayMicroseconds(1000); //длительность подачи сигнала на катушку
digitalWrite(pinSpark,LOW);

f=0;

Serial.print(rpm);
Serial.print(" ");
Serial.print(t);
Serial.print(" ");
Serial.print(uozbyrpm[x][y]);
Serial.print(" ");
Serial.println(Tign);


}
}
}
 

Вложения

  • 2.9 KB Просмотры: 3
Изменено:

bort707

★★★★★★✩
21 Сен 2020
3,056
910
этот код вообще не будет работать по назначению.
Здесь сигнал на катушку подается в случайный момент, никак не привязанный к положению коленвала.
Не говоря уж о том, что в основном цикле куча мусора, по длительности большая чем интервал между импульсами, поэтому код еще и срабатывать будет не каждый раз.
Править этот код бессмысленно, его лучше выкинуть и написать новый
 
  • Лойс +1
Реакции: ТехнарьКто и DAK

DAK

★★★✩✩✩✩
8 Окт 2020
517
137
@bort707, Всё очень работает, как раз переменная F связывает одно с другим :unsure: (гоню, в прерывани не могло менятся из-за неверного объявления)
Мне кажется автор не любит читать документацию, скорее он наверное с высшим образованием, такие как мы инструкций не читают до поры до времени... Для начала я бы почитал правила форума и особенно обратил внимание на то, что код надо вставлять по другому!!!!
Конечно лирика, как обычно никто даже не подскажет куда человеку идти...
@Tazovod, Дело в том, что не все функции работают в прерываниях, если почитать тут внимательно:
Ещё несколько важных моментов:


  • Переменные, изменяемые в прерывании, должны быть объявлены как
    volatile
  • В прерывании не работают задержки типа
    delay()
  • В прерывании не меняет своё значение
    millis()
    и
    micros()
  • В прерывании некорректно работает вывод в порт (
    Serial.print()
    ), также не стоит там его использовать – это нагружает ядро
  • В прерывании нужно стараться делать как можно меньше вычислений и вообще “долгих” действий – это будет тормозить работу МК при частых прерываниях! Что же делать? Читайте ниже.
Единственно что у Вас могло бы считаться в прерывании, это f, если бы Вы её правильно объявили;
Зачем двумерный массив? и в итоге в коде вы из INT массива делаете FlOAT, потом его делите на 2 и на 90, не проще ли сделать одномерный массив FLOAT с уже поделенными данными? Или допустим зачем переменная tz??
А вывод в ком порт можно делать только дома в режиме теста, времени на loop у Вас очень мало должно быть.
Я думаю Вы хотели сделать
C++:
float uozbyrpm[60]={
    5, 5, 5, 5, 5, 5, 5, 5, 5, 5,      // rpm: [0;999]
    6, 6, 7, 8, 8, 9,10,10,11,11,      // rpm: [1000;1999]
   12,12,13,14,14,15,16,16,17,17,      // rpm: [2000;2999]
   18,18,19,19,20,20,21,21,22,22,      // rpm: [3000;3999]       обороты коленчатого вала
   23,23,24,24,25,25,26,26,27,27,      // rpm: [4000;4999]
   27,27,27,27,27,27,27,27,27,27       // rpm: [5000;5980]
};
unsigned int rpm;
byte x,pinSpark=10;                   //pinSpark - пин для открытия оптрона
unsigned long lastflash,p,i;           //Tign - время до подачи сигнала на катушку
unsigned long t,tz,Tign;
volatile boolean f=false;


  void setup(){
    Serial.begin(9600);
    attachInterrupt(0,sens,FALLING);
    pinMode(pinSpark,OUTPUT);            //устанавливает режим выхода на пин для катушки
    pinMode(12,OUTPUT);                  //устанавливает режим выхода на 12 пин
    digitalWrite(12,HIGH);               //подает 5 вольт на 12 пин (для оптопары)
//    Serial.print("RPM           ");   просто  мусор
//    Serial.print("t             ");
//    Serial.print("Угол зажи          ");
//    Serial.println("Tign");

  for (i=0;i<60;i++){
    uozbyrpm[i]=1-uozbyrpm[i]/180;         // Готовим массив, чобы в теле бустрей считать
  }
  }

  void sens(){ 
    f=true;
  }

  void loop (){
  
   if (f){
   t=(micros()-lastflash);
   rpm=30000000/t;
   lastflash=micros();
   if(rpm<5980){                         //если обороты двигателя больше 5980, то искра не подается (отсечка)
     x=rpm/100;                          //вычисление позиции в массиве
     Tign=t*uozbyrpm[x];                 //расчет задержки до подачи сигнала на катушку
     tz=t/9;                             //длительность подачи сигнала на катушку
     delayMicroseconds(Tign);             //задержка до подачи сигнала на катушку
     digitalWrite(pinSpark,HIGH);         //подача сигнала на катушку
     delayMicroseconds(tz);              //длительность подачи сигнала на катушку
     digitalWrite(pinSpark,LOW);
     f=0;

/*  Serial.print(rpm);
    Serial.print("          ");
    Serial.print(t);
    Serial.print("               ");
    Serial.print(uozbyrpm[x]);
    Serial.print("            ");
    Serial.println(Tign);*/


   }
  }
  }

И да, получается на катушке сигнал будет 0,9 от времени 1 оборота. И пока я не понимаю будет это вообще работать или нет, ну в общем я уже переживаю за вашу катушку.
 
Изменено:
  • Лойс +1
Реакции: ТехнарьКто

ТехнарьКто

★★★★★✩✩
13 Янв 2020
270
437
Может быть код не правильно написан, мб в реализации недечеты.
Про реализацию в железе. Если Вы используете готовую плату ардуино, то для справки:
1.jpg
Беру три новых ардуино нано.
Заливаю скетч генерирующий максимальную частоту, для atmega это (половина от тактовой) в данном случае половина частоты кварцевого резонатора 16 Mhz. Измеряю, что получилось.
2.jpg
3.jpg
4.jpg

Умножаю на 2, получаю частоту на которой работает кварцевый резонатор для замеров 1, 2, 3.
1) 15 940 014 Гц
2) 16 013 210 Гц
3) 16 001 832 Гц
Наблюдаю достаточно большой разброс.

Для расчета беру замер №1. Имею право, поскольку исходить надо из худшего реального результата.

Изменение времени для ардуино нано будет равно отношению измеренной частоты к эталонной. Идеально 1. Но фактически ардуина не знает про настоящую секунду и будет использовать то, что есть, использовать во всех расчетах ардуины при взаимодействии с реальным миром.
15 940 014/16 000 000 = 0,99625088 время внутренней секунды микропроцессора относительно реальной секунды.

1(настоящая секунда) - (внутренняя секунда) примерно = 0,00375 погрешность взаимодействия выраженная через время секунды
У Вас
с датчика t=0,005с
Ошибка управления 100/0,005*0,00375 = 75 %.

На мой взгляд, управление процессом с ошибкой в 75% говорит о крупных недочетах в аппаратной части проекта.

Жду конструктивную критику, если где-то ошибаюсь.

PS Управление процессом при использовании atmega тактируемого от точного кварца, будет совсем не ардуино.
PPS Конечно это IMHO и бегать или нет по граблям, исключительно Ваш выбор.
 

poty

★★★★★★✩
19 Фев 2020
3,230
940
@ТехнарьКто, работать будет, потому что и время 0,005 с измеряется во "внутренних секундах", а не в реальных. Т.е., если перевести это в цифры на дисплее и сравнивать с эталонным измерителем, то будет заметно, а для внутренней замкнутой системы - нет.
Кстати, расчёты ошибки также неверны. Почему Вы считаете погрешность от одной секунды? Погрешность должна отсчитываться от измеряемого интервала в соответствии с дискретностью измерений. "Паспортная" дискретность измерений у Atmega (по функции micros()) - 4 микросекунды. Это наивысшая точность, с которой можно измерить интервал применёнными средствами.
В соответствии с Вашими измерениями (которые тоже имеют погрешность, думаю, более высокую, нежели показанные отклонения), этот интервал отличается от идеального в худшем случае в 0,0037 раз (или на 0,37%), т.е., для 0,005 с это будет 19мкс, что гораздо больше точности измерений (4мкс). Т.е., конечная точность будет этими 19мкс и ограничиваться.
 

DAK

★★★✩✩✩✩
8 Окт 2020
517
137
Да я если честно как посмотрю на этот масив, так у меня почти прямая в голове отрисовывается, прям ощущение, что вообще нах не надо сюда пихать МК, полюбому это можно сделать без МК, или с помощью мк управлять схемой запуска, тогда достаточно просто знать обороты и не надо будет кучу вычислений в милисекунду делать, да и надёжнее должно быть, вот над схемотехникой надо подумать, или погуглить. А то что схема с ардуинкой будет работать, я даже не удивлюсь, в первых переходных уазках для подстраховки специально стояло реле-которое само себя размыкало, и когда из строя выходил блок "зажигания", просто один провод кидался на это самое реле, и оно просто в произвольное время кидала искру на тромблёр, так двигло работало отлично и без проблем.
 

ТехнарьКто

★★★★★✩✩
13 Янв 2020
270
437
@ТехнарьКто, работать будет, потому что и время 0,005 с измеряется во "внутренних секундах", а не в реальных.
А вал двигателя крутится во внутренних секундах или в реальных?
6 000 оборотов в минуту, в минуте 60 секунд. 100 оборотов в секунду. Ошибка работы 0,00375/100 оборотов = 0,0000375 ошибка на оборот. 1 с / 100 = 0,01 сек на оборот. 0,01 / 360 градусов = 0,00002778 секунды на градус. Секунды/(секунды/градусы) 0,0000375/0,00002778 = 1,35 градуса на оборот будет ошибка в идеальных лабораторных условиях. Но я в Вас верю. Вы сможете создать такой код, чтобы ошибка возросла на порядок. Кроме того, частота кварца ардуино сильно изменяется в зависимости от температуры. Еще ардуина может вешаться, словив помехи от высоковольтной искры системы зажигания двигателя. Бег по граблям и набивание шишек на лбу, Вам никто не запрещает. Успехов в обруливании детских граблей бъющих по яйцам.
 

ТехнарьКто

★★★★★✩✩
13 Янв 2020
270
437
Обороты же считаются той же ардуиной по датчику хола, т.е. получается что во внутренних
Делай людям добро и бросай его в воду. Спасибо Похабыч, пусть бегают по граблям. Если даже ты не понял, то далее объяснять, смысл отсутствует.

PS Заделать дырку в МКС скотчем, наше все в высоких технологиях.
 

DAK

★★★✩✩✩✩
8 Окт 2020
517
137
Да всё тут правильно пишут. Всё в мире относительно, в этой замкнутой системе единица измерения одна, назовём её ардуиновской микросекундой, единственно на что влияет погрешность, которую Вы @ТехнарьКто выявили, так это на выбор угла опережения из массива, +-1 по позиции массива. Меня же более волнует то, что на катушку будет подано напряжение в течении 0,9 от времени оборота вала, потом с неё напряжение снимут и через доли секунды (если код сработает) опять подадут, я не уверен что это хорошая идея, хорошо, если катушка подключена через последовательно конденсатору (как положено), но тогда эту задержку можно вообще не учитывать и не высчитывать! Есть большая доля вероятности, что либо вообще искры не будет, либо то, что катушка от такого издевательства просто накалится и сдохнет.
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,301
Москва
Да , я не понял. Я понимаю, что код сыровато-корявый. Но попробую сам рассчитать. Беру исходные 100 оборотов в секунду, оборот 0.01, тут все ок. Точность выставления УОЗ я не знаю. Ну допустим пол градуса. Тогда время надо еще разделить на 720. Будем делить на 1000, для простоты. Итого 0.00001 по времени на максимальных оборотах. Полностью согласен с расчетами. Можно ли выставить с такой точностью ? Вот 1-ая ссылка по таймерам https://habr.com/ru/post/337430/ не берусь судить на сколько достоверно.
Если взять imer_init_ISR_100KHz(TIMER_DEFAULT); То это и будет точность пол градуса на оборот. Останется запустить искру в зависимости от положения и за определенное время до момента х.
А на сколько стабильно будет работать я уже не скажу. Т.е. и время оборота и время искры будет вычисляться по одному таймеру, то может будет приемлемо.
 

bort707

★★★★★★✩
21 Сен 2020
3,056
910
Можно ли выставить с такой точностью ?
можно, если и то и другое делать в прерывании по таймеру, а не так как у ТС - изменением каких-то флагов в Loop(), останавливаясь при этом "покурить" на время вывода в Сериал

Задачка в приниципе несложная, но исходный код ТС никуда не годится. Я сразу написал, что его нужно выкинуть... а вы что-то обсуждать бросились :)))

Любопытно, что ТС вбросил - и больше не показывается, а мы тут головы ломаем непонятно для кого:)
 
  • Лойс +1
Реакции: Nikanor

Tazovod

✩✩✩✩✩✩✩
25 Ноя 2020
3
0
Да всё тут правильно пишут. Всё в мире относительно, в этой замкнутой системе единица измерения одна, назовём её ардуиновской микросекундой, единственно на что влияет погрешность, которую Вы @ТехнарьКто выявили, так это на выбор угла опережения из массива, +-1 по позиции массива. Меня же более волнует то, что на катушку будет подано напряжение в течении 0,9 от времени оборота вала, потом с неё напряжение снимут и через доли секунды (если код сработает) опять подадут, я не уверен что это хорошая идея, хорошо, если катушка подключена через последовательно конденсатору (как положено), но тогда эту задержку можно вообще не учитывать и не высчитывать! Есть большая доля вероятности, что либо вообще искры не будет, либо то, что катушка от такого издевательства просто накалится и сдохнет.
Я забыл указать, что за один оборот на ардуино приходит 4 сигнала с датчика Холла)

@bort707, Всё очень работает, как раз переменная F связывает одно с другим :unsure: (гоню, в прерывани не могло менятся из-за неверного объявления)
Мне кажется автор не любит читать документацию, скорее он наверное с высшим образованием, такие как мы инструкций не читают до поры до времени... Для начала я бы почитал правила форума и особенно обратил внимание на то, что код надо вставлять по другому!!!!
Конечно лирика, как обычно никто даже не подскажет куда человеку идти...
@Tazovod, Дело в том, что не все функции работают в прерываниях, если почитать тут внимательно:
Ещё несколько важных моментов:


  • Переменные, изменяемые в прерывании, должны быть объявлены как
    volatile
  • В прерывании не работают задержки типа
    delay()
  • В прерывании не меняет своё значение
    millis()
    и
    micros()
  • В прерывании некорректно работает вывод в порт (
    Serial.print()
    ), также не стоит там его использовать – это нагружает ядро
  • В прерывании нужно стараться делать как можно меньше вычислений и вообще “долгих” действий – это будет тормозить работу МК при частых прерываниях! Что же делать? Читайте ниже.
Единственно что у Вас могло бы считаться в прерывании, это f, если бы Вы её правильно объявили;
Зачем двумерный массив? и в итоге в коде вы из INT массива делаете FlOAT, потом его делите на 2 и на 90, не проще ли сделать одномерный массив FLOAT с уже поделенными данными? Или допустим зачем переменная tz??
А вывод в ком порт можно делать только дома в режиме теста, времени на loop у Вас очень мало должно быть.
Я думаю Вы хотели сделать
C++:
float uozbyrpm[60]={
    5, 5, 5, 5, 5, 5, 5, 5, 5, 5,      // rpm: [0;999]
    6, 6, 7, 8, 8, 9,10,10,11,11,      // rpm: [1000;1999]
   12,12,13,14,14,15,16,16,17,17,      // rpm: [2000;2999]
   18,18,19,19,20,20,21,21,22,22,      // rpm: [3000;3999]       обороты коленчатого вала
   23,23,24,24,25,25,26,26,27,27,      // rpm: [4000;4999]
   27,27,27,27,27,27,27,27,27,27       // rpm: [5000;5980]
};
unsigned int rpm;
byte x,pinSpark=10;                   //pinSpark - пин для открытия оптрона
unsigned long lastflash,p,i;           //Tign - время до подачи сигнала на катушку
unsigned long t,tz,Tign;
volatile boolean f=false;


  void setup(){
    Serial.begin(9600);
    attachInterrupt(0,sens,FALLING);
    pinMode(pinSpark,OUTPUT);            //устанавливает режим выхода на пин для катушки
    pinMode(12,OUTPUT);                  //устанавливает режим выхода на 12 пин
    digitalWrite(12,HIGH);               //подает 5 вольт на 12 пин (для оптопары)
//    Serial.print("RPM           ");   просто  мусор
//    Serial.print("t             ");
//    Serial.print("Угол зажи          ");
//    Serial.println("Tign");

  for (i=0;i<60;i++){
    uozbyrpm[i]=1-uozbyrpm[i]/180;         // Готовим массив, чобы в теле бустрей считать
  }
  }

  void sens(){
    f=true;
  }

  void loop (){
 
   if (f){
   t=(micros()-lastflash);
   rpm=30000000/t;
   lastflash=micros();
   if(rpm<5980){                         //если обороты двигателя больше 5980, то искра не подается (отсечка)
     x=rpm/100;                          //вычисление позиции в массиве
     Tign=t*uozbyrpm[x];                 //расчет задержки до подачи сигнала на катушку
     tz=t/9;                             //длительность подачи сигнала на катушку
     delayMicroseconds(Tign);             //задержка до подачи сигнала на катушку
     digitalWrite(pinSpark,HIGH);         //подача сигнала на катушку
     delayMicroseconds(tz);              //длительность подачи сигнала на катушку
     digitalWrite(pinSpark,LOW);
     f=0;

/*  Serial.print(rpm);
    Serial.print("          ");
    Serial.print(t);
    Serial.print("               ");
    Serial.print(uozbyrpm[x]);
    Serial.print("            ");
    Serial.println(Tign);*/


   }
  }
  }

И да, получается на катушке сигнал будет 0,9 от времени 1 оборота. И пока я не понимаю будет это вообще работать или нет, ну в общем я уже переживаю за вашу катушку.
Вывод в ком порт нужен был чтобы понять какими числами оперирует ардуино, и понять что не правильно вычисляется, т.к. не работает должным образом. В готовой прошивке его не будет)
За код спасибо, испытаю как будет время!

можно, если и то и другое делать в прерывании по таймеру, а не так как у ТС - изменением каких-то флагов в Loop(), останавливаясь при этом "покурить" на время вывода в Сериал

Задачка в приниципе несложная, но исходный код ТС никуда не годится. Я сразу написал, что его нужно выкинуть... а вы что-то обсуждать бросились :)))

Любопытно, что ТС вбросил - и больше не показывается, а мы тут головы ломаем непонятно для кого:)
Небыло времени - студент)
 
Статус
В этой теме нельзя размещать новые ответы.