;=============================================================================
;Контроллер PIC18F252. Управление шагающим конвейером.
;(c) http://mcprogramming.ru/, 2017 год
;=============================================================================

;Предельное время срабатывания концевых выключателей
ID_OffTime	EQU	.3		;Время [с], за которое должен разомкнуться концевик при начале движения
ID_OnTime	EQU	.60		;Время [c], за которое должен замкнуться концевик в конце движения

;Длительность включения реле выдачи заготовки РВ [100 мс]
RVOnTime	EQU	.100		;10 с

;=============================================================================

;ПОДКЛЮЧЕНИЕ ПЕРИФЕРИИ:

;Датчик температуры (модуль DS18B20.inc)
#define		DS18B20_DQ_LAT	LATA,0,0	;Вывод (для записи)
#define		DS18B20_DQ_PORT	PORTA,0,0	;Вывод (для чтения)
#define		DS18B20_DQ_TRIS	TRISA,0,0	;Направление вывода

;Кнопки
#define		Input_KC	PORTA,2,0	;Вход  (0 = нажата кнопка "Цикл")
#define		Input_KR	PORTA,3,0	;Вход  (0 = нажата кнопка "Режим")
#define		Input_KS	PORTA,4,0	;Вход  (0 = нажата кнопка "Стоп")

;Индуктивные датчики
#define		Input_ID1_Down	PORTC,0,0	;Вход  (0 = достигнуто)
#define		Input_ID2_Up	PORTC,1,0	;Вход  (0 = достигнуто)
#define		Input_ID3_Right	PORTC,2,0	;Вход  (0 = достигнуто)
#define		Input_ID4_Left	PORTC,3,0	;Вход  (0 = достигнуто)
#define		Input_ID_Port		PORTC	;Порт входов Input_ID*
#define		Input_ID1_Down_Mask	0x01	;Маски входов для обращения через порт
#define		Input_ID2_Up_Mask	0x02
#define		Input_ID3_Right_Mask	0x04
#define		Input_ID4_Left_Mask	0x08

;Соленоиды
#define		Output_1S_Up	LATC,4,0	;Выход (1 = движение при включенном Output_5S_Oil)
#define		Output_2S_Down	LATC,5,0	;Выход (1 = движение при включенном Output_5S_Oil)
#define		Output_3S_Left	LATC,6,0	;Выход (1 = движение при включенном Output_5S_Oil)
#define		Output_4S_Right	LATC,7,0	;Выход (1 = движение при включенном Output_5S_Oil)
#define		Output_5S_Oil	LATB,0,0	;Выход (1 = подача давления масла на 1С..4С)

;Управление реле выдачи заготовки из печи, электродвигателем маслостанции РД и питанием РП
#define		Output_RV	LATB,1,0	;Выход (1 = включение привода выдачи заготовки РВ)
#define		Output_RD	LATB,2,0	;Выход (1 = включение электродвигателя маслостанции РД)
#define		Input_RP	PORTB,3,0	;Вход  (1 = аварийное отключение питания РП)

;Индикация
#define		Output_IU	LATB,4,0	;Выход (1 = горит)
#define		Output_IT	LATB,5,0	;Выход (1 = горит)

;Управление сигналом ("тревога") и реле контроля горелки РГ
#define		Output_T	LATB,6,0	;Выход (1 = включение сигнала тревоги Т)
#define		Output_RG	LATB,7,0	;Выход (1 = включение реле контроля горелки РГ)

;Неиспользуемые выводы
#define		Debug_RA1	PORTA,1,0	;Отладочный вывод (резерв под термопару)
#define		Debug_RA5	PORTA,5,0	;Отладочный вывод

;=============================================================================

;ИСПОЛЬЗОВАНИЕ РЕСУРСОВ МИКРОКОНТРОЛЛЕРА:

;TMR1		- рабочий таймер для периодических прерываний

;=============================================================================

	#include "Units\Header.inc"
	#include "D:\Working\PIC\Units\18\AddInstr.inc"

;===== КОНСТАНТЫ И ПЕРЕМЕННЫЕ ================================================

;Переменные
		CBLOCK	0x000
		  Flags1,Flags2:	1	;Флаги
		  DevSost:		1	;Состояние устройства (значения ниже)
		  TempBlink_10ms:	1	;Полупериод мигания индикатора ИТ [10 мс] (0 = горит постоянно)
		  Tmp1,Tmp2:		1	;Временные переменные

		  ;Переменные прерывания
		  Count10ms:		1	;Счётчик для отсчёта интервалов по 10 мс
		  Count100ms:		1	;Счётчик для отсчёта интервалов по 100 мс
		  Count1s:		1	;Счётчик для отсчёта интервалов по 1 с
		  DecEach1ms:		1	;Уменьшается на 1 каждую 1 мс до нуля (устанавливается программой)
		  DecEach10ms:		1	;Уменьшается на 1 каждые 10 мс до нуля (устанавливается программой)
		  DecEach10ms_IT:	1	;Уменьшается на 1 каждые 10 мс до нуля для управления ИТ
		  IncEach10ms_RDStart:	1	;Увеличивается на 1 каждые 10 мс от 0 до 255 после включения РД
		  IncEach10ms_PwrOn:	1	;Увеличивается на 1 каждые 10 мс от 0 до 255 после включения питания
		  IncEach10ms_T:	1	;Увеличивается на 1 каждые 10 мс до 255 для управления T (сбрасывается программой)
		  DecEach100ms:		1	;Уменьшается на 1 каждые 100 мс до нуля (устанавливается программой)
		  DecEach100ms_Sound:	1	;Уменьшается на 1 каждые 100 мс до нуля для генерации звука (устанавливается программой)
		  DecEach100ms_RV:	1	;Уменьшается на 1 каждые 100 мс до нуля для управления РВ (устанавливается программой)
		  IncEach100ms_KR:	1	;Увеличивается на 1 каждые 100 мс до 255 для контроля длинного нажатия КР
		  IncEach100ms_KS:	1	;Увеличивается на 1 каждые 100 мс до 255 для контроля длинного нажатия КС
		  DecEach1s:		1	;Уменьшается на 1 каждую 1 с до нуля (устанавливается программой)
		  DecEach1s_T:		1	;Уменьшается на 1 каждую 1 с до нуля для контроля температуры (устанавливается программой)
		  IncEach1s_AU:		1	;Увеличивается на 1 каждую 1 с до 255 для отсчёта задержки запуска при А30/А60
		  ITmp:			1	;Временная переменная
		ENDC

