Pull to refresh

Практическое программированние PIC16F***

Предыстория и анализ.


В общем как то раз столкнулся с новой не типичной для меня задачей, один мой друг поставил себе в волгу двигатель от газона-хлебовозки (змз511 v8 4,6литра обьемом). У того двигателя две проблемы, вечно глючное зажигание и карбюратор больше похожий на унитаз (качество смеси соответсвующще), сильно хотелось поставить современный инжектор но где его взять на 8 котлов? Друг просил придумать чтонибудь умеренное по деньгам, наши январи и микасы работают с 4ц моторами, в принципе при очень большом желании пожервовав адсорбером и регулятором хх можно запустить на 6ц моторе (соответсвенно переписав прошивку), поэтому эти контроллеры нам не подходят. Haltech E6X посчитали слижком дорогим по деньгам (волга вместе с мотором обошлась предположительно в аналогичную сумму), более дешевых контроллеров на v8 я так и не нашел (кроме 8цилиндров контроллер еще должен позволять себя переалибровать под конкретный движоки желательно в онлайне).
И тут меня осенило…

Задача


Сделать на дешевых компонентах повторитель сигналов на зажигание и форсунки со смещщением на 90градусов по коленвалу. Надо из 4 последовательных сигналов сделать 8 с запаздываннием на половину времени между сигналами, время каждого сигнала может быть в диапазоне от 1мс до времени полного цикла в два оборота коленвала (постоянный сигнал, на больших нагрузках когда форсунки полностью открыты). На первый взгляд все просто, но когда начинаем разбиратся более детально вылазит огромная куча подводных камней, например обычно подобные сигналы формируют при помощщи аппаратного ШИМ, считывают через CCMP, а на всех дешевых пиках максимум два канала для этих целей… Городить огород из кучи микросхем да еще и синхронизировать их между собой? Невариант. Поэтому включил мозги и начал эксперементировать.
Начал с того что скачал даташиты на микросхемы, среду разрабтки на сях, и симулятор-отладчик для проверки работоспособности и верности алгоритма, нашел все в сети, чтото бесплатное чтото с «лекарством», по понятным причинам ссылок приводить не буду? но могу поделится ссылками с ищщущими в «личном» порядке.

Пробы, поиск решения.


В общем читал до позеленения мозга даташиты, разбирал примеры кода, изучал особенности, и через две недели смог родить рабочий код. Суть алгоритма в следующщем:
Запускаем таймер который регулярно срабатывает максимально быстро (сосчитал количество тактов процессора на выполнение максимального обьема кода прибавил пару про запас и вычислил необходимое время по таймеру в 96). Вот теперь у нас есть статическая точка для отчета временных промежутков.
Каждый раз когда срабатывает таймер программа уходит в прерывание в котором сидит наш код.
Сначала мы изучаем изменения в состоянии каналов форсунок, вычисляем включившия и отключившиеся на этом цикле, дальше идем по условиям, если есть включившиеся то сохраняем время между сигналами и обнуляем счетчик, иначе плюсуем счетчик.
Проверяем сохраненный в переменную счетчик, если он больше нуля то то минусуем его с удвоенной скоростью (чтобы получить задержку в половину времени) иначе включаем на выходе дублеры (которые запаздывают на 90гр) последних включившихся форсунок.
Аналогично работает механизм отключения форсунок (считаем время между отключениями, запоминаем последние отключенные, когда время истекает выключаем).
Для зажигания алгоритм тот же с той лиш разницей что время сигнала по зажиганию всегда фиксированное.

Ну и собстна сам код:
// автор: nikll@rambler.ru
// программа написанна на microC (http://www.mikroe.com/), тестировал на симуляторе PIC Simulator IDE (www.oshonsoft.com)
//
// на PORTA целпяем форсунки (с 1 по 4 контакты) и катушки (с 5 по 8 либо 5 и 6 в зависимости от выводов контроллера)
// с PORTB (1-4) идут сигналы на дублирующщие через 90градусов форсунки, и (5-8 или 4-6) сигналы на дублирующщие через 90градусов катушки
//
// необходимо сделать обвязку на микросхему и чертеж печатной платы для изготовления опытного образца.

unsigned long
OnTimeFors = 0, // время между открытиями форсунок
LastOnTimeFors = 0, // время до открытия дублирующщей форсунки
OffTimeFors = 0, // время между закрытиями форсунок
LastOffTimeFors = 0, // время до закрытия дублирующщей форсунки
OnTimeIng = 0, // время между открытиями форсунок
LastOnTimeIng = 0, // время до открытия дублирующщей форсунки
OffTimeIng = 0, // время между закрытиями форсунок
LastOffTimeIng = 0; // время до закрытия дублирующщей форсунки

char
lastfors=0, // предыдущщее состояние форсунок
diff, // изменение в состоянии форсунок
onfors, // включившиися форсунки
lastonfors=0, // предыдущщие включившиися форсунки
offors, // отключившиися форсунки
lastoffors=0, // предыдущщие отключившиися форсунки
outfors=0, // состояние дублирующщих форсунок
oning, // включившиися катушки
lastoning=0, // предыдущщие включившиися катушки
offing, // отключившиися катушки
lastoffing=0; // предыдущщие отключившиися катушки

