ФУОЗ по дешману
|
на сайте:
ноя-08
нахождение:
Москва, Ясенево
|
|
|
26-11-20 18:13
|
|
Ребята на базе ардуино запилили фуоз, дешево и просто |
|
|
на сайте:
ноя-08
нахождение:
Москва, Ясенево
|
|
|
26-11-20 18:13
|
|
Ребята на базе ардуино запилили фуоз, дешево и просто |
|
Уже заказал :) попробуем
Кто сделает отпишитесь , результат очень интересен.
Интересная тема, послежу. Давно подумывал про ФУОЗ на ардуинке, но пока знаний по написанию кода не хватает.
___________________________________________________________________
"Человека можно уничтожить, но его нельзя победить" (Э. Хемингуэй)
Интересная тема.
Удалось пока вроде разобраться с углами опережения, можно в самом скетче прописать более детальный диапазон, например с шагом в 500об/мин
Пока не понял, можно ли сделать второй сигнал через нужное время. Вроде выход Р4 можно использовать, но до конца не разобрался.
Что я для себя вывел, т.к. зажигание выставляется уже в момент зажигания, надо углы прописывать с этим вычетом.
Обороты считает по сигналам от датчика (холла или опто) один сигнал - один оборот кв
nextIgnition = (500000 * (360 - ignitionDegree)) / (3 * rpm) + VMTtime;
Ниже задаётся для каких оборотов как угол использовать (но через формулу, в экселе подобрать удалось обратным методом)
Второй сигнал в целом можно использовать и для двух свечей в один цилиндр, и для v-моторов, и пр.
Тогда другую платку надо брать с большим объёмом памяти , либо несколько плат ардуино иметь и переключать их.
занято меньше половины памяти платы , так что думаю можно и второй вывод сделать со сдвигом, но интереснее сделать обработку ДАД.
(Скетч использует 2970 байт (49%) памяти устройства. Всего доступно 6012 байт.
Глобальные переменные используют 41 байт динамической памяти.)
вот цикл подсчета угла от оборотов и у меня большой вопрос возникает , график то получается не линейный..... с разрывами п... и плоской полкой после 4000 рпм
( if (rpm >= 700 && rpm <= 1000) { // 0 - 2.2
ignitionDegree = 0.007 * rpm + (-5.133);
}
if (rpm >= 1000 && rpm <= 2000) { // 2.2 - 15
ignitionDegree = 0.013 * rpm + (-10.600);
}
if (rpm >= 2000 && rpm <= 3000) { // 15 - 25.1
ignitionDegree = 0.013 * rpm + (-11.000);
}
if (rpm >= 3000 && rpm <= 4000) {// 25.1 - 33.7
ignitionDegree = 0.007 * rpm + (7.000);
}
if (rpm > 4000) {
ignitionDegree = 33.7;
})
Именно эту часть поправил, чтобы без разрывлв было, пример куска ниже., с чем удалось поиграться. Как прописать сдвиг например в формате + n градусов или + %
И свыше каких-то оборотов можно вывести на нужную полку как сделано у ребят. У меня скетч получился с учетом детализации на 60%, и пришлось коэффициент до 4го знака прописывать
if (rpm > 600 && rpm <= 800) { // 0 - 10.8
ignitionDegree = 0.0540 * rpm + (-32.400);
}
if (rpm > 800 && rpm <= 1000) { // 10.8 - 20.8
ignitionDegree = 0.0500 * rpm + (-29.200);
}
if (rpm > 1000 && rpm <= 1500) { // 20.8 - 18.6
ignitionDegree = 0.0044 * rpm + (25.200);
}
if (rpm > 1500 && rpm <= 2000) { // 18.6 - 17
ignitionDegree = 0.0032 * rpm + (-23.400);
}
....
if (rpm > 5500 && rpm <= 6000) { // 23.9 - 25
ignitionDegree = 0.0022 * rpm + (11.800);
}
if (rpm > 6000 && rpm <= 6500) { // 25 - 26.7
ignitionDegree = 0.0034 * rpm + (4.600);
}
if (rpm > 6500) {
ignitionDegree = 26.7;
}
ignitionDegree = 0.0044 * rpm + (25.200);
Знак забыл. У тебя тут получается 69,2 градуса угол. В космос улетит.
Рекомендовал бы оптимизировать расчёты, т.к. у ATMega/ATTiny нет блока для расчётов с плавающей точкой, и операции с дробными числами стоят дорого.
Текущий угол поворота можно перевести в uint16 / unsigned int (0...65535), а угол опережения - в uint8 / byte (0...255) - это освободит память и ускорит вычисления. Можно будет ещё каких свистелок-перделок напихать. :-)
Опять же, куча последовательных IF, когда может быть истинно только одно условие в один момент времени, не имеет смысла и будет тратить ресурсы, т.к. проверка не закончится на первом попавшемся истинном условии, а каждое будет проверяться на истинность. Лучше использовать CASE (как альтернатива - ELSE IF, но CASE красивее выглядит).
Но больше всего меня, конечно, беспокоит помехозащищённость. Я пока не победил наводки на Мегу от системы зажигания.
Peacedeath подкрался незаметно, но слышен был издалека
За знак спасибо, упустил при копипасте.
Я с программированием не очень знаком, если по переводу uint16 и uint8, а также IF ELSE в SWITCH CASE подскажешь, это очень поможет :)
Если дальше отодвинуть от системы зажигания?
как то так
if (rpm >= 700 && rpm <= 1000) { // 0 - 2.2
ignitionDegree = 0.007 * rpm + (-5.133);
}
else if (rpm > 1000 && rpm <= 2000) { // 2.2 - 15
ignitionDegree = 0.013 * rpm + (-10.600);
}
else if (rpm > 2000 && rpm <= 3000) { // 15 - 25.1
ignitionDegree = 0.013 * rpm + (-11.000);
}
else if (rpm > 3000 && rpm <= 4000) {// 25.1 - 33.7
ignitionDegree = 0.007 * rpm + (7.000);
}
else if (rpm > 4000) {
ignitionDegree = 33.7;
}
Ага. Только комменты всё равно сильно отличаются от истинных расчётных значений. Да и расчёт совсем не гибкий...
Я бы переписал чуть иначе.
Сначала надо придумать график и объявить его в самом начале кода в виде констант:
#define RPM_700 700 // Первая точка у нас на 700 об/мин
#define ANGLE_700 0
#define RPM_1000 1000
#define ANGLE_1000 22 // богомерзкий float умножаем на 10, пусть пока побудет целочисленным, а так это 2,2 градуса
#define RPM_2000 2000
#define ANGLE_2000 150
#define RPM_3000 3000
#define ANGLE_3000 251
#define RPM_4000 4000
#define ANGLE_4000 337
Вот мы получили график из 5 точек, соответствующий комментариям (не фактическим формулам!)
Так как графики между точками линейные, то вместо диких вычислений с плавающей точкой, волшебными коэффициентами и магическими слагаемыми можно взять более красивую функцию экстраполяции (map), а для наглядности и простоты добавления точек использовать switch case:
// расчёт угла по таблице из 5 точек с возможностью расширения до 60
float get_angle (unsigned int rpms) { // функция расчёта угла. Возвращает число с плавающей точкой, в качестве входного параметра принимает целочисленное значение оборотов в минуту
byte tempRPM = rpms / 100; // Делим обороты на 100, чтобы не расписывать варианты для каждого значения
switch (tempRPM) {
int tempDEG; // временная переменная, чтобы не трогать float
CASE 1:
CASE 2:
CASE 3:
CASE 4:
CASE 5:
CASE 6:
tempDEG = ANGLE_700; // вот мы поставили для первых 699 об/мин нулевой угол
break; // Не забываем ставить break там, где нужно прекратить проверку, если найдено сов падение
CASE 7:
CASE 8:
CASE 9:
tempDEG = map(rpm, RPM_700, RPM_1000, ANGLE_700, ANGLE_1000); // Вот пример использования функции. Приводим положение текущего значения оборотов в минуту в заданном диапазоне 700...1000 к соответствующей точке графика в диапазоне 0...22
break; // Не забываем ставить break!
CASE 10:
<...> // мне лень выписывать все строки от 10 до 19, да и пост слишком уж длинным получается, но они должны быть!
CASE 19:
tempDEG = map(rpm, RPM_1000, RPM_2000, ANGLE_1000, ANGLE_2000);
break;
CASE 20:
<...>
CASE 29:
tempDEG = map(rpm, RPM_2000, RPM_3000, ANGLE_2000, ANGLE_3000);
break;
CASE 30:
<...>
CASE 39:
tempDEG = map(rpm, RPM_3000, RPM_4000, ANGLE_3000, ANGLE_4000);
break;
CASE 40:
<...>
CASE 59:
tempDEG = ANGLE_4000; // А тут усреднение уже не нужно, тут полка угла.
break;
default:
tempDEG = 0; // По дефолту угол 0, если будет какое-то непредвиденное значение (например, 7000 RPM, или кто-то тоже поленится и не напишет CASE 25:, то при 2500 RPM тоже :-))
break;
}
return float(tempDEG) / 10; // и только сейчас переводим результат в число с плавающей точкой, делим на 10 и возвращаем в место вызова функции, хотя подозреваю, что оно нафиг не надо, т.к. в других местах тоже можно переписать под целые числа.
}
Этот кусок можешь вставить в конец кода.
А всю магию можно заменить на одну строчку:
ignitionDegree = get_angle(rpm);
Это можно написать ещё красивее, но мне лень. :-)
Peacedeath подкрался незаметно, но слышен был издалека
Подправил шрифт в комментариях к коду, для лучшей читабельности. BuffoG
вариант с map куда симпатичнее, но тут сразу возникает вопрос а не проще обойтись теми же 5 if else if меж кнтрольных точек, чем городить 40 кейсов ?
#define RPM_700 700 // Первая точка у нас на 700 об/мин
#define ANGLE_700 0
#define RPM_1000 1000
#define ANGLE_1000 22 // богомерзкий float умножаем на 10, пусть пока побудет целочисленным, а так это 2,2 градуса
#define RPM_2000 2000
#define ANGLE_2000 150
#define RPM_3000 3000
#define ANGLE_3000 251
#define RPM_4000 4000
#define ANGLE_4000 337
// это из тела программы в замен кривых расчетов .
if (rpm >= 0 && rpm <= 700) {
oldtime = micros();
ignitionFlag = true;
return;
}
else if (rpm >= 700 && rpm <= 1000) { // 0 - 2.2
ignitionDegree = (map(rpm, RPM_700, RPM_1000, ANGLE_700, ANGLE_1000))/10;
}
else if (rpm > 1000 && rpm <= 2000) { // 2.2 - 15
ignitionDegree = (map(rpm, RPM_1000, RPM_2000, ANGLE_1000, ANGLE_2000))/10;
}
else if (rpm > 2000 && rpm <= 3000) { // 15 - 25.1
ignitionDegree = (map(rpm, RPM_2000, RPM_3000, ANGLE_2000, ANGLE_3000))/10;
}
else if (rpm > 3000 && rpm <= 4000) {// 25.1 - 33.7
ignitionDegree = (map(rpm, RPM_3000, RPM_4000, ANGLE_3000, ANGLE_4000))/10;
}
else if (rpm > 4000) {
ignitionDegree = 33.7;
}
или в замен этого всего просто сделать такой
тут уж не надо констант и выборов .. а обработку ДАД сделать тем же мап .. допустим зашить ему изменение от 0 до -20 при изменении показания с датчика от 0 до 450мВ
if (rpm >= 0 && rpm <= 700) {
oldtime = micros();
ignitionFlag = true;
return;
}
else if (rpm >= 700 && rpm <= 5000) { // 0 - 2.2
rpm = rpm/100;
ignitionDegree = ((-0.02)*rpm*rpm)+1.96*rpm + (-15);
}
else if (rpm > 5000) {
ignitionDegree = 33.7;
Без полного кода править его куски - такое себе удовольствие. Ещё и в древовидной структуре, где ни фига не понятно.
В общем, если ты решил всерьёз заняться этим вопросом, то:
- Создай отдельную тему.
- Вынеси в её шапку ВЕСЬ код, а не отдельные куски.
- Обсудим в комментах к теме реализацию с участием других коллег-оппозитчиков
- Потом поправишь код в шапке исходя из того, что у нас наваяется.
Но вообще для совместной разработки принято использовать gitHub :-)
Peacedeath подкрался незаметно, но слышен был издалека
Скинь текст шестерёнкой, погляжу и покажу.
Увы, у меня несколько иная цель. Мне нужен в электронный тахометр сигнал системы зажигания, но гальванически развязанный от катушки, чтобы не спалить МК, и имеющий чёткие фронты без дребезга. Пока экспериментирую с оптопарой.
Peacedeath подкрался незаметно, но слышен был издалека
а если брать сигнал с датчика хола или оптики , который идет на комутатор ?
Отправил шестерней, потом можно сюда запостить итоговый вариант, чтобы при необходимости каждый сам мог просто углы свои проставить
метал корпус с заземлением?
а как график выглядет то есть формула в визуале его представить?
Дык Excel - наше всё.