;Флаги переменной Flags1
#define		Mode_AU			Flags1,0,0	;Автоматическое управление (0=РУ, 1=АУ/А30/А60)
#define		Mode_A30		Flags1,1,0	;При Mode_AU=1: Режим А30
#define		Mode_A60		Flags1,2,0	;При Mode_AU=1: Режим А60
#define		NeedStart		Flags1,3,0	;Требуется немедленный запуск
#define		NeedStop		Flags1,4,0	;Требуется немедленный останов
#define		StoppedNotInHome	Flags1,5,0	;Произведён останов вне "Дома"
#define		RDIsOn			Flags1,6,0	;Требуется включение двигателя РД

;Флаги переменной Flags2
#define		Trevoga			Flags2,0,0	;Включение общей тревоги по любому из флагов (общий флаг)
#define		Trevoga_Water		Flags2,1,0	;Включение тревоги по температуре воды
#define		Trevoga_IDs		Flags2,2,0	;Включение тревоги по неисправности индуктивных датчиков
#define		Trevoga_KS		Flags2,3,0	;Включение тревоги по нажатию КС
#define		KeyKRWasPressed3s	Flags2,6,0	;Кнопка "КР" была нажата 3 секунды (уст. прерыванием, сбрас. программой)
#define		KeyKSWasPressed3s	Flags2,7,0	;Кнопка "КС" была нажата 3 секунды (уст. прерыванием, сбрас. программой)

;Значения переменной DevSost
;0x00	"Дом"
;0x10	начато движение вверх
;0x20	начато движение влево
;0x30	начато движение вниз
;0x40	начато движение вправо
;0xFF	не определено

;===== ПОДПРОГРАММЫ ==========================================================

KeysChangeDelay	EQU	.6		;Задержка антидребезговой защиты кнопок [мс]

;Чтение аппаратного состояния кнопок в W
;Длительность 11 тактов вместе с вызовом
ReadTekKeysToW:	clrw
		If_0	Input_KC
		  bsw	0
		If_0	Input_KR
		  bsw	1
		If_0	Input_KS
		  bsw	2
		return

#define		KeyKCIsPressed		FixKeys,0,0	;Кнопка КЦ сейчас нажата
#define		KeyKRIsPressed		FixKeys,1,0	;Кнопка КР сейчас нажата
#define		KeyKSIsPressed		FixKeys,2,0	;Кнопка КС сейчас нажата
#define		KeyKCWasPressed		NewKeys,0,0	;Зафиксировано новое нажатие кнопки КЦ
#define		KeyKRWasPressed		NewKeys,1,0	;Зафиксировано новое нажатие кнопки КР
#define		KeyKSWasPressed		NewKeys,2,0	;Зафиксировано новое нажатие кнопки КС

	#include "d:\working\pic\units\18\Flash.inc"
	#include "d:\working\pic\units\18\Keys8.inc"
	#include "d:\working\pic\units\18\DS18B20.inc"

;===== ПРЕРЫВАНИЕ ============================================================

;Прерывание здесь происходит только от TMR1 каждую 1 мс (10000 тактов).
;Любой фронт прерывания может запоздать на 6..70 мкс за счёт запрета прерываний модулем DS18B20.inc,
;но на положении следующего за ним фронта прерывания это не сказывается (средний период не меняется).
;Прерывание отвечает за:
;	1. Отсчёт интервалов по 1 мс, 10 мс, 100 мс, 1 с.
;	2. Обработку клавиатуры.
;	3. Управление индикаторами управления ИУ (Output_IU) и температуры ИТ (Output_IT).
;	4. Управление звуковым сигналом "Тревога" (Output_T).
;	5. Управление реле выдачи заготовки из печи (Output_RV).
;	6. Управление электродвигателем маслостанции (Output_RD).
;Сохранение контекста (WREG, BSR, STATUS) происходит аппаратно

Intr:		;*** Вход в прерывание (4 такта переход + 9 тактов перезапуск) ***
		bcf	PIR1,TMR1IF,0		;Перезапуск таймера: сброс флага прерывания
		movff	TMR1L,ITmp		;Чтение TMR1_2 в TMR1H:ITmp (старший байт в TMR1H аппаратно за счёт RD16=1)
		addlw2f	0xD8F8,TMR1H,ITmp,0	;1 мс = 0x2710 циклов, -0x2710=0xD8F0, +8 на прибавление (проверено!) = 0xD8F8
		movff	ITmp,TMR1L		;Запись TMR1H:ITmp в TMR1_2 (старший байт из TMR1H аппаратно за счёт RD16=1)

