Программирование SMSD-1.5Modbus - Страница 2
 

Программирование SMSD-1.5Modbus

Автор andrey-rodin, 21 февраля 2021, 23:23:25

« назад - далее »

Administrator

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

Administrator

#16
ЦитироватьК сожалению, нет никакого контроля правильности IL-программы. Можно написать бессмысленный код, содержащий, например, две инструкции LD, следующие непосредственно друг за другом, и такая программа будет загружена и будет выполняться.

Еще небольшое пояснение по вопросу двух LD подряд.

Само по себе допустимо использование двух LD подряд. Например, если они впоследствии будут объединены командой ORB. Ошибкой будет являться только если чтение контакта в LD никак не используется в дальнейшем. Т.е. состояние контакта запоминается для последующего использования, но фактически не используется. Именно это вызовен переполнение стека. В таком случае контроллер отобразит ошибку переполнения с соответствующим кодом.

andrey-rodin

ЦитироватьМожно - для этого существует команда CJ.

Можно-то оно можно, да вот только работать это правильно не будет. Вот пример подпрограммы, которая портит выполнение основной программы:

P 28
LD M108
CALL P21 ;вызываем процедуру расчёта шагов стола
LD M108
DMOV K0 D18 ;обнуляем D18
P 29
DADD D18 D14 D18 ;прибавляем к D18 количество шагов для одного перемещения стола
DADD D18 D363 D20 ;складываем текущую позицию и D18
DLD< D20 K0 ;если результат всё ещё отрицательный, то
CJ P29 ;продолжаем
LD M108
DSUB K0 D18 D18 ;результат готов, меняем знак
SRET


ЦитироватьЕсли как и положено, код подпрограммы расположен после  FEND, то ее вызов будет выполняться из основного цикла при выполнении входного условия.
Кстати, эта подпрограмма никак не участвовала в текущей ветке исполнения, но приводила к неправильной работе в других ветках. Разумеется, код подпрограммы был расположен после FEND.

Насчёт двух LD я, возможно, был неправ. Это был просто пример. На самом деле, я имел в виду такой потенциально опасный код как в приведённой подпрограмме. Получается, никто не запрещает осуществлять переходы CJ через инструкции LD, а это приводит к непредсказуемым результатам.

Administrator

ЦитироватьМожно-то оно можно, да вот только работать это правильно не будет. Вот пример подпрограммы, которая портит выполнение основной программы:

Приведите полный код, пожалуйста.

Administrator

Цитировать
DMOV   K0   D18      ;обнуляем D18
P   29
DADD   D18   D14   D18   ;прибавляем к D18 количество шагов для одного перемещения стола
DADD   D18   D363   D20   ;складываем текущую позицию и D18

Вот эта часть уже некорректная.
Подпрограмма CALL P21  выполняется по условию LD M108.

Инструкция DMOV   K0   D18 - тоже по условию LD   M108.

Что дальше имеется ввиду?

По какому условию должны выполняться инструкции DADD   D18   D14   D18   и
DADD   D18   D363   D20   ?

Либо нужно убрать P29 (или переместить их в другое место) - тогда две инструкции DADD будут выполняться вместе с DMOV, либо  добавить после P29 входное отдельное условие для инструкций DADD.

andrey-rodin

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

Собственно, вот это я и имел в виду. Если какие-то конструкции являются неправильными, хорошо бы предупреждать об этом пользователя. Насколько я понял, переход по CJ через LD запрещён, либо он обрабатывается текущей прошивкой неправильно. То есть реализовать цикл с проверкой условия нельзя.

Вы просили полную программу, вот она. Пока она в работе, и в коде могут встречаться какие-то шероховатости. Тут подпрограмма P28 портит видеорежим, хотя она не участвует в этой ветке исполнения. После загрузки программы в контроллер и перещелкивания тумблера контроллер может не реагировать на нажатие кнопки Пуск (а может и реагировать!). Приходится переключаться в другие режимы и запускать двигатель, после этого в видеорежиме двигатель запускается. Если двигатель запускается, то всё вроде бы работает за исключением валидации максимальной скорости в подпрограмме 29 - можно перейти через установленный предел в 8000pps. Если удалить тело подпрограммы P28, всё начинает работать корректно.


