Пропуски сигналов сенсора

dmitry_k
Offline
Зарегистрирован: 13.10.2017

Здравствуйте, уважаемые участники форума.

Нужны Ваши советы, как оптимизировать код, чтобы Ардуино Мега2560 не пропускала сигналы с оптического сенсора FC-33. Код управляет движения тележки по 2 осям (Х, У), которая приводится в движение от 2 DC моторов с установлеными счетчиками оборотов на каждую ось. (В коде присутствует и 3-я ось N, не буду углубляться, зачем.) Управление осуществляется по координатам, загруженным с помощью PROGMEM (массив в начале кода). 

Опрос пинов, подключенных к сенсорам идет с помощью опроса порта Арудино (PORT C, pin №№ 31, 33, 35). До этого использовал Digital.Read() и библиотеку CyberLib - тоже пропускали.

Проблема: при быстром вращении моторов Арудино пропускает сигналы с сенсоров. (Например, за оборот должно суммировать 10 сигналов, а просуммировало только 6. Проверено опытным путем - вращал вручную и считал обороты).

Подскажите, пожалуйста, как оптимизировать код, чтобы считывание сигналов с сенсоров происходило непрерывно и без пропусков!

Код прилагается.

const byte X1Pin = 42; //  X-контактор1
const byte X2Pin = 44; //  X-контактор2
const byte Y1Pin = 46; //  Y-контактор1
const byte Y2Pin = 48; //  Y-контактор2
*/

#include <avr/pgmspace.h>  // для функции PROGMEM считывания координат
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x3F, 20, 4); // Задаем адрес и размерность дисплея ( или lcd(0x3F, 20, 4) )

const byte PinMotorX = 3;	//	пин мотора
const byte PinMotorY = 2;	//	пин мотора
const byte PinMotorN = 4;	//	пин мотора
const byte XR1Pin = 23;		//  направление вращения Х-мотора
const byte XR2Pin = 25;		//  направление вращения Х-мотора
const byte YR1Pin = 27;		//  направление вращения Y-мотора
const byte YR2Pin = 29; 	//  направление вращения Y-мотора

byte CoPgm = 2;		//	выставлена программа 2 (овал) по умолчанию
byte Line = 0;		//	номер строки кода
byte Level = 1; 	//	этапы основной ПРОГРАММЫ

byte Xspd;	// скорость из кода
byte Yspd;	// скорость из кода
byte Nspd;	// скорость из кода
byte Qline;		//	счетчик перехода к следующей строке Line кода выгрузки

int Xmin = 30;	// минимальное расстояние начала торможения двигателя осей
int Ymin = 20;	// минимальное расстояние начала торможения двигателя осей
int DoseN;		// значение контрольной дозы для розлива

int Xcod;		// координата из кода
int Ycod;		// координата из кода
int Ncod;		// координата из кода
int Lines0;		// вспомогательная. счетчик

boolean StartPrgm = false;

boolean Nshift;	// true - чтобы дождаться окончания розлива перед переходом к следующей строке кода
boolean Up;			// переменная управления направлением вращения моторов (вперед = true)
boolean OnMotorX;
boolean OnMotorY;
boolean OnMotorN;
boolean MotorOn;
boolean XtoZero;
boolean YtoZero;
boolean NtoZero;

byte KL1;
boolean A44;
boolean A55;
boolean P11 = false;
boolean P22 = false;
boolean P33 = false;

// http://www.nongnu.org/avr-libc/user-manual/group__avr__stdint.html#ga1f1...

char Tab;			//	переменная таблиц кода выгрузки (идут ниже)	

const PROGMEM uint16_t Circ[] = { // круг, координаты.	signed int with at least 16 bits.
880, 440, 0, 255, 255, 0, 0, 
40, 40, 0, 255, 255, 0, 0, 
32767, 0, 0, 0, 0, 0, 0
};

const PROGMEM uint16_t Oval[] = { // овал, координаты.	signed int with at least 16 bits.
471, 471, 0, 255, 255, 0, 0, 
471, 118, 1500, 0, 255, 255, 0, 
577, 118, 1500, 255, 0, 255, 0, 
577, 471, 1500, 0, 255, 255, 0, 
901, 471, 0, 255, 0, 0, 0, 
901, 118, 1500, 0, 255, 255, 0, 
1007, 118, 1500, 255, 0, 255, 0, 
1007, 471, 1500, 0, 255, 255, 0, 
1331, 471, 0, 255, 0, 0, 0, 
1331, 118, 1500, 0, 255, 255, 0, 
1437, 118, 1500, 255, 0, 255, 0, 
1437, 471, 1500, 0, 255, 255, 0, 
1761, 471, 0, 255, 0, 0, 0, 
1761, 118, 1500, 0, 255, 255, 0, 
1867, 118, 1500, 255, 0, 255, 0, 
1867, 471, 1500, 0, 255, 255, 0, 
2191, 471, 0, 255, 0, 0, 0, 
2191, 118, 1500, 0, 255, 255, 0, 
2297, 118, 1500, 255, 0, 255, 0, 
2297, 471, 1500, 0, 255, 255, 0, 
2621, 471, 0, 255, 0, 0, 0, 
2621, 118, 1500, 0, 255, 255, 0, 
2727, 118, 1500, 255, 0, 255, 0, 
2727, 471, 1500, 0, 255, 255, 0, 
3051, 471, 0, 255, 0, 0, 0, 
3051, 118, 1500, 0, 255, 255, 0, 
3157, 118, 1500, 255, 0, 255, 0, 
3157, 471, 1500, 0, 255, 255, 0, 
3481, 471, 0, 255, 0, 0, 0, 
3481, 118, 1500, 0, 255, 255, 0, 
3587, 118, 1500, 255, 0, 255, 0, 
3587, 471, 1500, 0, 255, 255, 0, 
3911, 471, 0, 255, 0, 0, 0, 
3911, 118, 1500, 0, 255, 255, 0, 
4017, 118, 1500, 255, 0, 255, 0, 
4017, 471, 1500, 0, 255, 255, 0, 
4341, 471, 0, 255, 0, 0, 0, 
4341, 118, 1500, 0, 255, 255, 0, 
4447, 118, 1500, 255, 0, 255, 0, 
4447, 471, 1500, 0, 255, 255, 0, 
4771, 471, 0, 255, 0, 0, 0, 
4771, 118, 1500, 0, 255, 255, 0, 
4877, 118, 1500, 255, 0, 255, 0, 
4877, 471, 1500, 0, 255, 255, 0, 
5201, 471, 0, 255, 0, 0, 0, 
5201, 118, 1500, 0, 255, 255, 0, 
5307, 118, 1500, 255, 0, 255, 0, 
5307, 471, 1500, 0, 255, 255, 0, 
40, 40, 0, 255, 255, 0, 0, 
32767, 0, 0, 0, 0, 0, 0
};