Intr_TimeCount:	;***** Отсчёт временных интервалов *****
		If_fNE0	DecEach1ms,0		;Действия каждую 1 мс: Уменьшаем DecEach1ms, если ещё не достигли нуля
		  decf	DecEach1ms,F,0
		decf	Count10ms,F,0		;Нарезка 10 мс
		bnz	_ITC_Exit
		mov_lwf	.10,Count10ms,0
		If_fNE0	DecEach10ms,0		;Действия каждые 10 мс: Уменьшаем DecEach10ms, если ещё не достигли нуля
		  decf	DecEach10ms,F,0
		If_fNE0	DecEach10ms_IT,0		;Уменьшаем DecEach10ms_IT, если ещё не достигли нуля
		  decf	DecEach10ms_IT,F,0
		incfsz	IncEach10ms_RDStart,W,0		;Увеличиваем IncEach10ms_RDStart, если ещё не достигли 255
		  incf	IncEach10ms_RDStart,F,0
		incfsz	IncEach10ms_PwrOn,W,0		;Увеличиваем IncEach10ms_PwrOn, если ещё не достигли 255
		  incf	IncEach10ms_PwrOn,F,0
		incfsz	IncEach10ms_T,W,0		;Увеличиваем IncEach10ms_T, если ещё не достигли 255
		  incf	IncEach10ms_T,F,0
		decf	Count100ms,F,0		;Нарезка 100 мс
		bnz	_ITC_Exit
		mov_lwf	.10,Count100ms,0
		If_fNE0	DecEach100ms,0		;Действия каждые 100 мс: Уменьшаем DecEach100ms, если ещё не достигли нуля
		  decf	DecEach100ms,F,0
		If_fNE0	DecEach100ms_Sound,0		;Уменьшаем DecEach100ms_Sound, если ещё не достигли нуля
		  decf	DecEach100ms_Sound,F,0
		If_fNE0	DecEach100ms_RV,0		;Уменьшаем DecEach100ms_RV, если ещё не достигли нуля
		  decf	DecEach100ms_RV,F,0
		incfsz	IncEach100ms_KR,W,0		;Увеличиваем IncEach100ms_KR, если ещё не достигли 255
		  incf	IncEach100ms_KR,F,0
		incfsz	IncEach100ms_KS,W,0		;Увеличиваем IncEach100ms_KS, если ещё не достигли 255
		  incf	IncEach100ms_KS,F,0
		decf	Count1s,F,0		;Нарезка 1 с
		bnz	_ITC_Exit
		mov_lwf	.10,Count1s,0
		If_fNE0	DecEach1s,0		;Действия каждую 1 с: Уменьшаем DecEach1s, если ещё не достигли нуля
		  decf	DecEach1s,F,0
		If_fNE0	DecEach1s_T,0			;Уменьшаем DecEach1s_T, если ещё не достигли нуля
		  decf	DecEach1s_T,F,0
		incfsz	IncEach1s_AU,W,0		;Увеличиваем IncEach1s_AU, если ещё не достигли 255
		  incf	IncEach1s_AU,F,0
_ITC_Exit:	;Конец отсчёта временных интервалов

_Intr_Keys:	;***** Обработка клавиатуры *****
		Keys_Intr			;Обработка (10..14 тактов + 11 тактов ReadTekKeysToW)
		If_0	KeyKRIsPressed		;Детектор длинного нажатия кнопки КР
		  clrf	IncEach100ms_KR
		cmp_lwf	.30,IncEach100ms_KR,0
		If_Z
		  bsf	KeyKRWasPressed3s
		If_0	KeyKSIsPressed		;Детектор длинного нажатия кнопки КС
		  clrf	IncEach100ms_KS
		cmp_lwf	.30,IncEach100ms_KS,0
		If_Z
		  bsf	KeyKSWasPressed3s
_IK_Exit:	;Конец обработки клавиатуры

_Intr_LedIU:	;***** Управление индикатором ИУ *****
		If_1	StoppedNotInHome	;Останов вне "дома" => Гашение
		  bra	_ILIU_Off
		If_0	Mode_AU			;Режим РУ => Горение
		  bra	_ILIU_On
		;Автоматическое управление
		If_fNE0	DevSost,0		;Не в "Доме" => Всегда мигание 1 Гц
		  bra	_ILIU_Bl1
		movlw	.30-.3			;Длительность ожидания минус 3 [с]
		If_1	Mode_A60
		  movlw	.60-.3
		subwf	IncEach1s_AU,W,0	;Сравнение с прошедшим временем задержки (как cmp_lwf)
		b2lt1	_ILIU_Bl1		;Прошедшее время не превышает задержку => Мигание с частотой 1 Гц
_ILIU_Bl5:	If_1	Count1s,0,0		;Мигание с частотой 5 Гц
		  bra	_ILIU_On
		bra	_ILIU_Off
_ILIU_Bl1:	movlw	.8			;Мигание с частотой 1 Гц: Момент гашения в зависимости от режима
		If_1	Mode_A30
		  movlw	.6
		If_1	Mode_A60
		  movlw	.4
		subwf	Count1s,W,0			;Момент достигнут (как cmp_lwf) => Гашение (иначе горение)
		b2lt1	_ILIU_Off
_ILIU_On:	bsf	Output_IU		;Включение индикатора ИУ
		bra	_ILIU_Exit
_ILIU_Off:	bcf	Output_IU		;Выключение индикатора ИУ
_ILIU_Exit:	;Конец управления индикатором ИУ

_Intr_LedIT:	;***** Управление индикатором ИТ *****
		testf	TempBlink_10ms,0	;Полупериод=0 => Горит постоянно
		bz	_ILIT_On
		If_fNE0	DecEach10ms_IT,0	;Мигание: Ожижание смены не истекло => Ждём
		  bra	_ILIT_Exit
		movff	TempBlink_10ms,DecEach10ms_IT	;Перезапуск ожидания
		btg	Output_IT			;Смена состояния индикатора ИТ
		bra	_ILIT_Exit
_ILIT_On:	bsf	Output_IT		;Включение индикатора ИТ
_ILIT_Exit:	;Конец управления индикатором ИТ

Intr_Sound:	;***** Управление звуковым сигналом "Тревога" *****
		If_0	Trevoga			;Есть флаг тревоги => Включение
		  If_1	DecEach100ms_Sound,1,0	;Есть прерывистый звуковой сигнал (2.5 Гц) => Включение
		  bra	_IS_On
_IS_Off:	bcf	Output_T		;Сигнала "Тревога" нет
		bra	_IS_Exit
_IS_On:		bsf	Output_T		;Сигнал "Тревога" есть
_IS_Exit:	;Конец управления звуковым сигналом