; Инициализация
; M0: 0 - требуется инициализация, 1 - инициализация уже осуществлена
LD M0 ;для пропуска участка инициализации проверяем
CJ P0 ;выполнение условия и будем переходить сразу к P0
LDP M108 ;передний фронт у M108 только после инициализации
FMOV K1500 D367 K2 ;ток разгона и торможения 1500мА
MOV K1200 D369 ;ток при движении с постоянной скоростью 1200мА
MOV K600 D370 ;ток режима удержания 600мА
TORQUE ;применить заданные значения токов
FMOV K0 D380 K3 ;без реакции на ошибки, сброс ошибок, использовать MIN_SPEED
DMOV K1480464 D100 ;в регистре D100 число шагов полного оборота стола при дроблении 1/256
SET M0 ;включаем условие обхода инициализации

P 0
LDP X3 ;при изменении положения галетного переключателя
ORF X3 ;проводим процедуру сброса
ORP X4
ORF X4
HHIZ ;останавливаем двигатель
ZRST M0 M30 ;обнуляем меркеры M0 - M30
ZRST Y0 Y11 ;обнуляем выходы

;Автоматический режим
LDI X3 ;галетный переключатель в положении A
AND X4 ;(X3 = 0 AND X4 = 1)
CALL P11

;Ручной режим
LDI X3 ;галетный переключатель в положении M
ANI X4 ;(X3 = 0 AND X4 = 0)
CALL P12

;Режим фото без остановок
LD X3 ;галетный переключатель в положении NS
AND X4 ;(X3 = 1 AND X4 = 1)
CALL P13

;Видеорежим
LD X3 ;галетный переключатель в положении V
ANI X4 ;(X3 = 1 AND X4 = 0)
CALL P14
FEND ;конец основной программы

;Автоматический режим
;M4: 0 - первое нажатие на кнопку Пуск/Стоп, 1 - второе нажатие
;M5: вспомогательный меркер для промежуточного хранения значения M4
;M6: меркер для счётчика C0
;M7: признак выполнения поворота на 90°
;M14, M15: меркеры временной задержки (выдержка фотоаппарата и успокоение модели)
;C0: счётчик количества шагов стола
;T0: таймер выдержки фотоаппарата
;T1: таймер задержки для успокоения модели съёмки после движения стола
;D0 - D9: таблица количества шагов стола
;A0 - индекс текущей градации шагов
P 11
LDI M7 ;не происходит поворот на 90°
ANI M4 ;кнопка Пуск/Стоп не была нажата, позволяем выставить градацию количества шагов
DIV D354 K410 A0 ;потенциометр 2: в A0 теперь индекс от 0 до 9
CALL P22 ;отображаем текущий результат по индексу

LDP X2 ;ловим фронт импульса при нажатии на кнопку Пуск/Стоп
CALL P23 ;вызываем процедуру инициализации
LD M6
CNT C0 D10 ;создаём счётчик C0 по меркеру M6

LDP X2 ;ловим фронт импульса при нажатии на кнопку Пуск/Стоп
ANI M7 ;не во время поворота
MPS
ANI M4 ;анализируем M4: если это первое нажатие на кнопку Пуск/Стоп, то
SET M5 ;устанавливаем меркер M5
SPIN ;запускаем двигатель и делаем первый шаг стола
RST C0 ;обнуляем счётчик С0
SET Y11 ;замыкаем реле Y11 (фотоаппарат готов)
MRD
AND M4 ;если это второе нажатие на кнопку Пуск/Стоп
RST M5 ;обнуляем меркер M5
RST Y11 ;размыкаем реле Y11 (освобождение фотоаппарата)
HHIZ ;останавливаем двигатель
MRD
AND M5
SET M4
MPP
ANI M5
RST M4

LD C0 ;если счётчик полон, завершаем процесс съёмки
SET Y10 ;снимаем последний кадр
HHIZ ;обесточиваем двигатель
RST Y10
RST Y11 ;размыкаем реле Y11 (освобождение фотоаппарата)
RST M4 ;конец работы