const PROGMEM uint16_t Rect[] = { // прямоугольник, координаты.	signed int with at least 16 bits.
2000, 1800, 0, 255, 255, 0, 0, 
40, 40, 0, 255, 255, 0, 0, 
32767, 0, 0, 0, 0, 0, 0
};



class OneTimer	{	//	вкл - true, длительность - 4 (тип - byte).			длительность будет +1 к указанному числу. 
	// старт, длительность. 	 результат - true
	private:
		byte Time0;
		byte Time1;
		byte pushTime;
		boolean TT1;
		
	public:
		boolean startTimer; // вкл / выкл
		byte Gap; // длительность
		boolean TimeUp; // время истекло
		
	void timeIt(boolean startTimer, byte Gap)	{
		if (!startTimer)	{
			Time1 = 0;
			Time0 = 0;
			TT1 = false;
			TimeUp = false;
		}
		else	{
			Time1 = millis();
			if (!TT1)	{	
				Time0 = Time1;
				TT1 = true;
			}
			else	{
				pushTime = Time1 - Time0;
				if (abs(pushTime) > Gap) {
					TimeUp = true;
					startTimer = false;
				}
			}
		}
		return TimeUp;
	}
};

OneTimer PumpingTime;
OneTimer DisplayTime;
OneTimer MotstopTime;


class Button  {	//	возвращает переменную "OnOff" = true / false
	private:
		boolean State1;    // текущий сигнал на кнопке
		boolean State0;    // предыдущий сигнал на кнопке
		OneTimer ButtonTime;

	public:
		boolean typeButton; // 2 типа кнопок: false - простая (есть/нет сигнала), true - с залипанием (вкл/выкл, 2-ое нажатие)
		byte PinBut;
		boolean OnOff; // состояние кнопки - в зависимости от типа кнопки

	void checkPush(boolean typeButton, byte PinBut)  { // тип кнопки, номер пина (true, 45), true - с залипанием
	State1 = digitalRead(PinBut);
	if (!typeButton)  {
		if (State0 != State1) { // сначала проверить, чтобы у кнопки изменилось состояние
			if (State1 == HIGH) { // далее, если кнопка нажата то
				ButtonTime.timeIt(true, 100);	// интервал фильтрации дребезга - 5 мсек	(2 + 1)
				if (ButtonTime.TimeUp == true) { 
					OnOff = true;
					State0 = State1;
				}
			}
			else  {
				OnOff = false;
				State0 = LOW;
				ButtonTime.timeIt(false, 0);
			}
		}
		else	{	ButtonTime.timeIt(false, 0);	}
	}
	else  {
		if (State0 != State1) { // сначала проверить, чтобы у кнопки изменилось состояние
			if (State1 == HIGH) { // далее, если кнопка нажата то
				ButtonTime.timeIt(true, 100);	// интервал фильтрации дребезга - 5 мсек	(2 + 1)
				if (ButtonTime.TimeUp == true) { // если время истекло (TimeUp == true)
					OnOff = !OnOff;
					State0 = State1;
				}
			}
			else  {
				ButtonTime.timeIt(false, 0);
				State0 = LOW;
			}
		}
		else	{	ButtonTime.timeIt(false, 0);	}
	}
	return OnOff;
	}
};

Button StartBut;
Button ProgramBut;
Button PumpBut;
Button DosingBut;

class Contacts	{	//	возвращает переменную "OnOff" = true / false
	private:
		boolean State0;    // предыдущий сигнал на контакторе
		OneTimer ContactTime;

	public:
		char Byte1;
		char Byte2;
		boolean OnOff; // состояние кнопки - в зависимости от типа кнопки

	void checkCont(char Byte1, char Byte2)  { // Byte1 и Byte2 записываются в битовой маске "B11101111" порта ардуины по даташиту
		if  (PINL == (PINL | Byte1))  { // есть контакт
			if (!State0) { // сначала проверить, чтобы у кнопки изменилось состояние
				ContactTime.timeIt(true, 0);	// интервал фильтрации дребезга - 1 мсек	(0 + 1)
					if (ContactTime.TimeUp == true) {
					OnOff = true;
					State0 = true;
				}
			}
		}
		else if (PINL == (PINL & Byte2))  { // нет контакта
			OnOff = false;
			State0 = false;
			ContactTime.timeIt(false, 0);
		}
	return OnOff;
	}
};

Contacts X1Cont;
Contacts X2Cont;
Contacts Y1Cont;
Contacts Y2Cont;

class OptoSens	{	//	возвращает переменную "Steps" посчитанных шагов (integer) = от -32 тыс. до +32 тыс.
	private:
		boolean Step0;    // предыдущий сигнал на оптосенсоре
		boolean Step1;    // текущий сигнал на оптосенсоре

	public:
		char Byte1;
		char Byte2;
		boolean Clockwise; // направление вращения
		boolean ToZero; // обнуление счетчика шагов
		int Steps; // посчитанные шаги

	void countSteps(char Byte1, char Byte2, boolean Clockwise, boolean ToZero)  { // см. аналогичное описание class Contacts
{
		if  (PINC == (PINC | Byte1))	{
			Step1 = true;
		}
		else if (PINC == (PINC & Byte2))	{
			Step1 = false;
		}
}
{ // счетчик шагов
		if (!ToZero) {
			if (Step0 != Step1) { // считает переходы 0-1 и 1-0
				if (Clockwise == true) { // счет ведется в зависимости от направления вращения двигателя
					Steps += 1;
					Step0 = Step1;
				}
				else  {
					Steps -= 1;
					Step0 = Step1;
				}
			}
		}
		else {	
			Steps = 0;	
		}
}		

		return Steps;	
	}
};

OptoSens Xopto;
OptoSens Yopto;
OptoSens Nopto;

class MotorDrive	{	//	ничего не возвращает
	// пин PWM, старт/стоп, макс. скорость
	private:
		byte i;	// шаги PWM, текущая скорость вращения двигателя
		byte StartSpeed = 145; // скорость старта и остановки. Нужна поскольку при PWM < 120 моторы не вращаются, т.е. работают не эффективно, греются и греют драйверы впустую.
	public:
		byte PWMpin; // пин управления мотором
		boolean rotate; // вкл / выкл
		byte MaxSpeed; // ограничение скорости из кода координат
		