_Intr_RV:	;***** Управление реле выдачи заготовки РВ *****
		testf	DecEach100ms_RV,0	;Идёт отсчёт работы реле => Включение, иначе выключение
		If_NZ
		  bsf	Output_RV
		If_Z
		  bcf	Output_RV
_IRV_Exit:	;Конец управления реле РВ

_Intr_RD:	;***** Управление реле электродвигателя маслостанции РД *****
		cmp_lwf	.100,IncEach10ms_PwrOn,0 ;Идёт отсчёт задержки включения => Выключение
		  b2lt1	_IRD_Off
		If_1	RDIsOn			;Должен быть включен => Включение электродвигателя
		  bra	_IRD_On
_IRD_Off:	bcf	Output_RD		;Выключение
		clrf	IncEach10ms_RDStart,0	;Инициализация отсчёта времени после включения РД
		bra	_IRD_Exit
_IRD_On:	bsf	Output_RD		;Включение
_IRD_Exit:	;Конец управления реле РД

_Int_Exit:	retfie	1			;Аппаратное восстановление контекста и выход из прерывания

;Длительность нахождения в прерывании (в тактах):
;15 - переход (4), перезапуск TMR1 (9), выход (2)
;5..40 - отсчёт временных интервалов
;33..37 - обработка клавиатуры
;4..22 - управление индикатором ИУ
;4..9 - управление индикатором ИТ
;5..6 - управление сигналом "Тревога"
;5 - управление РВ
;7..10 - управление РД
;ИТОГО максимальное время нахождения в прерывании: 15+40+37+22+9+6+5+10=144 такта (14.4 мкс)

;===== РАБОЧИЕ КОМАНДЫ И ПОДПРОГРАММЫ ========================================

;Отключение автоматического управления
StopAU:		bcf	Mode_AU			;Сброс всех флагов автоматического управления
		bcf	Mode_A30
		bcf	Mode_A60
		return

;Запись состояния в энергонезависимую память
SaveDevSost:	clrf	EEADR,0			;Адрес байта с состоянием
		FlashWriteFrom DevSost,0	;Запись
		return

;===== ОСНОВНАЯ ЧАСТЬ ПРОГРАММЫ ==============================================

Main:		;*** Инициализация портов ввода-вывода ***
		movlb	0			;Банк 0
		mov_lwf	0x07,ADCON1,0		;Настройка PORTA, PORTE как цифровых
		mov_lwf	0x01,LATA,0		;Начальное состояние выходов: DS18B20_DQ=1, Debug_RA1=0, Debug_RA5=0
		clrf	LATB,0				;Output_5S, Output_RV, Output_RD, Output_T, Output_RG = 0, индикация выключена
		clrf	LATC,0				;Output_1S..4S=0 (движения нет)
		mov_lwf	0x3F,TRISA,0		;Направление выводов: все входы, в т.ч. DS18B20_DQ, Debug_RA1 и Debug_RA5
		mov_lwf	0x08,TRISB,0			;Input_RP - вход, Output_5S, Output_RV, Output_RD, Output_T, Output_RG, инд. - выходы
		mov_lwf	0x0F,TRISC,0			;Input_ID1..4 - входы, Output_1S..4S - выходы
		FlashInit			;Инициализация работы с EEPROM-памятью
		KeyboardInit			;Инициализация работы кнопок
		DS18B20Init			;Инициализация работы измерителя температуры DS18B20
		DS18B20Set9bit			;Задание разрешения DS18B20 9 бит (преобразование 93.75 мс)

		;*** Настройка прерываний по таймеру ***
		movlw2f	0xD8F0,TMR1_2		;Первое прерывание через 1 мс (0x2710 циклов): -0x2710=0xD8F0
		mov_lwf	0x81,T1CON,0		;Настройка TMR1: включен, RD16=1, предделитель 1:1
		bsf	PIE1,TMR1IE,0		;Разрешение прерываний от таймера TMR1
		bsf	INTCON,PEIE,0
		bsf	INTCON,GIE,0
		mov_lwf	.1,Count10ms,0		;Инициализация нарезки временных интервалов
		mov_lwf	.1,Count100ms,0
		mov_lwf	.1,Count1s,0
		clrf	DecEach100ms_Sound,0	;Звук не генерируется
		clrf	DecEach10ms_IT,0	;Состояние индикатора ИТ не меняется
		clrf	IncEach10ms_RDStart,0	;Инициализация отсчёта времени после включения РД
		clrf	IncEach10ms_PwrOn,0	;Отсчёт времени после включения питания
		clrf	DecEach100ms_RV,0	;Реле РВ выключено
		clrf	IncEach100ms_KR,0	;Сброс отсчёта длинного нажатия кнопок
		clrf	IncEach100ms_KS,0

		;*** Подготовка к запуску цикла
		clrf	Flags1,0		;Сброс флагов
		clrf	Flags2,0
		clrf	EEADR,0			;Чтение состояния
		FlashReadTo DevSost,0
		clrf	TempBlink_10ms,0	;Пока нет значения температуры, индикатор ИТ горит постоянно
		rcall	_FCT_RunConvT		;Старт нового преобразования температуры
		incf	DecEach1s_T,F,0		;Увеличение паузы на 1 секунду для привязки к временной сетке
		bsf	RDIsOn			;Изначально включение электродвигателя маслостанции
		movfw	DevSost,0		;Переход по состоянию
		bz	Home				;0x00 - "Дом"
		If_0	DevSost,7,0			;Не "Дом", но состояние определено => Останов вне "Дома"
		  bra	Ostanov
		If_0	Input_ID1_Down			;ID1=ID3=0, ID2=ID4=1 => Считаем, что в "Доме"
		  If_1	Input_ID3_Right
		  bra	StepUndefined
		If_1	Input_ID2_Up
		  If_0	Input_ID4_Left
		  bra	StepUndefined
		bra	Home
StepUndefined:	mov_lwf	0x30,DevSost,0			;Состояние не определено => Считаем, что требуется возврат в "Дом"
		bra	Ostanov