LDP X5 ;ловим фронт импульса при нажатии на зелёную кнопку -90°(A)
ANI M5 ;не в процессе съёмки
ANI M7 ;не во время поворота
MOV K1 D374 ;задаём движение против часовой стрелки
CALL P9 ;вызываем подпрограмму поворота на 90°
LDP X6 ;ловим фронт импульса при нажатии на жёлтую кнопку +90°(A)
ANI M5 ;не в процессе съёмки
ANI M7 ;не во время поворота
MOV K0 D374 ;задаём движение по часовой стрелке
CALL P9 ;вызываем подпрограмму поворота на 90°
LD M7 ;если поворот завершён -
AND= K2 D371 ;т.е. двигатель остановился
HHIZ ;обесточиваем двигатель
RST M7 ;снимаем признак осуществления поворота

LD M4 ;в процессе съёмки
AND= K2 D371 ;когда двигатель в режиме удержания
ANI M14 ;нет задержки таймеров
ANI M15
RST M6
DIV D352 K82 D16 ;потенциометр 0
ADD D16 K1 D16 ;в D16 теперь значение задержки после передвижения стола от 1 до 50
SET M14
LD M14
AND M4 ;в процессе съёмки
TMR T1 D16 ;ждём, пока модель съёмки успокоится
AND T1
RST M14 ;задержка завершена
SET Y10 ;снимаем очередной кадр
SET M15
LD M15
AND M4 ;в процессе съёмки
TMR T0 K3 ;ждём 300ms (выдержка 0"3)
AND T0
RST M15 ;задержка завершена
RST Y10 ;размыкаем реле Y10 (спуск затвора)
SPIN ;запускаем двигатель
SET M6 ;увеличиваем значение счётчика C0
SRET

;Ручной режим
;M8: 0 - первое нажатие на кнопку Пуск/Стоп, 1 - второе нажатие
;M9: вспомогательный меркер для промежуточного хранения значения M8
;M10: меркер для счётчика C1
;M11, M12, M13: меркеры временной задержки (выдержка фотоаппарата)
;C1: счётчик количества шагов стола
;T2, T3, T4: таймеры выдержки фотоаппарата 0"3
;D0 - D9: таблица количества шагов стола
;A0 - индекс текущей градации шагов
P 12
LDI M8 ;кнопка Пуск/Стоп не была нажата, позволяем выставить градацию количества шагов
DIV D354 K410 A0 ;потенциометр 2: в A0 теперь индекс от 0 до 9
CALL P22 ;отображаем текущий результат по индексу

LDP X2 ;ловим фронт импульса при нажатии на кнопку Пуск/Стоп
CALL P23 ;вызываем процедуру инициализации
LD M10
CNT C1 D10 ;создаём счётчик C1 по меркеру M10

LDP X2 ;ловим фронт импульса при нажатии на кнопку Пуск/Стоп
MPS
ANI M8 ;анализируем M8: если это первое нажатие на кнопку Пуск/Стоп, то
SET M9 ;устанавливаем меркер M9
SPIN ;запускаем двигатель и делаем первый шаг стола
RST C1 ;обнуляем счётчик C1
SET Y11 ;замыкаем реле Y11 (фотоаппарат готов)
MRD
AND M8 ;если это второе нажатие на кнопку Пуск/Стоп
RST M9 ;обнуляем меркер M9
RST Y11 ;размыкаем реле Y11 (освобождение фотоаппарата)
HHIZ ;останавливаем двигатель
MRD
AND M9
SET M8
MPP
ANI M9
RST M8

LD C1 ;если счётчик полон, завершаем процесс съёмки
RST Y11 ;размыкаем реле Y11 (освобождение фотоаппарата)
HHIZ ;обесточиваем двигатель
RST M8 ;конец работы

LDP X5 ;ловим фронт импульса при нажатии на зелёную кнопку ЗАТВОР
AND M8 ;в процессе съёмки
ANI M11 ;когда нет задержки таймеров
ANI M12
ANI M13
SET Y10 ;производим съёмку - замыкаем реле Y10 (спуск затвора)
SET M11
LD M11
TMR T2 K3 ;ждём 300ms (выдержка 0"3)
AND T2
RST M11 ;задержка завершена
RST Y10 ;размыкаем реле Y10 (спуск затвора)

LDP X5 ;ловим фронт импульса при нажатии на зелёную кнопку ЗАТВОР
ANI M8 ;не в процессе съёмки
ANI M11 ;когда нет задержки таймеров
ANI M12
ANI M13
SET Y11 ;замыкаем реле Y11 (фотоаппарат готов)
SET Y10 ;производим съёмку - замыкаем реле Y10 (спуск затвора)
SET M13
LD M13
TMR T4 K3 ;ждём 300ms (выдержка 0"3)
AND T4
RST M13 ;задержка завершена
RST Y10 ;размыкаем реле Y10 (спуск затвора)
RST Y11 ;размыкаем реле Y11 (освобождение фотоаппарата)

LDP X6 ;ловим фронт импульса при нажатии на жёлтую кнопку ДАЛЬШЕ
AND M8 ;только в процессе съёмки
AND= K2 D371 ;когда двигатель в состоянии удерживания
ANI M11 ;когда нет задержки таймеров
ANI M12
ANI M13
RST M10
SET Y10 ;производим съёмку - замыкаем реле Y10 (спуск затвора)
SET M12
LD M12
TMR T3 K3 ;ждём 300ms (выдержка 0"3)
AND T3
RST M12 ;задержка завершена
RST Y10 ;размыкаем реле Y10 (спуск затвора)
SET M10 ;увеличиваем значение счётчика
SPIN ;передвигаем стол на очередной шаг
SRET

;Режим фото без остановок
;M14: 0 - первое нажатие на кнопку Пуск/Стоп, 1 - второе нажатие
;M15: вспомогательный меркер для промежуточного хранения значения M14
;M16: меркер для счётчика C2
;M17: признак изменения скорости
;M18: сделан очередной снимок
;T5: таймер выдержки фотоаппарата 0"3
;C2: счётчик количества шагов стола
P 13
LDI M14 ;кнопка Пуск/Стоп не была нажата, позволяем выставить градацию количества шагов
DIV D354 K410 A0 ;потенциометр 2: в A0 теперь индекс от 0 до 9
CALL P22 ;отображаем текущий результат по индексу

LDP X2 ;ловим фронт импульса при нажатии на кнопку Пуск/Стоп
CALL P25 ;вызываем процедуру инициализации
LD M16
CNT C2 D10 ;создаём счётчик C2 по меркеру M16

LDP X2 ;ловим фронт импульса при нажатии на кнопку Пуск/Стоп
MPS
ANI M14 ;анализируем M14: если это первое нажатие на кнопку Пуск/Стоп, то
AND= K1 D371 ;если двигатель обесточен
SET M15 ;устанавливаем меркер M15
SPIN ;запускаем двигатель
RST C2 ;обнуляем счётчик C2
SET Y11 ;замыкаем реле Y11 (фотоаппарат готов)
MRD
AND M14 ;если это второе нажатие на кнопку Пуск/Стоп
RST M15 ;обнуляем меркер M15
RST Y11 ;размыкаем реле Y11 (освобождение фотоаппарата)
HHIZ ;останавливаем двигатель
MRD
AND M15
SET M14
MPP
ANI M15
RST M14

LD C2 ;если счётчик полон, завершаем процесс съёмки
;!!!SET Y10 ;снимаем последний кадр
SHIZ ;обесточиваем двигатель
RST Y10
RST Y11 ;размыкаем реле Y11 (освобождение фотоаппарата)
RST M14 ;конец работы

LD M14 ;двигатель вращается
AND X5 ;зелёная кнопка нажата
SET M17
DSUB D332 K5000 D332 ;уменьшаем коэффициент
CALL P27 ;устанавливаем новую скорость
LD M17 ;скорость изменена
SPIN ;применяем новое значение скорости
RST M17

LD M14 ;двигатель вращается
AND X6 ;жёлтая кнопка нажата
SET M17
DADD D332 K5000 D332 ;увеличиваем коэффициент
CALL P27 ;устанавливаем новую скорость
LD M17 ;скорость изменена
SPIN ;применяем новое значение скорости
RST M17

LD M14 ;двигатель вращается
DAND<= D363 D18 ;точка съёмки достигнута
RST M16
SET Y10 ;делаем снимок
SET M18
CALL P28 ;вычисляем позицию следующей точки съёмки
LD M18 ;снимок сделан
TMR T5 K3 ;ждём 300ms (выдержка 0"3)
AND T5
RST M18 ;задержка завершена
SET M16 ;увеличиваем значение счётчика
RST Y10 ;размыкаем реле Y10 (спуск затвора)
SRET

;Видеорежим
;M1: 0 - первое нажатие на кнопку Пуск/Стоп, 1 - второе нажатие
;M2: вспомогательный меркер для промежуточного хранения значения M1
;M3: признак изменения направления движения
;D320: энергонезависимый регистр для хранения текущей скорости и направления движения (как знак)
P 14
LDP X2 ;ловим фронт импульса при нажатии на кнопку Пуск/Стоп
MPS
ANI M1 ;анализируем M1: если это первое нажатие на кнопку Пуск/Стоп, то
SET M2 ;устанавливаем меркер M2
CALL P24 ;вызываем процедуру инициализации
LD M2
CALL P20 ;запускаем двигатель
MRD
AND M1 ;если это второе нажатие на кнопку Пуск/Стоп
RST M2 ;обнуляем меркер M2
SHIZ ;останавливаем двигатель
MRD
AND M2
SET M1
MPP
ANI M2
RST M1
LD M1 ;двигатель вращается
AND X5 ;зелёная кнопка нажата
CALL P30 ;уменьшаем значение скорости в регистре D320
LD M1
AND X5
CALL P10 ;устанавливаем новую скорость и направление в драйвере двигателя
LD M1
AND X5
CALL P20 ;применяем новые значения скорости и направления
LD M1 ;двигатель вращается
AND X6 ;жёлтая кнопка нажата
CALL P31 ;увеличиваем значение скорости в регистре D320
LD M1
AND X6
CALL P10 ;устанавливаем новую скорость и направление в драйвере двигателя
LD M1
AND X6
CALL P20 ;применяем новые значения скорости и направления
SRET

;Подпрограмма поворота на 90°
P 9
LDI M7
FMOV K30000 D361 K2 ;Инициализация: ускорение и торможение 30000 pps2
DMOV K80000 D357 ;Инициализация: скорость 80000 pps
MOV K8 D366 ;Инициализация: дробление 1/256
MOV K1 D376 ;Инициализация: регистр CMD, команда MOVE
DMOV K370116 D372 ;Инициализация: TARGET_POS = четверть оборота стола
SET M7 ;выставляем признак осуществления поворота на 90°
SPIN ;запускаем двигатель
SRET

;Подпрограмма установки скорости и направления движения из регистра D320
P 10
DLD> D320 K0 ;Если значение скорости > 0
MOV K0 D374 ;движение по часовой стрелке
DMOV D320 D357 ;устанавливаем значение скорости из регистра D320
DLD< D320 K0 ;Если значение скорости < 0
MOV K1 D374 ;движение против часовой стрелки
DMOV D320 D0 ;помещаем значение в регистр D0
DABS D0 ;берём модуль
DMOV D0 D357 ;устанавливаем значение скорости из регистра D0
SRET

;Подпрограмма применения новых значений скорости и ускорения для видеорежима
P 20
LD M3
HSTOP ;необходимо остановить двигатель, поскольку направление движения изменилось
RST M3
LD M108
SPIN ;можем применять новое значение скорости и направления
SRET

;Подпрограмма валидации скорости в регистре D320 для видеорежима
P 29
DLD> D320 K-100 ;Если значение скорости в регистре D320 больше -100
DAND< D320 K100 ;и меньше 100
DMOV K5000 D320 ;записываем в D320 значение 5000
DLD< D320 K-8000 ;если скорость меньше, чем -8000
DMOV K-8000 D320 ;записываем в D320 значение скорости -8000
DLD> D320 K8000 ;если скорость больше, чем 8000
DMOV K8000 D320 ;записываем в D320 значение скорости 8000
SRET

;Подпрограмма уменьшения скорости для видеорежима
P 30
LD M108
DSUB D320 K5 D320 ;уменьшаем значение скорости в регистре D320 на 5
DLD> D320 K0 ;если значение скорости положительное
DAND< D320 K100 ;и стало меньше 100
DMOV K-100 D320 ;записываем в D320 значение скорости -100
SET M3 ;устанавливаем признак изменения направления
LD M108
CALL P29 ;валидируем максимальную скорость
SRET

;Подпрограмма увеличения скорости для видеорежима
P 31
LD M108
DADD D320 K5 D320 ;увеличиваем значение скорости в регистре D320 на 5
DLD< D320 K0 ;если значение скорости отрицательное
DAND> D320 K-100 ;и стало больше -100
DMOV K100 D320 ;записываем в D320 значение скорости 100
SET M3 ;устанавливаем признак изменения направления
LD M108
CALL P29 ;валидируем максимальную скорость
SRET

;Подпрограмма расчёта шагов стола для автоматического, ручного и безостановочного режимов
P 21
LD M108
MOV K18 D0 ;создаём таблицу шагов стола
MOV K20 D1
MOV K24 D2
MOV K30 D3
MOV K36 D4
MOV K45 D5
MOV K60 D6
MOV K72 D7
MOV K90 D8
MOV K120 D9
MOV D0A0 D10 ;в D10 теперь текущее значение шагов
DDIV D100 D10 D14 ;в D14 количество шагов для одного перемещения стола
SRET

;Подпрограмма выставления выходов для количества шагов из регистра A0
P 22
LD M108 ;сначала устанавливаем меркеры, потому что команду OUT нельзя применять повторно
ZRST M20 M30 ;обнуляем меркеры M20 - M30
LD= A0 K0
SET M20
LD= A0 K1
SET M21
LD= A0 K2
SET M22
LD= A0 K3
SET M23
LD= A0 K4
SET M24
LD= A0 K5
SET M25
LD= A0 K6
SET M26
LD= A0 K7
SET M27
LD= A0 K8
SET M26
SET M27
LD= A0 K9
SET M25
SET M26
SET M27
LD M20 ;теперь устанавливаем выходы по меркерам
OUT Y0
LD M21
OUT Y1
LD M22
OUT Y2
LD M23
OUT Y3
LD M24
OUT Y4
LD M25
OUT Y5
LD M26
OUT Y6
LD M27
OUT Y7
SRET

;Подпрограмма инициализации автоматического и ручного режимов
P 23
LD M108
FMOV K30000 D361 K2 ;ускорение и торможение 30000 pps2
DMOV K80000 D357 ;скорость 80000 pps
MOV K8 D366 ;дробление 1/256
MOV K0 D374 ;движение по часовой стрелке
MOV K1 D376 ;регистр CMD, команда MOVE
CALL P21 ;вызываем процедуру расчёта шагов стола
LD M108 ;в D14 теперь количество шагов для одного перемещения стола
DMOV D14 D372 ;TARGET_POS = 1 шаг стола
SRET

;Подпрограмма инициализации безостановочного режима
P 25
LD M108
FMOV K30000 D361 K2 ;ускорение и торможение 30000 pps2
MOV K8 D366 ;дробление 1/256
MOV K0 D374 ;движение по часовой стрелке
MOV K0 D376 ;регистр CMD, команда RUN
DMOV K0 D363 ;обнуляем текущую позицию ABS
CALL P27 ;устанавливаем скорость
LD M108 ;в D14 теперь количество шагов для одного перемещения стола
CALL P28 ;вычисляем позицию следующей точки съёмки
SRET

;Подпрограмма валидации коэффициента в регистре D332 для безостановочного режима
P 26
DLD< D332 K-500000
DMOV K-500000 D332
DLD> D332 K500000
DMOV K500000 D332
SRET

;Подпрограмма установки скорости из регистра 322 для безостановочного режима
P 27
LD M108
CALL P21 ;вызываем процедуру расчёта шагов стола
LD M108
CALL P26 ;валидируем коэффициент
LD M108
DADD D332 K1000000 D0 ;складываем константу с коэффициентом
DDIV D0 D10 D16 ;делим на количество шагов - в D16 теперь значение скорости
DMOV D16 D357 ;устанавливаем скорость
SRET

;Подпрограмма установки значения следующей позиции в регистре D18 для безостановочного режима
P 28
LD M108
CALL P21 ;вызываем процедуру расчёта шагов стола
LD M108
DMOV K0 D18 ;обнуляем D18
P 29
DADD D18 D14 D18 ;прибавляем к D18 количество шагов для одного перемещения стола
DADD D18 D363 D20 ;складываем текущую позицию и D18
DLD< D20 K0 ;если результат всё ещё отрицательный, то
CJ P29 ;продолжаем
LD M108
DSUB K0 D18 D18 ;результат готов, меняем знак
SRET

;Подпрограмма инициализации видеорежима
P 24
LD M108
FMOV K1000 D361 K2 ;ускорение и торможение 1000 pps2
DMOV K8 D359 ;минимальная скорость 8 pps
MOV K3 D366 ;дробление 1/16
MOV K0 D376 ;регистр CMD, команда RUN
MOV K0 D379 ;запрещаем переход на полный шаг
LD M108
CALL P29 ;валидируем значение скорости в D320
LD M108
CALL P10 ;вызываем подпрограмму установки скорости и направления движения из регистра D320
SRET
END

Administrator

ЦитироватьА почему это она некорректная? При помощи метки P29 я пытался организовать цикл DO-WHILE. Почему Вы называете это подпрограммой? И откуда следует, что после метки обязательно должно быть входное условие?

Я называю это "часть кода", а не подпрограмма.
Перед этим у Вас вызывается подпрограмма CALL P21 cо своим входным условием.

В момент выполнения инструкции CJ у Вас происходит выполнение инструкций DADD, которые уже оказываются без входного условия.

Administrator

#22
ЦитироватьСобственно, вот это я и имел в виду. Если какие-то конструкции являются неправильными, хорошо бы предупреждать об этом пользователя.

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

ЦитироватьНасколько я понял, переход по CJ через LD запрещён, либо он обрабатывается текущей прошивкой неправильно.

CJ - это простой переход к указанному участку программы (помеченному указателем P). Нет никаких указаний насчет того, чтобы нельзя было бы "переходить" через LD. Очень даже можно и нужно.

В инструкции есть примеры для указателей P и инструкций CJ.

Например:

LD X0
CJ P10
.
.
P 10
LD X1
OUT Y1





Administrator

ЦитироватьВы просили полную программу, вот она.

Хорошо, посмотрим код и работу программы. Но вероятно, уже не сегодня.

andrey-rodin

В принципе, я разобрался, в чём проблема. Дело в том, что инструкция CJ позволяет переходить только вниз по программе. Об этом прямо сказано в руководстве:
ЦитироватьС помощью CJ-инструкции может пропускаться часть программы.

То, что нельзя переходить вверх, подтверждает следующая программа:


LD X2
OUT Y0
LD M108
CALL P10
FEND

P 10
LD M108
OUT Y10
CJ P10 ;бесконечный цикл
SRET
END


Вроде бы всё работает, контроллер не реагирует на контакт X2, но лампочка Y10 не загорается. К сожалению, ни SMSD Controller Demonstrator, ни SMC-Program Modbus не предупреждают об этой проблеме. Впрочем, таких проблемных мест, которые могли бы быть определены на стадии предварительного сканирования, много.

Отсюда следует вывод, что организовать полноценный цикл с проверкой условия невозможно. Цикл FOR-NEXT не подходит для этой цели, потому что он ограничен значением 32767.

Ну а что касается конкретной реализации подпрограммы P28, её, конечно, можно реализовать проще путём деления и проверки остатка от деления. Как нибудь так:


;Подпрограмма установки значения следующей позиции в регистре D18 для безостановочного режима
P 28
LD M108
CALL P21 ;вызываем процедуру расчёта шагов стола
LD M108 ;имеем в виду: текущая позиция отрицательна
DDIV D363 D14 D20 ;делим текущую позицию на количество шагов для одного перемещения стола нацело
DMUL D20 D14 D18 ;умножаем полученное значение на количество шагов для одного перемещения стола
MOD D363 D14 D20 ;был ли остаток от деления?
LD<> D20 K0 ;если да, то
DSUB D18 D14 D18 ;отнимаем D14
LD= D18 K0 ;если в D18 ноль
DSUB D18 D14 D18 ;отнимаем D14
SRET


Остаётся только один вопрос: каким образом эта подпрограмма могла влиять на поведение в другой ветке исполнения?

Administrator

ЦитироватьВ принципе, я разобрался, в чём проблема. Дело в том, что инструкция CJ позволяет переходить только вниз по программе. Об этом прямо сказано в руководстве:
Цитировать
С помощью CJ-инструкции может пропускаться часть программы.

То, что нельзя переходить вверх, подтверждает следующая программа:

Нет, это неверно. CJ может использоваться для перехода и вверх, и вниз.

В Вашем коде есть кусок
<code>
LD   C0         //если счётчик полон, завершаем процесс съёмки
SET   Y10         //снимаем последний кадр
HHIZ            //обесточиваем двигатель
RST   Y10
RST   Y11         //размыкаем реле Y11 (освобождение фотоаппарата)
RST   M4         //конец работы
</code>

Физический выход Y10 в этом случае не включится никогда, так как все выходы устанавливаются после завершения всего скана программы.

Посмотрите раздел "3.2. Различия между логикой лестничных диаграмм в контроллере и физическими
релейно-контактными электрическими схемами."

Остальной код тоже посмотрим.

Administrator

Цитировать
LD   X2
OUT   Y0
LD   M108
CALL   P10
FEND

P   10
LD   M108
OUT   Y10
CJ   P10   ;бесконечный цикл
SRET
END

Этот код тоже перепроверим.

Administrator

ЦитироватьLD   X2
OUT   Y0
LD   M108
CALL   P10
FEND

P   10
LD   M108
OUT   Y10
CJ   P10   ;бесконечный цикл
SRET
END

Этот код должен зависнуть, Y10 не выставится, так как выполнение программы до end не дойдёт никогда, и  соответственно, входы и выходы не обновятся.

andrey-rodin

ЦитироватьЭтот код должен зависнуть, Y10 не выставится, так как выполнение программы до end не дойдёт никогда, и  соответственно, входы и выходы не обновятся.

Верно, я не сообразил сразу. Я был неправ, циклы организовать можно, вот работающий пример:


LD X2
OUT Y0
DMOV K0 D0
CALL P10
FEND

P 10
LD M108
DADD K1 D0 D0 ;тело цикла, инкрементируем D0
LD> D0 K10000000 ;если D0 > 10000000
CJ P20 ;выходим
LD M108
CJ P10 ;переход на начало цикла
P 20
LD M108
SET Y10
SRET
END


ЦитироватьВ Вашем коде есть кусок... Физический выход Y10 в этом случае не включится никогда, так как все выходы устанавливаются после завершения всего скана программы.

Спасибо за найденную ошибку! Выход Y10, конечно, не включается. Я просто забыл удалить эти строчки.

clive

Цитата: andrey-rodin от 03 марта 2021, 13:15:31
...

4. Можно написать бесконечный цикл. Тогда контроллер просто перестаёт реагировать на входные сигналы без сообщений об ошибках.


Обновление физического состояния входов и выходов происходит в начале и конце сканирования программы соответственно. Если мы не дойдём никогда до END (FEND- если используются подпрограммы), то и физическое состояние обновить входов не сможем. Но есть такая штука, называется прерывание, можно заюзать их для создания условия выхода из бесконечного цикла. При использовании прерываний актуальное состояние входов отображается M100...M107. Можно просто даже оставить пустыми обработчики прерываний и в теле основной программы использовать M100...M107.