	void driveit(byte PWMpin, boolean rotate, byte MaxSpeed)	{
		if (!rotate)	{ // если команда не вращать
			if (i > StartSpeed)	{ // если текущая скорость больше заданной
				i -= 1;
				analogWrite(PWMpin, i);
			}
			else	{
				i = 0;
				analogWrite(PWMpin, i);
			}
		}
		else	{
			if (i < StartSpeed)	{
				i = StartSpeed;
				analogWrite(PWMpin, i);
			}
			else if (i < MaxSpeed)	{
				i += 1;
				analogWrite(PWMpin, i);
			}
		}
	}
};

MotorDrive Xmotor;
MotorDrive Ymotor;
MotorDrive Nmotor;

class DistanceUp	{	//	возвращает расстояние "Distance" = целое число (int) и направление движения "Up" = true / false
	// координата, текущий шаг
	private:
		int differ; // разность координаты и текущего шага
	public:
		int Coordinat; // пин управления мотором
		int currStep; // вкл / выкл
		int Distance; // ограничение скорости из кода координат
		boolean Up; // направление движения - вперед или назад
		
	void toGo(int Coordinat, int currStep)	{ // координата из кода, текущий шаг
		differ = Coordinat - currStep;
		Distance = abs(differ);
		if (differ < 0)	{	Up = false;	}
		else	{	Up = true;	}
		return Distance, Up;
	}
};

DistanceUp Xaxis;
DistanceUp Yaxis;

class Forward	{			//	подает сигналы направления вращения на платы моторов
	private:
		byte State0 = 2;	
		byte StartSpeed = 145; // скорость старта и остановки. Нужна поскольку при PWM < 120 моторы не вращаются, т.е. работают не эффективно, греются и греют драйверы впустую.
	public:
		boolean F11;
		byte F22;
		byte F33;

	void showIt(boolean F11, byte F22, byte F33) {	//	направление вращения, пин1, пин2,. 
		if	(State0 != F11)	{
			if (!F11) { // вращение назад
				digitalWrite (F22, LOW); 
				digitalWrite (F33, HIGH); 
				State0 = F11;
			}
			else { // вращение вперед
				digitalWrite (F22, HIGH); 
				digitalWrite (F33, LOW);
				State0 = F11;
			}
		}	
	}		
};

Forward	showX;
Forward	showY;

boolean OnOffMotor(int Dist, int DistMin)	{	//	функция для упрощения кода. Вкл / выкл двигателя в зависимости от Distance
	if	(Dist > DistMin)	{	MotorOn = true;	}	// если больше минимального - вращаем двигатель
	else	{	MotorOn = false;	}						// если меньше минимального - останавливаем двигатель
return MotorOn;
}

int Coordinates1(int Lines)		{	//	Круг. считывает координаты кода из флэш-памяти
	if (Lines0 != Lines)	{
		Xcod 	= pgm_read_word_near(Circ + Lines);
		Ycod 	= pgm_read_word_near(Circ + (Lines + 1));
		Ncod	= pgm_read_word_near(Circ + (Lines + 2));
		Xspd	= pgm_read_word_near(Circ + (Lines + 3));
		Yspd	= pgm_read_word_near(Circ + (Lines + 4));
		Nspd	= pgm_read_word_near(Circ + (Lines + 5));
		Nshift	= pgm_read_word_near(Circ + (Lines + 6));
		Lines0 = Lines;
	}
	
	return Xcod, Ycod, Ncod, Xspd, Yspd, Nspd, Nshift;
}

int Coordinates2(int Lines)		{	//	Овал. считывает координаты кода из флэш-памяти
	if (Lines0 != Lines)	{
		Xcod 	= pgm_read_word_near(Oval + Lines);
		Ycod 	= pgm_read_word_near(Oval + (Lines + 1));
		Ncod	= pgm_read_word_near(Oval + (Lines + 2));
		Xspd	= pgm_read_word_near(Oval + (Lines + 3));
		Yspd	= pgm_read_word_near(Oval + (Lines + 4));
		Nspd	= pgm_read_word_near(Oval + (Lines + 5));
		Nshift	= pgm_read_word_near(Oval + (Lines + 6));
		Lines0 = Lines;
	}
	
	return Xcod, Ycod, Ncod, Xspd, Yspd, Nspd, Nshift;
}

int Coordinates3(int Lines)		{	//	Прямоугольник. считывает координаты кода из флэш-памяти
	if (Lines0 != Lines)	{
		Xcod 	= pgm_read_word_near(Rect + Lines);
		Ycod 	= pgm_read_word_near(Rect + (Lines + 1));
		Ncod	= pgm_read_word_near(Rect + (Lines + 2));
		Xspd	= pgm_read_word_near(Rect + (Lines + 3));
		Yspd	= pgm_read_word_near(Rect + (Lines + 4));
		Nspd	= pgm_read_word_near(Rect + (Lines + 5));
		Nshift	= pgm_read_word_near(Rect + (Lines + 6));
		Lines0 = Lines;
	}
	
	return Xcod, Ycod, Ncod, Xspd, Yspd, Nspd, Nshift;
}

class XYround	{			//	округляет текущее значение координаты для перехода к следующей строке кода
	private:
		int C11;
		int C22;
		int C33 = 10; 	// округление +- 10
	public:
		int XYstep;
		int XYcoordinate;
		boolean NextLine;

	void roundIt(int XYcoordinate, int XYstep) {	//	текущее значение шага, координата из кода
		C11 = XYcoordinate - C33; // 200, С1 = 190
		C22 = XYcoordinate + C33;	// 200. С2 = 210
		if (XYstep > C11 && XYstep < C22) { 
			NextLine = true;
		}
		else {	NextLine = false;	}
	return NextLine;
	}
};

XYround Xround;
XYround Yround;

int SelectPrgm()	{	// выбор программы розлива (загрузочного кода)
	ProgramBut.checkPush(false, 52);
	if (KL1 != ProgramBut.OnOff)	{
		if (ProgramBut.OnOff == true)	{
			CoPgm += 1;
			if (CoPgm > 3)	{	CoPgm = 1;	}
			KL1 = true;
		}
		else	{	KL1 = false;	}
	}
{
	if (CoPgm == 1)	{	
		Tab = 'C';		//	круг
		DoseN = 1000;		//	для контрольного розлива
	}	
	else if (CoPgm == 2)	{	
		Tab = 'O';		//	овал
		DoseN = 1500;		//	для контрольного розлива
	}	
	else		{	
		Tab = 'R';		//	прямоугольник
		DoseN = 2700;		//	для контрольного розлива
	}	
}
return Tab, DoseN;
}

