Simatic Step 7. STL. Номер установленного/сброшенного бита.

PLC
  По сути это программная реализация приоритетного шифратора (кодера). Он преобразует унитарный код с ведущей значащей единицей (нулем) в двоичный код номера установленного/сброшенного бита.
  Есть две разновидности алгоритма, поиск старшего или младшего бита. Например, в системе команд i386 для этого есть специальные команды BSR и BSF. Очевидно, если установленный бит один, результат будет одинаковый для обоих. Соответственно, если установленных битов несколько, результат будет отличаться.

  Начну с двойного слова (DWORD), затем будут рассмотрены реализации для WORD и BYTE. Выбор необходимой функции зависит от особенностей алгоритма прикладной программы.

Поиск номера установленного бита для DWORD:

FUNCTION FC43: VOID     

TITLE = Определение номера установленного младшего бита

AUTHOR:   Anakost  
FAMILY:   SOHO     
NAME:     NumSetBt // Number + Set + Bit  
VERSION:  1.0      

VAR_INPUT
  InBits: DWORD;//входное двойное слово
END_VAR

VAR_OUTPUT	
  NumBit: BYTE;	//номер установленного бита
END_VAR

VAR_TEMP	
  TmpNum: BYTE;	//временный счетчик
END_VAR

BEGIN

NETWORK
TITLE = Поиск установленного младшего бита DWORD

  L #InBits;	//входное двойное слово  
  L DW#16#0;	//
  T #TmpNum;	//обнулим временный счетчик
  ==D;		//проверка на 0
  JC A002;	//выход при 0
A001:
  POP;		//обрабатываемое двойное слово 
  SRD 1;	//сдвиг двойного слова вправо
  L #TmpNum;	//инкремент
  INC 1;	//временного
  T #TmpNum;	//счетчика
  JZ A001; 	//возврат на метку при последнем сдвинутом бите (CC 1) = 0
A002: 
//0-ошибка входа, действительные значения 1..32
  T #NumBit;	//результат
  
END_FUNCTION

  Необходимое предварительное условие, входное число не должно быть равно нулю. Иначе поиск установленного бита мог бы продолжаться до бесконечности.
  Обратите внимание на принудительное обнуление временного счетчика. Избалованный современными компиляторами ЯВУ я сначала этого не сделал, подумал что компилятор сам допишет код для обнуления при вызове функции. А вот фик вам. При втором и последующих вызовах счет продолжался с последнего оставшегося значения. Причем и в симуляторе PLCSIM и на рабочем контроллере.
  Команды INC и DEC работают только с младшим байтом аккумулятора и не влияют на флаги.
Модификация предыдущей функции для поиска установленного старшего бита:

NETWORK
TITLE = Поиск установленного старшего бита DWORD

  L DW#16#21;	//33
  T #TmpNum;	//предустановка временного счетчика
  
  L #InBits;	//входное двойное слово  
  L DW#16#0;	//
  ==D;		//проверка на 0
  JC A002;	//выход при 0
A001:
  POP;		//обрабатываемое двойное слово 
  SLD 1;	//сдвиг двойного слова влево
  L #TmpNum;	//декремент
  DEC 1;	//временного
  T #TmpNum;	//счетчика
  JZ A001; 	//возврат на метку при последнем сдвинутом бите (CC 1) = 0
A002: 
//0-ошибка входа, действительные значения 1..32
  T #NumBit;	//результат

  Т.к. DWORD и WORD это нативные форматы чисел для Simatic, поиск установленного бита для WORD отличается только форматами команд.

VAR_INPUT
  InBits: WORD;//входное слово
END_VAR
...
NETWORK
TITLE = Поиск установленного младшего бита WORD

  L #InBits;	//входное слово  
  L W#16#0;	//
  T #TmpNum;	//обнулим временный счетчик
  ==I;		//проверка на 0
  JC A002;	//выход при 0
A001:
  POP;		//обрабатываемое слово 
  SRW 1;	//сдвиг слова вправо
  L #TmpNum;	//инкремент
  INC 1;	//временного
  T #TmpNum;	//счетчика
  JZ A001; 	//возврат на метку при последнем сдвинутом бите (CC 1) = 0
A002: 
//0-ошибка входа, действительные значения 1..16
  T #NumBit;	//результат