;***** ОСНОВНОЙ ЦИКЛ РАБОТЫ **************************************************

Home:		;***** Положение "Дом" *****
		clrf	DevSost,0		;Код состояния
		bcf	NeedStart		;Сброс флагов, установленных до входа в состояние
		bcf	NeedStop
		clrf	IncEach1s_AU		;Сброс отсчёта задержки АУ с момента входа в состояние "Дом"
_H_Wait:	;Ожидание событий
		rcall	FoneControl		;Фоновая процедура (заполняет NeedStart, NeedStop)
		bcf	NeedStop		;Флаг "Требуется останов" игнорируется
		If_0	Mode_AU			;Ручной режим => Автоматический старт не осуществляется
		  bra	_H_NoAUStart
		clrw				;АУ => Определение необходимости старта: Длительность задержки в W [c]
		If_1	Mode_A30
		  movlw	.30
		If_1	Mode_A60
		  movlw	.60
		subwf	IncEach1s_AU,W,A		;Сравнение с прошедшим временем задержки (то же что cmp_lwf "=W",IncEach1s_AU,0)
		If_2GE1					;Прошедшее время превышает задержку => Требуется запуск цикла
		  bsf	NeedStart
_H_NoAUStart:	If_1	NeedStart		;Требуется запуск => Запуск
		  bra	GoUp
		bra	_H_Wait			;Ожидание
		;Конец положения "Дом"

GoUp:		;***** Движение вверх *****
		mov_lwf	0x10,DevSost,0		;Код состояния
		bcf	NeedStart		;Сброс флагов, установленных до входа в состояние
		bcf	NeedStop
		If_0	Input_ID2_Up		;Положение уже достигнуто (выполнение было прервано ранее) => Пропуск шага
		  bra	GoLeft
		rcall	WaitRD			;Ожидание готовности маслостанции
		If_1	NeedStop		;Требуется останов => Выполнение
		  bra	Ostanov
		bsf	Output_1S_Up		;Выбор направления
		mov_lwf	Input_ID1_Down_Mask,Tmp1,0 ;Маска концевика, который должен разомкнуться
		mov_lwf	Input_ID2_Up_Mask,Tmp2,0   ;Маска концевика, который должен замкнуться
		rcall	Move			;Движение в выбранном направлении
		If_1	NeedStop		;Требуется останов => Выполнение
		  bra	Ostanov
		;Конец движения вверх

GoLeft:		;***** Движение влево *****
		mov_lwf	0x20,DevSost,0		;Код состояния
		bcf	NeedStart		;Сброс флагов, установленных до входа в состояние
		bcf	NeedStop
		If_0	Input_ID4_Left		;Положение уже достигнуто (выполнение было прервано ранее) => Пропуск шага
		  bra	GoDown
		rcall	WaitRD			;Ожидание готовности маслостанции
		If_1	NeedStop		;Требуется останов => Выполнение
		  bra	Ostanov
		bsf	Output_3S_Left		;Выбор направления
		mov_lwf	Input_ID3_Right_Mask,Tmp1,0 ;Маска концевика, который должен разомкнуться
		mov_lwf	Input_ID4_Left_Mask,Tmp2,0  ;Маска концевика, который должен замкнуться
		rcall	Move			;Движение в выбранном направлении
		If_1	NeedStop		;Требуется останов => Выполнение
		  bra	Ostanov
		;Конец движения влево

GoDown:		;***** Движение вниз *****
		mov_lwf	0x30,DevSost,0		;Код состояния
		bcf	NeedStart		;Сброс флагов, установленных до входа в состояние
		bcf	NeedStop
		If_0	Input_ID1_Down		;Положение уже достигнуто (выполнение было прервано ранее) => Пропуск шага
		  bra	GoRight
		rcall	WaitRD			;Ожидание готовности маслостанции
		If_1	NeedStop		;Требуется останов => Выполнение
		  bra	Ostanov
		mov_lwf	RVOnTime,DecEach100ms_RV,0 ;Включаем реле выдачи заготовки РВ на заданное время
		movlw	.12			;В режиме АУ запускаем 3 звуковых сигнала (12 - чтобы значение 11 не было обгрызено)
		If_1	Mode_AU
		  movwf	DecEach100ms_Sound,0
		bsf	Output_2S_Down		;Выбор направления
		mov_lwf	Input_ID2_Up_Mask,Tmp1,0   ;Маска концевика, который должен разомкнуться
		mov_lwf	Input_ID1_Down_Mask,Tmp2,0 ;Маска концевика, который должен замкнуться
		rcall	Move			;Движение в выбранном направлении
		If_1	NeedStop		;Требуется останов => Выполнение
		  bra	Ostanov
		;Конец движения вниз

GoRight:	;***** Движение вправо *****
		mov_lwf	0x40,DevSost,0		;Код состояния
		bcf	NeedStart		;Сброс флагов, установленных до входа в состояние
		bcf	NeedStop
		If_0	Input_ID3_Right		;Положение уже достигнуто (выполнение было прервано ранее) => Пропуск шага
		  bra	Home
		rcall	WaitRD			;Ожидание готовности маслостанции
		If_1	NeedStop		;Требуется останов => Выполнение
		  bra	Ostanov
		bsf	Output_4S_Right		;Выбор направления
		mov_lwf	Input_ID4_Left_Mask,Tmp1,0  ;Маска концевика, который должен разомкнуться
		mov_lwf	Input_ID3_Right_Mask,Tmp2,0 ;Маска концевика, который должен замкнуться
		rcall	Move			;Движение в выбранном направлении
		If_1	NeedStop		;Требуется останов => Выполнение
		  bra	Ostanov
		;Конец движения вправо

		;***** Завершение цикла *****
		If_0	Mode_AU			;Режим РУ => Выключение электродвигателя маслостанции РД
		  bcf	RDIsOn
		bra	Home			;Цикл окончен, переход в состояние "Дом"