void Display()	{		//	работа дисплея
/*  Выводимая информация
	01234567890123456789
#0  PROGRAM - 5,5x11
#1  PGM OFF   Level = 2 
#2  Xstep  Ystep  Nstep
#3  12345  12345  12345
	01234567890123456789
*/ 
	DisplayTime.timeIt(true, 250);
	if	(DisplayTime.TimeUp == true)	{
		
		lcd.clear();
		
		//    строка #0
		lcd.setCursor(0, 0);
		lcd.print("PROGRAM -");   
		lcd.setCursor(10, 0);
		{
		if (CoPgm == 1)		{	lcd.print("6,5");	}
		else if (CoPgm == 2)	{	lcd.print("5,5x11");	}
		else				{	lcd.print("10x12");	}
		}

		//    строка #1
		{
		if (!StartBut.OnOff)  {
			lcd.setCursor(0, 1);
			lcd.print("PGM OFF");
		}
		else  {
			lcd.setCursor(0, 1);
			lcd.print("PGM ON");
		}
		}

		lcd.setCursor(10, 1);
		lcd.print("Level =");
		lcd.setCursor(18, 1);
		lcd.print(Level);
		
		//    строка #2
		lcd.setCursor(0, 2);
		lcd.print("Xstep");
		lcd.setCursor(7, 2);
		lcd.print("Ystep");
		lcd.setCursor(14, 2);
		lcd.print("Nstep");

		//    строка #3
		lcd.setCursor(0, 3);
		lcd.print(Xopto.Steps);
		lcd.setCursor(7, 3);
		lcd.print(Yopto.Steps);
		lcd.setCursor(14, 3);
		lcd.print(Nopto.Steps);  

		DisplayTime.timeIt(false, 0);
	}
}

void DoseCtrl()	{		// программа контрольного розлива дозы
	DosingBut.checkPush(true, 53);			// опрос кнопки, указать номер пина
	if (DosingBut.OnOff == true)	{	
		A55 = false;
		Nopto.countSteps(B00000100, B11111011, 1, NtoZero);	//	вкючаем счетчик оборотов
		NtoZero = false;					// снимаем обнуление счетчика оборотов
		if	(Nopto.Steps < DoseN)	{			// если не превышено
			Nmotor.driveit(PinMotorN, 1, 255);		// вращаем двигатель
		}
		else	{							// если превышено
			Nmotor.driveit(PinMotorN, 0, 0); 		// останавливаем двигатель
			PumpingTime.timeIt(true, 110);	// через 110 мсек перестанет повторяться цикл Nmotor.driveit(4, 0, 0) при выкл. кнопке
			if	(PumpingTime.TimeUp == true) { 
				PumpingTime.timeIt(false, 0);
				NtoZero = true;				// обнулили счетчик оборотов
				A55 = true;
			}
		}
	}
	else	{	
		if (!A55)	{	
			Nmotor.driveit(PinMotorN, 0, 0); // byte PWMpin, boolean rotate, byte MaxSpeed
			PumpingTime.timeIt(true, 110);	// через 50 мсек перестанет повторяться цикл Nmotor.driveit(4, 0, 0) при выкл. кнопке
			if	(PumpingTime.TimeUp == true) { 
				PumpingTime.timeIt(false, 0);
				NtoZero = true;				// обнулили счетчик оборотов
				A55 = true;
			}
		}
	
	}
}

void Pumping()	{		//	программа непрерывной работы насоса (прогрев, перегонка)
	PumpBut.checkPush(true, 51);			// опрос кнопки, указать номер пина
	if (PumpBut.OnOff == true)	{	
		Nmotor.driveit(PinMotorN, 1, 255);	
		A44 = false;
	}
	else	{
		if (!A44)	{	
			Nmotor.driveit(PinMotorN, 0, 0); // byte PWMpin, boolean rotate, byte MaxSpeed
			PumpingTime.timeIt(true, 110);	// через 110 мсек перестанет повторяться цикл Nmotor.driveit(4, 0, 0) при выкл. кнопке
			if	(PumpingTime.TimeUp == true) { 
				PumpingTime.timeIt(false, 0);
				A44 = true;
			}
		}
	}
}

byte checkStop()	{	// функция экстренной остановки либо остановки по окончанию кода выгрузки
	if	(Y1Cont.OnOff == true || Y2Cont.OnOff == true || X1Cont.OnOff == true || X2Cont.OnOff == true || Xcod == 32767)	{	// Тип данных "int" находится в диапазоне от -32 768 до 32 767. Поэтому примем значение 32767 как сигнал к окончанию выполнения кода и выхода оператора - остановка/
		Level = 4;
	}
}