Держи график по твоим формулам.
ignitionDegree = 0.007 * rpm + (-5.133);
}
if (rpm >= 1000 && rpm <= 2000) { // 2.2 - 15
ignitionDegree = 0.013 * rpm + (-10.600);
}
Истинны оба условия. На 1000 об/мин угол резко скакнёт с 1,87 до 2,4
И комменты неправильные.
1000 * 0,007 - 5,133 = 7 - 5,133 = 1,867, а никак не 2,2.
1000 * 0,013 - 10,6 = 13 - 10,6 = 2,4, а не 2,2
Peacedeath подкрался незаметно, но слышен был издалека
ох с таким графиком все должно вообще в одной строчке считаться линейно .. он почти прямой..
формулы не мои , это оригинал от разработчиков ))
почитал немного даташит на платку.. можно к ней прикрутить аналоговый датчик ДАД , например такой https://aliexpress.ru/item/4000274503044.html?spm=a2g0o.productlist.0.0....
и запилить в расчет от его показаний уменьшение оперяжения например от 0 до 25 градусов , по идеи это делается парой строчек
Ну не парой, не парой...
Тут уже получается трёхмерная карта, т.к. УОЗ начинает зависеть не только от оборотов, но и от разрежения.
Но если усреднить карту (сделать 5 точек по оборотам, 5 - по давлению, итого 25), то реально и в Tiny впихнуть.
Однако ДАД на нашем оппозите имеет смысл при монокарбе. Иначе нужно вешать либо два ДАД, либо смириться с некоторым несоответствием карты из-за разницы разрежения во впускных трактах разных цилиндров.
Peacedeath подкрался незаметно, но слышен был издалека
еще вопрос почему не считать все в милисекундах а не в микросекондах.. сорян за тупость с ардуино знаком я только вот пару часов сегодня.. или процессорное время единица в микроСек ?
Потому что при 6000 об/мин один оборот происходит за 1/6000 минуты, или 1/100 секунды, или 10 мс.
Таким образом, на одну миллисекунду приходится 36 градусов вращения.
Получается очень дискретное опережение - либо 0 градусов, либо 36...
Peacedeath подкрался незаметно, но слышен был издалека
Можно, конечно. Но чем больше точек, тем больше вес программы.
Чтобы составить таблицу, нужно определённое количество точек.
При самом оптимизированном размещении (3 байта на точку - 2 на об/мин, 1 - на угол опережения) на таблицу в 100 точек понадобится отвести 300 байт.
В 1000 точек - 3000 байт.
А то, что между точками, всё равно придётся высчитывать по формулам, ибо в диапазоне от 600 до 6000 об/мин - 5400 точек, и это уже почти 16 КБайт, что явно не влезет в Тини, это объём уже для Меги 32 и дальше.
Или я неправильно понял твою идею?
Если идея в том, чтобы задать пары "Обороты - угол" и ввести единую функцию для их расчёта, то тут примерно это и сделано. Только сделано коряво - объявлено "магическими числами" прямо в коде, а не вынесено в понятные человекочитаемые константы или переменные.
Peacedeath подкрался незаметно, но слышен был издалека
Чуть выше написал пример, как это можно написать более наглядно, масштабируемо и менее ресурсоёмко.
Peacedeath подкрался незаметно, но слышен был издалека
вывел формулу по которой изменяется график от 1000 до 4000 y=-0,0000011*x2+0,0162*x-12,8
если перед началом действий делить обороты на 100 по формула будет y=−0.02x2+1.96x−15 по трудозатратам хз как проще считать... но в это варианте получается перевернутая парабола с пиком на 4900 оборотов график начинается примерно с 850 оторотов
X = обороты, Y = угол, правильно понимаю?
Странные какие-то формулы, если честно. Но проверю. Откуда эти графики взял, если не секрет?
Вечером буду в жилище, нарисую график в Excel. Или сам скинь, полюбуюсь.
Оптимизировать будем потом, сначала надо добиться корректных расчётов. :-)
Peacedeath подкрался незаметно, но слышен был издалека
все верно, y- угол x - обороты.
сам график тупо брал по контрольным точкам от этого зажигания
вот на этом сайте подобрал формулу в онлайн расчетах
http://mathhelpplanet.com/static.php?p=onlayn-mnk-i-regressionniy-analiz
потом в экселе правил коэфиценты до приемлемых графиков.
файлик тут https://drive.google.com/file/d/1HN_ZuDV-9Lb1h3CIE2CUgWiGXlH9z2FA/view?u...
можно пользовать по ссылке его
Не лучше ли взять график типа Сарумана или Вуфера, и от них уже плясать? По крайней мере, под ними есть хоть какая-то исследовательская база и опыт использования.
Peacedeath подкрался незаметно, но слышен был издалека
докинул обработку MAP по спеки МАР выходное напряжение от 0 до 4.5В
но это если ардуино понимает сигнал с миливольтах
по спеки платы назначил аналоговый вход
pinMode(2, INPUT);
void countIgnitionTime() {
ignitionDegree =ignitionDegree + (map(analogRead(1), 450, 0, 0, -200) / 10);
nextIgnition = (500000 * (360 - ignitionDegree)) / (3 * rpm) + VMTtime;
ignitionFlagWithOffset = true;
}
Вот тут совсем не понял...
Сколько бит АЦП у платы? 10?
Диапазон АЦП - 0...5 вольт?
Тогда у тебя при 0 вольтах на аналоговом входе функция analogRead вернёт значение 0, а при 5 - 1023.
Какое напряжение на выходе датчика при атмосферном давлении и какое - при максимальном разрежении?
На какое значение ты хочешь корректировать УОЗ при атмосферном давлении, и на какое - при максимальном разрежении?
p.s. менять глобальные переменные внутри вызываемой функции (не loop) - моветон. При отладке придётся бегать по всем функциям и вспоминать, где какая переменная меняется. Глобальные переменные лучше менять только внутри основной (void loop() в случае Ардуино)
Peacedeath подкрался незаметно, но слышен был издалека
плата https://www.sgbotic.com/products/datasheets/sensors/02976-datasheet.pdf
предположительно надо брать от -100 до 0 модификация XGZP6847100KPGN
исходя из получается что берем значение от 0.5 до 4.5В изменяется при -100 до 0 кРа в реале подозреваю что значение будет меняться меньше .. это придется проверять опытным путем на моторе.
возможно надо просто записывать максимальное и минимальное значение в переменные во всем рабочем цикле и использовать уже их , тогда нам не важно какие именно будут значения на самом МАР
пришло вот такое в голову
long int LoadMax = 10;
long int LoadMin =9;
long int LoadD;
loop
if (ignitionDegree > 0) {
LoadD = analogRead(1);
if (LoadD > LoadMax) {
LoadMax = LoadD;
}
else if (LoadD < LoadMin) {
LoadMin = LoadD;
}
ignitionDegree =ignitionDegree + (map(LoadD, LoadMin, LoadMax, 0, -200) / 10);
countIgnitionTime();
}
П.С.
Понятно что придется делать ресивер и поместить под переднюю крышку , чтоб сгладить колебания , а подключать на место настроечных маномертор (которые подключаются меж головой и карбом) .
А почему ты берешь именно такой МАР? Этот МАР надо еще как-то вкорячить во впуск, почему не взять готовый МАР от авто?
Как вариант