void interrupt() {
if (INTCON.T0IF) {
TMR0 = 160;

diff = PORTA ^ lastfors; // получаем изменение в состоянии форсунок и катушек

onfors = diff & PORTA & 0x0F; // получаем список включившихся форсунок
offors = diff & lastfors & 0x0F; // получаем список отключившихся форсунок

oning = diff & PORTA & 0xF0; // получаем список включившихся катушек
offing = diff & lastfors & 0xF0; // получаем список отключившихся катушек

if (onfors>0) { // если есть открывшиеся форсунки то
lastonfors = onfors;
LastOnTimeFors = OnTimeFors; // сохраняем время между открытиями
OnTimeFors = 0; // и обнуляем счетчик.
} else OnTimeFors++; // иначе увелчиваем счетчик

if (oning>0) { // если есть сигнал на катушки то
lastoning = oning;
LastOnTimeIng = OnTimeIng; // сохраняем время между сигналами
OnTimeIng = 0; // и обнуляем счетчик.
} else OnTimeIng++; // иначе увелчиваем счетчик

if (LastOnTimeFors>1) { // если время еще не вышло то
LastOnTimeFors--; // отматываем таймер с двойной скоростью чтобы получить половиную задержку
LastOnTimeFors--;
} else outfors = outfors | lastonfors;

if (LastOnTimeIng>1) { // если время еще не вышло то
LastOnTimeIng--; // отматываем таймер с двойной скоростью чтобы получить половиную задержку
LastOnTimeIng--;
} else outfors = outfors | lastoning;

if (offors>0) { // если есть закрывшиеся форсунки то
lastoffors = offors;
LastOffTimeFors = OffTimeFors; // сохраняем время между закрытиями
OffTimeFors = 0; // и обнуляем счетчик.
} else OffTimeFors++; // иначе увелчиваем счетчик

if (offing>0) { // если нет сигнала на катушки то
lastoffing = offing;
LastOffTimeIng = OffTimeIng; // сохраняем время между пропаданиями сигнала
OffTimeIng = 0; // и обнуляем счетчик.
} else OffTimeIng++; // иначе увелчиваем счетчик

if (LastOffTimeFors>1) { // если время еще не вышло то
LastOffTimeFors--; // отматываем таймер с двойной скоростью чтобы получить половиную задержку
LastOffTimeFors--;
} else {
if (lastoffors.F0) outfors.F0=0;
if (lastoffors.F1) outfors.F1=0;
if (lastoffors.F2) outfors.F2=0;
if (lastoffors.F3) outfors.F3=0;
}

if (LastOffTimeIng>1) { // если время еще не вышло то
LastOffTimeIng--; // отматываем таймер с двойной скоростью чтобы получить половиную задержку
LastOffTimeIng--;
} else {
if (lastoffing.F4) outfors.F4=0;
if (lastoffing.F5) outfors.F5=0;
if (lastoffing.F6) outfors.F6=0;
if (lastoffing.F7) outfors.F7=0;
}

////////////////////////////////////////////////////////////////////
lastfors = PORTA; // сохраняем текущщее состояние
PORTB = outfors;
INTCON = 0x20; // Set T0IE, clear T0IF
}
}

void main() {
ANSEL = 0; // Configure AN pins as digital I/O
ANSELH = 0;
OPTION_REG = 0x80; // Assign prescaler to TMR0

PORTA = 0x00; // init PORTA
TRISA = 0xFF; // configure PORTA pins as input
PORTB = 0x00; // set PORTB to 0
TRISB = 0x00; // designate PORTB pins as output
PORTC = 0x00; // set PORTC to 0
TRISC = 0x00; // designate PORTC pins as output
PORTD = 0x00; // set PORTD to 0
TRISD = 0x00; // designate PORTD pins as output
TMR0 = 0xFE; // сразу уходим в прерывание
INTCON = 0xA0; // Enable TMRO interrupt
}


Как вы вижиье все максимально просто и при этом работоспособно, просто не стоит пытатся решать любую задачу в лоб, зачастую полезно просто включить мышление и как следует проанализировать саму задачу. Если бы я пытался решить это по классическим канонам то потребовалось бы целая куча микросхем либо одна хорошая но дорогая (например на 8051), если бы попытался решить как обычно (массивы в которых счетчики по каждому каналу) то вылител бы из памяти либо не вписался бы в нужную точность (хотелось точность повторения сигналов хотябы 0,1мс). Вся соль данного решения в том что обычными средствами и малоемкими or and xor реализовали относительно сложный алгоритм в минимально сжатую программу на очень дешевой железке (от 100 до 300р смотря где покупать).

Итог


Не бойтесь микроконтроллеров, они не кусаются, почти любой толковый программист хотябы поверхностно знакомый с Си способен сделать недорогие но полезные приблуды (то же опережение зажигания для карбюраторных двигателей или например часы).
Не бойтесь осваивать чтото новое, очень много моих знакомых штурмуют одну узкую специализацию становясь в ней асами но при этом не могут почти ничего больше, думайте о будующщем и о том где себе соломку подстелить, к примеру лет 6-8 назад очень модно было программировать на делфе и все ее учили, а потом делфя почти умерла и сейчас оочень сложно получить хорошую достойно оплачиваемую работу профессиональному делфи кодеру.

P.S. Если вам этот код сильно не нравится просьба не кидатсья камнями, это была моя первая попытка программированния МК, лудьше поплюсуйте маленько.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.