void PROGRAM()	{ // основная программа
{
	//	опрашиваем контакоры
	X1Cont.checkCont(B10000000, B01111111); //	pin 42	PL7	B10000000	B01111111	
	X2Cont.checkCont(B00100000, B11011111);	//	pin 44	PL5	B00100000	B11011111
	Y1Cont.checkCont(B00001000, B11110111);	//	pin 46	PL3	B00001000	B11110111
	Y2Cont.checkCont(B00000010, B11111101);	//	pin 48	PL1	B00000010	B11111101
	
	// считаем шаги с оптодатчиков
	Xopto.countSteps(B01000000, B10111111, Xaxis.Up, XtoZero);	//	пин PC6 или 31
	Yopto.countSteps(B00010000, B11101111, Yaxis.Up, YtoZero);	//	пин PC4 или 33
	Nopto.countSteps(B00000100, B11111011, 1, NtoZero);	//	пин PC2 или 35
	
	
	//	управляем вращением двигателей
	Xmotor.driveit(PinMotorX, OnMotorX, Xspd); // byte PWMpin, boolean rotate, byte MaxSpeed
	Ymotor.driveit(PinMotorY, OnMotorY, Yspd); // byte PWMpin, boolean rotate, byte MaxSpeed
	Nmotor.driveit(PinMotorN, OnMotorN, Nspd); // byte PWMpin, boolean rotate, byte MaxSpeed

}
	
	switch (Level)	{
		case 1:	{// приведение в начальное положение
		showX.showIt(0, XR1Pin, XR2Pin);	//	задаем направление вращения
		showY.showIt(0, YR1Pin, YR2Pin);	//	задаем направление вращения
		{	
			if (!X1Cont.OnOff && !X2Cont.OnOff)	{	//	вращаем двигатель Х до замыкания
				OnMotorX = true;	
				Xspd = 255;
			}
			else	{	//	останавливаем
				OnMotorX = false;	
				Xspd = 0;
				if	(!P11)	{
					XtoZero = true;
					Qline += 1;
					P11 = true;
				}
			}
		}
		{
			if (!Y1Cont.OnOff && !Y2Cont.OnOff)	{	//	вращаем двигатель У до замыкания
				OnMotorY = true;
				Yspd = 255;
			}
			else	{	//	останавливаем
				OnMotorY = false;
				Yspd = 0;
				if	(!P22)	{
					YtoZero = true;
					Qline += 1;
					P22 = true;
				}
			}
		}
			if	(Qline == 2)	{	// переходим к следующему шагу при значении Qline = 2
				MotstopTime.timeIt(true, 110);	
				if	(MotstopTime.TimeUp == true) { 
					MotstopTime.timeIt(false, 0);
					Qline = 0;
					P11 = false;
					P22 = false;
					Level = 2;
				}
			}
		break;
		}	
		
		case 2:	{	// вращение двигателей до размыкания контакторов
			showX.showIt(1, XR1Pin, XR2Pin);	//	задаем направление вращения
			showY.showIt(1, YR1Pin, YR2Pin);	//	задаем направление вращения
			{	
			if (!X1Cont.OnOff && !X2Cont.OnOff)	{	//	если контакторы разомкнуты
				OnMotorX = false;	//	останавливаем двигатель Х
				Xspd = 0;
				if	(!P11)	{
					XtoZero = false;
					Qline += 1;
					P11 = true;
				}
			}
			else	{	//	если контакторы замкнуты
				OnMotorX = true;	//	вращаем двигатель Х
				Xspd = 255;
				}
			}
			{	
			if (!Y1Cont.OnOff && !Y2Cont.OnOff)	{	//	если контакторы разомкнуты
				OnMotorY = false;	//	останавливаем двигатель У
				Yspd = 0;
				if	(!P22)	{
					YtoZero = false;
					Qline += 1;
					P22 = true;
				}
			}
			else	{	//	если контакторы замкнуты
				OnMotorY = true;	//	вращаем двигатель Х
				Yspd = 255;
				}
			}
			if	(Qline == 2)	{	//	переходим к следующему шагу при значении Qline = 2
				Qline = 0;
				P11 = false;
				P22 = false;
				Level = 3;
			}
		break;
		}
		
		case 3:  {						// основная программа розлива
			{
			if	(CoPgm == 1)	{	Coordinates1(Line);		}	//	считываем координаты кода для Круга
			else if	(CoPgm == 2)	{	Coordinates2(Line);		}	//	считываем координаты кода для Овала
			else	{	Coordinates3(Line);		}	//	считываем координаты кода для Прямоугольника
			}
			
			Xaxis.toGo(Xcod, Xopto.Steps);	//	определяем оставшееся расстояние по координате
			Yaxis.toGo(Ycod, Yopto.Steps);	//	координата из кода, текущий шаг
			showX.showIt(Xaxis.Up, XR1Pin, XR2Pin);	//	задаем направление вращения
			showY.showIt(Yaxis.Up, YR1Pin, YR2Pin);	//	задаем направление вращения
			OnMotorX = OnOffMotor(Xaxis.Distance, Xmin);	// вкл/выкл двигателя Х
			OnMotorY = OnOffMotor(Yaxis.Distance, Ymin);	// вкл/выкл двигателя Y
			OnMotorN = OnOffMotor(Ncod, Nopto.Steps);		// вкл/выкл двигателя N
			Xround.roundIt(Xcod, Xopto.Steps);	// округляем значение текущего шага
			Yround.roundIt(Ycod, Yopto.Steps);	// округляем значение текущего шага
			
			if	(!P11)	{		//	условие перехода к следующему шагу X
				if	(Xround.NextLine == true)	{
					Qline += 1;
					P11 = true;
				}
			}				
			if	(!P22)	{		//	условие перехода к следующему шагу Y
				if	(Yround.NextLine == true)	{
					Qline += 1;
					P22 = true;
				}
			}
			
			{			
			if (!Nshift)	{					//	если Nshift = 0, то не дожидаемся окончания розлива
				if	(Qline == 2)	{
					Qline = 0;
					P11 = false;
					P22 = false;
					Line += 7;
				}
			}
			else	{					//	если W = 1, то дожидаемся окончания розлива
				if	(!P33)	{			//	условие перехода к следующему шагу N
					if	(Nopto.Steps > Ncod)	{
						Qline += 1;
						NtoZero = true;
						NtoZero = false;
						P33 = true;
					}
				}
				if	(Qline == 3)	{
					Qline = 0;
					P11 = false;
					P22 = false;
					P33 = false;
					NtoZero = false;
					Line += 7;
				}
			}
			}
			
			checkStop(); 		//	проверка на замыкание или конец кода
		
		break;
	}
		  
		case 4: {// остановка, выключение
			OnMotorX = false;
			OnMotorY = false;
			OnMotorN = false;
			MotstopTime.timeIt(true, 110);	
			if	(MotstopTime.TimeUp == true) { 
				MotstopTime.timeIt(false, 0);
				Level = 5;
			}
		break;
		}
		
		case 5:	{
			
			P11 = false;
			P22 = false;
			P33 = false;
			XtoZero = true;
			YtoZero = true;
			NtoZero = true;
			XtoZero = false;
			YtoZero = false;
			NtoZero = false;
			Qline	= 0;
			Xcod 	= 0;
			Ycod 	= 0;
			Ncod	= 0;
			Xspd	= 0;
			Yspd	= 0;
			Nspd	= 0;
			Nshift	= 0;
			Line	= 0;
			Level	= 6;
		break;
		}
		
		case 6:	{
			
			StartBut.OnOff = false;
			StartPrgm = false;
			Level	= 1;
		break;
		}
	}
}



void setup()	{
	pinMode(StartBut.PinBut, INPUT);
    pinMode(PumpBut.PinBut, INPUT);
	
	pinMode(PinMotorX, OUTPUT); // ШИМ Х-мотора
	pinMode(XR1Pin, OUTPUT); // направление вращения Х-мотора
	pinMode(XR2Pin, OUTPUT); // направление вращения Х-мотора
	pinMode(PinMotorY, OUTPUT); // ШИМ Y-мотора
	pinMode(YR1Pin, OUTPUT); // направление вращения Y-мотора
	pinMode(YR2Pin, OUTPUT); // направление вращения Y-мотора
	pinMode(PinMotorN, OUTPUT); // ШИМ мотора насоса
	
	lcd.init();              // Инициализация lcd
	lcd.backlight();         // Включаем подсветку
}