NETWORK
TITLE = Поиск установленного старшего бита WORD

  L DW#16#11;	//17
  T #TmpNum;	//предустановка временного счетчика
  
  L #InBits;	//входное слово  
  L W#16#0;	//
  ==I;		//проверка на 0
  JC A002;	//выход при 0
A001:
  POP;		//обрабатываемое слово 
  SLW 1;	//сдвиг слова влево
  L #TmpNum;	//декремент
  DEC 1;	//временного
  T #TmpNum;	//счетчика
  JZ A001; 	//возврат на метку при последнем сдвинутом бите (CC 1) = 0
A002: 
//0-ошибка входа, действительные значения 1..16
  T #NumBit;	//результат

  У Simatic есть команды работающие только с форматом BYTE. Но специализированных команд для сдвига байта нет. Для сдвига вправо это не имеет значения, а вот сдвиг байта влево имеет особенность.

VAR_INPUT
  InBits: BYTE;//входной байт
END_VAR
...
NETWORK
TITLE = Поиск установленного младшего бита BYTE

  L #InBits;	//входной байт  
  L B#16#0;	//
  T #TmpNum;	//обнулим временный счетчик
  ==I;		//проверка на 0
  JC A002;	//выход при 0
A001:
  POP;		//обрабатываемый байт 
  SRW 1;	//сдвиг слова вправо
  L #TmpNum;	//инкремент
  INC 1;	//временного
  T #TmpNum;	//счетчика
  JZ A001; 	//возврат на метку при последнем сдвинутом бите (CC 1) = 0
A002: 
//0-ошибка входа, действительные значения 1..8
  T #NumBit;	//результат

Перед сдвигом влево байты в слове аккумулятора необходимо поменять местами. На флаги эта команда не влияет.

NETWORK
TITLE = Поиск установленного старшего бита BYTE

  L B#16#9;	//9
  T #TmpNum;	//предустановка временного счетчика
  
  L #InBits;	//младший байт, старший 0  
  CAW;		//обмен байтами в слове
  L W#16#0;	//
  ==I;		//проверка на 0
  JC A002;	//выход при 0
A001:
  POP;		//обрабатываемый байт 
  SLW 1;	//сдвиг слова влево
  L #TmpNum;	//декремент
  DEC 1;	//временного
  T #TmpNum;	//счетчика
  JZ A001; 	//возврат на метку при последнем сдвинутом бите (CC 1) = 0
A002: 
//0-ошибка входа, действительные значения 1..8
  T #NumBit;	//результат

Иногда возникает задача с обратным условием, найти первый/последний сброшенный бит. От приведенных примеров отличается только первичная проверка на допустимость числа (должно иметь хотя бы один сброшенный бит) и проверкой сдвинутого бита на сброшенность. Приведу пример для поиска младшего сброшенного бита в байте, для WORD все аналогично, для DWORD необходимо изменить форматы команд.

NETWORK
TITLE = Поиск сброшенного младшего бита BYTE

  L B#16#0;	//
  T #TmpNum;	//обнулим временный счетчик
  
  L #InBits;	//входной байт  
  L B#16#FF;	//
  ==I;		//проверка на допустимость числа
  INVI;		//инверсия аккумулятора
  JC A002;	//выход при отсутствии сброшенных бит
A001:
  POP;		//обрабатываемый байт 
  SRW 1;	//сдвиг слова вправо
  L #TmpNum;	//инкремент
  INC 1;	//временного
  T #TmpNum;	//счетчика
  JP A001; 	//возврат на метку при последнем сдвинутом бите (CC 1) = 1
A002: 
//0-ошибка входа, действительные значения 1..8
  T #NumBit;	//результат

После проверки числа на допустимость производится инверсия аккумулятора. Это сделано чтобы при ошибке ввода на выходе был 0. Команда INVI на флаги и результат предыдущей проверки не влияет.
  В стандартной библиотеке Simatic присутствует и функция FC64 «ENCO» для поиска наименьшего установленного значащего бита. Стандартными средствами посмотреть ее код нельзя, закрыта защитой. Но если очень хочется, то можно.

FUNCTION "ENCO" : INT
TITLE = ENCO (Encode Binary Position)
AUTHOR : SEA
FAMILY : CONVERT
NAME : ENCO
VERSION : 2.0

VAR_INPUT
  IN : DWORD ;	