Ostanov:	;***** Останов вне положения "Дом" (DevSost<>0) *****
		bsf	StoppedNotInHome	;Произведён останов вне "Дома"
		rcall	StopAU			;Отключение автоматического управления
		bcf	NeedStart		;Сброс флагов, установленных до входа в состояние
		bcf	NeedStop
		rcall	SaveDevSost		;Запись состояния в энергонезависимую память
		;Останов любого движения
		bcf	Output_5S_Oil		;Отключаем давление масла
		bcf	Output_1S_Up		;Отключаем все направления движения
		bcf	Output_2S_Down
		bcf	Output_3S_Left
		bcf	Output_4S_Right
		mov_lwf	.100,DecEach10ms,0	;Пауза 1 с после отключения всех соленоидов
_O_Pause:	rcall	FoneControl			;Фоновая процедура (заполняет NeedStart, NeedStop)
		bcf	NeedStart			;Флаг "Требуется запуск" игнорируется
		bcf	NeedStop			;Флаг "Требуется останов" игнорируется
		If_fNE0	DecEach10ms,0			;Ожидание завершения паузы
		  bra	_O_Pause
_O_Wait:	;Ожидание событий
		rcall	FoneControl		;Фоновая процедура (заполняет NeedStart, NeedStop)
		bcf	NeedStop		;Флаг "Требуется останов" игнорируется, т.к. уже итак останов
		testf	DevSost,0		;Состояние было сброщено кнопкой => Выход
		bz	_O_End
		If_0	NeedStart		;Запуск не требуется => Ожидание
		  bra	_O_Wait
_O_End:		bcf	StoppedNotInHome	;Останов вне "Дома" закончен
		swapf	DevSost,W,0		;Запуск по состоянию: Код состояния => 0..7 (используется 0..4, остальное на всякий случай)
		andlw	0x07
		bz	Home				;0 (0x00) = "дом"
		addlw	-.2
		bn	GoUp				;1 (0x10) = движение вверх
		bz	GoLeft				;2 (0x20) = движение влево
		addlw	-.2
		bn	GoDown				;3 (0x30) = движение вниз
		bz	GoRight				;4 (0x40) = движение вправо
		bra	GoDown				;Другие значения => Считаем, что требуется возврат в "дом"
		;Конец останова вне положения "Дом"

;***** ПРОЦЕДУРЫ ДВИЖЕНИЯ ****************************************************

;Ожидание готовности маслостанции
WaitRD:		bsf	RDIsOn			;На всякий случай - включение электродвигателя маслостанции
_WRD_Wait:	rcall	FoneControl		;Фоновая процедура (заполняет NeedStart, NeedStop)
		bcf	NeedStart		;Флаг "Требуется запуск" игнорируется
		If_1	NeedStop		;Требуется останов => Выход (обработка флага там)
		  return
		cmp_lwf	.100,IncEach10ms_RDStart,0 ;Пауза 1 с после пуска не истекла => Ждём
		b2lt1	_WRD_Wait
		return

;Начало движения в выбранном направлении.
;На входе: Tmp1 - маска концевика, который должен разомкнуться; Tmp2 - маска концевика, который должен замкнуться.
;Перед вызовом включить требуемый соленоид направления. На выходе проверить бит NeedStop.
;Портит DecEach10ms, DecEach100ms, DecEach1s
Move:		;Начало движения
		mov_lwf	.25,DecEach10ms,0	;Пауза 250 мс
_M_StartPause1:	rcall	FoneControl			;Фоновая процедура (заполняет NeedStart, NeedStop)
		bcf	NeedStart			;Флаг "Требуется запуск" игнорируется
		If_1	NeedStop			;Требуется останов => Выход (обработка флага там)
		  return
		If_fNE0	DecEach10ms,0			;Ожидание завершения паузы
		  bra	_M_StartPause1
		bsf	Output_5S_Oil		;Включаем давление масла
		;Ожидание размыкания первого концевика (может быть разомкнут сразу)
		mov_lwf	(ID_OffTime*10),DecEach100ms,0 ;Длительность ожидания размыкания первого концевика
_M_Wait1:	rcall	FoneControl		;Фоновая процедура (заполняет NeedStart, NeedStop)
		bcf	NeedStart		;Флаг "Требуется запуск" игнорируется
		testf	DecEach100ms,0		;Время ожидания истекло => Авария
		If_Z
		  bsf	NeedStop
		If_1	NeedStop		;Требуется останов => Выполнение
		  return
		and_ffw	Tmp1,0,Input_ID_Port,0	;Первый концевик не разомкнулся (=0) => Ждём
		bz	_M_Wait1
		;Ожидание замыкания второго концевика (может быть замкнут сразу)
		mov_lwf	ID_OnTime,DecEach1s,0	;Длительность ожидания замыкания второго концевика
_M_Wait2:	rcall	FoneControl		;Фоновая процедура (заполняет NeedStart, NeedStop)
		bcf	NeedStart		;Флаг "Требуется запуск" игнорируется
		testf	DecEach1s,0		;Время ожидания истекло => Авария
		If_Z
		  bsf	NeedStop
		If_1	NeedStop		;Требуется останов => Выполнение
		  return
		and_ffw	Tmp2,0,Input_ID_Port,0	;Второй концевик не замкнулся (=1) => Ждём
		bnz	_M_Wait2
		;Завершение движения в любом направлении
		bcf	Output_5S_Oil		;Отключаем давление масла
		bcf	Output_1S_Up		;Отключаем все соленоиды направления движения
		bcf	Output_2S_Down
		bcf	Output_3S_Left
		bcf	Output_4S_Right
		;Пауза после отключения всех соленоидов
		mov_lwf	.100,DecEach10ms,0	;Пауза 1 с
_M_StopPause2:	rcall	FoneControl			;Фоновая процедура (заполняет NeedStart, NeedStop)
		bcf	NeedStart			;Флаг "Требуется запуск" игнорируется
		If_1	NeedStop			;Требуется останов => Выход (обработка флага там)
		  return
		If_fNE0	DecEach10ms,0			;Ожидание завершения паузы
		  bra	_M_StopPause2
		return