void loop()	{
	// сначала опрашиваем кнопку старт/стоп:
	StartBut.checkPush(true, 50);		// тип кнопки, номер пина (true, 45), true - с залипанием
	
	if (!StartPrgm)	{ // основная программа включается сразу от кнопки старт/стоп, а выключается после полной остановки, т.е. старт/стоп может быть уже выключена, а программа будет еще выполняться, пока не достигнет заданного положения
		SelectPrgm(); // выбор программы розлива
		DoseCtrl; // запуск пробной дозации
		Pumping(); // постоянная работа насоса
		if (StartBut.OnOff == true)	{	// только если старт/стоп нажата и не работает насос, тогда запускается основная программа
			if (!PumpBut.OnOff && !DosingBut.OnOff)	{	StartPrgm = true;	}
		}
	}
	else	{	PROGRAM();	}
	
	Display();
}




 

dmitry_k
Offline
Зарегистрирован: 13.10.2017

Строки считывания данных с сенсоров в коде выделены серым (№№ 274 - 280).

Код считывания (без лишних переменных из моего кода):

	if  (PINC == (PINC | B01000000))	{ Step1 = true; }
	else if (PINC == (PINC & B10111111))	{ Step1 = false; }

 / / и счетчик шагов:
	if (Step0 != Step1) { // считает переходы 0-1 и 1-0
		Steps += 1;
		Step0 = Step1; }
	
И так для каждого датчика из трех:

	if  (PINC == (PINC | B00010000))	{ Step1 = true; }
	else if (PINC == (PINC & B11101111))	{ Step1 = false; }

	if  (PINC == (PINC | B00000100))	{ Step1 = true; }
	else if (PINC == (PINC & B11111011))	{ Step1 = false; }

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Ну... Какбэ... самый надежный из известных мне способов "не пропустить" - подвесить счетчики на внешние прерывания.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

А самый-самый надёжный -на тактовый вход таймера. Но Т.С. выбрал самый наихреновейший способ, за что и поплатился.

dmitry_k
Offline
Зарегистрирован: 13.10.2017

То есть, если мне нужно, чтобы сенсоры работали непрерывно, нужно подвесить их на непрерывное внешнее прерывание? Я ранее не работал с внешними прерываниями, правильно ли я понимаю, что когда исполняются внешние прерывания, основной код (вращение двигателей, опрос кнопки "СТОП", дисплей и пр.) не выполняется?

Вы не могли бы пояснить поподробнее, как вы себе это видете, чтобы я мог правильно вас понять? Если нужно чтобы двигатель Х совершил заданное количество оборотов, то в какой момент должны срабатывать внешние прерывания и как их согласовать с плавным стартом / остановкой двигателя и пр.? 

dmitry_k
Offline
Зарегистрирован: 13.10.2017

То есть, если мне нужно, чтобы сенсоры работали непрерывно, нужно подвесить их на непрерывное внешнее прерывание? Я ранее не работал с внешними прерываниями, правильно ли я понимаю, что когда исполняются внешние прерывания, основной код (вращение двигателей, опрос кнопки "СТОП", дисплей и пр.) не выполняется?

Вы не могли бы пояснить поподробнее, как вы себе это видете, чтобы я мог правильно вас понять? Если нужно чтобы двигатель Х совершил заданное количество оборотов, то в какой момент должны срабатывать внешние прерывания и как их согласовать с плавным стартом / остановкой двигателя и пр.? 

sadman41
Offline
Зарегистрирован: 19.10.2016

dmitry_k пишет:

То есть, если мне нужно, чтобы сенсоры работали непрерывно, нужно подвесить их на непрерывное внешнее прерывание? Я ранее не работал с внешними прерываниями, правильно ли я понимаю, что когда исполняются внешние прерывания, основной код (вращение двигателей, опрос кнопки "СТОП", дисплей и пр.) не выполняется?

Строго говоря - у вас всегда выполняется что-то одно по одной простой причине - ваш МК одноядерный. Просто подпрограмма, привязанная к прерыванию будет выполнятся сразу после получения импульса. И поэтому они утеряны не будут (в большинстве случаев). 

dmitry_k пишет:
Вы не могли бы пояснить поподробнее, как вы себе это видете, чтобы я мог правильно вас понять? Если нужно чтобы двигатель Х совершил заданное количество оборотов, то в какой момент должны срабатывать внешние прерывания и как их согласовать с плавным стартом / остановкой двигателя и пр.? 

 

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

Начните с функции attachInterrupt(), не забывайте про volatile и запрет прерываний на время чтения значения изменяемой в прерывании переменной.

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

dmitry_k пишет:

То есть, если мне нужно, чтобы сенсоры работали непрерывно, нужно подвесить их на непрерывное внешнее прерывание?

Нет, на внешнее прерывание их нужно посадить, чтобы не было пропусков в отсчетах.

Цитата:

правильно ли я понимаю, что когда исполняются внешние прерывания, основной код (вращение двигателей, опрос кнопки "СТОП", дисплей и пр.) не выполняется?

При поступлении прерывания основной код приостанавливается, текущее состояние процессора (регистры, флаги)сохраняется, выполняется код прерывания, содержимое регистров/флагов восстанавливается, программа продолжает работу с точки, в которой наступило прерывание. 

Грамотно написанное прерывание, как правило, работает совсем недолго - микросекунды. Т.е. именно на такое время происходи приостановка программы.

Если Вы понимаете именно так - то правильно.

Цитата:

Если нужно чтобы двигатель Х совершил заданное количество оборотов, то в какой момент должны срабатывать внешние прерывания и как их согласовать с плавным стартом / остановкой двигателя и пр.? 

Одно совершенно не связапно с другим:

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

Когда и как нужно разгонять/плавно останавливать двигатель - определяется математической моделью. Естественно, для ее применения нужна в том числе длина "пройденного пути" откуда она берется - математической модели все равно.

dmitry_k
Offline
Зарегистрирован: 13.10.2017

andriano, не могли бы вы назвать, какие функции из таблицы (см. ниже), должны работать внутри void loop(), а какие - как внешние прерывания?

dmitry_k
Offline
Зарегистрирован: 13.10.2017

Уважаемый dimax, вы не могли бы дать ссылку на информацию по использованию тактового входа таймера с подходящим примером - чтобы воспользоваться Вашим советом?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

dmitry_k,  думаю этот подходящий.  Он для Uno. Для Меги будет другой пин.

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

dmitry_k пишет:

andriano, не могли бы вы назвать, какие функции из таблицы (см. ниже), должны работать внутри void loop(), а какие - как внешние прерывания?

Ваш вопрос неполный, т.к. Вы не написали, для чего это Вам нужно.