END_VAR

VAR_TEMP
  wrkVal : DWORD ;	
END_VAR

BEGIN

NETWORK
TITLE =

  L     0; 		// set default out-value
  T     #RET_VAL; 	// .
  L     #IN; 		// load in-value
  <>D   ; 		// if(in <> 0)
  JCN   A001; 		// {
  T     #wrkVal; 	//   for(wrk = in; wrk & 1 == 0; wrk >>= 1)
A002: 
  L     #wrkVal; 	//   {
  SRD   1; 		//      if(wrk & 1) break
  JP    A001; 		//      .
  T     #wrkVal; 	//      wrk >>= 1
  L     #RET_VAL; 	//      out++
  L     1; 		//      .
  +I    ; 		//      .
  T     #RET_VAL; 	//      .
  JU    A002; 		//   }
A001: 
  SET   ; 		// }
  SAVE  ; 		// set BR
  
END_FUNCTION

Различия минимальны, алгоритм поиска совпадает.

Дополнение.

  В рассмотренных функциях алгоритм работы проверяет все биты в регистре в цикле. Т.к. DWORD содержит 32 бита, цикл может выполниться до 32 раз. Можно посчитать число команд (поиск установленного старшего бита DWORD), которые будут выполнены при входных аргументах 0x00000001 и 0x80000000:
  • аргумент 0x00000001, 6 + 6 * 1 + 1 = 13 команд;
  • аргумент 0x80000000, 6 + 6 * 32 + 1 = 199 команд;
  Поиск можно убыстрить, если применить вариант алгоритма бинарного поиска, его еще называют делением отрезка пополам.

FUNCTION FC44: VOID     

TITLE = Определение номера старшего бита

AUTHOR:   Anakost  
FAMILY:   SOHO     
NAME:     NumSetBt 	// Number + Set + Bit  
VERSION:  1.0      

VAR_INPUT
  InBits: DWORD;	//входное двойное слово
END_VAR

VAR_OUTPUT	
  NumBit: BYTE;		//номер установленного бита
END_VAR

VAR_TEMP	
  TmpNum: BYTE;		//временный счетчик
END_VAR

BEGIN

NETWORK
TITLE = Бинарный поиск установленного старшего бита DWORD

  L #InBits;		//входное двойное слово 
  L DW#16#0;		//
  ==D;			//проверка на 0
  JC A005;		//выход при 0
  INC 1;		//начальное значение счетчика
  T #TmpNum;		//
  POP;			//входное двойное слово    
//-------------------  
  AD DW#16#FFFF0000;    //
  POP;			//входное двойное слово    
  JZ A001;  		//переход при 0  
  SRD 16;		//сдвиг двойного слова вправо
  L #TmpNum;		//
  INC 16;		//
  T #TmpNum;		//
  POP;			//рабочее двойное слово    
A001:
//-------------------  
  AW W#16#FF00;         //
  POP;			//рабочее двойное слово    
  JZ A002;  		//переход при 0  
  SRW 8;		//сдвиг слова вправо
  L #TmpNum;		//
  INC 8;		//
  T #TmpNum;		//
  POP;			//рабочее слово    
A002: 
//-------------------  
  AW W#16#F0;           //
  POP;			//рабочее слово    
  JZ A003;  		//переход при 0  
  SRW 4;		//сдвиг слова вправо
  L #TmpNum;		//
  INC 4;		//
  T #TmpNum;		//
  POP;			//рабочий байт    
A003: 
//-------------------  
  AW W#16#C;            //
  POP;			//рабочий байт    
  JZ A004;  		//переход при 0  
  SRW 2;		//сдвиг слова вправо
  L #TmpNum;		//
  INC 2;		//
  T #TmpNum;		//
  POP;			//рабочий байт    
A004: 
//-------------------  
  AW W#16#2;            //
  POP;			//рабочий байт    
  JZ A005;  		//переход при 0  
  SRW 1;		//сдвиг слова вправо
  L #TmpNum;		//
  INC 1;		//
  T #TmpNum;		//
A005: 
//0-ошибка входа, действительные значения 1..32
  T #NumBit;	  	//результат
  
END_FUNCTION

  Если по алгоритму требуется определение установленного младшего бита, функцию легко изменить. Меняются шаблоны проверки и логика переходов:

NETWORK
TITLE = Бинарный поиск установленного младшего бита DWORD

  L #InBits;		//входное двойное слово 
  L DW#16#0;		//
  ==D;			//проверка на 0
  JC A005;		//выход при 0
  INC 1;		//начальное значение счетчика
  T #TmpNum;		//
  POP;			//входное двойное слово    
//-------------------  
  AD DW#16#0000FFFF;//
  POP;			//входное двойное слово    
  JP A001;  		//переход при CC = 1  
  SRD 16;		//сдвиг двойного слова вправо
  L #TmpNum;		//
  INC 16;		//
  T #TmpNum;		//
  POP;			//рабочее двойное слово    
A001:
//-------------------  
  AW W#16#00FF;
  POP;			//рабочее двойное слово    
  JP A002;  		//переход при  CC = 1  
  SRW 8;		//сдвиг слова вправо
  L #TmpNum;		//
  INC 8;		//
  T #TmpNum;		//
  POP;			//рабочее слово    
A002: 
//-------------------  
  AW W#16#0F;
  POP;			//рабочее слово    
  JP A003;  		//переход при  CC = 1  
  SRW 4;		//сдвиг слова вправо
  L #TmpNum;		//
  INC 4;		//
  T #TmpNum;		//
  POP;			//рабочий байт    
A003: 
//-------------------  
  AW W#16#3;
  POP;			//рабочий байт    
  JP A004;  		//переход при  CC = 1  
  SRW 2;		//сдвиг слова вправо
  L #TmpNum;		//
  INC 2;		//
  T #TmpNum;		//
  POP;			//рабочий байт    
A004: 
//-------------------  
  AW W#16#1;
  POP;			//рабочий байт    
  JP A005;  		//переход при  CC = 1  
  SRW 1;		//сдвиг слова вправо
  L #TmpNum;		//
  INC 1;		//
  T #TmpNum;		//
A005: 
//0-ошибка входа, действительные значения 1..32
  T #NumBit;		//результат

Подсчитаем число выполняемых команд при входных аргументах 0x00000001 и 0x80000000:
  • аргумент 0x00000001, 7 + 3 + 3 + 3 + 3 + 3 + 1 = 23 команды;
  • аргумент 0x80000000, 7 + 8 + 8 + 8 + 8 + 7 + 1 = 47 команд;
  Платой за быстродействие стал больший размер функции (186 слов вместо 86). Если входной аргумент ограничен словом WORD, функция упрощается, не нужен блок кода с шаблоном двойного слова. Если аргумент ограничен BYTE, можно убрать и блок кода с шаблоном слова.

  К сожалению в симуляторе PLCSIM этот код у меня не работает. Вот скриншот монитора в симуляторе:



  Команда POP копирует содержимое ACCU2 в ACCU1, содержимое ACCU2 при этом не изменяется. При запуске под симулятором оно обнуляется, код не работает.
При запуске в контроллере S7-313C все нормально:



  • -1
  • 08 декабря 2016, 14:58
  • anakost

Комментарии (29)

RSS свернуть / развернуть
Неужели нет более интересного материала по ПЛК, чем выкладывание кода примитивных функций?
0
  • avatar
  • Vga
  • 09 декабря 2016, 20:18
Вся программа ПЛК состоит из примитивных функций, как из кирпичиков. Ведь ПЛК это не числодробилка, это утилитарное управление агрегатом.
0
неинтересные у вас кирпичики :( такими глючными по сути кирпичами можно отбить познавание ПЛК — пишите подобные алгоритм на SCL
панельное строительство более наглядно
0
На таких кирпичиках строится все программирование на языках LAD и FBD.
Поконкретнее, plcist вы обнаружили в моих функциях глюки? Так поделитесь с окружающими, иначе это наговор.
-1
Основной Глюк — а для чего это надо?
в ПЛК не имеет смысла вычислять в слове входов какой бит слева или справа имеет значение 1… каждый бит самодостаточен для программы


Второй глюк — старческая болезнь алцГеймера… иначе не понять причину 2 раза в год постить одну и туже ерунду?


Объективно смотря на твои сообщения = лучше бы ты не лез в «учителя»… от тебя только вред в виде отвращения к ПЛК… мол мясорубщики ВСЕ тупые.
0
>> от тебя только вред…
Посмеялся, прикольно, может вы наконец разродитесь чем то нужным и полезным?
0
я не баба чтоб трепаться клавой
и не женщина чтоб рожать

я картинки с реальной аппаратурой и копиями экрана реального программного обеспечения просто ради стёба выкладываю :)
www.youtube.com/watch?v=4tPCDXPbH-M