https://www.avtoall.ru/datchik_absolyutnogo_davleniya_vozduha_gaz_uaz_um...
http://clubturbo.ru/market/elektronika/datchik_absolyutnogo_davleniya/da...
У последнего даже график работы есть
___________________________________________________________________
"Человека можно уничтожить, но его нельзя победить" (Э. Хемингуэй)
Что-то мы с тобой тему-то подзасрали, и начинаем лезть не в те дебри.
Давай сначала человечьим языком опишем, что мы хотим получить, прежде чем код трогать. Потому что сейчас я вижу:
// Объявили 3 глобальных переменных, зачем-то long, занимающий 4 байта, хотя двухбайтового беззнакового uint16 вполне хватило бы
long int LoadMax = 10;
long int LoadMin =9;
long int LoadD;
// циклически выполняющаяся функция
loop
if (ignitionDegree > 0) { // если УОЗ больше нуля, то
LoadD = analogRead(1); // читаем аналоговый вход (MAP)
if (LoadD > LoadMax) { // Если прочитанное значение больше текущего значения в переменной LoadMax
LoadMax = LoadD; // Устанавливаем значение LoadMax равное считанному значению.
}
else if (LoadD < LoadMin) { // иначе если прочитанное значение меньше LoadMin
LoadMin = LoadD; // Устанавливаем значение LoadMin равное считанному значению.
}
ignitionDegree =ignitionDegree + (map(LoadD, LoadMin, LoadMax, 0, -200) / 10); // прибавляем к переменной ignitionDegree значение (экстраполяция переменной LoadD в диапазоне от LoadMin до LoadMax на диапазон от 0 до -200 делённое на 10)
countIgnitionTime(); / вызываем некую функцию расчёта времени
}
В этом случае у тебя получится следующее:
При включении зажигания:
- На датчике атмосферное давление, на аналоговом входе 4,5V (analogRead(1) вернёт 920)
- ignitionDegree = 0
- Переменные LoadMin и LoadMax имеют значение 9 и 10 соответственно, т.к. ignitionDegree = 0
При запуске двигателя и ХХ 600 об/мин:
- На датчике появилось некое разрежение, условно -50 КПа, на аналоговом входе 2,5V (analogRead(1) вернёт 511)
- ignitionDegree = 0
- Переменные LoadMin и LoadMax имеют значение 9 и 10 соответственно, т.к. ignitionDegree = 0
При подъёме до 1000 об/мин:
- На датчике уменьшилось разрежение до -45 КПа, на аналоговом входе 2,7V
- Функция analogRead возвращает значение 552
- LoadD = 552
- ignitionDegree = 2,2
- Переменная LoadMin имеет значение 9, LoadMax принимает значение 552
- функция экстраполяции map(LoadD, LoadMin, LoadMax, 0, -200) возвращает значение -200, т.к. LoadD = LoadMax
- Переменная ignitionDegree принимает значение 2,2 + (- 200 / 10) = -17,8
- Мотор глохнет
Тебе точно нужен такой угол опережения?
Распиши сначала, какой именно алгоритм обработки тебе нужен. "Если - то - иначе". Сразу писать код тоже можно, конечно, но тогда хотя бы комментируй его, иначе через пару недель сам не вспомнишь, что и для чего.
Peacedeath подкрался незаметно, но слышен был издалека
первоначальные значения при назначении переменных пока стоят от балды , чисто для понимания,
по идеи можно прям со старта в setup присваивать loadMax значение AnalogRead(1) а LoadMin = LoadMax-10
тогда у нас максимальное разряжение будет писаться по мере работы двигателя, единственный момент что такая само установка покажет при сбросе газа например с 5000 оборотом и , не уплывет ли весь график после этого..
п.с.
и цифры например в -200 и 0 , их спокойно можно подвинуть например (100 , -200 )взята то же чисто для понимания алгоритма ,
возможно нам и не надо такой сложной системы и мы берем один раз в сетап присваиваем
loadMax = AnalogRead(1);
LoadMin = 0;
и может этого достаточно , так как атмосферного давления во впуске мы не получим, только приближение к нему при резко открытом газе.
почему беру именно этот дадчик , он дешев на али что то по 300р .. . можно и дадчик от газели прилепить... в этом то вроде проблем нет.
ссылка на скетч
как раз с этими правками
https://drive.google.com/file/d/1rRTP74p7kGGNRCdIZ0IxXFWP1vKnh5OF/view?u...
Выскажусь немного за МАР - при проектировании ФУОЗ надо отталкиваться от максимальной надежности, унифицированности и
доступности запасных частей в случае поломки (т.е. можно купить в любом автомаге). Датчик с Али хоть и дешев, но заказа с али еще надо дождаться, датчик выполнен в "бескорпусном" исполнении, нужно придумывать переходную плату под типовой водонепроницаемый разъем, корпус. Готовые автошные хоть и дороже, но уже лишены всех перечисленных недостатков.
___________________________________________________________________
"Человека можно уничтожить, но его нельзя победить" (Э. Хемингуэй)
Добавил скетч без обработки мар, с таблицей констант в которой можно выставлять точки графика в самом скече
https://drive.google.com/file/d/1-ncc-kpv4j7sMXa-U6K8jeBgD-EYJGkQ/view?u...
Вот этот же код, только разобранный по косточкам и с моими комментариями.
Работать будет даже с "магическими числами".
Но если ты собираешься его дорабатывать дальше - запихивать работу с MAP и вторым выходом, то его стоит рефакторить, ибо есть неиспользуемые переменные, переменные с непонятным принципом выбора типа данных и куски, где нет приведения данных к нужному типу, из-за чего, например, на 1200 RPM возникает погрешность - вместо расчётного угла 4,76 фактическое опережение будет от 4 до 5,1 градуса. Всё это затрудняет редактирование и увеличивает затраты времени и алкоголя на доработку, снижая прогнозируемость поведения.
Плюс если выключить зажигание на рабочем двигателе (щёлкнуть ДвигСтоп), а потом включить обратно, есть неиллюзорная вероятность пука в карбюраторы (см.строку 174). Но в общем и целом код работоспособен.
Peacedeath подкрался незаметно, но слышен был издалека
else if имеет смысл менять на case ? С твоими комментами перенес с использованием case (обновил файл по ссылке, первый был косячный)
https://drive.google.com/file/d/1pNQQIaCo9AkcKGHd8h8d4N3VcSqo8uAc/view?u...
Если правильно понял. По идее в самом начале достаточно только углы свои подставлять, больше ничего менять не надо.
Вот картинка, кстати, с таблицей про нагрузку, обороты и опережение:
https://drive.google.com/file/d/1huxCUYirpKRp0JddKR4rjXoWQLrNVisB/view?u...
Имеет смысл оценить размер скомпилированного кода в одном и в другом варианте,
Плюс в обоих вариантах стоит использовать временные переменные и однократный вызов функции map с передачей в неё этих переменных, чтобы не раздувать код - там вроде всего 8 кБайт в контроллере?
Плюс там есть зайчатки дебаггинга в виде вывода в serial значения оборотов в минуту.
Это дело хорошее, и его стоит продолжить, потому что можно, конечно, и мысленный эксперимент провести, как я сделал для трёх оборотов коленвала сферического двигателя в вакууме, но если прикручивать свистелки типа MAP-сенсора, то намного логичнее на стенде читать логи работы ФУОЗ в консоли, чем в сотый раз перечитывать в гараже прошивку и думать, почему мотоцикл начинает детонировать, стоит дать чуть нагрузки.
А вот таблица крайне спорная.
Не уверен, что её можно применить к нашим оппозитам. 20,7 градусов опережения на 600 об/мин???
Peacedeath подкрался незаметно, но слышен был издалека
Вот это Элиас огромное спасибо, да надо брать пиво и разбираться в коде и алгоритме по твоим комментариям .
В свое оправдание могу сказать что программировать для ардуино я не умею, ни синтаксиса, ни правил не знаю, правил скеч от блогеров опираясь на свои скудные знания паскаля и т.д. потому прошу не пинать. )) Пока время нет почитать методички по программированию на ардуино.... а мысли что возникают вечером , если не записать то забуду, и потом вспоминай какое изящное пришло решение с обработкой МАР сенсора например ))
П.С
почему ты пишешь : (map(rpm, RPM_700, RPM_1000, ANGLE_700, ANGLE_1000)) / 10; // устанавливаем float угол в пределах от 0 до 2 с шагом 1 градус (потому что оперируем не плавающей точкой, а целочисленными константами)
получается , что функция мар возвращает целочисленное число и её нельзя поделить с остатком?
тогда получается строка должна быть такой:
ignitionDegree = map(rpm, RPM_1000, RPM_2000, ANGLE_1000, ANGLE_2000);
ignitionDegree = ignitionDegree / 10;
?
так же в принципе все расчеты можно построить на значениях углов * 10 "как в объявлении констант" чтоб не пользовать запятых.
Да пожалуйста. Всё равно не спалось. :-)
Оправдания не нужно - я ж понимаю, что это левый код, написанный кем-то в Интернетах, и он почему-то работает, но хочется лучше.
Начало положено. Теперь он хотя бы понятный.
Да, функция map принимает на вход целые числа и возвращает целочисленное значение. Если она вернёт 47, то поделив это число на целочисленное 10, мы получим... Нет, не 4,7 и даже не 5. Мы получим 4. Дробная часть будет отброшена.
Поэтому целочисленное 47 сначала надо привести к типу с плавающей точкой, чтобы поделить его на 10 и получить дробное значение.
ignitionDegree = map(rpm, RPM_1000, RPM_2000, ANGLE_1000, ANGLE_2000);
ignitionDegree = ignitionDegree / 10;
?
Можно и так. Но здесь у тебя получается целых две операции, в которых участвует переменная с плавающей точкой.
Правильный путь - это
Запятые в этом коде не нужны, и если ты перепишешь код так, чтобы в нём остались только целые числа, ты ускоришь его выполнение и уменьшишь его размер.
Peacedeath подкрался незаметно, но слышен был издалека
Если мы считает угол так , угол*10
ignitionDegree = map(rpm, RPM_1000, RPM_2000, ANGLE_1000, ANGLE_2000);
то функция расчета времени искры будет такой:
void countIgnitionTime() { // Функция расчёта времени для следующего момента зажигания
nextIgnition = VMTtime + diffTime - (diffTime/360*ignitionDegree/10); // формула времявмт +время оборота- время оборота деленое на 360 градусов(нашли время одного градуса) умножили на необходимое количество градусов и разделили на 10 (так как градусы получаем в *10 состоянии)
ignitionFlagWithOffset = true;
}
немного причесал скеч с учетом правок и замечаний Элиоса , так же добавил условие первого запуска программы с бездействием до первого полного оборота по втм , так же в новый код сделал обработку МАР, расчеты угла сделал до 5000 оборотов , графики зашил пока для м72 лежит тут версия с МАР и без
Скачал файл без MAP.
Вот теперь появился смысл в глобальности переменной diffTime, потому что мы начали её использовать в двух разных функциях (язвительную часть из коммента к данной переменной можно смело убирать, что ты и сделал в строке 17 :-))
... и финальный шаг оптимизации в этой функции - два машинных и один человечий:
0. Повышаем точность вычисления.
1. Избавляемся от одной лишней операции.
2. Убираем из кода непонятные магические числа.
Возьмём для примера состояние, обозначенное в строке 196:
058340 мкс - ВМТ - oldModulator = 1, modulator = 0, ignitionFlag = 0, nextIgnition = 15899, ignitionFlagWithOffset = 0, oldtime = 8340
VMTtime = 58340
diffTime = 50000
rpm = 1200
ignitionDegree = map(1200, 1000, 2000, 22, 150) = 47
nextIgnition = 58340 + 50000 - (50000 / 360 * 47 / 10)
nextIgnition = 58340 + 50000 - (138 * 47 / 10)
nextIgnition = 58340 + 50000 - (6486 / 10)
nextIgnition = 58340 + 50000 - 648
nextIgnition = 108340 - 648 = 107692
Итого 5 операций (на самом деле, больше, потому что ATMega имеет восьмибитную архитектуру, и не умеет без костылей оперировать 4-байтовыми данными, но это уже ограничения архитектуры и от них особо никуда не деться, по крайней мере, с моими знаниями)
107692 мкс - это 4,66 градуса до ВМТ. Отлично, практически попали в цель. Формулу можно считать верной, но в ней есть два "магических числа" - 360 и 10, и одна лишняя операция деления, тратящая машинное время и вносящая путаницу в мозг того, кто пытается разобраться в коде.
Избавиться от них очень просто.
Для начала сократим выражение - объединим две последовательные операции деления и запишем формулу как
nextIgnition = VMTtime + diffTime - (diffTime/3600*ignitionDegree);
Оп! Сразу стало меньше на одно магическое число и на одну математическую операцию!
Для проверки пересчитаем:
nextIgnition = 58340 + 50000 - (50000/3600*47)
nextIgnition = 58340 + 50000 - (13*47)
nextIgnition = 58340 + 50000 - 611
nextIgnition = 108340 - 611 = 107729
Итого 4 операции.
Но 107729 мкс - это 4,4 градуса до ВМТ. Промахнулись на 3 десятки против 4 соток в предыдущем варианте. Почему же мы так сильно потеряли в точности?
Потому что, как объяснял ранее, при целочисленных операциях деления дробная часть отбрасывается.
Поэтому стоит сначала производить все операции умножения с оглядкой на тип данных, и только по завершении операций умножения производить деление, чтобы отбрасываемый остаток был минимальным.
Поэтому запишем формулу как
nextIgnition = VMTtime + diffTime - (diffTime*ignitionDegree/3600);
И произведём контрольный расчёт.
nextIgnition = 58340UL + 50000UL - (50000UL*47U/3600)
nextIgnition = 58340UL + 50000UL - (2350000UL/3600)
nextIgnition = 58340UL + 50000UL - 652UL
nextIgnition = 108340UL - 652UL = 107688UL
Итого 4 операции, как и было на предыдущей итерации, а 107688 мкс - это 4,69 градуса до ВМТ. Практически в яблочко!
Как можно заметить, у некоторых чисел появились суффиксы. Они нужны, чтобы наглядно оценить тип данных и не встретиться с переполнением.
Тип данных задаётся при объявлении константы или переменной. В нашем случае объявлены 3 глобальные переменные и 1 локальная константа:
unsigned long VMTtime (4 байта, беззнаковая, диапазон 0...4294967295, суффикс UL от Unsigned Long)
unsigned long diffTime (аналогично)
word ignitionDegree (2 байта, беззнаковая, диапазон 0...65535, суффикс U от Unsigned int)
3600 (по умолчанию тип int16 - 2 байта, знаковая, диапазон -32768...32767, суффикс отсутствует)
При выполнении математических операций с двумя переменными разных типов компилятор автоматически приводит в соответствие размеры переменных, но за типами данных лучше следить самостоятельно, потому что 5000 * 20 = -31072, а никак не ожидаемые 100000 - возникло переполнение типа int16, т.к. данные типа int16 не приведены явно к int32.
Правильно и предсказуемо будет вычисление 5000UL * 20 = 100000UL, или 5000L * 20 = 100000L.
Но в нашем случае всё в порядке, переменные изначально имеют нужные типы.
Теперь осталось дело за малым - избавиться от последнего магического числа в коде
В самом начале кода объявим константу:
#define DEC_DEGREES 3600 // Количество десятичных долей градуса в полном обороте, потому что все расчёты углов мы ведём в десятичных долях
И теперь мы можем записать формулу как
nextIgnition = VMTtime + diffTime - (diffTime*ignitionDegree/DEC_DEGREES);
Всё, функция оптимизирована.
Но поле для дальнейшей оптимизации и причёсывания кода ещё непаханое.
А это зачем? Чтобы точно не завести с первого кика? :-)
Если ты таким образом пытался избежать хлопка в карбюраторы при включении зажигания на уже крутящемся моторе, то, ИМХО, логичнее было бы чуть изменить алгоритм старта - первые две искры подавать точно в ВМТ, а расчёт скорости вращения и угла опережения начинать только со второй ВМТ. Основываясь на разнице времени между двумя ВМТ, уже можно считать обороты и входить в основной алгоритм.
Peacedeath подкрался незаметно, но слышен был издалека
после причесывания кода , прошивка стала меньше изначальной , теперь она 39 процента занимает больше нет плавающих точек.
так же можно ввести обработку кнопок если надо , например при нажатие кнопки понижать или повышать график на определенный угол , например одни раз нажал , график повысился , второй раз нажал , понизился , третий раз нажал вернулся в нормальное состояние , четвертый раз нажал вырубилось все нахрен , или двигстоп так реализовать. один раз нажал перестала искра идти, второй раз пошла.
Может реализовать как у Сарумана - смену графиков в зависимости от типов ДВС и нагрузки?
___________________________________________________________________
"Человека можно уничтожить, но его нельзя победить" (Э. Хемингуэй)
У сарумана 3 графика и пара функций на выбор , тут можно записать и 3 графика разных , но проще просто делать корректировку текущего графика на определённый угол , программно это менее затратно чем писать другие графики, хотя не факт что правильно для мотора .
По переключению графиков вообще не вижу смысла так как обработка МАР как раз и избавит от необходимости переключения графиков на тяговые, верховые и т.д. .
А вот допилить функции прогрева свечей может , хотя ни разу таким не пользовался.
или функцию откатки или охраны. и отсечку наверно надо добавить.
П.С.
хотя режим переключения возможно хорошо подойдет для отладки графиков под конкретный мотоцикл.
ты напиши сколько графиков хочешь видеть разных попробую продумать как это сделать
П.П.С.
к реализации обработки МАР придумал читать показания в середине момента впуска, это избавит от необходимости городить ресиверы и сглаживать пульсации , но необходимо брать показания сразу с двух цилиндров , например через тройник на шлангах. в код по ссылке это добавил.
Ну так Сарумановская схема была отлажена для работы без МАР от того то и сделали там 3 графика.
Предлагаю такую концепцию:
1. Схема без МАР - реализуем те же 3 графика как у Сарумана - колясыч, одичка и условно "спорт". Хотя городить третий график не вижу большого смысла
2. Схема МАР - прошивка подгоняется под конкретный датчик из недорогих и доступных. Вакуум берется через пресловутую переплюйку.
Режим прогрева свечей считаю функцией бесполезной, так как при нормально работающем ДВС это не нужно.
Сдвигать график туда сюда наверное не логично, а вот сдвиг участков графика в диапазонах определенных оборотов логичен.
___________________________________________________________________
"Человека можно уничтожить, но его нельзя победить" (Э. Хемингуэй)