Если предположить, что внешние прерывания нужны для того, чтобы отслеживать сигналы с оптосенсоров, то, вероятно, так:

в loop() : 1,2,3,5,6,7.

в прерывании: 4.

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Некоторые сенсоры выдают сигнал с дребезгом. Тогда тактовый вход таймера не подойдет. Тут только внешнее прерывание с сообтветствующей фильтрацией. 

dmitry_k
Offline
Зарегистрирован: 13.10.2017

Пока не обнаружил дребезга с оптосенсоров модели FC-33.

Изначально стояла цель устранить следующие пропуски сигналов с оптосенсора (рабочий код см. в начале): я вручную быстро прокручиваю двигатель на 1 полный оборот. За этот оборот с оптосенсора должны считаться 10 переключений состояния 0-1-0-1-0-1-0-1-0-1-0 и отобразиться на дисплее (LCD 2004). Статистика получается следующая: 4 раза считываются все 10 переключений. А на пятый раз - только два-три переключения. 

Почему это происходит? - Вероятно, процессор задумывается над чем-то раз 3-5 секунд.

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

И жду новых комментариев уважаемых участников.

dmitry_k
Offline
Зарегистрирован: 13.10.2017

andriano пишет:

Ваш вопрос неполный, т.к. Вы не написали, для чего это Вам нужно.

Если предположить, что внешние прерывания нужны для того, чтобы отслеживать сигналы с оптосенсоров, то, вероятно, так:

в loop() : 1,2,3,5,6,7.

в прерывании: 4.

На самом деле, не стоит цель использовать внешние прерывания. Цель - так оптимизировать последовательность выполняемых функций из таблицы (см. выше), чтобы не было пропусков при считывании сигналов с оптосеносоров. Может быть, как раз наоборот, нужно использовать прерывания для всех остальных функций (№ пп. 1,2,3,5,6,7) и только считывание сигналов исполнять внутри void loop() ?!

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

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

dmitry_k. Программа мощная, но если я в нее полезу, то вы ее "потеряете" - перепишу все.

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

ПС: это мое мнение. Но я не большой авторитет, что бы ожидать мое одобрание или неудовольствие. 

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Дмитрий. Это вам https://github.com/PaulStoffregen/Encoder

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

dmitry_k пишет:

На самом деле, не стоит цель использовать внешние прерывания. Цель - так оптимизировать последовательность выполняемых функций из таблицы (см. выше), чтобы не было пропусков при считывании сигналов с оптосеносоров. Может быть, как раз наоборот, нужно использовать прерывания для всех остальных функций (№ пп. 1,2,3,5,6,7) и только считывание сигналов исполнять внутри void loop() ?!

Что не цель - я с Вами согласен.

А вот насчет "оптимизации" - для нее по сути достаточно знания арифметики. Вот, скажем, у Вас на один оборот приходится 10 импульсов энкодера. Предположим (Вы это должны знать точно, а я - предполагаю), что энкодер выдает симметричные импульсы (т.е. длительность положительной фазы равна длительности отрицательной (нулевой))). Тогда длительность промежутка времени, в течение которого сигнал сохраняет значение, равен 1/20f, где f - частота вращения. Ну, желательно учесть разброс и сделать запас, тогда интервал между последовательными опросами датчика не должен быть больше примерно 1/30f.

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

Цитата:

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

Не совсем понятно, что зачит "по какому". В одном из вариантов понимания - "по любому" или "по тому, который Вам удобнее".

dmitry_k
Offline
Зарегистрирован: 13.10.2017

Я не специалист в программировании, поэтому не сомневаюсь в плохом качестве своего кода )).

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

P.S. Это моя третья редакция кода (переписал его с чистого листа): устранил сбои в работе, но не смог избавиться от пропусков сигналов. Поскольку решить проблему собственными силами я не смог, был вынужден обратиться за дельным советом к интернет сообществу Арудино.

P.S. P.S. Порой неизвестные тебе люди не откажут в помощи. Сталкивался с этим и надеюсь на это )))

 

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

Дмитрий, еще раз: Вам нужно обеспечить, чтобы интервал между двумя вызовами процедуры опроса энкодера не превосходил примерно тридцатой доли минимально возможного периода вращения. Этого можно добиться двумя способами:

1. Организовать цикл программы так, чтобы выполнялось заданное условие. Например, добиться, чтобы весь цикл loop() никогда не превосходил требуемого времени. Или на протяжении loop() вызывать процедуру опроса энкодеров неоднократно, чтобы добиться желаемого интервала между вызовами.

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

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

dmitry_k пишет:

Я не специалист в программировании, поэтому не сомневаюсь в плохом качестве своего кода )).

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

P.S. Это моя третья редакция кода (переписал его с чистого листа): устранил сбои в работе, но не смог избавиться от пропусков сигналов. Поскольку решить проблему собственными силами я не смог, был вынужден обратиться за дельным советом к интернет сообществу Арудино.

P.S. P.S. Порой неизвестные тебе люди не откажут в помощи. Сталкивался с этим и надеюсь на это )))

 

А можно вопрос. Вы на гитхаб по моей ссылке сходили ? Чем вам это не подошло?

dmitry_k
Offline
Зарегистрирован: 13.10.2017

brokly , только что посмотрел присланную Вами ссылку. Как я понял, это библиотека, которая считывает сигналы с энкодера (2-ух оптодатчиков - для определения направления вращения), в ней применяются прерывания, написанные на уровне языка "Ассемблер". 

Сейчас разбираюсь, можно ли использовать эту библиотеку для считывания сигналов с 1 оптодатчика (а не с 2-ух, как в описании). 

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Да что там разбираться то, попробуйте просто подключить только один. Да и про язык ассемблера , это вы сильно сказали, человек просто выдернул кусок на этапе компилляции и воткнул - типа оптимизировал :) Может решил что так красивее :) Там директивно можно отключить эту плюшку. Если у вас всего один датчик вам dimax дал очень дельный совет. Хотя переделать то что я вам отправил под два энкодера - дело двух минут.

dmitry_k
Offline
Зарегистрирован: 13.10.2017

dimax  , правильно ли я понял, Вы предлагаете подключить оптосенсор к пину 11 (Т1) платы Uno, и считывать данные из регистра TCCR1B в любой момент? Будет ли плата стабильно регистрировать сигналы независимо от загрузки процессора?

Не могли бы прояснить:

1) Сколько сигналов вместит этот регистр, прежде чем будете переполнен?

2) К каким пинам подключить 3 оптосеносора на МЕГА2560 (нашел только Т0 и Т5 на выводах)

3) Каким оператором считать значения из регистра TCCR1B?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

dmitry_k пишет:

