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

Различия минимальны, алгоритм поиска совпадает.
  • 0
  • 04 февраля 2016, 19:16
  • anakost

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

RSS свернуть / развернуть
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.