;***** ПРОЦЕДУРЫ ФОНОВОГО КОНТРОЛЯ *******************************************

;Фоновый контроль сигналов
FoneControl:	If_1	Input_RP		;Аварийное отключение питания => Обработка
		  rcall	PowerLoss
		If_1	KeyKCWasPressed		;Нажата КЦ => Обработка
		  rcall	_FC_KeyKC
		If_1	KeyKRWasPressed		;Нажата КР => Обработка
		  rcall	_FC_KeyKR
		If_1	KeyKSWasPressed		;Нажата КС => Обработка
		  rcall	_FC_KeyKS
		If_1	KeyKRWasPressed3s	;КР была нажата 3 секунды => Обработка
		  rcall	_FC_KeyKR3s
		If_1	KeyKSWasPressed3s	;КС была нажата 3 секунды => Обработка
		  rcall	_FC_KeyKS3s
		rcall	_FC_Input_IDs		;Контроль исправности концевиков
		rcall	_FC_Temp		;Контроль температуры воды
		rcall	_FC_Trevoga		;Формирование сигнала Trevoga по флагам тревоги
		return

;***** Потеря питания *****

;Действия при потере питания
PowerLoss:	cmp_lwf	.100,IncEach10ms_PwrOn,0 ;В первую секунду реакции на сигнал нет (защита от дребезга РП при его включении)
		If_2Lt1
		  return
		rcall	StopAU			;Отключение автоматического управления
		If_fNE0	DevSost,0		;Произведён останов вне "Дома" => Флаг для индикатора ИУ
		  bsf	StoppedNotInHome
		rcall	SaveDevSost		;Запись состояния в энергонезависимую память (как можно быстрее!)
		bcf	Output_5S_Oil		;Отключаем давление масла
		bcf	Output_1S_Up		;Отключаем все соленоиды направления движения
		bcf	Output_2S_Down
		bcf	Output_3S_Left
		bcf	Output_4S_Right
		bcf	RDIsOn			;Выключение электродвигателя маслостанции
		bra	$			;Зависание
		return

;***** Кнопки *****

;Нажатие кнопки КЦ ("Цикл")
_FC_KeyKC:	bcf	KeyKCWasPressed		;Нажатие обработано
		bcf	Trevoga_KS		;Выключение сигнала "Тревога" по нажатию КС
		bsf	NeedStart		;Требуется запуск
		bsf	RDIsOn			;Включение электродвигателя маслостанции
		bra	StopAU			;Отключение автоматического управления и выход (return там)

;Нажатие кнопки КР ("Режим")
_FC_KeyKR:	bcf	KeyKRWasPressed		;Нажатие обработано
		bcf	Trevoga_KS		;Выключение сигнала "Тревога" по нажатию КС
		If_0	Mode_AU			;Переход по текущему состоянию
		  bra	_FCKR_RU
		If_1	Mode_A30
		  bra	_FCKR_A30
		If_1	Mode_A60
		  bra	_FCKR_A60
_FCKR_AU:	bsf	Mode_A30		;АУ: Переход в А30
		return
_FCKR_A30:	bcf	Mode_A30		;А30: Переход в А60
		bsf	Mode_A60
		return
_FCKR_A60:	bcf	Mode_A60		;А60: Переход в АУ
		return
_FCKR_RU:	If_0	Trevoga			;РУ: В режиме "Тревога" нажатие игнорируется
		  If_fNE0 DevSost,0			;Не в "Доме" => Нажатие игнорируется
		  return
		bsf	RDIsOn				;Включение электродвигателя маслостанции
		bsf	Mode_AU				;Переход в АУ
		return

;Нажатие кнопки КС ("Стоп")
_FC_KeyKS:	bcf	KeyKSWasPressed		;Нажатие обработано
		bsf	NeedStop		;Флаг - требуется немедленный останов
		bcf	RDIsOn			;Выключение электродвигателя маслостанции РД
		clrf	DecEach100ms_RV,0	;Выключение реле выдачи заготовки РВ
		rcall	StopAU			;Отключение автоматического управления
		rcall	SaveDevSost		;Запись состояния в энергонезависимую память
		If_fNE0	DevSost,0		;Переход по состоянию
		  If_1	StoppedNotInHome
		  bra	_FCKS_Stop
_FCKS_Move:	bsf	Trevoga_KS		;Нажатие во время движения => Включение сигнала "Тревога"
		return
_FCKS_Stop:	bcf	Trevoga_KS		;Нажатие во время остановки => Выключение сигнала "Тревога"
		return

;Длительное нажатие кнопки КР ("Режим")
_FC_KeyKR3s:	bcf	KeyKRWasPressed3s	;Нажатие обработано
		bra	StopAU			;Отключение автоматического управления и выход (return там)

;Длительное нажатие кнопки КС ("Стоп")
_FC_KeyKS3s:	bcf	KeyKSWasPressed3s	;Нажатие обработано
		If_0	StoppedNotInHome	;Конвейер не остановлен => Нажатие игнорируется
		  return
		clrf	DevSost,0		;Код состояния "дом"
		rcall	SaveDevSost		;Запись состояния в энергонезависимую память
		mov_lwf	.4,DecEach100ms_Sound,0	;Запускаем звуковой сигнал (4 - чтобы значение 3 не было обгрызено)
		return

;***** Исправность концевиков *****

;Контроль исправности концевиков
_FC_Input_IDs:	bcf	Trevoga_IDs		;Флаг тревоги по концевикам
		;Вертикальные: не могут быть оба в нуле
		and_lfw	(Input_ID2_Up_Mask+Input_ID1_Down_Mask),Input_ID_Port,0
		If_Z
		  bsf	Trevoga_IDs
		;Горизонтальные: не могут быть оба в нуле
		and_lfw	(Input_ID4_Left_Mask+Input_ID3_Right_Mask),Input_ID_Port,0
		If_Z
		  bsf	Trevoga_IDs
		If_1	Trevoga_IDs		;Есть неисправность концевиков => Требуется останов
		  bsf	NeedStop
		If_1	Trevoga_IDs		;Есть неисправность концевиков => Выключение электродвигателя маслостанции
		  bcf	RDIsOn
		return