0
я не баба чтоб трепаться клавой
Да неужели? Твои слова расходятся с твоими делами.
0
Я мог бы пИсать на SCL, Паскаль знаю достаточно прилично. Нет смысла. Функция STL c одинаковым фуекционалом всегда быстрее и меньше по размеру функции SCL.
По работе я поддерживаю оборудование в исправном (работоспособном) состоянии. О написании SCL кода речь вообще не идет. В основном я SCL код вижу в STL виде. И соответственно ненужное выкидываю, нужное переделываю. Как то так…
0
Я использую такие панели и не пытаюсь перенести ненужные тупые команды i386 в битовый процессор S7, в котором и так есть возможность узнать значение любого бита
[url=http://5cm.ru/view/i7/ugDr.jpg][img]http://i7.5cm.ru/t/ugDr.jpg[/img][/url]
0
Как то так наглядно
0
битовая LAD логика anime
[url=http://5cm.ru/view/i7/eURy.gif][img]http://i7.5cm.ru/t/eURy.gif[/img][/url]
0
битовая LAD логика anime
0
битовая LAD логика anime
0
Учебная картинка из курса LAD. Что она означает?
0
Ну этот язык Высокого уровня не все могут понять :)
0
OP для Simatic наиболее часто используют Windows CE. Под ней адаптирован соответствующий WinAC. WinCC именно его и использует. При чем тут чистый Step 7?
0
Если выдумываешь аллегории про строительство мелкого кирпичного гаража,
то понимай чужие аллегории про крупносерийное панельное строительство
придёт пэцээсник и заявит: всё это фигня… вот монолитой пэцээс бетон это весщь
У тебя наверное и кресло неумное, а вот у меня :)
www.youtube.com/watch?v=7P5I3RoKJoA

0
Вся программа ПЛК состоит из примитивных функций, как из кирпичиков.
Любая программа состоит из подобных кирпичиков, но это не повод выкладывать кучу статей в стиле «выводим надпись hello world на си», «считаем сумму двух чисел на си», etc.
0
Согласен, для С такие статьи выкладывать не к чему. Их вагон и большая тележка.
Для STL это не так.
0
Знающим STL людям эти статьи не нужны — они и сами такое легко напишут.
Не знающим STL людям эти статьи не нужны тем более — чтобы прочитать код нужно изучить STL, после чего возвращаемся к пункту.

Писать есть смысл статьи, которые рассказывают про ПЛК, как оно работает и как этим пользоваться. Либо про неочевидные трюки и вещи. Но «читаем время из структуры struct {byte year, month, day, hours, minutes, seconds}» постыдится писать даже КО.
0
после чего возвращаемся к пункту.
*предыдущему пункту
0
Замените STL на С и посмотрите что получится.
Со всем согласен…
0
Замените STL на С и посмотрите что получится.
Где? В «Знающим STL людям эти статьи не нужны ...»? И? То же самое и получится. На С такие статьи обладают такой же ценностью — нулевой.

Единственный хоть как-то (для меня) интересный момент в этих статьях — взглянуть на то, как выглядит код для ПЛК. Нда. И на этом пишут ответственные задачи, ужос.
0
Я использую ВСЕ языки в зависимости от конкретных потребностей.
Для наглядности фрагменты логики управления шаговым двигателем пуск/направление/остановка по датчику или кнопке (слева) и фрагменты логики управления буферными транзисторами UNL2802 для конкретного движка справа
и панель управления

со сменой микрошага
www.youtube.com/watch?v=9-X9ZQuyQcg

0
И?
0
да… да… сейчас 1995-ый год… Бергер ещё не писал книжек и его ещё не перевили на русский

а у российского Сименса ещё нет интернет Портала с документацией и программисты учатся по шпаргалкам
https://yadi.sk/d/zrQzGvwqvgqgF
0
Ну так оставьте шпаргалку, про вас сервер ответил — «Сюда еще никто не успел написать».
0
Дополнил алгоритмом.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.