dimax  , правильно ли я понял, Вы предлагаете подключить оптосенсор к пину 11 (Т1) платы Uno, и считывать данные из регистра TCCR1B в любой момент? Будет ли плата стабильно регистрировать сигналы независимо от загрузки процессора?

Вы не справитесь, у вас уже в первом вопросе 2 ошибки от невнимательности прочтения всего несколько строк.   Результат счёта накапливается в регистре TCNT1, а TCCR1B -конфигурационный регистр. Вход на плате UNO -5 пин. Для меги это будет TCCR5B и TCNT5, какой номер  пина не помню.

dmitry_k
Offline
Зарегистрирован: 13.10.2017

 dimax 

1) Сколько сигналов вместит регистр TCNT1, прежде чем будете переполнен?

2) Возможно ли реализовать считывание с 3 сенсоров на МЕГА2560 Вашим методом (если да, то какие пины для подключения сенсоров)?

3) Каким оператором считать значения из регистра накопления (TCNT1)?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

dmitry_k, (1) "сколько сигналов" -не очень корректная фраза. Счётчик в данном случае считает входящие импульсы. Максимум 16-битного счётчика как не трудно догадаться 65535. (2) мега2560 хоть и распологает 4-мя 16-битными таймерами, но тактовый вход выведен на гребёнку только с пятого таймера. Остаётся либо припаиваться прямо к ногам МК, что есть ювелирная работа,  либо брать плату где выведены на разъём все пины контроллера. такие платы есть в продаже, но подороже. (3)  '='

dmitry_k
Offline
Зарегистрирован: 13.10.2017

brokly, возможно, за 2 дня я извлеку из предложенной библиотеки подходящий код, чтобы вставить его в свою программу. Но никак не за 2 минуты))

хорошо, если dimax объяснит "на пальцах": можно ли подключить 3 сенсора, будет ли плата стабильно регистрировать сигналы независимо от загрузки процессора, сколько сигналов вместит этот регистр, прежде чем будете переполнен и каким оператором считать значения.

Пока вникаю в Ваш вариант и вариант andriano.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

да вот еще проблема. У вас энкодер то оптический, на один выход  0-1-0-1-0-1-0-1-0 это выдаваяемая последовательность. Если мы прибавляем 1 при движении вперед с перехода 0-1, то при движении назад мы должны отнимать 1 при переходе 1-0 . Иначе у вас будет накапливающие ошибки при позиционировании.

ПС: можно конечно прибавлять и отнимать при любом переходе .и при любом движении(вперед или назад) Тогда точность позиционирования увеличится.

dmitry_k
Offline
Зарегистрирован: 13.10.2017

Хорошо). А каковы преимущества предложенного метода по сравнению с Digital.Read() или чтению битов по регистрам (PINC == (PINC | B01000000))? - Скорость выполнения функции? Автономность работы от основного кода?

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

dmitry_k, да и ещё важный момент - обработка методом тактирования ловит только полный импульс. т.е. переход 1-0-1, если нужно ловить половинки импульса -тогда нужно использовать внешние прерывания.

dmitry_k
Offline
Зарегистрирован: 13.10.2017
Здравствуйте, уважаемые участники форума.
 
Хочу сообщить, что мне, наконец, удалось решить озвученную выше проблему с пропуском сигналов. Для этого была поставлена задача вычистить свой код. Прежде всего я исбавился от передачи сигналов на LCD дисплей на важном участке кода (до этого информация на дисплее регулярно обновлялась раз в секунду). Как оказалось, библиотека LiquidCrystal_I2C.h очень сильно снижает быстродействие, поскольку в ней используются прерывания delay().
Также я избавился от команд digitalWrite() и digitalRead(), использующих системные библиотеки, и заменил их на прямое управление регистрами.
К сожалению, я не смог воспользоваться советом dimax (повесить датчики на тактовый вход таймера). И также решил не применять внешние прерывания, предложенные andriano. Весьма признателен им за участие.
 
Сейчас дописываю функцию прямого управления регистрами вместо analogWrite().
dmitry_k
Offline
Зарегистрирован: 13.10.2017
И опять нужна ваша помощь:
Мне трудно объяснить это простым языком - как мне присвоить переменной имя регистра (например, char Registor = OCR1B;), чтобы далее несколько раз вставлять эту переменную (в качестве имени регистра) и еще присваивать значение (не переменной, а регистру!!):
char Registor = OCR1B;
...
Registor = B00010000; // а нужно, чтобы OCR1B = B00010000
...
Registor = B00000111; // а нужно, чтобы OCR1B = B00000111
и.т.д.

 

 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015
#define Registor OCR1B

 

dmitry_k
Offline
Зарегистрирован: 13.10.2017

Вот, я не все объяснил. Требуется присвоить внутри класса:

class ChangePin	{
	public:
	char Registor;
	boolean i;
	void (char Registor, boolean i)	{
		if (!i)	{	Registor = B00010000	}	// нужно исправить ошибку в присвоении переменной Registor
		else	{	Registor = B00000111; 	}
	}
};

ChangePin Number1 (OCR1B, false);	//	нужно OCR1B = B00010000
ChangePin Number1 (OCR0A, true);	//	нужно OCR0A = B00000111

Не знаю, как это сделать. 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

dmitry_k пишет:

Вот, я не все объяснил. Требуется присвоить внутри класса:

class ChangePin	{
	public:
	char Registor;
	boolean i;
	void (char Registor, boolean i)	{
		if (!i)	{	Registor = B00010000	}	// нужно исправить ошибку в присвоении переменной Registor
		else	{	Registor = B00000111; 	}
	}
};

ChangePin Number1 (OCR1B, false);	//	нужно OCR1B = B00010000
ChangePin Number1 (OCR0A, true);	//	нужно OCR0A = B00000111

Не знаю, как это сделать. 

Каждый регистр - это просто адрес, посмотрите, как определена _SFR_IO16 в файле sfr_defs.h, и как через _SFR_IO16 определены регистры, тот же OCR1B ;) Можно просто передавать указатель на этот адрес, навскидку:

class ChangePin	{
	public:
	ChangePin(uint16_t* Registor, boolean i)	{
		if (!i)	{	*Registor = B00010000	}
		else	{	*Registor = B00000111; 	}
	}
};
ChangePin Number1 (&OCR1B, false);	//	нужно OCR1B = B00010000
ChangePin Number1 (&OCR0A, true);	//	нужно OCR0A = B00000111

 

dmitry_k
Offline
Зарегистрирован: 13.10.2017

Есть! Работает)...  Спасибо Вам, DIYMan.

Вы мне очень помогли!