;***** Температура воды *****

;Контроль температуры воды
_FC_Temp:	If_fNE0	DecEach1s_T,0		;Измерение не закончено => Ждём
		  return
		If_1	DS18B20_Present		;Датчик в норме => Чтение нового значения температуры в DS18B20_T
		  DS18B20ReadT				;Также обновляет значение DS18B20_Present
		If_1	DS18B20_Present		;Датчик в норме (после DS18B20ConvertT и DS18B20ReadT) => Контроль значения DS18B20_T
		  rcall	_FCT_CheckT
		If_0	DS18B20_Present		;Сбой датчика => Обработка
		  rcall	_FCT_DTError
_FCT_RunConvT:	;Запуск нового цикла измерения (вызывается также из инициализации программы)
		mov_lwf	.1,DecEach1s_T,0	;Запуск отсчёта нового цикла измерения
		DS18B20ConvertT			;Запуск нового преобразования температуры (DS18B20_Present будет обработан в конце цикла)
		return
_FCT_CheckT:	;Контроль значения DS18B20_T
		If_1	DS18B20_TH,7,0		;Отрицательное значение => Вода замерзает
		  bra	_FCT_T_Minus
		cmplw2f	(.110*16),DS18B20_T	;Сравнение с порогами
		b2ge1	_FCT_T_Over110
		cmplw2f	(.100*16),DS18B20_T
		b2ge1	_FCT_T_Over100
		cmplw2f	(.90*16),DS18B20_T
		b2ge1	_FCT_T_Over90
		cmplw2f	(.80*16),DS18B20_T
		b2ge1	_FCT_T_Over80
		cmplw2f	(.70*16),DS18B20_T
		b2ge1	_FCT_T_Over70
		cmplw2f	(.60*16),DS18B20_T
		b2ge1	_FCT_T_Over60
_FCT_T_Over0:	;Превышение 0 °С
		bsf	Output_RG		;Включение горелки
		bcf	Trevoga_Water		;Отключение тревоги по температуре воды
		mov_lwf	.100,TempBlink_10ms,0	;Индикатор ИТ мигает с частотой примерно 0.5 Гц (полупериод 1000 мс)
		return
_FCT_T_Over60:	;Превышение 60 °С
		bsf	Output_RG		;Включение горелки
		bcf	Trevoga_Water		;Отключение тревоги по температуре воды
		mov_lwf	.50,TempBlink_10ms,0	;Индикатор ИТ мигает с частотой примерно 1 Гц (полупериод 500 мс)
		return
_FCT_T_Over70:	;Превышение 70 °С
		bsf	Output_RG		;Включение горелки
		bcf	Trevoga_Water		;Отключение тревоги по температуре воды
		mov_lwf	.33,TempBlink_10ms,0	;Индикатор ИТ мигает с частотой примерно 1.5 Гц (полупериод 330 мс)
		return
_FCT_T_Over80:	;Превышение 80 °С
		bsf	Output_RG		;Включение горелки
		bcf	Trevoga_Water		;Отключение тревоги по температуре воды
		mov_lwf	.25,TempBlink_10ms,0	;Индикатор ИТ мигает с частотой примерно 2 Гц (полупериод 250 мс)
		return
_FCT_T_Over90:	;Превышение 90 °С
		bsf	Output_RG		;Включение горелки
		bsf	Trevoga_Water		;Включение тревоги по температуре воды
		mov_lwf	.17,TempBlink_10ms,0	;Индикатор ИТ мигает с частотой примерно 3 Гц (полупериод 170 мс)
		return
_FCT_T_Over100:	;Превышение 100 °С
		bcf	Output_RG		;Отключение горелки
		bsf	Trevoga_Water		;Включение тревоги по температуре воды
		clrf	TempBlink_10ms,0	;Индикатор ИТ горит постоянно
		return
_FCT_T_Over110:	;Превышение 110 °С
		bcf	Output_RG		;Отключение горелки
		bsf	Trevoga_Water		;Включение тревоги по температуре воды
		clrf	TempBlink_10ms,0	;Индикатор ИТ горит постоянно
		bra	StopAU			;Отключение автоматического управления и выход (return там)
_FCT_T_Minus:	;Отрицательные значения
		bra	_FCT_T_Over110		;Действия как при превышении 110 °C выход (return там)
_FCT_DTError:	;Сбой датчика
		bra	_FCT_T_Over110		;Действия как при превышении 110 °C выход (return там)

;***** Прочее *****

;Формирование сигнала Trevoga по флагам тревоги
_FC_Trevoga:	If_0	Trevoga_Water		;Есть хоть один из флагов тревоги => Установка сигнала
		  If_1	Trevoga_IDs
		  bra	_FCTr_ToSet
		If_1	Trevoga_KS
		  bra	_FCTr_ToSet
_FCTr_ToClear:	;Требуется сброс сигнала
		cmp_lwf	.100,IncEach10ms_T,0	;Минимальная длительность сигнала тревоги не набрана => Ждём
		b2lt1	_FCTr_Exit
		bcf	Trevoga			;Сигнала "Тревога" нет (заранее не сбрасывать, так как читается прерыванием)
		return
_FCTr_ToSet:	;Требуется установка сигнала
		If_0	Trevoga			;Сигнала ещё не было => Сброс отсчёта длительности
		  clrf	IncEach10ms_T,0
		bsf	Trevoga			;Сигнал "Тревога" есть
_FCTr_Exit:	return

;===== ДАННЫЕ, ОПРЕДЕЛЯЕМЫЕ ВО FLASH =========================================

EEPROM:		ORG	0xF00000

		DB	0xFF, 0			;Адрес 0: состояние (0xFF - не определено)

;=============================================================================

		END
